/*-----------------------------------------------------------------------------+
Interval Container Library
Author: Joachim Faulhaber
Copyright (c) 2007-2009: Joachim Faulhaber
Copyright (c) 1999-2006: Cortex Software GmbH, Kantstrasse 57, Berlin
+------------------------------------------------------------------------------+
   Distributed under the Boost Software License, Version 1.0.
      (See accompanying file LICENCE.txt or copy at
           http://www.boost.org/LICENSE_1_0.txt)
+-----------------------------------------------------------------------------*/

/** Example boost_party.cpp \file boost_party.cpp
    \brief Generates an attendance history of a party by inserting into an interval_map.
           Demonstrating aggregate on overlap.

    boost_party.cpp demonstrates the possibilities of an interval map
    (interval_map or split_interval_map). Boost::posix_time::ptime is used as time
    parameter. An interval_map maps intervals to a given content. In this case the
    content is a set of party guests represented by their name strings.

    As time goes by, groups of people join the party and leave later in the evening.
    So we add a time interval and a name set to the interval_map for the attendance
    of each group of people, that come together and leave together.

    On every overlap of intervals, the corresponding name sets are accumulated. At
    the points of overlap the intervals are split. The accumulation of content on
    overlap of intervals is done via an operator += that has to be implemented
    for the content parameter of the interval_map.

    Finally the interval_map contains the history of attendance and all points in
    time, where the group of party guests changed.

    boost_party.cpp demonstrates a principle that we call
    aggregate on overlap:
    On insertion a value associated to the interval is aggregated (added) to those
    values in the interval_map that overlap with the inserted value.

    There are two behavioral aspects to aggregate on overlap: a decompositional
    behavior and a accumulative behavior.

    The decompositional behavior splits up intervals on the time dimension of the
    interval_map so that the intervals change whenever associated values
    change.

    The accumulative behavior accumulates associated values on every overlap of
    an insertion for the associated values.

    \include boost_party_/boost_party.cpp
*/
//[example_boost_party
// The next line includes <boost/date_time/posix_time/posix_time.hpp>
// and a few lines of adapter code.
#include <boost/icl/ptime.hpp>
#include <iostream>
#include <boost/icl/interval_map.hpp>

using namespace std;
using namespace boost::posix_time;
using namespace boost::icl;

// Type set<string> collects the names of party guests. Since std::set is
// a model of the itl's set concept, the concept provides an operator +=
// that performs a set union on overlap of intervals.
typedef std::set<string> GuestSetT;

void boost_party()
{
    GuestSetT mary_harry;
    mary_harry.insert("Mary");
    mary_harry.insert("Harry");

    GuestSetT diana_susan;
    diana_susan.insert("Diana");
    diana_susan.insert("Susan");

    GuestSetT peter;
    peter.insert("Peter");

    // A party is an interval map that maps time intervals to sets of guests
    interval_map<ptime, GuestSetT> party;

    party.add( // add and element
      make_pair(
        interval<ptime>::right_open(
          time_from_string("2008-05-20 19:30"),
          time_from_string("2008-05-20 23:00")),
        mary_harry));

    party += // element addition can also be done via operator +=
      make_pair(
        interval<ptime>::right_open(
          time_from_string("2008-05-20 20:10"),
          time_from_string("2008-05-21 00:00")),
        diana_susan);

    party +=
      make_pair(
        interval<ptime>::right_open(
          time_from_string("2008-05-20 22:15"),
          time_from_string("2008-05-21 00:30")),
        peter);

    interval_map<ptime, GuestSetT>::iterator it = party.begin();
    cout << "----- History of party guests -------------------------\n";
    while(it != party.end())
    {
        interval<ptime>::type when = it->first;
        // Who is at the party within the time interval 'when' ?
        GuestSetT who = (*it++).second;
        cout << when << ": " << who << endl;
    }

}

int main()
{
    cout << ">>Interval Container Library: Sample boost_party.cpp <<\n";
    cout << "-------------------------------------------------------\n";
    boost_party();
    return 0;
}

// Program output:
/*-----------------------------------------------------------------------------
>>Interval Container Library: Sample boost_party.cpp <<
-------------------------------------------------------
----- History of party guests -------------------------
[2008-May-20 19:30:00, 2008-May-20 20:10:00): Harry Mary
[2008-May-20 20:10:00, 2008-May-20 22:15:00): Diana Harry Mary Susan
[2008-May-20 22:15:00, 2008-May-20 23:00:00): Diana Harry Mary Peter Susan
[2008-May-20 23:00:00, 2008-May-21 00:00:00): Diana Peter Susan
[2008-May-21 00:00:00, 2008-May-21 00:30:00): Peter
-----------------------------------------------------------------------------*/
//]


/*
 * @file
 * @author University of Warwick
 * @version 1.0
 *
 * @section LICENSE
 *
 * @section DESCRIPTION
 *
 * Unit Tests for the methods of the Tetrahedron class
 */

#define BOOST_TEST_MODULE Tetrahedron
#include <boost/test/unit_test.hpp>
#include <boost/test/output_test_stream.hpp>
#include <stdexcept>

#include "Tetrahedron.h"
#include "EuclideanPoint.h"
#include "Triangle3D.h"
#include "Pyramid.h"

using namespace cupcfd::geometry::shapes;
namespace euc = cupcfd::geometry::euclidean;
namespace utf = boost::unit_test;

// === Constructor ===
// Test 1: Create a Tetrahedron
BOOST_AUTO_TEST_CASE(constructor_test1, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	BOOST_CHECK_EQUAL(shape.apex.cmp[0], 2.5);
	BOOST_CHECK_EQUAL(shape.apex.cmp[1], 2.5);
	BOOST_CHECK_EQUAL(shape.apex.cmp[2], 7.0);

	// Stored in anti-clockwise order from outside/clockwise from inside
	BOOST_CHECK_EQUAL(shape.base.vertices[0].cmp[0], 2.5);
	BOOST_CHECK_EQUAL(shape.base.vertices[0].cmp[1], 5.0);
	BOOST_CHECK_EQUAL(shape.base.vertices[0].cmp[2], 0.0);

	BOOST_CHECK_EQUAL(shape.base.vertices[1].cmp[0], 5.0);
	BOOST_CHECK_EQUAL(shape.base.vertices[1].cmp[1], 0.0);
	BOOST_CHECK_EQUAL(shape.base.vertices[1].cmp[2], 0.0);

	BOOST_CHECK_EQUAL(shape.base.vertices[2].cmp[0], 0.0);
	BOOST_CHECK_EQUAL(shape.base.vertices[2].cmp[1], 0.0);
	BOOST_CHECK_EQUAL(shape.base.vertices[2].cmp[2], 0.0);
}

// === isPointInside ===
// Test 1: Point is inside Polygon
BOOST_AUTO_TEST_CASE(isPointInside_test1, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	euc::EuclideanPoint<double,3> point(2.5, 2.5, 1.78);

	bool isInside = shape.isPointInside(point);

	BOOST_CHECK_EQUAL(isInside, true);
}

// Test 2: Point is outside Polygon
BOOST_AUTO_TEST_CASE(isPointInside_test2, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	euc::EuclideanPoint<double,3> point(2.5, 2.5, 18.6);

	bool isInside = shape.isPointInside(point);

	BOOST_CHECK_EQUAL(isInside, false);
}

// Test 3: Point is on Base Point 0
BOOST_AUTO_TEST_CASE(isPointInside_test3, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	bool isInside = shape.isPointInside(f);

	BOOST_CHECK_EQUAL(isInside, true);
}

// Test 4: Point is on Base Point 1
BOOST_AUTO_TEST_CASE(isPointInside_test4, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	bool isInside = shape.isPointInside(bl);

	BOOST_CHECK_EQUAL(isInside, true);
}

// Test 5: Point is on Base Point 2
BOOST_AUTO_TEST_CASE(isPointInside_test5, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	bool isInside = shape.isPointInside(br);

	BOOST_CHECK_EQUAL(isInside, true);
}

// Test 6: Point is on Apex
BOOST_AUTO_TEST_CASE(isPointInside_test6, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	bool isInside = shape.isPointInside(apex);

	BOOST_CHECK_EQUAL(isInside, true);
}

// Test 7: Point is on Base Plane
BOOST_AUTO_TEST_CASE(isPointInside_test7, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	euc::EuclideanPoint<double,3> point(2.5,2.5,0.0);

	bool isInside = shape.isPointInside(point);

	BOOST_CHECK_EQUAL(isInside, true);
}

// Test 8: Point is on Triangular Face 0
BOOST_AUTO_TEST_CASE(isPointInside_test8, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	// Test Face apex, f, br
	euc::EuclideanPoint<double,3> point(3.125, 2.5, 3.5);

	bool isInside = shape.isPointInside(point);

	BOOST_CHECK_EQUAL(isInside, true);
}

// Test 9: Point is on Triangular Face 1
BOOST_AUTO_TEST_CASE(isPointInside_test9, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	// Test Face apex, f, bl
	euc::EuclideanPoint<double,3> point(1.875, 2.5, 3.5);

	bool isInside = shape.isPointInside(point);

	BOOST_CHECK_EQUAL(isInside, true);
}

// Test 10: Point is on Triangular Face 2
BOOST_AUTO_TEST_CASE(isPointInside_test10, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	// Test Face apex, bl, br
	euc::EuclideanPoint<double,3> point(2.5, 1.25, 3.5);

	bool isInside = shape.isPointInside(point);

	BOOST_CHECK_EQUAL(isInside, true);
}

// === getVolume ===
// Test 1: Compute Volume of Tetrahedron
BOOST_AUTO_TEST_CASE(getVolume_test1, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	double volume = shape.getVolume();

	BOOST_TEST(volume == 29.166667);
}

// Test 2: Compute Volume of Oblique Tetrahedron
BOOST_AUTO_TEST_CASE(getVolume_test2, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(1.4, 3.2, 6.4);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	double volume = shape.getVolume();

	BOOST_TEST(volume == 26.666667);
}

// === getCentroid ===
// Test 1: Compute Centroid of Pyramid
BOOST_AUTO_TEST_CASE(getCentroid_test1, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	euc::EuclideanPoint<double, 3> centroid = shape.getCentroid();

	BOOST_TEST(centroid.cmp[0] == 2.5);
	BOOST_TEST(centroid.cmp[1] == 1.875000);
	BOOST_TEST(centroid.cmp[2] == 1.75);
}

// Test 2: Compute Centroid of Oblique Pyramid
BOOST_AUTO_TEST_CASE(getCentroid_test2, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(1.4, 3.2, 6.4);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	euc::EuclideanPoint<double, 3> centroid = shape.getCentroid();

	BOOST_TEST(centroid.cmp[0] == 2.225);
	BOOST_TEST(centroid.cmp[1] == 2.05000);
	BOOST_TEST(centroid.cmp[2] == 1.6);
}

// === isPointOnEdge ===
// Test 1: Test point that is not on an edge
BOOST_AUTO_TEST_CASE(isPointOnEdge_test1, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	euc::EuclideanPoint<double, 3> point(1.2, 1.2, 0.6);

	bool onEdge = shape.isPointOnEdge(point);
	BOOST_CHECK_EQUAL(onEdge, false);
}

// Test 2: Test point that is on an edge
BOOST_AUTO_TEST_CASE(isPointOnEdge_test2, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	euc::EuclideanPoint<double, 3> point(1.25, 1.25, 3.5);

	bool onEdge = shape.isPointOnEdge(point);
	BOOST_CHECK_EQUAL(onEdge, true);
}

// === isPointOnVertex ===
// Test 1: Test point that is not on a vertex
BOOST_AUTO_TEST_CASE(isPointOnVertex_test1, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	euc::EuclideanPoint<double, 3> point(1.25, 1.25, 3.5);

	bool onEdge = shape.isPointOnVertex(point);
	BOOST_CHECK_EQUAL(onEdge, false);
}

// Test 2: Test point that is on a vertex
BOOST_AUTO_TEST_CASE(isPointOnVertex_test2, * utf::tolerance(0.00001))
{
	euc::EuclideanPoint<double, 3> f(2.5, 5.0, 0.0);
	euc::EuclideanPoint<double, 3> bl(0.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> br(5.0, 0.0, 0.0);
	euc::EuclideanPoint<double, 3> apex(2.5, 2.5, 7.0);

	Triangle3D<double> base(f, br, bl);
	Tetrahedron<double> shape(apex, base);

	euc::EuclideanPoint<double, 3> point(2.5, 2.5, 7.0);

	bool onEdge = shape.isPointOnVertex(point);
	BOOST_CHECK_EQUAL(onEdge, true);
}


//==================================================================================================
/**
  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
**/
//==================================================================================================
#include <boost/simd/function/shuffle.hpp>
#include <boost/simd/pack.hpp>
#include <simd_test.hpp>
#include "reference.hpp"

using namespace boost::simd;

STF_CASE_TPL( "unary shuffle of cardinal 2", STF_NUMERIC_TYPES)
{
  bs::pack<T,2> a{ Valmax<T>(), T(42) };

  STF_ALL_EQUAL( (shuffle< 0, 0>(a)), (unary_ref< 0, 0>(a)) );
  STF_ALL_EQUAL( (shuffle< 0, 1>(a)), (unary_ref< 0, 1>(a)) );
  STF_ALL_EQUAL( (shuffle< 0,-1>(a)), (unary_ref< 0,-1>(a)) );
  STF_ALL_EQUAL( (shuffle< 1,-1>(a)), (unary_ref< 1,-1>(a)) );
  STF_ALL_EQUAL( (shuffle< 1, 0>(a)), (unary_ref< 1, 0>(a)) );
  STF_ALL_EQUAL( (shuffle< 1, 1>(a)), (unary_ref< 1, 1>(a)) );
  STF_ALL_EQUAL( (shuffle<-1, 1>(a)), (unary_ref<-1, 1>(a)) );
  STF_ALL_EQUAL( (shuffle<-1, 0>(a)), (unary_ref<-1, 0>(a)) );
  STF_ALL_EQUAL( (shuffle<-1,-1>(a)), (unary_ref<-1,-1>(a)) );
}


#ifndef SM_EIGEN_MATRIX_SQRT_HPP
#define SM_EIGEN_MATRIX_SQRT_HPP

#include <sm/assert_macros.hpp>
#include <Eigen/Cholesky>

namespace sm {
    namespace eigen {

        /**
         * \brief Compute the square root of a matrix using the LDLt decomposition for square, positive semidefinite matrices
         *
         * To reconstruct the input matrix, \f$ \mathbf A \f$, from the returned matrix, \f$ \mathbf S \f$,
         * use, \f$ \mathbf A = \mathbf S \mathbf S^T \f$.
         *
         *
         * @param inMatrix      The square matrix whose square root should be computed.
         * @param outMatrixSqrt The output square root.
         */
        template<typename DERIVED1, typename DERIVED2>
        /*Eigen::ComputationInfo*/ void computeMatrixSqrt(const Eigen::MatrixBase<DERIVED1> & inMatrix,
                                                          const Eigen::MatrixBase<DERIVED2> & outMatrixSqrt)
        {
            SM_ASSERT_EQ_DBG(std::runtime_error, inMatrix.rows(), inMatrix.cols(), "This method is only valid for square input matrices");

            DERIVED2 & result = const_cast<DERIVED2 &>(outMatrixSqrt.derived());

            // This is tricky. Using the output matrix type causes the input matrix
            // type to be upgraded to a real numeric matrix. This is useful if,
            // for example, the inMatrix is something like Eigen::Matrix3d::Identity(),
            // which is not an actual matrix. Using DERIVED1 as the template argument
            // in that case will cause a firestorm of compiler errors.
            Eigen::LDLT< DERIVED2 > ldlt(inMatrix.derived());
            result = ldlt.matrixL();
            result = ldlt.transpositionsP().transpose() * result;
            result *= ldlt.vectorD().array().sqrt().matrix().asDiagonal();

            //return ldlt.info();

        }

    } // namespace eigen
} // namespace sm

#endif /* SM_EIGEN_MATRIX_SQRT_HPP */


#include "teca_integrated_water_vapor.h"

#include "teca_cartesian_mesh.h"
#include "teca_array_collection.h"
#include "teca_variant_array.h"
#include "teca_metadata.h"
#include "teca_coordinate_util.h"

#include <algorithm>
#include <iostream>
#include <string>
#include <cmath>

#if defined(TECA_HAS_BOOST)
#include <boost/program_options.hpp>
#endif

using std::string;
using std::vector;
using std::cerr;
using std::endl;
using std::cos;

//#define TECA_DEBUG

namespace {
template <typename coord_t, typename num_t>
void cartesian_iwv(unsigned long nx, unsigned long ny, unsigned long nz,
    const coord_t *plev, const num_t *q, num_t *iwv)
{
    unsigned long nxy = nx*ny;

    // initialize the result
    memset(iwv, 0, nxy*sizeof(num_t));

    // work an x-y slice at  a time
    unsigned long nzm1 = nz - 1;
    for (unsigned long k = 0; k < nzm1; ++k)
    {
        // dp over the slice
        num_t h2 = num_t(0.5) * (plev[k+1] - plev[k]);

        // the current two x-y-planes of data
        unsigned long knxy = k*nxy;
        const num_t *q_k0 = q + knxy;
        const num_t *q_k1 = q_k0 + nxy;

        // accumulate this plane of data using trapazoid rule
        for (unsigned long i = 0; i < nxy; ++i)
        {
            iwv[i] += h2 * (q_k0[i] + q_k1[i]);
        }
    }

    // check the sign, in this way we can handle both increasing and decreasing
    // pressure coordinates
    num_t s = plev[1] - plev[0] < num_t(0) ? num_t(-1) : num_t(1);

    // scale by -1/g
    num_t m1g = s/num_t(9.80665);
    for (unsigned long i = 0; i < nxy; ++i)
        iwv[i] *= m1g;
}

template <typename coord_t, typename num_t>
void cartesian_iwv(unsigned long nx, unsigned long ny, unsigned long nz,
    const coord_t *plev, const num_t *q, const char *q_valid, num_t *iwv)
{
    unsigned long nxy = nx*ny;

    // initialize the result
    memset(iwv, 0, nxy*sizeof(num_t));

    // work an x-y slice at a time
    unsigned long nzm1 = nz - 1;
    for (unsigned long k = 0; k < nzm1; ++k)
    {
        // dp over the slice
        num_t h2 = num_t(0.5) * (plev[k+1] - plev[k]);

        // the current two x-y-planes of data
        unsigned long knxy = k*nxy;
        const num_t *q_k0 = q + knxy;
        const num_t *q_k1 = q_k0 + nxy;

        const char *q_valid_k0 = q_valid + knxy;
        const char *q_valid_k1 = q_valid_k0 + nxy;

        // accumulate this plane of data using trapazoid rule
        for (unsigned long i = 0; i < nxy; ++i)
        {
            iwv[i] += ((q_valid_k0[i] && q_valid_k1[i]) ?
               h2 * (q_k0[i] + q_k1[i]) : num_t(0));
        }
    }

    // check the sign, in this way we can handle both increasing and decreasing
    // pressure coordinates
    num_t s = plev[1] - plev[0] < num_t(0) ? num_t(-1) : num_t(1);

    // scale by -1/g
    num_t m1g = s/num_t(9.80665);
    for (unsigned long i = 0; i < nxy; ++i)
        iwv[i] *= m1g;
}
}

// --------------------------------------------------------------------------
teca_integrated_water_vapor::teca_integrated_water_vapor() :
    specific_humidity_variable("Q"), iwv_variable("IWV"),
    fill_value(1.0e20)
{
    this->set_number_of_input_connections(1);
    this->set_number_of_output_ports(1);
}

// --------------------------------------------------------------------------
teca_integrated_water_vapor::~teca_integrated_water_vapor()
{}

#if defined(TECA_HAS_BOOST)
// --------------------------------------------------------------------------
void teca_integrated_water_vapor::get_properties_description(
    const string &prefix, options_description &global_opts)
{
    options_description opts("Options for "
        + (prefix.empty()?"teca_integrated_water_vapor":prefix));

    opts.add_options()
        TECA_POPTS_GET(std::string, prefix, specific_humidity_variable,
            "name of the variable containg the specific humidity")
        TECA_POPTS_GET(double, prefix, fill_value,
            "the value of the NetCDF _FillValue attribute")
        ;

    this->teca_algorithm::get_properties_description(prefix, opts);

    global_opts.add(opts);
}

// --------------------------------------------------------------------------
void teca_integrated_water_vapor::set_properties(
    const string &prefix, variables_map &opts)
{
    this->teca_algorithm::set_properties(prefix, opts);

    TECA_POPTS_SET(opts, std::string, prefix, specific_humidity_variable)
    TECA_POPTS_SET(opts, double, prefix, fill_value)
}
#endif

// --------------------------------------------------------------------------
teca_metadata teca_integrated_water_vapor::get_output_metadata(
    unsigned int port,
    const std::vector<teca_metadata> &input_md)
{
#ifdef TECA_DEBUG
    std::cerr << teca_parallel_id()
        << "teca_integrated_water_vapor::get_output_metadata" << std::endl;
#endif
    (void)port;

    // set things up in the first pass, and don't modify in subsequent passes
    // due to threading concerns

    if (this->get_number_of_derived_variables() == 0)
    {
        // the base class will handle dealing with the transformation of
        // mesh dimensions and reporting the array we produce, but we have
        // to determine the data type and tell the name of the produced array.
        const teca_metadata &md = input_md[0];

        teca_metadata attributes;
        if (md.get("attributes", attributes))
        {
            TECA_FATAL_ERROR("Failed to determine output data type "
                "because attributes are misisng")
            return teca_metadata();
        }

        teca_metadata hus_atts;
        if (attributes.get(this->specific_humidity_variable, hus_atts))
        {
            TECA_FATAL_ERROR("Failed to determine output data type "
                "because attributes for \"" << this->specific_humidity_variable
                << "\" are misisng")
            return teca_metadata();
        }

        int type_code = 0;
        if (hus_atts.get("type_code", type_code))
        {
            TECA_FATAL_ERROR("Failed to determine output data type "
                "because attributes for \"" << this->specific_humidity_variable
                << "\" is misisng a \"type_code\"")
            return teca_metadata();
        }

        teca_array_attributes iwv_atts(
            type_code, teca_array_attributes::point_centering,
            0, "kg m^{-1} s^{-1}", "longitudinal integrated vapor transport",
            "the longitudinal component of integrated vapor transport",
            1, this->fill_value);

        // install name and attributes of the output variables in the base classs
        this->append_derived_variable(this->iwv_variable);
        this->append_derived_variable_attribute(iwv_atts);
    }

    if (this->get_number_of_dependent_variables() == 0)
    {
        // install the names of the input variables in the base class
        this->append_dependent_variable(this->specific_humidity_variable);
    }

    // invoke the base class method, which does the work of transforming
    // the mesh and reporting the variables and their attributes.
    return teca_vertical_reduction::get_output_metadata(port, input_md);
}

// --------------------------------------------------------------------------
std::vector<teca_metadata> teca_integrated_water_vapor::get_upstream_request(
    unsigned int port,
    const std::vector<teca_metadata> &input_md,
    const teca_metadata &request)
{
    // invoke the base class method
    return teca_vertical_reduction::get_upstream_request(port, input_md, request);
}

// --------------------------------------------------------------------------
const_p_teca_dataset teca_integrated_water_vapor::execute(
    unsigned int port,
    const std::vector<const_p_teca_dataset> &input_data,
    const teca_metadata &request)
{
#ifdef TECA_DEBUG
    std::cerr << teca_parallel_id()
        << "teca_integrated_water_vapor::execute" << std::endl;
#endif
    (void)port;

    // get the input mesh
    const_p_teca_cartesian_mesh in_mesh
        = std::dynamic_pointer_cast<const teca_cartesian_mesh>(input_data[0]);

    if (!in_mesh)
    {
        TECA_FATAL_ERROR("Failed to compute IWV because a cartesian mesh is required.")
        return nullptr;
    }

    // get the input dimensions
    unsigned long extent[6] = {0};
    if (in_mesh->get_extent(extent))
    {
        TECA_FATAL_ERROR("Failed to compute IWV because mesh extent is missing.")
        return nullptr;
    }

    unsigned long nx = extent[1] - extent[0] + 1;
    unsigned long ny = extent[3] - extent[2] + 1;
    unsigned long nz = extent[5] - extent[4] + 1;

    // get the pressure coordinates
    const_p_teca_variant_array p = in_mesh->get_z_coordinates();
    if (!p)
    {
        TECA_FATAL_ERROR("Failed to compute IWV because pressure coordinates are missing")
        return nullptr;
    }

    if (p->size() < 2)
    {
        TECA_FATAL_ERROR("Failed to compute IWV because z dimensions "
            << p->size() << " < 2 as required by the integration method")
        return nullptr;
    }

    // gather the input arrays
    const_p_teca_variant_array q =
        in_mesh->get_point_arrays()->get(this->specific_humidity_variable);

    if (!q)
    {
        TECA_FATAL_ERROR("Failed to compute IWV because specific humidity \""
            << this->specific_humidity_variable << "\" is missing")
        return nullptr;
    }

    const_p_teca_variant_array q_valid =
           in_mesh->get_point_arrays()->get(this->specific_humidity_variable + "_valid");

    // the base class will construct the output mesh
    p_teca_cartesian_mesh out_mesh
        = std::dynamic_pointer_cast<teca_cartesian_mesh>(
            std::const_pointer_cast<teca_dataset>(
                teca_vertical_reduction::execute(port, input_data, request)));

    if (!out_mesh)
    {
        TECA_FATAL_ERROR("Failed to compute IWV because the output mesh was "
            "not constructed")
        return nullptr;
    }

    // allocate the output arrays
    unsigned long nxy = nx*ny;
    p_teca_variant_array iwv = q->new_instance(nxy);

    // store the result
    out_mesh->get_point_arrays()->set(this->iwv_variable, iwv);

    // calculate IWV
    NESTED_TEMPLATE_DISPATCH_FP(const teca_variant_array_impl,
        p.get(), _COORDS,

        const NT_COORDS *p_p = static_cast<TT_COORDS*>(p.get())->get();

        NESTED_TEMPLATE_DISPATCH_FP(teca_variant_array_impl,
            iwv.get(), _DATA,

            NT_DATA *p_iwv = static_cast<TT_DATA*>(iwv.get())->get();

            const NT_DATA *p_q = static_cast<const TT_DATA*>(q.get())->get();

            const char *p_q_valid = nullptr;
            if (q_valid)
            {
                using TT_MASK = teca_char_array;

                p_q_valid = dynamic_cast<const TT_MASK*>(q_valid.get())->get();

                ::cartesian_iwv(nx, ny, nz, p_p, p_q, p_q_valid, p_iwv);
            }
            else
            {
                ::cartesian_iwv(nx, ny, nz, p_p, p_q, p_iwv);
            }
            )
        )

    // pass 2D arrays through.
    p_teca_array_collection in_arrays =
        std::const_pointer_cast<teca_array_collection>(in_mesh->get_point_arrays());

    p_teca_array_collection out_arrays = out_mesh->get_point_arrays();

    int n_arrays = in_arrays->size();
    for (int i = 0; i < n_arrays; ++i)
    {
        p_teca_variant_array array = in_arrays->get(i);
        if (array->size() == nxy)
        {
            // pass the array.
            out_arrays->append(in_arrays->get_name(i), array);
        }
    }

    return out_mesh;
}


#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
// HalfedgeGraph adapters for Polyhedron_3
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/properties_Polyhedron_3.h>
#include <boost/foreach.hpp>
// #define CGAL_DEFORM_MESH_USE_EXPERIMENTAL_SR_ARAP
#include <CGAL/Surface_mesh_deformation.h>

#include <fstream>

typedef CGAL::Simple_cartesian<double>                                   Kernel;
typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_with_id_3> Polyhedron;

typedef boost::graph_traits<Polyhedron>::vertex_descriptor    vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::vertex_iterator        vertex_iterator;

typedef CGAL::Surface_mesh_deformation<Polyhedron,CGAL::Default, CGAL::Default, CGAL::ORIGINAL_ARAP> Surface_mesh_deformation;

int main(int argc,char** argv)
{
  if  ( argc!=4){
    std::cerr <<"Usage " << argv[0] << " input.off input.sel input.def\n";
    return 1;
  }
  Polyhedron mesh;
  std::ifstream input(argv[1]);

  if ( !input || !(input >> mesh) || mesh.empty() ) {
    std::cerr<< argv[1] << " is not a valid off file" << std::endl;
    return 1;
  }
  input.close();

  // Init the indices of the halfedges and the vertices.
  set_halfedgeds_items_id(mesh);

  // Create a deformation object
  Surface_mesh_deformation deform_mesh(mesh);

  // Definition of the region of interest (use the whole mesh)
  vertex_iterator vb,ve;
  boost::tie(vb, ve) = vertices(mesh);

  //the selection is set by a file
  input.open(argv[2]);
  std::string line;
  std::vector<vertex_descriptor> control_vertices;
  while(getline(input, line))
  {
    if (line[0]=='#') continue;
    if (line[0]=='1') deform_mesh.insert_roi_vertex(*vb);
    if (line[0]=='2') {
      deform_mesh.insert_control_vertex(*vb);
      control_vertices.push_back(*vb);
    }
    ++vb;
    if (vb==ve) break;
  }
  input.close();

  std::cout << "Using " << control_vertices.size() << " control vertices\n";
  // The definition of the ROI and the control vertices is done, call preprocess
  bool is_matrix_factorization_OK = deform_mesh.preprocess();
  if(!is_matrix_factorization_OK){
    std::cerr << "Error in preprocessing, check documentation of preprocess()" << std::endl;
    return 1;
  }

  //define the transformation
  input.open(argv[3]);
  double m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, hw, sink;
  getline(input, line); // skip first comment line
  input >> m00 >> m01 >> m02 >> m03;
  input >> m10 >> m11 >> m12 >> m13;
  input >> m20 >> m21 >> m22 >> m23;
  input >> sink >> sink >> sink >> hw;

  Kernel::Aff_transformation_3 aff(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23);
  BOOST_FOREACH(vertex_descriptor vd, control_vertices)
  {
    Surface_mesh_deformation::Point pos = vd->point().transform(aff);
    deform_mesh.set_target_position(vd, pos);
  }

  // Call the function deform() with one-time parameters:
  deform_mesh.deform(1000, 1e-4);

  // Save the deformed mesh into a file
  std::ofstream output("deform_res.off");
  output << mesh;
  output.close();
}


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

#include <ecto/ecto.hpp>
#include <ecto/registry.hpp>
#include <boost/random.hpp>
#include <boost/scoped_ptr.hpp>

using ecto::tendrils;

namespace ecto_test
{
  struct Uniform01
  {
    struct impl {

      boost::mt19937 generator;
      boost::uniform_real<> uniform_dist;
      boost::variate_generator<boost::mt19937&, boost::uniform_real<> > uni;

      impl(unsigned seed)
        : generator(seed), uniform_dist(0,1), uni(generator, uniform_dist)
      { }

      double operator()() {
        return uni();
      }
    };

    boost::scoped_ptr<impl> pimpl_;
    ecto::spore<double> out_;

    unsigned ncalls;

    static void declare_params(tendrils& parameters)
    {
      parameters.declare<unsigned> ("seed", "Seed.  By default the RNG is seeded from the system time.");
      parameters.declare<unsigned> ("ncalls", "Call this many times, return only the last. "
                                    "Used to generate CPU load in testing.", 1);
    }

    static void declare_io(const ecto::tendrils& parameters, ecto::tendrils& inputs, ecto::tendrils& outputs)
    {
      outputs.declare<double> ("out", "output");
    }

    void configure(const tendrils& parameters, const tendrils& inputs, const tendrils& outputs)
    {
      out_ = outputs["out"];
      ecto::spore<unsigned> seed_ = parameters["seed"];
      // if the user supplied a seed, use it, else use the system time
      if (seed_.user_supplied())
        pimpl_.reset(new impl(*seed_));
      else
        pimpl_.reset(new impl(static_cast<unsigned>(std::time(0))));
      ncalls=parameters.get<unsigned>("ncalls");
    }

    int process(const ecto::tendrils& inputs, const ecto::tendrils& outputs)
    {
      for (unsigned j=0; j<ncalls; ++j)
        *out_ = (*pimpl_)();
      return ecto::OK;
    }
  };
}

ECTO_CELL(ecto_test, ecto_test::Uniform01, "Uniform01", "Generate random doubles uniformly distributed on [0, 1)");


#include <memory>

#include <gtest/gtest.h>
#include <boost/bind.hpp>
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>

#include "mesh_utils.h"
#include "../src/garland/q_slim.h"
#include "../src/garland/models/quadric/6x6/quadric6.h"

class QuadricTest : public ::testing::Test {
protected:

    garland::Mesh *mesh = nullptr;

    void SetUp() override {

        mesh = new garland::Mesh();

        for (auto &vertex: getVertices()) {
            vertex->setQuadric(std::make_shared<garland::Quadric6>());
            mesh->insert(vertex);
        }

        for (auto &face: getFaces())
            mesh->insert(face);

        mesh->updateFaceNormals(mesh->getFaceKeys());
    }

    void TearDown() override {
        delete mesh;
    }

private:
    /**
     * The example from Garland's PHD Thesis on the page 88
     */
    static std::vector<std::shared_ptr<garland::Vertex>> getVertices() {
        auto vertices = std::vector<std::shared_ptr<garland::Vertex>>();

        vertices.emplace_back(std::make_shared<garland::Vertex>(0, 0, 0, 0.7, 0.3, 0.3, 0));
        vertices.emplace_back(std::make_shared<garland::Vertex>(1, 0, 0, 0.7, 0.4, 0.3, 1));
        vertices.emplace_back(std::make_shared<garland::Vertex>(2, 1, 0, 0.5, 0.5, 0.3, 2));
        vertices.emplace_back(std::make_shared<garland::Vertex>(2, 2, 0, 0.3, 0.5, 0.3, 3));
        vertices.emplace_back(std::make_shared<garland::Vertex>(1, 2, 0, 0.3, 0.4, 0.3, 4));
        vertices.emplace_back(std::make_shared<garland::Vertex>(0, 1, 0, 0.5, 0.3, 0.3, 5));
        vertices.emplace_back(std::make_shared<garland::Vertex>(1, 1, 0, 0.5, 0.4, 0.3, 6));

        return vertices;
    }

    static std::vector<std::shared_ptr<garland::Face>> getFaces() {
        auto faces = std::vector<std::shared_ptr<garland::Face>>();

        faces.emplace_back(std::make_shared<garland::Face>(0, 1, 6, 0));
        faces.emplace_back(std::make_shared<garland::Face>(0, 5, 6, 1));
        faces.emplace_back(std::make_shared<garland::Face>(1, 2, 6, 2));
        faces.emplace_back(std::make_shared<garland::Face>(6, 2, 3, 3));
        faces.emplace_back(std::make_shared<garland::Face>(6, 4, 3, 4));
        faces.emplace_back(std::make_shared<garland::Face>(4, 5, 6, 5));

        return faces;
    }
};

TEST_F(QuadricTest, checkQuadric6x6) {

    boost::program_options::variables_map options;
    auto keys = mesh->getFaceKeys();
    auto *garland = new garland::QSlim(*mesh, keys, options, 1);

    mesh->updateFaceNormals(keys);

    garland->collectQuadrics<garland::Quadric6>(garland::Weighting::none);

    auto vertex = mesh->getVertices()[6];

    Eigen::MatrixXd matrix(6, 6);

    matrix <<
           0.06, 0, 0, 0, -0.59, 0,
            0, 0.23, 0, 1.15, 0, 0,
            0, 0, 6, 0, 0, 0,
            0, 1.15, 0, 5.77, 0, 0,
            -0.59, 0, 0, 0, 5.94, 0,
            0, 0, 0, 0, 0, 6;

    ASSERT_FALSE(vertex->getQuadric()->A.isApprox(matrix));

    delete garland;
}

int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc, argv);
    return (RUN_ALL_TESTS());
}


//==================================================================================================
/**
  EVE - Expressive Vector Engine
  Copyright : EVE Contributors & Maintainers
  SPDX-License-Identifier: MIT
**/
//==================================================================================================
#include <boost/math/special_functions/laguerre.hpp>
#include <eve/function/laguerre.hpp>
#include <eve/constant/inf.hpp>
#include <eve/constant/minf.hpp>
#include <eve/constant/nan.hpp>
#include <eve/platform.hpp>
#include <cmath>

TTS_CASE_TPL("Check eve::laguerre return type", EVE_TYPE)
{
  TTS_EXPR_IS(eve::laguerre(0, T(0)), T);
}

TTS_CASE_TPL("Check eve::laguerre behavior", EVE_TYPE)
{

  auto eve__laguerre =  [](auto n, auto x) { return eve::laguerre(n, x); };
  auto boost_laguerre =  [](auto n, auto x) { return boost::math::laguerre(n, x); };
  using i_t = eve::as_integer_t<T,unsigned>;
  using elt_t = eve::element_type_t<T>;
  for(unsigned int i=0; i < 5; ++i)
  {
    for(elt_t v1 = -10; v1 <= 10; v1+=1)
    {
      auto v = v1/elt_t(10);
      TTS_ULP_EQUAL(eve__laguerre(i, T(v)), T(boost_laguerre(i, v)), 15);
      TTS_ULP_EQUAL(eve__laguerre(i_t(i), T(v)), T(boost_laguerre(i, v)), 15);
    }
  }
}


// Copyright (c) 2020 The Bitcoin Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <pow/aserti32d.h>

#include <chain.h>
#include <chainparams.h>
#include <config.h>
#include <consensus/activation.h>
#include <pow/pow.h>

#include <test/util/setup_common.h>

#include <boost/test/unit_test.hpp>

#include <cmath>

BOOST_FIXTURE_TEST_SUITE(aserti32d_tests, BasicTestingSetup)

static const int64_t t_block = 120;
static const int64_t dh_day = 24 * 3600 / t_block;

static CBlockIndex GetBlockIndex(CBlockIndex *pindexPrev, int64_t nTimeInterval,
                                 uint32_t nBits) {
    CBlockIndex block;
    block.pprev = pindexPrev;
    block.nHeight = pindexPrev->nHeight + 1;
    block.nTime = pindexPrev->nTime + nTimeInterval;
    block.nBits = nBits;

    block.BuildSkip();
    block.nChainWork = pindexPrev->nChainWork + GetBlockProof(block);
    return block;
}

static double TargetFromBits(const uint32_t nBits) {
    return (nBits & 0xff'ff'ff) * pow(256, (nBits >> 24) - 3);
}

static double GetASERTApproximationError(const CBlockIndex *pindexPrev,
                                         const uint32_t finalBits,
                                         const CBlockIndex *pindexAnchorBlock) {
    const int64_t nHeightDiff =
        pindexPrev->nHeight - pindexAnchorBlock->nHeight;
    const int64_t nTimeDiff =
        pindexPrev->GetBlockTime() - pindexAnchorBlock->pprev->GetBlockTime();
    const uint32_t initialBits = pindexAnchorBlock->nBits;

    BOOST_CHECK(nHeightDiff >= 0);
    double dInitialPow = TargetFromBits(initialBits);
    double dFinalPow = TargetFromBits(finalBits);

    double dExponent =
        double(nTimeDiff - (nHeightDiff + 1) * t_block) / double(2 * 24 * 3600);
    double dTarget = dInitialPow * pow(2, dExponent);

    return (dFinalPow - dTarget) / dTarget;
}

BOOST_AUTO_TEST_CASE(asert_difficulty_test) {
    DummyConfig config(CBaseChainParams::MAIN);

    std::vector<CBlockIndex> blocks(3000 + 2 * 24 * 3600);

    const Consensus::Params &params = config.GetChainParams().GetConsensus();
    const arith_uint256 powLimit = UintToArith256(params.powLimit);
    arith_uint256 currentPow = powLimit >> 3;
    uint32_t initialBits = currentPow.GetCompact();
    double dMaxErr = 0.0001168682888;

    // Genesis block, and parent of ASERT anchor block in this test case.
    blocks[0] = CBlockIndex();
    blocks[0].nHeight = 0;
    blocks[0].nTime = 1269211443;
    // The pre-anchor block's nBits should never be used, so we set it to a
    // nonsense value in order to trigger an error if it is ever accessed
    blocks[0].nBits = 0x0dedbeef;

    blocks[0].nChainWork = GetBlockProof(blocks[0]);

    // Block counter.
    size_t i = 1;

    // ASERT anchor block. We give this one a solvetime of 1/4 t_block to ensure
    // that the solvetime between the pre-anchor and the anchor blocks is
    // actually used.
    blocks[1] = GetBlockIndex(&blocks[0], t_block / 4, initialBits);
    // The nBits for the next block should not be equal to the anchor block's
    // nBits
    CBlockHeader blkHeaderDummy;
    uint32_t nBits = GetNextASERTWorkRequired(&blocks[i++], &blkHeaderDummy,
                                              params, &blocks[1]);
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[1])) < dMaxErr);
    BOOST_CHECK(nBits != initialBits);

    // If we add another block at 7/4 t_block, we should return to the anchor
    // block's nBits
    blocks[i] = GetBlockIndex(&blocks[i - 1], t_block * 7 / 4, nBits);
    nBits = GetNextASERTWorkRequired(&blocks[i++], &blkHeaderDummy, params,
                                     &blocks[1]);
    BOOST_CHECK(nBits == initialBits);
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[1])) < dMaxErr);

    currentPow = arith_uint256().SetCompact(nBits);
    // Before we do anything else, check that timestamps *before* the anchor
    // block work fine. Jumping 2 days into the past will give a timestamp
    // before the anchor, and should halve the target
    blocks[i] = GetBlockIndex(&blocks[i - 1], t_block - 172800, nBits);
    nBits = GetNextASERTWorkRequired(&blocks[i++], &blkHeaderDummy, params,
                                     &blocks[1]);
    currentPow = arith_uint256().SetCompact(nBits);
    // Because nBits truncates target, we don't end up with exactly 1/2 the
    // target
    BOOST_CHECK(currentPow <= arith_uint256().SetCompact(initialBits) / 2);
    BOOST_CHECK(currentPow >= arith_uint256().SetCompact(initialBits - 1) / 2);
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[1])) < dMaxErr);

    // Jumping forward 2 days should return the target to the initial value
    blocks[i] = GetBlockIndex(&blocks[i - 1], t_block + 172800, nBits);
    nBits = GetNextASERTWorkRequired(&blocks[i++], &blkHeaderDummy, params,
                                     &blocks[1]);
    currentPow = arith_uint256().SetCompact(nBits);
    BOOST_CHECK(nBits == initialBits);
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[1])) < dMaxErr);

    // Pile up some blocks every t_block to establish some history.
    for (; i < 150; i++) {
        blocks[i] = GetBlockIndex(&blocks[i - 1], t_block, nBits);
        BOOST_CHECK_EQUAL(blocks[i].nBits, nBits);
    }

    nBits = GetNextASERTWorkRequired(&blocks[i - 1], &blkHeaderDummy, params,
                                     &blocks[1]);

    BOOST_CHECK_EQUAL(nBits, initialBits);

    // Difficulty stays the same as long as we produce a block every t_block.
    for (size_t j = 0; j < 10; i++, j++) {
        blocks[i] = GetBlockIndex(&blocks[i - 1], t_block, nBits);
        BOOST_CHECK_EQUAL(GetNextASERTWorkRequired(&blocks[i], &blkHeaderDummy,
                                                   params, &blocks[1]),
                          nBits);
    }

    // If we add a two blocks whose solvetimes together add up to 2 t_block,
    // then the next block's target should be the same as the one before these
    // blocks (at this point, equal to initialBits).
    blocks[i] = GetBlockIndex(&blocks[i - 1], t_block / 2, nBits);
    nBits = GetNextASERTWorkRequired(&blocks[i++], &blkHeaderDummy, params,
                                     &blocks[1]);
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[1])) < dMaxErr);
    // relative
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[i - 2])) < dMaxErr);
    blocks[i] = GetBlockIndex(&blocks[i - 1], t_block * 3 / 2, nBits);
    nBits = GetNextASERTWorkRequired(&blocks[i++], &blkHeaderDummy, params,
                                     &blocks[1]);
    // absolute
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[1])) < dMaxErr);
    // relative
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[i - 2])) < dMaxErr);
    BOOST_CHECK_EQUAL(nBits, initialBits);
    BOOST_CHECK(nBits != blocks[i - 1].nBits);

    // Same in reverse - this time slower block first, followed by faster block.
    blocks[i] = GetBlockIndex(&blocks[i - 1], t_block * 3 / 2, nBits);
    nBits = GetNextASERTWorkRequired(&blocks[i++], &blkHeaderDummy, params,
                                     &blocks[1]);
    // absolute
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[1])) < dMaxErr);
    // relative
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[i - 2])) < dMaxErr);
    blocks[i] = GetBlockIndex(&blocks[i - 1], t_block / 2, nBits);
    nBits = GetNextASERTWorkRequired(&blocks[i++], &blkHeaderDummy, params,
                                     &blocks[1]);
    // absolute
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[1])) < dMaxErr);
    // relative
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[i - 2])) < dMaxErr);
    BOOST_CHECK_EQUAL(nBits, initialBits);
    BOOST_CHECK(nBits != blocks[i - 1].nBits);

    // Jumping forward 2 days should double the target (halve the difficulty)
    blocks[i] = GetBlockIndex(&blocks[i - 1], t_block + 2 * 24 * 3600, nBits);
    nBits = GetNextASERTWorkRequired(&blocks[i++], &blkHeaderDummy, params,
                                     &blocks[1]);
    // absolute
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[1])) < dMaxErr);
    // relative
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[i - 2])) < dMaxErr);
    currentPow = arith_uint256().SetCompact(nBits) / 2;
    BOOST_CHECK_EQUAL(currentPow.GetCompact(), initialBits);

    // Jumping backward 2 days should bring target back to where we started
    blocks[i] = GetBlockIndex(&blocks[i - 1], t_block - 2 * 24 * 3600, nBits);
    nBits = GetNextASERTWorkRequired(&blocks[i++], &blkHeaderDummy, params,
                                     &blocks[1]);
    BOOST_CHECK(fabs(GetASERTApproximationError(
                    &blocks[i - 1], nBits, &blocks[1])) < dMaxErr); // absolute
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[i - 2])) <
                dMaxErr); // relative
    BOOST_CHECK_EQUAL(nBits, initialBits);

    // Jumping backward 2 days should halve the target (double the difficulty)
    blocks[i] = GetBlockIndex(&blocks[i - 1], t_block - 2 * 24 * 3600, nBits);
    nBits = GetNextASERTWorkRequired(&blocks[i++], &blkHeaderDummy, params,
                                     &blocks[1]);
    // absolute
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[1])) < dMaxErr);
    // relative
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[i - 2])) < dMaxErr);
    currentPow = arith_uint256().SetCompact(nBits);
    // Because nBits truncates target, we don't end up with exactly 1/2 the
    // target
    BOOST_CHECK(currentPow <= arith_uint256().SetCompact(initialBits) / 2);
    BOOST_CHECK(currentPow >= arith_uint256().SetCompact(initialBits - 1) / 2);

    // And forward again
    blocks[i] = GetBlockIndex(&blocks[i - 1], t_block + 2 * 24 * 3600, nBits);
    nBits = GetNextASERTWorkRequired(&blocks[i++], &blkHeaderDummy, params,
                                     &blocks[1]);
    // absolute
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[1])) < dMaxErr);
    // relative
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[i - 2])) < dMaxErr);
    BOOST_CHECK_EQUAL(nBits, initialBits);
    blocks[i] = GetBlockIndex(&blocks[i - 1], t_block + 2 * 24 * 3600, nBits);
    nBits = GetNextASERTWorkRequired(&blocks[i++], &blkHeaderDummy, params,
                                     &blocks[1]);
    // absolute
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[1])) < dMaxErr);
    // relative
    BOOST_CHECK(fabs(GetASERTApproximationError(&blocks[i - 1], nBits,
                                                &blocks[i - 2])) < dMaxErr);
    currentPow = arith_uint256().SetCompact(nBits) / 2;
    BOOST_CHECK_EQUAL(currentPow.GetCompact(), initialBits);

    // Iterate over the entire -2*24*3600..+2*24*3600 range to check that our
    // integer approximation:
    //   1. Should be monotonic.
    //   2. Should change target at least once every 8 seconds (worst-case:
    //      15-bit precision on nBits)
    //   3. Should never change target by more than XXXX per 1-second step.
    //   4. Never exceeds dMaxError in absolute error vs a double float
    //      calculation.
    //   5. Has almost exactly the dMax and dMin errors we expect for the
    //      formula.
    double dMin = 0;
    double dMax = 0;
    double dErr;
    double dRelMin = 0;
    double dRelMax = 0;
    double dRelErr;
    double dMaxStep = 0;
    uint32_t nBitsRingBuffer[8];
    double dStep = 0;
    blocks[i] = GetBlockIndex(&blocks[i - 1], -2 * 24 * 3600 - 30, nBits);
    for (size_t j = 0; j < 4 * 24 * 3600 + 660; j++) {
        blocks[i].nTime++;
        nBits = GetNextASERTWorkRequired(&blocks[i], &blkHeaderDummy, params,
                                         &blocks[1]);

        if (j > 8) {
            // 1: Monotonic
            BOOST_CHECK(
                arith_uint256().SetCompact(nBits) >=
                arith_uint256().SetCompact(nBitsRingBuffer[(j - 1) % 8]));
            // 2: Changes at least once every 8 seconds (worst case: nBits =
            // 1d008000 to 1d008001)
            BOOST_CHECK(arith_uint256().SetCompact(nBits) >
                        arith_uint256().SetCompact(nBitsRingBuffer[j % 8]));
            // 3: Check 1-sec step size
            dStep = (TargetFromBits(nBits) -
                     TargetFromBits(nBitsRingBuffer[(j - 1) % 8])) /
                    TargetFromBits(nBits);
            dMaxStep = std::max(dMaxStep, dStep);
            // from nBits = 1d008000 to 1d008001
            BOOST_CHECK(dStep < 0.0000314812106363);
        }
        nBitsRingBuffer[j % 8] = nBits;

        // 4 and 5: check error vs double precision float calculation
        dErr = GetASERTApproximationError(&blocks[i], nBits, &blocks[1]);
        dRelErr = GetASERTApproximationError(&blocks[i], nBits, &blocks[i - 1]);
        dMin = std::min(dMin, dErr);
        dMax = std::max(dMax, dErr);
        dRelMin = std::min(dRelMin, dRelErr);
        dRelMax = std::max(dRelMax, dRelErr);
        BOOST_CHECK_MESSAGE(
            fabs(dErr) < dMaxErr,
            strprintf(
                "solveTime: %d\tStep size: %.8f%%\tdErr: %.8f%%\tnBits: %0x\n",
                int64_t(blocks[i].nTime) - blocks[i - 1].nTime, dStep * 100,
                dErr * 100, nBits));
        BOOST_CHECK_MESSAGE(
            fabs(dRelErr) < dMaxErr,
            strprintf("solveTime: %d\tStep size: %.8f%%\tdRelErr: "
                      "%.8f%%\tnBits: %0x\n",
                      int64_t(blocks[i].nTime) - blocks[i - 1].nTime,
                      dStep * 100, dRelErr * 100, nBits));
    }
    auto failMsg = strprintf(
        "Min error: %16.14f%%\tMax error: %16.14f%%\tMax step: %16.14f%%\n",
        dMin * 100, dMax * 100, dMaxStep * 100);
    BOOST_CHECK_MESSAGE(
        dMin < -0.0001011019566646 && dMin > -0.0001011019566647 &&
            dMax > 0.0001168682887368 && dMax < 0.0001168682887369,
        failMsg);
    failMsg = strprintf("Min relError: %16.14f%%\tMax relError: %16.14f%%\n",
                        dRelMin * 100, dRelMax * 100);
    BOOST_CHECK_MESSAGE(
        dRelMin < -0.0001011019566646 && dRelMin > -0.0001011019566647 &&
            dRelMax > 0.0001168682887368 && dRelMax < 0.0001168682887369,
        failMsg);

    // Difficulty increases as long as we produce fast blocks
    for (size_t j = 0; j < 100; i++, j++) {
        uint32_t nextBits;
        arith_uint256 currentTarget;
        currentTarget.SetCompact(nBits);

        blocks[i] = GetBlockIndex(&blocks[i - 1], t_block * 5 / 6, nBits);
        nextBits = GetNextASERTWorkRequired(&blocks[i], &blkHeaderDummy, params,
                                            &blocks[1]);
        arith_uint256 nextTarget;
        nextTarget.SetCompact(nextBits);

        // Make sure that target is decreased
        BOOST_CHECK(nextTarget <= currentTarget);

        nBits = nextBits;
    }
}

static std::string StrPrintCalcArgs(const arith_uint256 refTarget,
                                    const int64_t targetSpacing,
                                    const int64_t timeDiff,
                                    const int64_t heightDiff,
                                    const arith_uint256 expectedTarget,
                                    const uint32_t expectednBits) {
    return strprintf("\n"
                     "ref=         %s\n"
                     "spacing=     %d\n"
                     "timeDiff=    %d\n"
                     "heightDiff=  %d\n"
                     "expTarget=   %s\n"
                     "exp nBits=   0x%08x\n",
                     refTarget.ToString(), targetSpacing, timeDiff, heightDiff,
                     expectedTarget.ToString(), expectednBits);
}

// Tests of the CalculateASERT function.
BOOST_AUTO_TEST_CASE(calculate_asert_test) {
    DummyConfig config(CBaseChainParams::MAIN);
    const Consensus::Params &params = config.GetChainParams().GetConsensus();
    const int64_t nHalfLife = params.nDAAHalfLife;

    const arith_uint256 powLimit = UintToArith256(params.powLimit);
    arith_uint256 initialTarget = powLimit >> 4;
    int64_t height = 0;

    // The CalculateASERT function uses the absolute ASERT formulation
    // and adds +1 to the height difference that it receives.
    // The time difference passed to it must factor in the difference
    // to the *parent* of the reference block.
    // We assume the parent is ideally spaced in time before the reference
    // block.
    static const int64_t parent_time_diff = t_block;

    int64_t current_time = t_block;

    // Steady block
    current_time += t_block;
    arith_uint256 nextTarget = CalculateASERT(
        initialTarget, params.nPowTargetSpacing,
        current_time, ++height, powLimit, nHalfLife);
    BOOST_CHECK(nextTarget == initialTarget);

    // A block that arrives in half the expected time
    current_time += t_block / 2;
    nextTarget = CalculateASERT(initialTarget, params.nPowTargetSpacing,
                                current_time, ++height,
                                powLimit, nHalfLife);
    BOOST_CHECK(nextTarget < initialTarget);

    // A block that makes up for the shortfall of the previous one, restores the
    // target to initial
    current_time += (t_block / 2) * 3;
    arith_uint256 prevTarget = nextTarget;
    nextTarget = CalculateASERT(initialTarget, params.nPowTargetSpacing,
                                current_time, ++height,
                                powLimit, nHalfLife);
    BOOST_CHECK(nextTarget > prevTarget);
    BOOST_CHECK(nextTarget == initialTarget);

    // Two days ahead of schedule should double the target (halve the
    // difficulty)
    prevTarget = nextTarget;
    nextTarget =
        CalculateASERT(prevTarget, params.nPowTargetSpacing,
                       parent_time_diff + 4 * 24 * 3600,
                       2 * dh_day, powLimit, nHalfLife);
    BOOST_CHECK(nextTarget == prevTarget * 2);

    // Two days behind schedule should halve the target (double the difficulty)
    prevTarget = nextTarget;
    nextTarget =
        CalculateASERT(prevTarget, params.nPowTargetSpacing,
                       parent_time_diff + 0, 2 * dh_day, powLimit, nHalfLife);
    BOOST_CHECK(nextTarget == prevTarget / 2);
    BOOST_CHECK(nextTarget == initialTarget);

    // Ramp up from initialTarget to PowLimit - should only take 4 doublings...
    uint32_t powLimit_nBits = powLimit.GetCompact();
    uint32_t next_nBits;
    for (size_t k = 0; k < 3; k++) {
        prevTarget = nextTarget;
        nextTarget = CalculateASERT(prevTarget, params.nPowTargetSpacing,
                                    parent_time_diff + 4 * 24 * 3600,
                                    2 * dh_day,
                                    powLimit, nHalfLife);
        BOOST_CHECK(nextTarget == prevTarget * 2);
        BOOST_CHECK(nextTarget < powLimit);
        next_nBits = nextTarget.GetCompact();
        BOOST_CHECK(next_nBits != powLimit_nBits);
    }

    prevTarget = nextTarget;
    nextTarget =
        CalculateASERT(prevTarget, params.nPowTargetSpacing,
                       parent_time_diff + 4 * 24 * 3600,
                       2 * dh_day, powLimit, nHalfLife);
    next_nBits = nextTarget.GetCompact();
    BOOST_CHECK(nextTarget == prevTarget * 2);
    BOOST_CHECK(next_nBits == powLimit_nBits);

    // Fast periods now cannot increase target beyond POW limit, even if we try
    // to overflow nextTarget. prevTarget is a uint256, so 256*2 = 512 days
    // would overflow nextTarget unless CalculateASERT correctly detects this
    // error
    nextTarget = CalculateASERT(prevTarget, params.nPowTargetSpacing,
                                parent_time_diff + 512 * 144 * t_block, 0,
                                powLimit, nHalfLife);
    next_nBits = nextTarget.GetCompact();
    BOOST_CHECK(next_nBits == powLimit_nBits);

    // We also need to watch for underflows on nextTarget. We need to withstand
    // an extra ~446 days worth of blocks. This should bring down a powLimit
    // target to the a minimum target of 1.
    nextTarget = CalculateASERT(powLimit, params.nPowTargetSpacing, 0,
                                2 * (256 - 33) * dh_day, powLimit, nHalfLife);
    next_nBits = nextTarget.GetCompact();
    BOOST_CHECK_EQUAL(next_nBits, arith_uint256(1).GetCompact());

    // Define a structure holding parameters to pass to CalculateASERT.
    // We are going to check some expected results  against a vector of
    // possible arguments.
    struct calc_params {
        arith_uint256 refTarget;
        int64_t targetSpacing;
        int64_t timeDiff;
        int64_t heightDiff;
        arith_uint256 expectedTarget;
        uint32_t expectednBits;
    };

    // Define some named input argument values
    const arith_uint256 SINGLE_300_TARGET{
        "000000000ffb2000000000000000000000000000000000000000000000000000"};
    const arith_uint256 FUNNY_REF_TARGET{
        "000000000800000000000000000fffffffffffffffffffffffffffffffffffff"};

    // Define our expected input and output values.
    // The timeDiff entries exclude the `parent_time_diff` - this is
    // added in the call to CalculateASERT in the test loop.
    const std::vector<calc_params> calculate_args = {

        /* refTarget, targetSpacing, timeDiff, heightDiff, expectedTarget,
           expectednBits */

        {powLimit, t_block, 0, 2 * dh_day, powLimit >> 1, 0x1c080000},
        {powLimit, t_block, 0, 4 * dh_day, powLimit >> 2, 0x1c040000},
        {powLimit >> 1, t_block, 0, 2 * dh_day, powLimit >> 2, 0x1c040000},
        {powLimit >> 2, t_block, 0, 2 * dh_day, powLimit >> 3, 0x1c020000},
        {powLimit >> 3, t_block, 0, 2 * dh_day, powLimit >> 4, 0x1c010000},
        {powLimit, t_block, 0, 2 * (256 - 38) * dh_day, 4, 0x01040000},
        {powLimit, t_block, 0, 2 * (256 - 38) * dh_day + 1, 3, 0x01030000},
        {powLimit, t_block, 0, 2 * (256 - 38) * dh_day + 600 * 119 / t_block, 3,
         0x01030000},
        {powLimit, t_block, 0, 2 * (256 - 38) * dh_day + 600 * 120 / t_block, 2,
         0x01020000},
        {powLimit, t_block, 0, 2 * (256 - 37) * dh_day, 2, 0x01020000},
        // 1 bit less since we do not need to shift to 0
        {powLimit, t_block, 0, 2 * (256 - 37) * dh_day + 1, 1, 0x01010000},
        // more will not decrease below 1
        {powLimit, t_block, 0, 2 * (256 - 36) * dh_day, 1, 0x01010000},
        {1, t_block, 0, 2 * (256 - 36) * dh_day, 1, 0x01010000},
        {powLimit, t_block, 2 * (512 - 32) * dh_day, 0, powLimit,
         powLimit_nBits},
        {1, t_block, (512 - 64 - 8) * dh_day * t_block, 0, powLimit,
         powLimit_nBits},
        // clamps to powLimit
        {powLimit, t_block, 300, dh_day / 144, SINGLE_300_TARGET, 0x1c0ffb20},
        // confuses any attempt to detect overflow by inspecting result
        {FUNNY_REF_TARGET, t_block, t_block * 2 * 33 * dh_day, 0, powLimit,
         powLimit_nBits},
        {1, t_block, t_block * 2 * 220 * dh_day, 0, powLimit, powLimit_nBits},
        // just under powlimit (not clamped) and powlimit_nbits
        {1, t_block, t_block * 2 * 220 * dh_day - 1, 0,
         arith_uint256(0xffff8) << 200, 0x1c0ffff8},
    };

    for (auto &v : calculate_args) {
        nextTarget = CalculateASERT(v.refTarget, v.targetSpacing,
                                    parent_time_diff + v.timeDiff, v.heightDiff,
                                    powLimit, nHalfLife);
        next_nBits = nextTarget.GetCompact();
        const auto failMsg =
            StrPrintCalcArgs(v.refTarget, v.targetSpacing,
                             parent_time_diff + v.timeDiff, v.heightDiff,
                             v.expectedTarget, v.expectednBits) +
            strprintf("nextTarget=  %s\nnext nBits=  0x%08x\n",
                      nextTarget.ToString(), next_nBits);
        BOOST_CHECK_MESSAGE(nextTarget == v.expectedTarget &&
                                next_nBits == v.expectednBits,
                            failMsg);
    }
}

BOOST_AUTO_TEST_SUITE_END()


#include "exception.hh"
#include "network.hh"
#include "timer.hh"

#include <Eigen/Dense>
#include <iostream>
#include <utility>

#include <sys/resource.h>
#include <sys/time.h>

using namespace std;
using namespace Eigen;

constexpr size_t batch_size = 3;
constexpr size_t input_size = 1;

void program_body( const unsigned int num_iterations )
{
  /* remove limit on stack size */
  const rlimit limits { RLIM_INFINITY, RLIM_INFINITY };
  CheckSystemCall( "setrlimit", setrlimit( RLIMIT_STACK, &limits ) );

  /* seed C RNG for Eigen random weight initialization */
  srand( Timer::timestamp_ns() );

  /* construct neural network on heap */
  auto nn = make_unique<Network<float, batch_size, input_size, 1024, 1024, 1024, 1024, 1024, 1024, 1>>();
  // auto nn = make_unique<Network<batch_size, input_size, 5, 3, 2, 1>>();
  nn->initializeWeightsRandomly();

  /* initialize inputs */
  vector<Matrix<float, batch_size, input_size>> inputs;
  for ( unsigned int i = 0; i < num_iterations; i++ ) {
    inputs.emplace_back( Matrix<float, batch_size, input_size>::Random() );
  }

  /* run benchmark */
  const uint64_t start = Timer::timestamp_ns();
  for ( unsigned int i = 0; i < num_iterations; i++ ) {
    nn->apply( inputs[i] );
  }

  const uint64_t end = Timer::timestamp_ns();

  cout << "Average runtime (over " << num_iterations << " iterations, batch size=" << batch_size << "): ";
  Timer::pp_ns( cout, ( end - start ) / float( num_iterations ) );
  cout << " per iteration\n";
}

int main( int argc, char* argv[] )
{
  try {
    if ( argc <= 0 ) {
      abort();
    }

    if ( argc != 2 ) {
      cerr << "Usage: " << argv[0] << " NUM_ITERATIONS\n";
      return EXIT_FAILURE;
    }

    program_body( stoi( argv[1] ) );

    return EXIT_SUCCESS;
  } catch ( const exception& e ) {
    cerr << e.what() << "\n";
    return EXIT_FAILURE;
  }
}


//
// Implements Tree-iLQR.
//

#pragma once

#include <ilqr/tree.hh>
#include <lqr/lqr_types.hh>

#include <Eigen/Dense>

#include <memory>
#include <list>
#include <vector>

namespace lqr
{

// Shared pointer to a Node in the underlying Tree structure of the iLQR-Tree. Calling ->item()
// returns a shared pointer to the PlanNode that holds the state/control/dynamics/etc.
// information.
using TreeNodePtr = std::shared_ptr<data::Node<PlanNode>>;

class LQRTree
{
public:
    LQRTree(int state_dim, int control_dim);
    virtual ~LQRTree() = default;

    // Construct a PlanNode which represents the dynamics, cost functions as well as the state
    // and control policy at a specific time step along the tree. Nominal state and control inputs
    // are passed and used for the initial Taylor expansions of the cost and dynamics functions.
    std::shared_ptr<PlanNode> make_plan_node(const Eigen::MatrixXd &A,
                                             const Eigen::MatrixXd &B,
                                             const Eigen::MatrixXd &Q,
                                             const Eigen::MatrixXd &R,
                                             const double probablity);

    // Add the root node to the iLQR Tree with similar arguments to the make_plan_node function.
    TreeNodePtr add_root(const Eigen::MatrixXd &A,
                         const Eigen::MatrixXd &B,
                         const Eigen::MatrixXd &Q,
                         const Eigen::MatrixXd &R);

    // Add a PlanNode as the root node to the iLQR Tree. Requires the probability to be 1.
    TreeNodePtr add_root(const std::shared_ptr<PlanNode> &plan_node);

    // Add a list of PlanNodes as children under a parent. The probabilities must sum to 1.
    std::vector<TreeNodePtr> add_nodes(const std::vector<std::shared_ptr<PlanNode>> &plan_nodes,
            TreeNodePtr &parent);

    // Get the root node of the Tree.
    TreeNodePtr root();

    // Forward pass to generate new nominal points for iLQR.
    // The step-size is controlled by alpha (alpha * K).
    void forward_pass(const Eigen::VectorXd &x0);

    // Do a full bellman backup on the tree.
    void bellman_tree_backup();

private:
    int state_dim_ = 0;
    int control_dim_ = 0;

    data::Tree<PlanNode> tree_;

    // Zeros matrix of size [state_dim +1] x [state_dim +1]. Used as
    // initialization for the zeros matrix.
    const Eigen::MatrixXd ZERO_VALUE_MATRIX_;

    // The alpha is the step size to use in the control application.
    Eigen::MatrixXd forward_node(std::shared_ptr<PlanNode> node,
                                 const Eigen::MatrixXd &xt);

    // Special case for just the leaves of the tree. We can compute this by giving the leaves
    // synthetic children with $V_{T+1} = 0$.
    void control_and_value_for_leaves();

    // Backups the value matrix and control gains matrix from the children of the node to get
    // the value and control policies for the parents of the children. Returns a list of all the
    // parents.
    std::list<TreeNodePtr> backup_to_parents(const std::list<TreeNodePtr> &all_children);

    // Helper to compute the value matrix for plan_node given a child's V_{t+1} value matrix.
    // Returns the result and does not store the result in the PlanNode.
    Eigen::MatrixXd compute_value_matrix(const std::shared_ptr<PlanNode> &node,
            const Eigen::MatrixXd &Vt1);

    // Helper to compute the feedback and feedforward control policies. Stores them in the PlanNode.
    void compute_control_policy(std::shared_ptr<PlanNode> &node, const Eigen::MatrixXd &Vt1);
};

} // namespace lqr


// Copyright 2015-2018 Hans Dembinski
//
// 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)

//[ guide_histogram_streaming

#include <boost/histogram.hpp>
#include <boost/histogram/ostream.hpp>
#include <cassert>
#include <iostream>
#include <sstream>
#include <string>

int main() {
  using namespace boost::histogram;
  namespace tr = axis::transform;

  auto h =
      make_histogram(axis::regular<>(2, -1.0, 1.0),
                     axis::regular<double, tr::log>(2, 1.0, 10.0, "axis 1"),
                     axis::regular<double, tr::pow, use_default, axis::option::growth_t>(
                         tr::pow{1.5}, 2, 1.0, 10.0, "axis 2"),
                     // axis without metadata
                     axis::circular<double, axis::null_type>(4, 0.0, 360.0),
                     // axis without under-/overflow bins
                     axis::variable<double, use_default, axis::option::none_t>(
                         {-1.0, 0.0, 1.0}, "axis 4"),
                     axis::category<>({2, 1, 3}, "axis 5"),
                     axis::category<std::string>({"red", "blue"}, "axis 6"),
                     axis::integer<>(-1, 1, "axis 7"));

  std::ostringstream os;
  os << h;

  std::cout << os.str() << std::endl;

  assert(os.str() ==
         "histogram(\n"
         "  regular(2, -1, 1, options=underflow | overflow),\n"
         "  regular_log(2, 1, 10, metadata=\"axis 1\", options=underflow | overflow),\n"
         "  regular_pow(2, 1, 10, metadata=\"axis 2\", options=growth, power=1.5),\n"
         "  regular(4, 0, 360, options=overflow | circular),\n"
         "  variable(-1, 0, 1, metadata=\"axis 4\", options=none),\n"
         "  category(2, 1, 3, metadata=\"axis 5\", options=overflow),\n"
         "  category(\"red\", \"blue\", metadata=\"axis 6\", options=overflow),\n"
         "  integer(-1, 1, metadata=\"axis 7\", options=underflow | overflow)\n"
         ")");
}

//]


//=======================================================================
// Copyright (c) 2005 Aaron Windsor
//
// 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 <cstdlib>
#include <iostream>
#include <boost/property_map/vector_property_map.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/random.hpp>
#include <ctime>
#include <boost/random.hpp>

#include <boost/graph/max_cardinality_matching.hpp>

using namespace boost;

typedef adjacency_list<vecS,
                       vecS,
                       undirectedS,
                       property<vertex_index_t, int> >  undirected_graph;

typedef property_map<undirected_graph,vertex_index_t>::type vertex_index_map_t;
typedef vector_property_map<graph_traits<undirected_graph>::vertex_descriptor, vertex_index_map_t > mate_t;
typedef graph_traits<undirected_graph>::vertex_iterator vertex_iterator_t;
typedef graph_traits<undirected_graph>::vertex_descriptor vertex_descriptor_t;
typedef graph_traits<undirected_graph>::vertices_size_type v_size_t;

int main(int argc, char** argv)
{
  if (argc < 3)
    {
      std::cout << "Usage: " << argv[0] << " n m" << std::endl
                << "Tests the checked matching on a random graph w/ n vertices and m edges" << std::endl;
      exit(-1);
    }

  int n = atoi(argv[1]);
  int m = atoi(argv[2]);

  undirected_graph g(n);

  typedef boost::mt19937 base_generator_type;
  base_generator_type generator(static_cast<unsigned int>(std::time(0)));
  boost::uniform_int<> distribution(0, n-1);
  boost::variate_generator<base_generator_type&, boost::uniform_int<> > rand_num(generator, distribution);

  int num_edges = 0;
  bool success;

  while (num_edges < m)
    {
      vertex_descriptor_t u = random_vertex(g,rand_num);
      vertex_descriptor_t v = random_vertex(g,rand_num);
      if (u != v)
        {
          if (!edge(u,v,g).second)
            boost::tie(tuples::ignore, success) = add_edge(u, v, g);
          else
            success = false;

          if (success)
            num_edges++;
        }
    }

  mate_t mate(n);
  bool random_graph_result = checked_edmonds_maximum_cardinality_matching(g,mate);

  if (!random_graph_result)
    {
      std::cout << "TEST 1 FAILED!!!" << std::endl << std::endl;

      std::cout << "Graph has edges: ";
      typedef graph_traits<undirected_graph>::edge_iterator edge_iterator_t;
      edge_iterator_t ei,ei_end;
      for(boost::tie(ei,ei_end) = edges(g); ei != ei_end; ++ei)
        std:: cout << *ei << ", ";
      std::cout << std::endl;

      std::cout << "Matching is: ";
      vertex_iterator_t vi, vi_end;
      for(boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi)
        if (mate[*vi] != graph_traits<undirected_graph>::null_vertex() &&
            *vi < mate[*vi])
          std::cout << "{" << *vi << "," << mate[*vi] << "}, ";
      std::cout << std::endl;
    }

  //Now remove an edge from the random_mate matching.
  vertex_iterator_t vi, vi_end;
  for(boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi)
    if (mate[*vi] != graph_traits<undirected_graph>::null_vertex())
      break;

  mate[mate[*vi]] = graph_traits<undirected_graph>::null_vertex();
  mate[*vi] = graph_traits<undirected_graph>::null_vertex();

  //...and run the matching verifier - it should tell us that the matching isn't
  //a maximum matching.
  bool modified_random_verification_result =
    maximum_cardinality_matching_verifier<undirected_graph,mate_t,vertex_index_map_t>::verify_matching(g,mate,get(vertex_index,g));

  if (modified_random_verification_result)
    {
      std::cout << "TEST 2 FAILED!!!" << std::endl;
    }

  //find a greedy matching on the graph
  mate_t greedy_mate(n);
  greedy_matching<undirected_graph, mate_t>::find_matching(g,greedy_mate);

  if (matching_size(g,mate) > matching_size(g,greedy_mate) &&
      maximum_cardinality_matching_verifier<undirected_graph,mate_t,vertex_index_map_t>::verify_matching(g,greedy_mate,get(vertex_index,g)))
        std::cout << "TEST 3 FAILED!!!" << std::endl;

  return 0;
}