//
//  Copyright (c) 2010 Athanasios Iliopoulos
//
//  Distributed under the Boost Software License, Version 1.0. (See
//  accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef ASSIGNMENT_HPP
#define ASSIGNMENT_HPP
#include <boost/numeric/ublas/vector_expression.hpp>
#include <boost/numeric/ublas/matrix_expression.hpp>

/*! \file assignment.hpp
    \brief uBlas assignment operator <<=.
*/

namespace boost { namespace numeric { namespace ublas {

/** \brief A CRTP and Barton-Nackman trick index manipulator wrapper class.
 *
 * This class is not meant to be used directly.
 */
template <class TV>
class index_manipulator {
public:
    typedef TV type;
    BOOST_UBLAS_INLINE
    const type &operator () () const {
        return *static_cast<const type *> (this);
    }
    BOOST_UBLAS_INLINE
    type &operator () () {
        return *static_cast<type *> (this);
    }
};

/** \brief A move_to vector index manipulator.
 *
 * When member function \c manip is called the referenced
 * index will be set to the manipulators' index.
 *
 * \sa move_to(T i)
 */
template <typename T>
class vector_move_to_manip: public index_manipulator<vector_move_to_manip<T> > {
public:
    BOOST_UBLAS_INLINE
    vector_move_to_manip(const T &k): i(k) { }

    template <typename V>
    BOOST_UBLAS_INLINE
    void manip(V &k) const { k=i; }
private:
    T i;
};

/** \brief An object generator that returns a move_to vector index manipulator
 *
 * \param i The element number the manipulator will move to when \c manip member function is called
 * \return A move_to vector manipulator
 *
 * Example usage:
 * \code
 * vector<double> a(6, 0);
 * a <<= 1, 2, move_to(5), 3;
 * \endcode
 * will result in:
 * \code
 * 1 2 0 0 0 3
 * \endcode
 *
 * \tparam T Size type
 * \sa move_to()
 */
template <typename T>
BOOST_UBLAS_INLINE vector_move_to_manip<T>  move_to(T i) {
    return vector_move_to_manip<T>(i);
}

/** \brief A static move to vector manipulator.
 *
 * When member function \c manip is called the referenced
 * index will be set to the manipulators' index
 *
 * \sa move_to(T i) and move_to()
*/
template <std::size_t I>
class static_vector_move_to_manip: public index_manipulator<static_vector_move_to_manip<I> > {
public:
    template <typename V>
    BOOST_UBLAS_INLINE
    void manip(V &k) const { k=I; }
};

/** \brief An object generator that returns a static move_to vector index  manipulator.
 *
 * Typically faster than the dynamic version, but can be used only when the
 * values are known at compile time.
 *
 * \return A static move_to vector manipulator
 *
 * Example usage:
 * \code
 * vector<double> a(6, 0);
 * a <<= 1, 2, move_to<5>(), 3;
 * \endcode
 * will result in:
 * \code
 * 1 2 0 0 0 3
 * \endcode
 *
 * \tparam I The number of elements the manipulator will traverse the index when \c manip function is called
 */
template <std::size_t I>
BOOST_UBLAS_INLINE static_vector_move_to_manip<I>  move_to() {
    return static_vector_move_to_manip<I>();
}

/** \brief A move vector index manipulator.
 *
 * When member function traverse is called the manipulators'
 * index will be added to the referenced index.
 *
 * \see move(T i)
 */
template <typename T>
class vector_move_manip: public index_manipulator<vector_move_manip<T> > {
public:
    BOOST_UBLAS_INLINE
    vector_move_manip(const T &k): i(k) { }

    template <typename V>
    BOOST_UBLAS_INLINE void manip(V &k) const { k+=i; }
private:
    T i;
};

/**
* \brief  An object generator that returns a move vector index manipulator
*
* \tparam T Size type
* \param i The number of elements the manipulator will traverse the index when \c manip
* member function is called. Negative values can be used.
* \return A move vector manipulator
*
* Example usage:
* \code
* vector<double> a(6, 0);
* a <<= 1, 2, move(3), 3;
* \endcode
* will result in:
* \code
* 1 2 0 0 0 3
* \endcode
*
*/
template <typename T>
BOOST_UBLAS_INLINE vector_move_manip<T>  move(T i) {
    return vector_move_manip<T>(i);
}

/**
* \brief A static move vector manipulator
*
* When member function \c manip is called the manipulators
* index will be added to the referenced index
*
* \sa move()
*
* \todo Doxygen has some problems with similar template functions. Correct that.
*/
template <std::size_t I>
class static_vector_move_manip: public index_manipulator<static_vector_move_manip<I> > {
public:
    template <typename V>
    BOOST_UBLAS_INLINE void manip(V &k) const { k+=I; }
};

/**
* \brief An object generator that returns a static move vector index manipulator.
*
* Typically faster than the dynamic version, but can be used only when the
* values are known at compile time.
* \tparam I The Number of elements the manipulator will traverse the index when \c manip
* function is called.Negative values can be used.
* \return A static move vector manipulator
*
* Example usage:
* \code
* vector<double> a(6, 0);
* a <<= 1, 2, move<3>(), 3;
* \endcode
* will result in:
* \code
* 1 2 0 0 0 3
* \endcode
*
* \todo Doxygen has some problems with similar template functions. Correct that.
*/
template <std::size_t I>
BOOST_UBLAS_INLINE static_vector_move_manip<I>  move() {
    return static_vector_move_manip<I>();
}

/**
* \brief A move_to matrix manipulator
*
* When member function \c manip is called the referenced
* index will be set to the manipulators' index
*
* \sa move_to(T i, T j)
*
* \todo Doxygen has some problems with similar template functions. Correct that.
*/
template <typename T>
class matrix_move_to_manip: public index_manipulator<matrix_move_to_manip<T> > {
public:
    BOOST_UBLAS_INLINE
    matrix_move_to_manip(T k, T l): i(k), j(l) { }

    template <typename V1, typename V2>
    BOOST_UBLAS_INLINE
    void manip(V1 &k, V2 &l) const {
        k=i;
        l=j;
    }
private:
    T i, j;
};

/**
* \brief  An object generator that returns a "move_to" matrix index manipulator
*
* \tparam size type
* \param i The row number the manipulator will move to when \c manip
* member function is called
* \param j The column number the manipulator will move to when \c manip
* member function is called
* \return A move matrix manipulator
*
* Example usage:
* \code:
* matrix<double> A(3, 3, 0);
* A <<= 1, 2, move_to(A.size1()-1, A.size1()-1), 3;
* \endcode
* will result in:
* \code
* 1 2 0
* 0 0 0
* 0 0 3
* \endcode
* \sa move_to(T i, T j) and static_matrix_move_to_manip
*
* \todo Doxygen has some problems with similar template functions. Correct that.
*/
template <typename T>
BOOST_UBLAS_INLINE matrix_move_to_manip<T>  move_to(T i, T j) {
    return matrix_move_to_manip<T>(i, j);
}

/**
* \brief A static move_to matrix manipulator
* When member function traverse is called the referenced
* index will be set to the manipulators' index
*
* \sa move_to()
*
* \todo Doxygen has some problems with similar template functions. Correct that.
*/
template <std::size_t I, std::size_t J>
class static_matrix_move_to_manip: public index_manipulator<static_matrix_move_to_manip<I, J> > {
public:
    template <typename V, typename K>
    BOOST_UBLAS_INLINE
    void manip(V &k, K &l) const {
        k=I;
        l=J;
    }
};

/**
* \brief  An object generator that returns a static move_to matrix index manipulator.
*
* Typically faster than the dynamic version, but can be used only when the
* values are known at compile time.
* \tparam I The row number the manipulator will set the matrix assigner index to.
* \tparam J The column number the manipulator will set the matrix assigner index to.
* \return A static move_to matrix manipulator
*
* Example usage:
* \code:
* matrix<double> A(3, 3, 0);
* A <<= 1, 2, move_to<2,2>, 3;
* \endcode
* will result in:
* \code
* 1 2 0
* 0 0 0
* 0 0 3
* \endcode
* \sa move_to(T i, T j) and static_matrix_move_to_manip
*/
template <std::size_t I, std::size_t J>
BOOST_UBLAS_INLINE static_matrix_move_to_manip<I, J>  move_to() {
    return static_matrix_move_to_manip<I, J>();
}

/**
* \brief A move matrix index manipulator.
*
* When member function \c manip is called the manipulator's
* index will be added to the referenced' index.
*
* \sa move(T i, T j)
*/
template <typename T>
class matrix_move_manip: public index_manipulator<matrix_move_manip<T> > {
public:
    BOOST_UBLAS_INLINE
    matrix_move_manip(T k, T l): i(k), j(l) { }

    template <typename V, typename K>
    BOOST_UBLAS_INLINE
    void manip(V &k, K &l) const {
        k+=i;
        l+=j;
    }
private:
    T i, j;
};

/**
* \brief  An object generator that returns a move matrix index manipulator
*
* \tparam size type
* \param i The number of rows the manipulator will traverse the index when "manip"
* member function is called
* \param j The number of columns the manipulator will traverse the index when "manip"
* member function is called
* \return A move matrix manipulator
*
* Example:
* \code:
* matrix<double> A(3, 3, 0);
* A <<= 1, 2, move(1,0),
*            3,;
* \endcode
* will result in:
* \code
* 1 2 0
* 0 0 3
* 0 0 0
* \endcode
*/
template <typename T>
BOOST_UBLAS_INLINE matrix_move_manip<T>  move(T i, T j) {
    return matrix_move_manip<T>(i, j);
}

/**
* \brief A static move matrix index manipulator.
*
* When member function traverse is called the manipulator's
* index will be added to the referenced' index.
*
* \sa move()
*
* \todo Doxygen has some problems with similar template functions. Correct that.
*/
template <std::size_t I, std::size_t J>
class static_matrix_move_manip: public index_manipulator<static_matrix_move_manip<I, J> > {
public:
    template <typename V, typename K>
    BOOST_UBLAS_INLINE
    void manip(V &k, K &l) const {
        k+=I;
        l+=J;
    }
};

/**
* \brief  An object generator that returns a static "move" matrix index manipulator.
*
* Typically faster than the dynamic version, but can be used only when the
* values are known at compile time. Negative values can be used.
* \tparam I The number of rows the manipulator will trasverse the matrix assigner index.
* \tparam J The number of columns the manipulator will trasverse the matrix assigner index.
* \tparam size type
* \return A static move matrix manipulator
*
* Example:
* \code:
* matrix<double> A(3, 3, 0);
* A <<= 1, 2, move<1,0>(),
*            3,;
* \endcode
* will result in:
* \code
* 1 2 0
* 0 0 3
* 0 0 0
* \endcode
*
* \sa move_to()
*
* \todo Doxygen has some problems with similar template functions. Correct that.
*/
template <std::size_t I, std::size_t J>
BOOST_UBLAS_INLINE static_matrix_move_manip<I, J>  move() {
    return static_matrix_move_manip<I, J>();
}

/**
* \brief A begining of row manipulator
*
* When member function \c manip is called the referenced
* index will be be set to the begining of the row (i.e. column = 0)
*
* \sa begin1()
*/
class begin1_manip: public index_manipulator<begin1_manip > {
public:
    template <typename V, typename K>
    BOOST_UBLAS_INLINE
    void manip(V & k, K &/*l*/) const {
        k=0;
    }
};

/**
* \brief  An object generator that returns a begin1 manipulator.
*
* The resulted manipulator will traverse the index to the begining
* of the current column when its' \c manip member function is called.
*
* \return A begin1 matrix index manipulator
*
* Example usage:
* \code:
* matrix<double> A(3, 3, 0);
* A <<= 1, 2, next_row(),
*      3, 4, begin1(), 1;
* \endcode
* will result in:
* \code
* 1 2 1
* 3 4 0
* 0 0 0
* \endcode
* \sa begin2()
*/
inline begin1_manip  begin1() {
    return begin1_manip();
}

/**
* \brief A begining of column manipulator
*
* When member function \c manip is called the referenced
* index will be be set to the begining of the column (i.e. row = 0).
*
*
* \sa begin2()
*/
class begin2_manip: public index_manipulator<begin2_manip > {
public:
    template <typename V, typename K>
    BOOST_UBLAS_INLINE
    void manip(V &/*k*/, K &l) const {
        l=0;
    }
};

/**
* \brief  An object generator that returns a begin2 manipulator to be used to traverse a matrix.
*
* The resulted manipulator will traverse the index to the begining
* of the current row when its' \c manip member function is called.
*
* \return A begin2 matrix manipulator
*
* Example:
* \code:
* matrix<double> A(3, 3, 0);
* A <<= 1, 2, move<1,0>(),
*      3, begin2(), 1;
* \endcode
* will result in:
* \code
* 1 2 0
* 1 0 3
* 0 0 0
* \endcode
* \sa begin1() begin2_manip
*/
inline begin2_manip  begin2() {
    return begin2_manip();
}

/**
* \brief A next row matrix manipulator.
*
* When member function traverse is called the referenced
* index will be traveresed to the begining of next row.
*
* \sa next_row()
*/
class next_row_manip: public index_manipulator<next_row_manip> {
public:
    template <typename V, typename K>
    BOOST_UBLAS_INLINE
    void manip(V &k, K &l) const {
        k++;
        l=0;
    }
};

/**
* \brief  An object generator that returns a next_row manipulator.
*
* The resulted manipulator will traverse the index to the begining
* of the next row when it's manip member function is called.
*
* \return A next_row matrix manipulator.
*
* Example:
* \code:
* matrix<double> A(3, 3, 0);
* A <<= 1, 2, next_row(),
*      3, 4;
* \endcode
* will result in:
* \code
* 1 2 0
* 3 4 0
* 0 0 0
* \endcode
* \sa next_column()
*/
inline next_row_manip  next_row() {
    return next_row_manip();
}

/**
* \brief A next column matrix manipulator.
*
* When member function traverse is called the referenced
* index will be traveresed to the begining of next column.
*
* \sa next_column()
*/
class next_column_manip: public index_manipulator<next_column_manip> {
public:
    template <typename V, typename K>
    BOOST_UBLAS_INLINE
    void manip(V &k, K &l) const {
        k=0;
        l++;
    }
};

/**
* \brief  An object generator that returns a next_row manipulator.
*
* The resulted manipulator will traverse the index to the begining
* of the next column when it's manip member function is called.
*
* \return A next_column matrix manipulator.
*
* Example:
* \code:
* matrix<double> A(3, 3, 0);
* A <<= 1, 2, 0,
*      3, next_column(), 4;
* \endcode
* will result in:
* \code
* 1 2 4
* 3 0 0
* 0 0 0
* \endcode
*
*/
inline next_column_manip next_column() {
    return next_column_manip();
}

/**
* \brief  A wrapper for fill policy classes
*
*/
template <class T>
class fill_policy_wrapper {
public:
    typedef T type;
};

// Collection of the fill policies
namespace fill_policy {

    /**
    * \brief  An index assign policy
    *
    * This policy is used to for the simplified ublas assign through
    * normal indexing.
    *
    *
    */
    class index_assign :public fill_policy_wrapper<index_assign> {
    public:
        template <class T, typename S, typename V>
        BOOST_UBLAS_INLINE
        static void apply(T &e, const S &i, const V &v) {
            e()(i) = v;
        }
        template <class T, typename S, typename V>
        BOOST_UBLAS_INLINE
        static void apply(T &e, const S &i, const S &j, const V &v) {
            e()(i, j) = v;
        }
    };

    /**
    * \brief  An index plus assign policy
    *
    * This policy is used when the assignment is desired to be followed
    * by an addition.
    *
    *
    */
    class index_plus_assign :public fill_policy_wrapper<index_plus_assign> {
    public:
        template <class T, typename S, typename V>
        BOOST_UBLAS_INLINE
        static void apply(T &e, const S &i, const V &v) {
            e()(i) += v;
        }
        template <class T, typename S, typename V>
        BOOST_UBLAS_INLINE
        static void apply(T &e, const S &i, const S &j, const V &v) {
            e()(i, j) += v;
        }
    };

    /**
    * \brief  An index minus assign policy
    *
    * This policy is used when the assignment is desired to be followed
    * by a substraction.
    *
    *
    */
    class index_minus_assign :public fill_policy_wrapper<index_minus_assign> {
    public:
        template <class T, typename S, typename V>
        BOOST_UBLAS_INLINE
        static void apply(T &e, const S &i, const V &v) {
            e()(i) -= v;
        }
        template <class T, typename S, typename V>
        BOOST_UBLAS_INLINE
        static void apply(T &e, const S &i, const S &j, const V &v) {
            e()(i, j) -= v;
        }
    };

    /**
    * \brief  The sparse push_back fill policy.
    *
    * This policy is adequate for sparse types, when fast filling is required, where indexing
    * assign is pretty slow.

    * It is important to note that push_back assign cannot be used to add elements before elements
    * already existing in a sparse container. To achieve that please use the sparse_insert fill policy.
    */
    class sparse_push_back :public fill_policy_wrapper<sparse_push_back > {
    public:
        template <class T, class S, class V>
        BOOST_UBLAS_INLINE
        static void apply(T &e, const S &i, const V &v) {
            e().push_back(i, v);
        }
        template <class T, class S, class V>
        BOOST_UBLAS_INLINE
        static void apply(T &e, const S &i, const S &j, const V &v) {
            e().push_back(i,j, v);
        }
    };

    /**
    * \brief  The sparse insert fill policy.
    *
    * This policy is adequate for sparse types, when fast filling is required, where indexing
    * assign is pretty slow. It is slower than sparse_push_back fill policy, but it can be used to
    * insert elements anywhere inside the container.
    */
    class sparse_insert :public fill_policy_wrapper<sparse_insert> {
    public:
        template <class T, class S, class V>
        BOOST_UBLAS_INLINE
        static void apply(T &e, const S &i, const V &v) {
            e().insert_element(i, v);
        }
        template <class T, class S, class V>
        BOOST_UBLAS_INLINE
        static void apply(T &e, const S &i, const S &j, const V &v) {
            e().insert_element(i,j, v);
        }
    };

}

/** \brief A wrapper for traverse policy classes
*
*/
template <class T>
class traverse_policy_wrapper {
public:
    typedef T type;
};

// Collection of the traverse policies
namespace traverse_policy {

    /**
    * \brief  The no wrap policy.
    *
    * The no wrap policy does not allow wrapping when assigning to a matrix
    */
    struct no_wrap {
        /**
        * \brief  Element wrap method
        */
        template <class S1, class S2, class S3>
        BOOST_UBLAS_INLINE
        static void apply1(const S1 &/*s*/, S2 &/*i*/, S3 &/*j*/) {
        }

        /**
        * \brief  Matrix block wrap method
        */
        template <class S1, class S2, class S3>
        BOOST_UBLAS_INLINE
        static void apply2(const S1 &/*s1*/, const S1 &/*s2*/, S2 &/*i1*/, S3 &/*i2*/) {
        }
    };

    /**
    * \brief  The wrap policy.
    *
    * The wrap policy enables element wrapping when assigning to a matrix
    */
    struct wrap {
        /**
        * \brief  Element wrap method
        */
        template <class S1, class S2, class S3>
        BOOST_UBLAS_INLINE
        static void apply1(const S1 &s, S2 &i1, S3 &i2) {
            if (i2>=s) {
                i1++;
                i2=0;
            }
        }

        /**
        * \brief  Matrix block wrap method
        */
        template <class S1, class S2, class S3>
        BOOST_UBLAS_INLINE
        static void apply2(const S1 &s1, const S1 &s2, S2 &i1, S3 &i2) {
            if (i2>=s2) i2=0;   // Wrap to the next block
            else i1-=s1;        // Move up (or right) one block
        }
    };

    /**
    * \brief  The row_by_row traverse policy
    *
    * This policy is used when the assignment is desired to happen
    * row_major wise for performance or other reasons.
    *
    * This is the default behaviour. To change it globally please define BOOST_UBLAS_DEFAULT_ASSIGN_BY_COLUMN
    * in the compilation options or in an adequate header file.
    *
    * Please see EXAMPLES_LINK for usage information.
    *
    * \todo Add examples link
    */
    template <class Wrap = wrap>
    class by_row_policy :public traverse_policy_wrapper<by_row_policy<Wrap> > {
    public:
        template <typename S1, typename S2>
        BOOST_UBLAS_INLINE
        static void advance(S1 &/*i*/, S2 &j) { j++;}

        template <class E1, class E2, typename S1, typename S2, typename S3, typename S4, typename S5>
        BOOST_UBLAS_INLINE
        static bool next(const E1 &e, const E2 &me, S1 &i, S2 &j, const S3 &/*i0*/, const S3 &j0, S4 &k, S5 &l) {
            l++; j++;
            if (l>=e().size2()) {
                l=0; k++; j=j0; i++;
                // It is assumed that the iteration starts from 0 and happens only using this function from within
                // an assigner object.
                // Otherwise (i.e. if it is called outside the assigner object) apply2 should have been
                // outside the if statement.
                if (k>=e().size1()) {
                    j=j0+e().size2();
                    Wrap::apply2(e().size1(), me().size2(), i, j);
                    return false;
                }
            }
            return true;
        }

        template <class E, typename S1, typename S2>
        BOOST_UBLAS_INLINE
        static void apply_wrap(const E& e, S1 &i, S2 &j) {
            Wrap::apply1(e().size2(), i, j);
        }
    };

    /**
    * \brief  The column_by_column traverse policy
    *
    * This policy is used when the assignment is desired to happen
    * column_major wise, for performance or other reasons.
    *
    * This is the NOT the default behaviour. To set this as the default define BOOST_UBLAS_DEFAULT_ASSIGN_BY_COLUMN
    * in the compilation options or in an adequate header file.
    *
    * Please see EXAMPLES_LINK for usage information.
    *
    * \todo Add examples link
    */
    template <class Wrap = wrap>
    class by_column_policy :public traverse_policy_wrapper<by_column_policy<Wrap> > {
    public:
        template <typename S1, typename S2>
        BOOST_UBLAS_INLINE
        static void advance(S1 &i, S2 &/*j*/) { i++;}

        template <class E1, class E2, typename S1, typename S2, typename S3, typename S4, typename S5>
        BOOST_UBLAS_INLINE
        static bool next(const E1 &e, const E2 &me, S1 &i, S2 &j, const S3 &i0, const S3 &/*j0*/, S4 &k, S5 &l) {
            k++; i++;
            if (k>=e().size1()) {
                k=0; l++; i=i0; j++;
                // It is assumed that the iteration starts from 0 and happens only using this function from within
                // an assigner object.
                // Otherwise (i.e. if it is called outside the assigner object) apply2 should have been
                // outside the if statement.
                if (l>=e().size2()) {
                    i=i0+e().size1();
                    Wrap::apply2(e().size2(), me().size1(), j, i);
                    return false;
                }
            }
            return true;
        }

        template <class E, typename S1, typename S2>
        BOOST_UBLAS_INLINE
        static void apply_wrap(const E& e, S1 &i, S2 &j) {
            Wrap::apply1(e().size1(), j, i);
        }
    };
}
#ifndef BOOST_UBLAS_DEFAULT_NO_WRAP_POLICY
    typedef traverse_policy::wrap DEFAULT_WRAP_POLICY;
#else
    typedef traverse_policy::no_wrap DEFAULT_WRAP_POLICY;
#endif

#ifndef BOOST_UBLAS_DEFAULT_ASSIGN_BY_COLUMN
    typedef traverse_policy::by_row_policy<DEFAULT_WRAP_POLICY> DEFAULT_TRAVERSE_POLICY;
#else
    typedef traverse_policy::by_column<DEFAULT_WRAP_POLICY> DEFAULT_TRAVERSE_POLICY;
#endif

 // Traverse policy namespace
namespace traverse_policy {

    inline by_row_policy<DEFAULT_WRAP_POLICY> by_row() {
    return by_row_policy<DEFAULT_WRAP_POLICY>();
    }

    inline by_row_policy<wrap> by_row_wrap() {
        return by_row_policy<wrap>();
    }

    inline by_row_policy<no_wrap> by_row_no_wrap() {
        return by_row_policy<no_wrap>();
    }

    inline by_column_policy<DEFAULT_WRAP_POLICY> by_column() {
        return by_column_policy<DEFAULT_WRAP_POLICY>();
    }

    inline by_column_policy<wrap> by_column_wrap() {
        return by_column_policy<wrap>();
    }

    inline by_column_policy<no_wrap> by_column_no_wrap() {
        return by_column_policy<no_wrap>();
    }

}

/**
* \brief  An assigner object used to fill a vector using operator <<= and operator, (comma)
*
* This object is meant to be created by appropriate object generators.
* Please see EXAMPLES_LINK for usage information.
*
* \todo Add examples link
*/
template <class E, class Fill_Policy = fill_policy::index_assign>
class vector_expression_assigner {
public:
    typedef typename E::expression_type::value_type value_type;
    typedef typename E::expression_type::size_type size_type;

    BOOST_UBLAS_INLINE
    vector_expression_assigner(E &e):ve(e), i(0) {
    }

    BOOST_UBLAS_INLINE
    vector_expression_assigner(size_type k, E &e):ve(e), i(k) {
        // Overloaded like that so it can be differentiated from (E, val).
        // Otherwise there would be an ambiquity when value_type == size_type.
    }

    BOOST_UBLAS_INLINE
    vector_expression_assigner(E &e, value_type val):ve(e), i(0) {
        operator,(val);
    }

    template <class AE>
    BOOST_UBLAS_INLINE
    vector_expression_assigner(E &e, const vector_expression<AE> &nve):ve(e), i(0) {
        operator,(nve);
    }

    template <typename T>
    BOOST_UBLAS_INLINE
    vector_expression_assigner(E &e, const index_manipulator<T> &ta):ve(e), i(0) {
        operator,(ta);
    }

    BOOST_UBLAS_INLINE
    vector_expression_assigner &operator, (const value_type& val) {
        apply(val);
        return *this;
    }

    template <class AE>
    BOOST_UBLAS_INLINE
    vector_expression_assigner &operator, (const vector_expression<AE> &nve) {
        for (typename AE::size_type k = 0; k!= nve().size(); k++)
            operator,(nve()(k));
        return *this;
    }

    template <typename T>
    BOOST_UBLAS_INLINE
    vector_expression_assigner &operator, (const index_manipulator<T> &ta) {
        ta().manip(i);
        return *this;
    }

    template <class T>
    BOOST_UBLAS_INLINE
    vector_expression_assigner<E, T> operator, (fill_policy_wrapper<T>) const {
        return vector_expression_assigner<E, T>(i, ve);
    }

private:
    BOOST_UBLAS_INLINE
    vector_expression_assigner &apply(const typename E::expression_type::value_type& val) {
        Fill_Policy::apply(ve, i++, val);
        return *this;
    }

private:
    E &ve;
    size_type i;
};

/*
// The following static assigner is about 30% slower than the dynamic one, probably due to the recursive creation of assigner objects.
// It remains commented here for future reference.

template <class E, std::size_t I=0>
class static_vector_expression_assigner {
public:
    typedef typename E::expression_type::value_type value_type;
    typedef typename E::expression_type::size_type size_type;

    BOOST_UBLAS_INLINE
    static_vector_expression_assigner(E &e):ve(e) {
    }

    BOOST_UBLAS_INLINE
    static_vector_expression_assigner(E &e, value_type val):ve(e) {
        operator,(val);
    }

    BOOST_UBLAS_INLINE
    static_vector_expression_assigner<E, I+1> operator, (const value_type& val) {
        return apply(val);
    }

private:
    BOOST_UBLAS_INLINE
    static_vector_expression_assigner<E, I+1> apply(const typename E::expression_type::value_type& val) {
        ve()(I)=val;
        return static_vector_expression_assigner<E, I+1>(ve);
    }

private:
    E &ve;
};

template <class E>
BOOST_UBLAS_INLINE
static_vector_expression_assigner<vector_expression<E>, 1 > test_static(vector_expression<E> &v, const typename E::value_type &val) {
    v()(0)=val;
    return static_vector_expression_assigner<vector_expression<E>, 1 >(v);
}
*/

/**
* \brief  A vector_expression_assigner generator used with operator<<= for simple types
*
* Please see EXAMPLES_LINK for usage information.
*
* \todo Add examples link
*/
template <class E>
BOOST_UBLAS_INLINE
vector_expression_assigner<vector_expression<E> > operator<<=(vector_expression<E> &v, const typename E::value_type &val) {
    return vector_expression_assigner<vector_expression<E> >(v,val);
}

/**
* \brief  ! A vector_expression_assigner generator used with operator<<= for vector expressions
*
* Please see EXAMPLES_LINK for usage information.
*
* \todo Add examples link
*/
template <class E1, class E2>
BOOST_UBLAS_INLINE
vector_expression_assigner<vector_expression<E1> > operator<<=(vector_expression<E1> &v, const vector_expression<E2> &ve) {
    return vector_expression_assigner<vector_expression<E1> >(v,ve);
}

/**
* \brief  A vector_expression_assigner generator used with operator<<= for traverse manipulators
*
* Please see EXAMPLES_LINK for usage information.
*
* \todo Add examples link
*/
template <class E, typename T>
BOOST_UBLAS_INLINE
vector_expression_assigner<vector_expression<E> > operator<<=(vector_expression<E> &v, const index_manipulator<T> &nv) {
    return vector_expression_assigner<vector_expression<E> >(v,nv);
}

/**
* \brief  A vector_expression_assigner generator used with operator<<= for choice of fill policy
*
* Please see EXAMPLES_LINK for usage information.
*
* \todo Add examples link
*/
template <class E, typename T>
BOOST_UBLAS_INLINE
vector_expression_assigner<vector_expression<E>, T> operator<<=(vector_expression<E> &v, fill_policy_wrapper<T>) {
    return vector_expression_assigner<vector_expression<E>, T>(v);
}

/**
* \brief  An assigner object used to fill a vector using operator <<= and operator, (comma)
*
* This object is meant to be created by appropriate object generators.
* Please see EXAMPLES_LINK for usage information.
*
* \todo Add examples link
*/
template <class E, class Fill_Policy = fill_policy::index_assign, class Traverse_Policy = DEFAULT_TRAVERSE_POLICY >
class matrix_expression_assigner {
public:
    typedef typename E::expression_type::size_type size_type;

    BOOST_UBLAS_INLINE
    matrix_expression_assigner(E &e): me(e), i(0), j(0) {
    }

    BOOST_UBLAS_INLINE
    matrix_expression_assigner(E &e, size_type k, size_type l): me(e), i(k), j(l) {
    }

    BOOST_UBLAS_INLINE
    matrix_expression_assigner(E &e, typename E::expression_type::value_type val): me(e), i(0), j(0) {
        operator,(val);
    }

    template <class AE>
    BOOST_UBLAS_INLINE
    matrix_expression_assigner(E &e, const vector_expression<AE> &nve):me(e), i(0), j(0) {
        operator,(nve);
    }

    template <class AE>
    BOOST_UBLAS_INLINE
    matrix_expression_assigner(E &e, const matrix_expression<AE> &nme):me(e), i(0), j(0) {
        operator,(nme);
    }

    template <typename T>
    BOOST_UBLAS_INLINE
    matrix_expression_assigner(E &e, const index_manipulator<T> &ta):me(e), i(0), j(0) {
        operator,(ta);
    }

    BOOST_UBLAS_INLINE
    matrix_expression_assigner &operator, (const typename E::expression_type::value_type& val) {
        Traverse_Policy::apply_wrap(me, i ,j);
        return apply(val);
    }

    template <class AE>
    BOOST_UBLAS_INLINE
    matrix_expression_assigner &operator, (const vector_expression<AE> &nve) {
        for (typename AE::size_type k = 0; k!= nve().size(); k++) {
            operator,(nve()(k));
        }
        return *this;
    }

    template <class AE>
    BOOST_UBLAS_INLINE
    matrix_expression_assigner &operator, (const matrix_expression<AE> &nme) {
        return apply(nme);
    }

    template <typename T>
    BOOST_UBLAS_INLINE
    matrix_expression_assigner &operator, (const index_manipulator<T> &ta) {
        ta().manip(i, j);
        return *this;
    }

    template <class T>
    BOOST_UBLAS_INLINE
    matrix_expression_assigner<E, T, Traverse_Policy> operator, (fill_policy_wrapper<T>) const {
        return matrix_expression_assigner<E, T, Traverse_Policy>(me, i, j);
    }

    template <class T>
    BOOST_UBLAS_INLINE
    matrix_expression_assigner<E, Fill_Policy, T> operator, (traverse_policy_wrapper<T>) {
        Traverse_Policy::apply_wrap(me, i ,j);
        return matrix_expression_assigner<E, Fill_Policy, T>(me, i, j);
    }

private:
    BOOST_UBLAS_INLINE
    matrix_expression_assigner &apply(const typename E::expression_type::value_type& val) {
        Fill_Policy::apply(me, i, j, val);
        Traverse_Policy::advance(i,j);
        return *this;
    }

    template <class AE>
    BOOST_UBLAS_INLINE
    matrix_expression_assigner &apply(const matrix_expression<AE> &nme) {
        size_type bi = i;
        size_type bj = j;
        typename AE::size_type k=0, l=0;
        Fill_Policy::apply(me, i, j, nme()(k, l));
        while (Traverse_Policy::next(nme, me, i, j, bi, bj, k, l))
            Fill_Policy::apply(me, i, j, nme()(k, l));
        return *this;
    }

private:
    E &me;
    size_type i, j;
};

/**
* \brief  A matrix_expression_assigner generator used with operator<<= for simple types
*
* Please see EXAMPLES_LINK for usage information.
*
* \todo Add examples link
*/
template <class E>
BOOST_UBLAS_INLINE
matrix_expression_assigner<matrix_expression<E> > operator<<=(matrix_expression<E> &me, const typename E::value_type &val) {
    return matrix_expression_assigner<matrix_expression<E> >(me,val);
}

/**
* \brief  A matrix_expression_assigner generator used with operator<<= for choice of fill policy
*
* Please see EXAMPLES_LINK for usage information.
*
* \todo Add examples link
*/
template <class E, typename T>
BOOST_UBLAS_INLINE
matrix_expression_assigner<matrix_expression<E>, T> operator<<=(matrix_expression<E> &me, fill_policy_wrapper<T>) {
    return matrix_expression_assigner<matrix_expression<E>, T>(me);
}

/**
* \brief  A matrix_expression_assigner generator used with operator<<= for traverse manipulators
*
* Please see EXAMPLES_LINK for usage information.
*
* \todo Add examples link
*/
template <class E, typename T>
BOOST_UBLAS_INLINE
matrix_expression_assigner<matrix_expression<E> > operator<<=(matrix_expression<E> &me, const index_manipulator<T> &ta) {
    return matrix_expression_assigner<matrix_expression<E> >(me,ta);
}

/**
* \brief  A matrix_expression_assigner generator used with operator<<= for traverse manipulators
*
* Please see EXAMPLES_LINK for usage information.
*
* \todo Add examples link
*/
template <class E, typename T>
BOOST_UBLAS_INLINE
matrix_expression_assigner<matrix_expression<E>, fill_policy::index_assign, T> operator<<=(matrix_expression<E> &me, traverse_policy_wrapper<T>) {
    return matrix_expression_assigner<matrix_expression<E>, fill_policy::index_assign, T>(me);
}

/**
* \brief  A matrix_expression_assigner generator used with operator<<= for vector expressions
*
* Please see EXAMPLES_LINK for usage information.
*
* \todo Add examples link
*/
template <class E1, class E2>
BOOST_UBLAS_INLINE
matrix_expression_assigner<matrix_expression<E1> > operator<<=(matrix_expression<E1> &me, const vector_expression<E2> &ve) {
    return matrix_expression_assigner<matrix_expression<E1> >(me,ve);
}

/**
* \brief  A matrix_expression_assigner generator used with operator<<= for matrix expressions
*
* Please see EXAMPLES_LINK for usage information.
*
* \todo Add examples link
*/
template <class E1, class E2>
BOOST_UBLAS_INLINE
matrix_expression_assigner<matrix_expression<E1> > operator<<=(matrix_expression<E1> &me1, const matrix_expression<E2> &me2) {
    return matrix_expression_assigner<matrix_expression<E1> >(me1,me2);
}

} } }

#endif // ASSIGNMENT_HPP


/*  ==========================================================================================
    Author: Leonardo Citraro
    Company:
    Filename: ext3DLBPpy.cpp
    Last modifed:   09.04.2017 by Leonardo Citraro
    Description:    Boost-Python wrappers
    *
    Please cite the article:    L. Citraro, S. Mahmoodi, A. Darekar, B. Vollmer,
                                Extended three-dimensional rotation invariant local binary patterns,
                                Image and Vision Computing (2017),
                                http://dx.doi.org/10.1016/j.imavis.2017.03.004

    ==========================================================================================
    Copyright (c) 2017 Leonardo Citraro <ldo.citraro@gmail.com>

    Permission is hereby granted, free of charge, to any person obtaining a copy of this
    software and associated documentation files (the "Software"), to deal in the Software
    without restriction, including without limitation the rights to use, copy, modify,
    merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    permit persons to whom the Software is furnished to do so, subject to the following
    conditions:

    The above copyright notice and this permission notice shall be included in all copies
    or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
    PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
    FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
    OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
    ==========================================================================================
*/
#include <Python.h>
#include <boost/python.hpp>
#include <boost/python/extract.hpp>
#include <boost/python/numeric.hpp>
#include <numpy/ndarrayobject.h>
#include "ext3DLBP.hpp"

using namespace ext3DLBP;
using namespace boost::python;

template<typename T, size_t K>
Array3D<T,K,K,K> convert_numpy_to_STL(const numeric::array& data) {
    Array3D<T,K,K,K> array;
    for(int k=0; k<K; ++k) {
        for(int j=0; j<K; ++j) {
            for(int i=0; i<K; ++i) {
                array[i][j][k] = extract<T>(data[i][j][k]);
            }
        }
    }
    return array;
}

template<typename T, size_t K>
Array3D<T,K,K,K> my_slice(const numeric::array& data, const int istart, const int jstart, const int kstart) {
    Array3D<T,K,K,K> array;
    for(int k=0; k<K; ++k) {
        for(int j=0; j<K; ++j) {
            for(int i=0; i<K; ++i) {
                array[i][j][k] = extract<T>(data[i+istart][j+jstart][k+kstart]);
            }
        }
    }
    return array;
}

boost::python::tuple convert_to_python_return(const Array1D<int,2>& data) {
    return boost::python::make_tuple(data[0], data[1]);
}
boost::python::tuple convert_to_python_return(const Array1D<int,3>& data) {
    return boost::python::make_tuple(data[0], data[1], data[2]);
}
int convert_to_python_return(const int data) {
    return data;
}

//=========================================================================
// CI-LBP
//=========================================================================
struct CI_LBP_ : public CI_LBP {
    const static int bins = CI_LBP::bins;
    CI_LBP_(const double mur) : CI_LBP(mur) {}
    auto convert(const boost::python::numeric::array data) {
        int shape0 = extract<int>(data.attr("shape")[0]);
        if(shape0 == 3)
            return CI_LBP::convert(convert_numpy_to_STL<int,3>(data));
        else if(shape0 == 5)
            return CI_LBP::convert(convert_numpy_to_STL<int,5>(data));
        else if(shape0 == 7)
            return CI_LBP::convert(convert_numpy_to_STL<int,7>(data));
    }
    auto convert_3d_image(const numeric::array& image) {
        int depth = extract<int>(image.attr("shape")[0]);
        int cols = extract<int>(image.attr("shape")[1]);
        int rows = extract<int>(image.attr("shape")[2]);

        npy_intp size[3] = {depth-2, cols-2, rows-2};
        PyObject* arr_3d_1Obj = PyArray_SimpleNew(3, size, NPY_INT);

        for(int k=0; k<(depth); ++k) {
            for(int j=0; j<(cols); ++j) {
                for(int i=0; i<(rows); ++i) {
                    int chunk = extract<int>(image[i][j][k]);
                    int* ptr = (int*) PyArray_GETPTR3(arr_3d_1Obj, i, j, k);
                    *ptr = CI_LBP::convert(chunk);
                }
            }
        }
        boost::python::handle<> handle( arr_3d_1Obj );
        boost::python::numeric::array arr_3d_1( handle );
        return arr_3d_1;
    }
};
const int CI_LBP_::bins;

//=========================================================================
// NI-LBP
//=========================================================================

#define wrapper_class_1     struct fullname : public parent {\
                                const static int P = parent::P;\
                                const static int O = parent::O;\
                                const static int bins = parent::bins;\
                                const static int R = parent::R;\
                                const static int K = parent::K;\
                                fullname(const double mur, const int V) : parent(mur,V) {}\
                                auto convert(const boost::python::numeric::array& data) {\
                                    return convert_to_python_return(parent::convert(convert_numpy_to_STL<int,K>(data)));\
                                }\
                                auto convert_3d_image(const numeric::array& image) {\
                                    int depth = extract<int>(image.attr("shape")[0]);\
                                    int cols = extract<int>(image.attr("shape")[1]);\
                                    int rows = extract<int>(image.attr("shape")[2]);\
                                    \
                                    npy_intp size[3] = {depth-2*R, cols-2*R, rows-2*R};\
                                    PyObject* arr_3d_1Obj = PyArray_SimpleNew(3, size, NPY_INT);\
                                    \
                                    for(int k=0; k<(depth-K+1); ++k) {\
                                        for(int j=0; j<(cols-K+1); ++j) {\
                                            for(int i=0; i<(rows-K+1); ++i) {\
                                                auto chunk = my_slice<int,K>(image,i,j,k);\
                                                int* ptr = (int*) PyArray_GETPTR3(arr_3d_1Obj, i, j, k);\
                                                *ptr = parent::convert(chunk);\
                                            }\
                                        }\
                                    }\
                                    boost::python::handle<> handle( arr_3d_1Obj );\
                                    boost::python::numeric::array arr_3d_1( handle );\
                                    return arr_3d_1;\
                                }\
                            };\
                            const int fullname::P;\
                            const int fullname::bins;\
                            const int fullname::O;\
                            const int fullname::R;\
                            const int fullname::K;\

#define wrapper_class_2     struct fullname : public parent {\
                                const static int P = parent::P;\
                                const static int O = parent::O;\
                                const static int bins = parent::bins;\
                                const static int R = parent::R;\
                                const static int K = parent::K;\
                                fullname(const double mur, const int V) : parent(mur,V) {}\
                                auto convert(const boost::python::numeric::array& data) {\
                                    return convert_to_python_return(parent::convert(convert_numpy_to_STL<int,K>(data)));\
                                }\
                                auto convert_3d_image(const numeric::array& image) {\
                                    int depth = extract<int>(image.attr("shape")[0]);\
                                    int cols = extract<int>(image.attr("shape")[1]);\
                                    int rows = extract<int>(image.attr("shape")[2]);\
                                    \
                                    npy_intp size[3] = {depth-2*R, cols-2*R, rows-2*R};\
                                    PyObject* arr_3d_1Obj = PyArray_SimpleNew(3, size, NPY_INT);\
                                    PyObject* arr_3d_2Obj = PyArray_SimpleNew(3, size, NPY_INT);\
                                    \
                                    for(int k=0; k<(depth-K+1); ++k) {\
                                        for(int j=0; j<(cols-K+1); ++j) {\
                                            for(int i=0; i<(rows-K+1); ++i) {\
                                                auto chunk = my_slice<int,K>(image,i,j,k);\
                                                Array1D<int,2> codes = parent::convert(chunk);\
                                                int* ptr = (int*) PyArray_GETPTR3(arr_3d_1Obj, i, j, k);\
                                                *ptr = codes[0];\
                                                ptr = (int*) PyArray_GETPTR3(arr_3d_2Obj, i, j, k);\
                                                *ptr = codes[1];\
                                            }\
                                        }\
                                    }\
                                    boost::python::handle<> handle1( arr_3d_1Obj );\
                                    boost::python::numeric::array arr_3d_1( handle1 );\
                                    boost::python::handle<> handle2( arr_3d_2Obj );\
                                    boost::python::numeric::array arr_3d_2( handle2 );\
                                    return make_tuple(arr_3d_1, arr_3d_2);\
                                }\
                            };\
                            const int fullname::P;\
                            const int fullname::bins;\
                            const int fullname::O;\
                            const int fullname::R;\
                            const int fullname::K;\

#define wrapper_class_3     struct fullname : public parent {\
                                const static int P = parent::P;\
                                const static int O = parent::O;\
                                const static int bins = parent::bins;\
                                const static int R = parent::R;\
                                const static int K = parent::K;\
                                fullname(const double mur, const int V) : parent(mur,V) {}\
                                auto convert(const boost::python::numeric::array& data) {\
                                    return convert_to_python_return(parent::convert(convert_numpy_to_STL<int,K>(data)));\
                                }\
                                auto convert_3d_image(const numeric::array& image) {\
                                    int depth = extract<int>(image.attr("shape")[0]);\
                                    int cols = extract<int>(image.attr("shape")[1]);\
                                    int rows = extract<int>(image.attr("shape")[2]);\
                                    \
                                    npy_intp size[3] = {depth-2*R, cols-2*R, rows-2*R};\
                                    PyObject* arr_3d_1Obj = PyArray_SimpleNew(3, size, NPY_INT);\
                                    PyObject* arr_3d_2Obj = PyArray_SimpleNew(3, size, NPY_INT);\
                                    PyObject* arr_3d_3Obj = PyArray_SimpleNew(3, size, NPY_INT);\
                                    \
                                    for(int k=0; k<(depth-K+1); ++k) {\
                                        for(int j=0; j<(cols-K+1); ++j) {\
                                            for(int i=0; i<(rows-K+1); ++i) {\
                                                auto chunk = my_slice<int,K>(image,i,j,k);\
                                                Array1D<int,3> codes = parent::convert(chunk);\
                                                int* ptr = (int*) PyArray_GETPTR3(arr_3d_1Obj, i, j, k);\
                                                *ptr = codes[0];\
                                                ptr = (int*) PyArray_GETPTR3(arr_3d_2Obj, i, j, k);\
                                                *ptr = codes[1];\
                                                ptr = (int*) PyArray_GETPTR3(arr_3d_3Obj, i, j, k);\
                                                *ptr = codes[2];\
                                            }\
                                        }\
                                    }\
                                    boost::python::handle<> handle1( arr_3d_1Obj );\
                                    boost::python::numeric::array arr_3d_1( handle1 );\
                                    boost::python::handle<> handle2( arr_3d_2Obj );\
                                    boost::python::numeric::array arr_3d_2( handle2 );\
                                    boost::python::handle<> handle3( arr_3d_3Obj );\
                                    boost::python::numeric::array arr_3d_3( handle3 );\
                                    return make_tuple(arr_3d_1, arr_3d_2, arr_3d_3);\
                                }\
                            };\
                            const int fullname::P;\
                            const int fullname::bins;\
                            const int fullname::O;\
                            const int fullname::R;\
                            const int fullname::K;\

// P42g
#define fullname NI_LBP_P42g_R1
#define parent NI_LBP<P42g, R1>
wrapper_class_1

#define fullname NI_LBP_P42g_R2
#define parent NI_LBP<P42g, R2>
wrapper_class_1

// P92g
#define fullname NI_LBP_P92g_R1
#define parent NI_LBP<P92g, R1>
wrapper_class_1

#define fullname NI_LBP_P92g_R2
#define parent NI_LBP<P92g, R2>
wrapper_class_1

#define fullname NI_LBP_P92g_R3
#define parent NI_LBP<P92g, R3>
wrapper_class_1

// P252g
#define fullname NI_LBP_P252g_R2
#define parent NI_LBP<P252g, R2>
wrapper_class_1

#define fullname NI_LBP_P252g_R3
#define parent NI_LBP<P252g, R3>
wrapper_class_1

//=========================================================================
// RD-LBP
//=========================================================================
// P42g
#define fullname RD_LBP_P42g_R1
#define parent RD_LBP<P42g, R1>
wrapper_class_1

#define fullname RD_LBP_P42g_R2
#define parent RD_LBP<P42g, R2>
wrapper_class_1

// P92g
#define fullname RD_LBP_P92g_R1
#define parent RD_LBP<P92g, R1>
wrapper_class_1

#define fullname RD_LBP_P92g_R2
#define parent RD_LBP<P92g, R2>
wrapper_class_1

#define fullname RD_LBP_P92g_R3
#define parent RD_LBP<P92g, R3>
wrapper_class_1

// P252g
#define fullname RD_LBP_P252g_R2
#define parent RD_LBP<P252g, R2>
wrapper_class_1

#define fullname RD_LBP_P252g_R3
#define parent RD_LBP<P252g, R3>
wrapper_class_1

//=========================================================================
// NI-RD-LBP
//=========================================================================
//P42g
#define fullname NI_RD_LBP_P42g_R1
#define parent NI_RD_LBP<P42g, R1>
wrapper_class_2

#define fullname NI_RD_LBP_P42g_R2
#define parent NI_RD_LBP<P42g, R2>
wrapper_class_2

//P92g
#define fullname NI_RD_LBP_P92g_R1
#define parent NI_RD_LBP<P92g, R1>
wrapper_class_2

#define fullname NI_RD_LBP_P92g_R2
#define parent NI_RD_LBP<P92g, R2>
wrapper_class_2

#define fullname NI_RD_LBP_P92g_R3
#define parent NI_RD_LBP<P92g, R3>
wrapper_class_2

//P252g
#define fullname NI_RD_LBP_P252g_R2
#define parent NI_RD_LBP<P252g, R2>
wrapper_class_2

#define fullname NI_RD_LBP_P252g_R3
#define parent NI_RD_LBP<P252g, R3>
wrapper_class_2

//=========================================================================
// NI-RD-CI-LBP
//=========================================================================
//P42g
#define fullname NI_RD_CI_LBP_P42g_R1
#define parent NI_RD_CI_LBP<P42g, R1>
wrapper_class_3

#define fullname NI_RD_CI_LBP_P42g_R2
#define parent NI_RD_CI_LBP<P42g, R2>
wrapper_class_3

//P92g
#define fullname NI_RD_CI_LBP_P92g_R1
#define parent NI_RD_CI_LBP<P92g, R1>
wrapper_class_3

#define fullname NI_RD_CI_LBP_P92g_R2
#define parent NI_RD_CI_LBP<P92g, R2>
wrapper_class_3

#define fullname NI_RD_CI_LBP_P92g_R3
#define parent NI_RD_CI_LBP<P92g, R3>
wrapper_class_3

//P252g
#define fullname NI_RD_CI_LBP_P252g_R2
#define parent NI_RD_CI_LBP<P252g, R2>
wrapper_class_3

#define fullname NI_RD_CI_LBP_P252g_R3
#define parent NI_RD_CI_LBP<P252g, R3>
wrapper_class_3

BOOST_PYTHON_MODULE(ext3DLBPpy) {
    boost::python::numeric::array::set_module_and_type("numpy", "ndarray");

    import_array();

    // CI-LBP
    class_<CI_LBP_>("CI_LBP",init<double>())
        .def_readonly("mur", &CI_LBP_::mur)
        .def_readonly("bins", &CI_LBP_::bins)
        .def("convert", &CI_LBP_::convert)
        .def("convert_3d_image", &CI_LBP_::convert_3d_image);

#define STRINGIZE_NX(A) #A
#define STRINGIZE(A) STRINGIZE_NX(A)
#define boost_python_definition class_<fullname>(STRINGIZE(fullname),init<double, int>())\
                                .def_readonly("P", &fullname::P)\
                                .def_readonly("O", &fullname::O)\
                                .def_readonly("bins", &fullname::bins)\
                                .def_readonly("R", &fullname::R)\
                                .def_readonly("K", &fullname::K)\
                                .def_readonly("mur", &fullname::mur)\
                                .def_readonly("V", &fullname::V)\
                                .def("convert", &fullname::convert) \
                                .def("convert_3d_image", &fullname::convert_3d_image);
// NI-LBP
#define fullname NI_LBP_P42g_R1
boost_python_definition

#define fullname NI_LBP_P42g_R2
boost_python_definition

#define fullname NI_LBP_P92g_R1
boost_python_definition

#define fullname NI_LBP_P92g_R2
boost_python_definition

#define fullname NI_LBP_P92g_R3
boost_python_definition

#define fullname NI_LBP_P252g_R2
boost_python_definition

#define fullname NI_LBP_P252g_R3
boost_python_definition

// RD-LBP
#define fullname RD_LBP_P42g_R1
boost_python_definition

#define fullname RD_LBP_P42g_R2
boost_python_definition

#define fullname RD_LBP_P92g_R1
boost_python_definition

#define fullname RD_LBP_P92g_R2
boost_python_definition

#define fullname RD_LBP_P92g_R3
boost_python_definition

#define fullname RD_LBP_P252g_R2
boost_python_definition

#define fullname RD_LBP_P252g_R3
boost_python_definition

// NI-RD-LBP
#define fullname NI_RD_LBP_P42g_R1
boost_python_definition

#define fullname NI_RD_LBP_P42g_R2
boost_python_definition

#define fullname NI_RD_LBP_P92g_R1
boost_python_definition

#define fullname NI_RD_LBP_P92g_R2
boost_python_definition

#define fullname NI_RD_LBP_P92g_R3
boost_python_definition

#define fullname NI_RD_LBP_P252g_R2
boost_python_definition

#define fullname NI_RD_LBP_P252g_R3
boost_python_definition

// NI-RD-CI-LBP
#define fullname NI_RD_CI_LBP_P42g_R1
boost_python_definition

#define fullname NI_RD_CI_LBP_P42g_R2
boost_python_definition

#define fullname NI_RD_CI_LBP_P92g_R1
boost_python_definition

#define fullname NI_RD_CI_LBP_P92g_R2
boost_python_definition

#define fullname NI_RD_CI_LBP_P92g_R3
boost_python_definition

#define fullname NI_RD_CI_LBP_P252g_R2
boost_python_definition

#define fullname NI_RD_CI_LBP_P252g_R3
boost_python_definition

};


// File:  pointPicker.cpp
// Date:  7/29/2016
// Auth:  K. Loux
// Desc:  Object for picking points from images.

// Standard C++ headers
#include <cstdlib>
#include <cassert>
#include <cmath>
#include <limits>

// wxWidgets headers
#include <wx/clipbrd.h>

// Eigen headers
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4018)// signed/unsigned mismatch
#pragma warning(disable:4456)// declaration hides previous local declaration
#pragma warning(disable:4714)// function marked as __forceinline not inlined
#pragma warning(disable:4800)// forcing value to bool 'true' or 'false'
#pragma warning(disable:4189)// local variable is initialized but not referenced
#endif
#include <Eigen/Dense>
#ifdef _MSC_VER
#pragma warning(pop)
#endif

// Local headers
#include "pointPicker.h"
#include "pointEntryDialog.h"

//==========================================================================
// Class:			PointPicker
// Function:		PointPicker
//
// Description:		Constructor for PointPicker class.
//
// Input Arguments:
//		None
//
// Output Arguments:
//		None
//
// Return Value:
//		None
//
//==========================================================================
PointPicker::PointPicker()
{
	clipMode = ClipboardMode::None;
	dataMode = DataExtractionMode::None;
	curveIndex = 0;
	ResetErrorString();
}

//==========================================================================
// Class:			PointPicker
// Function:		AddPoint
//
// Description:		Processes new point data.
//
// Input Arguments:
//		rawX	= const double&
//		rawY	= const double&
//		xScale	= const double&
//		yScale	= const double&
//		xOffset	= const double&
//		yOffset	= const double&
//
// Output Arguments:
//		None
//
// Return Value:
//		None
//
//==========================================================================
void PointPicker::AddPoint(const double& rawX, const double& rawY,
	const double& xScale, const double& yScale,
	const double& xOffset, const double& yOffset)
{
	const double x(ScaleOrdinate(rawX, xScale, xOffset));
	const double y(ScaleOrdinate(rawY, yScale, yOffset));

	HandleClipboardMode(x, y);
	HandleDataMode(x, y);
}

//==========================================================================
// Class:			PointPicker
// Function:		HandleClipboardMode
//
// Description:		Handles necessary clipboard interaction.
//
// Input Arguments:
//		x	= const double&
//		y	= const double&
//
// Output Arguments:
//		None
//
// Return Value:
//		None
//
//==========================================================================
void PointPicker::HandleClipboardMode(const double& x, const double& y) const
{
	if (clipMode != ClipboardMode::None && wxTheClipboard->Open())
	{
		if (clipMode == ClipboardMode::X)
			wxTheClipboard->SetData(new wxTextDataObject(wxString::Format(_T("%f"), x)));
		else if (clipMode == ClipboardMode::Y)
			wxTheClipboard->SetData(new wxTextDataObject(wxString::Format(_T("%f"), y)));
		else if (clipMode == ClipboardMode::Both)
			wxTheClipboard->SetData(new wxTextDataObject(wxString::Format(_T("%f\t%f"), x, y)));
		else
			assert(false);

		wxTheClipboard->Close();
	}
}

//==========================================================================
// Class:			PointPicker
// Function:		HandleDataMode
//
// Description:		Handles necessary data collection.
//
// Input Arguments:
//		x	= const double&
//		y	= const double&
//
// Output Arguments:
//		None
//
// Return Value:
//		None
//
//==========================================================================
void PointPicker::HandleDataMode(const double& x, const double& y)
{
	if (dataMode == DataExtractionMode::None)
		return;

	if (dataMode == DataExtractionMode::Curve)
	{
		while (curvePoints.size() <= curveIndex)
			curvePoints.push_back(std::vector<Point>(0));

		lastPoint.x = x;
		lastPoint.y = y;
		curvePoints[curveIndex].push_back(lastPoint);
		return;
	}

	PointEntryDialog dialog(nullptr, wxID_ANY, _T("Coordinate Input"));
	if (dialog.ShowModal() == wxID_CANCEL)
		return;

	if (dataMode == DataExtractionMode::References)
	{
		lastPoint = dialog.GetPoint();
		referencePoints.push_back(ReferencePair(Point(x, y), lastPoint));
		UpdateTransformation();
	}
}

//==========================================================================
// Class:			PointPicker
// Function:		UpdateTransformation
//
// Description:		Updates the transformation matrix according to all stored
//					reference points.
//
// Input Arguments:
//		None
//
// Output Arguments:
//		None
//
// Return Value:
//		None
//
//==========================================================================
void PointPicker::UpdateTransformation()
{
	if (referencePoints.size() < 4)
	{
		ResetErrorString();
		return;
	}
	else
		errorString.clear();

	// Use the Direct Linear Transform approach to solve for the unknown
	// projective transforms.  Solve for the transforms assuming all different
	// combination of linear and logarithmic scales in order to determine
	// which combination provides the lowest error.
	double xLinYLinError;
	Eigen::MatrixXd xLinYLinTransformation(ComputeTransformation(referencePoints, PlotScaling::Linear, xLinYLinError));
	if (referencePoints.size() == 4)// Not enough information to determine if scaling is logarithmic
	{
		transformationMatrix = xLinYLinTransformation;
		xIsLogarithmic = false;
		yIsLogarithmic = false;
		return;
	}

	bool xLogIsOption(true);
	bool yLogIsOption(true);

	for (unsigned int i = 0; i < referencePoints.size(); i++)
	{
		if (referencePoints[i].valueCoords.x <= 0.0)
			xLogIsOption = false;
		if (referencePoints[i].valueCoords.y <= 0.0)
			yLogIsOption = false;
	}

	double xLinYLogError(std::numeric_limits<double>::max());
	double xLogYLinError(std::numeric_limits<double>::max());
	double xLogYLogError(std::numeric_limits<double>::max());

	Eigen::MatrixXd xLinYLogTransformation;
	Eigen::MatrixXd xLogYLinTransformation;
	Eigen::MatrixXd xLogYLogTransformation;

	if (yLogIsOption)
	{
		xLinYLogTransformation = ComputeTransformation(referencePoints, PlotScaling::SemiLogY, xLinYLogError);
		if (xLogIsOption)
			xLogYLogTransformation = ComputeTransformation(referencePoints, PlotScaling::SemiLogX, xLogYLogError);
	}

	if (xLogIsOption)
		xLogYLinTransformation = ComputeTransformation(referencePoints, PlotScaling::LogLog, xLogYLinError);

	// For linearly-scaled axes, the log-scaled transforms can have very similar error values to the
	// proper linear transforms but still give poor results.  When the correct scaling is logarithmic,
	// however, the linear transform will give an error that is orders of magnitude higher than that
	// given by the log transform.  So we assume linear unless the error is many times better when
	// using a log transform.
	const double linLogErrorRatio(1.0);

	if (xLinYLinError < xLinYLogError * linLogErrorRatio &&
		xLinYLinError < xLogYLinError * linLogErrorRatio &&
		xLinYLinError < xLogYLogError * linLogErrorRatio)
	{
		transformationMatrix = xLinYLinTransformation;
		xIsLogarithmic = false;
		yIsLogarithmic = false;
	}
	else if (xLinYLogError < xLogYLinError &&
		xLinYLogError < xLogYLogError)
	{
		transformationMatrix = xLinYLogTransformation;
		xIsLogarithmic = false;
		yIsLogarithmic = true;
	}
	else if (xLogYLinError < xLogYLogError)
	{
		transformationMatrix = xLogYLinTransformation;
		xIsLogarithmic = true;
		yIsLogarithmic = false;
	}
	else
	{
		transformationMatrix = xLogYLogTransformation;
		xIsLogarithmic = true;
		yIsLogarithmic = true;
	}
}

//==========================================================================
// Class:			PointPicker
// Function:		ComputeTransformation
//
// Description:		Computes the transformation for the specified correspondances.
//
// Input Arguments:
//		paris	= const std::vector<ReferencePair>&
//		scaling	= const PlotScaling&
//
// Output Arguments:
//		error	= double&
//
// Return Value:
//		Eigen::Matrix3d
//
//==========================================================================
Eigen::Matrix3d PointPicker::ComputeTransformation(
	const std::vector<ReferencePair>& pairs, const PlotScaling& scaling, double& error)
{
	Eigen::Matrix<double, Eigen::Dynamic, 9> model(2 * pairs.size(), 9);

	// Set up columns that will all have the same value
	model.block(0,2,pairs.size(),1).setOnes();
	model.block(0,3,pairs.size(),3).setZero();
	model.block(pairs.size(),0,pairs.size(),3).setZero();
	model.block(pairs.size(),5,pairs.size(),1).setOnes();

	const bool xLog(scaling == PlotScaling::LogLog || scaling == PlotScaling::SemiLogX);
	const bool yLog(scaling == PlotScaling::LogLog || scaling == PlotScaling::SemiLogY);

	for (unsigned int i = 0; i < pairs.size(); i++)
	{
		auto adjustValue([](const double& v, const bool& isLog)
		{
			if (isLog)
				return log10(v);
			return v;
		});

		const double xVal(adjustValue(pairs[i].valueCoords.x, xLog));
		const double yVal(adjustValue(pairs[i].valueCoords.y, yLog));

		// X-ordinate
		model(i,0) = pairs[i].imageCoords.x;
		model(i,1) = pairs[i].imageCoords.y;

		model(i,6) = -xVal * pairs[i].imageCoords.x;
		model(i,7) = -xVal * pairs[i].imageCoords.y;
		model(i,8) = -xVal;

		// Y-ordinate
		model(i + pairs.size(),3) = pairs[i].imageCoords.x;
		model(i + pairs.size(),4) = pairs[i].imageCoords.y;

		model(i + pairs.size(),6) = -yVal * pairs[i].imageCoords.x;
		model(i + pairs.size(),7) = -yVal * pairs[i].imageCoords.y;
		model(i + pairs.size(),8) = -yVal;
	}

	Eigen::JacobiSVD<Eigen::Matrix<double, Eigen::Dynamic, 9>> svd(model, Eigen::ComputeFullV);
	Eigen::Matrix<double, 9, 1> nullspace(svd.matrixV().col(8));
	Eigen::Matrix3d transform;
	transform.row(0) = nullspace.head<3>();
	transform.row(1) = nullspace.segment<3>(3);
	transform.row(2) = nullspace.tail<3>();

	// This doesn't work for log-scaled axes
	/*Eigen::VectorXd errorVector(model * nullspace);
	error = errorVector.dot(errorVector);*/

	error = 0.0;
	for (const auto& p : pairs)
	{
		Eigen::Vector3d imagePoint(p.imageCoords.x, p.imageCoords.y, 1.0);
		Eigen::Vector3d plotPoint(transform * imagePoint);
		Point result(plotPoint(0) / plotPoint(2), plotPoint(1) / plotPoint(2));
		if (xLog)
			result.x = pow(10, result.x);
		if (yLog)
			result.y = pow(10, result.y);

		error += (result.x - p.valueCoords.x) * (result.x - p.valueCoords.x) + (result.y - p.valueCoords.y) * (result.y - p.valueCoords.y);
	}

	return transform;
}

//==========================================================================
// Class:			PointPicker
// Function:		GetReferences
//
// Description:		Returns list of references.
//
// Input Arguments:
//		None
//
// Output Arguments:
//		None
//
// Return Value:
//		std::vector<PointPicker::Point>
//
//==========================================================================
std::vector<PointPicker::Point> PointPicker::GetReferences() const
{
	std::vector<Point> refs(referencePoints.size());
	for (unsigned int i = 0; i < refs.size(); ++i)
		refs[i] = referencePoints[i].valueCoords;

	return refs;
}

//==========================================================================
// Class:			PointPicker
// Function:		RemoveReference
//
// Description:		Removes the specified element from the list of references.
//
// Input Arguments:
//		i	= const unsigned int&
//
// Output Arguments:
//		None
//
// Return Value:
//		None
//
//==========================================================================
void PointPicker::RemoveReference(const unsigned int& i)
{
	referencePoints.erase(referencePoints.begin() + i);
}

//==========================================================================
// Class:			PointPicker
// Function:		ScaleOrdinate
//
// Description:		Scales the value according to the specified length and offset.
//
// Input Arguments:
//		value	= const double&
//		scale	= const double&
//		offset	= const double&
//
// Output Arguments:
//		None
//
// Return Value:
//		double
//
//==========================================================================
double PointPicker::ScaleOrdinate(const double& value, const double& scale,
	const double& offset)
{
	return value * scale + offset;
}

//==========================================================================
// Class:			PointPicker
// Function:		ResetReferences
//
// Description:		Resets the stored reference data.
//
// Input Arguments:
//		None
//
// Output Arguments:
//		None
//
// Return Value:
//		None
//
//==========================================================================
void PointPicker::ResetReferences()
{
	referencePoints.clear();
	ResetErrorString();
}

//==========================================================================
// Class:			PointPicker
// Function:		ResetCurveData
//
// Description:		Resets the stored curve data.
//
// Input Arguments:
//		curve	= const unsigned int&
//
// Output Arguments:
//		None
//
// Return Value:
//		None
//
//==========================================================================
void PointPicker::ResetCurveData(const unsigned int& curve)
{
	curvePoints.erase(curvePoints.begin() + curve);
}

//==========================================================================
// Class:			PointPicker
// Function:		Reset
//
// Description:		Resets all stored data.
//
// Input Arguments:
//		None
//
// Output Arguments:
//		None
//
// Return Value:
//		None
//
//==========================================================================
void PointPicker::Reset()
{
	ResetReferences();

	curvePoints.clear();
	curveIndex = 0;
}

//==========================================================================
// Class:			PointPicker
// Function:		GetCurveData
//
// Description:		Returns processed curve data.
//
// Input Arguments:
//		None
//
// Output Arguments:
//		None
//
// Return Value:
//		std::vector<std::vector<PointPicker::Point>>
//
//==========================================================================
std::vector<std::vector<PointPicker::Point>> PointPicker::GetCurveData() const
{
	if (!errorString.empty())
		return std::vector<std::vector<Point>>(0);

	std::vector<std::vector<Point>> data(curvePoints);
	for (unsigned int i = 0; i < curvePoints.size(); i++)
	{
		for (unsigned int j = 0; j < curvePoints[i].size(); j++)
			data[i][j] = ScalePoint(curvePoints[i][j]);
	}

	return data;
}

//==========================================================================
// Class:			PointPicker
// Function:		ScalePoint
//
// Description:		Converts the specified point from image to plot coordinates.
//
// Input Arguments:
//		imagePointIn	= const Point&
//
// Output Arguments:
//		None
//
// Return Value:
//		PointPicker::Point
//
//==========================================================================
PointPicker::Point PointPicker::ScalePoint(const Point& imagePointIn) const
{
	Eigen::Vector3d imagePoint(imagePointIn.x, imagePointIn.y, 1.0);
	Eigen::Vector3d plotPoint(transformationMatrix * imagePoint);

	Point p(plotPoint(0) / plotPoint(2), plotPoint(1) / plotPoint(2));

	if (xIsLogarithmic)
		p.x = pow(10.0, p.x);

	if (yIsLogarithmic)
		p.y = pow(10.0, p.y);

	return p;
}

//==========================================================================
// Class:			PointPicker
// Function:		ScaleSinglePoint
//
// Description:		Processes data for single point.
//
// Input Arguments:
//		rawX	= const double&
//		rawY	= const double&
//		xScale	= const double&
//		yScale	= const double&
//		xOffset	= const double&
//		yOffset	= const double&
//
// Output Arguments:
//		x		= double&
//		y		= double&
//
// Return Value:
//		Point
//
//==========================================================================
PointPicker::Point PointPicker::ScaleSinglePoint(const double& rawX, const double& rawY,
	const double& xScale, const double& yScale,
	const double& xOffset, const double& yOffset, double& x, double& y) const
{
	x = ScaleOrdinate(rawX, xScale, xOffset);
	y = ScaleOrdinate(rawY, yScale, yOffset);

	if (!errorString.empty())
		return Point(0.0, 0.0);

	return ScalePoint(Point(x, y));
}

//==========================================================================
// Class:			PointPicker
// Function:		ResetErrorString
//
// Description:		Resets the error string.
//
// Input Arguments:
//		None
//
// Output Arguments:
//		None
//
// Return Value:
//		None
//
//==========================================================================
void PointPicker::ResetErrorString() const
{
	errorString = _T("Not enough reference points");
}