/**************************************************************************\
|
|    Copyright (C) 2009 Marc Stevens
|
|    This program is free software: you can redistribute it and/or modify
|    it under the terms of the GNU General Public License as published by
|    the Free Software Foundation, either version 3 of the License, or
|    (at your option) any later version.
|
|    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
|    GNU General Public License for more details.
|
|    You should have received a copy of the GNU General Public License
|    along with this program.  If not, see <http://www.gnu.org/licenses/>.
|
\**************************************************************************/

#ifndef HASHCLASH_SIGNED_DIGIT_REPRESENTATION_HPP
#define HASHCLASH_SIGNED_DIGIT_REPRESENTATION_HPP

#include <vector>
#include <ostream>
#include <functional>

#ifndef NOSERIALIZATION
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>
#endif // NOSERIALIZATION

#include "types.hpp"

namespace hashclash {

	// A class that holds a binary Signed Digit Representation (SDR) (modulo 2^32):
	class sdr;
	// calculate hamming weight:
	inline unsigned hw(uint32 n);
//	inline unsigned hw(unsigned n) { return hw(uint32(n)); }
	inline unsigned hw(int n) { return hw(uint32(n)); }
	inline unsigned hw(uint64 n) {
		return hw(uint32(n>>32)) + hw(uint32(n));
	}
	// calculate hamming weight of the NAF of n - is the lowest hamming weight among all SDR's:
	inline unsigned hwnaf(uint32 n);
	// calculate the NAF of n:
	sdr naf(uint32 n);

	// when rotating a difference, 4 possible differences exist after rotation. Use this function to find them:
	// returns possible differences with probability >= than minprob/2^32
	void rotate_difference(uint32 diff, int rc, std::vector<uint32>& rotateddiff, uint32 minprob = uint32(1)<<30);
	void rotate_difference(uint32 diff, int rc, std::vector<std::pair<uint32,double> >& rotateddiff);
	uint32 best_rotated_difference(uint32 diff, int rc);

	class sdr {
	public:
		/**** constructors ****/
		sdr(): mask(0), sign(0) {}
		sdr(uint32 n): mask(n), sign(n) {}
		sdr(uint32 n1, uint32 n2): mask(n1^n2), sign(n2&~n1) {}
		sdr(const sdr& r): mask(r.mask), sign(r.sign) {}

		/**** assign functions/operators ****/
		sdr& set(const sdr& r)
		{
			mask = r.mask;
			sign = r.sign;
			return *this;
		}
		sdr& set(uint32 n)
		{
			mask = sign = n;
			return *this;
		}
		sdr& set(uint32 n1, uint32 n2)
		{
			mask = n1^n2;
			sign = n2 & ~n1;
			return *this;
		}
		sdr& operator= (const sdr& r)
		{ return set(r); }
		sdr& operator= (const uint32 n)
		{ return set(n); }
		sdr& clear()
		{ return set(0); }

		/**** compare operators ****/
		bool operator== (const sdr& r) const
		{ return mask == r.mask && sign == r.sign; }
		bool operator< (const sdr& r) const
		{ return mask < r.mask || (mask == r.mask && sign < r.sign); }
		bool operator!= (const sdr& r) const
		{ return !(*this == r); }
		bool operator> (const sdr& r) const
		{ return r < *this; }
		bool operator<= (const sdr& r) const
		{ return !(*this > r); }
		bool operator>= (const sdr& r) const
		{ return !(*this < r); }

		/**** arithmetic operators ****/
		sdr operator-() const
		{
			sdr tmp(*this);
			tmp.sign ^= tmp.mask;
			return tmp;
		}

		sdr& operator+= (const sdr& r)
		{
			uint32 set1 = set1conditions() + r.set1conditions();
			return set(set1, set1 + adddiff() + r.adddiff());
		}
		sdr operator+ (const sdr& r) const
		{ return sdr(*this) += r; }

		sdr& operator-= (const sdr& r)
		{
			uint32 set1 = set1conditions() + r.sign;
			return set(set1, set1 + adddiff() - r.adddiff());
		}
		sdr operator- (const sdr& r) const
		{ return sdr(*this) -= r; }

		sdr& operator^= (const sdr& r)
		{
			mask ^= r.mask;
			sign = mask & ~(sign^r.sign);
			return *this;
		}
		sdr operator^ (const sdr& r) const
		{ return sdr(*this) ^= r; }

		sdr& operator<<= (unsigned n)
		{
			mask <<= n;
			sign <<= n;
			return *this;
		}
		sdr operator<< (unsigned n) const
		{ return sdr(*this) <<= n; }

		sdr& operator>>= (unsigned n)
		{
			mask >>= n;
			sign >>= n;
			return *this;
		}
		sdr operator>> (unsigned n) const
		{ return sdr(*this) >>= n; }

		sdr rotate_left(unsigned n) const
		{
			sdr tmp(*this);
			tmp.mask = hashclash::rotate_left(mask, n);
			tmp.sign = hashclash::rotate_left(sign, n);
			return tmp;
		}
		sdr rotate_right(unsigned n) const
		{
			sdr tmp(*this);
			tmp.mask = hashclash::rotate_right(mask, n);
			tmp.sign = hashclash::rotate_right(sign, n);
			return tmp;
		}

		/**** other functions ****/
		uint32 adddiff() const
		{ return sign - (sign^mask); }
		uint32 xordiff() const
		{ return mask; }
		uint32 set0conditions() const
		{ return ~sign; }
		uint32 set1conditions() const
		{ return sign^mask; }

		int get(unsigned b) const
		{
			return int((sign>>b)&1) - int(((sign^mask)>>b)&1);
		}
		int operator[] (unsigned b) const
		{ return get(b); }

		unsigned hw() const
		{ return hashclash::hw(mask); }
		unsigned hwnaf() const
		{ return hashclash::hwnaf(adddiff()); }
		sdr naf() const
		{ return hashclash::naf(adddiff()); }

		/**** members ****/
		uint32 mask, sign;
	};

	inline void swap(sdr& l, sdr& r)
	{
		std::swap(l.mask, r.mask);
		std::swap(l.sign, r.sign);
	}

	std::ostream& operator<<(std::ostream& o, const sdr& n);
	std::istream& operator>>(std::istream& i, sdr& n);

	extern unsigned hw_table[0x800];
	inline unsigned hw(uint32 n)
	{
		unsigned w = hw_table[n & 0x7FF];
		w += hw_table[(n >> 11) & 0x7FF];
		w += hw_table[n >> 22];
		return w;
	}

	inline unsigned hwnaf(uint32 n)
	{
		uint32 a = n>>1;
		uint32 w = a ^ (n+a);
		return hw(w);
	}

	inline sdr naf(uint32 n)
	{
		uint32 a = n>>1;
		return sdr(a,a+n);
	}

	inline unsigned hw(const sdr& n)
	{ return n.hw(); }
	inline unsigned hwnaf(const sdr& n)
	{ return n.hwnaf(); }
	inline sdr naf(const sdr& n)
	{ return n.naf(); }

	unsigned count_sdrs(uint32 n, unsigned maxweight = 32);
	void table_sdrs(std::vector<sdr>& result, uint32 n, unsigned maxweight);

	unsigned count_sdrs(uint32 n, unsigned weight, bool signpos);
	void table_sdrs(std::vector<sdr>& result, uint32 n, unsigned weight, bool signpos);

	unsigned count_sdrs(sdr n, unsigned maxweight, unsigned rot);
	void table_sdrs(std::vector<sdr>& result, sdr n, unsigned maxweight, unsigned rot);

	// call this in a other global constructor using hw or hwnaf
	// due to the inpredictable order of which global constructors are called
	// otherwise the correct functioning of hw and hwnaf are not guaranteed
	void hashclash_sdr_hpp_init();

} // namespace hashclash

#ifndef NOSERIALIZATION
namespace boost {
	namespace serialization {

		template<class Archive>
		void serialize(Archive& ar, hashclash::sdr& d, const unsigned int file_version)
		{
			ar & make_nvp("mask", d.mask);
			ar & make_nvp("sign", d.sign);
		}

	}
}
#endif // NOSERIALIZATION

#endif // HASHCLASH_SIGNED_DIGIT_REPRESENTATION_HPP


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

#ifndef MTL_MATRIX_POISSON2D_DIRICHLET_INCLUDE
#define MTL_MATRIX_POISSON2D_DIRICHLET_INCLUDE

#include <boost/numeric/mtl/concept/collection.hpp>
#include <boost/numeric/mtl/utility/ashape.hpp>
#include <boost/numeric/mtl/utility/exception.hpp>
#include <boost/numeric/mtl/vector/mat_cvec_multiplier.hpp>

namespace mtl { namespace mat {

/// Matrix-free linear operator for a Poisson equation on a rectangular domain of \p m by \p n with Dirichlet boundary conditions
struct poisson2D_dirichlet
{
    /// Constructor
    poisson2D_dirichlet(int m, int n) : m(m), n(n), s(m * n) {}

    /// Member function that realizes the multiplication
    template <typename VectorIn, typename VectorOut, typename Assign>
    void mult(const VectorIn& v, VectorOut& w, Assign) const
    {
	MTL_DEBUG_THROW_IF(int(size(v)) != s, incompatible_size());
	MTL_DEBUG_THROW_IF(size(v) != size(w), incompatible_size());

	const int nb = n < 3 ? 1 : (n - 2) / 4 * 4 + 1;

	// Inner domain
	for (int i= 1; i < m-1; i++) {
	    int kmax= i * n + nb;
	    for (int k= i * n + 1; k < kmax; k+= 4) {
		typename Collection<VectorIn>::value_type const v0= v[k], v1= v[k+1], v2= v[k+2], v3= v[k+3];
		Assign::apply(w[k], 4 * v0 - v[k-n] - v[k+n] - v[k-1] - v1);
		Assign::apply(w[k+1], 4 * v1 - v[k-n+1] - v[k+n+1] - v0 - v2);
		Assign::apply(w[k+2], 4 * v2 - v[k-n+2] - v[k+n+2] - v1 - v3);
		Assign::apply(w[k+3], 4 * v3 - v[k-n+3] - v[k+n+3] - v2 - v[k+4]);
	    }
	    for (int j= nb, k= i * n + j; j < n-1; j++, k++)
		Assign::apply(w[k], 4 * v[k] - v[k-n] - v[k+n] - v[k-1] - v[k+1]);
	}

	// Upper border
	for (int j= 1; j < n-1; j++)
	    Assign::apply(w[j], 4 * v[j] - v[j+n] - v[j-1] - v[j+1]);

	// Lower border
	for (int j= 1, k= (m-1) * n + j; j < n-1; j++, k++)
	    Assign::apply(w[k], 4 * v[k] - v[k-n] - v[k-1] - v[k+1]);

	// Left border
	for (int i= 1, k= n; i < m-1; i++, k+= n)
	    Assign::apply(w[k], 4 * v[k] - v[k-n] - v[k+n] - v[k+1]);

	// Right border
	for (int i= 1, k= n+n-1; i < m-1; i++, k+= n)
	    Assign::apply(w[k], 4 * v[k] - v[k-n] - v[k+n] - v[k-1]);

	// Corners
	Assign::apply(w[0], 4 * v[0] - v[1] - v[n]);
	Assign::apply(w[n-1], 4 * v[n-1] - v[n-2] - v[2*n - 1]);
	Assign::apply(w[(m-1)*n], 4 * v[(m-1)*n] - v[(m-2)*n] - v[(m-1)*n+1]);
	Assign::apply(w[m*n-1], 4 * v[m*n-1] - v[m*n-2] - v[m*n-n-1]);
    }

    /// Multiplication is procastinated until we know where the product goes
    template <typename VectorIn>
    vec::mat_cvec_multiplier<poisson2D_dirichlet, VectorIn> operator*(const VectorIn& v) const
    {	return vec::mat_cvec_multiplier<poisson2D_dirichlet, VectorIn>(*this, v);    }

    int m, n, s;
};

inline std::size_t size(const poisson2D_dirichlet& A) { return A.s * A.s; } ///< Matrix size
inline std::size_t num_rows(const poisson2D_dirichlet& A) { return A.s; } ///< Number of rows
inline std::size_t num_cols(const poisson2D_dirichlet& A) { return A.s; } ///< Number of columns

}} // namespace mtl::matrix

namespace mtl {

    template <>
    struct Collection<mat::poisson2D_dirichlet>
    {
	typedef double value_type;
	typedef int    size_type;
    };

    namespace ashape {
	template <> struct ashape_aux<mtl::mat::poisson2D_dirichlet>
	{	typedef nonscal type;    };
    }
}

#endif // MTL_MATRIX_POISSON2D_DIRICHLET_INCLUDE


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

#ifndef MPO19_UTILITY_20062020
#define MPO19_UTILITY_20062020

#include <gmp.h>

extern "C" {
#include <paillier.h>
}

#include <boost/hana.hpp>

#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>

#include <cinttypes>
#include <vector>
#include <limits>
#include <cassert>
#include <memory>
#include  <experimental/propagate_const>

#define FOR_EACH_VARIADIC(expr)  using expander = int[]; expander{ ( (void) (expr), 0 )... }

template<typename T, typename U>
inline void nothrow_swap(T& t, U& u) noexcept
{
    using std::swap;
    static_assert(noexcept(swap(t, u))
                  , "swap may throw an exception");
    swap(t, u);
}

constexpr size_t words_to_hold_bitlen(size_t bits, size_t bits_in_word)
{
    assert(0 != bits_in_word);
    return bits/bits_in_word + (bits % bits_in_word == 0 ? 0 : 1);
}

constexpr size_t bytes_to_hold_bitlen(size_t bits)
{
    return words_to_hold_bitlen(bits, CHAR_BIT);
}

namespace detail
{

constexpr uint64_t integer_log2(uint64_t x, bool ceil)
{
    if(x == 0) return 0;

    constexpr size_t num_bits = sizeof(decltype(x)) * CHAR_BIT;
    uint64_t a = x;
    uint64_t res = 0;
    for(size_t i = num_bits/2; i != 0; i /=2) {
        //If right-shifting a results in a number not equal zero, then a
        //has between i and prev(i) bits, where prev(i) is defined as:
        //prev(0) -> num_bits
        //prev(i) -> value of i in previous iteration
        uint64_t tmp = a >> i;
        if(tmp != 0) {
            //We set a to the right shifted value
            a = tmp;
            //We add i to res, as a has at least res + i bits
            res = res + i;
        }
        //else a has between res and i bits, so we do nothing here
    }
    assert(a == 1);
    //res now contains ⌊log2(x)⌋
    assert( (1u << res) <= x && x < (1u << (res + 1)) );
    if(ceil && (1u << res) < x)
        return res + 1;
    else
        return res;
}

} //detail

//Calculates ceil_log2(a) = ⌈log2(a)⌉, for a ≠ 0
//and ceil_log2(a) = 1, for a = 0
constexpr uint64_t ceil_log2(uint64_t a)
{
    return detail::integer_log2(a, true);
}

//Calculates ceil_log2(a) = ⌊log2(a)⌋, for a ≠ 0
//and floor_log2(a) = 1, for a = 0
constexpr uint64_t floor_log2(uint64_t a)
{
    return detail::integer_log2(a, false);
}

constexpr unsigned char clear_msb = 0x7F;
constexpr unsigned char select_msb = 0x80;

template<typename CharT>
size_t encode_len(CharT* buf, size_t len)
{
    size_t i = 0;
    for(; len != 0; ++i) {
        unsigned char c =  static_cast<unsigned char>(len & clear_msb);
        len >>= CHAR_BIT - 1;
        if(len != 0) c |= select_msb;
        buf[i] = static_cast<CharT>(c);
    }
    assert(len == 0);
    return i;
}

template<typename Reader>
size_t decode_len(Reader&& reader)
{
    size_t res = 0;
    //End condition evaluated in body
    for(int i = 0;; i += (CHAR_BIT - 1) ) {
        assert(res >> i == 0);
        unsigned char c = 0;
        reader(&c, sizeof(c));
        res |= (c & clear_msb) << i;
        if((c & select_msb) == 0) break;
    }
    return res;
}

namespace detail
{
constexpr size_t calculate_encode_len_buffer_size()
{
    size_t res = (sizeof(size_t) * CHAR_BIT) / (CHAR_BIT - 1);
    if(res * (CHAR_BIT - 1) < sizeof(size_t) * CHAR_BIT) res += 1;
    return res;
}
}

constexpr size_t encode_len_buffer_size =
    detail::calculate_encode_len_buffer_size();

//Check if T is a range. A range is defined as any instance t of type T,
//for which both, boost::begin(t) and boost::end(t) form valid expressions
template<typename T>
constexpr bool is_range()
{
    using boost::hana::is_valid;
    //Function to check if std::begin(r) forms a valid expression
    auto test_begin = [](auto const& r) -> decltype((void) std::begin(r)) {};
    //Function to check if std::end(r) forms a valid expression
    auto test_end = [](auto const& r) -> decltype((void) std::end(r)) {};
    //We use decltype so that std::declval<T>() is in unevaluated context
    //The return type is either hana::true_ or hana::false_, which is implicitly
    //convertible to bool, holding the expected boolean value
    using bool_t = decltype(is_valid(test_begin, std::declval<T>())
                            && is_valid(test_end, std::declval<T>()));
    return bool_t{};
}

template<typename T1, typename T2, typename... Ts>
constexpr bool is_range()
{
    return boost::hana::and_(is_range<T1>(), is_range<T2>(), is_range<Ts>()...);
}

//Check if T serializable. An instance t of type T is serializable if, the expression
//os << t is valid, with os being an instance derived from std::ostream
template<typename T>
constexpr bool is_serializable()
{
    using boost::hana::is_valid;
    auto test_serialize = [](auto&& r) -> decltype((void) (std::declval<std::ostream>() << r)) {};
    using bool_t = decltype(is_valid(test_serialize, std::declval<T>()));
    return  bool_t{};
}

//Check if T deserializable. An instance t of type T is deserializable if, the expression
//is >> t is valid, with is being an instance derived from std::istream
template<typename T>
constexpr bool is_deserializable()
{
    using boost::hana::is_valid;
    auto test_deserialize = [](auto&& r) -> decltype((void) (std::declval<std::istream>() >> r)) {
        static_assert(std::is_same<decltype(r), T&>::value, "");
    };
    using bool_t = decltype(is_valid(test_deserialize, std::declval<T>()));
    return  bool_t{};
}

//Preprocessor will generate the following set of functions:
//template <typename Range0, ..., typename RangeN-1, typename Op>
//void multi_for_each(Range0&& range0, ..., RangeN-1&& rangeN-1, Op const& op);
//
//Applies N-Ary operation to every element in the input ranges, which are iterated in
//a parallel fashion, i.e. in iteration i, the following holds:
//op(range0[i], ..., rangeN-1[i]).
//Furthermore if the input ranges have multiple dimension, the ranges are iterated,
//as if they were flattened before.
//All ranges must be of equal size and equal dimension. In case of different sizes the
//behavior is unspecified and in case of different dimensions compilation fails.

//Using variadic templates to generate above functions, would yield more complex code

#define MPO19_INIT_ITERATORS(z, n, unused) \
    auto it ## n = std::begin(range ## n);

#define MPO19_DEREF_ITERATORS(z, n, unused) \
    auto&&  r ## n = *it ## n;

#define MPO19_MULTI_FOR_EACH(z, n, unused) \
    template <BOOST_PP_ENUM_PARAMS(n, typename Range), typename Op, \
              std::enable_if_t<is_range<BOOST_PP_ENUM_PARAMS(n, Range)>(), int> = 0> \
    void multi_for_each(BOOST_PP_ENUM_BINARY_PARAMS(n, Range, && range), Op&& op) \
    { \
        /*Initialize it0, ..., itN-1 with begin of range0, ..., rangeN-1*/ \
        BOOST_PP_REPEAT(n, MPO19_INIT_ITERATORS, unused) \
        \
        auto end0 = std::end(range0); \
        for(; it0 != end0; BOOST_PP_ENUM_PARAMS(n, ++it)/* == ++it0, ..., ++itN-1*/) \
        { \
            /*Dereference iterators and hold an rvalue reference, to prevent pass by value later on*/ \
            BOOST_PP_REPEAT(n, MPO19_DEREF_ITERATORS, unused) \
            \
            /*Recurse as the dereferenced values might be ranges themselves */ \
            multi_for_each(BOOST_PP_ENUM_PARAMS(n, r), op); \
            \
        } \
    } \
    /*Passed a non-range, so we simply apply op on every argument*/ \
    template <BOOST_PP_ENUM_PARAMS(n, typename T), typename Op, \
              std::enable_if_t<!is_range<BOOST_PP_ENUM_PARAMS(n, T)>(), int> = 0> \
    void multi_for_each(BOOST_PP_ENUM_BINARY_PARAMS(n, T, && t), Op&& op) \
    { \
        op(BOOST_PP_ENUM_PARAMS(n, t)); \
    }

BOOST_PP_REPEAT_FROM_TO(1, 4, MPO19_MULTI_FOR_EACH, ~)

#undef MPO19_INIT_ITERATORS
#undef MPO19_DEREF_ITERATORS
#undef MPO19_MULTI_FOR_EACH

namespace detail
{

template<typename T>
struct always_void {
    using type = void;
};

template<typename T>
using always_void_t = typename always_void<T>::type;

} //detail

template<typename T, typename = void>
struct is_invocable : std::false_type {};

template<typename F, typename... Args>
struct is_invocable<F(Args...)
, detail::always_void_t<decltype(
std::declval<F>()(std::declval<Args>()...))>> : std::true_type {};

constexpr struct identity_t {
    template<typename T>
    inline T operator()(T&& t) const
    {
        return t;
    }
} identity;

namespace detail
{

template<typename F, typename Arg,
         std::enable_if_t<
             is_invocable<F(Arg&&)>::value &&
             !is_invocable<F(Arg&&, size_t)>::value, int> = 0>
auto invoke_maybe_with_idx(F&& f, Arg&& arg, size_t)
{
    return f(std::forward<Arg>(arg));
}

template<typename F, typename Arg,
         std::enable_if_t<is_invocable<F(Arg&&, size_t)>::value, int> = 0>
auto invoke_maybe_with_idx(F&& f, Arg&& arg, size_t s)
{
    return f(std::forward<Arg>(arg), s);
}

template<typename RandomIt, typename BinaryOperation, typename LeafTransformation>
auto tree_accumulate_impl(RandomIt first
                          , RandomIt last
                          , BinaryOperation&& op
                          , LeafTransformation&& lt
                          , RandomIt const& base)
{
    if(std::distance(first, last) == 1)
        return invoke_maybe_with_idx(lt, *first, size_t(std::distance(base, first)));
    else
        return op(
                   tree_accumulate_impl(first, first + std::distance(first, last)/2, op, lt, base),
                   tree_accumulate_impl(first + std::distance(first, last)/2, last, op, lt, base)
               );
}

} //detail

template<typename RandomIt, typename BinaryOperation, typename LeafTransformation = decltype(identity)&>
auto tree_accumulate(RandomIt first, RandomIt last, BinaryOperation&& op, LeafTransformation&& lt = identity)
{
    return detail::tree_accumulate_impl(first, last, op, lt, first);
}

template<typename RandomAccessRange, typename BinaryOperation, typename LeafTransformation = decltype(identity)&>
auto tree_accumulate(RandomAccessRange&& range, BinaryOperation&& op, LeafTransformation&& lt = identity)
{
    return tree_accumulate(range.begin(), range.end(), op, lt);
}

#endif //MPO19_UTILITY_20062020


//=======================================================================
// 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 knapsack_greedy.hpp
 * @brief
 * @author Piotr Wygocki
 * @version 1.0
 * @date 2013-10-07
 */

#ifndef PAAL_KNAPSACK_GREEDY_HPP
#define PAAL_KNAPSACK_GREEDY_HPP

#include "paal/utils/accumulate_functors.hpp"
#include "paal/utils/knapsack_utils.hpp"

#include <boost/iterator/counting_iterator.hpp>
#include <boost/range/algorithm/remove_if.hpp>

namespace paal {
namespace detail {

// if the knapsack dynamic table is indexed by values,
// the procedure to find the best element is to find the biggest index i in the
// table that
// *i is smaller than given threshold(capacity)
template <typename MaxValueType, typename SizeType>
struct get_max_element_on_value_indexed_collection {
    get_max_element_on_value_indexed_collection(MaxValueType maxValue)
        : m_max_value(maxValue) {}

    template <typename Iterator, typename Comparator>
    Iterator operator()(Iterator begin, Iterator end, Comparator compare) {
        auto compareOpt = make_less_pointees_t(compare);
        // traverse in reverse order, skip the first
        for (auto iter = end - 1; iter != begin; --iter) {
            if (*iter && compareOpt(m_max_value, *iter)) {
                return iter;
            }
        }

        return end;
    }

  private:
    MaxValueType m_max_value;
};

// if the knapsack dynamic table is indexed by sizes,
// the procedure to find the best element is to find the biggest
// index i in the table that maximizes *i
template <typename ValueType>
struct get_max_element_on_capacity_indexed_collection {
    template <typename Iterator, typename Comparator>
    Iterator operator()(Iterator begin, Iterator end, Comparator compare) {
        return std::max_element(begin, end, make_less_pointees_t(compare));
    }
};

template <typename KnapsackData,
          typename Is_0_1_Tag,
          typename Value = typename KnapsackData::value,
          typename Size  = typename KnapsackData::size>
typename KnapsackData::return_type knapsack_general_two_app(
    KnapsackData knapsack_data, Is_0_1_Tag is_0_1_Tag) {

    using ObjectRef = typename KnapsackData::object_ref;

    static_assert(std::is_arithmetic<Value>::value &&
                      std::is_arithmetic<Size>::value,
                  "Size type and Value type must be arithmetic types");
    auto capacity = knapsack_data.get_capacity();

    auto bad_size = [=](ObjectRef o){return knapsack_data.get_size(o) > capacity;};

    auto objects = boost::remove_if<boost::return_begin_found>(knapsack_data.get_objects(), bad_size);

    if (boost::empty(objects)) {
        return std::pair<Value, Size>();
    }

    // finding the element with the greatest density
    auto greedyFill = get_greedy_fill(
            make_knapsack_data(
                objects, capacity,
                knapsack_data.get_size(),
                knapsack_data.get_value(),
                knapsack_data.get_output_iter()), is_0_1_Tag);

    // finding the biggest set elements with the greatest density
    // this is actually small optimization compare to original algorithm
    // note that largest is transformed iterator!
    auto largest = max_element_functor(objects, knapsack_data.get_value());

    if (*largest > std::get<0>(greedyFill)) {
        knapsack_data.out(*largest.base());
        return std::make_pair(*largest, knapsack_data.get_size(*largest.base()));
    } else {
        greedy_to_output(std::get<2>(greedyFill), knapsack_data.get_output_iter(), is_0_1_Tag);
        return std::make_pair(std::get<0>(greedyFill), std::get<1>(greedyFill));
    }
}

template <typename Range>
struct is_range_const {
    using ref = typename boost::range_reference<Range>::type;
    static const bool value = std::is_const<ref>::value ||
                     !std::is_reference<ref>::value;
};
} //! detail
} //! paal
#endif // PAAL_KNAPSACK_GREEDY_HPP


#ifndef TEST_UNIT_MATH_PRIM_MAT_VECTORIZE_EXPECT_PRIM_MATRIX_VALUE_HPP
#define TEST_UNIT_MATH_PRIM_MAT_VECTORIZE_EXPECT_PRIM_MATRIX_VALUE_HPP

#include <test/unit/math/prim/mat/vectorize/expect_val_eq.hpp>
#include <vector>
#include <Eigen/Dense>

template <typename F>
void expect_prim_matrix_value() {
  using Eigen::MatrixXd;
  using std::vector;

  vector<double> valid_inputs = F::valid_inputs();
  MatrixXd a(valid_inputs.size(), 3);

  for (int i = 0; i < a.size(); i++) {
    a(i) =  valid_inputs[(i % valid_inputs.size())];
  }

  MatrixXd fa = F::template apply<MatrixXd>(a);
  EXPECT_EQ(a.size(), fa.size());
  for (int i = 0; i < fa.size(); ++i) {
    expect_val_eq(F::apply_base(a(i)), fa(i));
  }

  vector<MatrixXd> d;
  d.push_back(a);
  d.push_back(a);
  vector<MatrixXd> fd = F::template apply<vector<MatrixXd> >(d);
  EXPECT_EQ(d.size(), fd.size());
  for (size_t i = 0; i < fd.size(); ++i) {
    EXPECT_EQ(d[i].rows(), fd[i].rows());
    EXPECT_EQ(d[i].cols(), fd[i].cols());
    EXPECT_EQ(d[i].size(), fd[i].size());
    for (int j = 0; j < fd[i].size(); ++j) {
      expect_val_eq(F::apply_base(d[i](j)), fd[i](j));
    }
  }

  MatrixXd fab = F::template apply<MatrixXd>(a.block(1, 1, 1, 1));
  expect_val_eq(F::apply_base(a(1,1)), fab(0,0));
}

#endif


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

#include <boost/hana/assert.hpp>
#include <boost/hana/functor.hpp>
#include <boost/hana/integral_constant.hpp>
#include <boost/hana/tuple.hpp>
using namespace boost::hana;

int main() {

{

//! [value]
auto i = integral_constant<int, 3>; // notice no constexpr
static_assert(value<decltype(i)>() == 3, "");
static_assert(value(i) == 3, "value(i) is always a constant expression!");
//! [value]

}{

//! [value_of]
constexpr auto xs = tuple_c<int, 1, 2, 3, 4, 5>;
constexpr auto vs = transform(xs, value_of);
static_assert(vs == make<Tuple>(1, 2, 3, 4, 5), "");
//! [value_of]

}

}


/*******************************************************************************
 * Copyright 2013-2014 Sebastian Niemann <niemann@sra.uni-hannover.de>.
 *
 * Licensed under the MIT License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/MIT
 *
 * Developers:
 *   Sebastian Niemann - Lead developer
 *   Daniel Kiechle - Unit testing
 ******************************************************************************/
#include <Expected.hpp>
using armadilloJava::Expected;

#include <iostream>
using std::cout;
using std::endl;

#include <algorithm>
using std::min;

#include <utility>
using std::pair;

#include <armadillo>
using arma::Col;
using arma::vec;

#include <InputClass.hpp>
using armadilloJava::InputClass;

#include <Input.hpp>
using armadilloJava::Input;

namespace armadilloJava {
  class ExpectedInPlaceGenColVecGenColVec : public Expected {
    public:
      ExpectedInPlaceGenColVecGenColVec() {
        cout << "Compute ExpectedInPlaceGenColVecGenColVec(): " << endl;

          vector<vector<pair<string, void*>>> inputs = Input::getTestParameters({
            InputClass::GenColVec,
            InputClass::GenColVec
          });

          for (vector<pair<string, void*>> input : inputs) {
            _fileSuffix = "";

            int n = 0;
            for (pair<string, void*> value : input) {
              switch (n) {
                case 0:
                  _fileSuffix += value.first;
                  _genColVecA = *static_cast<Col<double>*>(value.second);
                  break;
                case 1:
                  _fileSuffix += "," + value.first;
                  _genColVecB = *static_cast<Col<double>*>(value.second);
                  break;
              }
              ++n;
            }

            cout << "Using input: " << _fileSuffix << endl;

            _copyOfGenColVecA = _genColVecA;
            _copyOfGenColVecB = _genColVecB;

            expectedColInPlaceEqual();

            _genColVecA = _copyOfGenColVecA;
            _genColVecB = _copyOfGenColVecB;
            expectedColInPlacePlus();

            _genColVecA = _copyOfGenColVecA;
            _genColVecB = _copyOfGenColVecB;
            expectedColInPlaceMinus();

            _genColVecA = _copyOfGenColVecA;
            _genColVecB = _copyOfGenColVecB;
            expectedColInPlaceTimes();

            _genColVecA = _copyOfGenColVecA;
            _genColVecB = _copyOfGenColVecB;
            expectedColInPlaceElemTimes();

            _genColVecA = _copyOfGenColVecA;
            _genColVecB = _copyOfGenColVecB;
            expectedColInPlaceElemDivide();

            _genColVecA = _copyOfGenColVecA;
            _genColVecB = _copyOfGenColVecB;
            expectedColSwap();

            _genColVecA = _copyOfGenColVecA;
            _genColVecB = _copyOfGenColVecB;
            expectedColCopy_size();

            _genColVecA = _copyOfGenColVecA;
            _genColVecB = _copyOfGenColVecB;
            expectedColCol();
          }

          cout << "done." << endl;
        }

    protected:
      Col<double> _genColVecA;
      Col<double> _copyOfGenColVecA;

      Col<double> _genColVecB;
      Col<double> _copyOfGenColVecB;

      void expectedColInPlaceEqual() {
    	if(_genColVecA.n_rows != _genColVecB.n_rows || _genColVecA.n_cols != _genColVecB.n_cols) {
          return;
        }
        cout << "- Compute expectedColInPlaceEqual() ... ";

        _genColVecA = _genColVecB;
        save<double>("Col.inPlaceEqual", _genColVecA);

        cout << "done." << endl;
      }

      void expectedColInPlacePlus() {
        if(_genColVecA.n_rows != _genColVecB.n_rows || _genColVecA.n_cols != _genColVecB.n_cols) {
          return;
        }

        cout << "- Compute expectedColInPlace() ... ";

        _genColVecA += _genColVecB;
        save<double>("Col.inPlacePlus", _genColVecA);

        cout << "done." << endl;
      }

      void expectedColInPlaceMinus() {
        if(_genColVecA.n_rows != _genColVecB.n_rows || _genColVecA.n_cols != _genColVecB.n_cols) {
          return;
        }

        cout << "- Compute expectedColInPlaceMinus() ... ";

        _genColVecA -= _genColVecB;
        save<double>("Col.inPlaceMinus", _genColVecA);

        cout << "done." << endl;
      }

      void expectedColInPlaceTimes() {
        if(_genColVecA.n_cols != _genColVecB.n_rows) {
          return;
        }
        if(_genColVecA.n_cols != _genColVecB.n_cols) {
          return;
        }

        cout << "- Compute expectedColInPlaceTimes() ... ";

        _genColVecA *= _genColVecB;
        save<double>("Col.inPlaceTimes", _genColVecA);

        cout << "done." << endl;
      }

      void expectedColInPlaceElemTimes() {
        if(_genColVecA.n_rows != _genColVecB.n_rows || _genColVecA.n_cols != _genColVecB.n_cols) {
          return;
        }

        cout << "- Compute expectedColInPlaceElemTimes() ... ";

        _genColVecA %= _genColVecB;
        save<double>("Col.inPlaceElemTimes", _genColVecA);

        cout << "done." << endl;
      }

      void expectedColInPlaceElemDivide() {
        if(_genColVecA.n_rows != _genColVecB.n_rows || _genColVecA.n_cols != _genColVecB.n_cols) {
          return;
        }

        cout << "- Compute expectedColInPlaceElemDivide() ... ";

        _genColVecA /= _genColVecB;
        save<double>("Col.inPlaceElemDivide", _genColVecA);

        cout << "done." << endl;
      }

      void expectedColSwap() {
        if(!_genColVecB.is_colvec()) {
          return;
        }

        cout << "- Compute expectedColSwap() ... ";

        _genColVecA.swap(_genColVecB);
        save<double>("Col.swap", _genColVecA);

        cout << "done." << endl;
      }

      void expectedColCopy_size() {
        if(!_genColVecB.is_colvec()) {
          return;
        }
        cout << "- Compute expectedColCopy_size() ... ";

        _genColVecA.copy_size(_genColVecB);
        save<double>("Col.copySize", _genColVecA);

        cout << "done." << endl;
      }

      void expectedColCol() {
        if(!_genColVecB.is_colvec()) {
          return;
        }

        cout << "- Compute expectedColCol() ... ";

        _genColVecA = vec(_genColVecB);
        save<double>("Col.col", _genColVecA);

        cout << "done." << endl;
      }
  };
}


//---------------------------------------------------------------------------//
// Copyright (c) 2013-2014 Mageswaran.D <mageswaran1989@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.
//---------------------------------------------------------------------------//

//Code sample for calculating histogram using OpenCL and
//displaying image histogram in OpenCV.

#define BOOST_COMPUTE_DEBUG_KERNEL_COMPILATION

#include <iostream>
#include <string>

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <boost/compute/source.hpp>
#include <boost/compute/system.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/interop/opencv/core.hpp>
#include <boost/compute/interop/opencv/highgui.hpp>
#include <boost/program_options.hpp>

namespace compute = boost::compute;
namespace po = boost::program_options;

// number of bins
int histSize = 256;

// Set the ranges ( for B,G,R) )
// TryOut: consider the range in kernel calculation
float range[] = { 0, 256 } ;
const float* histRange = { range };

// Create naive histogram program
// Needs "cl_khr_local_int32_base_atomics" extension
const char source[] = BOOST_COMPUTE_STRINGIZE_SOURCE (
    __kernel void histogram(read_only image2d_t src_image,
                            __global int* b_hist,
                            __global int* g_hist,
                            __global int* r_hist)
            {
                sampler_t sampler =( CLK_NORMALIZED_COORDS_FALSE |
                                     CLK_FILTER_NEAREST |
                                     CLK_ADDRESS_CLAMP_TO_EDGE);

                int image_width = get_image_width(src_image);
                int image_height = get_image_height(src_image);

                int2 coords  = (int2)(get_global_id(0), get_global_id(1));
                float4 pixel = read_imagef(src_image,sampler, coords);

                //boundary condition
                if ((coords.x < image_width) && (coords.y < image_height))
                {
                    uchar indx_x, indx_y, indx_z;
                    indx_x = convert_uchar_sat(pixel.x * 255.0f);
                    indx_y = convert_uchar_sat(pixel.y * 255.0f);
                    indx_z = convert_uchar_sat(pixel.z * 255.0f);

                    atomic_inc(&b_hist[(uint)indx_z]);
                    atomic_inc(&g_hist[(uint)indx_y]);
                    atomic_inc(&r_hist[(uint)indx_x]);
                }
            }
);

inline void showHistogramWindow(cv::Mat &b_hist, cv::Mat &g_hist, cv::Mat &r_hist,
                         std::string window_name)
{
    // Draw the histograms for B, G and R
    int hist_w = 1024;
    int hist_h = 768;
    int bin_w = cvRound((double)hist_w/histSize);

    cv::Mat histImage(hist_h, hist_w, CV_8UC3, cv::Scalar(0,0,0));

    // Normalize the result to [ 0, histImage.rows ]
    cv::normalize(b_hist, b_hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
    cv::normalize(g_hist, g_hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
    cv::normalize(r_hist, r_hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());

    // Draw for each channel
    for (int i = 1; i < histSize; i++ )
    {
        cv::line(histImage,
                 cv::Point(bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1))),
                 cv::Point(bin_w*(i), hist_h - cvRound(b_hist.at<float>(i))),
                 cv::Scalar(255, 0, 0),
                 2,
                 8,
                 0);

        cv::line(histImage,
                 cv::Point(bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1))),
                 cv::Point(bin_w*(i), hist_h - cvRound(g_hist.at<float>(i))),
                 cv::Scalar(0, 255, 0),
                 2,
                 8,
                 0);

        cv::line(histImage,
                 cv::Point( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1))),
                 cv::Point( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ),
                 cv::Scalar( 0, 0, 255),
                 2,
                 8,
                 0);
    }

    // Display
    cv::namedWindow(window_name, CV_WINDOW_AUTOSIZE );
    cv::imshow(window_name, histImage );
}

//Get the device context
//Create GPU array/vector
//Copy the image & set up the kernel
//Execute the kernel
//Copy GPU data back to CPU cv::Mat data pointer
//OpenCV conversion for convienient display
void calculateHistogramUsingCL(cv::Mat src, compute::command_queue &queue)
{
    compute::context context = queue.get_context();

    // Convert image to BGRA (OpenCL requires 16-byte aligned data)
    cv::cvtColor(src, src, CV_BGR2BGRA);

    //3 channels & 256 bins : alpha channel is ignored
    compute::vector<int> gpu_b_hist(histSize, context);
    compute::vector<int> gpu_g_hist(histSize, context);
    compute::vector<int> gpu_r_hist(histSize, context);

    // Transfer image to gpu
    compute::image2d gpu_src =
            compute::opencv_create_image2d_with_mat(
                src, compute::image2d::read_only,
                queue
                );

    compute::program histogram_program =
            compute::program::create_with_source(source, context);
    histogram_program.build();

    // create histogram kernel and set arguments
    compute::kernel histogram_kernel(histogram_program, "histogram");
    histogram_kernel.set_arg(0, gpu_src);
    histogram_kernel.set_arg(1, gpu_b_hist.get_buffer());
    histogram_kernel.set_arg(2, gpu_g_hist.get_buffer());
    histogram_kernel.set_arg(3, gpu_r_hist.get_buffer());

    // run histogram kernel
    // each kernel thread updating red, green & blue bins
    size_t origin[2] = { 0, 0 };
    size_t region[2] = { gpu_src.width(),
                         gpu_src.height() };

    queue.enqueue_nd_range_kernel(histogram_kernel, 2, origin, region, 0);

    //Make sure kernel get executed and data copied back
    queue.finish();

    //create Mat and copy GPU bins to CPU memory
    cv::Mat b_hist(256, 1, CV_32SC1);
    compute::copy(gpu_b_hist.begin(), gpu_b_hist.end(), b_hist.data, queue);
    cv::Mat g_hist(256, 1, CV_32SC1);
    compute::copy(gpu_g_hist.begin(), gpu_g_hist.end(), g_hist.data, queue);
    cv::Mat r_hist(256, 1, CV_32SC1);
    compute::copy(gpu_r_hist.begin(), gpu_r_hist.end(), r_hist.data, queue);

    b_hist.convertTo(b_hist, CV_32FC1); //converted for displaying
    g_hist.convertTo(g_hist, CV_32FC1);
    r_hist.convertTo(r_hist, CV_32FC1);

    showHistogramWindow(b_hist, g_hist, r_hist, "Histogram");
}

int main( int argc, char** argv )
{
    // Get default device and setup context
    compute::device gpu = compute::system::default_device();
    compute::context context(gpu);
    compute::command_queue queue(context, gpu);

    cv::Mat src;

    // setup the command line arguments
    po::options_description desc;
    desc.add_options()
            ("help",  "show available options")
            ("image", po::value<std::string>(), "path to image file");

    // Parse the command lines
    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

    //check the command line arguments
    if(vm.count("help"))
    {
        std::cout << desc << std::endl;
        return 0;
    }

    //check for image paths
    if(vm.count("image"))
    {
        // Read image with OpenCV
        src = cv::imread(vm["image"].as<std::string>(),
                                       CV_LOAD_IMAGE_COLOR);
        if(!src.data){
            std::cerr << "Failed to load image" << std::endl;
            return -1;
        }
        calculateHistogramUsingCL(src, queue);
        cv::imshow("Image", src);
        cv::waitKey(0);
    }
    else
    {
        std::cout << desc << std::endl;
        return 0;
    }
    return 0;
}


/*
 *            Copyright 2009-2017 The VOTCA Development Team
 *                       (http://www.votca.org)
 *
 *      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 <votca/xtp/gwbse.h>

#include <boost/format.hpp>
#include <boost/filesystem.hpp>
#include <boost/numeric/ublas/operation.hpp>
#include <votca/xtp/aomatrix.h>
#include <votca/xtp/threecenters.h>
// #include <votca/xtp/logger.h>
#include <votca/xtp/qmpackagefactory.h>
#include <boost/math/constants/constants.hpp>
#include <boost/numeric/ublas/symmetric.hpp>
#include <votca/tools/linalg.h>

using boost::format;
using namespace boost::filesystem;

namespace votca {
    namespace xtp {
        namespace ub = boost::numeric::ublas;

        // +++++++++++++++++++++++++++++ //
        // MBPT MEMBER FUNCTIONS         //
        // +++++++++++++++++++++++++++++ //

        void GWBSE::BSE_qp_setup(){
            _eh_qp = ub::zero_matrix<real_gwbse>( _bse_size , _bse_size );
            BSE_Add_qp2H( _eh_qp );
            return;
        }

        void GWBSE::BSE_solve_triplets(){

            // add full QP Hamiltonian contributions to free transitions

            ub::matrix<real_gwbse> _bse=_eh_d;

            linalg_eigenvalues(  _bse, _bse_triplet_energies, _bse_triplet_coefficients, _bse_nmax);
            return;
        }

        void GWBSE::Solve_nonhermitian(ub::matrix<double>& H, ub::matrix<double>& LT) {

            // remove stuff from Cholesky and Calculated L^T,, because more efficient for mat prods
            #pragma omp parallel for
            for (unsigned i = 0; i < LT.size1(); i++) {
                for (unsigned j = i + 1; j < LT.size1(); j++) {
                    LT(i, j) = LT(j, i);
                    LT(j, i) = 0;
                }
            }

            CTP_LOG(ctp::logDEBUG, *_pLog) << ctp::TimeStamp() << " Removed non referenced part of Cholesky decompostion" << flush;
            // determine H = L^T(A-B)L
            ub::matrix<double> _temp = ub::prod(H, ub::trans(LT));
            H= ub::prod(LT, _temp);
            _temp.resize(0, 0);
            CTP_LOG(ctp::logDEBUG, *_pLog) << ctp::TimeStamp() << " Calculated H = L^T(A+B)L " << flush;

            // solve eigenvalue problem: HR_l = eps_l^2 R_l
            ub::vector<double> _eigenvalues;
            ub::matrix<double> _eigenvectors;

            linalg_eigenvalues(H, _eigenvalues, _eigenvectors, _bse_nmax);
            CTP_LOG(ctp::logDEBUG, *_pLog) << ctp::TimeStamp() << " Solved HR_l = eps_l^2 R_l " << flush;
            H.resize(0, 0);
            // reconstruct real eigenvalues eps_l = sqrt(eps_l^2)
            _bse_singlet_energies.resize(_bse_nmax);
            for (int _i = 0; _i < _bse_nmax; _i++) {
                _bse_singlet_energies(_i) = sqrt(_eigenvalues(_i)); // store positive energies in orbitals objects
            }

            // reconstruct real eigenvectors X_l = 1/2 [sqrt(eps_l) (L^T)^-1 + 1/sqrt(eps_l)L ] R_l
            //                               Y_l = 1/2 [sqrt(eps_l) (L^T)^-1 - 1/sqrt(eps_l)L ] R_l

            // determine inverse of L^T
            ub::matrix<double> _cholesky_transposed_invert;
            ub::matrix<double> L = ub::trans(LT);
            linalg_invert(LT, _cholesky_transposed_invert);

            int dim = L.size1();
            _bse_singlet_coefficients.resize(dim, _bse_nmax); // resonant part (_X_evec)
            _bse_singlet_coefficients_AR.resize(dim, _bse_nmax); // anti-resonant part (_Y_evec)

            for (int _i = 0; _i < _bse_nmax; _i++) {
                //real_gwbse sqrt_eval = sqrt(_eigenvalues(_i));
                double sqrt_eval = sqrt(_bse_singlet_energies(_i));
                // get l-th reduced EV
                ub::matrix<double> _reduced_evec = ub::project(_eigenvectors, ub::range(0, dim), ub::range(_i, _i + 1)); // potentially col<->row

                ub::matrix<double> _transform = 0.5 * (sqrt_eval * _cholesky_transposed_invert + 1.0 / sqrt_eval * L);
                ub::project(_bse_singlet_coefficients, ub::range(0, dim), ub::range(_i, _i + 1)) = ub::prod(_transform, _reduced_evec);
                _transform = 0.5 * (sqrt_eval * _cholesky_transposed_invert - 1.0 / sqrt_eval * L);
                ub::project(_bse_singlet_coefficients_AR, ub::range(0, dim), ub::range(_i, _i + 1)) = ub::prod(_transform, _reduced_evec);
            }
            return;
        }

      void GWBSE::BSE_solve_singlets_BTDA(){

        // For details of the method, see EPL,78(2007)12001,
        // Nuclear Physics A146(1970)449, Nuclear Physics A163(1971)257.

          // setup resonant (A) and RARC blocks (B)
          // TOCHECK: Isn't that memory overkill here? _A and _B are never needed again?
           // ub::matrix<real_gwbse> _A = _eh_d + 2.0 * _eh_x;
           // ub::matrix<real_gwbse> _B = _eh_d2 + 2.0 * _eh_x;
          ub::matrix<double> _ApB = _eh_d + _eh_d2 + 4.0 * _eh_x;
          ub::matrix<double> _AmB = _eh_d - _eh_d2;

          // calculate Cholesky decomposition of A-B = LL^T. It throws an error if not positive definite
            //(A-B) is not needed any longer and can be overwritten

          bool positive_definite=true;
          CTP_LOG(ctp::logDEBUG, *_pLog) << ctp::TimeStamp() << " Trying Cholesky decomposition of KAA-KAB" << flush;
          try
            {
            linalg_cholesky_decompose( _AmB );
            }
            catch (const std::runtime_error& error)
            {
                positive_definite=false;
                CTP_LOG(ctp::logDEBUG, *_pLog) << ctp::TimeStamp() <<error.what()<<endl;
                CTP_LOG(ctp::logDEBUG, *_pLog) << ctp::TimeStamp() << "Trying Cholesky decomposition of KAA+KAB" << flush;
                linalg_cholesky_decompose( _ApB );
                _AmB = _eh_d - _eh_d2;
            }

          if(positive_definite){
              Solve_nonhermitian(_ApB,_AmB);
          }
          else{
              Solve_nonhermitian(_AmB,_ApB);
          }

          return;
      }

      void GWBSE::BSE_Add_qp2H( ub::matrix<real_gwbse>& qp ){

          #pragma omp parallel for
            for ( size_t _v1 = 0 ; _v1 < _bse_vtotal ; _v1++){
                for ( size_t _c1 = 0 ; _c1 < _bse_ctotal ; _c1++){
                    size_t _index_vc = _bse_ctotal * _v1 + _c1;
                    // diagonal
                    qp( _index_vc , _index_vc ) += _vxc(_c1 + _bse_vtotal ,_c1 + _bse_vtotal ) - _vxc(_v1,_v1);
                    // v->c
                    for ( size_t _c2 = 0 ; _c2 < _bse_ctotal ; _c2++){
                        size_t _index_vc2 = _bse_ctotal * _v1 + _c2;
                        if ( _c1 != _c2 ){
                            qp( _index_vc , _index_vc2 ) += _vxc(_c1+ _bse_vtotal ,_c2 + _bse_vtotal );
                        }
                    }

                    // c-> v
                    for ( size_t _v2 = 0 ; _v2 < _bse_vtotal ; _v2++){
                        size_t _index_vc2 = _bse_ctotal * _v2 + _c1;
                        if ( _v1 != _v2 ){
                            qp( _index_vc , _index_vc2 ) -= _vxc(_v1,_v2);
                        }
                    }
                }
            }
            return;
      }

      void GWBSE::BSE_solve_singlets(){

            ub::matrix<real_gwbse> _bse = _eh_d + 2.0 * _eh_x;

            // _bse_singlet_energies.resize(_bse_singlet_coefficients.size1());
            linalg_eigenvalues(_bse, _bse_singlet_energies, _bse_singlet_coefficients, _bse_nmax);
            return;
        }

        void GWBSE::BSE_d_setup ( TCMatrix& _Mmn){
            // gwbasis size
            size_t _gwsize = _Mmn[_homo].size1();

            // messy procedure, first get two matrices for occ and empty subbparts
            // store occs directly transposed
            ub::matrix<real_gwbse> _storage_v = ub::zero_matrix<real_gwbse>(  _bse_vtotal * _bse_vtotal , _gwsize );
            #pragma omp parallel for
            for ( size_t _v1 = 0; _v1 < _bse_vtotal; _v1++){
                const ub::matrix<real_gwbse>& Mmn = _Mmn[_v1 + _bse_vmin ];
                for ( size_t _v2 = 0; _v2 < _bse_vtotal; _v2++){
                    size_t _index_vv = _bse_vtotal * _v1 + _v2;
                    for ( size_t _i_gw = 0 ; _i_gw < _gwsize ; _i_gw++) {
                        _storage_v( _index_vv , _i_gw ) = Mmn( _i_gw , _v2 + _bse_vmin );
                    }
                }
            }

            ub::matrix<real_gwbse> _storage_c = ub::zero_matrix<real_gwbse>( _gwsize, _bse_ctotal * _bse_ctotal );
            #pragma omp parallel for
            for ( size_t _c1 = 0; _c1 < _bse_ctotal; _c1++){
                const ub::matrix<real_gwbse>& Mmn = _Mmn[_c1 + _bse_cmin];
                for ( size_t _c2 = 0; _c2 < _bse_ctotal; _c2++){
                    size_t _index_cc = _bse_ctotal * _c1 + _c2;
                    for ( size_t _i_gw = 0 ; _i_gw < _gwsize ; _i_gw++) {
                        _storage_c( _i_gw , _index_cc ) = Mmn( _i_gw , _c2 + _bse_cmin );
                    }
                }
            }

            if ( ! _do_bse_singlets )  _Mmn.Cleanup();

            // store elements in a vtotal^2 x ctotal^2 matrix
            // cout << "BSE_d_setup 1 [" << _storage_v.size1() << "x" << _storage_v.size2() << "]\n" << std::flush;
            ub::matrix<real_gwbse> _storage_prod = ub::prod( _storage_v , _storage_c );

            // now patch up _storage for screened interaction
            #pragma omp parallel for
            for ( size_t _i_gw = 0 ; _i_gw < _gwsize ; _i_gw++ ){
                if (_ppm_weight(_i_gw) < 1.e-9) {
                    for ( size_t _v = 0 ; _v < (_bse_vtotal* _bse_vtotal) ; _v++){
                        _storage_v( _v , _i_gw ) = 0;
                    }
                    for ( size_t _c = 0 ; _c < (_bse_ctotal*_bse_ctotal) ; _c++){
                        _storage_c( _i_gw , _c ) =0;
                    }

                }else{
                    double _ppm_factor = sqrt( _ppm_weight( _i_gw ));
                    for ( size_t _v = 0 ; _v < (_bse_vtotal* _bse_vtotal) ; _v++){
                        _storage_v( _v , _i_gw ) = _ppm_factor * _storage_v(_v , _i_gw );
                    }
                    for ( size_t _c = 0 ; _c < (_bse_ctotal*_bse_ctotal) ; _c++){
                        _storage_c( _i_gw , _c ) = _ppm_factor * _storage_c( _i_gw , _c  );
                    }
                }
            }

            // multiply and subtract from _storage_prod

            _storage_prod -= ub::prod( _storage_v , _storage_c );

            // free storage_v and storage_c
            _storage_c.resize(0,0);
            _storage_v.resize(0,0);

            // finally resort into _eh_d
            // can be limited to upper diagonal !
            _eh_d = ub::zero_matrix<real_gwbse>( _bse_size , _bse_size );
            #pragma omp parallel for
            for ( size_t _v1 = 0 ; _v1 < _bse_vtotal ; _v1++){
                for ( size_t _v2 = 0 ; _v2 < _bse_vtotal ; _v2++){
                    size_t _index_vv = _bse_vtotal * _v1 + _v2;

                    for ( size_t _c1 = 0 ; _c1 < _bse_ctotal ; _c1++){
                        size_t _index_vc1 = _bse_ctotal * _v1 + _c1 ;

                        for ( size_t _c2 = 0 ; _c2 < _bse_ctotal ; _c2++){
                            size_t _index_vc2 = _bse_ctotal * _v2 + _c2 ;
                            size_t _index_cc  = _bse_ctotal * _c1 + _c2;

                            _eh_d( _index_vc1 , _index_vc2 ) = -_storage_prod( _index_vv , _index_cc );
                        }
                    }
                }
            }

            return;
        }

         void GWBSE::BSE_d2_setup ( TCMatrix& _Mmn){
            // gwbasis size
            size_t _gwsize = _Mmn[_homo].size1();

            // messy procedure, first get two matrices for occ and empty subbparts
            // store occs directly transposed
            ub::matrix<real_gwbse> _storage_cv = ub::zero_matrix<real_gwbse>(  _bse_vtotal * _bse_ctotal , _gwsize );
            #pragma omp parallel for
            for ( size_t _c1 = 0; _c1 < _bse_ctotal; _c1++){
                const ub::matrix<real_gwbse>& Mmn = _Mmn[_c1 + _bse_cmin ];
                for ( size_t _v2 = 0; _v2 < _bse_vtotal; _v2++){
                    size_t _index_cv = _bse_vtotal * _c1 + _v2;
                    for ( size_t _i_gw = 0 ; _i_gw < _gwsize ; _i_gw++) {
                        _storage_cv( _index_cv , _i_gw ) = Mmn( _i_gw , _v2 + _bse_vmin );
                    }
                }
            }

            ub::matrix<real_gwbse> _storage_vc = ub::zero_matrix<real_gwbse>( _gwsize, _bse_vtotal * _bse_ctotal );
            #pragma omp parallel for
            for ( size_t _v1 = 0; _v1 < _bse_vtotal; _v1++){
                const ub::matrix<real_gwbse>& Mmn = _Mmn[_v1 + _bse_vmin];
                for ( size_t _c2 = 0; _c2 < _bse_ctotal; _c2++){
                    size_t _index_vc = _bse_ctotal * _v1 + _c2;
                    for ( size_t _i_gw = 0 ; _i_gw < _gwsize ; _i_gw++) {
                        _storage_vc( _i_gw , _index_vc ) = Mmn( _i_gw , _c2 + _bse_cmin );
                    }
                }
            }

            if ( ! _do_bse_singlets )  _Mmn.Cleanup();

            // store elements in a vtotal^2 x ctotal^2 matrix
            ub::matrix<real_gwbse> _storage_prod = ub::prod( _storage_cv , _storage_vc );

            // now patch up _storage for screened interaction
            #pragma omp parallel for
            for ( size_t _i_gw = 0 ; _i_gw < _gwsize ; _i_gw++ ){
                if (_ppm_weight(_i_gw) < 1.e-9) {
                    for ( size_t _v = 0 ; _v < (_bse_vtotal* _bse_ctotal) ; _v++){
                    _storage_vc(  _i_gw , _v ) =0;
                }
                for ( size_t _c = 0 ; _c < (_bse_ctotal*_bse_vtotal) ; _c++){
                    _storage_cv( _c, _i_gw  ) = 0;
                }
                }else{
                double _ppm_factor = sqrt( _ppm_weight( _i_gw ));
                for ( size_t _v = 0 ; _v < (_bse_vtotal* _bse_ctotal) ; _v++){
                    _storage_vc(  _i_gw , _v ) = _ppm_factor * _storage_vc( _i_gw , _v );
                }
                for ( size_t _c = 0 ; _c < (_bse_ctotal*_bse_vtotal) ; _c++){
                    _storage_cv( _c, _i_gw  ) = _ppm_factor * _storage_cv( _c , _i_gw  );
                }
                    }
            }

            // multiply and subtract from _storage_prod
            _storage_prod -= ub::prod( _storage_cv , _storage_vc );

            // free storage_v and storage_c
            _storage_cv.resize(0,0);
            _storage_vc.resize(0,0);
            // finally resort into _eh_d
            // can be limited to upper diagonal !
            _eh_d2 = ub::zero_matrix<real_gwbse>( _bse_size , _bse_size );
            #pragma omp parallel for
            for ( size_t _v1 = 0 ; _v1 < _bse_vtotal ; _v1++){
                for ( size_t _v2 = 0 ; _v2 < _bse_vtotal ; _v2++){
                    for ( size_t _c1 = 0 ; _c1 < _bse_ctotal ; _c1++){
                        size_t _index_v1c1 = _bse_ctotal * _v1 + _c1 ;

                        size_t _index_c1v2 =_bse_vtotal * _c1 + _v2;

                        for ( size_t _c2 = 0 ; _c2 < _bse_ctotal ; _c2++){
                            size_t _index_v2c2 = _bse_ctotal * _v2 + _c2 ;
                            size_t _index_v1c2 = _bse_ctotal * _v1 + _c2;

                            _eh_d2( _index_v1c1 , _index_v2c2 ) = -_storage_prod( _index_c1v2 , _index_v1c2 );

                        }
                    }
                }
            }
         return;
        }

        void GWBSE::BSE_x_setup( TCMatrix& _Mmn){

            /* unlike the fortran code, we store eh interaction directly in
             * a suitable matrix form instead of a four-index array
             */

            // gwbasis size
            size_t _gwsize = _Mmn[_homo].size1();

            // get a different storage for 3-center integrals we need
            //cout<< "Starting to set up "<< endl;
            ub::matrix<real_gwbse> _storage = ub::zero_matrix<real_gwbse>( _gwsize , _bse_size);
            //cout<< "Storage set up"<< endl;

            // occupied levels
            #pragma omp parallel for
            for ( size_t _v = 0; _v < _bse_vtotal ; _v++ ){
                // cout << " act threads: " << omp_get_thread_num( ) << " total threads " << omp_get_num_threads( ) << " max threads " << omp_get_max_threads( ) <<endl;
                ub::matrix<real_gwbse>& Mmn = _Mmn[_v + _bse_vmin];
                // empty levels
                for (size_t _c =0 ; _c < _bse_ctotal ; _c++ ){
                    size_t _index_vc = _bse_ctotal * _v + _c ;
                    for (size_t _i_gw = 0 ; _i_gw < _gwsize ; _i_gw++ ){
                        _storage( _i_gw, _index_vc ) = Mmn( _i_gw, _c + _bse_cmin);
                    }
                }
            }

            _Mmn.Cleanup();
            // with this storage, _eh_x is obtained by matrix multiplication
	    _eh_x = ub::prod( ub::trans( _storage ), _storage );
            return;
        }

    }

};


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

// Copyright (c) 2015, Oracle and/or its affiliates.

// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle

// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html

#ifndef BOOST_GEOMETRY_GEOMETRIES_HELPER_GEOMETRY_HPP
#define BOOST_GEOMETRY_GEOMETRIES_HELPER_GEOMETRY_HPP

#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/point_type.hpp>
#include <boost/geometry/core/tag.hpp>
#include <boost/geometry/core/tags.hpp>

#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/geometries/point.hpp>

#include <boost/geometry/algorithms/not_implemented.hpp>

namespace boost { namespace geometry
{

namespace detail { namespace helper_geometries
{

template <typename Geometry, typename CS_Tag = typename cs_tag<Geometry>::type>
struct default_units
{
    typedef typename coordinate_system<Geometry>::type::units type;
};

// The Cartesian coordinate system does not define the type units.
// For that reason the generic implementation for default_units cannot be used
// and specialization needs to be defined.
// Moreover, it makes sense to define the units for the Cartesian
// coordinate system to be radians, as this way a Cartesian point can
// potentially be used in algorithms taking non-Cartesian strategies
// and work as if it was as point in the non-Cartesian coordinate
// system with radian units.
template <typename Geometry>
struct default_units<Geometry, cartesian_tag>
{
    typedef radian type;
};

template <typename Units, typename CS_Tag>
struct cs_tag_to_coordinate_system
{
    typedef cs::cartesian type;
};

template <typename Units>
struct cs_tag_to_coordinate_system<Units, spherical_equatorial_tag>
{
    typedef cs::spherical_equatorial<Units> type;
};

template <typename Units>
struct cs_tag_to_coordinate_system<Units, spherical_tag>
{
    typedef cs::spherical<Units> type;
};

template <typename Units>
struct cs_tag_to_coordinate_system<Units, geographic_tag>
{
    typedef cs::geographic<Units> type;
};

template
<
    typename Point,
    typename NewCoordinateType,
    typename NewUnits,
    typename CS_Tag = typename cs_tag<Point>::type
>
struct helper_point
{
    typedef model::point
        <
            NewCoordinateType,
            dimension<Point>::value,
            typename cs_tag_to_coordinate_system<NewUnits, CS_Tag>::type
        > type;
};

}} // detail::helper_geometries

namespace detail_dispatch
{

template
<
    typename Geometry,
    typename NewCoordinateType,
    typename NewUnits,
    typename Tag = typename tag<Geometry>::type>
struct helper_geometry : not_implemented<Geometry>
{};

template <typename Point, typename NewCoordinateType, typename NewUnits>
struct helper_geometry<Point, NewCoordinateType, NewUnits, point_tag>
{
    typedef typename detail::helper_geometries::helper_point
        <
            Point, NewCoordinateType, NewUnits
        >::type type;
};

template <typename Box, typename NewCoordinateType, typename NewUnits>
struct helper_geometry<Box, NewCoordinateType, NewUnits, box_tag>
{
    typedef model::box
        <
            typename helper_geometry
                <
                    typename point_type<Box>::type, NewCoordinateType, NewUnits
                >::type
        > type;
};

} // detail_dispatch

// Meta-function that provides a new helper geometry of the same kind as
// the input geometry and the same coordinate system type,
// but with a possibly different coordinate type and coordinate system units
template
<
    typename Geometry,
    typename NewCoordinateType = typename coordinate_type<Geometry>::type,
    typename NewUnits = typename detail::helper_geometries::default_units
        <
            Geometry
        >::type
>
struct helper_geometry
{
    typedef typename detail_dispatch::helper_geometry
        <
            Geometry, NewCoordinateType, NewUnits
        >::type type;
};

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_GEOMETRIES_HELPER_GEOMETRY_HPP


#include <cstdio>
#include <iostream>
#include <fstream>
#include <string.h>
//#include <boost/dynamic_bitset/dynamic_bitset.hpp>

#include "bitmatrix.hpp"

using namespace std;
using namespace argumatrix;

int main(int argc, char* argv[])
{
	bitvector bv("00001001010");
	cout << bv << endl;
	//// Static bit matrix
	//argumatrix::bitvector bv[10];
	//bv[0].resize(100);
	//bv[1].resize(30, 1);
	//
	//std::cout << "my bitset: " << bv[0].size() << "bits, " << bv[0] << std::endl;
	//std::cout << "my bitset: " << bv[1].size() << "bits, " << bv[1] << std::endl;
	//
	//// Dynamic bit matrix
	//argumatrix::bitvector *bv1;
	//bv1 = new argumatrix::bitvector[10];
	//bv1[0].resize(100);
	//
	//bv1[1].resize(20, true);
	////bv1[1].set(10, false);
	//
	//bv1[2].resize(20,false);
	////bv1[2].set(15, true);
	//
	//std::cout << "my bitset: " << bv1[0].size() << "bits, " << bv1[0] << std::endl;
	//std::cout << "my bitset: " << bv1[1].size() << "bits, " << bv1[1] << std::endl;
	//std::cout << "my bitset: " << bv1[2].size() << "bits, " << bv1[2] << std::endl;
	//std::cout << "bv1[1][10]: " << bv1[1][10] << std::endl;
	//std::cout << "bv1[1]*bv1[2]: " << (bv1[1]*bv1[2]) << std::endl;

	//// bit matrix test
	//argumatrix::bitmatrix bm(7,5);
	//bm[0][0] = true;
	//bm[2][3] = 1;
	//bm.show();
	//
	//argumatrix::bitvector v2(5);
	//v2[1]=true;
	//
	//std::cout << "v2: " << v2 << std::endl;
	//std::cout << "bm[1]*bm[2]: " << bm[1]*bm[2] << std::endl;
	//
	//argumatrix::bitvector v3 = bm*v2;
	//
	//std::cout << "bm*v2: " << v3 << std::endl;
	//std::cout << "transpose of bm: " << std::endl;
	//argumatrix::bitmatrix bm1 = bm.transpose();
	//bm1.show();
	//
	//argumatrix::bitmatrix bm3 = bm;
	//bm[3][4] = 1;

	//std::cout << std::endl;
	//bm3.show();

	//std::cout << std::endl;
	//bm.show();
	//
	//std::cout << "bm*bm'*bm*bm'*bm*bm" << std::endl;
	//argumatrix::bitmatrix bm_t = bm.transpose();
	//argumatrix::bitmatrix bm4 = bm*bm_t*bm*bm_t*bm*bm_t;
	//
	//bm4.show();
	//
	////boost::bitmatrix bm2 = bm* (bm.transpose());
	////std::cout << "bm*bm': " << std::endl;
	////bm2.show();
	//
	//argumatrix::bitmatrix bm5(50);
	//bm5[0][1] = 1;
	//bm5[1][0] = 1;
	//bm5[2][3] = 1;
	//bm5[2][4] = 1;
	//
	//bm5.show();
	//
	//argumatrix::bitmatrix bm6 = bm5;
	//for(int i=0; i<2; i++){
	//	bm6 = bm6 * bm5;
	//
	//	std::cout << "The " << i << "th iteration:" << std::endl;
	//	bm6.show();
	//}
	//
	//argumatrix::bitmatrix bm7(50, 1);
	//bm7[3][0] = 1;
	//
	//std::cout << "The bm7 = " << std::endl;
	//bm7.show();
	//
	//std::cout << "The bm5*bm7 = " << std::endl;
	//argumatrix::bitmatrix bm8 = bm5*bm7;
	//bm8.show();
	//
	//
	//argumatrix::bitmatrix D(5);
	//D[1][0] = 1; D[1][3] = 1; D[1][4] = 1;
	//D[2][1] = 1;
	//D[3][2] = 1; D[3][3] = 1;
	//D[4][3] = 1;
	//
	//D.show();
	//
	//argumatrix::bitvector g(5);
	//std::cout << "The initial bool vector: " << g << std::endl;

	//for(int i=0; i<1000; i++){
	//	argumatrix::bitvector g_bar = ~(D*(~(D*g)));
	//
	//	std::cout << "The (" << i << ")th result: " << g_bar << std::endl;
	//
	//	if(g_bar==g)
	//	{
	//		break;
	//	}
	//	g = g_bar;
	//}

	system("pause");

	return 0;
}


// Copyright 2004 The Trustees of Indiana University.

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

//  Authors: Jeremiah Willcock
//           Douglas Gregor
//           Andrew Lumsdaine
#include <boost/graph/gursoy_atun_layout.hpp>
#include "boost/graph/adjacency_list.hpp"
#include "boost/graph/random.hpp"
#include "boost/graph/graphviz.hpp"
#include "boost/random/mersenne_twister.hpp"
#include "boost/random/linear_congruential.hpp"
#include "boost/random/uniform_01.hpp"
#include <iostream>
#include <fstream>
#include <sstream>

#if 0
#include <boost/graph/plod_generator.hpp>
#include <boost/graph/small_world_generator.hpp>
#endif
using namespace boost;

template <class Property, class Vertex>
struct position_writer {
  const Property& property;

  position_writer(const Property& property): property(property) {}

  void operator()(std::ostream& os, const Vertex& v) const {
    os << "[pos=\"" << int(property[v][0]) << "," << int(property[v][1]) << "\"]";
  }
};

struct graph_writer {
  void operator()(std::ostream& os) const {
    os << "node [shape=point, width=\".01\", height=\".01\", fixedsize=\"true\"]"
       << std::endl;
  }
};

int main(int, char*[]) {
  // Generate a graph structured like a grid, cylinder, or torus; lay it out in
  // a square grid; and output it in dot format

  typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
                                boost::no_property,
                                boost::property<boost::edge_weight_t, double>
                                > graph_type;
  typedef boost::graph_traits<graph_type>::vertex_descriptor vertex_descriptor;
  // boost::mt19937 rng;
  // boost::generate_random_graph(graph, 100, 600, rng, false, false);

#if 1
  graph_type graph;

  // Make grid, like Gursoy and Atun used
  std::map<int, std::map<int, vertex_descriptor> > verts;
  const int grid_size = 20;
  boost::minstd_rand edge_weight_gen;
  boost::uniform_01<boost::minstd_rand> random_edge_weight(edge_weight_gen);
  for (int i = 0; i < grid_size; ++i)
    for (int j = 0; j < grid_size; ++j)
      verts[i][j] = add_vertex(graph);
  for (int i = 0; i < grid_size; ++i) {
    for (int j = 0; j < grid_size; ++j) {
      if (i != 0)
        add_edge(verts[i][j], verts[i-1][j], random_edge_weight(), graph);
      if (j != 0)
        add_edge(verts[i][j], verts[i][j-1], random_edge_weight(), graph);
#if 0
      // Uncomment parts of this to get a cylinder or torus
      if (i == 0)
        add_edge(verts[0][j], verts[grid_size-1][j], random_edge_weight(),
                 graph);
      if (j == 0)
        add_edge(verts[i][0], verts[i][grid_size-1], random_edge_weight(),
                 graph);
#endif
    }
  }
#else
  using namespace boost;

#if 0
  int n = 10000;
  double alpha = 0.4;
  double beta = 50;
  minstd_rand gen;
  graph_type graph(plod_iterator<minstd_rand, graph_type>(gen, n, alpha, beta),
                   plod_iterator<minstd_rand, graph_type>(),
                   n);
#else
  int n = 1000;
  int k = 6;
  double p = 0.001;
  minstd_rand gen;
  graph_type graph(small_world_iterator<minstd_rand>(gen, n, k, p),
                   small_world_iterator<minstd_rand>(n, k),
                   n);
#endif
#endif
  // boost::read_graphviz(stdin, graph);

  typedef boost::property_map<graph_type, boost::vertex_index_t>::type
    VertexIndexMap;
  VertexIndexMap vertex_index = get(boost::vertex_index_t(), graph);

  typedef boost::heart_topology<> topology;
  topology space;

  typedef topology::point_type point;
  std::vector<point> position_vector(num_vertices(graph));
  typedef boost::iterator_property_map<std::vector<point>::iterator,
                                       VertexIndexMap, point, point&> Position;
  Position position(position_vector.begin(), vertex_index);

  boost::gursoy_atun_layout(graph, space, position);

#if 0
  std::cerr << "--------Unweighted layout--------\n";
  boost::write_graphviz(std::cout, graph,
                        position_writer<Position, vertex_descriptor>(position),
                        boost::default_writer(),
                        graph_writer());
#endif

  boost::gursoy_atun_layout(graph, space, position,
                            weight_map(get(boost::edge_weight, graph)));

#if 0
  std::cerr << "--------Weighted layout--------\n";
  boost::write_graphviz(std::cout, graph,
                        position_writer<Position, vertex_descriptor>(position),
                        boost::default_writer(),
                        graph_writer());
#endif
  return 0;
}


// Copyright (c) 2012-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <test/scriptnum10.h>
#include <script/script.h>
#include <test/test_unite.h>

#include <boost/test/unit_test.hpp>
#include <limits.h>
#include <stdint.h>

BOOST_FIXTURE_TEST_SUITE(scriptnum_tests, ReducedTestingSetup)

/** A selection of numbers that do not trigger int64_t overflow
 *  when added/subtracted. */
static const int64_t values[] = { 0, 1, -2, 127, 128, -255, 256, (1LL << 15) - 1, -(1LL << 16), (1LL << 24) - 1, (1LL << 31), 1 - (1LL << 32), 1LL << 40 };

static const int64_t offsets[] = { 1, 0x79, 0x80, 0x81, 0xFF, 0x7FFF, 0x8000, 0xFFFF, 0x10000};

static bool verify(const CScriptNum10& bignum, const CScriptNum& scriptnum)
{
    return bignum.getvch() == scriptnum.getvch() && bignum.getint() == scriptnum.getint();
}

static void CheckCreateVch(const int64_t& num)
{
    CScriptNum10 bignum(num);
    CScriptNum scriptnum(num);
    BOOST_CHECK(verify(bignum, scriptnum));

    CScriptNum10 bignum2(bignum.getvch(), false);
    CScriptNum scriptnum2(scriptnum.getvch(), false);
    BOOST_CHECK(verify(bignum2, scriptnum2));

    CScriptNum10 bignum3(scriptnum2.getvch(), false);
    CScriptNum scriptnum3(bignum2.getvch(), false);
    BOOST_CHECK(verify(bignum3, scriptnum3));
}

static void CheckCreateInt(const int64_t& num)
{
    CScriptNum10 bignum(num);
    CScriptNum scriptnum(num);
    BOOST_CHECK(verify(bignum, scriptnum));
    BOOST_CHECK(verify(CScriptNum10(bignum.getint()), CScriptNum(scriptnum.getint())));
    BOOST_CHECK(verify(CScriptNum10(scriptnum.getint()), CScriptNum(bignum.getint())));
    BOOST_CHECK(verify(CScriptNum10(CScriptNum10(scriptnum.getint()).getint()), CScriptNum(CScriptNum(bignum.getint()).getint())));
}

static void CheckAdd(const int64_t& num1, const int64_t& num2)
{
    const CScriptNum10 bignum1(num1);
    const CScriptNum10 bignum2(num2);
    const CScriptNum scriptnum1(num1);
    const CScriptNum scriptnum2(num2);
    CScriptNum10 bignum3(num1);
    CScriptNum10 bignum4(num1);
    CScriptNum scriptnum3(num1);
    CScriptNum scriptnum4(num1);

    // int64_t overflow is undefined.
    bool invalid = (((num2 > 0) && (num1 > (std::numeric_limits<int64_t>::max() - num2))) ||
                    ((num2 < 0) && (num1 < (std::numeric_limits<int64_t>::min() - num2))));
    if (!invalid)
    {
        BOOST_CHECK(verify(bignum1 + bignum2, scriptnum1 + scriptnum2));
        BOOST_CHECK(verify(bignum1 + bignum2, scriptnum1 + num2));
        BOOST_CHECK(verify(bignum1 + bignum2, scriptnum2 + num1));
    }
}

static void CheckNegate(const int64_t& num)
{
    const CScriptNum10 bignum(num);
    const CScriptNum scriptnum(num);

    // -INT64_MIN is undefined
    if (num != std::numeric_limits<int64_t>::min())
        BOOST_CHECK(verify(-bignum, -scriptnum));
}

static void CheckSubtract(const int64_t& num1, const int64_t& num2)
{
    const CScriptNum10 bignum1(num1);
    const CScriptNum10 bignum2(num2);
    const CScriptNum scriptnum1(num1);
    const CScriptNum scriptnum2(num2);

    // int64_t overflow is undefined.
    bool invalid = ((num2 > 0 && num1 < std::numeric_limits<int64_t>::min() + num2) ||
                    (num2 < 0 && num1 > std::numeric_limits<int64_t>::max() + num2));
    if (!invalid)
    {
        BOOST_CHECK(verify(bignum1 - bignum2, scriptnum1 - scriptnum2));
        BOOST_CHECK(verify(bignum1 - bignum2, scriptnum1 - num2));
    }

    invalid = ((num1 > 0 && num2 < std::numeric_limits<int64_t>::min() + num1) ||
               (num1 < 0 && num2 > std::numeric_limits<int64_t>::max() + num1));
    if (!invalid)
    {
        BOOST_CHECK(verify(bignum2 - bignum1, scriptnum2 - scriptnum1));
        BOOST_CHECK(verify(bignum2 - bignum1, scriptnum2 - num1));
    }
}

static void CheckCompare(const int64_t& num1, const int64_t& num2)
{
    const CScriptNum10 bignum1(num1);
    const CScriptNum10 bignum2(num2);
    const CScriptNum scriptnum1(num1);
    const CScriptNum scriptnum2(num2);

    BOOST_CHECK((bignum1 == bignum1) == (scriptnum1 == scriptnum1));
    BOOST_CHECK((bignum1 != bignum1) ==  (scriptnum1 != scriptnum1));
    BOOST_CHECK((bignum1 < bignum1) ==  (scriptnum1 < scriptnum1));
    BOOST_CHECK((bignum1 > bignum1) ==  (scriptnum1 > scriptnum1));
    BOOST_CHECK((bignum1 >= bignum1) ==  (scriptnum1 >= scriptnum1));
    BOOST_CHECK((bignum1 <= bignum1) ==  (scriptnum1 <= scriptnum1));

    BOOST_CHECK((bignum1 == bignum1) == (scriptnum1 == num1));
    BOOST_CHECK((bignum1 != bignum1) ==  (scriptnum1 != num1));
    BOOST_CHECK((bignum1 < bignum1) ==  (scriptnum1 < num1));
    BOOST_CHECK((bignum1 > bignum1) ==  (scriptnum1 > num1));
    BOOST_CHECK((bignum1 >= bignum1) ==  (scriptnum1 >= num1));
    BOOST_CHECK((bignum1 <= bignum1) ==  (scriptnum1 <= num1));

    BOOST_CHECK((bignum1 == bignum2) ==  (scriptnum1 == scriptnum2));
    BOOST_CHECK((bignum1 != bignum2) ==  (scriptnum1 != scriptnum2));
    BOOST_CHECK((bignum1 < bignum2) ==  (scriptnum1 < scriptnum2));
    BOOST_CHECK((bignum1 > bignum2) ==  (scriptnum1 > scriptnum2));
    BOOST_CHECK((bignum1 >= bignum2) ==  (scriptnum1 >= scriptnum2));
    BOOST_CHECK((bignum1 <= bignum2) ==  (scriptnum1 <= scriptnum2));

    BOOST_CHECK((bignum1 == bignum2) ==  (scriptnum1 == num2));
    BOOST_CHECK((bignum1 != bignum2) ==  (scriptnum1 != num2));
    BOOST_CHECK((bignum1 < bignum2) ==  (scriptnum1 < num2));
    BOOST_CHECK((bignum1 > bignum2) ==  (scriptnum1 > num2));
    BOOST_CHECK((bignum1 >= bignum2) ==  (scriptnum1 >= num2));
    BOOST_CHECK((bignum1 <= bignum2) ==  (scriptnum1 <= num2));
}

template<typename T>
void CheckSerializeDeserialize(const T &value) {
    const std::vector<uint8_t> serialized = CScriptNum::serialize(value);
    T deserialized;
    BOOST_CHECK(CScriptNum::deserialize(serialized, deserialized));
    BOOST_CHECK_EQUAL(deserialized, value);
}

static void RunCreate(const int64_t& num)
{
    CheckCreateInt(num);
    CScriptNum scriptnum(num);
    if (scriptnum.getvch().size() <= CScriptNum::nDefaultMaxNumSize)
        CheckCreateVch(num);
    else
    {
        BOOST_CHECK_THROW (CheckCreateVch(num), scriptnum10_error);
    }
}

static void RunOperators(const int64_t& num1, const int64_t& num2)
{
    CheckAdd(num1, num2);
    CheckSubtract(num1, num2);
    CheckNegate(num1);
    CheckCompare(num1, num2);
}

BOOST_AUTO_TEST_CASE(creation)
{
    for(size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i)
    {
        for(size_t j = 0; j < sizeof(offsets) / sizeof(offsets[0]); ++j)
        {
            RunCreate(values[i]);
            RunCreate(values[i] + offsets[j]);
            RunCreate(values[i] - offsets[j]);
        }
    }
}

BOOST_AUTO_TEST_CASE(operators)
{
    for(size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i)
    {
        for(size_t j = 0; j < sizeof(offsets) / sizeof(offsets[0]); ++j)
        {
            RunOperators(values[i], values[i]);
            RunOperators(values[i], -values[i]);
            RunOperators(values[i], values[j]);
            RunOperators(values[i], -values[j]);
            RunOperators(values[i] + values[j], values[j]);
            RunOperators(values[i] + values[j], -values[j]);
            RunOperators(values[i] - values[j], values[j]);
            RunOperators(values[i] - values[j], -values[j]);
            RunOperators(values[i] + values[j], values[i] + values[j]);
            RunOperators(values[i] + values[j], values[i] - values[j]);
            RunOperators(values[i] - values[j], values[i] + values[j]);
            RunOperators(values[i] - values[j], values[i] - values[j]);
        }
    }
}

BOOST_AUTO_TEST_CASE(serialize_deserialize)
{
    CheckSerializeDeserialize(0);
    CheckSerializeDeserialize(-1);
    CheckSerializeDeserialize(1);

    // Values with MSB=0x80 require special handling
    CheckSerializeDeserialize(0x80);
    CheckSerializeDeserialize(0x8011);
    CheckSerializeDeserialize(0x801122);
    CheckSerializeDeserialize(0x80112233);
    CheckSerializeDeserialize(0x8011223344);
    CheckSerializeDeserialize(0x801122334455);
    CheckSerializeDeserialize(0x80112233445566);
    CheckSerializeDeserialize(-0x80);
    CheckSerializeDeserialize(-0x8011);
    CheckSerializeDeserialize(-0x801122);
    CheckSerializeDeserialize(-0x80112233);
    CheckSerializeDeserialize(-0x8011223344);
    CheckSerializeDeserialize(-0x801122334455);
    CheckSerializeDeserialize(-0x80112233445566);

    CheckSerializeDeserialize(std::numeric_limits<uint8_t>::max());
    CheckSerializeDeserialize(std::numeric_limits<int8_t>::max());
    CheckSerializeDeserialize(std::numeric_limits<int8_t>::min());

    CheckSerializeDeserialize(std::numeric_limits<uint16_t>::max());
    CheckSerializeDeserialize(std::numeric_limits<int16_t>::max());
    CheckSerializeDeserialize(std::numeric_limits<int16_t>::min());

    CheckSerializeDeserialize(std::numeric_limits<uint32_t>::max());
    CheckSerializeDeserialize(std::numeric_limits<int32_t>::max());
    CheckSerializeDeserialize(std::numeric_limits<int32_t>::min());

    CheckSerializeDeserialize(std::numeric_limits<int64_t>::max());
    CheckSerializeDeserialize(std::numeric_limits<int64_t>::min());
}

BOOST_AUTO_TEST_CASE(deserialize_to_smaller_or_bigger_type)
{
  const auto max_i64 = std::numeric_limits<int64_t>::max();
  const auto min_i64 = std::numeric_limits<int64_t>::min();
  const auto max_i32 = std::numeric_limits<int32_t>::max();
  const auto min_i32 = std::numeric_limits<int32_t>::min();
  int32_t i32;
  int64_t i64;

  // 64 -> 32 overflows and should not be deserialized
  BOOST_CHECK(!CScriptNum::deserialize(CScriptNum::serialize(max_i64), i32));
  BOOST_CHECK(!CScriptNum::deserialize(CScriptNum::serialize(min_i64), i32));

  // 32 -> 32 OK
  BOOST_CHECK(CScriptNum::deserialize(CScriptNum::serialize(max_i32), i32));
  BOOST_CHECK(CScriptNum::deserialize(CScriptNum::serialize(min_i32), i32));

  // 32 -> 64 OK
  BOOST_CHECK(CScriptNum::deserialize(CScriptNum::serialize(max_i32), i64));
  BOOST_CHECK(CScriptNum::deserialize(CScriptNum::serialize(min_i32), i64));

  uint32_t ui32;
  // Overflow because of sign
  BOOST_CHECK(!CScriptNum::deserialize(CScriptNum::serialize(min_i32), ui32));
}

BOOST_AUTO_TEST_SUITE_END()


//---------------------------------------------------------------------------//
//!
//! \file   MonteCarlo_FormFactorSquared.hpp
//! \author Alex Robinson
//! \brief  The form factor squared declaration
//!
//---------------------------------------------------------------------------//

#ifndef MONTE_CARLO_FORM_FACTOR_SQUARED_HPP
#define MONTE_CARLO_FORM_FACTOR_SQUARED_HPP

// Boost Includes
#include <boost/units/quantity.hpp>

// FRENSIE Includes
#include "Utility_InverseSquareCentimeterUnit.hpp"
#include "Utility_InverseCentimeterUnit.hpp"

namespace MonteCarlo{

//! The form factor squared class
class FormFactorSquared
{

public:

  //! The form factor independent quantity type
  typedef boost::units::quantity<Utility::Units::InverseCentimeter> ArgumentQuantity;

  //! The form factor squared independent quantity type
  typedef boost::units::quantity<Utility::Units::InverseSquareCentimeter> SquaredArgumentQuantity;

  //! Default constructor
  FormFactorSquared()
  { /* ... */ }

  //! Destructor
  virtual ~FormFactorSquared()
  { /* ... */ }

  //! Evaluate the form factor squared
  virtual double evaluate( const SquaredArgumentQuantity square_argument ) const = 0;

  //! Evaluate the form factor squared
  double evaluate( const ArgumentQuantity argument ) const;

  //! Sample from the form factor squared
  virtual SquaredArgumentQuantity sample() const = 0;

  //! Sample from the form factor squared in a subrange
  virtual SquaredArgumentQuantity sampleInSubrange( const SquaredArgumentQuantity square_arg ) const = 0;

  //! Return the max form factor squared value
  virtual double getMaxValue() const = 0;

  //! Return the min form factor squared value
  virtual double getMinValue() const = 0;

  //! Return the lower bound of the square argument
  virtual SquaredArgumentQuantity getLowerBoundOfSquaredArgument() const = 0;

  //! Return the upper bound of the square argument
  virtual SquaredArgumentQuantity getUpperBoundOfSquaredArgument() const = 0;
};

// Evaluate the form factor squared
/*! \details This method is provided for convenience. Instead of passing in
 * a squared argument the original argument can be passed in and it will be
 * squared before evaluating the squared form factor.
 */
inline double FormFactorSquared::evaluate(
                                        const ArgumentQuantity argument ) const
{
  return this->evaluate( argument*argument );
}

} // end MonteCarlo namespace

#endif // end MONTE_CARLO_FORM_FACTOR_SQUARED_HPP

//---------------------------------------------------------------------------//
// end MonteCarlo_FormFactorSquared.hpp
//---------------------------------------------------------------------------//


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

#pragma once

#include <bitset>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/math/distributions/hypergeometric.hpp>
#include <iostream>

#include "compound-config/compound-config.hpp"
#include "loop-analysis/coordinate-space-tile-info.hpp"

namespace problem
{

//-------------------------------------------------//
//         Density Distribution Specs              //
//-------------------------------------------------//

struct DensityDistributionSpecs
{
  virtual ~DensityDistributionSpecs();

  virtual std::shared_ptr<DensityDistributionSpecs> Clone() const = 0;

  virtual const std::string Type() const = 0;

  std::string type = "UNSET";

  // Serialization
  friend class boost::serialization::access;

  template <class Archive>
  void serialize(Archive& ar, const unsigned int version = 0)
  {
    if (version == 0)
    {
      ar& BOOST_SERIALIZATION_NVP(type);
    }
  }
};  // struct DensityDistributionSpecs

BOOST_SERIALIZATION_ASSUME_ABSTRACT(DensityDistributionSpecs)

//-------------------------------------------------//
//      Density Distribution (base class)          //
//-------------------------------------------------//

class DensityDistribution
{
public:
  // destructor
  virtual ~DensityDistribution();

  virtual void SetDensity(const double density) = 0;

  virtual void SetWorkloadTensorSize(const std::uint64_t size) = 0;

  virtual std::uint64_t GetWorkloadTensorSize() const = 0;
  virtual std::string GetDistributionType() const = 0;

  virtual std::uint64_t GetMaxTileOccupancyByConfidence (const tiling::CoordinateSpaceTileInfo& tile,
                                                         const double confidence = 1.0) const = 0;
  // for lightweight pre-evaluation check
  virtual std::uint64_t GetMaxTileOccupancyByConfidence_LTW (const std::uint64_t tile_shape,
                                                             const double confidence = 1.0) const = 0;
  virtual double GetMaxTileDensityByConfidence(const tiling::CoordinateSpaceTileInfo tile,
                                               const double confidence = 1.0) const = 0;
  virtual double GetMinTileDensity(const tiling::CoordinateSpaceTileInfo tile) const = 0;
  virtual double GetTileOccupancyProbability (const tiling::CoordinateSpaceTileInfo& tile,
                                              const std::uint64_t occupancy) const = 0;
  virtual double GetExpectedTileOccupancy (const tiling::CoordinateSpaceTileInfo tile) const = 0;

  // Serialization.
  friend class boost::serialization::access;
  template <class Archive>
  void serialize(Archive& ar, const unsigned int version=0)
  {
    (void) ar;
    (void) version;
  }

}; // class DensityDistribution

BOOST_SERIALIZATION_ASSUME_ABSTRACT(DensityDistribution)

} // namespace problem


//
// Copyright © 2019 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//

#include "DepthToSpace.hpp"

#include <DataLayoutIndexed.hpp>
#include <Permute.hpp>

#include <boost/assert.hpp>

using namespace armnnUtils;

namespace armnn
{

void DepthToSpace(const TensorInfo& inputInfo,
                  const DepthToSpaceDescriptor& descriptor,
                  const void* inputData,
                  void* outputData,
                  unsigned int dataTypeSize)
{
    const unsigned int blockSize = descriptor.m_BlockSize;
    BOOST_ASSERT(blockSize != 0u);

    const TensorShape& inputShape = inputInfo.GetShape();
    const unsigned int batches = inputShape[0];

    armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
    const unsigned int inDepth  = inputShape[dataLayoutIndexed.GetChannelsIndex()];
    const unsigned int inHeight = inputShape[dataLayoutIndexed.GetHeightIndex()];
    const unsigned int inWidth  = inputShape[dataLayoutIndexed.GetWidthIndex()];

    const unsigned int outDepth = inDepth / (blockSize * blockSize);

    // The 4D input data can be interpreted as 6D (implicitly reshaped) as follows:
    //
    // [batch, block size, block size, inDepth, inHeight, inWidth] for NCHW and
    // [batch, inHeight, inWidth, blockSize, blockSize, outDepth] for NHWC.
    //
    // DepthToSpace can then be implemented as a permutation in 6D resulting in
    // the following shapes:
    //
    // [batch, outDepth, inHeight, blockSize, inWidth, blockSize] for NCHW and
    // [batch, inHeight, blockSize, inWidth, blockSize, outDepth] for NHWC.
    //
    // NOTE:
    // Since 6D tensors are not currently supported, in practice we need to handle each
    // batch separately and execute 5D permutations

    TensorShape permDestShape;
    PermutationVector permVector{};
    if (descriptor.m_DataLayout == DataLayout::NCHW)
    {
        permDestShape = TensorShape({ outDepth, inHeight, blockSize, inWidth, blockSize });
        permVector    = { 2, 4, 0, 1, 3 };
    }
    else
    {
        permDestShape = TensorShape({ inHeight, blockSize, inWidth, blockSize, outDepth });
        permVector    = { 0, 2, 1, 3, 4 };
    }

    const unsigned int numElementsPerBatch = inputShape.GetNumElements() / batches;

    for (unsigned int batchIndex = 0u; batchIndex < batches; ++batchIndex)
    {
        const uintptr_t batchDataOffset = batchIndex * (numElementsPerBatch * dataTypeSize);

        armnnUtils::Permute(permDestShape,
                            permVector,
                            static_cast<const void*>(reinterpret_cast<const uint8_t*>(inputData) + batchDataOffset),
                            static_cast<void*>(reinterpret_cast<uint8_t*>(outputData) + batchDataOffset),
                            dataTypeSize);
    }
}

} // namespace armnn