/**
 * yatemath.h
 * This file is part of the YATE Project http://YATE.null.ro
 *
 * Math data types
 *
 * Yet Another Telephony Engine - a fully featured software PBX and IVR
 * Copyright (C) 2015-2023 Null Team
 *
 * This software is distributed under multiple licenses;
 * see the COPYING file in the main directory for licensing
 * information for this specific distribution.
 *
 * This use of this software may be subject to additional restrictions.
 * See the LEGAL file in the main directory for details.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifndef __YATEMATH_H
#define __YATEMATH_H

#include <yateclass.h>
#include <math.h>
#include <string.h>

namespace TelEngine {

#ifdef DEBUG
#ifdef _WINDOWS
#define YMATH_FAIL(cond,...) { \
    if (!(cond)) \
	Debug(DebugFail,__VA_ARGS__); \
}
#else
#define YMATH_FAIL(cond,args...) { \
    if (!(cond)) \
	Debug(DebugFail,args); \
}
#endif
#else
#ifdef _WINDOWS
#define YMATH_FAIL do { break; } while
#else
#define YMATH_FAIL(arg...)
#endif
#endif

/**
 * This class implements a complex number
 * @short A Complex (float) number
 */
class YATE_API Complex
{
public:
    /**
     * Constructor
     */
    inline Complex()
	: m_real(0), m_imag(0)
	{}

    /**
     * Constructor
     * @param real The real part of the complex number
     * @param imag The imaginary part of a complex number
     */
    inline Complex(float real, float imag = 0)
	: m_real(real), m_imag(imag)
	{}

    /**
     * Copy constructor
     * @param c The source complex number
     */
    inline Complex(const Complex& c)
	: m_real(c.m_real), m_imag(c.m_imag)
	{}

    /**
     * Obtain the real part of the complex number
     * @return The real part
     */
    inline float re() const
	{ return m_real; }

    /**
     * Set the real part of the complex number
     * @param r The new real part value
     */
    inline void re(float r)
	{ m_real = r; }

    /**
     * Obtain the imaginary part of a complex number
     * @return The imaginary part
     */
    inline float im() const
	{ return m_imag; }

    /**
     * Set the imaginary part of the complex number
     * @param i The new imaginary part value
     */
    inline void im(float i)
	{ m_imag = i; }

    /**
     * Set data
     * @param r The real part of the complex number
     * @param i The imaginary part of a complex number
     * @return A reference to this object
     */
    inline Complex& set(float r = 0, float i = 0) {
	    m_real = r;
	    m_imag = i;
	    return *this;
	}

    /**
     * Equality operator
     * @param c Complex number to compare with
     * @return True if equal, false otherwise
     */
    inline bool operator==(const Complex& c) const
	{ return m_real == c.m_real && m_imag == c.m_imag; }

    /**
     * Inequality operator
     * @param c Complex number to compare with
     * @return True if not equal, false otherwise
     */
    inline bool operator!=(const Complex& c) const
	{ return m_real != c.m_real || m_imag != c.m_imag; }

    /**
     * Assignment operator
     * @param c Complex number to assign
     * @return A reference to this object
     */
    inline Complex& operator=(const Complex& c)
	{ return set(c.m_real,c.m_imag); }

    /**
     * Assignment operator. Set the real part, reset the imaginary one
     * @param real New real part value
     * @return A reference to this object
     */
    inline Complex& operator=(float real)
	{ return set(real); }

    /**
     * Addition operator
     * @param c Complex number to add
     * @return A reference to this object
     */
    inline Complex& operator+=(const Complex& c)
	{ return set(m_real + c.m_real,m_imag + c.m_imag); }

    /**
     * Addition operator. Add a value to the real part
     * @param real Value to add to real part
     * @return A reference to this object
     */
    inline Complex& operator+=(float real) {
	    m_real += real;
	    return *this;
	}

    /**
     * Substraction operator
     * @param c Complex number to substract from this one
     * @return A reference to this object
     */
    inline Complex& operator-=(const Complex& c)
	{ return set(m_real - c.m_real,m_imag - c.m_imag); }

    /**
     * Substraction operator. Substract a value a value from the real part
     * @param real Value to substract from real part
     * @return A reference to this object
     */
    inline Complex& operator-=(float real) {
	    m_real -= real;
	    return *this; 
	}

    /**
     * Multiplication operator
     * @param c Complex number to multiply with
     * @return A reference to this object
     */
    inline Complex& operator*=(const Complex& c) {
	    return set(m_real * c.m_real - m_imag * c.m_imag,
		m_real * c.m_imag + m_imag * c.m_real);
	}

    /**
     * Multiplication operator. Multiply this number with a float number
     * @param f Value to multiply with
     * @return A reference to this object
     */
    inline Complex& operator*=(float f)
	{ return set(m_real * f,m_imag * f); }

    /**
     * Division operator
     * @param c Complex number to devide with
     * @return A reference to this object
     */
    inline Complex& operator/=(const Complex& c) {
	    float tmp = c.norm2();
	    return set((m_real * c.m_real + m_imag * c.m_imag) / tmp,
		(-m_real * c.m_imag + m_imag * c.m_real) / tmp);
	}

    /**
     * Division operator
     * @param f Float number to devide with
     * @return A reference to this object
     */
    inline Complex& operator/=(float f)
	{ return set(m_real / f,m_imag / f); }

    /**
     * Compute the absolute value of this complex number
     * @return The result
     */
    inline float abs() const
	{ return ::sqrtf(norm2()); }

    /**
     * Compute the modulus value of this complex number
     * @return The result
     */
    inline float mod() const
	{ return abs(); }

    /**
     * Compute the the argument of this complex number
     * @return Ther result
     */
    inline float arg() const
	{ return ::atan(m_imag / m_real); }

    /**
     * Computes the exponential of this complex number
     * @return The result
     */
    inline Complex exp() const {
	    float r = ::expf(m_real);
	    return Complex(r * ::cosf(m_imag),r * ::sinf(m_imag));
	}

    /**
     * Compute the norm of this complex number
     * @return The result
     */
    inline float norm() const
	{ return abs(); }

    /**
     * Compute the norm2 value of this complex number
     * @return The result
     */
    inline float norm2() const
	{ return m_real * m_real + m_imag * m_imag; }

private:
    float m_real;                        // The real part
    float m_imag;                        // The imaginary part
};


/**
 * This class holds a ref counted storage
 * @short A fixed ref counted storage
 */
class YATE_API RefStorage : public RefObject
{
    YCLASS(RefStorage,RefObject)
    YNOCOPY(RefStorage); // No automatic copies please
public:
    /**
     * Constructor
     * @param value Data to assign, may be NULL to fill with zeros
     * @param len Length of data, may be zero (then value is ignored)
     */
    inline RefStorage(const void* value, unsigned int len)
	: m_data((void*)value,len)
	{}

    /**
     * Get the length of the stored data
     * @return The length of the stored data, zero for NULL
     */
    inline unsigned int length() const
	{ return m_data.length(); }

    /**
     * Get a pointer to a byte range inside the stored data
     * @param offs Byte offset inside the stored data
     * @param len Number of bytes that must be valid starting at offset (must not be 0)
     * @return A pointer to the data or NULL if the range is not available
     */
    inline void* data(unsigned int offs, unsigned int len) const
	{ return len ? m_data.data(offs,len) : 0; }

    /**
     * Copy data to this storage
     * @param buf Buffer to copy
     * @param len The number of bytes to copy
     * @param offs The start index in this storage
     * @return True on success, false if there is not enough space in our storage or
     *  the buffer pointer is NULL
     */
    inline bool set(const void* buf, unsigned int len, unsigned int offs = 0)
	{ return copy(data(offs,len),buf,len); }

    /**
     * Fill a buffer
     * @param dest Destination buffer
     * @param len The number of bytes to fill
     * @param val Value to fill with
     */
    static inline void fill(void* dest, unsigned int len, int val = 0) {
	    if (dest && len)
		::memset(dest,val,len);
	}

    /**
     * Copy data
     * @param dest Destination buffer
     * @param src Source buffer
     * @param len The number of bytes to copy
     * @return True on success, false if parameters are invalid
     */
    static inline bool copy(void* dest, const void* src, unsigned int len) {
	    if (!(len && dest && src))
		return len == 0;
	    if (dest != src)
		::memcpy(dest,src,len);
	    return true;
	}

    /**
     * Compare data
     * @param buf1 First buffer
     * @param buf2 Second buffer
     * @param len The number of bytes to compare
     * @return True if equal
     */
    static inline bool equals(const void* buf1, const void* buf2, unsigned int len) {
	    if (len && buf1 && buf2)
		return (buf1 == buf2) || (::memcmp(buf1,buf2,len) == 0);
	    return true;
	}

    /**
     * Split a string and append lines to another one
     * @param buf Destination string
     * @param str Input string
     * @param lineLen Line length, characters to copy
     * @param offset Offset in first line (if incomplete). No data will be
     *  added on first line if offset is greater then line length
     * @param linePrefix Prefix for new lines.
     *  Set it to empty string or 0 to use the suffix
     * @param suffix End of line for the last line
     * @return Destination string address
     */
    static String& dumpSplit(String& buf, const String& str, unsigned int lineLen,
	unsigned int offset = 0, const char* linePrefix = 0,
	const char* suffix = "\r\n");

private:
    RefStorage() {}; // No default constructor please

    DataBlock m_data;
};


/**
 * Base class for vector class(es).
 * Its purpose is to offer a common interface when processing lists
 * @short Base class for vector class(es)
 */
class YATE_API MathVectorBase : public GenObject
{
    YCLASS(MathVectorBase,GenObject)
public:
    /**
     * Destructor. Does nothing, keeps the compiler satisfied
     */
    virtual ~MathVectorBase()
	{}

    /**
     * Retrieve vector size in bytes
     * @return Vector size in bytes
     */
    virtual unsigned int vectorSize() const = 0;
};


/**
 * Template for vectors holding a fixed storage and a slice in it.
 * This class works with objects not holding pointers: it uses memcpy to copy data
 * @short A slice vector
 */
template <class Obj> class SliceVector : public MathVectorBase
{
public:
    /**
     * Constructor
     */
    inline SliceVector()
	: m_storage(0), m_data(0), m_length(0), m_maxLen(0)
	{}

    /**
     * Copy constructor.
     * Builds a slice of another vector
     * @param other Original vector
     */
    inline SliceVector(const SliceVector& other)
	: m_storage(0), m_data(0), m_length(0), m_maxLen(0)
	{ initSlice(false,other); }

    /**
     * Constructor.
     * Build the vector storage
     * @param len Length of data
     * @param buf Optional init buffer ('len' elements will be copied from it to storage)
     * @param maxLen Optional vector maximum length
     *  (it will be adjusted to be at least len)
     */
    explicit inline SliceVector(unsigned int len, const Obj* buf = 0,
	unsigned int maxLen = 0)
	: m_storage(0), m_data(0), m_length(0), m_maxLen(0)
	{ initStorage(len,buf,maxLen); }

    /**
     * Constructor.
     * Build a vector by concatenating two existing ones
     * @param v1 The first vector
     * @param v2 The second vector
     */
    explicit inline SliceVector(const SliceVector& v1, const SliceVector& v2)
	: m_storage(0), m_data(0), m_length(0), m_maxLen(0) {
	    if (!initStorage(v1.length(),v1.data(),v1.length() + v2.length()))
		return;
	    resizeMax();
	    m_storage->set(v2.data(),v2.size(),v1.size());
	}

    /**
     * Constructor.
     * Build a vector by concatenating three existing ones
     * @param v1 The first vector
     * @param v2 The second vector
     * @param v3 The third vector
     */
    explicit inline SliceVector(const SliceVector& v1, const SliceVector& v2,
	const SliceVector& v3)
	: m_storage(0), m_data(0), m_length(0), m_maxLen(0) {
	    unsigned int n = v1.length() + v2.length() + v3.length();
	    if (!initStorage(v1.length(),v1.data(),n))
		return;
	    resizeMax();
	    m_storage->set(v2.data(),v2.size(),v1.size());
	    m_storage->set(v3.data(),v3.size(),v1.size() + v2.size());
	}

    /**
     * Constructor.
     * Builds a slice of another vector
     * @param other Original vector
     * @param offs Offset in the original vector
     * @param len The number of elements (0 to use all available from offset)
     */
    explicit inline SliceVector(const SliceVector& other, unsigned int offs,
	unsigned int len = 0)
	: m_storage(0), m_data(0), m_length(0), m_maxLen(0)
	{ initSlice(false,other,offs,len); }

    /**
     * Destructor
     */
    virtual ~SliceVector()
	{ setData(); }

    /**
     * Get a pointer to data if 'len' elements are available from offset
     * @param offs The offset
     * @param len The number of elements to retrieve (must not be 0)
     * @return A pointer to data at requested offset,
     *  NULL if there is not enough data available
     */
    inline Obj* data(unsigned int offs, unsigned int len) {
	    if (len && length() && offs + len <= length())
		return m_data + offs;
	    return 0;
	}

    /**
     * Get a pointer to data if 'len' elements are available from offset
     * @param offs The offset
     * @param len The number of elements to retrieve (must not be 0)
     * @return A pointer to data at requested offset,
     *  NULL if there is not enough data available
     */
    inline const Obj* data(unsigned int offs, unsigned int len) const {
	    if (len && length() && offs + len <= length())
		return m_data + offs;
	    return 0;
	}

    /**
     * Get a pointer to data from offset to vector end
     * @param offs The offset
     * @return A pointer to data at requested offset, NULL if there is no data available
     */
    inline Obj* data(unsigned int offs = 0)
	{ return data(offs,available(offs)); }

    /**
     * Get a pointer to data from offset to vector end
     * @param offs The offset
     * @return A pointer to data at requested offset, NULL if there is no data available
     */
    inline const Obj* data(unsigned int offs = 0) const
	{ return data(offs,available(offs)); }

    /**
     * Get a pointer to data from offset to vector end
     * @param offs The offset
     * @param len The number of elements to retrieve (must not be 0)
     * @param eod Pointer to be filled with end of data element
     *  (pointer to first element after requested number of elements)
     * @return A pointer to data data from requested offset,
     *  NULL if there is not enough data available
     */
    inline Obj* data(unsigned int offs, unsigned int len, Obj*& eod) {
	    Obj* d = data(offs,len);
	    eod = end(d,len);
	    return d;
	}

    /**
     * Get a pointer to data from offset to vector end
     * @param offs The offset
     * @param len The number of elements to retrieve (must not be 0)
     * @param eod Pointer to be filled with end of data element
     *  (pointer to first element after requested number of elements)
     * @return A pointer to data data from requested offset,
     *  NULL if there is not enough data available
     */
    inline const Obj* data(unsigned int offs, unsigned int len, const Obj*& eod) const {
	    const Obj* d = data(offs,len);
	    eod = end(d,len);
	    return d;
	}

    /**
     * Get the length of the vector
     * @return The length of the vector
     */
    inline unsigned int length() const
	{ return m_length; }

    /**
     * Get the maximum length of the vector
     * @return The maximum length of the vector
     *  (0 if the vector don't have a storage buffer)
     */
    inline unsigned int maxLen() const
	{ return m_maxLen; }

    /**
     * Get the vector size in bytes
     * @return Vector size in bytes
     */
    inline unsigned int size() const
	{ return size(length()); }

    /**
     * Retrieve the available number of elements from given offset
     *  (not more than required number)
     * @param offs The offset
     * @param len Required number of elements (-1: all available from offset)
     * @return The available number of elements from given offset
     *  (may be less than required)
     */
    inline unsigned int available(unsigned int offs, int len = -1) const {
	    if (len && offs < length()) {
		unsigned int rest = length() - offs;
		return (len < 0 || rest <= (unsigned int)len) ? rest : (unsigned int)len;
	    }
	    return 0;
	}
	
    /**
     * Retrieve the available number of elements from given offset.
     * Clamp the available number of elements to requested value
     * @param clamp Maximum number of elements to check
     * @param offs The offset
     * @param len Required number of elements (-1: all available from offset)
     * @return The available number of elements from given offset
     */
    inline unsigned int availableClamp(unsigned int clamp, unsigned int offs = 0,
	int len = -1) const {
	    offs = available(offs,len);
	    return clamp <= offs ? clamp : offs;
	}

    /**
     * Retrieve vector size in bytes
     * @return Vector size in bytes
     */
    virtual unsigned int vectorSize() const
	{ return size(); }

    /**
     * Change the vector length without changing the contents.
     * If the vector length is increased the new elements' value are not reset
     * @param len New vector length
     * @return True on success, false if requested length is greater than max length
     */
    inline bool resize(unsigned int len) {
	    if (len <= maxLen()) {
		m_length = len;
		return true;
	    }
	    return false;
	}

    /**
     * Change the vector length to maximum allowed without changing the contents
     */
    inline void resizeMax()
	{ resize(maxLen()); }

    /**
     * Steal other vector's data
     * @param other Original vector
     */
    inline void steal(SliceVector& other) {
	    m_storage = other.m_storage;
	    m_data = other.m_data;
	    m_length = other.m_length;
	    m_maxLen = other.m_maxLen;
	    other.m_storage = 0;
	    other.m_data = 0;
	    other.m_length = other.m_maxLen = 0;
	}

    /**
     * Change the vector storage (re-allocate)
     * @param len New vector length (0 to clear vector data)
     * @param maxLen Optional vector maximum length
     *  (it will be adjusted to be at least len)
     */
    inline void resetStorage(unsigned int len, unsigned int maxLen = 0) {
	    setData();
	    initStorage(len,0,maxLen);
	}

    /**
     * Set a slice containing another vector
     * @param other Original vector
     * @param offs Offset in the original vector
     * @param len The number of elements (0 to use all available from offset)
     * @return True on success, false on failure
     */
    inline bool setSlice(const SliceVector& other, unsigned int offs = 0,
	unsigned int len = 0)
	{ return initSlice(true,other,offs,len); }

    /**
     * Retrieve vector head
     * @param len The number of elements to retrieve
     * @return A vector containing the first elements of this vector
     */
    inline SliceVector head(unsigned int len) const
	{ return slice(0,len); }

    /**
     * Retrieve vector head
     * @param dest Destination vector
     * @param len The number of elements to retrieve
     * @return True on success, false on failure (not enough data in our vector)
     */
    inline bool head(SliceVector& dest, unsigned int len) const
	{ return slice(dest,0,len); }

    /**
     * Retrieve vector tail (last elements)
     * @param len The number of elements to retrieve
     * @return A vector containing the last elements of this vector
     */
    inline SliceVector tail(unsigned int len) const {
	    if (len < length())
		return SliceVector(*this,length() - len,len);
	    return SliceVector();
	}

    /**
     * Retrieve vector tail (last elements)
     * @param len The number of elements to retrieve
     * @param dest Destination vector
     * @return True on success, false on failure (not enough data in our vector)
     */
    inline bool tail(SliceVector& dest, unsigned int len) const {
	    if (len <= length())
		return dest.initSlice(true,*this,length() - len,len);
	    dest.setData();
	    return false;
	}

    /**
     * Retrieve a vector slice
     * @param offs Offset in our vector
     * @param len The number of elements to retrieve
     * @return A vector containing the requested slice
     *  (empty if offset/length are invalid)
     */
    inline SliceVector slice(unsigned int offs, unsigned int len) const
	{ return SliceVector(*this,offs,len); }

    /**
     * Set a slice of this vector to another one.
     * The destination vector will be changed
     * @param dest Destination vector
     * @param offs Offset in our vector
     * @param len The number of elements (0 to use all available from offset)
     * @return True on success, false on failure (not enough data in our vector)
     */
    inline bool slice(SliceVector& dest, unsigned int offs,
	unsigned int len = 0) const
	{ return dest.initSlice(true,*this,offs,len); }

    /**
     * Copies elements from another vector to this one.
     * NOTE: This method don't check for overlapping data
     * @param src The source vector
     * @param len The number of elements to copy
     * @param offs The start index in our vector
     * @param srcOffs The start index in the source vector
     * @return True on success, false on failure (not enough data in source vector or
     *  not enough space in this vector)
     */
    inline bool copy(const SliceVector& src, unsigned int len,
	unsigned int offs = 0, unsigned int srcOffs = 0)
	{ return RefStorage::copy(data(offs,len),src.data(srcOffs,len),size(len)); }

    /**
     * Fill the buffer with 0
     * @param offs The offset
     * @param len The number of elements to retrieve (must not be 0)
     */
    inline void bzero(unsigned int offs, unsigned int len)
	{ RefStorage::fill(data(offs,len),size(len)); }

    /**
     * Fill the buffer with 0
     */
    inline void bzero()
	{ RefStorage::fill(data(),size()); }

    /**
     * Fill the vector with a given value
     * @param value The value to be set in this vector
     */
    inline void fill(const Obj& value) {
	    Obj* d = data();
	    for (Obj* last = end(d,length()); d != last; ++d)
		*d = value;
	}

    /**
     * Apply an unary function to all elements in this vector
     * @param func Function to apply
     */
    inline void apply(void (*func)(Obj&)) {
	    Obj* d = data();
	    for (Obj* last = end(d,length()); d != last; ++d)
		(*func)(*d);
	}

    /**
     * Sum vector values
     * @return The sum of the vector elements
     */
    inline Obj sum() const {
	    Obj result(0);
	    const Obj* d = data();
	    for (const Obj* last = end(d,length()); d != last; ++d)
		result += *d;
	    return result;
	}

    /**
     * Apply a function to all vector elements and take the sum of the results
     * @param func Function to apply
     * @return The result
     */
    inline Obj sumApply(Obj (*func)(const Obj&)) const {
	    Obj result(0);
	    const Obj* d = data();
	    for (const Obj* last = end(d,length()); d != last; ++d)
		result += (*func)(*d);
	    return result;
	}

    /**
     * Apply a function to all vector elements and take the sum of the results
     * @param func Function to apply
     * @return The result
     */
    inline float sumApplyF(float (*func)(const Obj&)) const {
	    float result = 0;
	    const Obj* d = data();
	    for (const Obj* last = end(d,length()); d != last; ++d)
		result += (*func)(*d);
	    return result;
	}

    /**
     * Sum this vector with another one
     * @param other Vector to sum with this one
     * @return True on sucess, false on failure (vectors don't have the same length)
     */
    inline bool sum(const SliceVector& other) {
	    if (length() != other.length())
		return false;
	    const Obj* od = other.m_data;
	    Obj* d = data();
	    for (Obj* last = end(d,length()); d != last; ++d, ++od)
		*d += *od;
	    return true;
	}

    /**
     * Add a value to each element of the vector
     * @param value Value to add
     */
    inline void sum(const Obj& value) {
	    Obj* d = data();
	    for (Obj* last = end(d,length()); d != last; ++d)
		*d += value;
	}

    /**
     * Substract another vector from this one
     * @param other Vector to substract
     * @return True on sucess, false on failure (vectors don't have the same length)
     */
    inline bool sub(const SliceVector& other) {
	    if (length() != other.length())
		return false;
	    const Obj* od = other.m_data;
	    Obj* d = data();
	    for (Obj* last = end(d,length()); d != last; ++d, ++od)
		*d -= *od;
	    return true;
	}

    /**
     * Substract a value from each element of the vector
     * @param value Value to substract
     */
    inline void sub(const Obj& value) {
	    Obj* d = data();
	    for (Obj* last = end(d,length()); d != last; ++d)
		*d -= value;
	}

    /**
     * Multiply this vector with another one
     * @param other Vector to multiply with
     * @return True on sucess, false on failure (vectors don't have the same length)
     */
    inline bool mul(const SliceVector& other) {
	    if (length() != other.length())
		return false;
	    const Obj* od = other.m_data;
	    Obj* d = data();
	    for (Obj* last = end(d,length()); d != last; ++d, ++od)
		*d *= *od;
	    return true;
	}

    /**
     * Multiply this vector with a value
     * @param value Value to multiply with
     */
    inline void mul(const Obj& value) {
	    Obj* d = data();
	    for (Obj* last = end(d,length()); d != last; ++d)
		*d *= value;
	}

    /**
     * Multiply this vector with a value
     * @param value Value to multiply with
     */
    inline void mul(float value) {
	    Obj* d = data();
	    for (Obj* last = end(d,length()); d != last; ++d)
		*d *= value;
	}

    /**
     * Indexing operator with unsigned int
     * @param index Index of element to retrieve
     * @return The element at requested index
     */
    inline Obj& operator[](unsigned int index) {
	    YMATH_FAIL(index < m_length,
		"SliceVector::operator[] index out of bounds [%p]",this);
	    return m_data[index];
	}

    /**
     * Indexing operator with unsigned int
     * @param index Index of element to retrieve
     * @return The element at requested index
     */
    inline const Obj& operator[](unsigned int index) const {
	    YMATH_FAIL(index < m_length,
		"SliceVector::operator[] index out of bounds [%p]",this);
	    return m_data[index];
	}

    /**
     * Indexing operator with signed int
     * @param index Index of element to retrieve
     * @return The element at requested index
     */
    inline Obj& operator[](signed int index) {
	    YMATH_FAIL((unsigned int)index < m_length,
		"SliceVector::operator[] index out of bounds [%p]",this);
	    return m_data[index];
	}

    /**
     * Indexing operator with signed int
     * @param index Index of element to retrieve
     * @return The element at requested index
     */
    inline const Obj& operator[](signed int index) const {
	    YMATH_FAIL((unsigned int)index < m_length,
		"SliceVector::operator[] index out of bounds [%p]",this);
	    return m_data[index];
	}

    /**
     * Equality operator
     * @param other Original vector
     * @return True if the vectors are equal, false otherwise
     */
    inline bool operator==(const SliceVector& other) const
	{ return equals(other); }

    /**
     * Inequality operator
     * @param other Original vector
     * @return True if the vectors are not equal, false otherwise
     */
    inline bool operator!=(const SliceVector& other) const
	{ return !equals(other); }

    /**
     * Asignment operator. Builds a slice of another vector
     * @param other Original vector
     * @return A reference to this vector
     */
    inline SliceVector& operator=(const SliceVector& other) {
	    setSlice(other);
	    return *this;
	}

    /**
     * Sum this vector with another one
     * @param other Vector to sum with this one
     * @return A reference to this vector
     */
    inline SliceVector& operator+=(const SliceVector& other) {
	    YMATH_FAIL(length() == other.length(),
		"SliceVector(+=): invalid lengths [%p]",this);
	    sum(other);
	    return *this;
	}

    /**
     * Add a value to each element of the vector
     * @param value Value to add
     * @return A reference to this vector
     */
    inline SliceVector& operator+=(const Obj& value) {
	    sum(value);
	    return *this;
	}

    /**
     * Substract another vector from this one
     * @param other Vector to substract
     * @return A reference to this vector
     */
    inline SliceVector& operator-=(const SliceVector& other) {
	    YMATH_FAIL(length() == other.length(),
		"SliceVector(-=): invalid lengths [%p]",this);
	    sub(other);
	    return *this;
	}

    /**
     * Substract a value from each element of the vector
     * @param value Value to substract
     * @return A reference to this vector
     */
    inline SliceVector& operator-=(const Obj& value) {
	    sub(value);
	    return *this;
	}

    /**
     * Multiply this vector with another one
     * @param other Vector to multiply with
     * @return A reference to this vector
     */
    inline SliceVector& operator*=(const SliceVector& other) {
	    YMATH_FAIL(length() == other.length(),
		"SliceVector(*=): invalid lengths [%p]",this);
	    mul(other);
	    return *this;
	}

    /**
     * Multiply this vector with a value
     * @param value Value to multiply with
     * @return A reference to this vector
     */
    inline SliceVector& operator*=(const Obj& value) {
	    mul(value);
	    return *this;
	}

    /**
     * Multiply this vector with a value
     * @param value Value to multiply with
     * @return A reference to this vector
     */
    inline SliceVector& operator*=(float value) {
	    mul(value);
	    return *this;
	}

    /**
     * Compare this vector to another one (compare storage)
     * @param other Vector to compare with
     * @return True if they are equal
     */
    inline bool equals(const SliceVector& other) const {
	    return length() == other.length() &&
		RefStorage::equals(data(),other.data(),size());
	}

    /**
     * Dump data to a string (append)
     * @param buf Destination string
     * @param func Pointer to function who appends the object to a String
     *  (0 to dump all available from offset)
     * @param sep Vector elements separator
     * @param fmt Optional format to use
     * @return Destination string address
     */
    String& dump(String& buf,
	String& (*func)(String& s, const Obj& o, const char* sep, const char* fmt),
	const char* sep = ",", const char* fmt = 0) const {
	    const Obj* d = data();
	    if (!(d && func))
		return buf;
	    String localBuf;
	    for (const Obj* last = end(d,length()); d != last; ++d)
		(*func)(localBuf,*d,sep,fmt);
	    return buf.append(localBuf);
	}

    /**
     * Dump this vector to string, split it and append lines to a buffer.
     * Line prefix length is not included when line length is calculated.
     * Separator length is included in line length
     * @param buf Destination string
     * @param lineLen Line length in bytes
     * @param func Pointer to function who append the object to a String
     * @param offset Offset in first line (if incomplete). No data will be
     *  added on first line if offset is greater then line length
     * @param linePrefix Prefix for new lines. Empty string to use the suffix
     * @param suffix String to always add to final result (even if no data dumped)
     * @param sep Vector elements separator
     * @param fmt Optional format to use
     * @return Destination string address
     */
    String& dump(String& buf, unsigned int lineLen,
	String& (*func)(String& s, const Obj& o, const char* sep, const char* fmt),
	unsigned int offset = 0, const char* linePrefix = 0,
	const char* suffix = "\r\n", const char* sep = ",", const char* fmt = 0) const {
	    const Obj* d = data();
	    if (!(d && func))
		return buf.append(suffix);
	    if (TelEngine::null(linePrefix))
		linePrefix = suffix;
	    if (!lineLen || TelEngine::null(linePrefix))
		return dump(buf,func,sep,fmt) << suffix;
	    String localBuf;
	    for (const Obj* last = end(d,length()); d != last;) {
		String tmp;
		(*func)(tmp,*d,0,fmt);
		if (++d != last)
		    tmp << sep;
		offset += tmp.length();
		if (offset > lineLen) {
		    localBuf << linePrefix;
		    offset = tmp.length();
		}
		localBuf << tmp;
	    }
	    return buf << localBuf << suffix;
	}

    /**
     * Hexify data
     * @param buf Destination string
     * @param sep Optional separator
     * @return Destination string address
     */
    inline String& hexify(String& buf, char sep = 0) const
	{ return buf.hexify((void*)data(),size(),sep); }
	
    /**
     * Hexify data, split it and append lines to a string
     * @param buf Destination string
     * @param lineLen Line length, characters to copy
     * @param offset Offset in first line (if incomplete). No data will be
     *  added on first line if offset is greater then line length
     * @param linePrefix Prefix for new lines.
     *  Set it to empty string or 0 to use the suffix
     * @param suffix End of line for the last line
     * @return Destination string address
     */
    inline String& dumpHex(String& buf, unsigned int lineLen,
	unsigned int offset = 0, const char* linePrefix = 0,
	const char* suffix = "\r\n") const {
	    String h;
	    return RefStorage::dumpSplit(buf,hexify(h),lineLen,offset,linePrefix,suffix);
	}
	
    /**
     * Reset storage from a hexadecimal string representation.
     * Clears the vector at start, i.e. the vector will be empty on failure.
     * The vector may be empty on success also.
     * Each octet must be represented in the input string with 2 hexadecimal characters.
     * If a separator is specified, the octets in input string must be separated using
     *  exactly 1 separator. Only 1 leading or 1 trailing separators are allowed.
     * @param str Input character string
     * @param len Length of input string
     * @param sep Separator character used between octets.
     *  [-128..127]: expected separator (0: no separator is expected).
     *  Detect the separator if other value is given
     * @return 0 on success, negative if unhexify fails,
     *  positive if the result is not a multiple of Obj size
     */
    int unHexify(const char* str, unsigned int len, int sep = 255) {
	    setData();
	    DataBlock db;
	    bool ok = (sep < -128 || sep > 127) ? db.unHexify(str,len) :
		db.unHexify(str,len,(char)sep);
	    if (ok && (db.length() % objSize() == 0)) {
		initStorage(db.length() / objSize(),(const Obj*)db.data(0,db.length()));
		return 0;
	    }
	    return ok ? 1 : -1;
	}

    /**
     * Unhexify data
     * @param str Input string
     * @param sep Separator character used between octets
     * @return See unHexify(const char*,unsigned int,char)
     */
    inline int unHexify(const String& str, int sep = 255)
	{ return unHexify(str.c_str(),str.length(),sep); }

    /**
     * Retrieve the object size
     * @return Obj size in bytes
     */
    static inline unsigned int objSize()
	{ return sizeof(Obj); }

    /**
     * Retrieve the length in bytes of a buffer containing 'count' objects
     * @param len Buffer length
     * @return Buffer length in bytes
     */
    static inline unsigned int size(unsigned int len)
	{ return len * objSize(); }

protected:
    // Return end-of-data pointer from start and given length
    inline Obj* end(Obj* start, unsigned int len)
	{ return start ? (start + len) : 0; }
    inline const Obj* end(const Obj* start, unsigned int len) const
	{ return start ? (start + len) : 0; }
    // Set data. Reset storage if we don't have a valid data pointer
    // Return true if we have valid data
    inline bool setData(Obj* data = 0, unsigned int len = 0, unsigned int maxLen = 0) {
	    m_data = data;
	    if (m_data) {
		m_length = len;
		m_maxLen = maxLen;
	    }
	    else {
		m_length = m_maxLen = 0;
		TelEngine::destruct(m_storage);
	    }
	    return m_data != 0;
	}
    // Build storage, update data. This method assumes our data is cleared
    // If data is given 'len' elements will be copied from it to storage
    inline bool initStorage(unsigned int len, const Obj* data = 0,
	unsigned int maxLen = 0) {
	    if (maxLen < len)
		maxLen = len;
	    if (!maxLen)
		return false;
	    if (!data || maxLen == len)
		m_storage = new RefStorage(data,size(maxLen));
	    else {
		m_storage = new RefStorage(0,size(maxLen));
		m_storage->set(data,size(len));
	    }
	    return setData((Obj*)m_storage->data(0,1),len,maxLen);
	}
    // Build storage from slice and update data.
    // Clear data if requested
    inline bool initSlice(bool del, const SliceVector& other, unsigned int offs = 0,
	unsigned int len = 0) {
	    if (!len)
		len = other.length();
	    Obj* d = (Obj*)other.data(offs,len);
	    if (!d) {
		if (del)
		    setData();
		return len == 0;
	    }
	    if (m_storage == other.m_storage)
		return setData(d,len,len);
	    RefStorage* tmp = other.m_storage;
	    if (tmp->ref()) {
		TelEngine::destruct(m_storage);
		m_storage = tmp;
		return setData(d,len,len);
	    }
	    Debug(DebugFail,"SliceVector storage ref() failed");
	    return del ? setData() : false;
	}

    RefStorage* m_storage;               // Vector storage
    Obj* m_data;                         // Pointer to data
    unsigned int m_length;               // Data length
    unsigned int m_maxLen;               // Max storage
};

typedef SliceVector<Complex> ComplexVector;
typedef SliceVector<float> FloatVector;
typedef SliceVector<uint8_t> ByteVector;

/**
 * This vector holds bit values using 1 byte. It implements methods operating on bits.
 * NOTE: The array indexing operator allows setting invalid values (not 1 or 0).
 *  The pack/unpack methods are safe (they will handle non 0 values as bit 1).
 *  The comparison operators may fail for vectors containing values other than 0 or 1.
 * @short A slice vector holding bits
 */
class YATE_API BitVector : public ByteVector
{
public:
    /**
     * Constructor
     */
    inline BitVector()
	{}

    /**
     * Copy constructor.
     * Builds a slice of another vector
     * @param other Original vector
     */
    inline BitVector(const BitVector& other)
	: ByteVector(other)
	{}

    /**
     * Constructor.
     * Build the vector storage
     * @param len Length of data
     * @param maxLen Optional vector maximum length
     *  (it will be adjusted to be at least len)
     */
    explicit inline BitVector(unsigned int len, unsigned int maxLen = 0)
	: ByteVector(len,0,maxLen)
	{}

    /**
     * Constructor.
     * Builds a slice of another vector
     * @param other Original vector
     * @param offs Offset in the original vector
     * @param len The number of elements (0 to use all available from offset)
     */
    explicit inline BitVector(const BitVector& other, unsigned int offs,
	unsigned int len = 0)
	: ByteVector(other,offs,len)
	{}

    /**
     * Constructor. Build from string bits
     * @param str String bits ('1' -> 1, else -> 0)
     * @param maxLen Optional vector maximum length
     */
    explicit BitVector(const char* str, unsigned int maxLen = 0);

    /**
     * Check if this vector contains valid values (0 or 1)
     * @return True on success, false if the vector contains values other than 0 or 1
     */
    bool valid() const;

    /**
     * Set float bit values from this vector (0 -> 0.0F, non 0 -> 1.0F).
     * The destination vector will be resized to this vector's length
     * @param dest The destination vector
     * @return True on success, false if destination resize failed
     */
    bool get(FloatVector& dest) const;

    /**
     * Initializes this vector from float values (0.0F -> 0, non 0 -> 1).
     * The vector will be resized to input length
     * @param input The input vector
     * @return True on success, false if resize failed
     */
    bool set(const FloatVector& input);

    /**
     * Apply XOR on vector elements using a given value's bits, MSB first.
     * Given v31,v30,...,v0 the value's bits in MSB order the result will be
     *  data()[offs] ^= v31, data()[offs+1] ^= v30 ...
     * @param value Value to use
     * @param offs Start position in this BitVector
     * @param len The number of bits to use
     */
    void xorMsb(uint32_t value, unsigned int offs = 0, uint8_t len = 32);

    /**
     * Apply XOR on vector elements using a given value's bits, MSB first.
     * Given v15,v14,...,v0 the value's bits in MSB order the result will be
     *  data()[offs] ^= v15, data()[offs+1] ^= v14 ...
     * @param value Value to use
     * @param offs Start position in this BitVector
     * @param len The number of bits to use
     */
    inline void xorMsb16(uint16_t value, unsigned int offs = 0, uint8_t len = 16)
	{ return xorMsb((uint32_t)value << 16,offs,len <= 16 ? len : 16); }

    /**
     * Pack up to 64 bits, LSB-first (i.e. first bit goes to LSB in destination)
     * @param offs The start offset
     * @param len The number of elements to be packed (-1 to pack all available).
     *  No more than 64 bits will be packed
     * @return The packed 64-bit value
     */
    uint64_t pack(unsigned int offs = 0, int len = -1) const;

    /**
     * Unpack up to 64 bits into this vector, LSB first
     * @param value Value to unpack
     * @param offs Optional start offset
     * @param len The number of bits to unpack
     */
    void unpack(uint64_t value, unsigned int offs = 0, uint8_t len = 64);

    /**
     * Unpack up to 32 bits into this vector (MSB to LSB).
     * MSB from value is the first unpacked bit
     * @param value The value to be unpacked
     * @param offs Optional start offset
     * @param len The number of bits to unpack
     */
    void unpackMsb(uint32_t value, unsigned int offs = 0, uint8_t len = 32);

    /**
     * Unpack up to 16 bits into this vector (MSB to LSB).
     * MSB from value is the first unpacked bit
     * @param value The value to be unpacked
     * @param offs Optional start offset
     * @param len The number of bits to unpack
     */
    inline void unpackMsb16(uint16_t value, unsigned int offs = 0, uint8_t len = 16)
	{ unpackMsb((uint32_t)value << 16,offs,len <= 16 ? len : 16); }

    /**
     * Pack bits into a ByteVector (LSB source to MSB in destination).
     * MSB of first byte in destination will have the same value of the
     *  first bit in this vector.
     * Remaining elements in destination are left untouched
     * @param dest Destination vector
     * @return True on success, false on failure (not enough space in destination vector)
     */
    bool pack(ByteVector& dest) const;

    /**
     * Unpack a ByteVector into this BitVector.
     * MSB of the first element in source goes to first bit in this vector.
     * Remaining bits are left untouched
     * @param src Source byte vector
     * @return True on success, false if there is not enough space to unpack
     */
    bool unpack(const ByteVector& src);

    /**
     * Append bits to string
     * @param buf Destination string
     * @param offs Optional starting index
     * @param len The number of elements to be added (negative to add all)
     * @return Destination string address
     */
    String& appendTo(String& buf, unsigned int offs = 0, int len = -1) const;

    /**
     * Build a String from vector bits and return it
     * @param offs Optional starting index
     * @param len The number of elements to be added (negative to add all)
     * @return A newly created String containing vector bits
     */
    inline String toString(unsigned int offs, int len = -1) const {
	    String tmp;
	    return appendTo(tmp,offs,len);
	}

    /**
     * Set a slice containing another vector
     * @param other Original vector
     * @param offs Offset in the original vector
     * @param len The number of elements (0 to use all available from offset)
     * @return True on success, false on failure
     */
    inline bool setSlice(const BitVector& other, unsigned int offs = 0,
	unsigned int len = 0)
	{ return initSlice(true,other,offs,len); }

    /**
     * Retrieve vector head
     * @param len The number of elements to retrieve
     * @return A vector containing the first elements of this vector
     */
    inline BitVector head(unsigned int len) const
	{ return slice(0,len); }

    /**
     * Retrieve vector head
     * @param dest Destination vector
     * @param len The number of elements to retrieve
     * @return True on success, false on failure (not enough data in our vector)
     */
    inline bool head(BitVector& dest, unsigned int len) const
	{ return slice(dest,0,len); }

    /**
     * Retrieve vector tail (last elements)
     * @param len The number of elements to retrieve
     * @return A vector containing the last elements of this vector
     */
    inline BitVector tail(unsigned int len) const {
	    if (len < length())
		return BitVector(*this,length() - len,len);
	    return BitVector();
	}

    /**
     * Retrieve vector tail (last elements)
     * @param len The number of elements to retrieve
     * @param dest Destination vector
     * @return True on success, false on failure (not enough data in our vector)
     */
    inline bool tail(BitVector& dest, unsigned int len) const {
	    if (len <= length())
		return dest.initSlice(true,*this,length() - len,len);
	    dest.setData();
	    return false;
	}

    /**
     * Retrieve a vector slice
     * @param offs Offset in our vector
     * @param len The number of elements to retrieve
     * @return A vector containing the requested slice
     *  (empty if offset/length are invalid)
     */
    inline BitVector slice(unsigned int offs, unsigned int len) const
	{ return BitVector(*this,offs,len); }

    /**
     * Set a slice of this vector to another one.
     * The destination vector will be changed
     * @param dest Destination vector
     * @param offs Offset in our vector
     * @param len The number of elements (0 to use all available from offset)
     * @return True on success, false on failure (not enough data in our vector)
     */
    inline bool slice(BitVector& dest, unsigned int offs, unsigned int len = 0) const
	{ return dest.initSlice(true,*this,offs,len); }
};


/**
 * This class global Math utility methods
 * @short Math utilities
 */
class YATE_API Math
{
public:
    /**
     * Dump a Complex number to a String
     * @param buf Destination string
     * @param val Value to dump
     * @param sep Optional separator
     * @param fmt Format to use ("%g%+gi" if not given)
     * @return Destination string address
     */
    static String& dumpComplex(String& buf, const Complex& val, const char* sep = 0,
	const char* fmt = 0);

    /**
     * Dump a float number to a String
     * @param buf Destination string
     * @param val Value to dump
     * @param sep Optional separator
     * @param fmt Format to use ("%g" if not given)
     * @return Destination string address
     */
    static String& dumpFloat(String& buf, const float& val, const char* sep = 0,
	const char* fmt = 0);
};


/**
 * Addition operator
 * @param c1 First number
 * @param c2 Second number
 * @return The result
 */
inline Complex operator+(const Complex& c1, const Complex& c2)
{
    Complex tmp(c1);
    return (tmp += c2);
}

/**
 * Addition operator
 * @param c A Complex number
 * @param f A float value
 * @return The result
 */
inline Complex operator+(const Complex& c, float f)
{
    Complex tmp(c);
    return (tmp += f);
}

/**
 * Addition operator
 * @param f The float value
 * @param c The Complex number
 * @return The result
 */
inline Complex operator+(float f, const Complex& c)
{
    return operator+(c,f);
}

/**
 * Substraction operator
 * @param c1 First number
 * @param c2 Second number
 * @return The result
 */
inline Complex operator-(const Complex& c1, const Complex& c2)
{
    Complex tmp(c1);
    return (tmp -= c2);
}

/**
 * Substraction operator
 * @param c A Complex number
 * @param f A float value
 * @return The result
 */
inline Complex operator-(const Complex& c, float f)
{
    Complex tmp(c);
    return (tmp -= f);
}

/**
 * Multiplication operator
 * @param c1 First number
 * @param c2 Second number
 * @return The result
 */
inline Complex operator*(const Complex& c1, const Complex& c2)
{
    Complex tmp(c1);
    return (tmp *= c2);
}

/**
 * Multiplication operator
 * @param c A Complex number
 * @param f A float value
 * @return The result
 */
inline Complex operator*(const Complex& c, float f)
{
    Complex tmp(c);
    return (tmp *= f);
}

/**
 * Multiplication operator
 * @param f A float value
 * @param c A Complex number
 * @return The result
 */
inline Complex operator*(float f, const Complex& c)
{
    return operator*(c,f);
}

/**
 * Division operator
 * @param c1 First number
 * @param c2 Second number
 * @return The result
 */
inline Complex operator/(const Complex& c1, const Complex& c2)
{
    Complex tmp(c1);
    return (tmp /= c2);
}

/**
 * Division operator
 * @param c A Complex number
 * @param f A float value
 * @return The result
 */
inline Complex operator/(const Complex& c, float f)
{
    Complex tmp(c);
    return (tmp /= f);
}

/**
 * Append operator: append a Complex number to a String
 * @param str Destination string
 * @param c Complex number to append
 * @return Destination string reference
 */
inline String& operator<<(String& str, const Complex& c)
{
    return Math::dumpComplex(str,c);
}

/**
 * Append operator: append a BitVector to a String
 * @param str Destination string
 * @param b Vector to append
 * @return Destination string reference
 */
inline String& operator<<(String& str, const BitVector& b)
{
    return b.appendTo(str);
}

}; // namespace TelEngine

#endif /* __YATEMATH_H */

/* vi: set ts=8 sw=4 sts=4 noet: */
