#define BOOST_TEST_MODULE pcraster tab class_count_map
#include <boost/test/unit_test.hpp>
#include "stddefx.h"
#include "tab_classcountmap.h"
#include "tab_classclasscountmap.h"

BOOST_AUTO_TEST_CASE(count_map)
{
  using namespace tab;

  ClassCountMap<> m;
  std::set<int>   s;

  BOOST_CHECK(s==m.classes());
  BOOST_CHECK(m.size()==0);

  m.addClass(4);

  BOOST_CHECK(m.size()==1);
  BOOST_CHECK(s!=m.classes());
  s.insert(4);
  BOOST_CHECK(s==m.classes());
  s.insert(4);

  m.incr(3);
  BOOST_CHECK(m[3]==1);
  m.incr(3);

  BOOST_CHECK(m[4]==0);
  BOOST_CHECK(m[3]==2);
  BOOST_CHECK(m[1]==0); // implicit add

  BOOST_CHECK(m.size()==3); // 1,3,4

  s.insert(3);
  s.insert(1);

  BOOST_CHECK(s==m.classes());

  m.addClass(3); // no effect, already in
  BOOST_CHECK(m[3]==2);
  BOOST_CHECK(s==m.classes());

  BOOST_CHECK(m.getCount(4)==0);
  BOOST_CHECK(m.getCount(3)==2);
  BOOST_CHECK(m.getCount(1)==0);
  BOOST_CHECK(m.getCount(9)==0);
}

BOOST_AUTO_TEST_CASE(class_class_count_map)
{
  using namespace tab;

  ClassClassCountMap<> m;
  std::set<int>   row,col;

  BOOST_CHECK(row==m.rowClasses());
  BOOST_CHECK(col==m.colClasses());
  BOOST_CHECK(m.size()==0);

  m.addClass(4,2);

  BOOST_CHECK(m.size()==1);
  BOOST_CHECK(row!=m.rowClasses());
  BOOST_CHECK(col!=m.colClasses());
  row.insert(4);
  col.insert(2);
  BOOST_CHECK(row==m.rowClasses());
  BOOST_CHECK(col==m.colClasses());

  m.incr(3,5);
  m.incr(3,5);
  m.addClass(3,2);

  row.insert(3);
  col.insert(5);
  BOOST_CHECK(row==m.rowClasses());
  BOOST_CHECK(col==m.colClasses());

  m.incr(4,2);

  /*  * 2 5
   *  3 0 2
   *  4 1 0
   */
  BOOST_CHECK(m.getCount(3,2)==0);
  BOOST_CHECK(m.getCount(3,5)==2);
  BOOST_CHECK(m.getCount(4,2)==1);
  BOOST_CHECK(m.getCount(4,5)==0);
  BOOST_CHECK(m.getCount(9,9)==0);

  m.addClass(3,5); // no effect, already in
  BOOST_CHECK(m.getCount(3,5)==2);

  BOOST_CHECK(4==(m.rowClasses().size()*m.colClasses().size()));
}


/*
 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.
*/

/*! \file ored/configuration/basecorrelationcurveconfig.hpp
    \brief Base Correlation curve configuration classes
    \ingroup configuration
*/

#pragma once

#include <ored/configuration/curveconfig.hpp>
#include <ql/time/calendar.hpp>
#include <ql/time/dategenerationrule.hpp>
#include <ql/time/daycounter.hpp>
#include <ql/time/period.hpp>
#include <ql/types.hpp>
#include <boost/optional/optional.hpp>

namespace ore {
namespace data {
using ore::data::XMLNode;
using QuantLib::BusinessDayConvention;
using QuantLib::Calendar;
using QuantLib::DayCounter;
using QuantLib::Natural;
using QuantLib::Period;
using std::string;
using std::vector;

//! Base Correlation term structure configuration
/*!
  \ingroup configuration
*/
class BaseCorrelationCurveConfig : public CurveConfig {
public:
    //! \name Constructors/Destructors
    //@{
    //! Default constructor
    BaseCorrelationCurveConfig();

    //! Detailed constructor
    BaseCorrelationCurveConfig(
        const string& curveID,
        const string& curveDescription,
        const vector<string>& detachmentPoints,
        const vector<string>& terms,
        QuantLib::Size settlementDays,
        const QuantLib::Calendar& calendar,
        QuantLib::BusinessDayConvention businessDayConvention,
        QuantLib::DayCounter dayCounter,
        bool extrapolate,
        const std::string& quoteName = "",
        const QuantLib::Date& startDate = QuantLib::Date(),
        boost::optional<QuantLib::DateGeneration::Rule> rule = boost::none,
        bool adjustForLosses = true);
    //@}

    //! \name Serialisation
    //@{
    void fromXML(XMLNode* node) override;
    XMLNode* toXML(XMLDocument& doc) override;
    //@}

    //! \name Inspectors
    //@{
    const vector<string>& terms() const { return terms_; }
    const vector<string>& detachmentPoints() const { return detachmentPoints_; }
    const Size& settlementDays() const { return settlementDays_; }
    const Calendar& calendar() const { return calendar_; }
    const BusinessDayConvention& businessDayConvention() const { return businessDayConvention_; }
    const DayCounter& dayCounter() const { return dayCounter_; }
    const bool& extrapolate() const { return extrapolate_; }
    const std::string& quoteName() const { return quoteName_; }
    const QuantLib::Date& startDate() const { return startDate_; }
    const boost::optional<QuantLib::DateGeneration::Rule>& rule() const { return rule_; }
    const bool& adjustForLosses() const { return adjustForLosses_; }
    const vector<string>& quotes() override;
    //@}

    //! \name Setters
    //@{
    vector<string>& terms() { return terms_; }
    vector<string>& detachmentPoints() { return detachmentPoints_; }
    Size& settlementDays() { return settlementDays_; }
    Calendar& calendar() { return calendar_; }
    BusinessDayConvention& businessDayConvention() { return businessDayConvention_; }
    DayCounter& dayCounter() { return dayCounter_; }
    bool& extrapolate() { return extrapolate_; }
    //@}

private:
    vector<string> detachmentPoints_;
    vector<string> terms_;
    Size settlementDays_;
    Calendar calendar_;
    BusinessDayConvention businessDayConvention_;
    DayCounter dayCounter_;
    bool extrapolate_;
    std::string quoteName_;
    QuantLib::Date startDate_;
    boost::optional<QuantLib::DateGeneration::Rule> rule_;
    bool adjustForLosses_;
};
} // namespace data
} // namespace ore


//==============================================================================
//         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
//==============================================================================
#ifndef NT2_LINALG_FUNCTIONS_FACTORIZATIONS_GENEIG_HPP_INCLUDED
#define NT2_LINALG_FUNCTIONS_FACTORIZATIONS_GENEIG_HPP_INCLUDED

#include <nt2/linalg/functions/geneig.hpp>
#include <nt2/include/functions/divides.hpp>
#include <nt2/include/functions/ggev_w.hpp>
#include <nt2/include/functions/ggev_wvr.hpp>
#include <nt2/include/functions/ggev_wvrvl.hpp>
#include <nt2/include/functions/height.hpp>
#include <nt2/include/functions/from_diag.hpp>
#include <nt2/include/functions/of_size.hpp>
#include <nt2/include/functions/resize.hpp>
#include <nt2/include/functions/tie.hpp>
#include <nt2/linalg/details/utility/lapack_verify.hpp>
#include <nt2/linalg/options.hpp>
#include <nt2/core/container/table/table.hpp>
#include <nt2/core/utility/assign_swap.hpp>
#include <nt2/core/container/dsl/as_terminal.hpp>
#include <nt2/sdk/meta/as_real.hpp>
#include <boost/dispatch/attributes.hpp>

namespace nt2 { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT  ( geneig_, tag::cpu_
                            , (A0)
                            , (scalar_<unspecified_<A0> >)
                              (scalar_<unspecified_<A0> >)
                            )
  {
    typedef A0 result_type;

    BOOST_FORCEINLINE result_type operator()(const A0& a0, const A0& a1) const
    {
      return a1/a0;
    }
  };

  BOOST_DISPATCH_IMPLEMENT  ( geneig_, tag::cpu_
                            , (A0)(A1)
                            , (scalar_<unspecified_<A0> >)
                              (scalar_<unspecified_<A0> >)
                              (unspecified_<A1>)
                            )
  {
    typedef A0 result_type;

    BOOST_FORCEINLINE result_type operator()(const A0& a0, const A0& a1, const A1&) const
    {
      return a1/a0;
    }
  };

  BOOST_DISPATCH_IMPLEMENT  ( geneig_, tag::cpu_
                            , (A0)(A1)(A2)
                            , (scalar_<unspecified_<A0> >)
                              (scalar_<unspecified_<A0> >)
                              (unspecified_<A1>)
                              (unspecified_<A2>)
                            )
  {
    typedef A0 result_type;

    BOOST_FORCEINLINE result_type operator()(const A0& a0, const A0& a1, const A1&, const A2&) const
    {
      return a1/a0;
    }
  };

  //============================================================================
  //Geneig computations
  //============================================================================
  BOOST_DISPATCH_IMPLEMENT  ( geneig_, tag::cpu_
                            , (A0)(N0)(A1)(N1)
                            , ((node_<A0, nt2::tag::geneig_
                                    , N0, nt2::container::domain
                                      >
                              ))
                              ((node_<A1, nt2::tag::tie_
                                    , N1, nt2::container::domain
                                     >
                              ))
                            )
  {
    typedef void  result_type;
    typedef typename boost::proto::result_of::child_c<A0&,0>::value_type child0;
    typedef typename child0::value_type                                  type_t;
    typedef typename nt2::meta::as_real<type_t>::type                   rtype_t;
    typedef typename nt2::meta::as_complex<rtype_t>::type               ctype_t;
    typedef rtype_t T;
    typedef nt2::memory::container<tag::table_,  type_t, nt2::_2D>   o_semantic;
    typedef nt2::memory::container<tag::table_, rtype_t, nt2::_2D>   r_semantic;
    typedef nt2::memory::container<tag::table_, ctype_t, nt2::_2D>   c_semantic;

    BOOST_FORCEINLINE result_type operator()( A0& a0, A1& a1 ) const
    {
      eval(a0, a1, N0(), N1());
    }

    //==========================================================================
    /// INTERNAL ONLY - W = GENEIG(A)
    BOOST_FORCEINLINE
    void eval ( A0& a0, A1& a1
              , boost::mpl::long_<2> const&
              , boost::mpl::long_<1> const&
              ) const
    {
      eval1_3(a0, a1
             , nt2::policy<ext::vector_>());
    }

    //==========================================================================
    /// INTERNAL ONLY - W = GENEIG(A, B, matrix_/vector_)
    BOOST_FORCEINLINE
    void eval ( A0& a0, A1& a1
              , boost::mpl::long_<3> const&
              , boost::mpl::long_<1> const&
              ) const
    {
      eval1_3(a0, a1
             , boost::proto::value(boost::proto::child_c<2>(a0)));
    }

    //==========================================================================
    /// INTERNAL ONLY - [VR, W]= GENEIG(A, B)
    BOOST_FORCEINLINE
    void eval ( A0& a0, A1& a1
              , boost::mpl::long_<2> const&
              , boost::mpl::long_<2> const&
              ) const
    {
      eval2_3(a0, a1
             , nt2::policy<ext::matrix_>());
    }

    /// INTERNAL ONLY: 1o 3i
    BOOST_FORCEINLINE
    void eval1_3 ( A0& a0, A1& a1
                 , nt2::policy<ext::vector_> const &
                 ) const
    {
      nt2::container::table<type_t> wa, wb;
      NT2_AS_TERMINAL_INOUT(o_semantic, a
                           , boost::proto::child_c<0>(a0), wa);
      NT2_AS_TERMINAL_INOUT(o_semantic, b
                           , boost::proto::child_c<1>(a0), wb);
      NT2_AS_TERMINAL_OUT  (c_semantic, w
                           , boost::proto::child_c<0>(a1));
      size_t n = height(a);
      container::table <type_t>  beta(nt2::of_size(n,1));
      w.resize(nt2::of_size(n,1)); ;
      NT2_LAPACK_VERIFY(nt2::ggev_w( boost::proto::value(a)
                                   , boost::proto::value(b)
                                   , boost::proto::value(w)
                                   , boost::proto::value(beta)));
      assign_swap(boost::proto::child_c<0>(a1), w);
      boost::proto::child_c<0>(a1) /= beta;
    }

    /// INTERNAL ONLY: 1o 3i
    BOOST_FORCEINLINE
    void eval1_3 ( A0& a0, A1& a1
                 , nt2::policy<ext::matrix_> const &
                 ) const
    {
      nt2::container::table<type_t> wa, wb;
      NT2_AS_TERMINAL_INOUT(o_semantic, a
                           , boost::proto::child_c<0>(a0), wa);
      NT2_AS_TERMINAL_INOUT(o_semantic, b
                           , boost::proto::child_c<1>(a0), wb);
      size_t n = height(a);
      container::table <type_t> beta(nt2::of_size(n,1));
      container::table <ctype_t> alpha(nt2::of_size(n,1));

      NT2_LAPACK_VERIFY(nt2::ggev_w( boost::proto::value(a)
                                   , boost::proto::value(b)
                                   , boost::proto::value(alpha)
                                   , boost::proto::value(beta)));
      boost::proto::child_c<0>(a1) = from_diag(alpha/beta);
      //currently from_diag doesnt support aliasing
    }

    //==========================================================================
    /// INTERNAL ONLY - [alpha, beta] = GENEIG(A, B, alphabeta_) or
    ///                 [vr, w]       = GENEIG(A, B, matrix_/vector_)
    BOOST_FORCEINLINE
    void eval ( A0& a0, A1& a1
              , boost::mpl::long_<3> const&
              , boost::mpl::long_<2> const&
              ) const
    {
      eval2_3(a0, a1
             , boost::proto::value(boost::proto::child_c<2>(a0)));
    }

    /// INTERNAL ONLY: 2o 3i
    BOOST_FORCEINLINE
    void eval2_3 ( A0& a0, A1& a1
                 , nt2::policy<ext::alphabeta_> const &
                 ) const
    {
      nt2::container::table<type_t> wa, wb;
      NT2_AS_TERMINAL_INOUT(o_semantic, a
                           , boost::proto::child_c<0>(a0), wa);
      NT2_AS_TERMINAL_INOUT(o_semantic, b
                           , boost::proto::child_c<1>(a0), wb);
      NT2_AS_TERMINAL_OUT  (c_semantic, alpha
                           , boost::proto::child_c<0>(a1));
      NT2_AS_TERMINAL_OUT  (o_semantic, beta
                           , boost::proto::child_c<1>(a1));
      size_t n = height(a);
      beta.resize(nt2::of_size(n,1));
      alpha.resize(nt2::of_size(n,1)); ;
      NT2_LAPACK_VERIFY(nt2::ggev_w( boost::proto::value(a)
                                   , boost::proto::value(b)
                                   , boost::proto::value(alpha)
                                   , boost::proto::value(beta)));
      assign_swap(boost::proto::child_c<0>(a1), alpha);
      assign_swap(boost::proto::child_c<1>(a1), beta);

    }

    /// INTERNAL ONLY: 2o 3i
    BOOST_FORCEINLINE
    void eval2_3 ( A0& a0, A1& a1
                 , nt2::policy<ext::vector_> const &
                 ) const
    {
      nt2::container::table<type_t> wa, wb;
      NT2_AS_TERMINAL_INOUT(o_semantic, a
                           , boost::proto::child_c<0>(a0), wa);
      NT2_AS_TERMINAL_INOUT(o_semantic, b
                           , boost::proto::child_c<1>(a0), wb);
      NT2_AS_TERMINAL_OUT  (c_semantic, w
                           , boost::proto::child_c<1>(a1));
      NT2_AS_TERMINAL_OUT  (c_semantic, vr
                           , boost::proto::child_c<0>(a1));
      size_t n = height(a);
      w.resize(nt2::of_size(n,1));
      vr.resize(nt2::of_size(n,n));
      container::table <type_t>  beta(nt2::of_size(n,1));
      NT2_LAPACK_VERIFY(nt2::ggev_wvr( boost::proto::value(a)
                                     , boost::proto::value(b)
                                     , boost::proto::value(w)
                                     , boost::proto::value(beta)
                                     , boost::proto::value(vr)));
      assign_swap(boost::proto::child_c<1>(a1), w);
      boost::proto::child_c<1>(a1) /= beta;
      assign_swap(boost::proto::child_c<0>(a1), vr);
    }

    /// INTERNAL ONLY: 2o 3i
    BOOST_FORCEINLINE
    void eval2_3 ( A0& a0, A1& a1
                 , nt2::policy<ext::matrix_> const &
                 ) const
    {
      nt2::container::table<type_t> wa, wb;
      NT2_AS_TERMINAL_INOUT(o_semantic, a
                           , boost::proto::child_c<0>(a0), wa);
      NT2_AS_TERMINAL_INOUT(o_semantic, b
                           , boost::proto::child_c<1>(a0), wb);
      NT2_AS_TERMINAL_OUT  (c_semantic, vr
                           , boost::proto::child_c<0>(a1));
      size_t n = height(a);
      container::table <type_t> beta(nt2::of_size(n,1));
      container::table <ctype_t> alpha(nt2::of_size(n,1));
      vr.resize(nt2::of_size(n,n));
      NT2_LAPACK_VERIFY(nt2::ggev_wvr( boost::proto::value(a)
                                     , boost::proto::value(b)
                                     , boost::proto::value(alpha)
                                     , boost::proto::value(beta)
                                     , boost::proto::value(vr)));
      boost::proto::child_c<1>(a1) = from_diag(alpha/beta);
      assign_swap(boost::proto::child_c<0>(a1), vr);
      //from_diag doesnt support aliasing currently
    }

    //==========================================================================
    /// INTERNAL ONLY - [alpha, beta] = GENEIG(A, B, alphabeta_, matrix_/vectors)
    BOOST_FORCEINLINE
    void eval ( A0& a0, A1& a1
              , boost::mpl::long_<4> const&
              , boost::mpl::long_<2> const&
              ) const
    {
      eval2_4(a0, a1
             , boost::proto::value(boost::proto::child_c<3>(a0)));
    }

    /// INTERNAL ONLY: 2o 4i
    BOOST_FORCEINLINE
    void eval2_4 ( A0& a0, A1& a1
                 , nt2::policy<ext::vector_> const &
                 ) const
    {
      eval2_3(a0, a1
             , nt2::policy<ext::alphabeta_>());
    }

    /// INTERNAL ONLY: 2o 4i
    BOOST_FORCEINLINE
    void eval2_4 ( A0& a0, A1& a1
                 , nt2::policy<ext::matrix_> const &
                 ) const
    {
      nt2::container::table<type_t> wa, wb;
      NT2_AS_TERMINAL_INOUT(o_semantic, a
                           , boost::proto::child_c<0>(a0), wa);
      NT2_AS_TERMINAL_INOUT(o_semantic, b
                           , boost::proto::child_c<1>(a0), wb);
      size_t n = height(a);
      container::table <type_t> beta(nt2::of_size(n,1));
      container::table <ctype_t> alpha(nt2::of_size(n,1));
      NT2_LAPACK_VERIFY(nt2::ggev_w( boost::proto::value(a)
                                   , boost::proto::value(b)
                                   , boost::proto::value(alpha)
                                   , boost::proto::value(beta)
                                   ));
      boost::proto::child_c<0>(a1) = from_diag(alpha);
      boost::proto::child_c<1>(a1) = from_diag(beta);
    }

    //==========================================================================
    /// INTERNAL ONLY - [vr, w, vl] = GENEIG(A, B)
    BOOST_FORCEINLINE
    void eval ( A0& a0, A1& a1
              , boost::mpl::long_<2> const&
              , boost::mpl::long_<3> const&
              ) const
    {
      eval3_3(a0, a1, nt2::policy<ext::matrix_>());
    }

  //==========================================================================
    /// INTERNAL ONLY - [vr, alpha, beta] = GENEIG(A, B, alphabeta_) or
    ///                 [vr, w, vl]       = GENEIG(A, B, matrix_/vectors)
    BOOST_FORCEINLINE
    void eval ( A0& a0, A1& a1
              , boost::mpl::long_<3> const&
              , boost::mpl::long_<3> const&
              ) const
    {
      eval3_3(a0, a1, boost::proto::value(boost::proto::child_c<2>(a0)));
    }

    /// INTERNAL ONLY: 3o 3i
    BOOST_FORCEINLINE
    void eval3_3 ( A0& a0, A1& a1
                 , nt2::policy<ext::alphabeta_> const &
                 ) const
    {
      nt2::container::table<type_t> wa, wb;
      NT2_AS_TERMINAL_INOUT(o_semantic, a
                           , boost::proto::child_c<0>(a0), wa);
      NT2_AS_TERMINAL_INOUT(o_semantic, b
                           , boost::proto::child_c<1>(a0), wb);
      NT2_AS_TERMINAL_OUT  (c_semantic, vr
                           , boost::proto::child_c<0>(a1));
      size_t n = height(a);
      nt2::container::table <type_t> beta(nt2::of_size(n,1));
      nt2::container::table <ctype_t> alpha(nt2::of_size(n,1));
      vr.resize(nt2::of_size(n,n));
      NT2_LAPACK_VERIFY(nt2::ggev_wvr( boost::proto::value(a)
                                     , boost::proto::value(b)
                                     , boost::proto::value(alpha)
                                     , boost::proto::value(beta)
                                     , boost::proto::value(vr)
                                     ));
      boost::proto::child_c<1>(a1) = nt2::from_diag(alpha);
      boost::proto::child_c<2>(a1) = nt2::from_diag(beta);
      assign_swap(boost::proto::child_c<0>(a1), vr);
    }

    /// INTERNAL ONLY: 3o 3i
    BOOST_FORCEINLINE
    void eval3_3 ( A0& a0, A1& a1
                 , nt2::policy<ext::vector_> const &
                 ) const
    {
      nt2::container::table<type_t> wa, wb;
      NT2_AS_TERMINAL_INOUT(o_semantic, a
                           , boost::proto::child_c<0>(a0), wa);
      NT2_AS_TERMINAL_INOUT(o_semantic, b
                           , boost::proto::child_c<1>(a0), wb);
      NT2_AS_TERMINAL_OUT  (c_semantic, alpha
                           , boost::proto::child_c<1>(a1));
      NT2_AS_TERMINAL_OUT  (c_semantic, vr
                           , boost::proto::child_c<0>(a1));
      NT2_AS_TERMINAL_OUT  (c_semantic, vl
                           , boost::proto::child_c<2>(a1));
      size_t n = height(a);
      alpha.resize(nt2::of_size(n,1));
      vr.resize(nt2::of_size(n,n));
      container::table <type_t>  beta(nt2::of_size(n,1));
      NT2_LAPACK_VERIFY(nt2::ggev_wvrvl( boost::proto::value(a)
                                       , boost::proto::value(b)
                                       , boost::proto::value(alpha)
                                       , boost::proto::value(beta)
                                       , boost::proto::value(vr)
                                       , boost::proto::value(vl)
                                       ));
      assign_swap(boost::proto::child_c<1>(a1), alpha);
      boost::proto::child_c<1>(a1) /= beta;
      assign_swap(boost::proto::child_c<0>(a1), vr);
      assign_swap(boost::proto::child_c<2>(a1), vl);
    }

    /// INTERNAL ONLY: 3o 3i
    BOOST_FORCEINLINE
    void eval3_3 ( A0& a0, A1& a1
                 , nt2::policy<ext::matrix_> const &
                 ) const
    {
      nt2::container::table<type_t> wa, wb;
      NT2_AS_TERMINAL_INOUT(o_semantic, a
                           , boost::proto::child_c<0>(a0), wa);
      NT2_AS_TERMINAL_INOUT(o_semantic, b
                           , boost::proto::child_c<1>(a0), wb);
      NT2_AS_TERMINAL_OUT  (c_semantic, vr
                           , boost::proto::child_c<0>(a1));
      NT2_AS_TERMINAL_OUT  (c_semantic, vl
                           , boost::proto::child_c<2>(a1));
      size_t n = height(a);
      container::table <type_t> beta(nt2::of_size(n,1));
      container::table <ctype_t> alpha(nt2::of_size(n,1));
      vr.resize(nt2::of_size(n,n));
      vl.resize(nt2::of_size(n,n));
      NT2_LAPACK_VERIFY(nt2::ggev_wvrvl( boost::proto::value(a)
                                       , boost::proto::value(b)
                                       , boost::proto::value(alpha)
                                       , boost::proto::value(beta)
                                       , boost::proto::value(vr)
                                       , boost::proto::value(vl)
                                       ));
      boost::proto::child_c<1>(a1) = from_diag(alpha/beta);
      assign_swap(boost::proto::child_c<0>(a1), vr);
      assign_swap(boost::proto::child_c<2>(a1), vl);
      //from_diag doesnt support aliasing currently
    }

   //==========================================================================
    /// INTERNAL ONLY - [vr, alpha, beta, vl] = GENEIG(A, B) or
    BOOST_FORCEINLINE
    void eval ( A0& a0, A1& a1
              , boost::mpl::long_<2> const&
              , boost::mpl::long_<4> const&
              ) const
    {
      eval4_3(a0, a1
             , nt2::policy<ext::matrix_>());
    }
    //==========================================================================
    /// INTERNAL ONLY - [vr, alpha, beta] = GENEIG(A, B, alphabeta_, matrix_/vectors) or
    BOOST_FORCEINLINE
    void eval ( A0& a0, A1& a1
              , boost::mpl::long_<4> const&
              , boost::mpl::long_<3> const&
              ) const
    {
      eval3_4(a0, a1
             , boost::proto::value(boost::proto::child_c<2>(a0))
             , boost::proto::value(boost::proto::child_c<3>(a0)));
    }

   /// INTERNAL ONLY: 3o 4i
    BOOST_FORCEINLINE
    void eval3_4 ( A0& a0, A1& a1
                 , nt2::policy<ext::alphabeta_> const &
                 , nt2::policy<ext::matrix_>  const &
                 ) const
    {
      eval3_3(a0, a1, nt2::policy<ext::alphabeta_>());
    }

   /// INTERNAL ONLY: 3o 4i
    BOOST_FORCEINLINE
    void eval3_4 ( A0& a0, A1& a1
                 , nt2::policy<ext::alphabeta_> const &
                 , nt2::policy<ext::vector_>  const &
                 ) const
    {
      nt2::container::table<type_t> wa, wb;
      NT2_AS_TERMINAL_INOUT(o_semantic, a
                           , boost::proto::child_c<0>(a0), wa);
      NT2_AS_TERMINAL_INOUT(o_semantic, b
                           , boost::proto::child_c<1>(a0), wb);
      NT2_AS_TERMINAL_OUT  (c_semantic, vr
                           , boost::proto::child_c<0>(a1));
      NT2_AS_TERMINAL_OUT  (c_semantic, alpha
                           , boost::proto::child_c<1>(a1));
      NT2_AS_TERMINAL_OUT  (o_semantic, beta
                           , boost::proto::child_c<2>(a1));
      size_t n = height(a);
      beta.resize(nt2::of_size(n,1));
      alpha.resize(nt2::of_size(n,1));
      vr.resize(nt2::of_size(n,n));
      NT2_LAPACK_VERIFY(nt2::ggev_wvr( boost::proto::value(a)
                                     , boost::proto::value(b)
                                     , boost::proto::value(alpha)
                                     , boost::proto::value(beta)
                                     , boost::proto::value(vr)
                                     ));
      assign_swap(boost::proto::child_c<1>(a1), alpha);
      assign_swap(boost::proto::child_c<2>(a1), beta);
      assign_swap(boost::proto::child_c<0>(a1), vr);

    }

    //==========================================================================
    /// INTERNAL ONLY - [vr, alpha, beta, vl] = GENEIG(A, B, matrix_/vectors) or
    BOOST_FORCEINLINE
    void eval ( A0& a0, A1& a1
              , boost::mpl::long_<3> const&
              , boost::mpl::long_<4> const&
              ) const
    {
      eval4_3(a0, a1
             , boost::proto::value(boost::proto::child_c<2>(a0)));
    }

    /// INTERNAL ONLY: 4o 3i
    BOOST_FORCEINLINE
    void eval4_3 ( A0& a0, A1& a1
                 , nt2::policy<ext::vector_> const &
                 ) const
    {
      nt2::container::table<type_t> wa, wb;
      NT2_AS_TERMINAL_INOUT(o_semantic, a
                           , boost::proto::child_c<0>(a0), wa);
      NT2_AS_TERMINAL_INOUT(o_semantic, b
                           , boost::proto::child_c<1>(a0), wb);
      NT2_AS_TERMINAL_OUT  (c_semantic, alpha
                           , boost::proto::child_c<1>(a1));
      NT2_AS_TERMINAL_OUT  (o_semantic, beta
                           , boost::proto::child_c<2>(a1));
      NT2_AS_TERMINAL_OUT  (c_semantic, vr
                           , boost::proto::child_c<0>(a1));
      NT2_AS_TERMINAL_OUT  (c_semantic, vl
                           , boost::proto::child_c<3>(a1));
      size_t n = height(a);
      alpha.resize(nt2::of_size(n,1));
      beta.resize(nt2::of_size(n,1));
      vr.resize(nt2::of_size(n,n));
      vl.resize(nt2::of_size(n,n));
      NT2_LAPACK_VERIFY(nt2::ggev_wvrvl( boost::proto::value(a)
                                       , boost::proto::value(b)
                                       , boost::proto::value(alpha)
                                       , boost::proto::value(beta)
                                       , boost::proto::value(vr)
                                       , boost::proto::value(vl)
                                       ));
      assign_swap(boost::proto::child_c<0>(a1), vr);
      assign_swap(boost::proto::child_c<3>(a1), vl);
      assign_swap(boost::proto::child_c<1>(a1), alpha);
      assign_swap(boost::proto::child_c<2>(a1), beta);
    }

     /// INTERNAL ONLY: 4o 3i
    BOOST_FORCEINLINE
    void eval4_3 ( A0& a0, A1& a1
                 , nt2::policy<ext::matrix_> const &
                 ) const
    {
      nt2::container::table<type_t> wa, wb;
      NT2_AS_TERMINAL_INOUT(o_semantic, a
                           , boost::proto::child_c<0>(a0), wa);
      NT2_AS_TERMINAL_INOUT(o_semantic, b
                           , boost::proto::child_c<1>(a0), wb);
      NT2_AS_TERMINAL_OUT  (c_semantic, vr
                           , boost::proto::child_c<0>(a1));
      NT2_AS_TERMINAL_OUT  (c_semantic, vl
                           , boost::proto::child_c<3>(a1));
      size_t n = height(a);
      nt2::container::table <type_t> beta(nt2::of_size(n,1));
      nt2::container::table <ctype_t> alpha(nt2::of_size(n,1));
      vr.resize(nt2::of_size(n,n));
      vl.resize(nt2::of_size(n,n));
      NT2_LAPACK_VERIFY(nt2::ggev_wvrvl( boost::proto::value(a)
                                       , boost::proto::value(b)
                                       , boost::proto::value(alpha)
                                       , boost::proto::value(beta)
                                       , boost::proto::value(vr)
                                       , boost::proto::value(vl)
                                       ));
      assign_swap(boost::proto::child_c<0>(a1), vr);
      assign_swap(boost::proto::child_c<3>(a1), vl);
      boost::proto::child_c<1>(a1) = from_diag(alpha);
      boost::proto::child_c<2>(a1) = from_diag(beta);
      //from_diag doesnt support aliasing currently
    }

     /// INTERNAL ONLY: 4o 3i
    template < class T > BOOST_FORCEINLINE
    void eval4_3 ( A0& a0, A1& a1
                 , T const &
                 ) const
    {
      eval4_3(a0, a1
             , boost::proto::value(boost::proto::child_c<2>(a0)));
    }

  };
} }

#endif


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

#ifndef MTL_TRAITS_EXTENDED_COMPLEX_INCLUDE
#define MTL_TRAITS_EXTENDED_COMPLEX_INCLUDE

#include <complex>
#include <boost/numeric/mtl/utility/different_non_complex.hpp>
#include <boost/numeric/mtl/concept/std_concept.hpp>

namespace mtl { namespace traits {

template <typename T, typename U, bool enable>
struct extended_complex_aux
{};

template <typename T, typename U>
struct extended_complex_aux<T, U, true>
{
    typedef std::complex<typename mtl::Addable<T, U>::result_type> type;
};

/// Result type of extended complex binary arithmetic
/** If operation already exist in standard or non-complex are incompatible \p type does not exist.
    extended_complex<T,U>::type is an implicit enable_if and avoids ambiguities.  **/
template <typename T, typename U>
struct extended_complex
  : extended_complex_aux<T, U, different_non_complex<T, U>::value >
{};

}} // namespace mtl::traits

#endif // MTL_TRAITS_EXTENDED_COMPLEX_INCLUDE


/**
 * Copyright (c) 2011-2013 libwallet developers (see AUTHORS)
 *
 * This file is part of libwallet.
 *
 * libwallet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
#include "deterministic_wallet.hpp"

#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <openssl/ec.h>
#include <openssl/obj_mac.h>
#include <openssl/sha.h>
#include "constants.hpp"
#include "format.hpp"
#include "utility/assert.hpp"
#include "utility/sha256.hpp"

template <typename ssl_type>
struct ssl_wrapper
{
    ssl_wrapper(ssl_type* obj)
      : obj(obj) {}
    virtual ~ssl_wrapper()
    {
    }
    operator ssl_type*()
    {
        return obj;
    }

    ssl_type* obj;
};

struct ssl_bignum
  : public ssl_wrapper<BIGNUM>
{
    ssl_bignum()
      : ssl_wrapper(BN_new()) {}
    ~ssl_bignum()
    {
        BN_free(obj);
    }
};

#define SSL_TYPE(name, ssl_type, free_func) \
    struct name \
      : public ssl_wrapper<ssl_type> \
    { \
        name(ssl_type* obj) \
          : ssl_wrapper(obj) {} \
        ~name() \
        { \
            free_func(obj); \
        } \
    };

SSL_TYPE(ec_group, EC_GROUP, EC_GROUP_free)
SSL_TYPE(ec_point, EC_POINT, EC_POINT_free)
SSL_TYPE(bn_ctx, BN_CTX, BN_CTX_free)

const std::string bignum_hex(BIGNUM* bn)
{
    char* repr = BN_bn2hex(bn);
    std::string result = repr;
    OPENSSL_free(repr);
    boost::algorithm::to_lower(result);
    return result;
}

const data_chunk bignum_data(BIGNUM* bn)
{
    data_chunk result(32);
    size_t copy_offset = result.size() - BN_num_bytes(bn);
    BN_bn2bin(bn, result.begin() + copy_offset);
    // Zero out beginning 0x00 bytes (if they exist).
    std::fill(result.begin(), result.begin() + copy_offset, 0x00);
    return result;
}

void deterministic_wallet::new_seed()
{
    const size_t bits_needed = 8 * seed_size / 2;
    ssl_bignum rand_value;
    BN_rand(rand_value, bits_needed, 0, 0);
    bool set_success = set_seed(bignum_hex(rand_value));
    BITCOIN_ASSERT(set_success);
}

secret_parameter stretch_seed(const std::string& seed)
{
    BITCOIN_ASSERT(seed.size() == deterministic_wallet::seed_size);
    secret_parameter stretched;
    std::copy(seed.begin(), seed.end(), stretched.begin());
    secret_parameter oldseed = stretched;
    for (size_t i = 0; i < 100000; ++i)
    {
        SHA256_CTX ctx;
        SHA256_Init(&ctx);
        SHA256_Update(&ctx, stretched.data(), stretched.size());
        SHA256_Update(&ctx, oldseed.data(), oldseed.size());
        SHA256_Final(stretched.data(), &ctx);
    }
    return stretched;
}

data_chunk pubkey_from_secret(const secret_parameter& secret)
{
    elliptic_curve_key privkey;
    if (!privkey.set_secret(secret))
        return data_chunk();
    return privkey.public_key();
}

bool deterministic_wallet::set_seed(std::string seed)
{
    // Trim spaces and newlines around the string.
    boost::algorithm::trim(seed);
    if (seed.size() != seed_size)
        return false;
    seed_ = seed;
    stretched_seed_ = stretch_seed(seed);
    master_public_key_ = pubkey_from_secret(stretched_seed_);
    // Snip the beginning 04 byte for compat reasons.
    master_public_key_.erase(master_public_key_.begin());
    if (master_public_key_.empty())
        return false;
    return true;
}
const std::string& deterministic_wallet::seed() const
{
    return seed_;
}

bool deterministic_wallet::set_master_public_key(const data_chunk& mpk)
{
    master_public_key_ = mpk;
    return true;
}
const data_chunk& deterministic_wallet::master_public_key() const
{
    return master_public_key_;
}

data_chunk deterministic_wallet::generate_public_key(
    size_t n, bool for_change) const
{
    hash_digest sequence = get_sequence(n, for_change);

    ssl_bignum x, y, z;
    BN_bin2bn(sequence.data(), sequence.size(), z);
    BN_bin2bn(master_public_key_.begin(), 32, x);
    BN_bin2bn(master_public_key_.begin() + 32, 32, y);

    // Create a point.
    ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
    ec_point mpk(EC_POINT_new(group));
    bn_ctx ctx(BN_CTX_new());
    EC_POINT_set_affine_coordinates_GFp(group, mpk, x, y, ctx);
    ec_point result(EC_POINT_new(group));

    // result pubkey_point = mpk_pubkey_point + z*curve.generator
    ssl_bignum one;
    BN_one(one);
    EC_POINT_mul(group, result, z, mpk, one, ctx);

    // Create the actual public key.
    EC_POINT_get_affine_coordinates_GFp(group, result, x, y, ctx);
    // 04 + x + y
    data_chunk raw_pubkey;
    raw_pubkey.push_back(0x04);
    extend_data(raw_pubkey, bignum_data(x));
    extend_data(raw_pubkey, bignum_data(y));
    return raw_pubkey;
}

secret_parameter deterministic_wallet::generate_secret(
    size_t n, bool for_change) const
{
    if (seed_.empty())
        return null_hash;

    ssl_bignum z;
    hash_digest sequence = get_sequence(n, for_change);
    BN_bin2bn(sequence.data(), sequence.size(), z);

    ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
    ssl_bignum order;
    bn_ctx ctx(BN_CTX_new());
    EC_GROUP_get_order(group, order, ctx);

    // secexp = (stretched_seed + z) % order
    ssl_bignum secexp;
    BN_bin2bn(stretched_seed_.data(), stretched_seed_.size(), secexp);
    BN_add(secexp, secexp, z);
    BN_mod(secexp, secexp, order, ctx);

    secret_parameter secret;
    int secexp_bytes_size = BN_num_bytes(secexp);
    BITCOIN_ASSERT(secexp_bytes_size >= 0 &&
        static_cast<size_t>(BN_num_bytes(secexp)) <= secret.size());
    // If bignum value begins with 0x00, then
    // SSL will skip to the first significant digit.
    size_t copy_offset = secret.size() - BN_num_bytes(secexp);
    BN_bn2bin(secexp, secret.data() + copy_offset);
    // Zero out beginning 0x00 bytes (if they exist).
    std::fill(secret.begin(), secret.begin() + copy_offset, 0x00);
    return secret;
}

hash_digest deterministic_wallet::get_sequence(
    size_t n, bool for_change) const
{
    data_chunk chunk;
    extend_data(chunk, boost::lexical_cast<std::string>(n));
    chunk.push_back(':');
    chunk.push_back(for_change ? '1' : '0');
    chunk.push_back(':');
    extend_data(chunk, master_public_key_);
    hash_digest result = generate_sha256_hash(chunk);
    std::reverse(result.begin(), result.end());
    return result;
}


#include <Functions/FunctionFactory.h>
#include <Functions/FunctionsGeo.h>
#include <Functions/GeoUtils.h>
#include <Functions/FunctionHelpers.h>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>

#include <Columns/ColumnArray.h>
#include <Columns/ColumnFixedString.h>
#include <Columns/ColumnString.h>
#include <Columns/ColumnTuple.h>
#include <Common/ObjectPool.h>
#include <Common/ProfileEvents.h>
#include <DataTypes/DataTypeArray.h>
#include <DataTypes/DataTypeString.h>
#include <DataTypes/DataTypeTuple.h>
#include <IO/WriteHelpers.h>
#include <Interpreters/ExpressionActions.h>

#include <string>
#include <memory>

namespace ProfileEvents
{
    extern const Event PolygonsAddedToPool;
    extern const Event PolygonsInPoolAllocatedBytes;
}

namespace DB
{

namespace ErrorCodes
{
    extern const int TOO_FEW_ARGUMENTS_FOR_FUNCTION;
    extern const int BAD_ARGUMENTS;
    extern const int ILLEGAL_TYPE_OF_ARGUMENT;
}

namespace FunctionPointInPolygonDetail
{

template <typename Polygon, typename PointInPolygonImpl>
ColumnPtr callPointInPolygonImplWithPool(const IColumn & x, const IColumn & y, Polygon & polygon)
{
    using Pool = ObjectPoolMap<PointInPolygonImpl, std::string>;
    /// C++11 has thread-safe function-local statics on most modern compilers.
    static Pool known_polygons;

    auto factory = [& polygon]()
    {
        GeoUtils::normalizePolygon(polygon);
        auto ptr = std::make_unique<PointInPolygonImpl>(polygon);

        /// To allocate memory.
        ptr->init();

        ProfileEvents::increment(ProfileEvents::PolygonsAddedToPool);
        ProfileEvents::increment(ProfileEvents::PolygonsInPoolAllocatedBytes, ptr->getAllocatedBytes());

        return ptr.release();
    };

    std::string serialized_polygon = GeoUtils::serialize(polygon);
    auto impl = known_polygons.get(serialized_polygon, factory);

    return GeoUtils::pointInPolygon(x, y, *impl);
}

template <typename Polygon, typename PointInPolygonImpl>
ColumnPtr callPointInPolygonImpl(const IColumn & x, const IColumn & y, Polygon & polygon)
{
    PointInPolygonImpl impl(polygon);
    return GeoUtils::pointInPolygon(x, y, impl);
}

}

template <template <typename> typename PointInPolygonImpl, bool use_object_pool = false>
class FunctionPointInPolygon : public IFunction
{
public:

    template <typename Type>
    using Point = boost::geometry::model::d2::point_xy<Type>;
    template <typename Type>
    using Polygon = boost::geometry::model::polygon<Point<Type>, false>;
    template <typename Type>
    using Box = boost::geometry::model::box<Point<Type>>;

    static const char * name;

    static FunctionPtr create(const Context &)
    {
        return std::make_shared<FunctionPointInPolygon<PointInPolygonImpl, use_object_pool>>();
    }

    String getName() const override
    {
        return name;
    }

    bool isVariadic() const override
    {
        return true;
    }

    size_t getNumberOfArguments() const override
    {
        return 0;
    }

    DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
    {
        if (arguments.size() < 2)
        {
            throw Exception("Too few arguments", ErrorCodes::TOO_FEW_ARGUMENTS_FOR_FUNCTION);
        }

        auto getMsgPrefix = [this](size_t i) { return "Argument " + toString(i + 1) + " for function " + getName(); };

        for (size_t i = 1; i < arguments.size(); ++i)
        {
            auto * array = checkAndGetDataType<DataTypeArray>(arguments[i].get());
            if (array == nullptr && i != 1)
                throw Exception(getMsgPrefix(i) + " must be array of tuples.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);

            auto * tuple = checkAndGetDataType<DataTypeTuple>(array ? array->getNestedType().get() : arguments[i].get());
            if (tuple == nullptr)
                throw Exception(getMsgPrefix(i) + " must contains tuple.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);

            const DataTypes & elements = tuple->getElements();

            if (elements.size() != 2)
                throw Exception(getMsgPrefix(i) + " must have exactly two elements.", ErrorCodes::BAD_ARGUMENTS);

            for (auto j : ext::range(0, elements.size()))
            {
                if (!isNumber(elements[j]))
                {
                    throw Exception(getMsgPrefix(i) + " must contains numeric tuple at position " + toString(j + 1),
                                    ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
                }
            }
        }

        return std::make_shared<DataTypeUInt8>();
    }

    void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override
    {

        const IColumn * point_col = block.getByPosition(arguments[0]).column.get();
        auto const_tuple_col = checkAndGetColumn<ColumnConst>(point_col);
        if (const_tuple_col)
            point_col = &const_tuple_col->getDataColumn();
        auto tuple_col = checkAndGetColumn<ColumnTuple>(point_col);

        if (!tuple_col)
        {
            throw Exception("First argument for function " + getName() + " must be constant array of tuples.",
                            ErrorCodes::ILLEGAL_COLUMN);
        }

        const auto & tuple_columns = tuple_col->getColumns();
        const DataTypes & tuple_types = typeid_cast<const DataTypeTuple &>(*block.getByPosition(arguments[0]).type).getElements();

        bool use_float64 = WhichDataType(tuple_types[0]).isFloat64() || WhichDataType(tuple_types[1]).isFloat64();

        auto & result_column = block.safeGetByPosition(result).column;

        if (use_float64)
            result_column = executeForType<Float64>(*tuple_columns[0], *tuple_columns[1], block, arguments);
        else
            result_column = executeForType<Float32>(*tuple_columns[0], *tuple_columns[1], block, arguments);

        if (const_tuple_col)
            result_column = ColumnConst::create(result_column, const_tuple_col->size());
    }

private:

    Float64 getCoordinateFromField(const Field & field)
    {
        switch (field.getType())
        {
            case Field::Types::Float64:
                return field.get<Float64>();
            case Field::Types::Int64:
                return field.get<Int64>();
            case Field::Types::UInt64:
                return field.get<UInt64>();
            default:
            {
                std::string msg = "Expected numeric field, but got ";
                throw Exception(msg + Field::Types::toString(field.getType()), ErrorCodes::LOGICAL_ERROR);
            }
        }
    }

    template <typename Type>
    ColumnPtr executeForType(const IColumn & x, const IColumn & y, Block & block, const ColumnNumbers & arguments)
    {
        Polygon<Type> polygon;

        auto getMsgPrefix = [this](size_t i) { return "Argument " + toString(i + 1) + " for function " + getName(); };

        for (size_t i = 1; i < arguments.size(); ++i)
        {
            auto const_col = checkAndGetColumn<ColumnConst>(block.getByPosition(arguments[i]).column.get());
            auto array_col = const_col ? checkAndGetColumn<ColumnArray>(&const_col->getDataColumn()) : nullptr;
            auto tuple_col = array_col ? checkAndGetColumn<ColumnTuple>(&array_col->getData()) : nullptr;

            if (!tuple_col)
                throw Exception(getMsgPrefix(i) + " must be constant array of tuples.", ErrorCodes::ILLEGAL_COLUMN);

            const auto & tuple_columns = tuple_col->getColumns();
            const auto & column_x = tuple_columns[0];
            const auto & column_y = tuple_columns[1];

            if (!polygon.outer().empty())
                polygon.inners().emplace_back();

            auto & container = polygon.outer().empty() ? polygon.outer() : polygon.inners().back();

            auto size = column_x->size();

            if (size == 0)
                throw Exception(getMsgPrefix(i) + " shouldn't be empty.", ErrorCodes::ILLEGAL_COLUMN);

            for (auto j : ext::range(0, size))
            {
                Type x_coord = getCoordinateFromField((*column_x)[j]);
                Type y_coord = getCoordinateFromField((*column_y)[j]);
                container.push_back(Point<Type>(x_coord, y_coord));
            }

            /// Polygon assumed to be closed. Allow user to escape repeating of first point.
            if (!boost::geometry::equals(container.front(), container.back()))
                container.push_back(container.front());
        }

        auto callImpl = use_object_pool
            ? FunctionPointInPolygonDetail::callPointInPolygonImplWithPool<Polygon<Type>, PointInPolygonImpl<Type>>
            : FunctionPointInPolygonDetail::callPointInPolygonImpl<Polygon<Type>, PointInPolygonImpl<Type>>;

        return callImpl(x, y, polygon);
    }

};

const size_t GEOHASH_MAX_TEXT_LENGTH = 16;

// geohashEncode(lon float32/64, lat float32/64, length UInt8) => string
class FunctionGeohashEncode : public IFunction
{
public:
    static constexpr auto name = "geohashEncode";
    static FunctionPtr create(const Context &) { return std::make_shared<FunctionGeohashEncode>(); }

    String getName() const override
    {
        return name;
    }

    bool isVariadic() const override { return true; }
    size_t getNumberOfArguments() const override { return 0; }
    ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {2}; }
    bool useDefaultImplementationForConstants() const override { return true; }

    DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
    {
        validateArgumentType(*this, arguments, 0, isFloat, "float");
        validateArgumentType(*this, arguments, 1, isFloat, "float");
        if (arguments.size() == 3)
        {
            validateArgumentType(*this, arguments, 2, isInteger, "integer");
        }
        if (arguments.size() > 3)
        {
            throw Exception("Too many arguments for function " + getName() +
                            " expected at most 3",
                            ErrorCodes::TOO_MANY_ARGUMENTS_FOR_FUNCTION);
        }

        return std::make_shared<DataTypeString>();
    }

    template <typename LonType, typename LatType>
    bool tryExecute(const IColumn * lon_column, const IColumn * lat_column, UInt64 precision_value, ColumnPtr & result)
    {
        const ColumnVector<LonType> * longitude = checkAndGetColumn<ColumnVector<LonType>>(lon_column);
        const ColumnVector<LatType> * latitude = checkAndGetColumn<ColumnVector<LatType>>(lat_column);
        if (!latitude || !longitude)
            return false;

        auto col_str = ColumnString::create();
        ColumnString::Chars & out_vec = col_str->getChars();
        ColumnString::Offsets & out_offsets = col_str->getOffsets();

        const size_t size = lat_column->size();

        out_offsets.resize(size);
        out_vec.resize(size * (GEOHASH_MAX_TEXT_LENGTH + 1));

        char * begin = reinterpret_cast<char *>(out_vec.data());
        char * pos = begin;

        for (size_t i = 0; i < size; ++i)
        {
            const Float64 longitude_value = longitude->getElement(i);
            const Float64 latitude_value = latitude->getElement(i);

            const size_t encoded_size = GeoUtils::geohashEncode(longitude_value, latitude_value, precision_value, pos);

            pos += encoded_size;
            *pos = '\0';
            out_offsets[i] = ++pos - begin;
        }
        out_vec.resize(pos - begin);

        if (!out_offsets.empty() && out_offsets.back() != out_vec.size())
            throw Exception("Column size mismatch (internal logical error)", ErrorCodes::LOGICAL_ERROR);

        result = std::move(col_str);

        return true;

    }

    void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override
    {
        const IColumn * longitude = block.getByPosition(arguments[0]).column.get();
        const IColumn * latitude = block.getByPosition(arguments[1]).column.get();

        const UInt64 precision_value = std::min(GEOHASH_MAX_TEXT_LENGTH,
                arguments.size() == 3 ? block.getByPosition(arguments[2]).column->get64(0) : GEOHASH_MAX_TEXT_LENGTH);

        ColumnPtr & res_column = block.getByPosition(result).column;

        if (tryExecute<Float32, Float32>(longitude, latitude, precision_value, res_column) ||
            tryExecute<Float64, Float32>(longitude, latitude, precision_value, res_column) ||
            tryExecute<Float32, Float64>(longitude, latitude, precision_value, res_column) ||
            tryExecute<Float64, Float64>(longitude, latitude, precision_value, res_column))
            return;

        const char sep[] = ", ";
        std::string arguments_description = "";
        for (size_t i = 0; i < arguments.size(); ++i)
        {
            arguments_description += block.getByPosition(arguments[i]).column->getName() + sep;
        }
        if (arguments_description.size() > sizeof(sep))
        {
            arguments_description.erase(arguments_description.size() - sizeof(sep) - 1);
        }

        throw Exception("Unsupported argument types: " + arguments_description +
                        + " for function " + getName(),
                        ErrorCodes::ILLEGAL_COLUMN);
    }
};

// geohashDecode(string) => (lon float64, lat float64)
class FunctionGeohashDecode : public IFunction
{
public:
    static constexpr auto name = "geohashDecode";
    static FunctionPtr create(const Context &) { return std::make_shared<FunctionGeohashDecode>(); }

    String getName() const override
    {
        return name;
    }

    size_t getNumberOfArguments() const override { return 1; }
    bool useDefaultImplementationForConstants() const override { return true; }

    DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
    {
        validateArgumentType(*this, arguments, 0, isStringOrFixedString, "string or fixed string");

        return std::make_shared<DataTypeTuple>(
                DataTypes{std::make_shared<DataTypeFloat64>(), std::make_shared<DataTypeFloat64>()},
                Strings{"longitude", "latitude"});
    }

    template <typename ColumnTypeEncoded>
    bool tryExecute(const IColumn * encoded_column, ColumnPtr & result_column)
    {
        const auto * encoded = checkAndGetColumn<ColumnTypeEncoded>(encoded_column);
        if (!encoded)
            return false;

        const size_t count = encoded->size();

        auto latitude = ColumnFloat64::create(count);
        auto longitude = ColumnFloat64::create(count);

        ColumnFloat64::Container & lon_data = longitude->getData();
        ColumnFloat64::Container & lat_data = latitude->getData();

        for (size_t i = 0; i < count; ++i)
        {
            StringRef encoded_string = encoded->getDataAt(i);
            GeoUtils::geohashDecode(encoded_string.data, encoded_string.size, &lon_data[i], &lat_data[i]);
        }

        MutableColumns result;
        result.emplace_back(std::move(longitude));
        result.emplace_back(std::move(latitude));
        result_column = ColumnTuple::create(std::move(result));

        return true;
    }

    void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override
    {
        const IColumn * encoded = block.getByPosition(arguments[0]).column.get();
        ColumnPtr & res_column = block.getByPosition(result).column;

        if (tryExecute<ColumnString>(encoded, res_column) ||
            tryExecute<ColumnFixedString>(encoded, res_column))
            return;

        throw Exception("Unsupported argument type:" + block.getByPosition(arguments[0]).column->getName()
                        + " of argument of function " + getName(),
                        ErrorCodes::ILLEGAL_COLUMN);
    }
};

template <typename Type>
using Point = boost::geometry::model::d2::point_xy<Type>;

template <typename Type>
using PointInPolygonWithGrid = GeoUtils::PointInPolygonWithGrid<Type>;

template <>
const char * FunctionPointInPolygon<PointInPolygonWithGrid, true>::name = "pointInPolygon";

void registerFunctionsGeo(FunctionFactory & factory)
{
    factory.registerFunction<FunctionGreatCircleDistance>();
    factory.registerFunction<FunctionPointInEllipses>();

    factory.registerFunction<FunctionPointInPolygon<PointInPolygonWithGrid, true>>();
    factory.registerFunction<FunctionGeohashEncode>();
    factory.registerFunction<FunctionGeohashDecode>();
}
}


//
// Copyright (c) 2002--2010
// Toon Knapen, Karl Meerbergen, Kresimir Fresl,
// Thomas Klimpel and Rutger ter Borg
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// THIS FILE IS AUTOMATICALLY GENERATED
// PLEASE DO NOT EDIT!
//

#ifndef BOOST_NUMERIC_BINDINGS_BLAS_LEVEL2_GERU_HPP
#define BOOST_NUMERIC_BINDINGS_BLAS_LEVEL2_GERU_HPP

#include <boost/numeric/bindings/blas/level2/ger.hpp>

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

//
// geru is a synonym for ger
//
// Functions for direct use. These functions are overloaded for temporaries,
// so that wrapped types can still be passed and used for write-access.
//

//
// Overloaded function for geru.
//
template< typename VectorX, typename VectorY, typename MatrixA >
inline typename ger_impl< typename bindings::value_type<
        VectorX >::type >::result_type
geru( const typename bindings::value_type< VectorX >::type alpha,
        const VectorX& x, const VectorY& y, MatrixA& a ) {
    ger_impl< typename bindings::value_type< VectorX >::type >::invoke( alpha, x, y, a );
}

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

#endif


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

#include <boost/dispatch/meta/as.hpp>
#include <type_traits>

namespace boost { namespace simd { namespace details
{
  template<int... IndexMap>
  struct random_permute
  {
    template<int N, int... I> struct nth_t;

    template<int I0, int... In>
    struct nth_t<0, I0, In...> : std::integral_constant<int,I0>
    {};

    template<int N, int I0, int... In>
    struct nth_t<N, I0, In...> : nth_t<N-1, In...>::type
    {};

    template<typename Index, typename Cardinal>
    struct apply : nth_t<Index::value,IndexMap...>
    {};
  };

  template<int... IndexMap>
  using random_permute_t = boost::dispatch::meta
                                ::as_<details::random_permute<IndexMap...>>;
} } }

#endif


/*
* PCACoilGadget.cpp
*
*  Created on: Dec 13, 2011
*      Author: Michael S. Hansen
*/

#include "PCACoilGadget.h"
#include "hoNDArray_elemwise.h"
#include "ismrmrd/xml.h"
#include "hoNDArray_fileio.h"
#include "hoNDKLT.h"
#include "hoNDArray_linalg.h"

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

namespace Gadgetron {

    PCACoilGadget::PCACoilGadget()
        : max_buffered_profiles_(100)
        , samples_to_use_(16)
    {
    }

    PCACoilGadget::~PCACoilGadget()
    {
        std::map<int, hoNDKLT<std::complex<float> >* >::iterator it;
        it = pca_coefficients_.begin();
        while (it != pca_coefficients_.end()) {
            if (it->second) {
                delete it->second;
                it->second = 0;
            }
            it++;
        }
    }

    int PCACoilGadget::process_config(ACE_Message_Block *mb)
    {
        ISMRMRD::IsmrmrdHeader h;
        ISMRMRD::deserialize(mb->rd_ptr(), h);

        std::string uncomb_str = uncombined_channels_by_name.value();
        std::vector<std::string> uncomb;
        if (uncomb_str.size()) {
            GDEBUG("uncomb_str: %s\n", uncomb_str.c_str());
            boost::split(uncomb, uncomb_str, boost::is_any_of(","));
            for (unsigned int i = 0; i < uncomb.size(); i++) {
                std::string ch = boost::algorithm::trim_copy(uncomb[i]);
                if (h.acquisitionSystemInformation) {
                    for (size_t i = 0; i < h.acquisitionSystemInformation->coilLabel.size(); i++) {
                        if (ch == h.acquisitionSystemInformation->coilLabel[i].coilName) {
                            uncombined_channels_.push_back(i);//This assumes that the channels are sorted in the header
                            break;
                        }
                    }
                }
            }
        }

        present_uncombined_channels.value((int)uncombined_channels_.size());
        GDEBUG("Number of uncombined channels (present_uncombined_channels) set to %d\n", uncombined_channels_.size());

#ifdef USE_OMP
        omp_set_num_threads(1);
#endif // USE_OMP

        return GADGET_OK;
    }

    int PCACoilGadget::process(GadgetContainerMessage<ISMRMRD::AcquisitionHeader> *m1, GadgetContainerMessage<hoNDArray<std::complex<float> > > *m2)
    {
        bool is_noise = m1->getObjectPtr()->isFlagSet(ISMRMRD::ISMRMRD_ACQ_IS_NOISE_MEASUREMENT);

        //We should not be receiving noise here
        if (is_noise) {
            m1->release();
            return GADGET_OK;
        }

        std::map<int, bool>::iterator it;
        int location = m1->getObjectPtr()->idx.slice;
        bool is_last_scan_in_slice = m1->getObjectPtr()->isFlagSet(ISMRMRD::ISMRMRD_ACQ_LAST_IN_SLICE);
        int samples_per_profile = m1->getObjectPtr()->number_of_samples;
        int channels = m1->getObjectPtr()->active_channels;

        it = buffering_mode_.find(location);

        bool is_buffering = true;
        //Do we have an entry for this location
        if (it != buffering_mode_.end()) {
            is_buffering = it->second;
        }
        else {
            //else make an entry. We will always start in buffering mode for a given location.
            buffering_mode_[location] = is_buffering;
        }

        if (is_buffering)
        {
            buffer_[location].push_back(m1);
            int profiles_available = buffer_[location].size();

            //Are we ready for calculating PCA
            if (is_last_scan_in_slice || (profiles_available >= max_buffered_profiles_))
            {

                //GDEBUG("Calculating PCA coefficients with %d profiles for %d coils\n", profiles_available, channels);
                int samples_to_use = samples_per_profile > samples_to_use_ ? samples_to_use_ : samples_per_profile;

                //For some sequences there is so little data, we should just use it all.
                if (profiles_available < 16) {
                    samples_to_use = samples_per_profile;
                }

                int total_samples = samples_to_use*profiles_available;

                std::vector<size_t> dims(2);
                dims[0] = total_samples; dims[1] = channels;

                hoNDArray< std::complex<float> > A;
                try{ A.create(dims); }
                catch (std::runtime_error & err){
                    GDEBUG("Unable to create array for PCA calculation\n");
                    return GADGET_FAIL;
                }

                std::complex<float>* A_ptr = A.get_data_ptr();
                size_t sample_counter = 0;

                size_t data_offset = 0;
                if (m1->getObjectPtr()->center_sample >= (samples_to_use >> 1)) {
                    data_offset = m1->getObjectPtr()->center_sample - (samples_to_use >> 1);
                }

                //GDEBUG("Data offset = %d\n", data_offset);

                hoNDArray<std::complex<float> > means;
                std::vector<size_t> means_dims; means_dims.push_back(channels);

                try{ means.create(means_dims); }
                catch (std::runtime_error& err){
                    GDEBUG("Unable to create temporary stoorage for mean values\n");
                    return GADGET_FAIL;
                }

                means.fill(std::complex<float>(0.0f, 0.0f));

                std::complex<float>* means_ptr = means.get_data_ptr();

                for (size_t p = 0; p < profiles_available; p++) {
                    GadgetContainerMessage<hoNDArray<std::complex<float> > >* m_tmp =
                        AsContainerMessage<hoNDArray< std::complex<float> > >(buffer_[location][p]->cont());

                    if (!m_tmp) {
                        GDEBUG("Fatal error, unable to recover data from data buffer (%d,%d)\n", p, profiles_available);
                        return GADGET_FAIL;
                    }

                    std::complex<float>* d = m_tmp->getObjectPtr()->get_data_ptr();

                    for (unsigned s = 0; s < samples_to_use; s++) {
                        for (size_t c = 0; c < channels; c++) {
                            bool uncombined_channel = std::find(uncombined_channels_.begin(), uncombined_channels_.end(), c) != uncombined_channels_.end();
                            if (uncombined_channel) {
                                A_ptr[sample_counter + c *total_samples] = std::complex<float>(0.0, 0.0);
                            }
                            else {
                                A_ptr[sample_counter + c *total_samples] = d[c*samples_per_profile + data_offset + s];
                                means_ptr[c] += d[c*samples_per_profile + data_offset + s];
                            }
                        }

                        sample_counter++;
                        //GDEBUG("Sample counter = %d/%d\n", sample_counter, total_samples);
                    }
                }

                //Subtract off mean
                for (size_t c = 0; c < channels; c++) {
                    for (size_t s = 0; s < total_samples; s++) {
                        A_ptr[s + c *total_samples] -= means_ptr[c] / std::complex<float>(total_samples, 0);
                    }
                }

                //Collected data for temp matrix, now let's calculate SVD coefficients

                std::vector<size_t> VT_dims;
                VT_dims.push_back(channels);
                VT_dims.push_back(channels);
                pca_coefficients_[location] = new hoNDKLT < std::complex<float> > ;
                hoNDKLT< std::complex<float> >* VT = pca_coefficients_[location];

                //We will create a new matrix that explicitly preserves the uncombined channels
                if (uncombined_channels_.size())
                {
                    std::vector<size_t> untransformed(uncombined_channels_.size());
                    for (size_t un = 0; un < uncombined_channels_.size(); un++)
                    {
                        untransformed[un] = uncombined_channels_[un];
                    }

                    VT->prepare(A, (size_t)1, untransformed, (size_t)0, false);

                }
                else
                {
                    VT->prepare(A, (size_t)1, (size_t)0, false);
                }

                //Switch off buffering for this slice
                buffering_mode_[location] = false;

                //Now we should pump all the profiles that we have buffered back through the system
                for (size_t p = 0; p < profiles_available; p++) {
                    ACE_Message_Block* mb = buffer_[location][p];
                    if (inherited::process(mb) != GADGET_OK) {
                        GDEBUG("Failed to reprocess buffered data\n");
                        return GADGET_FAIL;
                    }
                }
                //Remove references in this buffer
                buffer_[location].clear();
            }
        }
        else {
            //GDEBUG("Not buffering anymore\n");
            GadgetContainerMessage< hoNDArray< std::complex<float> > >* m3 =
                new GadgetContainerMessage < hoNDArray< std::complex<float> > > ;

            try{ m3->getObjectPtr()->create(m2->getObjectPtr()->dimensions()); }
            catch (std::runtime_error& err){
                GEXCEPTION(err, "Unable to create storage for PCA coils\n");
                m3->release();
                return GADGET_FAIL;
            }

            if (pca_coefficients_[location] != 0)
            {
                pca_coefficients_[location]->transform(*(m2->getObjectPtr()), *(m3->getObjectPtr()), 1);
            }

            m1->cont(m3);

            //In case there are trajectories attached.
            m3->cont(m2->cont());
            m2->cont(0);

            m2->release();

            if (this->next()->putq(m1) < 0) {
                GDEBUG("Unable to put message on Q");
                return GADGET_FAIL;
            }
        }
        return GADGET_OK;
    }

    GADGET_FACTORY_DECLARE(PCACoilGadget)
}


// boost\math\distributions\geometric.hpp

// Copyright John Maddock 2010.
// Copyright Paul A. Bristow 2010.

// Use, modification and distribution are 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)

// geometric distribution is a discrete probability distribution.
// It expresses the probability distribution of the number (k) of
// events, occurrences, failures or arrivals before the first success.
// supported on the set {0, 1, 2, 3...}

// Note that the set includes zero (unlike some definitions that start at one).

// The random variate k is the number of events, occurrences or arrivals.
// k argument may be integral, signed, or unsigned, or floating point.
// If necessary, it has already been promoted from an integral type.

// Note that the geometric distribution
// (like others including the binomial, geometric & Bernoulli)
// is strictly defined as a discrete function:
// only integral values of k are envisaged.
// However because the method of calculation uses a continuous gamma function,
// it is convenient to treat it as if a continuous function,
// and permit non-integral values of k.
// To enforce the strict mathematical model, users should use floor or ceil functions
// on k outside this function to ensure that k is integral.

// See http://en.wikipedia.org/wiki/geometric_distribution
// http://documents.wolfram.com/v5/Add-onsLinks/StandardPackages/Statistics/DiscreteDistributions.html
// http://mathworld.wolfram.com/GeometricDistribution.html

#ifndef BOOST_MATH_SPECIAL_GEOMETRIC_HPP
#define BOOST_MATH_SPECIAL_GEOMETRIC_HPP

#include <boost/math/distributions/fwd.hpp>
#include <boost/math/special_functions/beta.hpp> // for ibeta(a, b, x) == Ix(a, b).
#include <boost/math/distributions/complement.hpp> // complement.
#include <boost/math/distributions/detail/common_error_handling.hpp> // error checks domain_error & logic_error.
#include <boost/math/special_functions/fpclassify.hpp> // isnan.
#include <boost/math/tools/roots.hpp> // for root finding.
#include <boost/math/distributions/detail/inv_discrete_quantile.hpp>

#include <boost/type_traits/is_floating_point.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/mpl/if.hpp>

#include <limits> // using std::numeric_limits;
#include <utility>

#if defined (BOOST_MSVC)
#  pragma warning(push)
// This believed not now necessary, so commented out.
//#  pragma warning(disable: 4702) // unreachable code.
// in domain_error_imp in error_handling.
#endif

namespace boost
{
  namespace math
  {
    namespace geometric_detail
    {
      // Common error checking routines for geometric distribution function:
      template <class RealType, class Policy>
      inline bool check_success_fraction(const char* function, const RealType& p, RealType* result, const Policy& pol)
      {
        if( !(boost::math::isfinite)(p) || (p < 0) || (p > 1) )
        {
          *result = policies::raise_domain_error<RealType>(
            function,
            "Success fraction argument is %1%, but must be >= 0 and <= 1 !", p, pol);
          return false;
        }
        return true;
      }

      template <class RealType, class Policy>
      inline bool check_dist(const char* function, const RealType& p, RealType* result, const Policy& pol)
      {
        return check_success_fraction(function, p, result, pol);
      }

      template <class RealType, class Policy>
      inline bool check_dist_and_k(const char* function,  const RealType& p, RealType k, RealType* result, const Policy& pol)
      {
        if(check_dist(function, p, result, pol) == false)
        {
          return false;
        }
        if( !(boost::math::isfinite)(k) || (k < 0) )
        { // Check k failures.
          *result = policies::raise_domain_error<RealType>(
            function,
            "Number of failures argument is %1%, but must be >= 0 !", k, pol);
          return false;
        }
        return true;
      } // Check_dist_and_k

      template <class RealType, class Policy>
      inline bool check_dist_and_prob(const char* function, RealType p, RealType prob, RealType* result, const Policy& pol)
      {
        if((check_dist(function, p, result, pol) && detail::check_probability(function, prob, result, pol)) == false)
        {
          return false;
        }
        return true;
      } // check_dist_and_prob
    } //  namespace geometric_detail

    template <class RealType = double, class Policy = policies::policy<> >
    class geometric_distribution
    {
    public:
      typedef RealType value_type;
      typedef Policy policy_type;

      geometric_distribution(RealType p) : m_p(p)
      { // Constructor stores success_fraction p.
        RealType result;
        geometric_detail::check_dist(
          "geometric_distribution<%1%>::geometric_distribution",
          m_p, // Check success_fraction 0 <= p <= 1.
          &result, Policy());
      } // geometric_distribution constructor.

      // Private data getter class member functions.
      RealType success_fraction() const
      { // Probability of success as fraction in range 0 to 1.
        return m_p;
      }
      RealType successes() const
      { // Total number of successes r = 1 (for compatibility with negative binomial?).
        return 1;
      }

      // Parameter estimation.
      // (These are copies of negative_binomial distribution with successes = 1).
      static RealType find_lower_bound_on_p(
        RealType trials,
        RealType alpha) // alpha 0.05 equivalent to 95% for one-sided test.
      {
        static const char* function = "boost::math::geometric<%1%>::find_lower_bound_on_p";
        RealType result = 0;  // of error checks.
        RealType successes = 1;
        RealType failures = trials - successes;
        if(false == detail::check_probability(function, alpha, &result, Policy())
          && geometric_detail::check_dist_and_k(
          function, RealType(0), failures, &result, Policy()))
        {
          return result;
        }
        // Use complement ibeta_inv function for lower bound.
        // This is adapted from the corresponding binomial formula
        // here: http://www.itl.nist.gov/div898/handbook/prc/section2/prc241.htm
        // This is a Clopper-Pearson interval, and may be overly conservative,
        // see also "A Simple Improved Inferential Method for Some
        // Discrete Distributions" Yong CAI and K. KRISHNAMOORTHY
        // http://www.ucs.louisiana.edu/~kxk4695/Discrete_new.pdf
        //
        return ibeta_inv(successes, failures + 1, alpha, static_cast<RealType*>(0), Policy());
      } // find_lower_bound_on_p

      static RealType find_upper_bound_on_p(
        RealType trials,
        RealType alpha) // alpha 0.05 equivalent to 95% for one-sided test.
      {
        static const char* function = "boost::math::geometric<%1%>::find_upper_bound_on_p";
        RealType result = 0;  // of error checks.
        RealType successes = 1;
        RealType failures = trials - successes;
        if(false == geometric_detail::check_dist_and_k(
          function, RealType(0), failures, &result, Policy())
          && detail::check_probability(function, alpha, &result, Policy()))
        {
          return result;
        }
        if(failures == 0)
        {
           return 1;
        }// Use complement ibetac_inv function for upper bound.
        // Note adjusted failures value: *not* failures+1 as usual.
        // This is adapted from the corresponding binomial formula
        // here: http://www.itl.nist.gov/div898/handbook/prc/section2/prc241.htm
        // This is a Clopper-Pearson interval, and may be overly conservative,
        // see also "A Simple Improved Inferential Method for Some
        // Discrete Distributions" Yong CAI and K. Krishnamoorthy
        // http://www.ucs.louisiana.edu/~kxk4695/Discrete_new.pdf
        //
        return ibetac_inv(successes, failures, alpha, static_cast<RealType*>(0), Policy());
      } // find_upper_bound_on_p

      // Estimate number of trials :
      // "How many trials do I need to be P% sure of seeing k or fewer failures?"

      static RealType find_minimum_number_of_trials(
        RealType k,     // number of failures (k >= 0).
        RealType p,     // success fraction 0 <= p <= 1.
        RealType alpha) // risk level threshold 0 <= alpha <= 1.
      {
        static const char* function = "boost::math::geometric<%1%>::find_minimum_number_of_trials";
        // Error checks:
        RealType result = 0;
        if(false == geometric_detail::check_dist_and_k(
          function, p, k, &result, Policy())
          && detail::check_probability(function, alpha, &result, Policy()))
        {
          return result;
        }
        result = ibeta_inva(k + 1, p, alpha, Policy());  // returns n - k
        return result + k;
      } // RealType find_number_of_failures

      static RealType find_maximum_number_of_trials(
        RealType k,     // number of failures (k >= 0).
        RealType p,     // success fraction 0 <= p <= 1.
        RealType alpha) // risk level threshold 0 <= alpha <= 1.
      {
        static const char* function = "boost::math::geometric<%1%>::find_maximum_number_of_trials";
        // Error checks:
        RealType result = 0;
        if(false == geometric_detail::check_dist_and_k(
          function, p, k, &result, Policy())
          &&  detail::check_probability(function, alpha, &result, Policy()))
        {
          return result;
        }
        result = ibetac_inva(k + 1, p, alpha, Policy());  // returns n - k
        return result + k;
      } // RealType find_number_of_trials complemented

    private:
      //RealType m_r; // successes fixed at unity.
      RealType m_p; // success_fraction
    }; // template <class RealType, class Policy> class geometric_distribution

    typedef geometric_distribution<double> geometric; // Reserved name of type double.

    template <class RealType, class Policy>
    inline const std::pair<RealType, RealType> range(const geometric_distribution<RealType, Policy>& /* dist */)
    { // Range of permissible values for random variable k.
       using boost::math::tools::max_value;
       return std::pair<RealType, RealType>(static_cast<RealType>(0), max_value<RealType>()); // max_integer?
    }

    template <class RealType, class Policy>
    inline const std::pair<RealType, RealType> support(const geometric_distribution<RealType, Policy>& /* dist */)
    { // Range of supported values for random variable k.
       // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
       using boost::math::tools::max_value;
       return std::pair<RealType, RealType>(static_cast<RealType>(0),  max_value<RealType>()); // max_integer?
    }

    template <class RealType, class Policy>
    inline RealType mean(const geometric_distribution<RealType, Policy>& dist)
    { // Mean of geometric distribution = (1-p)/p.
      return (1 - dist.success_fraction() ) / dist.success_fraction();
    } // mean

    // median implemented via quantile(half) in derived accessors.

    template <class RealType, class Policy>
    inline RealType mode(const geometric_distribution<RealType, Policy>&)
    { // Mode of geometric distribution = zero.
      BOOST_MATH_STD_USING // ADL of std functions.
      return 0;
    } // mode

    template <class RealType, class Policy>
    inline RealType variance(const geometric_distribution<RealType, Policy>& dist)
    { // Variance of Binomial distribution = (1-p) / p^2.
      return  (1 - dist.success_fraction())
        / (dist.success_fraction() * dist.success_fraction());
    } // variance

    template <class RealType, class Policy>
    inline RealType skewness(const geometric_distribution<RealType, Policy>& dist)
    { // skewness of geometric distribution = 2-p / (sqrt(r(1-p))
      BOOST_MATH_STD_USING // ADL of std functions.
      RealType p = dist.success_fraction();
      return (2 - p) / sqrt(1 - p);
    } // skewness

    template <class RealType, class Policy>
    inline RealType kurtosis(const geometric_distribution<RealType, Policy>& dist)
    { // kurtosis of geometric distribution
      // http://en.wikipedia.org/wiki/geometric is kurtosis_excess so add 3
      RealType p = dist.success_fraction();
      return 3 + (p*p - 6*p + 6) / (1 - p);
    } // kurtosis

     template <class RealType, class Policy>
    inline RealType kurtosis_excess(const geometric_distribution<RealType, Policy>& dist)
    { // kurtosis excess of geometric distribution
      // http://mathworld.wolfram.com/Kurtosis.html table of kurtosis_excess
      RealType p = dist.success_fraction();
      return (p*p - 6*p + 6) / (1 - p);
    } // kurtosis_excess

    // RealType standard_deviation(const geometric_distribution<RealType, Policy>& dist)
    // standard_deviation provided by derived accessors.
    // RealType hazard(const geometric_distribution<RealType, Policy>& dist)
    // hazard of geometric distribution provided by derived accessors.
    // RealType chf(const geometric_distribution<RealType, Policy>& dist)
    // chf of geometric distribution provided by derived accessors.

    template <class RealType, class Policy>
    inline RealType pdf(const geometric_distribution<RealType, Policy>& dist, const RealType& k)
    { // Probability Density/Mass Function.
      BOOST_FPU_EXCEPTION_GUARD
      BOOST_MATH_STD_USING  // For ADL of math functions.
      static const char* function = "boost::math::pdf(const geometric_distribution<%1%>&, %1%)";

      RealType p = dist.success_fraction();
      RealType result = 0;
      if(false == geometric_detail::check_dist_and_k(
        function,
        p,
        k,
        &result, Policy()))
      {
        return result;
      }
      if (k == 0)
      {
        return p; // success_fraction
      }
      RealType q = 1 - p;  // Inaccurate for small p?
      // So try to avoid inaccuracy for large or small p.
      // but has little effect > last significant bit.
      //cout << "p *  pow(q, k) " << result << endl; // seems best whatever p
      //cout << "exp(p * k * log1p(-p)) " << p * exp(k * log1p(-p)) << endl;
      //if (p < 0.5)
      //{
      //  result = p *  pow(q, k);
      //}
      //else
      //{
      //  result = p * exp(k * log1p(-p));
      //}
      result = p * pow(q, k);
      return result;
    } // geometric_pdf

    template <class RealType, class Policy>
    inline RealType cdf(const geometric_distribution<RealType, Policy>& dist, const RealType& k)
    { // Cumulative Distribution Function of geometric.
      static const char* function = "boost::math::cdf(const geometric_distribution<%1%>&, %1%)";

      // k argument may be integral, signed, or unsigned, or floating point.
      // If necessary, it has already been promoted from an integral type.
      RealType p = dist.success_fraction();
      // Error check:
      RealType result = 0;
      if(false == geometric_detail::check_dist_and_k(
        function,
        p,
        k,
        &result, Policy()))
      {
        return result;
      }
      if(k == 0)
      {
        return p; // success_fraction
      }
      //RealType q = 1 - p;  // Bad for small p
      //RealType probability = 1 - std::pow(q, k+1);

      RealType z = boost::math::log1p(-p, Policy()) * (k + 1);
      RealType probability = -boost::math::expm1(z, Policy());

      return probability;
    } // cdf Cumulative Distribution Function geometric.

      template <class RealType, class Policy>
      inline RealType cdf(const complemented2_type<geometric_distribution<RealType, Policy>, RealType>& c)
      { // Complemented Cumulative Distribution Function geometric.
      BOOST_MATH_STD_USING
      static const char* function = "boost::math::cdf(const geometric_distribution<%1%>&, %1%)";
      // k argument may be integral, signed, or unsigned, or floating point.
      // If necessary, it has already been promoted from an integral type.
      RealType const& k = c.param;
      geometric_distribution<RealType, Policy> const& dist = c.dist;
      RealType p = dist.success_fraction();
      // Error check:
      RealType result = 0;
      if(false == geometric_detail::check_dist_and_k(
        function,
        p,
        k,
        &result, Policy()))
      {
        return result;
      }
      RealType z = boost::math::log1p(-p, Policy()) * (k+1);
      RealType probability = exp(z);
      return probability;
    } // cdf Complemented Cumulative Distribution Function geometric.

    template <class RealType, class Policy>
    inline RealType quantile(const geometric_distribution<RealType, Policy>& dist, const RealType& x)
    { // Quantile, percentile/100 or Percent Point geometric function.
      // Return the number of expected failures k for a given probability p.

      // Inverse cumulative Distribution Function or Quantile (percentile / 100) of geometric Probability.
      // k argument may be integral, signed, or unsigned, or floating point.

      static const char* function = "boost::math::quantile(const geometric_distribution<%1%>&, %1%)";
      BOOST_MATH_STD_USING // ADL of std functions.

      RealType success_fraction = dist.success_fraction();
      // Check dist and x.
      RealType result = 0;
      if(false == geometric_detail::check_dist_and_prob
        (function, success_fraction, x, &result, Policy()))
      {
        return result;
      }

      // Special cases.
      if (x == 1)
      {  // Would need +infinity failures for total confidence.
        result = policies::raise_overflow_error<RealType>(
            function,
            "Probability argument is 1, which implies infinite failures !", Policy());
        return result;
       // usually means return +std::numeric_limits<RealType>::infinity();
       // unless #define BOOST_MATH_THROW_ON_OVERFLOW_ERROR
      }
      if (x == 0)
      { // No failures are expected if P = 0.
        return 0; // Total trials will be just dist.successes.
      }
      // if (P <= pow(dist.success_fraction(), 1))
      if (x <= success_fraction)
      { // p <= pdf(dist, 0) == cdf(dist, 0)
        return 0;
      }
      if (x == 1)
      {
        return 0;
      }

      // log(1-x) /log(1-success_fraction) -1; but use log1p in case success_fraction is small
      result = boost::math::log1p(-x, Policy()) / boost::math::log1p(-success_fraction, Policy()) - 1;
      // Subtract a few epsilons here too?
      // to make sure it doesn't slip over, so ceil would be one too many.
      return result;
    } // RealType quantile(const geometric_distribution dist, p)

    template <class RealType, class Policy>
    inline RealType quantile(const complemented2_type<geometric_distribution<RealType, Policy>, RealType>& c)
    {  // Quantile or Percent Point Binomial function.
       // Return the number of expected failures k for a given
       // complement of the probability Q = 1 - P.
       static const char* function = "boost::math::quantile(const geometric_distribution<%1%>&, %1%)";
       BOOST_MATH_STD_USING
       // Error checks:
       RealType x = c.param;
       const geometric_distribution<RealType, Policy>& dist = c.dist;
       RealType success_fraction = dist.success_fraction();
       RealType result = 0;
       if(false == geometric_detail::check_dist_and_prob(
          function,
          success_fraction,
          x,
          &result, Policy()))
       {
          return result;
       }

       // Special cases:
       if(x == 1)
       {  // There may actually be no answer to this question,
          // since the probability of zero failures may be non-zero,
          return 0; // but zero is the best we can do:
       }
       if (-x <= boost::math::powm1(dist.success_fraction(), dist.successes(), Policy()))
       {  // q <= cdf(complement(dist, 0)) == pdf(dist, 0)
          return 0; //
       }
       if(x == 0)
       {  // Probability 1 - Q  == 1 so infinite failures to achieve certainty.
          // Would need +infinity failures for total confidence.
          result = policies::raise_overflow_error<RealType>(
             function,
             "Probability argument complement is 0, which implies infinite failures !", Policy());
          return result;
          // usually means return +std::numeric_limits<RealType>::infinity();
          // unless #define BOOST_MATH_THROW_ON_OVERFLOW_ERROR
       }
       // log(x) /log(1-success_fraction) -1; but use log1p in case success_fraction is small
       result = log(x) / boost::math::log1p(-success_fraction, Policy()) - 1;
      return result;

    } // quantile complement

 } // namespace math
} // namespace boost

// This include must be at the end, *after* the accessors
// for this distribution have been defined, in order to
// keep compilers that support two-phase lookup happy.
#include <boost/math/distributions/detail/derived_accessors.hpp>

#if defined (BOOST_MSVC)
# pragma warning(pop)
#endif

#endif // BOOST_MATH_SPECIAL_GEOMETRIC_HPP


#include "tracer_factory.h"
#include "potential.hpp"
#include "tracer.hpp"
#include "observers/observer_factory.hpp"
#include "dynamics/dynamics_factory.hpp"
#include "dynamics/ray_dynamics.hpp"
#include <boost/lexical_cast.hpp>
#include <fstream>
#include "fileIO.hpp"
#include "interpolation.hpp"
#include "factory/factory.hpp"

using std::fstream;

TracerFactory::TracerFactory() = default;

void TracerFactory::loadFile( std::string filename )
{
	mFilename = std::move(filename);
	fstream ifile(mFilename, fstream::in | std::fstream::binary);
	if( !ifile.is_open() )
	{
		std::cerr << "could not open potential file " << mFilename << "\n";
		exit( EXIT_FAILURE );
	}

	setPotential(Potential::readFromFile(ifile));
}

void TracerFactory::setPotential( Potential p )
{
	mPotential = std::make_shared<Potential>( std::move(p) );
}

void TracerFactory::setPeriodicBondaries( bool pb )
{
	mPeriodicBoundaries = pb;
}

std::shared_ptr<Tracer> TracerFactory::createTracer() const
{
	if(!mPotential)
		THROW_EXCEPTION( std::runtime_error, "trying to create tracer, but no potential has been set!");

	// figure out whether we need monodromy
	// create observers
	bool monodromy = std::any_of(begin(mObserverConfig), end(mObserverConfig),
				[](const std::vector<std::string>& options ){
					return getObserverFactory().get_builder(options.front())->need_monodromy(); }
	);
	auto dynamics = getDynamicsFactory().create(mDynamicsType, mDynamicsConfig, *mPotential, mPeriodicBoundaries,
												monodromy);

	auto tracer = std::make_shared<Tracer>( *mPotential, std::move(dynamics));

	tracer->setMaxThreads( mThreads );

	// create observers
	for(const auto& cfg : mObserverConfig)
	{
		std::vector<std::string> options;
		std::copy(cfg.begin() + 1, cfg.end(), std::back_inserter(options));
		auto observer = getObserverFactory().create(cfg.front(), options, *mPotential);
		tracer->addObserver( std::move(observer) );
	}

	tracer->setErrorBounds( mAbsErr, mRelErr );
	tracer->setEndTime( mEndTime );
	tracer->setIntegrator(mIntegrator);

	if(mDT > 0) {
		tracer->setTimeStep(mDT);
	}

	return tracer;
}

void TracerFactory::setPotentialStrength( double strength )
{ ;
	mPotential->setStrength(strength);
}

std::string TracerFactory::getPotentialInfo() const
{
	std::stringstream potential_data_str;
	potential_data_str << "size: " << mPotential->getExtents()[0];
	for(unsigned i = 1; i < mPotential->getDimension(); ++i)
		potential_data_str << "x" << mPotential->getExtents()[i];
	potential_data_str << "\nsupport: " << mPotential->getSupport()[0];
	for(unsigned i = 1; i < mPotential->getDimension(); ++i)
		potential_data_str << "x" << mPotential->getSupport()[i];
	potential_data_str << "\nseed: " << mPotential->getSeed() << "\n";
	potential_data_str << "corr length: " << mPotential->getCorrelationLength() << "\n";
	potential_data_str << "strength: " << mPotential->getStrength() << "\n";
	potential_data_str << "version: " << mPotential->getPotgenVersion() << "\n";
	return potential_data_str.str();
}

void TracerFactory::setObserverConfig( std::vector<std::string> cfg )
{
	mObserverConfig.clear();
	std::vector<std::string> known_observers = getObserverFactory().getTypes();
	for( const auto& str : cfg )
	{
		if( std::count(known_observers.begin(), known_observers.end(), str) != 0 )
		{
			mObserverConfig.emplace_back();
		}

		if(mObserverConfig.empty())
			THROW_EXCEPTION(std::runtime_error, "%1% is not an observer name.", str);
		mObserverConfig.back().push_back(str);
	}
}

void TracerFactory::setDynamicsConfig( std::vector<std::string> cfg )
{
	mDynamicsConfig.clear();
	mDynamicsType = cfg[0];
	std::move(begin(cfg)+1, end(cfg), std::back_inserter(mDynamicsConfig) );
}

void TracerFactory::setIntegrator(const std::string& integrator) {
    if(integrator == "adaptive") {
        mIntegrator = Integrator::RUNGE_KUTTA_CASH_KARP_54_ADAPTIVE;
    } else if(integrator == "euler") {
        mIntegrator = Integrator::EULER_CONST;
    } else {
        THROW_EXCEPTION( std::runtime_error, "Unknown integrator %1%", integrator);
    }
}


#include "Approx_Objective.hxx"
#include "../sdp_solve.hxx"
#include "../sdp_read.hxx"

#include <boost/filesystem.hpp>

void Axpy(const El::BigFloat &alpha, const SDP &new_sdp, SDP &delta_sdp);

std::vector<std::pair<std::string, Approx_Objective>>
quadratic_approximate_objectives(
  const Block_Info &block_info, const El::Grid &grid, const SDP &sdp,
  const Block_Vector &x, const Block_Vector &y,
  const Block_Diagonal_Matrix &schur_complement_cholesky,
  const Block_Matrix &schur_off_diagonal,
  const El::DistMatrix<El::BigFloat> &Q,
  const boost::filesystem::path &input_path)
{
  std::vector<std::pair<std::string, Approx_Objective>> result;
  if(input_path.extension() == ".nsv")
    {
      for(auto &filename : read_file_list(input_path))
        {
          for(auto &objective : quadratic_approximate_objectives(
                block_info, grid, sdp, x, y, schur_complement_cholesky,
                schur_off_diagonal, Q, filename))
            {
              result.push_back(objective);
            }
        }
    }
  else
    {
      SDP new_sdp(input_path, block_info, grid), d_sdp(new_sdp);
      Axpy(El::BigFloat(-1), sdp, d_sdp);

      result.emplace_back(input_path.string(),
                          Approx_Objective(block_info, sdp, d_sdp, x, y,
                                           schur_complement_cholesky,
                                           schur_off_diagonal, Q));
    }
  return result;
}