#include "agent.hpp"
#include "parameters.hpp"
#include <boost/foreach.hpp>

/// random number generator
std::mt19937 rng;

/// function to update population Rtree
bgi::rtree< value, bgi::quadratic<16> > makeRtree (std::vector<Agent> &pop) {
    // initialise rtree
    bgi::rtree< value, bgi::quadratic<16> > tmpRtree;
    for (size_t i = 0; i < pop.size(); ++i)
    {
        point p = point(pop[i].x, pop[i].y);
        tmpRtree.insert(std::make_pair(p, i));
    }
    return tmpRtree;
}

/// random trait value dist
std::uniform_real_distribution<float> randTrait(-0.1f, 0.1f);

// normal distr around 0
std::normal_distribution<float> normDist(0.0f, 0.1f);

/// initialise the population at random positions
void Agent::randomPosition(const float landsize) {
    x = randTrait(rng) * static_cast<double>(landsize);
    y = randTrait(rng) * static_cast<double>(landsize);
}

/// initialise with random position
void popRandomPos(std::vector<Agent> &pop, const float landsize) {
  for(auto& indiv : pop) {
    indiv.randomPosition(landsize);
  }
}

/// pick random traits
void Agent::randomTraits() {
    coefFood = randTrait(rng);
    coefNbrs = randTrait(rng);
    coefRisk = randTrait(rng);
}

/// pop level random traits
void popRandomTraits(std::vector<Agent> &pop) {
  for(auto& indiv : pop) {
    indiv.randomTraits();
  }
}

/// function to count neighbours
int Agent::countNbrsAt(const float perception,
    const float xloc, const float yloc,
    bgi::rtree< value, bgi::quadratic<16> > &agentRtree) {
    // work in progress
    int nNbrs = 0;
    std::vector<value> nearAgents;

    // std::cout << "id = " << id << " at " << bg::wkt<point> (point(coordX[id], coordY[id])) << "\n";

    // query for a simple box
    agentRtree.query(bgi::satisfies([&](value const& v) {
        return bg::distance(v.first, point(xloc, yloc)) < perception;}),
        std::back_inserter(nearAgents));

    BOOST_FOREACH(value const& v, nearAgents) {
        // std::cout << bg::wkt<point> (v.first) << " - " << v.second << "\n";
        // subtract energy per neighbour within range
        nNbrs ++;
    }
    nearAgents.clear();
    return nNbrs;
}

/// function to get neighbour identities
std::vector<int> Agent::getNbrsId(const float perception,
    const float xloc, const float yloc,
    bgi::rtree< value, bgi::quadratic<16> > &agentRtree) {
    // work in progress
    int nNbrs = 0;
    std::vector<value> nearAgents;

    std::vector<int> nearAgentId;

    // query for a simple box
    agentRtree.query(bgi::satisfies([&](value const& v) {
        return bg::distance(v.first, point(xloc, yloc)) < perception;}),
        std::back_inserter(nearAgents));

    BOOST_FOREACH(value const& v, nearAgents) {
        nearAgentId.push_back(v.second);
    }
    nearAgents.clear();

    return nearAgentId;
}

/// agent function to choose a new position
void Agent::doSenseMove(FastNoiseLite &noise,
    const float t_, const float perception,
    const int directions,
    const float clamp, const float landsize,
    bgi::rtree< value, bgi::quadratic<16> > &agentRtree, const float costMove,
    const bool allow_compete) {

    // set default values --- stay in place
    float newX = x; float newY = y;
    float foodHere = noise.GetNoise(newX, newY, static_cast<float>(t_));

    // food here is 0 if less than clamp (0.f)
    foodHere = foodHere > clamp ? foodHere : 0.f;
    float nbrsHere = allow_compete ? static_cast<float>(countNbrsAt(perception, newX, newY, agentRtree)) : 0.f;

    // suitability at location is calculated as having risk = 0.0
    // that is, no risk is perceived here.
    float best_suit = (coefFood * foodHere) + (coefNbrs * nbrsHere) + (coefRisk * (nbrsHere * nbrsHere));

    float twopi = 2.f * M_PI;
    // what increment for nDirections samples in a circle around the agent
    float increment = twopi / static_cast<float>(directions);
    float sampleX, sampleY;
    // cycle through samples and check for angle with max resources
    for(float theta = 0.f; theta < twopi - increment; theta += increment) {

        sampleX = x + (perception * static_cast<float>(cos(theta)));
        sampleY = y + (perception * static_cast<float>(sin(theta)));

        // agent senses 0.f if food is less than clamp
        foodHere = noise.GetNoise(sampleX, sampleY, static_cast<float>(t_));
        foodHere = foodHere > clamp ? foodHere : 0.f;

        nbrsHere = allow_compete ? static_cast<float>(countNbrsAt(perception, sampleX, sampleY, agentRtree)) : 0.f;

        // suitability at new location is modified by individual's perceived risk
        float new_suit = (coefFood * foodHere) + (coefNbrs * nbrsHere) + (coefRisk * (nbrsHere * nbrsHere));

        if (new_suit > best_suit) {
            newX = sampleX;
            newY = sampleY;
            // best_suit = new_suit;
            break; // chose the new location if
        }
    }

    // subtract cost of movement if any
    // comparing floats is difficult so determine if moved distance is
    // more than 1% of the perception range (default movement distance)
    float dist = std::sqrt(std::pow(x - newX, 2.0) + std::pow(y - newY, 2.0));
    if(dist > perception / 1e2f) {
        energy -= costMove;
        moved += perception; // add distance moved
    }

    // get new position
    x = newX; y = newY;

    // crudely wrap movement
    if((x > landsize) | (x < 0.f)) {
        x = std::fabs(std::fmod(x, landsize));
    }
    if((y > landsize) | (y < 0.f)) {
        y = std::fabs(std::fmod(y, landsize));
    }
}

/// agent function to forage
void Agent::doEnergetics(FastNoiseLite &noise,
    bgi::rtree< value, bgi::quadratic<16> > &agentRtree,
    const float perception,
    const float t_, const float clamp,
    const bool allow_compete) {

    // energy
    float energy_here = (noise.GetNoise(x, y, static_cast<float>(t_)));
    // Rcpp::Rcout << "energy here = " << energy_here << "\n";

    // both divided by number of neighbours
    int nNbrs = allow_compete ? countNbrsAt(perception, x, y, agentRtree) : 0;

    float nbrs_f = static_cast<float>(nbrs); // + 1.f; // to prevent divisions by 0

    // Rcpp::Rcout << "nbrs here = " << nbrs << "\n";
    energy_here = energy_here + (1.f - (1.f / nbrs_f));

    // Rcpp::Rcout << "scaled energy here = " << energy_here << "\n";
    // energetic balance
    energy +=  (energy_here < clamp ? 0.f : energy_here);

    // Rcpp::Rcout << "overall energy here = " << (energy_here < clamp ? 0.f : energy_here) << "\n";
}

/* population level functions */
/// population moves about and forages
void popMoveForageCompete(std::vector<Agent>& pop, FastNoiseLite &noise,
    const float t_,
    const float perception, const int directions,
    const float landsize, const float clamp,
    const float costMove,
    const bool allow_compete) {

    // make Rtree
    bgi::rtree< value, bgi::quadratic<16> > agentRtree;
    if (allow_compete) {
        agentRtree = makeRtree(pop);
    }

    for(auto& indiv : pop) {
        indiv.doSenseMove(noise, t_, perception, directions, clamp, landsize,
            agentRtree, costMove, allow_compete);
        indiv.doEnergetics(noise, agentRtree, perception, t_, clamp,
            allow_compete);
    }
}

/// minor function to handle energy
void handleFitness(std::vector<float> &vecFitness) {

    // negative energy is 0, add 1e-5 to all positive values
    for (size_t i = 0; i < vecFitness.size(); i++)
    {
        // Rcpp::Rcout << "raw energy total = " << vecFitness[i] << "\n";
        vecFitness[i] = vecFitness[i] > 0.f ? vecFitness[i] + 1e-5 : 1e-5;
        // Rcpp::Rcout << "scaled energy total = " << vecFitness[i] << "\n";
        assert(vecFitness[i] > 0.f && "Agent energy is 0!");
    }

    std::vector<float> tmpvec = vecFitness;
    std::sort(tmpvec.begin(), tmpvec.end());

    // get max fitness
    float maxFitness = tmpvec.back();

    // rescale values between 1e-5f and 1.f
    for (size_t j = 0; j < vecFitness.size(); ++j) {
        vecFitness[j] = vecFitness[j] / (maxFitness);
        assert(vecFitness[j] > 0.f && "Agent energy is 0!");
    }

}

/// make cauchy distribution
std::bernoulli_distribution randMutEvent(mutProb);
std::cauchy_distribution<float> randMutSize(0.f, mutSize);

/// pop reproduces
void doReproduce(std::vector<Agent>& pop, const float landsize) {
    // make fitness vec
    std::vector<float> vecFitness (pop.size());
    for (size_t a = 0; a < pop.size(); a++)
    {
        vecFitness[a] = pop[a].energy;
    }
    handleFitness(vecFitness);

    // weighted lottery
    std::discrete_distribution<> weightedLottery(vecFitness.begin(), vecFitness.end());

    // create new population
    std::vector<Agent> tmpPop(pop.size());

    // assign parents
    for (size_t a = 0; a < pop.size(); a++) {

        size_t idParent = static_cast<size_t> (weightedLottery(rng));
        // get parent position
        tmpPop[a].x = pop[idParent].x;
        tmpPop[a].y = pop[idParent].y;
        tmpPop[a].coefFood = pop[idParent].coefFood;
        tmpPop[a].coefNbrs = pop[idParent].coefNbrs;
        tmpPop[a].coefRisk = pop[idParent].coefRisk;
        tmpPop[a].energy = 1e-5f;
        tmpPop[a].nbrs = 0;
    }

    // mutation
    for(auto& indiv : tmpPop) {
        if (randMutEvent(rng)) {
            indiv.coefFood += randMutSize(rng);
        }
        if (randMutEvent(rng)) {
            indiv.coefNbrs += randMutSize(rng);
        }
        if (randMutEvent(rng)) {
            indiv.coefRisk += randMutSize(rng);
        }
    }

    // swap tmp pop for pop
    std::swap(pop, tmpPop);
    // randomise position?
    popRandomPos(tmpPop, landsize);
    tmpPop.clear();
}


/*
 * generator_date.hpp
 *
 *  Created on: 27.02.2016
 *      Author: mike_gresens
 */

#pragma once

#include <boost/date_time/posix_time/posix_time_types.hpp>

namespace hessian {
namespace generator {

value_visitor::result_type
value_visitor::operator()(const date_t& value)
{
	static const date_t EPOCH(boost::gregorian::date(1970, 1, 1));
	const boost::posix_time::time_duration duration = value - EPOCH;
	if (duration.seconds() == 0 && duration.fractional_seconds() == 0)
	{
		push_back<std::int8_t>('K');
		push_back<std::int32_t>(duration.total_seconds() / 60);
	}
	else
	{
		push_back<std::int8_t>('J');
		push_back<std::int64_t>(duration.total_milliseconds());
	}
}

}
}


// Copyright (c) 2022 CNES
//
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "pyinterp/geohash/string.hpp"

#include <pybind11/eigen.h>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

#include <Eigen/Core>

#include "pyinterp/geohash/int64.hpp"

namespace py = pybind11;
namespace geohash = pyinterp::geohash;
namespace geodetic = pyinterp::geodetic;

// Checking the value defining the precision of a geohash.
constexpr static auto check_range(uint32_t precision) -> void {
  if (precision > 12) {
    throw std::invalid_argument("precision must be within [1, 12]");
  }
}

void init_geohash_string(py::module& m) {
  m.def(
       "encode",
       [](const Eigen::Ref<const Eigen::VectorXd>& lon,
          const Eigen::Ref<const Eigen::VectorXd>& lat,
          const uint32_t precision) -> pybind11::array {
         check_range(precision);
         return geohash::string::encode(lon, lat, precision);
       },
       py::arg("lon"), py::arg("lat"), py::arg("precision") = 12,
       R"__doc__(
Encode coordinates into geohash with the given precision.

Args:
  lon (numpy.ndarray) Longitudes in degrees.
  lat (numpy.ndarray) Latitudes in degrees.
  precision (int, optional) Number of bits used to encode the geohash code.
    Defaults to 12.
Returns:
  numpy.ndarray: geohash codes.
Raises:
  ValueError: If the given precision is not within [1, 12].
  ValueError: If the lon and lat vectors have different sizes.
)__doc__")
      .def(
          "decode",
          [](const pybind11::array& hash,
             const bool round) -> std::tuple<Eigen::VectorXd, Eigen::VectorXd> {
            return geohash::string::decode(hash, round);
          },
          py::arg("hash"), py::arg("round") = false,
          R"__doc__(
Decode hashes into a geographic points.

Args:
  hash (numpy.ndarray): GeoHash codes.
  round (optional, bool): If true, the coordinates of the point will be
    rounded to the accuracy defined by the GeoHash. Defaults to False.
Returns:
  tuple: longitudes/latitudes of the decoded points.
)__doc__")
      .def(
          "area",
          [](const pybind11::array& hash,
             const std::optional<geodetic::System>& wgs) -> Eigen::VectorXd {
            return geohash::string::area(hash, wgs);
          },
          py::arg("hash"), py::arg("wgs") = py::none(),
          R"__doc__(
Calculated the area caovered by the GeoHash codes.

Args:
  hash (numpy.ndarray): GeoHash codes.
  wgs (optional, pyinterp.geodetic.System): WGS used to calculate the area.
    Defaults to WGS84.

Returns:
  double: calculated areas.
)__doc__")
      .def(
          "bounding_boxes",
          [](const std::optional<geodetic::Box>& box,
             const uint32_t precision) -> py::array {
            check_range(precision);
            return geohash::string::bounding_boxes(box, precision);
          },
          py::arg("box") = py::none(), py::arg("precision") = 1,
          R"__doc__(
Returns all geohash codes contained in the defined bounding box.

Args:
  box (pyinterp.geohash.Box, optional): Bounding box. Default to the
    global bounding box.
  precision (int, optional): Required accuracy. Defaults to 1.
Returns:
  numpy.ndarray: GeoHash codes.
Raises:
    ValueError: If the given precision is not within [1, 12].
    MemoryError: If the memory is not sufficient to store the result.
)__doc__")
      .def(
          "bounding_boxes",
          [](const geodetic::Polygon& polygon, const uint32_t precision,
             const size_t num_threads) -> py::array {
            check_range(precision);
            return geohash::string::bounding_boxes(polygon, precision,
                                                   num_threads);
          },
          py::arg("box") = py::none(), py::arg("precision") = 1,
          py::arg("num_threads") = 0,
          R"__doc__(
Returns all geohash codes contained in the defined polygon.

Args:
  polygon (pyinterp.geodetic.Polygon): Polygon.
  precision (int, optional): Required accuracy.
    Defaults to 1.
  num_threads (int, optional): The number of threads to use for the
    computation. If 0 all CPUs are used. If 1 is given, no parallel
    computing code is used at all, which is useful for debugging.
    Defaults to ``0``.
Returns:
  numpy.ndarray: GeoHash codes.
Raises:
    ValueError: If the given precision is not within [1, 12].
    MemoryError: If the memory is not sufficient to store the result.
)__doc__")
      .def(
          "where",
          // We want to return an associative dictionary between bytes and
          // tuples and not str and tuples.
          [](const pybind11::array& hash) -> py::dict {
            auto result = py::dict();
            for (auto&& item : geohash::string::where(hash)) {
              auto key = py::bytes(item.first);
              result[key] = py::cast(item.second);
            }
            return result;
          },
          py::arg("hash"),
          R"__doc__(
Returns the start and end indexes of the different GeoHash boxes.

Args:
  hash (numpy.ndarray): GeoHash codes.
Returns:
  dict: dictionary between successive identical geohash codes and start and
    end indexes in the table provided as input.
)__doc__")
      .def(
          "transform",
          [](const py::array& hash, const uint32_t precision) -> py::array {
            check_range(precision);
            return geohash::string::transform(hash, precision);
          },
          py::arg("hash"), py::arg("precision") = 1, R"__doc__(
Transforms the given codes from one precision to another. If the given
precision is higher than the precision of the given codes, the result contains
a zoom in, otherwise it contains a zoom out.

Args:
  hash (numpy.ndarray): GeoHash codes.
  precision (int, optional): Required accuracy. Defaults to 1.
Returns:
  numpy.ndarray: GeoHash codes transformed.
Raises:
  ValueError: If the given precision is not within [1, 12].
)__doc__");
}


/* ----------------------------------------------------------------------------

 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
 * Atlanta, Georgia 30332-0415
 * All Rights Reserved
 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)

 * See LICENSE for the license information

 * -------------------------------------------------------------------------- */

/**
 *  @file  testEvent.cpp
 *  @brief Unit tests for space time "Event"
 *  @author Frank Dellaert
 *  @author Jay Chakravarty
 *  @date December 2014
 */

#include <gtsam/base/numericalDerivative.h>
#include <gtsam/nonlinear/Expression.h>
#include <gtsam_unstable/geometry/Event.h>

#include <CppUnitLite/TestHarness.h>

#include <boost/bind/bind.hpp>

using namespace boost::placeholders;
using namespace std;
using namespace gtsam;

// Create a noise model for the TOA error
static const double ms = 1e-3;
static const double cm = 1e-2;
typedef Eigen::Matrix<double, 1, 1> Vector1;
static SharedNoiseModel model(noiseModel::Isotropic::Sigma(1, 0.5 * ms));

static const double timeOfEvent = 25;
static const Event exampleEvent(timeOfEvent, 1, 0, 0);
static const Point3 microphoneAt0(0, 0, 0);

static const double kSpeedOfSound = 340;
static const TimeOfArrival kToa(kSpeedOfSound);

//*****************************************************************************
TEST(Event, Constructor) {
  const double t = 0;
  Event actual(t, 201.5 * cm, 201.5 * cm, (212 - 45) * cm);
}

//*****************************************************************************
TEST(Event, Toa1) {
  Event event(0, 1, 0, 0);
  double expected = 1. / kSpeedOfSound;
  EXPECT_DOUBLES_EQUAL(expected, kToa(event, microphoneAt0), 1e-9);
}

//*****************************************************************************
TEST(Event, Toa2) {
  double expectedTOA = timeOfEvent + 1. / kSpeedOfSound;
  EXPECT_DOUBLES_EQUAL(expectedTOA, kToa(exampleEvent, microphoneAt0), 1e-9);
}

//*************************************************************************
TEST(Event, Derivatives) {
  Matrix14 actualH1;
  Matrix13 actualH2;
  kToa(exampleEvent, microphoneAt0, actualH1, actualH2);
  Matrix expectedH1 = numericalDerivative11<double, Event>(
      boost::bind(kToa, _1, microphoneAt0, boost::none, boost::none),
      exampleEvent);
  EXPECT(assert_equal(expectedH1, actualH1, 1e-8));
  Matrix expectedH2 = numericalDerivative11<double, Point3>(
      boost::bind(kToa, exampleEvent, _1, boost::none, boost::none),
      microphoneAt0);
  EXPECT(assert_equal(expectedH2, actualH2, 1e-8));
}

//*****************************************************************************
TEST(Event, Expression) {
  Key key = 12;
  Expression<Event> event_(key);
  Expression<Point3> knownMicrophone_(microphoneAt0);  // constant expression
  Expression<double> expression(kToa, event_, knownMicrophone_);

  Values values;
  values.insert(key, exampleEvent);
  double expectedTOA = timeOfEvent + 1. / kSpeedOfSound;
  EXPECT_DOUBLES_EQUAL(expectedTOA, expression.value(values), 1e-9);
}

//*****************************************************************************
TEST(Event, Retract) {
  Event event, expected(1, 2, 3, 4);
  Vector4 v;
  v << 1, 2, 3, 4;
  EXPECT(assert_equal(expected, event.retract(v)));
}

//*****************************************************************************
int main() {
  TestResult tr;
  return TestRegistry::runAllTests(tr);
}
//*****************************************************************************


#include <type_traits>
#include <cassert>

#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/placeholders.hpp>

using namespace boost::mpl;
using namespace boost::mpl::placeholders;

int main()
{
    typedef vector_c<int, 1, 2, 3> t1;
    typedef typename transform<t1, plus<_1, int_<1>>>::type t3;
    return 0;
}


// Fair Exponential Smoothing with Small Alpha by Juha Reunanen, 2015

#include "../fessa_scalar.hpp"

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(scalar)
{
    const fessa::ScalarFilter<> filter(0.001);

    const auto filter1 = filter.filter(1);
    BOOST_REQUIRE_EQUAL(filter1.getS(), 1.0);

    const auto filter2 = filter1.filter(2);
    BOOST_REQUIRE_GT(filter2.getS(), 1.5);
    BOOST_REQUIRE_LT(filter2.getS(), 1.501);

    const auto filter3 = filter1.filter(3);
    BOOST_REQUIRE_GT(filter3.getS(), 2.0);
    BOOST_REQUIRE_LT(filter3.getS(), 2.001);
}


/*
  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.

  Copyright (c) 2019 Panda Team
*/
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE CrossfilterJSRemove
#include "utils.hpp"
#include <boost/test/unit_test.hpp>
#include <vector>
#include <list>
#include <string>
#include <iostream>
#include <utility>
#include <algorithm>
#include <cmath>
#include "../crossfilter.hpp"

auto Infinity = std::numeric_limits<int>::max();
struct Rec3 {
    double foo;
    bool operator==(const Rec3& lhs) const { return foo == lhs.foo; }
};

struct RecIterable {
    std::vector<int> foo;
    int bar;
};

BOOST_TEST_DONT_PRINT_LOG_VALUE(Rec3)
BOOST_TEST_DONT_PRINT_LOG_VALUE(RecIterable)

BOOST_AUTO_TEST_SUITE(crossfilter_remove)
BOOST_AUTO_TEST_CASE(removing_record_works_for_a_group_with_cardinality_one)
{

    cross::filter<Rec3> data;
    auto foo = data.dimension([](auto d) { return d.foo; });
    auto div2 = foo.feature_count([](auto value) { return std::floor(value / 2); });
    auto positive = foo.feature_count([](auto value) { return value > 0; });

    data.add(std::vector<Rec3> { { 1 }, { 1.1 }, { 1.2 } });
    foo.filter(1.1);
    data.remove();
    foo.filter_all();
    data.remove();
    BOOST_TEST(foo.top(Infinity).empty());
}
BOOST_AUTO_TEST_CASE(removing_record_works_for_another_group_with_cardinality_one)
{

    cross::filter<Rec3> data;
    auto foo = data.dimension([](auto d) { return d.foo; });
    auto div2 = foo.feature_count([](auto value) { return std::floor(value / 2); });
    auto positive = foo.feature_count([](auto value) { return value > 0; });

    data.add(std::vector<Rec3> { { 0 }, { -1 } });

    BOOST_TEST(positive.all() == (std::vector<std::pair<bool, std::size_t>> { { false, 2 } }),
        boost::test_tools::per_element());
    foo.filter(0);
    data.remove();
    BOOST_TEST(positive.all() == (std::vector<std::pair<bool, std::size_t>> { { false, 1 } }),
        boost::test_tools::per_element());
    foo.filter_all();
    auto top = foo.top(Infinity);
    BOOST_TEST(top.size() == (unsigned long)1);
    if (!top.empty()) {
        BOOST_TEST(top[0].foo == -1);
    }
    //  BOOST_TEST(foo.top(Infinity) == (std::vector<Rec3>{{-1}}), boost::test_tools::per_element());
    data.remove();
    BOOST_TEST(foo.top(Infinity).empty());
}
BOOST_AUTO_TEST_CASE(removing_record_updates_dimension)
{
    cross::filter<Rec3> data;
    auto foo = data.dimension([](auto d) { return d.foo; });
    auto div2 = foo.feature_count([](auto value) { return std::floor(value / 2); });
    auto positive = foo.feature_count([](auto value) { return value > 0; });

    data.add(std::vector<Rec3> { { 1 }, { 2 } });

    foo.filter_exact(1);
    data.remove();
    foo.filter_all();
    auto top1 = foo.top(Infinity);
    BOOST_TEST(top1.size() == (unsigned long)1);
    if (!top1.empty()) {
        BOOST_TEST(top1[0].foo == 2);
    }

    data.remove();
    BOOST_TEST(foo.top(Infinity).empty());
}
BOOST_AUTO_TEST_CASE(removing_records_updates_group)
{
    cross::filter<Rec3> data;
    auto foo = data.dimension([](auto d) { return d.foo; });
    auto div2 = foo.feature_count([](auto value) { return std::floor(value / 2); });
    auto positive = foo.feature_count([](auto value) { return value > 0; });

    data.add(std::vector<Rec3> { { 1 }, { 2 }, { 3 } });

    BOOST_TEST(foo.top(Infinity) == (std::vector<Rec3> { { 3 }, { 2 }, { 1 } }), boost::test_tools::per_element());

    BOOST_TEST(div2.all() == (std::vector<std::pair<double, std::size_t>> { { 0, 1 }, { 1, 2 } }),
        boost::test_tools::per_element());

    foo.filter_range(1, 3);
    data.remove();
    foo.filter_all();

    BOOST_TEST(foo.top(Infinity) == (std::vector<Rec3> { { 3 } }), boost::test_tools::per_element());
    BOOST_TEST(
        div2.all() == (std::vector<std::pair<double, std::size_t>> { { 1, 1 } }), boost::test_tools::per_element());
    data.remove();
    BOOST_TEST(foo.top(Infinity).empty());
    BOOST_TEST(div2.all().empty());
}

BOOST_AUTO_TEST_CASE(filtering_works_correctly_after_removing_record)
{
    cross::filter<Rec3> data;
    auto foo = data.dimension([](auto d) { return d.foo; });
    auto div2 = foo.feature_count([](auto value) { return std::floor(value / 2); });
    auto positive = foo.feature_count([](auto value) { return value > 0; });

    data.add(std::vector<Rec3> { { 1 }, { 2 }, { 3 } });
    foo.filter(2);
    data.remove();
    foo.filter_all();
    BOOST_TEST(foo.top(Infinity) == (std::vector<Rec3> { { 3 }, { 1 } }), boost::test_tools::per_element());

    data.remove();
    BOOST_TEST(foo.top(Infinity).empty());
}
BOOST_AUTO_TEST_SUITE_END();

BOOST_AUTO_TEST_SUITE(remove_with_predicate)
BOOST_AUTO_TEST_CASE(removing_record_workd_with_group_cardinality_one)
{
    cross::filter<Rec3> data;
    auto foo = data.dimension([](auto d) { return d.foo; });
    auto div2 = foo.feature_count([](auto value) { return std::floor(value / 2); });
    auto positive = foo.feature_count([](auto value) { return value > 0; });

    data.add(std::vector<Rec3> { { 1 }, { 1.1 }, { 1.2 } });
    data.remove([](auto d, int) { return d.foo == 1.1; });
    BOOST_TEST(data.all() == (std::vector<Rec3> { { 1 }, { 1.2 } }), boost::test_tools::per_element());

    data.remove([](auto, int) { return true; });
    BOOST_TEST(foo.top(Infinity).empty());
}

BOOST_AUTO_TEST_CASE(removing_record_works_for_another_group_with_cardinality_one)
{
    cross::filter<Rec3> data;
    auto foo = data.dimension([](auto d) { return d.foo; });
    auto div2 = foo.feature_count([](auto value) { return std::floor(value / 2); });
    auto positive = foo.feature_count([](auto value) { return value > 0; });

    data.add(std::vector<Rec3> { { 0 }, { -1 } });

    BOOST_TEST(positive.all() == (std::vector<std::pair<bool, std::size_t>> { { false, 2 } }),
        boost::test_tools::per_element());

    data.remove([](auto d, int) { return d.foo == 0; });
    BOOST_TEST(positive.all() == (std::vector<std::pair<bool, std::size_t>> { { false, 1 } }),
        boost::test_tools::per_element());
    BOOST_TEST(foo.top(Infinity) == (std::vector<Rec3> { { -1 } }), boost::test_tools::per_element());

    data.remove([](auto, int) { return true; });
    BOOST_TEST(foo.top(Infinity).empty());
}
BOOST_AUTO_TEST_CASE(removing_record_updates_dimension)
{
    cross::filter<Rec3> data;
    auto foo = data.dimension([](auto d) { return d.foo; });
    auto div2 = foo.feature_count([](auto value) { return std::floor(value / 2); });
    auto positive = foo.feature_count([](auto value) { return value > 0; });

    data.add(std::vector<Rec3> { { 1 }, { 2 } });

    data.remove([](auto d, int) { return d.foo == 1; });
    BOOST_TEST(foo.top(Infinity) == (std::vector<Rec3> { { 2 } }), boost::test_tools::per_element());

    data.remove([](auto, int) { return true; });
    BOOST_TEST(foo.top(Infinity).empty());
}

BOOST_AUTO_TEST_CASE(removing_records_updates_group)
{
    cross::filter<Rec3> data;
    auto foo = data.dimension([](auto d) { return d.foo; });
    auto div2 = foo.feature_count([](auto value) { return std::floor(value / 2); });
    auto positive = foo.feature_count([](auto value) { return value > 0; });

    data.add(std::vector<Rec3> { { 1 }, { 2 }, { 3 } });

    BOOST_TEST(foo.top(Infinity) == (std::vector<Rec3> { { 3 }, { 2 }, { 1 } }), boost::test_tools::per_element());

    BOOST_TEST(div2.all() == (std::vector<std::pair<double, std::size_t>> { { 0, 1 }, { 1, 2 } }),
        boost::test_tools::per_element());

    data.remove([](auto d, int) { return d.foo < 3; });

    BOOST_TEST(foo.top(Infinity) == (std::vector<Rec3> { { 3 } }), boost::test_tools::per_element());
    BOOST_TEST(
        div2.all() == (std::vector<std::pair<double, std::size_t>> { { 1, 1 } }), boost::test_tools::per_element());
    data.remove([](auto, int) { return true; });
    BOOST_TEST(foo.top(Infinity).empty());
    BOOST_TEST(div2.all().empty());
}
BOOST_AUTO_TEST_CASE(can_remove_records_while_filtering)
{
    cross::filter<Rec3> data;
    auto foo = data.dimension([](auto d) { return d.foo; });
    auto div2 = foo.feature_count([](auto value) { return std::floor(value / 2); });
    auto positive = foo.feature_count([](auto value) { return value > 0; });
    auto allSum = data.feature_sum([](auto d) { return d.foo; });
    data.add(std::vector<Rec3> { { 1 }, { 2 }, { 3 } });
    std::cout << "a1" << std::endl;
    BOOST_CHECK_EQUAL(allSum.value(), 6);
    std::cout << "a2" << std::endl;
    BOOST_TEST(positive.all() == (std::vector<std::pair<bool, std::size_t>> { { true, 3 } }),
        boost::test_tools::per_element());
    std::cout << "a3" << std::endl;
    foo.filter(2);
    std::cout << "a4" << std::endl;
    BOOST_CHECK_EQUAL(allSum.value(), 2);
    data.remove([](auto d, int) { return d.foo == 3; });
    BOOST_CHECK_EQUAL(allSum.value(), 2);
    BOOST_TEST(positive.all() == (std::vector<std::pair<bool, std::size_t>> { { true, 2 } }),
        boost::test_tools::per_element());
    data.remove([](auto d, int) { return d.foo == 2; });
    BOOST_CHECK_EQUAL(allSum.value(), 0);

    foo.filter_all();
    BOOST_CHECK_EQUAL(allSum.value(), 1);
    data.remove([](auto, int) { return true; });
    BOOST_TEST(foo.top(Infinity).empty());
}

BOOST_AUTO_TEST_CASE(can_remove_records_while_filtering_on_iterable_dimension)
{
    cross::filter<RecIterable> data;

    auto fooDimension = data.iterable_dimension([](auto d) { return d.foo; });
    auto fooGroup = fooDimension.feature_count();
    auto allBarSum = data.feature_sum([](auto d) { return d.bar; });
    auto fooBarSum = fooDimension.feature_sum([](auto d) { return d.bar; });

    data.add(std::vector<RecIterable> {
        { std::vector<int> { 1, 2, 3 }, 1 }, { std::vector<int> { 1, 2 }, 2 }, { std::vector<int> { 2, 3 }, 4 } });
    fooGroup.all();
    allBarSum.value();

    BOOST_CHECK_EQUAL(allBarSum.value(), 7);
    BOOST_TEST(fooGroup.all() == (std::vector<std::pair<int, std::size_t>> { { 1, 2 }, { 2, 3 }, { 3, 2 } }),
        boost::test_tools::per_element());
    BOOST_TEST(fooBarSum.all() == (std::vector<std::pair<int, int>> { { 1, 3 }, { 2, 7 }, { 3, 5 } }),
        boost::test_tools::per_element());

    fooDimension.filter(3);
    BOOST_CHECK_EQUAL(allBarSum.value(), 5);

    data.remove([](auto d, int) {
        return std::find(d.foo.begin(), d.foo.end(), 1) != d.foo.end();
    });
    BOOST_CHECK_EQUAL(allBarSum.value(), 4);
    BOOST_TEST(fooGroup.all() == (std::vector<std::pair<int, std::size_t>> { { 2, 1 }, { 3, 1 } }),
        boost::test_tools::per_element());
    BOOST_TEST(
        fooBarSum.all() == (std::vector<std::pair<int, int>> { { 2, 4 }, { 3, 4 } }), boost::test_tools::per_element());

    fooDimension.filter_all();
    BOOST_CHECK_EQUAL(allBarSum.value(), 4);
    data.remove([](auto, int) { return true; });
    BOOST_TEST(fooDimension.top(Infinity).empty());
}
BOOST_AUTO_TEST_SUITE_END();


//==============================================================================
//         Copyright 2003 - 2013   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2013   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
//==============================================================================
#include <nt2/combinatorial/include/functions/factorial.hpp>

#include <nt2/sdk/functor/meta/call.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>
#include <nt2/sdk/unit/tests/ulp.hpp>
#include <nt2/sdk/unit/tests/basic.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <boost/simd/sdk/config.hpp>
#include <nt2/include/functions/min.hpp>
#include <nt2/include/functions/saturate.hpp>

#include <nt2/include/constants/eight.hpp>
#include <nt2/include/constants/eleven.hpp>
#include <nt2/include/constants/five.hpp>
#include <nt2/include/constants/four.hpp>
#include <nt2/include/constants/nine.hpp>
#include <nt2/include/constants/one.hpp>
#include <nt2/include/constants/seven.hpp>
#include <nt2/include/constants/six.hpp>
#include <nt2/include/constants/ten.hpp>
#include <nt2/include/constants/three.hpp>
#include <nt2/include/constants/twelve.hpp>
#include <nt2/include/constants/two.hpp>
#include <nt2/include/constants/valmax.hpp>
#include <nt2/include/constants/zero.hpp>
#include <nt2/include/constants/inf.hpp>
#include <nt2/include/constants/nan.hpp>

NT2_TEST_CASE_TPL ( factorial_real__1_0,  NT2_REAL_TYPES)
{
  using nt2::factorial;
  using nt2::tag::factorial_;
  typedef typename nt2::meta::call<factorial_(T)>::type r_t;
  typedef T wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS(r_t, wished_r_t);

  // specific values tests
#ifndef BOOST_SIMD_NO_INVALIDS
  NT2_TEST_ULP_EQUAL(factorial(nt2::Inf<T>()), nt2::Inf<T>(), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Nan<T>()), nt2::Nan<T>(), 0);
#endif
  NT2_TEST_ULP_EQUAL(factorial(nt2::Eight<T>()), nt2::min((T(40320ll    )),nt2::Valmax<T>()), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Eleven<T>()), nt2::min((T(39916800ll )),nt2::Valmax<T>()), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Five<T>()), T(120), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Four<T>()), T(24), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Nine<T>()), nt2::min((T(362880ll   )),nt2::Valmax<T>()), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::One<T>()), nt2::One<T>(), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Seven<T>()), nt2::min((T(5040ll     )),nt2::Valmax<T>()), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Six<T>()), nt2::min((T(720ll      )),nt2::Valmax<T>()), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Ten<T>()), nt2::min((T(3628800ll  )),nt2::Valmax<T>()), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Three<T>()), nt2::Six<T>(), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Twelve<T>()), nt2::min((T(479001600ll)),nt2::Valmax<T>()), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Two<T>()), nt2::Two<T>(), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Zero<T>()), nt2::One<T>(), 0);
}

NT2_TEST_CASE_TPL ( factorial_integer__1_0,  NT2_INTEGRAL_TYPES)
{
  using nt2::factorial;
  using nt2::tag::factorial_;
  typedef typename nt2::meta::call<factorial_(T)>::type r_t;
  typedef T wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS(r_t, wished_r_t);

  // specific values tests
  NT2_TEST_ULP_EQUAL(factorial(nt2::Eight<T>()), T(nt2::saturate<T>(40320ull    )), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Eleven<T>()), T(nt2::saturate<T>(39916800ull)), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Five<T>()), T(nt2::saturate<T>(120)), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Four<T>()), T(nt2::saturate<T>(24)), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Nine<T>()), T(nt2::saturate<T>(362880ull   )), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::One<T>()), nt2::One<T>(), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Seven<T>()), T(nt2::saturate<T>(5040ull     )), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Six<T>()), T(nt2::saturate<T>(720ull      )), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Ten<T>()), T(nt2::saturate<T>(3628800ull  )), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Three<T>()), nt2::Six<T>(), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Twelve<T>()), T(nt2::saturate<T>(479001600ull)), 0);
  NT2_TEST_ULP_EQUAL(factorial(nt2::Zero<T>()), nt2::One<T>(), 0);
}


#pragma once
#include <Eigen/Core>
#include <libvisr/atomic_component.hpp>
#include <libvisr/audio_input.hpp>
#include <libvisr/signal_flow_context.hpp>
#include <map>
#include <memory>

namespace bear {
using namespace visr;

class DebugStorage {
 public:
  static DebugStorage &get_instance()
  {
    static DebugStorage debug_storage;
    return debug_storage;
  }

  Eigen::MatrixXf &get_write(std::string name, size_t num_channels, size_t block_size)
  {
    auto it = blocks.emplace(name, Eigen::MatrixXf{num_channels, block_size}).first;
    return it->second;
  }

  Eigen::MatrixXf &get(std::string name) { return blocks.at(name); }

  DebugStorage(DebugStorage const &) = delete;
  void operator=(DebugStorage const &) = delete;

 private:
  DebugStorage() {}

  std::map<std::string, Eigen::MatrixXf> blocks;
};

class DebugSink : public AtomicComponent {
 public:
  explicit DebugSink(const SignalFlowContext &ctx,
                     const char *name,
                     CompositeComponent *parent,
                     size_t n_channels)
      : AtomicComponent(ctx, name, parent),
        in("in", *this, n_channels),
        block(DebugStorage::get_instance().get_write(name, n_channels, ctx.period()))
  {
  }

  void process() override
  {
    for (size_t i = 0; i < in.width(); i++)
      for (size_t j = 0; j < period(); j++) block(i, j) = in.at(i)[j];
  }

 private:
  AudioInput in;
  Eigen::MatrixXf █
};

}  // namespace bear


#ifndef STAN_MATH_PRIM_SCAL_PROB_BERNOULLI_RNG_HPP
#define STAN_MATH_PRIM_SCAL_PROB_BERNOULLI_RNG_HPP

#include <stan/math/prim/scal/err/check_bounded.hpp>
#include <stan/math/prim/scal/err/check_finite.hpp>
#include <stan/math/prim/scal/meta/length.hpp>
#include <stan/math/prim/scal/meta/scalar_seq_view.hpp>
#include <stan/math/prim/scal/meta/VectorBuilder.hpp>
#include <boost/random/bernoulli_distribution.hpp>
#include <boost/random/variate_generator.hpp>

namespace stan {
namespace math {

/**
 * Return a Bernoulli random variate with specified chance of success
 * parameter using the specified random number generator.
 *
 * theta can be a scalar or a one-dimensional container.
 *
 * @tparam T_theta type of chance of success parameter
 * @tparam RNG type of random number generator
 * @param theta (Sequence of) chance of success parameter(s)
 * @param rng random number generator
 * @return (Sequence of) Bernoulli random variate(s)
 * @throw std::domain_error if chance of success parameter is less than zero or
 * greater than one.
 */
template <typename T_theta, class RNG>
inline typename VectorBuilder<true, int, T_theta>::type bernoulli_rng(
    const T_theta& theta, RNG& rng) {
  using boost::bernoulli_distribution;
  using boost::variate_generator;

  static const char* function = "bernoulli_rng";

  check_finite(function, "Probability parameter", theta);
  check_bounded(function, "Probability parameter", theta, 0.0, 1.0);

  scalar_seq_view<T_theta> theta_vec(theta);
  size_t N = length(theta);
  VectorBuilder<true, int, T_theta> output(N);

  for (size_t n = 0; n < N; ++n) {
    variate_generator<RNG&, bernoulli_distribution<> > bernoulli_rng(
        rng, bernoulli_distribution<>(theta_vec[n]));
    output[n] = bernoulli_rng();
  }

  return output.data();
}

}  // namespace math
}  // namespace stan
#endif


// -*- mode: c++; indent-tabs-mode: nil; -*-
//
// Copyright (c) 2009-2013 Illumina, Inc.
//
// This software is provided under the terms and conditions of the
// Illumina Open Source Software License 1.
//
// You should have received a copy of the Illumina Open Source
// Software License 1 along with this program. If not, see
// <https://github.com/sequencing/licenses/>
//

/// \file

/// \author Chris Saunders
///
#ifdef HAVE_FISHER_EXACT_TEST
extern "C" {
#include "fexact.h"
}
#endif

#include "blt_util/log.hh"
#include "blt_util/table_test.hh"

#include <boost/math/distributions/binomial.hpp>
#include <boost/math/distributions/chi_squared.hpp>

using boost::math::binomial;
using boost::math::cdf;
using boost::math::chi_squared;

#include <algorithm>

#ifdef HAVE_FISHER_EXACT_TEST
//#define DEBUG_EXACT
#ifdef DEBUG_EXACT
#include <iostream>
#endif

int workspace(2000000);

double*
get_exact_test_ws() {
    double* ws = make_ws(workspace);
    if (ws==0) {
        log_os << "ERROR: can't allocate exact test workspace.\n";
        exit(EXIT_FAILURE);
    }
    return ws;
}

double
table_exact_pval(const int* table,
                 const unsigned n_row,
                 const unsigned n_col,
                 double* ws) {

    static const unsigned MAX_DIM(10);

    bool is_free_ws(false);
    if (ws==0) {
        ws=get_exact_test_ws();
        is_free_ws=true;
    }

    assert((n_row != 0) and (n_col != 0));
    assert((n_row <= MAX_DIM) and (n_col <= MAX_DIM));

    int nr(n_row);
    int nc(n_col);

    double expect(5.);
    double percent(80.);
    double emin(1.);
    double prt(0.);
    double pval(0.);
    int mult(30);

    fexact(&nc,&nr,const_cast<int*>(table),&nc,&expect,&percent,&emin,&prt,&pval,&workspace,ws,&mult);

    if (is_free_ws) free(ws);

#ifdef DEBUG_EXACT
    std::cerr << "table:\n";
    for (unsigned i(0); i<n_row; ++i) {
        for (unsigned j(0); j<n_col++j) {
            if (j) std::cerr << " ";
            std::cerr << table[j+i*n_col];
        }
        std::cerr << "\n";
    }
    std::cerr "pval/alpha: " << pval << " " << alpha << "\n";
#endif

    return pval;
}

bool
is_reject_table_exact(const double alpha,
                      const int* table,
                      const unsigned n_row,
                      const unsigned n_col,
                      double* ws) {

    return (table_exact_pval(table,n_row,n_col,ws) < alpha);
}
#endif

double
table_chi_sqr_pval(const int* table,
                   const unsigned n_row,
                   const unsigned n_col) {

    static const unsigned MAX_DIM(10);

    assert((n_row > 1) && (n_col > 1));
    assert((n_row <= MAX_DIM) && (n_col <= MAX_DIM));

    int sum(0);
    int rsum[MAX_DIM];
    int csum[MAX_DIM];

    for (unsigned r(0); r<n_row; ++r) rsum[r] = 0;
    for (unsigned c(0); c<n_col; ++c) csum[c] = 0;

    for (unsigned r(0); r<n_row; ++r) {
        for (unsigned c(0); c<n_col; ++c) {
            const int obs(table[c+r*n_col]);
            assert(obs>=0);
            csum[c] += obs;
            rsum[r] += obs;
            sum += obs;
        }
    }

    if (sum <= 0.) return 1.;

    double xsq(0);
    for (unsigned r(0); r<n_row; ++r) {
        for (unsigned c(0); c<n_col; ++c) {
            const int obs(table[c+r*n_col]);
            const double expect((rsum[r]*csum[c])/sum);
            if (expect <= 0) continue;
            const double d(obs-expect);
            xsq += (d*d)/expect;
        }
    }

    unsigned n_df_row(n_row);
    unsigned n_df_col(n_col);
    for (unsigned r(0); r<n_row; ++r) {
        if (rsum[r] == 0) {
            n_df_row--;
            if (n_df_row<=1) return 1.;
        }
    }
    for (unsigned c(0); c<n_col; ++c) {
        if (csum[c] == 0) {
            n_df_col--;
            if (n_df_col<=1) return 1.;
        }
    }
    const unsigned df((n_df_row-1)*(n_df_col-1));
    chi_squared dist(df);
    return (1.-cdf(dist,xsq));
}

bool
is_reject_table_chi_sqr(const double alpha,
                        const int* table,
                        const unsigned n_row,
                        const unsigned n_col) {

    return (table_chi_sqr_pval(table,n_row,n_col) < alpha);
}

#if 0
bool
is_reject_table(const double alpha,
                const int* table,
                const unsigned n_row,
                const unsigned n_col) {

    static const unsigned exact_test_threshold(250);

    const unsigned n_trial(n_success+n_failure);

    if (n_trial > exact_test_threshold) {
        return is_reject_table_chi_sqr(alpha,table,n_row,n_col);
    } else {
        return is_reject_table_exact(alpha,table,n_row,n_col);
    }
}
#endif


/*
###############################################################################
#
#   Test: interfacing from Python to C++
#   Based on:
#   https://github.com/pybind/pybind11
#
#   AUTHOR: Maciej_Bak
#   AFFILIATION: Swiss_Institute_of_Bioinformatics
#   CONTACT: very.angry.maciek@gmail.com
#   CREATED: 05-10-2020
#   LICENSE: MIT
#
###############################################################################
*/

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/eigen.h>
#include <Eigen/LU>
namespace py = pybind11;

double add2numbers(double i, double j) {
    return i + j;
}

double square_a_number(double x) {
    return x * x;
}

double get00(Eigen::MatrixXd m) {
    return m(0, 0);
}

Eigen::MatrixXd inverse(Eigen::MatrixXd m) {
    return m.inverse();
}

// binding C++ code to Python
PYBIND11_MODULE(functions, m) {
    m.doc() = "Module: example C++ functions";
    m.def("add2numbers", &add2numbers, py::arg("x"), py::arg("y"));
    m.def("square_a_number", py::vectorize(square_a_number));
    m.def("get00", &get00);
    m.def("inverse", &inverse);
}


#ifndef TL_UTILS_HH
#define TL_UTILS_HH

#include <cpp_utils/cpp_utils.h>

#include <Eigen/Core>

#include <array>
#include <cstddef>
#include <ostream>
#include <sstream>
#include <iterator>
#include <type_traits>
#include <utility>
#include <numeric>

#define BOOST_RESULT_OF_USE_DECLTYPE

namespace tl
{

struct MinNotZero
{
    template <typename T>
    const T& operator()(const T& x, const T& y) const
    {
        if(x == T{0}) return y;
        if(y == T{0}) return x;
        return std::min(x, y);
    }
};

struct MinAbs
{
    template <typename T>
    T operator()(const T& x, const T& y) const
    {
        if((x*y) < T{0}) return T{0};
        return std::min(std::abs(x), std::abs(y));
    }
};

struct MaxAbs
{
    template <typename T>
    T operator()(const T& x, const T& y) const
    {
        return std::max(std::abs(x), std::abs(y));
    }
};

struct Max
{
    template <typename T>
    T operator()(const T& x, const T& y) const
    {
        return std::max(x, y);
    }
};

struct Min
{
    template <typename T>
    T operator()(const T& x, const T& y) const
    {
        return std::min(x, y);
    }
};

struct SameSign
{
    template <typename T1,
              typename T2,
              typename = std::enable_if_t<std::is_signed<T1>::value
                                          && std::is_signed<T2>::value>>
    int operator()(const T1& x, const T2& y) const
    {
        using cpp_utils::sgn;
        return x * y > 0 ? sgn(x) : 0;
    }
};

/**
 * Check if all numbers of a sequence are positive or negative
 *
 * @return 1 for all positive, -1 for all negative, 0 otherwise
 */
template <typename Sequence>
int sameSign(const Sequence& numbers)
{
    using cpp_utils::sgn;
    auto nelem = std::distance(std::begin(numbers), std::end(numbers));
    if(nelem == 0) return 0;
    auto first = *std::begin(numbers);
    if(nelem == 1) return sgn(first);
    using std::begin;
    using std::end;
    return std::accumulate(std::next(begin(numbers)),
                           end(numbers),
                           sgn(first),
                           SameSign{});
}

/// pretty print an Eigen::Vector3* without line breaks
template <typename T,
          typename = typename std::enable_if_t<T::SizeAtCompileTime == 3>>
std::string print(const T& in)
{
    std::stringstream out;
    out << "(" << in[0] << ", " << in[1] << ", " << in[2] << ")";
    return out.str();
}

} // namespace tl

#endif // TL_UTILS_HH


//  (C) Copyright 2006 Eric Niebler, Olivier Gygi
//  Use, modification and distribution are 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)

#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/random.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/median.hpp>
#include <sstream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

using namespace boost;
using namespace unit_test;
using namespace accumulators;

typedef accumulator_set<double, stats<tag::median(with_p_square_quantile) > > p_square_median_t;
typedef accumulator_set<double, stats<tag::median(with_density) > > dist_median_t;
typedef accumulator_set<double, stats<tag::median(with_p_square_cumulative_distribution) > > p_square_dist_median_t;

///////////////////////////////////////////////////////////////////////////////
// test_stat
//
void test_stat()
{
    // two random number generators
    double mu = 1.;
    boost::lagged_fibonacci607 rng;
    boost::normal_distribution<> mean_sigma(mu,1);
    boost::variate_generator<boost::lagged_fibonacci607&, boost::normal_distribution<> > normal(rng, mean_sigma);

    p_square_median_t acc;
    dist_median_t acc_dens( density_cache_size = 10000, density_num_bins = 1000 );
    p_square_dist_median_t acc_cdist( p_square_cumulative_distribution_num_cells = 100 );

    for (std::size_t i=0; i<100000; ++i)
    {
        double sample = normal();
        acc(sample);
        acc_dens(sample);
        acc_cdist(sample);
    }

    BOOST_CHECK_CLOSE(1., median(acc), 1.);
    BOOST_CHECK_CLOSE(1., median(acc_dens), 1.);
    BOOST_CHECK_CLOSE(1., median(acc_cdist), 3.);
}

///////////////////////////////////////////////////////////////////////////////
// test_persistency
//
void test_persistency()
{
    // two random number generators
    double mu = 1.;
    boost::lagged_fibonacci607 rng;
    boost::normal_distribution<> mean_sigma(mu,1);
    boost::variate_generator<boost::lagged_fibonacci607&, boost::normal_distribution<> > normal(rng, mean_sigma);
    std::stringstream ss;
    {
        p_square_median_t acc;
        dist_median_t acc_dens( density_cache_size = 10000, density_num_bins = 1000 );
        p_square_dist_median_t acc_cdist( p_square_cumulative_distribution_num_cells = 100 );

        for (std::size_t i=0; i<100000; ++i)
        {
            double sample = normal();
            acc(sample);
            acc_dens(sample);
            acc_cdist(sample);
        }

        BOOST_CHECK_CLOSE(1., median(acc), 1.);
        BOOST_CHECK_CLOSE(1., median(acc_dens), 1.);
        BOOST_CHECK_CLOSE(1., median(acc_cdist), 3.);

        boost::archive::text_oarchive oa(ss);
        acc.serialize(oa, 0);
        acc_dens.serialize(oa, 0);
        acc_cdist.serialize(oa, 0);
    }

    p_square_median_t acc;
    dist_median_t acc_dens( density_cache_size = 10000, density_num_bins = 1000 );
    p_square_dist_median_t acc_cdist( p_square_cumulative_distribution_num_cells = 100 );
    boost::archive::text_iarchive ia(ss);
    acc.serialize(ia, 0);
    acc_dens.serialize(ia, 0);
    acc_cdist.serialize(ia, 0);
    BOOST_CHECK_CLOSE(1., median(acc), 1.);
    BOOST_CHECK_CLOSE(1., median(acc_dens), 1.);
    BOOST_CHECK_CLOSE(1., median(acc_cdist), 3.);
}

///////////////////////////////////////////////////////////////////////////////
// init_unit_test_suite
//
test_suite* init_unit_test_suite( int argc, char* argv[] )
{
    test_suite *test = BOOST_TEST_SUITE("median test");

    test->add(BOOST_TEST_CASE(&test_stat));
    test->add(BOOST_TEST_CASE(&test_persistency));

    return test;
}


/**
 * Copyright (c) 2015
 * Jakob van Santen <jvansanten@icecube.wisc.edu>
 * and the IceCube Collaboration <http://www.icecube.wisc.edu>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *
 * $Id: Axes.cxx 163653 2018-06-25 17:54:17Z olivas $
 *
 * @file Axes.cxx
 * @version $LastChangedRevision: 163653 $
 * @date $Date: 2018-06-25 11:54:17 -0600 (Mon, 25 Jun 2018) $
 * @author Jakob van Santen
 */

#include "icetray/I3Units.h"
#include "clsim/tabulator/Axes.h"
#include "clsim/I3CLSimHelperToFloatString.h"
#include "opencl/I3CLSimHelperLoadProgramSource.h"

#include <boost/foreach.hpp>

namespace {

std::string
loadKernel(const std::string& name, bool header=false)
{
    const std::string I3_BUILD(getenv("I3_BUILD"));
    const std::string kernelBaseDir = I3_BUILD+"/clsim/resources/kernels/";
    const std::string ext = header ? ".h.cl" : ".c.cl";
    return I3CLSimHelper::LoadProgramSource(kernelBaseDir+name+ext);
}

}

namespace clsim {

namespace tabulator {

Axes::Axes(const std::vector<value_type> &axes) : axes_(axes), n_dim_(axes_.size()),
    shape_(n_dim_), strides_(n_dim_)
{
	int i = n_dim_-1;
	// NB: every axis has an over- and an under-flow bin.
	shape_[i] = axes_[i]->GetNBins()+2;
	strides_[i] = 1;
	for (i--; i >= 0; i--) {
		shape_[i] = axes_[i]->GetNBins()+2;
		strides_[i] = strides_[i+1]*shape_[i+1];
	}
	n_bins_ = strides_[0]*shape_[0];
}

Axes::~Axes()
{}

std::string
Axes::GetBinIndexFunction() const
{
	std::ostringstream ss;
	ss << "inline uint getBinIndex(coordinate_t coords)";
	ss << "\n{\n";
	ss << "    return ";

	for (uint i = 0; i < axes_.size(); i++) {
		std::ostringstream var;
		var << "coords.s" << i;
		ss << strides_[i] << "*" << axes_[i]->GetIndexCode(var.str());
		if (i+1 < axes_.size())
			ss << "\n         + ";
	}

	ss << ";\n}\n";

	return ss.str();
}

std::string
Axes::GenerateBinningCode() const
{
	return GetCoordinateFunction() + "\n"
	    + GetBoundsCheckFunction() + "\n"
	    + GetBinIndexFunction() + "\n";
}

std::string
SphericalAxes::GetCoordinateFunction() const
{
	std::ostringstream ss;
	if (at(1)->GetMax() > 180.)
		ss << "#define HAS_FULL_AZIMUTH_EXTENSION\n";
	ss << loadKernel("spherical_coordinates");
	return ss.str();
}

std::string
SphericalAxes::GetBoundsCheckFunction() const
{
	std::ostringstream ss;
	ss << "inline bool isOutOfBounds(const coordinate_t coords)";
	ss << "\n{\n";
	ss << "    return (coords.s3 > "<<I3CLSimHelper::ToFloatString(this->at(3)->GetMax())<<")"
	    << "|| (coords.s0 > "<<I3CLSimHelper::ToFloatString(this->at(0)->GetMax())<<")"
	    << ";";
	ss << "\n}\n";

	return ss.str();
}

double
SphericalAxes::GetBinVolume(const std::vector<size_t> &idxs) const
{
	// NB: since we combine the bins at azimuth > 180 degrees with the
	// other half of the sphere, the true volume of an azimuthal bin is
	// twice its nominal value.
	// In case of a full table the azimuthal bin volume is simply the nominal volume.
	assert(idxs.size() >= 3);
	double scalefactor = (at(1)->GetMax() > 180.) ? 1 : 2;
	return ((std::pow(at(0)->GetBinEdge(idxs[0]+1), 3) - std::pow(at(0)->GetBinEdge(idxs[0]), 3))/3.)
	    * scalefactor*I3Units::degree*(at(1)->GetBinEdge(idxs[1]+1) - at(1)->GetBinEdge(idxs[1]))
	    * (at(2)->GetBinEdge(idxs[2]+1) - at(2)->GetBinEdge(idxs[2]));

}

std::string
CylindricalAxes::GetCoordinateFunction() const
{
	return loadKernel("cylindrical_coordinates");
}

std::string
CylindricalAxes::GetBoundsCheckFunction() const
{
	std::ostringstream ss;
	ss << "inline bool isOutOfBounds(const coordinate_t coords)";
	ss << "\n{\n";
	ss << "    return (coords.s3 > "<<I3CLSimHelper::ToFloatString(this->at(3)->GetMax())<<");";
	ss << "\n}\n";

	return ss.str();
}

double
CylindricalAxes::GetBinVolume(const std::vector<size_t> &idxs) const
{
	// NB: since we combine the bins at azimuth > pi with the
	// other half of the cylinder, the true volume of an azimuthal bin is
	// twice its nominal value.
	assert(idxs.size() >= 3);
	return ((std::pow(at(0)->GetBinEdge(idxs[0]+1), 2) - std::pow(at(0)->GetBinEdge(idxs[0]), 2))/2.)
	    * 2*(at(1)->GetBinEdge(idxs[1]+1) - at(1)->GetBinEdge(idxs[1]))
	    * (at(2)->GetBinEdge(idxs[2]+1) - at(2)->GetBinEdge(idxs[2]));
}

}

}


#include <gtest/gtest.h>

#include <Eigen/Dense>
#include <ancse/reconstruction.hpp>

TEST(TestPWConstant, Example) {
    auto rc = PWConstantReconstruction{};

    Eigen::VectorXd ua(3), ub(3);
    ua << 1.0, 0, 1.50;
    ub << 0.1, 0, 0.15;

    auto [uL, uR] = rc(ua, ub);

    ASSERT_EQ(uL, ua);
    ASSERT_EQ(uR, ub);
}


// -*- C++ -*-
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//                                   Jiao Lin
//                      California Institute of Technology
//                         (C) 2008 All Rights Reserved
//
// {LicenseText}
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

#include <sstream>
#include <boost/python.hpp>
#include "mccomponents/math/random.h"

namespace wrap_mccomponents {

  void wrap_RandomNumberGenerator()
  {
    using namespace boost::python;

    def( "srandom", &mccomponents::math::srandom );
    def( "random", &mccomponents::math::random_DD );
  }
}

// version
// $Id$

// End of file


/**
 * @file model_tests.cpp
 * @author Kartik Dutt
 *
 * Tests for various functionalities and performance of models.
 *
 * mlpack is free software; you may redistribute it and/or modify it under the
 * terms of the 3-clause BSD license.  You should have received a copy of the
 * 3-clause BSD license along with mlpack.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */
#define BOOST_TEST_DYN_LINK
#include <utils/utils.hpp>
#include <ensmallen.hpp>
#include <dataloader/dataloader.hpp>
#include <models/darknet/darknet.hpp>
#include <boost/test/unit_test.hpp>

// Use namespaces for convenience.
using namespace boost::unit_test;

BOOST_AUTO_TEST_SUITE(FFNModelsTests);

/**
 * Simple test for Darknet model.
 */
BOOST_AUTO_TEST_CASE(DarknetModelTest)
{
  mlpack::ann::DarkNet<> darknetModel(3, 224, 224, 1000);
  arma::mat input(224 * 224 * 3, 1), output;
  input.ones();

  // Check output shape.
  darknetModel.GetModel().Predict(input, output);
  BOOST_REQUIRE_EQUAL(output.n_cols, 1);
  BOOST_REQUIRE_EQUAL(output.n_rows, 1000);

  // Repeat for DarkNet-53.
  mlpack::ann::DarkNet<> darknet53(3, 224, 224, 1000);
  darknet53.GetModel().Predict(input, output);
  BOOST_REQUIRE_EQUAL(output.n_cols, 1);
  BOOST_REQUIRE_EQUAL(output.n_rows, 1000);
}

BOOST_AUTO_TEST_SUITE_END();


/* Copyright © 2017 Apple Inc. All rights reserved.
 *
 * Use of this source code is governed by a BSD-3-clause license that can
 * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
 */
#ifndef TURI_LBFGS_2_H_
#define TURI_LBFGS_2_H_

#include <ml/optimization/optimization_interface.hpp>
#include <core/data/flexible_type/flexible_type.hpp>
#include <core/storage/sframe_data/sframe.hpp>

#include <ml/optimization/utils.hpp>
#include <ml/optimization/optimization_interface.hpp>
#include <ml/optimization/regularizer_interface.hpp>
#include <ml/optimization/line_search-inl.hpp>
#include <Eigen/Core>

typedef Eigen::VectorXd DenseVector;
typedef Eigen::MatrixXd DenseMatrix;

namespace turi {

namespace optimization {

/**
 * Solver status.
 */
struct solver_status {
  size_t iteration = 0;                         /*!< Iterations taken */
  double solver_time = 0;                       /*!< Wall clock time (s) */
  DenseVector solution;                         /*!< Current Solution */
  DenseVector gradient;                         /*!< Current gradient */
  DenseMatrix hessian;                          /*!< Current hessian */
  double residual = NAN;                        /*!< Residual norm */
  double function_value = NAN;                  /*!< Function value */
  size_t num_function_evaluations = 0;          /*!< Function evals */
  size_t num_gradient_evaluations = 0;          /*!< Gradient evals */
  double step_size = 0;                         /*!< Current step size */

  OPTIMIZATION_STATUS status = OPTIMIZATION_STATUS::OPT_UNSET;  /*!< Status */
};

/**
 * \ingroup group_optimization
 * \addtogroup LBFGS LBFGS
 * \{
 */

/**
 *
 * Solve a first_order_optimization_iterface model with an LBFGS
 * implementation.
 *
 * The implementation is based on Algorithm 7.4 (pg 178) of [1].
 *
 *  This subroutine solves an unconstrained minimization problem
 *  using the limited memory BFGS method. The routine is especially
 *  effective on problems involving a large number of variables. In
 *  a typical iteration of this method an approximation Hk to the
 *  inverse of the Hessian is obtained by applying M BFGS updates to
 *  a diagonal matrix Hk0, using information from the previous M steps.
 *  The user specifies the number M, which determines the amount of
 *  storage required by the routine. The user may also provide the
 *  diagonal matrices Hk0 if not satisfied with the default choice.
 *  The algorithm is described in [2].
 *
 *  The user is required to calculate the function value and its
 *  gradient.
 *
 *  The steplength is determined at each iteration by means of the
 *  line search routine MCVSRCH, which is a slight modification of
 *  the routine CSRCH written by More' and Thuente.
 *
 *
 * References:
 *
 * (1) Wright S.J  and J. Nocedal. Numerical optimization. Vol. 2.
 *                         New York: Springer, 1999.
 *
 * (2) "On the limited memory BFGS method for large scale optimization", by D.
 * Liu and J. Nocedal, Mathematical Programming B 45 (1989) 503-528.
 *
 * \param[in]     model  Model with first order optimization interface.
 * \param[in] init_point Starting point for the solver.
 * \param[in]     opts   Solver options.
 * \param[in]      reg   Shared ptr to an interface to a smooth regularizer.
 * \returns stats        Solver return stats.
 * \tparam Vector        Sparse or dense gradient representation.
 *
 */
class lbfgs_solver {
 public:

  /** Construct the solver around a specific model interface.
   *
   * \param[in]     model  Model with first order optimization interface.
   */
  lbfgs_solver(std::shared_ptr<first_order_opt_interface> _model)
      : model(_model) {}

  /** Sets up (or resets) the solver.
   *
   * \param[in] init_point Starting point for the solver.
   * \param[in]     opts   Solver options.  Options are "lbfgs_memory_level" and
   *                       "convergence_threshold".  If not given, defaults are
   *                       taken from the table in optimization_interface.hpp.
   *
   * \param[in]      reg   Shared ptr to an interface to a smooth regularizer.
   */
  void setup(const DenseVector& init_point,
             const std::map<std::string, flexible_type>& opts,
             const std::shared_ptr<smooth_regularizer_interface>& reg = nullptr);

  /** Perform the next update of the solution.
   *
   *  Call this method repeatedly to perform the optimization.
   *  Each iteration updates the solution point with one step.
   */
  bool next_iteration();

  /** The status after a given iteration.
   *
   *
   *  The best solution so far is given by status().solution.
   *
   */
  const solver_status& status() const { return m_status; }

 private:

  timer compute_timer;

  // The model used in the optimization.
  std::shared_ptr<first_order_opt_interface> model;

  std::shared_ptr<smooth_regularizer_interface> reg;

  size_t num_variables = 0;
  size_t lbfgs_memory_level = 0;
  double function_value = NAN, function_scaling_factor = 1.0;

  solver_status m_status;

  // LBFGS storage
  // The search steps and gradient differences are stored in a order
  // controlled by the start point.

  // Step difference (prev m iters)
  Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor> y;

  // Gradient difference (prev m iters)
  Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor> s;

  DenseVector q;         // Storage required for the 2-loop recursion
  DenseVector rho;       // Scaling factors (prev m iters)
  DenseVector alpha;     // Step sizes (prev m iters)

  // Buffers used internally.  The function value and gradient here is scaled my
  // m_status.function_scaling_value for numerical stability.
  DenseVector delta_point, gradient, delta_grad, previous_gradient;

  double convergence_threshold = 0;
};

// Old version for backwards compatibility with the previous interface.
// Includes printing.
solver_return lbfgs_compat(
    std::shared_ptr<first_order_opt_interface> model,
    const DenseVector& init_point,
    const std::map<std::string, flexible_type>& opts,
    const std::shared_ptr<smooth_regularizer_interface>& reg = nullptr);

/** Solves lbgfgs problem end-to-end.
 *
 *  This class wraps the above iterative solver in a convenience function,
 * iterating the solution until completion.
 *
 *  \param model The implementation of first_order_opt_interface used in the
 * optimization.
 *
 *  \param init_point The initial point at which the optimization starts.
 *
 *  \param opts The options.  Uses all the options given to setup() in the
 * lbfgs_solver class, plus "max_iterations" to terminate the optimization after
 * a given number of iterations.
 *
 * \param reg Optional regularization interface.
 *
 */
solver_status lbfgs(
    std::shared_ptr<first_order_opt_interface> model,
    const DenseVector& init_point,
    const std::map<std::string, flexible_type>& opts,
    const std::shared_ptr<smooth_regularizer_interface>& reg = nullptr);

}  // namespace optimization

/// \}
}  // namespace turi

#endif


/*
	MIT License

	Copyright (c) 2020 Miguel Sousa

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/
#include <string>
#include <ostream>

#include <boost/gil.hpp>
#include <boost/gil/extension/io/bmp.hpp>
#include <boost/gil/extension/numeric/resample.hpp>
#include <boost/gil/extension/numeric/sampler.hpp>

#include "generator.hpp"
#include "resize_method.hpp"

namespace ascii_refrigerator
{
	const character_space generator::bwSpace(" @");
	const character_space generator::gradient9Space(" .:;*+%#@");

	generator::generator() : resizeMethod(resize_method::nearest_neighbor), characterSpace(character_space(" #")) {}
	generator::generator(character_space characterSpace) : resizeMethod(resize_method::nearest_neighbor), characterSpace(characterSpace) {}
	generator::generator(resize_method resizeMethod) : resizeMethod(resizeMethod), characterSpace(character_space(" #")) {}
	generator::generator(resize_method resizeMethod, character_space characterSpace) : resizeMethod(resizeMethod), characterSpace(characterSpace) {}

	resize_method generator::get_resize_method() const
	{
		return resizeMethod;
	}

	character_space generator::get_character_space() const
	{
		return characterSpace;
	}

	void generator::generate(const std::string fileName, int width, int height, std::ostream &outputStream, const bool invertCharacterSpace) const
	{
		boost::gil::gray8_image_t inputFile;

		boost::gil::read_and_convert_image(fileName, inputFile, boost::gil::bmp_tag());

		if (width == 0)
		{
			width = inputFile.width();
		}

		if (height == 0)
		{
			height = inputFile.height();
		}

		if (width != inputFile.width() || height != inputFile.width())
		{
			boost::gil::gray8_image_t resizedImage(width, height);

			resize_view(boost::gil::const_view(inputFile), boost::gil::view(resizedImage));

			inputFile = resizedImage;
		}

		generate_ascii(boost::gil::const_view(inputFile), outputStream, invertCharacterSpace);
	}

	void generator::generate(const std::string fileName, std::ostream& outputStream, const bool invertCharacterSpace) const
	{
		generate(fileName, 0, 0, outputStream, invertCharacterSpace);
	}

	void generator::resize_view(boost::gil::gray8c_view_t sourceView, boost::gil::gray8_view_t destinationView) const
	{
		switch (resizeMethod)
		{
			case resize_method::nearest_neighbor:
			boost::gil::resize_view(sourceView, destinationView, boost::gil::nearest_neighbor_sampler());
			break;

			case resize_method::bilinear:
			boost::gil::resize_view(sourceView, destinationView, boost::gil::bilinear_sampler());
			break;
		}
	}

	void generator::generate_ascii(boost::gil::gray8c_view_t sourceView, std::ostream &outputStream, bool invertCharacterSpace) const
	{
		for (int y = 0; y < sourceView.height(); ++y)
		{
			for (int x = 0; x < sourceView.width(); ++x)
			{
				float grayValue = *sourceView.at(x, y) / 255.0f;
				std::size_t characterIndex = std::round(grayValue * static_cast<float>(characterSpace.size() - 1));
				char outputCharacter;

				if (invertCharacterSpace)
				{
					outputCharacter = characterSpace.at_reversed(characterIndex);
				}
				else
				{
					outputCharacter = characterSpace.at(characterIndex);
				}

				outputStream << outputCharacter;
			}

			outputStream << '\n';
		}
	}
}


/* test_old_uniform_int_distribution.cpp
 *
 * Copyright Steven Watanabe 2011
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * $Id$
 *
 */

#include <boost/random/uniform_int.hpp>
#include <limits>

#define BOOST_RANDOM_DISTRIBUTION boost::uniform_int<>
#define BOOST_RANDOM_ARG1 a
#define BOOST_RANDOM_ARG2 b
#define BOOST_RANDOM_ARG1_DEFAULT 0
#define BOOST_RANDOM_ARG2_DEFAULT 9
#define BOOST_RANDOM_ARG1_VALUE 5
#define BOOST_RANDOM_ARG2_VALUE 250

#define BOOST_RANDOM_DIST0_MIN 0
#define BOOST_RANDOM_DIST0_MAX 9
#define BOOST_RANDOM_DIST1_MIN 5
#define BOOST_RANDOM_DIST1_MAX 9
#define BOOST_RANDOM_DIST2_MIN 5
#define BOOST_RANDOM_DIST2_MAX 250

#define BOOST_RANDOM_TEST1_PARAMS (0, 9)
#define BOOST_RANDOM_TEST1_MIN 0
#define BOOST_RANDOM_TEST1_MAX 9

#define BOOST_RANDOM_TEST2_PARAMS (10, 19)
#define BOOST_RANDOM_TEST2_MIN 10
#define BOOST_RANDOM_TEST2_MAX 19

#include "test_distribution.ipp"

#define BOOST_RANDOM_UNIFORM_INT boost::uniform_int

#include "test_uniform_int.ipp"

#include <algorithm>
#include <boost/random/random_number_generator.hpp>

// Test that uniform_int<> can be used with std::random_shuffle
// Author: Jos Hickson
BOOST_AUTO_TEST_CASE(test_random_shuffle)
{
#ifndef BOOST_NO_CXX98_RANDOM_SHUFFLE
    typedef boost::uniform_int<> distribution_type;
    typedef boost::variate_generator<boost::mt19937 &, distribution_type> generator_type;

    boost::mt19937 engine1(1234);
    boost::mt19937 engine2(1234);

    boost::random::random_number_generator<boost::mt19937> referenceRand(engine1);

    distribution_type dist(0,10);
    generator_type testRand(engine2, dist);

    std::vector<int> referenceVec;

    for (int i = 0; i < 200; ++i) {
        referenceVec.push_back(i);
    }

    std::vector<int> testVec(referenceVec);

    std::random_shuffle(referenceVec.begin(), referenceVec.end(), referenceRand);
    std::random_shuffle(testVec.begin(), testVec.end(), testRand);

    BOOST_CHECK_EQUAL_COLLECTIONS(
        testVec.begin(), testVec.end(),
        referenceVec.begin(), referenceVec.end());
#endif
}


// BSD 3-Clause License

// Copyright (c) 2021, Chenyu
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:

// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.

// 2. 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.

// 3. Neither the name of the copyright holder 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 "rotation_averaging/l1_rotation_global_estimator.h"

#include <glog/logging.h>
#include <Eigen/SparseCore>

#include "geometry/rotation_utils.h"
#include "rotation_averaging/internal/rotation_estimator_util.h"
#include "solver/l1_solver.h"
#include "util/map_util.h"
#include "util/timer.h"

namespace gopt {

L1RotationGlobalEstimator::L1RotationGlobalEstimator(
    const int num_orientations, const int num_edges,
    const L1RotationOptions& options)
    : options_(options) {
  tangent_space_step_.resize((num_orientations - 1) * 3);
  tangent_space_residual_.resize(num_edges * 3);
}

void L1RotationGlobalEstimator::SetViewIdToIndex(
    const std::unordered_map<image_t, int>& view_id_to_index) {
  view_id_to_index_ = view_id_to_index;
}

void L1RotationGlobalEstimator::SetSparseMatrix(
    const Eigen::SparseMatrix<double>& sparse_matrix) {
  sparse_matrix_ = sparse_matrix;
}

bool L1RotationGlobalEstimator::SolveL1Regression(
    const std::unordered_map<ImagePair, TwoViewGeometry>& relative_rotations,
    std::unordered_map<image_t, Eigen::Vector3d>* global_rotations) {
  const int num_edges = relative_rotations.size();

  CHECK_NOTNULL(global_rotations);
  CHECK_GT(global_rotations->size(), 0);
  CHECK_GT(num_edges, 0);

  if (view_id_to_index_.empty()) {
    internal::ViewIdToAscentIndex(*global_rotations, &view_id_to_index_);
  }

  if (sparse_matrix_.rows() == 0) {
    internal::SetupLinearSystem(
        relative_rotations, (*global_rotations).size(),
        view_id_to_index_, &sparse_matrix_);
  }

  L1Solver<Eigen::SparseMatrix<double>>::Options l1_solver_options;
  l1_solver_options.max_num_iterations = 5;
  L1Solver<Eigen::SparseMatrix<double> > l1_solver(
      l1_solver_options, sparse_matrix_);

  tangent_space_step_.setZero();
  ComputeResiduals(relative_rotations, global_rotations);

  Timer timer;
  timer.Start();
  for (int i = 0; i < options_.max_num_l1_iterations; i++) {
    l1_solver.Solve(tangent_space_residual_, &tangent_space_step_);
    UpdateGlobalRotations(global_rotations);
    ComputeResiduals(relative_rotations, global_rotations);

    double avg_step_size = ComputeAverageStepSize();

    if (avg_step_size <= options_.l1_step_convergence_threshold) {
      break;
    }
    l1_solver_options.max_num_iterations *= 2;
    l1_solver.SetMaxIterations(l1_solver_options.max_num_iterations);
  }
  timer.Pause();

  LOG(INFO) << "Total time [L1Regression]: "
            << timer.ElapsedMicroSeconds() * 1e-3 << " ms.";

  return true;
}

void L1RotationGlobalEstimator::UpdateGlobalRotations(
    std::unordered_map<image_t, Eigen::Vector3d>* global_rotations) {
  for (auto& rotation : *global_rotations) {
    const int view_index = FindOrDie(view_id_to_index_, rotation.first) - 1;
    if (view_index == kConstantRotationIndex) {
      continue;
    }

    // Apply the rotation change to the global orientation.
    const Eigen::Vector3d& rotation_change =
        tangent_space_step_.segment<3>(3 * view_index);
    rotation.second = geometry::MultiplyRotations(rotation.second, rotation_change);
  }
}

void L1RotationGlobalEstimator::ComputeResiduals(
    const std::unordered_map<ImagePair, TwoViewGeometry>& relative_rotations,
    std::unordered_map<image_t, Eigen::Vector3d>* global_rotations) {
  int rotation_error_index = 0;

  for (const auto& relative_rotation : relative_rotations) {
    const Eigen::Vector3d& relative_rotation_aa = relative_rotation.second.rotation_2;
    const Eigen::Vector3d& rotation1 =
        FindOrDie(*global_rotations, relative_rotation.first.first);
    const Eigen::Vector3d& rotation2 =
        FindOrDie(*global_rotations, relative_rotation.first.second);

    // Compute the relative rotation error as:
    //   R_err = R2^t * R_12 * R1.
    tangent_space_residual_.segment<3>(3 * rotation_error_index) =
        geometry::MultiplyRotations(-rotation2,
            geometry::MultiplyRotations(relative_rotation_aa, rotation1));
    ++rotation_error_index;
  }
}

double L1RotationGlobalEstimator::ComputeAverageStepSize() {
  // compute the average step size of the update in tangent_space_step_
  const int num_vertices = tangent_space_step_.size() / 3;
  double delta_V = 0;
  for (int k = 0; k < num_vertices; ++k) {
    delta_V += tangent_space_step_.segment<3>(3 * k).norm();
  }
  return delta_V / num_vertices;
}

}  // namespace gopt


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

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

#include <boost/simd/detail/overload.hpp>
#include <boost/simd/function/pedantic.hpp>
#include <boost/simd/function/raw.hpp>
#include <boost/simd/detail/constant/limitexponent.hpp>
#include <boost/simd/detail/constant/mask1frexp.hpp>
#include <boost/simd/detail/constant/mask2frexp.hpp>
#include <boost/simd/detail/constant/maxexponentm1.hpp>
#include <boost/simd/constant/nbmantissabits.hpp>
#include <boost/simd/function/bitwise_and.hpp>
#include <boost/simd/function/bitwise_andnot.hpp>
#include <boost/simd/function/bitwise_cast.hpp>
#include <boost/simd/function/bitwise_or.hpp>
#include <boost/simd/function/if_else_zero.hpp>
#include <boost/simd/function/is_greater.hpp>
#include <boost/simd/function/is_nez.hpp>
#include <boost/simd/function/logical_notand.hpp>
#include <boost/simd/function/if_plus.hpp>
#include <boost/simd/function/shr.hpp>
#include <boost/simd/detail/dispatch/meta/as_integer.hpp>
#include <utility>

#ifndef BOOST_SIMD_NO_DENORMALS
#include <boost/simd/function/logical_and.hpp>
#include <boost/simd/function/is_less.hpp>
#include <boost/simd/function/if_else.hpp>
#include <boost/simd/function/is_nez.hpp>
#include <boost/simd/function/abs.hpp>
#include <boost/simd/constant/smallestposval.hpp>
#include <boost/simd/constant/twotonmb.hpp>
#endif

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  namespace bs = boost::simd;

  BOOST_DISPATCH_OVERLOAD ( ifrexp_
                          , (typename A0, typename X)
                          , bd::cpu_
                          , bs::pedantic_tag
                          , bs::pack_<bd::floating_<A0>, X>
                          )
  {
    using i_t = bd::as_integer_t<A0, signed>;
    BOOST_FORCEINLINE std::pair<A0,i_t> operator()(pedantic_tag const&, A0 const& a0) const
    {
      using s_type = typename A0::value_type;

#ifndef BOOST_SIMD_NO_DENORMALS
      auto test = logical_and(is_less(bs::abs(a0), Smallestposval<A0>()), is_nez(a0));
      auto    t = if_else_zero(test,Nbmantissabits<A0>());
      auto  aa0 = if_else(test, Twotonmb<A0>()*a0, a0);
#else
      auto  aa0 = a0;
#endif

      auto r1 = simd::bitwise_cast<i_t>(bitwise_and(aa0, Mask1frexp<A0>())); //extract exp.
      auto x  = bitwise_andnot(aa0, Mask1frexp<A0>());
           r1 = shr(r1,Nbmantissabits<s_type>()) - Maxexponentm1<A0>();
      auto r0 = bitwise_or(x,Mask2frexp<A0>());

      auto test0 = is_nez(aa0);
      auto test1 = is_greater(r1,Limitexponent<A0>());
              r1 = if_else_zero(logical_notand(test1, test0), r1);

#ifndef BOOST_SIMD_NO_DENORMALS
      r1 -= t ;
#endif

      return  { if_else_zero(test0, if_plus(test1,r0,aa0))
              , r1
              };
    }
  };

  BOOST_DISPATCH_OVERLOAD( ifrexp_
                          , (typename A0, typename X)
                          , bd::cpu_
                          , bs::pack_< bd::floating_<A0>, X>
                          )
  {
    using i_t = bd::as_integer_t<A0, signed>;

    BOOST_FORCEINLINE std::pair<A0,i_t> operator()(A0 const& a0) const BOOST_NOEXCEPT
    {
      auto that = raw_(ifrexp)(a0);
      return  { if_else_zero(a0,that.first), if_else_zero(a0,that.second) };
    }
  };

  BOOST_DISPATCH_OVERLOAD( ifrexp_
                          , (typename A0, typename X)
                          , bd::cpu_
                          , bs::raw_tag
                          , bs::pack_< bd::floating_<A0>, X>
                          )
  {
    using i_t = bd::as_integer_t<A0, signed>;

    BOOST_FORCEINLINE std::pair<A0,i_t> operator()(bs::raw_tag const&,A0 const& a0) const BOOST_NOEXCEPT
    {
      auto m1f = Mask1frexp<A0>();
      auto r1 = bitwise_cast<i_t>(bitwise_and(m1f, a0));
      auto x  = bitwise_andnot(a0, m1f);

      return  { bitwise_or(x,Mask2frexp<A0>())
              , shr(r1,Nbmantissabits<typename A0::value_type>()) - Maxexponentm1<A0>()
              };
    }
  };
} } }

#endif


#include "pch.h"

#define BOOST_TEST_MODULE AlgorithmTests
#include <boost/chrono/ceil.hpp>
#include <boost/test/included/unit_test.hpp>
#include <boost/thread/future.hpp>

#include "delete_elements_till.h"

using int_container_t = std::vector<int>;

void run_test(int_container_t elements, size_t expected, size_t expected_strict)
{
	using namespace boost::lambda;

	std::list<int> max_len_seq;
	algo::delete_till_copy(elements.begin(), elements.end(), std::front_inserter(max_len_seq), boost::lambda::_1 <= boost::lambda::_2, elements.size());
	BOOST_TEST_PASSPOINT();

	std::list<int> max_len_seq_strict;
	algo::delete_till_copy(elements.begin(), elements.end(), std::front_inserter(max_len_seq_strict), boost::lambda::_1 < boost::lambda::_2, elements.size());
	BOOST_TEST_PASSPOINT();

	BOOST_TEST(max_len_seq.size() == expected);
	BOOST_TEST(max_len_seq_strict.size() == expected_strict);
};
void run_test(int_container_t elements, size_t expected)
{
	run_test(std::move(elements), expected, expected);
};

BOOST_AUTO_TEST_CASE(ProofByInduction)
{
	boost::unit_test::unit_test_log.set_threshold_level(boost::unit_test::log_level::log_test_units);

	// Док-во по индукции
	run_test({ 1 }, 1);
	BOOST_TEST_PASSPOINT();

	run_test({ 1,2 }, 2);
	BOOST_TEST_PASSPOINT();
	run_test({ 2,1 }, 1);
	BOOST_TEST_PASSPOINT();

	run_test({ 1,3,2 }, 2);
	BOOST_TEST_PASSPOINT();
	run_test({ 1,2,3 }, 3);
	BOOST_TEST_PASSPOINT();
	run_test({ 2,1,3 }, 2);
	BOOST_TEST_PASSPOINT();
	run_test({ 2,3,1 }, 2);
	BOOST_TEST_PASSPOINT();
	run_test({ 3,1,2 }, 2);
	BOOST_TEST_PASSPOINT();
	run_test({ 3,2,1 }, 1);

	// рандомные тесты
	run_test({ 1,3,2,4 }, 3);
	BOOST_TEST_PASSPOINT();
	run_test({ 2,1,3,2 }, 2);
	BOOST_TEST_PASSPOINT();

	run_test({ 1,3,2,4,5 }, 4);
	BOOST_TEST_PASSPOINT();

	run_test({ 3,2,3,4,5 }, 4);
	BOOST_TEST_PASSPOINT();

	run_test({ 4,2,3,1,5 }, 3);
	BOOST_TEST_PASSPOINT();

	run_test({ 4,5,1,3,6 }, 3);
	BOOST_TEST_PASSPOINT();

	run_test({ 1,3,6,4,5 }, 4);
	BOOST_TEST_PASSPOINT();

	run_test({ 1,4,2,3,4 }, 4);
	BOOST_TEST_PASSPOINT();

	run_test({ 2,1,1,2,1 }, 3, 2);

}

BOOST_AUTO_TEST_CASE(DeleteOneElementFromALotOfValues)
{
	int_container_t a_lot_of_values(1000, 2);
	BOOST_TEST_PASSPOINT();

	a_lot_of_values.push_back(1);
	BOOST_TEST_PASSPOINT();

	run_test(std::move(a_lot_of_values), 1000, 1);
}


/*
 Copyright (C) 2016 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

/*! \file interpolateddiscountcurve2.hpp
    \brief interpolated discount term structure
    \ingroup termstructures
*/

#ifndef quantext_interpolated_discount_curve_2_hpp
#define quantext_interpolated_discount_curve_2_hpp

#include <ql/math/interpolations/loginterpolation.hpp>
#include <ql/patterns/lazyobject.hpp>
#include <ql/termstructures/yieldtermstructure.hpp>
#include <ql/time/calendars/nullcalendar.hpp>

#include <boost/make_shared.hpp>

namespace QuantExt {
using namespace QuantLib;
//! InterpolatedDiscountCurve2 as in QuantLib, but with floating discount quotes and floating reference date
/*! InterpolatedDiscountCurve2 as in QuantLib, but with
    floating discount quotes and floating reference date,
    reference date is always the global evaluation date,
    i.e. settlement days are zero and calendar is NullCalendar()

        \ingroup termstructures
*/
class InterpolatedDiscountCurve2 : public YieldTermStructure, public LazyObject {
public:
    //! \name Constructors
    //@{
    //! times based constructor, note that times should be consistent with day counter dc passed
    InterpolatedDiscountCurve2(const std::vector<Time>& times, const std::vector<Handle<Quote> >& quotes,
                               const DayCounter& dc)
        : YieldTermStructure(dc), times_(times), quotes_(quotes), data_(times_.size(), 1.0),
          today_(Settings::instance().evaluationDate()) {
        for (Size i = 0; i < quotes.size(); ++i) {
            QL_REQUIRE(times_.size() > 1, "at least two times required");
            QL_REQUIRE(times_.size() == quotes.size(), "size of time and quote vectors do not match");
            QL_REQUIRE(times_[0] == 0.0, "First time must be 0, got " << times_[0]);
            QL_REQUIRE(!quotes[i].empty(), "quote at index " << i << " is empty");
            registerWith(quotes_[i]);
        }
        interpolation_ = boost::make_shared<LogLinearInterpolation>(times_.begin(), times_.end(), data_.begin());
        registerWith(Settings::instance().evaluationDate());
    }
    //! date based constructor
    InterpolatedDiscountCurve2(const std::vector<Date>& dates, const std::vector<Handle<Quote> >& quotes,
                               const DayCounter& dc)
        : YieldTermStructure(dc), times_(dates.size(), 0.0), quotes_(quotes), data_(dates.size(), 1.0),
          today_(Settings::instance().evaluationDate()) {
        for (Size i = 0; i < dates.size(); ++i)
            times_[i] = dc.yearFraction(today_, dates[i]);
        for (Size i = 0; i < quotes.size(); ++i) {
            QL_REQUIRE(times_.size() > 1, "at least two times required");
            QL_REQUIRE(times_.size() == quotes.size(), "size of time and quote vectors do not match");
            QL_REQUIRE(times_[0] == 0.0, "First time must be 0, got " << times_[0]);
            QL_REQUIRE(!quotes[i].empty(), "quote at index " << i << " is empty");
            registerWith(quotes_[i]);
        }
        interpolation_ = boost::make_shared<LogLinearInterpolation>(times_.begin(), times_.end(), data_.begin());
        registerWith(Settings::instance().evaluationDate());
    }
    //@}

    Date maxDate() const { return Date::maxDate(); }
    void update() {
        LazyObject::update();
        TermStructure::update();
    }
    const Date& referenceDate() const {
        calculate();
        return today_;
    }

    Calendar calendar() const { return NullCalendar(); }
    Natural settlementDays() const { return 0; }

protected:
    void performCalculations() const {
        today_ = Settings::instance().evaluationDate();
        for (Size i = 0; i < times_.size(); ++i) {
            data_[i] = quotes_[i]->value();
            QL_REQUIRE(data_[i] > 0, "InterpolatedDiscountCurve2: invalid value " << data_[i] << " at index " << i);
        }
        interpolation_->update();
    }

    DiscountFactor discountImpl(Time t) const {
        calculate();
        if (t <= this->times_.back())
            return (*interpolation_)(t, true);
        // flat fwd extrapolation
        Time tMax = this->times_.back();
        DiscountFactor dMax = this->data_.back();
        Rate instFwdMax = -(*interpolation_).derivative(tMax) / dMax;
        return dMax * std::exp(-instFwdMax * (t - tMax));
    }

private:
    std::vector<Time> times_;
    std::vector<Handle<Quote> > quotes_;
    mutable std::vector<Real> data_;
    mutable Date today_;
    boost::shared_ptr<Interpolation> interpolation_;
};

} // namespace QuantExt

#endif