/*
 * Copyright (C) 2011, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

/// \file libport/umatrix.cc
/// \brief ufloat matrices

#include <stdexcept>
#include <libport/format.hh>
#include <libport/umatrix.hh>
#include <libport/uvector.hh>
#include <boost/numeric/ublas/lu.hpp>

#define FRAISE(Format, ...)                                             \
  throw std::runtime_error(libport::format(Format, ## __VA_ARGS__))

namespace boost
{
  namespace numeric
  {
    namespace ublas
    {

      bool
      operator==(const ::libport::matrix_type& e1,
                 const ::libport::matrix_type& e2)
      {
        return
          (e1.size1() == e2.size1()
           && e1.size2() == e2.size2()
           && norm_inf(e1 - e2) == 0);
      }

      std::ostream&
      operator<<(std::ostream& o, const ::libport::matrix_type& v)
      {
        const size_t height = v.size1();
        o << '<';
        for (unsigned i = 0; i < height; ++i)
        {
          if (i)
            o << ", ";
          o << boost::numeric::ublas::row(v, i);
        }
        return o << '>';
      }

      ::libport::matrix_type
      inverse(const ::libport::matrix_type& m)
      {
        size_t size1 = m.size1();
        size_t size2 = m.size2();
        if (size1 != size2)
          FRAISE("expected square matrix, got %dx%d", size1, size2);

        // Create a working copy of the input.
        ::libport::matrix_type A(m);

        // Create a permutation matrix for the LU-factorization.
        permutation_matrix<size_t> pm(size1);

        // Perform LU-factorization.
        if (lu_factorize(A, pm) != 0)
          FRAISE("non-invertible matrix: %s", m);

        // Create identity matrix of "inverse".
        ::libport::matrix_type res(size1, size2);
        res.assign(identity_matrix<libport::ufloat>(size1));
        // Backsubstitute to get the inverse.
        lu_substitute(A, pm, res);
        return res;
      }

    }
  }
}


/*
    Copyright 2005-2007 Adobe Systems Incorporated
    Distributed under the MIT License (see accompanying file LICENSE_1_0_0.txt
    or a copy at http://stlab.adobe.com/licenses.html)
*/

/*************************************************************************************************/

#ifndef ADOBE_ALGORITHM_SET_HPP
#define ADOBE_ALGORITHM_SET_HPP

#include <adobe/config.hpp>

#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/bind.hpp>

#include <algorithm>

/*************************************************************************************************/

namespace adobe {

/*************************************************************************************************/
/*!
\defgroup set set operations on sorted ranges
\ingroup sorting

\see
    - STL documentation for \ref stldoc_includes
    - STL documentation for \ref stldoc_set_union
    - STL documentation for \ref stldoc_set_intersection
    - STL documentation for \ref stldoc_set_difference
    - STL documentation for \ref stldoc_set_symmetric_difference
*/
/*************************************************************************************************/
/*!
    \ingroup set

    \brief set implementation
*/
template <class InputRange1, class InputRange2>
inline bool includes(const InputRange1& range1, const InputRange2& range2)
{
    return std::includes(   boost::begin(range1), boost::end(range1),
                            boost::begin(range2), boost::end(range2));
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputIterator1, class InputIterator2, class Compare>
inline bool includes(   InputIterator1 first1, InputIterator1 last1,
                        InputIterator2 first2, InputIterator2 last2,
                        Compare comp)

{
    return std::includes(first1, last1, first2, last2, boost::bind(comp, _1, _2));
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputRange1, class InputRange2, class Compare>
inline bool includes(const InputRange1& range1, const InputRange2& range2, Compare comp)
{
    return adobe::includes( boost::begin(range1), boost::end(range1),
                                boost::begin(range2), boost::end(range2),
                                comp);
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputRange1, class InputRange2, class OutputIterator>
inline OutputIterator set_union(const InputRange1& range1, const InputRange2& range2, OutputIterator result)
{
    return std::set_union(  boost::begin(range1), boost::end(range1),
                            boost::begin(range2), boost::end(range2),
                            result);
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputIterator1, class InputIterator2, class OutputIterator, class Compare>
inline OutputIterator set_union(InputIterator1 first1, InputIterator1 last1,
                                InputIterator2 first2, InputIterator2 last2,
                                OutputIterator result, Compare comp)
{
    return std::set_union(first1, last1, first2, last2, result, boost::bind(comp, _1, _2));
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputRange1, class InputRange2, class OutputIterator, class Compare>
inline OutputIterator set_union(const InputRange1& range1, const InputRange2& range2, OutputIterator result, Compare comp)
{
    return adobe::set_union(boost::begin(range1), boost::end(range1),
                                boost::begin(range2), boost::end(range2),
                                result, comp);
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputRange1, class InputRange2, class OutputIterator>
inline OutputIterator set_intersection(const InputRange1& range1, const InputRange2& range2, OutputIterator result)
{
    return std::set_intersection(   boost::begin(range1), boost::end(range1),
                                    boost::begin(range2), boost::end(range2),
                                    result);
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputIterator1, class InputIterator2, class OutputIterator, class Compare>
inline OutputIterator set_intersection( InputIterator1 first1, InputIterator1 last1,
                                        InputIterator2 first2, InputIterator2 last2,
                                        OutputIterator result, Compare comp)
{
    return std::set_intersection(first1, last1, first2, last2, result, boost::bind(comp, _1, _2));
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputRange1, class InputRange2, class OutputIterator, class Compare>
inline OutputIterator set_intersection(const InputRange1& range1, const InputRange2& range2, OutputIterator result, Compare comp)
{
    return adobe::set_intersection( boost::begin(range1), boost::end(range1),
                                        boost::begin(range2), boost::end(range2),
                                        result, comp);
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputRange1, class InputRange2, class OutputIterator>
inline OutputIterator set_difference(const InputRange1& range1, const InputRange2& range2, OutputIterator result)
{
    return std::set_difference( boost::begin(range1), boost::end(range1),
                                    boost::begin(range2), boost::end(range2),
                                    result);
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputIterator1, class InputIterator2, class OutputIterator, class Compare>
inline OutputIterator set_difference(   InputIterator1 first1, InputIterator1 last1,
                                        InputIterator2 first2, InputIterator2 last2,
                                        OutputIterator result, Compare comp)
{
    return std::set_difference(first1, last1, first2, last2, result, boost::bind(comp, _1, _2));
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputRange1, class InputRange2, class OutputIterator, class Compare>
inline OutputIterator set_difference(const InputRange1& range1, const InputRange2& range2, OutputIterator result, Compare comp)
{
    return adobe::set_difference(   boost::begin(range1), boost::end(range1),
                                        boost::begin(range2), boost::end(range2),
                                        result, comp);
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputRange1, class InputRange2, class OutputIterator>
inline OutputIterator set_symmetric_difference(const InputRange1& range1, const InputRange2& range2, OutputIterator result)
{
    return std::set_symmetric_difference(   boost::begin(range1), boost::end(range1),
                                            boost::begin(range2), boost::end(range2),
                                            result);
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputIterator1, class InputIterator2, class OutputIterator, class Compare>
inline OutputIterator set_symmetric_difference( InputIterator1 first1, InputIterator1 last1,
                                                InputIterator2 first2, InputIterator2 last2,
                                                OutputIterator result, Compare comp)
{
    return std::set_symmetric_difference(first1, last1, first2, last2, result, boost::bind(comp, _1, _2));
}

/*!
    \ingroup set

    \brief set implementation
*/
template <class InputRange1, class InputRange2, class OutputIterator, class Compare>
inline OutputIterator set_symmetric_difference(const InputRange1& range1, const InputRange2& range2, OutputIterator result, Compare comp)
{
    return adobe::set_symmetric_difference( boost::begin(range1), boost::end(range1),
                                                boost::begin(range2), boost::end(range2),
                                                result, comp);
}

/*************************************************************************************************/

} // namespace adobe

/*************************************************************************************************/

#endif

/*************************************************************************************************/


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

  @copyright 2016 NumScale SAS
  @copyright 2016 J.T.Lapreste

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

#include <boost/simd/config.hpp>
#include <boost/dispatch/function/make_callable.hpp>
#include <boost/dispatch/hierarchy/functions.hpp>
#include <boost/simd/detail/dispatch.hpp>

namespace boost { namespace simd
{
  namespace tag
  {
    BOOST_DISPATCH_MAKE_TAG(ext, modulo_, boost::dispatch::elementwise_<modulo_>);
  }

  namespace ext
  {
    BOOST_DISPATCH_FUNCTION_DECLARATION(tag, modulo_);
  }

  BOOST_DISPATCH_CALLABLE_DEFINITION(tag::modulo_,modulo);

} }

#endif


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
/*!
 * \file
**/
#ifndef BOOST_SIMD_IEEE_FUNCTIONS_ILOGB_HPP_INCLUDED
#define BOOST_SIMD_IEEE_FUNCTIONS_ILOGB_HPP_INCLUDED
#include <boost/simd/include/functor.hpp>
#include <boost/dispatch/include/functor.hpp>

/*!
 * \ingroup boost_simd_ieee
 * \defgroup boost_simd_ieee_ilogb ilogb
 *
 * \par Description
 * This function returns the integer truncation
 * of the base 2 logarithm of a0.
 * It coincides with the exponent function
 * on all platforms supported.
 *
 * \par Header file
 *
 * \code
 * #include <nt2/include/functions/ilogb.hpp>
 * \endcode
 *
 *
 * \synopsis
 *
 * \code
 * namespace boost::simd
 * {
 *   template <class A0>
 *     meta::call<tag::ilogb_(A0)>::type
 *     ilogb(const A0 & a0);
 * }
 * \endcode
 *
 * \param a0 the unique parameter of ilogb
 *
 * \return always an integer value
 *
 * \par Notes
 * In SIMD mode, this function acts elementwise on the inputs vectors elements
 * \par
 *
**/

namespace boost { namespace simd { namespace tag
  {
    /*!
     * \brief Define the tag ilogb_ of functor ilogb
     *        in namespace boost::simd::tag for toolbox boost.simd.ieee
    **/
    struct ilogb_ : ext::elementwise_<ilogb_> { typedef ext::elementwise_<ilogb_> parent; };
  }
  BOOST_DISPATCH_FUNCTION_IMPLEMENTATION(tag::ilogb_, ilogb, 1)
} }

#endif

// modified by jt the 25/12/2010


#include "JoinSplit.hpp"
#include "prf.h"
#include "sodium.h"

#include "zcash/util.h"

#include <memory>

#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/optional.hpp>
#include <fstream>
#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp"
#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
#include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp"
#include "libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp"

#include "sync.h"
#include "amount.h"

using namespace libsnark;

namespace libzcash {

#include "zcash/circuit/gadget.tcc"

CCriticalSection cs_ParamsIO;
CCriticalSection cs_LoadKeys;

template<typename T>
void saveToFile(std::string path, T& obj) {
    LOCK(cs_ParamsIO);

    std::stringstream ss;
    ss << obj;
    std::ofstream fh;
    fh.open(path, std::ios::binary);
    ss.rdbuf()->pubseekpos(0, std::ios_base::out);
    fh << ss.rdbuf();
    fh.flush();
    fh.close();
}

template<typename T>
void loadFromFile(std::string path, boost::optional<T>& objIn) {
    LOCK(cs_ParamsIO);

    std::stringstream ss;
    std::ifstream fh(path, std::ios::binary);

    if(!fh.is_open()) {
        throw std::runtime_error((boost::format("could not load param file at %s") % path).str());
    }

    ss << fh.rdbuf();
    fh.close();

    ss.rdbuf()->pubseekpos(0, std::ios_base::in);

    T obj;
    ss >> obj;

    objIn = std::move(obj);
}

template<size_t NumInputs, size_t NumOutputs>
class JoinSplitCircuit : public JoinSplit<NumInputs, NumOutputs> {
public:
    typedef default_r1cs_ppzksnark_pp ppzksnark_ppT;
    typedef Fr<ppzksnark_ppT> FieldT;

    boost::optional<r1cs_ppzksnark_proving_key<ppzksnark_ppT>> pk;
    boost::optional<r1cs_ppzksnark_verification_key<ppzksnark_ppT>> vk;
    boost::optional<r1cs_ppzksnark_processed_verification_key<ppzksnark_ppT>> vk_precomp;
    boost::optional<std::string> pkPath;

    JoinSplitCircuit() {}
    ~JoinSplitCircuit() {}

    void setProvingKeyPath(std::string path) {
        pkPath = path;
    }

    void loadProvingKey() {
        LOCK(cs_LoadKeys);

        if (!pk) {
            if (!pkPath) {
                throw std::runtime_error("proving key path unknown");
            }
            loadFromFile(*pkPath, pk);
        }
    }

    void saveProvingKey(std::string path) {
        if (pk) {
            saveToFile(path, *pk);
        } else {
            throw std::runtime_error("cannot save proving key; key doesn't exist");
        }
    }
    void loadVerifyingKey(std::string path) {
        LOCK(cs_LoadKeys);

        loadFromFile(path, vk);

        processVerifyingKey();
    }
    void processVerifyingKey() {
        vk_precomp = r1cs_ppzksnark_verifier_process_vk(*vk);
    }
    void saveVerifyingKey(std::string path) {
        if (vk) {
            saveToFile(path, *vk);
        } else {
            throw std::runtime_error("cannot save verifying key; key doesn't exist");
        }
    }
    void saveR1CS(std::string path) {
        auto r1cs = generate_r1cs();

        saveToFile(path, r1cs);
    }

    r1cs_constraint_system<FieldT> generate_r1cs() {
        protoboard<FieldT> pb;

        joinsplit_gadget<FieldT, NumInputs, NumOutputs> g(pb);
        g.generate_r1cs_constraints();

        return pb.get_constraint_system();
    }

    void generate() {
        LOCK(cs_LoadKeys);

        const r1cs_constraint_system<FieldT> constraint_system = generate_r1cs();
        r1cs_ppzksnark_keypair<ppzksnark_ppT> keypair = r1cs_ppzksnark_generator<ppzksnark_ppT>(constraint_system);

        pk = keypair.pk;
        vk = keypair.vk;
        processVerifyingKey();
    }

    bool verify(
        const ZCProof& proof,
        ProofVerifier& verifier,
        const uint256& pubKeyHash,
        const uint256& randomSeed,
        const boost::array<uint256, NumInputs>& macs,
        const boost::array<uint256, NumInputs>& nullifiers,
        const boost::array<uint256, NumOutputs>& commitments,
        uint64_t vpub_old,
        uint64_t vpub_new,
        const uint256& rt
    ) {
        if (!vk || !vk_precomp) {
            throw std::runtime_error("JoinSplit verifying key not loaded");
        }

        try {
            auto r1cs_proof = proof.to_libsnark_proof<r1cs_ppzksnark_proof<ppzksnark_ppT>>();

            uint256 h_sig = this->h_sig(randomSeed, nullifiers, pubKeyHash);

            auto witness = joinsplit_gadget<FieldT, NumInputs, NumOutputs>::witness_map(
                rt,
                h_sig,
                macs,
                nullifiers,
                commitments,
                vpub_old,
                vpub_new
            );

            return verifier.check(
                *vk,
                *vk_precomp,
                witness,
                r1cs_proof
            );
        } catch (...) {
            return false;
        }
    }

    ZCProof prove(
        const boost::array<JSInput, NumInputs>& inputs,
        const boost::array<JSOutput, NumOutputs>& outputs,
        boost::array<Note, NumOutputs>& out_notes,
        boost::array<ZCNoteEncryption::Ciphertext, NumOutputs>& out_ciphertexts,
        uint256& out_ephemeralKey,
        const uint256& pubKeyHash,
        uint256& out_randomSeed,
        boost::array<uint256, NumInputs>& out_macs,
        boost::array<uint256, NumInputs>& out_nullifiers,
        boost::array<uint256, NumOutputs>& out_commitments,
        uint64_t vpub_old,
        uint64_t vpub_new,
        const uint256& rt,
        bool computeProof
    ) {
        if (computeProof && !pk) {
            throw std::runtime_error("JoinSplit proving key not loaded");
        }

        if (vpub_old > MAX_MONEY) {
            throw std::invalid_argument("nonsensical vpub_old value");
        }

        if (vpub_new > MAX_MONEY) {
            throw std::invalid_argument("nonsensical vpub_new value");
        }

        uint64_t lhs_value = vpub_old;
        uint64_t rhs_value = vpub_new;

        for (size_t i = 0; i < NumInputs; i++) {
            // Sanity checks of input
            {
                // If note has nonzero value
                if (inputs[i].note.value != 0) {
                    // The witness root must equal the input root.
                    if (inputs[i].witness.root() != rt) {
                        throw std::invalid_argument("joinsplit not anchored to the correct root");
                    }

                    // The tree must witness the correct element
                    if (inputs[i].note.cm() != inputs[i].witness.element()) {
                        throw std::invalid_argument("witness of wrong element for joinsplit input");
                    }
                }

                // Ensure we have the key to this note.
                if (inputs[i].note.a_pk != inputs[i].key.address().a_pk) {
                    throw std::invalid_argument("input note not authorized to spend with given key");
                }

                // Balance must be sensical
                if (inputs[i].note.value > MAX_MONEY) {
                    throw std::invalid_argument("nonsensical input note value");
                }

                lhs_value += inputs[i].note.value;

                if (lhs_value > MAX_MONEY) {
                    throw std::invalid_argument("nonsensical left hand size of joinsplit balance");
                }
            }

            // Compute nullifier of input
            out_nullifiers[i] = inputs[i].nullifier();
        }

        // Sample randomSeed
        out_randomSeed = random_uint256();

        // Compute h_sig
        uint256 h_sig = this->h_sig(out_randomSeed, out_nullifiers, pubKeyHash);

        // Sample phi
        uint252 phi = random_uint252();

        // Compute notes for outputs
        for (size_t i = 0; i < NumOutputs; i++) {
            // Sanity checks of output
            {
                if (outputs[i].value > MAX_MONEY) {
                    throw std::invalid_argument("nonsensical output value");
                }

                rhs_value += outputs[i].value;

                if (rhs_value > MAX_MONEY) {
                    throw std::invalid_argument("nonsensical right hand side of joinsplit balance");
                }
            }

            // Sample r
            uint256 r = random_uint256();

            out_notes[i] = outputs[i].note(phi, r, i, h_sig);
        }

        if (lhs_value != rhs_value) {
            throw std::invalid_argument("invalid joinsplit balance");
        }

        // Compute the output commitments
        for (size_t i = 0; i < NumOutputs; i++) {
            out_commitments[i] = out_notes[i].cm();
        }

        // Encrypt the ciphertexts containing the note
        // plaintexts to the recipients of the value.
        {
            ZCNoteEncryption encryptor(h_sig);

            for (size_t i = 0; i < NumOutputs; i++) {
                NotePlaintext pt(out_notes[i], outputs[i].memo);

                out_ciphertexts[i] = pt.encrypt(encryptor, outputs[i].addr.pk_enc);
            }

            out_ephemeralKey = encryptor.get_epk();
        }

        // Authenticate h_sig with each of the input
        // spending keys, producing macs which protect
        // against malleability.
        for (size_t i = 0; i < NumInputs; i++) {
            out_macs[i] = PRF_pk(inputs[i].key, i, h_sig);
        }

        if (!computeProof) {
            return ZCProof();
        }

        protoboard<FieldT> pb;
        {
            joinsplit_gadget<FieldT, NumInputs, NumOutputs> g(pb);
            g.generate_r1cs_constraints();
            g.generate_r1cs_witness(
                phi,
                rt,
                h_sig,
                inputs,
                out_notes,
                vpub_old,
                vpub_new
            );
        }

        // The constraint system must be satisfied or there is an unimplemented
        // or incorrect sanity check above. Or the constraint system is broken!
        assert(pb.is_satisfied());

        // TODO: These are copies, which is not strictly necessary.
        std::vector<FieldT> primary_input = pb.primary_input();
        std::vector<FieldT> aux_input = pb.auxiliary_input();

        // Swap A and B if it's beneficial (less arithmetic in G2)
        // In our circuit, we already know that it's beneficial
        // to swap, but it takes so little time to perform this
        // estimate that it doesn't matter if we check every time.
        pb.constraint_system.swap_AB_if_beneficial();

        return ZCProof(r1cs_ppzksnark_prover<ppzksnark_ppT>(
            *pk,
            primary_input,
            aux_input,
            pb.constraint_system
        ));
    }
};

template<size_t NumInputs, size_t NumOutputs>
JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Generate()
{
    initialize_curve_params();
    auto js = new JoinSplitCircuit<NumInputs, NumOutputs>();
    js->generate();

    return js;
}

template<size_t NumInputs, size_t NumOutputs>
JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Unopened()
{
    initialize_curve_params();
    return new JoinSplitCircuit<NumInputs, NumOutputs>();
}

template<size_t NumInputs, size_t NumOutputs>
uint256 JoinSplit<NumInputs, NumOutputs>::h_sig(
    const uint256& randomSeed,
    const boost::array<uint256, NumInputs>& nullifiers,
    const uint256& pubKeyHash
) {
    const unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES]
        = {'Z','c','a','s','h','C','o','m','p','u','t','e','h','S','i','g'};

    std::vector<unsigned char> block(randomSeed.begin(), randomSeed.end());

    for (size_t i = 0; i < NumInputs; i++) {
        block.insert(block.end(), nullifiers[i].begin(), nullifiers[i].end());
    }

    block.insert(block.end(), pubKeyHash.begin(), pubKeyHash.end());

    uint256 output;

    if (crypto_generichash_blake2b_salt_personal(output.begin(), 32,
                                                 &block[0], block.size(),
                                                 NULL, 0, // No key.
                                                 NULL,    // No salt.
                                                 personalization
                                                ) != 0)
    {
        throw std::logic_error("hash function failure");
    }

    return output;
}

Note JSOutput::note(const uint252& phi, const uint256& r, size_t i, const uint256& h_sig) const {
    uint256 rho = PRF_rho(phi, i, h_sig);

    return Note(addr.a_pk, value, rho, r);
}

JSOutput::JSOutput() : addr(uint256(), uint256()), value(0) {
    SpendingKey a_sk = SpendingKey::random();
    addr = a_sk.address();
}

JSInput::JSInput() : witness(ZCIncrementalMerkleTree().witness()),
                     key(SpendingKey::random()) {
    note = Note(key.address().a_pk, 0, random_uint256(), random_uint256());
    ZCIncrementalMerkleTree dummy_tree;
    dummy_tree.append(note.cm());
    witness = dummy_tree.witness();
}

template class JoinSplit<ZC_NUM_JS_INPUTS,
                         ZC_NUM_JS_OUTPUTS>;

}


// Copyright (c) 2019 Egor Tensin <Egor.Tensin@gmail.com>
// This file is part of the "math-server" project.
// For details, see https://github.com/egor-tensin/math-server.
// Distributed under the MIT License.

#include <lexer/details/parse.hpp>
#include <lexer/error.hpp>
#include <lexer/lexer.hpp>
#include <lexer/token.hpp>
#include <lexer/token_type.hpp>

#include <boost/test/data/monomorphic.hpp>
#include <boost/test/data/test_case.hpp>
#include <boost/test/unit_test.hpp>

#include <ostream>
#include <string>
#include <string_view>
#include <vector>

namespace bdata = boost::unit_test::data;
using math::server::Lexer;
using math::server::LexerError;
using math::server::lexer::Token;
using math::server::lexer::token::Type;
namespace details = math::server::lexer::details;

namespace std {

ostream& operator<<(ostream& os, const vector<Token>& tokens) {
    for (const auto& token : tokens) {
        os << token;
    }
    return os;
}

} // namespace std

namespace {
namespace get_tokens::valid {

// clang-format off
// wtf, don't binpack this
const std::vector<std::string_view> input{
    "",
    " + - ",
    "1+2",
    ".5^-1 ^ 4",
    "1+2 *  (3- 4e-2)",
    " 2 * (1 + 3 * (1 - -3)) ",
};
// clang-format on

const std::vector<std::vector<Token>> expected{
    {},
    {
        Token{Type::PLUS},
        Token{Type::MINUS},
    },
    {
        Token{1},
        Token{Type::PLUS},
        Token{2},
    },
    {
        Token{.5},
        Token{Type::CARET},
        Token{Type::MINUS},
        Token{1},
        Token{Type::CARET},
        Token{4},
    },
    {
        Token{1},
        Token{Type::PLUS},
        Token{2},
        Token{Type::ASTERISK},
        Token{Type::LEFT_PAREN},
        Token{3},
        Token{Type::MINUS},
        Token{4e-2},
        Token{Type::RIGHT_PAREN},
    },
    {
        Token{2},
        Token{Type::ASTERISK},
        Token{Type::LEFT_PAREN},
        Token{1},
        Token{Type::PLUS},
        Token{3},
        Token{Type::ASTERISK},
        Token{Type::LEFT_PAREN},
        Token{1},
        Token{Type::MINUS},
        Token{Type::MINUS},
        Token{3},
        Token{Type::RIGHT_PAREN},
        Token{Type::RIGHT_PAREN},
    },
};

} // namespace get_tokens::valid

namespace get_tokens::invalid {

const std::vector<std::string_view> input{
    "&",
    " 1 + 123 & 456",
};

const std::vector<std::string> error_msg{
    "server error: lexer error: invalid input at: &",
    "server error: lexer error: invalid input at: & 456",
};

} // namespace get_tokens::invalid
} // namespace

BOOST_AUTO_TEST_SUITE(lexer_tests)

BOOST_AUTO_TEST_CASE(test_parse_number) {
    // These are valid numbers:
    BOOST_TEST(details::parse_number("0").value() == 0);
    BOOST_TEST(details::parse_number("1.").value() == 1.);
    BOOST_TEST(details::parse_number(".1").value() == .1);
    // parse_* functions only consume a single token:
    BOOST_TEST(details::parse_number(".1+1").value() == .1);
    BOOST_TEST(details::parse_number("1e3").value() == 1e3);
    BOOST_TEST(details::parse_number(".123e1").value() == .123e1);
    BOOST_TEST(details::parse_number("123e-1").value() == 123e-1);
    BOOST_TEST(details::parse_number("123e+1").value() == 123e+1);
    BOOST_TEST(details::parse_number("1.e6").value() == 1.e6);
    BOOST_TEST(details::parse_number("2.3e-4").value() == 2.3e-4);
    // These are not numbers, but perhaps something else?
    BOOST_TEST(!details::parse_number(".").has_value());
    BOOST_TEST(!details::parse_number(".e3").has_value());
    BOOST_TEST(!details::parse_number("e12").has_value());
    // This is definitely a number, but a malformed one (an exponent must be
    // followed by some digits).
    BOOST_CHECK_THROW(details::parse_number("12e"), LexerError);
}

BOOST_AUTO_TEST_CASE(test_parse_const_token) {
    BOOST_TEST(details::parse_const_token("+").value() == Type::PLUS);
    // parse_* functions only consume a single token:
    BOOST_TEST(details::parse_const_token("+/*").value() == Type::PLUS);
    BOOST_TEST(details::parse_const_token("-").value() == Type::MINUS);
    BOOST_TEST(details::parse_const_token("^^").value() == Type::CARET);
    BOOST_TEST(!details::parse_const_token("&+").has_value());
}

BOOST_DATA_TEST_CASE(test_get_tokens_valid,
                     bdata::make(get_tokens::valid::input) ^ get_tokens::valid::expected,
                     input,
                     expected) {
    Lexer lexer{input};
    const auto actual = lexer.get_tokens();
    BOOST_CHECK_EQUAL_COLLECTIONS(actual.cbegin(), actual.cend(), expected.cbegin(),
                                  expected.cend());
}

BOOST_DATA_TEST_CASE(test_get_tokens_invalid,
                     bdata::make(get_tokens::invalid::input) ^ get_tokens::invalid::error_msg,
                     input,
                     error_msg) {
    BOOST_REQUIRE_THROW(
        do {
            Lexer lexer{input};
            lexer.get_tokens();
        } while (0),
        LexerError);

    try {
        Lexer lexer{input};
        lexer.get_tokens();
    } catch (const LexerError& e) {
        BOOST_TEST(error_msg == e.what());
    }
}

BOOST_AUTO_TEST_SUITE_END()


// a sorting example that uses the worst-case for conventional MSD radix sorts.
//
//  Copyright Steven Ross 2009.
//
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/ for updates, documentation, and revision history.

#include <boost/algorithm/sorting/spread_sort.hpp>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
using namespace boost;
using namespace std;

#define DATA_TYPE boost::uint64_t

#define ALR_THRESHOLD 20

const unsigned max_count = ALR_THRESHOLD - 1;
const unsigned bit_shift = detail::rough_log_2_size(max_count) - detail::LOG_MEAN_BIN_SIZE;
const unsigned radix_threshold = detail::rough_log_2_size(max_count) + 1;
//Increase this size if too fast to test accurately
const unsigned top_splits = 12;

const DATA_TYPE typed_one = 1;

void
fill_vector(vector<DATA_TYPE> & input, const DATA_TYPE base_value, unsigned remaining_bits)
{
  if(remaining_bits >= radix_threshold) {
    input.push_back((base_value << remaining_bits) + ((typed_one << remaining_bits) - 1));
    fill_vector(input, base_value << bit_shift, remaining_bits - bit_shift);
  }
  else {
    for(unsigned u = 0; u < max_count; ++u)
      input.push_back((base_value << remaining_bits) + (rand() % (1 << remaining_bits)));
  }
}

//Pass in an argument to test std::sort
int main(int argc, const char ** argv) {
  vector<DATA_TYPE> input;
  for(int ii = (1 << top_splits) - 1; ii >= 0; --ii)
    fill_vector(input, ii, (sizeof(DATA_TYPE) * 8) - top_splits);
  //Run multiple loops, if requested
  for(unsigned u = 0; u < 2; ++u) {
    vector<DATA_TYPE> array = input;
    clock_t start, end;
    double elapsed;
    start = clock();
    if(u)
      std::sort(array.begin(), array.end());
    else
      spread_sort(array.begin(), array.end());
    end = clock();
    elapsed = ((double) (end - start));
    if(u)
      printf("std::sort elapsed time %f\n", elapsed / CLOCKS_PER_SEC);
    else
      printf("spreadsort elapsed time %f\n", elapsed / CLOCKS_PER_SEC);
    array.clear();
  }
  return 0;
}


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

typedef Eigen::MatrixXf MatrixXf;
typedef Eigen::Matrix<uint8_t, Eigen::Dynamic, Eigen::Dynamic> MatrixXb;

MatrixXb ca(const MatrixXf &img, int train_hs, int guard_hs, double tau)
{
  MatrixXb ret = MatrixXb::Zero(img.rows(), img.cols());

  for (int col = 0; col < img.cols(); ++col)
  {
    for (int row = train_hs + guard_hs; row < img.rows() - train_hs - guard_hs; ++row)
    {
      float sum_train = 0;
      for (int i = row - train_hs - guard_hs; i < row + train_hs + guard_hs + 1; ++i)
      {
        if (std::abs(i - row) > guard_hs)
          sum_train += img(i, col);
      }
      ret(row, col) = img(row, col) > tau * sum_train / (2.0 * train_hs);
    }
  }
  return ret;
}

MatrixXb soca(const MatrixXf &img, int train_hs, int guard_hs, double tau)
{
  MatrixXb ret = MatrixXb::Zero(img.rows(), img.cols());

  for (int col = 0; col < img.cols(); ++col)
  {
    for (int row = train_hs + guard_hs; row < img.rows() - train_hs - guard_hs; ++row)
    {
      float leading_sum = 0.0, lagging_sum = 0.0;
      for (int i = row - train_hs - guard_hs; i < row + train_hs + guard_hs + 1; ++i)
      {
        if ((i - row) > guard_hs)
          lagging_sum += img(i, col);
        else if ((i - row) < -guard_hs)
          leading_sum += img(i, col);
      }
      float sum_train = std::min(leading_sum, lagging_sum);
      ret(row, col) = img(row, col) > tau * sum_train / train_hs;
    }
  }
  return ret;
}

MatrixXb goca(const MatrixXf &img, int train_hs, int guard_hs, double tau)
{
  MatrixXb ret = MatrixXb::Zero(img.rows(), img.cols());

  for (int col = 0; col < img.cols(); ++col)
  {
    for (int row = train_hs + guard_hs; row < img.rows() - train_hs - guard_hs; ++row)
    {
      float leading_sum = 0.0, lagging_sum = 0.0;
      for (int i = row - train_hs - guard_hs; i < row + train_hs + guard_hs + 1; ++i)
      {
        if ((i - row) > guard_hs)
          lagging_sum += img(i, col);
        else if ((i - row) < -guard_hs)
          leading_sum += img(i, col);
      }
      float sum_train = std::max(leading_sum, lagging_sum);
      ret(row, col) = img(row, col) > tau * sum_train / train_hs;
    }
  }
  return ret;
}

MatrixXb os(const MatrixXf &img, int train_hs, int guard_hs, int k, double tau)
{
  MatrixXb ret = MatrixXb::Zero(img.rows(), img.cols());

  for (int col = 0; col < img.cols(); ++col)
  {
    for (int row = train_hs + guard_hs; row < img.rows() - train_hs - guard_hs; ++row)
    {
      float leading_sum = 0.0, lagging_sum = 0.0;
      std::vector<float> train;
      for (int i = row - train_hs - guard_hs; i < row + train_hs + guard_hs + 1; ++i)
      {
        if (std::abs(i - row) > guard_hs)
          train.push_back(img(i, col));
      }
      std::nth_element(train.begin(), train.begin() + k, train.end());
      ret(row, col) = img(row, col) > tau * train[k];
    }
  }
  return ret;
}

std::pair<MatrixXb, MatrixXf> ca2(const MatrixXf &img, int train_hs, int guard_hs, double tau)
{
  MatrixXb ret = MatrixXb::Zero(img.rows(), img.cols());
  MatrixXf ret2 = MatrixXf::Zero(img.rows(), img.cols());

  for (int col = 0; col < img.cols(); ++col)
  {
    for (int row = train_hs + guard_hs; row < img.rows() - train_hs - guard_hs; ++row)
    {
      float sum_train = 0;
      for (int i = row - train_hs - guard_hs; i < row + train_hs + guard_hs + 1; ++i)
      {
        if (std::abs(i - row) > guard_hs)
          sum_train += img(i, col);
      }
      ret(row, col) = img(row, col) > tau * sum_train / (2.0 * train_hs);
      ret2(row, col) = tau * sum_train / (2.0 * train_hs);
    }
  }
  return std::make_pair(ret, ret2);
}

std::pair<MatrixXb, MatrixXf> soca2(const MatrixXf &img, int train_hs, int guard_hs, double tau)
{
  MatrixXb ret = MatrixXb::Zero(img.rows(), img.cols());
  MatrixXf ret2 = MatrixXf::Zero(img.rows(), img.cols());

  for (int col = 0; col < img.cols(); ++col)
  {
    for (int row = train_hs + guard_hs; row < img.rows() - train_hs - guard_hs; ++row)
    {
      float leading_sum = 0.0, lagging_sum = 0.0;
      for (int i = row - train_hs - guard_hs; i < row + train_hs + guard_hs + 1; ++i)
      {
        if ((i - row) > guard_hs)
          lagging_sum += img(i, col);
        else if ((i - row) < -guard_hs)
          leading_sum += img(i, col);
      }
      float sum_train = std::min(leading_sum, lagging_sum);
      ret(row, col) = img(row, col) > tau * sum_train / train_hs;
      ret2(row, col) = tau * sum_train / train_hs;
    }
  }
  return std::make_pair(ret, ret2);
}

std::pair<MatrixXb, MatrixXf> goca2(const MatrixXf &img, int train_hs, int guard_hs, double tau)
{
  MatrixXb ret = MatrixXb::Zero(img.rows(), img.cols());
  MatrixXf ret2 = MatrixXf::Zero(img.rows(), img.cols());

  for (int col = 0; col < img.cols(); ++col)
  {
    for (int row = train_hs + guard_hs; row < img.rows() - train_hs - guard_hs; ++row)
    {
      float leading_sum = 0.0, lagging_sum = 0.0;
      for (int i = row - train_hs - guard_hs; i < row + train_hs + guard_hs + 1; ++i)
      {
        if ((i - row) > guard_hs)
          lagging_sum += img(i, col);
        else if ((i - row) < -guard_hs)
          leading_sum += img(i, col);
      }
      float sum_train = std::max(leading_sum, lagging_sum);
      ret(row, col) = img(row, col) > tau * sum_train / train_hs;
      ret2(row, col) = tau * sum_train / train_hs;
    }
  }
  return std::make_pair(ret, ret2);
}

std::pair<MatrixXb, MatrixXf> os2(const MatrixXf &img, int train_hs, int guard_hs, int k, double tau)
{
  MatrixXb ret = MatrixXb::Zero(img.rows(), img.cols());
  MatrixXf ret2 = MatrixXf::Zero(img.rows(), img.cols());

  for (int col = 0; col < img.cols(); ++col)
  {
    for (int row = train_hs + guard_hs; row < img.rows() - train_hs - guard_hs; ++row)
    {
      float leading_sum = 0.0, lagging_sum = 0.0;
      std::vector<float> train;
      for (int i = row - train_hs - guard_hs; i < row + train_hs + guard_hs + 1; ++i)
      {
        if (std::abs(i - row) > guard_hs)
          train.push_back(img(i, col));
      }
      std::nth_element(train.begin(), train.begin() + k, train.end());
      ret(row, col) = img(row, col) > tau * train[k];
      ret2(row, col) = tau * train[k];
    }
  }
  return std::make_pair(ret, ret2);
}

PYBIND11_MODULE(cfar, m)
{
  m.def("ca", &ca);
  m.def("soca", &soca);
  m.def("goca", &goca);
  m.def("os", &os);
  m.def("ca2", &ca2);
  m.def("soca2", &soca2);
  m.def("goca2", &goca2);
  m.def("os2", &os2);
}


#define BOOST_TEST_MODULE "test_omp_global_pdns_interaction"

#ifdef BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif

#include <mjolnir/math/math.hpp>
#include <mjolnir/core/BoundaryCondition.hpp>
#include <mjolnir/core/SimulatorTraits.hpp>
#include <mjolnir/omp/System.hpp>
#include <mjolnir/omp/RandomNumberGenerator.hpp>
#include <mjolnir/omp/UnlimitedGridCellList.hpp>
#include <mjolnir/omp/ProteinDNANonSpecificInteraction.hpp>
#include <mjolnir/util/make_unique.hpp>

BOOST_AUTO_TEST_CASE(omp_PDNS_calc_force)
{
    constexpr double tol = 1e-8;
    mjolnir::LoggerManager::set_default_logger("test_omp_pdns_interaction.log");

    using traits_type      = mjolnir::OpenMPSimulatorTraits<double, mjolnir::UnlimitedBoundary>;
    using coordinate_type  = typename traits_type::coordinate_type;
    using boundary_type    = typename traits_type::boundary_type;
    using system_type      = mjolnir::System<traits_type>;
    using topology_type    = mjolnir::Topology;
    using potential_type   = mjolnir::ProteinDNANonSpecificPotential<traits_type>;
    using partition_type   = mjolnir::UnlimitedGridCellList<traits_type, potential_type>;
    using interaction_type = mjolnir::ProteinDNANonSpecificInteraction<traits_type>;
    using rng_type         = mjolnir::RandomNumberGenerator<traits_type>;

    using sequencial_traits_type      = mjolnir::SimulatorTraits<double, mjolnir::UnlimitedBoundary>;
    using sequencial_potential_type   = mjolnir::ProteinDNANonSpecificPotential<sequencial_traits_type>;
    using sequencial_system_type      = mjolnir::System<sequencial_traits_type>;
    using sequencial_partition_type   = mjolnir::UnlimitedGridCellList<sequencial_traits_type, sequencial_potential_type>;
    using sequencial_interaction_type = mjolnir::ProteinDNANonSpecificInteraction<sequencial_traits_type>;

    const int max_number_of_threads = omp_get_max_threads();
    BOOST_TEST_WARN(max_number_of_threads > 2);
    BOOST_TEST_MESSAGE("maximum number of threads = " << max_number_of_threads);

    // o 0     5 o--o 6  |
    //  \       /        |
    //   o 1   o 7       |
    //  /       \        |
    // o 2     8 o--o 9  |
    //  \       /        |
    //   o 3   o 10      |
    //  /       \        |
    // o 4    11 o--o 12 |

    for(int num_thread=1; num_thread<=max_number_of_threads; ++num_thread)
    {
        omp_set_num_threads(num_thread);
        BOOST_TEST_MESSAGE("maximum number of threads = " << omp_get_max_threads());

        potential_type potential(1.0, 0.1, 5.0,
            std::vector<typename potential_type::contact_parameter_type>{
                typename potential_type::contact_parameter_type{1, 0, 2, 6.0, 5.0, 3.14 * 0.5, 3.14 * 0.667, 0.0, 0.0},
                typename potential_type::contact_parameter_type{2, 1, 3, 6.0, 5.0, 3.14 * 0.5, 3.14 * 0.667, 0.0, 0.0},
                typename potential_type::contact_parameter_type{3, 2, 4, 6.0, 5.0, 3.14 * 0.5, 3.14 * 0.667, 0.0, 0.0}
            },
            std::vector<typename potential_type::dna_index_type>{
                typename potential_type::dna_index_type{ 7, 8},
                typename potential_type::dna_index_type{10,11}
            },
            {},
            typename potential_type::ignore_molecule_type("Nothing"),
            typename potential_type::ignore_group_type({}));

        sequencial_potential_type seq_potential(1.0, 0.1, 5.0,
            std::vector<typename sequencial_potential_type::contact_parameter_type>{
                typename sequencial_potential_type::contact_parameter_type{1, 0, 2, 6.0, 5.0, 3.14 * 0.5, 3.14 * 0.667, 0.0, 0.0},
                typename sequencial_potential_type::contact_parameter_type{2, 1, 3, 6.0, 5.0, 3.14 * 0.5, 3.14 * 0.667, 0.0, 0.0},
                typename sequencial_potential_type::contact_parameter_type{3, 2, 4, 6.0, 5.0, 3.14 * 0.5, 3.14 * 0.667, 0.0, 0.0}
            },
            std::vector<typename sequencial_potential_type::dna_index_type>{
                typename sequencial_potential_type::dna_index_type{ 7, 8},
                typename sequencial_potential_type::dna_index_type{10,11}
            },
            {},
            typename sequencial_potential_type::ignore_molecule_type("Nothing"),
            typename sequencial_potential_type::ignore_group_type({}));

        rng_type    rng(123456789);
        system_type sys(13, boundary_type{});
        topology_type topol(13);
        sys.position( 0) = mjolnir::math::make_coordinate<coordinate_type>( 0.0,  3.3, 0.0);
        sys.position( 1) = mjolnir::math::make_coordinate<coordinate_type>( 1.9,  1.6, 0.0);
        sys.position( 2) = mjolnir::math::make_coordinate<coordinate_type>( 0.0,  0.0, 0.0);
        sys.position( 3) = mjolnir::math::make_coordinate<coordinate_type>( 1.9, -1.6, 0.0);
        sys.position( 4) = mjolnir::math::make_coordinate<coordinate_type>( 0.0, -3.3, 0.0);
        sys.position( 5) = mjolnir::math::make_coordinate<coordinate_type>( 8.8,  3.3, 0.0);
        sys.position( 6) = mjolnir::math::make_coordinate<coordinate_type>(12.0,  3.3, 0.0);
        sys.position( 7) = mjolnir::math::make_coordinate<coordinate_type>( 6.9,  1.6, 0.0);
        sys.position( 8) = mjolnir::math::make_coordinate<coordinate_type>( 8.8,  0.0, 0.0);
        sys.position( 9) = mjolnir::math::make_coordinate<coordinate_type>(12.0,  0.0, 0.0);
        sys.position(10) = mjolnir::math::make_coordinate<coordinate_type>( 6.9, -1.6, 0.0);
        sys.position(11) = mjolnir::math::make_coordinate<coordinate_type>( 8.8, -3.3, 0.0);
        sys.position(12) = mjolnir::math::make_coordinate<coordinate_type>(12.0, -3.3, 0.0);

        for(std::size_t i=0; i<sys.size(); ++i)
        {
            sys.mass(i)     = 1.0;
            sys.velocity(i) = mjolnir::math::make_coordinate<coordinate_type>(0, 0, 0);
            sys.force(i)    = mjolnir::math::make_coordinate<coordinate_type>(0, 0, 0);
            sys.name(i)     = "X";
            sys.group(i)    = "TEST";
        }

        topol.construct_molecules();

        // add perturbation
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            mjolnir::math::X(sys.position(i)) += rng.uniform_real(-0.1, 0.1);
            mjolnir::math::Y(sys.position(i)) += rng.uniform_real(-0.1, 0.1);
            mjolnir::math::Z(sys.position(i)) += rng.uniform_real(-0.1, 0.1);
        }
        potential.update(sys, topol);

        // init sequential one with the same coordinates
        sequencial_system_type seq_sys(13, boundary_type{});
        assert(sys.size() == seq_sys.size());
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            seq_sys.mass(i)     = sys.mass(i);
            seq_sys.position(i) = sys.position(i);
            seq_sys.velocity(i) = sys.velocity(i);
            seq_sys.force(i)    = sys.force(i);
            seq_sys.name(i)     = sys.name(i);
            seq_sys.group(i)    = sys.group(i);
        }
        seq_potential.update(seq_sys, topol);

        partition_type            celllist;
        sequencial_partition_type seq_celllist;

        topol.construct_molecules();

        interaction_type interaction(std::move(potential),
            mjolnir::SpatialPartition<traits_type, potential_type>(
                mjolnir::make_unique<partition_type>()));
        sequencial_interaction_type seq_interaction(std::move(seq_potential),
            mjolnir::SpatialPartition<sequencial_traits_type, sequencial_potential_type>(
                mjolnir::make_unique<sequencial_partition_type>()));

        interaction    .initialize(sys, topol);
        seq_interaction.initialize(seq_sys, topol);

        // calculate forces with openmp
        interaction.calc_force(sys);
        sys.postprocess_forces();

        // calculate forces without openmp
        seq_interaction.calc_force(seq_sys);

        // check the values are the same
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            BOOST_TEST(mjolnir::math::X(seq_sys.force(i)) == mjolnir::math::X(sys.force(i)),
                       boost::test_tools::tolerance(tol));
            BOOST_TEST(mjolnir::math::Y(seq_sys.force(i)) == mjolnir::math::Y(sys.force(i)),
                       boost::test_tools::tolerance(tol));
            BOOST_TEST(mjolnir::math::Z(seq_sys.force(i)) == mjolnir::math::Z(sys.force(i)),
                       boost::test_tools::tolerance(tol));
        }
        BOOST_TEST(interaction.calc_energy(sys) == seq_interaction.calc_energy(seq_sys),
                   boost::test_tools::tolerance(tol));
    }
}

BOOST_AUTO_TEST_CASE(omp_PDNS_calc_force_and_energy)
{
    mjolnir::LoggerManager::set_default_logger("test_omp_pdns_interaction.log");

    using traits_type      = mjolnir::OpenMPSimulatorTraits<double, mjolnir::UnlimitedBoundary>;
    using coordinate_type  = typename traits_type::coordinate_type;
    using boundary_type    = typename traits_type::boundary_type;
    using system_type      = mjolnir::System<traits_type>;
    using topology_type    = mjolnir::Topology;
    using potential_type   = mjolnir::ProteinDNANonSpecificPotential<traits_type>;
    using partition_type   = mjolnir::UnlimitedGridCellList<traits_type, potential_type>;
    using interaction_type = mjolnir::ProteinDNANonSpecificInteraction<traits_type>;
    using rng_type         = mjolnir::RandomNumberGenerator<traits_type>;

    const int max_number_of_threads = omp_get_max_threads();
    BOOST_TEST_WARN(max_number_of_threads > 2);
    BOOST_TEST_MESSAGE("maximum number of threads = " << max_number_of_threads);

    // o 0     5 o--o 6  |
    //  \       /        |
    //   o 1   o 7       |
    //  /       \        |
    // o 2     8 o--o 9  |
    //  \       /        |
    //   o 3   o 10      |
    //  /       \        |
    // o 4    11 o--o 12 |

    for(int num_thread=1; num_thread<=max_number_of_threads; ++num_thread)
    {
        omp_set_num_threads(num_thread);
        BOOST_TEST_MESSAGE("maximum number of threads = " << omp_get_max_threads());

        potential_type potential(1.0, 0.1, 5.0,
            std::vector<typename potential_type::contact_parameter_type>{
                typename potential_type::contact_parameter_type{1, 0, 2, 6.0, 5.0, 3.14 * 0.5, 3.14 * 0.667, 0.0, 0.0},
                typename potential_type::contact_parameter_type{2, 1, 3, 6.0, 5.0, 3.14 * 0.5, 3.14 * 0.667, 0.0, 0.0},
                typename potential_type::contact_parameter_type{3, 2, 4, 6.0, 5.0, 3.14 * 0.5, 3.14 * 0.667, 0.0, 0.0}
            },
            std::vector<typename potential_type::dna_index_type>{
                typename potential_type::dna_index_type{ 7, 8},
                typename potential_type::dna_index_type{10,11}
            },
            {},
            typename potential_type::ignore_molecule_type("Nothing"),
            typename potential_type::ignore_group_type({}));

        rng_type    rng(123456789);
        system_type sys(13, boundary_type{});
        topology_type topol(13);
        sys.position( 0) = mjolnir::math::make_coordinate<coordinate_type>( 0.0,  3.3, 0.0);
        sys.position( 1) = mjolnir::math::make_coordinate<coordinate_type>( 1.9,  1.6, 0.0);
        sys.position( 2) = mjolnir::math::make_coordinate<coordinate_type>( 0.0,  0.0, 0.0);
        sys.position( 3) = mjolnir::math::make_coordinate<coordinate_type>( 1.9, -1.6, 0.0);
        sys.position( 4) = mjolnir::math::make_coordinate<coordinate_type>( 0.0, -3.3, 0.0);
        sys.position( 5) = mjolnir::math::make_coordinate<coordinate_type>( 8.8,  3.3, 0.0);
        sys.position( 6) = mjolnir::math::make_coordinate<coordinate_type>(12.0,  3.3, 0.0);
        sys.position( 7) = mjolnir::math::make_coordinate<coordinate_type>( 6.9,  1.6, 0.0);
        sys.position( 8) = mjolnir::math::make_coordinate<coordinate_type>( 8.8,  0.0, 0.0);
        sys.position( 9) = mjolnir::math::make_coordinate<coordinate_type>(12.0,  0.0, 0.0);
        sys.position(10) = mjolnir::math::make_coordinate<coordinate_type>( 6.9, -1.6, 0.0);
        sys.position(11) = mjolnir::math::make_coordinate<coordinate_type>( 8.8, -3.3, 0.0);
        sys.position(12) = mjolnir::math::make_coordinate<coordinate_type>(12.0, -3.3, 0.0);

        for(std::size_t i=0; i<sys.size(); ++i)
        {
            sys.mass(i)     = 1.0;
            sys.velocity(i) = mjolnir::math::make_coordinate<coordinate_type>(0, 0, 0);
            sys.force(i)    = mjolnir::math::make_coordinate<coordinate_type>(0, 0, 0);
            sys.name(i)     = "X";
            sys.group(i)    = "TEST";
        }

        topol.construct_molecules();

        // add perturbation
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            mjolnir::math::X(sys.position(i)) += rng.uniform_real(-0.1, 0.1);
            mjolnir::math::Y(sys.position(i)) += rng.uniform_real(-0.1, 0.1);
            mjolnir::math::Z(sys.position(i)) += rng.uniform_real(-0.1, 0.1);
        }
        potential.update(sys, topol);

        partition_type celllist;
        topol.construct_molecules();

        interaction_type interaction(std::move(potential),
            mjolnir::SpatialPartition<traits_type, potential_type>(
                mjolnir::make_unique<partition_type>()));

        interaction.initialize(sys, topol);

        constexpr double tol = 1e-4;
        auto ref_sys = sys;

        // calculate forces with openmp
        const auto ref_ene = interaction.calc_energy(ref_sys);

        ref_sys.preprocess_forces();
        interaction.calc_force(ref_sys);
        ref_sys.postprocess_forces();

        sys.preprocess_forces();
        const auto ene = interaction.calc_force_and_energy(sys);
        sys.postprocess_forces();

        BOOST_TEST(ene == ref_ene, boost::test_tools::tolerance(tol));
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            BOOST_TEST(mjolnir::math::X(ref_sys.force(i)) == mjolnir::math::X(sys.force(i)), boost::test_tools::tolerance(tol));
            BOOST_TEST(mjolnir::math::Y(ref_sys.force(i)) == mjolnir::math::Y(sys.force(i)), boost::test_tools::tolerance(tol));
            BOOST_TEST(mjolnir::math::Z(ref_sys.force(i)) == mjolnir::math::Z(sys.force(i)), boost::test_tools::tolerance(tol));
        }
    }
}


#include "Day20-JurassicJigsaw.h"

#include "ImageReassembler.h"
#include "SeaMonsterFinder.h"

#include <AdventOfCodeCommon/DisableLibraryWarningsMacros.h>

__BEGIN_LIBRARIES_DISABLE_WARNINGS
#include <boost/algorithm/string.hpp>

#include <regex>
__END_LIBRARIES_DISABLE_WARNINGS

namespace AdventOfCode
{
namespace Year2020
{
namespace Day20
{

std::vector<std::string> convertImageToStringImage(const Image& image)
{
    std::vector<std::string> result;

    for (size_t j = 0; j < image.rows(); ++j)
    {
        std::string row;
        for (size_t i = 0; i < image.cols(); ++i)
        {
            row.push_back(image(j, i) ? '#' : '.');
        }
        result.push_back(std::move(row));
    }

    return result;
}

int createTileID(const std::string& tileIDLine)
{
    const std::regex idLineRegex{R"(Tile (\d+):)"};
    std::smatch matches;
    std::regex_search(tileIDLine, matches, idLineRegex);
    if (matches.size() != 2)
    {
        throw std::runtime_error("Invalid tile ID line: " + tileIDLine);
    }
    return std::stoi(matches[1]);
}

Image createImage(const std::vector<std::string>& imageLines)
{
    Image image{imageLines.size(), imageLines.at(0).size()};

    for (size_t j = 0; j < imageLines.size(); ++j)
    {
        for (size_t i = 0; i < imageLines.size(); ++i)
        {
            image(j, i) = (imageLines[j][i] == '#');
        }
    }

    return image;
}

Tile createTile(const std::vector<std::string>& tileLines)
{
    int tileID = createTileID(tileLines.front());

    std::vector<std::string> imageLines{std::next(tileLines.cbegin()), tileLines.cend()};
    Image image = createImage(imageLines);

    return Tile{std::move(tileID), std::move(image)};
}

std::vector<Tile> createTiles(const std::vector<std::string>& cameraArrayLines)
{
    std::vector<std::vector<std::string>> tokens;
    boost::split(tokens, cameraArrayLines, [](const auto& line) {return line.empty(); });

    std::vector<Tile> tiles;

    for (const auto& tileLines : tokens)
    {
        if (!tileLines.empty())
        {
            Tile tile = createTile(tileLines);
            tiles.push_back(std::move(tile));
        }
    }

    return tiles;
}

int64_t cornerTileIDsMultiplied(const std::vector<std::string>& cameraArrayLines)
{
    std::vector<Tile> tiles = createTiles(cameraArrayLines);
    ImageReassembler imageReassembler{std::move(tiles)};
    imageReassembler.reassemble();
    return imageReassembler.getCornerTileIDsMultiplied();
}

int numWaterNotPartOfASeaMonster(const std::vector<std::string>& cameraArrayLines)
{
    std::vector<Tile> tiles = createTiles(cameraArrayLines);
    ImageReassembler imageReassembler{std::move(tiles)};
    imageReassembler.reassemble();
    std::vector<Image> allPossibleReassembledImages = imageReassembler.getAllPossibleImages();

    for (const auto& image : allPossibleReassembledImages)
    {
        std::vector<std::string> stringImage = convertImageToStringImage(image);
        SeaMonsterFinder seaMonsterFinder{std::move(stringImage)};

        int originalNumWater = seaMonsterFinder.getNumWaterFields();
        seaMonsterFinder.markSeaMonsterFields();
        int numWaterNotPartOfASeaMonster = seaMonsterFinder.getNumWaterFields();

        if (originalNumWater != numWaterNotPartOfASeaMonster)
        {
            return numWaterNotPartOfASeaMonster;
        }
    }

    throw std::runtime_error("Unable to find any sea monsters in any of the possible images");
}

}
}
}


//
// Copyright (c) 2002--2010
// Toon Knapen, Karl Meerbergen, Kresimir Fresl,
// Thomas Klimpel and Rutger ter Borg
//
// 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)
//
// THIS FILE IS AUTOMATICALLY GENERATED
// PLEASE DO NOT EDIT!
//

#ifndef BOOST_NUMERIC_BINDINGS_BLAS_LEVEL1_ROTG_HPP
#define BOOST_NUMERIC_BINDINGS_BLAS_LEVEL1_ROTG_HPP

#include <boost/assert.hpp>
#include <boost/numeric/bindings/begin.hpp>
#include <boost/numeric/bindings/is_mutable.hpp>
#include <boost/numeric/bindings/remove_imaginary.hpp>
#include <boost/numeric/bindings/size.hpp>
#include <boost/numeric/bindings/stride.hpp>
#include <boost/numeric/bindings/value_type.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/remove_const.hpp>

//
// The BLAS-backend is selected by defining a pre-processor variable,
//  which can be one of
// * for CBLAS, define BOOST_NUMERIC_BINDINGS_BLAS_CBLAS
// * for CUBLAS, define BOOST_NUMERIC_BINDINGS_BLAS_CUBLAS
// * netlib-compatible BLAS is the default
//
#if defined BOOST_NUMERIC_BINDINGS_BLAS_CBLAS
#include <boost/numeric/bindings/blas/detail/cblas.h>
#include <boost/numeric/bindings/blas/detail/cblas_option.hpp>
#elif defined BOOST_NUMERIC_BINDINGS_BLAS_CUBLAS
#include <boost/numeric/bindings/blas/detail/cublas.h>
#include <boost/numeric/bindings/blas/detail/blas_option.hpp>
#else
#include <boost/numeric/bindings/blas/detail/blas.h>
#include <boost/numeric/bindings/blas/detail/blas_option.hpp>
#endif

namespace boost {
namespace numeric {
namespace bindings {
namespace blas {

//
// The detail namespace contains value-type-overloaded functions that
// dispatch to the appropriate back-end BLAS-routine.
//
namespace detail {

#if defined BOOST_NUMERIC_BINDINGS_BLAS_CBLAS
//
// Overloaded function for dispatching to
// * CBLAS backend, and
// * float value-type.
//
inline void rotg( float& a, float& b, float& c, float& s ) {
    cblas_srotg( &a, &b, &c, &s );
}

//
// Overloaded function for dispatching to
// * CBLAS backend, and
// * double value-type.
//
inline void rotg( double& a, double& b, double& c, double& s ) {
    cblas_drotg( &a, &b, &c, &s );
}

//
// Overloaded function for dispatching to
// * CBLAS backend, and
// * complex<float> value-type.
//
inline void rotg( std::complex<float>& a, std::complex<float>& b, float& c,
        std::complex<float>& s ) {
    // NOT FOUND();
}

//
// Overloaded function for dispatching to
// * CBLAS backend, and
// * complex<double> value-type.
//
inline void rotg( std::complex<double>& a, std::complex<double>& b, double& c,
        std::complex<double>& s ) {
    // NOT FOUND();
}

#elif defined BOOST_NUMERIC_BINDINGS_BLAS_CUBLAS
//
// Overloaded function for dispatching to
// * CUBLAS backend, and
// * float value-type.
//
inline void rotg( float& a, float& b, float& c, float& s ) {
    cublasSrotg( &a, &b, &c, &s );
}

//
// Overloaded function for dispatching to
// * CUBLAS backend, and
// * double value-type.
//
inline void rotg( double& a, double& b, double& c, double& s ) {
    cublasDrotg( &a, &b, &c, &s );
}

//
// Overloaded function for dispatching to
// * CUBLAS backend, and
// * complex<float> value-type.
//
inline void rotg( std::complex<float>& a, std::complex<float>& b, float& c,
        std::complex<float>& s ) {
    cublasCrotg( &a, b, &c, &s );
}

//
// Overloaded function for dispatching to
// * CUBLAS backend, and
// * complex<double> value-type.
//
inline void rotg( std::complex<double>& a, std::complex<double>& b, double& c,
        std::complex<double>& s ) {
    cublasZrotg( &a, b, &c, &s );
}

#else
//
// Overloaded function for dispatching to
// * netlib-compatible BLAS backend (the default), and
// * float value-type.
//
inline void rotg( float& a, float& b, float& c, float& s ) {
    BLAS_SROTG( &a, &b, &c, &s );
}

//
// Overloaded function for dispatching to
// * netlib-compatible BLAS backend (the default), and
// * double value-type.
//
inline void rotg( double& a, double& b, double& c, double& s ) {
    BLAS_DROTG( &a, &b, &c, &s );
}

//
// Overloaded function for dispatching to
// * netlib-compatible BLAS backend (the default), and
// * complex<float> value-type.
//
inline void rotg( std::complex<float>& a, std::complex<float>& b, float& c,
        std::complex<float>& s ) {
    BLAS_CROTG( &a, &b, &c, &s );
}

//
// Overloaded function for dispatching to
// * netlib-compatible BLAS backend (the default), and
// * complex<double> value-type.
//
inline void rotg( std::complex<double>& a, std::complex<double>& b, double& c,
        std::complex<double>& s ) {
    BLAS_ZROTG( &a, &b, &c, &s );
}

#endif

} // namespace detail

//
// Value-type based template class. Use this class if you need a type
// for dispatching to rotg.
//
template< typename Value >
struct rotg_impl {

    typedef Value value_type;
    typedef typename remove_imaginary< Value >::type real_type;
    typedef void result_type;

    //
    // Static member function that
    // * Deduces the required arguments for dispatching to BLAS, and
    // * Asserts that most arguments make sense.
    //
    static result_type invoke( value_type& a, value_type& b, real_type& c,
            value_type& s ) {
        namespace bindings = ::boost::numeric::bindings;
        detail::rotg( a, b, c, s );
    }
};

//
// Functions for direct use. These functions are overloaded for temporaries,
// so that wrapped types can still be passed and used for write-access. Calls
// to these functions are passed to the rotg_impl classes. In the
// documentation, the const-overloads are collapsed to avoid a large number of
// prototypes which are very similar.
//

//
// Overloaded function for rotg. Its overload differs for
//
template< typename Value >
inline typename rotg_impl< Value >::result_type
rotg( Value& a, Value& b, Value& c, Value& s ) {
    rotg_impl< Value >::invoke( a, b, c, s );
}

} // namespace blas
} // namespace bindings
} // namespace numeric
} // namespace boost

#endif


// Copyright (c) 2006 Johan Rade

// Copyright (c) 2011 Paul A. Bristow incorporated Boost.Math

// 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)

//#ifdef _MSC_VER
//#   pragma warning(disable : 4127 4511 4512 4701 4702)
//#endif

#define BOOST_TEST_MAIN

#include <limits>
#include <locale>
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/test/auto_unit_test.hpp>

#include <boost/math/special_functions/nonfinite_num_facets.hpp>
#include <boost/math/special_functions/sign.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#include "almost_equal.ipp"
#include "s_.ipp"

namespace {

// the anonymous namespace resolves ambiguities on platforms
// with fpclassify etc functions at global scope

using boost::lexical_cast;

using namespace boost::math;
using boost::math::signbit;
using boost::math::changesign;
using boost::math::isnan;

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

template<class CharType, class ValType> void lexical_cast_test_impl();

BOOST_AUTO_TEST_CASE(lexical_cast_test)
{
    lexical_cast_test_impl<char, float>();
    lexical_cast_test_impl<char, double>();
    lexical_cast_test_impl<char, long double>();
    lexical_cast_test_impl<wchar_t, float>();
    lexical_cast_test_impl<wchar_t, double>();
    lexical_cast_test_impl<wchar_t, long double>();
}

template<class CharType, class ValType> void lexical_cast_test_impl()
{
    if((std::numeric_limits<ValType>::has_infinity == 0) || (std::numeric_limits<ValType>::infinity() == 0))
       return;
    if((std::numeric_limits<ValType>::has_quiet_NaN == 0) || (std::numeric_limits<ValType>::quiet_NaN() == 0))
       return;

    std::locale old_locale;
    std::locale tmp_locale(old_locale,
        new nonfinite_num_put<CharType>(signed_zero));
    std::locale new_locale(tmp_locale, new nonfinite_num_get<CharType>);
    std::locale::global(new_locale);

    ValType a1 = static_cast<ValType>(0);
    ValType a2 = static_cast<ValType>(13);
    ValType a3 = std::numeric_limits<ValType>::infinity();
    ValType a4 = std::numeric_limits<ValType>::quiet_NaN();
    ValType a5 = std::numeric_limits<ValType>::signaling_NaN();
    ValType a6 = (changesign)(static_cast<ValType>(0));
    ValType a7 = static_cast<ValType>(-57);
    ValType a8 = -std::numeric_limits<ValType>::infinity();
    ValType a9 = (changesign)(std::numeric_limits<ValType>::quiet_NaN()); // -NaN
    ValType a10 = (changesign)(std::numeric_limits<ValType>::signaling_NaN()); // -NaN

    std::basic_string<CharType> s1 = S_("0");
    std::basic_string<CharType> s2 = S_("13");
    std::basic_string<CharType> s3 = S_("inf");
    std::basic_string<CharType> s4 = S_("nan");
    std::basic_string<CharType> s5 = S_("nan");
    std::basic_string<CharType> s6 = S_("-0");
    std::basic_string<CharType> s7 = S_("-57");
    std::basic_string<CharType> s8 = S_("-inf");
    std::basic_string<CharType> s9 = S_("-nan");
    std::basic_string<CharType> s10 = S_("-nan");

    BOOST_CHECK(lexical_cast<std::basic_string<CharType> >(a1) == s1);
    BOOST_CHECK(lexical_cast<std::basic_string<CharType> >(a2) == s2);
    BOOST_CHECK(lexical_cast<std::basic_string<CharType> >(a3) == s3);
    BOOST_CHECK(lexical_cast<std::basic_string<CharType> >(a4) == s4);
    BOOST_CHECK(lexical_cast<std::basic_string<CharType> >(a5) == s5);
    BOOST_CHECK(lexical_cast<std::basic_string<CharType> >(a6) == s6);
    BOOST_CHECK(lexical_cast<std::basic_string<CharType> >(a7) == s7);
    BOOST_CHECK(lexical_cast<std::basic_string<CharType> >(a8) == s8);
    BOOST_CHECK(lexical_cast<std::basic_string<CharType> >(a9) == s9);
    BOOST_CHECK(lexical_cast<std::basic_string<CharType> >(a10) == s10);

    BOOST_CHECK(lexical_cast<ValType>(s1) == a1);
    BOOST_CHECK(!(signbit)(lexical_cast<ValType>(s1)));
    BOOST_CHECK(lexical_cast<ValType>(s2) == a2);
    BOOST_CHECK(lexical_cast<ValType>(s3) == a3);
    BOOST_CHECK((isnan)(lexical_cast<ValType>(s4)));
    BOOST_CHECK(!(signbit)(lexical_cast<ValType>(s4)));
    BOOST_CHECK((isnan)(lexical_cast<ValType>(s5)));
    BOOST_CHECK(!(signbit)(lexical_cast<ValType>(s5)));
    BOOST_CHECK(lexical_cast<ValType>(a6) == a6);
    BOOST_CHECK((signbit)(lexical_cast<ValType>(s6)));
    BOOST_CHECK(lexical_cast<ValType>(s7) == a7);
    BOOST_CHECK(lexical_cast<ValType>(s8) == a8);
    BOOST_CHECK((isnan)(lexical_cast<ValType>(s9)));
    BOOST_CHECK((signbit)(lexical_cast<ValType>(s9)));
    BOOST_CHECK((isnan)(lexical_cast<ValType>(s10)));
    BOOST_CHECK((signbit)(lexical_cast<ValType>(s10)));

    std::locale::global(old_locale);
}

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

}   // anonymous namespace


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

/*
 Copyright (C) 2003, 2004, 2008 StatPro Italia srl

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

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

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

#ifndef quantlib_test_utilities_hpp
#define quantlib_test_utilities_hpp

#include <ql/instruments/payoffs.hpp>
#include <ql/math/matrix.hpp>
// #include <ql/exercise.hpp>
 #include <ql/termstructures/yieldtermstructure.hpp>
 #include <ql/termstructures/volatility/equityfx/blackvoltermstructure.hpp>
 #include <ql/quote.hpp>
#include <ql/settings.hpp>
#include <ql/patterns/observable.hpp>
#include <ql/time/daycounters/actual365fixed.hpp>
#include <boost/test/included/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/function.hpp>
#include <vector>
#include <string>
#include <numeric>
#include <iomanip>

#include <ql/instruments/payoffs.hpp>
// #include <ql/indexes/indexmanager.hpp>
 #include <ql/termstructures/yield/flatforward.hpp>
 #include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
 #include <ql/time/calendars/nullcalendar.hpp>

// This makes it easier to use array literals (alas, no std::vector literals)
#define LENGTH(a) (sizeof(a)/sizeof(a[0]))

/* the following works around a problem with Boost 1.32 where std::fixed
   and similar manipulators could not be sent to the Boost streams */
#if defined(QL_WORKING_BOOST_STREAMS)
#define QL_FIXED std::fixed
#define QL_SCIENTIFIC std::scientific
#else
#define QL_FIXED ""
#define QL_SCIENTIFIC ""
#endif

/* the following displays the elapsed time for the test if
   QL_DISPLAY_TEST_TIME is defined. */
#if defined(QL_DISPLAY_TEST_TIME)
#define QL_TEST_START_TIMING boost::progress_timer t;
#else
#define QL_TEST_START_TIMING
#endif

#define QUANTLIB_TEST_CASE(f) BOOST_TEST_CASE(QuantLib::detail::quantlib_test_case(f))

#define CHECK_DOWNCAST(Derived,Description) { \
    boost::shared_ptr<Derived> hd = boost::dynamic_pointer_cast<Derived>(h); \
    if (hd) \
        return Description; \
}

namespace QuantLib {

	namespace {

	    Real norm(const Matrix& m)
		{
			Real sum = 0.0;
			for (Size i=0; i<m.rows(); i++)
				for (Size j=0; j<m.columns(); j++)
					sum += m[i][j]*m[i][j];
			return std::sqrt(sum);
		}
	}

    namespace detail {

        // used to avoid no-assertion messages in Boost 1.35
        class quantlib_test_case {
            boost::function0<void> test_;
          public:
            template <class F>
            quantlib_test_case(F test) : test_(test) {}
            void operator()() const {
                Date before = Settings::instance().evaluationDate();
                BOOST_CHECK(true);
                test_();
                Date after = Settings::instance().evaluationDate();
                if (before != after)
                    BOOST_ERROR("Evaluation date not reset"
                                << "\n  before: " << before
                                << "\n  after:  " << after);
            }
            #if BOOST_VERSION <= 105300
            // defined to avoid unused-variable warnings. It doesn't
            // work after Boost 1.53 because the functions were
            // overloaded and the address can't be resolved.
            void _use_check(
                    const void* = &boost::test_tools::check_is_close,
                    const void* = &boost::test_tools::check_is_small) const {}
            #endif
        };

    }

    // std::string payoffTypeToString(const boost::shared_ptr<Payoff>&);
    // std::string exerciseTypeToString(const boost::shared_ptr<Exercise>&);

     boost::shared_ptr<YieldTermStructure>
     flatRate(const Date& today,
              const boost::shared_ptr<Quote>& forward,
              const DayCounter& dc);

     boost::shared_ptr<YieldTermStructure>
     flatRate(const Date& today,
              Rate forward,
              const DayCounter& dc);

     boost::shared_ptr<YieldTermStructure>
     flatRate(const boost::shared_ptr<Quote>& forward,
              const DayCounter& dc);

     boost::shared_ptr<YieldTermStructure>
     flatRate(Rate forward,
              const DayCounter& dc);

     boost::shared_ptr<BlackVolTermStructure>
     flatVol(const Date& today,
             const boost::shared_ptr<Quote>& volatility,
             const DayCounter& dc);

     boost::shared_ptr<BlackVolTermStructure>
     flatVol(const Date& today,
             Volatility volatility,
             const DayCounter& dc);

     boost::shared_ptr<BlackVolTermStructure>
     flatVol(const boost::shared_ptr<Quote>& volatility,
             const DayCounter& dc);

     boost::shared_ptr<BlackVolTermStructure>
     flatVol(Volatility volatility,
             const DayCounter& dc);

    Real relativeError(Real x1, Real x2, Real reference);

    //bool checkAbsError(Real x1, Real x2, Real tolerance){
    //    return std::fabs(x1 - x2) < tolerance;
    //};

    class Flag : public QuantLib::Observer {
      private:
        bool up_;
      public:
        Flag() : up_(false) {}
        void raise() { up_ = true; }
        void lower() { up_ = false; }
        bool isUp() const { return up_; }
        void update() { raise(); }
    };

    template<class Iterator>
    Real norm(const Iterator& begin, const Iterator& end, Real h) {
        // squared values
        std::vector<Real> f2(end-begin);
        std::transform(begin,end,begin,f2.begin(),
                       std::multiplies<Real>());
        // numeric integral of f^2
        Real I = h * (std::accumulate(f2.begin(),f2.end(),Real(0.0))
                      - 0.5*f2.front() - 0.5*f2.back());
        return std::sqrt(I);
    }

    // this cleans up index-fixing histories when destroyed
    // class IndexHistoryCleaner {
    //   public:
    //     IndexHistoryCleaner();
    //     ~IndexHistoryCleaner();
    // };

    // Allow streaming vectors to error messages.

    // The standard forbids defining new overloads in the std
    // namespace, so we have to use a wrapper instead of overloading
    // operator<< to send a vector to the stream directly.
    // Defining the overload outside the std namespace wouldn't work
    // with Boost streams because of ADT name lookup rules.

    template <class T>
    struct vector_streamer {
        vector_streamer(const std::vector<T>& v) : v(v) {}
        std::vector<T> v;
    };

    template <class T>
    vector_streamer<T> to_stream(const std::vector<T>& v) {
        return vector_streamer<T>(v);
    }

    template <class T>
    std::ostream& operator<<(std::ostream& out, const vector_streamer<T>& s) {
        out << "{ ";
        if (!s.v.empty()) {
            for (size_t n=0; n<s.v.size()-1; ++n)
                out << s.v[n] << ", ";
            out << s.v.back();
        }
        out << " }";
        return out;
    }

    // implementation

    // std::string payoffTypeToString(const boost::shared_ptr<Payoff>& h) {

    //     CHECK_DOWNCAST(PlainVanillaPayoff, "plain-vanilla");
    //     CHECK_DOWNCAST(CashOrNothingPayoff, "cash-or-nothing");
    //     CHECK_DOWNCAST(AssetOrNothingPayoff, "asset-or-nothing");
    //     CHECK_DOWNCAST(SuperSharePayoff, "super-share");
    //     CHECK_DOWNCAST(SuperFundPayoff, "super-fund");
    //     CHECK_DOWNCAST(PercentageStrikePayoff, "percentage-strike");
    //     CHECK_DOWNCAST(GapPayoff, "gap");
    //     CHECK_DOWNCAST(FloatingTypePayoff, "floating-type");

    //     QL_FAIL("unknown payoff type");
    // }

    // std::string exerciseTypeToString(const boost::shared_ptr<Exercise>& h) {

    //     CHECK_DOWNCAST(EuropeanExercise, "European");
    //     CHECK_DOWNCAST(AmericanExercise, "American");
    //     CHECK_DOWNCAST(BermudanExercise, "Bermudan");

    //     QL_FAIL("unknown exercise type");
    // }

     boost::shared_ptr<YieldTermStructure>
     flatRate(const Date& today,
              const boost::shared_ptr<Quote>& forward,
              const DayCounter& dc) {
         return boost::shared_ptr<YieldTermStructure>(
                           new FlatForward(today, Handle<Quote>(forward), dc));
     }

     boost::shared_ptr<YieldTermStructure>
     flatRate(const Date& today, Rate forward, const DayCounter& dc) {
         return flatRate(
                today, boost::shared_ptr<Quote>(new SimpleQuote(forward)), dc);
     }

     boost::shared_ptr<YieldTermStructure>
     flatRate(const boost::shared_ptr<Quote>& forward,
              const DayCounter& dc) {
         return boost::shared_ptr<YieldTermStructure>(
               new FlatForward(0, NullCalendar(), Handle<Quote>(forward), dc));
     }

     boost::shared_ptr<YieldTermStructure>
     flatRate(Rate forward, const DayCounter& dc) {
         return flatRate(boost::shared_ptr<Quote>(new SimpleQuote(forward)),
                         dc);
     }

     boost::shared_ptr<BlackVolTermStructure>
     flatVol(const Date& today,
             const boost::shared_ptr<Quote>& vol,
             const DayCounter& dc) {
         return boost::shared_ptr<BlackVolTermStructure>(new
             BlackConstantVol(today, NullCalendar(), Handle<Quote>(vol), dc));
     }

     boost::shared_ptr<BlackVolTermStructure>
     flatVol(const Date& today, Volatility vol,
             const DayCounter& dc) {
         return flatVol(today,
                        boost::shared_ptr<Quote>(new SimpleQuote(vol)),
                        dc);
     }

     boost::shared_ptr<BlackVolTermStructure>
     flatVol(const boost::shared_ptr<Quote>& vol,
             const DayCounter& dc) {
         return boost::shared_ptr<BlackVolTermStructure>(new
             BlackConstantVol(0, NullCalendar(), Handle<Quote>(vol), dc));
     }

     boost::shared_ptr<BlackVolTermStructure>
     flatVol(Volatility vol,
             const DayCounter& dc) {
         return flatVol(boost::shared_ptr<Quote>(new SimpleQuote(vol)), dc);
     }

    Real relativeError(Real x1, Real x2, Real reference) {
        if (reference != 0.0)
            return std::fabs(x1-x2)/reference;
        else
            // fall back to absolute error
            return std::fabs(x1-x2);
    }

    // IndexHistoryCleaner::IndexHistoryCleaner() {}

    // IndexHistoryCleaner::~IndexHistoryCleaner() {
    //     IndexManager::instance().clearHistories();
    // }

}

#endif


#ifndef WALI_SEMELEMSET_HPP
#define WALI_SEMELEMSET_HPP

#include "wali/SemElem.hpp"

#include <set>
#include "wali/util/unordered_set.hpp"

#include <boost/function.hpp>
//#include <boost/container/flat_set.hpp>

namespace wali
{
  namespace domains
  {

    /// Represents a set of other SemElem instances. You must provide the
    /// "base" SemElem in the constructor.
    ///
    /// 0 = {}
    /// 1 = {1 of the base SemElem}
    /// + = union
    /// * = pairwise extend (i.e. {a,b} * {c, d} = {a*c, a*d, b*c, b*d})
    class SemElemSet : public SemElem
    {
    public:
      typedef boost::function<std::pair<sem_elem_t, sem_elem_t>(sem_elem_t, sem_elem_t)>
              SemElemSubsumptionComputer;

      //typedef boost::container::flat_set<sem_elem_t, SemElemRefPtrFastLessThan> ElementSet;
      //typedef std::set<sem_elem_t, SemElemRefPtrFastLessThan> ElementSet;
      typedef wali::util::unordered_set<sem_elem_t,
                                        SemElemRefPtrHash, SemElemRefPtrEqual
                                       > ElementSet;

      typedef ref_ptr<SemElemSet> Ptr;
      typedef ref_ptr<SemElemSet const> ConstPtr;

      virtual
      std::ostream &
      print_typename(std::ostream & os) const {
        // The funky way of writing the following string is to avoid possible
        // interpretation as trigraphs; these are off in GCC by default, but
        // it warns about them. Not sure what the MSVC story is.
        os << "SemElemSet<?""?""?""?""?>";
        return os;
      }

      /// Constructs a SemElemSet with the given base element, which is only
      /// used to get a handle on base_element->one(). The result is semiring
      /// 0.
      SemElemSet(SemElemSubsumptionComputer keep_what, bool include_zeroes, sem_elem_t base_element);
      SemElemSet(SemElemSubsumptionComputer keep_what, bool include_zeroes, sem_elem_t base_element, ElementSet const & elements);

      virtual sem_elem_t one() const;
      virtual sem_elem_t zero() const;

      virtual sem_elem_t extend( SemElem * se );
      virtual sem_elem_t combine( SemElem * se );

      virtual bool equal( SemElem * se ) const;
      bool equal( sem_elem_t se ) const {
        return this->equal(se.get_ptr());
      }

      virtual std::ostream& print( std::ostream & o ) const;

      ElementSet const & getElements() const;

      size_t hash() const;

    private:
      sem_elem_t base_one;
      ElementSet elements;
      SemElemSubsumptionComputer keep_what;
      size_t the_hash;
      bool include_zeroes;

    public:
      static SemElemSubsumptionComputer const KeepAllNonduplicates;
      static SemElemSubsumptionComputer const KeepMaximalElements;
      static SemElemSubsumptionComputer const KeepMinimalElements;
    };

  }
}

// Yo emacs!
// Local Variables:
//     c-file-style: "ellemtel"
//     c-basic-offset: 2
//     indent-tabs-mode: nil
// End:

#endif


//-------------------------------------------------------------------------//
//
// Copyright 2017 Sascha Kaden
//
// 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 <boost/test/unit_test.hpp>

#include <ippp/modules/collisionDetection/CollisionDetection2D.hpp>
#include <ippp/modules/distanceMetrics/InfMetric.hpp>
#include <ippp/modules/distanceMetrics/L1Metric.hpp>
#include <ippp/modules/distanceMetrics/WeightedInfMetric.hpp>
#include <ippp/modules/distanceMetrics/WeightedL1Metric.hpp>
#include <ippp/modules/distanceMetrics/WeightedL2Metric.hpp>
#include <ippp/pathPlanner/NormalRRT.hpp>
#include <ippp/pathPlanner/PRM.hpp>
#include <ippp/pathPlanner/RRTStar.hpp>
#include <ippp/environment/PointRobot.h>

using namespace ippp;

BOOST_AUTO_TEST_SUITE(constructor)

BOOST_AUTO_TEST_CASE(obstacleWorkspace) {
    const unsigned int dim = 2;
    Logging::setLogLevel(LogLevel::none);

    std::vector<std::shared_ptr<DistanceMetric<dim>>> metrics;
    metrics.push_back(std::make_shared<DistanceMetric<dim>>(DistanceMetric<dim>()));
    metrics.push_back(std::make_shared<L1Metric<dim>>(L1Metric<dim>()));
    metrics.push_back(std::make_shared<InfMetric<dim>>(InfMetric<dim>()));
    metrics.push_back(std::make_shared<WeightVecL2Metric<dim>>(WeightVecL2Metric<dim>()));
    metrics.push_back(std::make_shared<WeightVecL1Metric<dim>>(WeightVecL1Metric<dim>()));
    metrics.push_back(std::make_shared<WeightVecInfMetric<dim>>(WeightVecInfMetric<dim>()));

    std::vector<SamplerMethod> samplerMethods;
    samplerMethods.push_back(SamplerMethod::randomly);
    samplerMethods.push_back(SamplerMethod::uniform);
    samplerMethods.push_back(SamplerMethod::standardDistribution);

    std::vector<SamplingStrategy> samplingStrategies;
    samplingStrategies.push_back(SamplingStrategy::normal);
    samplingStrategies.push_back(SamplingStrategy::nearObstacles);

    // create obstacle workspace;
    Eigen::MatrixXi workspace = Eigen::MatrixXi::Constant(100, 100, 250);
    for (int i = 20; i < 50; ++i) {
        workspace(i, i) = 0;
        workspace(i + 10, 30) = 0;
    }

    Vector2 minBoundary(0.0, 0.0);
    Vector2 maxBoundary(workspace.rows(), workspace.cols());
    std::shared_ptr<PointRobot> robot(new PointRobot(minBoundary, maxBoundary));
    std::shared_ptr<ModelContainer> model(new Model2D(workspace));
    robot->setWorkspace(model);
    std::shared_ptr<CollisionDetection<2>> collision(new CollisionDetection2D(robot));
    std::shared_ptr<Planner<dim>> planner;

    Vector2 start(5, 5);
    Vector2 goal(95, 95);

    BOOST_TEST_CHECKPOINT("Options order: sampler, sampling, distanceMetric");
    for (auto metric : metrics) {
        for (auto sampler : samplerMethods) {
            for (auto sampling : samplingStrategies) {
                PRMOptions<dim> prmOptions(30, 1, collision, sampler, sampling, metric);
                PRMPlanner<dim> prmPlanner(robot, prmOptions);
                BOOST_TEST_CHECKPOINT("Calling PRM planning with (SamplerMethod | SamplingStrategy | distanceMetric) ="
                                      << sampler << " | " << sampling << " | ");
                prmPlanner.computePath(start, goal, 300, 1);

                RRTOptions<dim> rrtOptions(30, 1, collision, sampler, sampling, metric);
                RRT<dim> normalRRTPlanner(robot, rrtOptions);
                BOOST_TEST_CHECKPOINT("Calling normal RRT planning with (SamplerMethod | SamplingStrategy | distanceMetric) ="
                                      << sampler << " | " << sampling << " | ");
                normalRRTPlanner.computePath(start, goal, 300, 1);

                RRTStarPlanner<dim> starRRTPlanner(robot, rrtOptions);
                BOOST_TEST_CHECKPOINT("Calling RRT* planning with (SamplerMethod | SamplingStrategy | distanceMetric) ="
                                      << sampler << " | " << sampling << " | ");
                starRRTPlanner.computePath(start, goal, 300, 1);
            }
        }
    }
}

BOOST_AUTO_TEST_SUITE_END()


//=======================================================================
// Copyright (c) 2013 Piotr Wygocki
//
// 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 metric_to_bgl.hpp
 * @brief
 * @author Piotr Wygocki
 * @version 1.0
 * @date 2013-02-01
 */
#ifndef PAAL_METRIC_TO_BGL_HPP
#define PAAL_METRIC_TO_BGL_HPP

#define BOOST_RESULT_OF_USE_DECLTYPE

#include "paal/data_structures/bimap.hpp"
#include "paal/data_structures/metric/metric_traits.hpp"
#include "paal/data_structures/metric/metric_on_idx.hpp"
#include "paal/utils/functors.hpp"

#include <boost/graph/adjacency_matrix.hpp>
#include <boost/range/adaptor/transformed.hpp>

namespace paal {
namespace data_structures {
// TODO it would be nice to adapt Matrix + something to bgl

/**
 * @brief type of adjacency_matrix, for given metric
 *
 * @tparam Metric
 */
template <typename Metric> struct adjacency_matrix {
    using MT = data_structures::metric_traits<Metric>;
    using type = boost::adjacency_matrix<
        boost::undirectedS, boost::no_property,
        boost::property<boost::edge_weight_t, typename MT::DistanceType>>;
};

/**
 * @brief we assume that vertices is sequence
 * of values  (0, vertices.size()).
 *
 * @param m
 * @param vertices
 */
template <typename Metric, typename Vertices>
typename adjacency_matrix<Metric>::type metric_to_bgl(const Metric &m,
                                                      Vertices && vertices) {
    using Graph = typename adjacency_matrix<Metric>::type;
    const unsigned N = boost::distance(vertices);
    using MT = metric_traits<Metric>;
    using Dist = typename MT::DistanceType;
    Graph g(N);
    for (auto && v : vertices) {
        for (auto && w : vertices) {
            if (v < w) {
                bool succ = add_edge(
                    v, w, boost::property<boost::edge_weight_t, Dist>(m(v, w)),
                    g).second;
                assert(succ);
            }
        }
    }
    return g;
}

/**
 * @brief  produces graph from metric with index
 *
 * @tparam Metric
 * @tparam Vertices
 * @param m
 * @param vertices
 * @param idx
 *
 * @return
 */
template <typename Metric, typename Vertices>
typename adjacency_matrix<Metric>::type metric_to_bgl_with_index(
    const Metric &m, Vertices && vertices,
    bimap<typename boost::range_value<Vertices>::type> &idx) {
    using MT = data_structures::metric_traits<Metric>;
    using VertexType = typename MT::VertexType;
    idx = data_structures::bimap<VertexType>(vertices);
    auto idxMetric = data_structures::make_metric_on_idx(m, idx);
    auto get_idx = [&](VertexType v) { return idx.get_idx(v); };

    return metric_to_bgl(idxMetric, vertices |
                         boost::adaptors::transformed(get_idx));
}

} //!data_structures
} //!paal
#endif // PAAL_METRIC_TO_BGL_HPP


#include <mc_rtc/constants.h>
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(TestConstants)
{
  namespace cst = mc_rtc::constants;
  double deg = 180;
  double rad = cst::PI;
  BOOST_REQUIRE(cst::toRad(deg) == rad);
  BOOST_REQUIRE(cst::toDeg(rad) == deg);

  BOOST_REQUIRE(cst::GRAVITY > 0);
}


///////////////////////////////////////////////////////////////////////////////
// flatten_iterator.hpp                              				 	 	 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_ITERATOR_FLATTEN_ITERATOR_HPP_ER_2010
#define BOOST_ITERATOR_FLATTEN_ITERATOR_HPP_ER_2010
#include <stdexcept>
//#include <boost/mpl/if.hpp>
#include <boost/next_prior.hpp>
#include <boost/range.hpp>
#include <boost/type_traits.hpp>
#include <boost/iterator/iterator_traits.hpp>
#include <boost/iterator/iterator_facade.hpp>

// As per M.P.G.'s suggestion

//TODO check demote_iterator_traversal_tag in chain iterator

namespace boost{

	template<typename It>
	struct flatten_iterator_nested
    {
    	typedef typename boost::iterator_reference<It>::type ref_range_;
        typedef typename boost::remove_reference<ref_range_>::type range_;

        // Why const? That's need to be made rigorous. For now it just works
        // in cases it doesn't without
        typedef typename boost::range_iterator<
        	typename boost::add_const<range_>::type
        >::type it_;
        typedef typename boost::iterator_reference<it_>::type ref_;
        typedef typename boost::remove_cv<
        	typename boost::remove_reference<ref_>::type
        >::type val_;
        typedef typename boost::iterator_difference<it_>::type diff_;
        // TODO category_traversal
    };

	template<typename It>
	struct flatten_iterator_category_traversal{
		// TODO
    	// weaker of outer and inner iterator
        // also not allowed decremment and advance(n<0)
    };

	template<
    	class It,
        typename C = random_access_traversal_tag,
        typename R =  typename flatten_iterator_nested<It>::ref_,
        typename D = typename flatten_iterator_nested<It>::diff_
    >
	class flatten_iterator : public boost::iterator_facade<
        flatten_iterator<It,C,R,D>
      , typename flatten_iterator_nested<It>::val_
      , C
      , R
      , D
    >{
		typedef flatten_iterator_nested<It> nested_;
		typedef typename nested_::it_ nit_;

		typedef typename boost::iterator_facade<
        	flatten_iterator<It,C,R,D>
      		, typename flatten_iterator_nested<It>::val_
      		, C
      		, R
      		, D
    	> super_;

        public:

		typedef typename super_::difference_type diff_;
		typedef typename super_::reference ref_;

        flatten_iterator(){}

        explicit flatten_iterator(It b,It e):super_(),b_(b),e_(e){
        	this->update();
        }

        flatten_iterator(const flatten_iterator& that)
        :super_(that),b_(that.b_),e_(that.e_)
        {
        	if(!this->is_end()){
				this->nb_ = (that.nb_);
                this->ne_ = (that.ne_);
            }
        }

		flatten_iterator&
        operator=(const flatten_iterator& that){
        	if(&that!=this){
            	super_::operator=(that);
				this->b_ = that.b_;
                this->e_ = that.e_;
                if(!this->is_end()){
                    this->nb_ = (that.nb_);
                    this->ne_ = (that.ne_);
                }
            }
            return (*this);
        }

		protected:
        friend class boost::iterator_core_access;

        void increment(){
        	BOOST_ASSERT(this->b_!=this->e_);
            ++this->nb_;
        	if(
            	(this->nb_) == (this->ne_)
            )
        	{
            	++this->b_;
            	this->update();
        	}
		}

        void decrement(){
			throw std::runtime_error(
				"flatten_iterator::decrement() not allowed"
            );
        }

		void advance(diff_ n){
            if(n>0){
            	BOOST_ASSERT(!this->is_end());
        		diff_ k = std::distance(this->nb_,this->ne_);
				if(n<k){
            		this->nb_ = boost::next(this->nb_,n);
            	}else{
            		++this->b_;
                	this->update();
            		return this->advance(n-k);
            	}
            }else{
            	if(n!=0){
                    throw std::runtime_error(
                        "flatten_iterator::advance(n<0) not allowed"
                    );
            	}
            }
        }

    	ref_ dereference()const
    	{
        	return (*this->nb_);
    	}

		public: // temporary

    	diff_ distance_to(const flatten_iterator& other)const
    	{

        	diff_ d;
            if(this->b_ < other.b_){
            	d = dist_impl(*this,other);
            }
        	if(this->b_ == other.b_){
            	d = std::distance(this->nb_,other.nb_);
            }
            if(this->b_ > other.b_){
            	d = -dist_impl(other,*this);
            }

			return d;
    	}

    	bool equal(const flatten_iterator& rhs)const
    	{
			// Beware : assumes the traversal over the same matrix.
            // TODO check it?

        	BOOST_ASSERT(this->e_==rhs.e_);

        	if(this->b_ == rhs.b_){
            	if(this->b_ == this->e_){
                    return true;
                }else{
                	BOOST_ASSERT(this->ne_ == rhs.ne_);
                	return (this->nb_ == rhs.nb_);
                }
            }else{
				return false;
            }

    	}

        mutable It b_,e_;
        mutable nit_ nb_,ne_; // nested in *b_

		bool is_end()const{ return ((this->b_) == (this->e_)); }

    	void update()const
    	{
        	if(!this->is_end()){
            	this->nb_ = boost::begin(*this->b_);
            	this->ne_ = boost::end(*this->b_);
        	}
    	}

    	static diff_ dist_impl(
        	const flatten_iterator& x,
            const flatten_iterator& y
        ){
        	BOOST_ASSERT(x.b_ < y.b_);

            diff_ d = std::distance(x.nb_,x.ne_);
            It it = x.b_;
            ++it;
            while(it!=y.b_){
            	nit_ nb = boost::begin(*it);
            	nit_ ne = boost::end(*it);
                d += std::distance(nb,ne);
                ++it;
            }
        	BOOST_ASSERT(it == y.b_);
			if(it!=x.e_){
            	nit_ nb = boost::begin(*it);
            	d += std::distance(nb,x.nb_);
            }
			return d;
        }

	};

	template<typename It>
    flatten_iterator<It>
	make_flatten_iterator(It b,It e){
    	typedef flatten_iterator<It> result_;
    	return result_(b,e);
    }

}//boost

#endif


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

#define BOOST_TEST_MODULE TestExtents
#include <boost/test/unit_test.hpp>

#include <algorithm>
#include <vector>

#include <boost/compute/utility/dim.hpp>
#include <boost/compute/utility/extents.hpp>

#include "context_setup.hpp"

namespace compute = boost::compute;

BOOST_AUTO_TEST_CASE(initialize)
{
    compute::extents<1> one(1);
    BOOST_CHECK_EQUAL(one[0], size_t(1));

    compute::extents<3> xyz = compute::dim(1, 2, 3);
    BOOST_CHECK_EQUAL(xyz[0], size_t(1));
    BOOST_CHECK_EQUAL(xyz[1], size_t(2));
    BOOST_CHECK_EQUAL(xyz[2], size_t(3));
}

BOOST_AUTO_TEST_CASE(size)
{
    BOOST_CHECK_EQUAL(compute::extents<1>().size(), size_t(1));
    BOOST_CHECK_EQUAL(compute::extents<2>().size(), size_t(2));
    BOOST_CHECK_EQUAL(compute::extents<3>().size(), size_t(3));
}

BOOST_AUTO_TEST_CASE(subscript_operator)
{
    compute::extents<3> xyz;
    BOOST_CHECK_EQUAL(xyz[0], size_t(0));
    BOOST_CHECK_EQUAL(xyz[1], size_t(0));
    BOOST_CHECK_EQUAL(xyz[2], size_t(0));

    xyz[0] = 10;
    xyz[1] = 20;
    xyz[2] = 30;
    BOOST_CHECK_EQUAL(xyz[0], size_t(10));
    BOOST_CHECK_EQUAL(xyz[1], size_t(20));
    BOOST_CHECK_EQUAL(xyz[2], size_t(30));
}

BOOST_AUTO_TEST_CASE(data)
{
    compute::extents<3> xyz = compute::dim(5, 6, 7);
    BOOST_CHECK_EQUAL(xyz.data()[0], size_t(5));
    BOOST_CHECK_EQUAL(xyz.data()[1], size_t(6));
    BOOST_CHECK_EQUAL(xyz.data()[2], size_t(7));
}

BOOST_AUTO_TEST_CASE(linear)
{
    compute::extents<2> uv = compute::dim(16, 16);
    BOOST_CHECK_EQUAL(uv.linear(), size_t(256));
}

BOOST_AUTO_TEST_CASE(equality_operator)
{
    BOOST_CHECK(compute::dim(10, 20) == compute::dim(10, 20));
    BOOST_CHECK(compute::dim(20, 10) != compute::dim(10, 20));
}

BOOST_AUTO_TEST_CASE(empty_dim)
{
    BOOST_CHECK(compute::dim<0>() == compute::dim());
    BOOST_CHECK(compute::dim<1>() == compute::dim(0));
    BOOST_CHECK(compute::dim<2>() == compute::dim(0, 0));
    BOOST_CHECK(compute::dim<3>() == compute::dim(0, 0, 0));
}

BOOST_AUTO_TEST_CASE(copy_to_vector)
{
    compute::extents<3> exts = compute::dim(4, 5, 6);

    std::vector<size_t> vec(3);
    std::copy(exts.begin(), exts.end(), vec.begin());
    BOOST_CHECK_EQUAL(vec[0], 4);
    BOOST_CHECK_EQUAL(vec[1], 5);
    BOOST_CHECK_EQUAL(vec[2], 6);
}

BOOST_AUTO_TEST_SUITE_END()