Librairie de Math


19 août 2007

Librairie de math

La librairie que je suis en train de concevoir pour exécuter, entre autres choses, des calculs sur des polynômes, des vecteurs et des matrices.

#include <stdarg.h>
#include <math.h>

using namespace std;

class   QiXMath : public std::exception
{
    public:
        QiXMath(char* msg)      { _whatMsg = msg; }
        virtual const char* what() const throw()    {   return _whatMsg;    }
    protected:
        char*   _whatMsg;
};


//Pour représenter des fractions, ce qui est très utile pour les polynôme quand on veut des calculs exacts.
//Note : pour simplifier des nombres décimaux qui ont des séquences répétitives voir http://en.wikipedia.org/wiki/QiFraction_(mathematics)
//TOCHECK : l'opérateur /(int) ne doit pas être nécéssaire puisque j'ai un constructeur qui a un int en entrée, pourquoi dois-je en avoir un ???
template <class T>
class   QiFraction
{
    public:
        //Faire un constructeur avec un seul paramètre : le numérateur.
        QiFraction(T n, T d)        {   _n = n; _d = d; }
        QiFraction(T n=0)       {   _n = n; _d = 1; }
        QiFraction(float n) {   _n = (T) n; _d = 1; }
        QiFraction(int n)   {   _n = (T) n; _d = 1; }
        void    dump()      const
        {
            if (_n == 0 || _d == 1)     cout << _n;
            else    {
                if (_n == _d)       cout << "1";
                else    cout << _n << "/" << _d;
            }
        }
        QiFraction<T>&  operator+=(const QiFraction<T>& a)
        {
            T   P = pgcd(_d, a._d);
            T   NewD = (_d * a._d) / P;
            _n = _n * (NewD / _d) + a._n * (NewD / a._d);
            _d = NewD;
            return *this;
        }
        QiFraction<T>&  operator-=(const QiFraction<T>& a)
        {
            T   P = pgcd(_d, a._d);
            T   NewD = (_d * a._d) / P;
            _n = _n * (NewD / _d) - a._n * (NewD / a._d);
            _d = NewD;
            return *this;
        }
        QiFraction<T>&  operator*=(const QiFraction<T>& a)
        {
            _n *= a._n;
            _d *= a._d;
            //On simplifie la fraction
            T   Common = pgcd(_n, _d);
            _n /= Common;
            _d /= Common;
            return *this;
        }
        QiFraction<T>&  operator/=(const QiFraction<T>& a)
        {
            _n *= a._d;
            _d *= a._n;
            //On simplifie la fraction
            T   Common = pgcd(_n, _d);
            _n /= Common;
            _d /= Common;
            return *this;
        }
        QiFraction<T>   operator+(const QiFraction<T>& a)       const
        {
            QiFraction  Result(_n, _d);
            return Result += a;
        }
        QiFraction<T>   operator-(const QiFraction<T>& a)       const
        {
            QiFraction  Result(_n, _d);
            return Result -= a;
        }
        QiFraction<T>   operator*(const QiFraction<T>& a)       const
        {
            QiFraction  Result(_n, _d);
            return Result *= a;
        }
        QiFraction<T>   operator/(const QiFraction<T>& a)       const
        {
            QiFraction  Result(_n, _d);
            return Result /= a;
        }
        QiFraction<T>   operator/(T a)      const
        {
            QiFraction<T>   Result(_n, _d);
            return Result /= QiFraction<T>(a);
        }
        QiFraction<T>   operator/(int a)        const
        {
            QiFraction<T>   Result(_n, _d);
            return Result /= QiFraction<T>(a);
        }
        bool    operator>=(float t) const       {   return (float)_n/(float)_d > t;     }
        bool    operator==(float t) const       {   return (float)_n/(float)_d == t;    }
        operator float()    const   {   return (float)_n/(float)_d; }
        QiFraction<T>&  inverse()
        {
            T   Tmp = _n;
            _n = _d;
            _d = _n;
            return *this;
        }
        T   pgcd(T u, T v/*, int verbose=false*/)
        {
            T   M = 1;
            if (u<0)        u = (T)0-u;
            if (v<0)        v = (T)0-v;
            while (u && v)      {
//      if (verbose)        printf("pgcd(%d, %d)\n", u, v);
                if (!(u&1) && !(v&1))       { M <<= 1; u>>=1, v>>=1; }
                else    if (!(u&1) && v&1)      u>>=1;
                else    if (u&1 && !(v&1))      v>>=1;
                else    {       //Les deux nombres sont impairs
                    if (u>v)    u=(u-v)>>1;
                    else        v=(v-u)>>1;
                }
            }
            return M * (u ? u : v);
        }
//Note : ppcm(a,b) = (a*b)/pgcd(a,b)

    T   _n, _d;     //Numérateur et dénominateur
};


//###TODO (25%) : Exceptions
//###TODO : cacher l'évaluation du degré d'un polynôme
//###TODO : faire la factorisation simple d'un polynôme
//###TODO : faire des opérateurs pour QiMatrix et QiPolynomial
//###TODO : faire des opérateurs pour QiVector
template <class T>
class   QiPolynomial
{
    public:
        QiPolynomial(int rank)
        {
            _comps = NULL;
            _init(rank);
        }
        ~QiPolynomial()
        {
            DELETEARRAY(_comps);
        }
        QiPolynomial(const QiPolynomial<T>& original)
        {
            if (this != &original)      {
                _init(original.rank());
                int i;
                for(i=0; i<=rank(); i++)        _comps[i] = original.get(i);
            }
        }
    protected:
        void    _init(int rank)
        {
            if (rank<0)     return;
            _rank = rank;
            _comps = new T[_rank+1];
        }
        void    _expand(int nbWantedSlots)
        {
            int NewSize = nbWantedSlots;
            _comps = new T[NewSize];
            if (!_comps)        throw new QiXMath("QiPolynomial::_expand : NotImplemented");
//          memcpy();
        }
    public:
        //
        void    null()
        {
            int i;
            for(i=0; i<=rank(); i++)        _comps[i] = 0.0f;
        }
        void    primitive()
        {
            int Degree = degree();
            if (rank() < Degree+1)      _expand(Degree+1);
            int i, MaxIndex = degree();
            for(i=MaxIndex; i>=0; i--)      _comps[i+1] = _comps[i]/(i+1);
            _comps[0] = 0.0f;
        }
        void    add(const QiPolynomial<T>& op)
        {
            int i;
            int MaxIndex = op.rank();
            if (rank() < MaxIndex)      MaxIndex = rank();
            for(i=0; i<=MaxIndex; i++)          set(i, get(i) + op.get(i));
        }
        QiPolynomial<T>&    operator *= (const QiPolynomial<T>& a)
        {
            multiply(a);
            return *this;
        }
        bool    multiply(const QiPolynomial<T>& op)
        {
            if (degree() + op.degree() > rank())        return false;
            QiPolynomial    Temp(degree() + op.degree());
            int MaxIt = op.degree(), i, j;
            int CurrentDegree = degree();
            Temp.null();
            for(i=0; i<=MaxIt; i++)     {
                for(j=0; j<=CurrentDegree; j++)     {
                    T   CurrentValue = Temp.get(i+j);
                    T   NewInc = op.get(i) * get(j);
                    Temp.set(i+j, CurrentValue + NewInc);
                }
            }
            set(Temp);
            return true;
        }
        void    multiply(T coeff)
        {
            int i;
            int MaxIndex = degree();
            for(i=0; i<=MaxIndex; i++)      set(i, get(i)*coeff);
        }
        QiPolynomial<T>&    operator /= (const T& a)
        {
            int i;
            int MaxIndex = degree();
            for(i=0; i<=MaxIndex; i++)      {
                set(i, get(i) / a);
            }
            return *this;
        }
        void    set(const QiPolynomial& src)
        {
            int i;
            null();
            int MaxIndex = rank();
            if (MaxIndex>src.rank())        MaxIndex = src.rank();
            for(i=0; i<=MaxIndex; i++)      set(i, src.get(i));
//          _rank = src.rank();
        }
        void    setRaw(int maxDegree, ...)
        {
            if (maxDegree > rank())     return;
            va_list args;
            va_start(args, maxDegree);
            int i, j;
            for(i=maxDegree; i>=0; i--)     {
                float   Value = (float) va_arg(args, double);
                _comps[i] = Value;
            }
            va_end(args);
        }
        //On remplace la variable x du polynôme par un autre polynôme (x par x+1 par exemple).
        //On substitue donc Q(x) à x dans P(x), ce qui donne P(Q(x)), un autre polynôme en x.
        //La méthode :
        void    substitute(const QiPolynomial& newVar)
        {
            QiPolynomial    Value(newVar.degree() + degree()), Total(newVar.degree() + degree());
            QiPolynomial    Param(newVar.degree() + degree());
            int i;
            Param.null();
            Param.set(0, 1.0f);
            for(i=0; i<=degree(); i++)      {
                Value.set(Param);
                Value.multiply(get(i));
                Total.add(Value);
                Param.multiply(newVar);
            }
            set(Total);
        }
        T   get(int index)  const
        {
            if (index<0 || index>rank()+1)      throw new QiXMath("QiPolynomial::get out of bound");
            return _comps[index];
        }
        void    set(int index, T value)
        {
            if (index<0 || index>rank()+1)      throw new QiXMath("QiPolynomial::set out of bound");
            _comps[index] = value;
        }
        float   evaluate(T x)   const
        {
            int MaxIndex = degree();
            float   Current = 1.0f, Total = 0.0f;
            int i;
            for(i=0; i<=MaxIndex; i++)      {
                Total += Current * _comps[i];
                Current *= x;
            }
            return Total;
        }
        int     degree()    const       //Renvoie le degré du polynôme courant : l'indice du premier coefficient non-nul en partant des puissances élevées.
        {
            int i = rank();
            while (get(i) == 0.0f && i>0)       i--;
            return i;
        }
        void    dump()  const
        {
            int i, Degree = degree();
            for(i=Degree; i>=0; i--)        {
                if (i<Degree && get(i)>=0.0f)       printf("+");
                printf("%.10f", (float) get(i));
                if (!i)     continue;
                if (i==1)   printf("X");
                if (i>1)    printf("X^%d", i);
            }
            printf("\n");
        }
        void    dumpObjects()   const
        {
            int i, Degree = degree();
            for(i=Degree; i>=0; i--)        {
                T   CurValue = get(i);
                if (i<Degree && CurValue>=0.0f)     printf("+");
                if (CurValue != 0.0f)       {
                    get(i).dump();
                    if (!i)     continue;
                    if (i==1)   printf("X");
                    if (i>1)    printf("X^%d", i);
                }
            }
            printf("\n");
        }
        int     rank()  const   {   return _rank;   }
    protected:
        T*      _comps;
        int     _rank;
};

template <class T>
class   QiVector
{
    public:
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        class   ExQiVector : public std::exception
        {
            public:
                ExQiVector(char*)       {   }
        };
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        QiVector(int rank)      {   _comps = new T[rank];   _rank = rank;}
        ~QiVector()     {   DELETEARRAY(_comps) }
        void    null()  {
            int i;
            for(i=0; i<rank(); i++)     _comps[i] = 0.0f;
        }
        //On crée un vecteur de base
        void    base(int index)
        {
            if (index<0 || index>=rank())       return;
            null();
            set(index, 1.0f);
        }
        //###TODO : faire des exceptions pour les out of bounds.
        bool    set(int index, const T& value)  {
            if (index<0 || index >= rank())     return false;
            _comps[index] = value;
            return true;
        }
        bool    set(QiVector<T>& newValue)
        {
            if (rank() != newValue.rank())      return false;
            int i;
            for(i=0; i<rank(); i++)     _comps[i] = newValue.get(i);
            return true;
        }
        bool    add(int index, const T& value)
        {
            if (index<0 || index>=rank())       return false;
            return set(index, get(index) + value);
        }
        T   get(int index)      const
        {
            return _comps[index];
        }
        QiVector<T>& operator=(const QiVector<T>& original)
        {
            //###TODO : si les rangs sont différents on doit changer le rang de l'objet courant
            if (this != &original && rank() == original.rank())     {
                int i;
                for(i=0; i<rank(); i++)     _comps[i] = original.get(i);
            }
            return *this;
        }
        int     rank()  const   {   return _rank;   }
        void    dump()  const
        {
            int i;
            printf("(");
            for(i=0; i<rank(); i++)     {
                if(i)   printf(", ");
                printf("%f", _comps[i]);
            }
            printf(")\n");
        }
        void    dumpObjects()   const
        {
            int i;
            printf("(");
            for(i=0; i<rank(); i++)     {
                if(i)   printf(", ");
                _comps[i].dump();
            }
            printf(")\n");
        }
    protected:
        T*  _comps;
        int _rank;
};

//Matrice carrée constituée de vecteurs QiVector
template <class T>
class   QiMatrix
{
    public:
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        class   ExQiMatrix : public std::exception
        {
            public:
                ExQiMatrix(char*)       {   }
        };
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        QiMatrix(int rank)      {   _init(rank);    }
    protected:
        bool _init(int rank)
        {
            _comps = new QiVector<T>*[rank];
            int i;
            for(i=0; i<rank; i++)       _comps[i] = new QiVector<T>(rank);
            _rank = rank;
            return true;
        }
        bool _release()
        {
            int i;
            for(i=0; i<rank(); i++)         {
                DELETESINGLE(_comps[i]);
            }
            DELETEARRAY(_comps);
        }
        void    _count()    const
        {
            _nbUps = 0, _nbLows = 0, _nbDiags = 0;
            int i, j;
            for(i=0; i<rank(); i++)     {
                for(j=0; j<rank(); j++)     {
                    float   Value = get(i, j);
                    if (i>j && Value)       _nbLows++;
                    if (i<j && Value)       _nbUps++;
                    if (i==j && Value)      _nbDiags++;
                }
            }
            //
            _nbItemsInHalf = _rank*(_rank-1)/2 + _rank;
        }
    public:
        ~QiMatrix()     {   _release(); }

        void    null()      {   int i;  for(i=0; i<rank(); i++)     _comps[i]->null();  }
        void identity()
        {
            null();
            int i;
            for(i=0; i<rank(); i++)     _comps[i]->set(i, 1.0f);
        }
        T   get(int i, int j)       const
        {
            if (i<0 || j<0)     return 0.0f;
            if (i>=rank() || j>=rank())     return 0.0f;
            return _comps[i]->get(j);
        }
        QiVector<T>& get(int index) const
        {
            if (index<0 || index>=rank())       return *_comps[0];
            return *_comps[index];
        }
        bool    set(int i, int j, T value)
        {
            if (j<0 || j>=rank())   return false;
            _comps[i]->set(j, value);
            return true;
        }
        bool    setRaw(int setRank, ...)        {
            if (setRank != rank())  return false;
            va_list args;
            va_start(args, setRank);
            int i, j;
            for(i=0; i<rank(); i++)     {
                for(j=0; j<rank(); j++)     {
                    if (!_comps[i]->set(j, (float) va_arg(args, double)))       {
                        va_end(args);
                        return false;
                    }
                }
            }
            va_end(args);
            return true;
        }
        bool    setRow(int index, QiVector<T>& row)
        {
            if (rank() != row.rank())       return false;
            _comps[index]->set(row);
            return true;
        }
        //Multiplication matrice-matrice inplace
        QiMatrix<T>&    operator *= (const QiMatrix<T>& op)
        {
            multiply(op);
            return *this;
        }
        bool    multiply(const QiMatrix<T>& op)
        {
            if (rank() != op.rank())        return false;
            QiVector<T> Temp(rank());
            int i, j, k;
            for(i=0; i<rank(); i++)     {
                for(Temp.null(), j=0; j<rank(); j++)        {
                    for(k=0; k<rank(); k++)     {
                        Temp.add(j, get(i, k) * op.get(k, j));
                    }
                }
                setRow(i, Temp);
            }
            return true;
        }
        //
        QiVector<T> operator * (const QiVector<T>& op)      const
        {
            QiVector<T>     Result(op.rank());
            multiply(op, Result);
            return Result;
        }
        void    multiply(const QiVector<T>& op, QiVector<T>& result)    const
        {
            int i, j;
            for(i=0; i<rank(); i++)     {
                T   Total = 0.0f;
                for(j=0; j<rank(); j++)     {
                    Total += get(i, j) * op.get(j);
                }
                result.set(i, Total);
            }
        }
        //Copy constructor
        QiMatrix(const QiMatrix<T>& original)
        {
            _init(original.rank());
            int i;
            for(i=0; i<rank(); i++)     *_comps[i] = original.get(i);
        }
        bool    isLower()   const
        {
            _count();
            return _nbLows + _nbDiags == _nbItemsInHalf;
        }
        bool    isDiagonal()    const
        {
            _count();
            return _nbDiags == rank();
        }
        //Inverse la matrice courante "inplace"
        void    inverse()
        {
            if (isLower())      {
                //Cas pour les matrices triangulaires inférieures
                QiVector<T> Temp(rank());
                int i, j, k;
                T   Total;
                for(i=0; i<rank(); i++)     {
                    Temp.null();
                    for(j=0; j<=i; j++)     {
                        Total = 0.0f;
                        for(k=j; k<i; k++)      {
                            T   NormalIK = get(i, k);
                            T   InvKJ = get(k, j);
                            Total += NormalIK * InvKJ;
                        }
                        T   DeltaIJ = i == j ? 1.0f : 0.0f;
                        T   MainCoeff = get(i, i);
                        T   InverseCoeff = (DeltaIJ - Total) / MainCoeff;
                        Temp.set(j, InverseCoeff);
                    }
                    setRow(i, Temp);
                }
            }   else    if(isDiagonal())        {
                int i;
                for(i=0; i<rank(); i++)     set(i, i, 1.0f/get(i, i));
            }   else    throw new QiXMath("QiMatrix::inverse : not yet implemented.");
        }

        void    dump()  const
        {
            int i;
            for(i=0; i<rank(); i++)     _comps[i]->dump();
        }
        void    dumpObjects()   const
        {
            int i;
            for(i=0; i<rank(); i++)     _comps[i]->dumpObjects();
        }
        int     rank()  const   {   return _rank;   }
        //
        bool    isUpper()   const;
        bool    isInversible()  const;
    protected:
        QiVector<T>**   _comps;
        int _rank;
        //
        mutable     int _nbUps, _nbLows, _nbDiags, _nbItemsInHalf;
};

Bien se souvenir qu'ici je cherche plus à être exact que rapide, c'est sûr que pour un moteur 3D j'utiliserai les classes QiMatrix et QiVector avec beaucoup de pincettes !!!

Accueil