#include <unsupported/Eigen/MatrixFunctions>
#include <Eigen/Eigenvalues>
#include "BlockMatrix.h"

BlockMatrix::BlockMatrix(int L) {
	init(L);
}

BlockMatrix::BlockMatrix(const BlockMatrix& bm) {
	init(bm._L);
	for (int i = 0; i < bm.size(); i++) {
		for (int j = 0; j < bm.size(); j++) {
			_mats[i][j] = bm._mats[i][j];
		}
	}
}

BlockMatrix::BlockMatrix(int L, const Array& diag) {
	init(L);
	std::vector<int> index = buildIndex(_L);
	for (int i = 0; i < size(); i++) {
		_mats[i][i] = diag.block(index[i], 0, StateCollection::Inst()->StateNumber(_L, i), 1).matrix().asDiagonal();
	}
}

BlockMatrix::~BlockMatrix() {
/*	for (size_t i = 0; i < _mats.size(); i++) {
		for (size_t j = 0; j < _mats.size(); j++) {
			if (_mats[i][j] != NULL) {
				delete _mats[i][j];
			}
		}
	}*/
}

void BlockMatrix::init(int L) {
	this->_L = L;
	_mats.resize((size_t)L + 1, std::vector<Matrix>(L + 1));
}

std::vector<int> BlockMatrix::buildIndex(int L) {
	StateCollection* inst = StateCollection::Inst();
	std::vector<int> index(L + 1);
	index[0] = 0;
	for (int i = 1; i <= L; i++) {
		index[i] = inst->StateNumber(L, i - 1) + index[i - 1];
	}

	return index;
}

bool BlockMatrix::isLadder(int* ladderSize) const {
	int diff = 0;
	bool first = true;
	for (int i = 0; i < size(); i++) {
		for (int j = 0; j < size(); j++) {
			if (empty(i, j)) continue;
			if (first) {
				diff = i - j;
				first = false;
			}
			else {
				if (i - j != diff) {
					return false;
				}
			}
		}
	}

	*ladderSize = diff;
	return true;
}

void BlockMatrix::fromMixing(const Array& left, const BlockMatrix& center, const Array& right) {
	init(center._L);

	std::vector<int> index = buildIndex(_L);
	for (int i = 0; i < center.size(); i++) {
		for (int j = 0; j < center.size(); j++) {
			if (center.empty(i, j)) continue;
			_mats[i][j].resize(center._mats[i][j].rows(), center._mats[i][j].cols());
			_mats[i][j].fill(0.0);

			for (int x = 0; x < _mats[i][j].rows(); x++) {
				for (int y = 0; y < _mats[i][j].cols(); y++) {
					_mats[i][j](x, y) = left(x + index[i]) * center._mats[i][j](x, y) * right(y + index[j]);
				}
//				_mats[i][j].row(x) = center._mats[i][j].row(x) * left(x + index[i]);
			}
//			for (int y = 0; y < _mats[i][j].cols(); y++) {
//				_mats[i][j].col(y) *= right(y + index[j]);
//			}
		}
	}
}

void BlockMatrix::mixWith(const Array& left, const Array& right) {
	std::vector<int> index = buildIndex(_L);
	for (int i = 0; i < this->size(); i++) {
		for (int j = 0; j < this->size(); j++) {
			if (empty(i, j)) continue;

			for (int x = 0; x < _mats[i][j].rows(); x++) {
				_mats[i][j].row(x) *= left(x + index[i]);
			}
			for (int y = 0; y < _mats[i][j].cols(); y++) {
				_mats[i][j].col(y) *= right(y + index[j]);
			}
		}
	}
}

void BlockMatrix::mixWith(const BlockMatrix& left, const BlockMatrix& right) {
	for (int i = 0; i < this->size(); i++) {
		for (int j = 0; j < this->size(); j++) {
			if (empty(i, j)) continue;
			_mats[i][j] = left._mats[i][i] * _mats[i][j] * right._mats[j][j];
		}
	}
}

void BlockMatrix::fromMixing(const BlockMatrix& left, const Array& diag, const BlockMatrix& right) {
	if (left._L != right._L) {
		throw OtocException("Dimension of left and right matrices are different.");
	}

	init(left._L);
	std::vector<int> index = buildIndex(_L);
	for (int i = 0; i < left.size(); i++) {
		for (int k = 0; k < left.size(); k++) {
			if (left.empty(i, k)) continue;
			Matrix tmp = Matrix::Zero(left._mats[i][k].rows(), left._mats[i][k].cols());
			for (int h = 0; h < tmp.cols(); h++) {
				tmp.col(h) = left._mats[i][k].col(h) * diag(h + index[k]);
			}

			for (int j = 0; j < right.size(); j++) {
				if (right.empty(k, j)) continue;
				if (this->empty(i, j)) {
					this->fill(i, j);
				}
				this->_mats[i][j].noalias() += tmp * right._mats[k][j];
			}
		}
	}
}

void BlockMatrix::setZeroAt(const std::vector<int>& zeroPos) {
	std::vector<int> index = buildIndex(_L);
	StateCollection* inst = StateCollection::Inst();
	int x = 0;
	for (int i = 0; i < zeroPos.size(); i++) {
		if (zeroPos[i] >= index[x] + inst->StateNumber(_L, x)) x++;
//		std::cout << "i=" << i << ", x=" << x << ", state#=" << inst->StateNumber(_L, x) << ", index[x]=" << index[x] << std::endl;
		int y = 0;
		for (int j = 0; j < zeroPos.size(); j++) {
			if (zeroPos[j] >= index[y] + inst->StateNumber(_L, y)) y++;
//			std::cout << "j=" << j << ", y=" << y << ", state#=" << inst->StateNumber(_L, y) << ", index[y]=" << index[y] << std::endl;
			if (empty(x, y)) {
				continue;
			}
			_mats[x][y](zeroPos[i] - index[x], zeroPos[j] - index[y]) = 0;
		}
	}
}

void BlockMatrix::diagonalize(Array& eigenValues, BlockMatrix& eigenVectors, BlockMatrix& eigenVectorsInv) const {
	eigenValues.resize(StateCollection::Inst()->StateNumber(_L));
	eigenVectors.init(_L);
	eigenVectorsInv.init(_L);

	std::vector<int> index = buildIndex(_L);
	Eigen::ComplexEigenSolver<Matrix> solver;
	for (int i = 0; i < size(); i++) {
		int n = _mats[i][i].rows();
		solver.compute(_mats[i][i], true);
		eigenValues.block(index[i], 0, n, 1) = solver.eigenvalues();
		eigenVectors._mats[i][i] = solver.eigenvectors();
		eigenVectorsInv._mats[i][i] = eigenVectors._mats[i][i].inverse();
	}
}

Matrix BlockMatrix::matrix() const {
	std::vector<int> index = buildIndex(_L);
	int size = StateCollection::Inst()->StateNumber(_L);
	Matrix ret = Matrix::Zero(size, size);
	for (int i = 0; i < this->size(); i++) {
		for (int j = 0; j < this->size(); j++) {
			if (this->empty(i, j)) {
				continue;
			}

			ret.block(index[i], index[j], this->_mats[i][j].rows(), this->_mats[i][j].cols()) = this->_mats[i][j];
		}
	}

	return ret;
}

void BlockMatrix::fill(int i, int j) {
	StateCollection* inst = StateCollection::Inst();
	_mats[i][j] = Matrix::Zero(inst->StateNumber(_L, i), inst->StateNumber(_L, j));
//	_mats[i][j]->fill(0.0);
}

Matrix& BlockMatrix::block(int i, int j) {
	return _mats[i][j];
}

BlockMatrix& BlockMatrix::operator += (const BlockMatrix& other) {
	if (this->_L != other._L) {
		throw OtocException("Matrix with different dimensions cannot add.");
	}

	for (int i = 0; i < size(); i++) {
		for (int j = 0; j < size(); j++) {
			if (other.empty(i, j)) {
				continue;
			}
			if (this->empty(i, j)) {
				_mats[i][j] = other._mats[i][j];
			}
			else {
				_mats[i][j] += other._mats[i][j];
			}
		}
	}

	return *this;
}

BlockMatrix& BlockMatrix::operator -= (const BlockMatrix& other) {
	if (this->_L != other._L) {
		throw OtocException("Matrix with different dimensions cannot add.");
	}

	for (int i = 0; i < size(); i++) {
		for (int j = 0; j < size(); j++) {
			if (other.empty(i, j)) {
				continue;
			}
			if (this->empty(i, j)) {
				_mats[i][j] = -other._mats[i][j];
			}
			else {
				_mats[i][j] -= other._mats[i][j];
			}
		}
	}

	return *this;
}

BlockMatrix BlockMatrix::operator - (const BlockMatrix& other) const {
	if (this->_L != other._L) {
		throw OtocException("Matrix with different dimensions cannot multiply.");
	}

	BlockMatrix res(this->size() - 1);

	for (int i = 0; i < size(); i++) {
		for (int j = 0; j < size(); j++) {
			if (this->empty(i, j) && other.empty(i, j)) {
				continue;
			}
			if (other.empty(i, j)) {
				res._mats[i][j] = this->_mats[i][j];
			}
			else if (this->empty(i, j)) {
				res._mats[i][j] = other._mats[i][j];
			}
			else {
				res._mats[i][j] = this->_mats[i][j] - other._mats[i][j];
			}
		}
	}

	return res;
}

BlockMatrix BlockMatrix::operator * (const BlockMatrix& other) const {
	if (this->_L != other._L) {
		throw OtocException("Matrix with different dimensions cannot multiply.");
	}

	BlockMatrix res(this->_L);

	for (int i = 0; i < size(); i++) {
		for (int j = 0; j < size(); j++) {
			for (int k = 0; k < size(); k++) {
				if (this->empty(i, k) || other.empty(k, j)) {
					continue;
				}
				if (res.empty(i, j)) {
					res.fill(i, j);
				}

				res._mats[i][j].noalias() += this->_mats[i][k] * other._mats[k][j];
			}
		}
	}

	return res;
}

void BlockMatrix::multiplyByLadder(const BlockMatrix& ladder, int ladderSize) {
	for (int i = 0; i < size(); i++) {
		if (ladderSize >= 0) {
			for (int j = 0; j < size(); j++) {
				int k = j + ladderSize;
				if (k >= size()) {
					_mats[i][j] = Matrix();
				}
				else {
					_mats[i][j] = _mats[i][k] * ladder._mats[k][j];
				}
			}
		}
		else {
			for (int j = size() - 1; j >= 0; j--) {
				int k = j + ladderSize;
				if (k < 0) {
					_mats[i][j] = Matrix();
				}
				else {
					_mats[i][j] = _mats[i][k] * ladder._mats[k][j];
				}
			}
		}
	}
}

BlockMatrix BlockMatrix::operator * (const var_t& val) const {
	BlockMatrix res(this->size() - 1);

	for (int i = 0; i < size(); i++) {
		for (int j = 0; j < size(); j++) {
			if (this->empty(i, j)) continue;
			res._mats[i][j] = _mats[i][j] * val;
		}
	}

	return res;
}

BlockMatrix& BlockMatrix::operator *= (const var_t& val) {
	for (int i = 0; i < size(); i++) {
		for (int j = 0; j < size(); j++) {
			if (this->empty(i, j)) continue;
			_mats[i][j] *= val;
		}
	}

	return *this;
}

BlockMatrix& BlockMatrix::operator *= (const Array& diag) {
	std::vector<int> index = buildIndex(_L);
	for (int i = 0; i < size(); i++) {
		for (int j = 0; j < size(); j++) {
			if (empty(i, j)) continue;
			for (int x = 0; x < _mats[i][j].cols(); x++) {
				_mats[i][j].col(x) *= diag(index[j] + x);
			}
		}
	}

	return *this;
}

BlockMatrix BlockMatrix::operator * (const Array& diag) {
	BlockMatrix ret(_L);
	std::vector<int> index = buildIndex(_L);
	for (int i = 0; i < size(); i++) {
		for (int j = 0; j < size(); j++) {
			if (empty(i, j)) continue;
			ret.fill(i, j);
			for (int x = 0; x < _mats[i][j].cols(); x++) {
				ret._mats[i][j].col(x) = _mats[i][j].col(x) * diag(index[j] + x);
			}
		}
	}

	return ret;
}

var_t BlockMatrix::trace() const {
	var_t ret(0.0);
	for (int i = 0; i < size(); i++) {
		if (!empty(i, i)) {
			ret += _mats[i][i].trace();
		}
	}

	return ret;
}

BlockMatrix BlockMatrix::exp() const {
	int ladderSize;
	if (!isLadder(&ladderSize)) {
		throw OtocException("Only ladder-like matrix can be exponentiated.");
	}

	BlockMatrix res(this->size() - 1);
	StateCollection* inst = StateCollection::Inst();
	if (ladderSize == 0) {
		for (int i = 0; i < res.size(); i++) {
			if (this->empty(i, i)) {
				res._mats[i][i] = Matrix::Identity(inst->StateNumber(this->_L, i), inst->StateNumber(this->_L, i));
			}
			else {
				res._mats[i][i] = _mats[i][i].exp();
			}
		}
	}
	else {
		Array id = Array::Constant(inst->StateNumber(_L), 1);
		BlockMatrix mat(_L, id);
		res += mat;
		for (int i = 1; i * std::abs(ladderSize) <= _L; i++) {
			mat.multiplyByLadder(*this, ladderSize);
			mat *= 1 / (f_type)i;
			res += mat;
		}
	}

	return res;
}

BlockMatrix BlockMatrix::conjugate() const {
	BlockMatrix res(this->size() - 1);
	for (int i = 0; i < res.size(); i++) {
		for (int j = 0; j < res.size(); j++) {
			if (this->empty(i, j)) continue;
			res._mats[i][j] = _mats[i][j].conjugate();
		}
	}

	return res;
}

BlockMatrix BlockMatrix::adjoint() const {
	BlockMatrix res(this->size() - 1);
	for (int i = 0; i < res.size(); i++) {
		for (int j = 0; j < res.size(); j++) {
			if (this->empty(i, j)) continue;
			res._mats[j][i] = _mats[i][j].adjoint();
		}
	}

	return res;
}

BlockMatrix& BlockMatrix::adjointInPlace() {
	for (int i = 0; i < size(); i++) {
		for (int j = i; j < size(); j++) {
			if (this->empty(i, j) && this->empty(j, i)) continue;
			if (i == j) {
				_mats[i][j].adjointInPlace();
			} else if (this->empty(i, j)) {
				_mats[i][j] = _mats[j][i].adjoint();
				_mats[j][i] = Matrix();
			}
			else if (this->empty(j, i)){
				_mats[j][i] = _mats[i][j].adjoint();
				_mats[i][j] = Matrix();
			}
			else {
				Matrix tmp = _mats[i][j];
				_mats[i][j] = _mats[j][i].adjoint();
				_mats[j][i] = tmp.adjoint();
/*				for (int x = 0; x < _mats[i][j].rows(); x++) {
					for (int y = 0; y < _mats[i][j].cols(); y++) {
						var_t tmp = _mats[i][j](x, y);
						_mats[i][j](x, y) = std::conj(_mats[j][i](y, x));
						_mats[j][i](y, x) = std::conj(tmp);
					}
				}*/
			}
		}
	}

	return *this;
}

BlockMatrix BlockMatrix::inverse() const {
	BlockMatrix res(this->size() - 1);
	for (int i = 0; i < res.size(); i++) {
		res._mats[i][i] = _mats[i][i].inverse();
	}

	return res;
}

BlockMatrix& BlockMatrix::inverseInPlace() {
	for (int i = 0; i < size(); i++) {
		_mats[i][i] = _mats[i][i].inverse();
	}

	return *this;
}

Array BlockMatrix::diagonal() const {
	std::vector<int> index = buildIndex(_L);
	StateCollection* inst = StateCollection::Inst();
	Array ret = Array::Zero(inst->StateNumber(this->_L));
	for (int m = 0; m <= this->_L; m++) {
		if (empty(m, m)) continue;
		ret.block(index[m], 0, _mats[m][m].rows(), 1) = _mats[m][m].diagonal().array();
	}

	return ret;
}

ostream& operator << (ostream& os, const BlockMatrix& bm) {
	for (int i = 0; i < bm.size(); i++) {
		for (int j = 0; j < bm.size(); j++) {
			if (bm.empty(i, j)) continue;
			os << "block(" << i << ", " << j << ")=" << std::endl;
			os << bm._mats[i][j] << std::endl;
		}
	}

	return os;
}


#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
#include "dbscan.h"
#include "dbscan_vp.h"
#include "dbscan_graph.h"

using namespace boost::python;
using namespace clustering;

namespace p = boost::python;
namespace np = boost::python::numpy;

struct ublas_matrix_to_python {
    static PyObject* convert( DBSCAN::ClusterData const& C )
    {
        PyObject* result = PyList_New( C.size1() );

        for ( size_t i = 0; i < C.size1(); ++i ) {
            PyObject* l = PyList_New( C.size2() );

            for ( size_t j = 0; j < C.size2(); ++j ) {
                PyList_SetItem( l, j, PyFloat_FromDouble( C( i, j ) ) );
            }

            PyList_SetItem( result, i, l );
        }

        return result;
    }
};

class PyDBSCAN : public DBSCAN {
public:
    PyDBSCAN()
        : DBSCAN()
    {
    }

    void pyfit( boost::python::list& pylist )
    {
        auto num_samples = boost::python::len( pylist );

        if ( num_samples == 0 ) {
            return;
        }

        boost::python::extract< boost::python::list > first_elem( pylist[0] );

        auto num_features = boost::python::len( first_elem );

        DBSCAN::ClusterData C( num_samples, num_features );

        for ( int i = 0; i < num_samples; ++i ) {
            boost::python::list sublist = boost::python::extract< boost::python::list >( pylist[i] );

            for ( int j = 0; j < num_features; ++j ) {
                C( i, j ) = boost::python::extract< double >( sublist[j] );
            }
        }

        fit( C );
    }

    void pywfit( boost::python::list& pylist, boost::python::list& pyweights )
    {
        auto num_samples = boost::python::len( pylist );

        if ( num_samples == 0 ) {
            return;
        }

        boost::python::extract< boost::python::list > first_elem( pylist[0] );

        auto num_features = boost::python::len( first_elem );

        DBSCAN::ClusterData C( num_samples, num_features );

        for ( int i = 0; i < num_samples; ++i ) {
            boost::python::list sublist = boost::python::extract< boost::python::list >( pylist[i] );

            for ( int j = 0; j < num_features; ++j ) {
                C( i, j ) = boost::python::extract< double >( sublist[j] );
            }
        }

        auto num_weights = boost::python::len( pyweights );

        DBSCAN::FeaturesWeights W( num_weights );

        for ( int i = 0; i < num_weights; ++i ) {
            W( i ) = boost::python::extract< double >( pyweights[i] );
        }

        wfit( C, W );
    }

    boost::python::list pyget_labels()
    {
        boost::python::list list;

        for ( const auto& l : get_labels() ) {
            list.append( l );
        }

        return list;
    }
};

class npDataset : public Dataset {
public:
    typedef boost::shared_ptr< npDataset > Ptr;

    npDataset()
        : Dataset()
    {
    }

    virtual ~npDataset()
    {
    }

    bool load_ndarray( const np::ndarray& in )
    {
        m_rows = in.shape( 0 );
        m_cols = in.shape( 1 );

        for ( size_t i = 0; i < m_rows; ++i ) {
            Eigen::VectorXf col_vector( m_cols );
            for ( size_t j = 0; j < m_cols; ++j ) {
                col_vector( j ) = p::extract< float >( in[i][j] );
            }

            _data.emplace_back( col_vector );
        }

        return true;
    }
};

class PyDBSCANvp {
public:
    PyDBSCANvp( const np::ndarray& d )
        : m_rows( 0 )
    {
        npDataset::Ptr np_dset = boost::make_shared< npDataset >();
        np_dset->load_ndarray( d );

        m_dbs = boost::make_shared< DBSCAN_VP >( np_dset );
        m_dbs->fit();
        m_rows = np_dset->rows();
    }

    const np::ndarray predict( double eps, size_t min_elems )
    {
        np::dtype dtype = np::dtype::get_builtin< int32_t >();
        p::tuple shape = p::make_tuple( m_rows );

        np::ndarray rnd = np::zeros( shape, dtype );

        m_dbs->predict( eps, min_elems );

        const DBSCAN_VP::Labels& labels = m_dbs->get_labels();

        for ( size_t i = 0; i < labels.size(); ++i ) {
            rnd[i] = labels[i];
        }

        return rnd;
    }

    const np::ndarray predict_eps( size_t k )
    {
        np::dtype dtype = np::dtype::get_builtin< float >();
        p::tuple shape = p::make_tuple( m_rows );

        np::ndarray rnd = np::zeros( shape, dtype );

        const std::vector< double > r = m_dbs->predict_eps( k );

        for ( size_t i = 0; i < r.size(); ++i ) {
            rnd[i] = r[i];
        }
        return rnd;
    }

    const np::ndarray query_radius( const np::ndarray& in, double eps )
    {
        DBSCAN_VP::TVpTree::Ptr vpt = m_dbs->get_vp();

        size_t rows = in.shape( 0 );

        Eigen::VectorXf col_vector( rows );
        for ( size_t j = 0; j < rows; ++j ) {
            col_vector( j ) = p::extract< float >( in[j] );
        }

        DBSCAN_VP::TVpTree::TNeighborsList neigh;

        vpt->search_by_dist( col_vector, eps, neigh );

        np::dtype dtype = np::dtype::get_builtin< uint32_t >();
        p::tuple shape = p::make_tuple( neigh.size() );

        np::ndarray rnd = np::zeros( shape, dtype );

        for ( size_t j = 0; j < neigh.size(); ++j ) {
            rnd[j] = neigh[j].first;
        }

        return rnd;
    }

private:
    size_t m_rows;
    DBSCAN_VP::Ptr m_dbs;
};

class PyDBSCANgraph {
public:
    PyDBSCANgraph( const np::ndarray& d )
        : m_rows( 0 )
    {
        npDataset::Ptr np_dset = boost::make_shared< npDataset >();
        np_dset->load_ndarray( d );

        m_rows = np_dset->rows();
        m_dbs = boost::make_shared< DBSCAN_GRAPH >( np_dset );
    }

    const np::ndarray predict( float eps, size_t min_elems )
    {
        np::dtype dtype = np::dtype::get_builtin< int32_t >();
        p::tuple shape = p::make_tuple( m_rows );

        np::ndarray rnd = np::zeros( shape, dtype );

        m_dbs->fit( eps, min_elems );

        m_dbs->predict();

        const DBSCAN_GRAPH::Labels& labels = m_dbs->get_labels();

        for ( size_t i = 0; i < labels.size(); ++i ) {
            rnd[i] = labels[i];
        }

        return rnd;
    }

    const np::ndarray get_Va0()
    {
        np::dtype dtype = np::dtype::get_builtin< uint32_t >();
        p::tuple shape = p::make_tuple( m_rows );

        np::ndarray rnd = np::zeros( shape, dtype );

        const std::vector< uint32_t > r = m_dbs->get_Va0();

        for ( size_t i = 0; i < r.size(); ++i ) {
            rnd[i] = r[i];
        }
        return rnd;
    }

    const np::ndarray get_Va1()
    {
        np::dtype dtype = np::dtype::get_builtin< uint64_t >();
        p::tuple shape = p::make_tuple( m_rows );

        np::ndarray rnd = np::zeros( shape, dtype );

        const std::vector< uint64_t > r = m_dbs->get_Va1();

        for ( size_t i = 0; i < r.size(); ++i ) {
            rnd[i] = r[i];
        }
        return rnd;
    }

private:
    size_t m_rows;
    DBSCAN_GRAPH::Ptr m_dbs;
};

BOOST_PYTHON_MODULE( pydbscan )
{
    np::initialize();

    def( "gen_cluster_data", &PyDBSCAN::gen_cluster_data );
    //def( "send_stat", & python_send_stat );

    class_< PyDBSCAN >( "DBSCAN" )
        .def( "init", &PyDBSCAN::init )
        .def( "fit", &PyDBSCAN::pyfit )
        .def( "wfit", &PyDBSCAN::pywfit )
        .def( "get_labels", &PyDBSCAN::pyget_labels );

    class_< PyDBSCANvp >( "DBSCANvp", p::init< np::ndarray >() )
        .def( "predict", &PyDBSCANvp::predict )
        .def( "predict_eps", &PyDBSCANvp::predict_eps )
        .def( "query_radius", &PyDBSCANvp::query_radius );

    class_< PyDBSCANgraph >( "DBSCANgraph", p::init< np::ndarray >() )
        .def( "predict", &PyDBSCANgraph::predict )
        .def( "get_Va0", &PyDBSCANgraph::get_Va0 )
        .def( "get_Va1", &PyDBSCANgraph::get_Va1 );

    to_python_converter< DBSCAN::ClusterData, ublas_matrix_to_python, false >();
}


//---------------------------------------------------------------------------//
//!
//! \file   Utility_InterpolationPolicy_def.hpp
//! \author Alex Robinson
//! \brief  Intrepolation policy struct definitions
//!
//---------------------------------------------------------------------------//

#ifndef UTILITY_INTERPOLATION_POLICY_DEF_HPP
#define UTILITY_INTERPOLATION_POLICY_DEF_HPP

// Std Lib Includes
#include <cmath>

// Boost Includes
#include <boost/mpl/or.hpp>

// Trilinos Includes
#include <Teuchos_ScalarTraits.hpp>

// FRENSIE Includes
#include "Utility_ContractException.hpp"

namespace Utility{

// Interpolate between two points
template<typename IndepType, typename DepType>
inline DepType LogLog::interpolate( const IndepType indep_var_0,
				    const IndepType indep_var_1,
				    const IndepType indep_var,
				    const DepType dep_var_0,
				    const DepType dep_var_1 )
{
  // The IndepType must be a floating point type
  testStaticPrecondition( (QuantityTraits<IndepType>::is_floating_point::value) );
  testStaticPrecondition( (QuantityTraits<DepType>::is_floating_point::value) );
  // Make sure the independent variables are valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_0 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_1 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LogLog::isIndepVarInValidRange( indep_var_0 ) );
  testPrecondition( LogLog::isIndepVarInValidRange( indep_var_1 ) );
  testPrecondition( LogLog::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var_0 < indep_var_1 );
  testPrecondition( indep_var >= indep_var_0 );
  testPrecondition( indep_var <= indep_var_1 );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_1 ) );
  testPrecondition( LogLog::isDepVarInValidRange( dep_var_0 ) );
  testPrecondition( LogLog::isDepVarInValidRange( dep_var_1 ) );

  return dep_var_0*
    pow((dep_var_1/dep_var_0),
	log(indep_var/indep_var_0)/log(indep_var_1/indep_var_0));
}

// Interpolate between two processed point
template<typename T>
inline T LogLog::interpolate( const T processed_indep_var_0,
			      const T processed_indep_var,
			      const T processed_dep_var_0,
			      const T processed_slope )
{
  // T must be a floating point type
  testStaticPrecondition( (boost::is_floating_point<T>::value) );
  // Make sure the processed independent variables are valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						     processed_indep_var_0 ) );
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						       processed_indep_var ) );
  testPrecondition( processed_indep_var_0 <= processed_indep_var );
  // Make sure the processed dependent variable is valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						       processed_dep_var_0 ) );
  // Make sure that the slope is valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf( processed_slope ) );

  return exp( processed_dep_var_0 +
	      processed_slope*(processed_indep_var - processed_indep_var_0) );
}

// Interpolate between two points and return the processed value
template<typename IndepType, typename DepType>
inline typename QuantityTraits<DepType>::RawType
LogLog::interpolateAndProcess( const IndepType indep_var_0,
			       const IndepType indep_var_1,
			       const IndepType indep_var,
			       const DepType dep_var_0,
			       const DepType dep_var_1 )
{
  // T must be a floating point type
  testStaticPrecondition( (QuantityTraits<IndepType>::is_floating_point::value) );
  testStaticPrecondition( (QuantityTraits<DepType>::is_floating_point::value) );
  // Make sure the independent variables are valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_0 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_1 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LogLog::isIndepVarInValidRange( indep_var_0 ) );
  testPrecondition( LogLog::isIndepVarInValidRange( indep_var_1 ) );
  testPrecondition( LogLog::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var_0 < indep_var_1 );
  testPrecondition( indep_var >= indep_var_0 );
  testPrecondition( indep_var <= indep_var_1 );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_1 ) );
  testPrecondition( LogLog::isDepVarInValidRange( dep_var_0 ) );
  testPrecondition( LogLog::isDepVarInValidRange( dep_var_1 ) );

  return log( getRawQuantity(dep_var_0) ) + log ( dep_var_1/dep_var_0 )*
    log( indep_var/indep_var_0 )/log( indep_var_1/indep_var_0 );
}

// Interpolate between two processed points and return the processed value
template<typename T>
inline T LogLog::interpolateAndProcess( const T processed_indep_var_0,
					const T processed_indep_var,
					const T processed_dep_var_0,
					const T processed_slope )
{
  // T must be a floating point type
  testStaticPrecondition( (boost::is_floating_point<T>::value) );
  // Make sure the processed independent variables are valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						     processed_indep_var_0 ) );
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						       processed_indep_var ) );
  testPrecondition( processed_indep_var_0 <= processed_indep_var );
  // Make sure the processed dependent variable is valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						       processed_dep_var_0 ) );
  // Make sure that the slope is valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf( processed_slope ) );

  return processed_dep_var_0 +
    processed_slope*(processed_indep_var - processed_indep_var_0);
}

// Process the independent value
template<typename T>
inline typename QuantityTraits<T>::RawType
LogLog::processIndepVar( const T indep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LogLog::isIndepVarInValidRange( indep_var ) );

  return log( getRawQuantity(indep_var) );
}

// Process the dependent value
template<typename T>
inline typename QuantityTraits<T>::RawType
LogLog::processDepVar( const T dep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LogLog::isIndepVarInValidRange( dep_var ) );

  return log( getRawQuantity(dep_var) );
}

// Recover the processed independent value
template<typename T>
inline T LogLog::recoverProcessedIndepVar( const T processed_indep_var )
{
  return exp( processed_indep_var );
}

// Recover the processed dependent value
template<typename T>
inline T LogLog::recoverProcessedDepVar( const T processed_dep_var )
{
  return exp( processed_dep_var );
}

// Test if the independent value is in a valid range (doesn't check nan/inf)
template<typename T>
inline bool LogLog::isIndepVarInValidRange( const T indep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( indep_var ) );

  return indep_var > QuantityTraits<T>::zero();
}

// Test if the dependent value is in a valid range (doesn't check nan/inf)
template<typename T>
inline bool LogLog::isDepVarInValidRange( const T dep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( dep_var ) );

  return dep_var > QuantityTraits<T>::zero();
}

// The name of the policy
inline const std::string LogLog::name()
{
  return "LogLog";
}

// Interpolate between two points
template<typename IndepType, typename DepType>
inline DepType LogLin::interpolate( const IndepType indep_var_0,
				    const IndepType indep_var_1,
				    const IndepType indep_var,
				    const DepType dep_var_0,
				    const DepType dep_var_1 )
{
  // T must be a floating point type
  testStaticPrecondition( (QuantityTraits<IndepType>::is_floating_point::value) );
  testStaticPrecondition( (QuantityTraits<DepType>::is_floating_point::value) );
  // Make sure the independent variables are valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_0 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_1 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LogLin::isIndepVarInValidRange( indep_var_0 ) );
  testPrecondition( LogLin::isIndepVarInValidRange( indep_var_1 ) );
  testPrecondition( LogLin::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var_0 < indep_var_1 );
  testPrecondition( indep_var >= indep_var_0 );
  testPrecondition( indep_var <= indep_var_1 );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_1 ) );
  testPrecondition( LogLin::isDepVarInValidRange( dep_var_0 ) );
  testPrecondition( LogLin::isDepVarInValidRange( dep_var_1 ) );

  return dep_var_0*pow((dep_var_1/dep_var_0), (indep_var-indep_var_0)/(indep_var_1-indep_var_0));
}

// Interpolate between two processed points
template<typename T>
inline T LogLin::interpolate( const T processed_indep_var_0,
			      const T processed_indep_var,
			      const T processed_dep_var_0,
			      const T processed_slope )
{
  // T must be a floating point type
  testStaticPrecondition( (boost::is_floating_point<T>::value) );
  // Make sure the processed independent variables are valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						     processed_indep_var_0 ) );
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						       processed_indep_var ) );
  testPrecondition( processed_indep_var_0 <= processed_indep_var );
  // Make sure the processed dependent variable is valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						       processed_dep_var_0 ) );
  // Make sure that the slope is valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf( processed_slope ) );

  return exp( processed_dep_var_0 +
	      processed_slope*(processed_indep_var - processed_indep_var_0) );
}

// Interpolate between two points and return the processed value
template<typename IndepType, typename DepType>
inline typename QuantityTraits<DepType>::RawType
LogLin::interpolateAndProcess( const IndepType indep_var_0,
			       const IndepType indep_var_1,
			       const IndepType indep_var,
			       const DepType dep_var_0,
			       const DepType dep_var_1 )
{
  // T must be a floating point type
  testStaticPrecondition( (QuantityTraits<IndepType>::is_floating_point::value) );
  testStaticPrecondition( (QuantityTraits<DepType>::is_floating_point::value) );
  // Make sure the independent variables are valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_0 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_1 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LogLin::isIndepVarInValidRange( indep_var_0 ) );
  testPrecondition( LogLin::isIndepVarInValidRange( indep_var_1 ) );
  testPrecondition( LogLin::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var_0 < indep_var_1 );
  testPrecondition( indep_var >= indep_var_0 );
  testPrecondition( indep_var <= indep_var_1 );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_1 ) );
  testPrecondition( LogLin::isDepVarInValidRange( dep_var_0 ) );
  testPrecondition( LogLin::isDepVarInValidRange( dep_var_1 ) );

  return log( getRawQuantity(dep_var_0) ) + log( dep_var_1/dep_var_0 )*
    (indep_var-indep_var_0)/(indep_var_1-indep_var_0);
}

// Interpolate between two processed points and return the processed value
template<typename T>
inline T LogLin::interpolateAndProcess( const T processed_indep_var_0,
					const T processed_indep_var,
					const T processed_dep_var_0,
					const T processed_slope )
{
  // T must be a floating point type
  testStaticPrecondition( (boost::is_floating_point<T>::value) );
  // Make sure the processed independent variables are valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						     processed_indep_var_0 ) );
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						       processed_indep_var ) );
  testPrecondition( processed_indep_var_0 <= processed_indep_var );
  // Make sure the processed dependent variable is valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						       processed_dep_var_0 ) );
  // Make sure that the slope is valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf( processed_slope ) );

  return processed_dep_var_0 +
    processed_slope*(processed_indep_var - processed_indep_var_0);
}

// Process the independent value
template<typename T>
inline typename QuantityTraits<T>::RawType
LogLin::processIndepVar( const T indep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LogLin::isIndepVarInValidRange( indep_var ) );

  return getRawQuantity(indep_var);
}

// Process the dependent value
template<typename T>
inline typename QuantityTraits<T>::RawType
LogLin::processDepVar( const T dep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LogLin::isDepVarInValidRange( dep_var ) );

  return log( getRawQuantity(dep_var) );
}

// Recover the processed independent value
template<typename T>
inline T LogLin::recoverProcessedIndepVar( const T processed_indep_var )
{
  return processed_indep_var;
}

// Recover the processed dependent value
template<typename T>
inline T LogLin::recoverProcessedDepVar( const T processed_dep_var )
{
  return exp( processed_dep_var );
}

// Test if the independent value is in a valid range (doesn't check nan/inf)
template<typename T>
inline bool LogLin::isIndepVarInValidRange( const T indep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( indep_var ) );

  return true;
}

// Test if the dependent value is in a valid range (doesn't check nan/inf)
template<typename T>
inline bool LogLin::isDepVarInValidRange( const T dep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( dep_var ) );

  return dep_var > QuantityTraits<T>::zero();
}

// The name of the policy
inline const std::string LogLin::name()
{
  return "LogLin";
}

// Interpolate between two points
template<typename IndepType, typename DepType>
inline DepType LinLog::interpolate( const IndepType indep_var_0,
				    const IndepType indep_var_1,
				    const IndepType indep_var,
				    const DepType dep_var_0,
				    const DepType dep_var_1 )
{
  // T must be a floating point type
  testStaticPrecondition( (QuantityTraits<IndepType>::is_floating_point::value) );
  testStaticPrecondition( (QuantityTraits<DepType>::is_floating_point::value) );
  // Make sure the independent variables are valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_0 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_1 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LinLog::isIndepVarInValidRange( indep_var_0 ) );
  testPrecondition( LinLog::isIndepVarInValidRange( indep_var_1 ) );
  testPrecondition( LinLog::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var_0 < indep_var_1 );
  testPrecondition( indep_var >= indep_var_0 );
  testPrecondition( indep_var <= indep_var_1 );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_1 ) );
  testPrecondition( LinLog::isDepVarInValidRange( dep_var_0 ) );
  testPrecondition( LinLog::isDepVarInValidRange( dep_var_1 ) );

  DepType term_2( (dep_var_1 - dep_var_0)*log(indep_var/indep_var_0)/
		  log(indep_var_1/indep_var_0) );

  return dep_var_0 + term_2;
}

// Interpolate between two processed point
template<typename T>
inline T LinLog::interpolate( const T processed_indep_var_0,
			      const T processed_indep_var,
			      const T processed_dep_var_0,
			      const T processed_slope )
{
  // T must be a floating point type
  testStaticPrecondition( (boost::is_floating_point<T>::value) );
  // Make sure the processed independent variables are valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						     processed_indep_var_0 ) );
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						       processed_indep_var ) );
  testPrecondition( processed_indep_var_0 <= processed_indep_var );
  // Make sure the processed dependent variable is valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						       processed_dep_var_0 ) );
  // Make sure that the slope is valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf( processed_slope ) );

  return processed_dep_var_0 +
    processed_slope*(processed_indep_var - processed_indep_var_0 );
}

// Interpolate between two points and return the processed value
template<typename IndepType, typename DepType>
inline typename QuantityTraits<DepType>::RawType
LinLog::interpolateAndProcess( const IndepType indep_var_0,
			       const IndepType indep_var_1,
			       const IndepType indep_var,
			       const DepType dep_var_0,
			       const DepType dep_var_1 )
{
  return getRawQuantity( interpolate( indep_var_0,
				      indep_var_1,
				      indep_var,
				      dep_var_0,
				      dep_var_1 ) );
}

// Interpolate between two processed points and return the processed value
template<typename T>
inline T LinLog::interpolateAndProcess( const T processed_indep_var_0,
					const T processed_indep_var,
					const T processed_dep_var_0,
					const T processed_slope )
{
  return interpolate( processed_indep_var_0,
		      processed_indep_var,
		      processed_dep_var_0,
		      processed_slope );
}

// Process the independent value
template<typename T>
inline typename QuantityTraits<T>::RawType
LinLog::processIndepVar( const T indep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LinLog::isIndepVarInValidRange( indep_var ) );

  return log( getRawQuantity(indep_var) );
}

// Process the dependent value
template<typename T>
inline typename QuantityTraits<T>::RawType
LinLog::processDepVar( const T dep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LinLog::isDepVarInValidRange( dep_var ) );

  return getRawQuantity(dep_var);
}

// Recover the processed independent value
template<typename T>
inline T LinLog::recoverProcessedIndepVar( const T processed_indep_var )
{
  return exp( processed_indep_var );
}

// Recover the processed dependent value
template<typename T>
inline T LinLog::recoverProcessedDepVar( const T processed_dep_var )
{
  return processed_dep_var;
}

// Test if the independent value is in a valid range (doesn't check nan/inf)
template<typename T>
inline bool LinLog::isIndepVarInValidRange( const T indep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( indep_var ) );

  return indep_var > QuantityTraits<T>::zero();
}

// Test if the dependent value is in a valid range (doesn't check nan/inf)
template<typename T>
inline bool LinLog::isDepVarInValidRange( const T dep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( dep_var ) );

  return true;
}

// The name of the policy
inline const std::string LinLog::name()
{
  return "LinLog";
}

// Interpolate between two points
template<typename IndepType, typename DepType>
inline DepType LinLin::interpolate( const IndepType indep_var_0,
				    const IndepType indep_var_1,
				    const IndepType indep_var,
				    const DepType dep_var_0,
				    const DepType dep_var_1 )
{
  // T must be a floating point type
  testStaticPrecondition( (QuantityTraits<IndepType>::is_floating_point::value) );
  testStaticPrecondition( (QuantityTraits<DepType>::is_floating_point::value) );
  // Make sure the independent variables are valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_0 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_1 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LinLin::isIndepVarInValidRange( indep_var_0 ) );
  testPrecondition( LinLin::isIndepVarInValidRange( indep_var_1 ) );
  testPrecondition( LinLin::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var_0 < indep_var_1 );
  testPrecondition( indep_var >= indep_var_0 );
  testPrecondition( indep_var <= indep_var_1 );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_1 ) );
  testPrecondition( LinLin::isDepVarInValidRange( dep_var_0 ) );
  testPrecondition( LinLin::isDepVarInValidRange( dep_var_1 ) );

  DepType term_2( (dep_var_1 - dep_var_0)/(indep_var_1 - indep_var_0)*
		  (indep_var - indep_var_0) );

  return dep_var_0 + term_2;
}

// Interpolate between two processed point
template<typename T>
inline T LinLin::interpolate( const T processed_indep_var_0,
			      const T processed_indep_var,
			      const T processed_dep_var_0,
			      const T processed_slope )
{
  // T must be a floating point type
  testStaticPrecondition( (boost::is_floating_point<T>::value) );
  // Make sure the processed independent variables are valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						     processed_indep_var_0 ) );
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						       processed_indep_var ) );
  testPrecondition( processed_indep_var_0 <= processed_indep_var );
  // Make sure the processed dependent variable is valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(
						       processed_dep_var_0 ) );
  // Make sure the slope is valid
  testPrecondition( !Teuchos::ScalarTraits<T>::isnaninf(processed_slope));

  return processed_dep_var_0 +
    processed_slope*(processed_indep_var - processed_indep_var_0 );
}

// Interpolate between two points and return the processed value
template<typename IndepType, typename DepType>
inline typename QuantityTraits<DepType>::RawType
LinLin::interpolateAndProcess( const IndepType indep_var_0,
			       const IndepType indep_var_1,
			       const IndepType indep_var,
			       const DepType dep_var_0,
			       const DepType dep_var_1 )
{
  return getRawQuantity( interpolate( indep_var_0,
				      indep_var_1,
				      indep_var,
				      dep_var_0,
				      dep_var_1 ) );
}

// Interpolate between two processed points and return the processed value
template<typename T>
inline T LinLin::interpolateAndProcess( const T processed_indep_var_0,
					const T processed_indep_var,
					const T processed_dep_var_0,
					const T processed_slope )
{
  return interpolate( processed_indep_var_0,
		      processed_indep_var,
		      processed_dep_var_0,
		      processed_slope );
}

// Process the independent value
template<typename T>
inline typename QuantityTraits<T>::RawType
LinLin::processIndepVar( const T indep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LinLin::isIndepVarInValidRange( indep_var  ) );

  return getRawQuantity(indep_var);
}

// Process the dependent value
template<typename T>
inline typename QuantityTraits<T>::RawType
LinLin::processDepVar( const T dep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LinLin::isIndepVarInValidRange( dep_var ) );

  return getRawQuantity(dep_var);
}

// Recover the processed independent value
template<typename T>
inline T LinLin::recoverProcessedIndepVar( const T processed_indep_var )
{
  return processed_indep_var;
}

// Recover the processed dependent value
template<typename T>
inline T LinLin::recoverProcessedDepVar( const T processed_dep_var )
{
  return processed_dep_var;
}

// Test if the independent value is in a valid range (doesn't check nan/inf)
template<typename T>
inline bool LinLin::isIndepVarInValidRange( const T indep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( indep_var ) );

  return true;
}

// Test if the dependent value is in a valid range (doesn't check nan/inf)
template<typename T>
inline bool LinLin::isDepVarInValidRange( const T dep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( dep_var ) );

  return true;
}

// The name of the policy
inline const std::string LinLin::name()
{
  return "LinLin";
}

} // end Utility namespace

#endif // end UTILITY_INTERPOLATION_POLICY_DEF_HPP

//---------------------------------------------------------------------------//
// end Utility_InterpolationPolicy_def.hpp
//---------------------------------------------------------------------------//


#ifndef CC_HPP_
#define CC_HPP_

// #define DEBUG

#include "util.hpp"

#include <boost/functional/hash.hpp>

// We provide an "abstract" congruence module, i.e., no references to
// SCIP are allowed. We accomplish this via templates (surprise,
// surprise). cc_solver "drives" the abstract CC implementation, i.e.,
// notifies us when the ILP solver branches, when we have to backtrack
// and so on.

// for the data structures and algorithms, see Niewenhuis & Oliveras,
// "Fast CC & Extensions"

// #define DEBUG

namespace cc {

  using util::with_offset;

  template <typename>
  class context;

  typedef unsigned int constant;

  struct opair {

    constant c1;
    constant c2;
    llint off;

    opair(constant x1, constant x2, llint o)
      : c1(x1), c2(x2), off(o)
    {}

  };

  template <typename T>
  class callback {

  public:

    virtual void operator()(T a, T b, llint o) = 0;

  };

  template <typename T>
  class context {

  private:

    bool consistent;

    typedef with_offset<T> o_t;
    // typedef typename offset<constant>::type o_constant;
    typedef pair<constant, llint> o_constant;
    typedef pair<constant, o_constant> app;

    // log entry for a class list update
    struct update {
      constant src;
      constant dst;
      llint offset;
      unsigned int use_length;
      update(constant s, constant d, llint o,
             unsigned int l)
        : src(s),
          dst(d),
          offset(o),
          use_length(l) {}
    };

    struct equation {
      app lhs;
      o_constant rhs;
      equation(app a, o_constant c): lhs(a), rhs(c) {}
    };

    // a frame groups the updates for a single "decision level"
    struct frame {
      vector<update> updates;
      vector<app> l_updates;
      frame(): updates(), l_updates() {}
    };

    unsigned int nxt;

    typedef unordered_map<app, constant> app_map;
    typedef pair<app, constant> app_map_pair;
    app_map app_m;

    typedef unordered_map<constant, T> symbol_map;
    typedef pair<constant, T> symbol_map_pair;
    symbol_map symbol_m;

    typedef unordered_map<T, constant> symbol_rev_map;
    typedef pair<T, constant> symbol_rev_map_pair;
    symbol_rev_map symbol_rev_m;

    typedef unordered_map<app, equation> lookup_map;
    typedef pair<app, equation> lookup_map_pair;
    lookup_map lookup_m;

    vector<opair> pending;
    vector<o_constant> repr_v;
    vector<list<o_constant> > class_v;
    vector<vector<equation> > use_v;
    vector<frame> frames;

    callback<T>* cb;

    inline void enqueue_pending(constant a, constant b, llint o)
    {
      pending.push_back(opair(a, b, o));
    }

    inline void enqueue_pending(o_constant a, o_constant b)
    {
      pending.push_back(opair(a.first, b.first,
                              b.second - a.second));
    }

    void merge_classes_call_back(constant a, constant b,
                                 llint o, bool all)
    {

      typename symbol_map::iterator it_a, it_b;

      BOOST_FOREACH (o_constant c1, class_v[a]) {
        it_a = symbol_m.find(c1.first);
        if (it_a == symbol_m.end()) continue;
        BOOST_FOREACH(o_constant c2, class_v[b]) {
          it_b = symbol_m.find(c2.first);
          if (it_b == symbol_m.end()) continue;
          cb->operator()(it_a->second,
                         it_b->second,
                         o + c2.second - c1.second);
          if (!all) return;
        }
      }

    }

    inline void merge_classes(constant a, constant b, llint o,
                              bool top)
    {

      // a = b + o

      assert(a != b);
      assert(!frames.empty());
      assert(repr_v[a].first == a);
      assert(repr_v[b].first == b);
      assert(!repr_v[a].second);
      assert(!repr_v[b].second);

      list<o_constant>& c_a = class_v[a];
      list<o_constant>& c_b = class_v[b];
      int n = 0;

      // if (!top)
      //   merge_classes_call_back(a, b, o, true);

      frames.back().updates.push_back(update(a, b, o, 0));

      BOOST_FOREACH (o_constant c, c_a) {
        assert(repr_v[c.first].second == - c.second);
        repr_v[c.first] = o_constant(b, o - c.second);
        c_b.push_back(o_constant(c.first, c.second - o));
      }

      assert(repr_v[a].first == repr_v[b].first);
      assert(repr_v[a].second - repr_v[b].second == o);

      BOOST_FOREACH (equation& eq, use_v[a]) {
        app p = repr(eq.lhs);
        typename lookup_map::iterator it = lookup_m.find(p);
        if (it != lookup_m.end()) {
          const equation& eq2 = it->second;
          enqueue_pending(eq.rhs, eq2.rhs);
        } else {
          lookup_m.insert(lookup_map_pair(p, eq));
          frames.back().l_updates.push_back(p);
          use_v[b].push_back(eq);
          n++;
        }
      }

      BOOST_FOREACH (list<o_constant>& lo, class_v) {
        if (lo.empty()) continue;
#ifdef DEBUG
        cout << "{ ";
        BOOST_FOREACH (o_constant& o, lo)
          cout << "$" << o.first << " + " << o.second << ", ";
        cout << "}\n";
#endif
      }

      // when we backtrack, we will just have to erase the last cnt
      // elements of use_v[b]
      frames.back().updates.back().use_length = n;

      // we will not erase the contents of use_v[a] and class_v[a]: we
      // are allowed to do this because a will never again be the
      // representative of a class; when we backtrack, we will just
      // use the old contents

    }

    inline void propagate(constant a, constant b, llint o, bool top)
    {

      // { a = b + o }

      const o_constant& a_prime = repr_v[a];
      const o_constant& b_prime = repr_v[b];
      constant v_a = a_prime.first, v_b = b_prime.first;
      llint o_a = a_prime.second, o_b = b_prime.second;

      // { v_a + o_a = v_b + o_b + o }

      if (v_a == v_b) {
        if (o_a != o_b + o) consistent = false;
        return;
      }

      if (class_v[v_a].size() <= class_v[v_b].size())
        merge_classes(v_a, v_b, o + o_b - o_a, top);
      else
        merge_classes(v_b, v_a, o_a - o_b - o, top);

    }

    void propagate()
    {

      while (!pending.empty()) {

        opair& p = pending.back();
        constant a = p.c1;
        constant b = p.c2;
        llint o = p.off;
        pending.pop_back();

        propagate(a, b, o, false);

        if (!consistent) {
          pending.erase(pending.begin(), pending.end());
          return;
        }

        typename symbol_map::iterator
          it_a = symbol_m.find(a),
          it_b = symbol_m.find(b);

        if (it_a != symbol_m.end()) {
          assert(it_b != symbol_m.end());
          cb->operator()(it_a->second, it_b->second, o);
        }

      }

    }

    void init_constant(constant c)
    {

      assert(repr_v.size() == c);
      assert(class_v.size() == c);
      assert(use_v.size() == c);

      repr_v.push_back(o_constant(c, 0));
      class_v.push_back(list<o_constant>(1, o_constant(c, 0)));
      use_v.push_back(vector<equation>());

      assert(repr_v[c].first == c);
      assert(repr_v[c].second == 0);
      assert(class_v[c].front().first == c);
      assert(class_v[c].front().second == 0);

    }

    inline constant get_constant(T a)
    {

      typename symbol_rev_map::const_iterator it =
        symbol_rev_m.find(a);

      if (it != symbol_rev_m.end()) return it->second;

      assert(frames.empty());

      constant rval(nxt++);
      symbol_rev_m.emplace(symbol_rev_map_pair(a, rval));
      init_constant(rval);
      symbol_m.insert(symbol_map_pair(rval, a));

      return rval;

    }

    constant get_constant(constant a, const o_constant& oc, llint o)
    {

      assert(frames.empty());

      app p(a, oc);
      typename app_map::const_iterator it =  app_m.find(p);

      if (it != app_m.end()) {
        assert(!o);
        return it->second;
      }

      constant rval(nxt++);
      app_m.emplace(p, rval);
      init_constant(rval);

      equation eq(p, o_constant(rval, o));
      lookup_m.emplace(p, eq);
      use_v[a].push_back(eq);
      use_v[oc.first].push_back(eq);

      return rval;

    }

    o_constant get_o_constant(const o_t& x)
    {
      return o_constant(get_constant(x.base), x.offset);
    }

    o_constant repr(const o_constant& o)
    {
      o_constant r = repr_v[o.first];
      return o_constant(r.first, r.second + o.second);
    }

    app repr(const app& a) {
      assert(repr_v[a.first].second == 0);
      // assert(repr_v[a.first].first == a.first);
      o_constant r = repr(a.second);
      return app(repr_v[a.first].first, r);
    }

  public:

    context(callback<T>* cback)
      /* TODO: more fields need initialization */
      : consistent(true),
        nxt(0),
        app_m(),
        symbol_m(),
        symbol_rev_m(),
        lookup_m(),
        pending(),
        repr_v(),
        class_v(),
        use_v(),
        frames(),
        cb(cback) {}

    context(const context& ctx, callback<T>* cback)
      : consistent(true),
        nxt(ctx.nxt),
        app_m(ctx.app_m),
        symbol_m(ctx.symbol_m),
        symbol_rev_m(ctx.symbol_rev_m),
        lookup_m(),
        pending(),
        repr_v(),
        class_v(),
        use_v(),
        frames(),
        cb(cback)
    {

      unreachable();

      assert(ctx.pending.empty());

      for (unsigned int i = 0; i < nxt; i++)
        init_constant(i);

      // looks reasonable; double-check
      typename app_map::iterator it = app_m.begin();
      while (it != app_m.end()) {
        const app& a = it->first;
        constant c = it->second;
        equation eq(a, o_constant(c, 0));
        lookup_m.emplace(a, eq);
        use_v[a.first].push_back(eq);
        use_v[a.second.first].push_back(eq);
        it++;
      }

    }

    // just push an empty frame to stack
    void push_frame()
    {
      assert(consistent);
      frames.push_back(frame());
    }

    // undo the updates described by the top frame
    void pop_frame()
    {

      assert(!frames.empty());
      assert(pending.empty());

      vector<update>& updates = frames.back().updates;
      vector<app>& l_updates = frames.back().l_updates;

      typename vector<update>::reverse_iterator r_it =
        updates.rbegin();
      while (r_it < updates.rend()) {
        constant src = r_it->src;
        constant dst = r_it->dst;
        list<o_constant>& c_dst = class_v[dst];
        // elements of class_v[src] never left
        list<o_constant>& c_src = class_v[src];
        BOOST_FOREACH (o_constant c, c_src) {
          assert(repr_v[c.first].first == dst);
          repr_v[c.first] = o_constant(src, - c.second);
        }
        // ugly code begin
        typename list<o_constant>::iterator it = c_dst.begin();
        llint size_diff = c_dst.size() - c_src.size();
        for (int i = 0; i < size_diff; i++) it++;
        c_dst.erase(it, c_dst.end());
        use_v[dst].erase(use_v[dst].end() - r_it->use_length,
                         use_v[dst].end());
        r_it++;
      }

      BOOST_FOREACH (const app& a, l_updates)
        lookup_m.erase(a);

      consistent = true;

      frames.pop_back();

    }

    void merge(T a, T b, llint o)
    {

      if (!consistent) return;

      constant
        cnst_a = get_constant(a),
        cnst_b = get_constant(b);

#ifdef DEBUG
      cout << "merge: " << cnst_a << " and " << cnst_b
           << " with offset " << o << endl;
#endif

      const o_constant& repr_a = repr_v[cnst_a];
      const o_constant& repr_b = repr_v[cnst_b];
      if (repr_a.first == repr_b.first) {
        if (repr_a.second != repr_b.second + o)
          consistent = false;
        return;
      }

      assert(pending.empty());
      propagate(cnst_a, cnst_b, o, true);
      propagate();

    }

    void call(o_t r, T f, const vector<o_t>& v)
    {

      // r = r_v + r_c = f(v)

      const T& r_v = r.base;
      const llint& r_c = r.offset;

      assert (!v.empty());

      constant c =
        get_constant(get_constant(f), get_o_constant(v[0]),
                     v.size() == 1 ? r_c : 0);

      if (v.size() > 1) {
        typename vector<o_t>::const_iterator it = v.begin() + 1;
        while (it < v.end() - 1) {
          c = get_constant(c, get_o_constant(*it), 0);
          it++;
        }
        c = get_constant(c, get_o_constant(*it), r_c);
      }

      symbol_m.insert(symbol_map_pair(c, r_v));
      symbol_rev_m.insert(symbol_rev_map_pair(r_v, c));

    }

    bool get_consistent()
    {
      return consistent;
    }

  };

}

#undef DEBUG

#endif


////////////////////////////////////////////////////////////////////////////////
// CorotatedLinearElasticity.hh
////////////////////////////////////////////////////////////////////////////////
/*! @file
//  A general linear elastic material model with geometric nonlinearities for
//  small strain, large deformation applications.
//
//  We use a (right) polar decomposition F = RS to remove the rigid rotation
//  component R from the deformation gradient F. The resulting large deformation
//  strain is S - I, also known as the Biot strain.
*/
//  Author:  Julian Panetta (jpanetta), julian.panetta@gmail.com
//  Created:  05/19/2020 12:15:32
////////////////////////////////////////////////////////////////////////////////
#ifndef COROTATEDLINEARELASTICITY_HH
#define COROTATEDLINEARELASTICITY_HH

#include <Eigen/Dense>
#include "EnergyTraits.hh"
#include "Tensor.hh"

// Dimension-specific calculations
template<typename _Real, size_t N>
struct CRQuantities;

template<typename _Real>
struct CRQuantities<_Real, 3> {
    using GType    = Eigen::Matrix<_Real, 3, 3>;
    using IRotType = Eigen::Matrix<_Real, 3, 1>; // infinitesimal rotation representation
    using Mat      = Eigen::Matrix<_Real, 3, 3>;

    template<typename Derived>
    static GType getG(const Eigen::MatrixBase<Derived> &S) { return S.trace() * GType::Identity() - S; }

    template<typename Derived>
    static GType getGinv(const Eigen::MatrixBase<Derived> &G) { return G.inverse(); }

    // Extract a vector representing the skew symmetric part 0.5 (A - A^T)
    //     0 -c  b      [a]
    //     c  0 -a  ==> [b]
    //    -b  a  0      [c]
    // (This is the vector `w` whose cross product `w x v` equals `0.5 (A - A^T) v`.)
    template<typename Derived>
    static IRotType sk_inv(const Eigen::MatrixBase<Derived> &A) {
        return IRotType(0.5 * (A(2, 1) - A(1, 2)),
                        0.5 * (A(0, 2) - A(2, 0)),
                        0.5 * (A(1, 0) - A(0, 1)));
    }

    // B * sk(w)
    template<typename Derived>
    static Mat right_mul_sk(const Eigen::MatrixBase<Derived> &B, const IRotType &w) {
        return B.rowwise().cross(w);
    }
};

template<typename _Real>
struct CRQuantities<_Real, 2> {
    using GType    = _Real;
    using IRotType = _Real;
    using Mat      = Eigen::Matrix<_Real, 2, 2>;

    template<typename Derived>
    static GType getG(const Eigen::MatrixBase<Derived> &S) { return S.trace(); }

    static GType getGinv(_Real G) { return 1.0 / G; }

    // Extract a scalar representing the skew symmetric part 0.5 (A - A^T)
    //   0 -a
    //   a  0
    // (This scalar represents the counterclockwise infinitesimal rotation
    //  applied by `0.5 (A - A^T)`
    template<typename Derived>
    static IRotType sk_inv(const Eigen::MatrixBase<Derived> &A) {
        return 0.5 * (A(1, 0) - A(0, 1));
    }

    // B * sk(w)
    template<typename Derived>
    static Mat right_mul_sk(const Eigen::MatrixBase<Derived> &B, const IRotType &w) {
        Mat result;
        result << w * B.col(1),
                 -w * B.col(0);
        return result;
    }
};

template <typename _Real, size_t _Dimension>
struct CorotatedLinearElasticity : public Concepts::CRLinearElaticEnergy {
    using SMatrix = SymmetricMatrixValue<_Real, _Dimension>;
    static constexpr EDensityType EDType = EDensityType::FBased;

    static constexpr size_t Dimension = _Dimension;
    static constexpr size_t N         = Dimension;
    using Real = _Real;
    using Matrix  = Eigen::Matrix<_Real, _Dimension, _Dimension>;
    using ETensor = ElasticityTensor<_Real, _Dimension>;
    using CRQ     = CRQuantities<_Real, _Dimension>;

    // We can use simplified formulas if we know `elasticity_tensor` is isotropic. This is
    // specified with the `isotropic` argument.
    CorotatedLinearElasticity(const ETensor& elasticity_tensor, bool isotropic = false) :
        m_elasticity_tensor(elasticity_tensor), m_isotropic(isotropic) {
        setDeformationGradient(Matrix::Identity());
    }

    // Constructor copying material properties only, not the current deformation
    CorotatedLinearElasticity(const CorotatedLinearElasticity &other, UninitializedDeformationTag &&)
        : m_elasticity_tensor(other.m_elasticity_tensor), m_isotropic(other.m_isotropic) { }

    void setDeformationGradient(const Matrix &F, const EvalLevel elevel = EvalLevel::Full) {
        m_F = F;
        Eigen::JacobiSVD<Matrix> svd;
        svd.compute(F, Eigen::ComputeFullU | Eigen::ComputeFullV );
        m_R = svd.matrixU() * svd.matrixV().transpose();
        if (m_R.determinant() < 0) {
            Matrix W = svd.matrixV();
            W.col(svd.matrixV().cols() - 1) *= -1;
            m_R = svd.matrixU() * W.transpose();
        }
        m_S = m_R.transpose() * F;

        // Analog to infinitesimal strain for linear elasticity.
        m_biotStrain = m_S - Matrix::Identity();

        // Analog to Cauchy stress for linear elasticity.
        m_biotStress = m_elasticity_tensor.doubleContract(SMatrix(m_biotStrain)).toMatrix();

        if (elevel == EvalLevel::EnergyOnly) return;

        m_G    = CRQ::getG(m_S);
        m_Ginv = CRQ::getGinv(m_G);
        if (!m_isotropic) {
            // 2 sk(G^{-1} sk^{-1}(sigma * S)) := sk(g)
            m_g = 2 * m_Ginv * CRQ::sk_inv(m_biotStress * m_S);
            m_pk1_stress = m_R * m_biotStress - CRQ::right_mul_sk(m_R, m_g);
        }
        else {
            // If the elasticity tensor is isotropic, then sigma and S commute
            // (Biot stress and stretch factor share eigenvectors) and all
            // "rotational stress" terms involving sk(g) vanish.
            m_pk1_stress = m_R * m_biotStress;
        }
    }

    const Matrix &getDeformationGradient() const { return m_F; }

    _Real energy() const {
        return 0.5 * doubleContract(m_biotStress, m_biotStrain);
    }

    // PK1 stress
    _Real denergy(const Matrix& dF) const { return doubleContract(denergy(), dF); }

    // Asymmetric!
    Matrix denergy() const {
        return m_pk1_stress;
    }

    // Symmetric!
    Matrix PK2Stress() const { return m_F.inverse() * denergy(); }

    const Matrix &R() const { return m_R; }
    const Matrix &S() const { return m_S; }
    const Matrix &biotStress() const { return m_biotStress; }

    template<class Mat_>
    Matrix delta_R(const Mat_ &dF) const {
        typename CRQ::IRotType w = 2 * m_Ginv * CRQ::sk_inv(m_R.transpose() * dF);
        return CRQ::right_mul_sk(m_R, w);
    }

    template<class Mat_>
    Matrix delta_S(const Mat_ &dF, const Matrix &dR) const {
        return dR.transpose() * m_F + m_R.transpose() * dF;
    }

    Matrix delta_sigma(const Matrix &dS) const {
        return m_elasticity_tensor.doubleContract(SMatrix(dS)).toMatrix();
    }

    template<class Mat_>
    Matrix delta_denergy(const Mat_ &dF) const {
        Matrix dR     = delta_R(dF);
        Matrix dS     = delta_S(dF, dR);
        Matrix dsigma = delta_sigma(dS);

        Matrix result = dR * m_biotStress + m_R * dsigma;

        if (!m_isotropic) {
            typename CRQ::IRotType dg = m_Ginv * (2 * CRQ::sk_inv(dsigma * m_S + m_biotStress * dS) - CRQ::getG(dS) * m_g);
            result -= CRQ::right_mul_sk(dR, m_g) + CRQ::right_mul_sk(m_R, dg);
        }
        return result;
    }

    _Real d2energy(const Matrix &dF_lhs, const Matrix &dF_rhs) const {
        return doubleContract(delta_denergy(dF_lhs), dF_rhs);
    }

    template<class Mat_, class Mat2_>
    Matrix delta2_denergy(const Mat_ &dF_a, const Mat2_ &dF_b) const {
        typename CRQ::IRotType w_a = 2 * m_Ginv * CRQ::sk_inv(m_R.transpose() * dF_a);
        Matrix dR_a = CRQ::right_mul_sk(m_R, w_a),
               dR_b = delta_R(dF_b);
        Matrix dS_a = delta_S(dF_a, dR_a),
               dS_b = delta_S(dF_b, dR_b);
        Matrix dsigma_a = delta_sigma(dS_a);
        Matrix dsigma_b = delta_sigma(dS_b);

        Matrix d2R, d2S, d2sigma;
        d2R = CRQ::right_mul_sk(dR_b, w_a)
            + CRQ::right_mul_sk(m_R, m_Ginv * (2 * CRQ::sk_inv(dR_b.transpose() * dF_a) - CRQ::getG(dS_b) * w_a));

        d2S = d2R.transpose() * m_F + dR_a.transpose() * dF_b + dR_b.transpose() * dF_a;
        d2sigma = delta_sigma(d2S);

        Matrix result = d2R * m_biotStress + dR_a * dsigma_b + dR_b * dsigma_a + m_R * d2sigma;

        if (!m_isotropic) {
            typename CRQ::IRotType dg_a = m_Ginv * (2 * CRQ::sk_inv(dsigma_a * m_S + m_biotStress * dS_a) - CRQ::getG(dS_a) * m_g),
                                   dg_b = m_Ginv * (2 * CRQ::sk_inv(dsigma_b * m_S + m_biotStress * dS_b) - CRQ::getG(dS_b) * m_g),
                                   d2g  = m_Ginv * (2 * CRQ::sk_inv(d2sigma  * m_S + dsigma_a * dS_b + dsigma_b * dS_a + m_biotStress * d2S) - CRQ::getG(d2S) * m_g - CRQ::getG(dS_a) * dg_b - CRQ::getG(dS_b) * dg_a);

            result -= CRQ::right_mul_sk(d2R, m_g) + CRQ::right_mul_sk(dR_a, dg_b) + CRQ::right_mul_sk(dR_b, dg_a)
                    + CRQ::right_mul_sk(m_R, d2g);
        }
        return result;
    }

    bool isIsotropic() const { return m_isotropic; }

private:
    ETensor m_elasticity_tensor;
    Matrix m_F,
           m_R, m_S, // Polar decomposition
           m_biotStrain, m_biotStress, m_pk1_stress,
           m_anisotropicRotationalStress; // Skew symmetric contribution to the PK1 stress; only nonzero for anisotropic material model.
    typename CRQ::GType m_G, m_Ginv;
    typename CRQ::IRotType m_g;
    bool m_isotropic;
};

#endif /* end of include guard: COROTATEDLINEARELASTICITY_HH */


#pragma once

#include <Eigen/Dense>

namespace icarus
{
    template<typename Delay, typename RegisterBank>
    struct AK8963
    {
        explicit AK8963(RegisterBank * device);
        void initialize();
        void read();

        Eigen::Matrix<float, 3, 1> magneticField() const
        {
            return mMagneticField;
        }
    private:
        RegisterBank * mDevice;

        Eigen::Matrix<float, 3, 1> mScale;
        Eigen::Matrix<float, 3, 1> mMagneticField;
    };
}


#ifndef PYTHONIC_INCLUDE_NUMPY_TANH_HPP
#define PYTHONIC_INCLUDE_NUMPY_TANH_HPP

#include "pythonic/include/utils/functor.hpp"
#include "pythonic/include/types/ndarray.hpp"
#include "pythonic/include/utils/numpy_traits.hpp"

#include <boost/simd/function/tanh.hpp>

namespace pythonic
{

  namespace numpy
  {
#define NUMPY_NARY_FUNC_NAME tanh
#define NUMPY_NARY_FUNC_SYM boost::simd::tanh
#include "pythonic/include/types/numpy_nary_expr.hpp"
  }
}

#endif


#include <bits/stdc++.h>
#include <armadillo>

using namespace std;
class LogisticRegression{
    public:
    int number_of_variables;
    arma::Col<double> weights;

    LogisticRegression(int num);

    void train(arma::mat X_train, arma::mat y_train, float alpha, int epochs);

    arma::mat predict(arma::mat X_predict);
};