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

/*
Copyright (C) 2005, 2007, 2009, 2014 Klaus Spanderen
Copyright (C) 2015 CompatibL

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

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

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

#ifndef cl_adjoint_heston_process_impl_hpp
#define cl_adjoint_heston_process_impl_hpp
#pragma once

#include "adjointhestonprocesstest.hpp"
#include "utilities.hpp"
#include "adjointtestutilities.hpp"
#include "adjointtestbase.hpp"
#include <ql/quotes/simplequote.hpp>
#include <ql/math/modifiedbessel.hpp>
#include <ql/processes/hestonprocess.hpp>
#include <ql/time/daycounters/actualactual.hpp>
#include <ql/time/schedule.hpp>
#include <boost/make_shared.hpp>

using namespace QuantLib;
using namespace boost::unit_test_framework;

#define OUTPUT_FOLDER_NAME "AdjointHestonProcess"

/*!
AdjointHestonProcessTest is tested using adjoint differentiation for the class hestonprocess.

Heston model describes the evolution of the volatility of an underlying asset.
It is a stochastic volatility model: such a model assumes that the
volatility of the asset is neither constant, nor even deterministic,
but is a random process.

This class describes the square root stochastic volatility
process governed by
\f[
\begin{array}{rcl}
dS(t, S)  &=& \mu S dt + \sqrt{v} S dW_1 \\
dv(t, S)  &=& \kappa (\theta - v) dt + \sigma \sqrt{v} dW_2 \\
dW_1 dW_2 &=& \rho dt
\end{array}
\f]

Tested functions are expectation, evolve, phi, drift, stdDeviation, covariance.

Phi is the continuous version of a characteristic function
for the exact sampling of the Heston process, s. page 8, formula 13,
M. Broadie, O. Kaya, Exact Simulation of Stochastic Volatility and
other Affine Jump Diffusion Processes
http://finmath.stanford.edu/seminars/documents/Broadie.pdf
Output of function Phi is value of haracteristic function.
Inputs are process, value of variable, start and finish variance distibution, and delta time.
Phi is obtained from fourier transform, so Phi has complex derivative with respect to input variable.

Output of fuction expectation is Expectation
\f$ E(\mathrm{x}_{t_0 + \Delta t}
| \mathrm{x}_{t_0} = \mathrm{x}_0) \f$
of the process after a time interval \f$ \Delta t \f$
according to the given discretization.
Inputs are time, start value, and delta time.
Expectation is differentiated with respect to delta time.

Output of fuction evolve is Evolve,
which returns the asset value after a time interval \f$ \Delta t
\f$ according to the given discretization. By default, it
returns
\f[
E(x_0,t_0,\Delta t) + S(x_0,t_0,\Delta t) \cdot \Delta w
\f]
where \f$ E \f$ is the expectation and \f$ S \f$ the
standard deviation.
Inputs are time, start value, and delta time.
Evolve is differentiated with respect to delta time.

Output of fuction drift is Drift (the change of the average value)
Inputs are time, array of average values.
Drift is differentiated with respect to average values.

Output of fuction stdDeviation is Standart Deviation.
Inputs are time, array of average values, and delta time.
StdDeviation is differentiated with respect to delta time.

Output of fuction covariance is covariances matrix
Inputs are time, array of average values, and delta time.
Covariance is differentiated with respect to delta time.
*/

namespace
{
    enum
    {
#if defined CL_GRAPH_GEN
        // Number of points for dependency plots.
        pointNo = 100,
        // Number of points for performance plot.
        iterNo = 30,
        // Step for portfolio size for performance testing .
        step = 1,
#else
        // Number of points for dependency plots.
        pointNo = 0,
        // Number of points for performance plot.
        iterNo = 0,
        // Step for portfolio size for performance testing .
        step = 1,
#endif
        // Defines performance accuracy. Its value is a minimum number
        // of calling of O(1) complexity methods per one performance test.
        iterNumFactor = 1000,
    };

    // copy of function Phi from hestonprocess.hpp
    // This is the continuous version of a characteristic function
    // for the exact sampling of the Heston process, s. page 8, formula 13,
    // M. Broadie, O. Kaya, Exact Simulation of Stochastic Volatility and
    // other Affine Jump Diffusion Processes
    // http://finmath.stanford.edu/seminars/documents/Broadie.pdf
    std::complex<Real> Phi1(const HestonProcess& process,
        const std::complex<Real>& a,
        Real nu_0, Real nu_t, Time dt) {
        const Real theta = process.theta();
        const Real kappa = process.kappa();
        const Real sigma = process.sigma();

        const Volatility sigma2 = sigma*sigma;
        const std::complex<Real> ga = std::sqrt(
            kappa*kappa - 2 * sigma2*a*std::complex<Real>(0.0, 1.0));
        const Real d = 4 * theta*kappa / sigma2;

        const Real nu = 0.5*d - 1;
        const std::complex<Real> z
            = ga*std::exp(-0.5*ga*dt) / (1.0 - std::exp(-ga*dt));
        const std::complex<Real> log_z
            = -0.5*ga*dt + std::log(ga / (1.0 - std::exp(-ga*dt)));

        const std::complex<Real> alpha
            = 4.0*ga*std::exp(-0.5*ga*dt) / (sigma2*(1.0 - std::exp(-ga*dt)));

        const std::complex<Real> beta = 4.0*kappa*std::exp(-0.5*kappa*dt)
            / (sigma2*(1.0 - std::exp(-kappa*dt)));

        return ga*std::exp(-0.5*(ga - kappa)*dt)*(1 - std::exp(-kappa*dt))
            / (kappa*(1.0 - std::exp(-ga*dt)))
            *std::exp((nu_0 + nu_t) / sigma2 * (
            kappa*(1.0 + std::exp(-kappa*dt)) / (1.0 - std::exp(-kappa*dt))
            - ga*(1.0 + std::exp(-ga*dt)) / (1.0 - std::exp(-ga*dt))))
            *std::exp(nu*log_z) / std::pow(z, nu)
            *((nu_t > 1e-8)
            ? modifiedBesselFunction_i(
            nu, std::sqrt(nu_0*nu_t)*alpha)
            / modifiedBesselFunction_i(
            nu, std::sqrt(nu_0*nu_t)*beta)
            : std::pow(alpha / beta, nu)
            );
    }

    struct CommonVars0
    {
        CommonVars0()
        : dayCounter_()
        {
            dayCounter_ = ActualActual();
        }
        DayCounter dayCounter_;

    };

    struct CommonVars : public CommonVars0
    {
        explicit CommonVars()
        : CommonVars0()
        , s0_(boost::make_shared<SimpleQuote>(SimpleQuote(1.05)))
        , process_(Handle<YieldTermStructure>(flatRate(0.04, dayCounter_)), Handle<YieldTermStructure>(flatRate(0.5, dayCounter_)), s0_, 0.3, 1.16, 0.2, 0.8, 0.8,
        HestonProcess::QuadraticExponentialMartingale)
        {
        }

        Handle<Quote> s0_;
        HestonProcess process_;
    };

    enum VariableName { expectation, evolve, phi, drift, stdDeviation, covariance };

    std::string toString(VariableName name)
    {
        switch (name)
        {
        case expectation:
            return "Expectation";
        case evolve:
            return "Evolve";
        case phi:
            return "Phi";
        case drift:
            return "Drift";
        case stdDeviation:
            return "stdDeviation";
        case covariance:
            return "covariance";
        default:
            return "";
        }
    }

    struct HestonProcessTestData : public CommonVars
    {
        explicit HestonProcessTestData(VariableName type)
        : CommonVars()
        , type_(type)
        , t(0.5)
        , x(2, 0.3, 0.2)
        , dw(2, 0.1, 0.4)
        {}

        // Calculate the an expectation in case of test of function Expectation
        // and an evolve in case of test of function Evolve.
        Real calculateRealFromDeltaTime(Real deltaTimes)
        {
            if (type_ == expectation)
            {
                return process_.expectation(t, x, deltaTimes)[0];
            }

            if (type_ == evolve)
            {
                return process_.evolve(t, x, deltaTimes, dw)[0];
            }

            return Real(0.0);
        }

        Matrix calculateMatrixFromDeltaTime(Real deltaTimes)
        {
            if (type_ == stdDeviation)
            {
                return process_.stdDeviation(t, x, deltaTimes);
            }

            if (type_ == covariance)
            {
                return process_.covariance(t, x, deltaTimes);
            }

            return Matrix();
        }

        VariableName type_;
        Real t;
        Array x;
        Array dw;
    };

    // Test of functions expectation and evolve.
    // Testing derivative with respect to delta time.
    struct HestonProcessRespectToDeltaTimeTest
        : public cl::AdjointTest<HestonProcessRespectToDeltaTimeTest>
    {
        explicit HestonProcessRespectToDeltaTimeTest(HestonProcessTestData* data, Size size, cl::tape_empty_test_output* logger = nullptr, double shift = 1e-10)
        : AdjointTest()
        , data_(data)
        , size_(size)
        , shift_(shift)
        , deltaTimes_(size)
        , Variables_(size)
        , SumVariables_(1)
        {
            setLogger(logger);
            for (int i = 0; i < size_; i++)
            {
                deltaTimes_[i] = (i + 1) * 0.2 / double(size);
            }
        }

        Size indepVarNumber() { return size_; }

        Size depVarNumber() { return 1; }

        Size minPerfIteration() { return iterNumFactor; }

        double absTol() const { return 1e-5; }

        void calcAnalytical()
        {
            analyticalResults_.resize(size_);
            for (int i = 0; i < size_; i++)
            {
                double deltaTimeUp = double(deltaTimes_[i]) + shift_;
                double deltaTimeDown = double(deltaTimes_[i]) - shift_;
                double varUp = double(data_->calculateRealFromDeltaTime(deltaTimeUp));
                double varDown = double(data_->calculateRealFromDeltaTime(deltaTimeDown));
                analyticalResults_[i] = (varUp - varDown) / 2 / shift_;
            }
        }

        void calculateVariables()
        {
            SumVariables_[0] = 0.0;
            for (int i = 0; i < size_; i++)
            {
                Variables_[i] = data_->calculateRealFromDeltaTime(deltaTimes_[i]);
                SumVariables_[0] += Variables_[i];
            }
        }

        void recordTape()
        {
            Independent(deltaTimes_);
            calculateVariables();
            f_ = std::make_unique<cl::tape_function<double>>(deltaTimes_, SumVariables_);
        }

        HestonProcessTestData* data_;
        Size size_;
        double shift_;
        std::vector<Real> deltaTimes_;
        std::vector<Real> Variables_;
        std::vector<Real> SumVariables_;
    };

    // Test of matrix functions stdDeviation and covariance.
    struct HestonProcessMatrixFunctionsTest :
        public cl::AdjointTest<HestonProcessMatrixFunctionsTest>
    {
        static const CalcMethod default_method = other;

        explicit HestonProcessMatrixFunctionsTest(HestonProcessTestData* data, cl::tape_empty_test_output* logger = nullptr, double shift = 1e-10)
        : AdjointTest()
        , data_(data)
        , shift_(shift)
        , deltaTimes_(1, 0.1)
        , size_(4)
        {}

        Size indepVarNumber() { return 1; }

        Size depVarNumber() { return size_; }

        Size minPerfIteration() { return iterNumFactor; }

        double absTol() const { return 1e-6; }

        void calculateMatrixRes()
        {
            Matrix StdDeviation = data_->calculateMatrixFromDeltaTime(deltaTimes_[0]);
            Results_.assign(StdDeviation.begin(), StdDeviation.end());
        }

        void recordTape()
        {
            Independent(deltaTimes_);
            calculateMatrixRes();
            f_ = std::make_unique<cl::tape_function<double>>(deltaTimes_, Results_);
        }

        void calcAdjoint()
        {
            int num = Results_.size();
            std::vector<double> w(1, 1);
            adjointResults_ = f_->Forward(1, w);
        }

        void calcAnalitical()
        {
            Time dtUp = deltaTimes_[0] + shift_;
            Time dtDown = deltaTimes_[0] - shift_;

            Matrix ResUp = data_->calculateMatrixFromDeltaTime(dtUp);
            Matrix ResDown = data_->calculateMatrixFromDeltaTime(dtDown);

            for (int i = 0; i < ResUp.columns(); i++)
            {
                for (int j = 0; j < ResUp.rows(); j++)
                {
                    double analyticalRes = double((ResUp[i][j] - ResDown[i][j]) / 2 / shift_);
                    analyticalResults_.push_back(analyticalRes);
                }
            }
        }

        HestonProcessTestData* data_;
        double shift_;
        std::vector<Real> deltaTimes_;
        std::vector<Real> Results_;
        Size size_;
    };

    struct HestonProcessDriftTest : public cl::AdjointTest<HestonProcessDriftTest>
    {
        static const CalcMethod default_method = other;

        explicit HestonProcessDriftTest(CommonVars* data, cl::tape_empty_test_output* logger = nullptr, double shift = 1e-10)
            : AdjointTest()
            , data_(data)
            , shift_(shift)
            , size_(2)
            , values_(size_)
            , x_(size_)
            , drift_(2)
            , t_(0.5)
            , indepDouble_(2)
        {
            for (int i = 0; i < size_; i++)
            {
                indepDouble_[i] = 0.3 + 0.2 * i;
                values_[i] = indepDouble_[i];
            }
        }

        Size indepVarNumber() { return size_; }

        Size depVarNumber() { return size_; }

        Size minPerfIteration() { return iterNumFactor; }

        double absTol() const { return 1e-6; }

        void calculateDrift()
        {
            for (int i = 0; i < size_; i++)
            {
                x_[i] = values_[i];
            }
            Array drift = data_->process_.drift(t_, x_);
            for (int i = 0; i < size_; i++)
            {
                drift_[i] = drift[i];
            }
        }

        void recordTape()
        {
            cl::Independent(values_);
            calculateDrift();
            f_ = std::make_unique<cl::tape_function<double>>(values_, drift_);
        }

        void calcAdjoint()
        {
            adjointResults_ = f_->Jacobian(indepDouble_);
        }

        void calcAnalitical()
        {
            analyticalResults_.resize(size_ * size_);
            Array xUp = x_;
            Array xDown = x_;
            for (int i = 0; i < size_; i++)
            {
                xUp[i] += shift_;
                Array DriftUp = data_->process_.drift(t_, xUp);
                xUp[i] -= shift_;

                xDown[i] -= shift_;
                Array DriftDown = data_->process_.drift(t_, xDown);
                xDown[i] += shift_;

                for (int j = 0; j < size_; j++)
                {
                    analyticalResults_[i + size_ * j] = (DriftUp[j] - DriftDown[j]) / 2 / shift_;
                }
            }
        }

        CommonVars* data_;
        double shift_;
        Size size_;
        std::vector<Real> values_;
        Array x_;
        std::vector<Real> drift_;
        Time t_;
        std::vector<double> indepDouble_;
    };

    // Struct for plots recording.
    struct DeltaTimeResult
    {
        static std::deque<std::string > get_columns()
        {
            static std::deque<std::string > columns =
            {
                "Delta time", ""
            };

            return columns;
        }

        template <typename stream_type>
        friend inline stream_type&
            operator << (stream_type& stm, DeltaTimeResult& v)
        {
                stm << v.deltaTime_
                    << ";" << v.result_
                    << std::endl;
                return stm;
            }

        Real deltaTime_;
        Real result_;
    };

    struct TestDataRespectDeltaTime
        : public HestonProcessTestData
    {
        explicit TestDataRespectDeltaTime(VariableName name)
        : HestonProcessTestData(name)
        , outPerform_title_("Swaption " + toString(name) + " differentiation performance with respect to delta time")
        , outAdjoint_title_("Swaption " + toString(name) + " adjoint differentiation with respect to fixed delta time")
        , out_title_(toString(name) + " dependence on delta time")
        , out_ylabel_(toString(name))
        , outPerform_(OUTPUT_FOLDER_NAME "//" + toString(name), {
            { "title", outPerform_title_ }
            , { "not_clear", "Not" }
            , { "ylabel", "Time (s)" }
            , { "xlabel", "Number of delta times" }
            , { "line_box_width", "-5" }
            , { "cleanlog", "true" }
            , { "smooth", "default" }
        })
            , outAdjoint_(OUTPUT_FOLDER_NAME "//" + toString(name), {
                { "title", outAdjoint_title_ }
            , { "filename", "Adjoint" }
            , { "not_clear", "Not" }
            , { "ylabel", "Time (s)" }
            , { "xlabel", "Number of delta times" }
            , { "cleanlog", "false" }
            , { "smooth", "default" }
        })
            , outSize_(OUTPUT_FOLDER_NAME "//" + toString(name), {
                { "title", "Tape size dependence on number of delta time" }
            , { "filename", "TapeSize" }
            , { "not_clear", "Not" }
            , { "ylabel", "Memory (MB)" }
            , { "cleanlog", "false" }
        })

            , out_(OUTPUT_FOLDER_NAME "//" + toString(name) + "//output", {
                { "filename", "DeltaTimeDependence" }
            , { "not_clear", "Not" }
            , { "title", out_title_ }
            , { "ylabel", out_ylabel_ }
            , { "xlabel", "delta time" }
            , { "cleanlog", "false" }
        })
        { }

        bool makeOutput()
        {
            bool ok = true;

            if (pointNo > 0)
            {
                ok &= recordDependencePlot();
            }
            ok &= cl::recordPerformance(*this, iterNo, step);

            return ok;
        }

        std::shared_ptr<HestonProcessRespectToDeltaTimeTest> getTest(size_t size)
        {
            return std::make_shared<HestonProcessRespectToDeltaTimeTest>(this, size, &outPerform_);
        }

        // Makes plots for derivative of blackVariance dependence.
        bool recordDependencePlot()
        {
            std::vector<DeltaTimeResult> outData(pointNo);
            auto test = getTest(pointNo);
            bool ok = test->testReverse();
            for (Size i = 0; i < pointNo; i++)
            {
                outData[i] = { test->deltaTimes_[i], test->Variables_[i] };
            }

            out_ << outData;
            return ok;

            return true;
        }

        std::string outPerform_title_;
        std::string outAdjoint_title_;
        std::string out_title_;
        std::string out_ylabel_;
        cl::tape_empty_test_output outPerform_;
        cl::tape_empty_test_output outAdjoint_;
        cl::tape_empty_test_output outSize_;
        cl::tape_empty_test_output out_;
    };

}

#endif


/**
 * @file tests/hyperplane_test.cpp
 *
 * Tests for Hyperplane and ProjVector implementations.
 *
 * 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.
 */
#include <mlpack/core.hpp>
#include <mlpack/core/tree/space_split/hyperplane.hpp>

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

using namespace mlpack;
using namespace mlpack::math;
using namespace mlpack::tree;
using namespace mlpack::metric;
using namespace mlpack::bound;

BOOST_AUTO_TEST_SUITE(HyperplaneTest);

/**
 * Ensure that a hyperplane, by default, consider all points to the left.
 */
BOOST_AUTO_TEST_CASE(HyperplaneEmptyConstructor)
{
  Hyperplane<EuclideanDistance> h1;
  AxisOrthogonalHyperplane<EuclideanDistance> h2;

  arma::mat dataset;
  dataset.randu(3, 20); // 20 points in 3 dimensions.

  for (size_t i = 0; i < dataset.n_cols; ++i)
  {
    BOOST_REQUIRE(h1.Left(dataset.col(i)));
    BOOST_REQUIRE(h2.Left(dataset.col(i)));
    BOOST_REQUIRE(!h1.Right(dataset.col(i)));
    BOOST_REQUIRE(!h2.Right(dataset.col(i)));
  }
}

/**
 * Ensure that we get the correct hyperplane given the projection vector.
 */
BOOST_AUTO_TEST_CASE(ProjectionTest)
{
  // General hyperplane.
  ProjVector projVect1(arma::vec("1 1"));
  Hyperplane<EuclideanDistance> h1(projVect1, 0);

  BOOST_REQUIRE_EQUAL(h1.Project(arma::vec("1 -1")), 0);
  BOOST_REQUIRE(h1.Left(arma::vec("1 -1")));
  BOOST_REQUIRE(!h1.Right(arma::vec("1 -1")));

  BOOST_REQUIRE_EQUAL(h1.Project(arma::vec("-1 1")), 0);
  BOOST_REQUIRE(h1.Left(arma::vec("-1 1")));
  BOOST_REQUIRE(!h1.Right(arma::vec("-1 1")));

  BOOST_REQUIRE_EQUAL(h1.Project(arma::vec("1 0")),
      h1.Project(arma::vec("0 1")));
  BOOST_REQUIRE(h1.Right(arma::vec("1 0")));
  BOOST_REQUIRE(!h1.Left(arma::vec("1 0")));

  BOOST_REQUIRE_EQUAL(h1.Project(arma::vec("-1 -1")),
      h1.Project(arma::vec("-2 0")));
  BOOST_REQUIRE(h1.Left(arma::vec("-1 -1")));
  BOOST_REQUIRE(!h1.Right(arma::vec("-1 -1")));

  // A simple 2-dimensional bound.
  BallBound<EuclideanDistance> b1(2);

  b1.Center() = arma::vec("-1 -1");
  b1.Radius() = 1.41;
  BOOST_REQUIRE(h1.Left(b1));
  BOOST_REQUIRE(!h1.Right(b1));

  b1.Center() = arma::vec("1 1");
  b1.Radius() = 1.41;
  BOOST_REQUIRE(h1.Right(b1));
  BOOST_REQUIRE(!h1.Left(b1));

  b1.Center() = arma::vec("0 0");
  b1.Radius() = 1.41;
  BOOST_REQUIRE(!h1.Right(b1));
  BOOST_REQUIRE(!h1.Left(b1));
}

/**
 * Ensure that we get the correct AxisOrthogonalHyperplane given the
 * AxisParallelProjVector.
 */
BOOST_AUTO_TEST_CASE(AxisOrthogonalProjectionTest)
{
  // AxisParallel hyperplane.
  AxisParallelProjVector projVect2(1);
  AxisOrthogonalHyperplane<EuclideanDistance> h2(projVect2, 1);

  BOOST_REQUIRE_EQUAL(h2.Project(arma::vec("0 0")), -1);
  BOOST_REQUIRE(h2.Left(arma::vec("0 0")));
  BOOST_REQUIRE(!h2.Right(arma::vec("0 0")));

  BOOST_REQUIRE_EQUAL(h2.Project(arma::vec("0 1")), 0);
  BOOST_REQUIRE(h2.Left(arma::vec("0 1")));
  BOOST_REQUIRE(!h2.Right(arma::vec("0 1")));

  BOOST_REQUIRE_EQUAL(h2.Project(arma::vec("0 2")), 1);
  BOOST_REQUIRE(h2.Right(arma::vec("0 2")));
  BOOST_REQUIRE(!h2.Left(arma::vec("0 2")));

  BOOST_REQUIRE_EQUAL(h2.Project(arma::vec("1 2")), 1);
  BOOST_REQUIRE(h2.Right(arma::vec("1 2")));
  BOOST_REQUIRE(!h2.Left(arma::vec("1 2")));

  BOOST_REQUIRE_EQUAL(h2.Project(arma::vec("1 0")), -1);
  BOOST_REQUIRE(h2.Left(arma::vec("1 0")));
  BOOST_REQUIRE(!h2.Right(arma::vec("1 0")));

  // A simple 2-dimensional bound.
  HRectBound<EuclideanDistance> b2(2);

  b2[0] = Range(-1.0, 1.0);
  b2[1] = Range(-1.0, 1.0);
  BOOST_REQUIRE(h2.Left(b2));
  BOOST_REQUIRE(!h2.Right(b2));

  b2[0] = Range(-1.0, 1.0);
  b2[1] = Range(1.001, 2.0);
  BOOST_REQUIRE(h2.Right(b2));
  BOOST_REQUIRE(!h2.Left(b2));

  b2[0] = Range(-1.0, 1.0);
  b2[1] = Range(0, 2.0);
  BOOST_REQUIRE(!h2.Right(b2));
  BOOST_REQUIRE(!h2.Left(b2));
}

BOOST_AUTO_TEST_SUITE_END();


/*
 * TriangleElement.hpp
 *
 *  Created on: Nov 18, 2012
 *      Author: petr
 */
#pragma once
#ifndef TRIANGLEELEMENT_HPP_
#define TRIANGLEELEMENT_HPP_

#undef max
#undef min

#include <cmath>
#include <limits>
#include <Eigen/Dense>
#include <Eigen/Geometry>
#include <Eigen/StdVector>
#include <vector>
#include <iostream>
#include "BoundingBox.hpp"
#include "utility.h"
#include "tribox3.hpp"

// #include <boost/archive/basic_binary_oarchive.hpp>
// #include <boost/archive/basic_binary_iarchive.hpp>

template<typename type>
struct SignedDistance {
	type            dist;
	type            angle;
	int             sign;
	Eigen::Vector3d minPoint;
};

template <typename type>
class TriangleElement {

public:

	/// Stores vertices of an element.
	/// This implementation uses triangle element, but in future, there will be support for multiple element types.
	std::vector<Eigen::Vector3d, Eigen::aligned_allocator<Eigen::Vector3d> > vertices;

	/// Face normal
	Eigen::Vector3d normal;

	/// element ID used to identify element
	long int ID;

	TriangleElement(): ID(-1) {};

	/// Triangle element contructor, triangle normal is computed automaticaly
	/// Normal is computed standard way : n = (v1 - v0) x (v2 - v0), where x is cross product
	TriangleElement(Eigen::Vector3d& v0, Eigen::Vector3d& v1, Eigen::Vector3d& v2, long int id);

	TriangleElement(const Eigen::Vector3d& v) {
		this->vertices.reserve(3);

		this->vertices.push_back(v);
		this->vertices.push_back(v);
		this->vertices.push_back(v);

		this->ID = -1;
	}

	/// Constructor to create the triangle element from complete list of vertices with an ID
	TriangleElement(std::vector<Eigen::Vector3d, Eigen::aligned_allocator<Eigen::Vector3d> >& vertices, long int id);

	TriangleElement(const TriangleElement<type> & element) : vertices(element.vertices), normal(element.normal), ID(element.ID) {};

	/// Test whether element is inside the rectangular! region
	bool isInsideRegion(const Box<type, 3>& bb) const;

	/// Test whether element is partly inside the rectangular! region
	bool isPartlyInRegion(const Box<type, 3>& bb) const;

	/// Return centroid of element
	Eigen::Vector3d centroid();

	static SignedDistance<type> computeDistance(const TriangleElement<type>& el, const Eigen::Vector3d& point);

	static type computePerpendicularDistance(const TriangleElement<type>& el, const Eigen::Vector3d& point);

	/// Return tight Axis Aligned Bounding Box around element
	BoundingBox<type, 3, TriangleElement<double> > getBoundingBox() const;

};

template<typename type>
TriangleElement<type>::TriangleElement(Eigen::Vector3d& v0, Eigen::Vector3d& v1, Eigen::Vector3d& v2, long int id) {

	this->vertices.reserve(3);

	this->vertices.push_back(v0);
	this->vertices.push_back(v1);
	this->vertices.push_back(v2);

	Eigen::Vector3d e0 = v1 - v0;
	Eigen::Vector3d e1 = v2 - v0;

	this->normal = e0.cross(e1);
	this->normal.normalize();

	this->ID = id;

}

template<typename type>
TriangleElement<type>::TriangleElement(std::vector<Eigen::Vector3d, Eigen::aligned_allocator<Eigen::Vector3d> >& vertices, long int id) {

	this->vertices = vertices;

	Eigen::Vector3d e0 = vertices[1] - vertices[0];
	Eigen::Vector3d e1 = vertices[2] - vertices[0];

	this->normal = e0.cross(e1);
	this->normal.normalize();

	this->ID = id;

}

template<typename type>
inline BoundingBox<type, 3, TriangleElement<double> > TriangleElement<type>::getBoundingBox() const {

	double minX[3], maxX[3];
	minX[0] =  std::numeric_limits<type>::max();
	minX[1] =  std::numeric_limits<type>::max();
	minX[2] =  std::numeric_limits<type>::max();

	maxX[0] = -std::numeric_limits<type>::max();
	maxX[1] = -std::numeric_limits<type>::max();
	maxX[2] = -std::numeric_limits<type>::max();

	for (auto vertex: vertices) {

		// find lower bound
		if ( vertex[0] < minX[0] ) // for x
			minX[0] = vertex[0];
		if ( vertex[1] < minX[1] ) // for y
			minX[1] = vertex[1];
		if ( vertex[2] < minX[2] ) // for z
			minX[2] = vertex[2];

		// find upper bound
		if ( vertex[0] > maxX[0] ) // for x
			maxX[0] = vertex[0];
		if ( vertex[1] > maxX[1] ) // for y
			maxX[1] = vertex[1];
		if ( vertex[2] > maxX[2] ) // for z
			maxX[2] = vertex[2];

	}

	return BoundingBox<type, 3, TriangleElement<double> >(minX, maxX, this);

}

template<typename type>
inline bool TriangleElement<type>::isInsideRegion(const Box<type, 3>& bb) const {

	bool vertex_0 = bb.pointInside(vertices[0].array());
	bool vertex_1 = bb.pointInside(vertices[1].array());
	bool vertex_2 = bb.pointInside(vertices[2].array());

	return vertex_0 && vertex_1 && vertex_2;

}

/// \brief Detects if triangle element collide with cuboid region. Tests for partial collisions
///        which could pose a tricky problem: long thin triangles, one vertex collision,
///        one edge collision, edge to edge collision.
///
/// The algorithm is taken from book: (Morgan Kaufmann series in  Interactive 3d technology)
///                                   Realtime collision detection - Christer Ericson
///
template<typename type>
inline bool TriangleElement<type>::isPartlyInRegion(const Box<type, 3>& bb) const {

	float trivert[3][3] = { {(float)vertices[0][0], (float)vertices[0][1], (float)vertices[0][2]},
							{(float)vertices[1][0], (float)vertices[1][1], (float)vertices[1][2]},
							{(float)vertices[2][0], (float)vertices[2][1], (float)vertices[2][2]}};

	float center[3] = {(float)bb.center(0), (float)bb.center(1), (float)bb.center(2)};

	float extent[3] = {(float)bb.extent(0), (float)bb.extent(1), (float)bb.extent(2)};

	// utilizes code I found on the internet, check the functionality
	return triBoxOverlap(center, extent, trivert);

}

template<typename type>
inline SignedDistance<type> TriangleElement<type>::computeDistance(const TriangleElement<type>& el, const Eigen::Vector3d &point) {
	// point is supposed to have right dimension, so do it nice and clear :)
	// this function computed the distance between triangle element and point in 3D
	// complete description of a method could be found here :
	//  	http://www.geometrictools.com/Documentation/DistancePoint3Triangle3.pdf
	// sample implementation could be found here :
	//		http://www.geometrictools.com/LibMathematics/Distance/Wm5DistPoint3Triangle3.cpp
	// and here :
	// 		http://www.mathworks.com/matlabcentral/fileexchange/22857-distance-between-a-point-and-a-triangle-in-3d/

	//double E0[3] = {el.vertex_x[1] - el.vertex_x[0], el.vertex_y[1] - el.vertex_y[0], el.vertex_z[1] - el.vertex_z[0]};
	//double E1[3] = {el.vertex_x[2] - el.vertex_x[0], el.vertex_y[2] - el.vertex_y[0], el.vertex_z[2] - el.vertex_z[0]};

	Eigen::Vector3d E0 = el.vertices[1] - el.vertices[0];
	Eigen::Vector3d E1 = el.vertices[2] - el.vertices[0];

	//double D[3]  = {el.vertex_x[0] - point[0], el.vertex_y[0] - point[1], el.vertex_z[0] - point[2]};

	Eigen::Vector3d D = el.vertices[0] - point;

//	double a00_ = E0[0]*E0[0] + E0[1]*E0[1] + E0[2]*E0[2];
	type a00 = E0.dot(E0);

//	std::cout << (a00_ - a00) << std::endl;

//	double a01_ = E0[0]*E1[0] + E0[1]*E1[1] + E0[2]*E1[2];
	type a01 = E0.dot(E1);

//	std::cout << (a01_ - a01) << std::endl;

//	double a11_ = E1[0]*E1[0] + E1[1]*E1[1] + E1[2]*E1[2];
	type a11 = E1.dot(E1);

//	std::cout << (a11_ - a11) << std::endl;

//	double b0_  = E0[0]*D[0]  + E0[1]*D[1]  + E0[2]*D[2];
	type b0 = E0.dot(D);

//	std::cout << (b0_ - b0) << std::endl;

//	double b1_  = E1[0]*D[0]  + E1[1]*D[1]  + E1[2]*D[2];
	type b1 = E1.dot(D);

//	std::cout << (b0_ - b0) << std::endl;

//	double c_   = D[0]*D[0]   + D[1]*D[1]   + D[2]*D[2];
	type c = D.dot(D);

//	std::cout << (c_ - c) << std::endl;

	type det = std::abs(a00*a11 - a01*a01);

	type s   = a01*b1 - a11*b0;
	type t   = a01*b0 - a00*b1;

	type sqrDistance = 0;

	if ( (s+t) <= det ) {
		if ( s < 0 ) {
			if ( t < 0 ) {
				// region 4
				if ( b0 < 0 ) {
					t = 0;
					if ( -b0 >= a00 ) {
						s = 1;
						sqrDistance = a00 + 2*b0 + c;
					} else {
						s = -b0/a00;
						sqrDistance = b0*s + c;
					}
				} else {
					s = 0;
					if ( b1 >= 0 ) {
						t = 0;
						sqrDistance = c;
					} else if ( -b1 >= a11 ) {
						t = 1;
						sqrDistance = a11 + 2*b1 + c;
					} else {
						t = -b1/a11;
						sqrDistance = b1*t + c;
					}
				}
				// end of region 4
			} else {
				// region 3
				s = 0;
				if ( b1 >= 0 ) {
					t = 0;
					sqrDistance = c;
				} else if ( -b1 >= a11 ) {
					t = 1;
					sqrDistance = a11 + 2*b1 + c;
				} else {
					t = -b1/a11;
					sqrDistance = b1*t + c;
				}
				// end of region 3
			}
		} else if ( t < 0) {
			// region 5
			t = 0;
			if ( b0 >= 0 ) {
				s = 0;
				sqrDistance = c;
			} else if ( -b0 >= a00 ) {
				s = 1;
				sqrDistance = a00 + 2*b0 + c;
			} else {
				s = -b0/a00;
				sqrDistance = b0*s + c;
			}
			// end of region 5
		} else {
			// region 0
			double invDet = 1 / det;
			s *= invDet;
			t *= invDet;
			sqrDistance = s*(a00*s + a01*t + 2*b0) + t*(a01*s + a11*t + 2*b1) + c;
			// end of region 0
		}
	} else {
		if ( s < 0 ) {
			// region 2
			double tmp0 = a01 + b0;
			double tmp1 = a11 + b1;
			if ( tmp1 > tmp0 ) { // minimum on edge s+t=1
				double numer = tmp1 - tmp0;
				double denom = a00 - 2*a01 + a11;
				if ( numer >= denom ) {
					s = 1;
					t = 0;
					sqrDistance = a00 + 2*b0 + c;
				} else {
					s = numer/denom;
					t = 1 - s;
					sqrDistance = s*(a00*s + a01*t + 2*b0) + t*(a01*s + a11*t + 2*b1) + c;
				}
			} else { // minimum on edge s=0
				s = 0;
				if ( tmp1 <= 0 ) {
					t = 1;
					sqrDistance = a11 + 2*b1 + c;
				} else if ( b1 >= 0 ) {
					t = 0;
					sqrDistance = c;
				} else {
					t = -b1/a11;
					sqrDistance = b1*t + c;
				}
			}
			// end of region 2
		} else if ( t < 0 ) {
			// region 6
			double tmp0 = a01 + b1;
			double tmp1 = a00 + b0;
			if ( tmp1 > tmp0 ) {
				double numer = tmp1 - tmp0;
				double denom = a00 - 2*a01 + a11;
				if ( numer >= denom ) {
					t = 1;
					s = 0;
					sqrDistance = a11 + 2*b1 + c;
				} else {
					t = numer/denom;
					s = 1 - t;
					sqrDistance = s*(a00*s + a01*t + 2*b0) + t*(a01*s + a11*t + 2*b1) + c;
				}
			} else {
				t = 0;
				if ( tmp1 <= 0 ) {
					s = 1;
					sqrDistance = a00 + 2*b0 + c;
				} else if ( b0 >= 0 ) {
					s = 0;
					sqrDistance = c;
				} else {
					s = -b0/a00;
					sqrDistance = b0*s + c;
				}
			}
			// end of region 6
		} else {
			// region 1
			double numer = a11 + b1 - a01 - b0;
			if ( numer <= 0 ) {
				s = 0;
				t = 1;
				sqrDistance = a11 + 2*b1 + c;
			} else {
				double denom = a00 - 2*a01 + a11;
				if ( numer >= denom ) {
					s = 1;
					t = 0;
					sqrDistance = a00 + 2*b0 + c;
				} else {
					s = numer / denom;
					t = 1 - s;
					sqrDistance = s*(a00*s + a01*t + 2*b0) + t*(a01*s + a11*t + 2*b1) + c;
				}
			}
			// end of region 1
		}
	}

	double eeps = my_eps;

	if (sqrDistance <= eeps)
		sqrDistance = 0.0;

	SignedDistance<type> dist;

	dist.dist = std::sqrt(sqrDistance);

	dist.minPoint = el.vertices[0] + s*E0 + t*E1;

//	dist.minPoint[0] = (el.vertex_x[0] + s*E0[0] + t*E1[0]);
//	dist.minPoint[1] = (el.vertex_y[0] + s*E0[1] + t*E1[1]);
//	dist.minPoint[2] = (el.vertex_z[0] + s*E0[2] + t*E1[2]);

//	double PP0[3] = { point[0] - dist.minPoint[0],
//					  point[1] - dist.minPoint[1],
//					  point[2] - dist.minPoint[2] };

//	double nrm = sqrt(PP0[0]*PP0[0] + PP0[1]*PP0[1] + PP0[2]*PP0[2]);

	// compute on which side of the triangle we are
	Eigen::Vector3d PP0 = point - dist.minPoint;

	if ( PP0.isZero(eeps) ) {
		// PP0 = Eigen::Vector3d::Zero();
	} else {
		PP0.normalize();
	}

	//double nrm_normal = sqrt(el.normal_x*el.normal_x + el.normal_y*el.normal_y + el.normal_z*el.normal_z);

//	double dprod = (el.normal_x/nrm_normal) * (PP0[0]/nrm) +
//				   (el.normal_y/nrm_normal) * (PP0[1]/nrm) +
//				   (el.normal_z/nrm_normal) * (PP0[2]/nrm);

	type dprod = el.normal.dot(PP0);

//	std::cout << el.normal.transpose() << " DOT " << PP0.transpose() << " = " << dprod << std::endl;

	int sgn = 0;
	if (dprod > eeps)
		sgn = 1;
	else if (dprod < -eeps)
		sgn = -1;

	dist.angle = dprod;
	dist.sign  = sgn;

	return dist;

}

template<typename type>
inline Eigen::Vector3d TriangleElement<type>::centroid() {

	Eigen::Vector3d c;

	for (auto vertex: vertices) {
		c += vertex;
	}

	return c*(1.0/3.0);

}

template<typename type>
inline type TriangleElement<type>::computePerpendicularDistance(const TriangleElement& el, const Eigen::Vector3d& point) {
	// compute projection onto surface defined by the triangle element
	Eigen::Vector3d vec;
	vec << point - el.vertices[0];

	type ortDist = std::abs( vec.dot(el.normal) );

	return ortDist;
}

// ostream operators

template <typename type>
std::ostream& operator<<(std::ostream& os, const TriangleElement<type>& el) {
	os << "vert_0: " << el.vertices[0].transpose() << std::endl;
	os << "vert_1: " << el.vertices[1].transpose() << std::endl;
	os << "vert_2: " << el.vertices[2].transpose() << std::endl;
	os << "normal: " << el.normal.transpose();

    return os;
}

struct TriangleCompare {

	bool operator() (spatial::dimension_type dim, const TriangleElement<double>& a, const TriangleElement<double>& b) const {

		switch (dim) {
			case 0:
//				a.vertices[0]
				return (a.vertices[0][0] < b.vertices[0][0]) &&
					   (a.vertices[1][0] < b.vertices[1][0]) &&
					   (a.vertices[2][0] < b.vertices[2][0]);
				break;
			case 1:
				return (a.vertices[0][1] < b.vertices[0][1]) &&
					   (a.vertices[1][1] < b.vertices[1][1]) &&
					   (a.vertices[2][1] < b.vertices[2][1]);
//				return (a._tr[0] < b._tr[0]);
				break;
			case 2:
				return (a.vertices[0][2] < b.vertices[0][2]) &&
					   (a.vertices[1][2] < b.vertices[1][2]) &&
					   (a.vertices[2][2] < b.vertices[2][2]);
//				return (a._bl[1] < b._bl[1]);
				break;

		}

		return false;
	}

};

struct TrianglePredicate {
	const Box<double, 3>& box;

	TrianglePredicate(const Box<double, 3>& b) : box(b) {}

	spatial::relative_order
	operator() (spatial::dimension_type dim, spatial::dimension_type, const TriangleElement<double>& t ) const {

		Box<double, 3> b = t.getBoundingBox();

		switch (dim) {
			case 0:
				return (b._bl[0] > box._tr[0] && b._tr[0] > box._tr[0]) ? spatial::above :
					   (b._tr[0] < box._bl[0] && b._bl[0] < box._bl[0]) ? spatial::below :
						spatial::matching;
				break;
			case 1:
				return (b._bl[1] > box._tr[1] && b._tr[1] > box._tr[1]) ? spatial::above :
					   (b._tr[1] < box._bl[1] && b._bl[1] < box._bl[1]) ? spatial::below :
						spatial::matching;
				break;
			case 2:
				return (b._bl[2] > box._tr[2] && b._tr[2] > box._tr[2]) ? spatial::above :
					   (b._tr[2] < box._bl[2] && b._bl[2] < box._bl[2]) ? spatial::below :
						spatial::matching;
				break;
		}

	}
};

struct TriangleMetric {
    // Check that DistanceType is a fundamental floating point type
    typedef double distance_type;

    /**
     *  Compute the distance between the point of \c origin and the \c key.
     *  \return The resulting square distance.
     */
    distance_type
    distance_to_key(spatial::dimension_type rank,
                    const TriangleElement<double>& origin,
                    const TriangleElement<double>& key) const {

    	distance_type result = distance_type(0.0);

//    	std::cout << "===============================================" << std::endl;
//    	std::cout << origin << std::endl;
//    	std::cout << "--------------" << std::endl;
//    	std::cout << key << std::endl;
//    	std::cout << "--------------" << std::endl;

    	Eigen::Array3d co = (origin.vertices[0] + origin.vertices[1] + origin.vertices[2]).array() / 3.0;
    	Eigen::Array3d ck = (key.vertices[0] + key.vertices[1] + key.vertices[2]).array() / 3.0;

    	// distance_type tmp = 0.0

		for (int ii = 0; ii < 3; ++ii) {
			double d = std::numeric_limits<double>::max();
			for (spatial::dimension_type i = 0; i < rank; ++i) {
				double tmp = std::abs( key.vertices[i][ii] - origin.vertices[i][ii] );
				d = (tmp < d) ? tmp : d;
			}

			result += d * d;
		}

//    	std::cout << "dist = " << result << std::endl;
//    	std::cout << "===============================================" << std::endl;
    	return result;

    }

    /**
     *  The distance between the point of \c origin and the closest point to
     *  the plane orthogonal to the axis of dimension \c dim and crossing \c
     *  key.
     *  \return The resulting square distance.
     */
    distance_type
    distance_to_plane(spatial::dimension_type,
    			      spatial::dimension_type dim,
                      const TriangleElement<double>& origin,
                      const TriangleElement<double>& key) const {

    	distance_type d = std::numeric_limits<double>::max();
		for (spatial::dimension_type i = 0; i < 3; ++i) {
			double tmp = std::abs( key.vertices[i][dim] - origin.vertices[i][dim] );
			d = (tmp < d) ? tmp : d;
		}

		return d * d;

    }

};

#endif /* TRIANGLEELEMENT_HPP_ */


#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <Eigen/Dense>

namespace py=pybind11;
typedef Eigen::MatrixXd Matrix;
typedef Eigen::VectorXd Vector;

Eigen::Ref<Matrix> distance_table(
  Eigen::Ref<const Matrix>& pos, double lbox)
{
  const int natom = pos.rows();
  const int ndim = pos.cols();
  Matrix dtable = Matrix::Zero(natom, natom);
  Vector drij(ndim);
  for (int i=0;i<natom;i++)
  {
    for (int j=i; j<natom; j++)
    {
      drij = pos.row(i) - pos.row(j);
      for (int idim=0;idim<ndim;idim++)
      {
        drij(idim) -= lbox*std::round(drij(idim)/lbox);
      }
      dtable(i, j) = drij.norm();
    }
  }
  return dtable;
}

PYBIND11_MODULE(example, m)
{
  //m.def("distance_table", &distance_table, "PBC distances");
  m.def("distance_table", &distance_table, "PBC distances",
        py::return_value_policy::reference_internal);
}


#include <QTime>
#include <QApplication>
#include <QAction>
#include <QMainWindow>
#include <QStringList>

#include "opengl_tools.h"
#include "Scene_polyhedron_item.h"
#include "Scene_surface_mesh_item.h"
#include "Scene_points_with_normal_item.h"
#include "Scene_polylines_item.h"
#include "Scene_polyhedron_selection_item.h"
#include "Polyhedron_type.h"

#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>

#include <CGAL/convex_hull_3.h>
#include <CGAL/boost/graph/copy_face_graph.h>
#include <boost/iterator/transform_iterator.hpp>
using namespace CGAL::Three;
class Polyhedron_demo_convex_hull_plugin :
  public QObject,
  public Polyhedron_demo_plugin_interface
{
  Q_OBJECT
  Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
  Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
public:
    void init(QMainWindow*mw,
              Scene_interface* scene_interface,
              Messages_interface*)
    {
        scene = scene_interface;
        this->mw = mw;
        QAction *actionConvexHull = new QAction("Convex Hull", mw);
        actionConvexHull->setProperty("subMenuName","3D Convex Hulls");
        connect(actionConvexHull, SIGNAL(triggered()), this, SLOT(on_actionConvexHull_triggered()));
        _actions <<actionConvexHull;
    }

  QList<QAction*> actions()const {return _actions;}

  bool applicable(QAction*) const {
    return
      qobject_cast<Scene_polyhedron_item*>(scene->item(scene->mainSelectionIndex())) ||
      qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex())) ||
      qobject_cast<Scene_polylines_item*>(scene->item(scene->mainSelectionIndex())) ||
      qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex())) ||
      qobject_cast<Scene_polyhedron_selection_item*>(scene->item(scene->mainSelectionIndex()));
  }

public Q_SLOTS:
  void on_actionConvexHull_triggered();
private:
  QList<QAction*> _actions;
  Scene_interface* scene;
  QMainWindow* mw;
}; // end Polyhedron_demo_convex_hull_plugin

// for transform iterator
struct Get_point {
  typedef const Polyhedron::Point_3& result_type;
  result_type operator()(const Polyhedron::Vertex_handle v) const
  { return v->point(); }
};

void Polyhedron_demo_convex_hull_plugin::on_actionConvexHull_triggered()
{
  const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();

  Scene_polyhedron_item* poly_item =
    qobject_cast<Scene_polyhedron_item*>(scene->item(index));

  Scene_points_with_normal_item* pts_item =
    qobject_cast<Scene_points_with_normal_item*>(scene->item(index));

  Scene_polylines_item* lines_item =
    qobject_cast<Scene_polylines_item*>(scene->item(index));

  Scene_polyhedron_selection_item* selection_item =
    qobject_cast<Scene_polyhedron_selection_item*>(scene->item(index));

  Scene_surface_mesh_item* sm_item =
    qobject_cast<Scene_surface_mesh_item*>(scene->item(index));

  if(poly_item || pts_item || lines_item || selection_item || sm_item)
  {
    // wait cursor
    QApplication::setOverrideCursor(Qt::WaitCursor);

    QTime time;
    time.start();
    std::cout << "Convex hull...";

    // add convex hull as new polyhedron
    SMesh *pConvex_hull  = new SMesh;
    if(selection_item) {
      CGAL::convex_hull_3(
        boost::make_transform_iterator(selection_item->selected_vertices.begin(), Get_point()),
        boost::make_transform_iterator(selection_item->selected_vertices.end(), Get_point()),
        *pConvex_hull);
    }
    else if ( poly_item ){
      Polyhedron* pMesh = poly_item->polyhedron();
      CGAL::convex_hull_3(pMesh->points_begin(),pMesh->points_end(),*pConvex_hull);
    }
    else if ( sm_item ){
      SMesh* pMesh = sm_item->polyhedron();
      typedef boost::property_map<SMesh,CGAL::vertex_point_t>::type Vpmap;

      typedef CGAL::Property_map_to_unary_function<Vpmap> Vpmap_fct;
      Vpmap vpm = get(CGAL::vertex_point,*pMesh);

      Vpmap_fct v2p(vpm);
      boost::graph_traits<SMesh>::vertex_iterator b,e;
      boost::tie(b,e) = vertices(*pMesh);

      CGAL::convex_hull_3(boost::make_transform_iterator(b,v2p),
                          boost::make_transform_iterator(e,v2p),
                          *pConvex_hull);

    }
    else{
      if (pts_item)
        CGAL::convex_hull_3(pts_item->point_set()->points().begin(),
                            pts_item->point_set()->points().end(),
                            *pConvex_hull);
      else{
        std::size_t nb_points=0;
        for(std::list<std::vector<Kernel::Point_3> >::const_iterator it = lines_item->polylines.begin();
            it != lines_item->polylines.end();
            ++it)  nb_points+=it->size();

        std::vector<Kernel::Point_3> all_points;
        all_points.reserve( nb_points );

        for(std::list<std::vector<Kernel::Point_3> >::const_iterator it = lines_item->polylines.begin();
            it != lines_item->polylines.end();
            ++it)  std::copy(it->begin(), it->end(),std::back_inserter( all_points ) );

        CGAL::convex_hull_3(all_points.begin(),all_points.end(),*pConvex_hull);
      }
    }
    std::cout << "ok (" << time.elapsed() << " ms)" << std::endl;

    if(mw->property("is_polyhedron_mode").toBool()){
      Polyhedron *poly = new Polyhedron;
      CGAL::copy_face_graph(*pConvex_hull,*poly);
      delete pConvex_hull;

      Scene_polyhedron_item* new_item = new Scene_polyhedron_item(poly);
      new_item->setName(tr("%1 (convex hull)").arg(scene->item(index)->name()));
      new_item->setColor(Qt::magenta);
      new_item->setRenderingMode(FlatPlusEdges);
      scene->addItem(new_item);
    } else {
       Scene_surface_mesh_item* new_item = new Scene_surface_mesh_item(pConvex_hull);
       new_item->setName(tr("%1 (convex hull)").arg(scene->item(index)->name()));
       new_item->setColor(Qt::magenta);
       new_item->setRenderingMode(FlatPlusEdges);
       scene->addItem(new_item);
    }

    // default cursor
    QApplication::restoreOverrideCursor();
  }

}

#include "Convex_hull_plugin.moc"


#include <boost/program_options.hpp>
#include <PDBClient.h>
#include <GenericWork.h>
#include "sharedLibraries/headers/MatrixBlock.h"
#include "sharedLibraries/headers/MatrixScanner.h"
#include "sharedLibraries/headers/MatrixMultiplyJoin.h"
#include "sharedLibraries/headers/MatrixMultiplyAggregation.h"
#include "sharedLibraries/headers/MatrixWriter.h"

using namespace pdb;
using namespace pdb::matrix;
namespace po = boost::program_options;

void initMatrix(
    pdb::PDBClient &pdbClient,
    const std::string &set,
    const size_t &blockSize,
    const uint32_t &matrixRows,
    const uint32_t &matrixColumns,
    const uint32_t &numRows,
    const uint32_t &numCols) {
  // A matrix consists of numRows x numCols grid of chunks and each
  // chunk consists of the appropriate number of values to make the whole
  // matrix matrixRows x matrixColumns.

  // For each chunk, add the MatrixBlocks. Once the allocation block is full
  // or the all MatrixBlocks have been created, send the data to pdbClient.
  uint32_t numChunks = numRows*numCols;
  uint32_t chunk = 0;
  while(chunk != numChunks) {

    // make the allocation block
    const pdb::UseTemporaryAllocationBlock tempBlock{blockSize * 1024 * 1024};

    // put the chunks here
    Handle<Vector<Handle<MatrixBlock>>> data = pdb::makeObject<Vector<Handle<MatrixBlock>>>();

    try {
      for(; chunk != numChunks; chunk++) {
        uint32_t r = chunk % numRows;
        uint32_t c = chunk / numRows;

        uint32_t numRowsInChunk = matrixRows    / numRows + (r < matrixRows    % numRows ? 1 : 0);
        uint32_t numColsInChunk = matrixColumns / numCols + (c < matrixColumns % numCols ? 1 : 0);

        // allocate a matrix block
        Handle<MatrixBlock> myInt = makeObject<MatrixBlock>(r, c, numRowsInChunk, numColsInChunk);

        // init the values
        float *vals = myInt->data->data->c_ptr();
        for (int v = 0; v < numRowsInChunk*numColsInChunk; ++v) {
          vals[v] = 1.0f * (float) v;
        }

        data->push_back(myInt);
      }
    } catch (pdb::NotEnoughSpace &n) {
    }

    // init the records
    getRecord(data);

    // send the data
    pdbClient.sendData<MatrixBlock>("myData", set, data);
  }
}

void printMatrix(
    pdb::PDBClient& pdbClient,
    std::string const& dbName,
    std::string const& setName) {

  auto it = pdbClient.getSetIterator<MatrixBlock>(dbName, setName);

  while(it->hasNextRecord()) {

        // grab the record
        auto r = it->getNextRecord();

        // write out the values
        float *values = r->data->data->c_ptr();
        for(int i = 0; i < r->data->numRows; ++i) {
            for(int j = 0; j < r->data->numCols; ++j) {
                std::cout << values[i * r->data->numCols + j] << ", ";
            }
            std::cout << "\n";
        }

        std::cout << "\n\n";
    }
}

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

  po::options_description desc{"Options"};

  size_t blockSize;
  uint32_t matrixRowsA, matrixRowsB, matrixColumnsB;
  uint32_t numRowsA, numRowsB, numColumnsB;

  // specify the options
  desc.add_options()("help,h", "Help screen");
  desc.add_options()("blockSize", po::value<size_t>(&blockSize)->default_value(1024),
      "Block size for allocation");
  desc.add_options()("matrixRowsA", po::value<uint32_t>(&matrixRowsA)->default_value(10000),
      "Number of rows in matrix A");
  desc.add_options()("matrixRowsB", po::value<uint32_t>(&matrixRowsB)->default_value(10000),
      "Number of columns in matrix A and number of rows in matrix B");
  desc.add_options()("matrixColumnsB", po::value<uint32_t>(&matrixColumnsB)->default_value(10000),
      "Number of columns in matrix B");
  desc.add_options()("numRowsA", po::value<uint32_t>(&numRowsA)->default_value(200),
      "Number of rows in each chunk of matrix A");
  desc.add_options()("numRowsB", po::value<uint32_t>(&numRowsB)->default_value(200),
      "Number of columns in each chunk of matrix A and number of rows in each chunk of matrix B");
  desc.add_options()("numColumnsB", po::value<uint32_t>(&numColumnsB)->default_value(200),
      "Number of columns in each chunk of matrix B");

  // grab the options
  po::variables_map vm;
  store(parse_command_line(argc, argv, desc), vm);
  notify(vm);

  // did somebody ask for help?
  if (vm.count("help")) {
    std::cout << desc << '\n';
    return 0;
  }

  // make a client
  pdb::PDBClient pdbClient(8108, "localhost");

  /// 1. Register the classes

  // now, register a type for user data
  pdbClient.registerType("libraries/libMatrixBlock.so");
  pdbClient.registerType("libraries/libMatrixBlockData.so");
  pdbClient.registerType("libraries/libMatrixBlockMeta.so");
  pdbClient.registerType("libraries/libMatrixMultiplyAggregation.so");
  pdbClient.registerType("libraries/libMatrixMultiplyJoin.so");
  pdbClient.registerType("libraries/libMatrixScanner.so");
  pdbClient.registerType("libraries/libMatrixWriter.so");

  /// 2. Create the set

  // now, create a new database
  pdbClient.createDatabase("myData");

  // now, create the input and output sets
  pdbClient.createSet<MatrixBlock>("myData", "A");
  pdbClient.createSet<MatrixBlock>("myData", "B");
  pdbClient.createSet<MatrixBlock>("myData", "C");

  /// 3. Fill in the data (single threaded)

  initMatrix(pdbClient, "A", blockSize, matrixRowsA, matrixRowsB,    numRowsA, numRowsB);
  initMatrix(pdbClient, "B", blockSize, matrixRowsB, matrixColumnsB, numRowsB, numColumnsB);

  /// 4. Make query graph an run query

  // for allocations
  const UseTemporaryAllocationBlock tempBlock{blockSize * 1024 * 1024};

  Handle <Computation> readA = makeObject <MatrixScanner>("myData", "A");
  Handle <Computation> readB = makeObject <MatrixScanner>("myData", "B");
  Handle <Computation> join = makeObject <MatrixMultiplyJoin>();
  join->setInput(0, readA);
  join->setInput(1, readB);
  Handle<Computation> myAggregation = makeObject<MatrixMultiplyAggregation>();
  myAggregation->setInput(join);
  Handle<Computation> myWriter = makeObject<MatrixWriter>("myData", "C");
  myWriter->setInput(myAggregation);

  //TODO this is just a preliminary version of the execute computation before we add back the TCAP generation
  pdbClient.executeComputations({ myWriter });

  /// 5. Get the set and print
  printMatrix(pdbClient, "myData", "C");

  // shutdown the server
  pdbClient.shutDownServer();

  return 0;
}


#ifndef constraint_hpp
#define constraint_hpp

#include <memory>
#include <vector>
#include <Eigen/Core>

namespace elasty
{
    struct Particle;

    enum class ConstraintType { Bilateral, Unilateral };

    class Constraint
    {
    public:

        Constraint(const std::vector<std::shared_ptr<Particle>>& particles,
                   const double stiffness) :
        m_stiffness(stiffness),
        m_particles(particles)
        {
        }

        /// \brief Calculates the constraint function value C(x).
        virtual double calculateValue() = 0;

        /// \brief Calculate the derivative of the constraint function
        /// grad C(x).
        /// \details As constraints can have different vector sizes, it will
        /// store the result to the passed raw buffer that should be allocated
        /// in the caller, rather than returning a dynamically allocated
        /// variable-length vector. This method does not check whether the
        /// buffer is adequately allocated, or not.
        virtual void calculateGrad(double* grad_C) = 0;

        /// \brief Manipulate the associated particles by projecting them to the
        /// constraint manifold.
        /// \details This method should be called by the core engine. As this
        /// method directly updates the predicted positions of the associated
        /// particles, it is intended to be used in a Gauss-Seidel-style solver.
        virtual void projectParticles() = 0;

        /// \brief Return the constraint type (i.e., either unilateral or
        /// bilateral).
        virtual ConstraintType getType() = 0;

        /// \brief Stiffness of this constraint, which should be in [0, 1].
        double m_stiffness;

    protected:

        /// \brief Associated particles.
        /// \details The number of particles is (in most cases) solely
        /// determined in each constraint. For example, a distance constraint
        /// need to have exactly two particles. Some special constraints
        /// (e.g., shape-matching constraint) could have a variable number of
        /// particles.
        std::vector<std::shared_ptr<Particle>> m_particles;
    };

    class BendingConstraint final : public Constraint
    {
    public:

        BendingConstraint(const std::shared_ptr<Particle> p_0,
                          const std::shared_ptr<Particle> p_1,
                          const std::shared_ptr<Particle> p_2,
                          const std::shared_ptr<Particle> p_3,
                          const double stiffness,
                          const double dihedral_angle);

        double calculateValue() override;
        void calculateGrad(double* grad_C) override;
        void projectParticles() override;
        ConstraintType getType() override { return ConstraintType::Bilateral; }

    private:

        const Eigen::Matrix<double, 12, 1> m_inv_M;
        const double m_dihedral_angle;
    };

    class DistanceConstraint final : public Constraint
    {
    public:

        DistanceConstraint(const std::shared_ptr<Particle> p_0,
                           const std::shared_ptr<Particle> p_1,
                           const double stiffness,
                           const double d);

        double calculateValue() override;
        void calculateGrad(double* grad_C) override;
        void projectParticles() override;
        ConstraintType getType() override { return ConstraintType::Bilateral; }

    private:

        const Eigen::Matrix<double, 6, 1> m_inv_M;
        const double m_d;
    };

    class EnvironmentalCollisionConstraint final : public Constraint
    {
    public:

        EnvironmentalCollisionConstraint(const std::shared_ptr<Particle> p_0,
                                         const double stiffness,
                                         const Eigen::Vector3d& n,
                                         const double d);

        double calculateValue() override;
        void calculateGrad(double* grad_C) override;
        void projectParticles() override;
        ConstraintType getType() override { return ConstraintType::Unilateral; }

    private:

        const Eigen::Matrix<double, 3, 1> m_inv_M;
        const Eigen::Vector3d m_n;
        const double m_d;
    };

    class FixedPointConstraint final : public Constraint
    {
    public:

        FixedPointConstraint(const std::shared_ptr<Particle> p_0,
                             const double stiffness,
                             const Eigen::Vector3d& point);

        double calculateValue() override;
        void calculateGrad(double* grad_C) override;
        void projectParticles() override;
        ConstraintType getType() override { return ConstraintType::Bilateral; }

    private:

        const Eigen::Matrix<double, 3, 1> m_inv_M;
        const Eigen::Vector3d m_point;
    };

    class IsometricBendingConstraint final : public Constraint
    {
    public:

        IsometricBendingConstraint(const std::shared_ptr<Particle> p_0,
                                   const std::shared_ptr<Particle> p_1,
                                   const std::shared_ptr<Particle> p_2,
                                   const std::shared_ptr<Particle> p_3,
                                   const double stiffness);

        double calculateValue() override;
        void calculateGrad(double* grad_C) override;
        void projectParticles() override;
        ConstraintType getType() override { return ConstraintType::Bilateral; }

    private:

        const Eigen::Matrix<double, 12, 1> m_inv_M;
        Eigen::Matrix4d m_Q;
    };
}

#endif /* constraint_hpp */


#include <blitz/array.h>

int main() {
  using namespace blitz;
  Array<int,2> A(7, 7);
  firstIndex i;
  secondIndex j;
  A=i+10*j;
  cout << A << endl << A.base() << endl << A.ubound() << endl << endl;
  A.reindexSelf(TinyVector<int,2>(2,3));
  cout << A << endl << A.base() << endl << A.ubound() << endl << endl;
}


#include "dynet/nodes.h"
#include "dynet/dynet.h"
#include "dynet/training.h"
#include "dynet/gpu-ops.h"
#include "dynet/expr.h"
#include "dynet/mp.h"
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include <iostream>
#include <fstream>

using namespace std;
using namespace dynet;
using namespace dynet::mp;

struct Datum {
  Datum() {}
  Datum(const vector<dynet::real>& x, const dynet::real y) : x(x), y(y) {}

  vector<dynet::real> x;
  dynet::real y;
};

class XorModel {
public:
  XorModel(const unsigned hidden_size, Model& dynet_model) : pcg(nullptr) {
    p_W = dynet_model.add_parameters({hidden_size, 2});
    p_b = dynet_model.add_parameters({hidden_size});
    p_V = dynet_model.add_parameters({1, hidden_size});
    p_a = dynet_model.add_parameters({1});
  }

  void new_graph(ComputationGraph& cg) {
    W = parameter(cg, p_W);
    b = parameter(cg, p_b);
    V = parameter(cg, p_V);
    a = parameter(cg, p_a);
    pcg = &cg;
  }

  Expression compute_loss(const Datum& datum) {
    Expression x = input(*pcg, {2}, &datum.x);
    Expression y = input(*pcg, &datum.y);

    Expression h = tanh(W*x + b);
    Expression y_pred = V*h + a;
    Expression loss_expr = squared_distance(y_pred, y);
    return loss_expr;
  }

private:
  XorModel() : pcg(nullptr) {}

  Parameter p_W, p_b, p_V, p_a;
  Expression W, b, V, a;
  ComputationGraph* pcg;

  friend class boost::serialization::access;
  template<class Archive>
  void serialize(Archive& ar, const unsigned int) {
    ar & p_W & p_b & p_V & p_a;
  }
};

void serialize(const XorModel* const xor_model, const Model& dynet_model, const Trainer* const trainer) {
  // Remove existing stdout output
  int r = ftruncate(fileno(stdout), 0);
  if (r != 0) {}

  // Move the cursor to the beginning of the stdout stream
  fseek(stdout, 0, SEEK_SET);

  // Dump the model to stdout
  boost::archive::text_oarchive oa(cout);
  oa & dynet_model;
  oa & xor_model;
  oa & trainer;
}

void deserialize(const string& filename, XorModel* xor_model, Model& dynet_model, Trainer* trainer) {
  ifstream in(filename.c_str());
  boost::archive::text_iarchive ia(in);
  ia & dynet_model;
  ia & xor_model;
  ia & trainer;
  in.close();
}

class SufficientStats {
public:
  dynet::real loss;
  unsigned example_count;

  SufficientStats() : loss(), example_count() {}

  SufficientStats(dynet::real loss, unsigned example_count) : loss(loss), example_count(example_count) {}

  SufficientStats& operator+=(const SufficientStats& rhs) {
    loss += rhs.loss;
    example_count += rhs.example_count;
    return *this;
  }

  friend SufficientStats operator+(SufficientStats lhs, const SufficientStats& rhs) {
    lhs += rhs;
    return lhs;
  }

  bool operator<(const SufficientStats& rhs) {
    return loss < rhs.loss;
  }

  friend std::ostream& operator<< (std::ostream& stream, const SufficientStats& stats) {
    return stream << exp(stats.loss / stats.example_count) << " (" << stats.loss << " over " << stats.example_count << " examples)";
  }
};

class Learner : public ILearner<Datum, SufficientStats> {
public:
  Learner(XorModel* xor_model, Model& dynet_model, const Trainer* const trainer, bool quiet) : xor_model(xor_model), dynet_model(dynet_model), trainer(trainer), quiet(quiet) {}
  ~Learner() {}
  SufficientStats LearnFromDatum(const Datum& datum, bool learn) {
    ComputationGraph cg;
    xor_model->new_graph(cg);
    Expression loss_expr = xor_model->compute_loss(datum);
    dynet::real loss = as_scalar(loss_expr.value());

    if (learn) {
      cg.backward(loss_expr);
    }
    return SufficientStats(loss, 1);
  }

  void SaveModel() {
    if (!quiet) {
      serialize(xor_model, dynet_model, trainer);
    }
  }

private:
  XorModel* xor_model;
  Model& dynet_model;
  const Trainer* const trainer;
  bool quiet;
};

int main(int argc, char** argv) {
  dynet::initialize(argc, argv, true);

  // parameters
  const unsigned num_cores = 4;
  const unsigned ITERATIONS = 1000;
  Model dynet_model;
  XorModel* xor_model = nullptr;
  Trainer* trainer = nullptr;

  if (argc == 2) {
    // Load the model and parameters from file if given.
    deserialize(argv[1], xor_model, dynet_model, trainer);
  }
  else {
    // Otherwise, just create a new model.
    const unsigned HIDDEN_SIZE = 8;
    xor_model = new XorModel(HIDDEN_SIZE, dynet_model);
    trainer = new SimpleSGDTrainer(dynet_model);
  }

  vector<Datum> data(4);
  data[0] = Datum({0, 0}, 0);
  data[1] = Datum({0, 1}, 1);
  data[2] = Datum({1, 0}, 1);
  data[3] = Datum({1, 1}, 0);

  Learner learner(xor_model, dynet_model, trainer, false);
  for (unsigned i = 0; i < ITERATIONS; ++i) {
    SufficientStats ss = run_mp_minibatch<Datum>(num_cores, &learner, data);
    trainer->update(1.0 / data.size());
    cout << ss << endl;
  }
}


/**
 * @file tests/augmented_rnns_tasks_test.cpp
 * @author Konstantin Sidorov
 *
 * Tests the rtasks for augmented network 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.
 */
#include <vector>
#include <algorithm>
#include <utility>

#include <mlpack/core.hpp>

#include <mlpack/core/data/binarize.hpp>

#include <mlpack/methods/ann/augmented/tasks/copy.hpp>
#include <mlpack/methods/ann/augmented/tasks/sort.hpp>
#include <mlpack/methods/ann/augmented/tasks/add.hpp>
#include <mlpack/methods/ann/augmented/tasks/score.hpp>

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

using std::vector;
using std::pair;
using std::make_pair;

using namespace mlpack::ann::augmented;
using namespace mlpack::ann::augmented::tasks;
using namespace mlpack::ann::augmented::scorers;

using mlpack::data::Binarize;

// The dummy model that simply copies the sequence
// the required number of times
// (yes, no ML here, we're unit testing :)
class HardCodedCopyModel
{
 public:
  HardCodedCopyModel() : nRepeats(1) {}

  void Train(arma::field<arma::mat>& predictors,
             arma::field<arma::mat>& labels)
  {
    arma::mat input = predictors.at(0);
    arma::mat output = labels.at(0);
    size_t zeroCnt = 0, oneCnt = 0;
    for (size_t i = 1; i < input.n_rows; i += 2)
    {
      size_t& addVar = (input.at(i, 0) == 0) ? zeroCnt : oneCnt;
      ++addVar;
    }
    assert(oneCnt % zeroCnt == 0);
    nRepeats = oneCnt / zeroCnt;
  }

  void Predict(arma::mat& predictors,
               arma::mat& labels)
  {
    size_t seqLen = (predictors.n_rows / 2) / (nRepeats + 1);
    size_t outputLen = nRepeats * seqLen;
    assert(2 * (seqLen + outputLen) == predictors.n_rows);
    labels.zeros(predictors.n_rows / 2, 1);
    for (size_t i = 0; i < outputLen; ++i)
    {
      labels.at(seqLen+i) = predictors.at(2 * (i % seqLen));
    }
  }

  void Predict(arma::field<arma::mat>& predictors,
               arma::field<arma::mat>& labels)
  {
    size_t sz = predictors.n_elem;
    labels = arma::field<arma::mat>(sz);
    for (size_t i = 0; i < sz; ++i)
    {
      Predict(predictors.at(i), labels.at(i));
    }
  }

 private:
  size_t nRepeats;
};

// The dummy model that simply sorts the sequence.
class HardCodedSortModel
{
 public:
  HardCodedSortModel(size_t bitLen) : bitLen(bitLen) {}

  void Train(arma::field<arma::mat>& predictors,
             arma::field<arma::mat>& labels)
  {
    mlpack::Log::Assert(predictors.n_elem == labels.n_elem);
  }

  void Predict(arma::mat& predictors,
               arma::mat& labels)
  {
    predictors = predictors.t();
    predictors.reshape(bitLen, predictors.n_elem / bitLen);
    size_t len = predictors.n_cols;
    labels.zeros(bitLen, len);
    vector<pair<int, int>> vals(len);
    for (size_t j = 0; j < len; ++j)
    {
      int val = 0;
      for (size_t k = 0; k < bitLen; ++k)
      {
        val <<= 1;
        val += predictors.at(k, j);
      }
      vals[j] = make_pair(val, j);
    }
    sort(vals.begin(), vals.end());
    for (size_t j = 0; j < len; ++j)
    {
      labels.col(j) = predictors.col(vals[j].second);
    }
    labels.reshape(predictors.n_elem, 1);
  }

  void Predict(arma::field<arma::mat>& predictors,
               arma::field<arma::mat>& labels)
  {
    size_t sz = predictors.n_elem;
    labels = arma::field<arma::mat>(sz);
    for (size_t i = 0; i < sz; ++i)
    {
      Predict(predictors.at(i), labels.at(i));
    }
  }

 private:
  size_t bitLen;
};

// The dummy model that simply add two binary numbers.
class HardCodedAddModel
{
 public:
  HardCodedAddModel() {}

  void Train(arma::field<arma::mat>& /* predictors */,
             arma::field<arma::mat>& /* labels */)
  {
    return;
  }

  void Predict(arma::mat& predictors,
               arma::mat& labels)
  {
    assert(predictors.n_elem % 3 == 0);
    predictors = predictors.t();
    predictors.reshape(3, predictors.n_elem / 3);
    assert(predictors.n_rows == 3);
    int num_A = 0, num_B = 0;
    bool num = false; // True iff we have already seen the separating symbol.
    size_t cnt = 0;
    for (size_t i = 0; i < predictors.n_cols; ++i)
    {
      double digit = arma::as_scalar(arma::find(1 == predictors.col(i), 1));
      if (digit != 0 && digit != 1)
      {
        // We should not see two separators
        // since we are adding *two* numbers in the task
        assert(!num);
        num = true;
        cnt = 0;
      }
      else
      {
        if (num)
        {
          num_B += static_cast<int>(digit) << cnt;
        }
        else
        {
          num_A += static_cast<int>(digit) << cnt;
        }
        ++cnt;
      }
    }
    int total = num_A + num_B;
    vector<int> binary_seq;
    while (total > 0)
    {
      binary_seq.push_back(total & 1);
      total >>= 1;
    }
    if (binary_seq.empty())
    {
      assert(num_A + num_B == 0);
      binary_seq.push_back(0);
    }
    size_t totLen = binary_seq.size();
    labels = arma::zeros(3, totLen);
    for (size_t j = 0; j < totLen; ++j)
    {
      labels.at(binary_seq[j], j) = 1;
    }
    labels.reshape(predictors.n_elem, 1);
  }

  void Predict(
      arma::field<arma::mat>& predictors,
      arma::field<arma::mat>& labels)
  {
    size_t sz = predictors.n_elem;
    labels = arma::field<arma::mat>(sz);
    for (size_t i = 0; i < sz; ++i)
    {
      Predict(predictors.at(i), labels.at(i));
    }
  }
};

BOOST_AUTO_TEST_SUITE(AugmentedRNNsTasks);

// Test of CopyTask instance generator.
// The data from generator is fed to the dummy hard-coded model above
// that should be able to solve the task perfectly.
BOOST_AUTO_TEST_CASE(CopyTaskTest)
{
  // Check the setup on various lengths...
  for (size_t maxLen = 2; maxLen <= 16; ++maxLen)
  {
    // .. and various numbers of repetitions.
    for (size_t nRepeats = 1; nRepeats <= 10; ++nRepeats)
    {
      CopyTask task(maxLen, nRepeats);
      arma::field<arma::mat> trainPredictor, trainResponse;
      task.Generate(trainPredictor, trainResponse, 8);
      arma::field<arma::mat> testPredictor, testResponse;
      task.Generate(testPredictor, testResponse, 8);
      HardCodedCopyModel model;
      model.Train(trainPredictor, trainResponse);
      arma::field<arma::mat> predResponse;
      model.Predict(testPredictor, predResponse);
      // A single failure is a failure.
      BOOST_REQUIRE_GE(SequencePrecision<arma::mat>(testResponse, predResponse),
                       0.99);
    }
  }
}

// Test of SortTask instance generator.
// The data from generator is fed to the dummy hard-coded model above
// that should be able to solve the task perfectly.
BOOST_AUTO_TEST_CASE(SortTaskTest)
{
  size_t bitLen = 5;
  for (size_t maxLen = 2; maxLen <= 16; ++maxLen)
  {
    SortTask task(maxLen, bitLen);
    arma::field<arma::mat> trainPredictor, trainResponse;
    task.Generate(trainPredictor, trainResponse, 8);
    arma::field<arma::mat> testPredictor, testResponse;
    task.Generate(testPredictor, testResponse, 8);
    HardCodedSortModel model(bitLen);
    model.Train(trainPredictor, trainResponse);
    arma::field<arma::mat> predResponse;
    model.Predict(testPredictor, predResponse);
    // A single failure is a failure.
    BOOST_REQUIRE_GE(SequencePrecision<arma::mat>(testResponse, predResponse),
                     0.99);
  }
}

// Test of AddTask instance generator.
// The data from generator is fed to the dummy hard-coded model above
// that should be able to solve the task perfectly.
BOOST_AUTO_TEST_CASE(AddTaskTest)
{
  for (size_t bitLen = 2; bitLen <= 16; ++bitLen)
  {
    AddTask task(bitLen);
    arma::field<arma::mat> trainPredictor, trainResponse;
    task.Generate(trainPredictor, trainResponse, 8);
    arma::field<arma::mat> testPredictor, testResponse;
    task.Generate(testPredictor, testResponse, 8);
    HardCodedAddModel model;
    model.Train(trainPredictor, trainResponse);
    arma::field<arma::mat> predResponse;
    model.Predict(testPredictor, predResponse);
    // A single failure is a failure.
    BOOST_REQUIRE_GE(SequencePrecision<arma::mat>(testResponse, predResponse),
                     0.99);
  }
}

BOOST_AUTO_TEST_SUITE_END();


// Distributed under the MIT License.
// See LICENSE.txt for details.

#include "Framework/TestingFramework.hpp"

#include <array>
#include <boost/rational.hpp>
#include <cstddef>
#include <vector>

#include "Domain/Structure/ElementId.hpp"  // IWYU pragma: keep
#include "Domain/Structure/InitialElementIds.hpp"
#include "Helpers/Domain/DomainTestHelpers.hpp"
#include "Utilities/ConstantExpressions.hpp"
#include "Utilities/Gsl.hpp"

namespace {
template <size_t VolumeDim>
void test_initial_element_ids(
    const std::vector<ElementId<VolumeDim>>& element_ids,
    const std::vector<std::array<size_t, VolumeDim>>& initial_refinement_levels,
    size_t grid_index) {
  size_t expected_number_of_elements = 0;
  for (const auto& initial_refinement_levels_of_block :
       initial_refinement_levels) {
    size_t expected_number_of_elements_in_block = 1;
    for (size_t d = 0; d < VolumeDim; ++d) {
      expected_number_of_elements_in_block *=
          two_to_the(gsl::at(initial_refinement_levels_of_block, d));
    }
    expected_number_of_elements += expected_number_of_elements_in_block;
  }
  CHECK(expected_number_of_elements == element_ids.size());
  const boost::rational<size_t> expected_logical_volume_of_blocks(
      initial_refinement_levels.size());
  boost::rational<size_t> logical_volume_of_blocks(0);
  for (const auto& element_id : element_ids) {
    logical_volume_of_blocks += fraction_of_block_volume(element_id);
    CHECK(element_id.grid_index() == grid_index);
  }
  CHECK(expected_logical_volume_of_blocks == logical_volume_of_blocks);
}
}  // namespace

SPECTRE_TEST_CASE("Unit.Domain.Structure.InitialElementIds", "[Domain][Unit]") {
  const std::vector<std::array<size_t, 1>> initial_refinement_levels_1d{{{2}},
                                                                        {{3}}};
  const auto element_ids_1d = initial_element_ids(initial_refinement_levels_1d);
  test_initial_element_ids(element_ids_1d, initial_refinement_levels_1d, 0);
  test_initial_element_ids(initial_element_ids(initial_refinement_levels_1d, 3),
                           initial_refinement_levels_1d, 3);

  const std::vector<std::array<size_t, 2>> initial_refinement_levels_2d{
      {{2, 0}}, {{3, 1}}};
  const auto element_ids_2d = initial_element_ids(initial_refinement_levels_2d);
  test_initial_element_ids(element_ids_2d, initial_refinement_levels_2d, 0);
  test_initial_element_ids(initial_element_ids(initial_refinement_levels_2d, 3),
                           initial_refinement_levels_2d, 3);

  const std::vector<std::array<size_t, 3>> initial_refinement_levels_3d{
      {{4, 2, 1}}, {{0, 3, 2}}};
  const auto element_ids_3d = initial_element_ids(initial_refinement_levels_3d);
  test_initial_element_ids(element_ids_3d, initial_refinement_levels_3d, 0);
  test_initial_element_ids(initial_element_ids(initial_refinement_levels_3d, 3),
                           initial_refinement_levels_3d, 3);
}