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

  @copyright 2016 NumScale SAS

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

/*!
  @ingroup group-constant
  @defgroup constant-Real Real (function template)

  Generates a constant from a compile-time IEEE754 bits pattern.

  @headerref{<boost/simd/constant/real.hpp>}

  @par Description

  @code
  template<typename T, std::uintmax_t Double, std::uintmax_t Single> T Real();
  @endcode

  Generates a value of type @c T that evaluates to a IEEE754 value which bits correspond to
  either `Double` or `Single` depending on the type of `scalar_of_t<T>`.

  @par Parameters

  | Name                | Description                                                         |
  |--------------------:|:--------------------------------------------------------------------|
  | **target**          | a [placeholder](@ref type-as) value encapsulating the constant type |

  @par Return Value
  A value of type @c T that evaluates to:
  - `T(bitwise_cast<scalar_of_t<T>>(Double))` if `scalar_of_t<T>` is `double`.
  - `T(bitwise_cast<scalar_of_t<T>>(Single))` if `scalar_of_t<T>` is `float`.

  @par Requirements
  - **T** models IEEEValue
**/

#include <boost/simd/constant/scalar/real.hpp>
#include <boost/simd/constant/simd/real.hpp>

#endif


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

#include "paal/utils/knapsack_utils.hpp"
#include "paal/utils/functors.hpp"
#include "paal/utils/type_functions.hpp"
#include "paal/greedy/knapsack/knapsack_greedy.hpp"

#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/filter_iterator.hpp>
#include <boost/range/algorithm/find_if.hpp>
#include <boost/range/algorithm/sort.hpp>

#include <type_traits>
#include <utility>

namespace paal {

namespace detail {
template <typename KnapsackData,
          typename ObjectIter = typename KnapsackData::object_iter,
          typename ObjectRef = typename KnapsackData::object_ref,
          typename Size = typename KnapsackData::size,
          typename Value = typename KnapsackData::value>
std::tuple<Value, Size, boost::iterator_range<ObjectIter>>
get_greedy_fill(KnapsackData knap_data, zero_one_tag) {

    auto density = knap_data.get_density();
    auto compare = utils::make_functor_to_comparator(density, utils::greater{});
    // objects must be lvalue because we return a subrange of this range
    auto &objects = knap_data.get_objects();

    // finding the biggest set elements with the greatest density
    boost::sort(objects, compare);

    Value valueSum{};
    Size sizeSum{};
    auto range = boost::find_if<boost::return_begin_found>(
        objects, [ =, &sizeSum, &valueSum](ObjectRef obj) {
        auto newSize = sizeSum + knap_data.get_size(obj);
        if (newSize > knap_data.get_capacity()) {
            return true;
        }
        sizeSum = newSize;
        valueSum += knap_data.get_value(obj);
        return false;
    });
    return std::make_tuple(valueSum, sizeSum, range);
}

template <typename ObjectsRange, typename OutputIter>
void greedy_to_output(ObjectsRange range, OutputIter & out, zero_one_tag) {
    for (auto obj : range) {
        *out = obj;
        ++out;
    }
}

} //! detail

/// this version of algorithm might permute, the input range
template <typename OutputIterator, typename Objects, typename ObjectSizeFunctor,
          typename ObjectValueFunctor,
          // this enable if assures that range can be permuted
          typename std::enable_if<
              !detail::is_range_const<Objects>::value>::type * = nullptr>
typename detail::knapsack_base<Objects, ObjectSizeFunctor,
                               ObjectValueFunctor>::return_type
knapsack_0_1_two_app(
    Objects &&objects,
    typename detail::FunctorOnRangePValue<ObjectSizeFunctor, Objects> capacity,
    OutputIterator out, ObjectValueFunctor value, ObjectSizeFunctor size) {
    return detail::knapsack_general_two_app(
        detail::make_knapsack_data(std::forward<Objects>(objects), capacity,
                                   size, value, out),
        detail::zero_one_tag());
}
}      //! paal
#endif // PAAL_KNAPSACK_0_1_TWO_APP_HPP


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#define NT2_UNIT_MODULE "nt2 arithmetic toolbox - round/scalar Mode"

//////////////////////////////////////////////////////////////////////////////
// unit test behavior of arithmetic components in scalar mode
//////////////////////////////////////////////////////////////////////////////
/// created by jt the 01/12/2010
///
#include <nt2/arithmetic/include/functions/round.hpp>
#include <boost/simd/sdk/simd/io.hpp>
#include <nt2/sdk/meta/as_integer.hpp>
#include <nt2/sdk/unit/tests.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>
#include <nt2/constant/constant.hpp>
#include <nt2/include/constants/one.hpp>
#include <nt2/include/constants/inf.hpp>
#include <nt2/include/constants/zero.hpp>
#include <nt2/include/constants/minf.hpp>
#include <nt2/include/constants/mone.hpp>
#include <nt2/include/constants/nan.hpp>

NT2_TEST_CASE_TPL ( round_real__1_0,  NT2_REAL_TYPES)
{

  using nt2::round;
  using nt2::tag::round_;
  typedef typename nt2::meta::call<round_(T)>::type r_t;
  typedef T wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS( r_t, wished_r_t );

  // specific values tests
  NT2_TEST_ULP_EQUAL(round(T(1.4)), r_t(1), 0);
  NT2_TEST_ULP_EQUAL(round(T(1.5)), r_t(2), 0);
  NT2_TEST_ULP_EQUAL(round(T(1.6)), r_t(2), 0);
  NT2_TEST_ULP_EQUAL(round(T(2.5)), r_t(3), 0);
  NT2_TEST_ULP_EQUAL(round(nt2::Half<T>()), nt2::One<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(round(nt2::Inf<T>()), nt2::Inf<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(round(nt2::Mhalf<T>()), nt2::Mone<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(round(nt2::Minf<T>()), nt2::Minf<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(round(nt2::Mone<T>()), nt2::Mone<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(round(nt2::Nan<T>()), nt2::Nan<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(round(nt2::One<T>()), nt2::One<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(round(nt2::Zero<T>()), nt2::Zero<r_t>(), 0);
} // end of test for floating_

NT2_TEST_CASE_TPL ( round_unsigned_int__1_0,  NT2_UNSIGNED_TYPES)
{

  using nt2::round;
  using nt2::tag::round_;
  typedef typename nt2::meta::call<round_(T)>::type r_t;
  typedef T wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS( r_t, wished_r_t );

  // specific values tests
  NT2_TEST_ULP_EQUAL(round(nt2::One<T>()), nt2::One<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(round(nt2::Zero<T>()), nt2::Zero<r_t>(), 0);
} // end of test for unsigned_int_

NT2_TEST_CASE_TPL ( round_signed_int__1_0,  NT2_INTEGRAL_SIGNED_TYPES)
{

  using nt2::round;
  using nt2::tag::round_;
  typedef typename nt2::meta::call<round_(T)>::type r_t;
  typedef T wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS( r_t, wished_r_t );

  // specific values tests
  NT2_TEST_ULP_EQUAL(round(nt2::Mone<T>()), nt2::Mone<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(round(nt2::One<T>()), nt2::One<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(round(nt2::Zero<T>()), nt2::Zero<T>(), 0);
} // end of test for signed_int_


/*
@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/config.hpp>
#include <boost/hana/ext/std/integral_constant.hpp>
#include <boost/hana/integral_constant.hpp>
#include <boost/hana/maybe.hpp>
#include <boost/hana/range.hpp>
#include <boost/hana/tuple.hpp>
#include <boost/hana/type.hpp>

#include <sstream>
#include <type_traits>
using namespace boost::hana;

int main() {

{

//! [head]
BOOST_HANA_CONSTEXPR_CHECK(head(make<Tuple>(1, '2', 3.3, nullptr)) == 1);
//! [head]

}{

//! [at]
BOOST_HANA_CONSTEXPR_CHECK(at(int_<0>, make<Tuple>(0, '1', 2.0)) == 0);
BOOST_HANA_CONSTEXPR_CHECK(at(int_<1>, make<Tuple>(0, '1', 2.0)) == '1');
BOOST_HANA_CONSTEXPR_CHECK(at(int_<2>, make<Tuple>(0, '1', 2.0)) == 2.0);
//! [at]

}{

//! [at_c]
BOOST_HANA_CONSTEXPR_CHECK(at_c<0>(make<Tuple>(0, '1', 2.0)) == 0);
BOOST_HANA_CONSTEXPR_CHECK(at_c<1>(make<Tuple>(0, '1', 2.0)) == '1');
BOOST_HANA_CONSTEXPR_CHECK(at_c<2>(make<Tuple>(0, '1', 2.0)) == 2.0);
//! [at_c]

}{

//! [drop]
constexpr auto xs = make<Tuple>(0, '1', 2.0);
BOOST_HANA_CONSTEXPR_CHECK(drop(int_<0>, xs) == xs);
BOOST_HANA_CONSTEXPR_CHECK(drop(int_<1>, xs) == make<Tuple>('1', 2.0));
BOOST_HANA_CONSTEXPR_CHECK(drop(int_<2>, xs) == make<Tuple>(2.0));
BOOST_HANA_CONSTANT_CHECK(drop(int_<3>, xs) == make<Tuple>());
BOOST_HANA_CONSTANT_CHECK(drop(int_<4>, xs) == make<Tuple>());
//! [drop]

}{

//! [drop_c]
BOOST_HANA_CONSTEXPR_CHECK(drop_c<2>(make<Tuple>(0, '1', 2.0)) == make<Tuple>(2.0));
//! [drop_c]

}{

//! [drop_until]
using namespace literals;
BOOST_HANA_CONSTEXPR_LAMBDA auto positive = [](auto x) {
    return x > int_<0>;
};

BOOST_HANA_CONSTANT_CHECK(
    drop_until(range(int_<-3>, int_<6>), positive) == range(int_<1>, int_<6>)
);

BOOST_HANA_CONSTANT_CHECK(
    drop_until(make<Tuple>(1_c, -2_c, 4_c, 5_c), positive) == make<Tuple>(1_c, -2_c, 4_c, 5_c)
);
//! [drop_until]

}{

//! [drop_while]
using namespace literals;
BOOST_HANA_CONSTEXPR_LAMBDA auto negative = [](auto x) {
    return x < int_<0>;
};

BOOST_HANA_CONSTANT_CHECK(
    drop_while(range(int_<-3>, int_<6>), negative) == range(int_<0>, int_<6>)
);

BOOST_HANA_CONSTANT_CHECK(
    drop_while(make<Tuple>(1_c, -2_c, 4_c, 5_c), negative) == make<Tuple>(1_c, -2_c, 4_c, 5_c)
);
//! [drop_while]

}{

//! [is_empty]
BOOST_HANA_CONSTANT_CHECK(!is_empty(make<Tuple>(1, '2')));
BOOST_HANA_CONSTANT_CHECK( is_empty(make<Tuple>()));
//! [is_empty]

}{

//! [last]
BOOST_HANA_CONSTEXPR_CHECK(last(make<Tuple>(1, '2', 3.3, nullptr)) == nullptr);
//! [last]

}{

//! [tail]
BOOST_HANA_CONSTEXPR_CHECK(
    tail(make<Tuple>(1, '2', 3.3, nullptr)) == make<Tuple>('2', 3.3, nullptr)
);
//! [tail]

}{

//! [Searchable]
BOOST_HANA_CONSTEXPR_CHECK(
    find_if(make<Tuple>(1.0, 2, '3'), trait_<std::is_integral>) == just(2)
);
BOOST_HANA_CONSTANT_CHECK(
    find_if(make<Tuple>(1.0, 2, '3'), trait_<std::is_class>) == nothing
);

BOOST_HANA_CONSTANT_CHECK(
    find(make<Tuple>(int_<1>, char_<'c'>, type<void>), type<void>) == just(type<void>)
);
BOOST_HANA_CONSTANT_CHECK(
    find(make<Tuple>(int_<1>, char_<'c'>, type<void>), type<int>) == nothing
);
//! [Searchable]

}

}


#include "stdafx.h"

#include "problem.hpp"
#include "utility.hpp"
#include "mdspan.hpp"

#include <fstream>
#include <utility>
#include <unordered_map>
#include <unordered_set>
#include <numeric>
#include <boost/algorithm/string.hpp>

struct advent_2017_14 : problem
{
	[[gsl::suppress(f.6)]]
	advent_2017_14() : problem(2017, 14) {
	}

protected:
	template<typename It>
	std::array<uint8_t, 256> sparse_hash(It first, It last) {
		std::array<uint8_t, 256> sparse;
		std::iota(std::begin(sparse), std::end(sparse), 0ui8);
		using cyclic = utility::cyclic_iterator<std::array<uint8_t, 256>::iterator>;
		cyclic cfirst = cyclic(std::begin(sparse), std::end(sparse));
		std::size_t skip_size = 0;
		for(std::size_t i = 0; i < 64; ++i) {
			const auto inner_loop = [&](const uint8_t length) {
				std::reverse(cfirst, cfirst + gsl::narrow<std::ptrdiff_t>(length));
				cfirst += gsl::narrow<std::ptrdiff_t>(length + skip_size);
				++skip_size;
			};
			for(It it = first; it != last; ++it) {
				const uint8_t length = gsl::narrow<uint8_t>(*it);
				inner_loop(length);
			}
			for(const uint8_t length : { 17ui8, 31ui8, 73ui8, 47ui8, 23ui8 }) {
				inner_loop(length);
			}
		}
		return sparse;
	}

	template<typename It>
	std::array<uint8_t, 16> knot_hash(It first, It last) {
		std::array<uint8_t, 256> sparse = sparse_hash(first, last);
		std::array<uint8_t, 16> dense;
		auto out = std::begin(dense);
		for(auto it = std::begin(sparse); it != std::end(sparse); std::advance(it, 16)) {
			*out++ = std::accumulate(it, std::next(it, 16), 0ui8, std::bit_xor<uint8_t>{});
		}
		return dense;
	}

	std::string key_string;
	std::size_t set_bits = 0;

	void prepare_input(std::ifstream& fin) override {
		std::getline(fin, key_string);
	}

	using grid_shape = md::extents<128, 128>;
	using grid_type  = md::mdspan<uint8_t, grid_shape>;

	std::unique_ptr<uint8_t[]> data{ std::make_unique<uint8_t[]>(128 * 128) };
	grid_type grid{ data.get() };

	void precompute() override {
		for(std::size_t i = 0; i < 128; ++i) {
			std::string row_input = key_string + "-" + std::to_string(i);
			std::array<uint8_t, 16> hash = knot_hash(std::begin(row_input), std::end(row_input));
			const gsl::span<const uint64_t> quads(reinterpret_cast<const uint64_t*>(hash.data()), 2);
			set_bits += __popcnt64(quads[0]);
			set_bits += __popcnt64(quads[1]);
			for(std::size_t j = 0; j < 16; ++j) {
				for(uint8_t k = 0; k < 8; ++k) {
					uint8_t bit = gsl::narrow<uint8_t>((hash[j] & (1ui8 << (7 - k))) >> (7 - k));
					grid(i, (j * 8) + k) = bit;
				}
			}
		}
	}

	std::string part_1() override {
		return std::to_string(set_bits);
	}

	void flood_fill(uint8_t x, uint8_t y) {
		if(grid(x, y) != 1) {
			return;
		}

		grid(x, y) = 2;
		if(x != 127) {
			flood_fill(gsl::narrow<uint8_t>(x + 1), y);
		}
		if(x != 0) {
			flood_fill(gsl::narrow<uint8_t>(x - 1), y);
		}
		if(y != 127) {
			flood_fill(x, gsl::narrow<uint8_t>(y + 1));
		}
		if(y != 0) {
			flood_fill(x, gsl::narrow<uint8_t>(y - 1));
		}
	}

	std::string part_2() override {
		std::size_t regions = 0;
		for(std::uint8_t i = 0; i < 128; ++i) {
			for(std::uint8_t j = 0; j < 128; ++j) {
				if(grid(i, j) == 1) {
					++regions;
					flood_fill(i, j);
				}
			}
		}
		return std::to_string(regions);
	}
};

REGISTER_SOLVER(2017, 14);


#ifndef INCLUDED_scheme_nest_maps_HecatonicosachoronMap_HH
#define INCLUDED_scheme_nest_maps_HecatonicosachoronMap_HH

#include "scheme/numeric/geom_4d.hh"

#include <Eigen/Dense>
#include "scheme/util/SimpleArray.hh"

#include <boost/static_assert.hpp>
#include <iostream>
#include <vector>

namespace scheme { namespace nest { namespace pmap {

	using namespace Eigen;
	using std::cout;
	using std::endl;

	template<class Float> Float cell_width(){ return 2.0*sqrt(2.0)-2.0; }

	template<class Float, class Index>
	Eigen::Map<Eigen::Quaternion<Float>const> hbt24_cellcen(Index const & i){
		// Float const * tmp = numeric::get_raw_48cell_half<Float>() + 4*i;
		// std::cout << "   raw hbt24_cellcen " << tmp[0] << " " << tmp[1] << " " << tmp[2] << " " << tmp[3] << std::endl;
		return Eigen::Map<Eigen::Quaternion<Float>const>(
		 numeric::get_raw_48cell_half<Float>() + 4*i );
	}

	template<
		int DIM=3,
		class Value = Eigen::Matrix3d,
		class Index = uint64_t,
		class Float = typename Value::Scalar
	>
	struct TetracontoctachoronMap {
		BOOST_STATIC_ASSERT_MSG(DIM==3,"TetracontoctachoronMap DIM must be == 3");

		static std::string pmap_name() { return "TetracontoctachoronMap"; }

		static int const DIMENSION = DIM;
		typedef Value ValueType ;
		typedef Float FloatType ;
		typedef Index IndexType ;
		typedef util::SimpleArray<DIM,Index> Indices;
		typedef util::SimpleArray<DIM,Float> Params;

		Index nside_;
		Float one_over_nside_;

		TetracontoctachoronMap() { init(1); }
		TetracontoctachoronMap(Index nside) { init(nside); }

		void init(uint64_t nside){ nside_ = nside; one_over_nside_ = 1.0/nside_; }

		///@brief sets value to parameters without change
		///@return false iff invalid parameters
		bool params_to_value(
			Params const & params,
			Index cell_index,
			Index resl,
			Value & value
		) const {
			// // cout << "        set p0 " << params << endl;
			Float const & w(cell_width<Float>());

			Params p = params * one_over_nside_;

			Index h48_cell_index = cell_index / (nside_*nside_*nside_);
			cell_index = cell_index % (nside_*nside_*nside_);
			p[0] += one_over_nside_ * (Float)( cell_index                   % nside_ );
			p[1] += one_over_nside_ * (Float)( cell_index /  nside_         % nside_ );
			p[2] += one_over_nside_ * (Float)( cell_index / (nside_*nside_) % nside_ );

			// if( !( p[0] >= 0.0 && p[0] <= 1.0 ) ) cout << "BAD param val: " << p[0] << endl;
			// if( !( p[1] >= 0.0 && p[1] <= 1.0 ) ) cout << "BAD param val: " << p[1] << endl;
			// if( !( p[2] >= 0.0 && p[2] <= 1.0 ) ) cout << "BAD param val: " << p[2] << endl;

			assert( p[0] >= -0.00001 && p[0] <= 1.00001 );
			assert( p[1] >= -0.00001 && p[1] <= 1.00001 );
			assert( p[2] >= -0.00001 && p[2] <= 1.00001 );
			p[0] = fmax(0.0,p[0]);
			p[1] = fmax(0.0,p[1]);
			p[2] = fmax(0.0,p[2]);
			p[0] = fmin(1.0,p[0]);
			p[1] = fmin(1.0,p[1]);
			p[2] = fmin(1.0,p[2]);

			// std::cout << cell_index << " " << p << " " << p << std::endl;
			// static int count = 0; if( ++count > 30 ) std::exit(-1);

			p = w*(p-0.5); // now |p| < sqrt(2)-1

			// if( resl > 3 ){
				Float corner_dist = fabs(p[0])+fabs(p[1])+fabs(p[2]);
				Float delta = sqrt(3.0) / 2.0 / w / (Float)(1<<resl);
			// // static int count = 0;
   			// //          std::cout << corner_dist << "    " << p << " " << p << std::endl;
		   //          if(++count > 100) std::exit(-1);
			 	if( corner_dist - delta > 1.0 ) return false; // TODO make this check more rigerous???
			// }

			// Eigen::Quaternion<Float> q( sqrt(1.0-p.squaredNorm()), p[0], p[1], p[2] );
			// assert( fabs(q.squaredNorm()-1.0) < 0.000001 );
			Eigen::Quaternion<Float> q( 1.0, p[0], p[1], p[2] );
			q.normalize();

			q = hbt24_cellcen<Float>( h48_cell_index ) * q;

			value = q.matrix();

			return true;
		}

		///@brief sets params/cell_index from value
		///@note necessary for value lookup and neighbor lookup
		bool value_to_params(
			Value const & value,
			Index /*resl*/,
			Params & params,
			Index & cell_index
		) const {
			Quaternion<Float> q(value);
			// q = numeric::to_half_cell(q);
			// // cout << "get q  " << q.coeffs().transpose() << endl;

			Index h48_cell_index;
			numeric::get_cell_48cell_half( q.coeffs(), h48_cell_index );

			q = hbt24_cellcen<Float>( h48_cell_index ).inverse() * q;

			q = numeric::to_half_cell(q);

			// // cout << q.w() << endl;
			// assert( q.w() > 0.7 );

			// // cout << "    get q0 " << q.coeffs().transpose() << endl;

			// // cout << "      get p  " << q.x() << " " << q.y() << " " << q.z() << endl;

			params[0] = q.x()/q.w()/cell_width<Float>() + 0.5;
			params[1] = q.y()/q.w()/cell_width<Float>() + 0.5;
			params[2] = q.z()/q.w()/cell_width<Float>() + 0.5;

			Indices ci = params * nside_;
			cell_index = ci[0] + ci[1]*nside_ + ci[2]*nside_*nside_;

			params = params * nside_ - ci.template cast<Float>();
			assert( params[0] >= 0.0 && params[0] <= 1.0 );
			assert( params[1] >= 0.0 && params[1] <= 1.0 );
			assert( params[2] >= 0.0 && params[2] <= 1.0 );

			// just for testing...
			// params[0] = fmax(0.000001,params[0]);
			// params[1] = fmax(0.000001,params[1]);
			// params[2] = fmax(0.000001,params[2]);
			// params[0] = fmin(0.999999,params[0]);
			// params[1] = fmin(0.999999,params[1]);
			// params[2] = fmin(0.999999,params[2]);

			cell_index += h48_cell_index * nside_*nside_*nside_;

			// cout << "        get p0 " << params << endl;

			return true;
		}

		///@brief get parameter space repr of Value for particular cell
		///@note necessary only for neighbor lookup
		void value_to_params_for_cell(
			Value const & value,
			Params & params
		) const {
			std::cerr << "Not Implemented" << std::endl;
			std::exit(-1);
		}

		///@brief return the cell_index of neighboring cells within radius of value
		///@note delta parameter is in "Parameter Space"
		template<class OutIter>
		void get_neighboring_cells(Value const & value, Float radius, OutIter out) const {
			std::cerr << "Not Implemented" << std::endl;
			std::exit(-1);
		}

		static Float const * get_covrad_data(){
			static Float const covrad[25] = {
					 62.76235 , // 1
					 38.63604 , // 2
					 26.71264 , // 3
					 20.62393 , // 4
					 17.02567 , // 5
					 14.25487 , // 6
					 12.42992 , // 7
					 11.02897 , // 8
					  9.62588 , // 9
					  8.70544 , // 10
					  7.82964 , // 11
					  7.28521 , // 12
					  6.62071 , // 13
					  6.13243 , // 14
					  5.81918 , // 15
					  5.44871 , // 16
					  5.14951 , // 17
					  4.82331 , // 18
					  4.52938 , // 19
					  4.31905 , // 20
					  4.07469 , // 21
					  3.93772 , // 22
					  3.77275 , // 23
					  3.64786 , // 24
					  3.44081   // 25
				};
				return covrad;
		}

		static int get_nside_for_rot_resl_deg( Float rot_resl_deg ){
			static Float const * covrad = get_covrad_data();
			int nside = 0;
			while( covrad[nside] > rot_resl_deg && nside < 23 ){
				// std::cout << nside << " " << covrad[nside] << std::endl;
				++nside;
			}
			return nside+1;
		}

		///@brief aka covering radius max distance from bin center to any value within bin
		Float bin_circumradius(Index resl) const {
			BOOST_VERIFY( resl < 6 );
			if( resl == 0 ){
				static Float const * covrad = get_covrad_data();
				if(nside_ > 25){
					std::cerr << "TetracontoctachoronMap::bin_circumradius > 25 not implemented" << std::endl;
					std::exit(-1);
				}
				return covrad[nside_-1] * M_PI / 180.0;
			}
			assert(nside_==1);
			static Float const covrad[6] = {
				 62.76235,
				 37.95720,
				 20.53126,
				 11.00814,
				  5.31355,
				  2.66953
			};
			return covrad[resl] * M_PI / 180.0;
		}

		///@brief maximum distance from the bin center which must be within the bin
		Float bin_inradius(Index resl) const {
			// double const delta = 1.0/(double)(1ul<<resl);
			// Vec pworst = Vec(1,1,1);// - delta*Vec(2.0,0,0);
			// Vec pNest0 = Vec(1,1,1) - delta*Vec(1.0,1.0,0);
			// cube_to_sphere(pworst);
			// cube_to_sphere(pNest0);
			// pworst.normalize();
			// pNest0.normalize();
			// return pworst.distance(pNest0) * 0.5999; // should be half of curcumradius based on geometry
		}

		///@brief cell size
		Index num_cells() const { return 24*nside_*nside_*nside_; }

	};

	template<
		int DIM,
		class Value,
		class Index,
		class Float
	>
	std::ostream & operator << ( std::ostream & out, TetracontoctachoronMap<DIM,Value,Index,Float> const & tm ){
		out << "TetracontoctachoronMap nside = " << tm.nside_ << " covrad0 = " << tm.bin_circumradius(0)*180.0/M_PI;
		return out;
	}

}}}

#endif


// Copyright (c) 2019 fortiss GmbH, Julian Bernhard, Klemens Esterle, Patrick
// Hart, Tobias Kessler
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.

#ifndef BARK_COMMONS_DISTRIBUTION_DISTRIBUTIONS_1D_HPP_
#define BARK_COMMONS_DISTRIBUTION_DISTRIBUTIONS_1D_HPP_

#include <boost/math/distributions.hpp>
#include <boost/random.hpp>
#include <random>

#include "bark/commons/distribution/distribution.hpp"
#include "bark/commons/params/params.hpp"

namespace bark {
namespace commons {

class FixedValue : public Distribution {
 public:
  FixedValue(const ParamsPtr& params)
      : Distribution(params),
        fixed_value_(params->GetListFloat(
            "FixedValue", "Value always returned when called sample", {1.0f})) {
  }

  virtual RandomVariate Sample() { return fixed_value_; }

  virtual Probability Density(const RandomVariate& variate) const {
    return 0.0f;
  };

  virtual Probability CDF(const RandomVariate& variate) const { return 0.0f; };

  virtual RandomVariableSupport GetSupport() const { return RandomVariableSupport(1,
                                                  std::make_pair(fixed_value_[0], fixed_value_[0])); };

 private:
  RandomVariate fixed_value_;
};

template <class BoostDistType>
class BoostDistribution1D : public Distribution {
 public:
  BoostDistribution1D(const ParamsPtr& params)
      : Distribution(params),
        seed_(params->GetInt(
            "RandomSeed", "Specifies seed for mersenne twister engine", 1234)),
        generator_(seed_),
        dist_(DistFromParams(params)),
        uniform_generator_(0, 1.0) {}

  virtual RandomVariate Sample();

  virtual Probability Density(const RandomVariate& variate) const {
    return boost::math::pdf(dist_, variate[0]);
  };

  virtual Probability CDF(const RandomVariate& variate) const {
    return boost::math::cdf(dist_, variate[0]);
  };

  virtual RandomVariableSupport GetSupport() const;

  BoostDistType DistFromParams(const ParamsPtr& params) const;

 private:
  RandomSeed seed_;
  std::mt19937 generator_;
  BoostDistType dist_;
  std::uniform_real_distribution<RandomVariableValueType> uniform_generator_;
};

template <class BoostDistType>
inline RandomVariate BoostDistribution1D<BoostDistType>::Sample() {
  // Boost does not provide direct sampling, but we can go over the quantile
  // function
  auto probability = uniform_generator_(generator_);
  auto sample = boost::math::quantile(dist_, probability);
  return RandomVariate(1, sample);
}

using boost_normal = boost::math::normal_distribution<RandomVariableValueType>;
using boost_uniform = boost::math::uniform_distribution<RandomVariableValueType>;

template <>
inline boost_uniform BoostDistribution1D<boost_uniform>::DistFromParams(
    const ParamsPtr& params) const {
  const RandomVariableValueType lower_bound =
      params->GetReal("LowerBound", "Lower bound of uniform distr.", 0.0f);
  const RandomVariableValueType upper_bound =
      params->GetReal("UpperBound", "Upper bound of uniform distr.", 1.0f);
  return boost_uniform(lower_bound, upper_bound);
}

template <>
inline boost_normal BoostDistribution1D<boost_normal>::DistFromParams(
    const ParamsPtr& params) const {
  const RandomVariableValueType mean =
      params->GetReal("Mean", "Mean of normal distribution", 0.0f);
  const RandomVariableValueType std = params->GetReal(
      "StdDev", "Standard deviation of normal distribution", 1.0f);
  return boost_normal(mean, std);
}

template <>
inline RandomVariableSupport BoostDistribution1D<boost_uniform>::GetSupport() const {
  return RandomVariableSupport(1, std::make_pair(dist_.lower(), dist_.upper()));
}

template <>
inline RandomVariableSupport BoostDistribution1D<boost_normal>::GetSupport() const {
  return RandomVariableSupport(1, std::make_pair
      (std::numeric_limits<RandomVariableValueType>::lowest(), std::numeric_limits<RandomVariableValueType>::max()));
}

using NormalDistribution1D = BoostDistribution1D<boost_normal>;
using UniformDistribution1D = BoostDistribution1D<boost_uniform>;

}  // namespace commons
}  // namespace bark

#endif  // BARK_COMMONS_DISTRIBUTION_DISTRIBUTIONS_1D_HPP_


#ifndef OPENGM_PYTHON_INTERFACE
#define OPENGM_PYTHON_INTERFACE 1
#endif

#include <stdexcept>
#include <stddef.h>
#include <string>
#include <boost/python.hpp>
#include <opengm/graphicalmodel/graphicalmodel.hxx>
#include <opengm/inference/inference.hxx>
#include <opengm/inference/graphcut.hxx>
#include <opengm/inference/alphabetaswap.hxx>
#include <opengm/inference/alphaexpansion.hxx>
#ifdef WITH_BOOST
#  include <opengm/inference/auxiliary/minstcutboost.hxx>
#endif
#ifdef WITH_MAXFLOW
#  include <opengm/inference/auxiliary/minstcutkolmogorov.hxx>
#endif

#include "nifty_iterator.hxx"
#include "inferencehelpers.hxx"

#include "export_typedes.hxx"

#define GRAPH_CUT_EXPORT_HELPER(GC_CLASS,GC_STRING,P_STRING,V_STRING)\
class_<typename GC_CLASS::Parameter > (P_STRING, init<>() ) \
      .def(init<const typename  PyGm::ValueType>())\
      .def ("set", &pygc::set<typename GC_CLASS::Parameter>, \
            ( \
            arg("scale")=10 \
            ) \
      ) \
      .def_readwrite("scale", & GC_CLASS::Parameter::scale_)\
      ; \
   OPENGM_PYTHON_VERBOSE_VISITOR_EXPORTER(typename GC_CLASS::VerboseVisitorType,V_STRING );\
   OPENGM_PYTHON_INFERENCE_NO_RESET_EXPORTER(GC_CLASS,GC_STRING)

using namespace boost::python;

namespace pygc{
   template<class PARAM>
   inline void set
   (
      PARAM & p,
      double scale
   ){
      p.scale_=scale;
   }
}

template<class GM,class ACC>
void export_graphcut(){
   import_array();
   typedef GM PyGm;
   typedef typename PyGm::ValueType ValueType;

   typedef opengm::MinSTCutBoost<size_t, ValueType, opengm::KOLMOGOROV> MinStCutBoostKolmogorov;
   typedef opengm::GraphCut<PyGm, ACC, MinStCutBoostKolmogorov> PyGraphCutBoostKolmogorov;

   GRAPH_CUT_EXPORT_HELPER(PyGraphCutBoostKolmogorov,"GraphCutBoostKolmogorov", "GraphCutBoostKolmogorovParameter","GraphCutBoostKolmogorovVerboseVisitor");

}

template void export_graphcut<GmAdder,opengm::Minimizer>();
//template void export_graphcut<GmAdder,opengm::Maximizer>();


/**
 * @file aug_lagrangian_test.cpp
 * @author Ryan Curtin
 *
 * Test of the AugmentedLagrangian class using the test functions defined in
 * aug_lagrangian_test_functions.hpp.
 *
 * mlpack is free software; you may redistribute it and/or modify it under the
 * terms of the 3-clause BSD license.  You should have received a copy of the
 * 3-clause BSD license along with mlpack.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */

#include <mlpack/core.hpp>
#include <mlpack/core/optimizers/aug_lagrangian/aug_lagrangian.hpp>
#include <mlpack/core/optimizers/aug_lagrangian/aug_lagrangian_test_functions.hpp>
#include <boost/test/unit_test.hpp>
#include "test_tools.hpp"

using namespace mlpack;
using namespace mlpack::optimization;

BOOST_AUTO_TEST_SUITE(AugLagrangianTest);

/**
 * Tests the Augmented Lagrangian optimizer using the
 * AugmentedLagrangianTestFunction class.
 */
BOOST_AUTO_TEST_CASE(AugLagrangianTestFunctionTest)
{
  // The choice of 10 memory slots is arbitrary.
  AugLagrangianTestFunction f;
  AugLagrangian<AugLagrangianTestFunction> aug(f);

  arma::vec coords = f.GetInitialPoint();

  if (!aug.Optimize(coords, 0))
    BOOST_FAIL("Optimization reported failure.");

  double finalValue = f.Evaluate(coords);

  BOOST_REQUIRE_CLOSE(finalValue, 70.0, 1e-5);
  BOOST_REQUIRE_CLOSE(coords[0], 1.0, 1e-5);
  BOOST_REQUIRE_CLOSE(coords[1], 4.0, 1e-5);
}

/**
 * Tests the Augmented Lagrangian optimizer using the Gockenbach function.
 */
BOOST_AUTO_TEST_CASE(GockenbachFunctionTest)
{
  GockenbachFunction f;
  AugLagrangian<GockenbachFunction> aug(f);

  arma::vec coords = f.GetInitialPoint();

  if (!aug.Optimize(coords, 0))
    BOOST_FAIL("Optimization reported failure.");

  double finalValue = f.Evaluate(coords);

  // Higher tolerance for smaller values.
  BOOST_REQUIRE_CLOSE(finalValue, 29.633926, 1e-5);
  BOOST_REQUIRE_CLOSE(coords[0], 0.12288178, 1e-3);
  BOOST_REQUIRE_CLOSE(coords[1], -1.10778185, 1e-5);
  BOOST_REQUIRE_CLOSE(coords[2], 0.015099932, 1e-3);
}

BOOST_AUTO_TEST_SUITE_END();


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

/*
 Copyright (C) 2019 SoftSolutions! S.r.l.

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

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

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

/*! \file globalbootstrap.hpp
    \brief global bootstrap, with additional restrictions
    \ingroup termstructures
*/

#ifndef quantlib_global_bootstrap_hpp
#define quantlib_global_bootstrap_hpp

#include <ql/math/functional.hpp>
#include <ql/math/interpolations/linearinterpolation.hpp>
#include <ql/math/optimization/levenbergmarquardt.hpp>
#include <ql/termstructures/bootstraperror.hpp>
#include <ql/termstructures/bootstraphelper.hpp>
#include <ql/utilities/dataformatters.hpp>

#include <boost/function.hpp>

namespace QuantLib {

//! Global boostrapper, with additional restrictions
template <class Curve> class GlobalBootstrap {
    typedef typename Curve::traits_type Traits;             // ZeroYield, Discount, ForwardRate
    typedef typename Curve::interpolator_type Interpolator; // Linear, LogLinear, ...

  public:
    GlobalBootstrap(Real accuracy = Null<Real>());
    /*! The set of (alive) additional dates is added to the interpolation grid. The set of additional dates must only
      depend on the current global evaluation date.  The additionalErrors functor must yield at least as many values
      such that

      number of (usual, alive) rate helpers + number of (alive) additional values >= number of data points - 1

      (note that the data points contain t=0). These values are treated as additional error terms in the optimization,
      the usual rate helpers return marketQuote - impliedQuote here. All error terms are equally weighted in the
      optimisation.

      The additional helpers are treated like the usual rate helpers, but no standard pillar dates are added for them.

      WARNING: This class is known to work with Traits Discount, ZeroYield, Forward (i.e. the usual traits for IR curves
      in QL), it might fail for other traits - check the usage of Traits::updateGuess(), Traits::guess(),
      Traits::minValueAfter(), Traits::maxValueAfter() in this class against them.
    */
    GlobalBootstrap(const std::vector<ext::shared_ptr<typename Traits::helper> > &additionalHelpers,
                    const boost::function<std::vector<Date>()> &additionalDates,
                    const boost::function<Array()> &additionalErrors,
                    Real accuracy = Null<Real>());
    void setup(Curve *ts);
    void calculate() const;

  private:
    void initialize() const;
    Curve *ts_;
    Real accuracy_;
    mutable std::vector<ext::shared_ptr<typename Traits::helper> > additionalHelpers_;
    boost::function<std::vector<Date>()> additionalDates_;
    boost::function<Array()> additionalErrors_;
    mutable bool initialized_, validCurve_;
    mutable Size firstHelper_, numberHelpers_;
    mutable Size firstAdditionalHelper_, numberAdditionalHelpers_;
    mutable Size firstAdditionalDate_, numberAdditionalDates_;
    mutable std::vector<Real> lowerBounds_, upperBounds_;
};

// template definitions

template <class Curve>
GlobalBootstrap<Curve>::GlobalBootstrap(Real accuracy)
: ts_(0), accuracy_(accuracy), initialized_(false), validCurve_(false) {}

template <class Curve>
GlobalBootstrap<Curve>::GlobalBootstrap(
    const std::vector<ext::shared_ptr<typename Traits::helper> > &additionalHelpers,
    const boost::function<std::vector<Date>()> &additionalDates,
    const boost::function<Array()> &additionalErrors,
    Real accuracy)
    : ts_(0), accuracy_(accuracy), additionalHelpers_(additionalHelpers),
      additionalDates_(additionalDates), additionalErrors_(additionalErrors),
      initialized_(false), validCurve_(false) {}

template <class Curve> void GlobalBootstrap<Curve>::setup(Curve *ts) {
    ts_ = ts;
    for (Size j = 0; j < ts_->instruments_.size(); ++j)
        ts_->registerWith(ts_->instruments_[j]);
    for (Size j = 0; j < additionalHelpers_.size(); ++j)
        ts_->registerWith(additionalHelpers_[j]);

    // do not initialize yet: instruments could be invalid here
    // but valid later when bootstrapping is actually required
}

template <class Curve> void GlobalBootstrap<Curve>::initialize() const {

    // ensure helpers are sorted
    std::sort(ts_->instruments_.begin(), ts_->instruments_.end(), detail::BootstrapHelperSorter());
    std::sort(additionalHelpers_.begin(), additionalHelpers_.end(), detail::BootstrapHelperSorter());

    // skip expired helpers
    Date firstDate = Traits::initialDate(ts_);

    firstHelper_ = 0;
    if (!ts_->instruments_.empty()) {
        while (firstHelper_ < ts_->instruments_.size() && ts_->instruments_[firstHelper_]->pillarDate() <= firstDate)
            ++firstHelper_;
    }
    numberHelpers_ = ts_->instruments_.size() - firstHelper_;

    // skip expired additional helpers
    firstAdditionalHelper_ = 0;
    if (!additionalHelpers_.empty()) {
        while (firstAdditionalHelper_ < additionalHelpers_.size() &&
               additionalHelpers_[firstAdditionalHelper_]->pillarDate() <= firstDate)
            ++firstAdditionalHelper_;
    }
    numberAdditionalHelpers_ = additionalHelpers_.size() - firstAdditionalHelper_;

    // skip expired additional dates
    std::vector<Date> additionalDates;
    if (additionalDates_)
        additionalDates = additionalDates_();
    firstAdditionalDate_ = 0;
    if (!additionalDates.empty()) {
        while (firstAdditionalDate_ < additionalDates.size() && additionalDates[firstAdditionalDate_] <= firstDate)
            ++firstAdditionalDate_;
    }
    numberAdditionalDates_ = additionalDates.size() - firstAdditionalDate_;

    QL_REQUIRE(numberHelpers_ + numberAdditionalDates_ >= Interpolator::requiredPoints - 1,
               "not enough alive instruments (" << numberHelpers_ << ") + additional dates (" << numberAdditionalDates_
                                                << ") = " << numberHelpers_ + numberAdditionalDates_ << " provided, "
                                                << Interpolator::requiredPoints - 1 << " required");

    // calculate dates and times
    std::vector<Date> &dates = ts_->dates_;
    std::vector<Time> &times = ts_->times_;

    // first populate the dates vector and make sure they are sorted and there are no duplicates
    dates.clear();
    dates.push_back(firstDate);
    for (Size j = 0; j < numberHelpers_; ++j)
        dates.push_back(ts_->instruments_[firstHelper_ + j]->pillarDate());
    for (Size j = firstAdditionalDate_; j < numberAdditionalDates_; ++j)
        dates.push_back(additionalDates[firstAdditionalDate_ + j]);
    std::sort(dates.begin(), dates.end());
    std::vector<Date>::iterator it = std::unique(dates.begin(), dates.end());
    QL_REQUIRE(it == dates.end(), "duplicate dates among alive instruments and additional dates");

    // build times vector
    times.clear();
    for (Size j = 0; j < dates.size(); ++j)
        times.push_back(ts_->timeFromReference(dates[j]));

    // determine maxDate
    Date maxDate = firstDate;
    for (Size j = 0; j < numberHelpers_; ++j) {
        maxDate = std::max(ts_->instruments_[firstHelper_ + j]->latestRelevantDate(), maxDate);
    }
    for (Size j = 0; j < numberAdditionalDates_; ++j) {
        maxDate = std::max(additionalDates[firstAdditionalDate_ + j], maxDate);
    }
    ts_->maxDate_ = maxDate;

    // set initial guess only if the current curve cannot be used as guess
    if (!validCurve_ || ts_->data_.size() != dates.size()) {
        // ts_->data_[0] is the only relevant item,
        // but reasonable numbers might be needed for the whole data vector
        // because, e.g., of interpolation's early checks
        ts_->data_ = std::vector<Real>(dates.size(), Traits::initialValue(ts_));
    }
    initialized_ = true;
}

template <class Curve> void GlobalBootstrap<Curve>::calculate() const {

    // we might have to call initialize even if the curve is initialized
    // and not moving, just because helpers might be date relative and change
    // with evaluation date change.
    // anyway it makes little sense to use date relative helpers with a
    // non-moving curve if the evaluation date changes
    if (!initialized_ || ts_->moving_)
        initialize();

    // setup helpers
    for (Size j = 0; j < numberHelpers_; ++j) {
        const ext::shared_ptr<typename Traits::helper> &helper = ts_->instruments_[firstHelper_ + j];
        // check for valid quote
        QL_REQUIRE(helper->quote()->isValid(), io::ordinal(j + 1)
                                                   << " instrument (maturity: " << helper->maturityDate()
                                                   << ", pillar: " << helper->pillarDate() << ") has an invalid quote");
        // don't try this at home!
        // This call creates helpers, and removes "const".
        // There is a significant interaction with observability.
        helper->setTermStructure(const_cast<Curve *>(ts_));
    }

    // setup additional helpers
    for (Size j = 0; j < numberAdditionalHelpers_; ++j) {
        const ext::shared_ptr<typename Traits::helper> &helper = additionalHelpers_[firstAdditionalHelper_ + j];
        QL_REQUIRE(helper->quote()->isValid(), io::ordinal(j + 1)
                                                   << " additional instrument (maturity: " << helper->maturityDate()
                                                   << ") has an invalid quote");
        helper->setTermStructure(const_cast<Curve *>(ts_));
    }

    Real accuracy = accuracy_ != Null<Real>() ? accuracy_ : ts_->accuracy_;

    // setup optimizer and EndCriteria
    Real optEps = accuracy;
    LevenbergMarquardt optimizer(optEps, optEps, optEps); // FIXME hardcoded tolerances
    EndCriteria ec(1000, 10, optEps, optEps, optEps);      // FIXME hardcoded values here as well

    // setup interpolation
    if (!validCurve_) {
        ts_->interpolation_ =
            ts_->interpolator_.interpolate(ts_->times_.begin(), ts_->times_.end(), ts_->data_.begin());
    }

    // determine bounds, we use an unconstrained optimisation transforming the free variables to [lowerBound,upperBound]
    std::vector<Real> lowerBounds(numberHelpers_ + numberAdditionalDates_),
        upperBounds(numberHelpers_ + numberAdditionalDates_);
    for (Size i = 0; i < numberHelpers_ + numberAdditionalDates_; ++i) {
        // just pass zero as the first alive helper, it's not used in the standard QL traits anyway
        lowerBounds[i] = Traits::minValueAfter(i + 1, ts_, validCurve_, 0);
        upperBounds[i] = Traits::maxValueAfter(i + 1, ts_, validCurve_, 0);
    }

    // setup cost function
    class TargetFunction : public CostFunction {
      public:
        TargetFunction(const Size firstHelper,
                       const Size numberHelpers,
                       const boost::function<Array()>& additionalErrors,
                       Curve* ts,
                       const std::vector<Real>& lowerBounds,
                       const std::vector<Real>& upperBounds)
        : firstHelper_(firstHelper), numberHelpers_(numberHelpers),
          additionalErrors_(additionalErrors), ts_(ts), lowerBounds_(lowerBounds),
          upperBounds_(upperBounds) {}

        Real transformDirect(const Real x, const Size i) const {
            return (std::atan(x) + M_PI_2) / M_PI * (upperBounds_[i] - lowerBounds_[i]) + lowerBounds_[i];
        }

        Real transformInverse(const Real y, const Size i) const {
            return std::tan((y - lowerBounds_[i]) * M_PI / (upperBounds_[i] - lowerBounds_[i]) - M_PI_2);
        }

        Real value(const Array &x) const {
            Array v = values(x);
            std::transform(v.begin(), v.end(), v.begin(), square<Real>());
            return std::sqrt(std::accumulate(v.begin(), v.end(), 0.0) / static_cast<Real>(v.size()));
        }

        Disposable<Array> values(const Array &x) const {
            for (Size i = 0; i < x.size(); ++i) {
                Traits::updateGuess(ts_->data_, transformDirect(x[i], i), i + 1);
            }
            ts_->interpolation_.update();
            std::vector<Real> result(numberHelpers_);
            for (Size i = 0; i < numberHelpers_; ++i) {
                result[i] = ts_->instruments_[firstHelper_ + i]->quote()->value() -
                            ts_->instruments_[firstHelper_ + i]->impliedQuote();
            }
            if (additionalErrors_) {
                Array tmp = additionalErrors_();
                result.resize(numberHelpers_ + tmp.size());
                for (Size i = 0; i < tmp.size(); ++i) {
                    result[numberHelpers_ + i] = tmp[i];
                }
            }
            Array asArray(result.begin(), result.end());
            return asArray;
        }

      private:
        Size firstHelper_, numberHelpers_;
        boost::function<Array()> additionalErrors_;
        Curve *ts_;
        const std::vector<Real> lowerBounds_, upperBounds_;
    };
    TargetFunction cost(firstHelper_, numberHelpers_, additionalErrors_, ts_, lowerBounds, upperBounds);

    // setup guess
    Array guess(numberHelpers_ + numberAdditionalDates_);
    for (Size i = 0; i < guess.size(); ++i) {
        // just pass zero as the first alive helper, it's not used in the standard QL traits anyway
        guess[i] = cost.transformInverse(Traits::guess(i + 1, ts_, validCurve_, 0), i);
    }

    // setup problem
    NoConstraint noConstraint;
    Problem problem(cost, noConstraint, guess);

    // run optimization
    optimizer.minimize(problem, ec);

    // evaluate target function on best value found to ensure that data_ contains the optimal value
    Real finalTargetError = cost.value(problem.currentValue());

    // check final error
    QL_REQUIRE(finalTargetError <= accuracy,
               "global bootstrap failed, error is " << finalTargetError << ", accuracy is " << accuracy);

    // set valid flag
    validCurve_ = true;
}

} // namespace QuantLib

#endif


#include "Application.h"
#include "OpLog.h"

#include <SM_Vector.h>
#include <SM_MinBoundingBox.h>
#include <SM_Calc.h>
#include <pimg/OutlineRaw.h>
#include <pimg/ImageData.h>
#include <painting2/DrawRT.h>
#include <sx/ResFileHelper.h>
#include <ns/NodeFactory.h>
#include <node2/RenderSystem.h>
#include <facade/ResPool.h>

#include <boost/filesystem.hpp>

#include <string>
#include <fstream>
#include <iostream>

namespace
{

static const char* TIME_FILEPATH = "rotate_crop_border_time.json";
static const char* LOG_FILEPATH  = "rotate_crop_border_log.json";

bool GetRotateTrimInfo(const uint8_t* pixels, int img_w, int img_h,
	                   int& width, int& height, sm::vec2& center, float& angle)
{
	pimg::OutlineRaw raw(pixels, img_w, img_h);
	raw.CreateBorderLineAndMerge();
	if (raw.GetBorderLine().empty()) {
		return false;
	}
	raw.CreateBorderConvexHull();

	sm::vec2 bound[4];
	bool is_rotate = sm::MinBoundingBox::Do(raw.GetConvexHull(), bound);

	center = (bound[0] + bound[2]) * 0.5f;
	center.x -= img_w * 0.5f;
	center.y -= img_h * 0.5f;

	center = -center;

	if (is_rotate) {
		float left = FLT_MAX;
		int left_idx;
		for (int i = 0; i < 4; ++i) {
			if (bound[i].x < left) {
				left = bound[i].x;
				left_idx = i;
			}
		}

		const sm::vec2& s = bound[left_idx];
		const sm::vec2& e = bound[left_idx == 3 ? 0 : left_idx + 1];
		sm::vec2 right = s;
		right.x += 1;
		angle = -sm::get_angle(s, e, right);
		center = sm::rotate_vector(center, angle);

		width  = static_cast<int>(std::ceil(sm::dis_pos_to_pos(s, e)));
		height = static_cast<int>(std::ceil(sm::dis_pos_to_pos(e, bound[(left_idx+2)%4])));
	} else {
		angle = 0;
	}

	return true;
}

bool Crop(const std::string& src_filepath, const std::string& dst_filepath,
	      sm::vec2& center, float& angle)
{
	if (sx::ResFileHelper::Type(src_filepath.c_str()) != sx::RES_FILE_IMAGE) {
		return false;
	}

	static const bool PRE_MUL_ALPHA(false);
	auto img = facade::ResPool::Instance().Fetch<pimg::ImageData>(src_filepath, PRE_MUL_ALPHA);

	int width, height;
	bool success = GetRotateTrimInfo(img->GetPixelData(), img->GetWidth(),
		img->GetHeight(), width, height, center, angle);
	if (!success || angle == 0) {
		return false;
	}

	auto casset = ns::NodeFactory::CreateAssetComp(src_filepath);
	if (!casset) {
		return false;
	}

	boost::filesystem::create_directory(
		boost::filesystem::path(dst_filepath).parent_path());

	sm::Matrix2D trans;
	trans.SetTransformation(center.x, center.y, angle, 1, 1, 0, 0, 0, 0);

	pt2::DrawRT rt;
	rt.Draw<n0::CompAsset>(*casset, [&](const n0::CompAsset& casset, const sm::Matrix2D& mt) {
		n2::RenderSystem::Draw(casset, trans * mt);
	}, true);
	rt.StoreToFile(dst_filepath.c_str(), width, height);

	return true;
}

bool CropSingle(const std::string& src_filepath, const std::string& dst_filepath)
{
	sm::vec2 center;
	float angle;
	return Crop(src_filepath, dst_filepath, center, angle);
}

bool CropMulti(const std::string& filepath, const std::string& src_dir, const std::string& dst_filepath,
	           std::unique_ptr<tc::OpLog>& op_log, rapidjson::MemoryPoolAllocator<>& alloc)
{
	sm::vec2 center;
	float angle;
	if (!Crop(filepath, dst_filepath, center, angle)) {
		return false;
	}

	if (op_log)
	{
		rapidjson::Value val;
		val.SetObject();

		auto relative_path = boost::filesystem::relative(filepath, src_dir);
		val.AddMember("filepath", rapidjson::Value(relative_path.string().c_str(), alloc), alloc);

		val.AddMember("center_x", center.x, alloc);
		val.AddMember("center_y", center.y, alloc);

		val.AddMember("angle", angle, alloc);

		op_log->Insert(relative_path.string(), val, alloc);
	}

	return true;
}

}

namespace tc
{
extern bool InitRender();
}

namespace crop
{

void RotateCropBorder(const std::string& src_path, const std::string& dst_path)
{
	tc::InitRender();

	auto modify_time_filepath = boost::filesystem::absolute(TIME_FILEPATH, dst_path);
	auto cfg_filepath = boost::filesystem::absolute(LOG_FILEPATH, dst_path);
	tc::Application app(modify_time_filepath.string(), cfg_filepath.string());
	app.Do(src_path, dst_path, CropSingle, CropMulti, sx::RES_FILE_IMAGE);
}

}


// Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
// 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 ETH Zurich and UNC Chapel Hill nor the names of
//       its contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)

#include "estimators/essential_matrix.h"

#include <complex>

#include <Eigen/Geometry>
#include <Eigen/LU>
#include <Eigen/SVD>

#include "base/polynomial.h"
#include "estimators/utils.h"
#include "util/logging.h"
#include "util/math.h"

namespace colmap {

std::vector<EssentialMatrixFivePointEstimator::M_t>
EssentialMatrixFivePointEstimator::Estimate(const std::vector<X_t>& points1,
                                            const std::vector<Y_t>& points2) {
  CHECK_EQ(points1.size(), points2.size());

  // Step 1: Extraction of the nullspace x, y, z, w.

  Eigen::Matrix<double, Eigen::Dynamic, 9> Q(points1.size(), 9);
  for (size_t i = 0; i < points1.size(); ++i) {
    const double x1_0 = points1[i](0);
    const double x1_1 = points1[i](1);
    const double x2_0 = points2[i](0);
    const double x2_1 = points2[i](1);
    Q(i, 0) = x1_0 * x2_0;
    Q(i, 1) = x1_1 * x2_0;
    Q(i, 2) = x2_0;
    Q(i, 3) = x1_0 * x2_1;
    Q(i, 4) = x1_1 * x2_1;
    Q(i, 5) = x2_1;
    Q(i, 6) = x1_0;
    Q(i, 7) = x1_1;
    Q(i, 8) = 1;
  }

  // Extract the 4 Eigen vectors corresponding to the smallest singular values.
  const Eigen::JacobiSVD<Eigen::Matrix<double, Eigen::Dynamic, 9>> svd(
      Q, Eigen::ComputeFullV);
  const Eigen::Matrix<double, 9, 4> E = svd.matrixV().block<9, 4>(0, 5);

  // Step 3: Gauss-Jordan elimination with partial pivoting on A.

  Eigen::Matrix<double, 10, 20> A;
#include "estimators/essential_matrix_poly.h"
  Eigen::Matrix<double, 10, 10> AA =
      A.block<10, 10>(0, 0).partialPivLu().solve(A.block<10, 10>(0, 10));

  // Step 4: Expansion of the determinant polynomial of the 3x3 polynomial
  //         matrix B to obtain the tenth degree polynomial.

  Eigen::Matrix<double, 13, 3> B;
  for (size_t i = 0; i < 3; ++i) {
    B(0, i) = 0;
    B(4, i) = 0;
    B(8, i) = 0;
    B.block<3, 1>(1, i) = AA.block<1, 3>(i * 2 + 4, 0);
    B.block<3, 1>(5, i) = AA.block<1, 3>(i * 2 + 4, 3);
    B.block<4, 1>(9, i) = AA.block<1, 4>(i * 2 + 4, 6);
    B.block<3, 1>(0, i) -= AA.block<1, 3>(i * 2 + 5, 0);
    B.block<3, 1>(4, i) -= AA.block<1, 3>(i * 2 + 5, 3);
    B.block<4, 1>(8, i) -= AA.block<1, 4>(i * 2 + 5, 6);
  }

  // Step 5: Extraction of roots from the degree 10 polynomial.
  Eigen::Matrix<double, 11, 1> coeffs;
#include "estimators/essential_matrix_coeffs.h"

  Eigen::VectorXd roots_real;
  Eigen::VectorXd roots_imag;
  if (!FindPolynomialRootsCompanionMatrix(coeffs, &roots_real, &roots_imag)) {
    return {};
  }

  std::vector<M_t> models;
  models.reserve(roots_real.size());

  for (Eigen::VectorXd::Index i = 0; i < roots_imag.size(); ++i) {
    const double kMaxRootImag = 1e-10;
    if (std::abs(roots_imag(i)) > kMaxRootImag) {
      continue;
    }

    const double z1 = roots_real(i);
    const double z2 = z1 * z1;
    const double z3 = z2 * z1;
    const double z4 = z3 * z1;

    Eigen::Matrix3d Bz;
    for (size_t j = 0; j < 3; ++j) {
      Bz(j, 0) = B(0, j) * z3 + B(1, j) * z2 + B(2, j) * z1 + B(3, j);
      Bz(j, 1) = B(4, j) * z3 + B(5, j) * z2 + B(6, j) * z1 + B(7, j);
      Bz(j, 2) = B(8, j) * z4 + B(9, j) * z3 + B(10, j) * z2 + B(11, j) * z1 +
                 B(12, j);
    }

    const Eigen::JacobiSVD<Eigen::Matrix3d> svd(Bz, Eigen::ComputeFullV);
    const Eigen::Vector3d X = svd.matrixV().block<3, 1>(0, 2);

    const double kMaxX3 = 1e-10;
    if (std::abs(X(2)) < kMaxX3) {
      continue;
    }

    Eigen::MatrixXd essential_vec = E.col(0) * (X(0) / X(2)) +
                                    E.col(1) * (X(1) / X(2)) + E.col(2) * z1 +
                                    E.col(3);
    essential_vec /= essential_vec.norm();

    const Eigen::Matrix3d essential_matrix =
        Eigen::Map<Eigen::Matrix<double, 3, 3, Eigen::RowMajor>>(
            essential_vec.data());
    models.push_back(essential_matrix);
  }

  return models;
}

void EssentialMatrixFivePointEstimator::Residuals(
    const std::vector<X_t>& points1, const std::vector<Y_t>& points2,
    const M_t& E, std::vector<double>* residuals) {
  ComputeSquaredSampsonError(points1, points2, E, residuals);
}

std::vector<EssentialMatrixEightPointEstimator::M_t>
EssentialMatrixEightPointEstimator::Estimate(const std::vector<X_t>& points1,
                                             const std::vector<Y_t>& points2) {
  CHECK_EQ(points1.size(), points2.size());

  // Center and normalize image points for better numerical stability.
  std::vector<X_t> normed_points1;
  std::vector<Y_t> normed_points2;
  Eigen::Matrix3d points1_norm_matrix;
  Eigen::Matrix3d points2_norm_matrix;
  CenterAndNormalizeImagePoints(points1, &normed_points1, &points1_norm_matrix);
  CenterAndNormalizeImagePoints(points2, &normed_points2, &points2_norm_matrix);

  // Setup homogeneous linear equation as x2' * F * x1 = 0.
  Eigen::Matrix<double, Eigen::Dynamic, 9> cmatrix(points1.size(), 9);
  for (size_t i = 0; i < points1.size(); ++i) {
    cmatrix.block<1, 3>(i, 0) = normed_points1[i].homogeneous();
    cmatrix.block<1, 3>(i, 0) *= normed_points2[i].x();
    cmatrix.block<1, 3>(i, 3) = normed_points1[i].homogeneous();
    cmatrix.block<1, 3>(i, 3) *= normed_points2[i].y();
    cmatrix.block<1, 3>(i, 6) = normed_points1[i].homogeneous();
  }

  // Solve for the nullspace of the constraint matrix.
  Eigen::JacobiSVD<Eigen::Matrix<double, Eigen::Dynamic, 9>> cmatrix_svd(
      cmatrix, Eigen::ComputeFullV);
  const Eigen::VectorXd ematrix_nullspace = cmatrix_svd.matrixV().col(8);
  const Eigen::Map<const Eigen::Matrix3d> ematrix_t(ematrix_nullspace.data());

  // De-normalize to image points.
  const Eigen::Matrix3d E_raw = points2_norm_matrix.transpose() *
                                ematrix_t.transpose() * points1_norm_matrix;

  // Enforcing the internal constraint that two singular values must be equal
  // and one must be zero.
  Eigen::JacobiSVD<Eigen::Matrix3d> E_raw_svd(
      E_raw, Eigen::ComputeFullU | Eigen::ComputeFullV);
  Eigen::Vector3d singular_values = E_raw_svd.singularValues();
  singular_values(0) = (singular_values(0) + singular_values(1)) / 2.0;
  singular_values(1) = singular_values(0);
  singular_values(2) = 0.0;
  const Eigen::Matrix3d E = E_raw_svd.matrixU() * singular_values.asDiagonal() *
                            E_raw_svd.matrixV().transpose();

  const std::vector<M_t> models = {E};
  return models;
}

void EssentialMatrixEightPointEstimator::Residuals(
    const std::vector<X_t>& points1, const std::vector<Y_t>& points2,
    const M_t& E, std::vector<double>* residuals) {
  ComputeSquaredSampsonError(points1, points2, E, residuals);
}

}  // namespace colmap


/*
 * Copyright [2019] [Christopher Syben]
 *
 * 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.
 *
 * Links the cone-beam projector layer from python to the actual kernel implementation. Implemented according to Tensorflow API.
 * Implementation partially adapted from CONRAD
 * PYRO-NN is developed as an Open Source project under the Apache License, Version 2.0.
*/
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "../helper_headers/helper_geometry_cpu.h"
#include "../helper_headers/helper_eigen.h"

#include <cuda_runtime_api.h>
#include <cuda.h>
#include <Eigen/QR>

#include <typeinfo>
using namespace tensorflow; // NOLINT(build/namespaces)

#define CUDA_OPERATOR_KERNEL "ConeProjection3D"
REGISTER_OP(CUDA_OPERATOR_KERNEL)
    .Input("volume: float")
    .Input("projection_shape: int32")
    .Input("volume_origin : float")
    .Input("volume_spacing : float")
    .Input("projection_matrices : float")
    .Input("step_size: float")
    .Input("projection_multiplier : float")
    .Attr("hardware_interp : bool = false")
    .Output("output: float")
    .SetShapeFn( []( ::tensorflow::shape_inference::InferenceContext* c )
    {
        ::tensorflow::shape_inference::ShapeHandle batch;
        ::tensorflow::shape_inference::ShapeHandle dim;
        ::tensorflow::shape_inference::ShapeHandle out;
        TF_RETURN_IF_ERROR(c->MakeShapeFromShapeTensor(1, &dim));
        TF_RETURN_IF_ERROR(c->Subshape(c->input(0),0,1,&batch));
        TF_RETURN_IF_ERROR(c->Concatenate(batch,dim,&out));
        c->set_output( 0,out);
        return Status::OK();
    } )
    .Doc(R"doc(
Computes the 3D cone forward projection of the input based on the given the trajectory

output: A Tensor.
  output = A_cone * x
)doc");

void Cone_Projection_Kernel_Launcher(const float *volume_ptr, float *out, const float *inv_AR_matrix, const float *src_points, const int number_of_projections,
                                    const int volume_width, const int volume_height, const int volume_depth,
                                    const float *volume_spacing,
                                    const int detector_width, const int detector_height, const float *step_size);

void Cone_Projection_Kernel_Tex_Interp_Launcher(const float *volume_ptr, float *out, const float *inv_AR_matrix, const float *src_points,
                                                const int number_of_projections, const int volume_width, const int volume_height,
                                                const int volume_depth, const float *volume_spacing,
                                                const int detector_width, const int detector_height, const float *step_size);

class ConeProjection3DOp : public OpKernel
{
    bool hardware_interp;

    Eigen::Tensor<float, 3, Eigen::RowMajor> inv_AR_matrix;
    Eigen::Tensor<float, 2, Eigen::RowMajor> src_points;

  public:

    explicit ConeProjection3DOp(OpKernelConstruction *context) : OpKernel(context)
    {
        OP_REQUIRES_OK(context, context->GetAttr("hardware_interp", &hardware_interp));
    }

    /*
    https://github.com/tensorflow/tensorflow/issues/5902
    tensorflow::GPUBFCAllocator* allocator = new tensorflow::GPUBFCAllocator(0, sizeof(float) * height * width * 3);
    tensorflow::Tensor input_tensor = tensorflow::Tensor(allocator, tensorflow::DataType::DT_FLOAT, tensorflow::TensorShape( { 1, height, width, 3 }));
    <copy output data from program A into the GPU memory allocated by input_tensor using a GPU->GPU copy>

    https://stackoverflow.com/questions/39797095/tensorflow-custom-allocator-and-accessing-data-from-tensor

    */
   void DecomposeProjectionMatrices( const float *projection_matrices, const float *volume_spacing, const float *volume_origin, const int number_of_projections){
       /*********************************************************************************************************************************************************************
         *
         *  P = [M | -MC]
         *  M = KR
         * 1. Extract Source Position (C) from P using SVD to calc right null space
         * 2. Calculate M^-1 and multiply with a 3x3 Matrix containing 1/voxel_spacing on the diagonal matrix
         * 3. Put src_points and inv_ar_matrix into the CUDA Kernel, like Cone-Projector from Conrad
         *
         * WARNING: The following code is not created under memory and runtime performance point of view.
         *          A better conversion from Tensorflow Tensor to Eigen::Tensor and Eigen::Matrizes are probably neccessary !!!!
         * WARNING: to support dynamic projection matrices the decomposition must be performed before each layer evaluation. Current
         *          implementation is CPU based, which means that the matrices are copied from device memory to host memory each evaluation call.
         *          TODO: Matrix decomposition should be done on GPU and non dynamic matrices would allow to decompose matrices during
         *                construction time.
         * ********************************************************************************************************************************************************************/

        // projection matrices stuff
		src_points = Eigen::Tensor<float, 2, Eigen::RowMajor>(number_of_projections, 3);
        inv_AR_matrix = Eigen::Tensor<float, 3, Eigen::RowMajor>(number_of_projections, 3, 3);

        // Copy spacing from device to host and check if sucessful
        float volume_spacing_host_ptr[3];
        {
            auto code = cudaMemcpy(&volume_spacing_host_ptr[0], volume_spacing, 3*sizeof(float), cudaMemcpyDeviceToHost);
            if (code != cudaSuccess)
            {
                fprintf(stderr,"GPUassert: %s \n", cudaGetErrorString(code));
                exit(code);
            }
        }
        // Copy origin from device to host and check if sucessful
        float volume_origin_host_ptr[3];
        {
            auto code = cudaMemcpy(&volume_origin_host_ptr[0], volume_origin, 3*sizeof(float), cudaMemcpyDeviceToHost);
            if (code != cudaSuccess)
            {
                fprintf(stderr,"GPUassert: %s \n", cudaGetErrorString(code));
                exit(code);
            }
        }
        // Copy projection matrices to host and check if sucessful
        auto matrices_size = number_of_projections * 12 * sizeof(float);
        float * projection_matrices_host_ptr = new float[matrices_size];
        {
            auto code = cudaMemcpy(projection_matrices_host_ptr, projection_matrices, matrices_size, cudaMemcpyDeviceToHost);
            if (code != cudaSuccess)
            {
                fprintf(stderr,"GPUassert: %s \n", cudaGetErrorString(code));
                exit(code);
            }
        }
        Eigen::Matrix3f scaling_matrix(3,3);
        scaling_matrix.setZero();
        scaling_matrix(0,0) = 1.0/volume_spacing_host_ptr[2];
        scaling_matrix(1,1) = 1.0/volume_spacing_host_ptr[1];
        scaling_matrix(2,2) = 1.0/volume_spacing_host_ptr[0];
        src_points.setZero();
        inv_AR_matrix.setZero();

        //for each projection
        for (int n = 0; n < number_of_projections; n++)
        {
            const float* matrix = &(projection_matrices_host_ptr[n*12]);
            Eigen::Matrix<float,3,4,Eigen::RowMajor> proj_mat(3,4);
            proj_mat << matrix[0], matrix[1], matrix[2 ], matrix[3 ],
                        matrix[4], matrix[5], matrix[6 ], matrix[7 ],
                        matrix[8], matrix[9], matrix[10], matrix[11];

            auto c = (Geometry::getCameraCenter(proj_mat) * -1).eval();

            src_points(n,0) = -((volume_origin_host_ptr[ 2 ] * scaling_matrix(0,0)) + c(0) * scaling_matrix(0,0));
            src_points(n,1) = -((volume_origin_host_ptr[ 1 ] * scaling_matrix(1,1)) + c(1) * scaling_matrix(1,1));
            src_points(n,2) = -((volume_origin_host_ptr[ 0 ] * scaling_matrix(2,2)) + c(2) * scaling_matrix(2,2));

            Eigen::Matrix<float,3,3, Eigen::RowMajor> inverted_scaled_result = (scaling_matrix * proj_mat.block<3,3>(0,0).inverse()).eval();

            //TODO: dont copy element-wise use Eigen::Map to map eigen::matrix to eigen::tensor
            for(int j = 0; j < inverted_scaled_result.cols();++j){
                for(int i = 0; i < inverted_scaled_result.rows(); ++i){
                    inv_AR_matrix(n,j,i) = inverted_scaled_result(j,i);
                }
            }
        }
        // free cpu mem
        delete[] projection_matrices_host_ptr;
   }

    void Compute(OpKernelContext *context) override
    {
        // Grab the input Tensor (Volume).  Assuming input Tensor with [batch, height, width]
        const Tensor &input_tensor = context->input(0);
        auto input = input_tensor.flat_outer_dims<float>();
        TensorShape input_shape = input_tensor.shape();
        int batch_size = input_tensor.shape().dim_size(0);

        // Grab the projection_shape Tensor.
        const Tensor &input_projection_shape = context->input(1);

        // Grab the volume_origin Tensor.
        const Tensor &input_volume_origin = context->input(2);
        auto volume_origin = input_volume_origin.flat_outer_dims<float>();

        // Grab the volumen_spacing Tensor.
        const Tensor &input_volume_spacing = context->input(3);
        auto volume_spacing = input_volume_spacing.flat_outer_dims<float>();

        // Grab the ray_vector Tensor.  Assuming input Tensor with [batch, ray_vectors]
        const Tensor &input_proj_matrices = context->input(4);
        auto proj_matrices = input_proj_matrices.flat_outer_dims<float>();
        TensorShape proj_matrices_shape = input_proj_matrices.shape();

        // Grab the detector_spacing Tensor.
        const Tensor &input_step_size = context->input(5);
        auto step_size = input_step_size.flat_outer_dims<float>();

        // Grab the detector_spacing Tensor.
        const Tensor &input_projection_multiplier = context->input(6);
        auto proj_multiplier = input_projection_multiplier.flat_outer_dims<float>();

        // Copy information on output shape to host memory.
        // Implicit assumption that the input shape is consistent over the batch !
        int sino_shape_host_ptr[3];
        auto code = cudaMemcpy(&sino_shape_host_ptr[0], input_projection_shape.flat<int>().data(), 3*sizeof(int), cudaMemcpyDeviceToHost);
        int detector_width =  sino_shape_host_ptr[ 2 ];
        int detector_height =  sino_shape_host_ptr[ 1 ];
        int number_of_projections = sino_shape_host_ptr[ 0 ];

        // Create output shape: [batch_size, projection_shape, detector_width] <--- This is the reason why we need consistent shapes in the batch
        TensorShape out_shape = TensorShape({batch_size, number_of_projections, detector_height, detector_width});
        Tensor *output_tensor = nullptr;
        OP_REQUIRES_OK(context, context->allocate_output(0, out_shape,
                                                         &output_tensor));

        auto output = output_tensor->flat_outer_dims<float>();

        // Call the cuda kernel launcher
        for(int index = 0; index < batch_size; ++index){
            DecomposeProjectionMatrices(&proj_matrices(index,0), &volume_spacing(index,0), &volume_origin(index,0), number_of_projections);
            if(hardware_interp){
                Cone_Projection_Kernel_Tex_Interp_Launcher(&input(index,0), &output(index,0), inv_AR_matrix.data(), src_points.data(), number_of_projections,
                                            input_shape.dim_size(3), input_shape.dim_size(2), input_shape.dim_size(1), &volume_spacing(index,0),
                                            detector_width, detector_height, &step_size(index,0));
            }
            else{
                // Call the cuda kernel launcher
                Cone_Projection_Kernel_Launcher(&input(index,0), &output(index,0), inv_AR_matrix.data(), src_points.data(), number_of_projections,
                                            input_shape.dim_size(3), input_shape.dim_size(2), input_shape.dim_size(1),&volume_spacing(index,0),
                                            detector_width, detector_height, &step_size(index,0));
            }
        }
    }
};

REGISTER_KERNEL_BUILDER(Name(CUDA_OPERATOR_KERNEL).Device(DEVICE_GPU), ConeProjection3DOp);


//------------------------------------------------------------------------------
/*
    This file is part of rippled: https://github.com/ripple/rippled
    Copyright (c) 2012, 2013 Ripple Labs Inc.

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

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

#include <BeastConfig.h>
#include <boost/algorithm/string.hpp>
#include <ripple/app/tx/impl/PaymentCT.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/protocol/Indexes.h>

#include <secp256k1.h>
#include <secp256k1_rangeproof.h>

namespace ripple {

TER
PaymentCT::preflight (PreflightContext const& ctx)
{
    // check vbc only
    STAmount amount = ctx.tx.getFieldAmount(sfAmount);
    if(!isVBC(amount)){
        return temBAD_AMOUNT;
    }
    return tesSUCCESS;
}

TER
DepositCT::preclaim(PreclaimContext const& ctx){
    return tesSUCCESS;

}

TER
DepositCT::doApply()
{
    auto& app = ctx_.app;
    const uint32_t curLgrSeq = view().info().seq;

    STAmount const saAmount (ctx_.tx.getFieldAmount (sfAmount));
    uint256 const txPubKey(ctx_.tx.getFieldU256(sfTxPublicKey)); // tx_private_key(random)
    STArray const commits (ctx_.tx.getFieldArray (sfCommitments));

    static secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);

    for(auto const commit : commits){
        if(commit.getFieldU8(sfCommitType) == CommitType::paymentCommit){
            //paymentCommit
        }
        uint256 blind = commit.getFieldU256(sfMaskCT);
        uint256 commitment = commit.getFieldU256(sfCommitment);
        uint64_t shieldValue = commit.getFieldU64(sfShieldValue);
        Blob proof = commit.getFieldVL(sfProof);

        // verify
        uint64_t minv = 0;
        uint64_t maxv = MAX_UINT64 - 1;
        int len = sizeof(proof);
        if(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commitment, proof, len, NULL, 0, secp256k1_generator_h) == 0){
            JLOG(j.error) << "Rangeproof verify error";
            return tefNO_AUTH_REQUIRED;
        }
    }

    // set account ctsle to change mask/value

    // create dst sle
    auto k = keylet::ct(commit, curLgrSeq)
    auto dstSle = view()->peek(k);
    if(dstSle){
        JLOG(j.error) << "commitment sle exists";
        return tefINTERNAL;
    }else{
        auto sle = std::make_shared<SLE>(k);
        sle->setFieldU256(sfMaskCT, );
        sle->setFieldU256(sfShieldValue);
    }

    return tesSUCCESS;
}

}// ripple