/* *************************************************************

Copyright 2010 Terry Lyons, Stephen Buckley, Djalil Chafai, 
Greg Gyurk� and Arend Janssen. 

Distributed under the terms of the GNU General Public License, 
Version 3. (See accompanying file License.txt)

************************************************************* */




#ifndef DJC_COROPA_LIBALGEBRA_POLYLIEBASISH_SEEN
#define DJC_COROPA_LIBALGEBRA_POLYLIEBASISH_SEEN
#include "basis_traits.h"

/// A basis for the polynomial Lie algebra, poly_lie.

/** A basis for the polynomial lie algebra. 
The basis elements are vector fields of the of the form std::pair(direction, monomial).
The product is the Lie bracket of two vector fields.
*/


template<typename SCA, typename RAT, DEG n_letters, DEG max_degree>
class poly_lie_basis : public basis_traits<With_Degree, n_letters, max_degree>
{
public:
    /// The basis elements of poly_basis.
    typedef poly_basis <SCA, RAT> POLYBASIS;
    /// The key of poly_basis (ie a monomial).
    typedef typename POLYBASIS::KEY POLYBASIS_KEY;
    /// A key is a pair of letter and monomial (ie a monomial in direction letter).
    typedef std::pair<LET, POLYBASIS_KEY> KEY;
    /// Polynomial algebra.
    typedef poly <SCA, RAT> POLY;
    /// The rationals.
    typedef RAT RATIONAL;
    typedef SCA SCALAR;

    /// The order in the MAP class reflects the degree
    struct KEY_LESS
    {
        bool inline operator()(const KEY &lhs, const KEY &rhs) const
        {
            return ((degree(lhs) < degree(rhs)) || ((degree(lhs) == degree(rhs)) && lhs < rhs));
        }
    };

    /// The MAP type.
    typedef std::map<KEY, SCA, KEY_LESS> MAP;
    /// The Multivariate Polynomials Algebra element type.
    typedef poly_lie <SCA, RAT, n_letters, max_degree> POLY_LIE;

public:
    // Property tags
    typedef alg::basis::with_degree<max_degree> degree_tag;
    typedef alg::basis::unordered ordering_tag;


public:
    /// Default constructor. Empty basis.
    poly_lie_basis(void)
    {}

public:

    ///Returns the Lie bracket of two monomial vector fields.

    /**
    Returns the Vector field corresponding to the Lie bracket of two vector fields.
    If we have have monomials m1 and m2 and Letters i and j then the product
    of m1*d/dxi with m2*d/dxj is equal to
    m1*(d/dxi m2)*d/dxj - m2*(d/dxj m1)*d/dxi
    */

    inline static POLY_LIE prod(const KEY &k1, const KEY &k2)
    {
        POLY poly1 = POLY::prediff(k2.second, k1.first);
        POLY poly2 = POLY::prediff(k1.second, k2.first);
        KEY mon1(k2.first, k1.second);
        KEY mon2(k1.first, k2.second);
        POLY_LIE result;
        result = prod2(poly1, mon1) - prod2(poly2, mon2);
        return result;
    }

    /// Multiplication of a polynomial poly1 by a monomial vector field liemon1.
    inline static POLY_LIE prod2(const POLY &poly1, const KEY &liemon1)
    {
        POLY_LIE result;
        for (typename POLY::const_iterator it = poly1.begin(); it != poly1.end(); it++) {
            SCA temp = it->value();
            POLYBASIS_KEY temp2 = POLYBASIS::prod2(liemon1.second, it->key());
            result[make_pair(liemon1.first, temp2)] = temp;
        }
        return result;
    }


    /// Turns a d/dx_i into a polynomial vector field by multiplying the empty monomial by d/dx_i.
    inline static KEY keyofletter(LET letter)
    {
        POLYBASIS empty;
        KEY result(letter, empty.empty_key);
        return result;
    }

    /// Returns the degree of the monomial in the pair (Let, monomial)
    inline static DEG degree(const KEY &k)
    {
        return POLYBASIS::degree(k.second);
    }

    /// Outputs a std::pair<poly_basis*, KEY> to an std::ostream.
    inline friend
    std::ostream &operator<<(std::ostream &os, const std::pair<poly_lie_basis *, KEY> &t)
    {
        POLYBASIS poly1;
        std::pair<POLYBASIS *, POLYBASIS_KEY> polypair;
        polypair.first = &poly1;
        polypair.second = t.second.second;
        os << "{" << polypair << "}" << "d/dx" << t.second.first << "}";
        return os;
    }


};


namespace vectors {

template<DEG n_letters, DEG max_degree, typename Field>
struct vector_type_selector<poly_lie_basis<typename Field::S, typename Field::Q, n_letters, max_degree>, Field>
{
    typedef poly_lie_basis<typename Field::S, typename Field::Q, n_letters, max_degree> BASIS;
    typedef sparse_vector <BASIS, Field,
    std::map<typename BASIS::KEY, typename Field::S, typename BASIS::KEY_LESS> > type;
};


}


#endif
