#include <algorithm>
#include <boost/functional/hash.hpp>
#include <functional>
#include <limits>
#include <queue>
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#include <doctest/doctest.h>

#include "solver_15_part2.h"

namespace {

using Coordinate = std::pair<unsigned int, unsigned int>;

class Vertex
{
private:
  Coordinate position_;
  unsigned int g_ = std::numeric_limits<unsigned int>::max();// Current lowest total cost to travel from start to this vertex (including this vertex's cost)
  unsigned int cost_;// Cost of entering this vertex

public:
  explicit Vertex(const Coordinate position, const unsigned int cost)
    : position_(position), cost_(cost) {}

  // Used for priority queue minimum
  friend bool operator>(const Vertex &lhs, const Vertex &rhs) { return lhs.g() > rhs.g(); }

  [[nodiscard]] Coordinate position() const
  {
    return position_;
  }

  unsigned int &g() { return g_; }
  [[nodiscard]] unsigned int g() const { return g_; }

  unsigned int &cost() { return cost_; }
};

std::vector<Coordinate> getNeighborCoords(Coordinate center, unsigned int width, unsigned int height)
{
  std::vector<Coordinate> neighbors;

  const auto &x = center.first;
  const auto &y = center.second;

  // Don't bother returning the start vertex at 0,0
  if (y > 0 ? x > 0 : x > 1) {
    neighbors.emplace_back(std::make_pair(x - 1, y));
  }

  if (x > 0 ? y > 0 : y > 1) {
    neighbors.emplace_back(std::make_pair(x, y - 1));
  }

  if (x < width - 1) {
    neighbors.emplace_back(std::make_pair(x + 1, y));
  }

  if (y < height - 1) {
    neighbors.emplace_back(std::make_pair(x, y + 1));
  }

  return neighbors;
}

}// namespace

std::string Solver_15_part2::solve(std::istream &is)
{
  std::vector<std::tuple<unsigned int, unsigned int, unsigned int>> base_inputs;

  unsigned int width = 0;
  unsigned int height = 0;

  // Ingest the map
  {
    unsigned int x = 0;
    unsigned int y = 0;

    char input = 0;
    while (is.get(input)) {
      if (input == ' ') {
        continue;
      }

      if (input == '\n') {
        if (x > width) {
          width = x;
        }
        if (x != 0) {
          y += 1;
        }
        x = 0;
      } else {
        int cost = input - '0';
        if (cost < 0 || cost > 9) {
          throw solver_runtime_error("Input out of range");
        }

        base_inputs.emplace_back(std::make_tuple(x, y, cost));

        x += 1;
      }
    }
    height = x == 0 ? y : y + 1;
  }

  std::vector<std::tuple<unsigned int, unsigned int, unsigned int>> inputs;

  // Duplicate the map 4 times to the right and downward
  for (const auto &[x, y, cost] : base_inputs) {
    inputs.emplace_back(x, y, cost);
    for (unsigned int cell_y = 0; cell_y < 5; ++cell_y) {
      for (unsigned int cell_x = 0; cell_x < 5; ++cell_x) {
        if (cell_x == 0 and cell_y == 0) {
          continue;
        }

        inputs.emplace_back(std::make_tuple(
          x + width * cell_x,
          y + height * cell_y,
          ((cost + cell_x + cell_y - 1) % 9) + 1));
      }
    }
  }

  width *= 5;
  height *= 5;

  unsigned int goal_x = width - 1;
  unsigned int goal_y = height - 1;

  Coordinate start = std::make_pair(0, 0);
  Coordinate goal = std::make_pair(goal_x, goal_y);

  std::unordered_map<Coordinate, Vertex, boost::hash<Coordinate>> cave_map;

  for (const auto &[x, y, cost] : inputs) {
    const Coordinate position = std::make_pair(x, y);
    cave_map.emplace(position, Vertex(position, cost));
  }

  std::priority_queue<Vertex, std::vector<Vertex>, std::greater<>> open_list;
  std::unordered_set<Coordinate, boost::hash<Coordinate>> open_set;
  std::unordered_set<Coordinate, boost::hash<Coordinate>> closed_set;

  cave_map.at(start).g() = 0;
  open_list.push(cave_map.at(start));// No need to place it on set too, it will just be immediately erased

  // Use BFS algorithm to find the shortest path
  while (!open_list.empty()) {
    const Vertex vertex = open_list.top();
    open_list.pop();
    open_set.erase(vertex.position());

    if (vertex.position() == goal) {
      // Goal found! Trace path and end
      return std::to_string(vertex.g());
    }

    // Only visit every vertex once
    if (closed_set.find(vertex.position()) != closed_set.end()) {
      continue;
    }
    closed_set.insert(vertex.position());

    // Now look at all vertex's neighbors and update their g if new g through this vertex is lower (even if neighbor's been visited)
    for (const auto &neighbor_coord : getNeighborCoords(vertex.position(), width, height)) {
      auto &neighbor_vertex = cave_map.at(std::make_pair(neighbor_coord.first, neighbor_coord.second));

      const auto new_g = neighbor_vertex.cost() + vertex.g();

      if (new_g < neighbor_vertex.g()) {
        neighbor_vertex.g() = new_g;

        if (open_set.find(neighbor_coord) == open_set.end()) {
          open_list.push(neighbor_vertex);
          open_set.insert(neighbor_coord);
        }
      }
    }
  }

  throw solver_runtime_error("Could not find a path to the goal");
}

TEST_CASE("testing solver for day 15 part 2 - path through chiton cave expanded 5 times right/downward")
{
  Solver_15_part2 solver;

  {
    std::istringstream is(std::string{ R"(
      1163751742
      1381373672
      2136511328
      3694931569
      7463417111
      1319128137
      1359912421
      3125421639
      1293138521
      2311944581
    )" });

    CHECK(solver.solve(is) == "315");
  }

  {
    std::istringstream is(std::string{ R"(
      19999
      19111
      11191
    )" });

    CHECK(solver.solve(is) == "158");
  }

  {
    std::istringstream is(std::string{ R"(
      59999
      19111
      11195
    )" });

    CHECK(solver.solve(is) == "138");
  }
}


/*
  Copyright 2011 Larry Gritz and the other authors and contributors.
  All Rights Reserved.

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

  (This is the Modified BSD License)
*/

/// \file
/// Implementation of ImageBufAlgo algorithms.

#include <iostream>
#include <cmath>

#include <boost/scoped_array.hpp>

#include <OpenEXR/ImathFun.h>
#include <OpenEXR/ImathColor.h>
using Imath::Color3f;

#include "fmath.h"
#include "imagebuf.h"
#include "imagebufalgo.h"
#include "dassert.h"

template<class T>
inline Imath::Vec3<T>
powf (const Imath::Vec3<T> &x, float y)
{
    return Imath::Vec3<T> (powf (x[0], y), powf (x[1], y), powf (x[2], y));
}

OIIO_NAMESPACE_ENTER
{

namespace
{

static bool
same_size (const ImageBuf &A, const ImageBuf &B)
{
    const ImageSpec &a (A.spec()), &b (B.spec());
    return (a.width == b.width && a.height == b.height &&
            a.depth == b.depth && a.nchannels == b.nchannels);
}

#define LAPLACIAN_MAX_LEVELS 8

class LaplacianPyramid
{
public:
    LaplacianPyramid (float *image, int _width, int _height)
        : w(_width), h(_height)
    {
        level[0].insert (level[0].begin(), image, image+w*h);
        for (int i = 1;  i < LAPLACIAN_MAX_LEVELS;  ++i)
            convolve (level[i], level[i-1]);
    }

    ~LaplacianPyramid () { }

    float value (int x, int y, int lev) const {
	return level[std::min (lev, LAPLACIAN_MAX_LEVELS-1)][y*w + x];
    }

private:
    int w, h;
    std::vector<float> level[LAPLACIAN_MAX_LEVELS];

    // convolve image b with the kernel and store it in a
    void convolve (std::vector<float> &a, const std::vector<float> &b) {
        const float kernel[] = {0.05f, 0.25f, 0.4f, 0.25f, 0.05f};
        a.resize (b.size());
        for (int y = 0, index = 0;  y < h;  ++y) {
            for (int x = 0;  x < w;  ++x, ++index) {
                a[index] = 0.0f;
                for (int i = -2;  i <= 2;  ++i) {
                    for (int j = -2;  j<= 2;  ++j) {
                        int nx = abs(x+i);
                        int ny = abs(y+j);
                        if (nx >= w)
                            nx=2*w-nx-1;
                        if (ny >= h)
                            ny=2*h-ny-1;
                        a[index] += kernel[i+2] * kernel[j+2] * b[ny * w + nx];
                    }
                }
            }
        }
    }
};

// Adobe RGB (1998) with reference white D65 -> XYZ
// matrix is from http://www.brucelindbloom.com/
inline Color3f
AdobeRGBToXYZ (const Color3f &rgb)
{
    return Color3f (rgb[0] * 0.576700f  + rgb[1] * 0.185556f  + rgb[2] * 0.188212f,
                    rgb[0] * 0.297361f  + rgb[1] * 0.627355f  + rgb[2] * 0.0752847f,
                    rgb[0] * 0.0270328f + rgb[1] * 0.0706879f + rgb[2] * 0.991248f);
}

/// Convert a color in XYZ space to LAB space.
///
static Color3f
XYZToLAB (const Color3f xyz)
{
    // Reference white point
    static const Color3f white (0.576700f + 0.185556f + 0.188212f,
                                0.297361f + 0.627355f + 0.0752847f,
                                0.0270328f + 0.0706879f + 0.991248f);
    const float epsilon = 216.0f / 24389.0f;
    const float kappa = 24389.0f / 27.0f;

    Color3f r = xyz / white;
    Color3f f;
    for (int i = 0; i < 3; i++) {
        if (r[i] > epsilon)
            f[i] = powf (r[i], 1.0f / 3.0f);
        else
            f[i] = (kappa * r[i] + 16.0f) / 116.0f;
    }
    return Color3f (116.0f * f[1] - 16.0f,    // L
                    500.0f * (f[0] - f[1]),   // A
                    200.0f * (f[1] - f[2]));  // B
}

// Contrast sensitivity function (Barten SPIE 1989)
static float
contrast_sensitivity (float cyclesperdegree, float luminance)
{
    float a = 440.0f * powf ((1.0f + 0.7f / luminance), -0.2f);
    float b = 0.3f * powf ((1.0f + 100.0f / luminance), 0.15f);
    return a * cyclesperdegree * expf(-b * cyclesperdegree)
             * sqrtf(1.0f + 0.06f * expf(b * cyclesperdegree));
}

// Visual Masking Function from Daly 1993
inline float
mask (float contrast)
{
    float a = powf (392.498f * contrast, 0.7f);
    float b = powf (0.0153f * a, 4.0f);
    return powf (1.0f + b, 0.25f);
}

// Given the adaptation luminance, this function returns the
// threshold of visibility in cd per m^2
// TVI means Threshold vs Intensity function
// This version comes from Ward Larson Siggraph 1997
static float
tvi (float adaptation_luminance)
{
    // returns the threshold luminance given the adaptation luminance
    // units are candelas per meter squared
    float r;
    float log_a = log10f(adaptation_luminance);
    if (log_a < -3.94f)
        r = -2.86f;
    else if (log_a < -1.44f)
        r = powf(0.405f * log_a + 1.6f , 2.18f) - 2.86f;
    else if (log_a < -0.0184f)
        r = log_a - 0.395f;
    else if (log_a < 1.9f)
        r = powf(0.249f * log_a + 0.65f, 2.7f) - 0.72f;
    else
        r = log_a - 1.255f;
    return powf (10.0f, r);
}

}

int
ImageBufAlgo::compare_Yee (const ImageBuf &img0, const ImageBuf &img1,
                           float luminance, float fov)
{
    const ImageSpec &spec (img0.spec());
    ASSERT (spec.format == TypeDesc::FLOAT);
    ASSERT (same_size (img0, img1));
    int nscanlines = spec.height * spec.depth;
    int npels = nscanlines * spec.width;

    bool luminanceOnly = false;

    // assuming colorspaces are in Adobe RGB (1998), convert to LAB
    boost::scoped_array<Color3f> aLAB (new Color3f[npels]);
    boost::scoped_array<Color3f> bLAB (new Color3f[npels]);
    boost::scoped_array<float> aLum (new float[npels]);
    boost::scoped_array<float> bLum (new float[npels]);
    ImageBuf::ConstIterator<float,float> pix0 (img0);
    ImageBuf::ConstIterator<float,float> pix1 (img1);
    for (int i = 0;  pix0.valid();  ++i, pix0++) {
        pix1.pos (pix0.x(), pix0.y());  // ensure alignment
        Color3f RGB, XYZ;
        RGB.setValue (pix0[0], pix0[1], pix0[2]);
        XYZ = AdobeRGBToXYZ (RGB);
        aLAB[i] = XYZToLAB (XYZ);
        aLum[i] = XYZ[1] * luminance;

        RGB.setValue (pix1[0], pix1[1], pix1[2]);
        XYZ = AdobeRGBToXYZ (RGB);
        bLAB[i] = XYZToLAB (XYZ);
        bLum[i] = XYZ[1] * luminance;
    }

    // Construct Laplacian pyramids
    LaplacianPyramid la (&aLum[0], spec.width, nscanlines);
    LaplacianPyramid lb (&bLum[0], spec.width, nscanlines);

    float num_one_degree_pixels = (float) (2 * tan(fov * 0.5 * M_PI / 180) * 180 / M_PI);
    float pixels_per_degree = spec.width / num_one_degree_pixels;

    unsigned int adaptation_level = 0;
    for (int i = 0, npixels = 1;
             i < LAPLACIAN_MAX_LEVELS && npixels <= num_one_degree_pixels;
             ++i, npixels *= 2)
        adaptation_level = i;

    float cpd[LAPLACIAN_MAX_LEVELS];
    cpd[0] = 0.5f * pixels_per_degree;
    for (int i = 1;  i < LAPLACIAN_MAX_LEVELS;  ++i)
        cpd[i] = 0.5f * cpd[i - 1];
    float csf_max = contrast_sensitivity (3.248f, 100.0f);

    float F_freq[LAPLACIAN_MAX_LEVELS - 2];
    for (int i = 0; i < LAPLACIAN_MAX_LEVELS - 2;  ++i)
        F_freq[i] = csf_max / contrast_sensitivity (cpd[i], 100.0f);

    unsigned int pixels_failed = 0;
    for (int y = 0, index = 0; y < nscanlines;  ++y) {
        for (int x = 0;  x < spec.width;  ++x, ++index) {
            float contrast[LAPLACIAN_MAX_LEVELS - 2];
            float sum_contrast = 0;
            for (int i = 0; i < LAPLACIAN_MAX_LEVELS - 2; i++) {
                float n1 = fabsf (la.value(x,y,i) - la.value(x,y,i+1));
                float n2 = fabsf (lb.value(x,y,i) - lb.value(x,y,i+1));
                float numerator = std::max (n1, n2);
                float d1 = fabsf (la.value(x,y,i+2));
                float d2 = fabsf (lb.value(x,y,i+2));
                float denominator = std::max (std::max (d1, d2), 1.0e-5f);
                contrast[i] = numerator / denominator;
                sum_contrast += contrast[i];
            }
            if (sum_contrast < 1e-5)
                sum_contrast = 1e-5f;
            float F_mask[LAPLACIAN_MAX_LEVELS - 2];
            float adapt = la.value(x,y,adaptation_level) + lb.value(x,y,adaptation_level);
            adapt *= 0.5f;
            if (adapt < 1e-5)
                adapt = 1e-5f;
            for (int i = 0; i < LAPLACIAN_MAX_LEVELS - 2; i++)
                F_mask[i] = mask(contrast[i] * contrast_sensitivity(cpd[i], adapt));
            float factor = 0;
            for (int i = 0; i < LAPLACIAN_MAX_LEVELS - 2; i++)
                factor += contrast[i] * F_freq[i] * F_mask[i] / sum_contrast;
            factor = Imath::clamp (factor, 1.0f, 10.0f);
            float delta = fabsf (la.value(x,y,0) - lb.value(x,y,0));
            bool pass = true;
            // pure luminance test
            if (delta > factor * tvi(adapt)) {
                pass = false;
            } else if (! luminanceOnly) {
                // CIE delta E test with modifications
                float color_scale = 1.0f;
                // ramp down the color test in scotopic regions
                if (adapt < 10.0f) {
                    color_scale = 1.0f - (10.0f - color_scale) / 10.0f;
                    color_scale = color_scale * color_scale;
                }
                float da = aLAB[index][1] - bLAB[index][1];  // diff in A
                float db = aLAB[index][2] - bLAB[index][2];  // diff in B
                da = da * da;
                db = db * db;
                float delta_e = (da + db) * color_scale;
                if (delta_e > factor)
                    pass = false;
            }
            if (!pass)
                ++pixels_failed;
        }
    }
//    std::cout << "Perceptual diff shows " << pixels_failed << " failures\n";

    return pixels_failed;
}

}
OIIO_NAMESPACE_EXIT


#include <iostream>

#include <armadillo>

#include <cpp-argparse/OptionParser.h>

#include <besiq/method/loglinear_method.hpp>
#include <besiq/method/method.hpp>

#include "common_options.hpp"

using namespace arma;
using namespace optparse;

const std::string USAGE = "besiq-loglinear [OPTIONS] pairs genotype_plink_prefix";
const std::string DESCRIPTION = "A log-linear based test for genetic interactions.";

int
main(int argc, char *argv[])
{
    OptionParser parser = create_common_options( USAGE, DESCRIPTION, false );

    Values options = parser.parse_args( argc, argv );
    if( parser.args( ).size( ) != 2 )
    {
        parser.print_help( );
        exit( 1 );
    }
    shared_ptr<common_options> parsed_data = parse_common_options( options, parser.args( ) );

    method_type *m = new loglinear_method( parsed_data->data );

    run_method( *m, parsed_data->genotypes, *parsed_data->pairs, *parsed_data->result_file );

    delete m;

    return 0;
}


#include <I3Test.h>
#include "MuonGun/SplineTable.h"

#include "common.h"
#include "MuonGun/Flux.h"
#include "MuonGun/RadialDistribution.h"
#include "MuonGun/EnergyDistribution.h"
#include <boost/make_shared.hpp>

namespace I3MuonGun {

std::string
get_tabledir()
{
	const char *I3_BUILD = getenv("I3_BUILD");
	ENSURE(I3_BUILD, "I3_BUILD is set");

	return std::string(I3_BUILD) + "/MuonGun/resources/tables/";
}

BundleModel
load_model(const std::string &base)
{
	using boost::make_shared;
	return BundleModel(boost::make_shared<SplineFlux>(get_tabledir() + base + ".single_flux.fits", get_tabledir() + base + ".bundle_flux.fits"),
	    boost::make_shared<SplineRadialDistribution>(get_tabledir() + base + ".radius.fits"),
	    boost::make_shared<SplineEnergyDistribution>(get_tabledir() + base + ".single_energy.fits", get_tabledir() + base + ".bundle_energy.fits"));
}

}

TEST_GROUP(SplineTable);

TEST(Equality)
{
	using namespace I3MuonGun;

	const SplineTable t1(get_tabledir() + "Hoerandel5_atmod12_SIBYLL.single_flux.fits");
	const SplineTable t2(get_tabledir() + "GaisserH4a_atmod12_SIBYLL.single_flux.fits");

	ENSURE(t1 == t1);
	ENSURE(t2 == t2);
	ENSURE(!(t1 == t2));
	ENSURE(!(t2 == t1));
}


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

  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/constant/mfive.hpp>
#include <boost/simd/as.hpp>
#include <scalar_test.hpp>

STF_CASE_TPL( "Check mfive behavior for all types"
            , (std::uint8_t)(std::uint16_t)(std::uint32_t)(std::uint64_t)
              (std::int8_t)(std::int16_t)(std::int32_t)(std::int64_t)
              (double)(float)
            )
{
  using boost::simd::as;
  using boost::simd::detail::mfive;
  using boost::simd::Mfive;

  STF_TYPE_IS(decltype(Mfive<T>()), T);
  STF_EQUAL(Mfive<T>(), T(-5));
  STF_EQUAL(mfive( as(T{}) ),T(-5));
}


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


//==============================================================================
//         Copyright 2003 - 2011   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
/*!
 * \file
**/
#ifndef BOOST_SIMD_TOOLBOX_CONSTANT_CONSTANTS_INT_SPLAT_HPP_INCLUDED
#define BOOST_SIMD_TOOLBOX_CONSTANT_CONSTANTS_INT_SPLAT_HPP_INCLUDED

#include <boost/simd/include/functor.hpp>
#include <boost/simd/sdk/meta/int_c.hpp>
#include <boost/simd/sdk/constant/constant.hpp>
#include <boost/dispatch/meta/scalar_of.hpp>

/*!
 * \ingroup boost_simd_constant
 * \defgroup boost_simd_constant_int_splat Int_splat
 *
 * \par Description
 * TODO Put description here
 *
 * \par Header file
 *
 * \code
 * #include <nt2/include/functions/int_splat.hpp>
 * \endcode
 *
 *
 * \synopsis
 *
 * \code
 * namespace boost::simd
 * {
 *   template <class T,class A0>
 *     meta::call<tag::int_splat_(A0)>::type
 *     Int_splat();
 * }
 * \endcode
 *
 *
 * \param T template parameter of Int_splat
 *
 * \return type T value
 *
 *
**/

namespace boost { namespace simd
{
  namespace tag
  {
    /*!
     * \brief Define the tag Int_splat of functor Int_splat
     *        in namespace boost::simd::tag for toolbox boost.simd.constant
    **/
    template<boost::simd::int64_t I>
    struct Intpattern : ext::pure_constant_< Intpattern<I> >
    {
      template<class Target, class Dummy=void>
      struct apply : meta::int_c< typename Target::type
                                , typename Target::type(I)
                                > {};
    };
  }

  template<class Target, typename boost::dispatch::meta::scalar_of<Target>::type N>
  inline Target integral_constant()
  {
    typename dispatch::make_functor< tag::Intpattern<boost::simd::int64_t(N)>, Target >::type callee;
    return callee( dispatch::meta::as_<Target>() );
  }
} }

#include <boost/simd/sdk/constant/common.hpp>

#endif


#include <physical/runtime.h>

#define BOOST_TEST_MODULE  unit_systems

#include <boost/test/unit_test.hpp>

#include <iostream>

BOOST_AUTO_TEST_SUITE( unit_systems );

  /* THIS TEST SHOULD NOT COMPILE.
   * With non-native types (i.e. a class type instead of double for example),
   * the automatic casting does not work. */
  BOOST_AUTO_TEST_CASE( DimQuantity_cast_runtime ) {
    using namespace runtime::physical;
    using runtime::physical::system::si;
    namespace dim = dimension;

    DimQuantity<si,dim::velocity> dsivel(Quantity(1.0));
    /* THIS LINE SHOULD NOT COMPILE: */
    BOOST_CHECK_EQUAL( Quantity(10.0)*(dsivel), Quantity(10.0) );

    /* THIS LINE SHOULD INDEED COMPILE: */
    BOOST_CHECK_EQUAL( Quantity(10.0)*static_cast<const Quantity&>(dsivel), Quantity(10.0) );
  }

BOOST_AUTO_TEST_SUITE_END();


#include "Day04-GiantSquid.h"

#include "BingoSubsystem.h"

#include <AdventOfCodeCommon/Utils.h>

#include <AdventOfCodeCommon/DisableLibraryWarningsMacros.h>

__BEGIN_LIBRARIES_DISABLE_WARNINGS
#include <boost/algorithm/string.hpp>
__END_LIBRARIES_DISABLE_WARNINGS

namespace AdventOfCode
{
namespace Year2021
{
namespace Day04
{

using TextSection = std::vector<std::string>;

Board parseBoardTextSection(const TextSection& boardTextSection)
{
    NumberTable numberTable;

    for (const auto& line : boardTextSection)
    {
        std::vector<int> numbers;
        AdventOfCode::Utils::splitStringIntoTypedVector(line, numbers);
        numberTable.push_back(std::move(numbers));
    }

    return Board{std::move(numberTable)};
}

std::vector<Board> parseBoardTextSections(const std::vector<TextSection>& boardTextSections)
{
    std::vector<Board> boards;

    for (const auto& boardTextSection : boardTextSections)
    {
        auto board = parseBoardTextSection(boardTextSection);
        boards.push_back(std::move(board));
    }

    return boards;
}

BingoSubsystem parseBingoSubsystemInputLines(const std::vector<std::string>& bingoSubsystemInputLines)
{
    std::vector<TextSection> bingoSubsystemInputTextSections;
    boost::split(bingoSubsystemInputTextSections, bingoSubsystemInputLines, [](const auto& elem) { return elem.empty(); });

    std::vector<int> numbersDrawn;
    AdventOfCode::Utils::splitStringIntoTypedVector(bingoSubsystemInputTextSections.at(0).at(0), numbersDrawn, ',');

    std::vector<TextSection> boardTextSections(std::next(bingoSubsystemInputTextSections.cbegin()), bingoSubsystemInputTextSections.cend());
    std::vector<Board> boards = parseBoardTextSections(boardTextSections);

    return BingoSubsystem{std::move(numbersDrawn), std::move(boards)};
}

int winningBoardFinalScore(const std::vector<std::string>& bingoSubsystemInputLines)
{
    BingoSubsystem bingoSubsystem = parseBingoSubsystemInputLines(bingoSubsystemInputLines);
    bingoSubsystem.playUntilFirstWinner();
    return bingoSubsystem.getLatestToWinBoardScore();
}

int lastToWinBoardFinalScore(const std::vector<std::string>& bingoSubsystemInputLines)
{
    BingoSubsystem bingoSubsystem = parseBingoSubsystemInputLines(bingoSubsystemInputLines);
    bingoSubsystem.playUntilLastWinner();
    return bingoSubsystem.getLatestToWinBoardScore();
}

}
}
}


#include "TaskFunction.h"
#include "TaskUserFunctions.h"

namespace {

using namespace tg;

TaskIR::tgFunction initPrintf ( ) {
	static Type *arguments[] = { createType<char *>() ( CURRENT_TASKGRAPH ) };
	static Type *returnType = createType<int>() ( CURRENT_TASKGRAPH );
	static bool init = false;
	static TaskIR::tgFunction func = 0;

	if ( !init ) {
		func = CURRENT_TASKGRAPH->createFunction ( "printf", returnType, 1, arguments, true );
		init = true;
	}
	return func;
}

}

#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/cat.hpp>

namespace tg
{

#define PRINTF_PARAM(z, n, text) , const TaskExpression & BOOST_PP_CAT(text,n)
#define PRINTF_DO_PARAM_LIST(SIZE) BOOST_PP_REPEAT(SIZE, PRINTF_PARAM, expr)
#define PRINTF_DO_GET_EXPRESSION(z, n, text) args[n+1] = BOOST_PP_CAT(expr,n).getExpression ( );

#define PRINTF_DO_FUNCTION(SIZE) \
    TaskIR::tgFunction func = initPrintf (); \
    std::vector<TaskIR::tgExpression> args(SIZE+1); \
    args[0] = TaskExpression ( test ).getExpression ( ); \
    BOOST_PP_REPEAT(SIZE, PRINTF_DO_GET_EXPRESSION, dummy) \
    CURRENT_TASKGRAPH->callFunction ( func, SIZE+1, &args[0] );

#define PRINTF_MAKE_FUNC(z,SIZE, text) \
    void tPrintf (const char *test  PRINTF_DO_PARAM_LIST(SIZE) ) { PRINTF_DO_FUNCTION(SIZE) }

BOOST_PP_REPEAT(MAX_TPRINTF_PARAMS, PRINTF_MAKE_FUNC, dummy)

// Double versions

TaskExpression tSqrt ( const TaskExpression &expr ) {
	static TaskFunction1<double, double> func ( "sqrt" );
	return func.call ( expr );
}

TaskExpression tAbs ( const TaskExpression &expr ) {
	static TaskFunction1<double, double> func ( "fabs" );
	return func.call ( expr );
}

TaskExpression tLog ( const TaskExpression &expr ) {
	static TaskFunction1<double, double> func ( "log" );
	return func.call ( expr );
}

TaskExpression tLog10 ( const TaskExpression &expr ) {
	static TaskFunction1<double, double> func ( "log10" );
	return func.call ( expr );
}

TaskExpression tPow ( const TaskExpression &base, const TaskExpression &exponent ) {
	static TaskFunction2<double, double, double> func ( "pow" );
	return func.call ( base, exponent );
}

// Float versions

TaskExpression tSqrtf ( const TaskExpression &expr ) {
	static TaskFunction1<float, float> func ( "sqrtf" );
	return func.call ( expr );
}

TaskExpression tAbsf ( const TaskExpression &expr ) {
	static TaskFunction1<float, float> func ( "fabsf" );
	return func.call ( expr );
}

TaskExpression tLogf ( const TaskExpression &expr ) {
	static TaskFunction1<float, float> func ( "logf" );
	return func.call ( expr );
}

TaskExpression tLog10f ( const TaskExpression &expr ) {
	static TaskFunction1<float, float> func ( "log10f" );
	return func.call ( expr );
}

TaskExpression tPowf ( const TaskExpression &base, const TaskExpression &exponent ) {
	static TaskFunction2<float, float, float> func ( "powf" );
	return func.call ( base, exponent );
}

}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
/*!
 * \file
**/
#ifndef BOOST_SIMD_IEEE_FUNCTIONS_ULP_HPP_INCLUDED
#define BOOST_SIMD_IEEE_FUNCTIONS_ULP_HPP_INCLUDED
#include <boost/simd/include/functor.hpp>
#include <boost/dispatch/include/functor.hpp>

/*!
 * \ingroup boost_simd_ieee
 * \defgroup boost_simd_ieee_ulp ulp
 *
 * \par Description
 * Returns the distance to the nearest (distinct) element of the same type.
 *
 * \par Header file
 *
 * \code
 * #include <nt2/include/functions/ulp.hpp>
 * \endcode
 *
 *
 * \synopsis
 *
 * \code
 * namespace boost::simd
 * {
 *   template <class A0>
 *     meta::call<tag::ulp_(A0)>::type
 *     ulp(const A0 & a0);
 * }
 * \endcode
 *
 * \param a0 the unique parameter of ulp
 *
 * \return a value of the same type as the parameter
 *
 * \par Notes
 * In SIMD mode, this function acts elementwise on the inputs vectors elements
 * \par
 *
**/

namespace boost { namespace simd { namespace tag
  {
    /*!
     * \brief Define the tag ulp_ of functor ulp
     *        in namespace boost::simd::tag for toolbox boost.simd.ieee
    **/
    struct ulp_ : ext::elementwise_<ulp_> { typedef ext::elementwise_<ulp_> parent; };
  }
  BOOST_DISPATCH_FUNCTION_IMPLEMENTATION(tag::ulp_, ulp, 1)
} }

#endif

// modified by jt the 25/12/2010


#ifndef READ_HPP
#define READ_HPP

#include <vector>
#include <string>
#include <fstream>
#include <iostream>
#include <utility>
#include <sstream>
#include <Eigen/Core>

using namespace std;
using namespace Eigen;

using VectorXld = Matrix<long double,Dynamic,1>;

namespace hhmm{

  vector<VectorXld> read(string const& filename)
  {
    vector<VectorXld> result;
    string str;
    string substr;
    fstream ifs(filename);
    while(getline(ifs,str)){

      istringstream iss(str);

      iss >> substr;
      //string chrname = substr;

      iss >> substr;
      //uint32_t indexBegin = stoi(substr);
      iss >> substr;
      //uint32_t indexEnd = stoi(substr);

      vector<long double> tmp;
      while(iss >> substr){
        long double obs = stod(substr);
        tmp.push_back(obs);
      }
      VectorXld innerResult(tmp.size());
      for(uint32_t i=0;i<tmp.size();++i){innerResult(i) = tmp[i];}
      result.push_back(innerResult);
    }
    result.shrink_to_fit();
    return result;
  }

}

#endif


/*
 * Copyright (c) 2017, Adrian Michel
 * http://www.amichel.com
 *
 * This software is released under the 3-Clause BSD License
 *
 * The complete terms can be found in the attached LICENSE file
 * or at https://opensource.org/licenses/BSD-3-Clause
 */

#pragma once

#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#include <boost/tokenizer.hpp>
#include <set>

#include "de_types.hpp"
#include "random_generator.hpp"

#if defined(max)
#undef max
#endif

#if defined(min)
#undef min
#endif

namespace amichel {
namespace de {

/**
 * Exception thrown in case of a constraint error
 */
class constraints_exception : public exception {
 public:
  constraints_exception(const std::string& message)
      : exception(message.c_str()) {}
};

/**
 * Abstract base class for concrete constraint classes
 *
 * A constraint class describes certain characteristics and
 * limits of the input variables fed to the objective function.
 */
class constraint {
 public:
  virtual ~constraint() {}

  /**
   * returns a random value limited to the type and range of the
   * constraint
   *
   * @return double
   */
  virtual double get_rand_value() = 0;

  /**
   * returns a random value limited to the type and range of the
   * constraint based on a previous value and an origin (see
   * specific implementation in derived classes)
   *
   * @param value
   * @param origin
   *
   * @return double
   */
  virtual double get_rand_value(double value, double origin) = 0;

  /**
   * returns the min limit of the range
   *
   * @return double
   */
  virtual double min() const = 0;

  /**
   * returns the max limit of the range
   *
   * @return double
   */
  virtual double max() const = 0;

  /**
   * Gets a random value within the limits set for the constraint,
   * but further limited to a range defined by its origin and the
   * width of the zone around this origin in pct of the total
   * width
   *
   * @param origin the origin (center) of the zone further
   *  			 limiting the constraint
   * @param zonePct the width of the zone in pct of the total
   *  			  width, around the origin
   *
   * @return double
   */
  virtual double get_rand_value_in_zone(double origin, double zonePct) const = 0;

  /**
   * Gets the point midway between min and max - will only work
   * for range constraints
   *
   * @return double
   */
  virtual double get_middle_point() = 0;
};

/**
 * A smart pointer to a Constraint
 */
using constraint_ptr = std::shared_ptr<constraint>;

/**
 * Base class for constraints that are range based. Each such
 * constraint has a min and a max value
 */
class range_constraint : public constraint {
 private:
  double m_min;
  double m_max;

 public:
  /**
   * constructor that takes the min and max limits of the range
   *
   * @param min
   * @param max
   */
  range_constraint(double min, double max) : m_min(min), m_max(max) {
    assert(min <= max);
  }

  /**
   * returns the min limit of the range
   *
   * @return double
   */
  double min() const { return m_min; }

  /**
   * returns the max limit of the range
   *
   * @return double
   */
  double max() const { return m_max; }
};

/**
 * A real constraint. Specifies that variables can have any
 * double value, within the specified limits.
 */
class real_constraint : public range_constraint {
 public:
  /**
   * constructor that takes the min and max limit of the real
   * constraint
   *
   * @param min
   * @param max
   */
  real_constraint(double min, double max) : range_constraint(min, max) {
    assert(min <= max);
  }

  /**
   * returns a random value limited to the type and range of the
   * constraint
   *
   * @return double
   */
  double get_rand_value() {
    return genrand(range_constraint::min(), range_constraint::max());
  }

  /**
   * returns a random value limited to the type and range of the
   * constraint based on a previous value and an origin
   *
   * @param value
   * @param origin
   *
   * @return double
   */
  double get_rand_value(double value, double origin) {
    double ret = value;

    while (ret < range_constraint::min()) {
      ret = range_constraint::min() + genrand() * (origin - range_constraint::min());
    }

    while (ret > range_constraint::max()) {
      ret = range_constraint::max() + genrand() * (origin - range_constraint::max());
    }

    return ret;
  }

  double get_rand_value_in_zone(double origin, double zonePct) const override {
    if (origin > max()) {
      throw constraints_exception("origin coordinate > max");
    }

    if (origin < min()) {
      throw constraints_exception("origin coordinate < min");
    }

    if (zonePct > 100.0) {
      throw constraints_exception("zonePct > 100%");
    }

    if (zonePct < 0) {
      throw constraints_exception("zonePct < 0%");
    }

    if (zonePct == 0) {
      throw constraints_exception("zonePct == 0%");
    }

    double zoneSize = (max() - min()) * zonePct / 100.0;

    double _min = std::max(min(), origin - zoneSize / 2.0);
    double _max = std::min(max(), origin + zoneSize / 2.0);

    return genrand(_min, _max);
  }

  double get_middle_point() override { return (max() + min()) / 2.0; }
};

/**
 * An integer constraint. Specifies that variables can have any
 * integer values within the specified limits.
 */
class int_constraint : public range_constraint {
 public:
  /**
   * constructor that takes the min and max limit of the integer
   * constraint
   *
   * @param min
   * @param max
   */
  int_constraint(double min, double max) : range_constraint(min, max) {
    assert(min <= max);
  }

  /**
   * returns a random value limited to the type and range of the
   * constraint
   *
   * @return double
   */
  double get_rand_value() {
    return genintrand(range_constraint::min(), range_constraint::max());
  }

  /**
   * returns a random value limited to the type and range of the
   * constraint based on a previous value and an origin
   *
   * @param value
   * @param origin
   *
   * @return double
   */
  double get_rand_value(double value, double origin) {
    double ret = boost::math::round(value);

    while (ret < range_constraint::min()) {
      ret = range_constraint::min() + genrand() * (origin - range_constraint::min());
      ret = boost::math::round(ret);
    }

    while (ret > range_constraint::max()) {
      ret = range_constraint::max() + genrand() * (origin - range_constraint::max());
      ret = boost::math::round(ret);
    }

    return ret;
  }

  virtual double get_rand_value_in_zone(double origin, double zonePct) const {
    if (origin > max()) {
      throw constraints_exception("origin coordinate > max");
    }

    if (origin < min()) {
      throw constraints_exception("origin coordinate < min");
    }

    if (zonePct > 100.0) {
      throw constraints_exception("zonePct > 100%");
    }

    if (zonePct < 0) {
      throw constraints_exception("zonePct < 0%");
    }

    if (zonePct == 0) {
      throw constraints_exception("zonePct == 0%");
    }

    double zoneSize = (max() - min()) * zonePct / 100.0;

    double _min = std::max(min(), origin - zoneSize / 2.0);
    double _max = std::min(max(), origin + zoneSize / 2.0);

    double val = boost::math::round(genrand(_min, _max));

    while (val < _min || val > _max) {
      val = boost::math::round(genrand(_min, _max));
    }

    return val;
  }

  virtual double get_middle_point() {
    return boost::math::round((max() - min()) / 2.0);
  }
};

/**
 * A set constraint. Specifies that variables can take any
 * values from a predefined set. Doesn't require min or max.
 *
 * Note that duplicate values will be removed
 */
class set_constraint : public constraint {
 private:
  class unique : public std::unary_function<double, bool> {
   public:
    bool operator()(double d) const { return m_unique.insert(d).second; }

   public:
    double min() const {
      if (m_unique.size() > 0) {
        return *m_unique.begin();
      }
      else {
        throw constraints_exception("could not get the min value of an empty set constraint");
      }
    }

    double max() const {
      if (m_unique.size() > 0) {
        return *m_unique.rbegin();
      }
      else {
        throw constraints_exception("could not get the max value of an empty set constraint");
      }
    }

   private:
    mutable std::set<double> m_unique;
  };

 private:
  unique m_unique;
  de::DVector m_values;

 public:
  /**
   * Constructs the set from a vector of Double values, and
   * removes duplicates to ensure a uniform distribution for
   * randomization
   *
   * @param values
   */
  set_constraint(const de::DVector& values) {
    // making sure the values in the "set" are unique
    std::remove_copy_if(values.begin(), values.end(), std::back_inserter(m_values), m_unique);
  }

  /**
   * adds an individual value to the "set", won't create duplicate
   * values.
   *
   * @param value
   */
  void add_value(double value) {
    if (m_unique(value)) {
      m_values.push_back(value);
    }
  }

  /**
   * returns a value randomly chosen from the set of available
   * values
   *
   * @return double
   */
  virtual double get_rand_value() {
    de::DVector::size_type index(genintrand(0, m_values.size() - 1));

    return m_values[index];
  }

  /**
   * returns a value randomly chosen from the set of available
   * values
   *
   * @return double
   */
  virtual double get_rand_value(double value, double origin) {
    return get_rand_value();
  }

  /**
   * returns the min limit of the range
   *
   * @return double
   */
  double min() const { return m_unique.min(); }

  /**
   * returns the max limit of the range
   *
   * @return double
   */
  double max() const { return m_unique.max(); }

  virtual double get_rand_value_in_zone(double origin, double zonePct) const {
    throw constraints_exception("get_rand_value_in_zone only supported for range constraints");
  }

  virtual double get_middle_point() {
    throw constraints_exception("get_middle_point not supported by set constraint");
  }
};

/**
 * A boolean constraint. Specifies that variables can take a
 * boolean value - true or false.
 */
class boolean_constraint : public constraint {
 public:
  /**
   * returns a random boolean value
   *
   * @return double
   */
  virtual double get_rand_value() { return genrand() < 0.5; }

  /**
   * returns a random boolean value
   *
   * @return double
   */
  virtual double get_rand_value(double value, double origin) {
    return get_rand_value();
  }

  /**
   * returns the min limit of the range
   *
   * @return double
   */
  double min() const { return 0; }

  /**
   * returns the max limit of the range
   *
   * @return double
   */
  double max() const { return 1; }

  virtual double get_rand_value_in_zone(double origin, double zonePct) const {
    throw constraints_exception("get_rand_value_in_zone only supported for range constraints");
  }

  virtual double get_middle_point() {
    throw constraints_exception("get_middle_point not supported by bool constraint");
  }
};

using constraints_base = std::vector<constraint_ptr>;

/**
 * A collection of constraints, implemented as a vector.
 *
 * Is used to define the constraints for all variables used
 * during an optimization session.
 */
class constraints : public constraints_base {
 private:
  using separator = boost::char_separator<char>;
  using tokenizer = boost::tokenizer<separator>;

 public:
  /**
   * Initializes a collection of constraints with default values.
   * All constraints are of type real and have the same default
   * min and max.
   *
   * @param varCount number of constraints
   * @param defMin default min limit
   * @param defMax default max limit
   */
  constraints(size_t varCount, double defMin, double defMax)
      : constraints_base(varCount, std::make_shared<real_constraint>(defMin, defMax)) {}

  /**
   * Initializes a collection of constraints from string
   * descriptions. Currently used only for range based
   * constraints.
   *
   * A constraint can be described as "type;min;max" where type
   * can be real or integer and min and max are the range limits.
   *
   * @param str a collection (vector) of constraint description
   *  		  strings, each string describing constraints for
   *  		  one variable
   * @param var_count the total number of variables (can be
   *  			   different than the number of strings). If
   *  			   there are more variables than strings, the
   *  			   extra constraints are set to be real and use
   *  			   the default min and max arguments for the
   *  			   range
   * @param def_min default min value in case the number of
   *  			 variables is higher than the number of
   *  			 constraints specified as strings.
   * @param def_max default max value in case the number of
   *  			 variables is higher than the number of
   *  			 constraints specified as strings.
   */
  constraints(const std::vector<std::string>& str, size_t var_count, double def_min, double def_max)
      : constraints_base( var_count, std::make_shared<real_constraint>(def_min, def_max)) {
    for (std::vector<std::string>::size_type i = 0; i < str.size(); ++i) {
      tokenizer tokens(str[i], separator(":;,"));

      std::string type;
      double _min;
      double _max;

      size_t count(0);

      for (tokenizer::const_iterator j = tokens.begin(); j != tokens.end(); ++j, ++count) {
        const std::string token(boost::trim_copy(*j));

        try {
          switch (count) {
            case 0:
              type = token;
              break;
            case 1:
              _min = boost::lexical_cast<double>(token.c_str());
              break;
            case 2:
              _max = boost::lexical_cast<double>(token.c_str());
              break;
            default:
              // too many fields
              throw constraints_exception((boost::format("wrong variable format in \"%1%\" - too many fields") % str[i]) .str());
          }
        }
        catch (const boost::bad_lexical_cast&) {
          throw constraints_exception((boost::format("wrong floating point number format: %1%") % token).str());
        }
      }

      // too few fields
      if (count < 3) {
        throw constraints_exception((boost::format("wrong variable format in \"%1%\" - too few fields") % str[i]).str());
      }

      if (i < var_count) {
        constraints_base::at(i) = str_to_constraint(type, _min, _max);
      }
      else {
        constraints_base::push_back(str_to_constraint(type, _min, _max));
      }
    }
  }

  /**
   * returns a random value limited to the type and range of the
   * constraint
   *
   * @return double
   */
  double get_rand_value(size_t index) const {
    if (index < constraints_base::size()) {
      return (*this)[index]->get_rand_value();
    }
    else {
      throw constraints_exception((boost::format("invalid constraint index: %1%, higher than max number of constraints: %2%") %
          index % constraints_base::size()).str());
    }
  }

  /**
   * returns a random value limited to the type and range of the
   * constraint based on a previous value and an origin
   *
   * @param index the constraint index
   * @param value previous value
   * @param origin origin
   *
   * @return double
   */
  double get_rand_value(size_t index, double value, double origin) const {
    if (index < constraints_base::size()) {
      return (*this)[index]->get_rand_value(value, origin);
    }
    else {
      throw constraints_exception((boost::format("invalid constraint index: %1%, higher than max number of constraints: %2%") %
        index % constraints_base::size()).str());
    }
  }

  /**
   * Generates a set of random values within a "hypercube" region
   * defined by an origin and an area expressed as a percentage of
   * the entire range
   *
   * @param origin the origin coordinates of the region
   * @param sidePct the side of the hypercube in pct of the entire
   *  			  side
   *
   * @return DVectorPtr
   */
  DVectorPtr get_square_zone_rand_values(const DVectorPtr origin, double sidePct) const {
    assert(origin);
    assert(sidePct > 0 && sidePct <= 100);

    if (origin->size() == constraints_base::size()) {
      DVectorPtr square(std::make_shared<DVector>(origin->size()));

      for (constraints_base::size_type n = 0; n < constraints_base::size(); ++n)
        (*square)[n] = (*this)[n]->get_rand_value_in_zone((*origin)[n], sidePct);

      return square;

    }
    else {
      throw constraints_exception("The origin vector must have the same number of elements as there are constraints");
    }
  }

  DVectorPtr get_middle_point() {
    DVectorPtr r(std::make_shared<DVector>(constraints_base::size()));

    for (constraints_base::size_type n = 0; n < constraints_base::size(); ++n)
      (*r)[n] = (*this)[n]->get_middle_point();

    return r;
  }

  /**
   * Get a set of random values within the limits set by the
   * constraints
   *
   * @return DVectorPtr
   */
  DVectorPtr get_rand_values() const {
    DVectorPtr r(std::make_shared<DVector>(constraints_base::size()));

    for (constraints_base::size_type n = 0; n < constraints_base::size(); ++n)
      (*r)[n] = (*this)[n]->get_rand_value();

    return r;
  }

 private:
  constraint_ptr str_to_constraint(const std::string& type, double min, double max) {
    if (boost::to_lower_copy(type) == "real") {
      return std::make_shared<real_constraint>(min, max);
    }
    else if (boost::to_lower_copy(type) == "int" || boost::to_lower_copy(type) == "integer") {
      return std::make_shared<int_constraint>(min, max);
    }
    else {
      throw constraints_exception((boost::format("invalid constraint type \"%1%\"") % type).str());
    }
  }
};

using constraints_ptr = std::shared_ptr<constraints>;
}  // namespace de
}  // namespace amichel


// Copyright (c) 2012 Christopher Lux <christopherlux@gmail.com>
// Distributed under the Modified BSD License, see license.txt.

#include "iomanip.h"

#include <ostream>
#include <iomanip>

#include <boost/io/ios_state.hpp>

namespace scm {
namespace io {

std::string
data_size_unit_string(const data_size_unit du)
{
    std::string s;

    switch (du) {
        case Byte: s.assign("B");    break;
        case KiB:  s.assign("KiB");  break;
        case MiB:  s.assign("MiB");  break;
        case GiB:  s.assign("GiB");  break;
        case TiB:  s.assign("TiB");  break;
        default:   s.assign("unknown unit"); break;
    }

    return s;
}

data_size::data_size(size_t ds, int dp)
  : _data_size(ds)
  , _data_unit(Byte)
  , _dec_places(dp)
  , _dynamic_unit(true)
{
}

data_size::data_size(size_t ds, data_size_unit du, int dp)
  : _data_size(ds)
  , _data_unit(du)
  , _dec_places(dp)
  , _dynamic_unit(false)
{
}

__scm_export(core) std::ostream& operator<<(std::ostream& os, const data_size& ds)
{
    std::ostream::sentry const  out_sentry(os);

    if (os) {
        boost::io::ios_all_saver saved_state(os);

        data_size_unit u = io::Byte;
        double         s = static_cast<double>(ds._data_size);

        if (ds._dynamic_unit) {
            if (0 < ds._data_size) {
                if      (0 < (ds._data_size / (1024ll * 1024ll * 1024ll * 1024ll))) { u = io::TiB; }
                else if (0 < (ds._data_size / (1024ll * 1024ll * 1024ll)))          { u = io::GiB; }
                else if (0 < (ds._data_size / (1024ll * 1024ll)))                   { u = io::MiB; }
                else if (0 < (ds._data_size / (1024ll)))                            { u = io::KiB; }
            }
        }
        else {
            u = ds._data_unit;
        }
        switch (u) {
            case KiB:  s = s / (1024.0); break;
            case MiB:  s = s / (1024.0 * 1024.0); break;
            case GiB:  s = s / (1024.0 * 1024.0 * 1024.0); break;
            case TiB:  s = s / (1024.0 * 1024.0 * 1024.0 * 1024.0); break;
            case Byte:
            default:   break;
        }

        std::string us = data_size_unit_string(u);

        if (io::Byte == u) {
            os << ds._data_size << us;
        }
        else {
            os << std::fixed << std::setprecision(ds._dec_places)
               << s << us;
        }
    }
    else {
        os.setstate(std::ios_base::failbit);
    }

    return os;
}

} // namespace io
} // namespace scm


#include <SFML/Graphics.hpp>
#include <Eigen/Dense>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/System/Vector2.hpp>

using namespace std;
using namespace Eigen;

class TileMap : public sf::Drawable, public sf::Transformable
{
public:
  bool load(const std::string& tileset, sf::Vector2u tileSize, sf::Vector2u displaySize, MatrixXf tiles);
private:
  virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const;
  sf::VertexArray m_vertices;
  sf::Texture m_tileset;
};


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

#include <vmmlib/types.hpp>
#include <vmmlib/vector.hpp>

#define BOOST_TEST_MODULE vector
#include <boost/test/unit_test.hpp>

#ifndef M_PI
#define M_PI 3.14159265358979323846264338327
#endif

using namespace vmml;

BOOST_AUTO_TEST_CASE(base)
{
    vector<4, double> v;
    double data[] = {1, 2, 3, 4};

    v.iter_set(data, data + 4);

    std::stringstream str;
    str << v;
    BOOST_CHECK_EQUAL(str.str(), "[ 1 2 3 4 ]");

    // tests copyFrom1DimCArray function
    size_t tmp = 1;
    for (size_t index = 0; index < 4; ++index, ++tmp)
    {
        BOOST_CHECK(v.at(index) == tmp);
    }

    tmp = 4;
    float dataf[] = {4, 3, 2, 1};
    v.iter_set(dataf, dataf + 4);
    for (size_t index = 0; index < 4; ++index, --tmp)
    {
        BOOST_CHECK(v.at(index) == tmp);
    }
}

BOOST_AUTO_TEST_CASE(plus)
{
    double data[] = {1, 2, 3, 4};
    double datad[] = {4, 3, 2, 1};

    // tests operator+ function
    vector<4, double> v(data);
    vector<4, double> v_other(datad);
    vector<4, double> v_result = v + v_other;

    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK(v_result.at(index) == 5);

    v_result = v;
    v_result += v_other;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK(v_result.at(index) == 5);

    v = vector<4, double>(data);
    v_result = v + 2.;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK(v_result.at(index) == index + 3);

    v_result = v;
    v_result += 2;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK(v_result.at(index) == index + 3);
}

BOOST_AUTO_TEST_CASE(minus)
{
    double data[] = {1, 2, 3, 4};
    double datad[] = {1, 2, 3, 4};

    // tests operator- function
    vector<4, double> v(data);
    vector<4, double> v_other(datad);
    vector<4, double> v_result;

    v_result = v - v_other;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK(v_result.at(index) == 0);

    v_result = v;
    v_result -= v_other;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK(v_result.at(index) == 0);

    v_result = v - 1.0;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK(v_result.at(index) == index);

    v_result = v;
    v_result -= 1.0;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK(v_result.at(index) == index);
}

BOOST_AUTO_TEST_CASE(times)
{
    double data[] = {1, 2, 3, 4};
    double datad[] = {24, 12, 8, 6};

    // tests operator* function
    vector<4, double> v(data);
    vector<4, double> v_other(datad);
    vector<4, double> v_result;

    v_result = v * v_other;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK(v_result.at(index) == 24);

    v_result = v;
    v_result *= v_other;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK(v_result.at(index) == 24);

    v_result = v * 2.0;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK(v_result.at(index) == v.at(index) * 2.0);

    v_result = v;
    v_result *= 2.0;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK(v_result.at(index) == v.at(index) * 2.0);
}

BOOST_AUTO_TEST_CASE(divide)
{
    double data[] = {1, 2, 3, 4};
    double datad[] = {2, 4, 6, 8};

    // tests operator/ function
    vector<4, double> v(data);
    vector<4, double> v_other(datad);
    vector<4, double> v_result;

    v_result = v / v_other;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK((v_result.at(index) - 0.5) < 1e-12);

    v_result = v;
    v_result /= v_other;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK((v_result.at(index) - 0.5) < 1e-12);

    v_result = v / 1.5;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK((v_result.at(index) - (v.at(index) / 1.5)) < 1e-12);

    v_result = v;
    v_result /= 1.5;
    for (size_t index = 0; index < 4; ++index)
        BOOST_CHECK((v_result.at(index) - (v.at(index) / 1.5)) < 1e-12);
}

BOOST_AUTO_TEST_CASE(vec_norm)
{
    vector<4, double> v;
    double data[] = {1, 2, 3, 4};

    // tests norm / normSquared (length/lengthSquared) computation
    vector<4, double> vec(data);

    double normSquared = vec.squared_length();
    BOOST_CHECK(normSquared == 1 * 1 + 2 * 2 + 3 * 3 + 4 * 4);

    double norm = vec.length();
    BOOST_CHECK(sqrt(normSquared) == norm);

    // tests normalize
    vec = vector<4, double>(data);
    vec.normalize();
    BOOST_CHECK_CLOSE(vec.length(), 1.0, 0.0000001);

    // constructor tests
    double vData[] = {1, 2, 3, 4};
    vector<4, double> v4(1, 2, 3, 4);

    vector<2, double> v2C(vData);
    vector<2, double> v2(1, 2);

    BOOST_CHECK(v2 == v2C);

    vector<3, double> v3C(vData);
    vector<3, double> v3(1, 2, 3);

    BOOST_CHECK(v3 == v3C);

    vector<4, double> v4C(vData);

    BOOST_CHECK(v4 == v4C);

    double vData2[] = {23, 23, 23, 23};
    v4C = vector<4, double>(vData2);

    vector<4, double> v4_(23);
    BOOST_CHECK(v4_ == v4C);

    v3 = vector<3, double>(vData);
    v4C = vector<4, double>(vData);

    vector<4, double> v4from3_1(v3, vData[3]);
    BOOST_CHECK(v4from3_1 == v4C);

    double hvData[] = {1., 2., 3., 0.25};
    double xvData[] = {4.0, 8.0, 12.0};

    vector<4, double> homogenous;
    homogenous.iter_set(hvData, hvData + 4);
    vector<3, double> nonh;
    nonh.iter_set(xvData, xvData + 3);

    vector<4, double> htest(nonh);

    // to-homogenous-coordinates ctor
    BOOST_CHECK((htest == vector<4, double>(4, 8., 12., 1.)));

    vector<3, double> nhtest(homogenous);

    // from homogenous-coordiates ctor
    BOOST_CHECK(nhtest == nonh);

    // set tests
    double vCData[] = {2, 3, 4, 5};
    vec.set(2, 3, 4, 5);
    vector<4, double> vecCorrect(vCData);
    BOOST_CHECK(vec == vecCorrect);

    vec.set(2);

    double vCData2[] = {2, 2, 2, 2};
    vecCorrect = vector<4, double>(vCData2);
    BOOST_CHECK(vec == vecCorrect);

    vector<3, double> v1(2, 3, 4);

    // component accessors
    vector<4, double> vd(1, 2, 3, 4);
    BOOST_CHECK(vd.x() == 1 && vd.y() == 2 && vd.z() == 3 && vd.w() == 4);
}

BOOST_AUTO_TEST_CASE(dotprod)
{
    // dot product
    vector<3, float> v0(1, 2, 3);
    vector<3, float> v1(-6, 5, -4);
    BOOST_CHECK(v0.dot(v1) == -8);
}

BOOST_AUTO_TEST_CASE(crossprod)
{
    vector<3, float> v0(1, 2, 3);
    const vector<3, float> v1(-6, 5, -4);
    const vector<3, float> vcorrect(-23, -14, 17);

    BOOST_CHECK_EQUAL(cross(v0, v1), vcorrect);
    BOOST_CHECK_EQUAL(v0.cross(v1), vcorrect);
}

BOOST_AUTO_TEST_CASE(normal)
{
    const vmml::vector<3, float> v1(0.f, 0.f, 0.f);
    const vmml::vector<3, float> v2(0.f, 1.f, 0.f);
    const vmml::vector<3, float> v3(1.f, 0.f, 0.f);
    const vmml::vector<3, float> n(0.f, 0.f, -1.f);
    BOOST_CHECK_EQUAL(vmml::compute_normal(v1, v2, v3), n);
}

BOOST_AUTO_TEST_CASE(minMax)
{
    vector<4, float> vf(-1.0f, 3.0f, -99.0f, -0.9f);
    vector<4, size_t> vui(0, 5, 2, 4);

    size_t index = vf.find_min_index();
    float f = vf.find_min();

    BOOST_CHECK(index == 2 && f == -99.0f);

    index = vf.find_max_index();
    f = vf.find_max();
    BOOST_CHECK(index == 1 && f == 3.0f);

    index = vui.find_min_index();
    size_t ui = vui.find_min();
    BOOST_CHECK(index == 0 && ui == 0);

    index = vui.find_max_index();
    ui = vui.find_max();
    BOOST_CHECK(index == 1 && ui == 5);
}

BOOST_AUTO_TEST_CASE(product)
{
    const vector<3, float> v0(1, 2, 3);
    BOOST_CHECK_EQUAL(v0.product(), 6);

    const vector<3, int> v1(-6, 5, -4);
    BOOST_CHECK_EQUAL(v1.product(), 120);
}

BOOST_AUTO_TEST_CASE(tbd1)
{
    vector<4, float> v1(-1.0f, 3.0f, -99.0f, -0.9f);
    float f = 4.0f;
    vector<4, float> v_scaled = f * v1;

    BOOST_CHECK(v_scaled == (vector<4, float>(-4.0f, 12.0f, -396.0f, -3.6f)));

    // ???
    vector<3, float> vf(3.0, 2.0, 1.0);
    vector<3, double> vd(vf);
    vector<3, double>::const_iterator it = vd.begin(), it_end = vd.end();
    vector<3, float>::const_iterator fit = vf.begin();
    for (; it != it_end; ++it, ++fit)
    {
        BOOST_CHECK(*it == *fit);
    }
    vd = vf;
    for (; it != it_end; ++it, ++fit)
    {
        BOOST_CHECK(*it == *fit);
    }
}

BOOST_AUTO_TEST_CASE(subVector)
{
    vector<4, float> v4(3.0, 2.0, 1.0, 1.0);
    vector<3, float> v3 = v4.get_sub_vector<3, 0>();
    BOOST_CHECK(v3.x() == v4.x() && v3.y() == v4.y());
    v3.normalize();

    BOOST_CHECK_NE(v3.x(), v4.x());
    BOOST_CHECK_NE(v3.y(), v4.y());

    v4.set_sub_vector<3, 1>(v3);
    BOOST_CHECK_EQUAL(v3.x(), v4.y());
    BOOST_CHECK_EQUAL(v3.y(), v4.z());
}

BOOST_AUTO_TEST_CASE(tbd2)
{
    // elementwise sqrt
    vector<4, double> vsq(9.0, 4.0, 1.0, 2.0);
    vector<4, double> vsq_check(3.0, 2.0, 1.0, std::sqrt(2.0));
    vsq.sqrt_elementwise();
    BOOST_CHECK_EQUAL(vsq, vsq_check);

    // elementwise sqrt
    vector<4, float> vr(9.0, 4.0, 1.0, 2.0);
    vector<4, float> vr_check(0.1111111119389534, 0.25, 1, 0.5);
    vr.reciprocal();
    BOOST_CHECK(vr == vr_check);
}

BOOST_AUTO_TEST_CASE(l2norm)
{
    vector<4, float> vr(9.0, 4.0, 1.0, 2.0);
    double v_norm_check = 10.09950493836208;
    double v_norm = vr.norm();

    BOOST_CHECK((v_norm - v_norm_check) < 0.0001);
}

BOOST_AUTO_TEST_CASE(rotateVec)
{
    vmml::Vector3f vector = vmml::Vector3f::forward();
    vector.rotate(float(M_PI), vmml::Vector3f::up());
    BOOST_CHECK_MESSAGE(vector.equals(vmml::Vector3f::backward()), vector);
    BOOST_CHECK_MESSAGE(vmml::rotate(vector, float(M_PI),
                                     vmml::Vector3f::left())
                            .equals(vmml::Vector3f::forward()),
                        vmml::rotate(vector, float(M_PI),
                                     vmml::Vector3f::left()));
}


#ifndef ANDRES_GRAPH_PYTHON_GRAPH_EXPORTER_HXX
#define ANDRES_GRAPH_PYTHON_GRAPH_EXPORTER_HXX

#include <boost/python/def_visitor.hpp>

namespace bp = boost::python;
namespace agraph = andres::graph;

template<class GRAPH>
class GraphExporter : public bp::def_visitor<GraphExporter<GRAPH> >
{
public:
    typedef GraphExporter<GRAPH> Exporter;
    typedef GRAPH Self;
    friend class def_visitor_access;

    GraphExporter(const std::string & clsName)
    : clsName_(clsName){
    }

    template <class classT>
    void visit(classT& c) const
    {
        c
            .add_property("numberOfVertices", &Self::numberOfVertices)
            .add_property("numberOfEdges",    &Self::numberOfEdges)

            .def("findEdge",&Exporter::findEdge)
            .def("uvIds",&Exporter::uvIds)
        ;
    }

    static bp::tuple findEdge(Self & self, const std::size_t u, const std::size_t v){
        const auto t = self.findEdge(u,v);
        return bp::make_tuple(t.first,t.second);
    }

    static vigra::NumpyAnyArray uvIds(const Self & self){
        vigra::TinyVector<int, 1> shape(self.numberOfEdges());
        vigra::NumpyArray<1, vigra::TinyVector<uint64_t,2> > out;
        out.reshapeIfEmpty(shape);
        for(auto e=0; e<self.numberOfEdges(); ++e){
            out(e)[0] =  self.vertexOfEdge(e, 0);
            out(e)[1] =  self.vertexOfEdge(e, 1);
        }
        return out;
    }

    std::string clsName_;
};

#endif


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

#include <boost/hana/detail/assert.hpp>
#include <boost/hana/integral.hpp>
#include <boost/hana/list/instance.hpp>
#include <boost/hana/type.hpp>
using namespace boost::hana;

int main() {
    //! [main]
    BOOST_HANA_CONSTEXPR_LAMBDA auto xs = list(
        int_<1>, type<int>, int_<2>, type<float>, int_<3>, type<void>, type<char>
    );
    BOOST_HANA_CONSTANT_ASSERT(
        filter(xs, in ^ list(int_<3>, type<int>, type<void>))
        ==
        list(type<int>, int_<3>, type<void>)
    );
    //! [main]
}


/*-------------HO.cpp---------------------------------------------------------//
*
*              HO.cpp -- Diffusion Monte Carlo for Schroedy
*
* Purpose: Implement the quantum Monte Carlo (diffusion) by Kosztin:
*          http://www.thphys.uni-heidelberg.de/~wetzel/qmc2006/KOSZ96.pdf
*
*    Note: This algorithm may be improved by later work
*          A "psip" is an imaginary configuration of electrons in space.
*          Requires c++11 for random and Eigen for matrix
*          Protons assume to be at origin
*          A lot of this algorithm is more easily understood here:
*              http://www.thphys.uni-heidelberg.de/~wetzel/qmc2006/KOSZ96.pdf
*
*-----------------------------------------------------------------------------*/

#include <iostream>
#include <random>
#include <Eigen/Core>
#include <fstream>

#define DOF 1
#define SIZE 2000
#define DIMS (DOF + 2)
#define binnum 40

typedef Eigen::Matrix<double, // typename Scalar
   SIZE, // int RowsAtCompileTime,
   DIMS, // int ColsAtCompileTime,
   0> // int Options = 0,
   MatrixPSIP;

struct H3plus {
    MatrixPSIP pos;
    double Vref, dt, Energy;
    int psipnum, id;
    std::vector<int> bins = std::vector<int>(binnum, 0);;
};

// Populate a distribution of particles for QMC
void populate(H3plus& state);

// Calculates energy of a configuration and stores it into the final element of
// the MatrixPSIP
void find_weights(H3plus& state);

// Branching scheme
void branch(H3plus& state);

// Random walking of matrix of position created in populate
void diffuse(H3plus& state, std::ostream& output);

// Adding function to bin data into wavefunction
void bin(H3plus& state, std::ostream& output);

// output certain elements in array
void arrayout(H3plus& state, int length);

/*----------------------------------------------------------------------------//
* MAIN
*-----------------------------------------------------------------------------*/

int main(){

    std::ofstream output("out.dat", std::ostream::out);
    H3plus state;

    state.Vref = 0;
    state.dt = 0.1;
    state.psipnum = 100;
    state.Energy = 0;
    state.id = 0;

    populate(state);

    std::cout << state.Vref << '\t' << state.psipnum << '\n';

    /*
    for (size_t i = 0; i < state.psipnum; i++){
        std::cout << " final element is: " << state.pos(i,DIMS-2) << '\n';
    }
    */

    diffuse(state, output);

    //output << state.pos << '\n';

}

/*----------------------------------------------------------------------------//
* SUBROUTINES
*-----------------------------------------------------------------------------*/

// Populate a distribution of particles for QMC
// Unlike Anderson, we are initilizing each psip randomly from a distribution.
// This might scre things up, because
void populate(H3plus& state){

    std::default_random_engine generator;
    std::uniform_real_distribution<double> distribution(-1.0,1.0);

    for (size_t i = 0; i < state.psipnum; i++){
        state.pos(i,0) = 0;
        state.pos(i,DIMS-1) = state.id;
        state.id++;
    }

    find_weights(state);

    /*
    for (size_t i = 0; i < state.psipnum; i++){
        std::cout << state.pos(i,DIMS-2) << '\n';
    }
    */

}

// Calculates energy of a configuration and stores it into the final element of
// the MatrixPSIP
// Note: When calculating the potential, I am not sure whether we need to use
//       absolute value of distance or the distance, itself.
// Note: Inefficient. Can calculate energy on the fly with every generation of
//       psip. Think about it.
// Note: Vref will be a dummy variable for now.
void find_weights(H3plus& state){

    double dist, pot, pot_tot = 0;

    std::default_random_engine gen;
    std::uniform_real_distribution<double> distribution(0,1);

    // Note that this is specific to the Anderson paper
    // Finding the distance between electrons, then adding the distances
    // from the protons to the electrons.
    for (size_t i = 0; i < state.psipnum; i++){
        pot = 0.5 * state.pos(i,0) * state.pos(i,0);
        state.pos(i,DIMS-2) = pot;

        pot_tot += pot;

    }

    // defining the new reference potential to psipnum down.
    state.Energy = pot_tot / state.psipnum;
    state.Vref = state.Energy
                 - ((state.psipnum - 1000) / (1000 * state.dt));

    for (size_t i = 0; i < state.psipnum; i++){
        state.pos(i,DIMS-2) = (int)(1-(state.pos(i,DIMS-2) - state.Vref)
                                    * state.dt
                              + distribution(gen));
        if (state.pos(i,DIMS-2) > 3){
            state.pos(i,DIMS-2) = 3;
        }

    }
}

// Branching scheme
void branch(H3plus& state){

    find_weights(state);

    /*
    for (size_t i = 0; i < state.psipnum; i++){
        std::cout << state.pos(i,DIMS-2) << '\n';
    }
    */

    int variable, offset = 0, psip_old = state.psipnum, births = 0, tmpi = 0;

    for (size_t i = 0; i < psip_old + births; i++){

        //std::cout << i << '\n';

        variable = state.pos(i, DIMS-2);

        switch (variable){
            // Destruction
            case 0: state.psipnum--;
                    break;

            // Creation of 1
            case 2: state.psipnum++;
                    births++;
                    for (size_t j = 0; j < state.pos.cols() - 2; j++){
                        state.pos(psip_old+births-1,j) = state.pos(i,j);
                    }
                    //std::cout << "writing: " << i << " to "
                    //          << psip_old+births-1 << '\n';
                    state.pos(psip_old+births-1,DIMS-2) = 1;
                    state.pos(psip_old+births-1,DIMS-1) = state.id;
                    state.id++;
                    break;

            // Creation of 2
            case 3: state.psipnum += 2;
                    births += 2;
                    for (size_t k = 0; k < 2; k++){
                        for (size_t j = 0; j < state.pos.cols() - 2; j++){
                            state.pos(psip_old-k+births-1, j) = state.pos(i, j);
                            //std::cout << state.pos(psip_old-k+births-1, j)
                            //          << '\n';
                        }
                        state.pos(psip_old-k+births-1,DIMS-2) = 1;
                        state.pos(psip_old-k+births-1,DIMS-1) = state.id;
                        state.id++;
                        //std::cout << "writing: " << i << " to "
                        //          << psip_old-k+births-1 << '\n';
                    }
                    break;

        }

    }

    //arrayout(state, state.psipnum);

    // Adjustment for offset
    // Note: Account for the situation where offset is greater than arraysize
    for (size_t i = 0; i < SIZE; i++){
        if (state.pos(i,DIMS-2) != 0){
            for (size_t j = 0; j < state.pos.cols(); j++){
                state.pos(tmpi,j) = state.pos(i,j);
            }
            tmpi++;
        }
        if (i > state.psipnum){
            for (size_t j = 0; j < state.pos.cols(); j++){
                state.pos(i,j) = 0;
            }

        }
    }

}

// Random walking of matrix of position created in populate
// Step 1: Move particles via 6D random walk
// Step 2: Destroy and create particles as need based on Anderson
// Step 3: check energy, end if needed.
void diffuse(H3plus& state, std::ostream& output){

    // Let's initialize the randomness
    std::default_random_engine gen;
    std::normal_distribution<double> gaussian(0,1);

    double diff = 1, Vsave = 0, tmpbin, x;
    int wavenum = 1000;

    for (size_t k = 0; k < wavenum; k++){
        // For now, I am going to set a definite number of timesteps
        // This will be replaced by a while loop in the future.
        for (size_t t = 0; t < 100; t++){
        //while (diff > 0.01){
            Vsave = state.Vref;
            #pragma omp parallel for
            for (size_t i = 0; i < state.psipnum; i++){
                for (size_t j = 0; j < state.pos.cols() - 1; j++){
                    state.pos(i, j) += sqrt(state.dt) * gaussian(gen);
                }
            }
            branch(state);
            diff = sqrt((Vsave - state.Vref)*(Vsave - state.Vref));
            std::cout << state.Vref << '\t' << state.psipnum << '\n';
        }

        bin(state, output);
    }

    for (size_t i = 0; i < binnum; i++){
        tmpbin = state.bins[i] * 0.0001;
        output << tmpbin << '\n';
    }

    output << '\n' << '\n';

    for (size_t i = 0; i < binnum; i++){
        x = ((double)i / (double)binnum)*8.0 - 4.0;
        std::cout << x << '\n';
        tmpbin = exp(- x*x / 2);
        output << tmpbin << '\n';
    }

}

// Adding function to bin data into wavefunction
void bin(H3plus& state, std::ostream& output){

    double bound = 4, max = bound, min = -bound;

    for (size_t i = 0; i < state.psipnum; i++){
        for (size_t j = 0; j < binnum; j++){
            if (state.pos(i,0) >= ((max - min) / binnum) * j  - bound&&
                state.pos(i,0) < ((max - min) / binnum) * (j+1) - bound){
                state.bins[j] += 1;
            }
        }
    }

}

// output certain elements in array
void arrayout(H3plus& state, int length){

    for (size_t i = 0; i < length; i++){
        for (size_t j  = 0; j < 2; j++){
            std::cout << state.pos(i,DIMS - 1 - j) << '\t';
        }
        std::cout << '\n';
    }
}


// Boost.Geometry Index
//
// n-dimensional box-linestring intersection
//
// Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland.
//
// 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_INDEX_DETAIL_ALGORITHMS_PATH_INTERSECTION_HPP
#define BOOST_GEOMETRY_INDEX_DETAIL_ALGORITHMS_PATH_INTERSECTION_HPP

#include <boost/geometry/index/detail/algorithms/segment_intersection.hpp>

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

namespace boost { namespace geometry { namespace index { namespace detail {

namespace dispatch {

template <typename Indexable, typename Geometry, typename IndexableTag, typename GeometryTag>
struct path_intersection
{
    BOOST_MPL_ASSERT_MSG((false), NOT_IMPLEMENTED_FOR_THIS_GEOMETRY_OR_INDEXABLE, (path_intersection));
};

// TODO: FP type must be used as a relative distance type!
// and default_distance_result can be some user-defined int type
// BUT! This code is experimental and probably won't be released at all
// since more flexible user-defined-nearest predicate should be added instead

template <typename Indexable, typename Segment>
struct path_intersection<Indexable, Segment, box_tag, segment_tag>
{
    typedef typename default_distance_result<typename point_type<Segment>::type>::type comparable_distance_type;

    static inline bool apply(Indexable const& b, Segment const& segment, comparable_distance_type & comparable_distance)
    {
        typedef typename point_type<Segment>::type point_type;
        point_type p1, p2;
        geometry::detail::assign_point_from_index<0>(segment, p1);
        geometry::detail::assign_point_from_index<1>(segment, p2);
        return index::detail::segment_intersection(b, p1, p2, comparable_distance);
    }
};

template <typename Indexable, typename Linestring>
struct path_intersection<Indexable, Linestring, box_tag, linestring_tag>
{
    typedef typename default_length_result<Linestring>::type comparable_distance_type;

    static inline bool apply(Indexable const& b, Linestring const& path, comparable_distance_type & comparable_distance)
    {
        typedef typename ::boost::range_value<Linestring>::type point_type;
        typedef typename ::boost::range_const_iterator<Linestring>::type const_iterator;
        typedef typename ::boost::range_size<Linestring>::type size_type;

        const size_type count = ::boost::size(path);

        if ( count == 2 )
        {
            return index::detail::segment_intersection(b, *::boost::begin(path), *(::boost::begin(path)+1), comparable_distance);
        }
        else if ( 2 < count )
        {
            const_iterator it0 = ::boost::begin(path);
            const_iterator it1 = ::boost::begin(path) + 1;
            const_iterator last = ::boost::end(path);

            comparable_distance = 0;

            for ( ; it1 != last ; ++it0, ++it1 )
            {
                typename default_distance_result<point_type, point_type>::type
                    dist = geometry::distance(*it0, *it1);

                comparable_distance_type rel_dist;
                if ( index::detail::segment_intersection(b, *it0, *it1, rel_dist) )
                {
                    comparable_distance += dist * rel_dist;
                    return true;
                }
                else
                    comparable_distance += dist;
            }
        }

        return false;
    }
};

} // namespace dispatch

template <typename Indexable, typename SegmentOrLinestring>
struct default_path_intersection_distance_type
{
    typedef typename dispatch::path_intersection<
        Indexable, SegmentOrLinestring,
        typename tag<Indexable>::type,
        typename tag<SegmentOrLinestring>::type
    >::comparable_distance_type type;
};

template <typename Indexable, typename SegmentOrLinestring> inline
bool path_intersection(Indexable const& b,
                       SegmentOrLinestring const& path,
                       typename default_path_intersection_distance_type<Indexable, SegmentOrLinestring>::type & comparable_distance)
{
    // TODO check Indexable and Linestring concepts

    return dispatch::path_intersection<
            Indexable, SegmentOrLinestring,
            typename tag<Indexable>::type,
            typename tag<SegmentOrLinestring>::type
        >::apply(b, path, comparable_distance);
}

}}}} // namespace boost::geometry::index::detail

#endif // BOOST_GEOMETRY_INDEX_DETAIL_ALGORITHMS_PATH_INTERSECTION_HPP