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

//! @author Jeff Ichnowski

#pragma once
#ifndef NIGH_IMPL_K_CENTERS_HPP
#define NIGH_IMPL_K_CENTERS_HPP

#include <Eigen/Dense>
#include <vector>
#include <random>
#include <iostream> // TODO: REMOVE

namespace unc::robotics::nigh::impl {
    template <typename T, typename Distance, unsigned maxK, unsigned maxDataSize>
    class KCenters {
        Eigen::Matrix<Distance, Eigen::Dynamic, Eigen::Dynamic, Eigen::AutoAlign, maxDataSize, maxK> dists_;
        std::size_t numCenters_;
        std::array<unsigned, maxK> centers_;

    public:

        Distance dist(int i, int j) const {
            return dists_(i, j);
        }

        std::size_t numCenters() const {
            return numCenters_;
        }

        unsigned center(std::size_t i) const {
            assert(i < numCenters_);
            return centers_[i];
        }

        template <typename RNG, typename DistFn>
        void compute(const std::vector<T>& data, unsigned k, RNG& rng, const DistFn& distFn) {
            assert(k <= maxK);

            Eigen::Matrix<Distance, Eigen::Dynamic, 1, Eigen::AutoAlign, maxDataSize, 1> minDist(data.size(), 1);
            minDist.fill(std::numeric_limits<Distance>::infinity());
            numCenters_ = 0;

            dists_.resize(data.size(), k);

            std::uniform_int_distribution<unsigned> uniformInt(0, data.size() - 1);
            centers_[numCenters_++] = uniformInt(rng);

            for (unsigned i = 1 ; i<k ; ++i) {
                unsigned center = centers_[i-1];
                Distance maxDist = -std::numeric_limits<Distance>::infinity();
                unsigned int maxIndex = 0;
                for (unsigned j = 0 ; j<data.size() ; ++j) {
                    if ((dists_(j, i-1) = distFn(data[j], data[center])) < minDist[j])
                        minDist[j] = dists_(j, i-1);
                    if (minDist[j] > maxDist)
                        maxDist = minDist[maxIndex = j];
                }

                if (maxDist < std::numeric_limits<Distance>::epsilon())
                    break;

                centers_[numCenters_++] = maxIndex;
            }

            unsigned i = numCenters_ - 1;
            unsigned center = centers_[i];
            for (unsigned j=0 ; j<data.size() ; ++j)
                dists_(j, i) = distFn(data[j], data[center]);
        }
    };
}

#endif


/* test_hyperexponential_distribution.ipp
 *
 * Copyright 2014 Marco Guazzone (marco.guazzone@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)
 *
 */

#include <boost/math/tools/precision.hpp>
#include <boost/random/hyperexponential_distribution.hpp>
#include <boost/random/linear_congruential.hpp>
#include <boost/random/lagged_fibonacci.hpp>
#include <boost/assign/list_of.hpp>
#include <limits>
#include <sstream>
#include <vector>

#include "concepts.hpp"

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

#define BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(T, actual, expected, tol)\
    do {                                                                       \
        std::vector<T> x = (actual);                                           \
        std::vector<T> y = (expected);                                         \
        BOOST_CHECK_EQUAL( x.size(), y.size() );                               \
        const std::size_t n = x.size();                                        \
        for (std::size_t i = 0; i < n; ++i)                                    \
        {                                                                      \
            BOOST_CHECK_CLOSE( x[i], y[i], tol );                              \
        }                                                                      \
    } while(false)

namespace /*<unnamed>*/ { namespace detail {

template <typename RealT>
RealT make_tolerance()
{
    // Tolerance is 100eps expressed as a percentage (as required by Boost.Build):
    return boost::math::tools::epsilon<RealT>() * 100 * 100;
}

}} // Namespace <unnamed>::detail

BOOST_CONCEPT_ASSERT((boost::random::test::RandomNumberDistribution< boost::random::hyperexponential_distribution<> >));

BOOST_AUTO_TEST_CASE( test_constructors )
{
    const double tol = detail::make_tolerance<double>();

    // Test default ctor
    boost::random::hyperexponential_distribution<> dist;
    BOOST_CHECK_EQUAL(dist.num_phases(), 1u);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist.probabilities(), boost::assign::list_of(1.0), tol);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist.rates(), boost::assign::list_of(1.0), tol);

#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) && !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)
    // Test ctor from initializer_list with probabilities and rates
    boost::random::hyperexponential_distribution<> dist_il_p_r = {{1, 2, 3, 4 }, {1, 2, 3, 4}};
    BOOST_CHECK_EQUAL(dist_il_p_r.num_phases(), 4u);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_il_p_r.probabilities(), boost::assign::list_of(.1)(.2)(.3)(.4), tol);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_il_p_r.rates(), boost::assign::list_of(1.)(2.)(3.)(4.), tol);

    // Test ctor from initializer_list with rates
    boost::random::hyperexponential_distribution<> dist_il_r = {{1, 2, 3, 4}};
    BOOST_CHECK_EQUAL(dist_il_r.num_phases(), 4u);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_il_r.probabilities(), boost::assign::list_of(.25)(.25)(.25)(.25), tol);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_il_r.rates(), boost::assign::list_of(1.)(2.)(3.)(4.), tol);
#endif

    const std::vector<double> probs = boost::assign::list_of(0.1)(0.2)(0.3)(0.4);
    const std::vector<double> rates = boost::assign::list_of(1.0)(2.0)(3.0)(4.0);

    // Test ctor from range
    boost::random::hyperexponential_distribution<> dist_r(probs, rates);
    BOOST_CHECK_EQUAL(dist_r.num_phases(), 4u);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_r.probabilities(), probs, tol);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_r.rates(), rates, tol);

    // Test ctor from iterators
    boost::random::hyperexponential_distribution<> dist_it(probs.begin(), probs.end(), rates.begin(), rates.end());
    BOOST_CHECK_EQUAL(dist_it.num_phases(), 4u);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_it.probabilities(), probs, tol);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_it.rates(), rates, tol);

    // Test ctor from rate iterators
    boost::random::hyperexponential_distribution<> dist_r_it(rates.begin(), rates.end());
    BOOST_CHECK_EQUAL(dist_r_it.num_phases(), 4u);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_r_it.probabilities(), boost::assign::list_of(.25)(.25)(.25)(.25), tol);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_r_it.rates(), rates, tol);

    // Test ctor from rate iterators #2
	{
        const double rates2[] = {1.0,2.0,3.0,4.0};
        boost::random::hyperexponential_distribution<> dist_r_it(rates2, rates2+4);
        BOOST_CHECK_EQUAL(dist_r_it.num_phases(), 4u);
        BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_r_it.probabilities(), boost::assign::list_of(.25)(.25)(.25)(.25), tol);
        BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_r_it.rates(), std::vector<double>(rates2, rates2+4), tol);
    }

    // Test ctor from rate range
    boost::random::hyperexponential_distribution<> dist_r_r(rates);
    BOOST_CHECK_EQUAL(dist_r_r.num_phases(), 4u);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_r_r.probabilities(), boost::assign::list_of(.25)(.25)(.25)(.25), tol);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist_r_r.rates(), rates, tol);

    // Test copy ctor
    boost::random::hyperexponential_distribution<> cp(dist);
    BOOST_CHECK_EQUAL(cp, dist);
    boost::random::hyperexponential_distribution<> cp_r(dist_r);
    BOOST_CHECK_EQUAL(cp_r, dist_r);
}

BOOST_AUTO_TEST_CASE( test_param )
{
    const double tol = detail::make_tolerance<double>();

    const std::vector<double> probs = boost::assign::list_of(0.1)(0.2)(0.3)(0.4);
    const std::vector<double> rates = boost::assign::list_of(1.0)(2.0)(3.0)(4.0);

    // Test param getter
    boost::random::hyperexponential_distribution<> dist(probs, rates);
    boost::random::hyperexponential_distribution<>::param_type param = dist.param();
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist.probabilities(), param.probabilities(), tol);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist.rates(), param.rates(), tol);

    // Test ctor from param
    boost::random::hyperexponential_distribution<> cp1(param);
    BOOST_CHECK_EQUAL(cp1, dist);

    // Test param setter
    boost::random::hyperexponential_distribution<> cp2;
    cp2.param(param);
    BOOST_CHECK_EQUAL(cp2, dist);

    // Test param constructors & operators
    boost::random::hyperexponential_distribution<>::param_type param_copy = param;
    BOOST_CHECK_EQUAL(param, param_copy);
    BOOST_CHECK(param == param_copy);
    BOOST_CHECK(!(param != param_copy));
    boost::random::hyperexponential_distribution<>::param_type param_default;
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, param_default.probabilities(), boost::assign::list_of(1.0), tol);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, param_default.rates(), boost::assign::list_of(1.0), tol);
    BOOST_CHECK(param != param_default);
    BOOST_CHECK(!(param == param_default));
#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) && !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)
    boost::random::hyperexponential_distribution<>::param_type param_il = {{1, 2, 3, 4 }, {1, 2, 3, 4}};
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, param_il.probabilities(), boost::assign::list_of(.1)(.2)(.3)(.4), tol);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, param_il.rates(), boost::assign::list_of(1.)(2.)(3.)(4.), tol);
#endif
    boost::random::hyperexponential_distribution<>::param_type param_r(probs, rates);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, param_r.probabilities(), probs, tol);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, param_r.rates(), rates, tol);

    boost::random::hyperexponential_distribution<>::param_type param_it(probs.begin(), probs.end(), rates.begin(), rates.end());
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, param_it.probabilities(), probs, tol);
    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, param_it.rates(), rates, tol);
}

BOOST_AUTO_TEST_CASE( test_min_max )
{
    //const double tol = detail::make_tolerance<double>();

    const std::vector<double> probs = boost::assign::list_of(0.1)(0.2)(0.3)(0.4);
    const std::vector<double> rates = boost::assign::list_of(1.0)(2.0)(3.0)(4.0);

    boost::random::hyperexponential_distribution<> dist;
    BOOST_CHECK_EQUAL((dist.min)(), 0);
    BOOST_CHECK_EQUAL((dist.max)(), (std::numeric_limits<double>::infinity)());

    boost::random::hyperexponential_distribution<> dist_r(probs, rates);
    BOOST_CHECK_EQUAL((dist_r.min)(), 0);
    BOOST_CHECK_EQUAL((dist_r.max)(), (std::numeric_limits<double>::infinity)());
}

BOOST_AUTO_TEST_CASE(test_comparison)
{
    //const double tol = detail::make_tolerance<double>();

    const std::vector<double> probs = boost::assign::list_of(0.1)(0.2)(0.3)(0.4);
    const std::vector<double> rates = boost::assign::list_of(1.0)(2.0)(3.0)(4.0);

    boost::random::hyperexponential_distribution<> dist;
    boost::random::hyperexponential_distribution<> dist_copy(dist);

    boost::random::hyperexponential_distribution<> dist_r(probs, rates);
    boost::random::hyperexponential_distribution<> dist_r_copy(dist_r);

    BOOST_CHECK(dist == dist_copy);
    BOOST_CHECK(!(dist != dist_copy));
    BOOST_CHECK(dist_r == dist_r_copy);
    BOOST_CHECK(!(dist_r != dist_r_copy));
    BOOST_CHECK(dist != dist_r);
    BOOST_CHECK(!(dist == dist_r));
}

BOOST_AUTO_TEST_CASE( test_streaming )
{
    //const double tol = detail::make_tolerance<double>();

    const std::vector<double> probs = boost::assign::list_of(0.1)(0.2)(0.3)(0.4);
    const std::vector<double> rates = boost::assign::list_of(1.0)(2.0)(3.0)(4.0);
    const std::vector<double> empty_vector;

    // Test the reading of param_type

    // - Test with valid input
    {
        boost::random::hyperexponential_distribution<>::param_type parm(probs, rates);
        std::stringstream ss;
        ss << parm;
        boost::random::hyperexponential_distribution<>::param_type restored_parm;
        ss >> restored_parm;
        BOOST_CHECK_EQUAL(parm, restored_parm);
    }

    // - Test with an empty probability vector and ios_base exceptions disabled
    {
        std::stringstream ss;
        boost::random::detail::print_vector(ss, empty_vector);
        ss << ' ';
        boost::random::detail::print_vector(ss, rates);
        boost::random::hyperexponential_distribution<>::param_type param;
        ss >> param;
        boost::random::hyperexponential_distribution<>::param_type check_param(std::vector<double>(rates.size(), 1), rates);
        BOOST_CHECK_EQUAL(param, check_param);
    }

    // - Test with an empty rate vector and ios_base exceptions disabled
    {
        std::stringstream ss;
        boost::random::detail::print_vector(ss, probs);
        ss << ' ';
        boost::random::detail::print_vector(ss, empty_vector);
        boost::random::hyperexponential_distribution<>::param_type param;
        ss >> param;
        boost::random::hyperexponential_distribution<>::param_type check_param(probs, std::vector<double>(probs.size(), 1));
        BOOST_CHECK_EQUAL(param, check_param);
    }

    // - Test with an empty probability and rate vectors and ios_base exceptions disabled
    {
        std::stringstream ss;
        boost::random::detail::print_vector(ss, empty_vector);
        ss << ' ';
        boost::random::detail::print_vector(ss, empty_vector);
        boost::random::hyperexponential_distribution<>::param_type param;
        ss >> param;
        boost::random::hyperexponential_distribution<>::param_type check_param;
        BOOST_CHECK_EQUAL(param, check_param);
    }

    // - Test with an empty probability vector and ios_base exceptions enabled
    {
        std::stringstream ss;
        boost::random::detail::print_vector(ss, empty_vector);
        ss << ' ';
        boost::random::detail::print_vector(ss, rates);
        boost::random::hyperexponential_distribution<>::param_type param;
        ss.exceptions(std::ios_base::failbit);
        ss >> param;
        boost::random::hyperexponential_distribution<>::param_type check_param(std::vector<double>(rates.size(), 1), rates);
        BOOST_CHECK_EQUAL(param, check_param);
    }

    // - Test with an empty rate vector and ios_base exceptions enabled
    {
        std::stringstream ss;
        boost::random::detail::print_vector(ss, probs);
        ss << ' ';
        boost::random::detail::print_vector(ss, empty_vector);
        boost::random::hyperexponential_distribution<>::param_type param;
        ss.exceptions(std::ios_base::failbit);
        ss >> param;
        boost::random::hyperexponential_distribution<>::param_type check_param(probs, std::vector<double>(probs.size(), 1));
        BOOST_CHECK_EQUAL(param, check_param);
    }

    // - Test with an empty probability and rate vectors and ios_base exceptions enabled
    {
        std::stringstream ss;
        boost::random::detail::print_vector(ss, empty_vector);
        ss << ' ';
        boost::random::detail::print_vector(ss, empty_vector);
        boost::random::hyperexponential_distribution<>::param_type param;
        ss.exceptions(std::ios_base::failbit);
        ss >> param;
        boost::random::hyperexponential_distribution<>::param_type check_param;
        BOOST_CHECK_EQUAL(param, check_param);
    }

    // The the reading of hyperexponential_distribution

    // - Test with valid input
    {
        boost::random::hyperexponential_distribution<> dist(probs, rates);
        std::stringstream ss;
        ss << dist;
        boost::random::hyperexponential_distribution<> restored_dist;
        ss >> restored_dist;
        BOOST_CHECK_EQUAL(dist, restored_dist);
    }

    // - Test with an empty probability vector and ios_base exceptions disabled
    {
        std::stringstream ss;
        boost::random::detail::print_vector(ss, empty_vector);
        ss << ' ';
        boost::random::detail::print_vector(ss, rates);
        boost::random::hyperexponential_distribution<> dist;
        ss >> dist;
        boost::random::hyperexponential_distribution<> check_dist(std::vector<double>(rates.size(), 1), rates);
        BOOST_CHECK_EQUAL(dist, check_dist);
    }

    // - Test with an empty rate vector and ios_base exceptions disabled
    {
        std::stringstream ss;
        boost::random::detail::print_vector(ss, probs);
        ss << ' ';
        boost::random::detail::print_vector(ss, empty_vector);
        boost::random::hyperexponential_distribution<> dist;
        ss >> dist;
        boost::random::hyperexponential_distribution<> check_dist(probs, std::vector<double>(probs.size(), 1));
        BOOST_CHECK_EQUAL(dist, check_dist);
    }

    // - Test with an empty probability and rate vectors and ios_base exceptions disabled
    {
        std::stringstream ss;
        boost::random::detail::print_vector(ss, empty_vector);
        ss << ' ';
        boost::random::detail::print_vector(ss, empty_vector);
        boost::random::hyperexponential_distribution<> dist;
        ss >> dist;
        boost::random::hyperexponential_distribution<> check_dist;
        BOOST_CHECK_EQUAL(dist, check_dist);
    }

    // - Test with an empty probability vector and ios_base exceptions enabled
    {
        std::stringstream ss;
        boost::random::detail::print_vector(ss, empty_vector);
        ss << ' ';
        boost::random::detail::print_vector(ss, rates);
        boost::random::hyperexponential_distribution<> dist;
        ss.exceptions(std::ios_base::failbit);
        ss >> dist;
        boost::random::hyperexponential_distribution<> check_dist(std::vector<double>(rates.size(), 1), rates);
        BOOST_CHECK_EQUAL(dist, check_dist);
    }

    // - Test with an empty rate vector and ios_base exceptions enabled
    {
        std::stringstream ss;
        boost::random::detail::print_vector(ss, probs);
        ss << ' ';
        boost::random::detail::print_vector(ss, empty_vector);
        boost::random::hyperexponential_distribution<> dist;
        ss.exceptions(std::ios_base::failbit);
        ss >> dist;
        boost::random::hyperexponential_distribution<> check_dist(probs, std::vector<double>(probs.size(), 1));
        BOOST_CHECK_EQUAL(dist, check_dist);
    }

    // - Test with an empty probability and rate vectors and ios_base exceptions enabled
    {
        std::stringstream ss;
        boost::random::detail::print_vector(ss, empty_vector);
        ss << ' ';
        boost::random::detail::print_vector(ss, empty_vector);
        boost::random::hyperexponential_distribution<> dist;
        ss.exceptions(std::ios_base::failbit);
        ss >> dist;
        boost::random::hyperexponential_distribution<> check_dist;
        BOOST_CHECK_EQUAL(dist, check_dist);
    }
}

//NOTE: test case commented since normalization check in \c hyperexp_detail::check_probabilities has been currently commented
//BOOST_AUTO_TEST_CASE( test_normalization )
//{
//    const double tol = detail::make_tolerance<double>();
//
//    const std::vector<double> probs = boost::assign::list_of(1023.0)(1.0);
//    const std::vector<double> rates = boost::assign::list_of(1023.0)(1.0);
//    const std::vector<double> norm_probs = boost::assign::list_of(1023.0/1024.0)(1.0/1024.0);
//
//    boost::random::hyperexponential_distribution<> dist(probs, rates);
//    BOOST_CHECK( boost::random::hyperexp_detail::check_params(dist.probabilities(), dist.rates()) );
//    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist.probabilities(), norm_probs, tol);
//
//    const std::vector<double> probs2 = boost::assign::list_of(1001.0)(1.0);
//    const std::vector<double> rates2 = boost::assign::list_of(1001.0)(1.0);
//    const std::vector<double> norm_probs2 = boost::assign::list_of(1001.0/1002.0)(1.0/1002.0);
//
//    boost::random::hyperexponential_distribution<> dist2(probs2, rates2);
//    BOOST_CHECK( boost::random::hyperexp_detail::check_params(dist2.probabilities(), dist2.rates()) );
//    BOOST_RANDOM_HYPEREXP_CHECK_CLOSE_COLLECTIONS(double, dist2.probabilities(), norm_probs2, tol);
//}

void use(boost::random::hyperexponential_distribution<>::result_type) {}

BOOST_AUTO_TEST_CASE(test_generation)
{
    //const double tol = detail::make_tolerance<double>();

    const std::vector<double> probs = boost::assign::list_of(0.1)(0.2)(0.3)(0.4);
    const std::vector<double> rates = boost::assign::list_of(1.0)(2.0)(3.0)(4.0);

    boost::minstd_rand0 gen;

    boost::random::hyperexponential_distribution<> dist;
    boost::random::hyperexponential_distribution<> dist_r(probs, rates);
    typedef boost::random::hyperexponential_distribution<>::result_type result_type;
    for(std::size_t i = 0; i < 10; ++i)
    {
        const result_type value = dist(gen);
        use(value);
        BOOST_CHECK_GE(value, static_cast<result_type>(0));
        const result_type value_r = dist_r(gen);
        use(value_r);
        BOOST_CHECK_GE(value_r, static_cast<result_type>(0));
        const result_type value_param = dist_r(gen, dist.param());
        use(value_param);
        BOOST_CHECK_GE(value_param, static_cast<result_type>(0));
        const result_type value_r_param = dist(gen, dist_r.param());
        use(value_r_param);
        BOOST_CHECK_GE(value_r_param, static_cast<result_type>(0));
    }
}

BOOST_AUTO_TEST_CASE( test_generation_float )
{
    //const double tol = detail::make_tolerance<double>();

    const std::vector<double> probs = boost::assign::list_of(0.1)(0.2)(0.3)(0.4);
    const std::vector<double> rates = boost::assign::list_of(1.0)(2.0)(3.0)(4.0);

    boost::lagged_fibonacci607 gen;

    boost::random::hyperexponential_distribution<> dist;
    boost::random::hyperexponential_distribution<> dist_r(probs, rates);
    typedef boost::random::hyperexponential_distribution<>::result_type result_type;
    for(std::size_t i = 0; i < 10; ++i)
    {
        const result_type value = dist(gen);
        use(value);
        BOOST_CHECK_GE(value, static_cast<result_type>(0));
        const result_type value_r = dist_r(gen);
        use(value_r);
        BOOST_CHECK_GE(value_r, static_cast<result_type>(0));
        const result_type value_param = dist_r(gen, dist.param());
        use(value_param);
        BOOST_CHECK_GE(value_param, static_cast<result_type>(0));
        const result_type value_r_param = dist(gen, dist_r.param());
        use(value_r_param);
        BOOST_CHECK_GE(value_r_param, static_cast<result_type>(0));
    }
}


#include <vector>

#include <boost/shared_ptr.hpp>
#include <gflags/gflags.h>
#include <glog/logging.h>

#include <cmath>

#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/mask_layer.hpp"
#include "caffe/util/math_functions.hpp"

namespace caffe {

template <typename Dtype>
void MaskTransformerLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {

	string prefix = "\t\tMask Transformer Layer:: LayerSetUp: \t";

	if(this->layer_param_.mask_param().to_compute_du()) {
		to_compute_dU_ = true;
	}
}

template <typename Dtype>
void MaskTransformerLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {

	string prefix = "\t\tMask Transformer Layer:: Reshape: \t";

	N = bottom[0]->shape(0);
	C = bottom[0]->shape(1);
	H = bottom[0]->shape(2);
	W = bottom[0]->shape(3);

	// reshape the parameter mm
	CHECK(bottom[1]->count(1) == H * W) << "The dimension of mask matrix and U is not the same!" << std::endl;
	CHECK(bottom[1]->shape(0) == N) << "The first dimension of mask matrix and U should be the same" << std::endl;

	// reshape V
	vector<int> shape(4);

	shape[0] = N;
	shape[1] = C;
	shape[2] = H;
	shape[3] = W;

	top[0]->Reshape(shape);
}

template <typename Dtype>
void MaskTransformerLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top) {

	string prefix = "\t\tMask Transformer Layer:: Forward_cpu: \t";

	if(global_debug) std::cout<<prefix<<"Starting!"<<std::endl;

	const Dtype* U = bottom[0]->cpu_data();
	const Dtype* mm = bottom[1]->cpu_data();
	Dtype* V = top[0]->mutable_cpu_data();

	caffe_set(top[0]->count(), (Dtype)0, V);

	int row_idx, idx;
	Dtype value;

	for(int i = 0; i < N; ++i)
		row_idx = i * (H*W);
		for(int j = 0; j < H*W; ++j) {
			value = mm[row_idx + j];
			idx = row_idx * C + j;
			if(value >= 0 && value <= 1) {
				V[idx + 0 * (H*W)] = value * U[idx + 0 * (H*W)];
				V[idx + 1 * (H*W)] = value * U[idx + 1 * (H*W)];
				V[idx + 2 * (H*W)] = value * U[idx + 2 * (H*W)];
			} else if(value > 1) {
				V[idx + 0 * (H*W)] = U[idx + 0 * (H*W)];
				V[idx + 1 * (H*W)] = U[idx + 1 * (H*W)];
				V[idx + 2 * (H*W)] = U[idx + 2 * (H*W)];
			}
		}

	if(global_debug) std::cout<<prefix<<"Finished."<<std::endl;
}

template <typename Dtype>
void MaskTransformerLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down,
    const vector<Blob<Dtype>*>& bottom) {

		string prefix = "\t\tMask Transformer Layer:: Backward_cpu: \t";

		if(global_debug) std::cout<<prefix<<"Starting!"<<std::endl;

		const Dtype* dV = top[0]->cpu_diff();
		const Dtype* U = bottom[0]->cpu_data();
		const Dtype* mm = bottom[1]->cpu_data();

		Dtype* dU = bottom[0]->mutable_cpu_diff();
		Dtype* dmm = bottom[1]->mutable_cpu_diff();

		caffe_set(bottom[0]->count(), (Dtype)0, dU);
		caffe_set(bottom[1]->count(), (Dtype)0, dmm);

		int row_idx, idx;
		Dtype value;

		for(int i = 0; i < N; ++i)
			row_idx = i * (H*W);
			for(int j = 0; j < H*W; ++j) {
				value = mm[row_idx + j];
				idx = row_idx * C + j;
				if(value >= 0 && value <= 1) {
					dmm[row_idx + j] += U[idx + 0 * (H*W)] * dV[idx + 0 * (H*W)];
					dmm[row_idx + j] += U[idx + 1 * (H*W)] * dV[idx + 1 * (H*W)];
					dmm[row_idx + j] += U[idx + 2 * (H*W)] * dV[idx + 2 * (H*W)];
				}
			}

		if(to_compute_dU_) {
			for(int i = 0; i < N; ++i)
				row_idx = i * (H*W);
				for(int j = 0; j < H*W; ++j) {
					value = mm[row_idx + j];
					idx = row_idx * C + j;
					if(value >= 0 && value <= 1) {
						dU[idx + 0 * (H*W)] = value * dV[idx + 0 * (H*W)];
						dU[idx + 1 * (H*W)] = value * dV[idx + 1 * (H*W)];
						dU[idx + 2 * (H*W)] = value * dV[idx + 2 * (H*W)];
					} else if(value > 1) {
						dU[idx + 0 * (H*W)] = dV[idx + 0 * (H*W)];
						dU[idx + 1 * (H*W)] = dV[idx + 1 * (H*W)];
						dU[idx + 2 * (H*W)] = dV[idx + 2 * (H*W)];
					}
				}
		}

		if(global_debug) std::cout<<prefix<<"Finished."<<std::endl;
}

#ifdef CPU_ONLY
STUB_GPU(MaskTransformerLayer);
#endif

INSTANTIATE_CLASS(MaskTransformerLayer);
REGISTER_LAYER_CLASS(MaskTransformer);

}  // namespace caffe


/*
// Copyright 2018 University of Liege
//
// 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.
//
// Authors:
// - Adrien Crovato
*/

//// Field creation
// Create field cells from data contained into box. Divide the initial box (whose coordinates are provided as input)
// by the number of cells (also provided as input). Compute center and corner points of field cells.
//
// I/O:
// - box: temporary array containg vertices of domain
// - fPan: field panels (structure)

#include <array>
#include <iostream>
#include <Eigen/Dense>
#include "create_field.h"

#define NDIM 3

using namespace std;
using namespace Eigen;

void create_field(array<array<double, 3>, 8> &box, Field &fPan) {

    // Temporary variables
    VectorXd X(fPan.nX,1), Y(fPan.nY,1), Z(fPan.nZ,1);
    VectorXd X0(fPan.nX,1), Y0(fPan.nY,1), Z0(fPan.nZ,1);
    VectorXd X1(fPan.nX,1), Y1(fPan.nY,1), Z1(fPan.nZ,1);
    int idx = 0;

    //// Begin
    cout << "Creating field cells... " << flush;

    // Resize field matrices
    fPan.CG.resize(fPan.nF, NDIM);
    fPan.vX.resize(fPan.nF, 2);
    fPan.vY.resize(fPan.nF, 2);
    fPan.vZ.resize(fPan.nF, 2);

    // Divide the domain
    fPan.deltaX = abs(box[1][0] - box[0][0]) / fPan.nX;
    for (int i = 0; i < fPan.nX; ++i) {
        X(i) = box[0][0] + (i + 0.5) * fPan.deltaX;
        X0(i) = box[0][0] + i * fPan.deltaX;
        X1(i) = box[0][0] + (i + 1) * fPan.deltaX;
    }
    fPan.deltaY = (box[4][1] - box[0][1]) / fPan.nY;
    for (int j = 0; j < fPan.nY; ++j) {
        Y(j) = box[0][1] + (j + 0.5) * fPan.deltaY;
        Y0(j) = box[0][1] + j * fPan.deltaY;
        Y1(j) = box[0][1] + (j + 1) * fPan.deltaY;
    }
    fPan.deltaZ = (box[2][2] - box[0][2]) / fPan.nZ;
    for (int k = 0; k < fPan.nZ; ++k) {
        Z(k) = box[0][2] + (k + 0.5) * fPan.deltaZ;
        Z0(k) = box[0][2] + k * fPan.deltaZ;
        Z1(k) = box[0][2] + (k + 1) * fPan.deltaZ;
    }

    // Center points & corner points
    for (int j = 0; j < fPan.nY; ++j) {
        for (int k = 0; k < fPan.nZ; ++k) {
            for (int i = 0; i < fPan.nX; ++i) {
                idx = i + k * fPan.nX + j * fPan.nX * fPan.nZ;
                fPan.CG(idx,0) = X(i);
                fPan.CG(idx,1) = Y(j);
                fPan.CG(idx,2) = Z(k);
                fPan.vX(idx,0) = X0(i);
                fPan.vX(idx,1) = X1(i);
                fPan.vY(idx,0) = Y0(j);
                fPan.vY(idx,1) = Y1(j);
                fPan.vZ(idx,0) = Z0(k);
                fPan.vZ(idx,1) = Z1(k);
            }
        }
    }

    //// Control display
    cout << "Done!" << endl;
    #ifdef VERBOSE
        cout << "Field cell center points: " << fPan.CG.rows() << 'X' << fPan.CG.cols() << endl;
        for (int i = 0; i < fPan.nF; ++i)
            cout << i << ' ' << fPan.CG(i,0) << ' ' << fPan.CG(i,1) << ' ' << fPan.CG(i,2) << endl;
        cout << "Field cell corner points: " << 8 * fPan.vX.rows() << 'X' << fPan.vX.cols() << endl;
        for (int i = 0; i < fPan.nF; ++i) {
            cout << i << '\t' << fPan.vX(i,0) << ' ' << fPan.vY(i,0) << ' ' << fPan.vZ(i,0) << endl;
            cout << '\t' << fPan.vX(i,1) << ' ' << fPan.vY(i,0) << ' ' << fPan.vZ(i,0) << endl;
            cout << '\t' << fPan.vX(i,0) << ' ' << fPan.vY(i,0) << ' ' << fPan.vZ(i,1) << endl;
            cout << '\t' << fPan.vX(i,1) << ' ' << fPan.vY(i,0) << ' ' << fPan.vZ(i,1) << endl;
            cout << '\t' << fPan.vX(i,0) << ' ' << fPan.vY(i,1) << ' ' << fPan.vZ(i,0) << endl;
            cout << '\t' << fPan.vX(i,1) << ' ' << fPan.vY(i,1) << ' ' << fPan.vZ(i,0) << endl;
            cout << '\t' << fPan.vX(i,0) << ' ' << fPan.vY(i,1) << ' ' << fPan.vZ(i,1) << endl;
            cout << '\t' << fPan.vX(i,1) << ' ' << fPan.vY(i,1) << ' ' << fPan.vZ(i,1) << endl;
        }
    #endif
    cout << endl;
}


/*!@file
 * @copyright This code is licensed under the 3-clause BSD license.
 *   Copyright ETH Zurich, Laboratory of Physical Chemistry, Reiher Group.
 *   See LICENSE.txt for details.
 */

#include <boost/test/unit_test.hpp>

#include "Molassembler/Temple/Functional.h"
#include "Molassembler/Temple/Random.h"
#include "Molassembler/Temple/Stringify.h"

#include "Molassembler/Temple/constexpr/Array.h"
#include "Molassembler/Temple/constexpr/Bitmask.h"
#include "Molassembler/Temple/constexpr/Bitset.h"
#include "Molassembler/Temple/constexpr/Containers.h"
#include "Molassembler/Temple/constexpr/DynamicArray.h"
#include "Molassembler/Temple/constexpr/DynamicMap.h"
#include "Molassembler/Temple/constexpr/DynamicSet.h"
#include "Molassembler/Temple/constexpr/Jsf.h"
#include "Molassembler/Temple/constexpr/LogicalOperatorTests.h"
#include "Molassembler/Temple/constexpr/Math.h"
#include "Molassembler/Temple/constexpr/TupleType.h"
#include "Molassembler/Temple/constexpr/TupleTypePairs.h"
#include "Molassembler/Temple/Permutations.h"

#include <iostream>

using namespace Scine::Molassembler;
extern Temple::Generator<> generator;

using namespace std::string_literals;

namespace ArrayTests {

constexpr auto testArr = Temple::Array<unsigned, 3> {4, 3, 5};

template<typename T, size_t size>
constexpr Temple::Array<T, size> modifyArray(
  const Temple::Array<T, size>& array
) {
  auto arrayCopy = array;
  inPlaceSwap(arrayCopy, 0, 1);
  return arrayCopy;
}

constexpr auto modf = modifyArray(testArr);

static_assert(
  modf == Temple::Array<unsigned, 3> {3, 4, 5},
  "Swap doesn't work as expected"
);

static_assert(
  std::is_same<
    decltype(Temple::makeArray(4, 3, 9)),
    Temple::Array<int, 3>
  >::value,
  "makeArray does not work as expected"
);

} // namespace ArrayTests

BOOST_AUTO_TEST_CASE(ArrayPermutation, *boost::unit_test::label("Temple")) {
  std::array<unsigned, 4> base {{0, 1, 2, 3}};
  std::array<unsigned, 4> STLComparison {{0, 1, 2, 3}};

  bool customHasNext;
  bool STLHasNext;

  do {
    customHasNext = Temple::inPlaceNextPermutation(base);
    STLHasNext = std::next_permutation(STLComparison.begin(), STLComparison.end());

    BOOST_CHECK_MESSAGE(
      base == STLComparison,
      "In forward permutation, base is {" << Temple::condense(base)
      << "} and and STL is {" << Temple::condense(STLComparison)
      << "}"
    );
  } while(customHasNext && STLHasNext);

  BOOST_CHECK_MESSAGE(
   !customHasNext && !STLHasNext,
   "The two permutation algorithms don't terminate at the same time"
  );

  base = {{3, 2, 1, 0}};
  STLComparison = {{3, 2, 1, 0}};

  do {
    customHasNext = Temple::inPlacePreviousPermutation(base);
    STLHasNext = std::prev_permutation(STLComparison.begin(), STLComparison.end());

    BOOST_CHECK_MESSAGE(
      base == STLComparison,
      "In backward permutation, base is {" << Temple::condense(base)
      << "} and and STL is {" << Temple::condense(STLComparison)
      << "}"
    );
  } while(customHasNext && STLHasNext);

  BOOST_CHECK_MESSAGE(
   !customHasNext && !STLHasNext,
   "The two permutation algorithms don't terminate at the same time"
  );

  // Variants of limited sections of the array

  base = {{0, 1, 2, 3}};
  STLComparison = {{0, 1, 2, 3}};

  do {
    customHasNext = Temple::inPlaceNextPermutation(base, 1, 3);
    STLHasNext = std::next_permutation(STLComparison.begin() + 1, STLComparison.end() - 1);

    BOOST_CHECK_MESSAGE(
      base == STLComparison,
      "In limited forward permutation, base is {" << Temple::condense(base)
      << "} and and STL is {" << Temple::condense(STLComparison)
      << "}"
    );
  } while(customHasNext && STLHasNext);

  BOOST_CHECK_MESSAGE(
   !customHasNext && !STLHasNext,
   "The two permutation algorithms don't terminate at the same time in limited "
   "forward permutation"
  );

  base = {{3, 2, 1, 0}};
  STLComparison = {{3, 2, 1, 0}};

  do {
    customHasNext = Temple::inPlacePreviousPermutation(base, 1, 3);
    STLHasNext = std::prev_permutation(STLComparison.begin() + 1, STLComparison.end() - 1);

    BOOST_CHECK_MESSAGE(
      base == STLComparison,
      "In limited backward permutation, base is {" << Temple::condense(base)
      << "} and and STL is {" << Temple::condense(STLComparison)
      << "}"
    );
  } while(customHasNext && STLHasNext);

  BOOST_CHECK_MESSAGE(
   !customHasNext && !STLHasNext,
   "The two permutation algorithms don't terminate at the same time in limited "
   "backward permutation"
  );
}

constexpr bool compileTimeDynTest() {
  Temple::DynamicArray<unsigned, 10> nonConstArr {4, 3, 6};
  nonConstArr.push_back(9);
  return nonConstArr.size() == 4;
}

constexpr bool dynArrSpliceTest() {
  Temple::DynamicArray<unsigned, 10> nonConstArr {4, 3, 6, 5, 1, 9};
  auto spliced = nonConstArr.splice(2);

  return (
    spliced == Temple::DynamicArray<unsigned, 10> {6, 5, 1, 9}
    && nonConstArr == Temple::DynamicArray<unsigned, 10> {4, 3}
  );
}

BOOST_AUTO_TEST_CASE(DynamicArrayTests, *boost::unit_test::label("Temple")) {
  constexpr Temple::DynamicArray<unsigned, 10> arr {4, 3, 5};

  static_assert(
    arr.size() == 3,
    "Array size isn't initialized correctly from parameter pack ctor"
  );
  static_assert(
    compileTimeDynTest(),
    "non-const dynamic array push_back does not work as expected"
  );
  static_assert(
    dynArrSpliceTest(),
    "non-const dynamic array splice does not work as expected"
  );
  static_assert(
    arr.end() - arr.begin() == 3,
    "Subtracting begin/end iterators does not yield dynamic length"
  );

  static_assert(
    arr.begin() - arr.end() == -3,
    "Subtracting begin/end iterators does not yield dynamic length"
  );

  constexpr Temple::Array<unsigned, 10> values {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};

  constexpr auto grouped = groupByEquality(
    values,
    std::equal_to<>()
  );

  static_assert(
    grouped.size() == 4
    && grouped.at(0).size() == 1
    && grouped.at(1).size() == 2
    && grouped.at(2).size() == 3
    && grouped.at(3).size() == 4,
    "Grouping does not work as expected"
  );

  constexpr Temple::DynamicArray<unsigned, 14> fromFixed {values};

  static_assert(fromFixed.size() == 10, "Construction from fixed doesn't work");
}

template<typename T, size_t size, class Comparator>
constexpr bool isSorted(const Temple::DynamicSet<T, size, Comparator>& set) {
  Comparator comparator;

  auto left = set.begin();
  auto right = set.begin(); ++right;

  while(right != set.end()) {
    if(comparator(*right, *left)) {
      return false;
    }

    ++right;
    ++left;
  }

  return true;
}

BOOST_AUTO_TEST_CASE(DynamicSetTests, *boost::unit_test::label("Temple")) {
  Temple::DynamicSet<unsigned, 10> set;

  BOOST_CHECK(set.size() == 0);
  BOOST_CHECK(
    std::distance(
      set.begin(),
      set.end()
    ) == 0
  );

  for(const auto& item : {9u, 3u, 5u}) {
    set.insert(item);
  }

  BOOST_CHECK(set.size() == 3);
  BOOST_CHECK(
    std::distance(
      set.begin(),
      set.end()
    ) == 3
  );
  BOOST_CHECK(set.contains(3) && set.contains(5) && set.contains(9));
  for(const auto& item : {2u, 4u, 8u, 10u}) {
    BOOST_CHECK_MESSAGE(
      !set.contains(item),
      "Set says it contains " << item << " when it shouldn't (set is {"
        << Temple::condense(set)
        << "}."
    );
  }
  BOOST_CHECK(isSorted(set));

  Temple::DynamicSet<unsigned, 10> setInitList {
    Temple::DynamicArray<unsigned, 10> {4, 9, 13}
  };

  BOOST_CHECK(setInitList.size() == 3);
  BOOST_CHECK(
    std::distance(
      setInitList.begin(),
      setInitList.end()
    ) == 3
  );

  setInitList.insert(0);

  BOOST_CHECK(setInitList.size() == 4);

  BOOST_CHECK_MESSAGE(
    setInitList.contains(4)
    && setInitList.contains(9)
    && setInitList.contains(13)
    && setInitList.contains(0)
    && !setInitList.contains(1)
    && !setInitList.contains(25),
    "setInitList {"
      << Temple::condense(setInitList)
      << "} does not conform to expectations concerning contains:\n"
      << std::boolalpha
      << "contains 4, expect true:" << setInitList.contains(4)
      << "\ncontains 9, expect true:" << setInitList.contains(9)
      << "\ncontains 13, expect true:" << setInitList.contains(13)
      << "\ncontains 0, expect true:" << setInitList.contains(0)
      << "\ncontains 1, expect false:" << setInitList.contains(1)
      << "\ncontains 25, expect false:" << setInitList.contains(25)
  );
  // Set of arrays
  Temple::Array<
    Temple::Array<unsigned, 4>,
    5
  > sampleArrays {
    Temple::Array<unsigned, 4> {1, 2, 3, 4},
    Temple::Array<unsigned, 4> {1, 2, 4, 3},
    Temple::Array<unsigned, 4> {1, 4, 3, 2},
    Temple::Array<unsigned, 4> {1, 4, 2, 3},
    Temple::Array<unsigned, 4> {2, 1, 3, 4}
  };

  Temple::DynamicSet<
    Temple::Array<unsigned, 4>,
    10
  > arraysSet;

  arraysSet.insert(sampleArrays.at(0));
  arraysSet.insert(sampleArrays.at(2));
  arraysSet.insert(sampleArrays.at(3));

  BOOST_CHECK(arraysSet.contains(sampleArrays.at(0)));
  BOOST_CHECK(arraysSet.contains(sampleArrays.at(2)));
  BOOST_CHECK(arraysSet.contains(sampleArrays.at(3)));
  BOOST_CHECK(!arraysSet.contains(sampleArrays.at(1)));
  BOOST_CHECK(!arraysSet.contains(sampleArrays.at(4)));

  BOOST_CHECK(arraysSet.size() == 3);
  BOOST_CHECK(
    std::distance(
      arraysSet.begin(),
      arraysSet.end()
    ) == 3
  );
}

template<typename T, size_t size>
bool validate(const Temple::DynamicSet<T, size>& set) {
  /* NOTE: std::is_sorted isn't constexpr yet, and the range adaptors
   * aren't constexpr, so we need to do the sequential pairs thing ourselves
   */

  // Is the set ordered?
  auto leftIter = set.begin();
  auto rightIter = leftIter; ++rightIter;
  auto bound = set.end(); --bound;

  if(leftIter == set.end() || rightIter == set.end()) {
    return true;
  }

  while(rightIter != bound) {
    BOOST_REQUIRE_LT(*leftIter, *rightIter);

    ++leftIter;
    ++rightIter;
  }

  // Is the reported size equal to a begin-end through-iteration?
  return (
    set.size() == static_cast<unsigned>(
      std::distance(
        set.begin(),
        set.end()
      )
    )
  );
}

BOOST_AUTO_TEST_CASE(ArrayOperators, *boost::unit_test::label("Temple")) {
  Temple::Array<unsigned, 4> a {4, 2, 3, 1};
  Temple::Array<unsigned, 4> b {4, 3, 2, 1};

  BOOST_CHECK(Temple::testLogicalOperators(a, b));
  BOOST_CHECK(Temple::testLogicalOperators(a, a));

  Temple::dynamic::explainLogicalOperatorFailures(a, b);
}

BOOST_AUTO_TEST_CASE(DynamicSetFuzzing, *boost::unit_test::label("Temple")) {
  for(unsigned N = 0; N < 100; ++N) {
    Temple::DynamicSet<unsigned, 100> subject;

    std::vector<unsigned> numbers;
    numbers.resize(50);
    std::iota(
      numbers.begin(),
      numbers.end(),
      0
    );

    Temple::Random::shuffle(numbers, generator.engine);

    for(const auto& number : numbers) {
      subject.insert(number);

      const bool isValid = validate(subject);
      if(!isValid) {
        std::cout << "After inserting " << number
          << ", set is left in invalid state. Set: {"
          << Temple::condense(subject) << "}\ninsert sequence {"
          << Temple::condense(numbers) << "}."
          << std::endl;
      }

      BOOST_REQUIRE(isValid);

      BOOST_CHECK(subject.contains(number));
    }
  }
}

namespace tuple_type_tests {

struct Apple {
  static constexpr unsigned number = 4;
};

struct Banana {
  static constexpr unsigned number = 3;
};

struct Cherry {
  static constexpr unsigned number = 50;
};

using Fruit = std::tuple<Apple, Banana, Cherry>;

template<typename ... FruitClasses>
struct sumNumbersValue {
  static constexpr unsigned initializer() {
    const std::array<unsigned, sizeof...(FruitClasses)> sizes = {{
      FruitClasses::number...
    }};

    unsigned sum = 0;

    for(unsigned i = 0; i < sizes.size(); ++i) {
      sum += sizes[i];
    }

    return sum;
  };

  static constexpr unsigned value = initializer();
};

template<typename ... FruitClasses>
struct sumNumbersFunctor {
  static constexpr unsigned value() {
    const std::array<unsigned, sizeof...(FruitClasses)> sizes = {{
      FruitClasses::number...
    }};

    unsigned sum = 0;

    for(unsigned i = 0; i < sizes.size(); ++i) {
      sum += sizes[i];
    }

    return sum;
  };
};

template<typename FruitClass>
struct getNumberFunctor {
  static constexpr unsigned value() {
    return FruitClass::number;
  }
};

template<typename FruitClass>
struct getNumberValue {
  static constexpr unsigned initialize() {
    return FruitClass::number;
  }

  static constexpr unsigned value = initialize();
};

template<typename FruitClass>
constexpr unsigned getNumberFunction() {
  return FruitClass::number;
}

template<typename A, typename B>
struct pairSumFunctor {
  static constexpr unsigned value() {
    return A::number + B::number;
  }
};

template<typename A, typename B>
struct pairSumValue {
  static constexpr unsigned initialize() {
    return A::number + B::number;
  }

  static constexpr unsigned value = initialize();
};

static_assert(
  Temple::Tuples::unpackToFunction<Fruit, sumNumbersValue>() == 57,
  "Unpacking fruit tuple to valueValue does not yield expected result"
);

static_assert(
  Temple::Tuples::unpackToFunction<Fruit, sumNumbersFunctor>() == 57,
  "Unpacking fruit tuple to valueFunctor does not yield expected result"
);

static_assert(
  Temple::arraysEqual(
    Temple::Tuples::map<Fruit, getNumberValue>(),
    Temple::Array<unsigned, 3> {{Apple::number, Banana::number, Cherry::number}}
  ),
  "Mapping with getNumberValue does not yield expected result!"
);

static_assert(
  Temple::arraysEqual(
    Temple::Tuples::map<Fruit, getNumberFunctor>(),
    Temple::Array<unsigned, 3> {{Apple::number, Banana::number, Cherry::number}}
  ),
  "Mapping with getNumberFunctor does not yield expected result!"
);

static_assert(
  Temple::arraysEqual(
    Temple::Tuples::mapAllPairs<Fruit, pairSumValue>(),
    Temple::Array<unsigned, 3> {{
      Apple::number + Banana::number,
      Apple::number + Cherry::number,
      Banana::number + Cherry::number
    }}
  ),
  "Mapping pairs with pairSumValue does not yield expected result!"
);

static_assert(
  Temple::arraysEqual(
    Temple::Tuples::mapAllPairs<Fruit, pairSumFunctor>(),
    Temple::Array<unsigned, 3> {{
      Apple::number + Banana::number,
      Apple::number + Cherry::number,
      Banana::number + Cherry::number
    }}
  ),
  "Mapping pairs with pairSumFunctor does not yield expected result!"
);

using countTestType = std::tuple<unsigned, float, double, unsigned, size_t>;

static_assert(
  Temple::Tuples::countType<countTestType, unsigned>() == 2,
  "Counting unsigned in countTestType does not return two!"
);

} // namespace tuple_type_tests

namespace ConcatenationTests {

constexpr Temple::Array<unsigned, 4> f {4, 2, 9, 3};
constexpr Temple::Array<unsigned, 4> g {11, 22, 33, 44};
constexpr Temple::Array<unsigned, 4> h {234, 292, 912, 304};
constexpr Temple::Array<unsigned, 8> fg {
  4, 2, 9, 3,
  11, 22, 33, 44
};
constexpr Temple::Array<unsigned, 12> fgh {
  4, 2, 9, 3,
  11, 22, 33, 44,
  234, 292, 912, 304
};

static_assert(
  Temple::arraysEqual(
    Temple::arrayConcatenate(f, g),
    fg
  ),
  "Pairwise concatenation does not preserve sequence!"
);

static_assert(
  Temple::arraysEqual(
    Temple::arrayConcatenate(f, g, h),
    fgh
  ),
  "Variadic concatenation does not work as expected"
);

} // namespace ConcatenationTests

namespace DynamicMapTests {

constexpr Temple::DynamicMap<unsigned, int, 20> generateMap() {
  Temple::DynamicMap<unsigned, int, 20> myMap;

  myMap.insert(4, -2);
  myMap.insert(1, 4);
  myMap.insert(3, 9);

  return myMap;
}

constexpr auto a = generateMap();

static_assert(a.at(4) == -2, "Map does not find element with key 4");
static_assert(a.at(1) == 4, "Map does not find element with key 1");
static_assert(a.at(3) == 9, "Map does not find element with key 3");

} // namespace DynamicMapTests

namespace UpperTriangularMatrixTests {

// Can default-construct
constexpr auto defaultMatr = Temple::UpperTriangularMatrix<bool, 15> {};
static_assert(decltype(defaultMatr)::N == 6, "Size isn't right");

constexpr auto matr = Temple::makeUpperTriangularMatrix(
  std::array<unsigned, 6> {{1, 2, 3, 4, 5, 6}}
);

static_assert(decltype(matr)::N == 4, "Size isn't right");

/*constexpr auto failing = Temple::makeUpperTriangularMatrix(
  std::array<unsigned, 5> {{1, 2, 3, 4, 5}}
);*/

constexpr auto fromArray = Temple::makeUpperTriangularMatrix(
  Temple::Array<unsigned, 6> {{1, 2, 3, 4, 5, 6}}
);
static_assert(decltype(fromArray)::N == 4, "Size isn't right");

} // namespace UpperTriangularMatrixTests

enum class ScopedEnum : unsigned {A, B, C};
enum UnscopedEnum : unsigned {D, E};

BOOST_AUTO_TEST_CASE(BitmaskAll, *boost::unit_test::label("Temple")) {
  using namespace Temple;

  constexpr auto a = make_bitmask(ScopedEnum::A) | ScopedEnum::C;
  static_assert(a & ScopedEnum::A, "A must be contained in the bitmask a");
  BOOST_CHECK(a[ScopedEnum::A] && a[ScopedEnum::C] && !a[ScopedEnum::B]);

  constexpr auto b = make_bitmask(UnscopedEnum::E);
  BOOST_CHECK(b[UnscopedEnum::E] && !b[UnscopedEnum::D]);
}

constexpr Temple::Bitset<300> make_a_bitset() {
  Temple::Bitset<300> a;

  a.set(0);
  a.set(299);
  a.set(63);
  a.set(128);

  return a;
}

BOOST_AUTO_TEST_CASE(BitsetTests, *boost::unit_test::label("Temple")) {
  using namespace Temple;

  constexpr auto a = make_a_bitset();

  static_assert(a.test(0), "Zero hasn't been set");

  BOOST_CHECK(a.test(0) && a.test(299) && a.test(63) && a.test(128));
  BOOST_CHECK(!a.test(1) && !a.test(298) && !a.test(64) && !a.test(127));

  auto b = make_a_bitset();
  b.unset(63);
  BOOST_CHECK(!b.test(63));
}

BOOST_AUTO_TEST_CASE(PermutationIndexTests, *boost::unit_test::label("Temple")) {
  using namespace Temple;

  auto a = iota<Array, unsigned, 6>();

  // expect monotonically increasing permutation index on inplaceNextPermutation
  auto index = permutationIndex(a);

  BOOST_CHECK_EQUAL(index, 0);

  bool pass = true;
  auto previousIndex = index;
  while(inPlaceNextPermutation(a)) {
    index = permutationIndex(a);
    if(index != previousIndex + 1) {
      pass = false;
      break;
    }
    previousIndex = index;
  }

  BOOST_CHECK_MESSAGE(
    pass,
    "inPlaceNextPermutation must yield monotonically increasing permutation "
    "index values"
  );
}

BOOST_AUTO_TEST_CASE(PermutationsTests, *boost::unit_test::label("Temple")) {
  using Array = Temple::Array<unsigned, 4>;
  Temple::Permutation<Array> p(Array {0, 1, 3, 2});
  BOOST_CHECK_EQUAL(p.index(), 1);

  // Applying it to a vector of other objects yields properly permuted elements
  const std::vector<int> nums {{10, -3, 4, -1}};
  const std::vector<int> permutedNums = p.apply(nums);
  const std::vector<int> expected {{10, -3, -1, 4}};
  BOOST_CHECK(permutedNums == expected);

  // Applying an ordering permutation yields an ordered vector
  const auto ordered = Temple::Permutation<Array>::ordering(nums).apply(nums);
  BOOST_CHECK(std::is_sorted(std::begin(ordered), std::end(ordered)));

  // Move forward and backward through permutations
  BOOST_CHECK(p.prev());
  BOOST_CHECK_EQUAL(p.index(), 0);
  BOOST_CHECK(p.next());
  BOOST_CHECK(p.next());
  BOOST_CHECK_EQUAL(p.index(), 2);

  // Applying the inverse yields an identity permutation
  BOOST_CHECK_EQUAL(p.compose(p.inverse()).index(), 0);

  using Vector = std::vector<unsigned>;
  Temple::Permutation<Vector> q(4);
  BOOST_CHECK_EQUAL(q.index(), 0);
  BOOST_CHECK(q.next());
  BOOST_CHECK_EQUAL(q.index(), 1);

  BOOST_CHECK_EQUAL(q.compose(q.inverse()).index(), 0);

  Temple::Permutation<Vector> r(4, 2);
  BOOST_CHECK_EQUAL(r.index(), 2);

  Temple::Permutation<Vector> s(10, 1001);
  BOOST_CHECK_EQUAL(s.index(), 1001);
}


/**
 * @author Eric Cousineau <eacousineau@gmail.com>, member of Dr. Aaron
 * Ames's AMBER Lab
 */
#ifndef EIGEN_UTILITIES_CONTAINER_UTILITIES_H
    #define EIGEN_UTILITIES_CONTAINER_UTILITIES_H

#include <Eigen/Dense>

#include <eigen_utilities/assert_size.hpp>

namespace eigen_utilities
{

template<typename DerivedA, typename DerivedB, typename Container>
void select_rows(const Eigen::MatrixBase<DerivedA> &in, const Container &indices, Eigen::MatrixBase<DerivedB> &out)
{
    assert_size(out, indices.size(), in.cols());
    for (int i = 0; i < indices.size(); ++i)
    {
        int ix = indices[i];
        common_assert_msg(ix >= 0 && ix < in.rows(), "ix: " << ix << ", in: " << in.rows());
        out.row(i) = in.row(ix);
    }
}

template<typename DerivedA, typename DerivedB, typename Container>
void select_rows_resize(const Eigen::MatrixBase<DerivedA> &in, const Container &indices, Eigen::MatrixBase<DerivedB> &out)
{
    out.derived().resize(indices.size(), in.cols());
    select_rows(in, indices, out);
}

template<typename DerivedA, typename DerivedB, typename Container>
void select_cols(const Eigen::MatrixBase<DerivedA> &in, const Container &indices, Eigen::MatrixBase<DerivedB> &out)
{
    assert_size(out, in.rows(), indices.size());
    for (int i = 0; i < indices.size(); ++i)
    {
        int ix = indices[i];
        common_assert_msg(ix >= 0 && ix < in.cols(), "ix: " << ix << ", in: " << in.cols());
        out.col(i) = in.col(ix);
    }
}

template<typename DerivedA, typename DerivedB, typename Container>
void select_cols_resize(const Eigen::MatrixBase<DerivedA> &in, const Container &indices, Eigen::MatrixBase<DerivedB> &out)
{
    out.derived().resize(in.rows(), indices.size());
    select_cols(in, indices, out);
}

template<typename DerivedA, typename DerivedB, typename ContainerA, typename ContainerB>
void select_grid(const Eigen::MatrixBase<DerivedA> &in, const ContainerA &rows, const ContainerB &cols, Eigen::MatrixBase<DerivedB> &out)
{
    assert_size(out, rows.size(), cols.size());
    for (int i = 0; i < rows.size(); ++i)
    {
        for (int j = 0; j < cols.size(); ++j)
            out(i, j) = in(rows[i], cols[j]);
    }
}

template<typename DerivedA, typename DerivedB, typename ContainerA, typename ContainerB>
void select_grid_resize(const Eigen::MatrixBase<DerivedA> &in, const ContainerA &rows, const ContainerB &cols, Eigen::MatrixBase<DerivedB> &out)
{
    out.derived().resize(rows.size(), cols.size());
    select_grid(in, rows, cols, out);
}

template<typename DerivedA, typename DerivedB, typename Container>
void select_rows_reverse(const Eigen::MatrixBase<DerivedA> &in, const Container &indices, Eigen::MatrixBase<DerivedB> &out)
{
    assert_size(in, indices.size(), in.cols());
    for (int i = 0; i < indices.size(); ++i)
    {
        int ox = indices[i];
        common_assert_msg(ox >= 0 && ox < out.rows(), "ox: " << ox << ", out: " << out.rows());
        out.row(ox) = in.row(i);
    }
}

template<typename DerivedA, typename DerivedB, typename Container>
void select_cols_reverse(const Eigen::MatrixBase<DerivedA> &in, const Container &indices, Eigen::MatrixBase<DerivedB> &out)
{
    assert_size(in, in.rows(), indices.size());
    for (int i = 0; i < indices.size(); ++i)
    {
        int ox = indices[i];
        common_assert_msg(ox >= 0 && ox < out.cols(), "ox: " << ox << ", out: " << out.cols());
        out.col() = in.col(i);
    }
}

template<typename DerivedA, typename DerivedB, typename ContainerA, typename ContainerB>
void select_rows(const Eigen::MatrixBase<DerivedA> &in, const ContainerA &in_indices, Eigen::MatrixBase<DerivedB> &out, const ContainerB &out_indices)
{
    assert_size_vector(out_indices, in_indices.size());
    for (int i = 0; i < in_indices.size(); ++i)
    {
        int ix = in_indices[i];
        int ox = out_indices[i];
        common_assert_msg(ix >= 0 && ix < in.rows(), "ix: " << ix << ", in: " << in.rows());
        common_assert_msg(ox >= 0 && ox < out.rows(), "ox: " << ox << ", out: " << out.rows());
        out.row(ox) = in.row(ix);
    }
}

template<typename DerivedA, typename DerivedB, typename ContainerA, typename ContainerB>
void select_cols(const Eigen::MatrixBase<DerivedA> &in, const ContainerA &in_indices, Eigen::MatrixBase<DerivedB> &out, const ContainerB &out_indices)
{
    assert_size_vector(out_indices, in_indices.size());
    for (int i = 0; i < in_indices.size(); ++i)
    {
        int ix = in_indices[i];
        int ox = out_indices[i];
        common_assert_msg(ix >= 0 && ix < in.cols(), "ix: " << ix << ", in: " << in.cols());
        common_assert_msg(ox >= 0 && ox < out.cols(), "ox: " << ox << ", out: " << out.cols());
        out.col(ox) = in.col(ix);
    }
}

}

#endif // EIGEN_UTILITIES_CONTAINER_UTILITIES_H


// Author(s): Wieger Wesselink
// Copyright: see the accompanying file COPYING or copy at
// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
//
// 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)
//
/// \file find_test.cpp
/// \brief Test for find functions.

#define BOOST_TEST_MODULE find_test
#include <iterator>
#include <set>
#include <vector>
#include <boost/test/included/unit_test_framework.hpp>
#include "mcrl2/core/detail/print_utility.h"
#include "mcrl2/data/find.h"
#include "mcrl2/data/function_sort.h"
#include "mcrl2/data/parse.h"
#include "mcrl2/data/standard_utility.h"

using namespace mcrl2;

inline
data::variable bool_(const std::string& name)
{
  return data::variable(core::identifier_string(name), data::sort_bool::bool_());
}

inline
data::variable nat(const std::string& name)
{
  return data::variable(core::identifier_string(name), data::sort_nat::nat());
}

inline
data::variable pos(const std::string& name)
{
  return data::variable(core::identifier_string(name), data::sort_pos::pos());
}

inline
data::data_expression parse_data_expression(const std::string& text)
{
  std::vector<data::variable> variable_context
  {
    nat("n"),
    nat("n1"),
    nat("n2"),
    nat("n3"),
    nat("n4"),
    bool_("b"),
    bool_("b1"),
    bool_("b2"),
    bool_("b3"),
    bool_("b4"),
    pos("p"),
    pos("p1"),
    pos("p2"),
    pos("p3"),
    pos("p4")
  };
  return data::parse_data_expression(text, variable_context);
};

template <typename T>
std::string print_set(const std::set<T>& v)
{
  std::set<std::string> s;
  for (const T& v_i: v)
  {
    s.insert(data::pp(v_i));
  }
  return core::detail::print_set(s);
}

BOOST_AUTO_TEST_CASE(test_containers)
{
  data::variable b = bool_("b");
  data::variable c = bool_("c");
  data::variable_vector v{b, c};
  BOOST_CHECK_EQUAL(print_set(find_all_variables(v)), "{ b, c }");
  BOOST_CHECK(data::search_variable(v, b));
  BOOST_CHECK(data::search_variable(v, c));

  data::variable_list l{b, c};
  BOOST_CHECK_EQUAL(print_set(find_all_variables(l)), "{ b, c }");
  BOOST_CHECK(data::search_variable(l, b));
  BOOST_CHECK(data::search_variable(l, c));
}

BOOST_AUTO_TEST_CASE(test_find)
{
  using utilities::detail::contains;

  data::variable n1 = nat("n1");
  data::variable n2 = nat("n2");
  data::variable n3 = nat("n3");
  data::variable n4 = nat("n4");

  data::variable b1 = bool_("b1");
  data::variable b2 = bool_("b2");
  data::variable b3 = bool_("b3");
  data::variable b4 = bool_("b4");

  data::variable p1 = pos("p1");
  data::variable p2 = pos("p2");
  data::variable p3 = pos("p3");
  data::variable p4 = pos("p4");

  std::set<data::variable> S{b1, p1, n1};
  BOOST_CHECK_EQUAL(print_set(find_all_variables(S)), "{ b1, n1, p1 }");
  BOOST_CHECK_EQUAL(print_set(find_sort_expressions(S)), "{ Bool, Nat, Pos }");
  BOOST_CHECK(search_variable(S, n1));
  BOOST_CHECK(!search_variable(S, n2));

  std::vector<data::variable> V{b1, p1, n1};
  BOOST_CHECK_EQUAL(print_set(find_all_variables(S)), "{ b1, n1, p1 }");
  BOOST_CHECK(search_variable(V, n1));
  BOOST_CHECK(!search_variable(V, n2));

  data::sort_expression_vector domain {data::sort_pos::pos(), data::sort_bool::bool_()};
  data::sort_expression sexpr = data::function_sort(domain, data::sort_nat::nat());
  data::variable q1(core::identifier_string("q1"), sexpr);
  BOOST_CHECK_EQUAL(print_set(find_sort_expressions(q1)), "{ Bool, Nat, Pos, Pos # Bool -> Nat }");

  data::data_expression x = parse_data_expression("(n1 == n2) && (n2 != n3)");
  BOOST_CHECK_EQUAL(print_set(find_all_variables(x)), "{ n1, n2, n3 }");
  BOOST_CHECK_EQUAL(print_set(find_free_variables(x)), "{ n1, n2, n3 }");
  BOOST_CHECK(search_variable(x, n1));
  BOOST_CHECK(search_variable(x, n2));
  BOOST_CHECK(search_variable(x, n3));
  BOOST_CHECK(!search_variable(x, n4));

  std::set<data::sort_expression> Z;
  find_sort_expressions(q1, std::inserter(Z, Z.end()));
  find_sort_expressions(S, std::inserter(Z, Z.end()));
  BOOST_CHECK_EQUAL(print_set(Z), "{ Bool, Nat, Pos, Pos # Bool -> Nat }");
}

BOOST_AUTO_TEST_CASE(find_all_variables_test)
{
  auto all_variables = [&](const std::string& text)
  {
    return print_set(data::find_all_variables(parse_data_expression(text)));
  };

  BOOST_CHECK_EQUAL(all_variables("n"), "{ n }");
  BOOST_CHECK_EQUAL(all_variables("exists n: Nat. (n == n)"), "{ n }");
  BOOST_CHECK_EQUAL(all_variables("exists n: Nat. true"), "{ n }");
  BOOST_CHECK_EQUAL(all_variables("2 whr n = n end"), "{ n }");
  BOOST_CHECK_EQUAL(all_variables("2 whr p = 3 end"), "{ p }");
}

BOOST_AUTO_TEST_CASE(find_free_variables_test)
{
  auto free_variables = [&](const std::string& text)
  {
    return print_set(data::find_free_variables(parse_data_expression(text)));
  };

  BOOST_CHECK_EQUAL(free_variables("n"), "{ n }");
  BOOST_CHECK_EQUAL(free_variables("exists n: Nat. (n == n)"), "{  }");
  BOOST_CHECK_EQUAL(free_variables("2 whr n = n end"), "{ n }");
  BOOST_CHECK_EQUAL(free_variables("2 whr p = 3 end"), "{  }");
}


//  (C) Copyright John Maddock 2007.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <pch.hpp>

#include <boost/math/concepts/real_concept.hpp>
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/math/special_functions/trunc.hpp>
#include <boost/math/special_functions/modf.hpp>
#include <boost/math/special_functions/sign.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <iostream>
#include <iomanip>

boost::mt19937 rng;

template <class T>
T get_random()
{
   //
   // Fill all the bits in T with random values,
   // likewise set the exponent to a random value
   // that will still fit inside a T, and always
   // have a remainder as well as an integer part.
   //
   int bits = boost::math::tools::digits<T>();
   int shift = 0;
   int exponent = rng() % (bits - 4);
   T result = 0;
   while(bits > 0)
   {
      result += ldexp(static_cast<T>(rng()), shift);
      shift += std::numeric_limits<int>::digits;
      bits -= std::numeric_limits<int>::digits;
   }
   return rng() & 1u ? -ldexp(frexp(result, &bits), exponent) : ldexp(frexp(result, &bits), exponent);
}

template <class T, class U>
void check_within_half(T a, U u)
{
   BOOST_MATH_STD_USING
   if(fabs(a-u) > 0.5f)
   {
      BOOST_ERROR("Rounded result differed by more than 0.5 from the original");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
   if((fabs(a - u) == 0.5f) && (fabs(static_cast<T>(u)) < fabs(a)))
   {
      BOOST_ERROR("Rounded result was towards zero with boost::round");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
}

//
// We may not have an abs overload for long long so provide a fall back:
//
template <class T>
inline T safe_abs(T const& v ...)
{
   return v < 0 ? -v : v;
}

template <class T, class U>
void check_trunc_result(T a, U u)
{
   BOOST_MATH_STD_USING
   if(fabs(a-u) >= 1)
   {
      BOOST_ERROR("Rounded result differed by more than 1 from the original");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
   if(abs(a) < safe_abs(u))
   {
      BOOST_ERROR("Truncated result had larger absolute value than the original");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
   if(fabs(static_cast<T>(u)) > fabs(a))
   {
      BOOST_ERROR("Rounded result was away from zero with boost::trunc");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
}

template <class T, class U>
void check_modf_result(T a, T fract, U ipart)
{
   BOOST_MATH_STD_USING
   if(fract + ipart != a)
   {
      BOOST_ERROR("Fractional and integer results do not add up to the original value");
      std::cerr << "Values were: " << std::setprecision(35) << " "
         << std::left << a << ipart << " " << fract << std::endl;
   }
   if((boost::math::sign(a) != boost::math::sign(fract)) && boost::math::sign(fract))
   {
      BOOST_ERROR("Original and fractional parts have differing signs");
      std::cerr << "Values were: " << std::setprecision(35) << " "
         << std::left << a << ipart << " " << fract << std::endl;
   }
   if((boost::math::sign(a) != boost::math::sign(ipart)) && boost::math::sign(ipart))
   {
      BOOST_ERROR("Original and integer parts have differing signs");
      std::cerr << "Values were: " << std::setprecision(35) << " "
         << std::left << a << ipart << " " << ipart << std::endl;
   }
   if(fabs(a-ipart) >= 1)
   {
      BOOST_ERROR("Rounded result differed by more than 1 from the original");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << ipart << std::endl;
   }
}

template <class T>
void test_round(T, const char* name )
{
   BOOST_MATH_STD_USING
#ifdef BOOST_HAS_LONG_LONG
   using boost::math::llround;  using boost::math::lltrunc;
#endif

   std::cout << "Testing rounding with type " << name << std::endl;

   for(int i = 0; i < 1000; ++i)
   {
      T arg = get_random<T>();
      T r = round(arg);
      check_within_half(arg, r);
      r = trunc(arg);
      check_trunc_result(arg, r);
      T frac = modf(arg, &r);
      check_modf_result(arg, frac, r);

      if(abs(r) < (std::numeric_limits<int>::max)())
      {
         int i = iround(arg);
         check_within_half(arg, i);
         i = itrunc(arg);
         check_trunc_result(arg, i);
         r = modf(arg, &i);
         check_modf_result(arg, r, i);
      }
      if(std::numeric_limits<T>::digits >= std::numeric_limits<int>::digits)
      {
         int si = iround(static_cast<T>((std::numeric_limits<int>::max)()));
         check_within_half(static_cast<T>((std::numeric_limits<int>::max)()), si);
         si = iround(static_cast<T>((std::numeric_limits<int>::min)()));
         check_within_half(static_cast<T>((std::numeric_limits<int>::min)()), si);
         si = itrunc(static_cast<T>((std::numeric_limits<int>::max)()));
         check_trunc_result(static_cast<T>((std::numeric_limits<int>::max)()), si);
         si = itrunc(static_cast<T>((std::numeric_limits<int>::min)()));
         check_trunc_result(static_cast<T>((std::numeric_limits<int>::min)()), si);
      }
      if(abs(r) < (std::numeric_limits<long>::max)())
      {
         long l = lround(arg);
         check_within_half(arg, l);
         l = ltrunc(arg);
         check_trunc_result(arg, l);
         r = modf(arg, &l);
         check_modf_result(arg, r, l);
      }
      if(std::numeric_limits<T>::digits >= std::numeric_limits<long>::digits)
      {
         long k = lround(static_cast<T>((std::numeric_limits<long>::max)()));
         check_within_half(static_cast<T>((std::numeric_limits<long>::max)()), k);
         k = lround(static_cast<T>((std::numeric_limits<long>::min)()));
         check_within_half(static_cast<T>((std::numeric_limits<long>::min)()), k);
         k = ltrunc(static_cast<T>((std::numeric_limits<long>::max)()));
         check_trunc_result(static_cast<T>((std::numeric_limits<long>::max)()), k);
         k = ltrunc(static_cast<T>((std::numeric_limits<long>::min)()));
         check_trunc_result(static_cast<T>((std::numeric_limits<long>::min)()), k);
      }

#ifdef BOOST_HAS_LONG_LONG
      if(abs(r) < (std::numeric_limits<boost::long_long_type>::max)())
      {
         boost::long_long_type ll = llround(arg);
         check_within_half(arg, ll);
         ll = lltrunc(arg);
         check_trunc_result(arg, ll);
         r = modf(arg, &ll);
         check_modf_result(arg, r, ll);
      }
      if(std::numeric_limits<T>::digits >= std::numeric_limits<boost::long_long_type>::digits)
      {
         boost::long_long_type j = llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()));
         check_within_half(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()), j);
         j = llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()));
         check_within_half(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()), j);
         j = lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()));
         check_trunc_result(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()), j);
         j = lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()));
         check_trunc_result(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()), j);
      }
#endif
   }
   //
   // Finish off by testing the error handlers:
   //
   BOOST_CHECK_THROW(iround(static_cast<T>(1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(iround(static_cast<T>(-1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(lround(static_cast<T>(1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(lround(static_cast<T>(-1e20)), boost::math::rounding_error);
#ifdef BOOST_HAS_LONG_LONG
   BOOST_CHECK_THROW(llround(static_cast<T>(1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(llround(static_cast<T>(-1e20)), boost::math::rounding_error);
#endif
   if(std::numeric_limits<T>::has_infinity)
   {
      BOOST_CHECK_THROW(round(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(iround(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(iround(-std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(lround(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(lround(-std::numeric_limits<T>::infinity()), boost::math::rounding_error);
   #ifdef BOOST_HAS_LONG_LONG
      BOOST_CHECK_THROW(llround(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(llround(-std::numeric_limits<T>::infinity()), boost::math::rounding_error);
   #endif
   }
   if(std::numeric_limits<T>::has_quiet_NaN)
   {
      BOOST_CHECK_THROW(round(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
      BOOST_CHECK_THROW(iround(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
      BOOST_CHECK_THROW(lround(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
   #ifdef BOOST_HAS_LONG_LONG
      BOOST_CHECK_THROW(llround(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
   #endif
   }
   BOOST_CHECK_THROW(itrunc(static_cast<T>(1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(itrunc(static_cast<T>(-1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(ltrunc(static_cast<T>(1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(ltrunc(static_cast<T>(-1e20)), boost::math::rounding_error);
#ifdef BOOST_HAS_LONG_LONG
   BOOST_CHECK_THROW(lltrunc(static_cast<T>(1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(lltrunc(static_cast<T>(-1e20)), boost::math::rounding_error);
#endif
   if(std::numeric_limits<T>::has_infinity)
   {
      BOOST_CHECK_THROW(trunc(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(itrunc(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(itrunc(-std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(ltrunc(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(ltrunc(-std::numeric_limits<T>::infinity()), boost::math::rounding_error);
   #ifdef BOOST_HAS_LONG_LONG
      BOOST_CHECK_THROW(lltrunc(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(lltrunc(-std::numeric_limits<T>::infinity()), boost::math::rounding_error);
   #endif
   }
   if(std::numeric_limits<T>::has_quiet_NaN)
   {
      BOOST_CHECK_THROW(trunc(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
      BOOST_CHECK_THROW(itrunc(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
      BOOST_CHECK_THROW(ltrunc(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
   #ifdef BOOST_HAS_LONG_LONG
      BOOST_CHECK_THROW(lltrunc(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
   #endif
   }
   if(std::numeric_limits<T>::digits >= std::numeric_limits<int>::digits)
   {
      BOOST_CHECK_THROW(itrunc(static_cast<T>((std::numeric_limits<int>::max)()) + 1), boost::math::rounding_error);
      BOOST_CHECK_THROW(itrunc(static_cast<T>((std::numeric_limits<int>::min)()) - 1), boost::math::rounding_error);
   }
   if(std::numeric_limits<T>::digits >= std::numeric_limits<long>::digits)
   {
      BOOST_CHECK_THROW(ltrunc(static_cast<T>((std::numeric_limits<long>::max)()) + 1), boost::math::rounding_error);
      BOOST_CHECK_THROW(ltrunc(static_cast<T>((std::numeric_limits<long>::min)()) - 1), boost::math::rounding_error);
   }
#ifndef BOOST_NO_LONG_LONG
   if(std::numeric_limits<T>::digits >= std::numeric_limits<boost::long_long_type>::digits)
   {
      BOOST_CHECK_THROW(lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()) + 1), boost::math::rounding_error);
      BOOST_CHECK_THROW(lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()) - 1), boost::math::rounding_error);
   }
#endif
   if(std::numeric_limits<T>::digits >= std::numeric_limits<int>::digits)
   {
      BOOST_CHECK_THROW(iround(static_cast<T>((std::numeric_limits<int>::max)()) + 1), boost::math::rounding_error);
      BOOST_CHECK_THROW(iround(static_cast<T>((std::numeric_limits<int>::min)()) - 1), boost::math::rounding_error);
   }
   if(std::numeric_limits<T>::digits >= std::numeric_limits<long>::digits)
   {
      BOOST_CHECK_THROW(lround(static_cast<T>((std::numeric_limits<long>::max)()) + 1), boost::math::rounding_error);
      BOOST_CHECK_THROW(lround(static_cast<T>((std::numeric_limits<long>::min)()) - 1), boost::math::rounding_error);
   }
#ifndef BOOST_NO_LONG_LONG
   if(std::numeric_limits<T>::digits >= std::numeric_limits<boost::long_long_type>::digits)
   {
      BOOST_CHECK_THROW(llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()) + 1), boost::math::rounding_error);
      BOOST_CHECK_THROW(llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()) - 1), boost::math::rounding_error);
   }
#endif
   //
   // try non-throwing error handlers:
   //
   boost::math::policies::policy<boost::math::policies::rounding_error<boost::math::policies::ignore_error> > pol;

   if(std::numeric_limits<T>::digits >= std::numeric_limits<int>::digits)
   {
      BOOST_CHECK_EQUAL(iround((std::numeric_limits<int>::max)() + T(1.0), pol), (std::numeric_limits<int>::max)());
      BOOST_CHECK_EQUAL(iround((std::numeric_limits<int>::min)() - T(1.0), pol), (std::numeric_limits<int>::min)());
      BOOST_CHECK_EQUAL(itrunc((std::numeric_limits<int>::max)() + T(1.0), pol), (std::numeric_limits<int>::max)());
      BOOST_CHECK_EQUAL(itrunc((std::numeric_limits<int>::min)() - T(1.0), pol), (std::numeric_limits<int>::min)());
   }
   if(std::numeric_limits<T>::digits >= std::numeric_limits<long>::digits)
   {
      BOOST_CHECK_EQUAL(lround((std::numeric_limits<long>::max)() + T(1.0), pol), (std::numeric_limits<long>::max)());
      BOOST_CHECK_EQUAL(lround((std::numeric_limits<long>::min)() - T(1.0), pol), (std::numeric_limits<long>::min)());
      BOOST_CHECK_EQUAL(ltrunc((std::numeric_limits<long>::max)() + T(1.0), pol), (std::numeric_limits<long>::max)());
      BOOST_CHECK_EQUAL(ltrunc((std::numeric_limits<long>::min)() - T(1.0), pol), (std::numeric_limits<long>::min)());
   }
#ifndef BOOST_NO_LONG_LONG
   if(std::numeric_limits<T>::digits >= std::numeric_limits<long long>::digits)
   {
      BOOST_CHECK_EQUAL(llround((std::numeric_limits<long long>::max)() + T(1.0), pol), (std::numeric_limits<long long>::max)());
      BOOST_CHECK_EQUAL(llround((std::numeric_limits<long long>::min)() - T(1.0), pol), (std::numeric_limits<long long>::min)());
      BOOST_CHECK_EQUAL(lltrunc((std::numeric_limits<long long>::max)() + T(1.0), pol), (std::numeric_limits<long long>::max)());
      BOOST_CHECK_EQUAL(lltrunc((std::numeric_limits<long long>::min)() - T(1.0), pol), (std::numeric_limits<long long>::min)());
   }
#endif
   // Again with bigger value:
   T big = 1e20f;
   BOOST_CHECK_EQUAL(iround(big, pol), (std::numeric_limits<int>::max)());
   BOOST_CHECK_EQUAL(lround(big, pol), (std::numeric_limits<long>::max)());
   BOOST_CHECK_EQUAL(iround(-big, pol), (std::numeric_limits<int>::min)());
   BOOST_CHECK_EQUAL(lround(-big, pol), (std::numeric_limits<long>::min)());
   BOOST_CHECK_EQUAL(itrunc(big, pol), (std::numeric_limits<int>::max)());
   BOOST_CHECK_EQUAL(ltrunc(big, pol), (std::numeric_limits<long>::max)());
   BOOST_CHECK_EQUAL(itrunc(-big, pol), (std::numeric_limits<int>::min)());
   BOOST_CHECK_EQUAL(ltrunc(-big, pol), (std::numeric_limits<long>::min)());
#ifndef BOOST_NO_LONG_LONG
   BOOST_CHECK_EQUAL(llround(big, pol), (std::numeric_limits<long long>::max)());
   BOOST_CHECK_EQUAL(llround(-big, pol), (std::numeric_limits<long long>::min)());
   BOOST_CHECK_EQUAL(lltrunc(big, pol), (std::numeric_limits<long long>::max)());
   BOOST_CHECK_EQUAL(lltrunc(-big, pol), (std::numeric_limits<long long>::min)());
#endif
}

BOOST_AUTO_TEST_CASE( test_main )
{
   test_round(0.1F, "float");
   test_round(0.1, "double");
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   test_round(0.1L, "long double");
   test_round(boost::math::concepts::real_concept(0.1), "real_concept");
#else
   std::cout << "<note>The long double tests have been disabled on this platform "
      "either because the long double overloads of the usual math functions are "
      "not available at all, or because they are too inaccurate for these tests "
      "to pass.</note>" << std::cout;
#endif

}


///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2016 Bjorn Reese <breese@users.sourceforge.net>
//
// 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)
//
///////////////////////////////////////////////////////////////////////////////

#include <boost/core/lightweight_test_trait.hpp>
#include <trial/online/detail/lightweight_test.hpp>

#include <ratio>
#include <trial/online/detail/type_traits.hpp>

//-----------------------------------------------------------------------------

namespace is_ratio_suite
{

struct valid_ratio
{
    static constexpr std::intmax_t num = 1;
    static constexpr std::intmax_t den = 2;
};

struct invalid_ratio_missing_den
{
    static constexpr std::intmax_t num = 1;
};

struct invalid_ratio_missing_num
{
    static constexpr std::intmax_t den = 1;
};

struct invalid_ratio_empty
{
};

void test()
{
    BOOST_TEST_TRAIT_TRUE((trial::online::detail::is_ratio<std::ratio<1,2>>));
    BOOST_TEST_TRAIT_TRUE((trial::online::detail::is_ratio<valid_ratio>));
    BOOST_TEST_TRAIT_FALSE((trial::online::detail::is_ratio<invalid_ratio_missing_den>));
    BOOST_TEST_TRAIT_FALSE((trial::online::detail::is_ratio<invalid_ratio_missing_num>));
    BOOST_TEST_TRAIT_FALSE((trial::online::detail::is_ratio<invalid_ratio_empty>));
}

} // namespace is_ratio_suite

//-----------------------------------------------------------------------------
// main
//-----------------------------------------------------------------------------

int main()
{
    is_ratio_suite::test();

    return boost::report_errors();
}


//=======================================================================
// Copyright (c) 2013 Piotr Wygocki
//               2014 Piotr Godlewski
//
// 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)
//=======================================================================
/**
 * @file steiner_network_example.cpp
 * @brief
 * @author Piotr Wygocki, Piotr Godlewski
 * @version 1.0
 * @date 2013-06-24
 */

//! [Steiner Network Example]
#include "paal/iterative_rounding/steiner_network/steiner_network.hpp"

#include <boost/graph/adjacency_list.hpp>

#include <iostream>
#include <vector>

int main() {
    using Graph = boost::adjacency_list<boost::vecS, boost::vecS,
        boost::undirectedS, boost::no_property,
        boost::property<boost::edge_weight_t, int>>;
    using Edge = boost::graph_traits<Graph>::edge_descriptor;

    // sample problem
    std::vector<std::pair<int, int>> edges {{0,1},{0,1},{1,2},{1,2},{2,0}};
    std::vector<int> costs {1,1,1,1,7};
    auto restrictions = [](int i, int j) {return 2;};

    Graph g(edges.begin(), edges.end(), costs.begin(), 3);

    std::vector<Edge> result_network;

    // optional input validity checking
    auto steiner_network = paal::ir::make_steiner_network(
        g, restrictions, std::back_inserter(result_network));
    auto error = steiner_network.check_input_validity();
    if (error) {
        std::cerr << "The input is not valid!" << std::endl;
        std::cerr << *error << std::endl;
        return -1;
    }

    // solve it
    auto result = paal::ir::steiner_network_iterative_rounding(
        g, restrictions, std::back_inserter(result_network));

    // print result
    if (result.first == paal::lp::OPTIMAL) {
        std::cout << "Edges in steiner network" << std::endl;
        for (auto e : result_network) {
            std::cout << "Edge " << e << std::endl;
        }
        std::cout << "Cost of the solution: " << *(result.second) << std::endl;
    } else {
        std::cout << "The instance is infeasible" << std::endl;
    }
    paal::lp::glp::free_env();
    return 0;
}
    //! [Steiner Network Example]


// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "main.h"

#include <Eigen/CXX11/Tensor>

using Eigen::Tensor;

struct InsertZeros {
  DSizes<DenseIndex, 2> dimensions(const Tensor<float, 2>& input) const {
    DSizes<DenseIndex, 2> result;
    result[0] = input.dimension(0) * 2;
    result[1] = input.dimension(1) * 2;
    return result;
  }

  template <typename Output, typename Device>
  void eval(const Tensor<float, 2>& input, Output& output, const Device& device) const
  {
    array<DenseIndex, 2> strides;
    strides[0] = 2;
    strides[1] = 2;
    output.stride(strides).device(device) = input;

    Eigen::DSizes<DenseIndex, 2> offsets(1,1);
    Eigen::DSizes<DenseIndex, 2> extents(output.dimension(0)-1, output.dimension(1)-1);
    output.slice(offsets, extents).stride(strides).device(device) = input.constant(0.0f);
  }
};

static void test_custom_unary_op()
{
  Tensor<float, 2> tensor(3,5);
  tensor.setRandom();

  Tensor<float, 2> result = tensor.customOp(InsertZeros());
  VERIFY_IS_EQUAL(result.dimension(0), 6);
  VERIFY_IS_EQUAL(result.dimension(1), 10);

  for (int i = 0; i < 6; i+=2) {
    for (int j = 0; j < 10; j+=2) {
      VERIFY_IS_EQUAL(result(i, j), tensor(i/2, j/2));
    }
  }
  for (int i = 1; i < 6; i+=2) {
    for (int j = 1; j < 10; j+=2) {
      VERIFY_IS_EQUAL(result(i, j), 0);
    }
  }
}

struct BatchMatMul {
  DSizes<DenseIndex, 3> dimensions(const Tensor<float, 3>& input1, const Tensor<float, 3>& input2) const {
    DSizes<DenseIndex, 3> result;
    result[0] = input1.dimension(0);
    result[1] = input2.dimension(1);
    result[2] = input2.dimension(2);
    return result;
  }

  template <typename Output, typename Device>
  void eval(const Tensor<float, 3>& input1, const Tensor<float, 3>& input2,
            Output& output, const Device& device) const
  {
    typedef Tensor<float, 3>::DimensionPair DimPair;
    array<DimPair, 1> dims;
    dims[0] = DimPair(1, 0);
    for (int i = 0; i < output.dimension(2); ++i) {
      output.template chip<2>(i).device(device) = input1.chip<2>(i).contract(input2.chip<2>(i), dims);
    }
  }
};

static void test_custom_binary_op()
{
  Tensor<float, 3> tensor1(2,3,5);
  tensor1.setRandom();
  Tensor<float, 3> tensor2(3,7,5);
  tensor2.setRandom();

  Tensor<float, 3> result = tensor1.customOp(tensor2, BatchMatMul());
  for (int i = 0; i < 5; ++i) {
    typedef Tensor<float, 3>::DimensionPair DimPair;
    array<DimPair, 1> dims;
    dims[0] = DimPair(1, 0);
    Tensor<float, 2> reference = tensor1.chip<2>(i).contract(tensor2.chip<2>(i), dims);
    TensorRef<Tensor<float, 2> > val = result.chip<2>(i);
    for (int j = 0; j < 2; ++j) {
      for (int k = 0; k < 7; ++k) {
        VERIFY_IS_APPROX(val(j, k), reference(j, k));
      }
    }
  }
}

void test_cxx11_tensor_custom_op()
{
  CALL_SUBTEST(test_custom_unary_op());
  CALL_SUBTEST(test_custom_binary_op());
}