#include "eval.hpp"
#include <Superpixels.hpp>
#include <boost/math/constants/constants.hpp>
#include <iostream>
#include <numeric>

namespace dasp {
namespace eval {

float Area(const Superpixels& u)
{
	// compute labels
	slimage::Image1i labels = u.ComputeLabels();
	std::vector<unsigned int> sp_area(u.clusterCount());
	for(std::size_t i=0; i<labels.size(); i++) {
		int label = labels[i];
		if(label == -1) {
			continue;
		}
		// compute pixel area
		sp_area[label] ++;
	}
	// mean
	return static_cast<float>(std::accumulate(sp_area.begin(), sp_area.end(), 0.0f)) / static_cast<float>(sp_area.size());
}

float Area3D(const Superpixels& u)
{
	// compute labels
	slimage::Image1i labels = u.ComputeLabels();
	std::vector<float> sp_area(u.clusterCount());
	for(std::size_t i=0; i<labels.size(); i++) {
		int label = labels[i];
		if(label == -1) {
			continue;
		}
		// 3D
		const Point& p = u.points[i];
		float size_of_a_px = p.depth() / u.opt.camera.focal;
		float area_of_a_px = size_of_a_px*size_of_a_px / p.computeCircularity();
		// compute pixel area
		sp_area[label] += area_of_a_px;
	}
	// mean
	return std::accumulate(sp_area.begin(), sp_area.end(), 0.0f) / static_cast<float>(sp_area.size());
}

slimage::Image1i MarkBoundary(const slimage::Image1i& labels)
{
	slimage::Image1i boundary(labels.dimensions(), slimage::Pixel1i{1});
	for(unsigned int y=1; y+1<boundary.height(); y++) {
		for(unsigned int x=1; x+1<boundary.width(); x++) {
			int q = labels(x, y);
			int qp0 = labels(x+1, y  );
			int qm0 = labels(x-1, y  );
			int q0p = labels(x  , y+1);
			int q0m = labels(x  , y-1);
			if(q == qp0 && q == qm0 && q == q0p && q == q0m) {
				boundary(x,y) = 0;
			}
		}
	}
	return boundary;
}

template<typename K>
std::pair<float,std::vector<float>> IsoperimetricQuotientImpl(const std::vector<std::pair<K,K>>& sp_area_length)
{
	// per cluster: v_i = 4*pi*A_i/L_i^2
	// total: sum_i v_i*A_i
	std::vector<float> ipq(sp_area_length.size());
	float ipq_total = 0.0f;
	float num_total = 0.0f;
	for(std::size_t i=0; i<ipq.size(); i++) {
		const auto& q = sp_area_length[i];
		float area = static_cast<float>(q.first);
		float len = static_cast<float>(q.second);
		float x = 4.0f*boost::math::constants::pi<float>() * area / (len*len);
		ipq[i] = x;
		ipq_total += x*area;
		num_total += area;
//		std::cout << "area=" << area << ", len=" << len << ", ipq=" << x << std::endl;
	}
	return {ipq_total / num_total, ipq};
}

std::pair<float,std::vector<float>> IsoperimetricQuotient(const Superpixels& u)
{
	// compute labels
	slimage::Image1i labels = u.ComputeLabels();
	// compute boundary image
	slimage::Image1i boundary = MarkBoundary(labels);
	// compute total pixel count and number of boundary pixels for each cluster
	std::vector<std::pair<int,int>> ipq_els(u.clusterCount(), {0,0});
	for(std::size_t i=0; i<labels.size(); i++) {
		int label = labels[i];
		if(label == -1) {
			continue;
		}
		if(boundary[i] == 1) {
			ipq_els[label].second ++;
		}
		else {
			ipq_els[label].first ++;
		}
	}
	// finalize
	return IsoperimetricQuotientImpl(ipq_els);
}

std::pair<float,std::vector<float>> IsoperimetricQuotient3D(const Superpixels& u)
{
	// compute labels
	slimage::Image1i labels = u.ComputeLabels();
	// compute boundary image
	slimage::Image1i boundary = MarkBoundary(labels);
	// compute total pixel count and number of boundary pixels for each cluster
	std::vector<std::pair<float,float>> ipq_els(u.clusterCount(), {0.0f,0.0f});
//	std::vector<std::vector<std::size_t>> seg_bnd(u.clusterCount());
	for(std::size_t i=0; i<labels.size(); i++) {
		int label = labels[i];
		if(label == -1) {
			continue;
		}
		// 3D
		const Point& p = u.points[i];
		float size_of_a_px = p.depth() / u.opt.camera.focal;
		float gamma = p.computeCircularity();
		float px_area = size_of_a_px*size_of_a_px / gamma;
		float px_len = (1.571 - 0.571 * gamma) * size_of_a_px;
		// compute pixel area
		ipq_els[label].first += px_area;
		// compute pixel diameter
		if(boundary[i] == 1) {
//			seg_bnd[label].push_back(i);
			ipq_els[label].second += px_len;
		}
	}
// 	// compute segment boundary length
// 	unsigned int w = u.width();
// 	for(unsigned int k=0; k<seg_bnd.size(); k++) {
// 		float L = 0.0f;
// 		const std::vector<std::size_t>& B = seg_bnd[k];
// 		for(unsigned int i=0; i<B.size(); i++) {
// 			unsigned int ii = B[i];
// 			int iy = ii/w;
// 			int ix = ii - iy*w;
// 			for(unsigned int j=i+1; j<B.size(); j++) {
// 				unsigned int jj = B[j];
// 				int jy = jj/w;
// 				int jx = jj - jy*w;
// 				int dx = std::abs(ix-jx);
// 				int dy = std::abs(iy-jy);
// 				if((dx == 1 && dy == 0) || (dx == 0 && dy == 1)) {
// 					L += (u.points[ii].position - u.points[jj].position).norm();
// 				}
// 			}
// 		}
// //		std::cout << k << ": " << ipq_els[k].second << " -> " << L << std::endl;
// 		ipq_els[k].second = L;
// 	}
	// finalize
	return IsoperimetricQuotientImpl(ipq_els);
}

}}


//---------------------------------------------------------------------------//
// Copyright (c) 2014 Roshan <thisisroshansmail@gmail.com>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
// See http://kylelutz.github.com/compute for more information.
//---------------------------------------------------------------------------//

#ifndef BOOST_COMPUTE_RANDOM_UNIFORM_INT_DISTRIBUTION_HPP
#define BOOST_COMPUTE_RANDOM_UNIFORM_INT_DISTRIBUTION_HPP

#include <boost/compute/command_queue.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/function.hpp>
#include <boost/compute/types/fundamental.hpp>
#include <boost/compute/algorithm/copy_if.hpp>
#include <boost/compute/algorithm/transform.hpp>

namespace boost {
namespace compute {

/// \class uniform_int_distribution
/// \brief Produces uniformily distributed random integers
///
/// The following example shows how to setup a uniform int distribution to
/// produce random integers 0 and 1.
///
/// \snippet test/test_uniform_int_distribution.cpp generate
///
template<class IntType = uint_>
class uniform_int_distribution
{
public:
    typedef IntType result_type;

    /// Creates a new uniform distribution producing numbers in the range
    /// [\p a, \p b].
    uniform_int_distribution(IntType a = 0, IntType b = 1)
        : m_a(a),
          m_b(b)
    {
    }

    /// Destroys the uniform_int_distribution object.
    ~uniform_int_distribution()
    {
    }

    /// Returns the minimum value of the distribution.
    result_type a() const
    {
        return m_a;
    }

    /// Returns the maximum value of the distribution.
    result_type b() const
    {
        return m_b;
    }

    /// Generates uniformily distributed integers and stores
    /// them to the range [\p first, \p last).
    template<class OutputIterator, class Generator>
    void generate(OutputIterator first,
                  OutputIterator last,
                  Generator &generator,
                  command_queue &queue)
    {
        size_t size = std::distance(first, last);
        typedef typename Generator::result_type g_result_type;

        vector<g_result_type> tmp(size, queue.get_context());
        vector<g_result_type> tmp2(size, queue.get_context());

        uint_ bound = ((uint_(-1))/(m_b-m_a+1))*(m_b-m_a+1);

        buffer_iterator<g_result_type> tmp2_iter;

        while(size>0)
        {
            generator.generate(tmp.begin(), tmp.begin() + size, queue);
            tmp2_iter = copy_if(tmp.begin(), tmp.begin() + size, tmp2.begin(),
                                _1 <= bound, queue);
            size = std::distance(tmp2_iter, tmp2.end());
        }

        BOOST_COMPUTE_FUNCTION(IntType, scale_random, (const g_result_type x),
        {
            return LO + (x % (HI-LO+1));
        });

        scale_random.define("LO", boost::lexical_cast<std::string>(m_a));
        scale_random.define("HI", boost::lexical_cast<std::string>(m_b));

        transform(tmp2.begin(), tmp2.end(), first, scale_random, queue);
    }

private:
    IntType m_a;
    IntType m_b;
};

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

#endif // BOOST_COMPUTE_RANDOM_UNIFORM_INT_DISTRIBUTION_HPP


/** @file
    @brief Implementation

    @date 2016

    @author
    Sensics, Inc.
    <http://sensics.com/osvr>
*/

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

// Internal Includes
#include "TestIMU_Common.h"

// Library/third-party includes
#include "FlexKalman/FlexibleUnscentedCorrect.h"
#include <Eigen/Eigenvalues>

// Standard includes
// - none

template <typename Derived>
inline bool isPositiveDefinite(MatrixBase<Derived> const &m) {
    Derived const &mat = m.derived();
    if (mat.rows() != mat.cols()) {
        /// not square!
        return false;
    }
    if (!mat.isApprox(mat.transpose())) {
        /// Not symmetric!
        return false;
    }
    // Having all positive eigenvalues is equivalent to being positive definite.
    return (mat.eigenvalues().real().array() > 0.).all();
}

template <typename Derived>
inline void checkSigmaPoints(MatrixBase<Derived> const &m) {
    SECTION("Sigma points") {
        Derived const &mat = m.derived();
        {
            INFO("Checking that there are at least two unique sigma points in "
                 "the collection of sigma points.");
            REQUIRE_FALSE((mat.rowwise().minCoeff().array() ==
                           mat.rowwise().maxCoeff().array())
                              .all());
        }
    }
}

template <typename Derived>
inline void thenCheckCovariance(MatrixBase<Derived> const &c) {
    Derived const &cov = c.derived();
    THEN("Covariance should be symmetric") {
        REQUIRE(cov.isApprox(cov.transpose()));
        AND_THEN("Covariance should be positive-definite") {
            REQUIRE(isPositiveDefinite(cov));
        }
    }
}

template <typename Reconstruction>
inline void thenCheckReconstruction(Reconstruction const &recon) {
    THEN("Reconstructed covariance should satisfy its invariants") {
        thenCheckCovariance(recon.getCov());
    }
}
template <typename Reconstruction>
inline void andThenCheckReconstruction(Reconstruction const &recon) {
    AND_THEN("Reconstructed covariance should satisfy its invariants") {
        thenCheckCovariance(recon.getCov());
    }
}
template <typename GeneratorType>
inline void generatorChecks(GeneratorType const &gen) {
    static const auto epsilon = NumTraits<double>::dummy_precision();

    SECTION("Weights") {
        CAPTURE(gen.getWeightsForMean().transpose());
        {
            INFO("Weights should be non-zero");
            REQUIRE(((gen.getWeightsForMean().array() > epsilon) ||
                     (gen.getWeightsForMean().array() < -epsilon))
                        .all());
        }
#ifdef DEMAND_NONNEGATIVE_WEIGHTS
        CHECK((gen.getWeightsForMean().array() >= 0.).all());
#endif

        CAPTURE(gen.getWeightsForCov().transpose());
        {
            INFO("Weights should be non-zero");
            REQUIRE(((gen.getWeightsForCov().array() > epsilon) ||
                     (gen.getWeightsForCov().array() < -epsilon))
                        .all());
        }
#ifdef DEMAND_NONNEGATIVE_WEIGHTS
        CHECK((gen.getWeightsForCov().array() >= 0.).all());
#endif
    }

    checkSigmaPoints(gen.getSigmaPoints());
}

template <typename GeneratorType>
inline void thenCheckSigmaPointGenerator(GeneratorType const &gen) {
    THEN("Sigma point generator should have reasonable output") {
        generatorChecks(gen);
    }
}
template <typename GeneratorType>
inline void andThenCheckSigmaPointGenerator(GeneratorType const &gen) {
    AND_THEN("Sigma point generator should have reasonable output") {
        generatorChecks(gen);
    }
}

TEST_CASE("Sigma point reconstruction validity") {
    Catch::StringMaker<float>::precision = 15;
    Catch::StringMaker<double>::precision = 35;

    static const auto DIM = 3;
    using namespace flexkalman;
    Matrix3d cov(Vector3d::Constant(10).asDiagonal());

    auto checkReconstruction = [&](Eigen::Vector3d const &mean) {
        using namespace flexkalman;
        using Generator = SigmaPointGenerator<DIM>;
        using Reconstructor =
            ReconstructedDistributionFromSigmaPoints<DIM, Generator>;
        const auto params = SigmaPointParameters();
        CAPTURE(mean);
        CAPTURE(cov);
        auto gen = Generator(mean, cov, params);
        thenCheckSigmaPointGenerator(gen);
#if 0
        static const auto numSigmaPoints = Generator::NumSigmaPoints;
        for (std::size_t i = 0; i < numSigmaPoints; ++i) {
            CAPTURE(gen.getSigmaPoint(i));
            CHECK(false);
        }
#endif
        AND_WHEN("reconstructed from the sigma points") {
            auto recon = Reconstructor(gen, gen.getSigmaPoints());
            thenCheckReconstruction(recon);
            THEN("the reconstructed distribution should be approximately equal "
                 "to the input") {
                CAPTURE(gen.getSigmaPoints());
                CAPTURE(recon.getMean());
                for (std::size_t i = 0; i < DIM; ++i) {
                    CAPTURE(i);
                    REQUIRE(recon.getMean()[i] == Approx(mean[i]));
                }
                CAPTURE(recon.getCov());
                REQUIRE(recon.getCov().isApprox(cov));
            }
        }
    };

    WHEN("Starting with a zero mean") { checkReconstruction(Vector3d::Zero()); }

    WHEN("Starting with a non-zero mean") {
        checkReconstruction(Vector3d(1, 2, 3));
    }
    WHEN("Starting with a small non-zero mean") {
        checkReconstruction(Vector3d(0.1, 0.2, 0.3));
    }
}

enum class Axis : std::size_t { X = 0, Y = 1, Z = 2 };

inline double zeroOrValueForAxis(Axis rotationAxis, Axis currentAxis,
                                 double value) {
    return ((rotationAxis == currentAxis) ? value : 0.);
}

inline Vector3d ZeroVec3dExceptAtAxis(Axis rotationAxis, double value) {
    return Vector3d(zeroOrValueForAxis(rotationAxis, Axis::X, value),
                    zeroOrValueForAxis(rotationAxis, Axis::Y, value),
                    zeroOrValueForAxis(rotationAxis, Axis::Z, value));
}

inline Vector3d Vec3dSmallValueAt(Axis rotationAxis) {
    return ZeroVec3dExceptAtAxis(rotationAxis, SMALL_VALUE);
}

inline Vector3d Vec3dUnit(Axis rotationAxis) {
    return ZeroVec3dExceptAtAxis(rotationAxis, 1.);
}

inline double getSignCorrect(bool positive) { return (positive ? 1. : -1.); }

template <typename MeasurementType, typename InProgressType>
inline void
commonSmallSingleAxisChecks(TestData *data, MeasurementType &kalmanMeas,
                            InProgressType &inProgress, Axis const rotationAxis,
                            bool const positive = true) {
    const double signCorrect = getSignCorrect(positive);
    Vector3d rotationVector = Vec3dSmallValueAt(rotationAxis) * signCorrect;
    CAPTURE(rotationVector.transpose());

    Vector3d residual = kalmanMeas.getResidual(data->state);
    CAPTURE(residual.transpose());

    AND_THEN("residual directly computed by measurement should be zero, except "
             "for the single axis of rotation, which should be of magnitude "
             "SMALL_VALUE") {
        CHECK(residual.isApprox(rotationVector));
    }

    CAPTURE(inProgress.deltaz);
    AND_THEN("computed deltaz should equal residual") {
        CHECK(residual.isApprox(inProgress.deltaz));
    }

    AND_THEN("delta z (residual/propagated mean residual) should be "
             "approximately 0, except for the single axis of rotation, which "
             "should be magnitude SMALL_VALUE") {
        REQUIRE(inProgress.deltaz[0] ==
                Approx(rotationVector[0]).margin(0.0001));
        REQUIRE(inProgress.deltaz[1] ==
                Approx(rotationVector[1]).margin(0.0001));
        REQUIRE(inProgress.deltaz[2] ==
                Approx(rotationVector[2]).margin(0.0001));
    }

    CAPTURE(inProgress.stateCorrection.transpose());
    REQUIRE(inProgress.stateCorrectionFinite);
    AND_THEN("state correction should have a rotation component with small "
             "absolute element (sign matching rotation vector) corresponding "
             "to rotation axis") {
        for (std::size_t i = 0; i < 3; ++i) {
            const auto stateIndex = i + 3;
            CAPTURE(stateIndex);
            if (static_cast<std::size_t>(rotationAxis) == i) {
                /// This is our rotation axis - correction should be in (0,
                /// SMALL_VALUE)
                REQUIRE((signCorrect * inProgress.stateCorrection[stateIndex]) >
                        0);
                REQUIRE((signCorrect * inProgress.stateCorrection[stateIndex]) <
                        SMALL_VALUE);
            } else {
                /// Not our rotation axis, correction should be approx 0.
                REQUIRE(inProgress.stateCorrection[stateIndex] ==
                        Approx(0.).margin(0.0001));
            }
        }
    }

    AND_THEN("state correction should have an angular velocity component "
             "with zero or small abs (sign matching rotation) corresponding to "
             "rotation axis") {

        for (std::size_t i = 0; i < 3; ++i) {
            const auto stateIndex = i + 9;
            CAPTURE(stateIndex);
            if (static_cast<std::size_t>(rotationAxis) == i) {
                /// This is our rotation axis - correction should be >= 0 if
                /// positive rotation.
                REQUIRE(signCorrect * inProgress.stateCorrection[stateIndex] >=
                        0.);
            } else {
                /// Not our rotation axis, correction should be approx 0.
                REQUIRE(inProgress.stateCorrection[stateIndex] ==
                        Approx(0.).margin(0.0001));
            }
        }
    }
    AND_THEN("state correction should not contain any translational/linear "
             "velocity components") {
        REQUIRE(inProgress.stateCorrection.template head<3>().isZero());
        REQUIRE(inProgress.stateCorrection.template segment<3>(6).isZero());
    }
    AND_WHEN("the correction is applied") {
        auto errorCovarianceCorrectionWasFinite = inProgress.finishCorrection();
        THEN("the new error covariance should be finite") {
            REQUIRE(errorCovarianceCorrectionWasFinite);
            AND_THEN("State error should have decreased") {
                REQUIRE((data->state.errorCovariance().array() <=
                         data->originalStateError.array())
                            .all());
            }
            thenCheckCovariance(data->state.errorCovariance());
        }
    }
}

template <typename MeasurementType>
inline void
unscentedSmallSingleAxisChecks(TestData *data, MeasurementType &kalmanMeas,
                               Axis rotationAxis, bool positive = true) {
    const auto params = flexkalman::SigmaPointParameters();
    auto inProgress =
        flexkalman::beginUnscentedCorrection(data->state, kalmanMeas, params);

    andThenCheckSigmaPointGenerator(inProgress.sigmaPoints);
    AND_THEN("transformed sigma points should not all equal 0") {
        CAPTURE(inProgress.transformedPoints.transpose());
        REQUIRE_FALSE(inProgress.transformedPoints.isZero());
    }
    checkSigmaPoints(inProgress.transformedPoints);
    andThenCheckReconstruction(inProgress.reconstruction);

    commonSmallSingleAxisChecks(data, kalmanMeas, inProgress, rotationAxis,
                                positive);
}

template <typename MeasurementType>
inline void checkEffectiveIdentityMeasurement(TestData *data,
                                              MeasurementType &kalmanMeas) {
    AND_THEN("residual should be zero") {
        CAPTURE(kalmanMeas.getResidual(data->state));
        REQUIRE(kalmanMeas.getResidual(data->state).isZero());
    }
    auto inProgress =
        flexkalman::beginUnscentedCorrection(data->state, kalmanMeas);

    andThenCheckSigmaPointGenerator(inProgress.sigmaPoints);
    AND_THEN("transformed sigma points should not all equal 0") {
        REQUIRE_FALSE(inProgress.transformedPoints.isZero());
    }
    checkSigmaPoints(inProgress.transformedPoints);

    auto &recon = inProgress.reconstruction;
    AND_THEN("propagated predicted measurement mean should be zero") {
        CAPTURE(recon.getMean().transpose());
        REQUIRE(recon.getMean().isZero());
    }
    AND_THEN("state correction should be finite - specifically, zero") {
        CAPTURE(inProgress.stateCorrection.transpose());
        REQUIRE(inProgress.stateCorrection.array().allFinite());
        REQUIRE(inProgress.stateCorrection.isZero());
    }

    const Quaterniond origQuat = data->state.getQuaternion();
    AND_WHEN("the correction is applied") {
        inProgress.finishCorrection();
        THEN("state should be unchanged") {
            CAPTURE(data->state.stateVector().transpose());
            REQUIRE(data->state.stateVector().isZero());
            CAPTURE(origQuat);
            CAPTURE(data->state.getQuaternion());
            REQUIRE(data->state.getQuaternion().coeffs().isApprox(
                origQuat.coeffs()));
        }
        THEN("State error should have decreased") {
            REQUIRE((data->state.errorCovariance().array() <=
                     data->originalStateError.array())
                        .all());
        }
    }
}

template <typename F, typename... Args>
inline void allSmallSingleAxisRotations(F &&f, Args &&... args) {
    WHEN("filtering in a small positive rotation about x") {
        std::forward<F>(f)(std::forward<Args>(args)..., Axis::X, true);
    }

    WHEN("filtering in a small positive rotation about y") {
        std::forward<F>(f)(std::forward<Args>(args)..., Axis::Y, true);
    }

    WHEN("filtering in a small positive rotation about z") {
        std::forward<F>(f)(std::forward<Args>(args)..., Axis::Z, true);
    }

    WHEN("filtering in a small negative rotation about x") {
        std::forward<F>(f)(std::forward<Args>(args)..., Axis::X, false);
    }
    WHEN("filtering in a small negative rotation about y") {
        std::forward<F>(f)(std::forward<Args>(args)..., Axis::Y, false);
    }

    WHEN("filtering in a small negative rotation about z") {
        std::forward<F>(f)(std::forward<Args>(args)..., Axis::Z, false);
    }
}

#if 0
CATCH_TYPELIST_DESCRIBED_TESTCASE(
    "unscented with identity calibration output", "[ukf]",
    /*flexkalman::QFirst, flexkalman::QLast,*/ flexkalman::SplitQ) {

#endif
TEST_CASE("unscented with identity calibration output", "[ukf]") {
    Catch::StringMaker<float>::precision = 15;
    Catch::StringMaker<double>::precision = 35;
    using TypeParam = flexkalman::SplitQ;

    const auto params = flexkalman::SigmaPointParameters();
    using MeasurementType = OrientationMeasurementUsingPolicy<TypeParam>;
    // using MeasurementType = TypeParam;
    unique_ptr<TestData> data(new TestData);
    GIVEN("an identity state") {
        WHEN("filtering in an identity measurement") {
            Quaterniond xformedMeas = data->xform(Quaterniond::Identity());

            THEN("the transformed measurement should equal the "
                 "measurement") {
                CAPTURE(xformedMeas);
                REQUIRE(xformedMeas.isApprox(Quaterniond::Identity()));

                MeasurementType kalmanMeas{xformedMeas, data->imuVariance};
                checkEffectiveIdentityMeasurement(data.get(), kalmanMeas);
            }
        }
        allSmallSingleAxisRotations([&](Axis rotationAxis, bool positive) {
            const double signCorrect = getSignCorrect(positive);
            auto radians = signCorrect * SMALL_VALUE;
            CAPTURE(radians);
            auto axisVec = Vec3dUnit(rotationAxis);
            CAPTURE(axisVec.transpose());
            Quaterniond smallRotation =
                Quaterniond(AngleAxisd(radians, axisVec));

            CAPTURE(smallRotation);
            Quaterniond xformedMeas = data->xform(smallRotation);
            CAPTURE(xformedMeas);

            THEN("the transformed measurement should equal the "
                 "measurement") {
                REQUIRE(xformedMeas.isApprox(smallRotation));
                /// Do the rest of the checks for a small rotation about y
                MeasurementType kalmanMeas{xformedMeas, data->imuVariance};
                unscentedSmallSingleAxisChecks(data.get(), kalmanMeas,
                                               rotationAxis, positive);
            }
        });
    }

    auto runIncrementalSmallRotChecksNonIdentityState = [&](Axis rotationAxis,
                                                            bool positive) {
        const double signCorrect = getSignCorrect(positive);
        auto radians = signCorrect * SMALL_VALUE;
        CAPTURE(radians);
        auto axisVec = Vec3dUnit(rotationAxis);
        CAPTURE(axisVec.transpose());
        Quaterniond smallRotation = Quaterniond(AngleAxisd(radians, axisVec)) *
                                    data->state.getQuaternion();

        CAPTURE(smallRotation);
        Quaterniond xformedMeas = data->xform(smallRotation);
        CAPTURE(xformedMeas);

        THEN("the transformed measurement should equal the "
             "measurement") {
            REQUIRE(xformedMeas.isApprox(smallRotation));
            /// Do the rest of the checks for a small rotation about y
            MeasurementType kalmanMeas{xformedMeas, data->imuVariance};
            unscentedSmallSingleAxisChecks(data.get(), kalmanMeas, rotationAxis,
                                           positive);
        }
    };
    GIVEN("a state rotated about y") {
        Quaterniond stateRotation(AngleAxisd(EIGEN_PI / 4., Vector3d::UnitY()));
        data->state.setQuaternion(stateRotation);
        allSmallSingleAxisRotations(
            runIncrementalSmallRotChecksNonIdentityState);
    }
    GIVEN("a state rotated about x") {
        Quaterniond stateRotation(AngleAxisd(EIGEN_PI / 4., Vector3d::UnitX()));
        data->state.setQuaternion(stateRotation);
        allSmallSingleAxisRotations(
            runIncrementalSmallRotChecksNonIdentityState);
    }
}

TEST_CASE("unscented with small x rotation calibration output", "[ukf]") {
    Catch::StringMaker<float>::precision = 15;
    Catch::StringMaker<double>::precision = 35;
    using MeasurementType =
        OrientationMeasurementUsingPolicy<flexkalman::SplitQ>;
    unique_ptr<TestData> data(new TestData);
    CAPTURE(SMALL_VALUE);
    data->roomToCameraRotation =
        Quaterniond(AngleAxisd(SMALL_VALUE, Vector3d::UnitX()));
    CAPTURE(data->roomToCameraRotation);
    INFO("This calibration value means that the camera is rotated negative "
         "about its x wrt. the IMU/room");
    INFO("Equivalently, that quaternion calibration value takes "
         "points/orientations in room space and moves them to camera space");
    GIVEN("an identity state in camera space") {
        CAPTURE(data->state.getQuaternion());
        WHEN("filtering in a measurement that's the inverse of the room to "
             "camera rotation (a measurement matching state)") {
            Quaterniond meas = data->roomToCameraRotation.inverse();
            CAPTURE(meas);
            Quaterniond xformedMeas = data->xform(meas);

            THEN("the transformed measurement should be approximately the "
                 "identity") {
                CAPTURE(xformedMeas);
                REQUIRE(xformedMeas.isApprox(Quaterniond::Identity()));

                MeasurementType kalmanMeas{xformedMeas, data->imuVariance};
                checkEffectiveIdentityMeasurement(data.get(), kalmanMeas);
            }
        }
    }
    GIVEN("an identity state in room space") {
        data->state.setQuaternion(data->roomToCameraRotation);
        CAPTURE(data->state.getQuaternion());
        WHEN("filtering in an identity measurement (a measurement matching "
             "state)") {
            Quaterniond xformedMeas = data->xform(Quaterniond::Identity());

            THEN("the transformed measurement should be approximately the "
                 "room to camera rotation") {
                CAPTURE(xformedMeas);
                REQUIRE(xformedMeas.isApprox(data->roomToCameraRotation));

                MeasurementType kalmanMeas{xformedMeas, data->imuVariance};
                checkEffectiveIdentityMeasurement(data.get(), kalmanMeas);
            }
        }
        allSmallSingleAxisRotations([&](Axis rotationAxis, bool positive) {
            const double signCorrect = getSignCorrect(positive);
            auto radians = signCorrect * SMALL_VALUE;
            CAPTURE(radians);
            auto axisVec = Vec3dUnit(rotationAxis);
            CAPTURE(axisVec.transpose());
            Quaterniond smallRelRotationInCameraSpace =
                Quaterniond(AngleAxisd(radians, axisVec)) *
                data->state.getQuaternion();
            CAPTURE(smallRelRotationInCameraSpace);
            Quaterniond smallRotation = data->roomToCameraRotation.inverse() *
                                        smallRelRotationInCameraSpace;
            CAPTURE(smallRotation);
            THEN("the transformed measurement should equal the original "
                 "measurement as computed in camera space") {
                Quaterniond xformedMeas = data->xform(smallRotation);
                CAPTURE(xformedMeas);
                REQUIRE(xformedMeas.isApprox(smallRelRotationInCameraSpace));

                /// Do the rest of the checks for a small rotation about a
                /// single axis
                MeasurementType kalmanMeas{xformedMeas, data->imuVariance};
                CAPTURE(kalmanMeas.getResidual(data->state));
                unscentedSmallSingleAxisChecks(data.get(), kalmanMeas,
                                               rotationAxis, positive);
            }
        });
    }
}

TEST_CASE("conceptual transformation orders") {
    Catch::StringMaker<float>::precision = 15;
    Catch::StringMaker<double>::precision = 35;
    Quaterniond positiveX(AngleAxisd(0.5, Vector3d::UnitX()));
    Quaterniond positiveY(AngleAxisd(0.5, Vector3d::UnitY()));
    Vector3d yVec = Vector3d::UnitY();
    SECTION(
        "Quaternion composition/multiplication order matches "
        "transformation order (transformation and quat mult are associative)") {
        CAPTURE((positiveX * (positiveY * yVec)).transpose());
        CAPTURE(((positiveX * positiveY) * yVec).transpose());
        REQUIRE((positiveX * (positiveY * yVec))
                    .isApprox((positiveX * positiveY) * yVec));
    }
    SECTION("Behavior of rotations") {
        INFO("positive X is a transformation taking points from a coordinate "
             "system pitched up, to a level coordinate system.");
        CAPTURE((positiveX * yVec).transpose());
        REQUIRE((positiveX * yVec).z() > 0.);
    }
}


#include <boost/random.hpp>

#include "math_functions.hpp"
#include <cmath>

namespace pwalk {

// Define random number generator type
typedef boost::mt19937 rng_t;

template <typename Dtype>
void sample_gaussian(const int n, const Dtype a,
                        const Dtype sigma, Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& r) {
  static rng_t gen(1234567);
  static boost::normal_distribution<Dtype> random_distribution(a, sigma);
  static boost::variate_generator<rng_t&, boost::normal_distribution<Dtype> >
      variate_generator(gen, random_distribution);

  for (int i = 0; i < n; ++i) {
    r[i] = variate_generator();
  }
}

template <typename Dtype>
Dtype rng_uniform(const Dtype a, const Dtype b) {
  static rng_t gen(1234567);
  static boost::uniform_real<Dtype> random_distribution(a, b);
  static boost::variate_generator<rng_t&, boost::uniform_real<Dtype> >
      variate_generator(gen, random_distribution);

  return variate_generator();
}

template <typename Dtype>
Dtype gaussian_density(const Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& x, const Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& mu, const Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic>& sqrt_cov) {
  Eigen::Matrix<Dtype, Eigen::Dynamic, 1> c = sqrt_cov * (x - mu);
  return std::exp(-0.5*c.dot(c)) * sqrt_cov.determinant();
}

template
void sample_gaussian<float>(const int n, const float mu,
                               const float sigma, Eigen::Matrix<float, Eigen::Dynamic, 1>& r);

template
void sample_gaussian<double>(const int n, const double mu,
                                const double sigma, Eigen::Matrix<double, Eigen::Dynamic, 1>& r);

template
float rng_uniform<float>(const float a, const float b);

template
double rng_uniform<double>(const double a, const double b);

template
float gaussian_density(const Eigen::Matrix<float, Eigen::Dynamic, 1>& x, const Eigen::Matrix<float, Eigen::Dynamic, 1>& mu, const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>& sqrt_inv_cov);

template
double gaussian_density(const Eigen::Matrix<double, Eigen::Dynamic, 1>& x, const Eigen::Matrix<double, Eigen::Dynamic, 1>& mu, const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>& sqrt_inv_cov);

} // namespace pwalk


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

#include <boost/hana/detail/assert.hpp>
#include <boost/hana/ext/std/integral_constant.hpp>
#include <boost/hana/functional.hpp>
#include <boost/hana/integer_list.hpp>
#include <boost/hana/integral.hpp>
using namespace boost::hana;
using namespace literals;

int main() {
    //! [main]
    BOOST_HANA_CONSTANT_ASSERT(
        take_until(_ < 2_c, integer_list<int, 3, 2, 1, 0>) == integer_list<int, 3, 2>
    );
    //! [main]
}


/* ScaFES
 * Copyright (c) 2018, ZIH, TU Dresden, Federal Republic of Germany.
 * For details, see the files COPYING and LICENSE in the base directory
 * of the package.
 */

/**
 *  @file MRIDataPBHEqnFDM.hpp
 *
 *  @brief Implementation of a n-dimensional Pennes bioheat equation problem
 *         with data from MRI.
 */

#include <iostream>
#include "ScaFES.hpp"

#include <boost/property_tree/ini_parser.hpp>

/******************************************************************************
 *****************************************************************************/
/**
 * \class MRIDataPBHEqnFDM
 *  @brief Class for discretized Pennes bioheat equation problem.
 *
*/
template<typename CT, std::size_t DIM>
class MRIDataPBHEqnFDM : public ScaFES::Problem<MRIDataPBHEqnFDM<CT,DIM>, CT, DIM> {
  private:
    /** Parser for ini files. */
    using PTree = boost::property_tree::ptree;
    const PTree ptree;

  public:
    /** constant h. Ambient convetion. */
    const CT H; /* W/(m^2 K) */

    /** constant T_amb. Ambient temperature. */
    const CT T_AMB; /* K */

    /** constant q_bc. Heat flux at surface inside the brain. */
    const CT Q_BC; /* W/(m^2) */

    /** constant q_skull. Heat flux at upper surface under skull. */
    const CT Q_SKULL; /* W/(m^2) */

    /** constant epsilon. Emissivity. */
    const CT EPSILON; /* - */

    /** constant sigma. Stefan–Boltzmann constant. */
    const CT SIGMA = 5.670373e-8; /* W/(m^2 K^4) */

    /** All fields which are related to the underlying problem
     * are added in terms of an entry of the parameters of
     * type \c std::vector.
     * @param params Set of ScaFES parameters.
     * @param gg Global grid.
     * @param useLeapfrog Should the leap frog scheme be used?
     * @param nameDatafield Name of the fields.
     * @param stencilWidth Stencil width of the fields.
     * @param isKnownDf Is the data field are known or unknown one?
     * @param ptree_ Config file parser.
     * @param nLayers Number of layers at the global boundary.
     * @param defaultValue Default value of fields.
     * @param writeToFile How often should the data field be written to file.
     * @param computeError Should the Linf error between the numerical
     *                     and exact solution be computed?
     * @param geomparamsInit Initial guess of geometrical parameters.
     * @param checkConvergence Should convergence be checked?
     */
    MRIDataPBHEqnFDM(ScaFES::Parameters const& params,
                     ScaFES::GridGlobal<DIM> const& gg,
                     bool useLeapfrog,
                     std::vector<std::string> const& nameDatafield,
                     std::vector<int> const& stencilWidth,
                     std::vector<bool> const& isKnownDf,
                     PTree const& ptree_,
                     std::vector<int> const& nLayers = std::vector<int>(),
                     std::vector<CT> const& defaultValue = std::vector<CT>(),
                     std::vector<ScaFES::WriteHowOften> const& writeToFile
                         = std::vector<ScaFES::WriteHowOften>(),
                     std::vector<bool> const& computeError
                         = std::vector<bool>(),
                     std::vector<CT> const& geomparamsInit = std::vector<CT>(),
                     std::vector<bool> const& checkConvergence
                         = std::vector<bool>() )
        : ScaFES::Problem<MRIDataPBHEqnFDM<CT, DIM>, CT, DIM>(params, gg,
                                                              useLeapfrog,
                                                              nameDatafield,
                                                              stencilWidth,
                                                              isKnownDf,
                                                              nLayers,
                                                              defaultValue,
                                                              writeToFile,
                                                              computeError,
                                                              geomparamsInit,
                                                              checkConvergence),
        ptree(ptree_),
        H(ptree.get<CT>("Parameters.H")),
        T_AMB(ptree.get<CT>("Parameters.T_INF")),
        Q_BC(ptree.get<CT>("Parameters.Q_BC")),
        Q_SKULL(ptree.get<CT>("Parameters.Q_SKULL")),
        EPSILON(ptree.get<CT>("Parameters.EPSILON"))
        { }

    /** Evaluates all fields at one given global inner grid node.
     */
    void evalInner(std::vector< ScaFES::DataField<CT, DIM> >& /*vNew*/,
                   ScaFES::Ntuple<int,DIM> const& /*idxNode*/,
                   int const& /*timestep*/) {
    }

    /** Evaluates all fields at one given global border grid node.
     */
    void evalBorder(std::vector< ScaFES::DataField<CT, DIM> >& /*vNew*/,
                    ScaFES::Ntuple<int,DIM> const& /*idxNode*/,
                    int const& /*timestep*/) {
    }

    /** Initializes all unknown fields at one given global inner grid node.
     */
    template<typename TT>
    void initInner(std::vector< ScaFES::DataField<TT, DIM> >& /*vNew*/,
                   std::vector<TT> const& /*vOld*/,
                   ScaFES::Ntuple<int,DIM> const& /*idxNode*/,
                   int const& /*timestep*/) {
    }

    /** Initializes all unknown fields at one given global border grid node.
     */
    template<typename TT>
    void initBorder(std::vector< ScaFES::DataField<TT, DIM> >& /*vNew*/,
                   std::vector<TT> const& /*vOld*/,
                   ScaFES::Ntuple<int,DIM> const& /*idxNode*/,
                   int const& /*timestep*/) {
    }

    /** Updates all unknown fields at one given global inner grid node.
     *  @param vNew Set of all unknown fields at new time step (return value).
     *  @param vOld Set of all unknown fields at old time step.
     *  @param idxNode Index of given grid node.
     */
    template<typename TT>
    void updateInner(std::vector<ScaFES::DataField<TT,DIM>>& vNew,
                     std::vector<ScaFES::DataField<TT,DIM>> const& vOld,
                     ScaFES::Ntuple<int,DIM> const& idxNode,
                     int const& /*timestep*/) {
        CT rho = this->knownDf(0, idxNode);
        CT c = this->knownDf(1, idxNode);
        CT k = this->knownDf(2, idxNode);
        CT rho_blood = this->knownDf(3, idxNode);
        CT c_blood = this->knownDf(4, idxNode);
        CT omega = this->knownDf(5, idxNode);
        CT T_blood = this->knownDf(6, idxNode);
        CT q = this->knownDf(7, idxNode);

        /* Discrete Pennes Bioheat Equation for updating inner nodes. */
        vNew[0](idxNode) = vOld[0](idxNode);
        for (std::size_t pp = 0; pp < DIM; ++pp) {
            vNew[0](idxNode) += this->tau() * (k/(rho*c))
                                * (vOld[0](this->connect(idxNode, 2*pp))
                                   + vOld[0](this->connect(idxNode, 2*pp+1))
                                   - 2.0 * vOld[0](idxNode))
                                / (this->gridsize(pp) * this->gridsize(pp));
        }
        vNew[0](idxNode) += this->tau() * ((rho_blood*c_blood)/(rho*c)) * omega
                            * (T_blood - vOld[0](idxNode));
        vNew[0](idxNode) += this->tau() * (1.0/(rho*c)) * q;
    }

    /** Updates all unknown fields at one given global border grid node.
     *  @param vNew Set of all unknown fields at new time step (return value).
     *  @param vOld Set of all unknown fields at old time step.
     *  @param idxNode Index of given grid node.
     */
    template<typename TT>
    void updateBorder(std::vector<ScaFES::DataField<TT,DIM>>& vNew,
                      std::vector<ScaFES::DataField<TT,DIM>>const& vOld,
                      ScaFES::Ntuple<int,DIM> const& idxNode,
                      int const& /*timestep*/) {
        CT rho = this->knownDf(0, idxNode);
        CT c = this->knownDf(1, idxNode);
        CT k = this->knownDf(2, idxNode);
        CT rho_blood = this->knownDf(3, idxNode);
        CT c_blood = this->knownDf(4, idxNode);
        CT omega = this->knownDf(5, idxNode);
        CT T_blood = this->knownDf(6, idxNode);
        CT q = this->knownDf(7, idxNode);
        int trepanationArea = this->knownDf(8, idxNode);

        /* Discrete Pennes Bioheat Equation with boundary conditions. */
        vNew[0](idxNode) = vOld[0](idxNode);
        for (std::size_t pp = 0; pp < DIM; ++pp) {
            if (idxNode.elem(pp) == (this->nNodes(pp)-1)) {
            /* vOld[0](this->connect(idxNode, 2*pp+1) needs to be replaced. */
                if (pp == (DIM-1)) {
                /* Last node/edge/surface in highest dimension will be brain surface. */
                    if (trepanationArea == 1) {
                    /* Open skull: Cauchy boundary condition and
                     * thermal radiation boundary condition. */
                        CT tempOld = vNew[0](idxNode) + 273.15;
                        CT tempOldPow4 = tempOld * tempOld * tempOld * tempOld;
                        CT tempAmb = T_AMB + 273.15;
                        CT tempAmbPow4 = tempAmb * tempAmb * tempAmb * tempAmb;

                        vNew[0](idxNode) += this->tau() * (k/(rho*c))
                                            * (vOld[0](this->connect(idxNode, 2*pp))
                                            /* vOld[0](this->connect(idxNode, 2*pp+1)
                                             * is replaced by                         */
                                               + vOld[0](this->connect(idxNode, 2*pp))
                                               - ((2.0*this->gridsize(pp)/k)
                                                  * H * (vOld[0](idxNode) - T_AMB))
                                               - ((2.0*this->gridsize(pp)/k)
                                                  * EPSILON * SIGMA
                                                  * (tempOldPow4 - tempAmbPow4))
                                            /********************************************/
                                               - 2.0 * vOld[0](idxNode))
                                            / (this->gridsize(pp) * this->gridsize(pp));
                    } else {
                    /* Closed skull: Neumann boundary condition. */
                        vNew[0](idxNode) += this->tau() * (k/(rho*c))
                                            * (vOld[0](this->connect(idxNode, 2*pp))
                                            /* vOld[0](this->connect(idxNode, 2*pp+1))
                                               is replaced by                          */
                                               + vOld[0](this->connect(idxNode, 2*pp))
                                               - ((2.0*this->gridsize(pp)/k)
                                                  * (-1.0 * Q_SKULL))
                                            /*********************************************/
                                               - 2.0 * vOld[0](idxNode))
                                            / (this->gridsize(pp) * this->gridsize(pp));
                    }
                } else {
                /* Neumann boundary condition. */
                    vNew[0](idxNode) += this->tau() * (k/(rho*c))
                                        * (vOld[0](this->connect(idxNode, 2*pp))
                                        /* vOld[0](this->connect(idxNode, 2*pp+1))
                                           is replaced by                            */
                                           + vOld[0](this->connect(idxNode, 2*pp))
                                           - ((2.0*this->gridsize(pp)/k)
                                              * (-1.0 * Q_BC))
                                        /*********************************************/
                                           - 2.0 * vOld[0](idxNode))
                                        / (this->gridsize(pp) * this->gridsize(pp));
                }
            } else if (idxNode.elem(pp) == 0){
            /* vOld[0](this->connect(idxNode, 2*pp) needs to be replaced.
             * Neumann boundary condition. */
                vNew[0](idxNode) += this->tau() * (k/(rho*c))
                                    /* vOld[0](this->connect(idxNode, 2*pp))
                                       is replaced by                          */
                                    * (vOld[0](this->connect(idxNode, 2*pp+1))
                                       + ((2.0*this->gridsize(pp)/k) * Q_BC)
                                    /*******************************************/
                                       + vOld[0](this->connect(idxNode, 2*pp+1))
                                       - 2.0 * vOld[0](idxNode))
                                    / (this->gridsize(pp) * this->gridsize(pp));
            } else {
            /* No value needs to be replaced.
             * Use central differencing scheme. */
                vNew[0](idxNode) += this->tau() * (k/(rho*c))
                                    * (vOld[0](this->connect(idxNode, 2*pp))
                                       + vOld[0](this->connect(idxNode, 2*pp+1))
                                       - 2.0 * vOld[0](idxNode))
                                    / (this->gridsize(pp) * this->gridsize(pp));
            }
        }

        /* These terms are independet of the boundary condition. */
        vNew[0](idxNode) += this->tau() * ((rho_blood*c_blood)/(rho*c)) * omega
                            * (T_blood - vOld[0](idxNode));
        vNew[0](idxNode) += this->tau() * (1.0/(rho*c)) * q;
    }

    /** Updates (2nd cycle) all unknown fields at one given global inner grid node.
     *  \remarks Only important if leap frog scheme is used.
     */
    template<typename TT>
    void updateInner2(std::vector<ScaFES::DataField<TT,DIM>>&,
                      std::vector<ScaFES::DataField<TT,DIM>> const&,
                      ScaFES::Ntuple<int,DIM> const&,
                      int const&) { }

    /** Updates (2nd cycle) all unknown fields at one given global border
     *  grid node.
     *  \remarks Only important if leap frog scheme is used.
     */
    template<typename TT>
    void updateBorder2(std::vector<ScaFES::DataField<TT,DIM>>&,
                       std::vector<ScaFES::DataField<TT,DIM>>const&,
                       ScaFES::Ntuple<int,DIM> const&,
                       int const&) { }
};


#include <bits/stdc++.h>
using namespace std;

#ifdef LOCAL
#include "/debug.h"
#else
#define db(...)
#endif

// #include <boost/multiprecision/cpp_int.hpp>
// using namespace boost::multiprecision;

#define all(v) v.begin(), v.end()
#define pb push_back
using ll = long long;
const int NAX = 2e5 + 5, MOD = 1000000007;

void solveCase()
{
    int n, k;
    n = k = 50;
    cin >> n >> k;
    // if (n < k)
    // {
    //     cout << 0 << '\n';
    //     return;
    // }
    vector<ll> boxes(n);
    ll temp = 8;
    for (auto &x : boxes)
    {
        x = temp;
        temp *= 2;
        cin >> x;
    }
    sort(all(boxes));
    ll a, b;
    a = 1e18, b = 1e18;
    cin >> a >> b;
    auto ncr = [](int n, int r) -> ll {
        if (r > n || n < 0 || r < 0)
            return 0;
        __int128_t num = 1, den = 1;
        r = min(r, n - r);
        for (int i = 1; i <= r; i++)
        {
            num *= n - r + i;
            num /= i;
        }
        db(n, r, (long long)num);
        assert(num <= LLONG_MAX);
        return num;
    };
    // db(ncr(50, 25));
    set<int> ss;
    function<ll(ll, int, int)> ways = [&](ll target, int boxcount, int rptr) -> ll {
        if (boxcount == 0)
            return 1;
        if (boxcount < 0 || target < 0 || target < boxes[0])
            return 0;
        int i = 0;
        for (; i < rptr; i++)
            if (target < boxes[i])
                break;
        auto dontchose = ncr(i - 1, boxcount);
        auto chose = 0LL;
        if (ss.count(i) == 0)
        {
            ss.insert(i);
            chose = ways(target - boxes[i - 1], boxcount - 1, i);
        }
        db(i, rptr, target, boxcount, dontchose, chose);
        return chose + dontchose;
    };
    db(boxes);
    auto one = ways(b, k, n);
    ss.clear();
    one -= ways(a - 1, k, n);
    cout << one << '\n';
}

int32_t main()
{
#ifndef LOCAL
    ios_base::sync_with_stdio(0);
    cin.tie(0);
#endif
    int t = 1;
    // cin >> t;
    for (int i = 1; i <= t; ++i)
        solveCase();
    return 0;
}


/*
 *
 * Copyright (c) Toon Knapen & Kresimir Fresl 2003
 *
 * 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)
 *
 * KF acknowledges the support of the Faculty of Civil Engineering,
 * University of Zagreb, Croatia.
 *
 */

#ifndef BOOST_NUMERIC_BINDINGS_LAPACK_LAPACK_HPP
#define BOOST_NUMERIC_BINDINGS_LAPACK_LAPACK_HPP

// linear systems

#include <boost/numeric/bindings/lapack/gesv.hpp>
#include <boost/numeric/bindings/lapack/posv.hpp>
#include <boost/numeric/bindings/lapack/ppsv.hpp>
#include <boost/numeric/bindings/lapack/sysv.hpp>
#include <boost/numeric/bindings/lapack/spsv.hpp>
#include <boost/numeric/bindings/lapack/hesv.hpp>
#include <boost/numeric/bindings/lapack/hpsv.hpp>

// eigenproblems

#include <boost/numeric/bindings/lapack/gees.hpp>
#include <boost/numeric/bindings/lapack/trevc.hpp>
#include <boost/numeric/bindings/lapack/trexc.hpp>
#include <boost/numeric/bindings/lapack/hbev.hpp>
#include <boost/numeric/bindings/lapack/syev.hpp>

// SVD

#include <boost/numeric/bindings/lapack/gesvd.hpp>
#include <boost/numeric/bindings/lapack/gesdd.hpp>
#include <boost/numeric/bindings/lapack/sygv.hpp>

// Miscellaneous
// QR
#include <boost/numeric/bindings/lapack/geqrf.hpp>
#include <boost/numeric/bindings/lapack/ormqr.hpp>
#include <boost/numeric/bindings/lapack/orgqr.hpp>

#endif // BOOST_NUMERIC_BINDINGS_LAPACK_LAPACK_HPP


//  (C) Copyright 2014 Alvaro J. Genial (http://alva.ro)
//  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).

#ifndef AJG_SYNTH_DETAIL_IS_INTEGER_HPP_INCLUDED
#define AJG_SYNTH_DETAIL_IS_INTEGER_HPP_INCLUDED

#include <cmath>

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_integral.hpp>

namespace ajg {
namespace synth {
namespace detail {

//
// is_integer:
//     Determines whether a floating-point number is an integer.
////////////////////////////////////////////////////////////////////////////////////////////////////

template <class T>
inline typename boost::enable_if<boost::is_integral<T>, bool>::type is_integer(T const) { return true; }

template <class T>
inline typename boost::disable_if<boost::is_integral<T>, bool>::type is_integer(T const t) {
    T integral_part;
    return (std::modf)(t, &integral_part) == static_cast<T>(0.0);
}

}}} // namespace ajg::synth::detail

#endif // AJG_SYNTH_DETAIL_IS_INTEGER_HPP_INCLUDED


<%
cfg['compiler_args'] = ['-std=c++14','-fopenmp']
cfg['linker_args'] = ['-fopenmp']
cfg['include_dirs'] = ['/home/eigen-3.3.7']
setup_pybind11(cfg)
%>

#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <Eigen/SparseCore>
#include <Eigen/Dense>
#include <omp.h>
#define BATCHSIZE 8
namespace py = pybind11;
using namespace Eigen;

typedef SparseMatrix<std::complex<double>>  SpMat;

VectorXcd dot(int i, int l, SpMat un, VectorXcd wave, int size)
{
 py::gil_scoped_acquire acquire; /* Acquire GIL before calling Python code */
 initParallel(); // not required after eigen 3.3 and c++11 compiler
 setNbThreads(8);
 int k;
 int chunk = pow(2, l-2*i); // dimensions of the incoming unitary sparse matrix
 int dim = pow(2, l)/size;
 int split = pow(2, 2*i)/size; // # of splitted wavefunction
 int batch = BATCHSIZE; // specify the batch size for each threads
 VectorXcd temp(dim); // temp vector to keep tract of mat-vec multiplication
 temp = VectorXcd::Zero(dim);
 // paralleling using openmp
 #pragma omp parallel shared(un, wave) private(k)
 {
 // since each mat-vec dot product is totally independent, we can safely parallel the loop
  #pragma omp for schedule(dynamic,batch)
  for (k = 0; k < split; k++){
    temp.segment(k*chunk, chunk) = un * wave.segment(k*chunk, chunk);
  }
}

 return temp;

}
// simple implementation without openmp; only for benchmark purpose
VectorXcd dot_simple(SpMat un, VectorXcd wave)
{
 return un*wave;
}

PYBIND11_MODULE(eigen_dot, m) {
    // Release GIL before calling into C++ code
    // and also set up the default argument to be one
    m.def("dot", &dot, py::call_guard<py::gil_scoped_release>());
    m.def("dot_simple", &dot_simple, py::call_guard<py::gil_scoped_release>());
 }


#ifndef STAN_MATH_PRIM_MAT_PROB_MULTI_NORMAL_RNG_HPP
#define STAN_MATH_PRIM_MAT_PROB_MULTI_NORMAL_RNG_HPP

#include <stan/math/prim/scal/err/check_finite.hpp>
#include <stan/math/prim/scal/err/check_positive.hpp>
#include <stan/math/prim/scal/fun/constants.hpp>
#include <stan/math/prim/scal/meta/include_summand.hpp>
#include <stan/math/prim/mat/err/check_pos_definite.hpp>
#include <stan/math/prim/mat/err/check_symmetric.hpp>
#include <stan/math/prim/mat/fun/trace_inv_quad_form_ldlt.hpp>
#include <stan/math/prim/mat/fun/log_determinant_ldlt.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/variate_generator.hpp>

namespace stan {
  namespace math {

    /**
     * Return a pseudo-random vector with a multi-variate normal
     * distribution given the specified location parameter and
     * covariance matrix and pseudo-random number generator.
     *
     * If calculating more than one multivariate-normal random draw
     * then it is more efficient to calculate the Cholesky factor of
     * the covariance matrix and use the function
     * stan::math::multi_normal_cholesky_rng.
     *
     * @tparam RNG Type of pseudo-random number generator.
     * @param mu Location parameter.
     * @param S Covariance parameter.
     * @param rng Pseudo-random number generator.
     */
    template <class RNG>
    inline Eigen::VectorXd
    multi_normal_rng(const Eigen::VectorXd& mu, const Eigen::MatrixXd& S,
                     RNG& rng) {
      using boost::variate_generator;
      using boost::normal_distribution;

      static const char* function("multi_normal_rng");

      check_positive(function, "Covariance matrix rows", S.rows());
      check_symmetric(function, "Covariance matrix", S);
      check_finite(function, "Location parameter", mu);

      Eigen::LLT<Eigen::MatrixXd> llt_of_S = S.llt();
      check_pos_definite("multi_normal_rng", "covariance matrix argument",
                         llt_of_S);

      variate_generator<RNG&, normal_distribution<> >
        std_normal_rng(rng, normal_distribution<>(0, 1));

      Eigen::VectorXd z(S.cols());
      for (int i = 0; i < S.cols(); i++)
        z(i) = std_normal_rng();

      return mu + llt_of_S.matrixL() * z;
    }

  }
}
#endif


#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE JPetCachedFunctionTest

#include "JPetCachedFunction/JPetCachedFunction.h"
#include "JPetLoggerInclude.h"
#include <boost/test/unit_test.hpp>

using namespace jpet_common_tools;
/// Returns Time-over-threshold for given deposited energy
/// the current parametrization is par1 + par2 * eDep
/// Returned value in ps, and eDep is given in keV.
double getToT1(double eDep, double  par1 = -91958., double par2 = 19341.)
{
  if (eDep < 0. ) return 0.;
  double value = par1 + eDep * par2;
  return value;
}

BOOST_AUTO_TEST_SUITE(JPetCachedFunctionTestSuite)

BOOST_AUTO_TEST_CASE(getTot_params)
{
  JPetCachedFunctionParams params("pol1", { -91958., 19341.});
  JPetCachedFunction1D func(params, Range(100, 0., 100.));
  BOOST_CHECK(func.getParams().fValidFunction);
  BOOST_CHECK_EQUAL(func.getParams().fParams.size(), 2);
  BOOST_CHECK_EQUAL(func.getParams().fParams[0], -91958.);
  BOOST_CHECK_EQUAL(func.getParams().fParams[1], 19341.);
  BOOST_CHECK_EQUAL(func.getValues().size(), 100);
  auto vals = func.getValues();
}

BOOST_AUTO_TEST_CASE(getTot_standardFunc)
{
  JPetCachedFunctionParams params("pol1", { -91958., 19341.});
  JPetCachedFunction1D func(params, Range( 10000, 0., 100.));
  BOOST_CHECK(func.getParams().fValidFunction);
  BOOST_CHECK_CLOSE(func(0.), getToT1(0.), 0.1);
  BOOST_CHECK_CLOSE(func(1.), getToT1(1.), 0.1);
  BOOST_CHECK_CLOSE(func(10.), getToT1(10.), 0.1);
  BOOST_CHECK_CLOSE(func(59.5), getToT1(59.5), 0.1);
  BOOST_CHECK_CLOSE(func(99.9), getToT1(99.9), 0.1);
}

BOOST_AUTO_TEST_CASE(getTot_quadratic)
{
  JPetCachedFunctionParams params("pol2", {1., 1., 1.}); /// 1 + x + x^2
  JPetCachedFunction1D func(params, Range(10000, 0., 100.));
  BOOST_CHECK(func.getParams().fValidFunction);
  BOOST_CHECK_CLOSE(func(0), 1., 0.1);
  BOOST_CHECK_CLOSE(func(1), 3., 0.1);
  BOOST_CHECK_CLOSE(func(2), 7., 0.1);
  BOOST_CHECK_CLOSE(func(3), 13., 0.1);
}

BOOST_AUTO_TEST_CASE(cached_2D)
{
  JPetCachedFunctionParams params("[0] + [1] * x  + [2] * y", {1., 1., 2.}); /// 1 + x + 2 * y
  JPetCachedFunction2D func(params, Range(100, 0., 100.), Range(100, 0., 100.));
  BOOST_CHECK(func.getParams().fValidFunction);
  BOOST_CHECK_CLOSE(func(0., 0.), 1., 0.1);
  BOOST_CHECK_CLOSE(func(1, 1.), 4., 0.1);
  BOOST_CHECK_CLOSE(func(0., 1.), 3., 0.1);
  BOOST_CHECK_CLOSE(func(1., 0.), 2., 0.1);
}

BOOST_AUTO_TEST_SUITE_END()


//==================================================================================================
/*!
  @file

  @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_FUNCTION_EPS_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_EPS_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{
  /*!

    @ingroup group-ieee
    Function object implementing eps capabilities

    This is the distance between x and the next representable value of x's type.

    @par Semantic:

    For every parameter of type @c T

    @code
    T r = eps(x);
    @endcode

    is similar to:

    @code

    if T is floating
      T  r = 2^(exponent(x))*Eps<T>()
    else if T is integral
      T r = 1;
    @endcode

    @see ulp, ulpdist, Eps
**/
  Value eps(Value const & v0);

} }
#endif

#include <boost/simd/function/scalar/eps.hpp>
#include <boost/simd/function/simd/eps.hpp>

#endif


#ifndef LIBKRIGING_ORDINARYKRIGING_HPP
#define LIBKRIGING_ORDINARYKRIGING_HPP

#include <armadillo>

#include "libKriging/libKriging_exports.h"
// #include "covariance.h"

/** Ordinary kriging regression
 * @ingroup Regression
 */
class OrdinaryKriging {
 public:
  struct Parameters {
    double sigma2;
    bool has_sigma2;
    arma::vec theta;
    bool has_theta;
  };

  enum class RegressionModel { Constant, Linear, Quadratic };

  struct RegressionModelUtils {
    LIBKRIGING_EXPORT static RegressionModel fromString(const std::string & s);
    LIBKRIGING_EXPORT static std::string toString(const RegressionModel & m);
  };

  const arma::mat& X() const { return m_X; };
  const arma::rowvec& centerX() const { return m_centerX; };
  const arma::rowvec& scaleX() const { return m_scaleX; };
  const arma::colvec& y() const { return m_y; };
  const double& centerY() const { return m_centerY; };
  const double& scaleY() const { return m_scaleY; };
  const RegressionModel& regmodel() const { return m_regmodel; };
  const arma::mat& F() const { return m_F; };
  const arma::mat& T() const { return m_T; };
  const arma::mat& M() const { return m_M; };
  const arma::colvec& z() const { return m_z; };
  const arma::colvec& beta() const { return m_beta; };
  const arma::vec& theta() const { return m_theta; };
  const double& sigma2() const { return m_sigma2; };

 private:
  arma::mat m_X;
  arma::rowvec m_centerX;
  arma::rowvec m_scaleX;
  arma::colvec m_y;
  double m_centerY;
  double m_scaleY;
  RegressionModel m_regmodel;
  arma::mat m_F;
  arma::mat m_T;
  arma::mat m_M;
  arma::colvec m_z;
  arma::colvec m_beta;
  arma::vec m_theta;
  double m_sigma2;

  std::function<double(const arma::vec&, const arma::vec&)> CovNorm_fun;  // Covariance function on normalized data
  std::function<double(const arma::vec&, const arma::vec&, int)>
      CovNorm_deriv;  // Covariance function derivative vs. theta

  // returns distance matrix form Xp to X
  LIBKRIGING_EXPORT arma::mat Cov(const arma::mat& X, const arma::mat& Xp);
  LIBKRIGING_EXPORT arma::mat Cov(const arma::mat& X);
  //  // same for one point
  //  LIBKRIGING_EXPORT arma::colvec Cov(const arma::mat& X, const arma::rowvec& x, const arma::colvec& theta);

  // This will create the dist(xi,xj) function above. Need to parse "kernel".
  void make_Cov(const std::string& covType);

 public:
  struct OKModel {
    arma::colvec y;
    arma::mat X;
    arma::mat F;
    arma::mat T;
    arma::mat M;
    arma::colvec z;
    arma::colvec beta;
    std::function<double(const arma::vec&, const arma::vec&)> covnorm_fun;
    std::function<double(const arma::vec&, const arma::vec&, int)> covnorm_deriv;
  };

  // LIBKRIGING_EXPORT double fit_ofn(const arma::vec& theta, arma::vec* grad_out, OKModel* okm_data);

  // at least, just call make_dist(kernel)
  LIBKRIGING_EXPORT OrdinaryKriging();  // const std::string & covType);

  /** Fit the kriging object on (X,y):
   * @param y is n length column vector of output
   * @param X is n*d matrix of input
   * @param regmodel is the regression model to be used for the GP mean (choice between contant, linear, quadratic)
   * @param parameters is starting value for hyper-parameters
   * @param optim_method is an optimizer name from OptimLib, or 'none' to keep parameters unchanged
   * @param optim_objective is 'loo' or 'loglik'. Ignored if optim_method=='none'.
   */
  LIBKRIGING_EXPORT void fit(const arma::colvec& y,
                             const arma::mat& X,
                             const RegressionModel& regmodel = RegressionModel::Constant,
                             bool normalize = false);  //,
  // const Parameters& parameters,
  // const std::string& optim_method,
  // const std::string& optim_objective);

  LIBKRIGING_EXPORT double logLikelihood(const arma::vec& theta);
  LIBKRIGING_EXPORT arma::vec logLikelihoodGrad(const arma::vec& theta);
  LIBKRIGING_EXPORT double loofun(const arma::vec& theta);
  LIBKRIGING_EXPORT arma::vec loofungrad(const arma::vec& theta);

  /** Compute the prediction for given points X'
   * @param Xp is m*d matrix of points where to predict output
   * @param std is true if return also stdev column vector
   * @param cov is true if return also cov matrix between Xp
   * @return output prediction: m means, [m standard deviations], [m*m full covariance matrix]
   */
  LIBKRIGING_EXPORT std::tuple<arma::colvec, arma::colvec, arma::mat> predict(const arma::mat& Xp,
                                                                              bool withStd,
                                                                              bool withCov);

  /** Draw sample trajectories of kriging at given points X'
   * @param Xp is m*d matrix of points where to simulate output
   * @param nsim is number of simulations to draw
   * @return output is m*nsim matrix of simulations at Xp
   */
  LIBKRIGING_EXPORT arma::mat simulate(const int nsim, const arma::mat& Xp, bool cond = true);

  /** Add new conditional data points to previous (X,y)
   * @param newy is m length column vector of new output
   * @param newX is m*d matrix of new input
   * @param optim_method is an optimizer name from OptimLib, or 'none' to keep previously estimated parameters unchanged
   * @param optim_objective is 'loo' or 'loglik'. Ignored if optim_method=='none'.
   */
  LIBKRIGING_EXPORT void update(const arma::vec& newy,
                                const arma::mat& newX,
                                const std::string& optim_method,
                                const std::string& optim_objective);
};

#endif  // LIBKRIGING_ORDINARYKRIGING_HPP


#include <boost/graph/eccentricity.hpp>


#include <iostream>
#include <iterator>
#include <string>
#include <vector>
#include <algorithm>
#include <boost/algorithm/string.hpp>

long no_paren_solution(std::string line) {
  std::string result;
  std::vector<std::string> items;
  split(items, line, boost::is_any_of(" "), boost::token_compress_on);
  if (items.size() == 1) {
    return std::stol(items[0]);
  } else if (items[1] == "*") {
    result = std::to_string(std::stol(items[0]) * std::stol(items[2]));
  } else if (items[1] == "+") {
    result = std::to_string(std::stol(items[0]) + std::stol(items[2]));
  }
  items.erase(items.begin(), items.begin() + 3);
  items.insert(items.begin(), result);
  return no_paren_solution(boost::join(items, " "));
}

long no_paren_solution_ordered(std::string line) {
  std::string result;
  std::vector<std::string> items;
  split(items, line, boost::is_any_of(" "), boost::token_compress_on);
  auto it = std::find(items.begin(), items.end(), "+");
  int first_sum = std::distance(items.begin(), it);
  if (it == items.end()){
    return no_paren_solution(line);
  }
  result = std::to_string(std::stol(items[first_sum - 1]) +
                          std::stol(items[first_sum + 1]));
  items.erase(it-1, it + 2);
  items.insert(it-1, result);
  return no_paren_solution_ordered(boost::join(items, " "));
}

long full_solution(std::string problem,
                   long (*no_paren_func)(std::string line)) {
  int open_par = problem.find_first_of("(");
  if (open_par == std::string::npos){
    return no_paren_func(problem);
  } else {
    int close_par = open_par;
    int n_open = 1;
    while (n_open > 0){
      close_par++;
      if (problem[close_par] == ')'){
        n_open--;
      } else if (problem[close_par] == '('){
        n_open++;
      }
    }
    std::string inside_paren;
    inside_paren = problem.substr(open_par+1, close_par-open_par-1);
    std::string new_problem = problem;
    std::string solved_inside = std::to_string(full_solution(inside_paren, no_paren_func));
    new_problem.replace(open_par, close_par-open_par+1, solved_inside);
    return full_solution(new_problem, no_paren_func);
  }
}

int main() {

  long answer_pt1 = 0;
  long answer_pt2 = 0;

  for (std::string line; std::getline(std::cin, line);){
    long curr_sol_pt1 = full_solution(line, no_paren_solution);
    answer_pt1 += curr_sol_pt1;
    long curr_sol_pt2 = full_solution(line, no_paren_solution_ordered);
    answer_pt2 += curr_sol_pt2;
    // break;
  }
  std::cout << "Part 1: " << answer_pt1 << std::endl;
  std::cout << "Part 2: " << answer_pt2 << std::endl;
}


//
// Created by kamil on 13.03.18.
//

#include "macd.hpp"
#include <fstream>
#include <cmath>
#include <algorithm>
#include <iterator>
#include <functional>
#include <Python.h>
#include <boost/format.hpp>
#include <future>

namespace mn_macd {

    std::shared_ptr<std::vector<double>>
    parse_file(path_t const &file_path, std::locale locale) {
        std::ifstream file(file_path);
        file.imbue(locale);
        auto result = std::make_shared<std::vector<double>>(std::istream_iterator<double>(file),
                                                            std::istream_iterator<double>());
        if (result->size() <=1 ) {
            cerr << "\nERROR: input file empty/corrupted/bad locale" << endl;
            exit(1);
        }
        return result;
    }

    std::shared_ptr<std::vector<double>>
    ema(std::vector<double> const &input, size_t period) {
        if (period > input.size()){
            cerr << "\nERROR: period is bigger than data" << endl;
            exit(2);
        }
        auto result = std::make_shared<std::vector<double>>(period);
        auto data_iterator = input.begin() + period;
        double alpha = 2 / (period - 1.0);
        while (data_iterator != input.end()) {
            double nominator = 0, denominator = 0;
            for (int i = 0; i <= period; ++i) {
                nominator += *(data_iterator - i) * std::pow(1 - alpha, i);
                denominator += std::pow(1 - alpha, i);
            }
            result->push_back(nominator / denominator);
            data_iterator++;
        }
        return result;
    }

    void write_to_file(path_t const &file_path, std::vector<double> const &data) {
        std::ofstream file;
        file.exceptions(std::ofstream::badbit | std::ofstream::failbit);
        try {
            file.open(file_path);
            for (auto const x : data) file << x << '\n';
        } catch (std::ios_base::failure const &e) {
            cerr << "An error occured while writing to file: " << file_path << '\n' << e.what()
                 << endl;
            exit(3);
        }

    }

    void
    py_gen_plots(path_t const &py_script_path,
                 std::string title,
                 path_t const &rate_in,
                 path_t const &signal_in,
                 path_t const &macd_in,
                 path_t const &rate_out,
                 path_t const &macd_signal_out) {

        if (!std::experimental::filesystem::exists(py_script_path)) {
            cerr << "Python program " << py_script_path
                 << " does not exists in current location. It is needed for a plot generation." << endl;
            exit(1);
        };
        Py_SetProgramName(const_cast<wchar_t *>(L"MACD plot generator"));
        Py_Initialize();
        auto file = std::ifstream(py_script_path);
        auto python_code_template = std::move(
                std::string(
                        std::istreambuf_iterator<char>(file),
                        std::istreambuf_iterator<char>()));
        file.close();
        auto python_code = boost::format(python_code_template) %
                           title %
                           signal_in %
                           macd_in %
                           rate_in %
                           macd_signal_out %
                           rate_out;
        PyRun_SimpleString(python_code.str().c_str());
        Py_Finalize();
    }

    std::tuple<
            std::shared_ptr<std::vector<double>>, // macd
            std::shared_ptr<std::vector<double>>, // signal
            std::shared_ptr<std::vector<double>>> // rate
    compute_macd(std::shared_ptr<std::vector<double>> input) {
        auto ema12_thread = std::async(std::launch::async, mn_macd::ema, *input, 12);
        auto ema26_thread = std::async(std::launch::async, mn_macd::ema, *input, 26);
        auto macd = std::make_shared<std::vector<double>>();
        macd->reserve(input->size());
        auto ema12 = ema12_thread.get();
        auto ema26 = ema26_thread.get();

        std::transform(ema12->begin(), ema12->end(), ema26->begin(), std::back_inserter(*macd),
                       std::minus<>()); //MACD = EMA12 - EMA26
        auto signal = mn_macd::ema(*macd, 9);

        // Trim data at beginning
        auto trim_data = [](auto x, size_t offset) { x->erase(x->begin(), x->begin() + offset); };
        trim_data(macd, 35);
        trim_data(signal, 35);
        trim_data(input, 35);
        return std::make_tuple(macd, signal, input);
    }

}


//  test_duration.cpp  ----------------------------------------------------------//

//  Copyright 2008 Howard Hinnant
//  Copyright 2008 Beman Dawes
//  Copyright 2009 Vicente J. Botet Escriba

//  Distributed under the Boost Software License, Version 1.0.
//  See http://www.boost.org/LICENSE_1_0.txt

/*
This code was extracted by Vicente J. Botet Escriba from Beman Dawes time2_demo.cpp which
was derived by Beman Dawes from Howard Hinnant's time2_demo prototype.
Many thanks to Howard for making his code available under the Boost license.
The original code was modified to conform to Boost conventions and to section
20.9 Time utilities [time] of the C++ committee's working paper N2798.
See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2798.pdf.

time2_demo contained this comment:

    Much thanks to Andrei Alexandrescu,
                   Walter Brown,
                   Peter Dimov,
                   Jeff Garland,
                   Terry Golubiewski,
                   Daniel Krugler,
                   Anthony Williams.
*/

#include <boost/assert.hpp>
#include <boost/chrono/chrono.hpp>
#include <boost/type_traits.hpp>

#include <iostream>

template <class Rep, class Period>
void inspect_duration(boost::chrono::duration<Rep, Period> d, const std::string& name)
{
    typedef boost::chrono::duration<Rep, Period> Duration;
    std::cout << "********* " << name << " *********\n";
    std::cout << "The period of " << name << " is " << (double)Period::num/Period::den << " seconds.\n";
    std::cout << "The frequency of " << name << " is " << (double)Period::den/Period::num << " Hz.\n";
    std::cout << "The representation is ";
    if (boost::is_floating_point<Rep>::value)
    {
        std::cout << "floating point\n";
        std::cout << "The precision is the most significant ";
        std::cout << std::numeric_limits<Rep>::digits10 << " decimal digits.\n";
    }
    else if (boost::is_integral<Rep>::value)
    {
        std::cout << "integral\n";
        d = Duration(Rep(1));
        boost::chrono::duration<double> dsec = d;
        std::cout << "The precision is " << dsec.count() << " seconds.\n";
    }
    else
    {
        std::cout << "a class type\n";
        d = Duration(Rep(1));
        boost::chrono::duration<double> dsec = d;
        std::cout << "The precision is " << dsec.count() << " seconds.\n";
    }
    d = Duration((std::numeric_limits<Rep>::max)());
    using namespace boost::chrono;
    typedef duration<double, boost::ratio_multiply<boost::ratio<24*3652425,10000>, hours::period>::type> Years;
    Years years = d;
    std::cout << "The range is +/- " << years.count() << " years.\n";
    std::cout << "sizeof(" << name << ") = " << sizeof(d) << '\n';
}

void inspect_all()
{
    using namespace boost::chrono;
    std::cout.precision(6);
    inspect_duration(nanoseconds(), "nanoseconds");
    inspect_duration(microseconds(), "microseconds");
    inspect_duration(milliseconds(), "milliseconds");
    inspect_duration(seconds(), "seconds");
    inspect_duration(minutes(), "minutes");
    inspect_duration(hours(), "hours");
    inspect_duration(duration<double>(), "duration<double>");
}

using namespace boost::chrono;
void test_duration_division()
{
    typedef boost::common_type<boost::chrono::hours::rep, boost::chrono::minutes::rep>::type h_min_rep;
    h_min_rep r3 = hours(3) / minutes(5);
    std::cout << r3 << '\n';
    std::cout << hours(3) / minutes(5) << '\n';
    std::cout << hours(3) / milliseconds(5) << '\n';
    std::cout << milliseconds(5) / hours(3) << '\n';
    std::cout << hours(1) / milliseconds(1) << '\n';
}

void test_duration_multiply()
{
    hours h15= 5 * hours(3);
    hours h6= hours(3) *2;
}

void f(duration<double> d, double res)  // accept floating point seconds
{
    // d.count() == 3.e-6 when passed microseconds(3)
    BOOST_ASSERT(d.count()==res);
}

void g(nanoseconds d, boost::intmax_t res)
{
    // d.count() == 3000 when passed microseconds(3)
    std::cout << d.count() << " " <<res << std::endl;
    BOOST_ASSERT(d.count()==res);
}

template <class Rep, class Period>
void tmpl(duration<Rep, Period> d, boost::intmax_t res)
{
    // convert d to nanoseconds, rounding up if it is not an exact conversion
    nanoseconds ns = duration_cast<nanoseconds>(d);
    if (ns < d)
        ++ns;
    // ns.count() == 333333334 when passed 1/3 of a floating point second
    BOOST_ASSERT(ns.count()==res);
}

template <class Period>
void tmpl2(duration<long long, Period> d, boost::intmax_t res)
{
    // convert d to nanoseconds, rounding up if it is not an exact conversion
    nanoseconds ns = duration_cast<nanoseconds>(d);
    if (ns < d)
        ++ns;
    // ns.count() == 333333334 when passed 333333333333 picoseconds
    BOOST_ASSERT(ns.count()==res);
}

int main()
{
    minutes m1(3);                 // m1 stores 3
    minutes m2(2);                 // m2 stores 2
    minutes m3 = m1 + m2;          // m3 stores 5
    BOOST_ASSERT(m3.count()==5);

    microseconds us1(3);           // us1 stores 3
    microseconds us2(2);           // us2 stores 2
    microseconds us3 = us1 + us2;  // us3 stores 5
    BOOST_ASSERT(us3.count()==5);

    microseconds us4 = m3 + us3;   // us4 stores 300000005
    BOOST_ASSERT(us4.count()==300000005);
    microseconds us5 = m3;   // us4 stores 300000000
    BOOST_ASSERT(us5.count()==300000000);

    //minutes m4 = m3 + us3; // won't compile

    minutes m4 = duration_cast<minutes>(m3 + us3);  // m4.count() == 5
    BOOST_ASSERT(m4.count()==5);

    typedef duration<double, boost::ratio<60> > dminutes;
    dminutes dm4 = m3 + us3;  // dm4.count() == 5.000000083333333
    BOOST_ASSERT(dm4.count()==5.000000083333333);

    f(microseconds(3), 0.000003);
    g(microseconds(3), 3000);
    duration<double> s(1./3);  // 1/3 of a second
    g(duration_cast<nanoseconds>(s), 333333333);  // round towards zero in conversion to nanoseconds
    //f(s);  // does not compile
    tmpl(duration<double>(1./3), 333333334);
    tmpl2(duration<long long, boost::pico>(333333333333LL), 333333334);  // About 1/3 of a second worth of picoseconds

    //f(3,3);  // Will not compile, 3 is not implicitly convertible to any `duration`
    //g(3,3);  // Will not compile, 3 is not implicitly convertible to any `duration`
    //tmpl(3,3);  // Will not compile, 3 is not implicitly convertible to any `duration`
    //tmpl2(3,3);  // Will not compile, 3 is not implicitly convertible to any `duration`

    {
    double r = double(milliseconds(3) / milliseconds(3));
    std::cout << r << '\n';

    duration<double, boost::milli> d = milliseconds(3) * 2.5;
    duration<double, boost::milli> d2 = 2.5 * milliseconds(3) ;
    duration<double, boost::milli> d3 = milliseconds(3) / 2.5;
    duration<double, boost::milli> d4 = milliseconds(3) + milliseconds(5) ;
    inspect_duration(milliseconds(3) * 2.5, "milliseconds(3) * 2.5");
    std::cout << d.count() << '\n';
//    milliseconds ms(3.5);  // doesn't compile
    std::cout << "milliseconds ms(3.5) doesn't compile\n";
    }

    test_duration_division();
    test_duration_multiply();
    return 0;
}


//////////////////////////////////////////////////////////////////////////////////////////////
/// \file NoiseModel.hpp
///
/// \author Sean Anderson, ASRL
//////////////////////////////////////////////////////////////////////////////////////////////

#ifndef STEAM_NOISE_MODEL_HPP
#define STEAM_NOISE_MODEL_HPP

#include <Eigen/Dense>
#include <mutex>

namespace steam {

/// @class NoiseEvaluator evaluates uncertainty based on a derived model.
template <int MEAS_DIM>
class NoiseEvaluator {
public:
  /// Convenience typedefs
  typedef std::shared_ptr<NoiseEvaluator<MEAS_DIM> > Ptr;
  typedef std::shared_ptr<const NoiseEvaluator<MEAS_DIM> > ConstPtr;

  /// \brief Default constructor.
  NoiseEvaluator()=default;

  /// \brief Default destructor.
  virtual ~NoiseEvaluator()=default;

  /// \brief mutex locked public exposure of the virtual evaluate() function.
  virtual Eigen::Matrix<double,MEAS_DIM,MEAS_DIM> evaluateCovariance() {
    std::lock_guard<std::mutex> lock(eval_mutex_);
    return evaluate();
  }

protected:
  /// \brief Evaluates the uncertainty based on a derived model.
  /// \return the uncertainty, in the form of a covariance matrix.
  virtual Eigen::Matrix<double,MEAS_DIM,MEAS_DIM> evaluate()=0;

private:
  std::mutex eval_mutex_;
};

/// Enumeration of ways to set the noise
enum MatrixType { COVARIANCE, INFORMATION, SQRT_INFORMATION };

/// @class BaseNoiseModel Base class for the steam noise models
template <int MEAS_DIM>
class BaseNoiseModel
{
 public:

  /// \brief Default constructor.
  BaseNoiseModel()=default;

  /// \brief Constructor
  /// \brief A noise matrix, determined by the type parameter.
  /// \brief The type of noise matrix set in the previous paramter.
  BaseNoiseModel(const Eigen::Matrix<double,MEAS_DIM,MEAS_DIM>& matrix,
             MatrixType type = COVARIANCE);

  /// \brief Deault destructor
  virtual ~BaseNoiseModel() = default;

  /// Convenience typedefs
  typedef std::shared_ptr<BaseNoiseModel<MEAS_DIM> > Ptr;
  typedef std::shared_ptr<const BaseNoiseModel<MEAS_DIM> > ConstPtr;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Set by covariance matrix
  //////////////////////////////////////////////////////////////////////////////////////////////
  void setByCovariance(const Eigen::Matrix<double,MEAS_DIM,MEAS_DIM>& matrix) const;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Set by information matrix
  //////////////////////////////////////////////////////////////////////////////////////////////
  void setByInformation(const Eigen::Matrix<double,MEAS_DIM,MEAS_DIM>& matrix) const ;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Set by square root of information matrix
  //////////////////////////////////////////////////////////////////////////////////////////////
  void setBySqrtInformation(const Eigen::Matrix<double,MEAS_DIM,MEAS_DIM>& matrix) const ;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Get a reference to the square root information matrix
  //////////////////////////////////////////////////////////////////////////////////////////////
  virtual const Eigen::Matrix<double,MEAS_DIM,MEAS_DIM>& getSqrtInformation() const = 0;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Get the norm of the whitened error vector, sqrt(rawError^T * info * rawError)
  //////////////////////////////////////////////////////////////////////////////////////////////
  virtual double getWhitenedErrorNorm(const Eigen::Matrix<double,MEAS_DIM,1>& rawError) const = 0;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Get the whitened error vector, sqrtInformation*rawError
  //////////////////////////////////////////////////////////////////////////////////////////////
  virtual Eigen::Matrix<double,MEAS_DIM,1> whitenError(
      const Eigen::Matrix<double,MEAS_DIM,1>& rawError) const = 0;

 protected:

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Assert that the matrix is positive definite
  //////////////////////////////////////////////////////////////////////////////////////////////
  void assertPositiveDefiniteMatrix(const Eigen::Matrix<double,MEAS_DIM,MEAS_DIM>& matrix) const;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief The square root information (found by performing an LLT decomposition on the
  ///        information matrix (inverse covariance matrix). This triangular matrix is
  ///        stored directly for faster error whitening.
  //////////////////////////////////////////////////////////////////////////////////////////////
  mutable Eigen::Matrix<double,MEAS_DIM,MEAS_DIM> sqrtInformation_;

 private:

};

/// @class StaticNoiseModel Noise model for uncertainties that do not change during the
///        steam optimization problem.
template <int MEAS_DIM>
class StaticNoiseModel : public BaseNoiseModel<MEAS_DIM>
{
 public:

  /// Convenience typedefs
  typedef std::shared_ptr<StaticNoiseModel<MEAS_DIM> > Ptr;
  typedef std::shared_ptr<const StaticNoiseModel<MEAS_DIM> > ConstPtr;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Default constructor
  //////////////////////////////////////////////////////////////////////////////////////////////
  StaticNoiseModel();

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief General constructor
  //////////////////////////////////////////////////////////////////////////////////////////////
  StaticNoiseModel(const Eigen::Matrix<double,MEAS_DIM,MEAS_DIM>& matrix,
             MatrixType type = COVARIANCE);

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Get a reference to the square root information matrix
  //////////////////////////////////////////////////////////////////////////////////////////////
  virtual const Eigen::Matrix<double,MEAS_DIM,MEAS_DIM>& getSqrtInformation() const;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Get the norm of the whitened error vector, sqrt(rawError^T * info * rawError)
  //////////////////////////////////////////////////////////////////////////////////////////////
  virtual double getWhitenedErrorNorm(const Eigen::Matrix<double,MEAS_DIM,1>& rawError) const;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Get the whitened error vector, sqrtInformation*rawError
  //////////////////////////////////////////////////////////////////////////////////////////////
  virtual Eigen::Matrix<double,MEAS_DIM,1> whitenError(
      const Eigen::Matrix<double,MEAS_DIM,1>& rawError) const;

private:

};

/// \brief DynamicNoiseModel Noise model for uncertainties that change during the steam optimization
///        problem.
template <int MEAS_DIM>
class DynamicNoiseModel : public BaseNoiseModel<MEAS_DIM>
{
 public:
  /// \brief Constructor
  /// \param eval a pointer to a noise evaluator.
  DynamicNoiseModel(std::shared_ptr<NoiseEvaluator<MEAS_DIM>> eval);

  /// \brief Deault destructor.
  ~DynamicNoiseModel()=default;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Get a reference to the square root information matrix
  //////////////////////////////////////////////////////////////////////////////////////////////
  virtual const Eigen::Matrix<double,MEAS_DIM,MEAS_DIM>& getSqrtInformation() const;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Get the norm of the whitened error vector, sqrt(rawError^T * info * rawError)
  //////////////////////////////////////////////////////////////////////////////////////////////
  virtual double getWhitenedErrorNorm(const Eigen::Matrix<double,MEAS_DIM,1>& rawError) const;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Get the whitened error vector, sqrtInformation*rawError
  //////////////////////////////////////////////////////////////////////////////////////////////
  virtual Eigen::Matrix<double,MEAS_DIM,1> whitenError(
      const Eigen::Matrix<double,MEAS_DIM,1>& rawError) const;

 private:
  /// \brief A pointer to a noise evaluator.
  std::shared_ptr<NoiseEvaluator<MEAS_DIM>> eval_;
};

//////////////////////////////////////////////////////////////////////////////////////////////
/// \brief Typedef for the general base noise model
//////////////////////////////////////////////////////////////////////////////////////////////
typedef BaseNoiseModel<Eigen::Dynamic> BaseNoiseModelX;

//////////////////////////////////////////////////////////////////////////////////////////////
/// \brief Typedef for the general static noise model
//////////////////////////////////////////////////////////////////////////////////////////////
typedef StaticNoiseModel<Eigen::Dynamic> StaticNoiseModelX;

//////////////////////////////////////////////////////////////////////////////////////////////
/// \brief Typedef for the general dynamic noise model
//////////////////////////////////////////////////////////////////////////////////////////////
typedef DynamicNoiseModel<Eigen::Dynamic> DynamicNoiseModelX;

} // steam

#include <steam/problem/NoiseModel-inl.hpp>

#endif // STEAM_NOISE_MODEL_HPP


//////////////////////////////////////////////////////////////////////////////////////////////
/// \file GemanMcClureLossFunc.hpp
///
/// \author Sean Anderson, ASRL
//////////////////////////////////////////////////////////////////////////////////////////////

#ifndef STEAM_GM_LOSS_FUNCTION_HPP
#define STEAM_GM_LOSS_FUNCTION_HPP

#include <Eigen/Core>

#include <steam/problem/lossfunc/LossFunctionBase.hpp>

namespace steam {

//////////////////////////////////////////////////////////////////////////////////////////////
/// \brief Geman-McClure loss function class
//////////////////////////////////////////////////////////////////////////////////////////////
class GemanMcClureLossFunc : public LossFunctionBase
{
 public:

  /// Convenience typedefs
  typedef std::shared_ptr<GemanMcClureLossFunc> Ptr;
  typedef std::shared_ptr<const GemanMcClureLossFunc> ConstPtr;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Constructor -- k is the `threshold' based on number of std devs (1-3 is typical)
  //////////////////////////////////////////////////////////////////////////////////////////////
  GemanMcClureLossFunc(double k) : k2_(k*k) {}

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Cost function (basic evaluation of the loss function)
  //////////////////////////////////////////////////////////////////////////////////////////////
  virtual double cost(double whitened_error_norm) const;

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief Weight for iteratively reweighted least-squares (influence function div. by error)
  //////////////////////////////////////////////////////////////////////////////////////////////
  virtual double weight(double whitened_error_norm) const;

 private:

  //////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief GM constant
  //////////////////////////////////////////////////////////////////////////////////////////////
  double k2_;
};

} // steam

#endif // STEAM_GM_LOSS_FUNCTION_HPP


/**
 *  @file    Problem.hpp
 *  @brief   Define a finite difference problem.
 *  @author  Francois Roy
 *  @date    12/01/2019
 */
#ifndef PROBLEM_H
#define PROBLEM_H

#include <map>
#include <string>
#include <vector>
#include <Eigen/SparseCore>
#include "spdlog/spdlog.h"
#include "Parameters.hpp"
#include "Mesh.hpp"

namespace numerical {

namespace fdm {

/*
 * This class defines the finite difference problem of the diffusion type:
 *
 * /f[
 *    \frac{\partial u}{\partial t} = \nabla\left(\alpha\nabla u) + f
 * /f]
 *
 * over the hypercube.
 */
template <typename T>
class Problem {
typedef Eigen::SparseMatrix<T> SpMat;
typedef Eigen::Triplet<T> Trip;
typedef Eigen::Matrix<T, Eigen::Dynamic, 1> Vec;
private:
    int m_dim;

protected:
    Parameters<T>* m_params;
    Vec m_u;  // The solution vector
public:
    Problem(Parameters<T>* params):
      m_params(params) {
        // define other variables variables
        m_dim = m_params->lengths.size();
  	    // define mesh
  	    Mesh<T> mesh = Mesh<T>(m_params->lengths, m_params->t0, m_params->tend,
          m_params->n, m_params->nt);
    }
    virtual ~Problem(){
    }
    /**
    * Diffusion coefficient value. The default is a constant obtained from
    * m_params.
    *
    * @param x The x-coordinate of the mesh node.
    * @param y The y-coordinate of the mesh node.
    * @param z The z-coordinate of the mesh node.
    * @param t The discrete time.
    * @return The diffusion coefficient value at a specified mesh location.
    */
    virtual T alpha(T x, T y, T z, T t){
  	    return m_params->alpha;
    }

    /**
    * @return The spatial dimension of the problem.
    */
    int dim(){
    	return m_dim;
    }

    /**
    * Left boundary value, i.e. for x = lengths[0][0].
    *
    * @param type Dirichlet = 0, Neumann = 1.
    * @param x1 The first coordinate of the mesh node.
    * @param x2 The second coordinate of the mesh node.
    * @param t The discrete time.
    * @return The boundary value at a specified mesh location.
    */
    virtual T left(int type, T x1, T x2, T t){
  	    return 0.0;
    }

    /**
    * Right boundary value, i.e. for x = lengths[0][1].
    *
    * @param type Dirichlet = 0, Neumann = 1.
    * @param x1 The first coordinate of the mesh node.
    * @param x2 The second coordinate of the mesh node.
    * @param t The discrete time.
    * @return The boundary value at a specified mesh location.
    */
    virtual T right(int type, T x1, T x2, T t){
        return 0.0;
    }

    /**
    * Bottom boundary value, i.e. at y = lengths[1][0]. Only for 2D and
    * 3D models.
    *
    * @param type Dirichlet = 0, Neumann = 1.
    * @param x1 The first coordinate of the mesh node.
    * @param x2 The second coordinate of the mesh node.
    * @param t The discrete time.
    * @return The boundary value at a specified mesh location.
    */
    virtual T bottom(int type, T x1, T x2, T t){
        return 0.0;
    }

    /**
    * Top boundary value, i.e. at y = lengths[1][1]. Only for 2D and 3D models.
    *
    * @param type Dirichlet = 0, Neumann = 1.
    * @param x1 The first coordinate of the mesh node.
    * @param x2 The second coordinate of the mesh node.
    * @param t The discrete time.
    * @return The boundary value at a specified mesh location.
    */
    virtual T top(int type, T x1, T x2, T t){
        return 0.0;
    }

    /**
    * Front boundary value, i.e. at cz = length[2][0]. Only for 3D models.
    *
    * @param type Dirichlet = 0, Neumann = 1.
    * @param x1 The first coordinate of the mesh node.
    * @param x2 The second coordinate of the mesh node.
    * @param t The discrete time.
    * @return The boundary value at a specified mesh location.
    */
    virtual T front(int type, T x1, T x2, T t){
        return 0.0;
    }

    /**
    * Back boundary value, i.e. at z = length[2][0]. Only for 3D models.
    *
    * @param type Dirichlet = 0, Neumann = 1.
    * @param x1 The first coordinate of the mesh node.
    * @param x2 The second coordinate of the mesh node.
    * @param t The discrete time.
    * @return The boundary value at a specified mesh location.
    */
    virtual T back(int type, T x1, T x2, T t){
        return 0.0;
    }

    /**
    * Initial  value.
    *
    * @param x The x-coordinate of the mesh node.
    * @param y The y-coordinate of the mesh node.
    * @param z The z-coordinate of the mesh node.
    * @return The initial value at a specified mesh location.
    */
    virtual T initial_value(T x, T y, T z){
  	    return 0.0;
    }

    /**
    *  Get the reference solution at a specified mesh location.
    *
    * @param x The x-coordinate of the mesh node.
    * @param y The y-coordinate of the mesh node.
    * @param z The z-coordinate of the mesh node.
    * @param t The discrete time.
    * @return The reference solution at a specified mesh location.
    */
    virtual T reference(T x, T y, T z, T t){
    	return 0.0;
    }

    /**
    *  Get the computed solution at a specified mesh location.
    *
    * @param x The x-coordinate of the mesh node.
    * @param y The y-coordinate of the mesh node.
    * @param z The z-coordinate of the mesh node.
    * @param t The discrete time.
    * @return The computed solution at a specified mesh location.
    */
    virtual T solution(T x, T y, T z, T t){
    	return 0.0;
    }

    /**
    * Source term.
    *
    * @param x The x-coordinate of the mesh node.
    * @param y The y-coordinate of the mesh node.
    * @param z The z-coordinate of the mesh node.
    * @param t The discrete time.
    * @return The source term at a specified mesh location.
    */
    virtual T source(T x, T y, T z, T t){
  	    return 0.0;
    }

};

}  // namespace fdm

}  // namespace numerical

#endif  // PROBLEM_H