/*=============================================================================
    Copyright (c) 2001-2003 Joel de Guzman

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/
#include <vector>
#include <algorithm>
#include <iostream>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/function.hpp>

struct factorial_impl
{
    template <typename Sig>
    struct result;

    template <typename This, typename Arg>
    struct result<This(Arg)>
        : result<This(Arg const &)>
    {};

    template <typename This, typename Arg>
    struct result<This(Arg &)>
    {
        typedef Arg type;
    };

    template <typename Arg>
    Arg operator()(Arg n) const
    {
        return (n <= 0) ? 1 : n * this->operator()(n-1);
    }
};

int
main()
{
    using boost::phoenix::arg_names::arg1;
    boost::phoenix::function<factorial_impl> factorial;
    int i = 4;
    std::cout << factorial(i)() << std::endl;
    std::cout << factorial(arg1)(i) << std::endl;
    return 0;
}


/*
@copyright Louis Dionne 2014
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/hana/detail/constexpr.hpp>
#include <boost/hana/list/instance.hpp>
#include <boost/hana/map.hpp>
#include <boost/hana/pair.hpp>
#include <boost/hana/record/mcd.hpp>
#include <boost/hana/type.hpp>

#include <cassert>
#include <string>
using namespace boost::hana;

struct Person {
    std::string name;
    int age;
};

// The keys can be anything as long as they are compile-time comparable.
constexpr auto name = decltype_(&Person::name);
constexpr auto age = decltype_(&Person::age);

namespace boost { namespace hana {
    template <>
    struct Record::instance<Person> : Record::mcd {
        static BOOST_HANA_CONSTEXPR_LAMBDA auto members_impl() {
            return list(
                pair(name, [](auto p) { return p.name; }),
                pair(age, [](auto p) { return p.age; })
            );
        }
    };
}}

int main() {
    Person john{"John", 30}, bob{"Bob", 40};
    assert(equal(john, john));
    assert(not_equal(john, bob));

    assert(lookup(john, name) == just("John"));
    assert(lookup(john, age) == just(30));
    assert(lookup(john, "clearly not a member") == nothing);

    assert(to<List>(john) == list("John", 30));
    assert(to<Map>(john) == map(
        pair(name, "John"),
        pair(age, 30)
    ));
}


/*
 Copyright (C) 2016 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

#include <boost/test/unit_test.hpp>
#include <ored/portfolio/schedule.hpp>
#include <oret/toplevelfixture.hpp>

using namespace boost::unit_test_framework;
using namespace ore::data;
using namespace QuantLib;

BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)

BOOST_AUTO_TEST_SUITE(ScheduleDataTests)

BOOST_AUTO_TEST_CASE(testScheduleData) {

    BOOST_TEST_MESSAGE("Testing ScheduleData...");

    ScheduleDates dates1("TARGET", "", "", {"2015-01-09", "2015-02-09", "2015-03-09", "2015-04-09"});

    ScheduleDates dates2("TARGET", "", "",
                         {"2015-04-09",
                          "2016-04-11", // 9th = Saturday
                          "2017-04-10", // 9th = Sunday
                          "2018-04-09"});

    ScheduleRules rules1("2015-01-09", "2015-04-09", "1M", "TARGET", "MF", "MF", "Forward");

    ScheduleRules rules2("2015-04-09", "2018-04-09", "1Y", "TARGET", "MF", "MF", "Forward");

    // Test <Dates> and <Dates>
    ScheduleData data(dates1);
    data.addDates(dates2);
    QuantLib::Schedule s = makeSchedule(data);
    BOOST_CHECK_EQUAL(s.size(), 7UL); // 7 dates, 6 coupons
    BOOST_CHECK_EQUAL(s.startDate(), Date(9, Jan, 2015));
    BOOST_CHECK_EQUAL(s.endDate(), Date(9, Apr, 2018));

    // Test <Rules> and <Rules>
    data = ScheduleData(rules1);
    data.addRules(rules2);
    QuantLib::Schedule s2 = makeSchedule(data);
    BOOST_CHECK_EQUAL(s2.size(), s.size());
    for (Size i = 0; i < s.size(); i++)
        BOOST_CHECK_EQUAL(s[i], s2[i]);

    // Test <Dates> and <Rules>
    data = ScheduleData(dates1);
    data.addRules(rules2);
    QuantLib::Schedule s3 = makeSchedule(data);
    BOOST_CHECK_EQUAL(s3.size(), s.size());
    for (Size i = 0; i < s.size(); i++)
        BOOST_CHECK_EQUAL(s[i], s3[i]);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()


//=======================================================================
// Copyright 2017 Felix Salfelder
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <boost/graph/minimum_degree_ordering.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/typeof/typeof.hpp>
#include <vector>
#include <map>

typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS > G;

int main(int argc, char** argv)
{
    size_t n = 10;
    G g(n);

    std::vector< int > inverse_perm(n, 0);
    std::vector< int > supernode_sizes(n, 1);
    BOOST_AUTO(id, boost::get(boost::vertex_index, g));
    std::vector< int > degree(n, 0);
    std::map< int, int > io;
    std::map< int, int > o;

    boost::minimum_degree_ordering(g,
        boost::make_iterator_property_map(degree.begin(), id, degree[0]),
        boost::make_assoc_property_map(io), boost::make_assoc_property_map(o),
        boost::make_iterator_property_map(
            supernode_sizes.begin(), id, supernode_sizes[0]),
        0, id);

    for (size_t k = 0; k < n; ++k)
    {
        BOOST_TEST(o[io[k]] == k);
    }

    return boost::report_errors();
}


//| This file is a part of the sferes2 framework.
//| Copyright 2009, ISIR / Universite Pierre et Marie Curie (UPMC)
//| Main contributor(s): Jean-Baptiste Mouret, mouret@isir.fr
//|
//| This software is a computer program whose purpose is to facilitate
//| experiments in evolutionary computation and evolutionary robotics.
//|
//| This software is governed by the CeCILL license under French law
//| and abiding by the rules of distribution of free software.  You
//| can use, modify and/ or redistribute the software under the terms
//| of the CeCILL license as circulated by CEA, CNRS and INRIA at the
//| following URL "http://www.cecill.info".
//|
//| As a counterpart to the access to the source code and rights to
//| copy, modify and redistribute granted by the license, users are
//| provided only with a limited warranty and the software's author,
//| the holder of the economic rights, and the successive licensors
//| have only limited liability.
//|
//| In this respect, the user's attention is drawn to the risks
//| associated with loading, using, modifying and/or developing or
//| reproducing the software by the user in light of its specific
//| status of free software, that may mean that it is complicated to
//| manipulate, and that also therefore means that it is reserved for
//| developers and experienced professionals having in-depth computer
//| knowledge. Users are therefore encouraged to load and test the
//| software's suitability as regards their requirements in conditions
//| enabling the security of their systems and/or data to be ensured
//| and, more generally, to use and operate it in the same conditions
//| as regards security.
//|
//| The fact that you are presently reading this means that you have
//| had knowledge of the CeCILL license and that you accept its terms.

#ifndef RAND_HPP_
#define RAND_HPP_

#include <cstdlib>
#include <cmath>
#include <list>
#include <stdlib.h>
#include <boost/swap.hpp>
#include <boost/random.hpp>

// someday we will have a real thread-safe random number generator...
namespace sferes {
  namespace misc {
    // NOT Thread-safe !
    template<typename T>
    inline T rand(T max = 1.0) {
      assert(max > 0);
      T v;
      do
        v = T(((double)max * ::rand())/(RAND_MAX + 1.0));
      while(v >= max); // this strange case happened... precision problem?
      assert(v < max);
      return v;
    }

    template<typename T>
    inline T rand(T min, T max) {
      assert(max != min);
      assert(max > min);
      T res = T(rand<double>() * ((long int) max - (long int) min) + min);
      assert(res >= min);
      assert(res < max);
      return res;
    }

    template<typename T>
    inline T gaussian_rand(T m=0.0,T  v=1.0) {
      float facteur = sqrt(-2.0f * log(rand<float>()));
      float trigo  = 2.0f * M_PI * rand<float>();

      return T(m + v * facteur * cos(trigo));

    }

    inline void rand_ind(std::vector<size_t>& a1, size_t size) {
      a1.resize(size);
      for (size_t i = 0; i < a1.size(); ++i)
        a1[i] = i;
      for (size_t i = 0; i < a1.size(); ++i) {
        size_t k = rand(i, a1.size());
        assert(k < a1.size());
        boost::swap(a1[i], a1[k]);
      }
    }

    /// return a random it in the list
    template<typename T>
    inline typename std::list<T>::iterator rand_in_list(std::list<T>& l) {
      int n = rand(l.size());
      typename std::list<T>::iterator it = l.begin();
      for (int i = 0; i < n; ++i)
        ++it;
      return it;
    }

    inline bool flip_coin() {
      return rand<float>() < 0.5f;
    }

    template<typename L>
    inline typename L::iterator rand_l(L& l) {
      size_t k = rand(l.size());
      typename L::iterator it = l.begin();
      for (size_t i = 0; i < k; ++i)
        ++it;
      return it;
    }
  }
}
#endif


// Boost.Geometry (aka GGL, Generic Geometry Library)
// Unit Test

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include <geometry_test_common.hpp>

#include <boost/geometry/arithmetic/arithmetic.hpp>

#include <boost/geometry/algorithms/assign.hpp>

#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/adapted/c_array.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>
#include <test_common/test_point.hpp>

BOOST_GEOMETRY_REGISTER_C_ARRAY_CS(cs::cartesian)
BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)

template <typename P, typename P2>
void test_addition()
{
    P p1;
    bg::assign_values(p1, 1, 2, 3);
    bg::add_value(p1, 10);
    BOOST_CHECK(bg::get<0>(p1) == 11);
    BOOST_CHECK(bg::get<1>(p1) == 12);
    BOOST_CHECK(bg::get<2>(p1) == 13);

    P2 p2(4, 5, 6);
    bg::add_point(p1, p2);
    BOOST_CHECK(bg::get<0>(p1) == 15);
    BOOST_CHECK(bg::get<1>(p1) == 17);
    BOOST_CHECK(bg::get<2>(p1) == 19);
}

template <typename P, typename P2>
void test_subtraction()
{
    P p1;
    bg::assign_values(p1, 1, 2, 3);
    bg::subtract_value(p1, 10);
    BOOST_CHECK(bg::get<0>(p1) == -9);
    BOOST_CHECK(bg::get<1>(p1) == -8);
    BOOST_CHECK(bg::get<2>(p1) == -7);

    P2 p2(4, 6, 8);
    bg::subtract_point(p1, p2);
    BOOST_CHECK(bg::get<0>(p1) == -13);
    BOOST_CHECK(bg::get<1>(p1) == -14);
    BOOST_CHECK(bg::get<2>(p1) == -15);
}

template <typename P, typename P2>
void test_multiplication()
{
    P p1;
    bg::assign_values(p1, 1, 2, 3);
    bg::multiply_value(p1, 5);
    BOOST_CHECK(bg::get<0>(p1) == 5);
    BOOST_CHECK(bg::get<1>(p1) == 10);
    BOOST_CHECK(bg::get<2>(p1) == 15);

    P2 p2(4, 5, 6);
    bg::multiply_point(p1, p2);
    BOOST_CHECK(bg::get<0>(p1) == 20);
    BOOST_CHECK(bg::get<1>(p1) == 50);
    BOOST_CHECK(bg::get<2>(p1) == 90);
}

template <typename P, typename P2>
void test_division()
{
    P p1;
    bg::assign_values(p1, 50, 100, 150);
    bg::divide_value(p1, 5);
    BOOST_CHECK(bg::get<0>(p1) == 10);
    BOOST_CHECK(bg::get<1>(p1) == 20);
    BOOST_CHECK(bg::get<2>(p1) == 30);

    P2 p2(2, 4, 6);
    bg::divide_point(p1, p2);
    BOOST_CHECK(bg::get<0>(p1) == 5);
    BOOST_CHECK(bg::get<1>(p1) == 5);
    BOOST_CHECK(bg::get<2>(p1) == 5);
}

template <typename P, typename P2>
void test_assign()
{
    P p1;
    P2 p2(12, 34, 56);
    bg::assign_values(p1, 12, 34, 56);
    bg::assign_point(p1, p2);
    BOOST_CHECK(bg::get<0>(p1) == 12);
    BOOST_CHECK(bg::get<1>(p1) == 34);
    BOOST_CHECK(bg::get<2>(p1) == 56);

    bg::assign_value(p1, 78);
    BOOST_CHECK(bg::get<0>(p1) == 78);
    BOOST_CHECK(bg::get<1>(p1) == 78);
    BOOST_CHECK(bg::get<2>(p1) == 78);
}

template <typename P>
void test_all()
{
    typedef test::test_const_point P2;

    test_addition<P, P2>();
    test_subtraction<P, P2>();
    test_multiplication<P, P2>();
    test_division<P, P2>();
    test_assign<P, P2>();
}

int test_main(int, char* [])
{
    test_all<int[3]>();
    test_all<float[3]>();
    test_all<double[3]>();
    test_all<test::test_point>();
    test_all<bg::model::point<int, 3, bg::cs::cartesian> >();
    test_all<bg::model::point<float, 3, bg::cs::cartesian> >();
    test_all<bg::model::point<double, 3, bg::cs::cartesian> >();

    return 0;
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*  This file is part of the library KASKADE 7                               */
/*    see http://www.zib.de/projects/kaskade7-finite-element-toolbox         */
/*                                                                           */
/*  Copyright (C) 2002-2016 Zuse Institute Berlin                            */
/*                                                                           */
/*  KASKADE 7 is distributed under the terms of the ZIB Academic License.    */
/*    see $KASKADE/academic.txt                                              */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifndef LINEARSPACE_HH
#define LINEARSPACE_HH

#include <cmath>
#include <type_traits>

#include <boost/version.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/sequence.hpp>

#include "linalg/crsutil.hh"

namespace Kaskade
{
  /// \internal
  // forward declaration
  template <class> class VariableSet;

  namespace LinearSpace_Detail
  {

    template <class Space0, class Space2, int id> struct Copy;

    template <class Space, int id, class VSDescriptions>
    struct Copy<VariableSet<VSDescriptions>,Space,id>
    {
      static void apply(const VariableSet<VSDescriptions>& from, Space& to)
      {
        boost::fusion::at_c<id>(to.data) = boost::fusion::at_c<id>(from.data).coefficients();
        Copy<VariableSet<VSDescriptions>,Space,id-1>::apply(from,to);
      }
    };

    template <class Space, class VSDescriptions>
    struct Copy<VariableSet<VSDescriptions>,Space,0>
    {
      static void apply(const VariableSet<VSDescriptions>& from, Space& to)
      {
        boost::fusion::at_c<0>(to.data) = boost::fusion::at_c<0>(from.data).coefficients();
      }
    };
  }
  /// \endinternal

  /**
   * \ingroup linalgbasic
   * \brief A product space of linear spaces.
   *
   * Access to the components of the product space is provided by the the boost::fusion
   * sequence data:
   * \code
   * LinearProductSpace<double,boost::fusion::vector<Dune::FieldVector<double,2>,Dune::FieldVector<double,3>>> x;
   * Dune::FieldVector<double,3>& comp1 = boost::fusion::at_c<1>(x.data);
   * \endcode
   *
   * Arithmetic operations are delegated componentwise to the heterogeneous subspaces.
   *
   *
   * \tparam Scalar the scalar field type of the linear space
   * \tparam Seq a boost::fusion sequence type defining the variables of the cartesian product
   */
  template <class Scalar_, class Seq>
  class LinearProductSpace
  {
  private:
    typedef LinearProductSpace<Scalar_,Seq> Self;

    // forward declarations (definitions at end of file)
    struct Add;
    struct Sub;
    struct Assign;
    struct ScalarMult;
    struct Axpy;
    struct ScalarProduct;
    struct ScalarProductHack;
    template <typename> struct ReadBlock;
    template <typename> struct WriteBlock;

  public:
    /// scalar type
    typedef Scalar_ Scalar;
    typedef Scalar field_type;

    /// boost::fusion::vector of element vectors.
    typedef Seq Sequence;
    typedef Seq Functions;

    // use with care: default constructed subvectors may not be properly initialized (e.g. size?)
    // LinearProductSpace() {}
    LinearProductSpace() = delete;

    /// Copy constructor
    LinearProductSpace(LinearProductSpace<Scalar,Sequence> const& y): data(y.data) {}

    /// Copy from VariableSet
    template <class VSDescriptions>
    explicit LinearProductSpace(const VariableSet<VSDescriptions>& y)
    {
      *this = y;
    }

    /// Constructor with explicit data
    template <class S>
    explicit LinearProductSpace(S const& init): data(init)
    {}

    template <typename... Args>
    explicit LinearProductSpace(boost::fusion::transform_view<Args...> const& data_) : data(data_)
    {
      applyUnaryOp(Assign(0));
    }

    /**
     *\brief Number of scalar degrees of freedom.
     * This computes the total number of degrees of freedom, i.e. the sum of the dimensions of
     * the components.
     */
    size_t dim() const
    {
      return boost::fusion::accumulate(data,0,[](auto dim, auto const& v) { return dim+v.dim(); });
    }

    /// Assignment from the same type
    Self& operator=(Self const& y) { if (this!=&y) data = y.data; return *this; }

    /// Assignment from a different (hopefully compatible) type
    template <class VSDescriptions>
    Self& operator=(VariableSet<VSDescriptions> const& y)
    {
      using namespace boost::fusion;
      static_assert(result_of::size<Sequence>::type::value == VSDescriptions::noOfVariables,"Numbers of variables do not match.");
      LinearSpace_Detail::Copy<VariableSet<VSDescriptions>,LinearProductSpace,
                               result_of::size<typename VSDescriptions::RepresentationData>::type::value-1>::apply(y,*this);
      return *this;
    } // need not check for self assignment here

    /// Assignment from a different (hopefully compatible) type
    template <class OtherSeq>
    Self& operator=(LinearProductSpace<Scalar,OtherSeq> const& y)
    {
      using namespace boost::fusion;
      static_assert(result_of::size<Sequence>::type::value == result_of::size<OtherSeq>::type::value, "Numbers of variables do not match.");
      data = y.data;
      return *this;
    } // need not check for self assignment here

    /// Assignment from a (hopefully compatible) FunctionSpaceElement
    template <class FunctionSpace, int m>
    Self& operator=(FunctionSpaceElement<FunctionSpace,m> const& fse)
    {
      data = fse.coefficients();
      return *this;
    }

    /// Assignment to constant scalar
    Self& operator=(Scalar a)
    {
      return applyUnaryOp(Assign(a));
    }

    /// Assignment from a (hopefully compatible) boost::fusion iterator range
    template <class First, class Last>
    Self& operator=(boost::fusion::iterator_range<First,Last> range)
    {
      static_assert(boost::fusion::result_of::size<Sequence>::type::value == boost::fusion::result_of::distance<First,Last>::type::value,
                    "Numbers of variables do not match.");
      data = range;
      return *this;
    }

    //

    /// Scaling
    Self& operator*=(Scalar a) { return applyUnaryOp(ScalarMult(a)); }

    /// In place addition
    template <class SequenceY>
    Self& operator+=(LinearProductSpace<Scalar,SequenceY> const& y) {
      return applyBinaryOp(Add(),y); }

    /// In place subtraction
    Self& operator-=(Self const& y) { return applyBinaryOp(Sub(),y); }

    /// this <- this + a*y
    Self& axpy(Scalar a, Self const& y) { return applyBinaryOp(Axpy(a),y); }

    /// Scalar product
    field_type operator*(Self const& y) const {
      // For some unknown reason, this does not work... (will work in more recent fusion version).
      // return boost::fusion::accumulate(boost::fusion::zip(data,y.data),0,ScalarProduct());
      // We therefore use the following ugly hack workaround
      using namespace boost::fusion;
      zip_view<vector<Sequence const&,Sequence const&> > zipped(vector<Sequence const&,Sequence const&>(data,y.data));
      ScalarProductHack sp;
      for_each(zipped,sp);
      return sp.s;
    }

    /// Scalar product
    field_type dot(Self const& y) const { return *this * y; }

    /**
     * \brief DEPRECATED use vectorFromSequence instead
     *
     * Reads the coefficients sequentially from an input iterator.
     * The InIterator's value type must be convertible to field_type.
     */
    template <class InIterator>
    void read(InIterator i)
    {
      vectorFromSequence(*this,i);
    }

    /**
     * \brief Reads the coefficients of the subrange [rbegin,rend[ sequentially from an input iterator.
     * The InIterator's value type must be convertible to field_type.
     */
    template <class InIterator>
    void read(int rbegin, int rend, InIterator i) { boost::fusion::for_each(data,ReadBlock<InIterator>(rbegin,rend,i)); }

    /**
     * \brief DEPRECATED use vectorToSequence instead
     *
     * Writes the coefficients sequentially to an output iterator (flattening).
     * The field_type must be convertible to the OutIterator's value type.
     */
    template <class OutIterator>
    void write(OutIterator i) const
    {
      vectorToSequence(*this,i);
    }

    /**
     * \brief Writes the coefficients of the subrange [rbegin,rend[ sequentially to an output iterator.
     * The InIterator's value type must be convertible to field_type.
     */
    template <class DataOutIter>
    void write(int rbegin, int rend, DataOutIter i) const
    {
      boost::fusion::for_each(data,WriteBlock<DataOutIter>(rbegin,rend,i));
    }

    /// Euclidean Norm
    double two_norm() const { return std::sqrt((*this)*(*this)); }

    /// Data
    Sequence data;

  private:

    // applies a binary operator to the elements of a pair
    template <class BinaryOp>
    struct PairOp
    {
      PairOp(BinaryOp const& op_): op(op_) {}

      template <class Pair> void operator()(Pair p) const {
        op(boost::fusion::at_c<0>(p),boost::fusion::at_c<1>(p));
      }

    private:
      BinaryOp const& op;
    };

    template <class UnaryOp>
    Self& applyUnaryOp(UnaryOp const& op) {
      boost::fusion::for_each(data,op);
      return *this;
    }

    template <class BinaryOp, class SpaceY>
    Self& applyBinaryOp(BinaryOp const& op, SpaceY const& y) {
      using namespace boost::fusion;
      typedef typename SpaceY::Sequence SequenceY;
      zip_view<vector<Sequence&,SequenceY const&> > zipped(vector<Sequence&,SequenceY const&>(data,y.data));
      for_each(zipped,PairOp<BinaryOp>(op));
      return *this;
    }

    struct Add { template <class T, class S> void operator()(T& a, S const& b) const { a += b; } };
    struct Sub { template <class T> void operator()(T& a, T const& b) const { a -= b; } };
    struct Assign {
      Assign(Scalar s_): s(s_) {}
      template <class T> void operator()(T& a) const { a = s; }
    private: Scalar s;
    };

    struct ScalarMult {
      ScalarMult(Scalar s_): s(s_) {}
      template <class Element> void operator()(Element& e) const { e *= s; }
    private: Scalar s;
    };

    struct Axpy
    {
      Axpy(Scalar s_): s(s_) {}
      template <class T> void operator()(T& a, T const& b) const {

        // Funny runtime error could only be avoided by this funny code.
        // Probably a compiler bug.
        // assert(a.size()==b.size());
        std::cout << "";

        a.axpy(s,b);
      }

    private: Scalar s;
    };

    struct ScalarProduct
    {
      template <class T> struct result {};

      template <class Pair, class T> struct result<ScalarProduct(Pair,T)> { typedef field_type type; };
      template <class Pair>
      field_type operator()(Pair const& pair, field_type res) const {
        using namespace boost::fusion;

        return res ;//+ at_c<0>(pair)*at_c<1>(pair);
      }
    };

    // Ugly hack -- see above
    struct ScalarProductHack
    {
      ScalarProductHack(): s(0) {}
      template <class Pair> void operator()(Pair const& p) const { s += boost::fusion::at_c<0>(p) * boost::fusion::at_c<1>(p); }
      mutable field_type s;
    };

    template <class DataOutIter>
    struct WriteBlock
    {

      WriteBlock(int& rbegin_, int& rend_, DataOutIter& out_): rbegin(rbegin_), rend(rend_), out(out_) {}
      template <class VectorBlock> void operator()(VectorBlock const& v) const
      {
        if (rbegin<=0 && rend>0)
          vectorToSequence(v,out);
        --rbegin;
        --rend;
      }
    private:
      int& rbegin;
      int& rend;
      DataOutIter& out;

    };

    template <class DataInIter>
    struct ReadBlock
    {
      ReadBlock(int& rbegin_, int& rend_, DataInIter& in_): rbegin(rbegin_), rend(rend_), in(in_) {}

      template <class VectorBlock> void operator()(VectorBlock& v) const
      {
        if (rbegin<=0 && rend>0)
          vectorFromSequence(v,in);
        --rbegin;
        --rend;
      }
    private:
      int& rbegin;
      int& rend;
      DataInIter& in;

    };

  };

  /**
   * \ingroup linalgbasic
   * \brief Provides access to the m-th component of a product space.
   *
   * This simplifies the access to individual components of a product space, e.g., a variable set of finite
   * element functions. Instead of
   * \code
   * boost::fusion::at_c<m>(x.data)
   * \endcode
   * one can write
   * \code
   * component<m>(x)
   * \endcode
   *
   * \tparam m the index of the component (nonnegative)
   * \tparam Scalar a scalar type
   * \tparam Sequence a boost::fusion sequence type
   *
   * \return a reference to the m-th component
   *
   * \relates LinearProductSpace
   */
  template <int m, class Scalar, class Sequence>
  typename boost::fusion::result_of::at_c<Sequence const,m>::type component(LinearProductSpace<Scalar,Sequence> const& x) { return boost::fusion::at_c<m>(x.data); }

  template <int m, class Scalar, class Sequence>
  typename boost::fusion::result_of::at_c<Sequence,m>::type component(LinearProductSpace<Scalar,Sequence>& x) { return boost::fusion::at_c<m>(x.data); }

  /**
   * \ingroup linalgbasic
   * \brief writes the coefficients of a vector to a flat scalar sequence
   * \related LinearProductSpace<Scalar,Seq>
   */
  template <class Scalar, class Seq, class OutIter>
  OutIter vectorToSequence(LinearProductSpace<Scalar,Seq> const& v, OutIter i)
  {
    boost::fusion::for_each(v.data,[&](auto& x) { i = vectorToSequence(x,i); });
    return i;
  }

  /**
   * \ingroup linalgbasic
   * \brief reads the coefficients of a vector from a flat scalar sequence
   * \related LinearProductSpace<Scalar,Seq>
   */
  template <class Scalar, class Seq, class InIter>
  InIter vectorFromSequence(LinearProductSpace<Scalar,Seq>& v, InIter i)
  {
    boost::fusion::for_each(v.data,[&](auto& x) { i = vectorFromSequence(x,i); });
    return i;
  }

} // end of namespace Kaskade

#endif


#include "path_finding.h"
#include <iostream>
#include <boost/foreach.hpp>

namespace pf
{

NodeList PathFinding::search(
	const Map&    map,
	const LPNode& nodeStart,
	const LPNode& nodeStop)
{
	NodeList path;

	for (int y = 0; y < map.getHeight(); y++)
	{
		for (int x = 0; x < map.getWidth(); x++)
		{
			LPField field = map.getField(x, y);
			field->closed = false;
			field->node_cost = Field::NodeCost();
		}
	}

	NodeList open;

	nodeStart->setCost(0, nodeStart->calcH(nodeStop));
	open.push_back(nodeStart);

	while (!open.empty())
	{
		LPNode node = getBest(open);

		if (*node == nodeStop->field)
		{
			path = reconstructPath(node);
			break;
		}

		open.remove(node);
		node->field->closed = true;

		NodeList childList = createChild(node, map);

		BOOST_FOREACH(const LPNode& child, childList)
		{
			int tentative_g_score = node->field->node_cost.g + child->calcGFromParent(node);

			NodeList::iterator openIt = open.begin();
			while (openIt != open.end())
			{
				if (**openIt == child)
				{
					break;
				}
				++openIt;
			}
			if (openIt != open.end())
			{
				if (tentative_g_score < (*openIt)->field->node_cost.g)
				{
					LPNode parent = (*openIt)->parent;
					parent->child.remove(*openIt);
					open.erase(openIt);
				}
				else
				{
					continue;
				}
			}

			child->parent = node;
			node->child.push_back(child);

			child->setCost(tentative_g_score, child->calcH(nodeStop));
			open.push_back(child);
		}
	}

	return path;
}

LPNode PathFinding::getBest(const NodeList& nodeList)
{
	NodeList::const_iterator it = nodeList.begin();
	LPNode result = *it;
	++it;
	while (it != nodeList.end())
	{
		if ((*it)->field->node_cost.f < result->field->node_cost.f)
		{
			result = *it;
		}
		++it;
	}
	return result;
}

NodeList PathFinding::reconstructPath(LPNode node)
{
	NodeList path;

	while (node->parent)
	{
		path.push_front(node);
		node = node->parent;
	}
	path.push_front(node);

	return path;
}

NodeList PathFinding::createChild(const LPNode& node, const Map& map)
{
	NodeList nodeList;
	LPNode child;

	child = createChild(node, map, node->field->x + 1, node->field->y);
	if (child)
	{
		nodeList.push_back(child);
	}
	child = createChild(node, map, node->field->x - 1, node->field->y);
	if (child)
	{
		nodeList.push_back(child);
	}
	child = createChild(node, map, node->field->x, node->field->y + 1);
	if (child)
	{
		nodeList.push_back(child);
	}
	child = createChild(node, map, node->field->x, node->field->y - 1);
	if (child)
	{
		nodeList.push_back(child);
	}
	child = createChild(node, map, node->field->x + 1, node->field->y + 1);
	if (child)
	{
		nodeList.push_back(child);
	}
	child = createChild(node, map, node->field->x - 1, node->field->y - 1);
	if (child)
	{
		nodeList.push_back(child);
	}
	child = createChild(node, map, node->field->x + 1, node->field->y - 1);
	if (child)
	{
		nodeList.push_back(child);
	}
	child = createChild(node, map, node->field->x - 1, node->field->y + 1);
	if (child)
	{
		nodeList.push_back(child);
	}

	return nodeList;
}

LPNode PathFinding::createChild(const LPNode& node, const Map& map, int x, int y)
{
	if (0 <= x && x < map.getWidth() &&
		0 <= y && y < map.getHeight())
	{
		if (!node->isParentPosition(x, y))
		{
			LPField field = map.getField(x, y);
			if (!field->isBlocked() && !field->closed)
			{
				LPNode child(new Node(field));
				return child;
			}
		}
	}

	return LPNode();
}

void PathFinding::print(
		const Map& map,
		const LPNode& nodeStart,
		const LPNode& nodeStop,
		const NodeList& path)
{
	for (int y = 0; y < map.getHeight(); y++)
	{
		std::cout << "    ";
		for (int x = 0; x < map.getWidth(); x++)
		{
			LPField field = map.getField(x, y);
			if (*field == *nodeStart->field)
			{
				std::cout << "  o";
			}
			else if (*field == *nodeStop->field)
			{
				std::cout << "  k";
			}
			else if (field->isBlocked())
			{
				std::cout << "###";
			}
			else
			{
				bool found = false;
				BOOST_FOREACH(const LPNode& node, path)
				{
					if (*field == *node->field)
					{
						found = true;
						break;
					}
				}
				if (found)
				{
					std::cout << " + ";
				}
				else
				{
					std::cout.width(3);
					std::cout << field->node_cost.g;
				}
			}
		}
		std::cout << std::endl;
	}
}

}


///////////////////////////////////////////////////////////////////////////////
// BSD 3-Clause License
//
// Copyright (C) 2019-2020, LAAS-CNRS, New York University, Max Planck Gesellschaft,
//                          University of Edinburgh
// Copyright note valid unless otherwise stated in individual files.
// All rights reserved.
///////////////////////////////////////////////////////////////////////////////

#ifndef CROCODDYL_CORE_NUMDIFF_STATE_HPP_
#define CROCODDYL_CORE_NUMDIFF_STATE_HPP_

#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>

#include "crocoddyl/core/fwd.hpp"
#include "crocoddyl/core/state-base.hpp"

namespace crocoddyl {

template <typename _Scalar>
class StateNumDiffTpl : public StateAbstractTpl<_Scalar> {
 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

  typedef _Scalar Scalar;
  typedef MathBaseTpl<Scalar> MathBase;
  typedef StateAbstractTpl<_Scalar> Base;
  typedef typename MathBase::VectorXs VectorXs;
  typedef typename MathBase::MatrixXs MatrixXs;

  explicit StateNumDiffTpl(boost::shared_ptr<Base> state);
  virtual ~StateNumDiffTpl();

  virtual VectorXs zero() const;
  virtual VectorXs rand() const;
  virtual void diff(const Eigen::Ref<const VectorXs>& x0, const Eigen::Ref<const VectorXs>& x1,
                    Eigen::Ref<VectorXs> dxout) const;
  virtual void integrate(const Eigen::Ref<const VectorXs>& x, const Eigen::Ref<const VectorXs>& dx,
                         Eigen::Ref<VectorXs> xout) const;
  /**
   * @brief This computes the Jacobian of the diff method by finite
   * differentiation:
   * \f{equation}{
   *    Jfirst[:,k] = diff(int(x_1, dx_dist), x_2) - diff(x_1, x_2)/disturbance
   * \f}
   * and
   * \f{equation}{
   *    Jsecond[:,k] = diff(x_1, int(x_2, dx_dist)) - diff(x_1, x_2)/disturbance
   * \f}
   *
   * @param Jfirst
   * @param Jsecond
   * @param firstsecond
   */
  virtual void Jdiff(const Eigen::Ref<const VectorXs>& x0, const Eigen::Ref<const VectorXs>& x1,
                     Eigen::Ref<MatrixXs> Jfirst, Eigen::Ref<MatrixXs> Jsecond, Jcomponent firstsecond = both) const;
  /**
   * @brief This computes the Jacobian of the integrate method by finite
   * differentiation:
   * \f{equation}{
   *    Jfirst[:,k] = diff( int(x, d_x), int( int(x, dx_dist), dx) )/disturbance
   * \f}
   * and
   * \f{equation}{
   *    Jsecond[:,k] = diff( int(x, d_x), int( x, dx + dx_dist) )/disturbance
   * \f}
   *
   * @param Jfirst
   * @param Jsecond
   * @param firstsecond
   */
  virtual void Jintegrate(const Eigen::Ref<const VectorXs>& x, const Eigen::Ref<const VectorXs>& dx,
                          Eigen::Ref<MatrixXs> Jfirst, Eigen::Ref<MatrixXs> Jsecond,
                          const Jcomponent firstsecond = both, const AssignmentOp op = setto) const;

  virtual void JintegrateTransport(const Eigen::Ref<const VectorXs>& x, const Eigen::Ref<const VectorXs>& dx,
                                   Eigen::Ref<MatrixXs> Jin, const Jcomponent firstsecond = both) const;

  const Scalar& get_disturbance() const;
  void set_disturbance(const Scalar& disturbance);

 private:
  /**
   * @brief This is the state we need to compute the numerical differentiation
   * from.
   */
  boost::shared_ptr<Base> state_;
  /**
   * @brief This the increment used in the finite differentiation and integration.
   */
  Scalar disturbance_;

 protected:
  using Base::has_limits_;
  using Base::lb_;
  using Base::ndx_;
  using Base::nq_;
  using Base::nv_;
  using Base::nx_;
  using Base::ub_;
};

}  // namespace crocoddyl

/* --- Details -------------------------------------------------------------- */
/* --- Details -------------------------------------------------------------- */
/* --- Details -------------------------------------------------------------- */
#include "crocoddyl/core/numdiff/state.hxx"

#endif  // CROCODDYL_CORE_NUMDIFF_STATE_HPP_


//
// Add --log_level=message to see the messages!
//
#include <boost/test/unit_test.hpp>

#include <sstream>

#include "layers/fclayer.h"
#include "core/training.h"
#include "core/utils.h"
#include "core/functions.h"

#include "timer.h"
#include "test_utils.h"
#include "test_layers.h"

using namespace std;
using namespace boost;
using namespace boost::unit_test;
using namespace yann;
using namespace yann::test;

struct FullyConnectedLayerTestFixture
{
  FullyConnectedLayerTestFixture()
  {

  }
  ~FullyConnectedLayerTestFixture()
  {

  }
};
// struct FullyConnectedLayerTestFixture

BOOST_FIXTURE_TEST_SUITE(FullyConnectedLayerTest, FullyConnectedLayerTestFixture);

BOOST_AUTO_TEST_CASE(IO_Test)
{
  BOOST_TEST_MESSAGE("*** FullyConnectedLayer IO test ...");

  const MatrixSize input_size = 5;
  const MatrixSize output_size = 3;
  auto one = make_unique<FullyConnectedLayer>(input_size, output_size);
  one->init(Layer::InitMode_Random, boost::none);

  BOOST_TEST_MESSAGE("FullyConnectedLayer before writing to file: " << "\n" << *one);
  ostringstream oss;
  oss << *one;
  BOOST_CHECK(!oss.fail());

  auto two = make_unique<FullyConnectedLayer>(input_size, output_size);
  std::istringstream iss(oss.str());
  iss >> *two;
  BOOST_CHECK(!iss.fail());
  BOOST_TEST_MESSAGE("FullyConnectedLayer after loading from file: " << "\n" << *two);

  BOOST_CHECK(one->is_equal(*two, TEST_TOLERANCE));
}

BOOST_AUTO_TEST_CASE(FeedForward_Test)
{
  BOOST_TEST_MESSAGE("*** FullyConnectedLayer FeedForward test ...");

  const MatrixSize input_size = 3;
  const MatrixSize output_size = 2;
  const MatrixSize batch_size = 2;

  Matrix ww(input_size, output_size);
  Vector bb(output_size);
  VectorBatch input, expected_output;
  resize_batch(input, batch_size, input_size);
  resize_batch(expected_output, batch_size, output_size);

  ww <<
      0.1, 0.4,
      0.2, 0.5,
      0.3, 0.6;

  bb <<
      0.003, 0.007;

  input <<
      0.1, 0.2, 0.3,
      0.4, 0.5, 0.6;

  auto layer = make_unique<FullyConnectedLayer>(input_size, output_size);
  BOOST_CHECK(layer);
  layer->set_values(ww, bb);

  // identity
  layer->set_activation_function(make_unique<IdentityFunction>());
  expected_output <<
      0.143, 0.327,
      0.323, 0.777;
  test_layer_feedforward(*layer, input, expected_output);

  // sigmoid
  layer->set_activation_function(make_unique<SigmoidFunction>());
  expected_output <<
      0.53569, 0.58103,
      0.58006, 0.68503;
  test_layer_feedforward(*layer, input, expected_output);
}

BOOST_AUTO_TEST_CASE(Backprop_Test)
{
  BOOST_TEST_MESSAGE("*** FullyConnectedLayer backprop test ...");

  const MatrixSize input_size = 3;
  const MatrixSize output_size = 2;
  const MatrixSize batch_size = 2;
  const double learning_rate = 0.01;
  const size_t epochs = 1000;

  Matrix ww(input_size, output_size);
  Vector bb(output_size);
  VectorBatch input, expected_output;
  resize_batch(input, batch_size, input_size);
  resize_batch(expected_output, batch_size, output_size);

  ww <<
      1, 4,
      2, 5,
      3, 6;

  bb <<
      0.3, 0.7;

  input <<
      0, 1, 2,
      3, 4, 5;

  expected_output <<
      14.3, 32.7,
      32.3, 77.7;

  auto layer = make_unique<FullyConnectedLayer>(input_size, output_size);
  BOOST_CHECK(layer);
  layer->set_activation_function(make_unique<IdentityFunction>());
  layer->set_values(ww, bb);

  test_layer_backprop(
      *layer,
      input,
      boost::none,
      expected_output,
      make_unique<QuadraticCost>(),
      learning_rate,
      epochs
  );
}

BOOST_AUTO_TEST_CASE(Backprop_WithSampling_Test)
{
  BOOST_TEST_MESSAGE("*** FullyConnectedLayer backprop with sampling test ...");

  const MatrixSize input_size = 3;
  const MatrixSize output_size = 2;
  const MatrixSize batch_size = 2;
  const double learning_rate = 0.01;
  const size_t epochs = 1000;
  const double sampling_rate = 0.5;

  Matrix ww(input_size, output_size);
  Vector bb(output_size);
  VectorBatch input, expected_output;
  resize_batch(input, batch_size, input_size);
  resize_batch(expected_output, batch_size, output_size);

  ww <<
      1, 4,
      2, 5,
      3, 6;

  bb <<
      0.3, 0.7;

  input <<
      0, 1, 2,
      3, 4, 5;

  expected_output <<
      14.3, 32.7,
      32.3, 77.7;

  auto layer = make_unique<FullyConnectedLayer>(input_size, output_size);
  BOOST_CHECK(layer);
  layer->set_activation_function(make_unique<IdentityFunction>());
  layer->set_values(ww, bb);
  layer->set_sampling_rate(sampling_rate);

  test_layer_backprop(
      *layer,
      input,
      boost::none,
      expected_output,
      make_unique<QuadraticCost>(),
      learning_rate,
      epochs
  );
}

BOOST_AUTO_TEST_CASE(RandomBackprop_Test)
{
  BOOST_TEST_MESSAGE("*** FullyConnectedLayer backprop with random inputs and weights ...");

  const MatrixSize input_size  = 4;
  const MatrixSize output_size = 3;
  const MatrixSize batch_size  = 5;
  const double learning_rate = 0.75;
  const size_t epochs = 200;

  auto layer = make_unique<FullyConnectedLayer>(input_size, output_size);
  BOOST_CHECK(layer);
  layer->set_activation_function(make_unique<SigmoidFunction>());
  layer->init(Layer::InitMode_Random, Layer::InitContext(123));

  test_layer_backprop_from_random(
      *layer,
      batch_size,
      make_unique<CrossEntropyCost>(),
      learning_rate,
      epochs
  );
}

BOOST_AUTO_TEST_CASE(Training_WithIdentity_Test)
{
  BOOST_TEST_MESSAGE("*** FullyConnectedLayer with Identity activation training test ...");

  const MatrixSize input_size  = 18;
  const MatrixSize output_size = 3;
  const MatrixSize batch_size = 2;

  VectorBatch input, expected;
  resize_batch(input, batch_size, input_size);
  resize_batch(expected, batch_size, output_size);

  input <<
      0.01, 0.02, 0.11, 0.10, 0.13, 0.18,
      0.03, 0.04, 0.12, 0.09, 0.14, 0.17,
      0.05, 0.06, 0.07, 0.08, 0.15, 0.16,
      ///////////////////////////////////
      0.02, 0.06, 0.08, 0.10, 0.13, 0.14,
      0.03, 0.02, 0.07, 0.09, 0.16, 0.15,
      0.04, 0.05, 0.13, 0.11, 0.16, 0.18;

  expected <<
      0.6, 0.7, 0.5,
      //////////////
      0.3, 0.8, 0.1;

  // create layer
  auto layer = make_unique<FullyConnectedLayer>(input_size, output_size);
  BOOST_CHECK(layer);
  layer->set_activation_function(make_unique<IdentityFunction>());
  layer->init(Layer::InitMode_Zeros, boost::none);

  // test
  test_layer_training(
      *layer,
      input,
      expected,
      batch_size, // tests num
      make_unique<QuadraticCost>(),
      0.75, // learning rate
      2000  // epochs
  );
}

BOOST_AUTO_TEST_CASE(Training_WithIdentityAndSampling_Test)
{
  BOOST_TEST_MESSAGE("*** FullyConnectedLayer with Identity activation and with sampling training test ...");

  const MatrixSize input_size  = 18;
  const MatrixSize output_size = 3;
  const MatrixSize batch_size = 2;
  const double sampling_rate = 0.334;

  VectorBatch input, expected;
  resize_batch(input, batch_size, input_size);
  resize_batch(expected, batch_size, output_size);

  input <<
      0.01, 0.02, 0.11, 0.10, 0.13, 0.18,
      0.03, 0.04, 0.12, 0.09, 0.14, 0.17,
      0.05, 0.06, 0.07, 0.08, 0.15, 0.16,
      ///////////////////////////////////
      0.02, 0.06, 0.08, 0.10, 0.13, 0.14,
      0.03, 0.02, 0.07, 0.09, 0.16, 0.15,
      0.04, 0.05, 0.13, 0.11, 0.16, 0.18;

  expected <<
      0.6, 0.7, 0.5,
      //////////////
      0.3, 0.8, 0.1;

  // create layer
  auto layer = make_unique<FullyConnectedLayer>(input_size, output_size);
  BOOST_CHECK(layer);
  layer->set_activation_function(make_unique<IdentityFunction>());
  layer->set_sampling_rate(sampling_rate);
  layer->init(Layer::InitMode_Zeros, boost::none);

  // test
  test_layer_training(
      *layer,
      input,
      expected,
      batch_size, // tests num
      make_unique<QuadraticCost>(),
      0.75, // learning rate
      2000  // epochs
  );
}

BOOST_AUTO_TEST_CASE(Training_WithSigmoid_Test)
{
  BOOST_TEST_MESSAGE("*** FullyConnectedLayer with Sigmoid activation training test ...");

  const MatrixSize input_size  = 18;
  const MatrixSize output_size = 3;
  const MatrixSize batch_size = 2;

  VectorBatch input, expected;
  resize_batch(input, batch_size, input_size);
  resize_batch(expected, batch_size, output_size);

  input <<
      0.01, 0.02, 0.11, 0.10, 0.13, 0.18,
      0.03, 0.04, 0.12, 0.09, 0.14, 0.17,
      0.05, 0.06, 0.07, 0.08, 0.15, 0.16,
      ///////////////////////////////////
      0.02, 0.06, 0.08, 0.10, 0.13, 0.14,
      0.03, 0.02, 0.07, 0.09, 0.16, 0.15,
      0.04, 0.05, 0.13, 0.11, 0.16, 0.18;

  expected <<
      0.6, 0.7, 0.5,
      //////////////
      0.3, 0.8, 0.1;

  // create layer
  auto layer = make_unique<FullyConnectedLayer>(input_size, output_size);
  BOOST_CHECK(layer);
  layer->set_activation_function(make_unique<SigmoidFunction>());
  layer->init(Layer::InitMode_Zeros, boost::none);

  // test
  test_layer_training(
      *layer,
      input,
      expected,
      batch_size, // tests num
      make_unique<CrossEntropyCost>(),
      3.0,  // learning rate
      5000 // epochs
  );
}

BOOST_AUTO_TEST_CASE(Training_WithSigmoidAndSampling_Test)
{
  BOOST_TEST_MESSAGE("*** FullyConnectedLayer with Sigmoid activation with sampling training test ...");

  const MatrixSize input_size  = 18;
  const MatrixSize output_size = 3;
  const MatrixSize batch_size = 2;
  const double sampling_rate = 0.334;

  VectorBatch input, expected;
  resize_batch(input, batch_size, input_size);
  resize_batch(expected, batch_size, output_size);

  input <<
      0.01, 0.02, 0.11, 0.10, 0.13, 0.18,
      0.03, 0.04, 0.12, 0.09, 0.14, 0.17,
      0.05, 0.06, 0.07, 0.08, 0.15, 0.16,
      ///////////////////////////////////
      0.02, 0.06, 0.08, 0.10, 0.13, 0.14,
      0.03, 0.02, 0.07, 0.09, 0.16, 0.15,
      0.04, 0.05, 0.13, 0.11, 0.16, 0.18;

  expected <<
      0.6, 0.7, 0.5,
      //////////////
      0.3, 0.8, 0.1;

  // create layer
  auto layer = make_unique<FullyConnectedLayer>(input_size, output_size);
  BOOST_CHECK(layer);
  layer->set_activation_function(make_unique<SigmoidFunction>());
  layer->init(Layer::InitMode_Zeros, boost::none);
  layer->set_sampling_rate(sampling_rate);

  // test
  test_layer_training(
      *layer,
      input,
      expected,
      batch_size, // tests num
      make_unique<CrossEntropyCost>(),
      3.0,  // learning rate
      5000 // epochs
  );
}

BOOST_AUTO_TEST_CASE(RandomTraining_Test)
{
  BOOST_TEST_MESSAGE("*** FullyConnectedLayer training with random inputs ...");

  const MatrixSize input_size  = 18;
  const MatrixSize output_size = 3;
  const MatrixSize batch_size = 5;
  const double learning_rate = 0.5;
  const size_t epochs = 1000;

  auto layer = make_unique<FullyConnectedLayer>(input_size, output_size);
  BOOST_CHECK(layer);
  layer->set_activation_function(make_unique<SigmoidFunction>());

  test_layer_training_from_random(
      *layer,
      batch_size,
      batch_size, // tests num
      make_unique<CrossEntropyCost>(),
      learning_rate,
      epochs
  );
}

BOOST_AUTO_TEST_SUITE_END()


// Distributed under the BSD License, see accompanying LICENSE.txt
// (C) Copyright 2010 John-John Tedro et al.
#include "image_base.hpp"

#include <cstring>

#include <boost/numeric/conversion/cast.hpp>

// image_base
void image_base::fill(color &q)
{
  for (pos_t x = 0; x < get_width(); x++) {
    for (pos_t y = 0; y < get_height(); y++) {
      set_pixel(x, y, q);
    }
  }
}

void image_base::clear() {
  boost::shared_array<color> line(new color[get_width()]);
  ::memset(line.get(), 0x0, get_width() * sizeof(color));

  for (pos_t y = 0; y < get_height(); y++)
    set_line(y, 0, get_width(), line.get());
}

void image_base::composite(int x, int y, image_operations_ptr opers)
{
  std::vector<image_operation>::size_type i = 0;

  align(x, y, opers->max_x, opers->max_y);

  while (i++ < opers->operations.size())
  {
    image_operation op = opers->operations[i];
    blend_pixel(x + op.x, y + op.y, op.c);
  }
}

void image_base::draw_line(pos_t x1, pos_t y1, pos_t x2, pos_t y2, color &c)
{
    int sx, sy;
    int dx = (x1<x2) ? x2-x1 : x1-x2;
    int dy = (y1<y2) ? y2-y1 : y1-y2;
    sx = (x1<x2) ? 1 : -1 ;
    sy = (y1<y2) ? 1 : -1 ;
    int err = dx-dy;

    do
    {
        this->set_pixel(x1, y1, c);
        int e2 = 2*err;
        if(e2 > -dy)
        {
            err -= dy;
            x1 += sx;
        }
        if(e2 < dx)
        {
            err += dx;
            y1 += sy;
        }
    } while(x1 != x2 || y1 != y2);
}

void image_base::safe_blend_pixel(pos_t x, pos_t y, color &c)
{
  if (x >= width) return;
  if (y >= height) return;
  blend_pixel(x, y, c);
}

void image_base::resize(image_ptr target) {
  if (target->get_width() > get_width()) {
    // scale up
    unsigned int factor = target->get_width() / get_width();
    boost::shared_array<color> line(new color[get_width()]);

    unsigned int scanline = 0;
    bool first = false;

    for (pos_t y = 0; y < target->get_height(); y++) {
      pos_t y_p = (y / factor);

      if (y_p != scanline || !first) {
        get_line(y_p, pos_t(0), get_width(), line.get());
        first = true;
        scanline = y_p;
      }

      for (pos_t x = 0; x < target->get_width(); x++) {
        pos_t x_p = (x / factor);
        target->set_pixel(x, y, line[x_p]);
      }
    }
  }
  else {
    // scale down
    unsigned int factor = get_width() / target->get_width();

    boost::shared_array<color> line(new color[get_width()]);

    for (pos_t y = 0; y < target->get_height(); y++) {
      pos_t y_p = ((y * factor) + factor / 2);
      get_line(y_p, pos_t(0), get_width(), line.get());

      for (pos_t x = 0; x < target->get_width(); x++) {
        pos_t x_p = ((x * factor) + factor / 2);
        target->set_pixel(x, y, line[x_p]);
      }
    }
  }
}


/*!
@file
Forward declares `boost::hana::Group`.

@copyright Louis Dionne 2015
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_HANA_FWD_GROUP_HPP
#define BOOST_HANA_FWD_GROUP_HPP

#include <boost/hana/detail/std/forward.hpp>
#include <boost/hana/fwd/core/datatype.hpp>
#include <boost/hana/fwd/core/operators.hpp>

namespace boost { namespace hana {
    //! @ingroup group-concepts
    //! The `Group` concept represents `Monoid`s where all objects have
    //! an inverse w.r.t. the `Monoid`'s binary operation.
    //!
    //! A [Group][1] is an algebraic structure built on top of a `Monoid`
    //! which adds the ability to invert the action of the `Monoid`'s binary
    //! operation on any element of the set. Specifically, a `Group` is a
    //! `Monoid` `(S, +)` such that every element `s` in `S` has an inverse
    //! (say `s'`) which is such that
    //! @code
    //!     s + s' == s' + s == identity of the Monoid
    //! @endcode
    //!
    //! There are many examples of `Group`s, one of which would be the
    //! additive `Monoid` on integers, where the inverse of any integer
    //! `n` is the integer `-n`. The method names used here refer to
    //! exactly this model.
    //!
    //!
    //! Superclass
    //! ----------
    //! `Monoid`
    //!
    //!
    //! Laws
    //! ----
    //! For all objects `x` of a `Group` `G`, the following laws must be
    //! satisfied:
    //! @code
    //!     plus(x, negate(x)) == zero<G>() // right inverse
    //!     plus(negate(x), x) == zero<G>() // left inverse
    //! @endcode
    //!
    //!
    //! Minimal complete definitions
    //! ----------------------------
    //! 1. `minus`\n
    //! When `minus` is specified, the `negate` method is defaulted by setting
    //! @code
    //!     negate(x) = minus(zero<G>(), x)
    //! @endcode
    //!
    //! 2. `negate`\n
    //! When `negate` is specified, the `minus` method is defaulted by setting
    //! @code
    //!     minus(x, y) = plus(x, negate(y))
    //! @endcode
    //!
    //!
    //! Provided models
    //! ---------------
    //! 1. For non-boolean arithmetic data types\n
    //! A data type `T` is arithmetic if `std::is_arithmetic<T>::%value` is
    //! true. For a non-boolean arithmetic data type `T`, a model of `Group`
    //! is automatically defined by setting
    //! @code
    //!     minus(x, y) = (x - y)
    //!     negate(x) = -x
    //! @endcode
    //!
    //! @note
    //! The rationale for not providing a Group model for `bool` is the same
    //! as for not providing a `Monoid` model.
    //!
    //!
    //! Operators
    //! ---------
    //! For convenience, the following operators are provided as an
    //! equivalent way of calling the corresponding method:
    //! @code
    //!     binary -  ->  minus
    //!     unary -   ->  negate
    //! @endcode
    //!
    //!
    //! Structure-preserving functions
    //! ------------------------------
    //! Let `A` and `B` be two `Group`s. A function `f : A -> B` is said to
    //! be a [Group morphism][2] if it preserves the group structure between
    //! `A` and `B`. Rigorously, for all objects `x, y` of data type `A`,
    //! @code
    //!     f(plus(x, y)) == plus(f(x), f(y))
    //! @endcode
    //! Because of the `Group` structure, it is easy to prove that the
    //! following will then also be satisfied:
    //! @code
    //!     f(negate(x)) == negate(f(x))
    //!     f(zero<A>()) == zero<B>()
    //! @endcode
    //! Functions with these properties interact nicely with `Group`s, which
    //! is why they are given such a special treatment.
    //!
    //!
    //! [1]: http://en.wikipedia.org/wiki/Group_(mathematics)
    //! [2]: http://en.wikipedia.org/wiki/Group_homomorphism
    struct Group { };

    //! Subtract two elements of a group.
    //! @relates Group
    //!
    //! Specifically, this performs the `Monoid` operation on the first
    //! argument and on the inverse of the second argument, thus being
    //! equivalent to:
    //! @code
    //!     minus(x, y) == plus(x, negate(y))
    //! @endcode
    //!
    //!
    //! Cross-type version of the method
    //! --------------------------------
    //! The `minus` method is "overloaded" to handle distinct data types
    //! with certain properties. Specifically, `minus` is defined for
    //! _distinct_ data types `A` and `B` such that
    //! 1. `A` and `B` share a common data type `C`, as determined by the
    //!    `common` metafunction
    //! 2. `A`, `B` and `C` are all `Group`s when taken individually
    //! 3. `to<C> : A -> B` and `to<C> : B -> C` are `Group`-embeddings, as
    //!    determined by the `is_embedding` metafunction.
    //!
    //! The definition of `minus` for data types satisfying the above
    //! properties is obtained by setting
    //! @code
    //!     minus(x, y) = minus(to<C>(x), to<C>(y))
    //! @endcode
    //!
    //!
    //! Example
    //! -------
    //! @snippet example/group.cpp minus
#ifdef BOOST_HANA_DOXYGEN_INVOKED
    constexpr auto minus = [](auto&& x, auto&& y) -> decltype(auto) {
        return tag-dispatched;
    };
#else
    template <typename T, typename U, typename = void>
    struct minus_impl;

    struct _minus {
        template <typename X, typename Y>
        constexpr decltype(auto) operator()(X&& x, Y&& y) const {
            return minus_impl<
                typename datatype<X>::type, typename datatype<Y>::type
            >::apply(
                detail::std::forward<X>(x),
                detail::std::forward<Y>(y)
            );
        }
    };

    constexpr _minus minus{};
#endif

    //! Return the inverse of an element of a group.
    //! @relates Group
    //!
    //!
    //! Example
    //! -------
    //! @snippet example/group.cpp negate
#ifdef BOOST_HANA_DOXYGEN_INVOKED
    constexpr auto negate = [](auto&& x) -> decltype(auto) {
        return tag-dispatched;
    };
#else
    template <typename G, typename = void>
    struct negate_impl;

    struct _negate {
        template <typename X>
        constexpr decltype(auto) operator()(X&& x) const {
            return negate_impl<typename datatype<X>::type>::apply(
                detail::std::forward<X>(x)
            );
        }
    };

    constexpr _negate negate{};
#endif

    template <>
    struct operators::of<Group>
        : decltype(minus), decltype(negate)
    { };
}} // end namespace boost::hana

#endif // !BOOST_HANA_FWD_GROUP_HPP


//
// HeatThreshold.cpp
// Performs a threshold for hot areas based on the config
//

#include "pipeline/HeatThreshold.hpp"
#include "config/ConfigParser.hpp"

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <boost/algorithm/string.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

namespace Pipeline
{
    const int MAX_RANGES { 4 };

    // Constructor
    HeatThreshold::HeatThreshold(const std::string &name, bool saveResults) : PipelineElement(name, saveResults)
    {
        // read in the heat ranges
        std::string bins = Config::ConfigParser::GetInstance().GetValue<std::string>("config.pipeline.histogram.bins");
        std::vector<std::string> binValues;
        boost::split(binValues, bins, boost::is_space());

        // check to ensure max 4 ranges
        if (binValues.size() > MAX_RANGES) {
            std::cerr << "\nOnly a max of 4 ranges is supported. Ignoring excess ranges" << std::endl;
            binValues.erase(binValues.begin() + MAX_RANGES, binValues.end());
        }

        std::transform(binValues.begin(), binValues.end(), std::back_inserter(m_HeatRanges), [&](const std::string& str) { return std::stoi(str); });
    }

    // Process
    void HeatThreshold::Process(const cv::Mat& input, cv::Mat& output, const std::shared_ptr<void>& previousElementResult, std::shared_ptr<void>& result, const std::string& filename)
    {
        int lower = 0;
        int upper = 0;
        int c = 0;

        // set output image
        output = cv::Mat(input.rows, input.cols, CV_8UC4);

        // apply threshold and save in separate channel of output
        for (auto iter = m_HeatRanges.begin(); iter != m_HeatRanges.end(); iter++)
        {
            lower = *iter;
            auto nextIter = std::next(iter, 1);
            upper = (nextIter != m_HeatRanges.end() ? *nextIter : 256);

            ThresholdImage(input, output, c, lower, upper);

            c++;
        }

        // save intermediate results if required
        if (m_SaveIntermediateResults)
        {
            std::vector<cv::Mat> thresholds;
            cv::split(output, thresholds);
            c = 0;

            for (int i = 0; i < m_HeatRanges.size(); i++) {
                SaveResult(thresholds[i], std::to_string(c++) + "_" + filename);
            }
        }
    }

    // Apply threshold to input and store in given channel
    void HeatThreshold::ThresholdImage(const cv::Mat &input, cv::Mat &output, int channel, int lower, int upper)
    {
        cv::Mat thresholdOutput;
        cv::threshold(input, thresholdOutput, lower, 255, cv::THRESH_TOZERO);
        cv::Vec4b channels;

        for (int row = 0; row < output.rows; row++)
        {
            for (int col = 0; col < output.cols; col++)
            {
                channels = output.at<cv::Vec4b>(row, col);
                channels[channel] = thresholdOutput.at<uchar>(row, col);
                output.at<cv::Vec4b>(row, col) = channels;
            }
        }
    }
}


/*
@copyright Louis Dionne 2014
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/hana/list/instance.hpp>

#include <boost/hana/detail/assert.hpp>
using namespace boost::hana;

int main() {
    BOOST_HANA_CONSTANT_ASSERT(is_empty(list()));
    BOOST_HANA_CONSTANT_ASSERT(!is_empty(list(0)));
    BOOST_HANA_CONSTANT_ASSERT(!is_empty(list(0, '1')));
    BOOST_HANA_CONSTANT_ASSERT(!is_empty(list(0, '1', 2.2)));
}


/*
*
*  Copyright 2011, 2012 by the CALATK development team
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
*
*
*/

#include <iostream>
#include <fstream>

#ifdef USE_BOOST

// boost includes
#include <boost/program_options/option.hpp>
#include <boost/program_options/positional_options.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/cmdline.hpp>

#include "boost/date_time/gregorian/gregorian.hpp"
#include "boost/date_time/posix_time/posix_time.hpp"

#endif

// itk includes

#include <itkImageFileReader.h>
#include <itkImageFileWriter.h>
#include <itkImage.h>
#include <itkVectorImage.h>

#include <itkDiffusionTensor3D.h>
#include "lstatGEECLP.h"

#include "itkLongitudinalStatisticsFilter.h"

const unsigned char DIMENSION = 3;

typedef float MyRealType;
typedef unsigned int MyIntegerType;
typedef itk::LongitudinalStatisticsFilter< MyRealType, MyIntegerType > LongitudinalStatisticsFilterType;
typedef itk::Image<MyRealType, DIMENSION> RealImageType;
typedef itk::VectorImage<MyRealType, DIMENSION> RealVectorImageType;

int outputAllSettings(int argc, const char* argv[] )
{
  PARSE_ARGS;

  std::cout << "Parameter settings:" << std::endl;
  std::cout << "-----------------------------------------" << std::endl;
  std::cout << "covarianceOutputVolume      = " << covarianceOutputVolume << std::endl;
  std::cout << "rawPValueOutputVolume       = " << rawPValueOutputVolume << std::endl;
  std::cout << "RSPvalueOutputVolume        = " << RSPvalueOutputVolume << std::endl;
  std::cout << "SCOREvalueOutputVolume      = " << SCOREvalueOutputVolume << std::endl;
  std::cout << "WALDvalueOutputVolume       = " << WALDvalueOutputVolume << std::endl;
  std::cout << "maskInputVolume             = " << maskInputVolume << std::endl;
  std::cout << "BETAOutputVolume            = " << BETAOutputVolume << std::endl;
  std::cout << "sCaseFile                   = " << sCaseFile << std::endl;
  std::cout << "sDesignMatrix               = " << sDesignMatrix << std::endl;
  std::cout << "sConstraintMatrixFileName   = " << sConstraintMatrix << std::endl;
  std::cout << "sPathPrefix                 = " << sPathPrefix << std::endl;
  std::cout << "bVERBOSE                    = " << bVERBOSE << std::endl;
  std::cout << "bNoLogTransform             = " << bNoLogTransform << std::endl;
  std::cout << "bNoTimePointScaling         = " << bNoTimePointScaling << std::endl;
  std::cout << "-----------------------------------------" << std::endl;
  std::cout << std::endl << std::endl << std::endl;

  return 0;

}

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

  outputAllSettings( argc, argv );

  PARSE_ARGS;

  LongitudinalStatisticsFilterType::Pointer pLongitudinalImageFilter = LongitudinalStatisticsFilterType::New();

  // set all the command line options

  pLongitudinalImageFilter->SetInput( &sCaseFile );
  pLongitudinalImageFilter->SetVerbose( bVERBOSE );
  pLongitudinalImageFilter->SetConstraintMatrixFileName( sConstraintMatrix );
  pLongitudinalImageFilter->SetPathPrefix( sPathPrefix );
  pLongitudinalImageFilter->SetDesignMatrix( sDesignMatrix );
  pLongitudinalImageFilter->SetCovaryWithGroup( bCovaryWithGroup );
  pLongitudinalImageFilter->SetCovaryWithGender( bCovaryWithGender );
  pLongitudinalImageFilter->SetMaskImage( maskInputVolume );
  pLongitudinalImageFilter->SetNoLogTransform( bNoLogTransform );
  pLongitudinalImageFilter->SetNoTimePointScaling( bNoTimePointScaling );

  // do it

#ifdef USE_BOOST
  if ( bVERBOSE )
    {
    using namespace boost::posix_time;
    ptime now = second_clock::local_time();
    // write out time when the computation started
    std::cout << "Processing started at: " << now << std::endl << std::endl;
    }
#endif

  pLongitudinalImageFilter->Update();

  // now get the outputs and write everything

  if (bVERBOSE)
    std::cerr << "Writing the final results." << std::endl;

  // Write reconstructed DWI file
  try
  {
    typedef itk::ImageFileWriter<RealImageType> RealFileWriterType;
    typedef itk::ImageFileWriter<RealVectorImageType> RealVectorFileWriterType;

    RealFileWriterType::Pointer realImageWriter = RealFileWriterType::New();
    RealVectorFileWriterType::Pointer realVectorImageWriter = RealVectorFileWriterType::New();

    // write them out one by one

    // covariance

    if ( covarianceOutputVolume.compare("None") != 0 )
      {
      if ( bVERBOSE )
	std::cout << "Writing covariance volume to: " << covarianceOutputVolume << " ... ";

      realVectorImageWriter->SetFileName( covarianceOutputVolume.c_str() );
      realVectorImageWriter->SetInput( pLongitudinalImageFilter->GetCovarianceOutputVolume() );
      realVectorImageWriter->SetUseCompression(true);
      realVectorImageWriter->Update();

      if ( bVERBOSE )
	std::cout << "done." << std::endl;
      }

    // raw p-value

    if ( rawPValueOutputVolume.compare("None") != 0 )
      {
      if ( bVERBOSE )
	std::cout << "Writing raw p-value image to: " << rawPValueOutputVolume << " ... ";

      realImageWriter->SetFileName( rawPValueOutputVolume.c_str() );
      realImageWriter->SetInput( pLongitudinalImageFilter->GetRawPValueOutputVolume() );
      realImageWriter->SetUseCompression(true);
      realImageWriter->Update();

      if ( bVERBOSE )
	std::cout << "done." << std::endl;
      }

    // RSP-value

    if ( RSPvalueOutputVolume.compare("None") != 0 )
      {
      if ( bVERBOSE )
	std::cout << "Writing the RS p-value image to: " << RSPvalueOutputVolume << " ... ";

      realImageWriter->SetFileName( RSPvalueOutputVolume.c_str() );
      realImageWriter->SetInput( pLongitudinalImageFilter->GetRSPValueOutputVolume() );
      realImageWriter->SetUseCompression(true);
      realImageWriter->Update();

      if ( bVERBOSE )
	std::cout << "done." << std::endl;
      }

    // SCORE

    if ( SCOREvalueOutputVolume.compare("None") != 0 )
      {
      if ( bVERBOSE )
	std::cout << "Writing SCORE image to: " << SCOREvalueOutputVolume << " ... ";

      realImageWriter->SetFileName( SCOREvalueOutputVolume.c_str() );
      realImageWriter->SetInput( pLongitudinalImageFilter->GetSCOREValueOutputVolume() );
      realImageWriter->SetUseCompression(true);
      realImageWriter->Update();

      if ( bVERBOSE )
	std::cout << "done." << std::endl;
      }

    // WALD

    if ( WALDvalueOutputVolume.compare("None") != 0 )
      {
      if ( bVERBOSE )
	std::cout << "Writing WALD image to: " << WALDvalueOutputVolume << " ... ";

      realImageWriter->SetFileName( WALDvalueOutputVolume.c_str() );
      realImageWriter->SetInput( pLongitudinalImageFilter->GetWALDValueOutputVolume() );
      realImageWriter->SetUseCompression(true);
      realImageWriter->Update();

      if ( bVERBOSE )
	std::cout << "done." << std::endl;
      }

    // now write out the betas: this is the filter output

    if ( bVERBOSE )
      std::cout << "Writing the BETA image to: " << BETAOutputVolume << " ... ";

    realVectorImageWriter->SetFileName( BETAOutputVolume.c_str() );
    realVectorImageWriter->SetInput( pLongitudinalImageFilter->GetOutput() );
    realVectorImageWriter->SetUseCompression(true);
    realVectorImageWriter->Update();

    if ( bVERBOSE )
      std::cout << "done." << std::endl;

  }
  catch (itk::ExceptionObject & e)
  {
    std::cerr << e << std::endl;
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;

}


/*=============================================================================
    Copyright (c) 2001-2003 Joel de Guzman
    Copyright (c) 2001-2003 Hartmut Kaiser
    http://spirit.sourceforge.net/

    Use, modification and distribution is subject to the Boost Software
    License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
    http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_assign_actor.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <iostream>
#include <climits>

using namespace std;
using namespace BOOST_SPIRIT_CLASSIC_NS;

template <typename T>
struct ts_real_parser_policies : public ureal_parser_policies<T>
{
    //  These policies can be used to parse thousand separated
    //  numbers with at most 2 decimal digits after the decimal
    //  point. e.g. 123,456,789.01

    typedef uint_parser<int, 10, 1, 2>  uint2_t;
    typedef uint_parser<T, 10, 1, -1>   uint_parser_t;
    typedef int_parser<int, 10, 1, -1>  int_parser_t;

    //////////////////////////////////  2 decimal places Max
    template <typename ScannerT>
    static typename parser_result<uint2_t, ScannerT>::type
    parse_frac_n(ScannerT& scan)
    { return uint2_t().parse(scan); }

    //////////////////////////////////  No exponent
    template <typename ScannerT>
    static typename parser_result<chlit<>, ScannerT>::type
    parse_exp(ScannerT& scan)
    { return scan.no_match(); }

    //////////////////////////////////  No exponent
    template <typename ScannerT>
    static typename parser_result<int_parser_t, ScannerT>::type
    parse_exp_n(ScannerT& scan)
    { return scan.no_match(); }

    //////////////////////////////////  Thousands separated numbers
    template <typename ScannerT>
    static typename parser_result<uint_parser_t, ScannerT>::type
    parse_n(ScannerT& scan)
    {
        typedef typename parser_result<uint_parser_t, ScannerT>::type RT;
        static uint_parser<unsigned, 10, 1, 3> uint3_p;
        static uint_parser<unsigned, 10, 3, 3> uint3_3_p;

        if (RT hit = uint3_p.parse(scan))
        {
            T n;
            typedef typename ScannerT::iterator_t iterator_t;
            iterator_t save = scan.first;
            while (match<> next = (',' >> uint3_3_p[assign_a(n)]).parse(scan))
            {
                hit.value((hit.value() * 1000) + n);
                scan.concat_match(hit, next);
                save = scan.first;
            }
            scan.first = save;
            return hit;
        }
        return scan.no_match();
    }
};

real_parser<double, ts_real_parser_policies<double> > const
    ts_real_p = real_parser<double, ts_real_parser_policies<double> >();

template <typename T>
struct no_trailing_dot : public real_parser_policies<T>
{
    static const bool allow_trailing_dot = false;
};

real_parser<double, no_trailing_dot<double> > const
    notrdot_real_p = real_parser<double, no_trailing_dot<double> >();

template <typename T>
struct no_leading_dot : public real_parser_policies<T>
{
    static const bool allow_leading_dot = false;
};

real_parser<double, no_leading_dot<double> > const
    nolddot_real_p = real_parser<double, no_leading_dot<double> >();

///////////////////////////////////////////////////////////////////////////////
int
main()
{
    cout << "/////////////////////////////////////////////////////////\n\n";
    cout << "\t\tNumeric tests...\n\n";
    cout << "/////////////////////////////////////////////////////////\n\n";

    //  *** The following assumes 32 bit integers. Modify these constant
    //  *** strings when appropriate. BEWARE PLATFORM DEPENDENT!

    char const* max_unsigned = "4294967295";
    char const* unsigned_overflow = "4294967296";
    char const* max_int = "2147483647";
    char const* int_overflow = "2147483648";
    char const* min_int = "-2147483648";
    char const* int_underflow = "-2147483649";
    char const* max_binary = "11111111111111111111111111111111";
    char const* binary_overflow = "100000000000000000000000000000000";
    char const* max_octal = "37777777777";
    char const* octal_overflow = "100000000000";
    char const* max_hex = "FFFFFFFF";
    char const* hex_overflow = "100000000";

#ifdef BOOST_HAS_LONG_LONG

    char const* max_long_long = "9223372036854775807";
    char const* long_long_overflow = "9223372036854775808";
    char const* min_long_long = "-9223372036854775808";
    char const* long_long_underflow = "-9223372036854775809";

#endif

//  BEGIN TESTS...

    unsigned u;

//  unsigned integer

    parse("123456", uint_p[assign_a(u)]);
    BOOST_TEST(u == 123456);

    parse(max_unsigned, uint_p[assign_a(u)]);
    BOOST_TEST(u == UINT_MAX);

    BOOST_TEST(!parse(unsigned_overflow, uint_p).full);

//  signed integer

    int i;

    parse("123456", int_p[assign_a(i)]);
    BOOST_TEST(i == 123456);

    parse("-123456", int_p[assign_a(i)]);
    BOOST_TEST(i == -123456);

    parse(max_int, int_p[assign_a(i)]);
    BOOST_TEST(i == INT_MAX);

    parse(min_int, int_p[assign_a(i)]);
    BOOST_TEST(i == INT_MIN);

    BOOST_TEST(!parse(int_overflow, int_p).full);
    BOOST_TEST(!parse(int_underflow, int_p).full);

    BOOST_TEST(!parse("-", int_p).hit);

//  binary

    parse("11111110", bin_p[assign_a(u)]);
    BOOST_TEST(u == 0xFE);

    parse(max_binary, bin_p[assign_a(u)]);
    BOOST_TEST(u == UINT_MAX);

    BOOST_TEST(!parse(binary_overflow, bin_p).full);

//  octal

    parse("12545674515", oct_p[assign_a(u)]);
    BOOST_TEST(u == 012545674515);

    parse(max_octal, oct_p[assign_a(u)]);
    BOOST_TEST(u == UINT_MAX);

    BOOST_TEST(!parse(octal_overflow, oct_p).full);

//  hex

    parse("95BC8DF", hex_p[assign_a(u)]);
    BOOST_TEST(u == 0x95BC8DF);

    parse("abcdef12", hex_p[assign_a(u)]);
    BOOST_TEST(u == 0xabcdef12);

    parse(max_hex, hex_p[assign_a(u)]);
    BOOST_TEST(u == UINT_MAX);

    BOOST_TEST(!parse(hex_overflow, hex_p).full);

//  limited fieldwidth

    uint_parser<unsigned, 10, 1, 3> uint3_p;
    parse("123456", uint3_p[assign_a(u)]);
    BOOST_TEST(u == 123);

    uint_parser<unsigned, 10, 1, 4> uint4_p;
    parse("123456", uint4_p[assign_a(u)]);
    BOOST_TEST(u == 1234);

    uint_parser<unsigned, 10, 3, 3> uint3_3_p;

//  thousand separated numbers

#define r (uint3_p >> *(',' >> uint3_3_p))

    BOOST_TEST(parse("1,234,567,890", r).full);     //  OK
    BOOST_TEST(parse("12,345,678,900", r).full);    //  OK
    BOOST_TEST(parse("123,456,789,000", r).full);   //  OK
    BOOST_TEST(!parse("1000,234,567,890", r).full); //  Bad
    BOOST_TEST(!parse("1,234,56,890", r).full);     //  Bad
    BOOST_TEST(!parse("1,66", r).full);             //  Bad

//  long long

#ifdef BOOST_HAS_LONG_LONG

// Some compilers have long long, but don't define the
// LONG_LONG_MIN and LONG_LONG_MAX macros in limits.h.  This
// assumes that long long is 64 bits.
#if !defined(LONG_LONG_MIN) && !defined(LONG_LONG_MAX) \
            && !defined(ULONG_LONG_MAX)
#define ULONG_LONG_MAX 0xffffffffffffffffLLU
#define LONG_LONG_MAX 0x7fffffffffffffffLL
#define LONG_LONG_MIN (-LONG_LONG_MAX - 1)
#endif

     ::boost::long_long_type ll;
    int_parser< ::boost::long_long_type> long_long_p;

    parse("1234567890123456789", long_long_p[assign_a(ll)]);
    BOOST_TEST(ll == 1234567890123456789LL);

    parse("-1234567890123456789", long_long_p[assign_a(ll)]);
    BOOST_TEST(ll == -1234567890123456789LL);

    parse(max_long_long, long_long_p[assign_a(ll)]);
    BOOST_TEST(ll == LONG_LONG_MAX);

    parse(min_long_long, long_long_p[assign_a(ll)]);
    BOOST_TEST(ll == LONG_LONG_MIN);

#if defined(__GNUG__) && (__GNUG__ == 3) && (__GNUC_MINOR__ < 3) \
    && !defined(__EDG__)
    // gcc 3.2.3 crashes on parse(long_long_overflow, long_long_p)
    // wrapping long_long_p into a rule avoids the crash
    rule<> gcc_3_2_3_long_long_r = long_long_p;
    BOOST_TEST(!parse(long_long_overflow, gcc_3_2_3_long_long_r).full);
    BOOST_TEST(!parse(long_long_underflow, gcc_3_2_3_long_long_r).full);
#else
    BOOST_TEST(!parse(long_long_overflow, long_long_p).full);
    BOOST_TEST(!parse(long_long_underflow, long_long_p).full);
#endif

#endif

//  real numbers

    double  d;

    BOOST_TEST(parse("1234", ureal_p[assign_a(d)]).full && d == 1234);      //  Good.
    BOOST_TEST(parse("1.2e3", ureal_p[assign_a(d)]).full && d == 1.2e3);    //  Good.
    BOOST_TEST(parse("1.2e-3", ureal_p[assign_a(d)]).full && d == 1.2e-3);  //  Good.
    BOOST_TEST(parse("1.e2", ureal_p[assign_a(d)]).full && d == 1.e2);      //  Good.
    BOOST_TEST(parse(".2e3", ureal_p[assign_a(d)]).full && d == .2e3);      //  Good.
    BOOST_TEST(parse("2e3", ureal_p[assign_a(d)]).full && d == 2e3);        //  Good. No fraction
    BOOST_TEST(!parse("e3", ureal_p).full);                                 //  Bad! No number
    BOOST_TEST(!parse("-1.2e3", ureal_p).full);                             //  Bad! Negative number
    BOOST_TEST(!parse("+1.2e3", ureal_p).full);                             //  Bad! Positive sign
    BOOST_TEST(!parse("1.2e", ureal_p).full);                               //  Bad! No exponent
    BOOST_TEST(!parse("-.3", ureal_p).full);                                //  Bad! Negative

    BOOST_TEST(parse("-1234", real_p[assign_a(d)]).full && d == -1234);     //  Good.
    BOOST_TEST(parse("-1.2e3", real_p[assign_a(d)]).full && d == -1.2e3);   //  Good.
    BOOST_TEST(parse("+1.2e3", real_p[assign_a(d)]).full && d == 1.2e3);    //  Good.
    BOOST_TEST(parse("-0.1", real_p[assign_a(d)]).full && d == -0.1);       //  Good.
    BOOST_TEST(parse("-1.2e-3", real_p[assign_a(d)]).full && d == -1.2e-3); //  Good.
    BOOST_TEST(parse("-1.e2", real_p[assign_a(d)]).full && d == -1.e2);     //  Good.
    BOOST_TEST(parse("-.2e3", real_p[assign_a(d)]).full && d == -.2e3);     //  Good.
    BOOST_TEST(parse("-2e3", real_p[assign_a(d)]).full && d == -2e3);       //  Good. No fraction
    BOOST_TEST(!parse("-e3", real_p).full);                                 //  Bad! No number
    BOOST_TEST(!parse("-1.2e", real_p).full);                               //  Bad! No exponent

    BOOST_TEST(!parse("1234", strict_ureal_p[assign_a(d)]).full);           //  Bad. Strict real
    BOOST_TEST(parse("1.2", strict_ureal_p[assign_a(d)]).full && d == 1.2); //  Good.
    BOOST_TEST(!parse("-1234", strict_real_p[assign_a(d)]).full);           //  Bad. Strict real
    BOOST_TEST(parse("123.", strict_real_p[assign_a(d)]).full && d == 123); //  Good.
    BOOST_TEST(parse("3.E6", strict_real_p[assign_a(d)]).full && d == 3e6); //  Good.

    BOOST_TEST(!parse("1234.", notrdot_real_p[assign_a(d)]).full);          //  Bad trailing dot
    BOOST_TEST(!parse(".1234", nolddot_real_p[assign_a(d)]).full);          //  Bad leading dot

//  Special thousands separated numbers

    BOOST_TEST(parse("123,456,789.01", ts_real_p[assign_a(d)]).full && d == 123456789.01);  //  Good.
    BOOST_TEST(parse("12,345,678.90", ts_real_p[assign_a(d)]).full && d == 12345678.90);    //  Good.
    BOOST_TEST(parse("1,234,567.89", ts_real_p[assign_a(d)]).full && d == 1234567.89);      //  Good.
    BOOST_TEST(!parse("1234,567,890", ts_real_p).full);     //  Bad.
    BOOST_TEST(!parse("1,234,5678,9", ts_real_p).full);     //  Bad.
    BOOST_TEST(!parse("1,234,567.89e6", ts_real_p).full);   //  Bad.
    BOOST_TEST(!parse("1,66", ts_real_p).full);             //  Bad.

//  END TESTS.

/////////////////////////////////////////////////////////////////
    return boost::report_errors();
}


// ExodusModel.cpp
// created by Kuangdai on 2-May-2016
// read Exodus mesh file

// Adapted from salvus project
// ExodusModel.cpp by Michael Afanasiev

#include "ExodusModel.h"
#include <sstream>
#include <iomanip>
#include <cmath>
#include <cfloat>

#include "XMPI.h"
#include "Mapping.h"
#include <boost/algorithm/string.hpp>

#include "AttBuilder.h"
#include "Geodesy.h"
#include "Parameters.h"
#include "MultilevelTimer.h"

#include "NetCDF_Reader.h"

ExodusModel::ExodusModel(const std::string &fileName): mExodusFileName(fileName) {
    std::vector<std::string> substrs = Parameters::splitString(mExodusFileName, "/");
    mExodusTitle = substrs[substrs.size() - 1];
    boost::trim_if(mExodusTitle, boost::is_any_of("\t "));
}

void ExodusModel::initialize() {
    MultilevelTimer::begin("Read Exodus", 1);
    if (XMPI::root()) {
        readRawData();
    }
    MultilevelTimer::end("Read Exodus", 1);

    MultilevelTimer::begin("Bcast Exodus", 1);
    bcastRawData();
    MultilevelTimer::end("Bcast Exodus", 1);

    MultilevelTimer::begin("Process Exodus", 1);
    formStructured();
    formAuxiliary();
    MultilevelTimer::end("Process Exodus", 1);
}

void ExodusModel::readRawData() {
    // open file
    NetCDF_Reader reader;
    reader.open(mExodusFileName);
    RDMatXX dbuffer;

    // global
    reader.readString("name_glo_var", mGlobalVariableNames);
    reader.read1D("vals_glo_var", mGlobalVariableValues);
    reader.readString("info_records", mGlobalRecordsRaw);

    // connectivity and coords
    reader.read2D("connect1", mConnectivity);
    mConnectivity.array() -= 1;
    reader.read1D("coordx", mNodalS);
    reader.read1D("coordy", mNodalZ);

    // elemental variables
    reader.readString("name_elem_var", mElementalVariableNames);
    mElementalVariableValues = RDMatXX::Zero(getNumQuads(), mElementalVariableNames.size());
    for (int i = 0; i < mElementalVariableNames.size(); i++) {
        std::stringstream ss;
        ss << "vals_elem_var" << i + 1 << "eb1";
        reader.read2D(ss.str(), dbuffer);
        mElementalVariableValues.col(i) = dbuffer.transpose();
    }

    // side sets
    reader.readString("ss_names", mSideSetNames);
    mSideSetValues = IMatXX::Zero(getNumQuads(), mSideSetNames.size());
    for (int i = 0; i < mSideSetNames.size(); i++) {
        std::stringstream sse, sss;
        IColX elems, sides;
        // elem
        sse << "elem_ss" << i + 1;
        reader.read1D(sse.str(), elems);
        // side
        sss << "side_ss" << i + 1;
        reader.read1D(sss.str(), sides);
        // processing
        IColX values = IColX::Constant(getNumQuads(), -1);
        for (int j = 0; j < sides.rows(); j++) {
            values(elems(j) - 1) = sides(j) - 1;
        }
        mSideSetValues.col(i) = values;
    }

    // do not read ellipticity for cartesian
    bool cartesian = false;
    for (int i = 0; i < mGlobalRecordsRaw.size(); i++) {
        std::vector<std::string> substrs = Parameters::splitString(mGlobalRecordsRaw[i], "=");
        if (boost::iequals(boost::trim_copy(substrs[0]), "crdsys") &&
            boost::iequals(boost::trim_copy(substrs[1]), "cartesian")) {
            cartesian = true;
            break;
        }
    }
    if (cartesian) {
        mEllipKnots = RDColX::Zero(0);
        mEllipCoeffs = RDColX::Zero(0);
        // set radius to PREM
        mGlobalVariables.insert(std::pair<std::string, double>("radius", 6371e3));
    } else {
        // ellipticity
        reader.read2D("ellipticity", dbuffer);
        mEllipKnots = dbuffer.row(0).transpose();
        mEllipCoeffs = dbuffer.row(1).transpose();
    }

    // close file
    reader.close();
}

void ExodusModel::bcastRawData() {
    XMPI::bcast(mGlobalVariableNames);
    XMPI::bcastEigen(mGlobalVariableValues);
    XMPI::bcast(mGlobalRecordsRaw);

    XMPI::bcastEigen(mConnectivity);
    XMPI::bcastEigen(mNodalS);
    XMPI::bcastEigen(mNodalZ);

    XMPI::bcast(mElementalVariableNames);
    XMPI::bcastEigen(mElementalVariableValues);

    XMPI::bcast(mSideSetNames);
    XMPI::bcastEigen(mSideSetValues);

    XMPI::bcastEigen(mEllipKnots);
    XMPI::bcastEigen(mEllipCoeffs);
}

void ExodusModel::formStructured() {
    // global variables
    for (int i = 0; i < mGlobalVariableNames.size(); i++) {
        std::string varName = mGlobalVariableNames[i];
        if (varName == "dt") {
            varName = "dt (nPol = 1)";
        }
        mGlobalVariables.insert(std::pair<std::string, double>(varName, mGlobalVariableValues(i)));
    }

    // global records
    std::vector<std::string> included = {"crdsys", "model"};
    for (int i = 0; i < mGlobalRecordsRaw.size(); i++) {
        std::vector<std::string> substrs = Parameters::splitString(mGlobalRecordsRaw[i], "=");
        if (std::find(included.begin(), included.end(), boost::trim_copy(substrs[0])) != included.end()) {
            mGlobalRecords.insert(std::pair<std::string, std::string>(
                boost::trim_copy(substrs[0]), boost::trim_copy(substrs[1])));
        }
    }

    // elemental variables
    for (int i = 0; i < mElementalVariableNames.size(); i++) {
        mElementalVariables.insert(std::pair<std::string, RDColX>(mElementalVariableNames[i],
            mElementalVariableValues.col(i)));
    }

    // side sets
    for (int i = 0; i < mSideSetNames.size(); i++) {
        mSideSets.insert(std::pair<std::string, IColX>(mSideSetNames[i],
            mSideSetValues.col(i)));
    }

    // name of axis and surface sets
    std::string sphere_axis_name = "t0";
    if (!isCartesian()) {
        if (mSideSets.find("t1") != mSideSets.end()) {
            sphere_axis_name = "t1";
        }
    }

    mSSNameAxis = isCartesian() ? "x0" : sphere_axis_name;
    mSSNameSurface = isCartesian() ? "y1" : "r1";

    // NOTE: we temporarily treat Cartesian meshes as special cases of spherical meshes
    //       by means of moving it to the "north pole". The introduced global
    //       curvature should be ignorable, or the problem itself is ill-defined
    //       as a local problem.
    if (isCartesian()) {
        double R_EARTH = getROuter();
        double maxz = mNodalZ.maxCoeff();
        mNodalZ.array() += R_EARTH - maxz;
    }
}

void ExodusModel::formAuxiliary() {
    MultilevelTimer::begin("Process Exodus DistTol", 2);
    // distance tolerance
    double distTol = DBL_MAX;
    for (int i = 0; i < getNumQuads(); i++) {
        if (i % XMPI::nproc() != XMPI::rank()) {
            continue;
        }
        double s0 = mNodalS(mConnectivity(i, 0));
        double z0 = mNodalZ(mConnectivity(i, 0));
        double s1 = mNodalS(mConnectivity(i, 1));
        double z1 = mNodalZ(mConnectivity(i, 1));
        double s2 = mNodalS(mConnectivity(i, 2));
        double z2 = mNodalZ(mConnectivity(i, 2));
        double s3 = mNodalS(mConnectivity(i, 3));
        double z3 = mNodalZ(mConnectivity(i, 3));
        double dist0 = sqrt((s0 - s1) * (s0 - s1) + (z0 - z1) * (z0 - z1)) / 1000.;
        double dist1 = sqrt((s1 - s2) * (s1 - s2) + (z1 - z2) * (z1 - z2)) / 1000.;
        double dist2 = sqrt((s2 - s3) * (s2 - s3) + (z2 - z3) * (z2 - z3)) / 1000.;
        double dist3 = sqrt((s3 - s0) * (s3 - s0) + (z3 - z0) * (z3 - z0)) / 1000.;
        distTol = std::min({dist0, dist1, dist2, dist3, distTol});
    }
    mDistTolerance = XMPI::min(distTol);
    MultilevelTimer::end("Process Exodus DistTol", 2);

    // average gll spacing
    MultilevelTimer::begin("Process Exodus GLL-Spacing", 2);
    std::vector<std::vector<int>> refElem(getNumNodes(), std::vector<int>());
    for (int i = 0; i < getNumQuads(); i++) {
        refElem[mConnectivity(i, 0)].push_back(i);
        refElem[mConnectivity(i, 1)].push_back(i);
        refElem[mConnectivity(i, 2)].push_back(i);
        refElem[mConnectivity(i, 3)].push_back(i);
    }
    mAveGLLSpacing = RDColX::Zero(getNumNodes());
    for (int i = 0; i < getNumNodes(); i++) {
        if (i % XMPI::nproc() != XMPI::rank()) {
            continue;
        }
        for (int j = 0; j < refElem[i].size(); j++) {
            int ielem = refElem[i][j];
            double s0 = mNodalS(mConnectivity(ielem, 0));
            double z0 = mNodalZ(mConnectivity(ielem, 0));
            double s1 = mNodalS(mConnectivity(ielem, 1));
            double z1 = mNodalZ(mConnectivity(ielem, 1));
            double s2 = mNodalS(mConnectivity(ielem, 2));
            double z2 = mNodalZ(mConnectivity(ielem, 2));
            double s3 = mNodalS(mConnectivity(ielem, 3));
            double z3 = mNodalZ(mConnectivity(ielem, 3));
            double dist0 = sqrt((s0 - s1) * (s0 - s1) + (z0 - z1) * (z0 - z1));
            double dist1 = sqrt((s1 - s2) * (s1 - s2) + (z1 - z2) * (z1 - z2));
            double dist2 = sqrt((s2 - s3) * (s2 - s3) + (z2 - z3) * (z2 - z3));
            double dist3 = sqrt((s3 - s0) * (s3 - s0) + (z3 - z0) * (z3 - z0));
            mAveGLLSpacing(i) += (dist0 + dist1 + dist2 + dist3) / 4. / nPol / refElem[i].size();
        }
    }
    XMPI::sumEigenDouble(mAveGLLSpacing);
    MultilevelTimer::end("Process Exodus GLL-Spacing", 2);

    // rotate nodes of axial elements such that side 3 is on axis
    MultilevelTimer::begin("Process Exodus Axis", 2);
    for (int axialQuad = 0; axialQuad < getNumQuads(); axialQuad++) {
        // loop over t0
        int axialSide = getSideAxis(axialQuad);
        if (axialSide == 3 || axialSide == -1) {
            continue;
        }

        // connectivity
        IRow4 con = mConnectivity.row(axialQuad);
        for (int j = 0; j < 4; j++) {
            mConnectivity(axialQuad, j) = con(Mapping::period0123(j + axialSide - 3));
        }

        // elemental fields
        for (auto it = mElementalVariables.begin(); it != mElementalVariables.end(); it++) {
            std::string vname = it->first;
            if (vname.substr(vname.length() - 2, 2) == std::string("_0")) {
                vname = vname.substr(0, vname.length() - 2);
                RDRow4 v_old;
                v_old(0) = mElementalVariables.at(vname + "_0")(axialQuad);
                v_old(1) = mElementalVariables.at(vname + "_1")(axialQuad);
                v_old(2) = mElementalVariables.at(vname + "_2")(axialQuad);
                v_old(3) = mElementalVariables.at(vname + "_3")(axialQuad);
                mElementalVariables.at(vname + "_0")(axialQuad) = v_old(Mapping::period0123(0 + axialSide - 3));
                mElementalVariables.at(vname + "_1")(axialQuad) = v_old(Mapping::period0123(1 + axialSide - 3));
                mElementalVariables.at(vname + "_2")(axialQuad) = v_old(Mapping::period0123(2 + axialSide - 3));
                mElementalVariables.at(vname + "_3")(axialQuad) = v_old(Mapping::period0123(3 + axialSide - 3));
            }
        }

        // side sets
        for (auto it = mSideSets.begin(); it != mSideSets.end(); it++) {
            if (it->second(axialQuad) != -1) {
                it->second(axialQuad) = Mapping::period0123(it->second(axialQuad) - axialSide + 3);
            }
        }

        // done
        // mSideSets.at(mSSNameAxis)(axialQuad) = 3;
    }
    MultilevelTimer::end("Process Exodus Axis", 2);

    // find elements that are not axial but neighboring axial elements
    MultilevelTimer::begin("Process Exodus Vicinal", 2);
    mVicinalAxis = IMatX4::Constant(getNumQuads(), 4, -1);
    // first find near-axis nodes and axial quads
    std::vector<bool> nodeNearAxis(getNumNodes(), false);
    std::vector<bool> quadOnAxis(getNumQuads(), false);
    for (int axialQuad = 0; axialQuad < getNumQuads(); axialQuad++) {
        int axialSide = getSideAxis(axialQuad);
        if (axialSide == -1) {
            continue;
        }
        nodeNearAxis[mConnectivity(axialQuad, 0)] = true;
        nodeNearAxis[mConnectivity(axialQuad, 1)] = true;
        nodeNearAxis[mConnectivity(axialQuad, 2)] = true;
        nodeNearAxis[mConnectivity(axialQuad, 3)] = true;
        quadOnAxis[axialQuad] = true;
    }
    // loop over quads
    for (int iquad = 0; iquad < getNumQuads(); iquad++) {
        if (quadOnAxis[iquad]) continue;
        for (int j = 0; j < 4; j++) {
            int nTag = mConnectivity(iquad, j);
            if (nodeNearAxis[nTag]) mVicinalAxis(iquad, j) = j;
        }
    }
    MultilevelTimer::end("Process Exodus Vicinal", 2);

    // check if ocean presents in mesh
    MultilevelTimer::begin("Process Exodus Check Ocean", 2);
    std::string strVs = isIsotropic() ? "VS_0" : "VSV_0";
    for (int iQuad = 0; iQuad < getNumQuads(); iQuad++) {
        if (iQuad % XMPI::nproc() != XMPI::rank()) {
            continue;
        }
        if (getSideSurface(iQuad) == -1) {
            continue;
        }
        double vs = mElementalVariables.at(strVs)(iQuad);
        if (vs < tinyDouble) {
            throw std::runtime_error("ExodusModel::finishReading || "
                "Ocean is detected in mesh. By far, realistic ocean is not implemented in AxiSEM3D. ||"
                "Use non-ocean models in the Mesher and add ocean load in inparam.basic.");
        }
    }
    MultilevelTimer::end("Process Exodus Check Ocean", 2);

    // CMB and ICB
    // MultilevelTimer::begin("Process Exodus CMB & ICB", 2);
    // mR_CMB = DBL_MIN;
    // mR_ICB = DBL_MAX;
    // for (int i = 0; i < getNumQuads(); i++) {
    //     bool isFluid = getElementalVariables("fluid", i) > .5;
    //     bool isAxis = getSideAxis(i) >= 0;
    //     if (isFluid && isAxis) {
    //         for (int j = 0; j < 4; j++) {
    //             double s = mNodalS(mConnectivity(i, j));
    //             double z = mNodalZ(mConnectivity(i, j));
    //             double r = std::sqrt(s * s + z * z);
    //             mR_CMB = std::max(mR_CMB, r);
    //             mR_ICB = std::min(mR_ICB, r);
    //         }
    //     }
    // }
    // MultilevelTimer::end("Process Exodus CMB & ICB", 2);
}

std::string ExodusModel::verbose() const {
    std::stringstream ss;
    ss << "\n======================= Exodus Model =======================" << std::endl;
    ss << "  Overview__________________________________________________" << std::endl;
    ss << "    Exodus Title      =   " << mExodusTitle << std::endl;
    ss << "    Mesh CS Type      =   " << (isCartesian() ? "Cartesian" : "Spherical") << std::endl;
    ss << "    Number of Nodes   =   " << getNumNodes() << std::endl;
    ss << "    Number of Quads   =   " << getNumQuads() << std::endl;
    ss << "  Global Variables__________________________________________" << std::endl;
    int widthname = -1;
    for (auto it = mGlobalVariables.begin(); it != mGlobalVariables.end(); it++) {
        widthname = std::max(widthname, (int)(it->first.length()));
    }
    for (auto it = mGlobalRecords.begin(); it != mGlobalRecords.end(); it++) {
        widthname = std::max(widthname, (int)(it->first.length()));
    }
    for (auto it = mGlobalVariables.begin(); it != mGlobalVariables.end(); it++) {
        ss << "    " << std::setw(widthname) << it->first << "   =   " << it->second << std::endl;
    }
    for (auto it = mGlobalRecords.begin(); it != mGlobalRecords.end(); it++) {
        ss << "    " << std::setw(widthname) << it->first << "   =   " << it->second << std::endl;
    }
    ss << "  Connectivity______________________________________________" << std::endl;
    int width = (int)std::log10(std::max(getNumQuads(), getNumNodes())) + 1;
    ss << "    " << std::setw(width) << 0 << ": ";
    for (int j = 0; j < 4; j++) {
        ss << std::setw(width) << mConnectivity(0, j) << " ";
    }
    ss << std::endl << "    " << std::setw(width) << "..." << std::endl;
    ss << "    " << std::setw(width) << getNumQuads() - 1 << ": ";
    for (int j = 0; j < 4; j++) {
        ss << std::setw(width) << mConnectivity(getNumQuads() - 1, j) << " ";
    }
    ss << std::endl;
    ss << "  Coordinates_______________________________________________" << std::endl;
    ss << "    " << std::setw(width) << 0 << ": ";
    ss << std::setw(13) << mNodalS(0) << std::setw(13) << mNodalZ(0) << std::endl;
    ss<< "    " << std::setw(width) << "..." << std::endl;
    ss << "    " << std::setw(width) << getNumNodes() - 1 << ": ";
    ss << std::setw(13) << mNodalS(getNumNodes() - 1) << std::setw(13) << mNodalZ(getNumNodes() - 1) << std::endl;
    ss << "  Elemental Variables_______________________________________" << std::endl;
    widthname = -1;
    for (auto it = mElementalVariables.begin(); it != mElementalVariables.end(); it++) {
        widthname = std::max(widthname, (int)(it->first.length()));
    }
    for (auto it = mElementalVariables.begin(); it != mElementalVariables.end(); it++) {
        ss << "    " << std::setw(widthname) << it->first << ": ";
        ss << std::setw(13) << it->second(0) << ", ..., ";
        ss << std::setw(13) << it->second(getNumQuads() - 1) << std::endl;
    }
    ss << "  Side Sets_________________________________________________" << std::endl;
    widthname = -1;
    for (auto it = mSideSets.begin(); it != mSideSets.end(); it++) {
        widthname = std::max(widthname, (int)(it->first.length()));
    }
    for (auto it = mSideSets.begin(); it != mSideSets.end(); it++) {
        ss << "    " << std::setw(widthname) << it->first << ":   ";
        int pair = 0;
        for (int q = 0; q < getNumQuads(); q++) {
            pair += (int)(it->second(q) >= 0);
        }
        ss << pair << " edges" << std::endl;
    }
    ss << "  Miscellaneous_____________________________________________" << std::endl;
    ss << "    Distance Tolerance / m   =   " << mDistTolerance << std::endl;
    if (!isCartesian()) {
        ss << "  External__________________________________________________" << std::endl;
        ss << "    Num. Ellipticity Spline Knots   =   " << mEllipKnots.size() << std::endl;
    }
    ss << "======================= Exodus Model =======================\n" << std::endl;
    return ss.str();
}

void ExodusModel::buildInparam(ExodusModel *&exModel, const Parameters &par,
    AttParameters *&attPar, int verbose) {
    if (exModel) {
        delete exModel;
    }
    std::string exfile = par.getValue<std::string>("MODEL_1D_EXODUS_MESH_FILE");
    exfile = Parameters::sInputDirectory + "/" + exfile;
    exModel = new ExodusModel(exfile);
    exModel->initialize();
    if (verbose) {
        XMPI::cout << exModel->verbose();
    }

    // form attenuation parameters
    if (exModel->hasAttenuation()) {
        int nr_lin_solids = (int)exModel->mGlobalVariables.at("nr_lin_solids");
        double f_min = exModel->mGlobalVariables.at("f_min");
        double f_max = exModel->mGlobalVariables.at("f_max");
        double f_ref = exModel->mGlobalVariables.at("f_ref");
        RDColX w(nr_lin_solids), y(nr_lin_solids);
        for (int i = 0; i < nr_lin_solids; i++) {
            std::stringstream sw, sy;
            sw << "w_" << i;
            sy << "y_" << i;
            w(i) = exModel->mGlobalVariables.at(sw.str());
            y(i) = exModel->mGlobalVariables.at(sy.str());
        }
        if (attPar) {
            delete attPar;
        }
        attPar = new AttParameters(nr_lin_solids, f_min, f_max, f_ref, w, y);
    } else {
        if (attPar) {
            delete attPar;
            attPar = 0;
        }
    }

    // ellipticity
    if (exModel->isCartesian()) {
        return;
    }
    std::string emode = par.getValue<std::string>("MODEL_3D_ELLIPTICITY_MODE");
    if (boost::iequals(emode, "off")) {
        // no ellipticity
        Geodesy::setup(exModel->getROuter(), 0., RDColX::Zero(0), RDColX::Zero(0));
    } else {
        double inv_f = par.getValue<double>("MODEL_3D_ELLIPTICITY_INVF");
        if (inv_f <= 0.) {
            throw std::runtime_error("ExodusModel::buildInparam || Invalid flattening.");
        }
        Geodesy::setup(exModel->getROuter(), 1. / inv_f, exModel->mEllipKnots, exModel->mEllipCoeffs);
    }
}


#include <boost/numeric/interval/constants.hpp>