//=======================================================================
// Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
//
// 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/config.hpp>
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>

/*
  Sample output:

  original graph:
  0 --> 3 2 3
  1 --> 3
  2 --> 0
  3 --> 2
  1(0,3) 2(0,2) 3(0,3) 4(1,3) 5(2,0) 6(3,2)

  removing edges connecting 0 to 3
  0 --> 2
  1 --> 3
  2 --> 0
  3 --> 2
  2(0,2) 4(1,3) 5(2,0) 6(3,2)
  removing edges with weight greater than 3
  0 --> 2
  1 -->
  2 -->
  3 -->
  2(0,2)

 */

using namespace boost;

typedef adjacency_list<vecS, vecS, bidirectionalS,
  no_property, property<edge_weight_t, int> > Graph;

struct has_weight_greater_than {
  has_weight_greater_than(int w_, Graph& g_) : w(w_), g(g_) { }
  bool operator()(graph_traits<Graph>::edge_descriptor e) {
#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300
    property_map<Graph, edge_weight_t>::type weight = get(edge_weight, g);
    return get(weight, e) > w;
#else
    // This version of get() breaks VC++
    return get(edge_weight, g, e) > w;
#endif
  }
  int w;
  Graph& g;
};

int
main()
{
  typedef std::pair<std::size_t,std::size_t> Edge;
  Edge edge_array[6] = { Edge(0,3), Edge(0,2), Edge(0, 3),
                         Edge(1,3),
                         Edge(2, 0),
                         Edge(3, 2) };

#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300
  Graph g(4);
  for (std::size_t j = 0; j < 6; ++j)
    add_edge(edge_array[j].first, edge_array[j].second, g);
#else
  Graph g(edge_array, edge_array + 6, 4);
#endif
  property_map<Graph, edge_weight_t>::type
    weight = get(edge_weight, g);

  int w = 0;
  graph_traits<Graph>::edge_iterator ei, ei_end;
  for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
    weight[*ei] = ++w;

  property_map<Graph, vertex_index_t>::type indexmap = get(vertex_index, g);

  std::cout << "original graph:" << std::endl;
  print_graph(g, indexmap);
  print_edges2(g, indexmap, weight);
  std::cout << std::endl;

  std::cout << "removing edges connecting 0 to 3" << std::endl;
  remove_out_edge_if(vertex(0,g), incident_to(vertex(3,g), g), g);
  print_graph(g, indexmap);
  print_edges2(g, indexmap, weight);

  std::cout << "removing edges with weight greater than 3" << std::endl;
  remove_edge_if(has_weight_greater_than(3, g), g);
  print_graph(g, indexmap);
  print_edges2(g, indexmap, weight);
  return 0;
}


//
// Created by Сергей Кривонос on 10.08.17.
//
#define BOOST_TEST_MODULE ImageCodec test
#include <boost/test/unit_test.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
#include <boost/gil/extension/io/targa.hpp>
#include "Extrapolator.h"

using namespace omnn::math;
using namespace boost::unit_test;
using namespace boost::gil;
using namespace std;

std::string l(const omnn::math::Valuable& v)
{
    std::stringstream ss;
    ss << v;
    return ss.str();
}

BOOST_AUTO_TEST_CASE(ImageCodec_test)
{
    rgba8_image_t src;
    read_image(TEST_SRC_DIR "g.tga", src, targa_tag());
    write_view(TEST_BIN_DIR "was.tga", view(src), targa_tag());

    auto rows = src.dimensions().y;
    auto cols = src.dimensions().x;
    Extrapolator a(rows, cols);
    Extrapolator r(rows, cols);
    Extrapolator g(rows, cols);
    Extrapolator b(rows, cols);

    auto v = view(src);
    for (auto i = rows; i--;) { // raw
        for (auto j = cols; j--;) { // column
            auto px = v(i,j);
            a(i,j) = get_color(px,alpha_t());
            r(i,j) = get_color(px,red_t());
            g(i,j) = get_color(px,green_t());
            b(i,j) = get_color(px,blue_t());
        }
    }

    Variable x, y, z;
    std::list<Variable> formulaParamSequence = { y, x };
    auto fa = a.Factors(y, x, z);
//    fa.SetView(Valuable::View::Flat);
    fa.optimize();
    std::cout << fa << std::endl;
    auto fr = r.Factors(y, x, z);
//    fr.SetView(Valuable::View::Flat);
    fr.optimize();
    std::cout << fr << std::endl;
    auto fg = g.Factors(y, x, z);
//    fg.SetView(Valuable::View::Flat);
    fg.optimize();
    std::cout << fg << std::endl;
    auto fb = b.Factors(y, x, z);
//    fb.SetView(Valuable::View::Flat);
    fb.optimize();
    std::cout << fb << std::endl;

    // Unification
    FormulaOfVaWithSingleIntegerRoot
        afo(z, fa, &formulaParamSequence),
        rfo(z, fr, &formulaParamSequence),
        gfo(z, fg, &formulaParamSequence),
        bfo(z, fb, &formulaParamSequence);

    // inbound data deduce
    decltype(src) dst(src.dimensions());
    auto dv = view(dst);
    for (auto i = rows; i--;) { // raw
        for (auto j = cols; j--;) { // column
            auto& d = dv(i, j);
            auto& s = v(i,j);
            get_color(d,alpha_t()) = static_cast<unsigned char>(afo(i,j));
            get_color(d,red_t()) = static_cast<unsigned char>(rfo(i,j));
            get_color(d,green_t()) = static_cast<unsigned char>(gfo(i,j));
            get_color(d,blue_t()) = static_cast<unsigned char>(bfo(i,j));

            BOOST_TEST(unsigned(d[0])==unsigned(s[0]));
            BOOST_TEST(unsigned(d[1])==unsigned(s[1]));
            BOOST_TEST(unsigned(d[2])==unsigned(s[2]));
            BOOST_TEST(unsigned(d[3])==unsigned(s[3]));
        }
    }
    write_view(TEST_BIN_DIR "o.tga", dv, targa_tag());

    // outband data deduce
    afo.SetMode(FormulaOfVaWithSingleIntegerRoot::Newton);
    afo.SetMin(0); afo.SetMax(255);
    rfo.SetMode(FormulaOfVaWithSingleIntegerRoot::Newton);
    rfo.SetMin(0); rfo.SetMax(255);
    gfo.SetMode(FormulaOfVaWithSingleIntegerRoot::Newton);
    gfo.SetMin(0); gfo.SetMax(255);
    bfo.SetMode(FormulaOfVaWithSingleIntegerRoot::Newton);
    bfo.SetMin(0); bfo.SetMax(255);

    const auto d = 2; // 5
    cols+=d;rows+=d;
    dst = decltype(src)(rows+d, cols+d);
    dv = view(dst);
    for (auto i = rows; i>=-d; --i) { // raw
        for (auto j = cols; j>=-d; --j) { // column
            auto c=j+d;
            auto r = i+d;
            auto& d = dv(r, c);
            get_color(d,alpha_t()) = static_cast<unsigned char>(afo(i,j));
            get_color(d,red_t()) = static_cast<unsigned char>(rfo(i,j));
            get_color(d,green_t()) = static_cast<unsigned char>(gfo(i,j));
            get_color(d,blue_t()) = static_cast<unsigned char>(bfo(i,j));
        }
    }
    write_view(TEST_BIN_DIR "e.tga", dv, targa_tag());
}


#include <boost/test/unit_test.hpp>

#include "logs.h"
#include "route.h"
#include "track.h"

using namespace GPS;

BOOST_AUTO_TEST_SUITE( Route_TotalLength )

const bool isFileName = true;

// Check if the function can find the total length within the ABCD.gpx
BOOST_AUTO_TEST_CASE( RouteABCD )
{
    Route route = Route(LogFiles::GPXRoutesDir + "ABCD.gpx", isFileName);
   BOOST_CHECK_EQUAL( route.totalLength(), 82.6);
}

// Check if the function can find the total length within the RouteNorthYorkMoors.gpx
BOOST_AUTO_TEST_CASE( RouteNorthYorkMoors )
{
    Route route = Route(LogFiles::GPXRoutesDir + "NorthYorkMoors.gpx", isFileName);
   BOOST_CHECK_EQUAL( route.totalLength(), 56.8113);
}

// Check if the function can find the total length within the RouteLatitudeGreater90.gpx
BOOST_AUTO_TEST_CASE( RouteLatitudeGreater90 )
{
    Route route = Route(LogFiles::GPXRoutesDir + "RouteLatitudeGreater90.gpx", isFileName);
   BOOST_CHECK_EQUAL( route.totalLength(), 142.7);
}

//Check if the function can handle stationary negative points in a route file
BOOST_AUTO_TEST_CASE( RouteStationaryNegativePoints )
{
   Route route = Route(LogFiles::GPXRoutesDir + "RouteStationaryNegativePoints.gpx", isFileName);
   BOOST_CHECK_EQUAL( route.totalLength(), -19.32);
}

//Check if the function can handle stationary negative points in a track file
BOOST_AUTO_TEST_CASE( TrackStationaryNegativePoints )
{
   Track track = Track(LogFiles::GPXTracksDir + "TrackStationaryNegativePoints.gpx", isFileName);
   BOOST_CHECK_EQUAL( track.totalLength(), -41.56);
}

BOOST_AUTO_TEST_SUITE_END()


#include "math/SparseMatrix.h"

/// Disable some warning to compile Eigen with gcc 7.1
#ifdef SPH_GCC
#if __GNUC__ >= 7
#pragma GCC diagnostic ignored "-Wint-in-bool-context"
#pragma GCC diagnostic ignored "-Wduplicated-branches"
#endif
#endif

#ifdef SPH_USE_EIGEN
#include <Eigen/IterativeLinearSolvers>
#include <Eigen/SparseLU>
#endif

NAMESPACE_SPH_BEGIN

#ifdef SPH_USE_EIGEN

class SparseMatrix::Impl {
private:
    Eigen::SparseMatrix<Float> matrix;
    Array<Eigen::Triplet<Float>> triplets;

    using SparseVector = Eigen::Matrix<Float, Eigen::Dynamic, 1>;

public:
    Impl(const Size rows, const Size cols)
        : matrix(rows, cols) {}

    void insert(const Size i, const Size j, const Float value) {
        SPH_ASSERT(i < matrix.innerSize() && j < matrix.innerSize(), i, j);
        triplets.push(Eigen::Triplet<Float>(i, j, value));
    }

    Expected<Array<Float>> solve(const Array<Float>& values,
        const SparseMatrix::Solver solver,
        const Float tolerance) {
        SPH_ASSERT(values.size() == matrix.innerSize());
        // this is takes straigh from Eigen documentation
        // (http://eigen.tuxfamily.org/dox-devel/group__TopicSparseSystems.html)

        matrix.setFromTriplets(triplets.begin(), triplets.end());
        /// \todo experiment with different solvers

        SparseVector b;
        b.resize(values.size());
        for (Size i = 0; i < values.size(); ++i) {
            b(i) = values[i];
        }

        Expected<SparseVector> a;
        switch (solver) {
        case SparseMatrix::Solver::LU: {
            Eigen::SparseLU<Eigen::SparseMatrix<Float>, Eigen::COLAMDOrdering<int>> solver;
            a = solveImpl(solver, b);
            break;
        }
        case SparseMatrix::Solver::CG: {
#ifdef SPH_DEBUG
            // check that the matrix is symmetric
            {
                const Size n = matrix.innerSize();
                for (Size i = 0; i < n; ++i) {
                    for (Size j = i; j < n; ++j) {
                        const Float mij = matrix.coeff(i, j);
                        const Float mji = matrix.coeff(j, i);
                        SPH_ASSERT(almostEqual(mij, mji), mij, mji);
                    }
                }
            }
#endif
            Eigen::ConjugateGradient<Eigen::SparseMatrix<Float>, Eigen::Lower | Eigen::Upper> solver;
            if (tolerance > 0._f) {
                solver.setTolerance(tolerance);
            }
            a = solveImpl(solver, b);
            break;
        }
        case SparseMatrix::Solver::LSCG: {
            Eigen::LeastSquaresConjugateGradient<Eigen::SparseMatrix<Float>> solver;
            if (tolerance > 0._f) {
                solver.setTolerance(tolerance);
            }
            a = solveImpl(solver, b);
            break;
        }
        case SparseMatrix::Solver::BICGSTAB: {
            Eigen::BiCGSTAB<Eigen::SparseMatrix<Float>> solver;
            if (tolerance > 0._f) {
                solver.setTolerance(tolerance);
            }
            a = solveImpl(solver, b);
            break;
        }
        default:
            NOT_IMPLEMENTED;
        }

        if (!a) {
            return makeUnexpected<Array<Float>>(a.error());
        }
        Array<Float> result;
        result.resize(matrix.outerSize());
        for (Size i = 0; i < result.size(); ++i) {
            result[i] = a.value()(i);
        }
        return Expected<Array<Float>>(std::move(result));
    }

private:
    template <typename TSolver>
    Expected<SparseVector> solveImpl(TSolver& solver, const SparseVector& b) {
        solver.compute(matrix);
        if (solver.info() != Eigen::Success) {
            return makeUnexpected<SparseVector>("Decomposition of matrix failed");
        }
        SparseVector a;
        a = solver.solve(b);
        if (solver.info() != Eigen::Success) {
            return makeUnexpected<SparseVector>("Equations cannot be solved");
        }
        return a;
    }
};

SparseMatrix::SparseMatrix() = default;

SparseMatrix::SparseMatrix(const Size rows, const Size cols)
    : impl(makeAuto<Impl>(rows, cols)) {}

SparseMatrix::~SparseMatrix() = default;

void SparseMatrix::resize(const Size rows, const Size cols) {
    impl = makeAuto<Impl>(rows, cols);
}

void SparseMatrix::insert(const Size i, const Size j, const Float value) {
    impl->insert(i, j, value);
}

Expected<Array<Float>> SparseMatrix::solve(const Array<Float>& values,
    const Solver solver,
    const Float tolerance) {
    return impl->solve(values, solver, tolerance);
}

#endif

NAMESPACE_SPH_END


/**
 *  @file
 *  @copyright defined in eos/LICENSE
 */
#include <eosio/chain/block_timestamp.hpp>

#include <boost/test/unit_test.hpp>

#include <fc/time.hpp>
#include <fc/exception/exception.hpp>

using namespace eosio;
using namespace chain;

BOOST_AUTO_TEST_SUITE(block_timestamp_tests)

BOOST_AUTO_TEST_CASE(constructor_test) {
	block_timestamp_type bt;
        BOOST_TEST( bt.slot == 0u, "Default constructor gives wrong value");

	fc::time_point t(fc::seconds(978307200));
	block_timestamp_type bt2(t);
	BOOST_TEST( bt2.slot == (978307200 - 946684800)/3/**2*/, "Time point constructor gives wrong value");
}

BOOST_AUTO_TEST_CASE(conversion_test) {
	block_timestamp_type bt;
	fc::time_point t = (fc::time_point)bt;
	BOOST_TEST(t.time_since_epoch().to_seconds() == 946684800ll, "Time point conversion failed");

	block_timestamp_type bt1(200);
	t = (fc::time_point)bt1;
	BOOST_TEST(t.time_since_epoch().to_seconds() == 946685400ll/*946684900ll*/, "Time point conversion failed");

}

BOOST_AUTO_TEST_SUITE_END()


/*
* Copyright (c) 2017, Chennakesava Kadapa (c.kadapa@swansea.ac.uk).
* All rights reserved.
* Date: 17-July-2017
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* The author is not responsible for any damage.
*
*
* =========================================================================
*/

#include "headersVTK.h"
#include "headersBasic.h"
#include "utilfuns.h"

#include <boost/foreach.hpp>

#include "findCutcellsVTK.h"
//#include "findCutcellsCGAL.h"
#include "findCutcellsCGAL2.h"

int main(int argc, char* argv[])
{
    //char infile_nodes[]="sphere-nodes.dat";
    //char infile_trias[]="sphere-trias.dat";

    //char infile_nodes[]="reliefvalve3Dblock-nodes.dat";
    //char infile_trias[]="reliefvalve3Dblock-trias.dat";

    char fileNamePoints[]="turekbeam3d-nodes.dat";
    char fileNameFaces[]="turekbeam3d-quads.dat";

    // char infile_nodes[]="thickplate-nodes.dat";
    // char infile_trias[]="thickplate-elems.dat";

    // char fileNamePoints[]="cylinder3d-nodes.dat";
    // char fileNameFaces[]="cylinder3d-quads.dat";

    //////////////////////////////////////////
    //////////////////////////////////////////
    //////////////////////////////////////////

    myGridData  gridData;

    gridData.elType=4;

    gridData.x0 = -1.6;    gridData.x1 =  1.6;    gridData.nEx = 5;
    gridData.y0 = -1.6;    gridData.y1 =  1.6;    gridData.nEy = 5;
    gridData.z0 = -1.6;    gridData.z1 =  1.6;    gridData.nEz = 5;

    if(argc == 0)
      cerr << " Input data " << endl;
    else
    {
       gridData.x0  = atof(argv[1]);
       gridData.x1  = atof(argv[2]);
       gridData.nEx = atoi(argv[3]);

       gridData.y0  = atof(argv[4]);
       gridData.y1  = atof(argv[5]);
       gridData.nEy = atoi(argv[6]);

       gridData.z0  = atof(argv[7]);
       gridData.z1  = atof(argv[8]);
       gridData.nEz = atoi(argv[9]);
    }

    gridData.nElem = gridData.nEx*gridData.nEy*gridData.nEz;

    gridData.nNx = gridData.nEx+1;
    gridData.nNy = gridData.nEy+1;
    gridData.nNz = gridData.nEz+1;

    gridData.nNode = gridData.nNx*gridData.nNy*gridData.nNz;

    gridData.printInfo();

    /////////////////////////////////////////
    //
    // VTK algorithm

    findCutcellsVTK(gridData, fileNamePoints, fileNameFaces);

    /////////////////////////////////////////
    //
    // CGAL algorithm

    //findCutcellsCGAL(gridData, fileNamePoints, fileNameFaces);

    /////////////////////////////////////////
    //
    // CGAL algorithm

    findCutcellsCGAL2(gridData, fileNamePoints, fileNameFaces);

    return 0;
}


 // Copyright 2010 Paul A. Bristow
//
// 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)

/* Temporary test program to discover which platforms support

numeric_limits

digits10 and

new max_digits10.

This is needed to produce or select a macro to avoid compilation failure in Boost.Test
for platforms that do not include either or both of these.

BOOST_NO_NUMERIC_LIMITS_LOWEST is suitable but deprecated.

[Boost C++ Libraries]

#5758: Boost.Test Floating-point comparison diagnostic output does not support radix 10.

*/

#include <boost/config.hpp>
#include <boost/version.hpp>

#include <iostream>
#include <limits>

int main()
{
   std::cout  << "Platform: " << BOOST_PLATFORM            << '\n'
        << "Compiler: " << BOOST_COMPILER            << '\n'
        << "STL     : " << BOOST_STDLIB              << '\n'
        << "Boost   : " << BOOST_VERSION/100000      << "."
                        << BOOST_VERSION/100 % 1000  << "."
                        << BOOST_VERSION % 100       << std::endl;

   int digits10 = std::numeric_limits<double>::digits10;
   int max_digits10 = std::numeric_limits<double>::max_digits10;

   std::cout << "std::numeric_limits<double>::digits10 = " << digits10 << std::endl;

   std::cout << "std::numeric_limits<double>::max_digits10 = " << max_digits10 << std::endl;

} // int main()

/*

Output:

  Description: Autorun "J:\Cpp\MathToolkit\test\Math_test\Debug\ztest_max_digits10.exe"
  Platform: Win32
  Compiler: Microsoft Visual C++ version 10.0
  STL     : Dinkumware standard library version 520
  Boost   : 1.50.0
  std::numeric_limits<double>::digits10 = 15
  std::numeric_limits<double>::max_digits10 = 17

*/


// think-cell public library
//
// Copyright (C) 2016-2018 think-cell Software GmbH
//
// 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 "tc/range.h"
#include "tc/format.h"
#include "tc/make_c_str.h"

#include <boost/range/adaptors.hpp>

#include <vector>
#include <cstdio>

namespace {

template <typename... Args>
void print(Args&&... args) noexcept {
	std::printf("%s", boost::implicit_cast<char const*>(tc::make_c_str<char>(std::forward<Args>(args)...)));
}

//---- Basic ------------------------------------------------------------------------------------------------------------------
void basic () {
	std::vector<int> v = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};

	tc::for_each(
		tc::filter(v, [](const int& n){ return (n%2==0);}),
		[&](auto const& n) {
			print(tc::as_dec(n), ", ");
		}
	);
	print("\n");
}

//---- Generator Range --------------------------------------------------------------------------------------------------------
namespace {
	struct generator_range {
		template< typename Func >
		void operator()( Func func ) const& {
			for(int i=0;i<50;++i) {
				func(i);
			}
		}
	};
}

void ex_generator_range () {
	tc::for_each( tc::filter( generator_range(), [](int i){ return i%2==0; } ), [](int i) {
		print(tc::as_dec(i), ", ");
	});
	print("\n");
}

//---- Generator Range (with break) -------------------------------------------------------------------------------------------
namespace {
	struct generator_range_break {
		template< typename Func >
		tc::break_or_continue operator()( Func func ) const& {
			using namespace tc;
			for(int i=0;i<5000;++i) {
				if (func(i)==break_) { return break_; }
			}
			return continue_;
		}
	};
}

void ex_generator_range_break () {
	tc::for_each( tc::filter( generator_range_break(), [](int i){ return i%2==0; } ), [](int i) -> tc::break_or_continue {
		print(tc::as_dec(i), ", ");
		return (i>=50)? tc::break_ : tc::continue_;
	});
	print("\n");
}

//---- Stacked filters --------------------------------------------------------------------------------------------------------
void stacked_filters() {
	tc::for_each( tc::filter( tc::filter( tc::filter(
								generator_range_break(),
								[](int i){ return i%2!=0; } ),
								[](int i){ return i%3!=0; } ),
								[](int i){ return i%5!=0; } )
			, [](int i) -> tc::break_or_continue
	{
		print(tc::as_dec(i), ", ");
		return (i>25)? tc::break_ : tc::continue_;
	});
	print("\n");
}

}

int main() {
	print("-- Running Examples ----------\n");

	basic();
	ex_generator_range();
	ex_generator_range_break();
	stacked_filters();

	using namespace tc;

	int av[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
	auto v = std::vector<int> (av, av+sizeof(av)/sizeof(int));

	//---- filter example with iterators  -------------------------------------------

	auto r =  tc::filter( tc::filter( tc::filter(
								v,
								[](int i){ return i%2!=0; } ),
								[](int i){ return i%3!=0; } ),
								[](int i){ return i%5!=0; } );

	for (auto it = std::begin(r),
				end = std::end(r);
		it != end;
		++it)
	{
		print(tc::as_dec(*it), ", ");
	}
	print("\n");

	//---- boost for comparison -----------------------------------------------------

	auto br = v | boost::adaptors::filtered([](int i){ return i%2!=0; })
				| boost::adaptors::filtered([](int i){ return i%3!=0; })
				| boost::adaptors::filtered([](int i){ return i%5!=0; });

	for (auto it = std::begin(br),
		end = std::end(br);
		it != end;
		++it)
	{
		print(tc::as_dec(*it), ", ");
	}
	print("\n");

	print("-- Done ----------\n");
	std::fflush(stdout);

	return EXIT_SUCCESS;
}


#define BOOST_TEST_MODULE vector

#include <boost/test/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <boost/mpl/list.hpp>

#include <mla/vector/all.h++>
#include <mla/vector/convert.h++>

typedef boost::mpl::list<
	mla::vector::Dense<float>,
	mla::vector::Dense<double>,
	mla::vector::SparseCS<float>,
	mla::vector::SparseCS<double>
> vector_type_list;

using VectorTypeTo = mla::vector::SparseCS<float>;

BOOST_AUTO_TEST_SUITE(vector)

BOOST_AUTO_TEST_CASE_TEMPLATE( convert, VectorTypeFrom, vector_type_list )
{
	size_t vector_size = 6;

	VectorTypeFrom from(vector_size);

	from.setValue(1, 1.0f);
	from.setValue(3, 3.0f);
	from.setValue(5, 5.0f);

	//VectorTypeTo to(vector_size);	// g++ throws segfault
	mla::vector::SparseCS<float> to(vector_size);

	mla::vector::convert(from, to, 0.001f);

	BOOST_CHECK_EQUAL(to.data.values.size(), 3);
	BOOST_CHECK_EQUAL(to.data.values[0], 1.0f);
	BOOST_CHECK_EQUAL(to.data.values[1], 3.0f);
	BOOST_CHECK_EQUAL(to.data.values[2], 5.0f);
	BOOST_CHECK_EQUAL(to.data.column_index[0], 1);
	BOOST_CHECK_EQUAL(to.data.column_index[1], 3);
	BOOST_CHECK_EQUAL(to.data.column_index[2], 5);
}

BOOST_AUTO_TEST_CASE_TEMPLATE( convert_ignore_nulls, VectorTypeFrom, vector_type_list )
{
	/**
	 * Test intended to check if convert actually ignores values lower than the
	 * established non-null limit.
	 */

	size_t vector_size = 6;
	float non_null_limit = 0.001f;	// the non-null limit for this case

	VectorTypeFrom from(vector_size);

	from.setValue(1, 1.0f);
	from.setValue(2, non_null_limit/2);
	from.setValue(3, 3.0f);
	from.setValue(5, 5.0f);

	//VectorTypeTo to(vector_size);	// g++ throws segfault
	mla::vector::SparseCS<float> to(vector_size);

	mla::vector::convert(from, to, non_null_limit);

	BOOST_CHECK_EQUAL(to.data.values.size(), 3);
	BOOST_CHECK_EQUAL(to.data.values[0], 1.0f);
	BOOST_CHECK_EQUAL(to.data.values[1], 3.0f);
	BOOST_CHECK_EQUAL(to.data.values[2], 5.0f);
	BOOST_CHECK_EQUAL(to.data.column_index[0], 1);
	BOOST_CHECK_EQUAL(to.data.column_index[1], 3);
	BOOST_CHECK_EQUAL(to.data.column_index[2], 5);
}

BOOST_AUTO_TEST_SUITE_END()


#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
#include <iomanip>
#include <optional>
#include <cctype>
#include <map>
#include <vector>
#include <range/v3/view.hpp>
#include <range/v3/algorithm.hpp>
#include <boost/dynamic_bitset.hpp>
#include <complex>
using namespace std::literals::complex_literals;
#include "../aoc2020/matrix.h"
namespace views = ranges::views;
using Position = std::complex<int>;
class Game
{
  int totalsteps_ = 0;
  static inline constexpr int NX = 202;
  using Network = FixedMatrix<char, NX, NX>;
  Network networks_;
  Position dir_;
  Position cur_;
  std::string path_;
  Position left() const
  {
    return cur_ + dir_ * Position(0, 1);
  }
  Position right() const
  {
    return cur_ + dir_ * Position(0, -1);
  }

  void turnleft()
  {
    dir_ *= 1i;
  }
  void turnright()
  {
    dir_ *= -1i;
  }
  bool isEnd() const
  {
    return curch() == '+';
  }
  char curch(Position pos) const
  {
    return networks_(pos.imag(), pos.real());
  }
  char curch() const
  {
    return networks_(cur_.imag(), cur_.real());
  }
  bool valid(Position pos) const
  {
    return (curch(pos) != ' ');
  }

public:
  auto path() const
  {
    return path_;
  }
  Game() : dir_(0, 1), cur_(27, 0) {}
  friend std::istream &operator>>(std::istream &is, Game &g)
  {
    int i = 0;
    for (std::string s; std::getline(is, s); ++i) {
      ranges::copy(s, g.networks_.row(i).begin());
    }
    return is;
  }
  void onestep()
  {
    if (std::isalpha(curch())) {
      path_.push_back(curch());
    }
    if (isEnd()) {
      if (valid(left())) {
        turnleft();
      } else if (valid(right())) {
        turnright();
      }
    }
    ++totalsteps_;
    cur_ += dir_;
  }
  void walk()
  {
    while (networks_(cur_.imag(), cur_.real()) != ' ') {
      onestep();
    }
  }
  int steps() const
  {
    return totalsteps_;
  }
};

int main(int argc, char **argv)
{
  if (argc > 1) {
    Game g;
    std::ifstream iss(argv[1]);
    iss >> g;
    g.walk();
    std::cout << g.path() << std::endl;
    std::cout << g.steps() << std::endl;
  }
}


#include "temperature.h"
#include "../basic.h"

#include <random>

#include <boost/log/trivial.hpp>
#include <boost/random.hpp>

#include <OpenSimplexNoise.h>

namespace WorldEngine
{

static void PermeabilityCalculation(World& world, uint32_t seed);

void PermeabilitySimulation(World& world, uint32_t seed)
{
   BOOST_LOG_TRIVIAL(info) << "Permeability simulation start";

   const PermeabilityArrayType& perm  = world.GetPermeabilityData();
   const OceanArrayType&        ocean = world.GetOceanData();

   PermeabilityCalculation(world, seed);

   world.SetThreshold(PermeabilityLevel::Low,
                      FindThresholdF(perm, 0.75f, &ocean));
   world.SetThreshold(PermeabilityLevel::Medium,
                      FindThresholdF(perm, 0.25f, &ocean));
   world.SetThreshold(PermeabilityLevel::High,
                      std::numeric_limits<float>::max());

   BOOST_LOG_TRIVIAL(info) << "Permeability simulation finish";
}

static void PermeabilityCalculation(World& world, uint32_t seed)
{
   BOOST_LOG_TRIVIAL(debug) << "Seed: " << seed;

   std::mt19937                                      generator(seed);
   boost::random::uniform_int_distribution<uint32_t> distribution;

   OpenSimplexNoise::Noise noise(distribution(generator));

   uint32_t width  = world.width();
   uint32_t height = world.height();

   PermeabilityArrayType& perm = world.GetPermeabilityData();
   perm.resize(boost::extents[height][width]);

   const uint32_t octaves = 6u;
   const float    freq    = 64.0f * octaves;
   const float    nScale  = 1.0f;

   for (uint32_t y = 0; y < height; y++)
   {
      for (uint32_t x = 0; x < width; x++)
      {
         double n   = Noise(noise, //
                          x * nScale / freq,
                          y * nScale / freq,
                          octaves);
         perm[y][x] = static_cast<float>(n);
      }
   }
}

} // namespace WorldEngine


//=======================================================================
// Copyright 2001 Indiana University.
// Author: Jeremy G. Siek
//
// 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/config.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <iostream>

int
main()
{
  using namespace boost;
  typedef adjacency_list<vecS, vecS, bidirectionalS, no_property,
    property<int, edge_weight_t>, no_property, vecS> Graph;

  const std::size_t n = 3;
  typedef std::pair<std::size_t, std::size_t> E;
  E edge_array[] = { E(0,1), E(0,2), E(0,1) };
  const std::size_t m = sizeof(edge_array) / sizeof(E);
  Graph g(edge_array, edge_array + m, n);

  graph_traits<Graph>::edge_iterator edge_iterator;
  for (std::size_t i = 0; i < m; ++i) {
    const graph_traits<Graph>::edge_iterator e = edges(g).first + i;
    std::cout << *e << " ";
  }
  std::cout << std::endl;

  return 0;
}


//==================================================================================================
/**
  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/function/interleave_even.hpp>
#include <boost/simd/constant/valmin.hpp>
#include <boost/simd/constant/valmax.hpp>
#include <boost/simd/pack.hpp>
#include <simd_test.hpp>

namespace bs = boost::simd;

STF_CASE_TPL( "interleave_even based shuffle", STF_NUMERIC_TYPES)
{
  // cardinal 4
  {
    bs::pack<T,4> a{ bs::Valmax<T>(), T(42), T(69), bs::Valmin<T>() };
    bs::pack<T,4> b{ T(13), bs::Valmin<T>(), T(37), bs::Valmax<T>() };
    bs::pack<T,4> z(0);

    STF_ALL_EQUAL( (bs::shuffle< 0, 4, 2, 6>(a,b)), (bs::interleave_even(a,b)) );
    STF_ALL_EQUAL( (bs::shuffle<-1, 4,-1, 6>(a,b)), (bs::interleave_even(z,b)) );
    STF_ALL_EQUAL( (bs::shuffle< 0,-1, 2,-1>(a,b)), (bs::interleave_even(a,z)) );

    STF_ALL_EQUAL( (bs::shuffle< 4, 0, 6, 2>(a,b)), (bs::interleave_even(b,a)) );
    STF_ALL_EQUAL( (bs::shuffle<-1, 0,-1, 2>(a,b)), (bs::interleave_even(z,a)) );
    STF_ALL_EQUAL( (bs::shuffle< 4,-1, 6,-1>(a,b)), (bs::interleave_even(b,z)) );
  }

  // cardinal 8
  {
    bs::pack<T,8> a{ bs::Valmax<T>(), T(42), T(69), bs::Valmin<T>(), T(37), bs::Valmax<T>(), T(55), T(99) };
    bs::pack<T,8> b{ T(13), bs::Valmax<T>(), T(42), bs::Valmax<T>(), T(76), bs::Valmin<T>(), T(42), T(69) };
    bs::pack<T,8> z(0);

    STF_ALL_EQUAL( (bs::shuffle< 0, 8, 2,10, 4,12, 6,14>(a,b)), (bs::interleave_even(a,b)) );
    STF_ALL_EQUAL( (bs::shuffle<-1, 8,-1,10,-1,12,-1,14>(a,b)), (bs::interleave_even(z,b)) );
    STF_ALL_EQUAL( (bs::shuffle< 0,-1, 2,-1, 4,-1, 6,-1>(a,b)), (bs::interleave_even(a,z)) );

    STF_ALL_EQUAL( (bs::shuffle< 8, 0,10, 2,12, 4,14, 6>(a,b)), (bs::interleave_even(b,a)) );
    STF_ALL_EQUAL( (bs::shuffle<-1, 0,-1, 2,-1, 4,-1, 6>(a,b)), (bs::interleave_even(z,a)) );
    STF_ALL_EQUAL( (bs::shuffle< 8,-1,10,-1,12,-1,14,-1>(a,b)), (bs::interleave_even(b,z)) );
  }
}


/*
Copyright (c) 2015, Wator Vapor
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 wator nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "wator.hpp"
using namespace Wator;

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using namespace boost::property_tree;

static float const fConstCoulombDiff = 0.0001;

/**
 * Constructor
 **/
CoulombLayer::CoulombLayer()
{
    for(int y = 0 ;y < h_;y++){
        for(int x =0;x < w_ ;x++){
            // 电荷中心，在几何中心。高斯分布,中心亮，外部暗。
            float w = (x- w_/2) * (x- w_/2) + (y- h_/2) * (y- h_/2) +1;
//            float w = std::abs(x- w_/2) + std::abs(y- h_/2) +1;
            w = 1/w;
            TRACE_VAR(x);
            TRACE_VAR(y);
            weight_.push_back(w);
            TRACE_VAR(w);
        }
    }

    /// weight bias cal. 调整截距，使相同亮度的输出为0
    float sum = 1.0;
    bool minus = true;
    bool shock = false;
    float fConstCoulombStepOne = fConstCoulombDiff * weight_.size();
    float fConstCoulombStepTwo = fConstCoulombDiff / weight_.size();

    do {
        auto rate = fConstCoulombStepOne;
        if(shock) {
            rate = fConstCoulombStepTwo;
        }
        sum = 0.0;
        for (int i = 0;i < weight_.size();i++) {
            if (minus) {
                weight_[i] -= rate;
            } else {
                weight_[i] += rate;
            }

            sum += weight_[i];
        }
        TRACE_VAR(sum);
        if( sum - fConstCoulombDiff < 0) {
            minus = false;
        } else {
            if (false == minus) {
                shock = true;
            }
            minus = true;
        }
        TRACE_VAR(minus);
    } while (std::abs(sum) > fConstCoulombDiff);

    for (int i = 0;i < weight_.size();i++) {
        INFO_VAR(weight_[i]);
    }
    INFO_VAR(sum);
    INFO_VAR(weight_.size());

/*
 not use it.
    for(int y = 0 ;y < h_;y++){
        for(int x =0;x < w_ ;x++){
            // 电荷中心，在几何中心。高斯分布,中心暗，外部亮。
            float w = (x- w_/2) * (x- w_/2) + (y- h_/2) * (y- h_/2) +1;
            w = 1- 1/w;
            TRACE_VAR(x);
            TRACE_VAR(y);
            weight_R_.push_back(w);
            TRACE_VAR(w);
        }
    }

    /// weight bias cal. 调整截距，使相同亮度的输出为0
    fConstCoulombStepOne = fConstCoulombDiff * weight_R_.size();
    fConstCoulombStepTwo = fConstCoulombDiff / weight_R_.size();
    sum = 1.0;

    do {
        auto rate = fConstCoulombStepOne;
        if(shock) {
            rate = fConstCoulombStepTwo;
        }
        sum = 0.0;
        for (int i = 0;i < weight_R_.size();i++) {
            if (minus) {
                weight_R_[i] -= rate;
            } else {
                weight_R_[i] += rate;
            }

            sum += weight_R_[i];
        }
        TRACE_VAR(sum);
        if( sum - fConstCoulombDiff < 0) {
            minus = false;
        } else {
            if (false == minus) {
                shock = true;
            }
            minus = true;
        }
        TRACE_VAR(minus);
    } while (std::abs(sum) > fConstCoulombDiff);

    for (int i = 0;i < weight_R_.size();i++) {
        INFO_VAR(weight_R_[i]);
    }
    INFO_VAR(sum);
    INFO_VAR(weight_R_.size());
 */
}

/**
 * update
 * @return None.
 **/
void CoulombLayer::updateW(void){
}

/**
 * update
 * @return None.
 **/
void CoulombLayer::update(void)
{
    INFO_VAR("finnish CoulombLayer::update");
}

/**
 * round
 * @return None.
 **/
void CoulombLayer::round(void)
{
    this->forward();
    this->update();
    INFO_VAR("finnish CoulombLayer::round");
    LayerBase::round();
    this->dump();
}

/*
 mat
 x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12
 y1,y2,y3,y4,y5,y6,y7,y8,y9,y10,y11,y12
 z1,z2,z3,z4,z5,z6,z7,z8,z9,z10,z11,z12
 u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12
 v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12
 w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11,w12
 r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12
 s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12
 t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12
 o1,o2,o3,o4,o5,o6,o7,o8,o9,o10,o11,o12
 p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12
 q1,q2,q3,q4,q5,q6,q7,q8,q9,q10,q11,q12

 ------------------
 image blob
 x1,x2,x3,y1,y2,y3,z1,z2,z3,
 x4,x5,x6,y4,y5,y6,z4,z5,z6,
 x7,x8,x9,y7,y8,y9,z7,z8,z9,
 x10,x11,x12,y10,y11,y12,z10,z11,z12,
 u1,u2,u3,v1,v2,v3,w1,w2,w3,
 u4,u5,u6,v4,v5,v6,w4,w5,w6,
 u7,u8,u9,v7,v8,v9,w7,w8,w9,
 u10,u11,u12,v10,v11,v12,w10,w11,w12,
 r1,r2,r3,s1,s2,s3,t1,t2,t3,
 r4,r5,r6,s4,s5,s6,t4,t5,t6,
 r10,r11,r12,s10,s11,s12,t10,t11,t12,

 o1,o2,o3,p1,p2,p3,q1,q2,q3
 o4,o5,o6,p4,p5,p6,q4,q5,q6
 o7,o8,o9,p7,p8,p9,q7,q8,q9
 o10,o11,o12,p10,p11,p12,q10,q11,q12

 ------------------
 coulomb blob
 xyz123,xyz456,xyz789,xyz101112,
 uvw123,uvw456,uvw789,uvw101112,
 rst123,rst456,rst789,rst101112,
 opq123,opq456,opq789,opq101112
 */

/**
 * forward
 * @return None.
 **/
void CoulombLayer::forward(void)
{
    blobsRaw_.clear();
    blobs_.clear();
    for(auto btm:bottom_){
        auto input = dynamic_pointer_cast<LayerInput>(btm);
        auto inBlob = input->getBlob(this);
        INFO_VAR(inBlob->w_);
        INFO_VAR(inBlob->h_);
        INFO_VAR(inBlob->ch_);
        INFO_VAR(weight_.size());
        this->wGrid_ = inBlob->w_/this->w_;
        this->hGrid_ = inBlob->h_/this->h_;
        this->chGrid_ = inBlob->ch_;
        INFO_VAR(this->wGrid_);
        INFO_VAR(this->hGrid_);
        auto raw = shared_ptr<Blob<float>>(new Blob<float>(this->wGrid_,this->hGrid_,this->chGrid_));
        max_ = 0.0;
        min_ = 255.0 *255.0;
        for(int i = 0 ;i < raw->size_ ;i++) {
            float sum =0;
            float minLocal = 255.0 *255.0;
            float maxLocal = 0.0;
            float sumBase = 0.0;
            for(int j = 0;j<weight_.size();j++) {
                int index = i*weight_.size() + j;
                int data = inBlob->data_[index];
                sum += weight_[j] * (float)(data);
                sumBase += data;
                if(data > maxLocal){
                    maxLocal = data;
                }
                if(data < minLocal) {
                    minLocal = data;
                }
            }
            if(sum > max_) {
                max_ = sum;
            }
            if(sum < min_) {
                min_ = sum;
            }
            float rate1 = maxLocal-minLocal;
            float rate2 = 0;
            if(sumBase >0) {
                rate2 = 255.0/sumBase;
            }
            raw->data_[i] = sum * rate1 * rate2;
        }
        blobsRaw_.push_back(raw);

        /// cal every blob for every top
        for (auto top:top_) {
            auto blob = shared_ptr<Blob<bool>>(new Blob<bool>(this->wGrid_,this->hGrid_,this->chGrid_));
            auto v1 = dynamic_pointer_cast<V1CortexLayer>(top);
            auto gridW = this->wGrid_/v1->iW_;
            auto gridH = this->hGrid_/v1->iH_;
            for (int ch = 0; ch <this->chGrid_;ch++) {
                for (int x = 0;x < gridW;x++){
                    for (int y = 0;y < gridH;y++){
                        int activeSize = v1->iSparse_ + 1;
                        TRACE_VAR(activeSize);
                        const int maxActive = v1->iSparse_;
                        int iCounter = 0;
                        while (activeSize > maxActive) {
                            max_ = 0.0;
                            min_ = 255.0 *255.0;
                            activeSize = 0;
                            for(int x2 = 0 ;x2 < v1->iW_ ;x2++) {
                                for(int y2 = 0 ;y2 < v1->iH_ ;y2++) {
                                    /* index */
                                    auto index = ch * this->wGrid_* this->hGrid_ + (y * v1->iH_+y2) * this->wGrid_ + x*v1->iW_ + x2;
                                    if(iCounter == 0) {
                                    //if(0) {
                                        if(raw->data_[index] > max_) {
                                            max_ = raw->data_[index];
                                        }
                                        if(raw->data_[index] < min_) {
                                            min_ = raw->data_[index];
                                        }
                                    }
                                    float delta = std::abs(raw->data_[index] - thresholdCenter_) - threshold_;
                                    TRACE_VAR(delta);
                                    TRACE_VAR(raw->data_[index])
                                    if(0 < delta ){
                                        blob->data_[index] = true;
                                        activeSize++;
                                    }else{
                                        blob->data_[index] = false;
                                    }
                                }
                            }
                            if(iCounter == 0) {
                                // first time count
                                threshold_ = std::abs(0.8*(max_ - min_)/2);
                                thresholdCenter_ = (max_ + min_)/2;
                                activeSize = maxActive + 1;
                                TRACE_VAR(thresholdCenter_);
                                TRACE_VAR(max_);
                                TRACE_VAR(min_);
                            } else {
                                threshold_ += thresholdStep_;
                            }
                            TRACE_VAR(iCounter);
                            TRACE_VAR(activeSize);
                            TRACE_VAR(threshold_);
                            iCounter++;
                        }
                        TRACE_VAR(activeSize);
                        TRACE_VAR(maxActive);
                    }
                }
            }
            blobs_.push_back(blob);
        }
    }
    INFO_VAR(blobs_.size());
    INFO_VAR("finnish CoulombLayer::forward");
}

/**
 * dump to png
 * @return None.
 **/
void CoulombLayer::dump(void){
    INFO_VAR(blobs_.size());
    for (auto blob:blobs_) {
        blob->dump(typeid(this).name());
    }
}

/**
 * get ptr
 * @return None.
 **/
shared_ptr<Blob<bool>> CoulombLayer::getBlob(const LayerBase* who)
{
    int i = 0;
    TRACE_VAR(top_.size());
    for(auto top:top_)
    {
        if(who == top.get())
        {
            TRACE_VAR(blobs_.size());
            if(blobs_.size() > i)
            {
                return blobs_[i];
            }
            else
            {
                FATAL_VAR("fatal errro");
            }
        }
        i++;
    }
    return nullptr;
}


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <limits>
#include <vector>
#include <algorithm>
#include <string>
#include <sstream>
#include <timing.h>

#include <boost/filesystem.hpp>

#include <ScrimpBlocked.hpp>
#include <logging.hpp>
#include <papiwrapper.hpp>

using namespace matrix_profile;

static FactoryRegistration<ScrimpSequBlocked> s_sequRegistr("scrimp_blocked");
static const int notification_interval_iter = 10000;
static const int PRIMARY_BLOCKING_SIZE = 500;
static const int SECONDARY_BLOCKING_SIZE = 50;

template<int NUM_DIAGS, int BLOCKLEN> void ScrimpSequBlocked::eval_diagonal_block(aligned_int_vec& profileIndex, const aligned_tsdtype_vec& A, aligned_tsdtype_vec& initial_zs, const int windowSize, const aligned_tsdtype_vec& ASigmaInv, const aligned_tsdtype_vec& AMeanScaledSigSqrM, const int diag, aligned_tsdtype_vec& profile)
{
	const int profileLength = AMeanScaledSigSqrM.size();
	tsa_dtype corrScore;
#ifdef PROFILING
	long updateCtr = 0;
#endif

	for (int blockstart = 0; blockstart < profileLength; blockstart+= BLOCKLEN)
	{
		int block_lim = std::min(blockstart+BLOCKLEN, profileLength);
		const int diag_lim = std::min(NUM_DIAGS, profileLength-diag-blockstart);

		for (int diag_iter = 0; diag_iter<diag_lim; ++diag_iter) {
			int ilimit =  std::min(profileLength-diag-diag_iter, block_lim);
			tsa_dtype tmpz = initial_zs[diag+diag_iter]; //rather use a local, to avoid innecessary writes to the referenced memory

//std::cout << "diag " << diag+diag_iter << " lastz: " << tmpz << std::endl;
			for (int i=blockstart; i<ilimit; i++)
			{
				int j=i+diag+diag_iter;

//std::cout << "eval i: " << i << " j: " << j << "  ilimit:" << ilimit << " blockstart: " << blockstart << " lastz" << tmpz << std::endl;
				corrScore = (tmpz* (ASigmaInv[j] * ASigmaInv[i]) - AMeanScaledSigSqrM[j] * AMeanScaledSigSqrM[i]) ;
				tmpz += A[j+windowSize]*A[i+windowSize]  - A[j]*A[i];

				if (corrScore > profile[j])
				{
					profile[j] = corrScore;
					profileIndex [j] = i;
                    #ifdef PROFILING
					updateCtr+=1;
                    #endif
				}
				if (corrScore > profile[i])
				{
					profile[i] = corrScore;
					profileIndex [i] = j;
                    #ifdef PROFILING
					updateCtr+=1;
                    #endif
				}
			}
			initial_zs[diag+diag_iter]=tmpz;
		}
	}

#ifdef PROFILING
	_profileUpdateCounter += updateCtr;
#endif
}

void ScrimpSequBlocked::compute_matrix_profile(const Scrimppp_params& params)
{
	Timepoint tstart, tend;
	Timespan time_elapsed;
	aligned_tsdtype_vec A = fetch_time_series<aligned_tsdtype_vec::allocator_type>(params); //load the time series data
	aligned_tsdtype_vec AMeanScaledSqrtM(A.size());
	aligned_tsdtype_vec ASigmaInv(A.size());
	int windowSize = params.query_window_len;
	int exclusionZone = windowSize / 4;
	int timeSeriesLength = A.size();
	int ProfileLength = timeSeriesLength - windowSize + 1;
	BOOST_ASSERT_MSG( ProfileLength>0, "No profile results from sepcified args. Choose a smaller window length / longer time series");
	//Initialize Matrix Profile and Matrix Profile Index
	aligned_tsdtype_vec profile(ProfileLength, -1.0);
	aligned_int_vec profileIndex(ProfileLength, 0);
	aligned_tsdtype_vec initial_zs(timeSeriesLength); // stores products between two Timeseries values with a distinct offset
	std::vector<int> idx; // store indices of the diagonals, defining their evaluation order
	idx.reserve(ProfileLength-exclusionZone-1);

	//several monitors for performance measurement
	PerfCounters setup_perf("setup");
	PerfCounters init_diag_perf("diagonal initialization");
	PerfCounters eval_diag_perf("diagonal evaluation");

	//validation of parameters
	if (timeSeriesLength < windowSize) {
		throw std::invalid_argument("ERROR: Time series is shorter than the window length, can not proceed");
	}

	EXEC_INFO( "Sequential blocked SCRIMP matrix profile computation with profile length " << ProfileLength << " window size " << windowSize);
	EXEC_INFO ( "diagonal BLOCK size" << PRIMARY_BLOCKING_SIZE << " (secondary) blocking toegether " << SECONDARY_BLOCKING_SIZE << " diagonals");

	{
		ScopedPerfAccumulator monitor(setup_perf);
		//precompute the mean and standard deviations of the sliding windows along the time series
		precompute_window_statistics(windowSize, A, ProfileLength, AMeanScaledSqrtM, ASigmaInv);

		//start time measurment
		tstart = get_cur_time();
	}
	// compute the first correlation values in the diagonals (i.e. compute the correlation between the first windows)
	{
		ScopedPerfAccumulator monitor(init_diag_perf);
		init_all_diagonals(initial_zs, A, windowSize);
	}
	{
		ScopedPerfAccumulator monitor(setup_perf);
		/******************** SCRIMP ********************/
		//Random shuffle the computation order of the diagonals of the distance matrix
		int i;
		for (i = exclusionZone+1; i <= ProfileLength-SECONDARY_BLOCKING_SIZE; i+=SECONDARY_BLOCKING_SIZE) {
			idx.push_back(i);
		}
		std::random_shuffle(idx.begin(), idx.end());
		//sequentially evaluate diagonals, which did not fit the vector size
		for (i;i<ProfileLength; ++i)
		{
			eval_diagonal_block<1, PRIMARY_BLOCKING_SIZE>(profileIndex, A, initial_zs, windowSize, ASigmaInv, AMeanScaledSqrtM, i, profile);
		}
	}

	//iteratively evaluate the diagonals of the distance matrix
	for (int ri = 0; ri < idx.size(); ri++)
	    {
		//select a random diagonal
		int diag = idx[ri];

		//evaluate the second to the last distance values along the diagonal in the matrix and update the matrix profile/matrix profile index.
		{
			ScopedPerfAccumulator monitor(eval_diag_perf);
			eval_diagonal_block<SECONDARY_BLOCKING_SIZE, PRIMARY_BLOCKING_SIZE>(profileIndex, A, initial_zs, windowSize, ASigmaInv, AMeanScaledSqrtM, diag, profile);
		}

		//Show time per 10000 iterations
		if ((ri+1) % notification_interval_iter == 0)
		{
			tend = get_cur_time();
			time_elapsed = tend - tstart;
			EXEC_INFO ( "finished " << ri+1 << " blocked iterations after " << std::setprecision(std::numeric_limits<tsa_dtype>::digits10 + 2) << time_elapsed << " seconds.");
		}
	}

	// apply a correction of the distance values, as we dropped a factor of 2 to avoid unnecessary computations
	tsa_dtype twice_m = 2.0*static_cast<tsa_dtype>(windowSize);
	for (auto iter=profile.begin(); iter<profile.end(); ++iter) {
		(*iter) = twice_m - 2.0 * (*iter);
	}

	// end timer
	// tend = time(0);
	tend = get_cur_time();
	time_elapsed = tend - tstart;

	PERF_LOG ( "total computation time: " << std::setprecision(std::numeric_limits<tsa_dtype>::digits10 + 2) << time_elapsed << " seconds." );
	const double triang_len = ProfileLength-exclusionZone;
	PERF_LOG ( "throughput computations: " << triang_len * triang_len / get_seconds(time_elapsed) << " matrix entries/second");

	//store the result
	store_matrix_profile(profile, profileIndex, params);

	setup_perf.log_perf();
	init_diag_perf.log_perf();
	eval_diag_perf.log_perf();
#ifdef PROFILING
	PERF_LOG ( "number of matrix profile updates: " << _profileUpdateCounter);
#endif

}


#pragma once
#ifndef ANNIHILATE_HPP
#define ANNIHILATE_HPP

#include "devavxprng.h"
#include <NTL/GF2X.h>
#include <MTToolBox/period.hpp>
#include <MTToolBox/AlgorithmReducibleRecursionSearch.hpp>
#include "w256.hpp"

namespace MTToolBox {
    /**
     * @tparam G generator
     * @tparam U simd vector like w256_t
     * @tparam V base rng width like uint32_t
     */
    template<typename G, typename U>
    class Annihilate {
    public:
        bool anni(G& sf) {
            using namespace NTL;
            using namespace std;
            //G gen(sf);
            GF2X poly;
            minpoly(poly, sf);
            GF2X irreducible = poly;
            if (!hasFactorOfDegree(irreducible, sf.getMexp())) {
                cout << "error does not have factor of degree(0) "
                     << dec << sf.getMexp()
                     << endl;
                cout << "deg poly = " << dec << deg(poly) << endl;
                cout << "deg irreducible = " << dec << deg(irreducible) << endl;
                cout << sf.getParamString() << endl;
                sf.d_p();
                cout << "poly:" << endl;
                cout << poly << endl;
                cout << "irreducible:" << endl;
                cout << irreducible << endl;
                return false;
            }
#if 1
            calcCharacteristicPolynomial(&sf, poly);
            if (deg(poly) != sf.bitSize()) {
                getLCMPoly(poly, sf);
            }
#endif
            int degp = deg(poly);
            //printBinary(stdout, poly);
            GF2X quotient = poly / irreducible;
#if defined(DEBUG)
            cout << "deg irreducible = " << dec << deg(irreducible) << endl;
            cout << "deg characteristic = " << dec << deg(poly)
                 << endl;
            cout << "deg quotient = " << dec << deg(quotient) << endl;
#endif
            GF2X tmp = quotient * irreducible;
            if (tmp != poly) {
                cout << "quotient * irreducible != poly" << endl;
                return false;
            }
            annihilate<U>(&sf, quotient);
            minpoly(poly, sf);
            if (poly != irreducible) {
                cout << "annihilate failed" << endl;
                cout << "deg poly = " << dec << degp << endl;
                cout << "deg irreducible = " << dec << deg(irreducible) << endl;
                cout << "deg quotient = " << dec << deg(quotient) << endl;
                cout << "after annihilate deg minpoly = " << dec << deg(poly)
                     << endl;
                return false;
            }
#if defined(DEBUG)
            cout << "after annihilate deg poly = " << dec << deg(poly) << endl;
#endif
            if (!hasFactorOfDegree(poly, sf.getMexp())) {
                cout << "error does not have factor of degree(1) "
                     << dec << sf.getMexp() << endl;
                cout << "deg poly = " << dec << deg(poly) << endl;
                return false;
            }
            return true;
        }
        void getLCMPoly(NTL::GF2X& lcm, const G& sf) {
            using namespace NTL;
            using namespace std;
            G gen(sf);
            lungLCM(lcm, gen);
            int bitSize = gen.bitSize();
            GF2X poly;
            for (int i = 0; i < bitSize; i++) {
                gen.setOneBit(i);
#if 0
                for (int j = 0; j < 256; j++) {
                    minPolyLung(poly, gen, j);
                    //minpoly(poly, gen, j);
                    LCM(lcm, lcm, poly);
                    if (deg(lcm) == bitSize) {
                        return;
                    }
                }
#else
                minPolyLung(poly, gen, 0);
                //minpoly(poly, gen);
                LCM(lcm, lcm, poly);
                if (deg(lcm) == bitSize) {
                    return;
                }
#endif
            }
        }
    private:
        void lungLCM(NTL::GF2X& lcm, G& sf) {
            using namespace NTL;
            using namespace std;
            GF2X poly;
            for (int i = 0; i < 256; i++) {
                minPolyLung(poly, sf, i);
                LCM(lcm, lcm, poly);
            }
        }

        void minPolyLung(NTL::GF2X& poly, G& sf, int pos) {
            using namespace NTL;
            using namespace std;
            Vec<GF2> v;
            int size = sf.bitSize();
            v.SetLength(2 * size);
            for (int i = 0; i < 2 * size; i++) {
                sf.generate();
                U w = sf.getParityValue();
                v[i] = getBitOfPos(w, pos);
            }
            MinPolySeq(poly, v, size);
        }
    };
}
#endif // ANNIHILATE_HPP


// Copyright Abel Sinkovics (abel@sinkovics.hu) 2011.
// 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 <mpllibs/metamonad/maybe.hpp>
#include <mpllibs/metamonad/list.hpp>
#include <mpllibs/metamonad/return_.hpp>
#include <mpllibs/metamonad/bind.hpp>
#include <mpllibs/metamonad/fail.hpp>
#include <mpllibs/metamonad/lambda_c.hpp>

#include <mpllibs/metamonad/mzero.hpp>
#include <mpllibs/metamonad/mplus.hpp>

#include <mpllibs/metamonad/mempty.hpp>
#include <mpllibs/metamonad/mappend.hpp>

#include <boost/test/unit_test.hpp>

#include "common.hpp"

#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/list_c.hpp>
#include <boost/mpl/assert.hpp>

namespace
{
  using mpllibs::metamonad::maybe_tag;
  using mpllibs::metamonad::nothing;
  using mpllibs::metamonad::just;

  using boost::mpl::equal_to;
  using boost::mpl::list_c;

  template <class T>
  struct get_maybe_data;

  template <class T>
  struct get_maybe_data<mpllibs::metamonad::just<T> > :
    mpllibs::metamonad::returns<T>
  {};

  typedef int11::tag int_tag;
  typedef maybe_tag<int_tag> mt;

  typedef just<int11> just11;
  typedef just<int13> just13;
}

BOOST_AUTO_TEST_CASE(test_maybe)
{
  using mpllibs::metamonad::return_;
  using mpllibs::metamonad::bind;
  using mpllibs::metamonad::fail;

  using mpllibs::metamonad::lambda_c;

  using boost::mpl::plus;

  typedef list_c<char, 'h', 'e', 'l', 'l', 'o'> s_hello;

  typedef lambda_c<n, just<plus<n, int2> > > maybe_add_2;

  // test_return
  BOOST_MPL_ASSERT((equal_to<return_<mt, int13>, just13>));

  // test_bind_with_just
  BOOST_MPL_ASSERT((equal_to<bind<mt, just11, maybe_add_2>::type, just13>));

  // test_bind_with_nothing
  BOOST_MPL_ASSERT((equal_to<bind<mt, nothing, maybe_add_2>::type, nothing>));

  // test_fail
  BOOST_MPL_ASSERT((
    equal_to<bind<mt, fail<mt, s_hello>, maybe_add_2>::type, nothing>
  ));
}

BOOST_AUTO_TEST_CASE(test_maybe_monad_plus)
{
  using mpllibs::metamonad::mzero;
  using mpllibs::metamonad::mplus;

  typedef mzero<mt>::type zero;

  // test mzero
  BOOST_MPL_ASSERT((equal_to<zero, nothing>));

  // test mzero + mzero
  BOOST_MPL_ASSERT((equal_to<zero, mplus<mt, zero, zero>::type>));

  // test mzero + x
  BOOST_MPL_ASSERT((equal_to<just13, mplus<mt, zero, just13>::type>));

  // test x + mzero
  BOOST_MPL_ASSERT((equal_to<just13, mplus<mt, just13, zero>::type>));

  // test x + y
  BOOST_MPL_ASSERT((equal_to<just13, mplus<mt, just13, just11>::type>));
}

BOOST_AUTO_TEST_CASE(test_maybe_monoid)
{
  using mpllibs::metamonad::list_tag;
  using mpllibs::metamonad::mempty;
  using mpllibs::metamonad::mappend;

  using boost::mpl::equal;

  typedef maybe_tag<list_tag> mlt;

  typedef mempty<mlt>::type empty_val;

  // test mempty
  BOOST_MPL_ASSERT((equal_to<nothing, empty_val>));

  // test mempty + mempty
  BOOST_MPL_ASSERT((
    equal_to<empty_val, mappend<mlt, empty_val, empty_val>::type>
  ));

  // test x + mempty
  BOOST_MPL_ASSERT((
    equal<
      list_c<int, 13>,
      get_maybe_data<mappend<mlt, just<list_c<int,13> >, empty_val>::type>::type
    >
  ));

  // test mempty + x
  BOOST_MPL_ASSERT((
    equal<
      list_c<int, 13>,
      get_maybe_data<
        mappend<mlt, empty_val, just<list_c<int, 13> > >::type
      >::type
    >
  ));

  // test x + y
  BOOST_MPL_ASSERT((
    equal<
      list_c<int, 11, 13>,
      get_maybe_data<
        mappend<mlt, just<list_c<int, 11> >, just<list_c<int, 13> > >::type
      >::type
    >
  ));
}


//
// Copyright (c) 2015-2021 CNRS INRIA
//

#include "pinocchio/spatial/fwd.hpp"
#include "pinocchio/spatial/se3.hpp"
#include "pinocchio/multibody/visitor.hpp"
#include "pinocchio/multibody/model.hpp"
#include "pinocchio/multibody/data.hpp"
#include "pinocchio/algorithm/crba.hpp"
#include "pinocchio/algorithm/centroidal.hpp"
#include "pinocchio/algorithm/rnea.hpp"
#include "pinocchio/algorithm/jacobian.hpp"
#include "pinocchio/algorithm/compute-all-terms.hpp"
#include "pinocchio/parsers/sample-models.hpp"
#include "pinocchio/utils/timer.hpp"

#include <boost/test/unit_test.hpp>

using namespace pinocchio;

BOOST_AUTO_TEST_SUITE( BOOST_TEST_MODULE )

void run_test(const Model & model, const Eigen::VectorXd & q, const Eigen::VectorXd & v)
{
  pinocchio::Data data(model);
  pinocchio::Data data_other(model);

  computeAllTerms(model,data,q,v);

  nonLinearEffects(model,data_other,q,v);
  crba(model,data_other,q);
  getJacobianComFromCrba(model, data_other);
  computeJointJacobiansTimeVariation(model,data_other,q,v);
  centerOfMass(model, data_other, q, v, true);
  computeKineticEnergy(model, data_other, q, v);
  computePotentialEnergy(model, data_other, q);
  dccrba(model, data_other, q, v);
  computeGeneralizedGravity(model, data_other, q);

  BOOST_CHECK(data.nle.isApprox(data_other.nle));
  BOOST_CHECK(Eigen::MatrixXd(data.M.triangularView<Eigen::Upper>())
              .isApprox(Eigen::MatrixXd(data_other.M.triangularView<Eigen::Upper>())));
  BOOST_CHECK(data.J.isApprox(data_other.J));
  BOOST_CHECK(data.dJ.isApprox(data_other.dJ));
  BOOST_CHECK(data.Jcom.isApprox(data_other.Jcom));
  BOOST_CHECK(data.Ag.isApprox(data_other.Ag));
  BOOST_CHECK(data.dAg.isApprox(data_other.dAg));
  BOOST_CHECK(data.hg.isApprox(data_other.hg));
  BOOST_CHECK(data.Ig.isApprox(data_other.Ig));
  BOOST_CHECK(data.g.isApprox(data_other.g));

  for(int k=0; k<model.njoints; ++k)
  {
    BOOST_CHECK(data.com[(size_t)k].isApprox(data_other.com[(size_t)k]));
    BOOST_CHECK(data.vcom[(size_t)k].isApprox(data_other.vcom[(size_t)k]));
    BOOST_CHECK_CLOSE(data.mass[(size_t)k], data_other.mass[(size_t)k], 1e-12);
  }

  BOOST_CHECK_CLOSE(data.kinetic_energy, data_other.kinetic_energy, 1e-12);
  BOOST_CHECK_CLOSE(data.potential_energy, data_other.potential_energy, 1e-12);
}

BOOST_AUTO_TEST_CASE( test_against_algo )
{
  using namespace Eigen;

  pinocchio::Model model; buildModels::humanoidRandom(model);

  VectorXd q(VectorXd::Random(model.nq));
  VectorXd v(VectorXd::Random(model.nv));

  // -------
  q.setZero();
  v.setZero();

  run_test(model,q,v);

  // -------
  q.setZero();
  v.setOnes();

  run_test(model,q,v);

  // -------
  q.setOnes();
  q.segment<4>(3).normalize();
  v.setOnes();

  run_test(model,q,v);

  // -------
  q.setRandom();
  q.segment<4>(3).normalize();
  v.setRandom();

  run_test(model,q,v);
}

BOOST_AUTO_TEST_SUITE_END()


#ifndef MANIFOLDS_FUNCTIONS_MULTI_MATRIX_REDUCTION_HH
#define MANIFOLDS_FUNCTIONS_MULTI_MATRIX_REDUCTION_HH

#include "data/multi_matrix.hh"
#include "function.hh"
#include "full_function_defs.hh"
#include <boost/mpl/find.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/mpl/end.hpp>
#include <utility>

namespace manifolds {
template <int n1, int n2> struct ReduxPair {
  static const int first = n1;
  static const int second = n2;
};

template <class... ReductionPairs>
struct Reduction : Function<int_<26>, 1, 1>,
                   FunctionCommon<Reduction<ReductionPairs...> > {
  using FunctionCommon<Reduction>::operator();
  template <class Arg, class Array, class... Indices,
            class = typename std::enable_if<
                (sizeof...(Indices) == Arg::dimensions)>::type>
  static auto CoeffFromArray(void *, Arg arg, Array a, Indices... indices) {
    return arg.Coeff(indices...);
  }

  template <class Arg, class Array, class... Indices,
            class = typename std::enable_if<
                (sizeof...(Indices) != Arg::dimensions)>::type>
  static auto CoeffFromArray(int *, Arg arg, Array a, Indices... indices) {
    return CoeffFromArray(arg, a, indices..., a[sizeof...(Indices)]);
  }

  template <class Vector, int index>
  using is_not_in = typename boost::is_same<
      typename boost::mpl::find<Vector, int_<index> >::type,
      typename boost::mpl::end<Vector>::type>::type;

  template <class MMatrix, class Skips, class IndicesTuple = tuple<>,
            int index = 0, bool = MMatrix::dimensions == index>
  struct output_type {
    typedef int_<MMatrix::template dimension<index>::value> dim;
    typedef typename std::conditional<
        is_not_in<Skips, index>::value,
        decltype(push_back(std::declval<IndicesTuple>(), dim())),
        IndicesTuple>::type next_tuple;

    typedef typename output_type<MMatrix, Skips, next_tuple, index + 1>::type
    type;
  };

  template <class MMatrix, class Skips, int index, int... dimensions>
  struct output_type<MMatrix, Skips, tuple<int_<dimensions>...>, index, true> {
    typedef MultiMatrix<typename MMatrix::CoefficientType, dimensions...> type;
  };

  template <class Array> static bool inc_pair(Array &a, int b, int c, int dim) {
    ++a[b];
    ++a[c];
    return a[b] == dim;
  }

  template <class Arg> auto eval(Arg arg) const {
    static const bool all_good = and_<bool_<
        Arg::template dimension<ReductionPairs::first>::value ==
        Arg::template dimension<ReductionPairs::second>::value>...>::value;
    static_assert(all_good, "Can only contract indices "
                            "of the same dimension");
    static std::array<std::pair<int, int>, sizeof...(ReductionPairs)> reduxes =
        { { std::make_pair(ReductionPairs::first,
                           ReductionPairs::second)... } };
    int in_indices[Arg::dimensions] = {};
    typename output_type<
        Arg, boost::mpl::vector<int_<ReductionPairs::first>...,
                                int_<ReductionPairs::second>...> >::type result;
    int out_indices[decltype(result)::dimensions] = {};
    while (true) {
      while (true) {
        result.Coeff(out_indices) += arg.Coeff(in_indices);
        unsigned index = 0;
        while (inc_pair(in_indices, reduxes[index].first, reduxes[index].second,
                        arg.Dimension(reduxes[index].second))) {
          in_indices[reduxes[index].first] = 0;
          in_indices[reduxes[index].second] = 0;
          if (++index == sizeof...(ReductionPairs)) {
            goto output_index_done;
          }
        }
      }
    output_index_done:
      int index = 0;
      while (true) {
        if (std::find_if(reduxes.begin(), reduxes.end(), [index](auto x) {
              return x.first == index || x.second == index;
            }) == reduxes.end()) {
          if (++in_indices[index] == arg.Dimension(index)) {
            int smaller_ones = 0;
            for (auto x : reduxes) {
              smaller_ones += ((x.first) < index) + (x.second < index);
            }
            in_indices[index] = out_indices[index - smaller_ones] = 0;
            if (++index == (int)Arg::dimensions)
              return result;
          } else {
            int smaller_ones = 0;
            for (auto x : reduxes) {
              smaller_ones += ((x.first) < index) + (x.second < index);
            }
            ++out_indices[index - smaller_ones];
            break;
          }
        } else if (++index == (int)Arg::dimensions)
          return result;
      }
    }
  }
};
}

#endif


#ifndef STAN_MATH_GPU_MULTIPLY_TRANSPOSE_HPP
#define STAN_MATH_GPU_MULTIPLY_TRANSPOSE_HPP
#ifdef STAN_OPENCL
#include <stan/math/gpu/matrix_gpu.hpp>
#include <stan/math/gpu/kernels/multiply_transpose.hpp>
#include <stan/math/gpu/err/check_square.hpp>
#include <Eigen/Dense>

namespace stan {
namespace math {
/**
 * Computes the product of a square GPU matrix with its transpose.
 *
 * Computes the matrix multiplication C = A x A^T
 *
 * @param A input matrix
 * @return the product of the input matrix and its transpose
 *
 */
inline matrix_gpu multiply_transpose(const matrix_gpu& A) {
  matrix_gpu temp(A.rows(), A.rows());
  if (A.size() == 0)
    return temp;
  // padding the matrices so the dimensions are divisible with local
  // improves performance becasuse we can omit if statements in the
  // multiply kernel
  int local = opencl_kernels::multiply_transpose.make_functor.get_opts().at(
      "THREAD_BLOCK_SIZE");
  int Mpad = ((A.rows() + local - 1) / local) * local;
  int Npad = ((A.cols() + local - 1) / local) * local;
  matrix_gpu tempPad(Mpad, Mpad);
  matrix_gpu Apad(Mpad, Npad);
  opencl_kernels::zeros(cl::NDRange(Mpad, Npad), Apad.buffer(), Mpad, Npad,
                        TriangularViewGPU::Entire);
  Apad.sub_block(A, 0, 0, 0, 0, A.rows(), A.cols());
  int wpt = opencl_kernels::multiply_transpose.make_functor.get_opts().at(
      "WORK_PER_THREAD");
  try {
    opencl_kernels::multiply_transpose(
        cl::NDRange(Mpad, Mpad / wpt), cl::NDRange(local, local / wpt),
        Apad.buffer(), tempPad.buffer(), Apad.rows(), Apad.cols());
  } catch (cl::Error& e) {
    check_opencl_error("multiply self transpose", e);
  }
  temp.sub_block(tempPad, 0, 0, 0, 0, temp.rows(), temp.cols());
  return temp;
}
}  // namespace math
}  // namespace stan

#endif
#endif


//////////////////////////////////////////////////////////////////////////////
///   Copyright 2003 and onward LASMEA UMR 6602 CNRS/U.B.P Clermont-Ferrand
///   Copyright 2009 and onward LRI    UMR 8623 CNRS/Univ Paris Sud XI
///
///          Distributed under the Boost Software License, Version 1.0
///                 See accompanying file LICENSE.txt or copy at
///                     http://www.boost.org/LICENSE_1_0.txt
//////////////////////////////////////////////////////////////////////////////
#ifndef NT2_TOOLBOX_IEEE_FUNCTION_SIMD_COMMON_NEXTPOW2_HPP_INCLUDED
#define NT2_TOOLBOX_IEEE_FUNCTION_SIMD_COMMON_NEXTPOW2_HPP_INCLUDED
#include <nt2/sdk/constant/digits.hpp>
#include <nt2/sdk/meta/as_integer.hpp>
#include <nt2/sdk/constant/real.hpp>
#include <boost/fusion/tuple.hpp>
#include <nt2/sdk/meta/strip.hpp>
#include <nt2/include/functions/tofloat.hpp>
#include <nt2/include/functions/toint.hpp>
#include <nt2/include/functions/seladd.hpp>
#include <nt2/include/functions/frexp.hpp>
#include <nt2/include/functions/popcnt.hpp>
#include <nt2/include/functions/group.hpp>
#include <nt2/include/functions/split.hpp>
#include <nt2/include/functions/firstbitset.hpp>

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is arithmetic_
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::nextpow2_, tag::cpu_,
                           (A0)(X),
                           ((simd_<arithmetic_<A0>,X>))
                          );

namespace nt2 { namespace ext
{
  template<class X, class Dummy>
  struct call<tag::nextpow2_(tag::simd_<tag::arithmetic_, X> ),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0>
    struct result<This(A0)> : meta::strip<A0>{};

    NT2_FUNCTOR_CALL(1)
      {
      typedef typename meta::as_integer<A0, unsigned>::type utype;
      return simd::native_cast<A0>(nextpow2(simd::native_cast<utype >(abs(a0))));
      //    return seladd(is_not_equal(popcnt(abs(a0)),One<A0>()), simd::native_cast<A0>(firstbitset(abs(a0))), One<A0>());
      }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is unsigned_
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::nextpow2_, tag::cpu_,
                           (A0)(X),
                           ((simd_<unsigned_<A0>,X>))
                          );

namespace nt2 { namespace ext
{
  template<class X, class Dummy>
  struct call<tag::nextpow2_(tag::simd_<tag::unsigned_, X> ),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0>
    struct result<This(A0)> : meta::strip<A0>{};

    NT2_FUNCTOR_CALL(1)
      {
      typedef typename meta::as_real<A0>::type rtype;
      typedef typename meta::as_integer<A0, signed>::type itype;
      rtype m;
      itype p;
      boost::fusion::tie(m, p) = frexp(tofloat(a0));
      //    std::cout << "a0 " << a0 << "  p " << p<< "  m " << m << std::endl;
      return simd::native_cast<A0>(seladd(is_equal(m, Half<rtype>()), p, Mone<itype>()));
      }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is uint16_t
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::nextpow2_, tag::cpu_,
                           (A0)(X),
                           ((simd_<uint16_<A0>,X>))
                          );

namespace nt2 { namespace ext
{
  template<class X, class Dummy>
  struct call<tag::nextpow2_(tag::simd_<tag::uint16_, X> ),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0>
    struct result<This(A0)> : meta::strip<A0>{};

    NT2_FUNCTOR_CALL(1)
      {
      typedef typename meta::scalar_of<A0>::type  stype;
      typedef typename meta::upgrade<stype>::type itype;
      typedef simd::native<itype,X>              ivtype;
      ivtype a0l, a0h;
      boost::fusion::tie(a0l, a0h) = split(a0);
      return simd::native_cast<A0>(group(nextpow2(a0l),nextpow2(a0h)));
      //seladd(is_not_equal(popcnt(abs(a0)),One<A0>()), simd::native_cast<A0>(lastbitset(abs(a0))), One<A0>());
      }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is uint8_t
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::nextpow2_, tag::cpu_,
                           (A0)(X),
                           ((simd_<uint8_<A0>,X>))
                          );

namespace nt2 { namespace ext
{
  template<class X, class Dummy>
  struct call<tag::nextpow2_(tag::simd_<tag::uint8_, X> ),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0>
    struct result<This(A0)> : meta::strip<A0>{};

    NT2_FUNCTOR_CALL(1)
      {
      typedef typename meta::scalar_of<A0>::type  stype;
      typedef typename meta::upgrade<stype>::type itype;
      typedef simd::native<itype, X>             ivtype;
      ivtype a0l, a0h;
      boost::fusion::tie(a0l, a0h) = split(a0);
      return simd::native_cast<A0>(group(nextpow2(a0l),nextpow2(a0h)));
      //seladd(is_not_equal(popcnt(abs(a0)),One<A0>()), simd::native_cast<A0>(lastbitset(abs(a0))), One<A0>());
      }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is real_
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::nextpow2_, tag::cpu_,
                           (A0)(X),
                           ((simd_<real_<A0>,X>))
                          );

namespace nt2 { namespace ext
{
  template<class X, class Dummy>
  struct call<tag::nextpow2_(tag::simd_<tag::real_, X> ),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0>
    struct result<This(A0)> : meta::strip<A0>{};

    NT2_FUNCTOR_CALL(1)
      {
      typedef typename meta::as_integer<A0>::type int_type;
      A0 m;
      int_type p;
      boost::fusion::tie(m, p) = frexp(abs(a0));
      return tofloat(seladd(is_equal(m, Half<A0>()), p, Mone<int_type>()));
      }
  };
} }

#endif
// modified by jt the 04/01/2011


#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK

#include <iostream>
#include <random>
#include <boost/test/unit_test.hpp>

#include "Map/2d/ProbabilityGrid.h"
#include "Map/ProbabilityValues.h"

namespace VISFS {
namespace Map {

BOOST_AUTO_TEST_CASE(ConstructorGridType) {
    ValueConversionTables conversionTables;
    ProbabilityGrid probabilityGrid(MapLimits(1.0, Eigen::Vector2d(1.0, 1.0), CellLimits(2, 2)), &conversionTables);
    BOOST_CHECK(probabilityGrid.getGridType() == GridType::PROBABILITY_GRID);
}

BOOST_AUTO_TEST_CASE(ApplyOdds) {
    ValueConversionTables conversionTables;
    ProbabilityGrid probabilityGrid(MapLimits(1.0, Eigen::Vector2d(1.0, 1.0), CellLimits(2, 2)), &conversionTables);
    const MapLimits & limits = probabilityGrid.limits();

    BOOST_CHECK(limits.contains(Eigen::Array2i(0, 0))== true);
    BOOST_CHECK(limits.contains(Eigen::Array2i(0, 1))== true);
    BOOST_CHECK(limits.contains(Eigen::Array2i(1, 0))== true);
    BOOST_CHECK(limits.contains(Eigen::Array2i(1, 1))== true);
    BOOST_CHECK(probabilityGrid.isKnown(Eigen::Array2i(0, 0)) == false);
    BOOST_CHECK(probabilityGrid.isKnown(Eigen::Array2i(0, 1)) == false);
    BOOST_CHECK(probabilityGrid.isKnown(Eigen::Array2i(1, 0)) == false);
    BOOST_CHECK(probabilityGrid.isKnown(Eigen::Array2i(1, 1)) == false);

    probabilityGrid.setProbability(Eigen::Array2i(1, 0), 0.5);
    BOOST_CHECK_EQUAL(probabilityGrid.getProbability(Eigen::Array2i(1, 0)), 0.5);
    probabilityGrid.applyLookUpTable(Eigen::Array2i(1, 0), computeLookupTableToApplyCorrespondenceCostOdds(odds(0.9)));
    probabilityGrid.finishUpdate();
    BOOST_CHECK_GT(probabilityGrid.getProbability(Eigen::Array2i(1, 0)), 0.5);

    probabilityGrid.setProbability(Eigen::Array2i(0, 1), 0.5);
    probabilityGrid.applyLookUpTable(Eigen::Array2i(0, 1), computeLookupTableToApplyCorrespondenceCostOdds(odds(0.1)));
    probabilityGrid.finishUpdate();
    BOOST_CHECK_LT(probabilityGrid.getProbability(Eigen::Array2i(0, 1)), 0.5);

    // Tests adding odds to an unknown cell.
    probabilityGrid.applyLookUpTable(Eigen::Array2i(1, 1), computeLookupTableToApplyCorrespondenceCostOdds(odds(0.42)));
    BOOST_CHECK_CLOSE(probabilityGrid.getProbability(Eigen::Array2i(1, 1)), 0.42, 1e-2);
    // Tests that further updates are ignored if FinishUpdate() isn't called.
    probabilityGrid.applyLookUpTable(Eigen::Array2i(1, 1), computeLookupTableToApplyCorrespondenceCostOdds(odds(0.9)));
    BOOST_CHECK_CLOSE(probabilityGrid.getProbability(Eigen::Array2i(1, 1)), 0.42, 1e-2);
    probabilityGrid.finishUpdate();
    probabilityGrid.applyLookUpTable(Eigen::Array2i(1, 1), computeLookupTableToApplyCorrespondenceCostOdds(odds(0.9)));
    BOOST_CHECK_GT(probabilityGrid.getProbability(Eigen::Array2i(1, 1)), 0.42);
}

BOOST_AUTO_TEST_CASE(GetProbability) {
    ValueConversionTables conversionTables;
    ProbabilityGrid probabilityGrid(MapLimits(1.0, Eigen::Vector2d(1.0, 2.0), CellLimits(2, 2)), &conversionTables);

    const MapLimits & limits = probabilityGrid.limits();
    BOOST_CHECK_EQUAL(1.0, limits.max().x());
    BOOST_CHECK_EQUAL(2.0, limits.max().y());

    const CellLimits & cellLmitis = limits.cellLimits();
    BOOST_REQUIRE_EQUAL(2, cellLmitis.numXcells);
    BOOST_REQUIRE_EQUAL(2, cellLmitis.numYcells);

    probabilityGrid.setProbability(limits.getCellIndex(Eigen::Vector2d(-0.5, 0.5)), kMaxProbability);
    BOOST_CHECK_CLOSE(probabilityGrid.getProbability(limits.getCellIndex(Eigen::Vector2d(-0.5, 0.5))), kMaxProbability, 1e-6);
    for (const Eigen::Array2i & xyIndex : {limits.getCellIndex(Eigen::Vector2d(-0.5, 1.5)),
                                            limits.getCellIndex(Eigen::Vector2d(0.5, 0.5)),
                                            limits.getCellIndex(Eigen::Vector2d(0.5, 1.5))}) {
        BOOST_CHECK_EQUAL(limits.contains(xyIndex), true);
        BOOST_CHECK_EQUAL(probabilityGrid.isKnown(xyIndex), false);
    }

}

BOOST_AUTO_TEST_CASE(GetCellIndex) {
    ValueConversionTables conversionTables;
    ProbabilityGrid probabilityGrid(MapLimits(2.0, Eigen::Vector2d(8.0, 14.0), CellLimits(14, 8)), &conversionTables);

    const MapLimits & limits = probabilityGrid.limits();
    const CellLimits & cellLimits = limits.cellLimits();

    BOOST_REQUIRE_EQUAL(14, cellLimits.numXcells);
    BOOST_REQUIRE_EQUAL(8, cellLimits.numYcells);

    BOOST_CHECK_EQUAL((Eigen::Array2i(0, 0) == limits.getCellIndex(Eigen::Vector2d(7.0, 13.0))).all(), true);
    BOOST_CHECK_EQUAL((Eigen::Array2i(13, 0) == limits.getCellIndex(Eigen::Vector2d(7.0, -13.0))).all(), true);
    BOOST_CHECK_EQUAL((Eigen::Array2i(0, 7) == limits.getCellIndex(Eigen::Vector2d(-7.0, 13.0))).all(), true);
    BOOST_CHECK_EQUAL((Eigen::Array2i(13, 7) == limits.getCellIndex(Eigen::Vector2d(-7.0, -13.0))).all(), true);
    // Check around the origin.
    BOOST_CHECK_EQUAL((Eigen::Array2i(6, 3) == limits.getCellIndex(Eigen::Vector2d(0.5, 0.5))).all(), true);
    BOOST_CHECK_EQUAL((Eigen::Array2i(6, 3) == limits.getCellIndex(Eigen::Vector2d(1.5, 1.5))).all(), true);
    BOOST_CHECK_EQUAL((Eigen::Array2i(7, 3) == limits.getCellIndex(Eigen::Vector2d(0.5, -0.5))).all(), true);
    BOOST_CHECK_EQUAL((Eigen::Array2i(6, 4) == limits.getCellIndex(Eigen::Vector2d(-0.5, 0.5))).all(), true);
    BOOST_CHECK_EQUAL((Eigen::Array2i(7, 4) == limits.getCellIndex(Eigen::Vector2d(-0.5, -0.5))).all(), true);
}

BOOST_AUTO_TEST_CASE(CorrectCropping) {
    // Creat a probability grid with random values.
    std::random_device rd;
    std::mt19937 rng(rd());
    std::uniform_real_distribution<double> valueDistribution(kMinProbability, kMaxProbability);

    ValueConversionTables conversionTables;
    ProbabilityGrid probabilityGrid(MapLimits(0.05, Eigen::Vector2d(10.0, 10.0), CellLimits(400, 400)), &conversionTables);

    for (const Eigen::Array2i & xyIndex : XYIndexRangeIterator(Eigen::Array2i(100, 100), Eigen::Array2i(299, 299))) {
        probabilityGrid.setProbability(xyIndex, valueDistribution(rng));
    }

    Eigen::Array2i offset;
    CellLimits limits;
    probabilityGrid.computeCroppedLimits(&offset, &limits);
    BOOST_CHECK_EQUAL((offset == Eigen::Array2i(100, 100)).all(), true);
    BOOST_CHECK_EQUAL(limits.numXcells, 200);
    BOOST_CHECK_EQUAL(limits.numYcells, 200);
}

BOOST_AUTO_TEST_CASE(GRID2IMAGE) {
    ValueConversionTables conversionTables;
    ProbabilityGrid probabilityGrid(MapLimits(0.05, Eigen::Vector2d(10.0, 10.0), CellLimits(400, 400)), &conversionTables);

    for (const Eigen::Array2i & xyIndex : XYIndexRangeIterator(Eigen::Array2i(100, 100), Eigen::Array2i(299, 299))) {
        probabilityGrid.setProbability(xyIndex, kMaxProbability);
    }

    cv::Mat image = probabilityGrid.grid2Image();
}

}   // Map
}   // VISFS


// Copyright András Vukics 2006–2020. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.txt)
#define BOOST_TEST_MODULE Transformation
#include <boost/test/unit_test.hpp>

#include "ComplexArrayExtensions.h"
#include "Transformation.h"

#include "Randomized.h"

#include <boost/function.hpp>

#include <iostream>

using namespace quantumdata;
using namespace blitzplusplus;

typedef CArray<1> Array1;
typedef CArray<2> Array2;
typedef CArray<4> Array4;
typedef CArray<6> Array6;

typedef transformation::Traits<Array2> Array2Traits;
typedef transformation::Traits<Array4> Array4Traits;

typedef transformation::Identity<1> Identity1;
typedef transformation::Traits<Identity1> Identity1Traits;

typedef transformation::Identity<2> Identity2;

template<int RANK>
struct Dummy {};

namespace quantumdata {

namespace transformation {

template<int RANK>
struct Traits<Dummy<RANK> >
{
  static const int N_RANK=RANK;
};

} // transformation

} // quantumdata

BOOST_AUTO_TEST_CASE(CompositeAuxiliaryVector)
{
  typedef boost::mpl::first<transformation::namehider::Algorithm<boost::fusion::result_of::make_vector<Dummy<2>,transformation::Identity<3>,Dummy<1>,Dummy<3>,transformation::Identity<4>,Dummy<3> >::type>::type>::type AuxiliaryVector;

  BOOST_STATIC_ASSERT((tmptools::numerical_equal<AuxiliaryVector,boost::mpl::vector_c<int,0,5,6,13> >::value));

}

BOOST_AUTO_TEST_CASE(LinearTransformation)
{
  Array1 x(3), x_(3);
  x=1,2,3;

  Array2 T(3,3);
  T=
    1,0,0,
    0,1,dcomp(0.5,0.5),
    0,dcomp(0.5,-0.5),1;
  Array2Traits::transform(T, x, x_);
  BOOST_CHECK_EQUAL(x_(0), T(0,0)*x(0) + T(0,1)*x(1) + T(0,2)*x(2));
  BOOST_CHECK_EQUAL(x_(1), T(1,0)*x(0) + T(1,1)*x(1) + T(1,2)*x(2));
  BOOST_CHECK_EQUAL(x_(2), T(2,0)*x(0) + T(2,1)*x(1) + T(2,2)*x(2));
}

BOOST_AUTO_TEST_CASE(IdentityTransformation)
{
  Array1 x(3);
  x=1,2,3;
  Identity1 I;
  Array1 y(3);
  Identity1Traits::transform(I, x, y);

  BOOST_CHECK(all(y==x));
}

BOOST_AUTO_TEST_CASE(CompositeTransformation)
{
  Array1 x(2), x_(2), y(3), y_(3);

  x=1,2; y=3,4,-1;

  Array2 T1(2,2), T2(3,3);
  T1=
    1,dcomp(0,1),
    dcomp(0,-1),1;
  T2=
     2, 1,-1,
     1, 3,-2,
    -1,-2, 5;

  Array2 xy(2,3), xy_1(2,3), xy_2(2,3);
  typedef transformation::Compose<Array2,Array2> Composite;
  Composite::type c(Composite::compose(T1,T2));
  typedef transformation::Traits<Composite::type> CTraits;

  Array2Traits::transform(T1,x,x_);
  Array2Traits::transform(T2,y,y_);

  xy_1 = blitzplusplus::doDirect<blitzplusplus::dodirect::multiplication>(x_,y_);

  xy   = blitzplusplus::doDirect<blitzplusplus::dodirect::multiplication>(x ,y );

  CTraits::transform(c,xy,xy_2);

  BOOST_CHECK(blitz::all(xy_1==xy_2));

  // std::cout<<max(sqrAbs(xy_1-xy_2));

}

void trafoFunction(const Array2& in, Array2& out) {out=in; swap(out(1,2),out(2,1));}

BOOST_AUTO_TEST_CASE(MoreCompositeTransformation)
{
  using randomized::fillWithRandom; using namespace blitzplusplus;

  Array1
    psi1(6), psi1v(psi1.shape()),
    psi4(4), psi4v(psi4.shape()); // x_(2), y(3), y_(3);

  Array2
    psi23(5,3), psi23v(psi23.shape()),
    psi56(3,2), psi56v(psi56.shape());

  Array2 t1(6,6), t4(4,4);

  Array4 t56(3,2,3,2);

  fillWithRandom(t56,fillWithRandom(t4,fillWithRandom(t1,fillWithRandom(psi56,fillWithRandom(psi4,fillWithRandom(psi23,fillWithRandom(psi1)))))));

  t1 +=hermitianConjugate(t1 );
  t4 +=hermitianConjugate(t4 );
  t56+=hermitianConjugate(t56);

  Array6 psiAll(6,5,3,4,3,2), psiAllv(psiAll.shape()), psiAllvv(psiAll.shape());

  ////////////////////////////////////
  // Composite trafo with one Identity
  ////////////////////////////////////

  Array2Traits::transform(t1,psi1,psi1v);
  Array2Traits::transform(t4,psi4,psi4v);

  Array4Traits::transform(t56,psi56,psi56v);

  {
    using namespace blitz::tensor;
    psiAll =psi1 (i)*psi23(j,k)*psi4 (l)*psi56 (m,n);
    psiAllv=psi1v(i)*psi23(j,k)*psi4v(l)*psi56v(m,n);
  }

  {
    using namespace boost::fusion;
    transformation::Composite<result_of::make_vector<Array2,Identity2,Array2,Array4>::type>
      (make_vector(t1,Identity2(),t4,t56))
      .transform(psiAll,psiAllvv);
  }

  BOOST_CHECK( max( sqrAbs(psiAllv-psiAllvv) ) < 1e-24 );
  // For some reason, there is no exact equality

  ////////////////////////////////////////
  // Composite trafo with one non-Identity
  ////////////////////////////////////////

  trafoFunction(psi23,psi23v);

  {
    using namespace blitz::tensor;
    psiAllv=psi1(i)*psi23v(j,k)*psi4(l)*psi56(m,n);
  }

  {
    using namespace boost::fusion;

    transformation::Composite<result_of::make_vector<Identity1,void(*)(const Array2&, Array2&),Identity1,Identity2>::type>
      (make_vector(Identity1(),&trafoFunction,Identity1(),Identity2())).transform(psiAll,psiAllvv);
  }

  BOOST_CHECK( max( sqrAbs(psiAllv-psiAllvv) ) < 1e-24 );

}


/* ------------------------------------------------------
 *
 * @file random_forest.cpp
 *
 * @brief Random Forest functions
 *
 *
 */ /* ----------------------------------------------------------------------- */

#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <list>
#include <iterator>

#include <dbconnector/dbconnector.hpp>
#include <boost/random/discrete_distribution.hpp>
#include <boost/random/variate_generator.hpp>

#include "DT_proto.hpp"
#include "DT_impl.hpp"
#include "ConSplits.hpp"

#include <math.h>       /* fabs */

#include "random_forest.hpp"

namespace madlib {

// Use Eigen
using namespace dbal::eigen_integration;

using boost::random::discrete_distribution;
using boost::random::variate_generator;

namespace modules {

namespace recursive_partitioning {

typedef DecisionTree<RootContainer> Tree;

/*
 * Permute each categorical variable and predict
 */
AnyType
rf_cat_imp_score::run(AnyType &args) {
    if (args[0].isNull() || args[7].isNull()) { return Null(); }
    Tree dt = args[0].getAs<ByteString>();
    MutableNativeIntegerVector cat_features;
    NativeColumnVector con_features;
    try {
        if (args[1].isNull()){
            // no cat features
            return Null();
        }
        else {
            MutableNativeIntegerVector xx_cat = args[1].getAs<MutableNativeIntegerVector>();
            cat_features.rebind(xx_cat.memoryHandle(), xx_cat.size());
        }
        if (args[2].isNull()){
            con_features.rebind(this->allocateArray<double>(0));
        }
        else {
            NativeColumnVector xx_con = args[2].getAs<NativeColumnVector>();
            con_features.rebind(xx_con.memoryHandle(), xx_con.size());
        }
    } catch (const ArrayWithNullException &e) {
        // not expect to reach here
        // if max_surr = 0, nulls are filtered
        // otherwise, mapped to -1 or NaN
        return Null();
    }

    MappedIntegerVector cat_n_levels = args[3].getAs<MappedIntegerVector>();

    int n_permutations = args[4].getAs<int>();
    double y = args[5].getAs<double>();
    bool is_classification = args[6].getAs<bool>();
    MappedMatrix distributions = args[7].getAs<MappedMatrix>();

    // returning
    MutableNativeColumnVector permuted_predictions(
            this->allocateArray<double>(cat_n_levels.size()));

    // permute each and predict
    NativeRandomNumberGenerator generator;
    for (int p = 0; p < n_permutations; p ++) {
        for (Index i = 0; i < cat_n_levels.size(); i ++) {
            int orig_i = cat_features(i);
            discrete_distribution<> ddist(distributions.col(i).data(),
                    distributions.col(i).data() + cat_n_levels(i) + 1);
            variate_generator<NativeRandomNumberGenerator, discrete_distribution<> >
                    rvt(generator, ddist);

            cat_features(i) = rvt() - 1;

            // calling NativeIntegerVector for a const cast
            // see EigenIntegration_impl.hpp in ports for details
            double prediction = dt.predict_response(
                NativeIntegerVector(cat_features.memoryHandle()), con_features);
            double score = 0.;
            if (is_classification) {
                score = y - prediction < 1e-3 ? 1. : 0.;
            } else {
                score = - (y - prediction) * (y - prediction);
            }
            permuted_predictions(i) += score;

            cat_features(i) = orig_i;
        }
    }
    permuted_predictions /= n_permutations;
    return permuted_predictions;
}
// ------------------------------------------------------------

/*
 * Permute each continuous variable and predict
 */
AnyType
rf_con_imp_score::run(AnyType &args) {
    if (args[0].isNull() || args[7].isNull()) { return Null(); }
    Tree dt = args[0].getAs<ByteString>();
    NativeIntegerVector cat_features;
    MutableNativeColumnVector con_features;
    try {
        if (args[1].isNull()){
            // no cat features
            cat_features.rebind(this->allocateArray<int>(0));
        }
        else {
            NativeIntegerVector xx_cat = args[1].getAs<NativeIntegerVector>();
            cat_features.rebind(xx_cat.memoryHandle(), xx_cat.size());
        }
        if (args[2].isNull()){
            //no con features
            return Null();
        }
        else {
            MutableNativeColumnVector xx_con = args[2].getAs<MutableNativeColumnVector>();
            con_features.rebind(xx_con.memoryHandle(), xx_con.size());
        }
    } catch (const ArrayWithNullException &e) {
        // not expect to reach here
        // if max_surr = 0, nulls are filtered
        // otherwise, mapped to -1 or NaN
        return Null();
    }

    // con_splits size = num_con_features x num_bins
    // When num_con_features = 0, the input will be an empty string that is read
    // as a ByteString
    ConSplitsResult<RootContainer> splits_results = args[3].getAs<ByteString>();

    int n_permutations = args[4].getAs<int>();
    double y = args[5].getAs<double>();
    bool is_classification = args[6].getAs<bool>();
    MappedMatrix distributions = args[7].getAs<MappedMatrix>();

    // returning
    MutableNativeColumnVector permuted_predictions(
            this->allocateArray<double>(con_features.size()));

    // permute each and predict
    NativeRandomNumberGenerator generator;
    for (int p = 0; p < n_permutations; p ++) {
        for (Index i = 0; i < con_features.size(); i ++) {
            double orig_i = con_features(i);
            discrete_distribution<> ddist(distributions.col(i).data(),
                    distributions.col(i).data() + distributions.rows());
            variate_generator<NativeRandomNumberGenerator, discrete_distribution<> >
                    rvt(generator, ddist);

            int outcome = rvt();
            if (outcome == 0) {
                con_features(i) = std::numeric_limits<double>::quiet_NaN();
            } else if (outcome == static_cast<int>(distributions.rows()) - 1) {
                // bin value that is larger than the last separator (last value in con_splits)
                con_features(i) = splits_results.con_splits(i, outcome-2) + 1.;
            } else {
                con_features(i) = splits_results.con_splits(i, outcome-1);
            }

            // calling NativeColumnVector for a const cast
            // see EigenIntegration_impl.hpp in ports for details
            double prediction = dt.predict_response(
                cat_features, NativeColumnVector(con_features.memoryHandle()));
            double score = 0.;
            if (is_classification) {
                score = y - prediction < 1e-3 ? 1. : 0.;
            } else {
                score = - (y - prediction) * (y - prediction);
            }
            permuted_predictions(i) += score;

            con_features(i) = orig_i;
        }
    }
    permuted_predictions /= n_permutations;
    return permuted_predictions;
}
// ------------------------------------------------------------

} // namespace recursive_partitioning
} // namespace modules
} // namespace madlib