#ifndef QUBUS_QTL_AST_TRANSLATION_HPP
#define QUBUS_QTL_AST_TRANSLATION_HPP

#include <hpx/config.hpp>

#include <qubus/qtl/ast.hpp>

#include <qubus/IR/qir.hpp>
#include <qubus/qtl/IR/all.hpp>

#include <hpx/include/naming.hpp>

#include <boost/hana/for_each.hpp>

#include <qubus/util/unreachable.hpp>

#include <qubus/util/optional_ref.hpp>
#include <qubus/util/unused.hpp>

#include <boost/range/adaptor/reversed.hpp>

#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>

namespace qubus
{
namespace qtl
{
namespace ast
{

template <typename T, long int Rank>
class tensor_expr;

template <binary_operator_tag Tag, typename LHS, typename RHS>
auto translate_ast(binary_operator<Tag, LHS, RHS> node, domain& dom)
{
    auto lhs = translate_ast(node.lhs(), dom);
    auto rhs = translate_ast(node.rhs(), dom);

    switch (node.tag())
    {
    case binary_operator_tag::plus:
        return qubus::operator+(std::move(lhs), std::move(rhs));
    case binary_operator_tag::minus:
        return qubus::operator-(std::move(lhs), std::move(rhs));
    case binary_operator_tag::multiplies:
        return qubus::operator*(std::move(lhs), std::move(rhs));
    case binary_operator_tag::divides:
        return qubus::operator/(std::move(lhs), std::move(rhs));
    }

    QUBUS_UNREACHABLE();
}

template <typename Body>
auto translate_ast(contraction<index, Body> node, domain& dom)
{
    auto idx_var = node.contraction_index().var();

    auto body = translate_ast(node.body(), dom);

    return qubus::qtl::sum(std::move(idx_var), std::move(body));
}

template <long int Rank, typename Body>
auto translate_ast(contraction<multi_index<Rank>, Body> node, domain& dom)
{
    auto multi_idx_var = node.contraction_index().var();

    std::vector<variable_declaration> indices;

    for (long int i = 0; i < node.contraction_index().rank(); ++i)
    {
        auto idx_var = node.contraction_index()[i].var();

        indices.push_back(idx_var);
    }

    auto body = translate_ast(node.body(), dom);

    return qubus::qtl::sum(std::move(indices), std::move(multi_idx_var), std::move(body));
}

template <typename Tensor, typename... Indices>
auto translate_ast(subscripted_tensor<Tensor, Indices...> node, domain& dom)
{
    auto tensor = translate_ast(node.tensor(), dom);

    auto accessible_tensor = tensor->template try_as<access_expr>();

    if (!accessible_tensor)
        throw 0; // Invalid tensor

    std::vector<std::unique_ptr<expression>> indices;

    boost::hana::for_each(node.indices(), [&indices, &dom](auto index) {
        indices.push_back(translate_ast(index, dom));
    });

    // FIXME: Avoid the copy of accessible_tensor.
    return subscription(clone(*accessible_tensor), std::move(indices));
}

template <typename Tensor, typename... Indices>
auto translate_ast(sliced_tensor<Tensor, Indices...> node, domain& dom)
{
    auto tensor = translate_ast(node.tensor(), dom);

    std::vector<std::unique_ptr<expression>> indices;
    indices.reserve(sizeof...(Indices));

    boost::hana::for_each(node.ranges(), [&indices, &dom](const auto& index) {
        indices.push_back(translate_ast(index, dom));
    });

    return subscription(std::move(tensor), std::move(indices));
}

inline auto translate_ast(const index& idx, domain& /*unused*/)
{
    return var(idx.var());
}

template <long int Rank>
auto translate_ast(const multi_index<Rank>& idx, domain& /*unused*/)
{
    return capture_multi_index(idx);
}

template <typename T, long int Rank>
auto translate_ast(const tensor_expr<T, Rank>& tensor, domain& /*unused*/)
{
    return clone(tensor.code());
}

template <typename T, long int Rank>
auto translate_ast(const tensor_var<T, Rank>& tensor, domain& /*unused*/)
{
    return var(tensor.var());
}

template <typename T, long int Rank>
auto translate_ast(const sparse_tensor_var<T, Rank>& tensor, domain& /*unused*/)
{
    return var(tensor.var());
}

template <typename Tensor,
          typename /*Enabled*/ = typename std::enable_if<!std::is_arithmetic<Tensor>::value>::type>
auto translate_ast(const Tensor& tensor, domain& /*unused*/)
{
    return qubus::qtl::obj(tensor.get_object());
}

template <typename T,
        typename /*Enabled*/ = typename std::enable_if<std::is_arithmetic<T>::value>::type>
auto translate_ast(T node, domain& dom)
{
    return lit(node);
}

inline auto translate_ast(const range& r, domain& dom)
{
    auto start = translate_ast(r.start, dom);
    auto end = translate_ast(r.end, dom);
    auto stride = translate_ast(r.stride, dom);

    return ::qubus::range(std::move(start), std::move(end), std::move(stride));
}

template <typename FirstIndex, typename SecondIndex>
auto translate_ast(kronecker_delta<FirstIndex, SecondIndex> node, domain& dom)
{
    auto first_index = translate_ast(node.first_index(), dom);
    auto second_index = translate_ast(node.second_index(), dom);

    return qubus::qtl::kronecker_delta(node.extent(), std::move(first_index),
                                       std::move(second_index));
}
}
}
}

#endif


/*
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.

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

#include <limits>

#include "oslexec_pvt.h"
#include "noiseimpl.h"
#include "OSL/dual_vec.h"
#include "OSL/Imathx.h"

#include <OpenImageIO/fmath.h>

using namespace OSL;

OSL_NAMESPACE_ENTER
namespace pvt {

#if 0 // only when testing the statistics of perlin noise to normalize the range

#include <boost/random.hpp>

void test_perlin(int d) {
    HashScalar h;
    float noise_min = +std::numeric_limits<float>::max();
    float noise_max = -std::numeric_limits<float>::max();
    float noise_avg = 0;
    float noise_avg2 = 0;
    float noise_stddev;
    boost::mt19937 rndgen;
    boost::uniform_01<boost::mt19937, float> rnd(rndgen);
    printf("Running perlin-%d noise test ...\n", d);
    const int n = 100000000;
    const float r = 1024;
    for (int i = 0; i < n; i++) {
        float noise;
        float nx = rnd(); nx = (2 * nx - 1) * r;
        float ny = rnd(); ny = (2 * ny - 1) * r;
        float nz = rnd(); nz = (2 * nz - 1) * r;
        float nw = rnd(); nw = (2 * nw - 1) * r;
        switch (d) {
            case 1: perlin(noise, h, nx); break;
            case 2: perlin(noise, h, nx, ny); break;
            case 3: perlin(noise, h, nx, ny, nz); break;
            case 4: perlin(noise, h, nx, ny, nz, nw); break;
        }
        if (noise_min > noise) noise_min = noise;
        if (noise_max < noise) noise_max = noise;
        noise_avg += noise;
        noise_avg2 += noise * noise;
    }
    noise_avg /= n;
    noise_stddev = std::sqrt((noise_avg2 - noise_avg * noise_avg * n) / n);
    printf("Result: perlin-%d noise stats:\n\tmin: %.17g\n\tmax: %.17g\n\tavg: %.17g\n\tdev: %.17g\n",
            d, noise_min, noise_max, noise_avg, noise_stddev);
    printf("Normalization: %.17g\n", 1.0f / std::max(fabsf(noise_min), fabsf(noise_max)));
}

#endif

/***********************************************************************
 * noise routines callable by the LLVM-generated code.
 */

#if 1

#define NOISE_IMPL(opname,implname)                                     \
OSL_SHADEOP float osl_ ##opname## _ff (float x) {                       \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, x);                                                        \
    return r;                                                           \
}                                                                       \
                                                                        \
OSL_SHADEOP float osl_ ##opname## _fff (float x, float y) {             \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, x, y);                                                     \
    return r;                                                           \
}                                                                       \
                                                                        \
OSL_SHADEOP float osl_ ##opname## _fv (char *x) {                       \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, VEC(x));                                                   \
    return r;                                                           \
}                                                                       \
                                                                        \
OSL_SHADEOP float osl_ ##opname## _fvf (char *x, float y) {             \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, VEC(x), y);                                                \
    return r;                                                           \
}                                                                       \
                                                                        \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vf (char *r, float x) {               \
    implname impl;                                                      \
    impl (VEC(r), x);                                                   \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vff (char *r, float x, float y) {     \
    implname impl;                                                      \
    impl (VEC(r), x, y);                                                \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vv (char *r, char *x) {               \
    implname impl;                                                      \
    impl (VEC(r), VEC(x));                                              \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vvf (char *r, char *x, float y) {     \
    implname impl;                                                      \
    impl (VEC(r), VEC(x), y);                                           \
}

#define NOISE_IMPL_DERIV(opname,implname)                               \
OSL_SHADEOP void osl_ ##opname## _dfdf (char *r, char *x) {             \
    implname impl;                                                      \
    impl (DFLOAT(r), DFLOAT(x));                                        \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdfdf (char *r, char *x, char *y) {  \
    implname impl;                                                      \
    impl (DFLOAT(r), DFLOAT(x), DFLOAT(y));                             \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdff (char *r, char *x, float y) {   \
    implname impl;                                                      \
    impl (DFLOAT(r), DFLOAT(x), Dual2<float>(y));                       \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dffdf (char *r, float x, char *y) {   \
    implname impl;                                                      \
    impl (DFLOAT(r), Dual2<float>(x), DFLOAT(y));                       \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdv (char *r, char *x) {             \
    implname impl;                                                      \
    impl (DFLOAT(r), DVEC(x));                                          \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvdf (char *r, char *x, char *y) {  \
    implname impl;                                                      \
    impl (DFLOAT(r), DVEC(x), DFLOAT(y));                               \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvf (char *r, char *x, float y) {   \
    implname impl;                                                      \
    impl (DFLOAT(r), DVEC(x), Dual2<float>(y));                         \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfvdf (char *r, char *x, char *y) {   \
    implname impl;                                                      \
    impl (DFLOAT(r), Dual2<Vec3>(VEC(x)), DFLOAT(y));                   \
}                                                                       \
                                                                        \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdf (char *r, char *x) {             \
    implname impl;                                                      \
    impl (DVEC(r), DFLOAT(x));                                          \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdfdf (char *r, char *x, char *y) {  \
    implname impl;                                                      \
    impl (DVEC(r), DFLOAT(x), DFLOAT(y));                               \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdff (char *r, char *x, float y) {   \
    implname impl;                                                      \
    impl (DVEC(r), DFLOAT(x), Dual2<float>(y));                         \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvfdf (char *r, float x, char *y) {   \
    implname impl;                                                      \
    impl (DVEC(r), Dual2<float>(x), DFLOAT(y));                         \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdv (char *r, char *x) {             \
    implname impl;                                                      \
    impl (DVEC(r), DVEC(x));                                            \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvdf (char *r, char *x, char *y) {  \
    implname impl;                                                      \
    impl (DVEC(r), DVEC(x), DFLOAT(y));                                 \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvf (char *r, char *x, float y) {   \
    implname impl;                                                      \
    impl (DVEC(r), DVEC(x), Dual2<float>(y));                           \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvvdf (char *r, char *x, char *y) {   \
    implname impl;                                                      \
    impl (DVEC(r), Dual2<Vec3>(VEC(x)), DFLOAT(y));                     \
}

#define NOISE_IMPL_DERIV_OPT(opname,implname)                           \
OSL_SHADEOP void osl_ ##opname## _dfdf (char *name, char *r, char *x, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DFLOAT(x), (ShaderGlobals *)sg, (NoiseParams *)opt);                                   \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdfdf (char *name, char *r, char *x, char *y, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DFLOAT(x), DFLOAT(y), (ShaderGlobals *)sg, (NoiseParams *)opt);                        \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdv (char *name, char *r, char *x, char *sg, char *opt) {  \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DVEC(x), (ShaderGlobals *)sg, (NoiseParams *)opt);                                     \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvdf (char *name, char *r, char *x, char *y, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DVEC(x), DFLOAT(y), (ShaderGlobals *)sg, (NoiseParams *)opt);                          \
}                                                                       \
                                                                        \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdf (char *name, char *r, char *x, char *sg, char *opt) {  \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DFLOAT(x), (ShaderGlobals *)sg, (NoiseParams *)opt);                                     \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdfdf (char *name, char *r, char *x, char *y, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DFLOAT(x), DFLOAT(y), (ShaderGlobals *)sg, (NoiseParams *)opt);                                     \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdv (char *name, char *r, char *x, char *sg, char *opt) {  \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DVEC(x), (ShaderGlobals *)sg, (NoiseParams *)opt);                                       \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvdf (char *name, char *r, char *x, char *y, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DVEC(x), DFLOAT(y), (ShaderGlobals *)sg, (NoiseParams *)opt);                            \
}

NOISE_IMPL (cellnoise, CellNoise)
NOISE_IMPL (noise, Noise)
NOISE_IMPL_DERIV (noise, Noise)
NOISE_IMPL (snoise, SNoise)
NOISE_IMPL_DERIV (snoise, SNoise)
NOISE_IMPL (simplexnoise, SimplexNoise)
NOISE_IMPL_DERIV (simplexnoise, SimplexNoise)
NOISE_IMPL (usimplexnoise, USimplexNoise)
NOISE_IMPL_DERIV (usimplexnoise, USimplexNoise)

#define PNOISE_IMPL(opname,implname)                                    \
    OSL_SHADEOP float osl_ ##opname## _fff (float x, float px) {        \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, x, px);                                                    \
    return r;                                                           \
}                                                                       \
                                                                        \
OSL_SHADEOP float osl_ ##opname## _fffff (float x, float y, float px, float py) { \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, x, y, px, py);                                             \
    return r;                                                           \
}                                                                       \
                                                                        \
OSL_SHADEOP float osl_ ##opname## _fvv (char *x, char *px) {            \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, VEC(x), VEC(px));                                          \
    return r;                                                           \
}                                                                       \
                                                                        \
OSL_SHADEOP float osl_ ##opname## _fvfvf (char *x, float y, char *px, float py) { \
    implname impl;                                                      \
    float r;                                                            \
    impl (r, VEC(x), y, VEC(px), py);                                   \
    return r;                                                           \
}                                                                       \
                                                                        \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vff (char *r, float x, float px) {    \
    implname impl;                                                      \
    impl (VEC(r), x, px);                                               \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vffff (char *r, float x, float y, float px, float py) { \
    implname impl;                                                      \
    impl (VEC(r), x, y, px, py);                                        \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vvv (char *r, char *x, char *px) {    \
    implname impl;                                                      \
    impl (VEC(r), VEC(x), VEC(px));                                     \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _vvfvf (char *r, char *x, float y, char *px, float py) { \
    implname impl;                                                      \
    impl (VEC(r), VEC(x), y, VEC(px), py);                              \
}

#define PNOISE_IMPL_DERIV(opname,implname)                              \
OSL_SHADEOP void osl_ ##opname## _dfdff (char *r, char *x, float px) {  \
    implname impl;                                                      \
    impl (DFLOAT(r), DFLOAT(x), px);                                    \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdfdfff (char *r, char *x, char *y, float px, float py) { \
    implname impl;                                                      \
    impl (DFLOAT(r), DFLOAT(x), DFLOAT(y), px, py);                     \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdffff (char *r, char *x, float y, float px, float py) { \
    implname impl;                                                      \
    impl (DFLOAT(r), DFLOAT(x), Dual2<float>(y), px, py);               \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dffdfff (char *r, float x, char *y, float px, float py) { \
    implname impl;                                                      \
    impl (DFLOAT(r), Dual2<float>(x), DFLOAT(y), px, py);               \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvv (char *r, char *x, char *px) {  \
    implname impl;                                                      \
    impl (DFLOAT(r), DVEC(x), VEC(px));                                 \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvdfvf (char *r, char *x, char *y, char *px, float py) { \
    implname impl;                                                      \
    impl (DFLOAT(r), DVEC(x), DFLOAT(y), VEC(px), py);                  \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvfvf (char *r, char *x, float y, char *px, float py) { \
    implname impl;                                                      \
    impl (DFLOAT(r), DVEC(x), Dual2<float>(y), VEC(px), py);            \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfvdfvf (char *r, char *x, char *y, char *px, float py) { \
    implname impl;                                                      \
    impl (DFLOAT(r), Dual2<Vec3>(VEC(x)), DFLOAT(y), VEC(px), py);      \
}                                                                       \
                                                                        \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdff (char *r, char *x, float px) {  \
    implname impl;                                                      \
    impl (DVEC(r), DFLOAT(x), px);                                      \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdfdfff (char *r, char *x, char *y, float px, float py) { \
    implname impl;                                                      \
    impl (DVEC(r), DFLOAT(x), DFLOAT(y), px, py);                       \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdffff (char *r, char *x, float y, float px, float py) { \
    implname impl;                                                      \
    impl (DVEC(r), DFLOAT(x), Dual2<float>(y), px, py);                 \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvfdfff (char *r, float x, char *y, float px, float py) { \
    implname impl;                                                      \
    impl (DVEC(r), Dual2<float>(x), DFLOAT(y), px, py);                 \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvv (char *r, char *x, char *px) {  \
    implname impl;                                                      \
    impl (DVEC(r), DVEC(x), VEC(px));                                   \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvdfvf (char *r, char *x, char *y, char *px, float py) { \
    implname impl;                                                      \
    impl (DVEC(r), DVEC(x), DFLOAT(y), VEC(px), py);                    \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvfvf (char *r, char *x, float y, float *px, float py) { \
    implname impl;                                                      \
    impl (DVEC(r), DVEC(x), Dual2<float>(y), VEC(px), py);              \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvvdfvf (char *r, char *x, char *px, char *y, float py) { \
    implname impl;                                                      \
    impl (DVEC(r), Dual2<Vec3>(VEC(x)), DFLOAT(y), VEC(px), py);        \
}

#define PNOISE_IMPL_DERIV_OPT(opname,implname)                          \
OSL_SHADEOP void osl_ ##opname## _dfdff (char *name, char *r, char *x, float px, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DFLOAT(x), px, (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdfdfff (char *name, char *r, char *x, char *y, float px, float py, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DFLOAT(x), DFLOAT(y), px, py, (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvv (char *name, char *r, char *x, char *px, char *sg, char *opt) {  \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DVEC(x), VEC(px), (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dfdvdfvf (char *name, char *r, char *x, char *y, char *px, float py, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DFLOAT(r), DVEC(x), DFLOAT(y), VEC(px), py, (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdff (char *name, char *r, char *x, float px, char *sg, char *opt) {  \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DFLOAT(x), px, (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdfdfff (char *name, char *r, char *x, char *y, float px, float py, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DFLOAT(x), DFLOAT(y), px, py, (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvv (char *name, char *r, char *x, char *px, char *sg, char *opt) {  \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DVEC(x), VEC(px), (ShaderGlobals *)sg, (NoiseParams *)opt); \
}                                                                       \
                                                                        \
OSL_SHADEOP void osl_ ##opname## _dvdvdfvf (char *name, char *r, char *x, char *y, char *px, float py, char *sg, char *opt) { \
    implname impl;                                                      \
    impl (USTR(name), DVEC(r), DVEC(x), DFLOAT(y), VEC(px), py, (ShaderGlobals *)sg, (NoiseParams *)opt); \
}

PNOISE_IMPL (pcellnoise, PeriodicCellNoise)
PNOISE_IMPL (pnoise, PeriodicNoise)
PNOISE_IMPL_DERIV (pnoise, PeriodicNoise)
PNOISE_IMPL (psnoise, PeriodicSNoise)
PNOISE_IMPL_DERIV (psnoise, PeriodicSNoise)

struct GaborNoise {
    GaborNoise () { }

    // Gabor always uses derivatives, so dual versions only

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<float> &x,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = gabor (x, opt);
    }

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<float> &x, const Dual2<float> &y,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = gabor (x, y, opt);
    }

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<Vec3> &p,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = gabor (p, opt);
    }

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<Vec3> &p, const Dual2<float> &t,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        // FIXME -- This is very broken, we are ignoring 4D!
        result = gabor (p, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<float> &x,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = gabor3 (x, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<float> &x, const Dual2<float> &y,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = gabor3 (x, y, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<Vec3> &p,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = gabor3 (p, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<Vec3> &p, const Dual2<float> &t,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        // FIXME -- This is very broken, we are ignoring 4D!
        result = gabor3 (p, opt);
    }
};

struct GaborPNoise {
    GaborPNoise () { }

    // Gabor always uses derivatives, so dual versions only

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<float> &x, float px,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = pgabor (x, px, opt);
    }

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<float> &x, const Dual2<float> &y,
                            float px, float py,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = pgabor (x, y, px, py, opt);
    }

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<Vec3> &p, const Vec3 &pp,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = pgabor (p, pp, opt);
    }

    inline void operator() (ustring noisename, Dual2<float> &result,
                            const Dual2<Vec3> &p, const Dual2<float> &t,
                            const Vec3 &pp, float tp,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        // FIXME -- This is very broken, we are ignoring 4D!
        result = pgabor (p, pp, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<float> &x, float px,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = pgabor3 (x, px, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<float> &x, const Dual2<float> &y,
                            float px, float py,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = pgabor3 (x, y, px, py, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<Vec3> &p, const Vec3 &pp,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        result = pgabor3 (p, pp, opt);
    }

    inline void operator() (ustring noisename, Dual2<Vec3> &result,
                            const Dual2<Vec3> &p, const Dual2<float> &t,
                            const Vec3 &pp, float tp,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        // FIXME -- This is very broken, we are ignoring 4D!
        result = pgabor3 (p, pp, opt);
    }
};

NOISE_IMPL_DERIV_OPT (gabornoise, GaborNoise)
PNOISE_IMPL_DERIV_OPT (gaborpnoise, GaborPNoise)

struct GenericNoise {
    GenericNoise () { }

    // Template on R, S, and T to be either float or Vec3

    // dual versions -- this is always called with derivs

    template<class R, class S>
    inline void operator() (ustring name, Dual2<R> &result, const Dual2<S> &s,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        if (name == Strings::uperlin || name == Strings::noise) {
            Noise noise;
            noise(result, s);
        } else if (name == Strings::perlin || name == Strings::snoise) {
            SNoise snoise;
            snoise(result, s);
        } else if (name == Strings::simplexnoise || name == Strings::simplex) {
            SimplexNoise simplexnoise;
            simplexnoise(result, s);
        } else if (name == Strings::usimplexnoise || name == Strings::usimplex) {
            USimplexNoise usimplexnoise;
            usimplexnoise(result, s);
        } else if (name == Strings::cell) {
            CellNoise cellnoise;
            cellnoise(result.val(), s.val());
            result.clear_d();
        } else if (name == Strings::gabor) {
            GaborNoise gnoise;
            gnoise (name, result, s, sg, opt);
        } else {
            ((ShadingContext *)sg->context)->error ("Unknown noise type \"%s\"", name.c_str());
        }
    }

    template<class R, class S, class T>
    inline void operator() (ustring name, Dual2<R> &result,
                            const Dual2<S> &s, const Dual2<T> &t,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        if (name == Strings::uperlin || name == Strings::noise) {
            Noise noise;
            noise(result, s, t);
        } else if (name == Strings::perlin || name == Strings::snoise) {
            SNoise snoise;
            snoise(result, s, t);
        } else if (name == Strings::simplexnoise || name == Strings::simplex) {
            SimplexNoise simplexnoise;
            simplexnoise(result, s, t);
        } else if (name == Strings::usimplexnoise || name == Strings::usimplex) {
            USimplexNoise usimplexnoise;
            usimplexnoise(result, s, t);
        } else if (name == Strings::cell) {
            CellNoise cellnoise;
            cellnoise(result.val(), s.val(), t.val());
            result.clear_d();
        } else if (name == Strings::gabor) {
            GaborNoise gnoise;
            gnoise (name, result, s, t, sg, opt);
        } else {
            ((ShadingContext *)sg->context)->error ("Unknown noise type \"%s\"", name.c_str());
        }
    }
};

NOISE_IMPL_DERIV_OPT (genericnoise, GenericNoise)

struct GenericPNoise {
    GenericPNoise () { }

    // Template on R, S, and T to be either float or Vec3

    // dual versions -- this is always called with derivs

    template<class R, class S>
    inline void operator() (ustring name, Dual2<R> &result, const Dual2<S> &s,
                            const S &sp,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        if (name == Strings::uperlin || name == Strings::noise) {
            PeriodicNoise noise;
            noise(result, s, sp);
        } else if (name == Strings::perlin || name == Strings::snoise) {
            PeriodicSNoise snoise;
            snoise(result, s, sp);
        } else if (name == Strings::cell) {
            PeriodicCellNoise cellnoise;
            cellnoise(result.val(), s.val(), sp);
            result.clear_d();
        } else if (name == Strings::gabor) {
            GaborPNoise gnoise;
            gnoise (name, result, s, sp, sg, opt);
        } else {
            ((ShadingContext *)sg->context)->error ("Unknown noise type \"%s\"", name.c_str());
        }
    }

    template<class R, class S, class T>
    inline void operator() (ustring name, Dual2<R> &result,
                            const Dual2<S> &s, const Dual2<T> &t,
                            const S &sp, const T &tp,
                            ShaderGlobals *sg, const NoiseParams *opt) const {
        if (name == Strings::uperlin || name == Strings::noise) {
            PeriodicNoise noise;
            noise(result, s, t, sp, tp);
        } else if (name == Strings::perlin || name == Strings::snoise) {
            PeriodicSNoise snoise;
            snoise(result, s, t, sp, tp);
        } else if (name == Strings::cell) {
            PeriodicCellNoise cellnoise;
            cellnoise(result.val(), s.val(), t.val(), sp, tp);
            result.clear_d();
        } else if (name == Strings::gabor) {
            GaborPNoise gnoise;
            gnoise (name, result, s, t, sp, tp, sg, opt);
        } else {
            ((ShadingContext *)sg->context)->error ("Unknown noise type \"%s\"", name.c_str());
        }
    }
};

PNOISE_IMPL_DERIV_OPT (genericpnoise, GenericPNoise)

// Utility: retrieve a pointer to the ShadingContext's noise params
// struct, also re-initialize its contents.
OSL_SHADEOP void *
osl_get_noise_options (void *sg_)
{
    ShaderGlobals *sg = (ShaderGlobals *)sg_;
    RendererServices::NoiseOpt *opt = sg->context->noise_options_ptr ();
    new (opt) RendererServices::NoiseOpt;
    return opt;
}

OSL_SHADEOP void
osl_noiseparams_set_anisotropic (void *opt, int a)
{
    ((RendererServices::NoiseOpt *)opt)->anisotropic = a;
}

OSL_SHADEOP void
osl_noiseparams_set_do_filter (void *opt, int a)
{
    ((RendererServices::NoiseOpt *)opt)->do_filter = a;
}

OSL_SHADEOP void
osl_noiseparams_set_direction (void *opt, void *dir)
{
    ((RendererServices::NoiseOpt *)opt)->direction = VEC(dir);
}

OSL_SHADEOP void
osl_noiseparams_set_bandwidth (void *opt, float b)
{
    ((RendererServices::NoiseOpt *)opt)->bandwidth = b;
}

OSL_SHADEOP void
osl_noiseparams_set_impulses (void *opt, float i)
{
    ((RendererServices::NoiseOpt *)opt)->impulses = i;
}

} // namespace pvt
OSL_NAMESPACE_EXIT

#endif


// Copyright 2019 Josh Pieper, jjp@pobox.com.
//
// 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.

#include "mjlib/base/eigen.h"

#include <boost/test/auto_unit_test.hpp>

#include "mjlib/base/json5_write_archive.h"

using namespace mjlib;

BOOST_AUTO_TEST_CASE(BasicEigenVisit) {
  Eigen::Matrix3d matrix;
  matrix(0, 0) = 1;
  matrix(0, 1) = 2;
  matrix(0, 2) = 3;
  matrix(2, 2) = 9;
  const auto actual = base::Json5WriteArchive::Write(matrix);
  const std::string expected = R"XXX([
    [
    1,
    0,
    0
  ],
    [
    2,
    0,
    0
  ],
    [
    3,
    0,
    9
  ]
  ])XXX";
  BOOST_TEST(actual == expected);
}

BOOST_AUTO_TEST_CASE(EigenVectorVisit) {
  Eigen::Vector3d vector;
  vector(0) = 2;
  vector(1) = 5;
  vector(2) = 6;
  const auto actual = base::Json5WriteArchive::Write(vector);
  const std::string expected = R"XXX([
    2,
    5,
    6
  ])XXX";
  BOOST_TEST(actual == expected);
}

namespace {
struct StructWithEigen {
  Eigen::Vector3d vector;

  template <typename Archive>
  void Serialize(Archive* a) {
    a->Visit(MJ_NVP(vector));
  }
};
}

BOOST_AUTO_TEST_CASE(EigenStruct) {
  StructWithEigen test;
  const auto actual = base::Json5WriteArchive::Write(test);
  const std::string expected = R"XXX({
    "vector" : [
      0,
      0,
      0
    ]
  })XXX";
  BOOST_TEST(actual == expected);
}


// Filename: init_list_example.cpp (part of MTL4)

#include <iostream>
#include <boost/numeric/mtl/mtl.hpp>

template <typename Matrix>
void p(const Matrix& A)
{
    std::cout << A;
}

int main()
{
#if defined(MTL_WITH_INITLIST) && defined(MTL_WITH_AUTO) && defined(MTL_WITH_RANGEDFOR)
    const mtl::dense2D<double> A={{3, 4},
	  	                 {5, 6}};
    mtl::dense2D<double>       B;
    B= {{3, 4},
	{5, 6}};

    mtl::dense_vector<double> v= {2, 3, 4, 5};

    p(mtl::dense2D<double>{{1, 2}, {2, 3}});
#endif
    return 0;
}


#include "data.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <boost/foreach.hpp>

using namespace std;

Pair::Pair(const std::vector<float>& x1,const std::vector<float>& x2,const int label) :
m_x1(x1), m_x2(x2), m_label(label)
{
}

Pair::~Pair()
{
}

// FileData
/**
 * DataFile Format should be:
 * datatype: eg: int/float/double/etc.)
 * size: eg: numRows numCols
 * data (last column should be the labels)
 */
FileData::FileData()
{
}

FileData::FileData(const std::string& dataFileName, const std::string& labelsFileName) : m_dataFileName( dataFileName ),
        m_labelsFileName( labelsFileName )
{

}

FileData::~FileData()
{
    // Implement me
}

void FileData::setDataFileName(const std::string& fileName)
{
    m_dataFileName = fileName;
}

void FileData::setLabelsFileName(const std::string& fileName)
{
    m_labelsFileName = fileName;
}

void FileData::readData()
{
    std::fstream myfile(m_dataFileName.c_str(),ios::in);
    if (myfile.is_open())
    {
        std::string line;
        // get datatype
        getline(myfile,line);
        // get size
        getline(myfile,line);
        stringstream ss(line);
        string tmp;
        int numRows, numCols;
        ss >> numRows;
        ss >> numCols;
        // pop-up the dense/sparse thing
        getline(myfile,line);

        m_data.resize(numRows,numCols);

        for (int row = 0; row < numRows; row++)
        {
            getline(myfile,line);
            stringstream ss2(line);
            for (int col = 0; col < numCols; col++)
            {
                ss2 >> m_data(row,col);
            }
        }

        myfile.close();
    }

    else
    {
        cout << "Unable to open file" << m_dataFileName << endl;
        exit(-1);
    }
}

void FileData::readLabels()
{
    std::fstream myfile(m_labelsFileName.c_str(),ios::in);
    if (myfile.is_open())
    {
        std::string line;
        // get datatype
        getline(myfile,line);
        // get size
        getline(myfile,line);
        stringstream ss(line);
        string tmp;
        int numLabels;
        ss >> numLabels;

        // pop-up the dense/sparse thing
        getline(myfile,line);

        m_labels.resize(numLabels);
        for (int row = 0; row < numLabels; row++)
        {
            getline(myfile, line);
            stringstream ss2(line);
            ss2 >> m_labels[row];
        }

        myfile.close();
    }

    else
    {
        cout << "Unable to open file" << m_dataFileName << endl;
        exit(-1);
    }
}

void FileData::writeData()
{
    std::fstream myfile;
    std::string dataFileName = m_dataFileName;
    dataFileName += ".data";
    myfile.open(dataFileName.c_str(), std::ios::out);

    myfile << "float" << endl;
    myfile << m_data.size1() << " " << m_data.size2() << endl;
    for (size_t row = 0; row < m_data.size1(); row++  )
    {
        for (size_t col = 0; col < m_data.size2(); col++)
        {
            myfile << m_data(row,col) << " ";
        }
        myfile << endl;
    }

    myfile.flush();
    myfile.close();
}

void FileData::writeLabels()
{
    std::fstream myfile;
    std::string dataFileName = m_labelsFileName;
    dataFileName += ".labels";
    myfile.open(dataFileName.c_str(), std::ios::out);

    myfile << "int" << endl;
    myfile << m_labels.size() << " " << 1 << endl;

    for (size_t row = 0; row < m_labels.size(); row++)
    {
        myfile << m_labels[row];
        myfile << endl;
    }

    myfile.flush();
    myfile.close();
}

void FileData::createPairs()
{
    int numSamples = m_data.size1();
    int numFeatures = m_data.size2();
    for(int p = 0; p < numSamples;p+=2) {
        std::vector<float> x1;
        std::vector<float> x2;
        for( int feat = 0; feat < numFeatures; feat++) {
            x1.push_back(m_data(p,feat));
            x2.push_back(m_data(p+1,feat));
        }
        int label = (m_labels[p] == 1) ? 1 : 0;
        Pair newPair(x1,x2,label);
        m_pairs.push_back(newPair);
    }
}


//------------------------------------------------------------------------------
/*
    This file is part of Beast: https://github.com/vinniefalco/Beast
    Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>

    Permission to use, copy, modify, and/or distribute this software for any
    purpose  with  or without fee is hereby granted, provided that the above
    copyright notice and this permission notice appear in all copies.

    THE  SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    WITH  REGARD  TO  THIS  SOFTWARE  INCLUDING  ALL  IMPLIED  WARRANTIES  OF
    MERCHANTABILITY  AND  FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    ANY  SPECIAL ,  DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    WHATSOEVER  RESULTING  FROM  LOSS  OF USE, DATA OR PROFITS, WHETHER IN AN
    ACTION  OF  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================

#include <ripple/basics/hardened_hash.h>
#include <ripple/beast/unit_test.h>
#include <boost/functional/hash.hpp>
#include <array>
#include <cstdint>
#include <functional>
#include <iomanip>
#include <unordered_map>
#include <unordered_set>

namespace ripple {
namespace detail {

template <class T>
class test_user_type_member
{
private:
    T t;

public:
    explicit test_user_type_member(T const& t_ = T()) : t(t_)
    {
    }

    template <class Hasher>
    friend void
    hash_append(Hasher& h, test_user_type_member const& a) noexcept
    {
        using beast::hash_append;
        hash_append(h, a.t);
    }
};

template <class T>
class test_user_type_free
{
private:
    T t;

public:
    explicit test_user_type_free(T const& t_ = T()) : t(t_)
    {
    }

    template <class Hasher>
    friend void
    hash_append(Hasher& h, test_user_type_free const& a) noexcept
    {
        using beast::hash_append;
        hash_append(h, a.t);
    }
};

}  // namespace detail
}  // namespace ripple

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

namespace ripple {

namespace detail {

template <class T>
using test_hardened_unordered_set = std::unordered_set<T, hardened_hash<>>;

template <class T>
using test_hardened_unordered_map = std::unordered_map<T, int, hardened_hash<>>;

template <class T>
using test_hardened_unordered_multiset =
    std::unordered_multiset<T, hardened_hash<>>;

template <class T>
using test_hardened_unordered_multimap =
    std::unordered_multimap<T, int, hardened_hash<>>;

}  // namespace detail

template <std::size_t Bits, class UInt = std::uint64_t>
class unsigned_integer
{
private:
    static_assert(
        std::is_integral<UInt>::value && std::is_unsigned<UInt>::value,
        "UInt must be an unsigned integral type");

    static_assert(
        Bits % (8 * sizeof(UInt)) == 0,
        "Bits must be a multiple of 8*sizeof(UInt)");

    static_assert(
        Bits >= (8 * sizeof(UInt)),
        "Bits must be at least 8*sizeof(UInt)");

    static std::size_t const size = Bits / (8 * sizeof(UInt));

    std::array<UInt, size> m_vec;

public:
    using value_type = UInt;

    static std::size_t const bits = Bits;
    static std::size_t const bytes = bits / 8;

    template <class Int>
    static unsigned_integer
    from_number(Int v)
    {
        unsigned_integer result;
        for (std::size_t i(1); i < size; ++i)
            result.m_vec[i] = 0;
        result.m_vec[0] = v;
        return result;
    }

    void*
    data() noexcept
    {
        return &m_vec[0];
    }

    void const*
    data() const noexcept
    {
        return &m_vec[0];
    }

    template <class Hasher>
    friend void
    hash_append(Hasher& h, unsigned_integer const& a) noexcept
    {
        using beast::hash_append;
        hash_append(h, a.m_vec);
    }

    friend std::ostream&
    operator<<(std::ostream& s, unsigned_integer const& v)
    {
        for (std::size_t i(0); i < size; ++i)
            s << std::hex << std::setfill('0') << std::setw(2 * sizeof(UInt))
              << v.m_vec[i];
        return s;
    }
};

using sha256_t = unsigned_integer<256, std::size_t>;

#ifndef __INTELLISENSE__
static_assert(sha256_t::bits == 256, "sha256_t must have 256 bits");
#endif

}  // namespace ripple

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

namespace ripple {

class hardened_hash_test : public beast::unit_test::suite
{
public:
    template <class T>
    void
    check()
    {
        T t{};
        hardened_hash<>()(t);
        pass();
    }

    template <template <class T> class U>
    void
    check_user_type()
    {
        check<U<bool>>();
        check<U<char>>();
        check<U<signed char>>();
        check<U<unsigned char>>();
        // These cause trouble for boost
        // check <U <char16_t>> ();
        // check <U <char32_t>> ();
        check<U<wchar_t>>();
        check<U<short>>();
        check<U<unsigned short>>();
        check<U<int>>();
        check<U<unsigned int>>();
        check<U<long>>();
        check<U<long long>>();
        check<U<unsigned long>>();
        check<U<unsigned long long>>();
        check<U<float>>();
        check<U<double>>();
        check<U<long double>>();
    }

    template <template <class T> class C>
    void
    check_container()
    {
        {
            C<detail::test_user_type_member<std::string>> c;
        }

        pass();

        {
            C<detail::test_user_type_free<std::string>> c;
        }

        pass();
    }

    void
    test_user_types()
    {
        testcase("user types");
        check_user_type<detail::test_user_type_member>();
        check_user_type<detail::test_user_type_free>();
    }

    void
    test_containers()
    {
        testcase("containers");
        check_container<detail::test_hardened_unordered_set>();
        check_container<detail::test_hardened_unordered_map>();
        check_container<detail::test_hardened_unordered_multiset>();
        check_container<detail::test_hardened_unordered_multimap>();
    }

    void
    run() override
    {
        test_user_types();
        test_containers();
    }
};

BEAST_DEFINE_TESTSUITE(hardened_hash, basics, ripple);

}  // namespace ripple


#pragma once

#include <algorithm>
#include <cstddef>
#include <functional>
#include <iterator>
#include <map>
#include <type_traits>
#include <vector>
#include <utility>

#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/join.hpp>

namespace linq {

    namespace detail {

        template <typename Function>
        struct function_traits: public function_traits<decltype(&Function::operator())>
        {};

        template <typename ClassType, typename ReturnType, typename... Args>
        struct function_traits<ReturnType(ClassType::*)(Args...) const> {
            typedef std::function<ReturnType(Args...)> function;
        };

        template <typename Function>
        typename function_traits<Function>::function
        to_function(Function &lambda) {
            return static_cast<typename function_traits<Function>::function>(lambda);
        }

        template <typename R>
        class LinqObj {
        private:
            R __range;

        public:
            typedef typename R::value_type value_type;

        public:
            LinqObj(R &&range): __range(range) {}

            auto begin() const {
                return std::begin(__range);
            }

            auto end() const {
                return std::end(__range);
            }

            template <typename F>
            auto select(const F &f) {
                auto func = to_function(f);
                return LinqObj<
                    boost::range_detail::transformed_range<
                        typename function_traits<F>::function, R>
                    >(boost::adaptors::transform(__range, func));
            }

            template <typename F>
            auto where(const F &f) {
                auto func = to_function(f);
                return LinqObj<
                    boost::range_detail::filtered_range<
                        typename function_traits<F>::function, R>
                    >(boost::adaptors::filter(__range, func));
            }

            template <typename F>
            value_type max(const F &f) const {
                return *std::max_element(begin(), end(), f);
            }

            value_type max() const {
                return *std::max_element(begin(), end());
            }

            template <typename F>
            value_type min(const F &f) const {
                return *std::min_element(begin(), end(), f);
            }

            value_type min() const {
                return *std::min_element(begin(), end());
            }

            auto reverse() {
                return LinqObj(boost::adaptors::reverse(__range));
            }

            template <typename F>
            auto first(const F &f) {
                return std::find_if(begin(), end(), f);
            }

            template <typename F>
            auto last(const F &f) {
                return reverse().first(f);
            }

            bool empty() {
                return begin() == end();
            }

            template <typename F>
            bool any(const F &f) const {
                return std::any_of(begin(), end(), f);
            }

            template <typename F>
            bool all(const F &f) const {
                return std::all_of(begin(), end(), f);
            }

            template <typename F>
            void for_each(const F &f) const {
                std::for_each(begin(), end(), f);
            }

            template <typename F>
            bool contains(const F &f) const {
                return std::find_if(begin(), end(), f);
            }

            template <typename F>
            auto distinct(const F& f) {
                return LinqObj(boost::range::unique(__range, f));
            }

            auto distinct() {
                return LinqObj(boost::uniqued_range<R>(__range | boost::adaptors::uniqued));
            }

            template <typename F>
            value_type aggregate(const F &f) const {
                auto init{ begin() };
                auto value = *init;
                return std::accumulate(++init, end(), std::move(value), f);
            }

            value_type sum() const {
                return aggregate(std::plus<value_type>());
            }

            template <typename F>
            std::ptrdiff_t count(const F &f) {
                return std::count_if(begin, end(), f);
            }

            std::ptrdiff_t count() const {
                return std::distance(begin(), end());
            }

            value_type average() const {
                return sum() / count();
            }

            template <typename R2>
            auto concat(const R2 &other) {
                return LinqObj(boost::join(__range, other));
            }

            template <typename T>
            value_type element_at(T index) const {
                return *std::next(begin(), index);
            }

            template <typename R2>
            void except(const R2 &other, std::vector<value_type> result) {
                std::set_difference(begin(), end(), std::begin(other), std::end(other),
                                    std::inserter(result, result.begin()));
            }

            auto find(const value_type &x) {
                return std::find(begin(), end(), x);
            }

            template <typename Fn>
            auto group_by(const Fn& f) {
                using key_t = typename std::result_of<Fn(value_type)>::type;
                std::multimap<key_t, value_type> retval;
                std::for_each(begin(), end(), [&](value_type item) {
                    retval.insert(std::make_pair(f(item), item));
                });
                return retval;
            }

            template <typename KeyFn, typename ValFn>
            auto group_by(const KeyFn &kfn, const ValFn &vfn) {
                using key_t = typename std::result_of<KeyFn(value_type)>::type;
                using val_t = typename std::result_of<ValFn(value_type)>::type;
                std::multimap<key_t, val_t> retval;
                std::for_each(begin(), end(), [&](value_type item) {
                    retval.insert(std::make_pair(kfn(item), vfn(item)));
                });
                return retval;
            }

            auto keys() const {
                return LinqObj(boost::adaptors::keys(__range));
            }

            auto values() const {
                return LinqObj(boost::adaptors::values(__range));
            }

            template <typename P>
            auto order_by(const P &pfn) {
                return LinqObj(boost::range::sort(__range, pfn));
            }

            auto order_by() {
                return LinqObj(boost::range::sort(__range));
            }

            auto order_by_descending() {
                return order_by().reverse();
            }

            template <typename T>
            auto skip(T n) const {
                return LinqObj(boost::make_iterator_range(begin()+n, end()));
            }

            template <typename F>
            auto skip_while(const F &f) const {
                return LinqObj(boost::make_iterator_range(std::find_if_not(begin(), end(), f), end()));
            }

            template <typename R2>
            bool sequence_equal(R2 other) const {
                return count() == std::distance(std::begin(other), std::end(other)) &&
                        std::equal(begin(), end(), std::begin(other), std::end(other));
            }

            template <typename T>
            auto take(T start, T end) const {
                return LinqObj(boost::adaptors::slice(__range, start, end));
            }

            template <typename F>
            auto take_while(const F &f) const {
                return LinqObj(boost::make_iterator_range(begin(), std::find_if(begin(), end(), f)));
            }

            template <typename R2>
            bool includes(const R2 &other) const {
                return std::includes(begin(), end(), std::begin(other), std::end(other));
            }

        };

        template <
            template <typename T>
            class IteratorRange,
            typename C>
        using Range = IteratorRange<decltype(std::begin(std::declval<C>()))>;

        template <typename ContainerT>
        using iteratorRange = Range<boost::iterator_range, ContainerT>;
    };

    template <typename ContainerT>
    auto from(const ContainerT &range) {
        return detail::LinqObj<detail::iteratorRange<ContainerT>>(detail::iteratorRange<ContainerT>(range));
    }

};


#ifndef __VSIM_COLLISION_SHAPE_HPP__
#define __VSIM_COLLISION_SHAPE_HPP__

#include <Eigen/Geometry>
#include <memory>

#include <vsim/env/pose.hpp>
#include <vsim/env/base_element.hpp>
#include <vsim/env/scene_fwd.hpp>

namespace vsim {

struct CollisionShape ;
typedef std::shared_ptr<CollisionShape> CollisionShapePtr ;

struct CollisionShape: public BaseElement {
public:

    CollisionShape() = default ;

    GeometryPtr geom_ ;
    Pose pose_ ;
};

}
#endif


#pragma once

#include <boost/uuid/sha1.hpp>

#include <vector>
#include <array>

namespace usagi
{
  namespace hash
  {
    using sha1_digest_type = std::array< std::uint32_t, 5 >;

    static inline auto sha1( const std::uint8_t* data, const std::size_t size )
      -> sha1_digest_type
    {
      boost::uuids::detail::sha1 sha1;
      std::uint32_t digest[5];

      sha1.process_bytes( data, size );
      sha1.get_digest( digest );

      return { digest[0], digest[1], digest[2], digest[3], digest[4] };
    }

    static inline auto sha1( const std::vector< std::uint8_t >& data )
      -> sha1_digest_type
    {
      return sha1( &data[0], data.size() );
    }

  }
}


// Boost.Geometry (aka GGL, Generic Geometry Library)
// Unit Test

// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2011 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2011 Mateusz Loskot, London, UK.

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

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

#include <geometry_test_common.hpp>

#include <boost/geometry/util/select_most_precise.hpp>

#include <boost/mpl/int.hpp>

using namespace boost::geometry;

struct user_defined
{
};

template <typename T> struct check_selection : public boost::mpl::int_<0> {};

template <> struct check_selection<short int> : public boost::mpl::int_<1> {};
template <> struct check_selection<int> : public boost::mpl::int_<2> {};
template <> struct check_selection<float> : public boost::mpl::int_<11> {};
template <> struct check_selection<double> : public boost::mpl::int_<12> {};
template <> struct check_selection<long double> : public boost::mpl::int_<13> {};
template <> struct check_selection<user_defined> : public boost::mpl::int_<99> {};

template <> struct check_selection<void> : public boost::mpl::int_<98> {};

template <typename T1, typename T2, typename TypeToBeSelected>
void test()
{
    typedef typename bg::select_most_precise<T1, T2>::type type;
    BOOST_CHECK_EQUAL(check_selection<type>::type::value,
        check_selection<TypeToBeSelected>::type::value);
}

int test_main(int, char* [])
{
    // fp only
    test<float, float, float>();
    test<float, double, double>();
    test<double, float, double>();
    test<double, double, double>();

    // integer only
    test<int, int, int>();
    test<int, short int, int>();
    test<short int, int, int>();
    test<short int, short int, short int>();

    // int/fp
    test<double, int, double>();
    test<int, double, double>();
    test<long double, long double, long double>();
    test<float, int, float>();
    test<int, float, float>();

#ifndef _MSC_VER
    // This cannot be done for MSVC because double/long double is the same
    test<double, long double, long double>();
    test<long double, double, long double>();
#endif

    // with any other non-integer/float class
    test<int, user_defined, user_defined>();
    test<user_defined, int, user_defined>();
    test<long double, user_defined, user_defined>();
    test<user_defined, long double, user_defined>();
    test<user_defined, user_defined, user_defined>();

    // should not compile
    //test<void, void, void>();

    return 0;
}


#define BOOST_TEST_MODULE pcrblock add
#include <boost/test/unit_test.hpp>
#include "dal_MathUtils.h"
#include "discr_block.h"
#include "discr_blockdata.h"
#include "discr_raster.h"
#include "discr_rasterdata.h"
#include "block_compactors.h"
#include "block_dummycompactor.h"
#include "block_functions.h"

BOOST_AUTO_TEST_CASE(no_compaction_add)
{
  using namespace block;

  {
    // Create block.
    // Create raster with thicknesses.
    // Add voxels.
    // Test block.

    discr::Raster raster(3, 1, 1.0, 0.0, 0.0);
    discr::RasterData<REAL4> baseElevation(&raster, REAL4(-5.0));
    discr::Block block(baseElevation);
    typedef discr::Block::ThicknessType T;

    discr::RasterData<T> thicknesses(&raster);
    thicknesses.cell(0) = T(0.0);
    thicknesses.cell(1) = T(1.0);
    pcr::setMV(thicknesses.cell(2));

    noCompactionAdd(block, thicknesses);

    BOOST_CHECK(dal::comparable(block.cell(0).baseElevation(), T(-5.0)));
    BOOST_CHECK(block.cell(0).size() == 0);
    BOOST_CHECK(dal::comparable(block.cell(0).thickness(), T(0.0)));
    BOOST_CHECK(dal::comparable(block.cell(0).surfaceElevation(), T(-5.0)));

    BOOST_CHECK(dal::comparable(block.cell(1).baseElevation(), T(-5.0)));
    BOOST_CHECK(block.cell(1).size() == 1);
    BOOST_CHECK(dal::comparable(block.cell(1).thickness(), T(1.0)));
    BOOST_CHECK(dal::comparable(block.cell(1).surfaceElevation(), T(-4.0)));

    BOOST_CHECK(block.cell(2).isMV());
  }
}

BOOST_AUTO_TEST_CASE(macky_bridge_add)
{
  using namespace block;

  size_t nrRows = 3;
  size_t nrCols = 4;
  double cellSize = 1.5;
  double west = 1.0;
  double north = 0.0;

  // Discretisations.
  discr::Raster raster(nrRows, nrCols, cellSize, west, north);
  discr::RasterData<REAL4> baseElevation(&raster, REAL4(5.0));
  discr::Block block(baseElevation);

  // Data.
  discr::BlockData<REAL4> originalThickness(&block);
  discr::BlockData<INT4> sediment(&block, 3);

  // Add function arguments.
  discr::RasterData<REAL4> thickness(&raster, REAL4(15.5));
  REAL4 maxVoxelThickness = REAL4(1.0);

  // No compaction.
  Compactors<MackeyBridgeCompactor> compactors;
  compactors.setCompactor(3, DummyCompactor());

  // TODO work around this add, see ResampleTest, remove functions.h inclusion.
  mackeyBridgeAdd(block, originalThickness, sediment, thickness,
         maxVoxelThickness, compactors);

  // Check discretisation.
  for(size_t i = 0; i < block.nrCells(); ++i) {
    BOOST_CHECK(block.cell(i).size() == 16);
    BOOST_CHECK(block.cell(i).thickness() == REAL4(15.5));
    BOOST_CHECK(block.cell(i).back() == REAL4(0.5));
  }

  // Check data.
  for(size_t i = 0; i < block.nrCells(); ++i) {
    for(size_t j = 0; j < block.cell(i).size() - 1; ++j) {
      BOOST_CHECK(originalThickness.cell(i)[j] == maxVoxelThickness);
      BOOST_CHECK(sediment.cell(i)[j] == 3);
    }

    BOOST_CHECK(originalThickness.cell(i).back() == REAL4(0.5));
    BOOST_CHECK(sediment.cell(i).back() == 3);
  }
}

BOOST_AUTO_TEST_CASE(de_haan_add)
{
  using namespace block;

  size_t nrRows = 1;
  size_t nrCols = 1;
  double cellSize = 1.0;
  double west = 0.0;
  double north = 0.0;

  REAL4 timeStepDuration = 36000.0;
  size_t nrTimeSteps = 22;

  discr::Raster raster(nrRows, nrCols, cellSize, west, north);
  discr::RasterData<REAL4> baseElevation(&raster, 0.0);
  discr::Block block(baseElevation);

  discr::BlockData<REAL4> initialThickness(&block);
  discr::BlockData<REAL4> cummulativeLoad(&block);
  discr::BlockData<REAL4> cummulativeDuration(&block, timeStepDuration);
  discr::BlockData<INT4> sediment(&block, 3);

  discr::RasterData<REAL4> thickness(&raster, 2.0);

  Compactors<DeHaanCompactor> compactors;
  compactors.setCompactor(3, DeHaanCompactor(0.2, 0.02, 6.0));

  for(size_t i = 1; i <= nrTimeSteps; ++i) {
    deHaanAdd(block, sediment, initialThickness, cummulativeLoad,
         cummulativeDuration, thickness, compactors);
  }

  // Check discretisation.
  PRECOND(block.nrCells() == 1);
  discr::VoxelStack const& stack(block.cell(0));
  BOOST_CHECK(!stack.isMV());
  BOOST_CHECK(stack.size() == nrTimeSteps);

  // From spreadsheet Theo:
  // 1.377602705
  // 1.465631195
  // 1.511233963
  // 1.541209414
  // 1.563188267
  // 1.580362374
  // 1.594355
  // 1.60609826
  // 1.61617404
  // 1.624968544
  // 1.632750422
  // 1.639713639
  // 1.646002529
  // 1.651727223
  // 1.656973542
  // 1.661809581
  // 1.666290221
  // 1.670460297
  // 1.674356882
  // 1.678010954
  // 1.681448649
  // 1.684692206

  BOOST_CHECK(dal::comparable(stack[nrTimeSteps - 1], REAL4(1.377602705)));
  BOOST_CHECK(dal::comparable(stack[0], REAL4(1.684692206)));
}


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#ifndef MTL_DIM_SPLITTER_INCLUDE
#define MTL_DIM_SPLITTER_INCLUDE

#include <algorithm>
#include <boost/numeric/mtl/recursion/utility.hpp>

namespace mtl { namespace recursion {

// Splits dimensions of a matrix separately into halfs (first value rounded up)
template <typename Matrix>
struct half_splitter
{
    typedef typename Matrix::size_type                            size_type;

    explicit half_splitter(Matrix const& matrix)
    {
	size_type nr= matrix.num_rows(), nc= matrix.num_cols();
	nr-= nr / 2; // keep the part (by removind the down-rounded half)
	nc-= nc / 2;
	my_row_split= matrix.begin_row() + nr;
	my_col_split= matrix.begin_col() + nc;
    }

    // End of northern half and beginning of southern
    size_type row_split() const
    {
	return my_row_split;
    }

    // End of western half and beginning of eastern
    size_type col_split() const
    {
	return my_col_split;
    }

private:
    size_type             my_row_split, my_col_split;
};

// Splits dimensions of a matrix separately into a first part that
//   is the largest power of 2 smaller than m or n, plus rest;
//   doesn't yield empty submatrices if both dimension > 1
template <typename Matrix>
struct separate_dim_splitter
{
    typedef typename Matrix::size_type                            size_type;

    explicit separate_dim_splitter(Matrix const& matrix)
	: my_row_split(matrix.begin_row() + first_part(matrix.num_rows())),
	  my_col_split(matrix.begin_col() + first_part(matrix.num_cols()))
    {}

    // End of northern half and beginning of southern
    size_type row_split() const
    {
	return my_row_split;
    }

    // End of western half and beginning of eastern
    size_type col_split() const
    {
	return my_col_split;
    }

private:
    size_type             my_row_split, my_col_split;
};

// Splits dimensions of a matrix separately into a first part that
//   is the largest power of 2 smaller than the maximum of m and n;
//   can yield 1 or two empty submatrices if matrix is rather unproportional;
//   helps creating square submatrices
template <typename Matrix>
struct max_dim_splitter
{
    typedef typename Matrix::size_type                            size_type;

    explicit max_dim_splitter(Matrix const& matrix)
	: // matrix(matrix),
	  my_split(std::max(first_part(matrix.num_rows()), first_part(matrix.num_cols()))),
	  my_row_split(std::min(matrix.begin_row() + my_split, matrix.end_row())),
	  my_col_split(std::min(matrix.begin_col() + my_split, matrix.end_col()))
   {}

    // End of northern half and beginning of southern (limited to end_row)
    size_type row_split() const
    {
	return my_row_split;
    }

    // End of western half and beginning of eastern (limited to end_col)
    size_type col_split() const
    {
	return my_col_split;
    }

private:
    //    Matrix const&   matrix;
    size_type             my_split, // minimal 2^(k-1) such that 2^k >= max(num_rows, num_cols)
                    my_row_split, my_col_split;
};

// Splitting within bounding box of power of 2, using recursators
// For instance, the upper left part of a 530 x 17 matrix is
//   530 x 17   if the bound is 2048 or larger
//   512 x 17   if the bound is 1024
//                 bound of 512 or smaller is a wrong bound
template <typename Recursator>
struct outer_bound_splitter
{
    typedef typename Recursator::size_type                            size_type;

    explicit outer_bound_splitter(Recursator const& recursator)
    {
	typename Recursator::matrix_type const& matrix= recursator.get_value();
	my_row_split= std::min(matrix.begin_row() + recursator.bound() / 2, matrix.end_row());
	my_col_split= std::min(matrix.begin_col() + recursator.bound() / 2, matrix.end_col());
    }

    // End of northern half and beginning of southern (limited to end_row)
    size_type row_split() const
    {
	return my_row_split;
    }

    // End of western half and beginning of eastern (limited to end_col)
    size_type col_split() const
    {
	return my_col_split;
    }

private:
    size_type             my_row_split, my_col_split;
};

}} // namespace mtl::recursion

#endif // MTL_DIM_SPLITTER_INCLUDE


// Copyright Louis Dionne 2013-2016
// 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/assert.hpp>
#include <boost/hana/at_key.hpp>
#include <boost/hana/fold_left.hpp>
#include <boost/hana/integral_constant.hpp>
#include <boost/hana/map.hpp>
#include <boost/hana/pair.hpp>
#include <boost/hana/range.hpp>
namespace hana = boost::hana;

//
// Make sure that http://stackoverflow.com/q/32702383/627587 works.
//

auto at = [](auto& map, auto key) -> auto& {
    return map[key];
};

template <typename Map, typename Keys>
auto& traverse(Map& map, Keys const& keys){
    return hana::fold_left(keys, map, at);
}

int main() {
    auto xs = hana::make_map(hana::make_pair(hana::int_c<0>,
                hana::make_map(hana::make_pair(hana::int_c<1>,
                    hana::make_map(hana::make_pair(hana::int_c<2>,
                        hana::make_map(hana::make_pair(hana::int_c<3>, 10))))))));

    int& i = traverse(xs, hana::range_c<int, 0, 4>);
    BOOST_HANA_RUNTIME_CHECK(i == 10);
    i = 99;
    BOOST_HANA_RUNTIME_CHECK(traverse(xs, hana::range_c<int, 0, 4>) == 99);
}


/*
 * Copyright (c) 2019 BitShares Blockchain Foundation, and contributors.
 *
 * The MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#pragma once

#ifdef __SIZEOF_INT128__

#include <stdint.h>

namespace fc {

using int128_t  = __int128_t;
using uint128_t = __uint128_t;

inline uint64_t uint128_lo64(const uint128_t x) {
  return static_cast<uint64_t>(x & 0xffffffffffffffffULL);
}
inline uint64_t uint128_hi64(const uint128_t x) {
  return static_cast<uint64_t>( x >> 64 );
}

} // namespace fc

#else // __SIZEOF_INT128__

#include <boost/multiprecision/integer.hpp>

namespace fc {

using boost::multiprecision::int128_t;
using boost::multiprecision::uint128_t;

inline uint64_t uint128_lo64(const uint128_t& x) {
  return static_cast<uint64_t>(x & 0xffffffffffffffffULL);
}
inline uint64_t uint128_hi64(const uint128_t& x) {
  return static_cast<uint64_t>( x >> 64 );
}

} // namespace fc

#endif // __SIZEOF_INT128__

namespace fc {

inline uint128_t uint128( const uint64_t hi, const uint64_t lo ) {
   return ( uint128_t(hi) << 64 ) + lo;
}

} // namespace fc