#include <CGAL/Surface_mesh_segmentation/internal/Expectation_maximization.h>

#include <boost/random.hpp>
#include <boost/random/normal_distribution.hpp>
/**
 * Generates sample points using a few gauissians.
 * Then applies gmm fitting on these generated points.
 * Provides a heuristic score for each gmm fitting result.
 *
 */
int main(void)
{
    boost::mt19937 engine;
    engine.seed(1340818006);

    // generate random data using gauissians below
    std::vector< boost::normal_distribution<double> > distributions;
    distributions.push_back(boost::normal_distribution<double>(0.1, 0.05));
    distributions.push_back(boost::normal_distribution<double>(0.4, 0.1));
    distributions.push_back(boost::normal_distribution<double>(0.55, 0.05));
    distributions.push_back(boost::normal_distribution<double>(0.7, 0.1));
    distributions.push_back(boost::normal_distribution<double>(0.9, 0.05));
    distributions.push_back(boost::normal_distribution<double>(1.0, 0.05));

    std::vector<double> data;
    for(std::vector< boost::normal_distribution<double> >::iterator it = distributions.begin();
      it != distributions.end(); ++it)
    {
        boost::variate_generator<boost::mt19937&, boost::normal_distribution<double> > var_nor(engine, *it);

        for(std::size_t i = 0; i < 300; ++i) { data.push_back(var_nor()); }
    }

    // calculate closest center (using above gauissians) for each generated points
    // we will compare it with gmm fitting results
    // also we might want to compute mixing coef for each center and select centers according to mixing_coef * prob(data)
    std::vector<std::size_t> data_centers;
    for(std::vector<double>::iterator it = data.begin(); it != data.end(); ++it)
    {
        std::size_t center_id = (std::numeric_limits<std::size_t>::max)(), center_counter = 0;;
        double min_distance = (std::numeric_limits<double>::max)();
        for(std::vector< boost::normal_distribution<double> >::iterator dis_it = distributions.begin();
          dis_it != distributions.end(); ++dis_it, ++center_counter)
        {
            double distance = std::abs(*it - dis_it->mean());
            if(min_distance > distance)
            {
                min_distance = distance;
                center_id = center_counter;
            }
        }
        data_centers.push_back(center_id);
    }

    // apply gmm fitting clustering
    typedef CGAL::internal::Expectation_maximization E_M;
    std::vector<E_M> gmm_fitters;
    gmm_fitters.push_back(E_M(distributions.size(), data, E_M::PLUS_INITIALIZATION));
    gmm_fitters.push_back(E_M(distributions.size(), data, E_M::RANDOM_INITIALIZATION));
    gmm_fitters.push_back(E_M(distributions.size(), data, E_M::K_MEANS_INITIALIZATION));

    std::vector< std::vector<std::size_t> > calculated_centers(gmm_fitters.size());
    std::vector< std::vector<std::size_t> >::iterator calc_centers_it = calculated_centers.begin();
    for(std::vector<E_M>::iterator it = gmm_fitters.begin(); it != gmm_fitters.end(); ++it, ++calc_centers_it)
    {
        it->fill_with_center_ids(*calc_centers_it);
    }

    std::cout << "Compare results of EM with 'expected' (but be aware, it is not optimal result in terms of likelihood)" << std::endl;
    std::cout << "Another words a clustering which has higher likelihood can result in worse score in here" << std::endl;
    for(std::vector< std::vector<std::size_t> >::iterator calc_centers_it = calculated_centers.begin();
        calc_centers_it != calculated_centers.end(); ++calc_centers_it)
    {
        std::size_t true_count = 0;
        std::vector<std::size_t>::iterator calculated_it = calc_centers_it->begin();
        for(std::vector<std::size_t>::iterator it = data_centers.begin(); it != data_centers.end(); ++it, ++calculated_it)
        {
            if( (*it) == (*calculated_it) ) { ++true_count; }
        }
        double app_fit = static_cast<double>(true_count) / data_centers.size();
        std::cout << "[0,1]: " << app_fit << std::endl;
        if(app_fit < 0.7) {
            std::cerr << "There might be a problem if above printed comparison is too low." << std::endl;
            return EXIT_FAILURE;
        }
    }
}


#include <vector>
#include <type_traits>
#include <iterator>

#include <Eigen/Core>
#include <iostream>

#include "utils.hpp"

namespace tridiagonal {
    using std::vector;
    using Eigen::MatrixBase;

    namespace internal {

        // #####################################################################################
        //
        //                                   SOLVER
        //
        // #####################################################################################

        template <typename IterA, typename IterB, typename IterC, typename IterD,
                typename A = typename std::iterator_traits<IterA>::value_type,
                typename B = typename std::iterator_traits<IterB>::value_type,
                typename C = typename std::iterator_traits<IterC>::value_type,
                typename D = typename std::iterator_traits<IterD>::value_type,
                typename std::enable_if<std::is_base_of<MatrixBase<A>, A>::value, int>::type = 0,
                typename std::enable_if<std::is_base_of<MatrixBase<B>, B>::value, int>::type = 0,
                typename std::enable_if<std::is_base_of<MatrixBase<C>, C>::value, int>::type = 0,
                typename std::enable_if<std::is_base_of<MatrixBase<D>, D>::value, int>::type = 0
        >
        vector<D> solve_off_tridiagonal(IterA lower_diagonal_begin, IterA lower_diagonal_end,
                                        IterB diagonal_begin, IterB diagonal_end,
                                        IterC upper_diagonal_begin, IterC upper_diagonal_end,
                                        IterD rhs_begin, IterD rhs_end){

            long n = std::distance(diagonal_begin, diagonal_end);

            if(n == 1){
                //
                //  | B_0 | | x_0 | = | D_0 |
                //
                D x = (*diagonal_begin).fullPivHouseholderQr().solve(*rhs_begin);
                return {x};
            }

            A const & a0 = *lower_diagonal_begin;
            B const & b0 = *diagonal_begin;
            C const & c0 = *upper_diagonal_begin;
            D const & d0 = *rhs_begin;

            auto b0inv_c0 = b0.inverse() * c0;
            auto b0inv_d0 = b0.inverse() * d0;

            ++lower_diagonal_begin; ++diagonal_begin; ++upper_diagonal_begin; ++rhs_begin;

            if(n == 2){
                //
                //  | B_0 C_0 | | x_0 | = | D_0 |
                //  | A_0 B_1 | | x_1 | = | D_1 |
                //

                // B_1' = B_1 - A_0 * B_0.inv() * C_0
                *diagonal_begin = *diagonal_begin - a0 * b0inv_c0;

                // D_1' = D_1 - A_0 * B_0.inv() * D_0
                *rhs_begin = *rhs_begin - a0 * b0inv_d0;

                vector<D> x_list = solve_off_tridiagonal(lower_diagonal_begin, lower_diagonal_end,
                                                         diagonal_begin, diagonal_end,
                                                         upper_diagonal_begin, upper_diagonal_end,
                                                         rhs_begin, rhs_end);

                D x = b0inv_d0 - b0inv_c0 * x_list[0];

                x_list.insert(x_list.begin(), x);

                return x_list;
            }

            --lower_diagonal_end; --diagonal_end; --upper_diagonal_end; --rhs_end;
            auto b0inv_a0 = b0.inverse() * a0;

            // B_1' = B_1 - A_1 * B_0.inv() * C_0
            *diagonal_begin = *diagonal_begin - *lower_diagonal_begin * b0inv_c0;

            // B_{n-1}' = B_{n-1} - C_{n-1} * B_0.inv() * A_0
            *diagonal_end = *diagonal_end - *upper_diagonal_end * b0inv_a0;

            // D_1' = D_1 - A_1 * B_0.inv() * D_0
            *rhs_begin = *rhs_begin - *lower_diagonal_begin * b0inv_d0;

            // D_{n-1}' = D_{n-1} - C_{n-1} * B_0.inv() * D_0
            *rhs_end = *rhs_end - *upper_diagonal_end * b0inv_d0;

            if(n == 3){
                //
                //  | B_0 C_0 A_0 | | x_0 | = | D_0 |
                //  | A_1 B_1 C_1 | | x_1 | = | D_1 |
                //  | C_2 A_2 B_2 | | x_2 | = | D_2 |
                //

                // C_1' = C_1 - A_1 * B_0.inv() * A_0
                *upper_diagonal_begin = *upper_diagonal_begin - *lower_diagonal_begin * b0inv_a0;

                // A_2' = A_2 - C_2 * B_0.inv() * C_0
                *lower_diagonal_end = *lower_diagonal_end - *upper_diagonal_end * b0inv_c0;

                ++lower_diagonal_begin; --upper_diagonal_end;
            }
            else{
                // A_1' = - A_1 * B_0.inv() * A_0
                *lower_diagonal_begin = -1 * *lower_diagonal_begin * b0inv_a0;

                // C_{n-1}' = - C_{n-1} * B_0*inv() * C_0
                *upper_diagonal_end = -1 * *upper_diagonal_end * b0inv_c0;
            }

            ++lower_diagonal_end; ++diagonal_end; ++upper_diagonal_end; ++rhs_end;
            vector<D> x_list = solve_off_tridiagonal(lower_diagonal_begin, lower_diagonal_end,
                                                     diagonal_begin, diagonal_end,
                                                     upper_diagonal_begin, upper_diagonal_end,
                                                     rhs_begin, rhs_end);

            // X_0 = B_0.inv() * (D_0 - C_0 * X_1 - A_0 * X_{n-1})
            D x = b0inv_d0 - b0inv_c0 * x_list[0] - b0inv_a0 * x_list[x_list.size() - 1];

            x_list.insert(x_list.begin(), x);

            return x_list;
        }

        // #####################################################################################
        //
        //                                  INPUT CHECKING
        //
        // #####################################################################################

        template <typename A, typename B, typename C, typename D,
                typename std::enable_if<std::is_base_of<MatrixBase<A>, A>::value, int>::type = 0,
                typename std::enable_if<std::is_base_of<MatrixBase<B>, B>::value, int>::type = 0,
                typename std::enable_if<std::is_base_of<MatrixBase<C>, C>::value, int>::type = 0,
                typename std::enable_if<std::is_base_of<MatrixBase<D>, D>::value, int>::type = 0
        >
        void check_valid_off_tridiagonal_number_of_matrices(vector<A> const &lower_diagonal,
                                                            vector<B> const &diagonal,
                                                            vector<C> const &upper_diagonal,
                                                            vector<D> const &rhs) {

            if(diagonal.size() != rhs.size()){
                throw std::invalid_argument(invalid_number_of_elements_message(lower_diagonal,
                                                                               diagonal,
                                                                               upper_diagonal,
                                                                               rhs));
            }

            if(diagonal.empty()){
                throw std::invalid_argument(invalid_number_of_elements_message(lower_diagonal,
                                                                               diagonal,
                                                                               upper_diagonal,
                                                                               rhs));
            }
            else if(diagonal.size() == 1){
                if(lower_diagonal.empty() and !upper_diagonal.empty()) {
                    throw std::invalid_argument(invalid_number_of_elements_message(lower_diagonal,
                                                                                diagonal,
                                                                                upper_diagonal,
                                                                                rhs));
                }
            }
            else if(diagonal.size() == 2){
                if(lower_diagonal.size() != 1 and upper_diagonal.size() != 1) {
                    throw std::invalid_argument(invalid_number_of_elements_message(lower_diagonal,
                                                                                diagonal,
                                                                                upper_diagonal,
                                                                                rhs));
                }
            }
            else if(diagonal.size() != lower_diagonal.size() or diagonal.size() != upper_diagonal.size()){
                throw std::invalid_argument(invalid_number_of_elements_message(lower_diagonal,
                                                                               diagonal,
                                                                               upper_diagonal,
                                                                               rhs));
            }
        }

        template <typename A, typename B, typename C, typename D,
                typename std::enable_if<std::is_base_of<MatrixBase<A>, A>::value, int>::type = 0,
                typename std::enable_if<std::is_base_of<MatrixBase<B>, B>::value, int>::type = 0,
                typename std::enable_if<std::is_base_of<MatrixBase<C>, C>::value, int>::type = 0,
                typename std::enable_if<std::is_base_of<MatrixBase<D>, D>::value, int>::type = 0
        >
        void check_valid_off_tridiagonal_matrix_dimensions(vector<A> const &lower_diagonal,
                                                           vector<B> const &diagonal,
                                                           vector<C> const &upper_diagonal,
                                                           vector<D> const &rhs) {
            unsigned long n = diagonal.size();

            if (n == 1){
                if (diagonal[0].rows() != rhs[0].rows()){
                    throw std::invalid_argument("Invalid matrix dimensions");
                }
            }
            else if(n == 2){
                // upper left corner
                if(diagonal[0].cols() != lower_diagonal[0].cols() or
                   diagonal[0].rows() != upper_diagonal[0].rows() or
                   diagonal[0].rows() != rhs[0].rows()) {
                    throw std::invalid_argument("Invalid matrix dimensions");
                }

                // bottom right corner
                if(diagonal[1].cols() != upper_diagonal[0].cols() or
                   diagonal[1].rows() != lower_diagonal[0].rows() or
                   diagonal[1].rows() != rhs[1].rows()){
                    throw std::invalid_argument("Invalid matrix dimensions");
                }
            }
            else{
                // upper left corner
                if(diagonal[0].cols() != lower_diagonal[1].cols() or
                   diagonal[0].cols() != upper_diagonal[n-1].cols() or
                   diagonal[0].rows() != upper_diagonal[0].rows() or
                   diagonal[0].rows() != lower_diagonal[0].rows() or
                   diagonal[0].rows() != rhs[0].rows()) {
                    throw std::invalid_argument("Invalid matrix dimensions");
                }

                // middle
                for(int i = 1; i < diagonal.size()-1; i++){
                    if(diagonal[i].cols() != upper_diagonal[i-1].cols() or
                       diagonal[i].cols() != lower_diagonal[i+1].cols() or
                       diagonal[i].rows() != upper_diagonal[i].rows() or
                       diagonal[i].rows() != lower_diagonal[i].rows() or
                       diagonal[i].rows() != rhs[i].rows()){
                        throw std::invalid_argument("Invalid matrix dimensions");
                    }
                }

                // bottom right corner
                if(diagonal[n-1].cols() != upper_diagonal[n-2].cols() or
                   diagonal[n-1].cols() != lower_diagonal[0].cols() or
                   diagonal[n-1].rows() != lower_diagonal[n-1].rows() or
                   diagonal[n-1].rows() != upper_diagonal[n-1].rows() or
                   diagonal[n-1].rows() != rhs[n-1].rows()){
                    throw std::invalid_argument("Invalid matrix dimensions");
                }
            }

            // check that all diagonal matrices are square
            for (auto const & d : diagonal){
                if (d.cols() != d.rows()){
                    throw std::invalid_argument("All diagonal matrices must be square");
                }
            }

            // check that all rhs matrices have same number of columns
            long col_size = rhs[0].cols();
            for (auto const & r : rhs) {
                if (r.cols() != col_size){
                    throw std::invalid_argument("All right-hand-side matrices must have the same number of columns.");
                }
            }
        }

    }

    // #####################################################################################
    //
    //                                INTERFACE FUNCTIONS
    //
    // #####################################################################################

    template <typename A, typename B, typename C, typename D,
            typename std::enable_if<!is_dynamic<A>::value, int>::type,
            typename std::enable_if<!is_dynamic<B>::value, int>::type,
            typename std::enable_if<!is_dynamic<C>::value, int>::type,
            typename std::enable_if<!is_dynamic<D>::value, int>::type,
            typename std::enable_if<A::RowsAtCompileTime == A::ColsAtCompileTime, int>::type,
            typename std::enable_if<A::RowsAtCompileTime == B::RowsAtCompileTime, int>::type,
            typename std::enable_if<A::ColsAtCompileTime == B::ColsAtCompileTime, int>::type,
            typename std::enable_if<A::RowsAtCompileTime == C::RowsAtCompileTime, int>::type,
            typename std::enable_if<A::ColsAtCompileTime == C::ColsAtCompileTime, int>::type,
            typename std::enable_if<A::RowsAtCompileTime == D::RowsAtCompileTime, int>::type
    >
    vector<D> solve_off_tridiagonal(vector<A> const & lower_diagonal,
                                    vector<B> const & diagonal,
                                    vector<C> const & upper_diagonal,
                                    vector<D> const & rhs) {

        internal::check_valid_off_tridiagonal_number_of_matrices(lower_diagonal, diagonal, upper_diagonal, rhs);

        vector<A> _lower_diagonal = lower_diagonal;
        vector<B> _diagonal = diagonal;
        vector<C> _upper_diagonal = upper_diagonal;
        vector<D> _rhs = rhs;

        return internal::solve_off_tridiagonal(_lower_diagonal.begin(), _lower_diagonal.end(),
                                               _diagonal.begin(), _diagonal.end(),
                                               _upper_diagonal.begin(), _upper_diagonal.end(),
                                               _rhs.begin(), _rhs.end());
    }

    template <typename A, typename B, typename C, typename D,
            typename std::enable_if<is_dynamic<A>::value or
                                    is_dynamic<B>::value or
                                    is_dynamic<C>::value or
                                    is_dynamic<D>::value, int>::type
    >
    vector<D> solve_off_tridiagonal(vector<A> const & lower_diagonal,
                                    vector<B> const & diagonal,
                                    vector<C> const & upper_diagonal,
                                    vector<D> const & rhs) {

        internal::check_valid_off_tridiagonal_number_of_matrices(lower_diagonal, diagonal, upper_diagonal, rhs);
        internal::check_valid_off_tridiagonal_matrix_dimensions(lower_diagonal, diagonal, upper_diagonal, rhs);

        vector<A> _lower_diagonal = lower_diagonal;
        vector<B> _diagonal = diagonal;
        vector<C> _upper_diagonal = upper_diagonal;
        vector<D> _rhs = rhs;

        return internal::solve_off_tridiagonal(_lower_diagonal.begin(), _lower_diagonal.end(),
                                               _diagonal.begin(), _diagonal.end(),
                                               _upper_diagonal.begin(), _upper_diagonal.end(),
                                               _rhs.begin(), _rhs.end());
    }

}


// Copyright Louis Dionne 2013-2016
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/integral_c.hpp>
#include <boost/mpl/minus.hpp>
#include <boost/mpl/multiplies.hpp>
#include <boost/mpl/pair.hpp>
#include <boost/mpl/plus.hpp>

#include <boost/hana/assert.hpp>
#include <boost/hana/concept/constant.hpp>
#include <boost/hana/equal.hpp>
#include <boost/hana/integral_constant.hpp>
#include <boost/hana/minus.hpp>
#include <boost/hana/mult.hpp>
#include <boost/hana/pair.hpp>
#include <boost/hana/plus.hpp>

#include <type_traits>
namespace hana = boost::hana;

template <typename T, typename = std::enable_if_t<
  !hana::Constant<T>::value
>>
constexpr T sqrt(T x) {
  T inf = 0, sup = (x == 1 ? 1 : x/2);
  while (!((sup - inf) <= 1 || ((sup*sup <= x) && ((sup+1)*(sup+1) > x)))) {
    T mid = (inf + sup) / 2;
    bool take_inf = mid*mid > x ? 1 : 0;
    inf = take_inf ? inf : mid;
    sup = take_inf ? mid : sup;
  }

  return sup*sup <= x ? sup : inf;
}

template <typename T, typename = std::enable_if_t<
  hana::Constant<T>::value
>>
constexpr auto sqrt(T const&) {
  return hana::integral_c<typename T::value_type, sqrt(T::value)>;
}

namespace then {
namespace mpl = boost::mpl;

template <typename N>
struct sqrt
  : mpl::integral_c<typename N::value_type, ::sqrt(N::value)>
{ };

template <typename X, typename Y>
struct point {
  using x = X;
  using y = Y;
};

//! [distance-mpl]
template <typename P1, typename P2>
struct distance {
  using xs = typename mpl::minus<typename P1::x,
                                 typename P2::x>::type;
  using ys = typename mpl::minus<typename P1::y,
                                 typename P2::y>::type;
  using type = typename sqrt<
    typename mpl::plus<
      typename mpl::multiplies<xs, xs>::type,
      typename mpl::multiplies<ys, ys>::type
    >::type
  >::type;
};

static_assert(mpl::equal_to<
  distance<point<mpl::int_<3>, mpl::int_<5>>,
           point<mpl::int_<7>, mpl::int_<2>>>::type,
  mpl::int_<5>
>::value, "");
//! [distance-mpl]
}

namespace now {
namespace hana = boost::hana;
using namespace hana::literals;

template <typename X, typename Y>
struct _point {
  X x;
  Y y;
};
template <typename X, typename Y>
constexpr _point<X, Y> point(X x, Y y) { return {x, y}; }

//! [distance-hana]
template <typename P1, typename P2>
constexpr auto distance(P1 p1, P2 p2) {
  auto xs = p1.x - p2.x;
  auto ys = p1.y - p2.y;
  return sqrt(xs*xs + ys*ys);
}

BOOST_HANA_CONSTANT_CHECK(distance(point(3_c, 5_c), point(7_c, 2_c)) == 5_c);
//! [distance-hana]

void test() {

//! [distance-dynamic]
auto p1 = point(3, 5); // dynamic values now
auto p2 = point(7, 2); //
BOOST_HANA_RUNTIME_CHECK(distance(p1, p2) == 5); // same function works!
//! [distance-dynamic]

}
}

int main() {
  now::test();
}


/* ScaFES
 * Copyright (c) 2011-2016, ZIH, TU Dresden, Federal Republic of Germany.
 * For details, see the files COPYING and LICENSE in the base directory
 * of the package.
 */

/**
 *  @file ScaFES_Complex.hpp
 *  @brief Contains the class template Complex.
 */

#ifndef SCAFES_COMPLEX_HPP_
#define SCAFES_COMPLEX_HPP_

#include <iostream>
#include <iomanip>
#include <ios>
#include <cmath>
#include <cstdlib>
#include <type_traits>
#include <stdexcept>

#ifdef SCAFES_HAVE_BOOST
#include <boost/version.hpp>
#endif

#ifdef SCAFES_HAVE_BOOST_SERIALIZATION
namespace boost
{
namespace serialization
{
    class access;
}
}
#include <boost/serialization/version.hpp>
#if BOOST_VERSION < 105900
    #include <boost/serialization/pfto.hpp>
#endif
//#include <boost/serialization/complex.hpp>
#endif

namespace ScaFES
{
/*******************************************************************************
 ******************************************************************************/
/** \class Complex
 * @brief The class template \c Complex represents a complex number
 * of a given type \c TT (usually of type \c double).
 *
 * All important unary and binary operators are implemented resp. overloaded
 * and behave as expected. Especially, many operators work componentwise.
 */
template <typename TT> class Complex
{
public:
    /*----------------------------------------------------------------------
    | TYPE DEFINITIONS.
    ----------------------------------------------------------------------*/
    /** Re-export typename TT STL-like. */
    typedef TT value_type;

#ifdef SCAFES_HAVE_BOOST_SERIALIZATION
    /*----------------------------------------------------------------------
    | FRIEND CLASSES.
    ----------------------------------------------------------------------*/
    friend class boost::serialization::access;
#endif

    /*----------------------------------------------------------------------
    | LIFE CYCLE METHODS.
    ----------------------------------------------------------------------*/
    /** Creates the default constructor. */
    Complex<TT>();

    /** Creates a special constructor for threedimensional vectors:
     *  Both parts are initialized by given values. */
    Complex<TT>(TT const&, TT const&);

    /** Creates own constructor:
     *  \remark Only The REAL PART is initalized by the given value,
     *  the imaginary part remains ZERO! */
    Complex<TT>(TT const& re);

    /** Creates copy constructor. */
    Complex<TT>(ScaFES::Complex<TT> const& rhs);

    /** Creates the destructor. */
    ~Complex();

    /** Creates copy assignment operator using the copy-and-swap idiom. */
    ScaFES::Complex<TT>& operator=(ScaFES::Complex<TT> rhs);

    /*----------------------------------------------------------------------
    | GETTER METHODS.
    ----------------------------------------------------------------------*/
    /** Returns the real part of this complex number. */
    const TT& real() const;

    /** Returns the imaginary part of this complex number. */
    const TT& imag() const;

    /** Returns the element at position \c idx in the complex number. */
    const TT& operator[](std::size_t const& idx) const;

    /*----------------------------------------------------------------------
    | SETTER METHODS.
    ----------------------------------------------------------------------*/
    /** Returns a reference to the element at position \c idx in the complex
     * number.
     */
    TT& operator[](std::size_t const& idx);

    /*----------------------------------------------------------------------
    | COMPARISON METHODS.
    ----------------------------------------------------------------------*/
    /** Compares elementwise if this complex number is smaller
     *  than a given rhs complex number. */
    bool operator<(ScaFES::Complex<TT> const& rhs) const;

    /** Compares elementwise if this complex number is greater or equal
     *  than a a given rhs complex number. */
    bool operator>=(ScaFES::Complex<TT> const& rhs) const;

    /** Compares elementwise if this complex number is smaller or equal
     *  than a a given rhs complex number. */
    bool operator<=(ScaFES::Complex<TT> const& rhs) const;

    /** Compares elementwise if this complex number is greater than
     *  a given rhs complex number. */
    bool operator>(ScaFES::Complex<TT> const& rhs) const;

    /** Compares elementwise if this complex number is equal to a given rhs
     *  complex number. */
    bool operator==(ScaFES::Complex<TT> const& rhs) const;

    /** Compares elementwise if this complex number is unequal to
     * a given rhs complex number. */
    bool operator!=(ScaFES::Complex<TT> const& rhs) const;

    /*----------------------------------------------------------------------
    | WORK METHODS
    ----------------------------------------------------------------------*/
    /** Computes the dimension of the complex number,
     * i.e., all elements will be multiplied.
     */
    TT size() const;

    /** Returns the absolute value. */
    double fabs() const;

#ifdef SCAFES_HAVE_BOOST_SERIALIZATION
    /** Serializes this class. */
    template <class Archive>
    void serialize(Archive& ar, unsigned int const version);
#endif

    /*----------------------------------------------------------------------
    | ARITHMETIC METHODS.
    ----------------------------------------------------------------------*/
    /** Creates an assignment operator.
     * Only the REAL part will be assigned to the given scalar.
     */
    ScaFES::Complex<TT>& operator=(TT const& re);

    /** Computes the additive inverse of this complex number. */
    ScaFES::Complex<TT>& operator-();

    /** Adds this complex number to a given scalar (elementwise). */
    ScaFES::Complex<TT>& operator+=(TT const& sca);

    /** Subtracts this complex number from a given scalar (elementwise). */
    ScaFES::Complex<TT>& operator-=(TT const& sca);

    /** Multiplies this complex number by a given scalar (elementwise). */
    ScaFES::Complex<TT>& operator*=(TT const& sca);

    /** Divides this complex number by a given scalar (elementwise). */
    ScaFES::Complex<TT>& operator/=(TT const& sca);

    /** Adds a given rhs complex number to this complex number. */
    ScaFES::Complex<TT>& operator+=(ScaFES::Complex<TT> const& rhs);

    /** Subtracts a given rhs complex number from this complex number. */
    ScaFES::Complex<TT>& operator-=(ScaFES::Complex<TT> const& rhs);

    /** Multiplies this complex number by a given rhs complex number. */
    ScaFES::Complex<TT>& operator*=(ScaFES::Complex<TT> const& rhs);

    /** Divides this complex number by a given rhs complex number. */
    ScaFES::Complex<TT>& operator/=(ScaFES::Complex<TT> const& rhs);

    /*----------------------------------------------------------------------
    | FREE METHODS WHICH ARE FRIENDS OF THIS CLASS.
    ----------------------------------------------------------------------*/
    /** Prints a complex number in the way: '(re, im})'. */
    template <typename RR>
    friend std::ostream& operator<<(std::ostream& output,
                                    ScaFES::Complex<RR> const& t);

    /** Method to swap two complex numbers. */
    template <typename RR>
    friend void swap(ScaFES::Complex<RR>& first, ScaFES::Complex<RR>& second);

private:
    /*----------------------------------------------------------------------
    | MEMBER VARIABLES.
    ----------------------------------------------------------------------*/
    /** Real part of complex number. */
    TT mReal;

    /** Imaginary part of complex number. */
    TT mImag;
}; // End of class //

/*******************************************************************************
 * FREE METHODS.
 ******************************************************************************/
/** Swaps two objects of the class \c Complex. */
template <typename TT>
void swap(ScaFES::Complex<TT>& first, ScaFES::Complex<TT>& second);
/*----------------------------------------------------------------------------*/
/** Preprares writing a complex number to output. */
template <typename TT>
std::ostream& operator<<(std::ostream& output, ScaFES::Complex<TT> const& rhs);

/*******************************************************************************
 * LIFE CYCLE METHODS.
 ******************************************************************************/
template <typename TT>
inline Complex<TT>::Complex()
: mReal(static_cast<TT>(0)), mImag(static_cast<TT>(0))
{
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline Complex<TT>::Complex(TT const& re, TT const& im)
: mReal(re), mImag(im)
{
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline Complex<TT>::Complex(TT const& re)
: mReal(re), mImag(static_cast<TT>(0))
{
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline ScaFES::Complex<TT>& Complex<TT>::operator=(ScaFES::Complex<TT> rhs)
{
    swap(*this, rhs);
    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline Complex<TT>::Complex(ScaFES::Complex<TT> const& rhs)
: mReal(rhs.real()), mImag(rhs.imag())
{
}
/*----------------------------------------------------------------------------*/
template <typename TT> inline Complex<TT>::~Complex()
{
}

/*******************************************************************************
 * GETTER METHODS.
 ******************************************************************************/
template <typename TT> inline const TT& Complex<TT>::real() const
{
    return this->mReal;
}
/*----------------------------------------------------------------------------*/
template <typename TT> inline const TT& Complex<TT>::imag() const
{
    return this->mImag;
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline const TT& Complex<TT>::operator[](std::size_t const& position) const
{
    if (0 == position)
    {
        return this->mReal;
    }
    else
    {
        return this->mImag;
    }
}

/*******************************************************************************
 * SETTER METHODS.
 ******************************************************************************/
template <typename TT>
inline TT& Complex<TT>::operator[](std::size_t const& pos)
{
    return const_cast<TT&>(static_cast<const ScaFES::Complex<TT>&>(*this)[pos]);
}

/*******************************************************************************
 * WORK METHODS.
 ******************************************************************************/
template <typename TT> inline TT Complex<TT>::size() const
{
    return (this->mReal * this->mImag);
}
/*----------------------------------------------------------------------------*/
template <typename TT> inline double Complex<TT>::fabs() const
{
    return (this->mReal * this->mReal + this->mImag * this->mImag);
}
#ifdef SCAFES_HAVE_BOOST_SERIALIZATION
/*----------------------------------------------------------------------------*/
template <typename TT>
template <class Archive>
inline void Complex<TT>::serialize(Archive& ar, unsigned int const version)
{
    if (1 <= version)
    {
        ar&(this->mReal);
        ar&(this->mImag);
    }
}
#endif
/*******************************************************************************
 * ARITHMETIC METHODS.
 ******************************************************************************/
template <typename TT> inline ScaFES::Complex<TT>& Complex<TT>::operator-()
{
    this->mReal *= static_cast<TT>(-1);
    this->mImag *= static_cast<TT>(-1);
    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline ScaFES::Complex<TT>& Complex<TT>::operator=(TT const& sca)
{
    this->mReal = sca;
    this->mImag = sca;
    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline ScaFES::Complex<TT>& Complex<TT>::operator+=(TT const& sca)
{
    this->mReal += sca;
    this->mImag += sca;
    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline ScaFES::Complex<TT>& Complex<TT>::operator-=(TT const& sca)
{
    this->mReal -= sca;
    this->mImag -= sca;
    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline ScaFES::Complex<TT>& Complex<TT>::operator*=(TT const& sca)
{
    this->mReal *= sca;
    this->mImag *= sca;
    return *this;
}
/*----------------------------------------------------------------------------*/
// TODO: Throw an exception if the given scalar is near to zero.
template <typename TT>
inline ScaFES::Complex<TT>& Complex<TT>::operator/=(TT const& sca)
{
    this->mReal /= sca;
    this->mImag /= sca;
    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline ScaFES::Complex<TT>& Complex<TT>::
operator+=(ScaFES::Complex<TT> const& rhs)
{
    this->mReal += rhs.real();
    this->mImag += rhs.imag();
    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline ScaFES::Complex<TT>& Complex<TT>::
operator-=(ScaFES::Complex<TT> const& rhs)
{
    this->mReal -= rhs.real();
    this->mImag -= rhs.imag();
    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline ScaFES::Complex<TT>& Complex<TT>::
operator*=(ScaFES::Complex<TT> const& rhs)
{
    const TT tmpRe = this->mReal * rhs.real() - this->mImag * rhs.imag();
    this->mReal = tmpRe;
    const TT tmpIm = this->mReal * rhs.imag() + this->mImag * rhs.real();
    this->mImag = tmpIm;
    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline ScaFES::Complex<TT>& Complex<TT>::
operator/=(ScaFES::Complex<TT> const& rhs)
{
    const TT denom = rhs.imag() * rhs.imag() + rhs.real() * rhs.real();
    if (::fabs(denom) < 2.2e-12)
    {
        throw std::runtime_error("Denominator is very near to zero.");
    }
    const TT tmpRe =
        (this->mReal * rhs.real() + this->mImag * rhs.imag()) / denom;
    this->mReal = tmpRe;
    const TT tmpIm =
        (this->mImag * rhs.real() - this->mReal * rhs.imag()) / denom;
    this->mImag = tmpIm;
    return *this;
}

/*******************************************************************************
 * COMPARISON METHODS.
 ******************************************************************************/
template <typename TT>
inline bool Complex<TT>::operator==(ScaFES::Complex<TT> const& rhs) const
{
    bool res = true;

    if (!(::fabs(this->mReal - rhs.real()) < 2.2e-15))
    {
        res = false;
    }

    if (!(::fabs(this->mImag - rhs.imag()) < 2.2e-15))
    {
        res = false;
    }

    return res;
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline bool Complex<TT>::operator!=(ScaFES::Complex<TT> const& rhs) const
{
    return !(*this == rhs);
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline bool Complex<TT>::operator<(ScaFES::Complex<TT> const& rhs) const
{
    bool res = true;

    if (!(this->mReal < rhs.real()))
    {
        res = false;
    }

    if (!(this->mImag < rhs.imag()))
    {
        res = false;
    }

    return res;
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline bool Complex<TT>::operator<=(ScaFES::Complex<TT> const& rhs) const
{
    bool res = true;

    if ((this->mReal > rhs.real()))
    {
        res = false;
    }

    if ((this->mImag > rhs.imag()))
    {
        res = false;
    }

    return res;
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline bool Complex<TT>::operator>=(ScaFES::Complex<TT> const& rhs) const
{
    bool res = true;

    if ((this->mReal < rhs.real()))
    {
        res = false;
    }

    if ((this->mImag < rhs.imag()))
    {
        res = false;
    }

    return res;
    // return !(*this < rhs); Does not work because we are working elementwise!
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline bool Complex<TT>::operator>(ScaFES::Complex<TT> const& rhs) const
{
    bool res = true;

    if (!(mReal > rhs.real()))
    {
        res = false;
    }

    if (!(this->mImag > rhs.imag()))
    {
        res = false;
    }

    return res;
    // return !(*this <= rhs); Does not work because we are working elementwise!
}

/*******************************************************************************
 * FREE METHODS WHICH ARE FRIENDS OF THIS CLASS.
 ******************************************************************************/
template <typename TT>
inline void swap(ScaFES::Complex<TT>& first, ScaFES::Complex<TT>& second)
{
    std::swap(first.mReal, second.mReal);
    std::swap(first.mImag, second.mImag);
}
/*----------------------------------------------------------------------------*/
template <typename TT>
inline std::ostream& operator<<(std::ostream& output,
                                ScaFES::Complex<TT> const& rhs)
{
    const TT EPSILON = static_cast<TT>(2.2e-12);

    if (fabs(rhs.real()) < EPSILON)
    {
        output << "[    0,";
    }
    else
    {
        output << "[" << ::std::setw(4) << ::std::right << rhs.real() << ";";
    }

    if (fabs(rhs.imag()) < EPSILON)
    {
        output << "    0i]";
    }
    else
    {
        output << ::std::setw(4) << ::std::right << rhs.imag() << "i]";
    }

    return output;
}

} // End of namespace. //

/*******************************************************************************
 ******************************************************************************/
#ifdef SCAFES_HAVE_BOOST_SERIALIZATION
namespace boost
{
namespace serialization
{
    /** Designed to set the boost serialization version of a class template. */
    template <typename TT> struct version<ScaFES::Complex<TT>>
    {
        /** Sets the version number for serialization. */
        BOOST_STATIC_CONSTANT(unsigned long int, value = 2);
    };
}
}
#endif

#endif


// solving A * X = B
// A symmetric in packed format
// sytrf() & sytrs()

#include <cstddef>
#include <iostream>
#include <complex>
#include <boost/numeric/bindings/lapack/computational/sptrf.hpp>
#include <boost/numeric/bindings/lapack/computational/sptri.hpp>
#include <boost/numeric/bindings/lapack/computational/sptrs.hpp>
#include <boost/numeric/bindings/ublas/matrix.hpp>
#include <boost/numeric/bindings/ublas/symmetric.hpp>
#include <boost/numeric/bindings/std/vector.hpp>
#include "utils.h"

namespace ublas = boost::numeric::ublas;
namespace lapack = boost::numeric::bindings::lapack;

using std::size_t;
using std::cin;
using std::cout;
using std::endl;

typedef double real_t;
typedef std::complex<real_t> cmplx_t;

typedef ublas::matrix<real_t, ublas::column_major> m_t;
typedef ublas::matrix<cmplx_t, ublas::column_major> cm_t;

typedef
  ublas::symmetric_matrix<real_t, ublas::lower, ublas::column_major> symml_t;
typedef
  ublas::symmetric_matrix<cmplx_t, ublas::lower, ublas::column_major> csymml_t;

typedef
  ublas::symmetric_matrix<real_t, ublas::upper, ublas::column_major> symmu_t;
typedef
  ublas::symmetric_matrix<cmplx_t, ublas::upper, ublas::column_major> csymmu_t;

template <typename M>
void init_symm2 (M& m) {
  for (int i = 0; i < m.size1(); ++i)
    for (int j = i; j < m.size1(); ++j)
      m (i, j) = m (j, i) = 1 + j - i;
}

int main (int argc, char **argv) {
  size_t n = 0;
  if (argc > 1) {
    n = atoi(argv [1]);
  }

  cout << endl;

  cout << "real symmetric\n" << endl;

  if (n <= 0) {
    cout << "n -> ";
    cin >> n;
  }
  if (n < 5) n = 5;
  cout << "min n = 5" << endl << endl;
  size_t nrhs = 2;
  symml_t sal (n, n);   // symmetric matrix
  symmu_t sau (n, n);   // symmetric matrix
  m_t x (n, nrhs);
  m_t bl (n, nrhs), bu (n, nrhs);  // RHS matrices

  init_symm2 (sal);
  print_m (sal, "sal");
  cout << endl;

  init_symm2 (sau);
  print_m (sau, "sau");
  cout << endl;

  for (int i = 0; i < x.size1(); ++i) {
    x (i, 0) = 1.;
    x (i, 1) = 2.;
  }
  bl = prod (sal, x);
  bu = prod (sau, x);

  print_m (bl, "bl");
  cout << endl;
  print_m (bu, "bu");
  cout << endl;

  std::vector<fortran_int_t> ipiv (n);

  int err = lapack::sptrf (sal, ipiv);
  if (err == 0) {
    symml_t isal (sal);
    lapack::sptrs (sal, ipiv, bl);
    print_m (bl, "xl");
    lapack::sptri (isal, ipiv);
    print_m (isal, "isal");
  }
  cout << endl;

  err = lapack::sptrf (sau, ipiv);
  if (err == 0) {
    symmu_t isau (sau);
    lapack::sptrs (sau, ipiv, bu);
    print_m (bu, "xu");
    lapack::sptri (isau, ipiv);
    print_m (isau, "isau");
  }
  else
    cout << "?" << endl;
  cout << endl;

  //////////////////////////////////////////////////////////
  cout << "\n==========================================\n" << endl;
  cout << "complex symmetric\n" << endl;

  csymml_t scal (n, n);   // symmetric matrix
  csymmu_t scau (n, n);   // symmetric matrix
  cm_t cx (n, 1);
  cm_t cbl (n, 1), cbu (n, 1);  // RHS

  init_symm2 (scal);
  init_symm2 (scau);
  scal *= cmplx_t (0.1, 0.25);
  scau *= cmplx_t (-1, -0.5);

  print_m (scal, "scal");
  cout << endl;
  print_m (scau, "scau");
  cout << endl;

  for (int i = 0; i < cx.size1(); ++i)
    cx (i, 0) = cmplx_t (1, -1);
  print_m (cx, "cx");
  cout << endl;
  cbl = prod (scal, cx);
  cbu = prod (scau, cx);
  print_m (cbl, "cbl");
  cout << endl;
  print_m (cbu, "cbu");
  cout << endl;

  int ierr = lapack::sptrf (scal, ipiv);
  if (ierr == 0) {
    csymml_t iscal (scal);
    lapack::sptrs (scal, ipiv, cbl);
    print_m (cbl, "cxl");
    lapack::sptri (iscal, ipiv);
    print_m (iscal, "iscal");
  }
  else
    cout << "?" << endl;
  cout << endl;

  ierr = lapack::sptrf (scau, ipiv);
  if (ierr == 0) {
    csymmu_t iscau (scau);
    lapack::sptrs (scau, ipiv, cbu);
    print_m (cbu, "cxu");
    lapack::sptri (iscau, ipiv);
    print_m (iscau, "iscau");
  }
  else
    cout << "?" << endl;
  cout << endl;

}


#include <iostream>
#include <vector>
#include <map>
#include <numeric>

#include <Eigen/Dense>

using HouseProperties = std::map<std::string, double>;

struct House
{
    double price;

    HouseProperties properties;
};

struct Model
{
    double constant;
    std::map<std::string, double> coefs;

    Model() : constant(0.), coefs() {}
};

int main()
{
    std::cout << "Linear regression example\n";

    //selling price of houses
    //from: http://people.sc.fsu.edu/~jburkardt/datasets/regression/x26.txt
    std::vector<House> houses_data_set {
        { 25.9,{ { "bathroom",1.0 },{ "area", 3.4720 },{ "livingArea", 0.998 },{ "garage", 1.0 },{ "room",  7. },{ "bedroom", 4. },{ "age", 42. } } },
        { 29.5,{ { "bathroom",1.0 },{ "area", 3.5310 },{ "livingArea", 1.500 },{ "garage", 2.0 },{ "room",  7. },{ "bedroom", 4. },{ "age", 62. } } },
        { 27.9,{ { "bathroom",1.0 },{ "area", 2.2750 },{ "livingArea", 1.175 },{ "garage", 1.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 40. } } },
        { 25.9,{ { "bathroom",1.0 },{ "area", 4.0500 },{ "livingArea", 1.232 },{ "garage", 1.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 54. } } },
        { 29.9,{ { "bathroom",1.0 },{ "area", 4.4550 },{ "livingArea", 1.121 },{ "garage", 1.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 42. } } },
        { 29.9,{ { "bathroom",1.0 },{ "area", 4.4550 },{ "livingArea", 0.988 },{ "garage", 1.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 56. } } },
        { 30.9,{ { "bathroom",1.0 },{ "area", 5.8500 },{ "livingArea", 1.240 },{ "garage", 1.0 },{ "room",  7. },{ "bedroom", 3. },{ "age", 51. } } },
        { 28.9,{ { "bathroom",1.0 },{ "area", 9.5200 },{ "livingArea", 1.501 },{ "garage", 0.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 32. } } },
        { 84.9,{ { "bathroom",2.5 },{ "area", 9.8000 },{ "livingArea", 3.420 },{ "garage", 2.0 },{ "room", 10. },{ "bedroom", 5. },{ "age", 42. } } },
        { 82.9,{ { "bathroom",2.5 },{ "area",12.8000 },{ "livingArea", 3.000 },{ "garage", 2.0 },{ "room",  9. },{ "bedroom", 5. },{ "age", 14. } } },
        { 35.9,{ { "bathroom",1.0 },{ "area", 6.4350 },{ "livingArea", 1.225 },{ "garage", 2.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 32. } } },
        { 31.5,{ { "bathroom",1.0 },{ "area", 4.9883 },{ "livingArea", 1.552 },{ "garage", 1.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 30. } } },
        { 31.0,{ { "bathroom",1.0 },{ "area", 5.5200 },{ "livingArea", 0.975 },{ "garage", 1.0 },{ "room",  5. },{ "bedroom", 2. },{ "age", 30. } } },
        { 30.9,{ { "bathroom",1.0 },{ "area", 6.6660 },{ "livingArea", 1.121 },{ "garage", 2.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 32. } } },
        { 30.0,{ { "bathroom",1.0 },{ "area", 5.0000 },{ "livingArea", 1.020 },{ "garage", 0.0 },{ "room",  5. },{ "bedroom", 2. },{ "age", 46. } } },
        { 28.9,{ { "bathroom",1.0 },{ "area", 9.5200 },{ "livingArea", 1.501 },{ "garage", 0.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 32. } } },
        { 36.9,{ { "bathroom",1.5 },{ "area", 5.1500 },{ "livingArea", 1.664 },{ "garage", 2.0 },{ "room",  8. },{ "bedroom", 4. },{ "age", 50. } } },
        { 41.9,{ { "bathroom",1.5 },{ "area", 6.9020 },{ "livingArea", 1.488 },{ "garage", 1.5 },{ "room",  7. },{ "bedroom", 3. },{ "age", 22. } } },
        { 40.5,{ { "bathroom",1.5 },{ "area", 7.1020 },{ "livingArea", 1.376 },{ "garage", 1.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 17. } } },
        { 43.9,{ { "bathroom",1.0 },{ "area", 7.8000 },{ "livingArea", 1.500 },{ "garage", 1.5 },{ "room",  7. },{ "bedroom", 3. },{ "age", 23. } } },
        { 37.5,{ { "bathroom",1.0 },{ "area", 5.5200 },{ "livingArea", 1.256 },{ "garage", 2.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 40. } } },
        { 37.9,{ { "bathroom",1.5 },{ "area", 4.0000 },{ "livingArea", 1.690 },{ "garage", 1.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 22. } } },
        { 44.5,{ { "bathroom",1.5 },{ "area", 9.8900 },{ "livingArea", 1.820 },{ "garage", 2.0 },{ "room",  8. },{ "bedroom", 4. },{ "age", 50. } } },
        { 37.9,{ { "bathroom",1.5 },{ "area", 6.7265 },{ "livingArea", 1.652 },{ "garage", 1.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 44. } } },
        { 38.9,{ { "bathroom",1.5 },{ "area", 9.1500 },{ "livingArea", 1.777 },{ "garage", 2.0 },{ "room",  8. },{ "bedroom", 4. },{ "age", 48. } } },
        { 36.9,{ { "bathroom",1.0 },{ "area", 8.0000 },{ "livingArea", 1.504 },{ "garage", 2.0 },{ "room",  7. },{ "bedroom", 3. },{ "age",  3. } } },
        { 45.8,{ { "bathroom",1.5 },{ "area", 7.3262 },{ "livingArea", 1.831 },{ "garage", 1.5 },{ "room",  8. },{ "bedroom", 4. },{ "age", 31. } } }
        //this observation is not added to the list
        //it will be used to test the linear model
        //{ 41.0,{ { "bathroom",1.5 },{ "area", 5.0000 },{ "livingArea", 1.200 },{ "garage", 2.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 30. } } }
    };

    std::cout << houses_data_set.size() << " observations\n";

    //function to evaluate the price of a house knowing its properties and using a linear model
    const auto evaluatePrice = [&](const Model& model, const HouseProperties& house)
    {
        double price = model.constant;

        for (const auto& c : model.coefs)
        {
            auto hIt = house.find(c.first);
            if (hIt != house.end())
            {
                price += c.second * hIt->second;
            }
        }
        return price;
    };

    //function to evaluate the error of a model based on the training data
    const auto evaluateError = [&houses_data_set, &evaluatePrice](const Model& model)
    {
        double error = 0.;
        for (const auto& d : houses_data_set)
        {
            const auto p = evaluatePrice(model, d.properties);
            error += std::pow(d.price - p, 2);
        }
        return error / static_cast<double>(houses_data_set.size());
    };

    //consider a model
    Model model; //the model is empty
    model.constant = 3.; //a constant is set
    model.coefs["room"] = 1.; //a coefficient is set for the "room" property
    std::cout << "Error: " << evaluateError(model) << '\n'; //the error of the model is printed

    //definition of the predictors
    std::vector<std::string> variables{ "bathroom", "area", "livingArea", "garage", "room", "bedroom", "age" };

    //definition of the design matrix
    Eigen::MatrixXd X(houses_data_set.size(), variables.size() + 1);
    //definition of the observation vector
    Eigen::VectorXd Y(houses_data_set.size());

    int i = 0;
    for (const auto& d : houses_data_set)
    {
        int j = 0;
        //the first line of the matrix is set to 1 to compute the constant term of the linear model
        X(i, j++) = 1.;

        for (const auto& var : variables)
        {
            auto pIt = d.properties.find(var);
            if (pIt != d.properties.end())
                X(i, j) = pIt->second;
            j++;
        }

        Y(i) = d.price;
        ++i;
    }

    //solving the normal equation
    const Eigen::VectorXd B = (X.transpose() * X).ldlt().solve(X.transpose() * Y);

    i = 0;

    //convert the computation result into the model data structure
    model.constant = B(i++);
    std::cout << "Model constant: " << model.constant << '\n';
    model.coefs.clear();
    for (const auto& var : variables)
    {
        model.coefs[var] = B(i++);
        std::cout << "Model (" << var << "): " << model.coefs[var] << '\n';
    }

    //compute the mean price of the observation data
    double meanPrice = std::accumulate(std::begin(houses_data_set), std::end(houses_data_set), 0.,
        [](double a, const House& h) {return a + h.price; }) / static_cast<double>(houses_data_set.size());

    std::cout << "Mean price: " << meanPrice << '\n';

    //compute the residual error of the model considering the observation data
    double ssres = evaluateError(model);
    std::cout << "Residual: " << ssres << '\n';

    //compute the total sum of squares (compared to the mean price)
    double sstot = std::accumulate(std::begin(houses_data_set), std::end(houses_data_set), 0.,
        [&meanPrice](double a, const House& h) {return a + std::pow(h.price - meanPrice, 2); }) / static_cast<double>(houses_data_set.size());
    std::cout << "Total sum of squares: " << sstot << '\n';

    //compute the coefficient of determination
    double rSquared = 1. - ssres / sstot;
    std::cout << "Coefficient of determination R^2: " << rSquared << '\n';

    //let's consider another observation, not used to train our model
    const House anotherObservation = { 41., { { "bathroom",1.5 },{ "area", 5.0000 },{ "livingArea", 1.200 },{ "garage", 2.0 },{ "room",  6. },{ "bedroom", 3. },{ "age", 30. } } };

    //estimate its price and compare to the real price
    double estimatedPrice = evaluatePrice(model, anotherObservation.properties);
    std::cout << "Estimate price of a house: " << estimatedPrice << ", compared to " << anotherObservation.price << '\n';

    return 0;
}


/*    Copyright (c) 2010-2015, Delft University of Technology
 *    All rights reserved.
 *
 *    Redistribution and use in source and binary forms, with or without modification, are
 *    permitted provided that the following conditions are met:
 *      - Redistributions of source code must retain the above copyright notice, this list of
 *        conditions and the following disclaimer.
 *      - Redistributions in binary form must reproduce the above copyright notice, this list of
 *        conditions and the following disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *      - Neither the name of the Delft University of Technology nor the names of its contributors
 *        may be used to endorse or promote products derived from this software without specific
 *        prior written permission.
 *
 *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
 *    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *    COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *    AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 *    OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *    Changelog
 *      YYMMDD    Author            Comment
 *      121105    K. Kumar          File created from content in other files.
 *      121210    D. Dirkx          Added function implementations for class.
 *
 *    References
 *
 *    Notes
 *
 */

#include <cmath>
#include <stdexcept>

#include <boost/exception/all.hpp>

#include "Tudat/Astrodynamics/BasicAstrodynamics/stateVectorIndices.h"
#include "Tudat/Astrodynamics/Gravitation/centralJ2J3J4GravityModel.h"

namespace tudat
{
namespace gravitation
{

//! Compute gravitational acceleration due to J4.
Eigen::Vector3d computeGravitationalAccelerationDueToJ4(
        const Eigen::Vector3d& positionOfBodySubjectToAcceleration,
        const double gravitationalParameterOfBodyExertingAcceleration,
        const double equatorialRadiusOfBodyExertingAcceleration,
        const double j4CoefficientOfGravityField,
        const Eigen::Vector3d& positionOfBodyExertingAcceleration )
{
    // Set constant values reused for optimal computation of acceleration components.
    const double distanceBetweenBodies = ( positionOfBodySubjectToAcceleration
                                           - positionOfBodyExertingAcceleration ).norm( );

    const double preMultiplier = gravitationalParameterOfBodyExertingAcceleration
            / std::pow( distanceBetweenBodies, 6.0 ) * 4.375 * j4CoefficientOfGravityField
            * std::pow( equatorialRadiusOfBodyExertingAcceleration, 4.0 );

    const double scaledZCoordinate = ( positionOfBodySubjectToAcceleration.z( )
                                       - positionOfBodyExertingAcceleration.z( ) )
            / distanceBetweenBodies;

    const double scaledZCoordinateSquared = scaledZCoordinate * scaledZCoordinate;

    const double scaledZCoordinateToPower4 = scaledZCoordinateSquared * scaledZCoordinateSquared;

    const double factorForXAndYDirections = ( 3.0 / 7.0 - 6.0 * scaledZCoordinateSquared
                                              + 9.0 * scaledZCoordinateToPower4 )
            / distanceBetweenBodies;

    // Compute components of acceleration due to J4-effect.
    Eigen::Vector3d gravitationalAccelerationDueToJ4 = Eigen::Vector3d::Constant( preMultiplier );

    gravitationalAccelerationDueToJ4( orbital_element_conversions::xCartesianPositionIndex )
            *= ( positionOfBodySubjectToAcceleration.x( )
                 - positionOfBodyExertingAcceleration.x( ) ) * factorForXAndYDirections;

    gravitationalAccelerationDueToJ4( orbital_element_conversions::yCartesianPositionIndex )
            *= ( positionOfBodySubjectToAcceleration.y( )
                 - positionOfBodyExertingAcceleration.y( ) ) * factorForXAndYDirections;

    gravitationalAccelerationDueToJ4( orbital_element_conversions::zCartesianPositionIndex )
            *= ( 15.0 / 7.0 - 10.0 * scaledZCoordinateSquared + 9.0 * scaledZCoordinateToPower4 )
            * scaledZCoordinate;

    return gravitationalAccelerationDueToJ4;
}

//! Compute gravitational acceleration zonal sum.
Eigen::Vector3d computeGravitationalAccelerationZonalSum(
        const Eigen::Vector3d& positionOfBodySubjectToAcceleration,
        const double gravitationalParameterOfBodyExertingAcceleration,
        const double equatorialRadiusOfBodyExertingAcceleration,
        const std::map< int, double > zonalCoefficientsOfGravityField,
        const Eigen::Vector3d& positionOfBodyExertingAcceleration )
{
    // Check that only coefficients for the gravity field are given up to J2 (i.e., size of
    // vector is 3 at max), else throw an error.
    if ( zonalCoefficientsOfGravityField.size( ) > 3 )
    {
        boost::throw_exception(
                    boost::enable_error_info(
                        std::runtime_error(
                            "Currently, accelerations can only be computed up to J4." ) ) );
    }

    // Check if position of body subject to acceleration falls within the effective radius of
    // the central body. If so, throw an error.
    if ( ( positionOfBodySubjectToAcceleration - positionOfBodyExertingAcceleration ).norm( )
         < equatorialRadiusOfBodyExertingAcceleration )
    {
        boost::throw_exception(
                    boost::enable_error_info(
                        std::runtime_error(
                            "Position of body subject to acceleration is within effective radius."
                            ) ) );
    }

    // Set gravitational acceleration sum equal to central term contribution.
    Eigen::Vector3d gravitationalAccelerationSum
            = computeGravitationalAcceleration(
                positionOfBodySubjectToAcceleration,
                gravitationalParameterOfBodyExertingAcceleration,
                positionOfBodyExertingAcceleration );

    // Add contributions from zonal terms. The switch statement checks
    for ( std::map< int, double >::const_iterator mapIterator
          = zonalCoefficientsOfGravityField.begin( );
          mapIterator != zonalCoefficientsOfGravityField.end( ); mapIterator++ )
    {
        switch ( mapIterator->first )
        {
        case 2:

            gravitationalAccelerationSum += computeGravitationalAccelerationDueToJ2(
                        positionOfBodySubjectToAcceleration,
                        gravitationalParameterOfBodyExertingAcceleration,
                        equatorialRadiusOfBodyExertingAcceleration,
                        mapIterator->second,
                        positionOfBodyExertingAcceleration );

            break;

        case 3:

            gravitationalAccelerationSum += computeGravitationalAccelerationDueToJ3(
                        positionOfBodySubjectToAcceleration,
                        gravitationalParameterOfBodyExertingAcceleration,
                        equatorialRadiusOfBodyExertingAcceleration,
                        mapIterator->second,
                        positionOfBodyExertingAcceleration );

            break;

        case 4:

            gravitationalAccelerationSum += computeGravitationalAccelerationDueToJ4(
                        positionOfBodySubjectToAcceleration,
                        gravitationalParameterOfBodyExertingAcceleration,
                        equatorialRadiusOfBodyExertingAcceleration,
                        mapIterator->second,
                        positionOfBodyExertingAcceleration );

            break;

        default:

            boost::throw_exception(
                        boost::enable_error_info(
                            std::runtime_error(
                                "Degree must be 2, 3, or 4 in current implementation." ) ) );
        };
    }

    // Return total gravitational acceleration computed.
    return gravitationalAccelerationSum;
}

//! Get gravitational acceleration.
Eigen::Vector3d CentralJ2J3J4GravitationalAccelerationModel::getAcceleration( )
{
    // Sum and return constituent acceleration terms.
    return computeGravitationalAcceleration(
                this->positionOfBodySubjectToAcceleration,
                this->gravitationalParameter,
                this->positionOfBodyExertingAcceleration )
            + computeGravitationalAccelerationDueToJ2(
                this->positionOfBodySubjectToAcceleration,
                this->gravitationalParameter,
                this->equatorialRadius,
                this->j2GravityCoefficient,
                this->positionOfBodyExertingAcceleration )
            + computeGravitationalAccelerationDueToJ3(
                this->positionOfBodySubjectToAcceleration,
                this->gravitationalParameter,
                this->equatorialRadius,
                this->j3GravityCoefficient,
                this->positionOfBodyExertingAcceleration )
            + computeGravitationalAccelerationDueToJ4(
                this->positionOfBodySubjectToAcceleration,
                this->gravitationalParameter,
                this->equatorialRadius,
                this->j4GravityCoefficient,
                this->positionOfBodyExertingAcceleration );
}

} // namespace gravitation
} // namespace tudat


//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_CONSTANT_INVLOG_10_HPP_INCLUDED
#define BOOST_SIMD_CONSTANT_INVLOG_10_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{
  /*!

    @ingroup group-constant

    Generates constant 1/log(10).

    @par Header <boost/simd/constant/invlog_10.hpp>

    @par Semantic:

    @code
    T r = Invlog_10<T>();
    @endcode

    is similar to:

    @code
    r =  T(0.4342944819032518276511289189166050822943970058036666);
    @endcode

**/
  template<typename T> T Invlog_10();

  namespace functional
  {
    /*!
      @ingroup group-callable-constant

      Generates constant 1/log(10).

      Generate the  constant invlog_10.

      @return The Invlog_10 constant for the proper type
    **/
    const boost::dispatch::functor<tag::invlog_10_> invlog_10 = {};
  }
} }
#endif

#include <boost/simd/constant/scalar/invlog_10.hpp>
#include <boost/simd/constant/simd/invlog_10.hpp>

#endif


/*! \file scfloop.cpp
    \brief SCFを行うクラスの実装

    Copyright ©  2015 @dc1394 All Rights Reserved.
    This software is released under the BSD 2-Clause License.
*/

#include "eigenvaluesearch.h"
#include "normalization.h"
#include "readinputfile.h"
#include "scfloop.h"
#include "simpson.h"
#include <iomanip>                              // for std::setw
#include <iostream>                             // for std::cout
#include <stdexcept>                            // for std::runtime_error
#include <boost/math/constants/constants.hpp>   // for boost::math::constants

namespace schrac {
    // #region コンストラクタ

    ScfLoop::ScfLoop(std::pair<std::string, bool> const & arg) :
        PData([this]{ return std::cref(pdata_); }, nullptr),
        PDiffData([this]{ return std::cref(pdiffdata_); }, nullptr),
        PEhartree([this]{ return std::cref(ehartree_); }, nullptr),
        ehartree_(std::nullopt)
    {
        ReadInputFile rif(arg);         // ファイルを読み込む
        rif.readFile();
        pdata_ = rif.PData;

        initialize();

        if (pdata_->chemical_symbol_ == Data::Chemical_Symbol[0]) {
            pdiffsolver_ = std::make_shared<DiffSolver>(pdata_, pdiffdata_);
        }
        else {
            prho_ = std::make_shared<Rho>(pdiffdata_);
            pvh_ = std::make_shared<Vhartree>(pdiffdata_->r_mesh_);
            pdiffsolver_ = std::make_shared<DiffSolver>(pdata_, pdiffdata_, prho_, pvh_);
        }

        message();
    }

    // #endregion コンストラクタ

    // #region publicメンバ関数

    void ScfLoop::message() const
    {
        std::cout << pdata_->chemical_symbol_
            << "原子の"
            << pdata_->orbital_
            << "軌道";

        if (pdata_->eq_type_ == Data::Eq_type::DIRAC && pdata_->spin_orbital_ == Data::ALPHA) {
            std::cout << "、スピン上向き";
        }
        else if (pdata_->eq_type_ == Data::Eq_type::DIRAC && pdata_->spin_orbital_ == Data::BETA) {
            std::cout << "、スピン下向き";
        }

        std::cout << "の波動関数と固有値を計算します。\n";
    }

    ScfLoop::mypair ScfLoop::operator()()
    {
        if (pdata_->chemical_symbol_ == Data::Chemical_Symbol[0]) {
            return std::make_pair(std::move(pdiffdata_), run());
        }
        else {
            return std::make_pair(std::move(pdiffdata_), scfrun());
        }
    }

    // #endregion publicメンバ関数

    // #region privateメンバ関数

    bool ScfLoop::check_converge(dvector const & newrho, std::int32_t scfloop)
    {
        auto const normrd = std::abs(req_normrd(newrho, prho_->PRho));

        req_hartree_energy(newrho, pvh_->Vhart);
        std::cout << std::setw(2) << "Iteration # "
            << scfloop
            << ": NormRD = " << normrd
            << ", Energy = " << req_energy(pdiffdata_->E_)
            << std::endl;

        return normrd < pdata_->scf_criterion_;
    }

    void ScfLoop::initialize()
    {
        pdiffdata_ = std::make_shared<DiffData>(pdata_);

        pdiffdata_->r_mesh_.reserve(pdata_->grid_num_ + 1);
        for (auto i = 0; i <= pdata_->grid_num_; i++) {
            pdiffdata_->r_mesh_.push_back(std::exp(pdata_->xmin_ + static_cast<double>(i) * pdiffdata_->dx_));
        }
    }

    void ScfLoop::make_vhartree()
    {
        pdiffsolver_->solve_poisson();
        pvh_->set_vhartree_boundary_condition(pdata_->Z_);
        pvh_->vhart_init();
    }

    void ScfLoop::req_hartree_energy(dvector const & rho, dvector const & vhartree)
    {
        dvector u2;
        u2.reserve(pdata_->grid_num_ + 1);
        for (auto i = 0; i <= pdata_->grid_num_; i++) {
            u2.push_back(sqr(pdiffdata_->r_mesh_[i]) * rho[i]);
        }

        Simpson simpson(pdiffdata_->dx_);
        ehartree_.emplace(simpson(vhartree, u2, pdiffdata_->r_mesh_, 1));
    }

    double ScfLoop::req_energy(double eigen) const
    {
        return 2.0 * eigen - *ehartree_;
    }

    double ScfLoop::req_normrd(dvector const & newrho, dvector const & oldrho) const
    {
        using namespace boost::math::constants;
        BOOST_ASSERT(newrho.size() == oldrho.size());

        dvector residual;
        residual.reserve(newrho.size());

        for (auto i = 0; i <= pdata_->grid_num_; i++) {
            residual.push_back(newrho[i] - oldrho[i]);
        }

        Simpson const simpson(pdiffdata_->dx_);
        return 4.0 * pi<double>() * simpson(residual, residual, pdiffdata_->r_mesh_, 3);
    }

    dvector ScfLoop::req_newrho(dvector const & rf) const
    {
        dvector newrho;
        newrho.reserve(pdata_->grid_num_ + 1);
        for (auto i = 0; i <= pdata_->grid_num_; i++) {
            newrho.push_back(sqr(rf[i]));
        }

        return newrho;
    }

    ScfLoop::mymap ScfLoop::run()
    {
        EigenValueSearch evs(pdata_, pdiffdata_, prho_, pvh_);

        if (!evs.search()) {
            throw std::runtime_error("固有値が見つかりませんでした。終了します。");
        }

        return nomalization(evs.PDiffSolver);
    }

    ScfLoop::mymap ScfLoop::scfrun()
    {
        auto scfloop = 1;
        ScfLoop::mymap wavefunctions;
        for (; scfloop <= pdata_->scf_maxiter_; scfloop++) {
            prho_->init();
            make_vhartree();

            EigenValueSearch evs(pdata_, pdiffdata_, prho_, pvh_);

            if (!evs.search()) {
                throw std::runtime_error("固有値が見つかりませんでした。終了します。");
            }

            wavefunctions = nomalization(evs.PDiffSolver);
            auto const newrho = req_newrho(wavefunctions.at("2 Eigen function"));
            if (check_converge(newrho, scfloop)) {
                break;
            }
            prho_->rhomix(newrho);
        }

        if (scfloop == pdata_->scf_maxiter_) {
            throw std::runtime_error("SCFが収束しませんでした。終了します。");
        }

        return wavefunctions;
    }

    // #endregion privateメンバ関数
}


#include <iostream>
#include <cmath>
#include <random>
#include <stdexcept>
#include <fstream>
#include <cstdlib>
#include <string>
#include <Eigen/Dense>
#include "aggregate_gen_CCA.hpp"
using namespace Eigen;
using namespace std;

default_random_engine re(random_device{}()); // random seed
//default_random_engine re; //default fixed seed
uniform_real_distribution<double> urf {0,1.0};

int main(int argc, char *argv[]){

    // argv[0] == program name
    // argv[1] == num_sph_SA
    // argv[2] == levels
    // argv[3] == fractal prefactor kf
    // argv[4] == fractal dimension Df
    // argv[5] == error tolerance of attached distance

    double a= {1.0}; // radius of spherule
    double tol= {1.0e-2}; // error torelance of the distance between attached spherules
    int num_sph_SA;
    int levels;
    double kf;
    double Df;
    int num_agg;

    switch (argc)
    {
    case 1:
        // use default parameter_list
        num_sph_SA= {4}; // number of spherule in aggregate generated given by SA
        levels= {10}; //number of hierarchical coagulation
        kf= {1.0}; // fractal prefactor
        Df= {2.5}; // fractal dimension
        tol= {1.0e-3}; // error torelance of the distance between attached spherules
        num_agg= {1}; // number of aggregate
        break;
    case 6:
        // parameter_list from the command line arguments
        num_sph_SA= atoi(argv[1]); // number of spherule in aggregate generated given by SA
        levels= atoi(argv[2]); //number of hierarchical coagulation
        kf= atof(argv[3]); // fractal prefactor
        Df= atof(argv[4]); // fractal dimension
        num_agg= atoi(argv[5]); // number of aggregate
        break;
    default:
        runtime_error("incorrect parameter_list");
        break;
    }

    cout << "num_sph_SA: " << num_sph_SA << endl;
    cout << "levels: " << levels << endl;
    cout << "kf: " << kf << endl;
    cout << "Df: " << Df << endl;
    cout << "tol: " << tol << endl;
    cout << "num_agg: " << num_agg << endl;

    for(int ind_agg=0; ind_agg < num_agg; ++ind_agg){
      vector<MatrixXd> pos_sph_levels= aggregate_gen_CCA(a,num_sph_SA,levels,kf,Df,tol);

      for(int k=0; k<pos_sph_levels.size(); ++k){
          int num_sph = num_sph_SA*pow(2,k);
          cout << "writing " << k << "th level aggregate, num_sph= " << num_sph << endl;
          MatrixXd pos_sph  {pos_sph_levels[k]};
          string oname = "agg"+to_string(ind_agg)+"_N"+to_string(num_sph)+"_kf"+to_string(kf).erase(3)+"_Df"+to_string(Df).erase(3)+".out";
          ofstream ost {oname};
          for(int i=0; i<num_sph; ++i){
              ost << scientific << showpoint
               << a << ' '
               << pos_sph(0,i) << ' '
               << pos_sph(1,i) << ' '
               << pos_sph(2,i) << endl;
          }
          ost.close();
      }
    }

}


// ---------------------------------------------------------
//
//  trianglequality.cpp
//  Tyson Brochu 2011
//  Christopher Batty, Fang Da 2014
//
//  Functions for getting various triangle mesh measures.
//
// ---------------------------------------------------------

#include <trianglequality.h>

#include <limits>
#include <surftrack.h>
#include <set>
#include <Eigen/Core>
#include <Eigen/Eigenvalues>
// ---------------------------------------------------------
///
/// Determine the "mixed" voronoi and barycentric area of the vertex within the given triangle.
///
// ---------------------------------------------------------

namespace LosTopos {

double mixed_area( size_t vertex_index, size_t triangle_index, const SurfTrack& surf )
{
    const Vec3st& tri = surf.m_mesh.get_triangle(triangle_index);

    Vec2st opposite_edge;
    if ( vertex_index == tri[0] )
    {
        opposite_edge = Vec2st( tri[1], tri[2] );
    }
    else if ( vertex_index == tri[1] )
    {
        opposite_edge = Vec2st( tri[2], tri[0] );
    }
    else
    {
        opposite_edge = Vec2st( tri[0], tri[1] );
    }

    const Vec3d& a = surf.get_position(vertex_index);
    const Vec3d& b = surf.get_position(opposite_edge[0]);
    const Vec3d& c = surf.get_position(opposite_edge[1]);

    bool obtuse_triangle = ( ( dot(b-a, c-a) < 0.0 ) || ( dot(a-b, c-b) < 0.0 ) || ( dot(a-c, b-c) < 0.0 ) );

    if ( obtuse_triangle )
    {
        //std::cout << "obtuse_triangle " << triangle_index << ": " << tri << std::endl;

        if ( dot(b-a, c-a) < 0.0 )
        {
            // obtuse at a
            return 0.5 * surf.get_triangle_area( triangle_index );
        }
        else
        {
            // obtuse somewhere else
            return 0.25 * surf.get_triangle_area( triangle_index );
        }
    }
    else
    {
        // not obtuse, use voronoi area

        double cross_c = mag( cross( a-c, b-c ) );
        double cot_c = dot( a-c, b-c) / cross_c;

        double cross_b = mag( cross( a-b, c-b ) );
        double cot_b = dot( a-b, c-b) / cross_b;

        return 1.0 / 8.0 * (mag2(b-a) * cot_c + mag2(c-a) * cot_b);
    }

}

// ---------------------------------------------------------
///
/// Get Kappa * n, the surface normal multiplied by mean curvature at the specified vertex.
///
// ---------------------------------------------------------

void vertex_mean_curvature_normal( size_t vertex_index, const SurfTrack& surf, Vec3d& out, double& weight_sum )
{
    Vec3d mean_curvature_normal( 0, 0, 0 );
    weight_sum = 0;

    double edge_length_sum = 0.0;

    for ( size_t i = 0; i < surf.m_mesh.m_vertex_to_edge_map[vertex_index].size(); ++i )
    {
        size_t e = surf.m_mesh.m_vertex_to_edge_map[vertex_index][i];
        const Vec2st& curr_edge = surf.m_mesh.m_edges[e];
        Vec3d edge_vector;
        if ( curr_edge[0] == vertex_index )
        {
            edge_vector = surf.get_position( curr_edge[1] ) - surf.get_position( vertex_index );
        }
        else
        {
            assert( curr_edge[1] == vertex_index );
            edge_vector = surf.get_position( curr_edge[0] ) - surf.get_position( vertex_index );
        }

        edge_length_sum += mag( edge_vector );

        if ( surf.m_mesh.m_edge_to_triangle_map[e].size() != 2 )
        {
            // TODO: properly handle more than 2 incident triangles
            out = Vec3d(0,0,0);
            return;
        }

        size_t tri0 = surf.m_mesh.m_edge_to_triangle_map[e][0];
        size_t tri1 = surf.m_mesh.m_edge_to_triangle_map[e][1];

        size_t third_vertex_0 = surf.m_mesh.get_third_vertex( curr_edge[0], curr_edge[1], surf.m_mesh.get_triangle(tri0) );
        size_t third_vertex_1 = surf.m_mesh.get_third_vertex( curr_edge[0], curr_edge[1], surf.m_mesh.get_triangle(tri1) );

        Vec3d v00 = surf.get_position( curr_edge[0] ) - surf.get_position( third_vertex_0 );
        Vec3d v10 = surf.get_position( curr_edge[1] ) - surf.get_position( third_vertex_0 );

        double cross_0 = mag( cross( v00, v10 ) );
        if ( cross_0 < 1e-10 )
        {
            continue;
        }
        double cot_0 = dot(v00, v10) / cross_0;

        Vec3d v01 = surf.get_position( curr_edge[0] ) - surf.get_position( third_vertex_1 );
        Vec3d v11 = surf.get_position( curr_edge[1] ) - surf.get_position( third_vertex_1 );

        double cross_1 = mag( cross( v01, v11 ) );
        if ( cross_1 < 1e-10 )
        {
            continue;
        }

        double cot_1 = dot(v01, v11) / cross_1;

        double weight = cot_0 + cot_1;
        weight_sum += weight;

        mean_curvature_normal += weight * edge_vector;

    }

    double vertex_area = 0.0;
    for ( size_t i = 0; i < surf.m_mesh.m_vertex_to_triangle_map[vertex_index].size(); ++i )
    {
        vertex_area += mixed_area( vertex_index, surf.m_mesh.m_vertex_to_triangle_map[vertex_index][i], surf );
    }

    double coeff = 1.0 / (2.0 * vertex_area);

    weight_sum *= coeff;

    out = coeff * mean_curvature_normal;

}

// ---------------------------------------------------------
///
/// Return an estimate for mean curvature at the given vertex, computed using the Kappa * n estimate above.
///
// ---------------------------------------------------------

double unsigned_vertex_mean_curvature( size_t vertex_index, const SurfTrack& surf )
{
    Vec3d mc_normal;
    double weight_sum;

    vertex_mean_curvature_normal( vertex_index, surf, mc_normal, weight_sum );

    return mag( mc_normal );
}

// ---------------------------------------------------------
///
/// Return an estimate for curvature by computing the minimum radius of a sphere defined by the edge neighbourhood around a vertex,
/// and taking the reciprocal.
///
// ---------------------------------------------------------

double inv_min_radius_curvature( const SurfTrack& surf, size_t vertex )
{

    Vec3d normal = surf.get_vertex_normal( vertex );

    //   double min_radius = BIG_DOUBLE;

    double inv_min_radius = -BIG_DOUBLE;

    for ( size_t i = 0; i < surf.m_mesh.m_vertex_to_edge_map[vertex].size(); ++i )
    {
        size_t edge_index = surf.m_mesh.m_vertex_to_edge_map[vertex][i];

        assert( edge_index < surf.m_mesh.m_edges.size() );

        const Vec2st& edge = surf.m_mesh.m_edges[ edge_index ];

        Vec3d P;
        if ( edge[0] == vertex )
        {
            P = surf.get_position( edge[1] ) - surf.get_position( vertex );
        }
        else
        {
            P = surf.get_position( edge[0] ) - surf.get_position( vertex );
        }

        //      double radius = 0.5 * dot( P, P ) / dot( normal, P );
        //      min_radius = min( min_radius, radius );

        double inv_radius = 2.0 * dot( normal, P ) / dot( P, P );
        inv_min_radius = max( inv_min_radius, inv_radius );

    }

    return inv_min_radius;

}

// ---------------------------------------------------------
///
/// Compute curvature at a vertex using quadric fitting
///
// ---------------------------------------------------------

double estimated_max_curvature(const SurfTrack& surf, size_t vertex) {

    Vec3d normal = surf.get_vertex_normal(vertex);

    Vec3d seed(0,0,0);
    if(abs(normal[0]) < 0.5)
        seed[0] = 1.0;
    else
        seed[1] = 1.0;
    Vec3d u = cross(normal, seed);
    normalize(u);
    Vec3d v = cross(normal, u);

    std::set<size_t> twoneighbors;
    twoneighbors.insert(vertex);
    for(int i=0; i<2; i++)
    {
        std::set<size_t> toadd;
        for(std::set<size_t>::iterator it = twoneighbors.begin(); it != twoneighbors.end(); ++it)
        {
            size_t curVert = *it;
            const std::vector<size_t>& nbrEdges = surf.m_mesh.m_vertex_to_edge_map[curVert];
            for(unsigned int nbrIdx = 0; nbrIdx < nbrEdges.size(); ++nbrIdx)
            {
                size_t edge_id = nbrEdges[nbrIdx];
                Vec2st edge = surf.m_mesh.m_edges[edge_id];
                size_t fv = edge[0];
                if(fv == curVert)
                    toadd.insert(edge[1]);
                else
                    toadd.insert(fv);
            }
        }
        for(std::set<size_t>::iterator it = toadd.begin(); it != toadd.end(); ++it)
            twoneighbors.insert(*it);
    }

    int numneighbors = (int)twoneighbors.size();
    if(numneighbors < 6)
        return 0;
    //TODO I havn't fully verified this after porting to Eigen, so user beware!

    //a u^2 + b v^2 + c uv + d u + e v + f
    Eigen::MatrixXd Mat_Eigen(numneighbors, 6);
    Eigen::VectorXd Rhs_Eigen(numneighbors);

    Vec3d centpt = surf.get_position(vertex);
    int row=0;
    for(std::set<size_t>::iterator it = twoneighbors.begin(); it != twoneighbors.end(); ++it)
    {
        size_t curVert = *it;
        Vec3d adjpt = surf.get_position(curVert);
        Vec3d diff;
        for(int i=0; i<3; i++)
            diff[i] = adjpt[i]-centpt[i];
        double uval = dot(diff,u);
        double vval = dot(diff,v);
        Rhs_Eigen[row] = dot(diff, normal);

        Mat_Eigen(row, 0) = uval*uval;
        Mat_Eigen(row, 1) = vval*vval;
        Mat_Eigen(row, 2) = uval*vval;
        Mat_Eigen(row, 3) = uval;
        Mat_Eigen(row, 4) = vval;
        Mat_Eigen(row, 5) = 1;

        row++;
    }
    assert(row == numneighbors);

    Eigen::VectorXd soln_Eigen = Mat_Eigen.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(Rhs_Eigen);

    std::vector<double> coeffs(6);
    for(int id = 0; id < 6; ++id) coeffs[id] = Rhs_Eigen[id];

    double areaElt = sqrt(1+coeffs[3]*coeffs[3]+coeffs[4]*coeffs[4]);
    Vec3d newnormal = (normal - coeffs[3]*u - coeffs[4]*v) / areaElt;

    double E = 1 + coeffs[3]*coeffs[3];
    double F = coeffs[3]*coeffs[4];
    double G = 1 + coeffs[4]*coeffs[4];

    //double fac = normal.dot(newnormal);
    double L = 2*coeffs[0]/areaElt;
    double M = coeffs[2]/areaElt;
    double N = 2*coeffs[1]/areaElt;

    double det = E*G-F*F;
    if(abs(det) < 1e-6)
        return 0;

    Mat22d H(L, M, M, N);

    //LosTopos stores things in column major rather than row major, I believe, hence the transposing
    Mat33d U(u[0], v[0], normal[0],
        u[1], v[1], normal[1],
        u[2], v[2], normal[2]);
    U = U.transpose();

    Mat32d J(1, 0, coeffs[3],
             0, 1, coeffs[4]);

    J = U*J;

    Mat22d shapeOperator = 1/areaElt * inverse(J.transpose()*J) * H;

    //now do an eigensolve on the shape operator to find the principal curvatures/directions
    Eigen::Matrix2d shape_Eigen;
    shape_Eigen << shapeOperator(0, 0), shapeOperator(0, 1), shapeOperator(1, 0), shapeOperator(1, 1);
    Eigen::SelfAdjointEigenSolver<Eigen::Matrix2d> es;
    es.compute(shape_Eigen, false);
    if (es.info() != Eigen::Success) assert(0);
    auto eigenvalues_Eigen = es.eigenvalues();

    double max_curvature = max(std::fabs(eigenvalues_Eigen[0]), std::fabs(eigenvalues_Eigen[1]));

    return max_curvature;

}

// ---------------------------------------------------------
///
/// Compute curvatures at all vertices using inv_min_radius_curvature.
///
// ---------------------------------------------------------

void compute_vertex_curvatures( const SurfTrack& surf, std::vector<double>& vertex_curvatures )
{

    vertex_curvatures.resize( surf.get_num_vertices() );

    for ( size_t i = 0; i < surf.get_num_vertices(); ++i )
    {

        if ( surf.m_mesh.m_is_boundary_vertex[i] )
        {
            vertex_curvatures[i] = 1.0;
            continue;
        }

        vertex_curvatures[i] = inv_min_radius_curvature( surf, i );
    }
}

#define USE_INV_MIN_RADIUS

// ---------------------------------------------------------
///
/// Get the length of the specified edge, scaled by an estimate of curvature at each of the vertices.
///
// ---------------------------------------------------------

double get_curvature_scaled_length(const SurfTrack& surf,
                                   size_t vertex_a,
                                   size_t vertex_b,
                                   double min_curvature_multiplier,
                                   double max_curvature_multiplier,
                                   double rest_curvature )
{

    assert( vertex_a < surf.get_num_vertices() );
    assert( vertex_b < surf.get_num_vertices() );

    double length = dist(  surf.get_position( vertex_a ), surf.get_position( vertex_b ) );

    //std::cout << "\n\nTrue length: " << length << std::endl;
#ifdef USE_INV_MIN_RADIUS
    double curv_a = std::fabs( inv_min_radius_curvature( surf, vertex_a ) );
#else
    double curv_a = unsigned_vertex_mean_curvature( vertex_a, surf );
#endif

    curv_a /= rest_curvature;
    curv_a = std::max( min_curvature_multiplier, curv_a );
    curv_a = std::min( max_curvature_multiplier, curv_a );

    //std::cout << "Curv a: " << curv_a << std::endl;

#ifdef USE_INV_MIN_RADIUS
    double curv_b = std::fabs( inv_min_radius_curvature( surf, vertex_b ) );
#else
    double curv_b = unsigned_vertex_mean_curvature( vertex_b, m_surf );
#endif

    curv_b /= rest_curvature;
    curv_b = std::max( min_curvature_multiplier, curv_b );
    curv_b = std::min( max_curvature_multiplier, curv_b );

    //std::cout << "Curv b: " << curv_b << std::endl;

    length *= 0.5 * ( curv_a + curv_b );

    //std::cout << "Multiplier: " << 0.5*(curv_a+curv_b) << std::endl;

    return length;

}

// ---------------------------------------------------------
///
/// Get the length of the specified edge, scaled by an estimate of curvature at each of the vertices.
///
// ---------------------------------------------------------

double get_edge_curvature(const SurfTrack& surf,
  size_t vertex_a,
  size_t vertex_b)
{

  assert( vertex_a < surf.get_num_vertices() );
  assert( vertex_b < surf.get_num_vertices() );

#ifdef USE_INV_MIN_RADIUS
  double curv_a = estimated_max_curvature( surf, vertex_a );
  //double curv_a = std::fabs( inv_min_radius_curvature( surf, vertex_a ) );
#else
  double curv_a = unsigned_vertex_mean_curvature( vertex_a, surf );
#endif

#ifdef USE_INV_MIN_RADIUS
  double curv_b = estimated_max_curvature( surf, vertex_b );
  //double curv_b = std::fabs( inv_min_radius_curvature( surf, vertex_b ) );
#else
  double curv_b = unsigned_vertex_mean_curvature( vertex_b, surf );
#endif

  return max(curv_a, curv_b);//0.5 * ( curv_a + curv_b );

}

// ---------------------------------------------------------
///
/// Return the minimum triangle area in the specified surface.
///
// ---------------------------------------------------------

double min_triangle_area( const SurfTrack& surf )
{
    double min_area = BIG_DOUBLE;
    for ( size_t i = 0; i < surf.m_mesh.num_triangles(); ++i )
    {
        if ( surf.m_mesh.triangle_is_deleted(i) ) { continue; }
        if ( surf.triangle_is_all_solid(i) ) { continue; }

        double area = surf.get_triangle_area(i);
        min_area = std::min( area, min_area );
    }

    return min_area;

}

// ---------------------------------------------------------
///
/// Return the minimun triangle angle in the specified surface.
///
// ---------------------------------------------------------

double min_triangle_angle( const SurfTrack& surf )
{
    double min_angle = BIG_DOUBLE;
    for ( size_t i = 0; i < surf.m_mesh.num_triangles(); ++i )
    {
        if ( surf.m_mesh.triangle_is_deleted(i) ) { continue; }

        const Vec3d& a = surf.get_position( surf.m_mesh.get_triangle(i)[0] );
        const Vec3d& b = surf.get_position( surf.m_mesh.get_triangle(i)[1] );
        const Vec3d& c = surf.get_position( surf.m_mesh.get_triangle(i)[2] );

        double curr_min_angle = min_triangle_angle( a, b, c );

        min_angle = std::min( curr_min_angle, min_angle );
    }

    return min_angle;

}

// ---------------------------------------------------------
///
/// Return the maximum triangle angle in the specified surface.
///
// ---------------------------------------------------------

double max_triangle_angle( const SurfTrack& surf )
{
    double max_angle = -BIG_DOUBLE;

    for ( size_t i = 0; i < surf.m_mesh.num_triangles(); ++i )
    {
        if ( surf.m_mesh.triangle_is_deleted(i) ) { continue; }

        const Vec3d& a = surf.get_position( surf.m_mesh.get_triangle(i)[0] );
        const Vec3d& b = surf.get_position( surf.m_mesh.get_triangle(i)[1] );
        const Vec3d& c = surf.get_position( surf.m_mesh.get_triangle(i)[2] );

        double curr_max_angle = max_triangle_angle( a, b, c );

        max_angle = std::max( curr_max_angle, max_angle );
    }

    return max_angle;
}

// ---------------------------------------------------------
///
/// Count the number of triangle angles below the given threshold.
///
// ---------------------------------------------------------

size_t num_angles_below_threshold( const SurfTrack& surf, double low_threshold )
{
    size_t num_small_angles = 0;

    for ( size_t i = 0; i < surf.m_mesh.num_triangles(); ++i )
    {
        if ( surf.m_mesh.triangle_is_deleted(i) ) { continue; }

        const Vec3d& a = surf.get_position( surf.m_mesh.get_triangle(i)[0] );
        const Vec3d& b = surf.get_position( surf.m_mesh.get_triangle(i)[1] );
        const Vec3d& c = surf.get_position( surf.m_mesh.get_triangle(i)[2] );

        double angle_a, angle_b, angle_c;
        triangle_angles( a, b, c, angle_a, angle_b, angle_c );

        if ( angle_a < low_threshold ) { ++num_small_angles; }
        if ( angle_b < low_threshold ) { ++num_small_angles; }
        if ( angle_c < low_threshold ) { ++num_small_angles; }
    }

    return num_small_angles;

}

// ---------------------------------------------------------
///
/// Count the number of triangle angles above the given threshold.
///
// ---------------------------------------------------------

size_t num_angles_above_threshold( const SurfTrack& surf, double high_threshold )
{
    size_t num_large_angles = 0;

    for ( size_t i = 0; i < surf.m_mesh.num_triangles(); ++i )
    {
        if ( surf.m_mesh.triangle_is_deleted(i) ) { continue; }

        const Vec3d& a = surf.get_position( surf.m_mesh.get_triangle(i)[0] );
        const Vec3d& b = surf.get_position( surf.m_mesh.get_triangle(i)[1] );
        const Vec3d& c = surf.get_position( surf.m_mesh.get_triangle(i)[2] );

        double angle_a, angle_b, angle_c;
        triangle_angles( a, b, c, angle_a, angle_b, angle_c );

        if ( angle_a > high_threshold ) { ++num_large_angles; }
        if ( angle_b > high_threshold ) { ++num_large_angles; }
        if ( angle_c > high_threshold ) { ++num_large_angles; }
    }

    return num_large_angles;

}

// ---------------------------------------------------------
///
/// Compute the aspect ratio of the given triangle
///
// ---------------------------------------------------------

double triangle_aspect_ratio( const SurfTrack& surf, size_t triangle_index )
{
    const Vec3st& tri = surf.m_mesh.get_triangle(triangle_index);
    assert( tri[0] != tri[1] );
    return triangle_aspect_ratio( surf.get_position(tri[0]), surf.get_position(tri[1]), surf.get_position(tri[2]) );
}

// ---------------------------------------------------------
///
/// Find the smallest triangle aspect ratio in the given mesh
///
// ---------------------------------------------------------

double min_triangle_aspect_ratio( const SurfTrack& surf, size_t& output_triangle_index )
{
    double min_ratio = std::numeric_limits<double>::max();
    output_triangle_index = (size_t)~0;

    for ( size_t i = 0; i < surf.m_mesh.num_triangles(); ++i )
    {
        double a_ratio = triangle_aspect_ratio( surf, i );
        if ( a_ratio < min_ratio )
        {
            output_triangle_index = i;
            min_ratio = a_ratio;
        }
    }
    return min_ratio;
}

// ---------------------------------------------------------
///
/// Find the greatest triangle aspect ratio in the given mesh
///
// ---------------------------------------------------------

double max_triangle_aspect_ratio( const SurfTrack& surf, size_t& output_triangle_index )
{
    double max_ratio = -1.0;
    output_triangle_index = (size_t)~0;

    for ( size_t i = 0; i < surf.m_mesh.num_triangles(); ++i )
    {
        double a_ratio = triangle_aspect_ratio( surf, i );
        if ( a_ratio > max_ratio )
        {
            output_triangle_index = i;
            max_ratio = a_ratio;
        }
    }
    return max_ratio;
}

}


// solving A * X = B
// A symmetric/hermitian positive definite in packed format
// factor (potrf()) and solve (potrs())

#include <cstddef>
#include <iostream>
#include <complex>
#include <boost/numeric/bindings/lapack/computational/pptrf.hpp>
#include <boost/numeric/bindings/lapack/computational/pptri.hpp>
#include <boost/numeric/bindings/lapack/computational/pptrs.hpp>
#include <boost/numeric/bindings/ublas/matrix.hpp>
#include <boost/numeric/bindings/ublas/symmetric.hpp>
#include <boost/numeric/bindings/ublas/hermitian.hpp>
#include "utils.h"

namespace ublas = boost::numeric::ublas;
namespace lapack = boost::numeric::bindings::lapack;

using std::size_t;
using std::cout;
using std::endl;

typedef float real_t;
typedef std::complex<real_t> cmplx_t;

typedef ublas::matrix<real_t, ublas::column_major> m_t;
typedef ublas::matrix<cmplx_t, ublas::column_major> cm_t;

typedef
  ublas::symmetric_matrix<real_t, ublas::lower, ublas::column_major> symml_t;
typedef
  ublas::hermitian_matrix<cmplx_t, ublas::lower, ublas::column_major> herml_t;

typedef
  ublas::symmetric_matrix<real_t, ublas::upper, ublas::column_major> symmu_t;
typedef
  ublas::hermitian_matrix<cmplx_t, ublas::upper, ublas::column_major> hermu_t;

int main() {

  // for more descriptive comments see ublas_ppsv.cc
  cout << endl;

  // symmetric
  cout << "real symmetric\n" << endl;

  size_t n = 5;
  size_t nrhs = 2;
  symml_t sal (5, 5);   // symmetric matrix
  symmu_t sau (5, 5);   // symmetric matrix
  m_t x (n, nrhs);
  m_t bl (n, nrhs), bu (n, nrhs);  // RHS matrices

  init_symm (sal, 'l');
  init_symm (sau, 'u');

  print_m (sal, "sal");
  cout << endl;
  print_m (sau, "sau");
  cout << endl;

  for (int i = 0; i < x.size1(); ++i) {
    x (i, 0) = 1.;
    x (i, 1) = 2.;
  }
  bl = prod (sal, x);
  bu = prod (sau, x);

  print_m (bl, "bl");
  cout << endl;
  print_m (bu, "bu");
  cout << endl;

  int ierr = lapack::pptrf (sal);
  if (!ierr) {
    symml_t isal (sal);
    lapack::pptrs (sal, bl);
    print_m (bl, "xl");
    cout << endl;
    lapack::pptri (isal);
    print_m (isal, "isal");
  }
  cout << endl;

  ierr = lapack::pptrf (sau);
  if (!ierr) {
    symmu_t isau (sau);
    lapack::pptrs (sau, bu);
    print_m (bu, "xu");
    cout << endl;
    lapack::pptri (isau);
    print_m (isau, "isau");
  }
  cout << endl;

  //////////////////////////////////////////////////////////
  // hermitian
  cout << "\n==========================================\n" << endl;
  cout << "complex hermitian\n" << endl;

  herml_t hal (3, 3);   // hermitian matrix
  hermu_t hau (3, 3);   // hermitian matrix
  cm_t cx (3, 1);
  cm_t cbl (3, 1), cbu (3, 1);  // RHS

  hal (0, 0) = cmplx_t (25, 0);
  hal (1, 0) = cmplx_t (-5, 5);
  hal (1, 1) = cmplx_t (51, 0);
  hal (2, 0) = cmplx_t (10, -5);
  hal (2, 1) = cmplx_t (4, 6);
  hal (2, 2) = cmplx_t (71, 0);

  hau (0, 0) = cmplx_t (25, 0);
  hau (0, 1) = cmplx_t (-5, -5);
  hau (0, 2) = cmplx_t (10, 5);
  hau (1, 1) = cmplx_t (51, 0);
  hau (1, 2) = cmplx_t (4, -6);
  hau (2, 2) = cmplx_t (71, 0);

  print_m (hal, "hal");
  cout << endl;
  print_m (hau, "hau");
  cout << endl;

  cm_t cbl2 (3, 2);
  cbl2 (0, 0) = cmplx_t (60, -55);
  cbl2 (1, 0) = cmplx_t (34, 58);
  cbl2 (2, 0) = cmplx_t (13, -152);
  cbl2 (0, 1) = cmplx_t (70, 10);
  cbl2 (1, 1) = cmplx_t (-51, 110);
  cbl2 (2, 1) = cmplx_t (75, 63);
  cm_t cbu2 (cbl2);
  print_m (cbl2, "cbl");
  cout << endl;

  ierr = lapack::pptrf (hal);
  if (ierr == 0) {
    herml_t ihal (hal);
    lapack::pptrs (hal, cbl2);
    print_m (cbl2, "cxl");
    cout << endl;
    lapack::pptri (ihal);
    print_m (ihal, "ihal");
  }
  else
    cout << "matrix is not positive definite: ierr = "
         << ierr << endl << endl;
  cout << endl;

  ierr = lapack::pptrf (hau);
  if (ierr == 0) {
    hermu_t ihau (hau);
    ierr = lapack::pptrs (hau, cbu2);
    print_m (cbu2, "cxu");
    cout << endl;
    lapack::pptri (ihau);
    print_m (ihau, "ihau");
  }
  else
    cout << "matrix is not positive definite: ierr = "
         << ierr << endl << endl;
  cout << endl;

}