#define BOOST_TEST_MODULE pcraster dal array
#include <boost/test/unit_test.hpp>
#include "dal_MathUtils.h"

BOOST_AUTO_TEST_CASE(clamp_)
{
  using namespace dal;

  size_t first, last, interval;

  {
    first = 5;
    last = 10;
    interval = 1;
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 0), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 1), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 4), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 5), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 6), size_t(6));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 9), size_t(9));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 10), size_t(10));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 11), size_t(10));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 12), size_t(10));
  }

  {
    first = 5;
    last = 5;
    interval = 1;
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 3), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 4), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 5), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 6), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 7), size_t(5));
  }

  {
    first = 5;
    last = 6;
    interval = 1;
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 3), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 4), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 5), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 6), size_t(6));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 7), size_t(6));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 8), size_t(6));
  }

  {
    first = 5;
    last = 10;
    interval = 2;
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 0), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 1), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 4), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 5), size_t(5));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 6), size_t(7));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 7), size_t(7));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 8), size_t(9));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 9), size_t(9));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 10), size_t(9));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 11), size_t(9));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 12), size_t(9));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 13), size_t(9));
  }

  {
    first = 1;
    last = 100;
    interval = 2;
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 99), size_t(99));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 100), size_t(99));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 101), size_t(99));
    BOOST_CHECK_EQUAL(clamp<size_t>(first, last, interval, 102), size_t(99));
  }

  {
    float first, last, interval;

    {
      first    = 0.01f;
      last     = 0.99f;
      interval = 0.01f;

      // Below interval.
      BOOST_CHECK_CLOSE(clamp<float>(first, last, interval, 0.0f),
         float(0.01), 0.001f);

      // Between two values in the interval.
      BOOST_CHECK_CLOSE(clamp<float>(first, last, interval, 0.49f),
         float(0.49), 0.001f);
      BOOST_CHECK_CLOSE(clamp<float>(first, last, interval, 0.494f),
         float(0.49), 0.001f);
      BOOST_CHECK_CLOSE(clamp<float>(first, last, interval, 0.495f),
         float(0.5), 0.001f);
      BOOST_CHECK_CLOSE(clamp<float>(first, last, interval, 0.5f),
         float(0.5), 0.001f);

      // Above interval.
      BOOST_CHECK_CLOSE(clamp<float>(first, last, interval, 1.0f),
         float(0.99), 0.001f);
    }
  }
}

BOOST_AUTO_TEST_CASE(is_regular_increasing_range)
{
  using namespace dal;

  {
    int range[] = { 1, 2, 3, 4, 5 };
    int first, last, interval;
    BOOST_CHECK(isRegularIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
    BOOST_CHECK_EQUAL(first, 1);
    BOOST_CHECK_EQUAL(last, 5);
    BOOST_CHECK_EQUAL(interval, 1);
  }

  {
    std::vector<size_t> steps;
    steps.push_back(1);
    steps.push_back(2);
    steps.push_back(3);
    size_t first, last, interval;
    BOOST_CHECK(isRegularIncreasingRange(first, last, interval,
         steps.begin(), steps.end()));
    BOOST_CHECK_EQUAL(first, size_t(1));
    BOOST_CHECK_EQUAL(last, size_t(3));
    BOOST_CHECK_EQUAL(interval, size_t(1));
  }

  {
    int range[] = { 0 }; // empty array illegal
    int first, last, interval;
    BOOST_CHECK(!isRegularIncreasingRange(first, last, interval,
         range, range + 0 ));
  }

  {
    int range[] = { 5 };
    int first, last, interval;
    BOOST_CHECK(!isRegularIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
  }

  {
    int range[] = { 5, 6 };
    int first, last, interval;
    BOOST_CHECK(isRegularIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
    BOOST_CHECK_EQUAL(first, 5);
    BOOST_CHECK_EQUAL(last, 6);
    BOOST_CHECK_EQUAL(interval, 1);
  }

  {
    int range[] = { 1, 3, 5 };
    int first, last, interval;
    BOOST_CHECK(isRegularIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
    BOOST_CHECK_EQUAL(first, 1);
    BOOST_CHECK_EQUAL(last, 5);
    BOOST_CHECK_EQUAL(interval, 2);
  }

  {
    int range[] = { 1, 2, 3, 5 };
    int first, last, interval;
    BOOST_CHECK(!isRegularIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
  }

  {
    int range[] = { 1, 3, 4, 5 };
    int first, last, interval;
    BOOST_CHECK(!isRegularIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
  }

  {
    int range[] = { 46, 47, 48, 49, 50 };
    int first, last, interval;
    BOOST_CHECK(isRegularIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
    BOOST_CHECK_EQUAL(first, 46);
    BOOST_CHECK_EQUAL(last, 50);
    BOOST_CHECK_EQUAL(interval, 1);
  }
}

BOOST_AUTO_TEST_CASE(is_increasing_range)
{
  using namespace dal;

  {
    int range[] = { 1, 2, 3, 4, 5 };
    int first, last, interval;
    BOOST_CHECK(isIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
    BOOST_CHECK_EQUAL(first   , 1);
    BOOST_CHECK_EQUAL(last    , 5);
    BOOST_CHECK_EQUAL(interval, 1);
  }

  {
    int range[] = { 2, 4, 6 };
    int first, last, interval;
    BOOST_CHECK(isIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
    BOOST_CHECK_EQUAL(first   , 2);
    BOOST_CHECK_EQUAL(last    , 6);
    BOOST_CHECK_EQUAL(interval, 2);
  }

  {
    int range[] = { 2, 4, 7 };
    int first, last, interval;
    BOOST_CHECK(isIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
    BOOST_CHECK_EQUAL(first   , 2);
    BOOST_CHECK_EQUAL(last    , 7);
    BOOST_CHECK_EQUAL(interval, 1);
  }

  {
    std::vector<size_t> steps;
    steps.push_back(1);
    steps.push_back(2);
    steps.push_back(3);
    size_t first, last, interval;
    BOOST_CHECK(isIncreasingRange(first, last, interval,
         steps.begin(), steps.end()));
    BOOST_CHECK_EQUAL(first   , size_t(1));
    BOOST_CHECK_EQUAL(last    , size_t(3));
    BOOST_CHECK_EQUAL(interval, size_t(1));
  }

  {
    int range[] = { 0 }; // empty array illegal
    int first, last, interval;
    BOOST_CHECK(!isIncreasingRange(first, last, interval,
         range, range + 0 ));
  }

  {
    int range[] = { 5 };
    int first, last, interval;
    BOOST_CHECK(!isIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
  }

  {
    int range[] = { 5, 6 };
    int first, last, interval;
    BOOST_CHECK(isIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
    BOOST_CHECK_EQUAL(first   , 5);
    BOOST_CHECK_EQUAL(last    , 6);
    BOOST_CHECK_EQUAL(interval, 1);
  }

  {
    int range[] = { 1, 3, 5 };
    int first, last, interval;
    BOOST_CHECK(isIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
    BOOST_CHECK_EQUAL(first   , 1);
    BOOST_CHECK_EQUAL(last    , 5);
    BOOST_CHECK_EQUAL(interval, 2);
  }

  {
    int range[] = { 1, 2, 3, 5 };
    int first, last, interval;
    BOOST_CHECK(isIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
    BOOST_CHECK_EQUAL(first   , 1);
    BOOST_CHECK_EQUAL(last    , 5);
    BOOST_CHECK_EQUAL(interval, 1);
  }

  {
    int range[] = { 1, 3, 4, 5 };
    int first, last, interval;
    BOOST_CHECK(isIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
    BOOST_CHECK_EQUAL(first   , 1);
    BOOST_CHECK_EQUAL(last    , 5);
    BOOST_CHECK_EQUAL(interval, 1);
  }

  {
    int range[] = { 46, 47, 48, 49, 50 };
    int first, last, interval;
    BOOST_CHECK(isIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(int)));
    BOOST_CHECK_EQUAL(first   , 46);
    BOOST_CHECK_EQUAL(last    , 50);
    BOOST_CHECK_EQUAL(interval, 1);
  }

  {
    double range[] = { 0.10, 0.25, 0.50, 0.75, 0.90 };
    double first, last, interval;
    BOOST_CHECK(isIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(double)));
    BOOST_CHECK_CLOSE(first   , 0.10, 0.001);
    BOOST_CHECK_CLOSE(last    , 0.90, 0.001);
    BOOST_CHECK_CLOSE(interval, 0.05, 0.001);
  }

  {
    double range[] = { 0.10, 0.25, 0.50, 0.75, 0.90, 0.99 };
    double first, last, interval;
    BOOST_CHECK(isIncreasingRange(first, last, interval,
         range, range + sizeof(range) / sizeof(double)));
    BOOST_CHECK_CLOSE(first   , 0.10, 0.001);
    BOOST_CHECK_CLOSE(last    , 0.99, 0.001);
    BOOST_CHECK_CLOSE(interval, 0.01, 0.001);
  }
}

BOOST_AUTO_TEST_CASE(comparable_)
{
  using namespace dal;

  BOOST_CHECK(comparable<float>(0.0f, 0.0f));
}

BOOST_AUTO_TEST_CASE(interpolate_)
{
  using namespace dal;

  {
    Array<double> array;
    BOOST_CHECK(array.empty());
    interpolate(array);
    BOOST_CHECK(array.empty());
  }

  {
    Array<double> array;
    array.resize(1);
    array[0] = 1.0;
    interpolate(array);
    BOOST_CHECK(comparable<double>(array[0], 1.0));
  }

  {
    Array<double> array;
    array.resize(1);
    pcr::setMV(array[0]);
    interpolate(array);
    pcr::isMV(array[0]);
  }

  {
    Array<double> array;
    array.resize(3);
    array[0] = 1.0;
    pcr::setMV(array[1]);
    array[2] = 3.0;
    interpolate(array);
    BOOST_CHECK(comparable<double>(array[0], 1.0));
    BOOST_CHECK(comparable<double>(array[1], 2.0));
    BOOST_CHECK(comparable<double>(array[2], 3.0));
  }

  {
    Array<double> array;
    array.resize(5);
    array[0] = 1.0;
    pcr::setMV(array[1]);
    array[2] = 3.0;
    pcr::setMV(array[3]);
    array[4] = 5.0;
    interpolate(array);
    BOOST_CHECK(comparable<double>(array[0], 1.0));
    BOOST_CHECK(comparable<double>(array[1], 2.0));
    BOOST_CHECK(comparable<double>(array[2], 3.0));
    BOOST_CHECK(comparable<double>(array[3], 4.0));
    BOOST_CHECK(comparable<double>(array[4], 5.0));
  }
}

BOOST_AUTO_TEST_CASE(fill_using_previous_value)
{
  using namespace dal;

  bool testImplemented = false;
  BOOST_WARN(testImplemented);
}

BOOST_AUTO_TEST_CASE(merge_ranges)
{
  using namespace dal;

  {
    // Integral tests.
    size_t first, last, step;

    {
      first = 1;
      last = 10;
      step = 2;
      mergeRanges<size_t>(first, last, step, 1, 10, 2);

      BOOST_CHECK_EQUAL(first, size_t(1));
      BOOST_CHECK_EQUAL(last, size_t(10));
      BOOST_CHECK_EQUAL(step, size_t(2));
    }

    {
      first = 1;
      last = 10;
      step = 2;
      mergeRanges<size_t>(first, last, step, 2, 10, 2);

      BOOST_CHECK_EQUAL(first, size_t(1));
      BOOST_CHECK_EQUAL(last, size_t(10));
      BOOST_CHECK_EQUAL(step, size_t(1));
    }

    {
      first = 1;
      last = 10;
      step = 2;
      mergeRanges<size_t>(first, last, step, 4, 10, 2);

      BOOST_CHECK_EQUAL(first, size_t(1));
      BOOST_CHECK_EQUAL(last, size_t(10));
      BOOST_CHECK_EQUAL(step, size_t(1));
    }

    {
      first = 1;
      last = 10;
      step = 2;
      mergeRanges<size_t>(first, last, step, 1, 10, 2);

      BOOST_CHECK_EQUAL(first, size_t(1));
      BOOST_CHECK_EQUAL(last, size_t(10));
      BOOST_CHECK_EQUAL(step, size_t(2));
    }

    {
      first = 1;
      last = 10;
      step = 2;
      mergeRanges<size_t>(first, last, step, 1, 1, 1);

      BOOST_CHECK_EQUAL(first, size_t(1));
      BOOST_CHECK_EQUAL(last, size_t(10));
      BOOST_CHECK_EQUAL(step, size_t(2));
    }

    {
      first = 1;
      last = 10;
      step = 2;
      mergeRanges<size_t>(first, last, step, 2, 2, 2);

      BOOST_CHECK_EQUAL(first, size_t(1));
      BOOST_CHECK_EQUAL(last, size_t(10));
      BOOST_CHECK_EQUAL(step, size_t(1));
    }

    {
      first = 1;
      last = 10;
      step = 2;
      mergeRanges<size_t>(first, last, step, 15, 16, 2);

      BOOST_CHECK_EQUAL(first, size_t(1));
      BOOST_CHECK_EQUAL(last, size_t(16));
      BOOST_CHECK_EQUAL(step, size_t(2));
    }

    {
      first = 1;
      last = 10;
      step = 2;
      mergeRanges<size_t>(first, last, step, 16, 17, 1);

      BOOST_CHECK_EQUAL(first, size_t(1));
      BOOST_CHECK_EQUAL(last, size_t(17));
      BOOST_CHECK_EQUAL(step, size_t(1));
    }
    {
      first = 1;
      last =  9;
      step =  2;
      mergeRanges<size_t>(first, last, step, 1, 9, 2);

      BOOST_CHECK(comparable<size_t>(first,1));
      BOOST_CHECK(comparable<size_t>(last, 9));
      BOOST_CHECK(comparable<size_t>(step, 2));
    }
  }

  // test made to discover gcdFloat bugs in _MSC_VER release mode
  BOOST_CHECK_EQUAL(gcd<size_t>(2, 2), size_t(2));
  BOOST_CHECK_EQUAL(gcd<size_t>(18, 12), size_t(6));
  BOOST_CHECK(comparable<double>(gcd<double>(0.2, 0.2), 0.2));
  BOOST_CHECK(comparable<float>(gcd<float>(0.2f, 0.2f), 0.2f));
  BOOST_CHECK(comparable<float>(gcd<float>(0.18f, 0.12f), 0.06f));
  BOOST_CHECK(comparable<float>(gcd<float>(0.01f, 0.01f), 0.01f));

  {
    // Floating points tests.
    float first, last, step;

    {
      // Because of bug, only in release mode.
      // result:     /cumulative probabilities[0.01, 0.99, -2.14748e-07]
      first = 0.01f;
      last = 0.99f;
      step = 0.01f;

      mergeRanges<float>(first, last, step, 0.01f, 0.99f, 0.01f);
      BOOST_CHECK(comparable<float>(first, 0.01f));
      BOOST_CHECK(comparable<float>(last, 0.99f));
      BOOST_CHECK(comparable<float>(step, 0.01f));
    }

    {
      first = 0.1f;
      last =  0.9f;
      step =  0.2f;
      mergeRanges<float>(first, last, step, 0.1f, 0.9f, 0.2f);

      BOOST_CHECK(comparable<float>(first, 0.1f));
      BOOST_CHECK(comparable<float>(last, 0.9f));
      BOOST_CHECK(comparable<float>(step, 0.2f));
    }

    {
      first = 0.1f;
      last =  0.9f;
      step =  0.2f;
      mergeRanges<float>(first, last, step, 0.1f, 0.1f, 0.2f);

      BOOST_CHECK(comparable<float>(first, 0.1f));
      BOOST_CHECK(comparable<float>(last, 0.9f));
      BOOST_CHECK(comparable<float>(step, 0.2f));
    }

    {
      first = 0.1f;
      last =  0.9f;
      step =  0.2f;
      mergeRanges<float>(first, last, step, 0.2f, 0.2f, 0.2f);

      BOOST_CHECK(comparable<float>(first, 0.1f));
      BOOST_CHECK(comparable<float>(last, 0.9f));
      BOOST_CHECK(comparable<float>(step, 0.1f));
    }

    {
      first = 0.1f;
      last =  0.9f;
      step =  0.2f;
      mergeRanges<float>(first, last, step, 0.01f, 0.99f, 0.01f);

      BOOST_CHECK(comparable<float>(first, 0.01f));
      BOOST_CHECK(comparable<float>(last, 0.99f));
      BOOST_CHECK(comparable<float>(step, 0.01f));
    }

    {
      first = 0.1f;
      last =  0.9f;
      step =  0.2f;
      mergeRanges<float>(first, last, step, 0.01f, 0.01f, 0.01f);

      BOOST_CHECK(comparable<float>(first, 0.01f));
      BOOST_CHECK(comparable<float>(last, 0.9f));
      BOOST_CHECK(comparable<float>(step, 0.01f));
    }

    {
      first = 0.1f;
      last =  0.9f;
      step =  0.2f;
      mergeRanges<float>(first, last, step, 0.99f, 0.99f, 0.01f);

      BOOST_CHECK(comparable<float>(first, 0.1f));
      BOOST_CHECK(comparable<float>(last, 0.99f));
      BOOST_CHECK(comparable<float>(step, 0.01f));
    }

    {
      first = 0.1f;
      last =  0.9f;
      step =  0.2f;
      mergeRanges<float>(first, last, step, 0.9f, 0.9f, 0.01f);

      BOOST_CHECK(comparable<float>(first, 0.1f));
      BOOST_CHECK(comparable<float>(last, 0.9f));
      // Fails on windows only?
      BOOST_CHECK(comparable<float>(step, 0.2f));
    }
  }
}

BOOST_AUTO_TEST_CASE(round_)
{
  using namespace dal;

  BOOST_CHECK_EQUAL((round<float, int>(0.0f )), 0);
  BOOST_CHECK_EQUAL((round<float, int>(0.4f )), 0);
  BOOST_CHECK_EQUAL((round<float, int>(0.5f )), 0);
  BOOST_CHECK_EQUAL((round<float, int>(0.51f)), 1);
  BOOST_CHECK_EQUAL((round<float, int>(0.6f )), 1);

  BOOST_CHECK_EQUAL((round<float, int>(-0.4f )),  0);
  BOOST_CHECK_EQUAL((round<float, int>(-0.5f )),  0);
  BOOST_CHECK_EQUAL((round<float, int>(-0.51f)), -1);
  BOOST_CHECK_EQUAL((round<float, int>(-0.6f )), -1);

  BOOST_CHECK_EQUAL((round<float, size_t>(0.0f )), size_t(0));
  BOOST_CHECK_EQUAL((round<float, size_t>(0.4f )), size_t(0));
  BOOST_CHECK_EQUAL((round<float, size_t>(0.5f )), size_t(0));
  BOOST_CHECK_EQUAL((round<float, size_t>(0.51f)), size_t(1));
  BOOST_CHECK_EQUAL((round<float, size_t>(0.6f )), size_t(1));

  BOOST_CHECK_EQUAL((round<float, size_t>(-0.4f )), size_t(0));
  BOOST_CHECK_THROW((round<float, size_t>(-0.51f )), std::bad_cast);
}

BOOST_AUTO_TEST_CASE(rintf_)
{
  using namespace dal;

#ifdef _MSC_VER
  bool seeIfVS2005HasRintfC99=false;
  BOOST_WARN(seeIfVS2005HasRintfC99);
#endif
}

BOOST_AUTO_TEST_CASE(value_in_range)
{
  using namespace dal;

  BOOST_CHECK(valueInRange(0, 10, 2, 4));
  BOOST_CHECK(!valueInRange(0, 10, 2, 5));

  BOOST_CHECK(valueInRange(0.0, 1.0, 0.2, 0.4));
  BOOST_CHECK(!valueInRange(0.0, 1.0, 0.2, 0.5));

  BOOST_CHECK(valueInRange(0.0f, 1.0f, 0.2f, 0.4f));
  BOOST_CHECK(!valueInRange(0.0f, 1.0f, 0.2f, 0.5f));
}

BOOST_AUTO_TEST_CASE(clockwise_angle)
{
  using namespace dal;

  BOOST_CHECK_CLOSE(clockwiseAngle( 3.0,  4.0),         36.8699, 0.001);
  BOOST_CHECK_CLOSE(clockwiseAngle( 4.0, -3.0),  90.0 + 36.8699, 0.001);
  BOOST_CHECK_CLOSE(clockwiseAngle(-3.0, -4.0), 180.0 + 36.8699, 0.001);
  BOOST_CHECK_CLOSE(clockwiseAngle(-4.0,  3.0), 270.0 + 36.8699, 0.001);

  BOOST_CHECK_CLOSE(clockwiseAngle( 0.0,  4.0),   0.0, 0.001);
  BOOST_CHECK_CLOSE(clockwiseAngle( 4.0,  0.0),  90.0, 0.001);
  BOOST_CHECK_CLOSE(clockwiseAngle( 0.0, -4.0), 180.0, 0.001);
  BOOST_CHECK_CLOSE(clockwiseAngle(-4.0,  0.0), 270.0, 0.001);

  BOOST_CHECK_CLOSE(clockwiseAngle(0.0, 0.0), 0.0, 0.001);
}


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

#include "detection.hpp"
#include "match.hpp"
#include "tools.hpp"

#include <iostream>
#include <iterator>
#include <stdio.h>

#include <boost/filesystem.hpp>

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

#include <cmath>

#define RESET_COLOR "\e[m"
#define MAKE_RED "\e[31m"

using namespace std;
using namespace cv;
namespace fs = boost::filesystem;

const static Scalar colors[] =  { 	CV_RGB(0,0,255),
		CV_RGB(0,128,255),
		CV_RGB(0,255,255),
		CV_RGB(0,255,0),
		CV_RGB(255,128,0),
		CV_RGB(255,255,0),
		CV_RGB(255,0,0),
		CV_RGB(255,0,255)};

/*
 * Detect an image with Mat#detect(Mat&, CascadeClassifier&, double)
 * an shows it in a window named windowName
 */
void Detection::detectAndDraw( Mat * img, CascadeClassifier& cascade, double scale, string windowName)
{
	Mat result = detectMat(img, cascade, scale);
	cv::imshow(windowName, result);
}

/*
 * Detects objects in img and returns a vector of rectangles of object regions
 */
vector<Rect> Detection::detect(Mat *img, CascadeClassifier &cascade, double scale)
{
	vector<Rect> objects;
	Mat gray;
	Mat smallImg( cvRound (img->rows/scale), cvRound(img->cols/scale), CV_8UC1 );

	cvtColor(*img, gray, CV_BGR2GRAY);
	resize(gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR);
	equalizeHist(smallImg, smallImg);

	cascade.detectMultiScale(smallImg, objects,
			1.1, 2, 0
			//|CV_HAAR_FIND_BIGGEST_OBJECT
			//|CV_HAAR_DO_ROUGH_SEARCH
			|CV_HAAR_SCALE_IMAGE,
			Size(30, 30));
	return objects;
}

/*
 * Returns probability that the detected object in  imga = imgb
 * with CascadeClassifier cascade and int scaleHI, scaleLO
 * probability is from range <0, 1>
 */
double Detection::probability(Mat *imga, Mat *imgb, CascadeClassifier &cascade, const int scaleLO, const int scaleHI) { //implemented according to whiteboard, needs optimization

	int probTrue = 0;
	int counterAll = 0;

	double dscale = (double) scaleLO/100;

	int scale = 1;
	vector<Rect> imgaObj = detect(imga, cascade, scale);
	vector<Rect> imgbObj = detect(imgb, cascade, scale);

	Rect detectedA = imgaObj.front();
	Rect detectedB = imgbObj.front();
	//

	Mat cropped;
	RotatedRect sceneCornersRect;
	vector<Point2f> scene_corners;
	//
	for (int i=scaleLO; i < scaleHI; i++) {
		cropped = Detection::crop(imga, detectedA,dscale);
		scene_corners = Match::sceneCornersGoodMatches(&cropped, imgb, true);

		if(scene_corners.empty()) {
			Tools::debugMessage("SCENE_CORNERS EMPTY");
			counterAll++;
			continue;
		}

		sceneCornersRect = minAreaRect(scene_corners);

		Size2f sizeSCR = Size_<float>(sceneCornersRect.size);
		Size2f sizeCropped = Size_<float>((float) cropped.cols, (float) cropped.rows);

		if(Detection::evaluatef(sizeSCR.area(), sizeCropped.area())) {
			probTrue++;
			//cout << "TRUE:	"<< "SizeSCR=" << sizeSCR.area() << "	;SizeCropped=" << sizeCropped.area() << endl;
		}

		counterAll++;
	}

	dscale = (double) scaleLO/100;
	for (int i=scaleLO; i < scaleHI; i++) {
		cropped = Detection::crop(imgb, detectedB,dscale);
		scene_corners = Match::sceneCornersGoodMatches(&cropped, imga, true);

		if(scene_corners.empty()) { //todo: works?
			Tools::debugMessage("SCENE_CORNERS EMPTY");
			counterAll++;
			continue;
		}

		sceneCornersRect = minAreaRect(scene_corners);

		Size2f sizeSCR = Size_<float>(sceneCornersRect.size);
		Size2f sizeCropped = Size_<float>((float) cropped.cols, (float) cropped.rows);

		if(Detection::evaluatef(sizeSCR.area(), sizeCropped.area())) {
			probTrue++;
			//cout << "TRUE:	"<< "SizeSCR=" << sizeSCR.area() << "	;SizeCropped=" << sizeCropped.area() << endl;
		}

		counterAll++;
	}

	double dProbTrue = (double) probTrue;
	double prob = (double) dProbTrue/counterAll;

	//cout << "Prob=" << prob << "	;ProbTrue=" << dProbTrue << "	;CounterAll=" << counterAll << endl;

	return prob;
}

/*
 * Detects objects in img, draws rectangles around them and returns the img
 */
Mat Detection::detectMat(Mat *img, CascadeClassifier &cascade, double scale) {

	int i = 0;
	vector<Rect> objects = detect(img, cascade, scale);

	for( vector<Rect>::const_iterator r = objects.begin(); r != objects.end(); r++, i++ )
	{
		Point center;
		Scalar color = colors[i%8];
		int radius;

		double aspect_ratio = (double)r->width/r->height;
		if( 0.75 < aspect_ratio && aspect_ratio < 1.3 )
		{
			center.x = cvRound((r->x + r->width*0.5)*scale);
			center.y = cvRound((r->y + r->height*0.5)*scale);
			radius = cvRound((r->width + r->height)*0.25*scale);
			circle( *img, center, radius, color, 3, 8, 0 );
		}
		else {
			rectangle( *img, cvPoint(cvRound(r->x*scale), cvRound(r->y*scale)),
					cvPoint(cvRound((r->x + r->width-1)*scale), cvRound((r->y + r->height-1)*scale)),
					color, 3, 8, 0);
		}
	}
	return *img;
}

/*
 * Returns true if detected object is in img, false if not.
 *
 */
bool Detection::isDetected(Mat *img, CascadeClassifier &cascade, double scale)
{
	return (countDetected(img, cascade, scale) > 0 ? true : false);
}

/*
 * Returns the number of detected objects in img
 */
int Detection::countDetected(Mat *img, CascadeClassifier &cascade, double scale)
{
	return detect(img, cascade, scale).size();
}

/*
 * Returns true if successfully sorted
 */
bool Detection::detectAndSort(Mat *img, CascadeClassifier &cascade, double scale, string posdir, string negdir, string filename)
{
	fs::path filenamePath = filename;
	//filenamePath = fs::absolute(filenamePath);
	fs::path posdirPath = posdir;
	//posdirPath = fs::absolute(posdirPath);
	fs::path negdirPath = negdir;
	//negdirPath = fs::absolute(negdirPath);

	if (!fs::exists(posdirPath) || !fs::is_directory(posdirPath)) {
		fs::create_directory(posdirPath);
	}

	if (!fs::exists(negdirPath) || fs::is_directory(negdirPath)) {
		fs::create_directory(negdirPath);
	}

	if (isDetected(img, cascade, scale)) {
		posdirPath/=filename;
		Mat rect = detectMat(img, cascade, scale);
		//cout << "Moving image " << filenamePath.c_str() << " to " << posdirPath.c_str() << endl;
		cout << "COPY: " << filenamePath << " to: " << posdirPath << endl;
		imwrite(posdirPath.generic_string(), rect);
		//fs::copy_file(filename, posdirPath);
	} else {
		negdirPath/=filename;
		//cout << "Moving image " << filenamePath.c_str() << " to " << negdirPath.c_str() << endl;
		cout << "COPY: " << filenamePath << " to: " << negdirPath << endl;
		fs::copy_file(filename, negdirPath);
	}
	return true;
}

/*
 * Scales the roi and scales it
 * Note!!! scale must be < 1
 */
Mat Detection::crop(Mat *img, Rect &roi, double &scale) {
	Rect scaledROI = scaleRect(roi, scale);
	Mat ret = (*img)(scaledROI);
	return ret;

}

/*
 * Calculates the center point of Rect r
 */
Point2d Detection::center(Rect r) {
	Point2d p;
	p.x = r.x+((r.width)/2);
	p.y = r.y+((r.height)/2);
	return p;
}

bool Detection::isInRect(Rect is, Rect in) { //should work
	bool inside = false;

	list<Point2d> isBoundary;
	list<Point2d> inBoundary;
	Point2d p;

	//is:
	for(int x = is.x; x < is.x+is.width; x++) {
		p = Point2d(x, is.y);
		isBoundary.push_back(p);
	}

	for(int x = is.x; x < is.x+is.width; x++) {
		p = Point2d(x, is.y+is.height);
		isBoundary.push_back(p);
	}

	for(int y = is.y; y < is.y+is.width; y++) {
		p = Point2d(is.x, y);
		isBoundary.push_back(p);
	}

	for(int y = is.y; y < is.y+is.width; y++) {
		p = Point2d(is.x+is.width, y);
		isBoundary.push_back(p);
	}

	//in:
	for(int x = in.x; x < in.x+in.width; x++) {
		p = Point2d(x, in.y);
		inBoundary.push_back(p);
	}

	for(int x = in.x; x < in.x+in.width; x++) {
		p = Point2d(x, in.y+in.height);
		inBoundary.push_back(p);
	}

	for(int y = in.y; y < in.y+in.width; y++) {
		p = Point2d(in.x, y);
		inBoundary.push_back(p);
	}

	for(int y = in.y; y < in.y+in.width; y++) {
		p = Point2d(in.x+in.width, y);
		inBoundary.push_back(p);
	}

	list<Point2d>::iterator temp;

	for (list<Point2d>::iterator i = isBoundary.begin(); i != isBoundary.end(); i++) {
		temp = find(inBoundary.begin(), inBoundary.end(), *i);

		if (temp != inBoundary.end()) {
			inside = true;
			break;
		}
	}

	for (list<Point2d>::iterator i = inBoundary.begin(); i != inBoundary.end(); i++) {
		temp = find(isBoundary.begin(), isBoundary.end(), *i);

		if (temp != isBoundary.end()) {
			inside = true;
			break;
		}
	}

	return inside;
}

/*
 * Scales the given rect to keep ~ the same center point, just be scaled
 */
Rect Detection::scaleRect(Rect roi, double scale) {
	Rect scaledROI;

	scaledROI.height = roi.height*scale;
	scaledROI.width = roi.width*scale;

	Point2d croi = center(roi);
	scaledROI.x = croi.x-((roi.width*scale)/2);
	scaledROI.y = croi.y-((roi.height*scale)/2);
	//scaledROI.x = roi.x+(roi.width*(1-scale));
	//scaledROI.y = roi.y+(roi.height*(1-scale));

	return scaledROI;
}

/*
 * Prints info about the rectangle to cout
 */
void Detection::coutp(string name, Rect roi) {
	cout << name.c_str() << ":		" << "Point[" << roi.x << ", " << roi.y << "];height=" << roi.height << ";width=" << roi.width << endl;
}

/*
void Detection::testCropping(Mat &forcrop, Mat &comp, CascadeClassifier &cascade, double &scale) {
	vector<Rect> forcropObj = detect(forcrop, cascade, scale);
	vector<Rect> compObj = detect(comp, cascade, scale);

	Rect roi = forcropObj.front();
	Match m;
	Mat lol;

	cvNamedWindow("forcropcropped");
	cvNamedWindow("matched");

	int start = 85;
	int limit = 90;

	for(int i = start; i < limit; i++) {
		double di = (double) i;
		cout << di << endl;
		double scale = di/limit;
		cout << scale << endl;

		Mat cropped = crop(forcrop, roi, scale);
		imshow("forcropcropped", cropped);

		lol = m.matGoodMatches(cropped, comp, true);
		imshow("matched", lol);
		waitKey(0);

		Mat img_matches = comp.clone(); //(Size(1000,1000), CV_8UC1);
		Mat img_object = comp.clone();
		vector<Point2f> scene_corners = m.sceneCornersGoodMatches(cropped, comp, true);
		cout << scene_corners << endl;
		//-- Draw lines between the corners (the mapped object in the scene - image_2 )
			  line( img_matches, scene_corners[0] + Point2f( img_object.cols, 0), scene_corners[1] + Point2f( img_object.cols, 0), Scalar(0, 255, 0), 4 );
			  line( img_matches, scene_corners[1] + Point2f( img_object.cols, 0), scene_corners[2] + Point2f( img_object.cols, 0), Scalar( 0, 255, 0), 4 );
			  line( img_matches, scene_corners[2] + Point2f( img_object.cols, 0), scene_corners[3] + Point2f( img_object.cols, 0), Scalar( 0, 255, 0), 4 );
			  line( img_matches, scene_corners[3] + Point2f( img_object.cols, 0), scene_corners[0] + Point2f( img_object.cols, 0), Scalar( 0, 255, 0), 4 );
		imshow("forcropcropped", img_matches);
		waitKey(0);
	}
	cvDestroyAllWindows();
	return;
}
 */

bool Detection::evaluatef(const float a, const float b) {
	float absA = fabsf(a);
	float absB = fabsf(b);
	float absdiff = absA > absB ? absA-absB : absB-absA ;

	if (absdiff < 5000) {
		return true;
	}
	else {
		return false;
	}
}


/*******************************************************************************
*
*  Filename    : Parameter_Formatting.cc
*  Description : String Formatting functions for Parameter class
*  Author      : Yi-Mu "Enoch" Chen [ ensc@hep1.phys.ntu.edu.tw ]
*
*  Using boost::format and std::regex for proper styling
*
*******************************************************************************/
#include "ManagerUtils/Maths/interface/Parameter.hpp"

#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include <regex>
#include <string>

using namespace std;

namespace mgr
{
/*******************************************************************************
*   Double variable string representations
*******************************************************************************/
string
FloatingPoint( double x, const int precision, const bool spaceflag )
{
  static const int max_precision = 8;
  static const int max_digits    = 27;

  boost::format fltfmt =
    ( precision < 0  || precision >= max_precision ) ?
    boost::format( boost::str( boost::format( "%%.%dlf" ) % max_precision ) ) :
    boost::format( boost::str( boost::format( "%%.%df" ) % precision ) );

  string retstr = boost::str( fltfmt % x );

  // stripping trailing zero after decimal point
  if( precision < 0 && retstr.find( '.' ) != string::npos ){
    boost::trim_right_if( retstr, boost::is_any_of( "0" ) );
  }
  // stripping trailing decimal point
  boost::trim_right_if( retstr, boost::is_any_of( "." ) );

  // Add latex "\," spacing every 3 digits away from decimal point
  for( int space = max_digits; space > 0 && spaceflag; space = space-3 ){
    if( retstr.find( '.' ) != string::npos ){
      // If decimal point exists, expand around decimal point
      boost::format bfexp( "(.*\\d)(\\d{%d}\\..*)" );
      boost::format afexp( "(.*\\.\\d{%d})(\\d.*)" );
      const std::regex beforedec( boost::str( bfexp % space ) );
      const std::regex afterdec( boost::str( afexp % space ) );
      retstr = std::regex_replace( retstr, beforedec, "$1\\,$2" );
      retstr = std::regex_replace( retstr, afterdec,  "$1\\,$2" );
    } else {
      // If decimal point doesnt exist, expand around right most side
      boost::format bfexp( "(.*\\d)(\\d{%d})" );
      const std::regex beforedec( boost::str( bfexp % space ) );
      retstr = std::regex_replace( retstr, beforedec, "$1\\,$2" );
    }
  }

  return retstr;
}

/******************************************************************************/

string
Scientific( double x, const unsigned precision, const bool space )
{
  // Getting exponent
  int exp = 0;

  while( x && fabs( x ) > 10 ){
    x /= 10;
    exp++;
  }

  while( x && fabs( x ) < 1 ){
    x *= 10;
    exp--;
  }

  if( exp ){
    return boost::str( boost::format( "%s\\times10^{%d}" )
      % FloatingPoint( x, precision, space )
      % exp
      );
  } else {
    return boost::str( boost::format( "%s" ) % FloatingPoint( x, precision, space ) );
  }
}

/*******************************************************************************
*   Floating point formating options
*******************************************************************************/
string
FloatingPoint( const Parameter& x,  const int precision, const bool space )
{
  string ans;

  const string cen = FloatingPoint( x.CentralValue(),  precision, space );
  const string up  = FloatingPoint( x.AbsUpperError(), precision, space );
  const string lo  = FloatingPoint( x.AbsLowerError(), precision, space );

  boost::format symmfmt( "%s\\pm%s" );
  boost::format asymfmt( "%s^{+%s}_{-%s}" );

  if( x.AbsUpperError() == x.AbsLowerError() ){
    if( x.AbsUpperError() == 0. ){
      ans = cen;
    } else {
      ans = str( symmfmt % cen % up );
    }
  } else {
    ans = str( asymfmt % cen % up % lo );
  }
  return ans;
}

/******************************************************************************/

string
Scientific( const Parameter& x, const unsigned precision, const bool space )
{
  boost::format symmbase( "%s\\pm%s" );
  boost::format asymbase( "%s^{+%s}_{-%s}" );

  boost::format symmexpfmt( "(%s)\\times10^{%d}" );
  boost::format asymexpfmt( "%s\\times10^{%d}" );

  string base;
  string ans;
  int exponent = 0;

  double cen = x.CentralValue();
  double up  = x.AbsUpperError();
  double lo  = x.AbsLowerError();

  if( cen != 0 ){
    while( fabs( cen ) > 10 ){
      exponent++;
      cen /= 10;
      up  /= 10;
      lo  /= 10;
    }

    while( fabs( cen ) < 1.  ){
      exponent--;
      cen *= 10;
      up  *= 10;
      lo  *= 10;
    }
  } else if( up != 0 || lo != 0 ){
    while( fabs( lo ) > 1 && fabs( up ) > 1 ){
      exponent++;
      cen /= 10;
      up  /= 10;
      lo  /= 10;
    }

    while( fabs( lo ) < 0.1  && fabs( up ) < 0.1 ){
      exponent--;
      cen *= 10;
      up  *= 10;
      lo  *= 10;
    }
  }

  const string censtr = FloatingPoint( cen, precision, space );
  const string upstr  = FloatingPoint( up, precision, space );
  const string lostr  = FloatingPoint( lo, precision, space );

  if( up == lo ){
    if( up == 0 ){
      base = censtr;
    } else {
      base = str( symmbase % censtr % upstr );
    }
  } else {
    base = str( asymbase % censtr % upstr % lostr );
  }

  if( exponent == 0 ){
    ans = base;
  } else {
    if( up == lo && up != 0 ){// Special case for symmetric errors
      ans = str( symmexpfmt % base % exponent );
    } else {
      ans = str( asymexpfmt %  base% exponent );
    }
  }
  return ans;
}

/******************************************************************************/

string
HiggsDataCard( const Parameter& x, const double scale )
{
  static const string null = "--";
  boost::format symmfmt( "%lf" );
  boost::format asymfmt( "%lf/%lf" );

  if( x.CentralValue() == 0 &&
      x.AbsUpperError() == 0 &&
      x.AbsLowerError() == 0 ){
    return null;
  } else if( x.AbsUpperError() == x.AbsLowerError() ){
    return str( symmfmt % ( 1+scale*x.RelAvgError() ) );
  } else {
    return str( asymfmt % ( 1.+ scale*x.RelUpperError() ) %  ( 1.- scale*x.RelLowerError() ) );
  }
  return null;
}

}/* mgr */


/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2014, 2015 Johannes Göttker-Schnetmann
 Copyright (C) 2014, 2015 Klaus Spanderen

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

#ifndef quantlib_test_heston_slv_model_hpp
#define quantlib_test_heston_slv_model_hpp

#include <boost/test/unit_test.hpp>
#include "speedlevel.hpp"

/* remember to document new and/or updated tests in the Doxygen
   comment block of the corresponding class */

class HestonSLVModelTest {
public:
    static void testBlackScholesFokkerPlanckFwdEquation();
    static void testSquareRootZeroFlowBC();
    static void testTransformedZeroFlowBC();
    static void testSquareRootEvolveWithStationaryDensity();
    static void testSquareRootLogEvolveWithStationaryDensity();
    static void testSquareRootFokkerPlanckFwdEquation();
    static void testHestonFokkerPlanckFwdEquation();
    static void testHestonFokkerPlanckFwdEquationLogLVLeverage();
    static void testBlackScholesFokkerPlanckFwdEquationLocalVol();
    static void testFDMCalibration();
    static void testLocalVolsvSLVPropDensity();
    static void testBarrierPricingViaHestonLocalVol();
    static void testBarrierPricingMixedModels();
    static void testMonteCarloVsFdmPricing();
    static void testMonteCarloCalibration();
    static void testMoustacheGraph();
    static void testForwardSkewSLV();

    static boost::unit_test_framework::test_suite* experimental(SpeedLevel);

};

#endif


// Canberra AccuSpec MCA
// Licence: Lesser GNU Public License 2.1 (LGPL)

#define BUILDING_XYLIB
#include "canberra_mca.h"

#include <cmath>
#include <boost/cstdint.hpp>

#include "util.h"

using namespace std;
using namespace xylib::util;
using boost::uint16_t;
using boost::uint32_t;

namespace xylib {

const FormatInfo CanberraMcaDataSet::fmt_info(
    "canberra_mca",
    "Canberra MCA",
    "mca dat",
    true,                       // whether binary
    false,                      // whether has multi-blocks
    &CanberraMcaDataSet::ctor,
    &CanberraMcaDataSet::check
);

bool CanberraMcaDataSet::check(istream &f, string*)
{
    const int file_size = 2*512+2048*4;
    char *all_data = new char[file_size];
    f.read(all_data, file_size);
    uint16_t word_at_0 = from_le<uint16_t>(all_data + 0);
    uint16_t word_at_34 = from_le<uint16_t>(all_data + 34);
    uint16_t word_at_36 = from_le<uint16_t>(all_data + 36);
    uint16_t word_at_38 = from_le<uint16_t>(all_data + 38);
    delete [] all_data;
    return f.gcount() == file_size
           && word_at_0 == 0
           && word_at_34 == 4
           && word_at_36 == 2048
           && word_at_38 == 1;
}

void CanberraMcaDataSet::load_data(std::istream &f)
{
    const int file_size = 2*512+2048*4;
    char *all_data = new char[file_size];
    f.read(all_data, file_size);
    if (f.gcount() != file_size) {
        delete [] all_data;
        throw FormatError("Unexpected end of file.");
    }

    double energy_offset = from_pdp11((unsigned char*) all_data + 108);
    double energy_slope = from_pdp11((unsigned char*) all_data + 112);
    double energy_quadr = from_pdp11((unsigned char*) all_data + 116);

    Block* blk = new Block;

    Column *xcol = NULL;
    if (energy_quadr) {
        VecColumn *vc = new VecColumn;
        for (int i = 1; i <= 2048; i++) {
            //FIXME should it be from 1 ?
            // perhaps from 0 to 2047, description was not clear.
            double x = energy_offset + energy_slope * i + energy_quadr * i * i;
            vc->add_val(x);
        }
        xcol = vc;
    }
    else {
        xcol = new StepColumn(energy_offset+energy_slope, energy_slope);
    }
    blk->add_column(xcol);

    VecColumn *ycol = new VecColumn;
    uint16_t data_offset = from_le<uint16_t>(all_data+24);
    for (int i = 0; i < 2048; i++) {
        uint32_t y = from_le<uint32_t>(all_data + data_offset + 4*i);
        ycol->add_val(y);
    }
    delete [] all_data;
    blk->add_column(ycol);

    add_block(blk);
}

} // namespace xylib


#define CATCH_CONFIG_MAIN

#include "KKSFreeEnergyFunctionDiluteBinary.h"

#include "catch.hpp"

#include <boost/optional/optional.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>

#include <string>

namespace pt = boost::property_tree;

TEST_CASE("Dilute binary KKS", "[dilute binary kks]")
{
    Thermo4PFM::EnergyInterpolationType energy_interp_func_type
        = Thermo4PFM::EnergyInterpolationType::PBG;
    Thermo4PFM::ConcInterpolationType conc_interp_func_type
        = Thermo4PFM::ConcInterpolationType::LINEAR;

    // read dilute alloy informaton
    pt::ptree conc_db;
    try
    {
        pt::read_json("dilute_binary.json", conc_db);
    }
    catch (std::exception& e)
    {
        std::cerr << "exception caught: " << e.what() << std::endl;
    }
    pt::write_json(std::clog, conc_db);

    Thermo4PFM::KKSFreeEnergyFunctionDiluteBinary cafe(
        conc_db, energy_interp_func_type, conc_interp_func_type);

    const Thermo4PFM::PhaseIndex pi0 = Thermo4PFM::PhaseIndex::phaseL;
    const Thermo4PFM::PhaseIndex pi1 = Thermo4PFM::PhaseIndex::phaseA;

    // initial guesses
    double c_init0 = 0.5;
    double c_init1 = 0.5;
    double sol[2]  = { c_init0, c_init1 };

    // solve KKS equations
    double conc        = 0.05;
    double phi         = 0.5;
    double temperature = 1710.;
    cafe.computePhaseConcentrations(temperature, &conc, &phi, &sol[0]);

    std::cout << "-------------------------------" << std::endl;
    std::cout << "Temperature = " << temperature << std::endl;
    std::cout << "Result for c = " << conc << " and phi = " << phi << std::endl;
    std::cout << "   cL = " << sol[0] << std::endl;
    std::cout << "   cS = " << sol[1] << std::endl;

    // verify concentrations satisfy equal chemical potentials
    std::cout << "Verification:" << std::endl;

    double derivL;
    double derivS;
    cafe.computeDerivFreeEnergy(temperature, &sol[0], pi0, &derivL);
    std::cout << "   dfL/dcL = " << derivL << std::endl;

    cafe.computeDerivFreeEnergy(temperature, &sol[1], pi1, &derivS);
    std::cout << "   dfS/dcS = " << derivS << std::endl;

    CHECK(derivS == Approx(derivL).margin(1.e-5));
}


/**
 ** Copyright (c) 2014 Illumina, Inc.
 **
 ** This file is part of Illumina's Enhanced Artificial Genome Engine (EAGLE),
 ** covered by the "BSD 2-Clause License" (see accompanying LICENSE file)
 **
 ** \author Lilian Janin
 **/

#ifndef EAGLE_MODEL_FRAGMENT_LENGTH_DIST_HH
#define EAGLE_MODEL_FRAGMENT_LENGTH_DIST_HH

#include <vector>
#include <boost/filesystem.hpp>

#include "common/Exceptions.hh"
#include "common/Logger.hh"
#include "io/Text.hh"

namespace eagle
{
namespace model
{

class GcPercentage
{
public:
    double operator[]( const unsigned int globalPos ) const
    {
        return 0.5;
    }
};

class CNV
{
public:
    double operator[]( const unsigned int globalPos ) const
    {
        return 1.0;
    }
};

class GcTrans
{
public:
    double operator[]( const double gcPercentage ) const
    {
        return 1.0;
    }
};

class BlockMatrix
{
public:
    double  operator[]( const unsigned long pos ) const { return val0_; }
    double& operator[]( const unsigned long pos )       { return val0_; }
    unsigned long size() const { return 0; }

private:
    double val0_;
};

class FragmentLengthDist
{
public:
    FragmentLengthDist( const boost::filesystem::path& filename );
    unsigned int min() const { return min_; }
    unsigned int max() const { return max_; }
    unsigned int size() const { return max() - min() + 1; }
    double operator[]( const unsigned int templateLength ) const
    {
        assert( templateLength >= min_ );
        assert( templateLength - min_ < templateLengthDist_.size() );
        return templateLengthDist_[ templateLength - min_ ];
    }

private:
    unsigned int min_;
    unsigned int max_;
    std::vector<double> templateLengthDist_;
};

class FragmentLengthProbabilityMatrix
{
public:
    FragmentLengthProbabilityMatrix( const unsigned long chrLength, const boost::filesystem::path& templateLengthTableFilename )
        : fragmentLengthDist_( templateLengthTableFilename )
        , P_FL_pos( new std::vector< double >() )
    {
        phase1( chrLength, fragmentLengthDist_ );
//        assert( P_FL_pos.get() != 0 );
    }
/*
    FragmentLengthProbabilityMatrix( const boost::filesystem::path& blockCoverageFilename, const boost::filesystem::path& templateLengthTableFilename )
        : fragmentLengthDist_( templateLengthTableFilename )
        , blockCoverage_( blockCoverageFilename )
    {
//        phase1( chrLength, fragmentLengthDist_ );
//        assert( P_FL_pos.get() != 0 );
    }
*/
    FragmentLengthProbabilityMatrix( const FragmentLengthProbabilityMatrix& obj )
        : fragmentLengthDist_( obj.fragmentLengthDist_ )
        , sum_P_FL_pos( obj.sum_P_FL_pos )
        , P_FL_pos( obj.P_FL_pos )
    {
//        assert( P_FL_pos.get() != 0 );
    }

    const std::vector< double >& getProbabilities() { return *P_FL_pos; };
//    const BlockMatrix& getProbabilities() { return P_FL_pos; };

    FragmentLengthDist fragmentLengthDist_;
//    BlockCoverage blockCoverage_;
    double sum_P_FL_pos;
    boost::shared_ptr< std::vector< double > > P_FL_pos;
//    BlockMatrix P_FL_pos;

private:
    void phase1( const unsigned long chrLength, const FragmentLengthDist& fragmentLengthDist );
};

} // namespace model
} // namespace eagle

#endif //EAGLE_MODEL_FRAGMENT_LENGTH_DIST_HH


#ifndef ASLAM_ERROR_TERM_DS_HPP
#define ASLAM_ERROR_TERM_DS_HPP

#include <sparse_block_matrix/sparse_block_matrix.h>
#include <boost/shared_ptr.hpp>
#include <aslam/Exceptions.hpp>
#include <sm/eigen/NumericalDiff.hpp>
#include <sm/timing/Timer.hpp>
#include <sm/eigen/matrix_sqrt.hpp>
#include <aslam/backend/ErrorTerm.hpp>

namespace aslam {
  namespace backend {
    /**
     * \class ErrorTermDs
     * \brief An implementation of a vector-valued error term.
     *
     * This class fills in some of the functionality needed for "normal", vector-valued error terms.
     * In most cases, one will want to derive from this class when implementing an error term.
     *
     */
    class ErrorTermDs : public aslam::backend::ErrorTerm {
    public:
      EIGEN_MAKE_ALIGNED_OPERATOR_NEW

      typedef Eigen::VectorXd error_t;
      typedef Eigen::MatrixXd inverse_covariance_t;

      ErrorTermDs(int dimensionErrorTerm);
      ~ErrorTermDs() override;

      /// \brief retrieve the error vector
      const error_t& error() const;

      /// \brief Get the square root of the inverse covariance matrix.
      /// To recover the inverse covariance matrix from this matrix,
      /// \f$ A \f$, use \f$ \mathbf A \mathbf A^T\f$
      const inverse_covariance_t& sqrtInvR() const;

      /// \brief the inverse covariance matrix.
      inverse_covariance_t invR() const;

      void getInvR(Eigen::MatrixXd& invR) const override;
      Eigen::MatrixXd vsInvR() const override;
      void vsSetInvR(const Eigen::MatrixXd& invR) override;

      void getWeightedJacobians(JacobianContainer& outJc, bool useMEstimator) override;
      void getWeightedError(Eigen::VectorXd& e, bool useMEstimator) const override;

      /// Check if Jacobians are finite
      void checkJacobiansFinite() const;
      /// Check if analytical and numerical Jacobians match
      void checkJacobiansNumerical(double tolerance = 1e-6);

    protected:

      /// \brief build the hessian.
      void buildHessianImplementation(SparseBlockMatrix& outHessian, Eigen::VectorXd& outRhs, bool useMEstimator) override;

      /// \brief Clear the Jacobians
      virtual void clearJacobians();

      /// \brief get the current value of the error.
      Eigen::VectorXd vsErrorImplementation() const override;

      size_t getDimensionImplementation() const override {
        return _dimensionErrorTerm;
      }

      // This probably has to have a jacobian container...
      // \todo make this fixed sized.
      JacobianContainerSparse<Eigen::Dynamic> _jacobians;

      /// \brief set the error vector.
      //void setError(const Eigen::MatrixBase<double> & e);
      void setError(const Eigen::VectorXd& e);

      /// \brief set the inverse covariance matrix. Note, this will compute the
      ///  square root of this matrix numerically. If you have error terms that
      ///  reuse the same uncertainty, consider computing the square root once and
      ///  calling setSqrtInvR()
      void setInvR(const Eigen::MatrixXd& invR);

      /// \brief sets the square root inverse covariance matrix.
      void setSqrtInvR(const Eigen::MatrixXd& sqrtInvR);

      /// \brief evaluate the squared error from the error vector and
      ///        square root covariance matrix.
      double evaluateChiSquaredError() const;

      /// Initializes the jacobian container with a new number of rows
      void resizeJacobianContainer(int nrows);

    protected:
      int _dimensionErrorTerm;
      //int _dimensionDesignVariables;

    private:
      /// \brief the error term. This must be filled in on a call to evaluateError()

      error_t _error;
      /// \brief the inverse uncertainty matrix.
      inverse_covariance_t _sqrtInvR;

      // swap to enable
      //typedef sm::timing::Timer Timer;
      // swap to disable
      typedef sm::timing::DummyTimer Timer;

      Timer _evalJacobianTimer;
      Timer _buildHessianTimer;
    };

  } // namespace backend
} // namespace aslam

#endif /* ASLAM_ERROR_TERM_DS_HPP */


#pragma once

#include <bit>
#include <chrono>
#include <cstdint>
#include <functional>
#include <locale>
#include <mutex>
#include <numeric>
#include <numbers>
#include <optional>
#include <stdexcept>
#include <stdlib.h>
#include <thread>
#include <typeindex>
#include <variant>

#include <iostream>
#include <fstream>

#include <bitset>
#include <string>
#include <forward_list>
#include <filesystem>
#include <list>
#include <map>
#include <vector>
#include <deque>
#include <queue>
#include <set>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <ranges>
#include <span>

#include <math.h>

#ifdef WIN32
#include <Windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <WS2tcpip.h>
#ifdef _DEBUG
#include <crtdbg.h>
#endif
#undef near
#undef far
#undef free

#ifdef STRATUM_EXPORTS
#define STRATUM_API __declspec(dllexport)
#else
#define STRATUM_API __declspec(dllimport)
#endif

#ifdef STRATUM_PLUGIN_EXPORTS
#define PLUGIN_API __declspec(dllexport)
#else
#define PLUGIN_API __declspec(dllimport)
#endif

#endif // #ifdef WIN32

#ifdef __linux
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <dlfcn.h>
#pragma GCC diagnostic ignored "-Wformat-security"
#define STRATUM_API
#define PLUGIN_API
#endif // #ifdef __linux

#include <Eigen/Dense>

#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
#include <vulkan/vulkan.hpp>


#ifndef draw_utils_hpp
#define draw_utils_hpp

#include <three-dim-util/opengl2/gl.hpp>
#include <three-dim-util/opengl2/gl-wrappers.hpp>
#include <three-dim-util/opengl2/primitives/cube.hpp>
#include <three-dim-util/opengl2/primitives/sphere.hpp>
#include <Eigen/Core>
#include <Eigen/Geometry>

namespace threedimutil
{
    inline void draw_point_with_border(const Eigen::Vector3d& p, double size = 12.0)
    {
        GLint function;
        gl()->glGetIntegerv(GL_DEPTH_FUNC, &function);

        GLdouble color[4];
        gl()->glGetDoublev(GL_CURRENT_COLOR, color);

        gl()->glDepthFunc(GL_LEQUAL);

        gl()->glPointSize(size);
        gl()->glColor3d(1.0, 1.0, 1.0);
        gl()->glBegin(GL_POINTS);
        vertex_3d(p);
        gl()->glEnd();

        glPointSize(size * 0.8);
        gl()->glColor4dv(color);
        gl()->glBegin(GL_POINTS);
        vertex_3d(p);
        gl()->glEnd();

        gl()->glDepthFunc(function);
    }

    inline void draw_point(const Eigen::Vector3d& p, double size = 12.0)
    {
        gl()->glPointSize(size);
        gl()->glBegin(GL_POINTS);
        vertex_3d(p);
        gl()->glEnd();
    }

    inline void draw_points(const Eigen::MatrixXd& P, const Eigen::MatrixXd& C, double size = 12.0)
    {
        assert(P.rows() == 3);
        assert(C.rows() == 3);
        assert(P.cols() == C.cols());

        const int num_points = static_cast<int>(P.cols());

        gl()->glPointSize(size);
        gl()->glEnableClientState(GL_VERTEX_ARRAY);
        gl()->glEnableClientState(GL_COLOR_ARRAY);
        gl()->glVertexPointer(3, GL_DOUBLE, 0, P.data());
        gl()->glColorPointer(3, GL_DOUBLE, 0, C.data());
        gl()->glDrawArrays(GL_POINTS, 0, num_points);
        gl()->glDisableClientState(GL_VERTEX_ARRAY);
        gl()->glDisableClientState(GL_COLOR_ARRAY);
    }

    inline void draw_edges(const Eigen::MatrixXd& P, const Eigen::MatrixXi& E)
    {
        assert(P.rows() == 3);
        assert(E.rows() == 2);
        gl()->glBegin(GL_LINES);
        for (int i = 0; i < E.cols(); ++ i)
        {
            vertex_3d(P.col(E(0, i)));
            vertex_3d(P.col(E(1, i)));
        }
        gl()->glEnd();
    }

    inline void draw_mesh(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F)
    {
        assert(V.rows() == 3);
        assert(F.rows() == 3);
        gl()->glBegin(GL_TRIANGLES);
        for (int i = 0; i < F.cols(); ++ i)
        {
            vertex_3d(V.col(F(0, i)));
            vertex_3d(V.col(F(1, i)));
            vertex_3d(V.col(F(2, i)));
        }
        gl()->glEnd();
    }

    inline void draw_cube(const Eigen::Vector3d& t, double x, double y, double z)
    {
        const GLboolean is_gl_normalize_enabled = gl()->glIsEnabled(GL_NORMALIZE);
        gl()->glEnable(GL_NORMALIZE);
        gl()->glMatrixMode(GL_MODELVIEW);
        gl()->glPushMatrix();
        translate(t);
        gl()->glScaled(x, y, z);
        Cube::Draw();
        gl()->glPopMatrix();
        if (!is_gl_normalize_enabled) { gl()->glDisable(GL_NORMALIZE); }
    }

    inline void draw_cube(double size = 1.0)
    {
        draw_cube(Eigen::Vector3d::Zero(), size, size, size);
    }

    inline void draw_circle(double radius, int resolution = 30)
    {
        Eigen::MatrixXd vertices(2, resolution);
        for (int i = 0; i < resolution; ++ i)
        {
            constexpr double pi = M_PI;
            const double theta = 2.0 * static_cast<double>(i) * pi / static_cast<double>(resolution);
            vertices.col(i) = radius * Eigen::Vector2d(std::cos(theta), std::sin(theta));
        }
        Eigen::MatrixXd normals = Eigen::MatrixXd::Zero(3, resolution);
        normals.row(2) = Eigen::VectorXd::Ones(resolution);

        gl()->glEnableClientState(GL_VERTEX_ARRAY);
        gl()->glEnableClientState(GL_NORMAL_ARRAY);
        gl()->glVertexPointer(2, GL_DOUBLE, 0, vertices.data());
        gl()->glNormalPointer(GL_DOUBLE, 0, normals.data());
        gl()->glDrawArrays(GL_TRIANGLE_FAN, 0, resolution);
        gl()->glDisableClientState(GL_VERTEX_ARRAY);
        gl()->glDisableClientState(GL_NORMAL_ARRAY);
    }

    inline void draw_cylinder(double radius, double height, int resolution = 30)
    {
        constexpr double pi = M_PI;

        static Eigen::MatrixXd vertices;
        static Eigen::MatrixXd normals;

        // Create the vertex data if this is the first draw call or the resolution is specified differently from the last time
        if (vertices.cols() != resolution * 4)
        {
            vertices.resize(3, resolution * 4);
            normals.resize(3, resolution * 4);

            for (int i = 0; i < resolution; ++ i)
            {
                const double theta_1 = 2.0 * static_cast<double>(i + 0) * pi / static_cast<double>(resolution);
                const double theta_2 = 2.0 * static_cast<double>(i + 1) * pi / static_cast<double>(resolution);
                const double cos_1   = std::cos(theta_1);
                const double cos_2   = std::cos(theta_2);
                const double sin_1   = std::sin(theta_1);
                const double sin_2   = std::sin(theta_2);

                vertices.col(i * 4 + 0) = Eigen::Vector3d(cos_1, sin_1, 0.0);
                vertices.col(i * 4 + 1) = Eigen::Vector3d(cos_2, sin_2, 0.0);
                vertices.col(i * 4 + 2) = Eigen::Vector3d(cos_2, sin_2, 1.0);
                vertices.col(i * 4 + 3) = Eigen::Vector3d(cos_1, sin_1, 1.0);

                normals.col(i * 4 + 0) = Eigen::Vector3d(cos_1, sin_1, 0.0);
                normals.col(i * 4 + 1) = Eigen::Vector3d(cos_2, sin_2, 0.0);
                normals.col(i * 4 + 2) = Eigen::Vector3d(cos_2, sin_2, 0.0);
                normals.col(i * 4 + 3) = Eigen::Vector3d(cos_1, sin_1, 0.0);
            }
        }

        gl()->glMatrixMode(GL_MODELVIEW);

        // Draw a cylinder
        const GLboolean is_gl_normalize_enabled = gl()->glIsEnabled(GL_NORMALIZE);
        gl()->glEnable(GL_NORMALIZE);
        gl()->glPushMatrix();
        gl()->glScaled(radius, radius, height);
        gl()->glEnableClientState(GL_VERTEX_ARRAY);
        gl()->glEnableClientState(GL_NORMAL_ARRAY);
        gl()->glVertexPointer(3, GL_DOUBLE, 0, vertices.data());
        gl()->glNormalPointer(GL_DOUBLE, 0, normals.data());
        gl()->glDrawArrays(GL_QUADS, 0, resolution * 4);
        gl()->glDisableClientState(GL_VERTEX_ARRAY);
        gl()->glDisableClientState(GL_NORMAL_ARRAY);
        gl()->glPopMatrix();
        if (!is_gl_normalize_enabled) { gl()->glDisable(GL_NORMALIZE); }

        // Draw circles at both ends
        gl()->glPushMatrix();
        gl()->glScaled(-1.0, 1.0, -1.0);
        draw_circle(radius, resolution);
        gl()->glScaled(-1.0, 1.0, -1.0);
        gl()->glTranslated(0.0, 0.0, height);
        draw_circle(radius, resolution);
        gl()->glPopMatrix();
    }

    inline void draw_cylinder(double radius, const Eigen::Vector3d& p_1, const Eigen::Vector3d& p_2, int resolution = 30)
    {
        const Eigen::Vector3d t   = p_2 - p_1;
        const Eigen::Vector3d t_0 = Eigen::Vector3d::UnitZ();
        const double          c   = t.dot(t_0) / t.norm();
        const double          q   = std::acos(c);
        const Eigen::Vector3d ax  = t_0.cross(t).normalized();
        const double          h   = t.norm();
        const Eigen::Matrix4d rot = [&]()
        {
            return std::isnan(q) ? Eigen::Matrix4d::Identity() : Eigen::Affine3d(Eigen::AngleAxisd(q, ax)).matrix();
        }();

        gl()->glMatrixMode(GL_MODELVIEW);
        gl()->glPushMatrix();
        translate(p_1);
        mult_matrix(rot);
        draw_cylinder(radius, h, resolution);
        gl()->glPopMatrix();
    }

    inline void draw_sphere(double radius)
    {
        const GLboolean is_gl_normalize_enabled = gl()->glIsEnabled(GL_NORMALIZE);
        gl()->glEnable(GL_NORMALIZE);
        gl()->glMatrixMode(GL_MODELVIEW);
        gl()->glPushMatrix();
        gl()->glScaled(radius, radius, radius);
        Sphere::Draw();
        gl()->glPopMatrix();
        if (!is_gl_normalize_enabled) { gl()->glDisable(GL_NORMALIZE); }
    }

    inline void draw_sphere(double radius, const Eigen::Vector3d& t)
    {
        gl()->glMatrixMode(GL_MODELVIEW);
        gl()->glPushMatrix();
        translate(t);
        draw_sphere(radius);
        gl()->glPopMatrix();
    }

    inline void draw_floor(double                 size       = 200.0,
                           int                    resolution = 20,
                           const Eigen::Vector3d& color_1    = Eigen::Vector3d(0.9, 0.9, 0.9),
                           const Eigen::Vector3d& color_2    = Eigen::Vector3d(0.8, 0.8, 0.8))
    {
        const GLboolean is_gl_lighting_enabled = gl()->glIsEnabled(GL_LIGHTING);
        gl()->glDisable(GL_LIGHTING);

        gl()->glBegin(GL_QUADS);
        for (int i = 0; i < resolution; ++ i)
        {
            const double x_m = - 0.5 * size + (i + 0) * size / static_cast<double>(resolution);
            const double x_p = - 0.5 * size + (i + 1) * size / static_cast<double>(resolution);

            for (int j = 0; j < resolution; ++ j)
            {
                const double z_m = - 0.5 * size + (j + 0) * size / static_cast<double>(resolution);
                const double z_p = - 0.5 * size + (j + 1) * size / static_cast<double>(resolution);

                if ((i + j) % 2 == 0) { color_3d(color_1); } else { color_3d(color_2); }

                gl()->glVertex3d(x_p, 0.0, z_p);
                gl()->glVertex3d(x_m, 0.0, z_p);
                gl()->glVertex3d(x_m, 0.0, z_m);
                gl()->glVertex3d(x_p, 0.0, z_m);
            }
        }
        gl()->glEnd();

        if (is_gl_lighting_enabled) { gl()->glEnable(GL_LIGHTING); }
    }

    inline void draw_rectangle(const Eigen::Vector2d& t, double width, double height)
    {
        gl()->glMatrixMode(GL_MODELVIEW);
        gl()->glPushMatrix();
        translate(t);
        gl()->glBegin(GL_QUADS);
        gl()->glNormal3d(0.0, 0.0, 1.0);
        gl()->glVertex2d(+ 0.5 * width, + 0.5 * height);
        gl()->glVertex2d(- 0.5 * width, + 0.5 * height);
        gl()->glVertex2d(- 0.5 * width, - 0.5 * height);
        gl()->glVertex2d(+ 0.5 * width, - 0.5 * height);
        gl()->glEnd();
        gl()->glPopMatrix();
    }
}

#endif /* draw_utils_hpp */