//          Copyright Gavin Band 2008 - 2012.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <utility>
#include <string>
#include <boost/bind.hpp>
#include <Eigen/Core>
#include "genfile/VariantIdentifyingData.hpp"
#include "genfile/VariantEntry.hpp"
#include "genfile/VariantDataReader.hpp"
#include "genfile/vcf/get_set.hpp"
#include "components/SNPSummaryComponent/ClusterFitComputation.hpp"
#include "metro/DataRange.hpp"
#include "metro/DataSubset.hpp"
#include "metro/likelihood/MultivariateT.hpp"
#include "metro/likelihood/Mixture.hpp"
#include "metro/ValueStabilisesStoppingCondition.hpp"
#include "metro/log_sum_exp.hpp"

// #define DEBUG_CLUSTERFITCOMPUTATION 1

namespace stats {
	ClusterFitComputation::ClusterFitComputation(
		double nu,
		double regularisationVariance,
		double regularisationWeight,
		double call_threshhold
	):
		m_call_threshhold( call_threshhold ),
		m_nu( nu ),
		m_scale( "X:Y" ),
		m_xAxisName( "X" ),
		m_yAxisName( "Y" ),
		m_regularisingSigma( regularisationVariance * Eigen::MatrixXd::Identity( 2, 2 ) ),
		m_regularisingWeight( regularisationWeight )
	{}

	namespace {
		metro::DataSubset compute_data_subset( std::size_t N, boost::function< bool( std::size_t ) > predicate ) {
			metro::DataSubset result ;
			bool in_range = false ;
			int start_of_range = 0 ;
			for( int i = 0; i < N; ++i ) {
				bool in = predicate( i ) ;
				if( in ) {
					if( !in_range ) {
						start_of_range = i ;
						in_range = true ;
					}
				} else {
					if( in_range ) {
						result.add( metro::DataRange( start_of_range, i )) ;
						in_range = false ;
					}
				}
			}
			if( in_range ) {
				result.add( metro::DataRange( start_of_range, N )) ;
			}
			return( result ) ;
		}

		bool nonmissing_genotypes_and_intensities(
			std::size_t i,
			int g,
			Eigen::MatrixXd const& genotypes,
			Eigen::MatrixXd const& nonmissingness,
			double call_threshhold
		) {
			// return true if the intensities are there
			// and the genotype meets a hard call threshhold.
			return ( nonmissingness.row(i).sum() == 2 )
				&& (
					g < 3
					? ( genotypes( i, g ) > call_threshhold )
					: ( genotypes.row(i).maxCoeff() < call_threshhold )
				)
			;
		}

		bool nonmissing_intensities(
			std::size_t i,
			Eigen::MatrixXd const& nonmissingness
		) {
			return ( nonmissingness.row(i).sum() == 2 ) ;
		}
	}

	void ClusterFitComputation::set_scale( std::string const& scale ) {
		if( scale == "X:Y" ) {
			m_xAxisName = "X" ;
			m_yAxisName = "Y" ;
		} else if( scale == "contrast:logR" ) {
			m_xAxisName = "contrast" ;
			m_yAxisName = "logR" ;
		} else {
			throw genfile::BadArgumentError(
				"ClusterFitComputation::set_transform()",
				"transform=\"" + scale + "\"",
				"Unrecognised transform.  Use \"X:Y\" or \"contrast:logR\""
			) ;
		}
		m_scale = scale ;
	}

	void ClusterFitComputation::operator()(
		VariantIdentifyingData const& snp,
		Genotypes const& genotypes,
		Ploidy const&,
		genfile::VariantDataReader& data_reader,
		ResultCallback callback
	) {
		if( !data_reader.supports( "XY" )) {
			return ;
		}

		int const N = genotypes.rows() ;

		{
			m_intensities.setZero() ;
			m_nonmissingness.setZero() ;
			genfile::vcf::MatrixSetter< IntensityMatrix > intensity_setter( m_intensities, m_nonmissingness ) ;
			data_reader.get( "XY", intensity_setter ) ;
			assert( m_intensities.rows() == N ) ;
			assert( m_intensities.cols() == 2 ) ;
			assert( m_nonmissingness.rows() == N ) ;
			assert( m_nonmissingness.cols() == 2 ) ;
		}

#if DEBUG_CLUSTERFITCOMPUTATION
		std::cerr << "At " << snp << ":\n"
			<< "genotypes =\n"
			<< genotypes.block( 0, 0, 10, 3 )
			<< "\n, intensities =\n"
			<< m_intensities.block( 0, 0, 10, 2 )
			<< ".\n" ;
#endif

		if( m_scale == "X:Y" ) {
			// do nothing.
		} else if( m_scale == "contrast:logR" ) {
			Eigen::MatrixXd transform( 2, 2 ) ;
			transform <<
				1, 1,
				-1, 1
			;
			m_intensities *= transform ;
			m_intensities.col(0).array() /= m_intensities.col(1).array() ;
			m_intensities.col(1).array() = m_intensities.col(1).array().log() ;
		} else {
			assert(0) ;
		}

		// We compute log-likelihood under a model which is a equally-weighted
		// mixture of the three clusters.
		// For this purpose we record the parameters etc.
		Eigen::MatrixXd parameters = Eigen::MatrixXd::Constant( 5, 3, std::numeric_limits< double >::quiet_NaN() ) ;
		Eigen::VectorXd counts = Eigen::VectorXd::Zero( 3 ) ;
		Eigen::VectorXd genotypeLLs = Eigen::VectorXd::Zero( 3 ) ;
		metro::DataSubset const nonMissingIntensitiesSubset = compute_data_subset(
			N,
			boost::bind( &nonmissing_intensities, _1, m_nonmissingness )
		) ;

		// We also compute the log-likelihood P( intensities | clusters ) on the set of samples that have genotypes
		// and compare it with the loglikelihood
		// P( genotypes, intensities | clusters ) = P( intensities | genotypes, clusters ) P( genotypes | clusters )
		// If the latter is much bigger than the former, this indicates the genotypes add a lot of information, which
		// might indicate a badly clustered SNP.
		metro::DataSubset nonMissingGenotypesAndIntensitySubset ;

		metro::likelihood::Mixture< double, Eigen::VectorXd, Eigen::MatrixXd > mixture( m_intensities ) ;

		int numberOfClusters = 0 ;

		callback( "clustering-scale", m_scale ) ;

#if DEBUG_CLUSTERFITCOMPUTATION
				std::cerr << "snp: " << snp << ".\n" ;
#endif

		for( int g = 0; g < 3; ++g ) {
			std::string const stub = "g=" + ( g == 3 ? std::string( "NA" ) : genfile::string_utils::to_string( g ) ) ;
			metro::DataSubset subset = compute_data_subset(
				N,
				boost::bind(
					&nonmissing_genotypes_and_intensities,
					_1, g, genotypes, m_nonmissingness, m_call_threshhold
				)
			) ;

			counts(g) = subset.size() ;
			callback( stub + ":count", genfile::VariantEntry::Integer( subset.size() ) ) ;

			typedef metro::likelihood::MultivariateT< double, Eigen::VectorXd, Eigen::MatrixXd > Cluster ;
			Cluster::UniquePtr cluster( new Cluster( m_intensities, m_nu ) ) ;
			metro::ValueStabilisesStoppingCondition stoppingCondition( 0.01, 100 ) ;

			if( cluster->estimate_by_em( subset, stoppingCondition, m_regularisingSigma, m_regularisingWeight ) ) {
				callback( stub + ":nu", m_nu ) ;
				callback( stub + ":mu_" + m_xAxisName, cluster->mean()(0) ) ;
				callback( stub + ":mu_" + m_yAxisName, cluster->mean()(1) ) ;
				callback( stub + ":sigma_" + m_xAxisName + m_xAxisName, cluster->sigma()(0,0) ) ;
				callback( stub + ":sigma_" + m_xAxisName + m_yAxisName, cluster->sigma()(1,0) ) ;
				callback( stub + ":sigma_" + m_yAxisName + m_yAxisName, cluster->sigma()(1,1) ) ;

				nonMissingGenotypesAndIntensitySubset.add( subset ) ;

				++numberOfClusters ;

				genotypeLLs(g) = cluster->get_value_of_function() ;

#if DEBUG_CLUSTERFITCOMPUTATION
				std::cerr << "cluster " << g << ", "
					<< "subset: " << subset << "\n"
					<< "count = " << subset.size()
					<< ", parameters = " << std::setprecision( 4 ) << cluster->parameters().transpose()
					<< ", ll = " << cluster->get_value_of_function() << ".\n" ;
#endif

				mixture.add_component( stub, 1, cluster ) ;
			} else {
#if DEBUG_CLUSTERFITCOMPUTATION
				std::cerr << "!! No convergence.\n" ;
#endif
				if( subset.size() > 0 ) {
					std::cerr << "!! For SNP " << snp << ", cluster " << g << " has size " << subset.size() << " but distribution did not converge.\n" ;
				}
				genfile::MissingValue const NA = genfile::MissingValue();
				callback( stub + ":nu", NA ) ;
				callback( stub + ":mu_" + m_xAxisName, NA ) ;
				callback( stub + ":mu_" + m_yAxisName, NA ) ;
				callback( stub + ":sigma_" + m_xAxisName + m_xAxisName, NA ) ;
				callback( stub + ":sigma_" + m_xAxisName + m_yAxisName, NA ) ;
				callback( stub + ":sigma_" + m_yAxisName + m_yAxisName, NA ) ;
			}

			callback( stub + ":iterations", genfile::VariantEntry::Integer( stoppingCondition.iterations() ) ) ;
		}

		// Now output the loglikelihoods under a model conditional on genotype...
		callback( "number-of-clusters", numberOfClusters ) ;
		callback( "informative-sample-count", genfile::VariantEntry::Integer( nonMissingGenotypesAndIntensitySubset.size() ) ) ;
		callback( "total-sample-count", genfile::VariantEntry::Integer( genotypes.rows() )) ;
		callback( "ll-given-genotype",  genotypeLLs.sum() ) ;
		// And under equal-weighted mixtures...
		mixture.set_data( m_intensities ) ;
		mixture.evaluate_at( mixture.parameters(), nonMissingGenotypesAndIntensitySubset ) ;
		double const mixtureLL = mixture.get_value_of_function() ;
#if DEBUG_CLUSTERFITCOMPUTATION
		std::cerr << "numberOfClusters = " << numberOfClusters << "\n"
			<< "informative sample count = " << nonMissingGenotypesAndIntensitySubset.size() << "\n" ;
		std::cerr << "non-missing intensities subset count = " << nonMissingIntensitiesSubset.size() << "\n" ;
		std::cerr << "Mixture parameters = " << mixture.parameters().transpose() << ".\n" ;
		std::cerr << "mixtureLL = " << mixtureLL << ".\n" ;
		Eigen::VectorXd terms( m_intensities.rows() ) ;
		mixture.get_terms_of_function( terms ) ;
		std::cerr << "terms = " << terms.transpose() << ".\n" ;
#endif
		callback( "equal-weighted-mixture:genotyped_samples:ll", mixtureLL ) ;
		mixture.evaluate_at( mixture.parameters(), nonMissingIntensitiesSubset ) ;
		callback( "equal-weighted-mixture:all_samples:ll",  mixture.get_value_of_function() ) ;
		callback(
			"ll-comparison",
			( genotypeLLs.sum() - ( nonMissingGenotypesAndIntensitySubset.size() * std::log( numberOfClusters )) - mixtureLL )
		) ;
	}

	std::string ClusterFitComputation::get_summary( std::string const& prefix, std::size_t column_width ) const {
		return prefix + "ClusterFitComputation" ;
	}
}


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

// Copyright (c) 2017 Barend Gehrels, Amsterdam, the Netherlands.

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

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_SELF_TURNS_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_SELF_TURNS_HPP

#include <boost/range.hpp>

#include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
#include <boost/geometry/algorithms/within.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace overlay
{

struct discard_turns
{
    template <typename Turns, typename Geometry0, typename Geometry1>
    static inline
    void apply(Turns& , Geometry0 const& , Geometry1 const& )
    {}
};

template <overlay_type OverlayType, operation_type OperationType>
struct discard_closed_turns : discard_turns {};

// It is only implemented for operation_union, not in buffer
template <>
struct discard_closed_turns<overlay_union, operation_union>
{

    template <typename Turns, typename Geometry0, typename Geometry1>
    static inline
    void apply(Turns& turns,
            Geometry0 const& geometry0, Geometry1 const& geometry1)
    {
        typedef typename boost::range_value<Turns>::type turn_type;

        for (typename boost::range_iterator<Turns>::type
                it = boost::begin(turns);
             it != boost::end(turns);
             ++it)
        {
            turn_type& turn = *it;

            if (turn.cluster_id >= 0
                    || turn.discarded
                    || ! is_self_turn<overlay_union>(turn))
            {
                continue;
            }

            bool const within =
                    turn.operations[0].seg_id.source_index == 0
                    ? geometry::within(turn.point, geometry1)
                    : geometry::within(turn.point, geometry0);

            if (within)
            {
                // It is in the interior of the other geometry
                turn.discarded = true;
            }
        }
    }
};

struct discard_self_intersection_turns
{
    template <typename Turns, typename Geometry0, typename Geometry1>
    static inline
    void apply(Turns& turns,
            Geometry0 const& geometry0, Geometry1 const& geometry1)
    {
        typedef typename boost::range_value<Turns>::type turn_type;

        for (typename boost::range_iterator<Turns>::type
                it = boost::begin(turns);
             it != boost::end(turns);
             ++it)
        {
            turn_type& turn = *it;

            if (turn.cluster_id >= 0
                    || turn.discarded
                    || ! is_self_turn<overlay_intersection>(turn))
            {
                continue;
            }

            segment_identifier const& id0 = turn.operations[0].seg_id;
            segment_identifier const& id1 = turn.operations[1].seg_id;
            if (id0.multi_index != id1.multi_index
                    || (id0.ring_index == -1 && id1.ring_index == -1)
                    || (id0.ring_index >= 0 && id1.ring_index >= 0))
            {
                // Not an ii ring (int/ext) on same ring
                continue;
            }

            // It is a non co-located ii self-turn
            // Check if it is within the other geometry
            // If not, it can be ignored

            bool const within =
                    turn.operations[0].seg_id.source_index == 0
                    ? geometry::within(turn.point, geometry1)
                    : geometry::within(turn.point, geometry0);

            if (! within)
            {
                // It is not within another geometry, discard the turn
                turn.discarded = true;
            }
        }
    }
};

template <overlay_type OverlayType, operation_type OperationType>
struct discard_open_turns : discard_turns {};

// Handler it for intersection
template <>
struct discard_open_turns<overlay_intersection, operation_intersection>
        : discard_self_intersection_turns {};

// For difference, it should be done in a different way (TODO)

}} // namespace detail::overlay
#endif //DOXYGEN_NO_DETAIL

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_SELF_TURNS_HPP


// Copyright 2015-2018 Hans Dembinski
//
// 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)

#ifndef BOOST_HISTOGRAM_DETAIL_AT_HPP
#define BOOST_HISTOGRAM_DETAIL_AT_HPP

#include <boost/histogram/axis/option.hpp>
#include <boost/histogram/axis/traits.hpp>
#include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/linearize.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/algorithm.hpp>
#include <tuple>

namespace boost {
namespace histogram {
namespace detail {

template <class A, class... Us>
optional_index at(const A& axes, const std::tuple<Us...>& args) noexcept {
  optional_index idx{0}; // offset not used by linearize_index
  mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Us)>>(
      [&, stride = static_cast<std::size_t>(1)](auto i) mutable {
        stride *= linearize_index(idx, stride, axis_get<i>(axes),
                                  static_cast<axis::index_type>(std::get<i>(args)));
      });
  return idx;
}

template <class A, class U>
optional_index at(const A& axes, const U& args) noexcept {
  optional_index idx{0};
  using std::begin;
  for_each_axis(axes, [&, it = begin(args),
                       stride = static_cast<std::size_t>(1)](const auto& a) mutable {
    stride *= linearize_index(idx, stride, a, static_cast<axis::index_type>(*it++));
  });
  return idx;
}

} // namespace detail
} // namespace histogram
} // namespace boost

#endif


#include <boost/hana/fwd/concept/group.hpp>


/*
 * Copyright (c) 2011-2019, The DART development contributors
 * All rights reserved.
 *
 * The list of contributors can be found at:
 *   https://github.com/dartsim/dart/blob/master/LICENSE
 *
 * This file is provided under the following "BSD-style" License:
 *   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.
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 *   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 *   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *   POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef DART_MATH_MATHTYPES_HPP_
#define DART_MATH_MATHTYPES_HPP_

#include <map>
#include <vector>

#include <Eigen/Dense>

#include "dart/common/Deprecated.hpp"
#include "dart/common/Memory.hpp"

// You can turn on DART_USE_ARBITRARY_PRECISION as a variable in the root
// CMakeLists.txt file.

#ifdef DART_USE_ARBITRARY_PRECISION
#include <unsupported/Eigen/MPRealSupport>
#include "mpreal.h"
typedef mpfr::mpreal s_t;
#else
typedef double s_t;
using std::abs;
using std::ceil;
using std::cos;
using std::floor;
using std::isfinite;
using std::isnan;
using std::max;
using std::pow;
using std::sin;
#endif

//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
namespace Eigen {

using Vector6s = Matrix<s_t, 6, 1>;
using Matrix6s = Matrix<s_t, 6, 6>;

typedef Matrix<s_t, Dynamic, Dynamic> MatrixXs;
typedef Matrix<s_t, Dynamic, 1> VectorXs;
typedef Matrix<s_t, 2, 1> Vector2s;
typedef Matrix<s_t, 3, 1> Vector3s;
typedef Matrix<s_t, 4, 1> Vector4s;
typedef Matrix<s_t, 5, 1> Vector5s;
typedef Matrix<s_t, 6, 1> Vector6s;
typedef Matrix<s_t, 2, 2> Matrix2s;
typedef Matrix<s_t, 3, 3> Matrix3s;
typedef Matrix<s_t, 4, 4> Matrix4s;
typedef Matrix<s_t, 5, 5> Matrix5s;
typedef Matrix<s_t, 6, 6> Matrix6s;
typedef Transform<s_t, 2, Isometry> Isometry2s;
typedef Transform<s_t, 3, Isometry> Isometry3s;
typedef Transform<s_t, 3, Affine> Affine3s;
typedef Quaternion<s_t> Quaternion_s;
typedef AngleAxis<s_t> AngleAxis_s;
typedef Translation<s_t, 3> Translation3s;

inline Vector6s compose(
    const Eigen::Vector3s& _angular, const Eigen::Vector3s& _linear)
{
  Vector6s composition;
  composition << _angular, _linear;
  return composition;
}

// Deprecated
using EIGEN_V_VEC3D = std::vector<Eigen::Vector3s>;

// Deprecated
using EIGEN_VV_VEC3D = std::vector<std::vector<Eigen::Vector3s>>;

#if EIGEN_VERSION_AT_LEAST(3, 2, 1) && EIGEN_VERSION_AT_MOST(3, 2, 8)

// Deprecated in favor of dart::common::aligned_vector
template <typename _Tp>
using aligned_vector
    = std::vector<_Tp, dart::common::detail::aligned_allocator_cpp11<_Tp>>;

// Deprecated in favor of dart::common::aligned_map
template <typename _Key, typename _Tp, typename _Compare = std::less<_Key>>
using aligned_map = std::map<
    _Key,
    _Tp,
    _Compare,
    dart::common::detail::aligned_allocator_cpp11<std::pair<const _Key, _Tp>>>;

#else

// Deprecated in favor of dart::common::aligned_vector
template <typename _Tp>
using aligned_vector = std::vector<_Tp, Eigen::aligned_allocator<_Tp>>;

// Deprecated in favor of dart::common::aligned_map
template <typename _Key, typename _Tp, typename _Compare = std::less<_Key>>
using aligned_map = std::map<
    _Key,
    _Tp,
    _Compare,
    Eigen::aligned_allocator<std::pair<const _Key, _Tp>>>;

#endif

// Deprecated in favor of dart::common::make_aligned_shared
template <typename _Tp, typename... _Args>
DART_DEPRECATED(6.2)
std::shared_ptr<_Tp> make_aligned_shared(_Args&&... __args)
{
  return ::dart::common::make_aligned_shared<_Tp, _Args...>(
      std::forward<_Args>(__args)...);
}

} // namespace Eigen

namespace dart {
namespace math {

using Inertia = Eigen::Matrix6s;
using LinearJacobian = Eigen::Matrix<s_t, 3, Eigen::Dynamic>;
using AngularJacobian = Eigen::Matrix<s_t, 3, Eigen::Dynamic>;
using Jacobian = Eigen::Matrix<s_t, 6, Eigen::Dynamic>;

} // namespace math
} // namespace dart

#endif // DART_MATH_MATHTYPES_HPP_


/*
 * ---------------------------------------------------------------------------
 * @section LICENSE
 *
 * Copyright (c) 2016 Georgia Tech Research Institute (GTRI)
 *               All Rights Reserved
 *
 * 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.
 * ---------------------------------------------------------------------------
 * @file filename.ext
 * @author Kevin DeMarco <kevin.demarco@gtri.gatech.edu>
 * @author Eric Squires <eric.squires@gtri.gatech.edu>
 * @version 1.0
 * ---------------------------------------------------------------------------
 * @brief A brief description.
 *
 * @section DESCRIPTION
 * A long description.
 * ---------------------------------------------------------------------------
 */
#include <scrimmage/motion/MotionModel.h>
#include <functional>
#include <boost/numeric/odeint.hpp>

namespace pl = std::placeholders;

namespace scrimmage {

MotionModel::MotionModel() : mass_(1.0) {}

std::string MotionModel::type() { return std::string("MotionModel"); }

bool MotionModel::init(std::map<std::string, std::string> &info, std::map<std::string, std::string> &params)
{ return false; }

bool MotionModel::step(double time, double dt) { return false; }

bool MotionModel::posthumous(double t) { return true; }

StatePtr &MotionModel::state() {return state_;}

void MotionModel::set_state(StatePtr &state) {state_ = state;}

void MotionModel::teleport(StatePtr &state) {state_ = state;}

void MotionModel::ode_step(double dt) {
    auto sys = std::bind(&MotionModel::model, this, pl::_1, pl::_2, pl::_3);
    boost::numeric::odeint::runge_kutta4<std::vector<double>> stepper;
    stepper.do_step(sys, x_, 0, dt);
}

void MotionModel::model(const MotionModel::vector_t &x, MotionModel::vector_t &dxdt, double t) {}

void MotionModel::set_external_force(Eigen::Vector3d force)
{
    ext_force_ = force;
}

}


//////////////////////////////////////////////////////////////////////////////
///   Copyright 2003 and onward LASMEA UMR 6602 CNRS/U.B.P Clermont-Ferrand
///   Copyright 2009 and onward 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
//////////////////////////////////////////////////////////////////////////////
#ifndef NT2_TOOLBOX_IEEE_FUNCTION_SIMD_COMMON_PREDECESSOR_HPP_INCLUDED
#define NT2_TOOLBOX_IEEE_FUNCTION_SIMD_COMMON_PREDECESSOR_HPP_INCLUDED
#include <nt2/sdk/meta/strip.hpp>
#include <nt2/sdk/constant/properties.hpp>
#include <nt2/sdk/constant/infinites.hpp>
#include <nt2/sdk/constant/real.hpp>
#include <nt2/sdk/meta/as_integer.hpp>
#include <nt2/sdk/constant/digits.hpp>
#include <boost/fusion/tuple.hpp>
#include <nt2/sdk/meta/strip.hpp>
#include <nt2/include/functions/select.hpp>
#include <nt2/include/functions/bitinteger.hpp>
#include <nt2/include/functions/bitfloating.hpp>
#include <nt2/include/functions/tofloat.hpp>
#include <nt2/include/functions/seladd.hpp>
#include <nt2/include/functions/select.hpp>
#include <nt2/include/functions/fast_frexp.hpp>
#include <nt2/include/functions/fast_ldexp.hpp>
#include <nt2/include/functions/is_eqz.hpp>
#include <nt2/include/functions/prev.hpp>

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is arithmetic_ unary
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::predecessor_, tag::cpu_,
                              (A0)(X),
                              ((simd_<arithmetic_<A0>,X>))
                             );

namespace nt2 { namespace ext
{
  template<class X, class Dummy>
  struct call<tag::predecessor_(tag::simd_<tag::arithmetic_, X>),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0>
      struct result<This(A0)> : meta::strip<A0>{};

    NT2_FUNCTOR_CALL(1)
      {
	return seladd(neq(a0, Valmin<A0>()), a0, Mone<A0>());
      }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is real_ unary
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::predecessor_, tag::cpu_,
                              (A0)(X),
                              ((simd_<real_<A0>,X>))
                             );

namespace nt2 { namespace ext
{
  template<class X, class Dummy>
  struct call<tag::predecessor_(tag::simd_<tag::real_, X>),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0>
      struct result<This(A0)> : meta::strip<A0>{};

    NT2_FUNCTOR_CALL(1)
      {
	return prev(a0);
      }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is arithmetic_
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::predecessor_, tag::cpu_,
		      (A0)(A1)(X),
                              ((simd_<integer_<A0>,X>))
                              ((simd_<integer_<A1>,X>))
                             );

namespace nt2 { namespace ext
{
  template<class X, class Dummy>
  struct call<tag::predecessor_(tag::simd_<tag::integer_, X> ,
                                tag::simd_<tag::integer_, X> ),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0, class A1>
      struct result<This(A0, A1)>  : meta::strip<A0>{};

    NT2_FUNCTOR_CALL(2)
    {
      return selsub( le(Valmin<A0>()+nt2::abs(a1), a0), a0, nt2::abs(a1));
    }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is real_
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::predecessor_, tag::cpu_,
		      (A0)(A1)(X),
		      ((simd_<real_<A0>,X>))
		      ((simd_<integer_<A1>,X>))
		      );

namespace nt2 { namespace ext
{
  template<class X, class Dummy>
  struct call<tag::predecessor_(tag::simd_<tag::real_, X> ,
                                tag::simd_<tag::integer_, X> ),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0,class A1>
      struct result<This(A0, A1)>  : meta::strip<A0>{};

    NT2_FUNCTOR_CALL(2)
      {
	return sel(eq(a0, Minf<A0>()), a0,  bitfloating(bitinteger(a0)-nt2::abs(a1)));
//       typedef typename meta::as_integer<A0, signed>::type itype;
//       A0 m;
//       itype expon;
//       const A0 fac =  nt2::abs(tofloat(a1));
//       boost::fusion::tie(m, expon) = fast_frexp(a0);
//       expon =  seladd(is_equal(m, Mhalf<A0>()), expon, Mone<itype>());
//       A0 diff =  fast_ldexp(One<A0>(), expon-Nbdigits<A0>());
//       diff = b_and(sel(is_eqz(diff)||is_eqz(a0),  Mindenormal<A0>(), diff), is_finite(a0));
//       return sel(is_equal(a0, Inf<A0>()), fac*Valmax<A0>(), a0-fac*diff);
      }
  };
} }

#endif
// modified by jt the 04/01/2011


#ifndef DETAIL_VALUE_WITH_ERROR_HPP
#define DETAIL_VALUE_WITH_ERROR_HPP

#include <cmath>
#include <iostream>
#include <iomanip>
#include <boost/math/special_functions/digamma.hpp>
#include <boost/math/special_functions/hypot.hpp>
#include <boost/math/tools/promotion.hpp>
#include <boost/type_traits/has_less.hpp>

#include "fwd.hpp" // for DEFAULT_POLICY_CLASS define

#if __cplusplus >= 201103L

  #include <type_traits>
  #include <initializer_list>

  // CONST is being mapped to constexpr (C++11) or const (C++98)
  #ifdef BOOST_NO_CXX11_CONSTEXPR
    #define CONSTEXPR
    #define CONST const
  #else
    #define CONST constexpr
    #define CONSTEXPR constexpr
  #endif
#else
  #include <boost/type_traits.hpp>
  #define CONST const
  #define CONSTEXPR
#endif // __cplusplus >= 201103L

namespace error_propagation {
  namespace detail {

    using boost::math::tools::promote_args;

    typedef boost::math::policies::policy<
                                          boost::math::policies::domain_error<boost::math::policies::errno_on_error>,
                                          boost::math::policies::pole_error<boost::math::policies::errno_on_error>,
                                          boost::math::policies::overflow_error<boost::math::policies::errno_on_error>,
                                          boost::math::policies::evaluation_error<boost::math::policies::errno_on_error>
                                         > policy_errno_on_error;

    // Only use hypot for types which support std::less
    template<typename T, typename U, bool has_less_op>
    struct hypot_impl;

    template<typename T, typename U>
    struct hypot_impl<T, U, false>
    {
      CONSTEXPR typename promote_args<T, U>::type
      operator()(const T& a, const U& b) const
      {
        using std::sqrt;
        return sqrt(a*a + b*b);
      }
    };

    template<typename T, typename U>
    struct hypot_impl<T, U, true>
    {
      CONSTEXPR typename promote_args<T, U>::type
      operator()(const T& a, const U& b) const
      {
        // we don't use std::hypot here as that does not know how to work with boost::multiprecision
        return boost::math::hypot(a,b,policy_errno_on_error());
      }
    };

    template<typename T, typename U>
    CONSTEXPR typename promote_args<T,U>::type
    hypot(const T& a, const U& b)
    {
      return hypot_impl<T, U, boost::has_less<T,U,bool>::value>()(a,b);
    }

    template<typename T>
    CONSTEXPR T digamma(const T& a)
    {
      return boost::math::digamma(a,policy_errno_on_error());
    }

  } // namespace detail
} // namespace error_propagation

#endif // DETAIL_VALUE_WITH_ERROR_HPP


#define BOOST_TEST_MODULE "SU2VertexSingleParticleTest"
#include <boost/test/included/unit_test.hpp>
#include "LatticeModelFactory.hpp"
#include "SU2/SU2VertexSingleParticle.hpp"

class SpinParser
{
public:
	SpinParser(Lattice *l, FrequencyDiscretization *f)
	{
		FrgCommon::_lattice = l;
		FrgCommon::_frequency = f;
	}

	~SpinParser()
	{
		delete FrgCommon::_lattice;
		delete FrgCommon::_frequency;
	}
};

struct SU2VertexSingleParticleFixture
{
	SU2VertexSingleParticleFixture()
	{
		//construct square lattice
		LatticeModelFactory::LatticeUnitCell uc;
		uc.basisSites.push_back(geometry::Vec3<double>(0.0, 0.0, 0.0));
		uc.latticeVectors.push_back(geometry::Vec3<double>(1.0, 0.0, 0.0));
		uc.latticeVectors.push_back(geometry::Vec3<double>(0.0, 1.0, 0.0));
		uc.latticeVectors.push_back(geometry::Vec3<double>(0.0, 0.0, 1.0));
		uc.latticeBonds.push_back(LatticeModelFactory::LatticeBond(0, 0, 1, 0, 0));
		uc.latticeBonds.push_back(LatticeModelFactory::LatticeBond(0, 0, 0, 1, 0));

		LatticeModelFactory::SpinModelUnitCell model;
		LatticeModelFactory::SpinInteraction i1(LatticeModelFactory::LatticeSite(0, 0, 0, 0), LatticeModelFactory::LatticeSite(1, 0, 0, 0));
		i1.interactionStrength[0][0] = 1.0f;
		i1.interactionStrength[1][1] = 1.0f;
		i1.interactionStrength[2][2] = 1.0f;
		model.interactions.push_back(i1);
		LatticeModelFactory::SpinInteraction i2(LatticeModelFactory::LatticeSite(0, 0, 0, 0), LatticeModelFactory::LatticeSite(0, 1, 0, 0));
		i2.interactionStrength[0][0] = 1.0f;
		i2.interactionStrength[1][1] = 1.0f;
		i2.interactionStrength[2][2] = 1.0f;
		model.interactions.push_back(i2);

		Log::log << Log::setDisplayLogLevel(Log::LogLevel::None);
		std::pair<Lattice *, SpinModel *> product = LatticeModelFactory::newLatticeModel(uc, model, 3);
		Lattice *l = product.first;
		delete product.second;

		//construct frequency discretization
		std::vector<float> values({ 1.0, 2.0, 3.0, 4.0, 5.0 });
		FrequencyDiscretization *f = new FrequencyDiscretization(values);

		//write lattice and frequency to FrgCommon
		spinParser = new SpinParser(l, f);

		//construct vertex
		v = new SU2VertexSingleParticle;
	}

	~SU2VertexSingleParticleFixture()
	{
		delete spinParser;
		delete v;
	}

	SU2VertexSingleParticle *v;
	SpinParser *spinParser;
};

BOOST_FIXTURE_TEST_SUITE(SU2VertexSingleParticleTest, SU2VertexSingleParticleFixture);

BOOST_AUTO_TEST_CASE(ExpandIterator)
{
	for (int i = 0; i < v->size; ++i) v->getValueRef(i) = float(i);
	for (int i = 0; i < v->size; ++i)
	{
		float w;
		v->expandIterator(i, w);
		BOOST_CHECK_EQUAL(v->getValue(w), float(i));
	}
}

BOOST_AUTO_TEST_CASE(getValueSymmetry)
{
	for (int i = 0; i < v->size; ++i) v->getValueRef(i) = float(i);
	for (auto w = FrgCommon::frequency().begin(); w != FrgCommon::frequency().end(); ++w)
	{
		BOOST_CHECK_EQUAL(v->getValue(*w), -v->getValue(-*w));
	}
}

BOOST_AUTO_TEST_CASE(getValueInterpolation)
{
	float lower = 2.0f;
	float upper = 3.0f;
	v->getValueRef(0) = lower;
	v->getValueRef(1) = upper;

	float lambda = 0.2f;
	float w = (1.0f - lambda) * *FrgCommon::frequency().begin() + lambda * *(++FrgCommon::frequency().begin());
	BOOST_CHECK_EQUAL(v->getValue(w), (1.0f - lambda) * lower + lambda * upper);
}

BOOST_AUTO_TEST_SUITE_END();


/*
 Copyright 2013 Karsten Ahnert
 Copyright 2013 Mario Mulansky
 Copyright 2013 Pascal Germroth

 Distributed under the Boost Software License, Version 1.0.
 (See accompanying file LICENSE_1_0.txt or
 copy at http://www.boost.org/LICENSE_1_0.txt)
 */

#include <iostream>
#include <sstream>
#include <cstdlib>

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

#include <boost/numeric/odeint/external/mpi/mpi.hpp>

using namespace boost::numeric::odeint;

boost::mpi::environment env;

BOOST_AUTO_TEST_SUITE( norm_test_suite )

BOOST_AUTO_TEST_CASE( norm_test )
{
    boost::mpi::communicator world;

    int ref_value = 0;
    std::vector<int> in_data;
    mpi_state< std::vector<int> > state(world);

    // generate data and reference value on master
    if(world.rank() == 0) {
        for(size_t i = 0 ; i < 400 ; i++)
            in_data.push_back( rand() % 10000 );
        ref_value = *std::max_element(in_data.begin(), in_data.end());
    }
    boost::mpi::broadcast(world, ref_value, 0);

    // copy to nodes
    split( in_data, state );

    int value = mpi_nested_algebra< range_algebra >::norm_inf( state );

    {
        std::ostringstream ss;
        ss << "state[" << world.rank() << "]"
           << " local:" << range_algebra::norm_inf( state() )
           << " global:" << value
           << " ref:" << ref_value << "\n";
        std::clog << ss.str() << std::flush;
    }

    BOOST_REQUIRE_EQUAL( value, ref_value );
}

BOOST_AUTO_TEST_SUITE_END()


#pragma once
#include <deip/protocol/config.hpp>

#include <fc/container/flat_fwd.hpp>
#include <fc/io/varint.hpp>
#include <fc/io/enum_type.hpp>
#include <fc/crypto/sha224.hpp>
#include <fc/crypto/ripemd160.hpp>
#include <fc/crypto/elliptic.hpp>
#include <fc/reflect/reflect.hpp>
#include <fc/reflect/variant.hpp>
#include <fc/optional.hpp>
#include <fc/safe.hpp>
#include <fc/container/flat.hpp>
#include <fc/string.hpp>
#include <fc/fixed_string.hpp>
#include <fc/io/raw.hpp>
#include <fc/uint128.hpp>
#include <fc/static_variant.hpp>
#include <fc/smart_ref_fwd.hpp>

#include <boost/multiprecision/cpp_int.hpp>

#include <memory>
#include <vector>
#include <deque>
#include <cstdint>

namespace deip {

using fc::uint128_t;
typedef boost::multiprecision::uint256_t u256;
typedef boost::multiprecision::uint512_t u512;

using std::deque;
using std::enable_shared_from_this;
using std::make_pair;
using std::map;
using std::pair;
using std::set;
using std::shared_ptr;
using std::string;
using std::tie;
using std::unique_ptr;
using std::unordered_map;
using std::vector;
using std::weak_ptr;

using fc::enum_type;
using fc::flat_map;
using fc::flat_set;
using fc::optional;
using fc::safe;
using fc::signed_int;
using fc::smart_ref;
using fc::static_variant;
using fc::time_point;
using fc::time_point_sec;
using fc::unsigned_int;
using fc::variant;
using fc::variant_object;
using fc::ecc::commitment_type;
using fc::ecc::range_proof_info;
using fc::ecc::range_proof_type;
struct void_t
{
};

namespace protocol {

typedef fc::ecc::private_key private_key_type;
typedef fc::sha256 chain_id_type;
typedef fc::fixed_string_40 account_name_type;
typedef fc::fixed_string_40 external_id_type;
typedef fc::ripemd160 block_id_type;
typedef fc::ripemd160 checksum_type;
typedef fc::ripemd160 transaction_id_type;
typedef fc::sha256 digest_type;
typedef fc::ecc::compact_signature signature_type;
typedef safe<int64_t> share_type;
typedef uint16_t weight_type;
typedef uint64_t asset_symbol_type;
typedef int64_t assessment_criteria_value;

struct public_key_type
{
    struct binary_key
    {
        binary_key()
        {
        }
        uint32_t check = 0;
        fc::ecc::public_key_data data;
    };
    fc::ecc::public_key_data key_data;
    public_key_type();
    public_key_type(const fc::ecc::public_key_data& data);
    public_key_type(const fc::ecc::public_key& pubkey);
    explicit public_key_type(const std::string& base58str);
    operator fc::ecc::public_key_data() const;
    operator fc::ecc::public_key() const;
    explicit operator std::string() const;
    friend bool operator==(const public_key_type& p1, const fc::ecc::public_key& p2);
    friend bool operator==(const public_key_type& p1, const public_key_type& p2);
    friend bool operator<(const public_key_type& p1, const public_key_type& p2)
    {
        return p1.key_data < p2.key_data;
    }
    friend bool operator!=(const public_key_type& p1, const public_key_type& p2);
};

struct extended_public_key_type
{
    struct binary_key
    {
        binary_key()
        {
        }
        uint32_t check = 0;
        fc::ecc::extended_key_data data;
    };

    fc::ecc::extended_key_data key_data;

    extended_public_key_type();
    extended_public_key_type(const fc::ecc::extended_key_data& data);
    extended_public_key_type(const fc::ecc::extended_public_key& extpubkey);
    explicit extended_public_key_type(const std::string& base58str);
    operator fc::ecc::extended_public_key() const;
    explicit operator std::string() const;
    friend bool operator==(const extended_public_key_type& p1, const fc::ecc::extended_public_key& p2);
    friend bool operator==(const extended_public_key_type& p1, const extended_public_key_type& p2);
    friend bool operator!=(const extended_public_key_type& p1, const extended_public_key_type& p2);
};

struct extended_private_key_type
{
    struct binary_key
    {
        binary_key()
        {
        }
        uint32_t check = 0;
        fc::ecc::extended_key_data data;
    };

    fc::ecc::extended_key_data key_data;

    extended_private_key_type();
    extended_private_key_type(const fc::ecc::extended_key_data& data);
    extended_private_key_type(const fc::ecc::extended_private_key& extprivkey);
    explicit extended_private_key_type(const std::string& base58str);
    operator fc::ecc::extended_private_key() const;
    explicit operator std::string() const;
    friend bool operator==(const extended_private_key_type& p1, const fc::ecc::extended_private_key& p2);
    friend bool operator==(const extended_private_key_type& p1, const extended_private_key_type& p2);
    friend bool operator!=(const extended_private_key_type& p1, const extended_private_key_type& p2);
};

enum class authority_type : uint16_t
{
    unknown = 0,
    owner = 1,
    active = 2,

    FIRST = owner,
    LAST = active
};

typedef uint32_t percent_type;

} // namespace protocol
} // namespace deip

namespace fc {
void to_variant(const deip::protocol::public_key_type& var, fc::variant& vo);
void from_variant(const fc::variant& var, deip::protocol::public_key_type& vo);
void to_variant(const deip::protocol::extended_public_key_type& var, fc::variant& vo);
void from_variant(const fc::variant& var, deip::protocol::extended_public_key_type& vo);
void to_variant(const deip::protocol::extended_private_key_type& var, fc::variant& vo);
void from_variant(const fc::variant& var, deip::protocol::extended_private_key_type& vo);

std::string static_variant_name_from_type(const std::string& type_name);

struct from_static_variant_type
{
    variant& var;
    from_static_variant_type(variant& dv)
        : var(dv)
    {
    }

    typedef void result_type;
    template <typename T> void operator()(const T& v) const
    {
        auto name = static_variant_name_from_type(fc::get_typename<T>::name());
        var = variant(std::make_pair(name, v));
    }
};

struct get_static_variant_type
{
    string& name;
    get_static_variant_type(string& dv)
        : name(dv)
    {
    }

    typedef void result_type;
    template <typename T> void operator()(const T& v) const
    {
        name = static_variant_name_from_type(fc::get_typename<T>::name());
    }
};

} // namespace fc

#define DECLARE_STATIC_VARIANT_TYPE(StaticVariantType)                                                                 \
    namespace fc {                                                                                \
                                                                                \
    void to_variant(const StaticVariantType&, fc::variant&);                                                           \
    void from_variant(const fc::variant&, StaticVariantType&);                                                         \
                                                                                \
    } /* fc */

#define DEFINE_STATIC_VARIANT_TYPE(StaticVariantType)                                                                  \
    namespace fc {                                                                                \
                                                                                \
    void to_variant(const StaticVariantType& var, fc::variant& vo)                                                     \
    {                                                                                \
        var.visit(from_static_variant_type(vo));                                                                       \
    }                                                                                \
                                                                                \
    void from_variant(const fc::variant& var, StaticVariantType& vo)                                                   \
    {                                                                                \
        static std::map<string, uint32_t> to_tag = []() {                                                              \
            std::map<string, uint32_t> name_map;                                                                       \
            for (int i = 0; i < StaticVariantType::count(); ++i)                                                       \
            {                                                                                \
                StaticVariantType tmp;                                                                                \
                tmp.set_which(i);                                                                                \
                string n;                                                                                \
                tmp.visit(get_static_variant_type(n));                                                                 \
                name_map[n] = i;                                                                                \
            }                                                                                \
            return name_map;                                                                                \
        }();                                                                                \
                                                                                \
        auto ar = var.get_array();                                                                                \
        if (ar.size() < 2)                                                                                \
            return;                                                                                \
        if (ar[0].is_uint64())                                                                                \
            vo.set_which(ar[0].as_uint64());                                                                           \
        else                                                                                \
        {                                                                                \
            auto itr = to_tag.find(ar[0].as_string());                                                                 \
            FC_ASSERT(itr != to_tag.end(), "Invalid static variant type: ${n}", ("n", ar[0]));                         \
            vo.set_which(to_tag[ar[0].as_string()]);                                                                   \
        }                                                                                \
        vo.visit(fc::to_static_variant(ar[1]));                                                                        \
    }                                                                                \
    } /* fc */

FC_REFLECT(deip::protocol::public_key_type, (key_data))
FC_REFLECT(deip::protocol::public_key_type::binary_key, (data)(check))
FC_REFLECT(deip::protocol::extended_public_key_type, (key_data))
FC_REFLECT(deip::protocol::extended_public_key_type::binary_key, (check)(data))
FC_REFLECT(deip::protocol::extended_private_key_type, (key_data))
FC_REFLECT(deip::protocol::extended_private_key_type::binary_key, (check)(data))

FC_REFLECT_TYPENAME(deip::protocol::share_type)

FC_REFLECT(deip::void_t, )

FC_REFLECT_ENUM(deip::protocol::authority_type,
  (unknown)
  (owner)
  (active)
)


/*
 Copyright (C) 2017 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

#include <boost/make_shared.hpp>
#include <boost/test/unit_test.hpp>
#include <ored/marketdata/marketimpl.hpp>
#include <ored/portfolio/builders/cms.hpp>
#include <ored/portfolio/capfloor.hpp>
#include <ored/portfolio/enginedata.hpp>
#include <ored/portfolio/envelope.hpp>
#include <ored/portfolio/legdata.hpp>
#include <ored/portfolio/schedule.hpp>
#include <ored/portfolio/swap.hpp>
#include <ored/utilities/indexparser.hpp>
#include <oret/toplevelfixture.hpp>
#include <ql/cashflows/digitalcoupon.hpp>
#include <ql/termstructures/volatility/swaption/swaptionconstantvol.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/daycounters/actualactual.hpp>
#include <qle/termstructures/flatcorrelation.hpp>

#include <iostream>

using namespace QuantLib;
using namespace boost::unit_test_framework;
using namespace std;
using namespace ore::data;

// CMS Swap test

namespace {

class TestMarket : public MarketImpl {
public:
    TestMarket() {
        asof_ = Date(3, Feb, 2016);

        // build discount
        yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "EUR")] = flatRateYts(0.02);

        // build swaption vols
        swaptionCurves_[make_pair(Market::defaultConfiguration, "EUR")] = flatRateSvs(0.1);

        // build ibor index
        Handle<IborIndex> hEUR(ore::data::parseIborIndex(
            "EUR-EURIBOR-6M", yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "EUR")]));
        iborIndices_[make_pair(Market::defaultConfiguration, "EUR-EURIBOR-6M")] = hEUR;

        // add swap index
        boost::shared_ptr<ore::data::Convention> swapEURConv(new ore::data::IRSwapConvention(
            "EUR-6M-SWAP-CONVENTIONS", "TARGET", "Annual", "MF", "30/360", "EUR-EURIBOR-6M"));
        conventions_.add(swapEURConv);
        boost::shared_ptr<ore::data::Convention> swapIndexEURLongConv1(
            new ore::data::SwapIndexConvention("EUR-CMS-2Y", "EUR-6M-SWAP-CONVENTIONS"));
        boost::shared_ptr<ore::data::Convention> swapIndexEURLongConv2(
            new ore::data::SwapIndexConvention("EUR-CMS-30Y", "EUR-6M-SWAP-CONVENTIONS"));
        conventions_.add(swapIndexEURLongConv1);
        conventions_.add(swapIndexEURLongConv2);
        addSwapIndex("EUR-CMS-2Y", "EUR-EURIBOR-6M", Market::defaultConfiguration);
        addSwapIndex("EUR-CMS-30Y", "EUR-EURIBOR-6M", Market::defaultConfiguration);

        correlationCurves_[make_tuple(Market::defaultConfiguration, "EUR-CMS-30Y", "EUR-CMS-2Y")] = flatCorr(0.8);
    }

private:
    Handle<YieldTermStructure> flatRateYts(Real forward) {
        boost::shared_ptr<YieldTermStructure> yts(new FlatForward(0, NullCalendar(), forward, ActualActual()));
        return Handle<YieldTermStructure>(yts);
    }
    Handle<SwaptionVolatilityStructure> flatRateSvs(Volatility forward) {
        boost::shared_ptr<SwaptionVolatilityStructure> Svs(
            new ConstantSwaptionVolatility(0, NullCalendar(), ModifiedFollowing, forward, ActualActual()));
        return Handle<SwaptionVolatilityStructure>(Svs);
    }
    Handle<QuantExt::CorrelationTermStructure> flatCorr(Real corr) {
        boost::shared_ptr<QuantExt::CorrelationTermStructure> cs(
            new QuantExt::FlatCorrelation(0, NullCalendar(), corr, ActualActual()));

        return Handle<QuantExt::CorrelationTermStructure>(cs);
    }
};

struct CommonVars {
    // global data
    string ccy;
    bool isPayer;
    string start;
    string end;
    string fixtenor;
    string cmstenor;
    Calendar cal;
    string calStr;
    string conv;
    string rule;
    Size days;
    string fixDC;
    Real fixedRate;
    string index1;
    string index2;
    int fixingdays;
    bool isinarrears;
    double notional;
    string longShort;
    vector<double> notionals;

    boost::shared_ptr<ore::data::Swap> makeDigitalCmsSpreadOption(bool call, vector<double> strikes,
                                                                  vector<double> payoffs) {
        ScheduleData cmsSchedule(ScheduleRules(start, end, cmstenor, calStr, conv, conv, rule));

        vector<LegData> legData;
        boost::shared_ptr<DigitalCMSSpreadLegData> cmsLegData;
        if (call) {
            cmsLegData = boost::make_shared<DigitalCMSSpreadLegData>(
                boost::make_shared<CMSSpreadLegData>(
                    index1, index2, fixingdays, isinarrears, vector<double>(), vector<string>(), vector<double>(),
                    vector<string>(), vector<double>(), vector<string>(), vector<double>(), vector<string>(), false),
                Position::Long, false, strikes, vector<string>(), payoffs, vector<string>());
        } else {
            cmsLegData = boost::make_shared<DigitalCMSSpreadLegData>(
                boost::make_shared<CMSSpreadLegData>(
                    index1, index2, fixingdays, isinarrears, vector<double>(), vector<string>(), vector<double>(),
                    vector<string>(), vector<double>(), vector<string>(), vector<double>(), vector<string>(), false),
                Position::Long, false, vector<double>(), vector<string>(), vector<double>(), vector<string>(),
                Position::Long, false, strikes, vector<string>(), payoffs);
        }

        LegData leg(cmsLegData, isPayer, ccy, cmsSchedule, fixDC, notionals);
        legData.push_back(leg);

        Envelope env("CP1");
        boost::shared_ptr<ore::data::Swap> swap(new ore::data::Swap(env, legData));
        return swap;
    }

    boost::shared_ptr<ore::data::Swap> makeCmsSpreadOption(vector<double> spreads) {
        ScheduleData cmsSchedule(ScheduleRules(start, end, cmstenor, calStr, conv, conv, rule));

        vector<LegData> legData;
        CMSSpreadLegData cmsLegData(index1, index2, fixingdays, isinarrears, spreads, vector<string>(),
                                    vector<double>(), vector<string>(), vector<double>(), vector<string>(),
                                    vector<double>(), vector<string>(), false);

        LegData leg(boost::make_shared<CMSSpreadLegData>(cmsLegData), isPayer, ccy, cmsSchedule, fixDC, notionals);
        legData.push_back(leg);

        Envelope env("CP1");
        boost::shared_ptr<ore::data::Swap> swap(new ore::data::Swap(env, legData));
        return swap;
    }

    boost::shared_ptr<ore::data::Swap> makeCmsSpreadFloor(vector<double> floors) {
        ScheduleData cmsSchedule(ScheduleRules(start, end, cmstenor, calStr, conv, conv, rule));

        vector<LegData> legData;
        CMSSpreadLegData cmsLegData(index1, index2, fixingdays, isinarrears, vector<double>(), vector<string>(),
                                    vector<double>(), vector<string>(), floors, vector<string>(), vector<double>(),
                                    vector<string>(), false);

        LegData leg(boost::make_shared<CMSSpreadLegData>(cmsLegData), isPayer, ccy, cmsSchedule, fixDC, notionals);
        legData.push_back(leg);

        Envelope env("CP1");
        boost::shared_ptr<ore::data::Swap> swap(new ore::data::Swap(env, legData));
        return swap;
    }

    boost::shared_ptr<ore::data::Swap> makeCmsSpreadCap(vector<double> caps) {
        ScheduleData cmsSchedule(ScheduleRules(start, end, cmstenor, calStr, conv, conv, rule));

        vector<LegData> legData;
        CMSSpreadLegData cmsLegData(index1, index2, fixingdays, isinarrears, vector<double>(), vector<string>(), caps,
                                    vector<string>(), vector<double>(), vector<string>(), vector<double>(),
                                    vector<string>(), false);

        LegData leg(boost::make_shared<CMSSpreadLegData>(cmsLegData), isPayer, ccy, cmsSchedule, fixDC, notionals);
        legData.push_back(leg);

        Envelope env("CP1");
        boost::shared_ptr<ore::data::Swap> swap(new ore::data::Swap(env, legData));
        return swap;
    }

    CommonVars() : ccy("EUR"), isPayer(false), start("20160301"), end("20360301"), fixtenor("1Y"), cmstenor("6M") {

        longShort = isPayer ? "Short" : "Long";
        cal = TARGET();
        calStr = "TARGET";
        conv = "MF";
        rule = "Forward";
        fixDC = "ACT/360";
        fixedRate = 0.0;
        index1 = "EUR-CMS-30Y";
        index2 = "EUR-CMS-2Y";
        fixingdays = 2;
        isinarrears = false;
        notional = 10000000;
        notionals.push_back(10000000);
    }
};

void outputCoupons(boost::shared_ptr<ore::data::Swap> cmsSwap) {
    Leg leg = cmsSwap->legs().at(0);
    for (auto cf : leg) {
        boost::shared_ptr<FloatingRateCoupon> frc = boost::dynamic_pointer_cast<FloatingRateCoupon>(cf);
        BOOST_TEST_MESSAGE("Coupon Date: " << frc->date() << "; Rate: " << frc->rate()
                                           << "; DayCount: " << frc->dayCounter());
    }
};
} // namespace

BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)

BOOST_AUTO_TEST_SUITE(DigitalCmsTests)

// TODO FIXME
BOOST_AUTO_TEST_CASE(testDigitalCMSSpreadCoupon) {
    BOOST_TEST_MESSAGE("Testing CMS Digital CMS Spread coupon...");

    // build market
    boost::shared_ptr<Market> market = boost::make_shared<TestMarket>();
    Settings::instance().evaluationDate() = market->asofDate();
    CommonVars vars;

    // Build and price
    boost::shared_ptr<EngineData> engineData = boost::make_shared<EngineData>();
    engineData->model("CMS") = "LinearTSR";
    engineData->engine("CMS") = "LinearTSRPricer";

    map<string, string> engineparams1;
    engineparams1["MeanReversion"] = "0.0";
    engineparams1["Policy"] = "RateBound";
    engineparams1["LowerRateBoundLogNormal"] = "0.0001";
    engineparams1["UpperRateBoundLogNormal"] = "2";
    engineparams1["LowerRateBoundNormal"] = "-2";
    engineparams1["UpperRateBoundNormal"] = "2";
    engineparams1["VegaRatio"] = "0.01";
    engineparams1["PriceThreshold"] = "0.0000001";
    engineparams1["BsStdDev"] = "3";
    engineData->engineParameters("CMS") = engineparams1;

    engineData->model("CMSSpread") = "BrigoMercurio";
    engineData->engine("CMSSpread") = "Analytic";
    map<string, string> engineparams2;
    engineparams2["IntegrationPoints"] = "16";
    engineData->engineParameters("CMSSpread") = engineparams2;
    engineData->model("Swap") = "DiscountedCashflows";
    engineData->engine("Swap") = "DiscountingSwapEngineOptimised";

    boost::shared_ptr<EngineFactory> engineFactory = boost::make_shared<EngineFactory>(engineData, market);
    engineFactory->registerBuilder(boost::make_shared<AnalyticHaganCmsCouponPricerBuilder>());

    // test edge cases
    // If strike >> rate then NPV(digital call option) == NPV(option with spread = 0)
    // and NPV(digital put option == NPV(option with spread = payoff)

    {
        double strike = 1;
        double pay = 0.0001;
        boost::shared_ptr<ore::data::Swap> cmsDigitalSwapCall =
            vars.makeDigitalCmsSpreadOption(true, vector<double>(1, strike), vector<double>(1, pay));
        boost::shared_ptr<ore::data::Swap> cmsDigitalSwapPut =
            vars.makeDigitalCmsSpreadOption(false, vector<double>(1, strike), vector<double>(1, pay));
        boost::shared_ptr<ore::data::Swap> cmsSwap1 = vars.makeCmsSpreadOption(vector<double>(1, pay));
        boost::shared_ptr<ore::data::Swap> cmsSwap2 = vars.makeCmsSpreadOption(vector<double>(1, 0));
        cmsDigitalSwapCall->build(engineFactory);
        cmsDigitalSwapPut->build(engineFactory);
        cmsSwap1->build(engineFactory);
        cmsSwap2->build(engineFactory);

        BOOST_TEST_MESSAGE("digital call coupons");
        outputCoupons(cmsDigitalSwapCall);
        BOOST_TEST_MESSAGE("digital put coupons");
        outputCoupons(cmsDigitalSwapPut);
        BOOST_TEST_MESSAGE("coupon 1");
        outputCoupons(cmsSwap1);
        BOOST_TEST_MESSAGE("coupon 2");
        outputCoupons(cmsSwap2);

        BOOST_TEST_MESSAGE("NPV Call = " << cmsDigitalSwapCall->instrument()->NPV());
        BOOST_TEST_MESSAGE("NPV Put = " << cmsDigitalSwapPut->instrument()->NPV());
        BOOST_TEST_MESSAGE("NPV1 = " << cmsSwap1->instrument()->NPV());
        BOOST_TEST_MESSAGE("NPV2 = " << cmsSwap2->instrument()->NPV());

        BOOST_CHECK_CLOSE(cmsDigitalSwapCall->instrument()->NPV(), cmsSwap2->instrument()->NPV(), 0.1);
        BOOST_CHECK_CLOSE(cmsDigitalSwapPut->instrument()->NPV(), cmsSwap1->instrument()->NPV(), 0.1);
    }

    // check put cash-or-nothing payoffs
    {
        double strike = 0.0001;
        double pay = 0.0001;
        double eps = 1e-4;
        boost::shared_ptr<ore::data::Swap> cmsDigitalSwapPut =
            vars.makeDigitalCmsSpreadOption(false, vector<double>(1, strike), vector<double>(1, pay));
        boost::shared_ptr<ore::data::Swap> cmsSwap1 = vars.makeCmsSpreadFloor(vector<double>(1, strike + eps / 2));
        boost::shared_ptr<ore::data::Swap> cmsSwap2 = vars.makeCmsSpreadFloor(vector<double>(1, strike - eps / 2));
        cmsDigitalSwapPut->build(engineFactory);
        cmsSwap1->build(engineFactory);
        cmsSwap2->build(engineFactory);

        Leg leg = cmsDigitalSwapPut->legs().at(0);
        Leg leg1 = cmsSwap1->legs().at(0);
        Leg leg2 = cmsSwap2->legs().at(0);

        for (Size i = 0; i < leg.size(); i++) {
            boost::shared_ptr<DigitalCoupon> dc = boost::dynamic_pointer_cast<DigitalCoupon>(leg[i]);
            boost::shared_ptr<FloatingRateCoupon> frc1 = boost::dynamic_pointer_cast<FloatingRateCoupon>(leg1[i]);
            boost::shared_ptr<FloatingRateCoupon> frc2 = boost::dynamic_pointer_cast<FloatingRateCoupon>(leg2[i]);

            double r = pay * (frc1->rate() - frc2->rate()) / eps;

            BOOST_CHECK_CLOSE(r, dc->putOptionRate(), 0.1);
        }
    }

    // check call cash-or-nothing payoffs
    {
        double strike = 0.0001;
        double pay = 0.0001;
        double eps = 1e-4;
        boost::shared_ptr<ore::data::Swap> cmsDigitalSwapCall =
            vars.makeDigitalCmsSpreadOption(true, vector<double>(1, strike), vector<double>(1, pay));
        boost::shared_ptr<ore::data::Swap> cmsSwap1 = vars.makeCmsSpreadCap(vector<double>(1, strike + eps / 2));
        boost::shared_ptr<ore::data::Swap> cmsSwap2 = vars.makeCmsSpreadCap(vector<double>(1, strike - eps / 2));
        cmsDigitalSwapCall->build(engineFactory);
        cmsSwap1->build(engineFactory);
        cmsSwap2->build(engineFactory);

        Leg leg = cmsDigitalSwapCall->legs().at(0);
        Leg leg1 = cmsSwap1->legs().at(0);
        Leg leg2 = cmsSwap2->legs().at(0);

        for (Size i = 0; i < leg.size(); i++) {
            boost::shared_ptr<DigitalCoupon> dc = boost::dynamic_pointer_cast<DigitalCoupon>(leg[i]);
            boost::shared_ptr<FloatingRateCoupon> frc1 = boost::dynamic_pointer_cast<FloatingRateCoupon>(leg1[i]);
            boost::shared_ptr<FloatingRateCoupon> frc2 = boost::dynamic_pointer_cast<FloatingRateCoupon>(leg2[i]);

            double r = pay * (frc1->rate() - frc2->rate()) / eps;

            BOOST_CHECK_CLOSE(r, dc->callOptionRate(), 0.1);
        }
    }
    // check put asset-or-nothing payoffs
    {
        double strike = 0.0001;
        double eps = 1e-4;
        boost::shared_ptr<ore::data::Swap> cmsDigitalSwapPut =
            vars.makeDigitalCmsSpreadOption(false, vector<double>(1, strike), vector<double>());
        boost::shared_ptr<ore::data::Swap> cmsSwap1 = vars.makeCmsSpreadFloor(vector<double>(1, strike + eps / 2));
        boost::shared_ptr<ore::data::Swap> cmsSwap2 = vars.makeCmsSpreadFloor(vector<double>(1, strike - eps / 2));
        boost::shared_ptr<ore::data::Swap> cmsSwap3 = vars.makeCmsSpreadFloor(vector<double>(1, strike));
        boost::shared_ptr<ore::data::Swap> cmsSwap4 = vars.makeCmsSpreadFloor(vector<double>());
        cmsDigitalSwapPut->build(engineFactory);
        cmsSwap1->build(engineFactory);
        cmsSwap2->build(engineFactory);
        cmsSwap3->build(engineFactory);
        cmsSwap4->build(engineFactory);

        Leg leg = cmsDigitalSwapPut->legs().at(0);
        Leg leg1 = cmsSwap1->legs().at(0);
        Leg leg2 = cmsSwap2->legs().at(0);
        Leg leg3 = cmsSwap3->legs().at(0);
        Leg leg4 = cmsSwap4->legs().at(0);

        for (Size i = 0; i < leg.size(); i++) {
            boost::shared_ptr<DigitalCoupon> dc = boost::dynamic_pointer_cast<DigitalCoupon>(leg[i]);
            boost::shared_ptr<FloatingRateCoupon> frc1 = boost::dynamic_pointer_cast<FloatingRateCoupon>(leg1[i]);
            boost::shared_ptr<FloatingRateCoupon> frc2 = boost::dynamic_pointer_cast<FloatingRateCoupon>(leg2[i]);
            boost::shared_ptr<FloatingRateCoupon> frc3 = boost::dynamic_pointer_cast<FloatingRateCoupon>(leg3[i]);
            boost::shared_ptr<FloatingRateCoupon> frc4 = boost::dynamic_pointer_cast<FloatingRateCoupon>(leg4[i]);

            double r = strike * (frc1->rate() - frc2->rate()) / eps;
            double put = -frc4->rate() + frc3->rate();
            BOOST_TEST_MESSAGE(frc4->rate() << " " << frc3->rate() << " " << r << " " << put);

            BOOST_CHECK_CLOSE(r - put, dc->putOptionRate(), 0.1);
        }
    }

    // check call asset-or-nothing payoffs
    {
        double strike = 0.0001;
        double eps = 1e-4;
        boost::shared_ptr<ore::data::Swap> cmsDigitalSwapCall =
            vars.makeDigitalCmsSpreadOption(true, vector<double>(1, strike), vector<double>());
        boost::shared_ptr<ore::data::Swap> cmsSwap1 = vars.makeCmsSpreadCap(vector<double>(1, strike + eps / 2));
        boost::shared_ptr<ore::data::Swap> cmsSwap2 = vars.makeCmsSpreadCap(vector<double>(1, strike - eps / 2));
        boost::shared_ptr<ore::data::Swap> cmsSwap3 = vars.makeCmsSpreadCap(vector<double>(1, strike));
        boost::shared_ptr<ore::data::Swap> cmsSwap4 = vars.makeCmsSpreadCap(vector<double>());
        cmsDigitalSwapCall->build(engineFactory);
        cmsSwap1->build(engineFactory);
        cmsSwap2->build(engineFactory);
        cmsSwap3->build(engineFactory);
        cmsSwap4->build(engineFactory);

        Leg leg = cmsDigitalSwapCall->legs().at(0);
        Leg leg1 = cmsSwap1->legs().at(0);
        Leg leg2 = cmsSwap2->legs().at(0);
        Leg leg3 = cmsSwap3->legs().at(0);
        Leg leg4 = cmsSwap4->legs().at(0);

        for (Size i = 0; i < leg.size(); i++) {
            boost::shared_ptr<DigitalCoupon> dc = boost::dynamic_pointer_cast<DigitalCoupon>(leg[i]);
            boost::shared_ptr<FloatingRateCoupon> frc1 = boost::dynamic_pointer_cast<FloatingRateCoupon>(leg1[i]);
            boost::shared_ptr<FloatingRateCoupon> frc2 = boost::dynamic_pointer_cast<FloatingRateCoupon>(leg2[i]);
            boost::shared_ptr<FloatingRateCoupon> frc3 = boost::dynamic_pointer_cast<FloatingRateCoupon>(leg3[i]);
            boost::shared_ptr<FloatingRateCoupon> frc4 = boost::dynamic_pointer_cast<FloatingRateCoupon>(leg4[i]);

            double r = strike * (frc1->rate() - frc2->rate()) / eps;
            double call = frc4->rate() - frc3->rate();

            BOOST_CHECK_CLOSE(r + call, dc->callOptionRate(), 0.1);
        }
    }
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()


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

// Copyright (c) 2020 Barend Gehrels, Amsterdam, the Netherlands.

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

#ifndef BOOST_GEOMETRY_STRATEGIES_CARTESIAN_TURN_IN_RING_WINDING_HPP
#define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_TURN_IN_RING_WINDING_HPP

#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/config.hpp>
#include <boost/geometry/algorithms/detail/make/make.hpp>

namespace boost { namespace geometry
{

namespace strategy { namespace buffer
{

#ifndef DOXYGEN_NO_DETAIL

enum place_on_ring_type
{
    // +----offsetted----> (offsetted is considered as outside)
    // |                 |
    // |                 |
    // left              right (first point outside, rest inside)
    // |                 |
    // |                 |
    // <-----original----+ (original is considered as inside)
    place_on_ring_offsetted,
    place_on_ring_original,
    place_on_ring_to_offsetted,
    place_on_ring_from_offsetted,
};

template <typename CalculationType>
class turn_in_ring_winding
{

    // Implements the winding rule.
    // Basic calculations (on a clockwise ring of 5 segments)
    // (as everywhere in BG, -1 = right, 0 = on segment, +1 = left)
    // +--------2--------+  // P : For 1/3, nothing happens, it returns
    // |                 |  //     For 2, side is right (-1), multiplier=2, -2
    // |        P        |  //     For 4, side is right (-1), multiplier=1, -1
    // 1                 3  //     For 5, side is right (-1), multiplier=1, -1, total -4
    // |             Q   |  // Q : For 2: -2, for 4: -2, total -4
    // |                 |  // R : For 2: -2, for 5: +2, total 0
    // +----5---*----4---+  // S : For 2: -1, 3: nothing, 4: +1, total 0
    //
    //     R             S
    //

public:

    struct counter
    {
        inline counter()
            : m_count(0)
            , m_min_distance(0)
            , m_close_to_offset(false)
        {}

        //! Returns -1 for outside, 1 for inside
        inline int code() const
        {
            return m_count == 0 ? -1 : 1;
        }

        //! Counter, is increased if point is left of a segment (outside),
        //! and decreased if point is right of a segment (inside)
        int m_count;

        //! Indicate an indication of distance. It is always set, unless
        //! the point is located on the border-part of the original.
        //! It is not guaranteed to be the minimum distance, because it is only
        //! calculated for a selection of the offsetted ring.
        CalculationType m_min_distance;
        bool m_close_to_offset;
    };

    typedef counter state_type;

    template <typename Point, typename PointOfSegment>
    static inline bool in_vertical_range(Point const& point,
                             PointOfSegment const& s1,
                             PointOfSegment const& s2)
    {
        CalculationType const py = get<1>(point);
        CalculationType const s1y = get<1>(s1);
        CalculationType const s2y = get<1>(s2);

        return (s1y >= py && s2y <= py)
            || (s2y >= py && s1y <= py);
    }

    template <typename Dm, typename Point, typename PointOfSegment>
    static inline void apply_on_boundary(Point const& point,
                             PointOfSegment const& s1,
                             PointOfSegment const& s2,
                             place_on_ring_type place_on_ring,
                             counter& the_state)
    {
        if (place_on_ring == place_on_ring_offsetted)
        {
            // Consider the point as "outside"
            the_state.m_count = 0;
            the_state.m_close_to_offset = true;
            the_state.m_min_distance = 0;
        }
        else if (place_on_ring == place_on_ring_to_offsetted
            || place_on_ring == place_on_ring_from_offsetted)
        {
            // Check distance from "point" to either s1 or s2
            // on a line perpendicular to s1-s2
            typedef model::infinite_line<CalculationType> line_type;

            line_type const line
                    = detail::make::make_perpendicular_line<CalculationType>(s1, s2,
                    place_on_ring == place_on_ring_to_offsetted ? s2 : s1);

            Dm perp;
            perp.measure = arithmetic::side_value(line, point);

            // If it is to the utmost point s1 or s2, it is "outside"
            the_state.m_count = perp.is_zero() ? 0 : 1;
            the_state.m_close_to_offset = true;
            the_state.m_min_distance = std::fabs(perp.measure);
        }
        else
        {
            // It is on the border, the part of the original
            // Consider it as "inside".
            the_state.m_count = 1;
        }
    }

    template <typename Dm, typename Point, typename PointOfSegment>
    static inline bool apply(Point const& point,
                             PointOfSegment const& s1,
                             PointOfSegment const& s2,
                             Dm const& dm,
                             place_on_ring_type place_on_ring,
                             counter& the_state)
    {
        CalculationType const px = get<0>(point);
        CalculationType const s1x = get<0>(s1);
        CalculationType const s2x = get<0>(s2);

        bool const in_horizontal_range
                 = (s1x >= px && s2x <= px)
                || (s2x >= px && s1x <= px);

        bool const vertical = s1x == s2x;
        bool const measured_on_boundary = dm.is_zero();

        if (measured_on_boundary
            && (in_horizontal_range
                || (vertical && in_vertical_range(point, s1, s2))))
        {
            apply_on_boundary<Dm>(point, s1, s2, place_on_ring, the_state);
            // Indicate that no further processing is necessary.
            return false;
        }

        bool const is_on_right_side = dm.is_negative();

        if (place_on_ring == place_on_ring_offsetted
            && is_on_right_side
            && (! the_state.m_close_to_offset
                || -dm.measure < the_state.m_min_distance))
        {
            // This part of the ring was the offsetted part,
            // keep track of the distance WITHIN the ring
            // with respect to the offsetted part
            // NOTE: this is also done if it is NOT in the horizontal range.
            the_state.m_min_distance = -dm.measure;
            the_state.m_close_to_offset = true;
        }

        if (in_horizontal_range)
        {
            // Use only absolute comparisons, because the ring is continuous -
            // what was missed is there earlier or later, and turns should
            // not be counted twice (which can happen if an epsilon is used).
            bool const eq1 = s1x == px;
            bool const eq2 = s2x == px;

            // Account for  1 or  2 for left side (outside)
            //     and for -1 or -2 for right side (inside)
            int const side = is_on_right_side ? -1 : 1;
            int const multiplier = eq1 || eq2 ? 1 : 2;

            the_state.m_count += side * multiplier;
        }

        return true;
    }
};

#endif // DOXYGEN_NO_DETAIL

}} // namespace strategy::buffer

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_TURN_IN_RING_WINDING_HPP


/**
 *  biascorrection.cpp
 *  MetaQuant
 *
 *  Kevin McLoughlin
 *  Based on code from eXpress, created by Adam Roberts in 2011.
 *  Copyright 2014 Kevin McLoughlin, Adam Roberts. All rights reserved.
 *
 */

#include <algorithm>
#include <boost/assign.hpp>
#include <cassert>

#include "biascorrection.h"

#include "fragments.h"
#include "frequencymatrix.h"
#include "main.h"
#include "sequence.h"
#include "targets.h"

using namespace std;

// size of bias window
const int WINDOW = 21;
// center position in window
const int CENTER = 11;
// number of bases on either side of center
const int SURROUND = 10;

SeqWeightTable::SeqWeightTable(size_t window_size, size_t order, double alpha)
    : _order(order),
      _observed(order, window_size, window_size, alpha),
      _expected(order, window_size, order+1, EPSILON) {
}

SeqWeightTable::SeqWeightTable(size_t window_size, size_t order,
                               string param_file_name, string identifier)
    : _order(order),
      _observed(order, window_size, window_size, 0),
      _expected(order, window_size, order+1, 0) {

  //TODO: Allow for orders and window sizes to be read from param file.

  ifstream infile (param_file_name.c_str());
  const size_t BUFF_SIZE = 99999;
  char line_buff[BUFF_SIZE];
  if (!infile.is_open()) {
    logger.severe("Unable to open parameter file '%s'.",
                  param_file_name.c_str());
  }

  do {
    infile.getline (line_buff, BUFF_SIZE, '\n');
  } while (strncmp(line_buff, identifier.c_str(), identifier.size()));
  infile.getline (line_buff, BUFF_SIZE, '\n');

  do {
    infile.getline (line_buff, BUFF_SIZE, '\n');
  } while (strcmp(line_buff, "\tObserved Conditional Probabilities"));
  infile.getline (line_buff, BUFF_SIZE, '\n');

  //FG
  for (size_t k = 0; k < (size_t)WINDOW; ++k) {
    infile.getline (line_buff, BUFF_SIZE, '\n');
    char *p = strtok(line_buff, "\t");
    for (size_t i=0; i < pow(4.0, (double)min(order, k)); ++i) {
      for (size_t j=0; j < NUM_NUCS; ++j) {
        p = strtok(NULL, "\t");
        _observed.update(k, i, j, log(strtod(p,NULL)));
      }
    }
  }

  infile.getline (line_buff, BUFF_SIZE, '\n');
  infile.getline (line_buff, BUFF_SIZE, '\n');
  //BG
  for (size_t k = 0; k < order+1; ++k) {
    infile.getline (line_buff, BUFF_SIZE, '\n');
    char *p = strtok(line_buff, "\t");
    for (size_t i=0; i < pow(4.0, (double)min(order, k)); ++i) {
      for (size_t j=0; j < NUM_NUCS; ++j) {
        p = strtok(NULL, "\t");
        _expected.update(k, i, j, log(strtod(p,NULL)));
      }
    }
  }
}

void SeqWeightTable::copy_observed(const SeqWeightTable& other) {
  _observed = other._observed;
}

void SeqWeightTable::copy_expected(const SeqWeightTable& other) {
  _expected = other._expected;
}

void SeqWeightTable::increment_expected(const Sequence& seq, double mass,
                                        const vector<double>& fl_cdf) {
  _expected.fast_learn(seq, mass, fl_cdf);
}

void SeqWeightTable::normalize_expected() {
  _expected.calc_marginals();
}

void SeqWeightTable::increment_observed(const Sequence& seq, size_t i,
                                        double mass) {
  int left = (int)i - SURROUND;
  _observed.update(seq, left, mass);
}

double SeqWeightTable::get_weight(const Sequence& seq, size_t i) const {
  int left = (int)i - SURROUND;
  return _observed.seq_prob(seq, left) - _expected.seq_prob(seq, left);
}

void SeqWeightTable::append_output(ofstream& outfile) const {
  char buff[200];
  string header = "";
  for (int i = 0; i < WINDOW; i++) {
    sprintf(buff, "\t%d", i-CENTER+1);
    header += buff;
  }
  header += '\n';

  outfile << "\tObserved Marginal Distribution\n" << header;

  for (size_t j = 0; j < NUM_NUCS; j++) {
    outfile << NUCS[j] << ":\t";
    for (int i = 0; i < WINDOW; i++) {
      outfile << scientific << sexp(_observed.marginal_prob(i,j)) << "\t";
    }
    outfile<<endl;
  }

  outfile << "\tObserved Conditional Probabilities\nPosition\t";

  for (size_t j = 0; j < pow((double)NUM_NUCS, (double)_order+1); j++) {
    string s = "->";
    s += NUCS[j & 3];
    size_t cond = j >> 2;
    for (size_t k = 0; k < _order; ++k) {
      s = NUCS[cond & 3] + s;
      cond = cond >> 2;
    }
    outfile << s << '\t';
  }
  outfile << endl;

  for (int i = 0; i < WINDOW; i++) {
    outfile << i-SURROUND << ":\t";
    for (size_t j = 0; j < pow((double)NUM_NUCS, (double)_order+1); j++) {
      outfile << scientific << sexp(_observed.transition_prob(i,j>>2,j&3))
              << "\t";
    }
    outfile<<endl;
  }

  outfile << "\tBackground Conditional Probabilities\nPosition\t";

  for (size_t j = 0; j < pow((double)NUM_NUCS, (double)_order+1); j++) {
    string s = "->";
    s += NUCS[j & 3];
    size_t cond = j >> 2;
    for (size_t k = 0; k < _order; ++k) {
      s = NUCS[cond & 3] + s;
      cond = cond >> 2;
    }
    outfile << s << '\t';
  }
  outfile << endl;

  for (size_t i = 0; i < _order+1; i++) {
    outfile << i+1 << ":\t";
    for (size_t j = 0; j <  pow((double)NUM_NUCS, (double)_order+1); j++) {
      outfile << scientific << sexp(_expected.transition_prob(i,j>>2,j&3))
              << "\t";
    }
    outfile<<endl;
  }
}

BiasBoss::BiasBoss(size_t order, double alpha)
    : _order(order),
      _5_seq_bias(WINDOW, order, alpha),
      _3_seq_bias(WINDOW, order, alpha){
}

BiasBoss::BiasBoss(size_t order, string param_file_name)
    : _order(order),
      _5_seq_bias(WINDOW, order, param_file_name, ">5"),
      _3_seq_bias(WINDOW, order, param_file_name, ">3") {
}

void BiasBoss::copy_observations(const BiasBoss& other) {
  _5_seq_bias.copy_observed(other._5_seq_bias);
  _3_seq_bias.copy_observed(other._3_seq_bias);
}

void BiasBoss::copy_expectations(const BiasBoss& other) {
  _5_seq_bias.copy_expected(other._5_seq_bias);
  _3_seq_bias.copy_expected(other._3_seq_bias);
}

void BiasBoss::update_expectations(const Target& targ, double mass,
                                   const vector<double>& fl_cdf) {
  if ((mass == LOG_0) || (mass < LOG_MIN_TARG_RHO)) {
    return;
  }

  if (direction != R) {
    const Sequence& seq_fwd = targ.seq(0);
    _5_seq_bias.increment_expected(seq_fwd, mass, fl_cdf);
  }
  if (direction != F) {
    const Sequence& seq_rev = targ.seq(1);
    _3_seq_bias.increment_expected(seq_rev, mass, fl_cdf);
  }
}

void BiasBoss::normalize_expectations() {
  _5_seq_bias.normalize_expected();
  _3_seq_bias.normalize_expected();
}

void BiasBoss::update_observed(const FragHit& hit, double normalized_mass)
{
  assert (hit.pair_status() != PAIRED || (int)hit.length() > WINDOW);

  const Sequence& t_seq_fwd = hit.target()->seq(0);
  const Sequence& t_seq_rev = hit.target()->seq(1);

  if (hit.pair_status() != RIGHT_ONLY) {
    _5_seq_bias.increment_observed(t_seq_fwd, hit.left(), normalized_mass);
  }
  if (hit.pair_status() != LEFT_ONLY) {
    _3_seq_bias.increment_observed(t_seq_rev, t_seq_rev.length()-hit.right(),
                                   normalized_mass);
  }
}

double BiasBoss::get_target_start_bias(const Target& targ, size_t pos) const {

  const Sequence& t_seq_fwd = targ.seq(0);
  return _5_seq_bias.get_weight(t_seq_fwd, pos);
}

double BiasBoss::get_target_end_bias(const Target& targ, size_t pos) const {

  const Sequence& t_seq_rev = targ.seq(1);
  return _3_seq_bias.get_weight(t_seq_rev, targ.length()-pos-1);
}

double BiasBoss::get_average_target_bias(std::map<const size_t, float>& start_bias,
                                         std::map<const size_t, float>& end_bias,
                                         const Target& targ) const {
  double tot_start = LOG_0;
  double tot_end = LOG_0;

  for (size_t i = 0; i < targ.length(); ++i) {
    double start_bias_i;
    double end_bias_i;
    try {
      start_bias_i = start_bias.at(i);
    } catch (out_of_range) {
      start_bias_i = get_target_start_bias(targ, i);
    }
    try {
      end_bias_i = end_bias.at(i);
    } catch (out_of_range) {
      end_bias_i = get_target_end_bias(targ, i);
    }
    tot_start = log_add(tot_start, start_bias_i);
    tot_end = log_add(tot_end, end_bias_i);
  }

  //double avg_start_bias = (tot_start ) - (log((double)targ.length()));
  //double avg_end_bias = (tot_end) - (log((double)targ.length()));
  double avg_bias = (tot_start + tot_end) - (2*log((double)targ.length()));
  //logger.info("Target %s avg biases: start = %f, end = %f, total = %f", targ.name().c_str(),
  //   avg_start_bias, avg_end_bias, avg_bias);
  assert(!isnan(avg_bias));
  return avg_bias;
}

void BiasBoss::update_target_bias(std::map<const size_t, float>& start_bias,
                                 std::map<const size_t, float>& end_bias,
                                 const Target& targ) const {

  const Sequence& t_seq_fwd = targ.seq(0);
  const Sequence& t_seq_rev = targ.seq(1);

  for (std::map<const size_t, float>::iterator it = start_bias.begin();
       it != start_bias.end(); ++it) {
    //=-=TODO: Shouldn't this be it->first???
    size_t pos = it->second;
    start_bias[pos] = _5_seq_bias.get_weight(t_seq_fwd, pos);
  }
  for (std::map<const size_t, float>::iterator it = end_bias.begin();
       it != end_bias.end(); ++it) {
    size_t pos = it->second;
    end_bias[targ.length()-pos-1] = _3_seq_bias.get_weight(t_seq_rev, pos);
  }
}

void BiasBoss::append_output(ofstream& outfile) const {
  outfile << ">5' Sequence-Specific Bias\n";
  _5_seq_bias.append_output(outfile);
  outfile << ">3' Sequence-Specific Bias\n";
  _3_seq_bias.append_output(outfile);
}


// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009-2010 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <Eigen/Core>
#include <Eigen/Jacobi>

#define NOTR    0
#define TR      1
#define ADJ     2

#define LEFT    0
#define RIGHT   1

#define UP      0
#define LO      1

#define NUNIT   0
#define UNIT    1

#define INVALID 0xff

#define OP(X)   (   ((X)=='N' || (X)=='n') ? NOTR   \
                  : ((X)=='T' || (X)=='t') ? TR     \
                  : ((X)=='C' || (X)=='c') ? ADJ    \
                  : INVALID)

#define SIDE(X) (   ((X)=='L' || (X)=='l') ? LEFT   \
                  : ((X)=='R' || (X)=='r') ? RIGHT  \
                  : INVALID)

#define UPLO(X) (   ((X)=='U' || (X)=='u') ? UP     \
                  : ((X)=='L' || (X)=='l') ? LO     \
                  : INVALID)

#define DIAG(X) (   ((X)=='N' || (X)=='n') ? NUNIT  \
                  : ((X)=='U' || (X)=='u') ? UNIT   \
                  : INVALID)

inline bool check_op(const char* op)
{
  return OP(*op)!=0xff;
}

inline bool check_side(const char* side)
{
  return SIDE(*side)!=0xff;
}

inline bool check_uplo(const char* uplo)
{
  return UPLO(*uplo)!=0xff;
}

using namespace Eigen;

typedef float Scalar;
typedef NumTraits<Scalar>::Real RealScalar;
typedef std::complex<RealScalar> Complex;

enum
{
  IsComplex = Eigen::NumTraits<float>::IsComplex,
  Conj = IsComplex
};

typedef Matrix<Scalar,Dynamic,Dynamic,ColMajor> PlainMatrixType;
typedef Map<Matrix<Scalar,Dynamic,Dynamic,ColMajor>, 0, OuterStride<> > MatrixType;
typedef Map<Matrix<Scalar,Dynamic,1>, 0, InnerStride<Dynamic> > StridedVectorType;
typedef Map<Matrix<Scalar,Dynamic,1> > CompactVectorType;

template<typename T>
Map<Matrix<T,Dynamic,Dynamic,ColMajor>, 0, OuterStride<> >
matrix(T* data, int rows, int cols, int stride)
{
  return Map<Matrix<T,Dynamic,Dynamic,ColMajor>, 0, OuterStride<> >(data, rows, cols, OuterStride<>(stride));
}

template<typename T>
Map<Matrix<T,Dynamic,1>, 0, InnerStride<Dynamic> > vector(T* data, int size, int incr)
{
  return Map<Matrix<T,Dynamic,1>, 0, InnerStride<Dynamic> >(data, size, InnerStride<Dynamic>(incr));
}

template<typename T>
Map<Matrix<T,Dynamic,1> > vector(T* data, int size)
{
  return Map<Matrix<T,Dynamic,1> >(data, size);
}

template<typename T>
T* get_compact_vector(T* x, int n, int incx)
{
  if(incx==1)
    return x;

  T* ret = new Scalar[n];
  if(incx<0) vector(ret,n) = vector(x,n,-incx).reverse();
  else       vector(ret,n) = vector(x,n, incx);
  return ret;
}

template<typename T>
T* copy_back(T* x_cpy, T* x, int n, int incx)
{
  if(x_cpy==x)
    return 0;

  if(incx<0) vector(x,n,-incx).reverse() = vector(x_cpy,n);
  else       vector(x,n, incx)           = vector(x_cpy,n);
  return x_cpy;
}

#define EIGEN_BLAS_FUNC(X) EIGEN_CAT(eigen_,X)

static inline int EIGEN_BLAS_FUNC(gemm)(char *opa, char *opb, int *m, int *n, int *k, RealScalar *palpha, RealScalar *pa, int *lda, RealScalar *pb, int *ldb, RealScalar *pbeta, RealScalar *pc, int *ldc)
{
//   std::cerr << "in gemm " << *opa << " " << *opb << " " << *m << " " << *n << " " << *k << " " << *lda << " " << *ldb << " " << *ldc << " " << *palpha << " " << *pbeta << "\n";
  typedef void (*functype)(DenseIndex, DenseIndex, DenseIndex, const Scalar *, DenseIndex, const Scalar *, DenseIndex, Scalar *, DenseIndex, Scalar, internal::level3_blocking<Scalar,Scalar>&, Eigen::internal::GemmParallelInfo<DenseIndex>*);
  static functype func[12];

  static bool init = false;
  if(!init)
  {
    for(int kk=0; kk<12; ++kk)
      func[kk] = 0;
    func[NOTR  | (NOTR << 2)] = (internal::general_matrix_matrix_product<DenseIndex,Scalar,ColMajor,false,Scalar,ColMajor,false,ColMajor>::run);
    func[TR    | (NOTR << 2)] = (internal::general_matrix_matrix_product<DenseIndex,Scalar,RowMajor,false,Scalar,ColMajor,false,ColMajor>::run);
    func[ADJ   | (NOTR << 2)] = (internal::general_matrix_matrix_product<DenseIndex,Scalar,RowMajor,Conj, Scalar,ColMajor,false,ColMajor>::run);
    func[NOTR  | (TR   << 2)] = (internal::general_matrix_matrix_product<DenseIndex,Scalar,ColMajor,false,Scalar,RowMajor,false,ColMajor>::run);
    func[TR    | (TR   << 2)] = (internal::general_matrix_matrix_product<DenseIndex,Scalar,RowMajor,false,Scalar,RowMajor,false,ColMajor>::run);
    func[ADJ   | (TR   << 2)] = (internal::general_matrix_matrix_product<DenseIndex,Scalar,RowMajor,Conj, Scalar,RowMajor,false,ColMajor>::run);
    func[NOTR  | (ADJ  << 2)] = (internal::general_matrix_matrix_product<DenseIndex,Scalar,ColMajor,false,Scalar,RowMajor,Conj, ColMajor>::run);
    func[TR    | (ADJ  << 2)] = (internal::general_matrix_matrix_product<DenseIndex,Scalar,RowMajor,false,Scalar,RowMajor,Conj, ColMajor>::run);
    func[ADJ   | (ADJ  << 2)] = (internal::general_matrix_matrix_product<DenseIndex,Scalar,RowMajor,Conj, Scalar,RowMajor,Conj, ColMajor>::run);
    init = true;
  }

  Scalar* a = reinterpret_cast<Scalar*>(pa);
  Scalar* b = reinterpret_cast<Scalar*>(pb);
  Scalar* c = reinterpret_cast<Scalar*>(pc);
  Scalar alpha  = *reinterpret_cast<Scalar*>(palpha);
  Scalar beta   = *reinterpret_cast<Scalar*>(pbeta);

  if(beta!=Scalar(1))
  {
    if(beta==Scalar(0)) matrix(c, *m, *n, *ldc).setZero();
    else                matrix(c, *m, *n, *ldc) *= beta;
  }

  internal::gemm_blocking_space<ColMajor,Scalar,Scalar,Dynamic,Dynamic,Dynamic> blocking(*m,*n,*k, 1, false);

  int code = OP(*opa) | (OP(*opb) << 2);
  func[code](*m, *n, *k, a, *lda, b, *ldb, c, *ldc, alpha, blocking, 0);
  return 0;
}