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

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_TEST_UNION_HPP
#define BOOST_GEOMETRY_TEST_UNION_HPP

#include <fstream>

#include <geometry_test_common.hpp>

#include <boost/range/algorithm/copy.hpp>

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

#include <boost/geometry/algorithms/area.hpp>
#include <boost/geometry/algorithms/correct.hpp>
#include <boost/geometry/algorithms/length.hpp>
#include <boost/geometry/algorithms/num_points.hpp>

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

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

#include <boost/geometry/io/wkt/wkt.hpp>

#if defined(TEST_WITH_SVG)
#  include <boost/geometry/extensions/io/svg/svg_mapper.hpp>
#endif

template <typename OutputType, typename G1, typename G2>
void test_union(std::string const& caseid, G1 const& g1, G2 const& g2,
        std::size_t expected_count, std::size_t expected_hole_count,
        std::size_t expected_point_count, double expected_area,
        double percentage)
{
    typedef typename bg::coordinate_type<G1>::type coordinate_type;
    std::vector<OutputType> clip;
    bg::union_(g1, g2, clip);

    typename bg::default_area_result<G1>::type area = 0;
    std::size_t n = 0;
    std::size_t holes = 0;
    for (typename std::vector<OutputType>::iterator it = clip.begin();
            it != clip.end(); ++it)
    {
        area += bg::area(*it);
        holes += bg::num_interior_rings(*it);
        n += bg::num_points(*it, true);
    }

    {
        // Test inserter functionality
        // Test if inserter returns output-iterator (using Boost.Range copy)
        std::vector<OutputType> inserted, array_with_one_empty_geometry;
        array_with_one_empty_geometry.push_back(OutputType());
        boost::copy(array_with_one_empty_geometry, bg::detail::union_::union_insert<OutputType>(g1, g2, std::back_inserter(inserted)));

        typename bg::default_area_result<G1>::type area_inserted = 0;
        int index = 0;
        for (typename std::vector<OutputType>::iterator it = inserted.begin();
                it != inserted.end();
                ++it, ++index)
        {
            // Skip the empty polygon created above to avoid the empty_input_exception
            if (bg::num_points(*it) > 0)
            {
                area_inserted += bg::area(*it);
            }
        }
        BOOST_CHECK_EQUAL(boost::size(clip), boost::size(inserted) - 1);
        BOOST_CHECK_CLOSE(area_inserted, expected_area, percentage);
    }

    /***
    std::cout << "case: " << caseid
        << " n: " << n
        << " area: " << area
        << " polygons: " << boost::size(clip)
        << " holes: " << holes
        << std::endl;
    ***/

    BOOST_CHECK_MESSAGE(std::size_t(n) == expected_point_count,
            "union: " << caseid
            << " #points expected: " << expected_point_count
            << " detected: " << n
            << " type: " << string_from_type<coordinate_type>::name()
            );

    BOOST_CHECK_EQUAL(clip.size(), expected_count);
    BOOST_CHECK_EQUAL(holes, expected_hole_count);
    BOOST_CHECK_CLOSE(area, expected_area, percentage);

#if defined(TEST_WITH_SVG)
    {
        bool const ccw =
            bg::point_order<G1>::value == bg::counterclockwise
            || bg::point_order<G2>::value == bg::counterclockwise;
        bool const open =
            bg::closure<G1>::value == bg::open
            || bg::closure<G2>::value == bg::open;

        std::ostringstream filename;
        filename << "union_"
            << caseid << "_"
            << string_from_type<coordinate_type>::name()
            << (ccw ? "_ccw" : "")
            << (open ? "_open" : "")
            << ".svg";

        std::ofstream svg(filename.str().c_str());

        bg::svg_mapper
            <
                typename bg::point_type<G2>::type
            > mapper(svg, 500, 500);
        mapper.add(g1);
        mapper.add(g2);

        mapper.map(g1, "fill-opacity:0.5;fill:rgb(153,204,0);"
                "stroke:rgb(153,204,0);stroke-width:3");
        mapper.map(g2, "fill-opacity:0.3;fill:rgb(51,51,153);"
                "stroke:rgb(51,51,153);stroke-width:3");
        //mapper.map(g1, "opacity:0.6;fill:rgb(0,0,255);stroke:rgb(0,0,0);stroke-width:1");
        //mapper.map(g2, "opacity:0.6;fill:rgb(0,255,0);stroke:rgb(0,0,0);stroke-width:1");

        for (typename std::vector<OutputType>::const_iterator it = clip.begin();
                it != clip.end(); ++it)
        {
            mapper.map(*it, "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,0,0);"
                    "stroke:rgb(255,0,255);stroke-width:8");
            //mapper.map(*it, "opacity:0.6;fill:none;stroke:rgb(255,0,0);stroke-width:5");
        }
    }
#endif
}

template <typename OutputType, typename G1, typename G2>
void test_one(std::string const& caseid, std::string const& wkt1, std::string const& wkt2,
        std::size_t expected_count, std::size_t expected_hole_count,
        std::size_t expected_point_count, double expected_area,
        double percentage = 0.001)
{
    G1 g1;
    bg::read_wkt(wkt1, g1);

    G2 g2;
    bg::read_wkt(wkt2, g2);

    // Reverse if necessary
    bg::correct(g1);
    bg::correct(g2);

    test_union<OutputType>(caseid, g1, g2,
        expected_count, expected_hole_count, expected_point_count,
        expected_area, percentage);
}

#endif


#ifndef VECTOR_HH
#define VECTOR_HH

#include <dune/common/dynvector.hh>

#ifdef AS_LIB
#include <boost/python.hpp>
#endif

class Vector : public Dune::DynamicVector<double> {
public:

  Vector(int dim, double val) : Dune::DynamicVector<double>(dim, val) {}
  Vector(int dim) : Dune::DynamicVector<double>(dim) {}
  Vector(const Vector& other) : Dune::DynamicVector<double>(other) {}

  using Dune::DynamicVector<double>::operator =;

  Vector* copy()
  {
    return new Vector(*this);
  }

  void assign(Vector& other)
  {
    (*this) = other;
  }

  void setitem(std::size_t i, double value)
  {
    (*this)[i] = value;
  }

  double getitem(std::size_t i)
  {
    return (*this)[i];
  }

  double dot(const Vector& other)
  {
    return Dune::DynamicVector<double>::dot(other);
  }

  void scal(const double a)
  {
    (*this) *= a;
  }

  void axpy(const double a, const Vector& other)
  {
    Dune::DynamicVector<double>::axpy(a, other);
  }

  std::size_t amaxInd() {
    std::size_t currentIndex = 0;
    double currentValue = fabs((*this)[0]);
    auto sz = size();
    for (std::size_t i = 1; i < sz; i++) {
      double newValue = fabs((*this)[i]);
      if (newValue > currentValue) {
        currentValue = newValue;
        currentIndex = i;
      }
    }
    return currentIndex;
  }

#ifdef AS_LIB
  static boost::python::object buffer(Vector& self)
  {
    using boost::python::object;
    using boost::python::handle;
    PyObject* py_buf = PyBuffer_FromReadWriteMemory(&(self[0]), self.dim() * sizeof(double));
    object retval = object(handle<>(py_buf));
    return retval;
  }

  static void export_()
  {
    using boost::python::object;
    using boost::python::class_;
    using boost::python::init;
    using boost::python::return_value_policy;
    using boost::python::manage_new_object;
    using boost::python::return_internal_reference;

    class_<Vector>("Vector", init<int, double>())
        .add_property("dim", &Vector::size)
        .def("l1Norm", &Vector::one_norm)
        .def("l2Norm", &Vector::two_norm)
        .def("supNorm", &Vector::infinity_norm)
        .def("axpy", &Vector::axpy)
        .def("__setitem__", &Vector::setitem)
        .def("__getitem__", &Vector::getitem)
        .def("dot", &Vector::dot)
        .def("copy", &Vector::copy, return_value_policy<manage_new_object>())
        .def("assign", &Vector::assign)
        .def("scal", &Vector::scal)
        .def("amaxInd", &Vector::amaxInd)
        .def("buffer", &buffer)
    ;
  }
#endif
};

#endif  // VECTOR_HH


#define BOOST_TEST_MODULE "dsn::bitfield"

#include <dsnutil/bitfield.hpp>
#include <iomanip>
#include <iostream>

#include <boost/test/unit_test.hpp>

enum class Test : unsigned short {
    None = 0,
    Foo = (1 << 0),
    Bar = (1 << 1),
    Bla = (1 << 2),
};

std::ostream& operator<<(std::ostream& stream, const Test& t)
{
    using T = typename std::underlying_type<Test>::type;
    constexpr auto bitset_size = sizeof(T) * CHAR_BIT;
    using binary_type = std::bitset<bitset_size>;
    auto value = static_cast<T>(t);

    return stream << binary_type(value).to_string() << " (" << value << ")";
}

using field = dsn::bitfield<Test>;

BOOST_AUTO_TEST_CASE(mp_helpers)
{
    std::cout << "dsn::bitfield<Test> requires " << field::num_bits() << "bits of storage." << std::endl;
    BOOST_CHECK(field::num_bits() == sizeof(std::underlying_type<Test>::type) * CHAR_BIT);
}

BOOST_AUTO_TEST_CASE(default_construction)
{
    std::cout << "Test enum values are: " << std::endl
              << "\tNone= " << Test::None << std::endl
              << "\tFoo = " << Test::Foo << std::endl
              << "\tBar = " << Test::Bar << std::endl
              << "\tBla = " << Test::Bla << std::endl;

    field f;
    std::cout << "-- Testing default construction" << std::endl;
    std::cout << "bitfield() = " << f << std::endl;

    BOOST_CHECK(f.value() == Test::None);
}

BOOST_AUTO_TEST_CASE(construct_with_value)
{
    std::cout << "-- Testing construction with value" << std::endl;
    field f(Test::Bla);
    std::cout << "bitfield(" << Test::Bla << ") = " << f << std::endl;

    BOOST_CHECK(f.value() == Test::Bla);
}

BOOST_AUTO_TEST_CASE(construct_with_initializer_list)
{
    std::cout << "-- Testing construction with initializer list" << std::endl;
    field f({ Test::Foo, Test::Bar });
    std::cout << "bitfield({" << Test::Foo << "," << Test::Bar << "}) = " << f << std::endl;

    BOOST_CHECK(f.is_set(Test::Foo) == true);
    BOOST_CHECK(f.is_set(Test::Bar) == true);
}

BOOST_AUTO_TEST_CASE(copy_construct)
{
    std::cout << "-- Testing copy construction" << std::endl;
    field f1(Test::Bla);
    std::cout << "f1 = " << f1 << std::endl;
    BOOST_CHECK(f1.is_set(Test::Bla) == true);

    field f2(f1);
    std::cout << "f2 = " << f2 << std::endl;
    BOOST_CHECK(f1.value() == f2.value());
}

BOOST_AUTO_TEST_CASE(copy_assign)
{
    std::cout << "-- Testing copy assignment" << std::endl;
    field f1(Test::Bla);
    std::cout << "f1 = " << f1 << std::endl;
    BOOST_CHECK(f1.is_set(Test::Bla) == true);

    field f2;
    std::cout << "f2 = " << f2 << " (before assignment)" << std::endl;
    BOOST_CHECK(f2.is_set(Test::Bla) == false);

    f2 = f1;
    std::cout << "f2 = " << f2 << " (after assignment)" << std::endl;
    BOOST_CHECK(f2.is_set(Test::Bla) == true);
}

BOOST_AUTO_TEST_CASE(move_construct)
{
    field f1(Test::Bla);
    std::cout << "f1 = " << f1 << " (before move)" << std::endl;
    BOOST_CHECK(f1.is_set(Test::Bla) == true);

    field f2(std::move(f1));
    std::cout << "f2 = " << f2 << std::endl << "f1 = " << f1 << " (after move)" << std::endl;
    BOOST_CHECK(f2.is_set(Test::Bla) == true);
    BOOST_CHECK(f1.is_set(Test::Bla) == false);
}

BOOST_AUTO_TEST_CASE(move_assign)
{
    std::cout << "-- Testing move assignment" << std::endl;
    field f1(Test::Bla);
    std::cout << "f1 = " << f1 << " (before move)" << std::endl;
    BOOST_CHECK(f1.is_set(Test::Bla) == true);

    field f2;
    std::cout << "f2 = " << f2 << " (before move)" << std::endl;
    BOOST_CHECK(f2.is_set(Test::Bla) == false);

    f2 = std::move(f1);
    std::cout << "f1 = " << f1 << " (after move)" << std::endl << "f2 = " << f2 << " (after move)" << std::endl;
    BOOST_CHECK(f2.is_set(Test::Bla) == true);
    BOOST_CHECK(f1.is_set(Test::Bla) == false);
}

BOOST_AUTO_TEST_CASE(set_flag)
{
    std::cout << "-- Testing set(flag)" << std::endl;
    field f;
    std::cout << "f = " << f << " (before set)" << std::endl;
    BOOST_CHECK(f.is_set(Test::Bla) == false);

    f.set(Test::Bla);
    std::cout << "f = " << f << " (after set)" << std::endl;
    BOOST_CHECK(f.is_set(Test::Bla) == true);
}

BOOST_AUTO_TEST_CASE(clear_flags)
{
    std::cout << "-- Testing clear without mask" << std::endl;
    field f(Test::Bla);
    std::cout << "f = " << f << " (before clear)" << std::endl;
    BOOST_CHECK(f.is_set(Test::Bla) == true);

    f.clear();
    std::cout << "f = " << f << " (after clear)" << std::endl;
    BOOST_CHECK(f.is_set(Test::Bar) == false);
}

BOOST_AUTO_TEST_CASE(clear_flags_with_mask)
{
    std::cout << "-- Testing clear with mask" << std::endl;
    field f({ Test::Foo, Test::Bla });
    std::cout << "f = " << f << " (before clear)" << std::endl;
    BOOST_CHECK(f.is_set(Test::Foo) and f.is_set(Test::Bla));

    f.clear(Test::Foo);
    std::cout << "f = " << f << " (after clear)" << std::endl;
    BOOST_CHECK(f.is_set(Test::Foo) and !f.is_set(Test::Bla));
}

BOOST_AUTO_TEST_CASE(operator_equality)
{
    field f1(Test::Bla);
    field f2(f1);
    std::cout << "-- Testing comparison operator ==" << std::endl
              << "f1 = " << f1 << std::endl
              << "f2 = " << f2 << std::endl;

    BOOST_CHECK(f1 == f2);
}

BOOST_AUTO_TEST_CASE(operator_inequality)
{
    field f1(Test::Bla);
    field f2(Test::Foo);
    std::cout << "-- Testing comparison operator !=" << std::endl
              << "f1 = " << f1 << std::endl
              << "f2 = " << f2 << std::endl;

    BOOST_CHECK(f1 != f2);
}

BOOST_AUTO_TEST_CASE(raw_value)
{
    field f(Test::Foo);
    const int expected{ static_cast<int>(Test::Foo) };
    std::cout << "-- Testing raw_value()" << std::endl << "f = " << f << ", expected=" << expected << std::endl;
    BOOST_CHECK(f.raw_value() == expected);
}

BOOST_AUTO_TEST_CASE(friend_operator_or_value)
{
    field source(Test::Foo);
    std::cout << "-- Testing operator| with T" << std::endl << "source = " << source << std::endl;

    field f1 = (Test::Bar | source);
    std::cout << "f1 = " << f1 << std::endl;
    BOOST_CHECK((f1.is_set(Test::Foo) == true) and (f1.is_set(Test::Bar) == true));

    field f2 = (source | Test::Bar);
    BOOST_CHECK(f2 == f1);
}

BOOST_AUTO_TEST_CASE(operator_or_bitfield)
{
    field src1(Test::Foo);
    field src2(Test::Bar);
    std::cout << "-- Testing operator| with bitfield<T>" << std::endl
              << "src1 = " << src1 << std::endl
              << "src2 = " << src2 << std::endl;

    field dst1 = src1;
    std::cout << "dst1 = " << dst1 << " (before |=)" << std::endl;
    BOOST_CHECK(dst1 == src1);
    dst1 |= src2;
    std::cout << "dst1 = " << dst1 << " (after |=)" << std::endl;
    BOOST_CHECK((dst1.is_set(Test::Foo) == true) and (dst1.is_set(Test::Bar) == true));
}

BOOST_AUTO_TEST_CASE(operator_and_value)
{
    field f1({ Test::Foo, Test::Bar });
    std::cout << "-- Testing operator &= with T" << std::endl << "f1 = " << f1 << std::endl;
    BOOST_CHECK(f1.is_set(Test::Foo) and f1.is_set(Test::Bar));

    f1 &= Test::Foo;
    std::cout << "f1 = " << f1 << " (after &= " << Test::Foo << ")" << std::endl;
    BOOST_CHECK(f1.is_set(Test::Foo) and !f1.is_set(Test::Bar));

    f1 &= Test::Bla;
    std::cout << "f1 = " << f1 << " (after &= " << Test::Bla << ")" << std::endl;
    BOOST_CHECK(!f1.is_set(Test::Foo));
}

BOOST_AUTO_TEST_CASE(operator_and_bitfield)
{
    field f1({ Test::Foo, Test::Bar });
    std::cout << "-- Testing operator &= with bitfield<T>" << std::endl << "f1 = " << f1 << std::endl;
    BOOST_CHECK(f1.is_set(Test::Foo) and f1.is_set(Test::Bar));

    field f2(Test::Foo);
    std::cout << "f2 = " << f2 << std::endl;
    BOOST_CHECK(f1.is_set(Test::Foo));

    f1 &= f2;
    std::cout << "f1 = " << f1 << " (after &= f2)" << std::endl;
    BOOST_CHECK(f1.is_set(Test::Foo) and !f1.is_set(Test::Bar));

    field f3(Test::Bla);
    std::cout << "f3 = " << f3 << std::endl;
    BOOST_CHECK(f3.is_set(Test::Bla));

    f2 &= f3;
    std::cout << "f2 = " << f2 << " (after &= f3)" << std::endl;
    BOOST_CHECK(!f2.is_set(Test::Foo) and !f2.is_set(Test::Bla));
}

BOOST_AUTO_TEST_CASE(operator_xor_value)
{
    field f({ Test::Foo, Test::Bla });
    field g({ Test::Bla });
    std::cout << "-- Testing operator ^= with T" << std::endl
              << "f = " << f << " (before &=)" << std::endl
              << "g = " << g << " (expected value)" << std::endl;
    f ^= Test::Foo;
    BOOST_CHECK(f == g);
}

BOOST_AUTO_TEST_CASE(operator_xor_bitfield)
{
    field f({ Test::Foo, Test::Bla });
    std::cout << "-- Testing operator ^= with bitfield<T>" << std::endl << "f = " << f << " (before &=)" << std::endl;

    field g({ Test::Foo, Test::Bar });
    field h({ Test::Bar, Test::Bla });
    std::cout << "g = " << g << std::endl << "h = " << h << " (expected result)" << std::endl;

    f ^= g;
    std::cout << "f = " << f << " (after &= g)" << std::endl;
    BOOST_CHECK(f == h);
}

BOOST_AUTO_TEST_CASE(operator_negate)
{
    field f1(Test::Bla);
    std::cout << "-- Testing operator!" << std::endl << "f1 = " << f1 << std::endl;
    field f2 = !f1;
    field::type expected_value{0xfffb};
    std::cout << "Expected value is " << std::hex << expected_value << std::endl
              << "f2 = " << f2 << std::endl;
    BOOST_CHECK(f2.raw_value() == expected_value);
}


//
// Created by przestaw on 28.09.2020.
//
#include <boost/test/unit_test.hpp>
#include <gpuclassifier/GPULearnColumn.hpp>

BOOST_AUTO_TEST_SUITE(nbc4gpu_TestSuite)

BOOST_AUTO_TEST_SUITE(LearnColumn_TestSuite)

BOOST_AUTO_TEST_CASE(PrecalulatedTest_Size0) {
  boost::compute::device device = boost::compute::system::default_device();
  boost::compute::context context(device);
  boost::compute::command_queue queue(context, device);

  std::vector<float> vec = {};

  nbc4gpu::GPULearnColumn<float> learner =
      nbc4gpu::GPULearnColumn<float>(vec, queue);

  BOOST_CHECK_THROW(learner(), nbc4gpu::error::ZeroValuesProvided);
}

BOOST_AUTO_TEST_CASE(PrecalulatedTest_Size6) {
  boost::compute::device device = boost::compute::system::default_device();
  boost::compute::context context(device);
  boost::compute::command_queue queue(context, device);

  std::vector<float> vec = {1, 1, 1, 2, 2, 2};

  nbc4gpu::GPULearnColumn<float> learner =
      nbc4gpu::GPULearnColumn<float>(vec, queue);
  const auto res = learner();

  BOOST_CHECK_CLOSE(res.first, 1.5, 0.1);
  BOOST_CHECK_CLOSE(res.second, 0.3, 0.1);
}

BOOST_AUTO_TEST_CASE(PrecalulatedTest_Size12) {
  boost::compute::device device = boost::compute::system::default_device();
  boost::compute::context context(device);
  boost::compute::command_queue queue(context, device);

  std::vector<float> vec = {91, 11, 1111, 32, 26, 2, 22, 894, 345};

  nbc4gpu::GPULearnColumn<float> learner =
      nbc4gpu::GPULearnColumn<float>(vec, queue);
  const auto res = learner();

  BOOST_CHECK_CLOSE(res.first, 281.55555555556, 0.1);
  BOOST_CHECK_CLOSE(res.second, 181213.77777778, 0.1);
}

BOOST_AUTO_TEST_CASE(CompareWithCpu_LargeVectorTest) {
  boost::compute::device device = boost::compute::system::default_device();
  boost::compute::context context(device);
  boost::compute::command_queue queue(context, device);

  srand(1234); // set rand seed to make test repeatable

  std::vector<float> vec(500000);
  std::generate(vec.begin(), vec.end(), []() { return rand() % 250; });

  nbc4gpu::GPULearnColumn<float> learner =
      nbc4gpu::GPULearnColumn<float>(vec, queue);
  const auto res = learner();

  float sum = 0.0;
  sum       = std::accumulate(vec.begin(), vec.end(), sum);
  float avg = sum / static_cast<float>(vec.size());

  BOOST_CHECK_CLOSE(res.first, avg, 1);

  float variance = 0;
  for (float i : vec) {
    float dif = i - avg;
    variance += dif * dif;
  }
  variance = variance / static_cast<float>(vec.size() - 1);

  BOOST_CHECK_CLOSE(res.second, variance, 1);
}

BOOST_AUTO_TEST_CASE(PrecalulatedTest_Size0_double) {
  boost::compute::device device = boost::compute::system::default_device();
  boost::compute::context context(device);
  boost::compute::command_queue queue(context, device);

  std::vector<double> vec = {};

  nbc4gpu::GPULearnColumn<double> learner =
      nbc4gpu::GPULearnColumn<double>(vec, queue);

  BOOST_CHECK_THROW(learner(), nbc4gpu::error::ZeroValuesProvided);
}

BOOST_AUTO_TEST_CASE(PrecalulatedTest_Size6_double) {
  boost::compute::device device = boost::compute::system::default_device();
  boost::compute::context context(device);
  boost::compute::command_queue queue(context, device);

  std::vector<double> vec = {1, 1, 1, 2, 2, 2};

  nbc4gpu::GPULearnColumn<double> learner =
      nbc4gpu::GPULearnColumn<double>(vec, queue);
  const auto res = learner();

  BOOST_CHECK_CLOSE(res.first, 1.5, 0.1);
  BOOST_CHECK_CLOSE(res.second, 0.3, 0.1);
}

BOOST_AUTO_TEST_CASE(PrecalulatedTest_Size12_double) {
  boost::compute::device device = boost::compute::system::default_device();
  boost::compute::context context(device);
  boost::compute::command_queue queue(context, device);

  std::vector<double> vec = {91, 11, 1111, 32, 26, 2, 22, 894, 345};

  nbc4gpu::GPULearnColumn<double> learner =
      nbc4gpu::GPULearnColumn<double>(vec, queue);
  const auto res = learner();

  BOOST_CHECK_CLOSE(res.first, 281.55555555556, 0.1);
  BOOST_CHECK_CLOSE(res.second, 181213.77777778, 0.1);
}

BOOST_AUTO_TEST_CASE(CompareWithCpu_LargeVectorTest_double) {
  boost::compute::device device = boost::compute::system::default_device();
  boost::compute::context context(device);
  boost::compute::command_queue queue(context, device);

  srand(1234); // set rand seed to make test repeatable

  std::vector<double> vec(5000000);
  std::generate(vec.begin(), vec.end(), []() { return rand() % 250; });

  nbc4gpu::GPULearnColumn<double> learner =
      nbc4gpu::GPULearnColumn<double>(vec, queue);
  const auto res = learner();

  double sum = 0.0;
  sum        = std::accumulate(vec.begin(), vec.end(), sum);
  double avg = sum / static_cast<double>(vec.size());

  BOOST_CHECK_CLOSE(res.first, avg, 1);

  double variance = 0;
  for (double i : vec) {
    double dif = i - avg;
    variance += dif * dif;
  }
  variance = variance / static_cast<double>(vec.size() - 1);

  BOOST_CHECK_CLOSE(res.second, variance, 1);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()


// Copyright 2015-2017 Peter Dimov.
//
// 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/mp11/function.hpp>
#include <boost/mp11/integral.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <type_traits>

int main()
{
    using boost::mp11::mp_and;
    using boost::mp11::mp_true;
    using boost::mp11::mp_false;
    using boost::mp11::mp_int;
    using boost::mp11::mp_size_t;

    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<>, mp_true>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_true>, mp_true>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_false>, mp_false>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_int<-7>>, mp_true>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_int<0>>, mp_false>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_size_t<7>>, mp_true>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_size_t<0>>, mp_false>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_true, mp_true>, mp_true>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_true, mp_false>, mp_false>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_false, void>, mp_false>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_int<-4>, mp_int<5>>, mp_true>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_int<-4>, mp_int<0>>, mp_false>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_int<0>, void>, mp_false>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_size_t<7>, mp_size_t<8>>, mp_true>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_size_t<7>, mp_size_t<0>>, mp_false>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_size_t<0>, void>, mp_false>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_true, mp_true, mp_true>, mp_true>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_true, mp_true, mp_false>, mp_false>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_true, mp_false, void>, mp_false>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_false, void, void>, mp_false>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_true, mp_true, mp_true, mp_true>, mp_true>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_true, mp_true, mp_true, mp_false>, mp_false>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_true, mp_true, mp_false, void>, mp_false>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_true, mp_false, void, void>, mp_false>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_false, void, void, void>, mp_false>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_int<1>, mp_int<2>, mp_int<-11>, mp_int<14>>, mp_true>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_int<1>, mp_int<0>, void, void>, mp_false>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_size_t<1>, mp_size_t<2>, mp_size_t<114>, mp_size_t<8>, mp_size_t<94>>, mp_true>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<mp_and<mp_size_t<1>, mp_size_t<2>, mp_size_t<0>, void, void>, mp_false>));

    return boost::report_errors();
}


#include <Eigen/Core>

#include <numpy_eigen/boost_python_headers.hpp>
Eigen::Matrix<boost::uint8_t, 1, 4> test_uchar_1_4(const Eigen::Matrix<boost::uint8_t, 1, 4> & M)
{
	return M;
}
void export_uchar_1_4()
{
	boost::python::def("test_uchar_1_4",test_uchar_1_4);
}


#ifndef ARRAY_3D_CONSTRUCT_DATA
#define ARRAY_3D_CONSTRUCT_DATA

#include <bredala/data_model/baseconstructdata.hpp>
#include <boost/multi_array.hpp>
#include <boost/serialization/array.hpp>
#include <bredala/data_model/multiarrayserialize.hpp>
#include <bredala/data_model/block.hpp>
#include <bredala/data_model/blockconstructdata.hpp>
#include <math.h>

// TODO :  See http://www.boost.org/doc/libs/1_46_1/libs/serialization/doc/special.html
// for serialization optimizations

namespace decaf {

//Tool function to extract the overlap between two intervale
bool hasOverlap(unsigned int baseMin, unsigned int baseMax,
                unsigned int otherMin, unsigned int otherMax,
                unsigned int & overlapMin, unsigned int & overlapMax)
{
    if(otherMax < baseMin || otherMin > baseMax )
        return false;

    overlapMin = std::max(baseMin, otherMin);
    overlapMax = std::min(baseMax, otherMax);
    return true;
}

//TODO : pack the copies if possible with boost API
template<typename T>
void copy3DArray(boost::multi_array<T, 3>* dest, Block<3>& blockDest,
                 boost::multi_array<T, 3>* src, Block<3>& blockSrc)
{
    assert(blockDest.hasLocalExtends_ && blockSrc.hasLocalExtends_);

    int offsetX = blockSrc.localExtends_[0] - blockDest.localExtends_[0];
    int offsetY = blockSrc.localExtends_[1] - blockDest.localExtends_[1];
    int offsetZ = blockSrc.localExtends_[2] - blockDest.localExtends_[2];

    assert(offsetX >= 0 && offsetY >= 0 && offsetZ >= 0);

    for(unsigned int x = 0; x < src->shape()[0]; x++)
    {
        for(unsigned int y = 0; y < src->shape()[1]; y++)
        {
            for(unsigned int z = 0; z < src->shape()[2]; z++)
                (*dest)[x + offsetX][y + offsetY][z + offsetZ] += (*src)[x][y][z];
        }
    }
}

template<typename T>
class Array3DConstructData : public BaseConstructData {
public:
    Array3DConstructData(mapConstruct map = mapConstruct(), bool bCountable = false)
        : BaseConstructData(map, bCountable), value_(NULL), isOwner_(false){}

    Array3DConstructData(boost::multi_array<T, 3> *value, Block<3> block = Block<3>(),
                           bool isOwner = false, mapConstruct map = mapConstruct(), bool bCountable = false) :
        BaseConstructData(map, bCountable), value_(value), block_(block), isOwner_(isOwner)
    {
        // Nothing to do
    }

    virtual ~Array3DConstructData()
    {
        if(isOwner_)
            delete value_;
    }

    virtual bool isBlockSplitable(){ return true; }

    void setBlock(Block<3> block){ block_ = block; }

    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        boost::serialization::base_object<BaseConstructData>(*this);
        ar & BOOST_SERIALIZATION_NVP(value_);
        ar & BOOST_SERIALIZATION_NVP(block_);
    }

    boost::multi_array<T, 3>* getArray(){ return value_; }

    Block<3>& getBlock(){ return block_; }

    virtual int getNbItems(){ return value_->shape()[0] * value_->shape()[1] * value_->shape()[2]; }

	virtual std::string getTypename(){ return std::string("Array3D_" + boost::typeindex::type_id<T>().pretty_name()); }

    virtual bool appendItem(std::shared_ptr<BaseConstructData> dest, unsigned int index, ConstructTypeMergePolicy = DECAF_MERGE_DEFAULT)
    {
	std::cout<<"ERROR : appendItem not implemented yet for 3d arrays."<<std::endl;
	return false;
    }

    virtual void preallocMultiple(int nbCopies , int nbItems, std::vector<std::shared_ptr<BaseConstructData> >& result)
    {
        // TODO : that's bullshit, redundant with the info from boost
        Block<3> block;
        std::vector<unsigned int> extends = {0, 0, 0, 1, 1, 1};
        std::vector<float> box = {0.0, 0.0, 0.0, 1.0, 1.0, 1.0};
        block.setGhostSizes(0);
        block.setGlobalBBox(box);
        block.setLocalBBox(box);
        block.setOwnBBox(box);
        block.setGlobalExtends(extends);
        block.setLocalExtends(extends);
        block.setOwnExtends(extends);
        for(int i = 0; i < nbCopies; i++)
        {
            //Creating the new array with the correct shape
            boost::multi_array<T, 3> *newArray = new boost::multi_array<T, 3>(boost::extents[1][1][1]);

            result.push_back(
                        std::make_shared<Array3DConstructData<T> >(
                                newArray,
                                block,
                                true,
                                mapConstruct(),
                                bCountable_)
                        );

        }

        return;
    }

    virtual std::vector<std::shared_ptr<BaseConstructData> > split(
            const std::vector<int>& range,
            std::vector< mapConstruct >& partial_map,
            ConstructTypeSplitPolicy policy = DECAF_SPLIT_DEFAULT )
    {
        std::vector<std::shared_ptr<BaseConstructData> > result;
        std::cout<<"ERROR : the spliting of a 3-dimension array is not implemted yet."<<std::endl;
        return result;
    }

    virtual std::vector<std::shared_ptr<BaseConstructData> > split(
            const std::vector< std::vector<int> >& range,
            std::vector< mapConstruct >& partial_map,
            ConstructTypeSplitPolicy policy = DECAF_SPLIT_DEFAULT )
    {
        std::vector<std::shared_ptr<BaseConstructData> > result;
        std::cout<<"ERROR : the spliting of a n-dimension array is not implemted yet."<<std::endl;
        return result;
    }

    virtual void split(
            const std::vector<int>& range,
            std::vector< mapConstruct >& partial_map,
            std::vector<std::shared_ptr<BaseConstructData> >& fields,
            ConstructTypeSplitPolicy policy = DECAF_SPLIT_DEFAULT)
    {
        std::vector<std::shared_ptr<BaseConstructData> > result;
        std::cout<<"ERROR : the spliting of a n-dimension array with buffers is not implemented yet."<<std::endl;
        return ;
    }

    virtual std::vector<std::shared_ptr<BaseConstructData> > split(
            const std::vector< Block<3> >& range,
            std::vector< mapConstruct >& partial_map,
            ConstructTypeSplitPolicy policy = DECAF_SPLIT_DEFAULT)
    {
        std::vector<std::shared_ptr<BaseConstructData> > result;

        for(unsigned int i = 0; i < range.size(); i++)
        {

            if(!range.at(i).hasLocalExtends_)
            {
                std::cerr<<"ERROR : the block "<<i<<" doesn't have a local box to split a 3d array."<<std::endl;
                return result;
            }

            if(!block_.hasLocalExtends_)
            {
                std::cerr<<"ERROR : the block of the 3d array doesn't have informations about the local grid"<<std::endl;
                return result;
            }
        }

        // Naive method to write the subarray
        unsigned int startX, endX, startY, endY, startZ, endZ;
        startX = block_.localExtends_[0];
        endX = block_.localExtends_[0] + block_.localExtends_[3];
        startY = block_.localExtends_[1];
        endY = block_.localExtends_[1] + block_.localExtends_[4];
        startZ = block_.localExtends_[2];
        endZ = block_.localExtends_[2] + block_.localExtends_[5];

        for(unsigned int i = 0; i < range.size(); i++)
        {
            //Computing the window of data to cut for each dimension
            unsigned int startSplitX, endSplitX, startSplitY, endSplitY, startSplitZ, endSplitZ;
            startSplitX = range.at(i).localExtends_[0];
            endSplitX = range.at(i).localExtends_[0] + range.at(i).localExtends_[3];
            startSplitY = range.at(i).localExtends_[1];
            endSplitY = range.at(i).localExtends_[1] + range.at(i).localExtends_[4];
            startSplitZ = range.at(i).localExtends_[2];
            endSplitZ = range.at(i).localExtends_[2] + range.at(i).localExtends_[5];

            unsigned int xmin, xmax, ymin, ymax, zmin, zmax;
            bool overlapX = hasOverlap(startX, endX, startSplitX, endSplitX, xmin, xmax);
            bool overlapY = hasOverlap(startY, endY, startSplitY, endSplitY, ymin, ymax);
            bool overlapZ = hasOverlap(startZ, endZ, startSplitZ, endSplitZ, zmin, zmax);
            unsigned int dx = xmax - xmin, dy = ymax - ymin, dz = zmax - zmin;

            if(overlapX && overlapY && overlapZ)
            {
                //There is an intersection between the blocks
                boost::multi_array<T, 3>* subArray = new boost::multi_array<T, 3>(boost::extents[dx][dy][dz]);
                for(int x = xmin; x < xmax; x++)
                {
                    for(int y = ymin; y < ymax; y++)
                    {
                        for(int z = zmin; z < zmax; z++)
                        {
                            (*subArray)[x-xmin][y-ymin][z-zmin] =
                                    (*value_)[x-startX][y-startY][z-startZ];
                        }
                    }
                }

                Block<3> subBlock = block_;

                std::vector<unsigned int> localExtends = {xmin, ymin, zmin, xmax - xmin, ymax - ymin, zmax - zmin};
                std::vector<float> localBBox = { subBlock.globalBBox_[0] + xmin * subBlock.gridspace_,
                                                 subBlock.globalBBox_[1] + ymin * subBlock.gridspace_,
                                                 subBlock.globalBBox_[2] + zmin * subBlock.gridspace_,
                                                 (xmax - xmin) * subBlock.gridspace_,
                                                 (ymax - ymin) * subBlock.gridspace_,
                                                 (zmax - zmin) * subBlock.gridspace_};
                subBlock.setLocalExtends(localExtends);
                subBlock.setLocalBBox(localBBox);

                // Updating the own information as well
                if(range.at(i).hasOwnExtends_)
                {
                    startSplitX = range.at(i).ownExtends_[0];
                    endSplitX = range.at(i).ownExtends_[0] + range.at(i).ownExtends_[3];
                    startSplitY = range.at(i).ownExtends_[1];
                    endSplitY = range.at(i).ownExtends_[1] + range.at(i).ownExtends_[4];
                    startSplitZ = range.at(i).ownExtends_[2];
                    endSplitZ = range.at(i).ownExtends_[2] + range.at(i).ownExtends_[5];

                    overlapX = hasOverlap(startX, endX, startSplitX, endSplitX, xmin, xmax);
                    overlapY = hasOverlap(startY, endY, startSplitY, endSplitY, ymin, ymax);
                    overlapZ = hasOverlap(startZ, endZ, startSplitZ, endSplitZ, zmin, zmax);

                    if(overlapX && overlapY && overlapZ)
                    {
                        subBlock.ownExtends_ = {xmin, ymin, zmin, xmax - xmin, ymax - ymin, zmax - zmin};
                        subBlock.ownBBox_ = { subBlock.globalBBox_[0] + xmin * subBlock.gridspace_,
                                                subBlock.globalBBox_[1] + ymin * subBlock.gridspace_,
                                                subBlock.globalBBox_[2] + zmin * subBlock.gridspace_,
                                                (xmax - xmin) * subBlock.gridspace_,
                                                (ymax - ymin) * subBlock.gridspace_,
                                                (zmax - zmin) * subBlock.gridspace_};

                        if(subBlock.localExtends_ == subBlock.ownExtends_)
                            subBlock.ghostSize_ = 0;
                        else
                        {
                            subBlock.ghostSize_ = std::max(subBlock.ownExtends_[0] - subBlock.localExtends_[0],
                                     subBlock.ownExtends_[1] - subBlock.localExtends_[1]);
                            subBlock.ghostSize_ = std::max(subBlock.ghostSize_,
                                     subBlock.ownExtends_[2] - subBlock.localExtends_[2]);
                            subBlock.ghostSize_ = std::max(subBlock.ghostSize_,
                                     subBlock.localExtends_[0] - subBlock.ownExtends_[0]);
                            subBlock.ghostSize_ = std::max(subBlock.ghostSize_,
                                     subBlock.localExtends_[1] - subBlock.ownExtends_[1]);
                            subBlock.ghostSize_ = std::max(subBlock.ghostSize_,
                                     subBlock.localExtends_[2] - subBlock.ownExtends_[2]);
                        }

                    }
                }
                else
                {
                    subBlock.ghostSize_ = 0;
                    subBlock.setOwnExtends(subBlock.localExtends_);
                    subBlock.setOwnBBox(subBlock.localBBox_);

                }
                std::shared_ptr<Array3DConstructData<T> > data = std::make_shared<Array3DConstructData<T> >(
                            subArray, subBlock, true);

                result.push_back(data);

            }
            else
            {
                //No data here
                boost::multi_array<T, 3> *subArray = new boost::multi_array<T, 3>(boost::extents[0][0][0]);
                Block<3> subBlock = block_;

                subBlock.localExtends_ = {0, 0, 0, 0, 0, 0};
                subBlock.localBBox_ = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
                subBlock.ownExtends_ = subBlock.localExtends_;
                subBlock.ownBBox_ = subBlock.localBBox_;
                subBlock.ghostSize_ = 0;

                std::shared_ptr<Array3DConstructData<T> > data = std::make_shared<Array3DConstructData<T> >(
                            subArray, subBlock, true);

                result.push_back(data);
            }

        }

        return result;
    }

    virtual void split(
            const std::vector< std::vector<int> >& range,
	    std::vector< mapConstruct >& partial_map,
            std::vector<std::shared_ptr<BaseConstructData> >& fields,
            ConstructTypeSplitPolicy policy = DECAF_SPLIT_DEFAULT)
    {
        std::cerr<<"Hello function not implemented."<<std::endl;
	return;
    }

    virtual bool merge( std::shared_ptr<BaseConstructData> other,
                        mapConstruct partial_map,
                        ConstructTypeMergePolicy policy = DECAF_MERGE_DEFAULT)
    {
        switch(policy)
        {
            case DECAF_MERGE_DEFAULT:
            {
                //Getting the shape of the merged array
                /*std::map<std::string, datafield>::iterator field = partial_map->find("domain_block");
                if(field == partial_map->end())
                {
                    std::cerr<<"ERROR : unable to find the field \'domain_block\' "
                             <<"required to merge array3dconstructdata type."<<std::endl;
                    return false;
                }
                std::shared_ptr<BlockConstructData> domainBlock =
                        std::dynamic_pointer_cast<BlockConstructData>(std::get<3>(field->second));

                Block<3>& domain = domainBlock->getData();
                */
                std::shared_ptr<Array3DConstructData<T> >other_ =
                        std::dynamic_pointer_cast<Array3DConstructData<T> >(other);
                //std::cerr<<"Extends of the domain block : "<<std::endl;
                //domain.printExtends();

                // Checking if the current array has the correct shape
                if(block_.doExtendsInclude(other_->block_))
                {
                    // The block has already the correct shape, we simply add the
                    // values from the other array
                    copy3DArray(value_, block_, other_->value_, other_->block_);
                }
                else
                {
                    //Merging the two domain
                    Block<3> unionBlock;
                    block_.makeExtendsUnion(other_->block_, unionBlock);

                    //Creating the new array with the correct shape
                    boost::multi_array<T, 3> *newArray = new boost::multi_array<T, 3>(boost::extents[unionBlock.localExtends_[3]][unionBlock.localExtends_[4]][unionBlock.localExtends_[5]]);

                    //Adding the current array
                    copy3DArray(newArray, unionBlock, value_, block_);

                    //Adding the other array
                    copy3DArray(newArray, unionBlock, other_->value_, other_->block_);

                    //We can now replace the old array
                    if(isOwner_) delete value_;
                    value_ = newArray;
                    block_ = unionBlock;
                    isOwner_ = true;
                }

                break;
            }
            default:
            {
                std::cerr<<"ERROR : Policy "<<policy<<" not supported for Array3constructData."<<std::endl;
                return false;
            }

        }

        return true;
    }

    virtual bool merge(std::vector<std::shared_ptr<BaseConstructData> >& others,
                       mapConstruct partial_map,
                       ConstructTypeMergePolicy policy = DECAF_MERGE_DEFAULT)
    {
	std::cout<<"ERROR : merge multiples partial data is not implemented in array3d."<<std::endl;
	return false;
    }

    virtual bool canMerge(std::shared_ptr<BaseConstructData> other)
    {
        std::shared_ptr<Array3DConstructData<T> >other_ = std::dynamic_pointer_cast<Array3DConstructData<T> >(other);
        if(!other_)
        {
            std::cout<<"ERROR : trying to merge two objects with different types"<<std::endl;
            return false;
        }
        return true;
    }

    virtual void softClean()
    {
	std::cout<<"ERROR : soft clean not implemented yet for 3d array."<<std::endl;
	return;
    }

protected:
    boost::multi_array<T, 3>* value_;
    Block<3> block_;
    bool isOwner_;
};

} // namespace

#endif


#pragma once

#include <vector>
#include <stdexcept>
#include <iostream>
#include <string>
#include <boost/functional/hash.hpp>

namespace std {
template<typename num_t>
inline ostream & operator<<(ostream & os, vector<num_t> const & v)
{
    string sep = "";
    os << "(";
    for (auto e : v) {
        os << sep << e;
        sep = ", ";
    }
    os << ")";
    return os;
}
}

struct Vector2i {
    int x, y;
    inline int & operator[](size_t i) {
        switch (i) {
        case 0:
            return x;
        case 1:
            return y;
        default:
            throw std::out_of_range{"index must be at most 1"};
        }
    }
    inline int const & operator[](size_t i) const {
        switch (i) {
        case 0:
            return x;
        case 1:
            return y;
        default:
            throw std::out_of_range{"index must be at most 1"};
        }
    }
    inline constexpr size_t size() const noexcept { return 2; }
};
inline bool operator==(Vector2i const & lhs, Vector2i const & rhs)
{
    return lhs.x == rhs.x && lhs.y == rhs.y;
}
namespace std
{
template<> struct hash<Vector2i>
{
    std::size_t operator()(Vector2i const & v) const noexcept
    {
        std::size_t seed = 0;
        boost::hash_combine(seed, v.x);
        boost::hash_combine(seed, v.y);
        return seed;
    }
};
inline ostream & operator<<(ostream & os, Vector2i const & v)
{
    os << "(" << v.x << ", " << v.y << ")";
    return os;
}
}

struct Vector3d {
    double x, y, z;
    inline double & operator[](size_t i)
    {
        switch (i) {
        case 0:
            return x;
        case 1:
            return y;
        case 2:
            return z;
        default:
            throw std::out_of_range{"index must be at most 2"};
        }
    }
    inline double const & operator[](size_t i) const
    {
        switch (i) {
        case 0:
            return x;
        case 1:
            return y;
        case 2:
            return z;
        default:
            throw std::out_of_range{"index must be at most 2"};
        }
    }
    inline constexpr size_t size() const noexcept { return 3; }
};
inline bool operator==(Vector3d const & lhs, Vector3d const & rhs)
{
    return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z;
}
namespace std
{
template<> struct hash<Vector3d>
{
    std::size_t operator()(Vector3d const & v) const noexcept
    {
        std::size_t seed = 0;
        boost::hash_combine(seed, v.x);
        boost::hash_combine(seed, v.y);
        boost::hash_combine(seed, v.z);
        return seed;
    }
};
inline ostream & operator<<(ostream & os, Vector3d const & v)
{
    os << "(" << v.x << ", " << v.y << ", " << v.z << ")";
    return os;
}
}


#include <iostream>
#include <cmath>
#include <typeinfo>
#include <type_traits>
#include <boost/numeric/mtl/mtl.hpp>

// namespace to avoid ambiguities with mtl
namespace tst {

    template <typename T>
    struct is_const
    {
	static const bool value= false;
    };

    template <typename T>
    struct is_const<const T>
    {
	static const bool value= true;
    };

    template <bool Condition, typename ThenType, typename ElseType>
    struct conditional
    {
	using type= ThenType;
    };

    template <typename ThenType, typename ElseType>
    struct conditional<false, ThenType, ElseType>
    {
	using type= ElseType;
    };

    template <bool Condition, typename ThenType, typename ElseType>
    using conditional_t= typename conditional<Condition, ThenType, ElseType>::type;

    template <typename T>
    struct is_matrix
      : std::false_type
    {};

    template <typename Value, typename Para>
    struct is_matrix<mtl::dense2D<Value, Para> >
      : std::true_type
    {};

#if 1
    template <typename T>
    struct is_matrix<const T>
      : is_matrix<T> {};
#endif

#if 0
    template <typename Matrix>
    class transposed_view
    {
      public:
	using value_type= typename Matrix::value_type;
	using size_type=  typename Matrix::size_type;

	// typedef typename mtl::Collection<Matrix>::value_type  value_type;
	// typedef typename mtl::Collection<Matrix>::size_type   size_type;

	explicit transposed_view(Matrix& A) : ref(A) {}

	value_type& operator()(size_type r, size_type c) { return ref(c, r); }
	const value_type& operator()(size_type r, size_type c) const { return ref(c, r); }

      private:
	Matrix& ref;
    };

    template <typename Matrix>
    class const_transposed_view
    {
      public:
	using value_type= typename Matrix::value_type;
	using size_type=  typename Matrix::size_type;

	explicit const_transposed_view(const Matrix& A) : ref(A) {}

	const value_type& operator()(size_type r, size_type c) const { return ref(c, r); }

      private:
	const Matrix& ref;
    };

    template <typename Matrix>
    inline const_transposed_view<Matrix> trans(const Matrix& A)
    {
	return const_transposed_view<Matrix>(A);
    }

#else
    template <typename Matrix>
    class transposed_view
    {
	static_assert(is_matrix<Matrix>::value, "template argument is not a Matrix");
      public:
	using value_type= typename Matrix::value_type;
	using size_type=  typename Matrix::size_type;

      private:
	using vref_type= conditional_t<is_const<Matrix>::value,
				       const value_type&,
				       value_type&>;
      public:
	explicit transposed_view(Matrix& A) : ref(A) {}

	vref_type operator()(size_type r, size_type c) { return ref(c, r); }
	const value_type& operator()(size_type r, size_type c) const { return ref(c, r); }

      private:
	Matrix& ref;
    };
#endif

    template <typename Matrix>
    transposed_view<Matrix> inline trans(Matrix& A)
    {
	return transposed_view<Matrix>(A);
    }

    template <typename Matrix>
    struct is_matrix<transposed_view<Matrix> >
      : is_matrix<Matrix>
    {};

}

template <int M>
void f()
{
    typedef typename tst::conditional<M < 100, double, float>::type & value_type;
    typedef const typename tst::conditional<M < 100, const double, const float>::type const_value_type;
    std::cout << "typeid = " << typeid(value_type).name() << '\n';
}

int main (int argc, char* argv[])
{
    // const double eps= 0.00000001;
    const int n = 10;
    typedef tst::conditional<n < 100, double, float>::type& value_type;
    typedef const tst::conditional<n < 100, double, float>::type const_value_type;
    std::cout << "typeid = " << typeid(value_type).name() << '\n';
    f<17>();

    mtl::dense2D<float> A= {{2, 3, 4},
			    {5, 6, 7},
			    {8, 9, 10}};
    std::cout << "trans(A) is\n" << trans(A);

    trans(A)[2][0]= 4.5;
    std::cout << "trans(A) is\n" << trans(A);

    const mtl::dense2D<float> B(A);
    std::cout << "trans(B) is\n" << trans(B);

    tst::transposed_view<mtl::dense2D<float> >  At(A);
    At(2, 0)= 4.5;

    std::cout << "tst::trans(A)(2, 0) = " << tst::trans(A)(2, 0) << '\n';
    tst::trans(A)(2, 0)= 4.6;
    std::cout << "trans(A) after modification is\n" << trans(A);

    std::cout << "typeid of trans(A) = " << typeid(tst::trans(A)).name() << '\n';
    std::cout << "typeid of trans(B) = " << typeid(tst::trans(B)).name() << '\n';
    // int ta= trans(A);
    // int tb= trans(B);
    // int tar= trans(A).ref;
    // int tbr= trans(B).ref;

    std::cout << "tst::trans(B)(2, 0) = " << tst::trans(B)(2, 0) << '\n';
    // tst::trans(B)(2, 0)= 4.6;

    const tst::transposed_view<const mtl::dense2D<float> >  Bt(B);
    std::cout << "Bt(2, 0) = " << Bt(2, 0) << '\n';

    return 0 ;
}


// -----------------------------------------------------------------------
// RTToolbox - DKFZ radiotherapy quantitative evaluation library
//
// Copyright (c) German Cancer Research Center (DKFZ),
// Software development for Integrated Diagnostics and Therapy (SIDT).
// ALL RIGHTS RESERVED.
// See rttbCopyright.txt or
// http://www.dkfz.de/en/sidt/projects/rttb/copyright.html
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE.  See the above copyright notices for more information.
//
//------------------------------------------------------------------------

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

#include "litCheckMacros.h"

#include "rttbBaseType.h"
#include "rttbBaseTypeModels.h"
#include "rttbBioModel.h"
#include "rttbBioModelScatterPlots.h"
#include "../core/DummyDVHGenerator.h"
#include "DummyModel.h"
#include "rttbIntegration.h"

#include <istream>
#include <sstream>
#include <fstream>

namespace rttb
{
	namespace testing
	{

		typedef models::ScatterPlotType ScatterPlotType;

		/*! @brief BioModelScatterPlotTest. Test the scatterplot methods.
		1) test if parameters are set correctly
		2) test if scatterData contain each model value exactly once
		*/
		int BioModelScatterPlotTest(int argc, char* argv[])
		{
			PREPARE_DEFAULT_TEST_REPORTING;

			//generate artificial DVH and corresponding statistical values
			DummyDVHGenerator dummyDVH;

			const IDType structureID = "myStructure";
			const IDType doseID = "myDose";

			core::DVH::Pointer dvhPtr = boost::make_shared<core::DVH>(dummyDVH.generateDVH(structureID,
			                               doseID, 0, 2000));

			//test Dummy Model
			models::BioModelParamType param1 = 0.35;
			models::BioModelParamType param2 = 0.023333333333333;
			models::BioModelParamType param3 = 10000000;

			//model values will be dose/normalisationDose
			DoseTypeGy normalisationDose = 10;

			rttb::models::DummyModel model(dvhPtr);
			model.resetCounters();

			//default number of points is 100
			CHECK_NO_THROW(models::getScatterPlotVary1Parameter(model, 0, param1, 0.5, normalisationDose));
			model.resetCounters();
			ScatterPlotType scatter = models::getScatterPlotVary1Parameter(model, 0, param1, 0.5,
			                          normalisationDose);
			//only 1st parameter should gave been changed
			CHECK_EQUAL(100, model.getSetParam1Count());
			CHECK_EQUAL(0, model.getSetParam2Count());
			CHECK_EQUAL(0, model.getSetParam3Count());
			CHECK_EQUAL(100, model.getCalcCount());

			// all model values should match the corresponding dose/normalizationDose
			ScatterPlotType::iterator iter;

			for (iter = scatter.begin(); iter != scatter.end(); ++iter)
			{
				double currentKey = iter->first;

				if ((currentKey / normalisationDose) != iter->second.first)
				{
					CHECK_EQUAL((currentKey / normalisationDose), iter->second.first);
				}
			}

			model.resetCounters();

			//number of points is 50
			CHECK_NO_THROW(models::getScatterPlotVary1Parameter(model, 1, param2, 0.25, normalisationDose, 50));
			model.resetCounters();
			scatter = models::getScatterPlotVary1Parameter(model, 1, param2, 0.25, normalisationDose, 50);
			//only 1st parameter should gave been changed
			CHECK_EQUAL(0, model.getSetParam1Count());
			CHECK_EQUAL(50, model.getSetParam2Count());
			CHECK_EQUAL(0, model.getSetParam3Count());
			CHECK_EQUAL(50, model.getCalcCount());

			// all model values should match the corresponding dose/normalizationDose
			for (iter = scatter.begin(); iter != scatter.end(); ++iter)
			{
				double currentKey = iter->first;

				if ((currentKey / normalisationDose) != iter->second.first)
				{
					CHECK_EQUAL((currentKey / normalisationDose), iter->second.first);
				}
			}

			model.resetCounters();

			//number of points is 150
			CHECK_NO_THROW(models::getScatterPlotVary1Parameter(model, 2, param3, 0.75, normalisationDose,
			               150));
			model.resetCounters();
			scatter = models::getScatterPlotVary1Parameter(model, 2, param3, 0.75, normalisationDose, 150);
			//only 1st parameter should gave been changed
			CHECK_EQUAL(0, model.getSetParam1Count());
			CHECK_EQUAL(0, model.getSetParam2Count());
			CHECK_EQUAL(150, model.getSetParam3Count());
			CHECK_EQUAL(150, model.getCalcCount());

			// all model values should match the corresponding dose/normalizationDose
			for (iter = scatter.begin(); iter != scatter.end(); ++iter)
			{
				double currentKey = iter->first;

				if ((currentKey / normalisationDose) != iter->second.first)
				{
					CHECK_EQUAL((currentKey / normalisationDose), iter->second.first);
				}
			}

			model.resetCounters();
			std::vector<int> paramIDVec;
			models::BioModel::ParamVectorType meanVec;
			models::BioModel::ParamVectorType varianceVec;

			paramIDVec.push_back(0);
			meanVec.push_back(1);
			varianceVec.push_back(0.6);
			paramIDVec.push_back(1);
			meanVec.push_back(10);
			varianceVec.push_back(0.5);
			paramIDVec.push_back(2);
			meanVec.push_back(100);
			varianceVec.push_back(0.4);

			//number of points is 50
			CHECK_NO_THROW(models::getScatterPlotVaryParameters(model, paramIDVec, meanVec, varianceVec,
			               normalisationDose, 50));
			model.resetCounters();
			scatter = models::getScatterPlotVaryParameters(model, paramIDVec, meanVec, varianceVec,
			          normalisationDose, 50);
			//only 1st parameter should gave been changed
			CHECK_EQUAL(50, model.getSetParam1Count());
			CHECK_EQUAL(50, model.getSetParam2Count());
			CHECK_EQUAL(50, model.getSetParam3Count());
			CHECK_EQUAL(50, model.getCalcCount());

			// all model values should match the corresponding dose/normalizationDose
			for (iter = scatter.begin(); iter != scatter.end(); ++iter)
			{
				double currentKey = iter->first;

				if ((currentKey / normalisationDose) != iter->second.first)
				{
					CHECK_EQUAL((currentKey / normalisationDose), iter->second.first);
				}
			}

			model.resetCounters();

			//test 2000 points
			CHECK_NO_THROW(models::getScatterPlotVaryParameters(model, paramIDVec, meanVec, varianceVec,
			               normalisationDose, 10000));

			//test iterativeIntegration
			/*std::string bedFileName="d:/Temp/bed.txt";
			std::ifstream dvh_ifstr(bedFileName,std::ios::in);
			std::vector<DoseCalcType> volumeV2;
			std::vector<DoseTypeGy> bedV2;

			//std::cout  << "iterative integration"<< std::endl;
			if ( !dvh_ifstr.is_open() )
			{
				std::cerr<< "DVH file name invalid: could not open the dvh file!"<<std::endl;
			}
			else {
				while(!dvh_ifstr.eof())
				{
					std::string line;
					std::getline(dvh_ifstr,line);
					std::size_t found1=line.find("(");
					std::size_t found2=line.find(",");
					std::size_t found3=line.find(");");
					if(found1!= std::string::npos && found2!=std::string::npos && found3!=std::string::npos){
						std::string lineSub1 = line.substr(found1+1,found2-found1-1);
						std::stringstream ss(lineSub1);
						double volume;
						ss >> volume;
						volumeV2.push_back(volume);
						//std::cout  << "("<< volume << ",";

						std::string lineSub2 = line.substr(found2+1,found3-found2-1);
						//std::cout << lineSub2 << std::endl;
						std::stringstream ss2(lineSub2);
						double bed;
						ss2 >> bed;
						bedV2.push_back(bed);
						//std::cout  << bed << ");";

					}

				}

			}

			struct rttb::models::TcpParams params={0.302101,0.08,10000000,volumeV2,bedV2};

			double result = rttb::models::integrateTCP(0, params);*/

			RETURN_AND_REPORT_TEST_SUCCESS;

		}

	}//testing
}//rttb


/*!
@file
Defines `boost::hana::detail::minimal::Monoid`.

@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#ifndef BOOST_HANA_DETAIL_MINIMAL_MONOID_HPP
#define BOOST_HANA_DETAIL_MINIMAL_MONOID_HPP

#include <boost/hana/comparable/equal_mcd.hpp>
#include <boost/hana/core/datatype.hpp>
#include <boost/hana/monoid/mcd.hpp>

namespace boost { namespace hana {
namespace detail { namespace minimal {
    template <typename mcd = hana::Monoid::mcd>
    struct Monoid { };

    template <typename mcd, typename = operators<hana::Monoid, hana::Comparable>>
    struct monoid_impl {
        int value;
        using hana_datatype = Monoid<mcd>;
    };

    template <typename mcd>
    struct make_monoid_impl {
        constexpr auto operator()(int i) const
        { return monoid_impl<mcd>{i}; }
    };

    template <typename mcd = hana::Monoid::mcd>
    constexpr make_monoid_impl<mcd> monoid{};
}} // end namespace detail::minimal

// Provided for convenience in the unit tests.
template <typename mcd>
struct Comparable::instance<
    detail::minimal::Monoid<mcd>, detail::minimal::Monoid<mcd>
> : Comparable::equal_mcd
{
    static constexpr bool equal_impl(
        detail::minimal::monoid_impl<mcd> x,
        detail::minimal::monoid_impl<mcd> y
    )
    { return x.value == y.value; }
};

template <>
struct Monoid::instance<
    detail::minimal::Monoid<Monoid::mcd>,
    detail::minimal::Monoid<Monoid::mcd>
> : Monoid::mcd
{
    static constexpr auto plus_impl(
        detail::minimal::monoid_impl<Monoid::mcd> x,
        detail::minimal::monoid_impl<Monoid::mcd> y
    )
    { return detail::minimal::monoid<Monoid::mcd>(x.value + y.value); }

    static constexpr auto zero_impl()
    { return detail::minimal::monoid<Monoid::mcd>(0); }
};
}} // end namespace boost::hana

#endif // !BOOST_HANA_DETAIL_MINIMAL_MONOID_HPP


//==================================================================================================
/*!

  Copyright 2016 NumScale SAS

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

#include <ns.bench.hpp>
#include <boost/config.hpp>
#include <boost/simd/pack.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>

namespace ns { namespace bench {

template <typename T, std::size_t N>
struct format_type<boost::simd::pack<T, N>>
{
  static std::string to_string()
  {
    std::stringstream ss;

    ss << "pack<" << format_type<T>::to_string() << ", " << N << ">";
    return ss.str();
  }
};

} }

ns::bench::setup setup()
{
  namespace nsb = ns::bench;
  return nsb::setup()
     .median(nsb::units::cpe_)
     .really_during(2.)
   ;
}

ns::bench::results& results()
{
  static ns::bench::results r;
  return r;
}

void print_results()
{
  std::cout << results() << std::endl;
}

template <std::size_t N, typename F, typename... Args>
void run_bench(std::string const& name, F f, Args&&... args)
{
  namespace nsb = ns::bench;
  nsb::run(results(), setup(), name, nsb::make_sized_function(N, f), std::forward<Args>(args)...);
}

template <std::size_t N, typename F, typename... Args>
void run_bench(const char* name, F f, Args&&... args)
{
  run_bench<N>(std::string(name), f, std::forward<Args>(args)...);
}

template <std::size_t N, typename F, typename... Args>
void run_bench(F f, Args&&... args)
{
  namespace nsb = ns::bench;
  run_bench<N>(nsb::type_id<F>(), f, std::forward<Args>(args)...);
}

namespace nsb = ns::bench;

template <typename Experiment, typename Type, std::size_t ElementSize>
struct bench_experiment : ns::bench::experiment
{
  //using type = typename Experiment::type;
  using type = Type;

  void which_type()
  {
    std::cout << ":: [T = " << nsb::type_id<type>() << "]" << std::endl;
  }

  template <typename U>
  void operator()(U min0, U max0)
  {
    which_type();
    run_bench<ElementSize>
      ( Experiment::name()
      , Experiment::functor()
      , nsb::generators::rand<type>(min0, max0)
      );
  }

  template <typename U>
  void operator()(U min0, U max0, U min1, U max1)
  {
    which_type();
    run_bench<ElementSize>
      ( Experiment::name()
      , Experiment::functor()
      , nsb::generators::rand<type>(min0, max0)
      , nsb::generators::rand<type>(min1, max1)
      );
  }

  template <typename U>
  void operator()(U min0, U max0, U min1, U max1, U min2, U max2)
  {
    which_type();
    run_bench<ElementSize>
      ( Experiment::name()
      , Experiment::functor()
      , nsb::generators::rand<type>(min0, max0)
      , nsb::generators::rand<type>(min1, max1)
      , nsb::generators::rand<type>(min2, max2)
      );
  }
};

void describe()
{
  std::cout << "::- --------------------------------------------------------------------------------------------------------------------------------------------------";
  std::cout << std::endl;
  std::cout << "::- Compiler: " << BOOST_COMPILER << std::endl;
  std::cout << "::- Platform: " << BOOST_PLATFORM << std::endl;
  std::cout << "::- SIMD:     " << nsb::type_id<BOOST_SIMD_DEFAULT_SITE>() << std::endl;
  std::cout << "::- --------------------------------------------------------------------------------------------------------------------------------------------------";
  std::cout << std::endl;
}

template <typename T>                            struct template_of;
template <template <class> class Tp, typename T> struct template_of<Tp<T>> { using type = T; };
template <typename T>
using template_of_t = typename template_of<T>::type;

template <typename Experiment>
using simd_experiment =
  bench_experiment< Experiment
                  , boost::simd::pack<template_of_t<Experiment>>
                  , boost::simd::pack<template_of_t<Experiment>>::static_size
                  >;

template <typename Experiment>
using scalar_experiment =
  bench_experiment< Experiment
                  , template_of_t<Experiment>
                  , 1
                  >;

#define DEFINE_BENCH(name_, f, experiment)                                                         \
  template <typename T>                                                                            \
  struct name_ : experiment<name_<T>>                                                              \
  {                                                                                \
    static const char* name()    { return BOOST_PP_STRINGIZE(name_); }                             \
    static decltype(f) functor() { return f; }                                                     \
  }                                                                                \
/**/

#define DEFINE_SIMD_BENCH(name, f)   DEFINE_BENCH(name, f, simd_experiment)
#define DEFINE_SCALAR_BENCH(name, f) DEFINE_BENCH(name, f, scalar_experiment)

#define DEFINE_BENCH_MAIN()                                                                        \
  void main2();                                                                                \
  int main(int argc, char** argv)                                                                  \
  {                                                                                \
    nsb::parse_args(argc, argv);                                                                   \
    main2();                                                                                \
    describe();                                                                                \
    print_results();                                                                               \
    return 0;                                                                                \
  }                                                                                \
  void main2()                                                                                \
/**/

#endif


#ifndef __Rocket_Flight_DM_HH__
#define __Rocket_Flight_DM_HH__
/********************************* TRICK HEADER *******************************
PURPOSE:
      (Describe the Rocket Flgiht Dynamics Module Variables and Algorithm)
LIBRARY DEPENDENCY:
      ((../src/Rocket_Flight_DM.cpp)
       (../../math/src/nrutil.cpp))
PROGRAMMERS:
      (((Lai Jun Xu) () () () ))
*******************************************************************************/
#include <armadillo>
#include "Module.hh"
#include "Time_management.hh"
#include "aux.hh"
#include "global_constants.hh"
#include "icf_trx_ctrl.h"
#include "simgen_remote.h"

class Rocket_Flight_DM : public Dynamics {
  TRICK_INTERFACE(Rocket_Flight_DM);

 public:
  Rocket_Flight_DM(Data_exchang &input);
  Rocket_Flight_DM(const Rocket_Flight_DM &other);
  Rocket_Flight_DM &operator=(const Rocket_Flight_DM &other);
  struct icf_ctrlblk_t *dm_icf_info_hook;
  int enqueue_to_simgen_buffer(struct icf_ctrlblk_t *C, double ext_porlation);
  int stand_still_motion_data(struct icf_ctrlblk_t *C, double ext_porlation);

  virtual void init();
  virtual void algorithm(double int_step);
  void load_location(double lonx_in, double latx_in, double alt_in);
  void load_geodetic_velocity(double alpha0x, double beta0x, double dvbe);
  void load_angle(double yaw, double roll, double pitch);
  void load_angular_velocity(double ppx_in, double qqx_in, double rrx_in);
  void update_diagnostic_attributes(double int_step);
  void Interpolation_Extrapolation(double T, double int_step,
                                   double ext_porlation);
  void set_reference_point(double rp);

  arma::vec3 get_VBED();
  void set_liftoff(int in);

  struct TX_data {
    double SBEE[3];
    double VBEE[3];
    double ABEE[3];
    double JBEE[3];
    double psibd;
    double thtbd;
    double phibd;
    double WBEB[3];
  } TX_data_forward;

  void collect_forces_and_propagate();
  void set_DOF(int ndof);
  void set_aero_flag(unsigned int in);

 private:
  double get_ppx();
  double get_qqx();
  double get_rrx();
  double get_psibdx();
  double get_thtbdx();
  double get_phibdx();
  double get_dvbe();
  double get_dbi();
  double get_dvbi();
  double get_thtvdx();
  double get_psivdx();
  double get_thtbdx_in(double &cthtbd);

  void propagate_position_speed_acceleration(double int_step);
  void propagate_aeroloss(double int_step);
  void propagate_gravityloss(double int_step);
  void propagate_control_loss(double int_step);
  void vibration(double int_step);
  void propagate_TBI(double int_step, arma::vec3 WBIB_in);
  void propagate_TBI_Q(double int_step, arma::vec3 WBIB_in);
  void propagate_WBIB(double int_step, arma::vec3 FMB_in, arma::mat33 IBBB);
  void orbital(arma::vec3 SBII_in, arma::vec3 VBII_in, double dbi_in);
  void build_WEII();
  void aux_calulate(arma::mat33 TEI, arma::mat33 TBI_in);
  void RK4F(std::vector<arma::vec> Var_in, std::vector<arma::vec> &Var_out);
  void Send();

  double calculate_alphaix(arma::vec3 VBIB);
  double calculate_betaix(arma::vec3 VBIB);
  double calculate_alppx(arma::vec3 VBAB_in, double dvba);
  double calculate_phipx(arma::vec3 VBAB_in);
  double calculate_alphax(arma::vec3 VBAB_in);
  double calculate_betax(arma::vec3 VBAB, double dvba);

  arma::vec build_VBEB(double _alpha0x, double _beta0x, double dvbe);
  arma::mat calculate_TBD(double lonx_in, double latx_in, double alt_in);
  arma::vec3 calculate_WBII(arma::mat33 TBI_in);
  arma::vec3 calculate_fspb(arma::vec3 FAPB_in, double vmass);
  arma::vec3 calculate_WBEB(arma::mat33 TBI_in);
  arma::vec3 euler_angle(arma::mat33 TBD_in);

  void gamma_beta();
  void Gravity_Q();
  void AeroDynamics_Q();
  void calculate_I1();
  void funcv(int n, double *x, double *ff);
  void broydn(double x[], int n, int *check);
  void rsolv(double **a, int n, double d[], double b[]);
  void fdjac(int n, double x[], double fvec_in[], double **df);
  double f_min(double x[]);
  void lnsrch(int n, double xold[], double fold, double g[], double p[],
              double x[], double *f_in, double stpmax, int *check);
  void qrdcmp(double **a, int n, double *c, double *d, int *sing);
  void qrupdt(double **r, double **qt, int n, double u[], double v[]);
  void rotate(double **r, double **qt, int n, int i, double a, double b);

  MATRIX(TBI, 3, 3); /* *o (--)    Transformation Matrix of body coord wrt
                    inertia coord */

  MATRIX(TBID, 3, 3); /* *o (--)    Transformation Matrix of body coord wrt
                     inertia coord derivative */

  VECTOR(TBI_Q, 4); /* *o (--)    Transformation Matrix of body coord wrt
                      inertia coord (Quaternion) */

  VECTOR(TBID_Q, 4); /* *o (--)    Transformation Matrix of body coord wrt
                       inertia coord derivative (Quaternion) */

  MATRIX(TBD, 3, 3); /* *o (--)    Transformation Matrix of body coord wrt
                    geodetic coord */
  VECTOR(TBDQ, 4);   /* *o  (--)    Quaternion from geodetic to body */

  VECTOR(VBAB, 3); /* *o  (m/s)    Air speed in body frame */

  MATRIX(WEII_skew, 3,
         3); /* *o  (r/s)    Earth's angular velocity (skew-sym) */

  VECTOR(SBIIP, 3); /* *o  (m)      Vehicle position in inertia coord */

  VECTOR(VBIIP, 3); /* *o  (m/s)    Vehicle inertia velocity */

  VECTOR(SBII, 3); /* *o  (m)      Vehicle position in inertia coord */

  VECTOR(VBII, 3); /* *o  (m/s)    Vehicle inertia velocity */

  VECTOR(ABII, 3); /* *o  (m/s2)   Vehicle inertia acceleration */

  VECTOR(ABIB,
         3); /* *o  (m/s2)   Vehicle inertia acceleration on body coordinate */

  VECTOR(SBEE, 3); /* *o  (m)     Vehicle position in earth coord  */

  VECTOR(VBEE, 3); /* *o  (m/s)     Vehicle speed in earth coord  */

  VECTOR(ABEE, 3); /* *o  (m/s2)   Vehicle acceleration in ECEF */

  VECTOR(SBEE_old, 3); /* *o  (m)     Vehicle position in earth coord  */

  VECTOR(VBEE_old, 3); /* *o  (m/s)     Vehicle speed in earth coord  */

  VECTOR(ABEE_old, 3); /* *o  (m/s2)   Vehicle acceleration in ECEF */

  VECTOR(JBII, 3); /* *o (m/s3)    Vehicle Jerk in ECI*/

  VECTOR(JBEE, 3); /* *o (m/s3)    Vehicle Jerk in ECEF */

  MATRIX(TDI, 3, 3); /* **  (--)     Transformation Matrix of geodetic wrt
                    inertial coordinates */

  MATRIX(
      TGI, 3,
      3); /* **  (--)     Transformation Matrix geocentric wrt inertia coord */

  VECTOR(VBED, 3); /* *o (m/s)   NED velocity */

  VECTOR(FSPB, 3); /* *o  (m/s2)   Specific force in body coord */

  VECTOR(NEXT_ACC, 3); /* *o (m/s2)   New Inertial acceleration */

  MATRIX(TDE, 3, 3); /* ** (--)  T.M. from ECEF to geodetic */

  VECTOR(VBII_old, 3); /* ** (m/s)  Prior body inertia velocity */

  VECTOR(WEII, 3); /* ** (--)  Earth rate in inertia coordinate */

  VECTOR(WBII, 3); /* *o (r/s)        Vehicle's inertia angular velocity in
                     inertia coord */

  VECTOR(WBEB, 3); /* *o (r/s)        Angular velocity of vehicle wrt earth in
                     body coord */

  VECTOR(WBIB, 3); /* *o (r/s)        Augular velocity of vehicle wrt inertia in
                     body coord */

  VECTOR(WBIBD, 3); /* *o (r/s2)       Angular velocity of vehicle wrt inertia
                      in body coord - derivative */

  MATRIX(TVD, 3, 3); /* **  (--)     Transformation Matrix of geographic
                    velocity wrt geodetic coord */

  VECTOR(SBEE_test, 3); /* *o  (m)     Vehicle position in earth coord  */

  VECTOR(VBEE_test, 3); /* *o  (m/s)     Vehicle speed in earth coord  */

  VECTOR(ABEE_test, 3); /* *o  (m/s2)   Vehicle acceleration in ECEF */

  MATRIX(TLI, 3, 3); /* **  (--)  T.M. from inertia to launch site */

  VECTOR(LT_euler, 3); /* ** (rad)  Launch site coordinate Euler angle */

  VECTOR(TBLQ, 4); /* ** (--) Quaternion from launch site to body */

  VECTOR(ABID, 3); /* ** (--) Vehicle inertia body acceleration in geodetic */

  VECTOR(FAPB,
         3); /* *o (N)      Aerodynamic and propulsion forces in body axes */

  VECTOR(FAP, 3); /* *o (N)      Aerodynamic force in body axes */

  VECTOR(FMB,
         3); /* *o (N*m)    Aerodynamic and propulsion moment in body axes */

  VECTOR(FMAB,
         3); /* *o (N*m)    Aerodynamic and propulsion moment in body axes */

  VECTOR(Q_G, 6); /* *o (--)     External force generated by gravity */

  VECTOR(Q_Aero, 6); /* *o (--)  External force generated by aerodynamics */

  VECTOR(rhoC_1, 3); /* *o  (m)  Level arm from reference ponit to CG */

  MATRIX(I1, 3, 3); /* *o (kg*m2)  MOI of vehicle */

  VECTOR(ddrP_1, 3); /* *o  (m/s2)  Vehicle acceleration */

  VECTOR(ddang_1, 3); /* *o  (r/s2)  Vehicle angular acceleration */

  VECTOR(dang_1, 3); /* *o  (r/s)   Vehicle angular rate */

  VECTOR(ddrhoC_1, 3); /* *o  (m/s2)  Centrifugal acceleration and tangential
                          acceleration term */

  VECTOR(p_b1_ga,
         3); /* *o (--)   General dynamics equations 1st DoF to 3rd DoF */

  VECTOR(p_b1_be,
         3); /* *o (--)   General dynamics equations 4th DoF to 6th DoF */

  VECTOR(f, 6); /* *o  (--)  Summation of external force & internal force */

  VECTOR(gamma_b1_q1, 3); /* *o (--)  Vehicle's 1st DoF velocity coefficient */

  VECTOR(gamma_b1_q2, 3); /* *o (--)  Vehicle's 2nd DoF velocity coefficient */

  VECTOR(gamma_b1_q3, 3); /* *o (--)  Vehicle's 3rd DoF velocity coefficient */

  VECTOR(beta_b1_q4,
         3); /* *o (--)  Vehicle's 4th DoF angular velocity coefficient */

  VECTOR(beta_b1_q5,
         3); /* *o (--)  Vehicle's 5th DoF angular velocity coefficient */

  VECTOR(beta_b1_q6,
         3); /* *o (--)  Vehicle's 6th DoF angular velocity coefficient */

  /* Generating Outputs */
  double
      ortho_error; /* *o (--)    Direction cosine matrix orthogonality error*/
  double alphax;   /* *o (d)     Angle of attack */
  double betax;    /* *o (d)     Sideslip angle */
  double alppx;    /* *o (d)     Total angle of attack */
  double phipx;    /* *o (d)     Aerodynamic roll angle*/
  double alphaix;  /* *o (d)     Angle of attack, inertia velocity*/
  double betaix;   /* *o (d)     Sideslip angle, inertia velocity*/
  double psibdx; /* *o (d)     Yaw angle of Vehicle wrt geodetic coord - deg */
  double
      thtbdx; /* *o (d)     Pitch angle of Vehicle wrt geodetic coord - deg */
  double phibdx; /* *o (d)     Roll angle of Vehicle wrt geodetic coord - deg */
  double psibd;  /* *o (r)     Yaw angle of Vehicle wrt geodetic coord - rad */
  double thtbd; /* *o (r)     Pitch angle of Vehicle wrt geodetic coord - rad */
  double phibd; /* *o (r)     Roll angle of Vehicle wrt geodetic coord - rad */
  double alt;   /* *o  (m)      Vehicle altitude */
  double lonx;  /* *o  (d)      Vehicle longitude */
  double latx;  /* *o  (d)      Vehicle latitude */
  double _aero_loss; /* *o  (m/s)    Velocity loss caused by aerodynamic drag */
  double gravity_loss; /* *o  (m/s)    Velocity loss caused by gravity */
  // double t;            /* *o (s)       timer */
  double _grndtrck; /* *o  (m)     [DIAG] Vehicle ground track on earth */
  double _gndtrkmx; /* *o  (km)    [DIAG] Ground track - km */
  double _gndtrnmx; /* **  (nm)    [DIAG] Ground track - nm */
  double _ayx;      /* *o  (m/s2)  [DIAG] Achieved side acceleration */
  double _anx;      /* *o  (m/s2)  [DIAG] Achieved normal acceleration */
  double _dbi;    /* *o  (m)     [DIAG] Vehicle distance from center of earth */
  double _dvbi;   /* *o  (m/s)   [DIAG] Vehicle inertia speed */
  double _dvbe;   /* *o  (m/s)   [DIAG] Vehicle geographic speed */
  double _thtvdx; /* *o  (d)     [DIAG] Vehicle's flight path angle */
  double _psivdx; /* *o  (d)     [DIAG] Vehicle's heading angle */
  int liftoff;    /* *i  (--)     To check wether the rocket liftoff or
                              not: liftoff = 1, not liftoff = 0 */
  int cadorbin_flag; /* Orbit calculation status flag */
  double ppx; /* *o (d/s)        Body roll angular velocity wrt earth in body
                 axes */
  double qqx; /* *o (d/s)        Body pitch angular velocity wrt earth in body
                 axes */
  double rrx; /* *o (d/s)        Body yaw angular velocity wrt earth in body
                 axes */
  double control_loss; /* *o (--) Velocity loss due to control effect */

  /* Orbital Logging */
  double _inclination;  /* *o  (deg)   [DIAG] Orbital inclination is the minimun
                        angle between reference plane and the orbital plane or
                        direction of an object in orbit around another object */
  double _eccentricity; /* *o  (--)    [DIAG] Determines the amount by which its
                           orbit around another body deviates from a perfect
                           circle */
  double _semi_major; /* *o  (m)     [DIAG] the major axis of an ellipse is its
                         longest diameter */
  double _ha;         /* *o  (m)     [DIAG] Orbital Apogee */
  double _hp;         /* *o  (m)     [DIAG] Orbital Perigee */
  double _lon_anodex; /* *o  (deg)   [DIAG] The longitude of the ascending node
                         (☊ or Ω) is one of the orbital elements used to specify
                         the orbit of an object in space. It is the angle from a
                         reference direction, called the origin of longitude, to
                         the direction of the ascending node, measured in a
                         reference plane */
  double _arg_perix;  /* *o  (deg)   [DIAG] The argument of periapsis (also
                         called argument of perifocus or argument of pericenter),
                         symbolized as ω, is one of the orbital elements of an
                         orbiting body. Parametrically, ω is the angle from the
                         body's ascending node to its periapsis, measured in the
                         direction of motion */
  double _true_anomx; /* *o  (deg)   [DIAG] In celestial mechanics, true anomaly
                      is an angular parameter that defines the position of a
                      body moving along a Keplerian orbit. It is the angle
                      between the direction of periapsis and the current
                      position of the body, as seen from the main focus of the
                      ellipse (the point around which the object orbits) */
  double _ref_alt;    /* *o  (m)     [DIAG] */
  double reference_point; /* *o (m)    Multibody dynamics reference point */
  double Roll;
  double Pitch;
  double Yaw;
  unsigned int Interpolation_Extrapolation_flag;
  // double xp; /* *o (m) Reference point  */
  int its;                /* *o (--) Number of iterations */
  int DOF;                /* *o (--)  Number of Degree of Freedom */
  unsigned int Aero_flag; /* *o (-)  Aerodynamics flag */
};
#endif


/* Copyright (C) 2010 Ion Torrent Systems, Inc. All Rights Reserved */
#include <armadillo>
#include <iostream>
#include <vector>
#include <string>
#include "IonErr.h"
#include "Image.h"
#include "Mask.h"
#include "OptArgs.h"
#include "SampleQuantiles.h"

using namespace arma;
using namespace std;

#define D '\t'
#define NUM_SAMPLE 10000
#define MAX_MILL_SEC 80
class CompressReporter {
public:
  virtual void Init() = 0;
  virtual void Report(int rowStart, int rowEnd, int colStart, int colEnd,
                      int flow, const std::vector<int> &wellMapping,
                      Mask &mask,
                      const Mat<float> &raw, const Mat<float> &compressed) = 0;
  virtual void Finish() = 0;
};

class WellReporter : public CompressReporter {
public:
  WellReporter(const std::string &fileOut) {
    mFileName = fileOut;
    mMad.Init(10000);
    mAutoCor.Init(10000);
  }
  void Init() {
    mOut.open(mFileName.c_str());
    mOut << "well\trow\tcol\tmad\tabs25\tabs75\trms\tacor" << endl;
  }

  void Report(int rowStart, int rowEnd, int colStart, int colEnd,
              int flow, const std::vector<int> &wellMapping,
              Mask &mask,
              const Mat<float> &raw, const Mat<float> &compressed) {
    Mat<float> diff = raw - compressed;
    for (size_t row = 0; row < raw.n_rows; row++) {
      SampleQuantiles<float> q(diff.n_cols);
      SampleStats<float>  s;
      SampleStats<float>  m;
      for (size_t col = 0; col < diff.n_cols; col++) {
        float val = diff(row, col);
        m.AddValue(val);
        q.AddValue(fabs(val));
        s.AddValue(val*val);
      }
      SampleStats<float> a;
      double mean = m.GetMean();
      for (size_t col = 1; col < diff.n_cols; col++) {
        float val1 = diff(row, col);
        float val2 = diff(row, col-1);
        a.AddValue((val1 - mean) * (val2 - mean));
      }
      double acor = a.GetMean() / m.GetVar();
      mAutoCor.AddValue(acor);
      mMad.AddValue(q.GetQuantile(.5));
      mOut << wellMapping[row] << D << wellMapping[row] / mask.W() << D << wellMapping[row] % mask.W() << D
           << q.GetQuantile(.5) << D << q.GetQuantile(.25) << D << q.GetQuantile(.75) << D << sqrt(s.GetMean()) << D << acor << endl;
    }
  }

  virtual void Finish() {
    cout << "Quantiles\tmad\tautocor" << endl;
    for (int i = 0; i <= 10; i++) {
      cout << i * 10 << D << mMad.GetQuantile(i / 10.0) << D << mAutoCor.GetQuantile(i / 10.0) << endl;
    }
    mOut.close();
  }
  SampleQuantiles<float> mMad;
  SampleQuantiles<float> mAutoCor;
  std::string mFileName;
  std::ofstream mOut;
};

class RegionIndividualReporter : public CompressReporter {
public:
  RegionIndividualReporter(const std::string &fileOut, int rowStart, int rowEnd, int colStart, int colEnd) {
    mFileName = fileOut;
    mRowStart = rowStart;
    mRowEnd = rowEnd;
    mColStart = colStart;
    mColEnd = colEnd;
    mFirst = true;
  }

  void Init() {
    mOut.open(mFileName.c_str());
    mOut << "well\ttype";
  }

  void Report(int rowStart, int rowEnd, int colStart, int colEnd,
              int flow, const std::vector<int> &wellMapping,
              Mask &mask,
              const Mat<float> &raw, const Mat<float> &compressed) {
    if (rowStart == mRowStart && rowEnd == mRowEnd && colStart == mColStart && colEnd == mColEnd) {
      if (mFirst) {
        for (size_t col = 0; col < raw.n_cols; col++) {
          mOut << D << "col." << col;
        }
        mOut << endl;
        mFirst = false;
      }
      for (size_t row = 0; row < raw.n_rows; row++) {
        mOut << wellMapping[row] << D << "raw";
        for (size_t col = 0; col < raw.n_cols; col++) {
          mOut << D << raw(row,col);
        }
        mOut << endl;
      }
      for (size_t row = 0; row < raw.n_rows; row++) {
        mOut << wellMapping[row] << D << "compressed";
        for (size_t col = 0; col < raw.n_cols; col++) {
          mOut << D << compressed(row,col);
        }
        mOut << endl;
      }
    }
  }

  virtual void Finish() { mOut.close(); }
  bool mFirst;
  int mRowStart, mRowEnd, mColStart, mColEnd;
  std::string mFileName;
  std::ofstream mOut;
};

class DeviationSummaryReporter : public CompressReporter {
public:
  DeviationSummaryReporter(const std::string &fileOut, const std::string &runName) {
    mFileName = fileOut;
    mRunName = runName;
  }

  void Init() {
    mQuantiles.Init(10000);
    mSample.Clear();
    mSqSample.Clear();
  }

  void Report(int rowStart, int rowEnd, int colStart, int colEnd,
              int flow, const std::vector<int> &wellMapping,
              Mask &mask,
              const Mat<float> &raw, const Mat<float> &compressed) {
    Mat<float> diff = raw - compressed;
    for (size_t row = 0; row < raw.n_rows; row++) {
      for (size_t col = 0; col < diff.n_cols; col++) {
        float val = fabs(diff(row, col));
        mQuantiles.AddValue(val);
        mSqSample.AddValue(val*val);
        mSample.AddValue(val);
      }
    }
  }

  virtual void Finish() {
    mOut.open(mFileName.c_str());
    mOut << "{" << endl;;
    mOut << "  run_name : " << mRunName << "," << endl;
    mOut << "  fabs_mean : " << mSample.GetMean() << ", "  << endl;
    mOut << "  fabs_sd : " << mSample.GetSD() << ", " << endl;
    mOut << "  rms : " << sqrt(mSqSample.GetMean()) << ", "  << endl;
    mOut << "  fabs_median : " << mQuantiles.GetQuantile(.5) << ", " << endl;
    mOut << "  fabs_iqr : " << mQuantiles.GetIQR() << ", "  << endl;
    mOut << "  fabs_quantiles: [";
    for (size_t i = 0; i < 10; i++) {
      float q = i / 10.0f;
      mOut << mQuantiles.GetQuantile(q) << ",";
    }
    mOut << mQuantiles.GetQuantile(1.0) <<  "], "  << endl;
    mOut << "  fabs_quantiles_100: [";
    for (size_t i = 0; i < 100; i++) {
      float q = i / 100.0f;
      mOut << mQuantiles.GetQuantile(q) << ",";
    }
    mOut << mQuantiles.GetQuantile(1.0) <<  "]"  << endl;
    mOut << "}" << endl;
    mOut.close();
  }

  std::string mFileName;
  SampleQuantiles<float> mQuantiles;
  SampleStats<float> mSqSample;
  SampleStats<float> mSample;
  std::string mRunName;
  std::ofstream mOut;
};

class FrameReporter : public CompressReporter {
public:
  FrameReporter(const std::string &fileOut) {
    mFileName = fileOut;
    mStarted = false;
  }
  void Init() {
    mOut.open(mFileName.c_str());
    mOut << "rowStart\trowEnd\tcolStart\tcolEnd\tnumWells\tflow\tstat";
    mStarted = false;
  }

  void Report(int rowStart, int rowEnd, int colStart, int colEnd,
              int flow, const std::vector<int> &wellMapping,
              Mask &mask,
              const Mat<float> &raw, const Mat<float> &compressed) {
    if (!mStarted) {
      for (size_t i = 0; i < raw.n_cols; i++) {
        mOut << D << "frame." << i;
      }
      mOut << endl;
      mQuantiles.resize(raw.n_cols);
      mStats.resize(raw.n_cols);
      mStarted = true;
    }
    for (size_t i = 0; i < raw.n_cols; i++) {
      mQuantiles[i].Clear();
      mQuantiles[i].Init(raw.n_rows);
      mStats[i].Clear();
    }
    Mat<float> diff = raw - compressed;
    for (size_t row = 0; row < diff.n_rows; row++) {
      for (size_t col = 0; col < diff.n_cols; col++) {
        float val = fabs(diff(row, col));
        mQuantiles[col].AddValue(val);
        mStats[col].AddValue(val);
      }
    }
    mOut << rowStart << D << rowEnd << D << colStart << D << raw.n_rows << D << flow << D << "Fabs.Q25";
    for (size_t i = 0; i < mQuantiles.size(); i++) { mOut << D << mQuantiles[i].GetQuantile(.25); }
    mOut << std::endl;
    mOut << rowStart << D << rowEnd << D << colStart << D << raw.n_rows << D << flow << D << "Fabs.Q50";
    for (size_t i = 0; i < mQuantiles.size(); i++) { mOut << D << mQuantiles[i].GetQuantile(.5); }
    mOut << std::endl;
    mOut << rowStart << D << rowEnd << D << colStart << D << raw.n_rows << D << flow << D << "Fabs.Q75";
    for (size_t i = 0; i < mQuantiles.size(); i++) { mOut << D << mQuantiles[i].GetQuantile(.75); }
    mOut << std::endl;
    mOut << rowStart << D << rowEnd << D << colStart << D << raw.n_rows << D << flow << D << "Fabs.IQR";
    for (size_t i = 0; i < mQuantiles.size(); i++) { mOut << D << mQuantiles[i].GetQuantile(.75) - mQuantiles[i].GetQuantile(.25); }
    mOut << std::endl;
    mOut << rowStart << D << rowEnd << D << colStart << D << raw.n_rows << D << flow << D << "Fabs.Mean";
    for (size_t i = 0; i < mStats.size(); i++) { mOut << D << mStats[i].GetMean(); }
    mOut << std::endl;
    mOut << rowStart << D << rowEnd << D << colStart << D << raw.n_rows << D << flow << D << "Fabs.SD";
    for (size_t i = 0; i < mStats.size(); i++) { mOut << D << mStats[i].GetSD(); }
    mOut << std::endl;
  }

  virtual void Finish() { mOut.close(); }

  std::vector<SampleQuantiles<float> > mQuantiles;
  std::vector<SampleStats<float>  > mStats;
  bool mStarted;
  std::string mFileName;
  std::ofstream mOut;
};

class CompressOpts {
public:
  int rowStart, rowEnd, colStart, colEnd;
  int rowStep, colStep;
  double minPercent;
  std::string datIn;
  std::string maskFile;
  int flowNum;
  std::string prefixOut;
  std::string wellOut;
  std::string frameOut;
  std::string runName;
  int numBasis;
};

void AddMatVector(Mat<float> &M, const Col<float> &vec) {
  for (size_t i = 0; i < M.n_rows; i++) {
    for (size_t j = 0; j < M.n_cols; j++) {
      M(i,j) = M(i,j) + vec(i);
    }
  }
}

void Compress(const Mat<float> &raw, int numBasis, Mat<float> &compressed) {
  Col<float> dc = mean(raw, 1);
  Mat<float> X = raw;
  dc = dc * -1;
  AddMatVector(X, dc);
  Mat<float> Cov = X.t() * X;    // L x L
  Mat<float> EVec;
  Col<float> EVal;
  eig_sym(EVal, EVec, Cov);
  Mat<float> V(EVec.n_rows, numBasis );  // L x numBasis
  int count = 0;
  for(size_t v = V.n_rows - 1; v >= V.n_rows - numBasis; v--) {
    copy(EVec.begin_col(v), EVec.end_col(v), V.begin_col(count++));
  }
  Mat<float> A = X * V;  // N x numBasis
  Mat<float> P = A * V.t();  // Prediction N x L
  dc = dc * -1;
  AddMatVector(P, dc);
  compressed = P;
}

int LoadData(int rowStart, int rowEnd, int colStart, int colEnd,
              Mask &mask, Image &img, vector<int> &mapping, Mat<float> &raw) {
  int numOk = 0;
  for (int row = rowStart; row < rowEnd; row++) {
    for (int col = colStart; col < colEnd; col++) {
      if (!mask.Match(col, row, MaskPinned) && !mask.Match(col, row, MaskExclude)) {
        numOk++;
        mapping.push_back(mask.ToIndex(row, col));
      }
    }
  }
  const RawImage *rawImg = img.GetImage();
  cout << "Timestamps:" << endl;
  vector<bool> colOk(img.GetFrames(), false);
  int count = 0;
  if (rawImg->timestamps[0] < MAX_MILL_SEC) {
    count++;
    colOk[0] = true;
  }
  for (int i = 1; i < img.GetFrames(); i++) {
    cout << i << ":\t" << rawImg->timestamps[i] << "\t" << rawImg->timestamps[i] - rawImg->timestamps[i-1] << endl;
    if (rawImg->timestamps[i] - rawImg->timestamps[i-1] < MAX_MILL_SEC) {
      colOk[i] = true;
      count++;
    }
  }
  cout << "Got: " << count << " frames with no vfr" << endl;
  raw.set_size(numOk, count);
  for (size_t wIx = 0; wIx < mapping.size(); wIx++) {
    int idx = mapping[wIx];
    int cIx = 0;
    for (int fIx = 0; fIx < img.GetFrames(); fIx++) {
      if (colOk[fIx]) {
        raw(wIx,cIx++) = img.At(idx, fIx);
      }
    }
  }
  return numOk;
}

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

  OptArgs opts;
  opts.ParseCmdLine(argc, argv);
  CompressOpts o;
  opts.GetOption(o.maskFile, "", '-', "mask");
  Mask mask(o.maskFile.c_str());
  opts.GetOption(o.rowStart, "0", '-', "row-start");
  opts.GetOption(o.rowEnd, "-1", '-', "row-end");
  if(o.rowEnd < 0) { o.rowEnd = mask.H(); }

  opts.GetOption(o.colStart, "0", '-', "col-start");
  opts.GetOption(o.colEnd, "-1", '-', "col-end");
  if(o.colEnd < 0) { o.colEnd = mask.W(); }

  opts.GetOption(o.datIn, "", '-', "dat-file");
  opts.GetOption(o.prefixOut, "noise", '-', "out-prefix");
  opts.GetOption(o.numBasis, "6", '-', "num-basis");
  opts.GetOption(o.rowStep, "100", '-', "row-step");
  opts.GetOption(o.colStep, "100", '-', "col-step");
  opts.GetOption(o.minPercent, ".5", '-', "min-percent");
  opts.GetOption(o.runName, "", '-', "run-name");
  // Load file
  Image img;
  img.SetImgLoadImmediate (false);
  img.SetIgnoreChecksumErrors (false);
  ION_ASSERT(img.LoadRaw (o.datIn.c_str()), "Couldn't load: " + o.datIn);

  // Create reporters
  vector<CompressReporter *> reporters;
  WellReporter wRep(o.prefixOut + ".wells.txt");
  reporters.push_back(&wRep);
  FrameReporter frameRep(o.prefixOut + ".frames.txt");
  reporters.push_back(&frameRep);
  DeviationSummaryReporter devRep(o.prefixOut + ".summary.json", o.runName);
  reporters.push_back(&devRep);
  int regRowStart = 0;
  if (o.rowStep * 5 < mask.H()) {
    regRowStart = o.rowStep * 4;
  }
  int regColStart = 0;
  if (o.colStep * 5 < mask.H()) {
    regColStart = o.colStep * 4;
  }
  RegionIndividualReporter regionRep(o.prefixOut + ".region.txt", regRowStart, regRowStart + o.rowStep, regColStart, regColStart + o.colStep);
  reporters.push_back(&regionRep);
  for (size_t rIx = 0; rIx < reporters.size(); rIx++) {
    reporters[rIx]->Init();
  }
  // For each region do compression and run report.
  for (int row = o.rowStart; row < o.rowEnd; row += o.rowStep) {
    for (int col = o.colStart; col < o.colEnd; col += o.colStep) {
      int rowStop = min(row + o.rowStep, o.rowEnd);
      int colStop = min(col + o.colStep, o.colEnd);
      Mat<float> raw;
      vector<int> mapping;
      int numOk = LoadData(row, rowStop, col, colStop, mask, img,  mapping, raw);
      if (numOk > o.minPercent * (rowStop - row) * (colStop - col)) {
        Mat<float> compressed(raw.n_rows, raw.n_cols);
        Compress(raw, o.numBasis, compressed);
        for (size_t rIx = 0; rIx < reporters.size(); rIx++) {
          reporters[rIx]->Report(row, rowStop, col, colStop, 0,
                                 mapping, mask, raw, compressed);
        }
      }
    }
  }
  for (size_t rIx = 0; rIx < reporters.size(); rIx++) {
    reporters[rIx]->Finish();
  }

  // Spin things down.

}


#include <numpy_eigen/boost_python_headers.hpp>
#include <sm/timing/TimestampCorrector.hpp>
#include <boost/cstdint.hpp>

template<typename TIME_T>
void exportTimestampCorrector(const std::string & className)
{
  using namespace boost::python;
  using namespace sm::timing;

  class_< TimestampCorrector<TIME_T> >( className.c_str(), init<>() )
    .def("correctTimestamp", &TimestampCorrector<TIME_T>::correctTimestamp, "correctedEventLocalTime = correctTimestamp(eventRemoteTime, eventLocalTimes).\nNote: This function must be called with monotonically increasing remote timestamps.")
    .def("getLocalTime", &TimestampCorrector<TIME_T>::getLocalTime, "eventLocalTime = getLocalTime(eventRemoteTime)")
    .def("convexHullSize", &TimestampCorrector<TIME_T>::convexHullSize)
    .def("printHullPoints", &TimestampCorrector<TIME_T>::printHullPoints)
      .def("getSlope", &TimestampCorrector<TIME_T>::getSlope)
      .def("getOffset", &TimestampCorrector<TIME_T>::getOffset)
    ;

}

void exportTimestampCorrectors()
{
  exportTimestampCorrector<double>("DoubleTimestampCorrector");
  exportTimestampCorrector<boost::int64_t>("LongTimestampCorrector");
}


//---------------------------------------------------------------------------//
// Copyright (c) 2013-2014 Kyle Lutz <kyle.r.lutz@gmail.com>
//
// 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
//
// See http://boostorg.github.com/compute for more information.
//---------------------------------------------------------------------------//

#ifndef BOOST_COMPUTE_FUNCTIONAL_POPCOUNT_HPP
#define BOOST_COMPUTE_FUNCTIONAL_POPCOUNT_HPP

#include <boost/compute/function.hpp>
#include <boost/compute/type_traits/type_name.hpp>

namespace boost {
namespace compute {

/// Returns the number of non-zero bits in \p x.
///
/// \see_opencl_ref{popcount}
template<class T>
class popcount : public function<T(T)>
{
public:
    popcount()
        : function<T(T)>("boost_popcount")
    {
        std::stringstream s;
        s << "inline " << type_name<T>() << " boost_popcount"
          << "(const " << type_name<T>() << " x)\n"
          << "{\n"
          // use built-in popcount if opencl 1.2 is supported
          << "#if __OPENCL_VERSION__ >= 120\n"
          << "    return popcount(x);\n"
          // fallback to generic popcount() implementation
          << "#else\n"
          << "    " << type_name<T>() << " count = 0;\n"
          << "    for(" << type_name<T>() << " i = 0; i < sizeof(i) * CHAR_BIT; i++){\n"
          << "        if(x & (" << type_name<T>() << ") 1 << i){\n"
          << "            count++;\n"
          << "        }\n"
          << "    }\n"
          << "    return count;\n"
          << "#endif\n"
          << "}\n";
        this->set_source(s.str());
    }
};

} // end compute namespace
} // end boost namespace

#endif // BOOST_COMPUTE_FUNCTIONAL_POPCOUNT_HPP