////////////////////////////////////////////////////////////////////////////////////////////////////
// main.cpp
//
//  Copyright 2012 Eric Niebler. 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)

#pragma clang diagnostic ignored "-Wmultichar"
#include <typeinfo>
#include <map>
#include <iostream>
#include <boost/proto/v5/proto.hpp>

namespace mpl = boost::mpl;
namespace proto = boost::proto;
namespace fusion = boost::fusion;
using proto::_;

using namespace proto;
struct Expr
  : def<
        match(
            case_(terminal(unsigned long long),     _value)
          , case_(plus(Expr, Expr),                 plus(Expr(_left), Expr(_right))) // here
          , case_(multiplies(Expr, Expr),           multiplies(Expr(_left), Expr(_right)))
        )
    >
{};

template<typename A>
struct ExprF
  : def<
        match(
            terminal(unsigned long long)
          , plus(A, A)
          , multiplies(A, A)
        )
    >
{};

template<template<typename> class F>
struct Fix
  : F<Fix<F>>
{};

using Expr2 = Fix<ExprF>;

int main()
{
    using namespace proto::literals;
    auto x = 1_et + 2_et;

    proto::assert_matches<Expr2>(x);

    unsigned long long y = Expr()(x);
    std::cout << y << std::endl;

    void done();
    done();
}

//// The argument placeholder type
//template<typename I>
//struct placeholder
//  : I, proto::env_tag<placeholder<I>>
//{
//    using proto::env_tag<placeholder<I>>::operator=;
//};
//
//// Give each rule in the grammar a "name". This is so that we
//// can easily dispatch on it later.
//struct calc_grammar;
//
//struct divides_rule
//  : proto::def<proto::divides(calc_grammar, calc_grammar)>
//  , proto::env_tag<divides_rule>
//{
//    using proto::env_tag<divides_rule>::operator=;
//};
//
//// Use external transforms in calc_gramar
//struct calc_grammar
//  : proto::def<
//        proto::match(
//            proto::case_(
//                proto::terminal(placeholder<_>)
//              , proto::get_env(proto::_value)
//            )
//          , proto::case_(
//                proto::terminal(proto::convertible_to<double>)
//              , proto::_value
//            )
//          , proto::case_(
//                proto::plus(calc_grammar, calc_grammar)
//              , proto::eval_with(calc_grammar)
//            )
//          , proto::case_(
//                proto::minus(calc_grammar, calc_grammar)
//              , proto::eval_with(calc_grammar)
//            )
//          , proto::case_(
//                proto::multiplies(calc_grammar, calc_grammar)
//              , proto::eval_with(calc_grammar)
//            )
//            // Note that we don't specify how division nodes are
//            // handled here. proto::external is a placeholder
//            // for an actual action.
//          , proto::case_(
//                divides_rule
//              , proto::external
//            )
//        )
//    >
//{};
//
//template<typename ExprDesc>
//struct calc_expr
//  : proto::expr<calc_expr<ExprDesc>>
//{
//    using proto::expr<calc_expr<ExprDesc>>::expr;
//    using proto::expr<calc_expr<ExprDesc>>::operator=;
//};
//
//constexpr calc_expr<proto::terminal(placeholder<mpl::int_<0>>)> _1 {};
//constexpr calc_expr<proto::terminal(placeholder<mpl::int_<1>>)> _2 {};
//constexpr calc_expr<proto::terminal(placeholder<mpl::int_<2>>)> _3 {};
//constexpr calc_expr<proto::terminal(placeholder<mpl::int_<3>>)> _4 {};
//constexpr calc_expr<proto::terminal(placeholder<mpl::int_<4>>)> _5 {};
//constexpr calc_expr<proto::terminal(placeholder<mpl::int_<5>>)> _6 {};
//
//struct non_checked_division
//  : proto::def<proto::eval_with(calc_grammar)>
//{};
//
//struct division_by_zero
//  : std::exception
//{};
//
//struct do_checked_divide
//{
//    int operator()(int left, int right) const
//    {
//        if (right == 0)
//            throw division_by_zero();
//        return left / right;
//    }
//};
//
//// Use proto::external_transforms again, this time to map the divides_rule
//// to a transforms that performs checked division.
//struct checked_division
//  : proto::def<do_checked_divide(calc_grammar(proto::_left), calc_grammar(proto::_right))>
//{};
//
////namespace detail
////{
////    using namespace proto;
////
////    struct key_value_grammar
////      : def<assign(terminal(_), terminal(_))>
////    {};
////
////    struct as_env_
////    {
////        template<typename KV, typename Env
////            , BOOST_PROTO_ENABLE_IF(!matches<KV, key_value_grammar>())>
////        constexpr envs::empty_env operator()(KV &&, Env &&env) const noexcept
////        {
////            static_assert(matches<KV, key_value_grammar>(), "Expected a key/value pair");
////            return envs::empty_env();
////        }
////
////        template<typename KV, typename Env
////            , BOOST_PROTO_ENABLE_IF(matches<KV, key_value_grammar>())>
////        constexpr auto operator()(KV &&kv, Env &&env) const
////        BOOST_PROTO_AUTO_RETURN(
////            proto::envs::make_env(
////                proto::value(proto::left(static_cast<KV &&>(kv)))
////              , proto::value(proto::right(static_cast<KV &&>(kv)))
////              , static_cast<Env &&>(env)
////            )
////        )
////    };
////
////    struct make_env_
////    {
////        template<typename Env>
////        constexpr auto operator()(Env &&env) const
////        BOOST_PROTO_AUTO_RETURN(
////            Env(static_cast<Env &&>(env))
////        )
////
////        template<typename Env, typename KV, typename ...Rest, typename Impl = make_env_>
////        constexpr auto operator()(Env &&env, KV &&kv, Rest &&... rest) const
////        BOOST_PROTO_AUTO_RETURN(
////            BOOST_PROTO_TRY_CALL(Impl())(
////                BOOST_PROTO_TRY_CALL(::detail::as_env_())(
////                    static_cast<KV &&>(kv)
////                  , static_cast<Env &&>(env)
////                )
////              , static_cast<Rest &&>(rest)...
////            )
////        )
////    };
////}
////
////template<typename ...KeyValue>
////constexpr auto make_env(KeyValue &&... key_value)
////BOOST_PROTO_AUTO_RETURN(
////    BOOST_PROTO_TRY_CALL(detail::make_env_())(
////        proto::envs::empty_env()
////      , static_cast<KeyValue &&>(key_value)...
////    )
////)
//
//int main()
//{
//    //auto env = ::make_env(_1 = 1, _2 = 2, _3 = 3, _4 = 4, _5 = 5, _6 = 6, _1 = 7);
//    //std::cout << proto::detail::name_of<decltype(env)>() << std::endl;
//
//    //std::cout << env( proto::value(_1) ) << std::endl;
//    //std::cout << env( proto::value(_2) ) << std::endl;
//    //std::cout << env( proto::value(_3) ) << std::endl;
//    //std::cout << env( proto::value(_4) ) << std::endl;
//    //std::cout << env( proto::value(_5) ) << std::endl;
//    //std::cout << env( proto::value(_6) ) << std::endl;
//
//    //placeholder<mpl::int_<0>> _1_; // BUGBUG would be nice to just say _1 = 2;
//    //placeholder<mpl::int_<1>> _2_;
//    //int result1 = calc_grammar()(_1 / _2, (_1_ = 6, _2_ = 2, divides_rule() = non_checked_division()));
//    //std::cout << result1 << std::endl;
//
//    //try
//    //{
//    //    // This should throw
//    //    int result2 = calc_grammar()(_1 / _2, (_1_ = 6, _2_ = 0, divides_rule() = checked_division()));
//    //    //throw division_by_zero();
//    //}
//    //catch(division_by_zero const &e)
//    //{
//    //    std::cout << "here " << e.what() << std::endl;
//    //}
//    ////catch(...)
//    ////{
//    ////    std::cout << "oops" << std::endl;
//    ////}
//
//    void done();
//    done();
//}

//namespace proto = boost::proto;
//using namespace proto;
//using namespace literals;
//
//struct map_list_of_  {};
//
//struct MapListOf : def<
//    match(
//        case_(  terminal(map_list_of_),
//            void()
//        ),
//        case_(  function(MapListOf, terminal(_), terminal(_)),
//            MapListOf(_child0),
//            assign(subscript(_data, _value(_child1)), _value(_child2))
//        )
//    )
//> {};
//
//template<typename ExprDesc>
//struct map_list_of_expr
//  : expr<map_list_of_expr<ExprDesc>, domain<_, MapListOf>>
//{
//    using expr<map_list_of_expr, domain<_, MapListOf>>::expr;
//
//    template<class K, class V, class C, class A>
//    operator std::map<K,V,C,A>() const
//    {
//        BOOST_PROTO_ASSERT_MATCHES(*this, MapListOf);
//        std::map<K,V,C,A> m;
//        MapListOf()( *this, data = m );
//        return m;
//    }
//};
//
//constexpr map_list_of_expr<terminal(map_list_of_)> map_list_of {};
//
//int main()
//{
//    std::map<int, int> m = map_list_of(1,2)(2,3)(3,4)(4,5)(5,6);
//
//    for(auto p : m)
//    {
//        std::cout << p.first << " => " << p.second << std::endl;
//    }
//
//    void done();
//    done();
//}

/*
#include <cstdio>
#include <utility>
#include <typeinfo>
#include <type_traits>
#include <memory>
#include <cstdlib>
#include <boost/proto/v5/proto.hpp>
#include <boost/proto/v5/debug.hpp>

namespace proto = boost::proto;
using proto::_;

template<typename T>
struct placeholder
  : proto::env_tag<placeholder<T>>
{
    using proto::env_tag<placeholder<T>>::operator=;
};

static_assert(std::is_trivial<placeholder<std::integral_constant<std::size_t, 0>>>::value, "");

// So placeholder terminals can be pretty-printed with display_expr
template<typename T>
std::ostream & operator << (std::ostream & s, placeholder<T>)
{
    return s << "_" << T::value + 1;
}

template<std::size_t I>
using placeholder_c = placeholder<std::integral_constant<std::size_t, I>>;

struct lambda_eval
  : proto::def<
        proto::match(
            proto::case_( proto::terminal(placeholder<_>),
                proto:get_env(proto::_value)
            )
          , proto::case_( proto::terminal(_),
                proto::_value
            )
          , proto::default_(
                proto::eval_with(lambda_eval)
            )
        )
    >
{};

template<std::size_t ...I, typename E, typename ...T>
inline auto lambda_eval_(proto::utility::indices<I...>, E && e, T &&... t)
BOOST_PROTO_AUTO_RETURN(
    lambda_eval()(
        std::forward<E>(e)
      , proto::make_env(placeholder_c<I>() = std::forward<T>(t)...)
    )
)

template<typename ExprDesc>
struct lambda_expr;

struct lambda_domain
  : proto::domain<lambda_domain>
{
    using make_expr = proto::make_custom_expr<lambda_expr<_>>;
    using store_child = proto::utility::by_ref;
};

template<typename ExprDesc>
struct lambda_expr
  : proto::basic_expr<ExprDesc>
  , proto::expr_assign<lambda_expr<ExprDesc>>
  , proto::expr_subscript<lambda_expr<ExprDesc>>
{
    using proto_domain_type = lambda_domain;
    using proto::basic_expr<ExprDesc>::basic_expr;
    using proto::expr_assign<lambda_expr>::operator=;

    template<typename ...T>
    auto operator()(T &&... t) const
    BOOST_PROTO_AUTO_RETURN(
        lambda_eval_(proto::utility::make_indices<sizeof...(T)>(), *this, std::forward<T>(t)...)
    )
};

template<typename T>
using lambda_var = lambda_expr<proto::terminal(T)>;

namespace
{
    constexpr auto const & _1 = proto::utility::static_const<lambda_var<placeholder_c<0>>>::value;
    constexpr auto const & _2 = proto::utility::static_const<lambda_var<placeholder_c<1>>>::value;
    constexpr auto const & _3 = proto::utility::static_const<lambda_var<placeholder_c<2>>>::value;
}

static_assert(std::is_trivial<lambda_var<placeholder_c<0>>>::value, "_1 should be trivial");
BOOST_PROTO_IGNORE_UNUSED(_1, _2, _3);

using make_minus = proto::functional::make_expr<proto::minus>;

struct Invert
  : proto::def<
        proto::match(
            proto::case_(   proto::terminal(_),
                            proto::_                            )
          , proto::case_(   proto::plus(_,_),
                            proto::minus(Invert(proto::_left),
                                         Invert(proto::_right)) )
          , proto::case_(   _(Invert...),
                            proto::passthru                         )
        )
    >
{};

int main()
{
    std::printf("*** \n");
    std::printf("*** This program demonstrates how to build a lambda library with Proto.\n");
    std::printf("*** \n");

    std::cout << "expr:\n"
              << proto::pretty_expr(_1 + 42 * _2, 4)
              << std::endl;

    // Create a lambda
    auto fun = proto::deep_copy(_1 + 42 * _2);

    std::cout << "deep-copied expr:\n"
              << proto::pretty_expr(fun, 4)
              << std::endl;

    std::cout << "Invert'ed expr:\n"
              << proto::pretty_expr(Invert()(fun), 4)
              << std::endl;

    // call the lambda
    int i = fun(8, 2);

    // print the result
    std::printf("the lambda '_1 + 42 * _2' yields '%d' when called with 8 and 2.\n", i);

    void done();
    done();
}
//*/

#include <iostream>
void done()
{
    char ch = 0;
    std::cout << "CTRL+D to end...";
    std::cin >> ch;
}


#pragma once

#include <boost/python.hpp>
#include <boost/python/refcount.hpp>

#include <Eigen/Eigen>

struct EigenM33f_to_python
{
   static PyObject* convert(const Eigen::Matrix<float, 3, 3> &m)
   {
      return
         boost::python::incref(
            boost::python::make_tuple(
               boost::python::make_tuple(m(0,0), m(1,0), m(2,0)),
               boost::python::make_tuple(m(0,1), m(1,1), m(2,1)),
               boost::python::make_tuple(m(0,2), m(1,2), m(2,2))
            ).ptr()
         );
   }
};


#include <gtwrap/matlab.h>
#include <map>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/export.hpp>

#include <folder/path/to/Test.h>
#include <gtsam/geometry/Cal3Bundler.h>
#include <gtsam/geometry/Point2.h>
#include <gtsam/geometry/Point3.h>
#include <gtsam/nonlinear/Values.h>
#include <path/to/ns1.h>
#include <path/to/ns1/ClassB.h>
#include <path/to/ns2.h>
#include <path/to/ns2/ClassA.h>
#include <path/to/ns3.h>

typedef Fun<double> FunDouble;
typedef PrimitiveRef<double> PrimitiveRefDouble;
typedef MyVector<3> MyVector3;
typedef MyVector<12> MyVector12;
typedef MultipleTemplates<int, double> MultipleTemplatesIntDouble;
typedef MultipleTemplates<int, float> MultipleTemplatesIntFloat;
typedef MyFactor<gtsam::Pose2, gtsam::Matrix> MyFactorPosePoint2;
typedef MyTemplate<gtsam::Point2> MyTemplatePoint2;
typedef MyTemplate<gtsam::Matrix> MyTemplateMatrix;
typedef gtsam::PinholeCamera<gtsam::Cal3Bundler> PinholeCameraCal3Bundler;
typedef gtsam::GeneralSFMFactor<gtsam::PinholeCamera<gtsam::Cal3Bundler>, gtsam::Point3> GeneralSFMFactorCal3Bundler;

BOOST_CLASS_EXPORT_GUID(gtsam::Point2, "gtsamPoint2");
BOOST_CLASS_EXPORT_GUID(gtsam::Point3, "gtsamPoint3");

typedef std::set<boost::shared_ptr<FunRange>*> Collector_FunRange;
static Collector_FunRange collector_FunRange;
typedef std::set<boost::shared_ptr<FunDouble>*> Collector_FunDouble;
static Collector_FunDouble collector_FunDouble;
typedef std::set<boost::shared_ptr<Test>*> Collector_Test;
static Collector_Test collector_Test;
typedef std::set<boost::shared_ptr<PrimitiveRefDouble>*> Collector_PrimitiveRefDouble;
static Collector_PrimitiveRefDouble collector_PrimitiveRefDouble;
typedef std::set<boost::shared_ptr<MyVector3>*> Collector_MyVector3;
static Collector_MyVector3 collector_MyVector3;
typedef std::set<boost::shared_ptr<MyVector12>*> Collector_MyVector12;
static Collector_MyVector12 collector_MyVector12;
typedef std::set<boost::shared_ptr<MultipleTemplatesIntDouble>*> Collector_MultipleTemplatesIntDouble;
static Collector_MultipleTemplatesIntDouble collector_MultipleTemplatesIntDouble;
typedef std::set<boost::shared_ptr<MultipleTemplatesIntFloat>*> Collector_MultipleTemplatesIntFloat;
static Collector_MultipleTemplatesIntFloat collector_MultipleTemplatesIntFloat;
typedef std::set<boost::shared_ptr<ForwardKinematics>*> Collector_ForwardKinematics;
static Collector_ForwardKinematics collector_ForwardKinematics;
typedef std::set<boost::shared_ptr<MyFactorPosePoint2>*> Collector_MyFactorPosePoint2;
static Collector_MyFactorPosePoint2 collector_MyFactorPosePoint2;
typedef std::set<boost::shared_ptr<gtsam::Point2>*> Collector_gtsamPoint2;
static Collector_gtsamPoint2 collector_gtsamPoint2;
typedef std::set<boost::shared_ptr<gtsam::Point3>*> Collector_gtsamPoint3;
static Collector_gtsamPoint3 collector_gtsamPoint3;
typedef std::set<boost::shared_ptr<MyBase>*> Collector_MyBase;
static Collector_MyBase collector_MyBase;
typedef std::set<boost::shared_ptr<MyTemplatePoint2>*> Collector_MyTemplatePoint2;
static Collector_MyTemplatePoint2 collector_MyTemplatePoint2;
typedef std::set<boost::shared_ptr<MyTemplateMatrix>*> Collector_MyTemplateMatrix;
static Collector_MyTemplateMatrix collector_MyTemplateMatrix;
typedef std::set<boost::shared_ptr<ForwardKinematicsFactor>*> Collector_ForwardKinematicsFactor;
static Collector_ForwardKinematicsFactor collector_ForwardKinematicsFactor;
typedef std::set<boost::shared_ptr<ns1::ClassA>*> Collector_ns1ClassA;
static Collector_ns1ClassA collector_ns1ClassA;
typedef std::set<boost::shared_ptr<ns1::ClassB>*> Collector_ns1ClassB;
static Collector_ns1ClassB collector_ns1ClassB;
typedef std::set<boost::shared_ptr<ns2::ClassA>*> Collector_ns2ClassA;
static Collector_ns2ClassA collector_ns2ClassA;
typedef std::set<boost::shared_ptr<ns2::ns3::ClassB>*> Collector_ns2ns3ClassB;
static Collector_ns2ns3ClassB collector_ns2ns3ClassB;
typedef std::set<boost::shared_ptr<ns2::ClassC>*> Collector_ns2ClassC;
static Collector_ns2ClassC collector_ns2ClassC;
typedef std::set<boost::shared_ptr<ClassD>*> Collector_ClassD;
static Collector_ClassD collector_ClassD;
typedef std::set<boost::shared_ptr<gtsam::Values>*> Collector_gtsamValues;
static Collector_gtsamValues collector_gtsamValues;
typedef std::set<boost::shared_ptr<gtsam::NonlinearFactorGraph>*> Collector_gtsamNonlinearFactorGraph;
static Collector_gtsamNonlinearFactorGraph collector_gtsamNonlinearFactorGraph;
typedef std::set<boost::shared_ptr<gtsam::SfmTrack>*> Collector_gtsamSfmTrack;
static Collector_gtsamSfmTrack collector_gtsamSfmTrack;
typedef std::set<boost::shared_ptr<PinholeCameraCal3Bundler>*> Collector_gtsamPinholeCameraCal3Bundler;
static Collector_gtsamPinholeCameraCal3Bundler collector_gtsamPinholeCameraCal3Bundler;
typedef std::set<boost::shared_ptr<GeneralSFMFactorCal3Bundler>*> Collector_gtsamGeneralSFMFactorCal3Bundler;
static Collector_gtsamGeneralSFMFactorCal3Bundler collector_gtsamGeneralSFMFactorCal3Bundler;

void _deleteAllObjects()
{
  mstream mout;
  std::streambuf *outbuf = std::cout.rdbuf(&mout);

  bool anyDeleted = false;
  { for(Collector_FunRange::iterator iter = collector_FunRange.begin();
      iter != collector_FunRange.end(); ) {
    delete *iter;
    collector_FunRange.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_FunDouble::iterator iter = collector_FunDouble.begin();
      iter != collector_FunDouble.end(); ) {
    delete *iter;
    collector_FunDouble.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_Test::iterator iter = collector_Test.begin();
      iter != collector_Test.end(); ) {
    delete *iter;
    collector_Test.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_PrimitiveRefDouble::iterator iter = collector_PrimitiveRefDouble.begin();
      iter != collector_PrimitiveRefDouble.end(); ) {
    delete *iter;
    collector_PrimitiveRefDouble.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_MyVector3::iterator iter = collector_MyVector3.begin();
      iter != collector_MyVector3.end(); ) {
    delete *iter;
    collector_MyVector3.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_MyVector12::iterator iter = collector_MyVector12.begin();
      iter != collector_MyVector12.end(); ) {
    delete *iter;
    collector_MyVector12.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_MultipleTemplatesIntDouble::iterator iter = collector_MultipleTemplatesIntDouble.begin();
      iter != collector_MultipleTemplatesIntDouble.end(); ) {
    delete *iter;
    collector_MultipleTemplatesIntDouble.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_MultipleTemplatesIntFloat::iterator iter = collector_MultipleTemplatesIntFloat.begin();
      iter != collector_MultipleTemplatesIntFloat.end(); ) {
    delete *iter;
    collector_MultipleTemplatesIntFloat.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_ForwardKinematics::iterator iter = collector_ForwardKinematics.begin();
      iter != collector_ForwardKinematics.end(); ) {
    delete *iter;
    collector_ForwardKinematics.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_MyFactorPosePoint2::iterator iter = collector_MyFactorPosePoint2.begin();
      iter != collector_MyFactorPosePoint2.end(); ) {
    delete *iter;
    collector_MyFactorPosePoint2.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_gtsamPoint2::iterator iter = collector_gtsamPoint2.begin();
      iter != collector_gtsamPoint2.end(); ) {
    delete *iter;
    collector_gtsamPoint2.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_gtsamPoint3::iterator iter = collector_gtsamPoint3.begin();
      iter != collector_gtsamPoint3.end(); ) {
    delete *iter;
    collector_gtsamPoint3.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_MyBase::iterator iter = collector_MyBase.begin();
      iter != collector_MyBase.end(); ) {
    delete *iter;
    collector_MyBase.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_MyTemplatePoint2::iterator iter = collector_MyTemplatePoint2.begin();
      iter != collector_MyTemplatePoint2.end(); ) {
    delete *iter;
    collector_MyTemplatePoint2.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_MyTemplateMatrix::iterator iter = collector_MyTemplateMatrix.begin();
      iter != collector_MyTemplateMatrix.end(); ) {
    delete *iter;
    collector_MyTemplateMatrix.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_ForwardKinematicsFactor::iterator iter = collector_ForwardKinematicsFactor.begin();
      iter != collector_ForwardKinematicsFactor.end(); ) {
    delete *iter;
    collector_ForwardKinematicsFactor.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_ns1ClassA::iterator iter = collector_ns1ClassA.begin();
      iter != collector_ns1ClassA.end(); ) {
    delete *iter;
    collector_ns1ClassA.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_ns1ClassB::iterator iter = collector_ns1ClassB.begin();
      iter != collector_ns1ClassB.end(); ) {
    delete *iter;
    collector_ns1ClassB.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_ns2ClassA::iterator iter = collector_ns2ClassA.begin();
      iter != collector_ns2ClassA.end(); ) {
    delete *iter;
    collector_ns2ClassA.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_ns2ns3ClassB::iterator iter = collector_ns2ns3ClassB.begin();
      iter != collector_ns2ns3ClassB.end(); ) {
    delete *iter;
    collector_ns2ns3ClassB.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_ns2ClassC::iterator iter = collector_ns2ClassC.begin();
      iter != collector_ns2ClassC.end(); ) {
    delete *iter;
    collector_ns2ClassC.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_ClassD::iterator iter = collector_ClassD.begin();
      iter != collector_ClassD.end(); ) {
    delete *iter;
    collector_ClassD.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_gtsamValues::iterator iter = collector_gtsamValues.begin();
      iter != collector_gtsamValues.end(); ) {
    delete *iter;
    collector_gtsamValues.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_gtsamNonlinearFactorGraph::iterator iter = collector_gtsamNonlinearFactorGraph.begin();
      iter != collector_gtsamNonlinearFactorGraph.end(); ) {
    delete *iter;
    collector_gtsamNonlinearFactorGraph.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_gtsamSfmTrack::iterator iter = collector_gtsamSfmTrack.begin();
      iter != collector_gtsamSfmTrack.end(); ) {
    delete *iter;
    collector_gtsamSfmTrack.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_gtsamPinholeCameraCal3Bundler::iterator iter = collector_gtsamPinholeCameraCal3Bundler.begin();
      iter != collector_gtsamPinholeCameraCal3Bundler.end(); ) {
    delete *iter;
    collector_gtsamPinholeCameraCal3Bundler.erase(iter++);
    anyDeleted = true;
  } }
  { for(Collector_gtsamGeneralSFMFactorCal3Bundler::iterator iter = collector_gtsamGeneralSFMFactorCal3Bundler.begin();
      iter != collector_gtsamGeneralSFMFactorCal3Bundler.end(); ) {
    delete *iter;
    collector_gtsamGeneralSFMFactorCal3Bundler.erase(iter++);
    anyDeleted = true;
  } }
  if(anyDeleted)
    cout <<
      "WARNING:  Wrap modules with variables in the workspace have been reloaded due to\n"
      "calling destructors, call 'clear all' again if you plan to now recompile a wrap\n"
      "module, so that your recompiled module is used instead of the old one." << endl;
  std::cout.rdbuf(outbuf);
}

void _special_cases_RTTIRegister() {
  const mxArray *alreadyCreated = mexGetVariablePtr("global", "gtsam_special_cases_rttiRegistry_created");
  if(!alreadyCreated) {
    std::map<std::string, std::string> types;
    types.insert(std::make_pair(typeid(MyBase).name(), "MyBase"));
    types.insert(std::make_pair(typeid(MyTemplatePoint2).name(), "MyTemplatePoint2"));
    types.insert(std::make_pair(typeid(MyTemplateMatrix).name(), "MyTemplateMatrix"));
    types.insert(std::make_pair(typeid(ForwardKinematicsFactor).name(), "ForwardKinematicsFactor"));

    mxArray *registry = mexGetVariable("global", "gtsamwrap_rttiRegistry");
    if(!registry)
      registry = mxCreateStructMatrix(1, 1, 0, NULL);
    typedef std::pair<std::string, std::string> StringPair;
    for(const StringPair& rtti_matlab: types) {
      int fieldId = mxAddField(registry, rtti_matlab.first.c_str());
      if(fieldId < 0)
        mexErrMsgTxt("gtsam wrap:  Error indexing RTTI types, inheritance will not work correctly");
      mxArray *matlabName = mxCreateString(rtti_matlab.second.c_str());
      mxSetFieldByNumber(registry, 0, fieldId, matlabName);
    }
    if(mexPutVariable("global", "gtsamwrap_rttiRegistry", registry) != 0)
      mexErrMsgTxt("gtsam wrap:  Error indexing RTTI types, inheritance will not work correctly");
    mxDestroyArray(registry);

    mxArray *newAlreadyCreated = mxCreateNumericMatrix(0, 0, mxINT8_CLASS, mxREAL);
    if(mexPutVariable("global", "gtsam_geometry_rttiRegistry_created", newAlreadyCreated) != 0)
      mexErrMsgTxt("gtsam wrap:  Error indexing RTTI types, inheritance will not work correctly");
    mxDestroyArray(newAlreadyCreated);
  }
}

void gtsamNonlinearFactorGraph_collectorInsertAndMakeBase_0(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
  mexAtExit(&_deleteAllObjects);
  typedef boost::shared_ptr<gtsam::NonlinearFactorGraph> Shared;

  Shared *self = *reinterpret_cast<Shared**> (mxGetData(in[0]));
  collector_gtsamNonlinearFactorGraph.insert(self);
}

void gtsamNonlinearFactorGraph_deconstructor_1(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
  typedef boost::shared_ptr<gtsam::NonlinearFactorGraph> Shared;
  checkArguments("delete_gtsamNonlinearFactorGraph",nargout,nargin,1);
  Shared *self = *reinterpret_cast<Shared**>(mxGetData(in[0]));
  Collector_gtsamNonlinearFactorGraph::iterator item;
  item = collector_gtsamNonlinearFactorGraph.find(self);
  if(item != collector_gtsamNonlinearFactorGraph.end()) {
    delete self;
    collector_gtsamNonlinearFactorGraph.erase(item);
  }
}

void gtsamNonlinearFactorGraph_addPrior_2(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
  checkArguments("addPriorPinholeCameraCal3Bundler",nargout,nargin-1,3);
  auto obj = unwrap_shared_ptr<gtsam::NonlinearFactorGraph>(in[0], "ptr_gtsamNonlinearFactorGraph");
  size_t key = unwrap< size_t >(in[1]);
  gtsam::PinholeCamera<gtsam::Cal3Bundler>& prior = *unwrap_shared_ptr< gtsam::PinholeCamera<gtsam::Cal3Bundler> >(in[2], "ptr_gtsamPinholeCameraCal3Bundler");
  boost::shared_ptr<gtsam::noiseModel::Base> noiseModel = unwrap_shared_ptr< gtsam::noiseModel::Base >(in[3], "ptr_gtsamnoiseModelBase");
  obj->addPrior<gtsam::PinholeCamera<gtsam::Cal3Bundler>>(key,prior,noiseModel);
}

void gtsamSfmTrack_collectorInsertAndMakeBase_3(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
  mexAtExit(&_deleteAllObjects);
  typedef boost::shared_ptr<gtsam::SfmTrack> Shared;

  Shared *self = *reinterpret_cast<Shared**> (mxGetData(in[0]));
  collector_gtsamSfmTrack.insert(self);
}

void gtsamSfmTrack_deconstructor_4(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
  typedef boost::shared_ptr<gtsam::SfmTrack> Shared;
  checkArguments("delete_gtsamSfmTrack",nargout,nargin,1);
  Shared *self = *reinterpret_cast<Shared**>(mxGetData(in[0]));
  Collector_gtsamSfmTrack::iterator item;
  item = collector_gtsamSfmTrack.find(self);
  if(item != collector_gtsamSfmTrack.end()) {
    delete self;
    collector_gtsamSfmTrack.erase(item);
  }
}

void gtsamPinholeCameraCal3Bundler_collectorInsertAndMakeBase_5(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
  mexAtExit(&_deleteAllObjects);
  typedef boost::shared_ptr<gtsam::PinholeCamera<gtsam::Cal3Bundler>> Shared;

  Shared *self = *reinterpret_cast<Shared**> (mxGetData(in[0]));
  collector_gtsamPinholeCameraCal3Bundler.insert(self);
}

void gtsamPinholeCameraCal3Bundler_deconstructor_6(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
  typedef boost::shared_ptr<gtsam::PinholeCamera<gtsam::Cal3Bundler>> Shared;
  checkArguments("delete_gtsamPinholeCameraCal3Bundler",nargout,nargin,1);
  Shared *self = *reinterpret_cast<Shared**>(mxGetData(in[0]));
  Collector_gtsamPinholeCameraCal3Bundler::iterator item;
  item = collector_gtsamPinholeCameraCal3Bundler.find(self);
  if(item != collector_gtsamPinholeCameraCal3Bundler.end()) {
    delete self;
    collector_gtsamPinholeCameraCal3Bundler.erase(item);
  }
}

void gtsamGeneralSFMFactorCal3Bundler_collectorInsertAndMakeBase_7(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
  mexAtExit(&_deleteAllObjects);
  typedef boost::shared_ptr<gtsam::GeneralSFMFactor<gtsam::PinholeCamera<gtsam::Cal3Bundler>, gtsam::Point3>> Shared;

  Shared *self = *reinterpret_cast<Shared**> (mxGetData(in[0]));
  collector_gtsamGeneralSFMFactorCal3Bundler.insert(self);
}

void gtsamGeneralSFMFactorCal3Bundler_deconstructor_8(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
  typedef boost::shared_ptr<gtsam::GeneralSFMFactor<gtsam::PinholeCamera<gtsam::Cal3Bundler>, gtsam::Point3>> Shared;
  checkArguments("delete_gtsamGeneralSFMFactorCal3Bundler",nargout,nargin,1);
  Shared *self = *reinterpret_cast<Shared**>(mxGetData(in[0]));
  Collector_gtsamGeneralSFMFactorCal3Bundler::iterator item;
  item = collector_gtsamGeneralSFMFactorCal3Bundler.find(self);
  if(item != collector_gtsamGeneralSFMFactorCal3Bundler.end()) {
    delete self;
    collector_gtsamGeneralSFMFactorCal3Bundler.erase(item);
  }
}

void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[])
{
  mstream mout;
  std::streambuf *outbuf = std::cout.rdbuf(&mout);

  _special_cases_RTTIRegister();

  int id = unwrap<int>(in[0]);

  try {
    switch(id) {
    case 0:
      gtsamNonlinearFactorGraph_collectorInsertAndMakeBase_0(nargout, out, nargin-1, in+1);
      break;
    case 1:
      gtsamNonlinearFactorGraph_deconstructor_1(nargout, out, nargin-1, in+1);
      break;
    case 2:
      gtsamNonlinearFactorGraph_addPrior_2(nargout, out, nargin-1, in+1);
      break;
    case 3:
      gtsamSfmTrack_collectorInsertAndMakeBase_3(nargout, out, nargin-1, in+1);
      break;
    case 4:
      gtsamSfmTrack_deconstructor_4(nargout, out, nargin-1, in+1);
      break;
    case 5:
      gtsamPinholeCameraCal3Bundler_collectorInsertAndMakeBase_5(nargout, out, nargin-1, in+1);
      break;
    case 6:
      gtsamPinholeCameraCal3Bundler_deconstructor_6(nargout, out, nargin-1, in+1);
      break;
    case 7:
      gtsamGeneralSFMFactorCal3Bundler_collectorInsertAndMakeBase_7(nargout, out, nargin-1, in+1);
      break;
    case 8:
      gtsamGeneralSFMFactorCal3Bundler_deconstructor_8(nargout, out, nargin-1, in+1);
      break;
    }
  } catch(const std::exception& e) {
    mexErrMsgTxt(("Exception from gtsam:\n" + std::string(e.what()) + "\n").c_str());
  }

  std::cout.rdbuf(outbuf);
}


#ifdef COMPILATION// -*-indent-tabs-mode:t;c-basic-offset:4;tab-width:4;-*-
$CXX $0 -o $0x -lboost_unit_test_framework -lboost_timer \
`pkg-config --libs blas` \
`#-Wl,-rpath,/usr/local/Wolfram/Mathematica/12.0/SystemFiles/Libraries/Linux-x86-64 -L/usr/local/Wolfram/Mathematica/12.0/SystemFiles/Libraries/Linux-x86-64 -lmkl_intel_ilp64 -lmkl_intel_thread -lmkl_core -liomp5` \
&&$0x&&rm $0x;exit
#endif
// © Alfredo A. Correa 2019-2020

#ifndef MULTI_ADAPTORS_BLAS_SYRK_HPP
#define MULTI_ADAPTORS_BLAS_SYRK_HPP

#include "../blas/core.hpp"

#include "../blas/numeric.hpp"
#include "../blas/filling.hpp"

namespace boost{
namespace multi{namespace blas{

using core::syrk;

template<typename AA, typename BB, class A2D, class C2D>
auto syrk(filling c_side, AA alpha, A2D const& a, BB beta, C2D&& c)
->decltype(syrk('\0', '\0', size(c), size(a), alpha, base(a), stride(rotated(a)), beta, base(c), stride(c)), std::forward<C2D>(c)){
	assert( size(c) == size(rotated(c)) );
	if(stride(a)==1)
		if(stride(c)==1) syrk(flip(c_side)==filling::upper?'L':'U', 'N', size(c), size(a         ), alpha, base(a), stride(rotated(a)), beta, base(c), stride(rotated(c)));
		else             syrk(c_side      ==filling::upper?'L':'U', 'N', size(c), size(rotated(a)), alpha, base(a), stride(rotated(a)), beta, base(c), stride(        c ));
	else
		if(stride(c)==1) syrk(flip(c_side)==filling::upper?'L':'U', 'T', size(c), size(rotated(a)), alpha, base(a), stride(a), beta, base(c), stride(rotated(c)));
		else             syrk(c_side      ==filling::upper?'L':'U', 'T', size(c), size(rotated(a)), alpha, base(a), stride(a), beta, base(c), stride(        c ));
	return std::forward<C2D>(c);
}

template<typename AA, class A2D, class C2D>
auto syrk(filling c_side, AA alpha, A2D const& a, C2D&& c)
->decltype(syrk(c_side, alpha, a, 0., std::forward<C2D>(c))){
	return syrk(c_side, alpha, a, 0., std::forward<C2D>(c));}

template<typename AA, class A2D, class C2D>
auto syrk(AA alpha, A2D const& a, C2D&& c)
->decltype(syrk(filling::upper, alpha, a, syrk(filling::lower, alpha, a, std::forward<C2D>(c)))){
	return syrk(filling::upper, alpha, a, syrk(filling::lower, alpha, a, std::forward<C2D>(c)));}

template<typename AA, class A2D, class Ret = typename A2D::decay_type>
NODISCARD("because input argument is const") // this decay in the return type is important
auto syrk(AA alpha, A2D const& a)->std::decay_\
t<decltype(syrk(alpha, a, Ret({size(a), size(a)}, get_allocator(a))))>{
	return syrk(alpha, a, Ret({size(a), size(a)}, get_allocator(a)));}

template<class A2D>
NODISCARD("")
auto syrk(A2D const& A)
->decltype(syrk(1., A)){
	return syrk(1., A);}

}}}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

#if not __INCLUDE_LEVEL__ // _TEST_MULTI_ADAPTORS_BLAS_SYRK

#define BOOST_TEST_MODULE "C++ Unit Tests for Multi cuBLAS syrk"
#define BOOST_TEST_DYN_LINK
#include<boost/test/unit_test.hpp>

#include "../blas/gemm.hpp"

#include "../../array.hpp"
#include "../../utility.hpp"

#include <boost/timer/timer.hpp>

#include<complex>
#include<cassert>
#include<iostream>
#include<numeric>
#include<algorithm>

//#include<catch.hpp>

using std::cout;
using std::cerr;

namespace multi = boost::multi;

template<class M> decltype(auto) print(M const& C){
	using boost::multi::size;
	for(int i = 0; i != size(C); ++i){
		for(int j = 0; j != size(C[i]); ++j)
			std::cout << C[i][j] << ' ';
		std::cout << std::endl;
	}
	return std::cout << std::endl;
}

BOOST_AUTO_TEST_CASE(multi_blas_syrk_real){
	multi::array<double, 2> const a = {
		{ 1., 3., 4.},
		{ 9., 7., 1.}
	};
	{
		multi::array<double, 2> c({3, 3}, 9999.);
		namespace blas = multi::blas;
		using blas::filling;
		using blas::transposed;
		syrk(filling::lower, 1., transposed(a), 0., c); // c⸆=c=a⸆a=(a⸆a)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[2][1] == 19. );
		BOOST_REQUIRE( c[1][2] == 9999. );
	}
	{
		multi::array<double, 2> c({3, 3}, 9999.);
		namespace blas = multi::blas;
		using blas::filling;
		using blas::transposed;
		syrk(filling::upper, 1., transposed(a), 0., c); // c⸆=c=a⸆a=(a⸆a)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[1][2] == 19. );
		BOOST_REQUIRE( c[2][1] == 9999. );
	}
	{
		multi::array<double, 2> c({2, 2}, 9999.);
		namespace blas = multi::blas;
		using blas::filling;
		using blas::syrk;
		syrk(filling::lower, 1., a, 0., c); // c⸆=c=a⸆a=(a⸆a)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[1][0] == 34. );
		BOOST_REQUIRE( c[0][1] == 9999. );
	}
	{
		multi::array<double, 2> c({2, 2}, 9999.);
		namespace blas = multi::blas;
		using blas::filling;
		syrk(filling::upper, 1., a, 0., c); // c⸆=c=a⸆a=(a⸆a)⸆, a⸆a, `c` in lower triangular
		BOOST_REQUIRE( c[0][1] == 34. );
		BOOST_REQUIRE( c[1][0] == 9999. );
	}
	{
		multi::array<double, 2> c({2, 2}, 9999.);
		namespace blas = multi::blas;
		using blas::filling;
		syrk(filling::upper, 1., a, 0., c); // c⸆=c=a⸆a=(a⸆a)⸆, a⸆a, `c` in lower triangular
		BOOST_REQUIRE( c[0][1] == 34. );
		BOOST_REQUIRE( c[1][0] == 9999. );
	}
}

BOOST_AUTO_TEST_CASE(multi_blas_syrk_real_special_case){
	multi::array<double, 2> const a = {
		{ 1., 3., 4.},
	};
	{
		multi::array<double, 2> c({1, 1}, 9999.);
		namespace blas = multi::blas;
		using blas::filling;
		syrk(filling::lower, 1., a, 0., c); // c⸆=c=a⸆a=(a⸆a)⸆, `c` in lower triangular
		//BOOST_REQUIRE( c[1][0] == 34. );
		//BOOST_REQUIRE( c[0][1] == 9999. );
	}
}

BOOST_AUTO_TEST_CASE(multi_blas_syrk_complex_real_case){
	using complex = std::complex<double>;
	multi::array<complex, 2> const a = {
		{ 1., 3., 4.},
		{ 9., 7., 1.}
	};
	{
		multi::array<complex, 2> c({3, 3}, 9999.);
		namespace blas = multi::blas;
		using blas::filling;
		using blas::transposed;
		syrk(filling::lower, 1., transposed(a), 0., c); // c⸆=c=a⸆a=(a⸆a)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[2][1] == 19. );
		BOOST_REQUIRE( c[1][2] == 9999. );
	}
}

BOOST_AUTO_TEST_CASE(multi_blas_syrk_complex){
	using complex = std::complex<double>;
	constexpr auto const I = complex{0., 1.};
	multi::array<complex, 2> const a = {
		{ 1. + 3.*I, 3.- 2.*I, 4.+ 1.*I},
		{ 9. + 1.*I, 7.- 8.*I, 1.- 3.*I}
	};
	{
		multi::array<complex, 2> c({3, 3}, 9999.);
		namespace blas = multi::blas;
		using blas::filling;
		using blas::transposed;
		syrk(filling::lower, 1., transposed(a), 0., c); // c⸆=c=a⸆a=(a⸆a)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[2][1] == complex(-3., -34.) );
		BOOST_REQUIRE( c[1][2] == 9999. );
	}
	{
		multi::array<complex, 2> c({2, 2}, 9999.);
		namespace blas = multi::blas;
		using blas::filling;
		syrk(filling::lower, 1., a, 0., c); // c⸆=c=aa⸆=(aa⸆)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[1][0] == complex(18., -21.) );
		BOOST_REQUIRE( c[0][1] == 9999. );
	}
	{
		multi::array<complex, 2> c({2, 2}, 9999.);
		namespace blas = multi::blas;
		using blas::filling;
		syrk(filling::upper, 1., a, 0., c); // c⸆=c=aa⸆=(aa⸆)⸆, `c` in upper triangular
		BOOST_REQUIRE( c[0][1] == complex(18., -21.) );
		BOOST_REQUIRE( c[1][0] == 9999. );
	}
}

BOOST_AUTO_TEST_CASE(multi_blas_syrk_automatic_operation_complex){
	using complex = std::complex<double>;
	constexpr auto const I = complex{0., 1.};
	multi::array<complex, 2> const a = {
		{ 1. + 3.*I, 3.- 2.*I, 4.+ 1.*I},
		{ 9. + 1.*I, 7.- 8.*I, 1.- 3.*I}
	};
	{
		multi::array<complex, 2> c({2, 2}, 9999.);
		using multi::blas::filling;
		syrk(filling::lower, 1., a, 0., c); // c⸆=c=aa⸆=(aa⸆)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[1][0]==complex(18., -21.) );
		BOOST_REQUIRE( c[0][1]==9999. );
	}
	{
		multi::array<complex, 2> c({3, 3}, 9999.);
		namespace blas = multi::blas;
		using blas::filling;
		using blas::transposed;
		syrk(filling::lower, 1., transposed(a), 0., c); // c⸆=c=a⸆a=(aa⸆)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[2][1]==complex(-3.,-34.) );
		BOOST_REQUIRE( c[1][2]==9999. );
	}
	{
		multi::array<complex, 2> c({3, 3}, 9999.);
		namespace blas = multi::blas;
		using blas::filling;
		using blas::transposed;
		syrk(filling::lower, 1., rotated(a), 0., c); // c⸆=c=a⸆a=(aa⸆)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[2][1]==complex(-3.,-34.) );
		BOOST_REQUIRE( c[1][2]==9999. );
	}
}

BOOST_AUTO_TEST_CASE(multi_blas_syrk_automatic_operation_real){
	multi::array<double, 2> const a = {
		{ 1., 3., 4.},
		{ 9., 7., 1.}
	};
	{
		multi::array<double, 2> c({2, 2}, 9999.);
		using multi::blas::filling;
		syrk(filling::lower, 1., a, 0., c); // c⸆=c=aa⸆=(aa⸆)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[1][0] == 34. );
		BOOST_REQUIRE( c[0][1] == 9999. );
	}
	{
		multi::array<double, 2> c({2, 2}, 9999.);
		using multi::blas::filling;
		syrk(filling::upper, 1., a, 0., c); // c⸆=c=aa⸆=(aa⸆)⸆, `c` in upper triangular
		BOOST_REQUIRE( c[0][1] == 34. );
		BOOST_REQUIRE( c[1][0] == 9999. );
	}
	{
		multi::array<double, 2> c({3, 3}, 9999.);
		using multi::blas::filling;
		syrk(filling::lower, 1., rotated(a), 0., c); // c⸆=c=a⸆a=(a⸆a)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[2][1] == 19. );
		BOOST_REQUIRE( c[1][2] == 9999. );
	}
	{
		multi::array<double, 2> c({3, 3}, 9999.);
		namespace blas = multi::blas;
		using blas::transposed;
		using blas::filling;
		syrk(filling::lower, 1., transposed(a), 0., c); // c⸆=c=a⸆a=(a⸆a)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[2][1] == 19. );
		BOOST_REQUIRE( c[1][2] == 9999. );
	}
	{
		multi::array<double, 2> c({3, 3}, 9999.);
		namespace blas = multi::blas;
		using blas::transposed;
		using blas::filling;
		syrk(filling::upper, 1., transposed(a), 0., c); // c⸆=c=a⸆a=(a⸆a)⸆, `c` in upper triangular
		BOOST_REQUIRE( c[1][2] == 19. );
		BOOST_REQUIRE( c[2][1] == 9999. );
	}
	{
		multi::array<double, 2> c({2, 2}, 9999.);
		using multi::blas::filling;
		using multi::blas::transposed;
		syrk(filling::upper, 1., a, 0., transposed(c)); // c⸆=c=aa⸆=(aa⸆)⸆, `c` in upper triangular
		BOOST_REQUIRE( c[0][1] == 9999. );
		BOOST_REQUIRE( c[1][0] == 34. );
	}
}

BOOST_AUTO_TEST_CASE(multi_blas_syrk_automatic_implicit_zero){
	multi::array<double, 2> const a = {
		{ 1., 3., 4.},
		{ 9., 7., 1.}
	};
	{
		multi::array<double, 2> c({2, 2}, 9999.);
		using multi::blas::filling;
		syrk(filling::lower, 1., a, c); // c⸆=c=aa⸆=(aa⸆)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[1][0] == 34. );
		BOOST_REQUIRE( c[0][1] == 9999. );
	}
}

BOOST_AUTO_TEST_CASE(multi_blas_syrk_automatic_symmetrization){
	multi::array<double, 2> const a = {
		{ 1., 3., 4.},
		{ 9., 7., 1.}
	};
	{
		multi::array<double, 2> c({2, 2}, 9999.);
		using multi::blas::syrk;
		using multi::blas::gemm;
		using multi::blas::T;
		syrk(1., a, c); // c⸆=c=aa⸆=(aa⸆)⸆
		BOOST_REQUIRE( c[1][0] == 34. );
		BOOST_REQUIRE( c[0][1] == 34. );
		BOOST_REQUIRE( syrk(a) == gemm(a, T(a)) );
	}
	{
		using multi::blas::syrk;
		multi::array<double, 2> c = syrk(1., a); // c⸆=c=aa⸆=(aa⸆)⸆
		BOOST_REQUIRE( c[1][0] == 34. );
		BOOST_REQUIRE( c[0][1] == 34. );
	}
	{
		using multi::blas::syrk;
		multi::array<double, 2> c = syrk(a); // c⸆=c=aa⸆=(aa⸆)⸆
		BOOST_REQUIRE( c[1][0] == 34. );
		BOOST_REQUIRE( c[0][1] == 34. );
	}
	{
		using multi::blas::transposed;
		using multi::blas::syrk;
		multi::array<double, 2> c = syrk(transposed(a)); // c⸆=c=a⸆a=(a⸆a)⸆
		BOOST_REQUIRE( c[2][1] == 19. );
		BOOST_REQUIRE( c[1][2] == 19. );
	}
}

#if 0

}

}

#if 0
	{

		{
			multi::array<complex, 2> C({2, 2}, 9999.);
			syrk(1., rotated(A), rotated(C)); // C^T=C=A*A^T=(A*A^T)^T
			assert( C[1][0] == complex(18., -21.) );
		}
		{
			multi::array<complex, 2> C({2, 2}, 9999.);
			syrk(rotated(A), rotated(C)); // C^T=C=A*A^T=(A*A^T)^T
			assert( C[1][0] == complex(18., -21.) );
		}
		{
			complex C[2][2];
			using multi::rotated;
			syrk(rotated(A), rotated(C)); // C^T=C=A*A^T=(A*A^T)^T
			assert( C[1][0] == complex(18., -21.) );
		}
		{
			auto C = syrk(1., A); // C = C^T = A^T*A, C is a value type matrix (with C-ordering, information is everywhere)
			assert( C[1][2]==complex(-3.,-34.) );
		}
		{
//			what(rotated(syrk(A)));
			multi::array C = rotated(syrk(A)); // C = C^T = A^T*A, C is a value type matrix (with C-ordering, information is in upper triangular part)
			print(C) <<"---\n";
		}

	}
#if 0
	{
		multi::array<complex, 2> const A = {
			{ 1. + 3.*I, 3.- 2.*I, 4.+ 1.*I},
			{ 9. + 1.*I, 7.- 8.*I, 1.- 3.*I}
		};
		auto C = rotated(syrk(A)).decay(); // C = C^T = A^T*A, C is a value type matrix (with C-ordering, information is in upper triangular part)
		print(C) <<"---\n";
//		print(C) <<"---\n";
	}
	return 0;
	{
		multi::array<complex, 2> const A = {
			{ 1. + 3.*I, 3.- 2.*I, 4.+ 1.*I},
			{ 9. + 1.*I, 7.- 8.*I, 1.- 3.*I}
		};
		auto C = syrk(rotated(A)); // C = C^T = A^T*A, C is a value type matrix (with C-ordering)
		print(C) <<"---\n";
	}
#endif
#endif
}

BOOST_AUTO_TEST_CASE(multi_blas_syrk_herk_fallback){
	multi::array<double, 2> const a = {
		{ 1., 3., 4.},
		{ 9., 7., 1.}
	};
	{
		multi::array<double, 2> c({2, 2}, 9999.);
		namespace blas = multi::blas;
		using blas::filling;
		syrk(filling::lower, 1., a, 0., c); // c⸆=c=a⸆a=(a⸆a)⸆, `c` in lower triangular
		BOOST_REQUIRE( c[1][0] == 34. );
		BOOST_REQUIRE( c[0][1] == 9999. );
	}
}
#endif

#endif
#endif


#pragma once
#include <string>
#include <Eigen/Dense>
#include <autoppl/util/traits/dist_expr_traits.hpp>

namespace ppl {

/**
 * MCMC Output object when a sampling algorithm returns.
 * Stores useful information from MCMC sampling routine such as:
 * - continuous and discrete samples
 * - warmup and sampling time
 * - name of mcmc algorithm invoked
 */
template <int Major = Eigen::ColMajor>
struct MCMCResult
{
    using cont_samples_t = Eigen::Matrix<util::cont_param_t, Eigen::Dynamic, Eigen::Dynamic, Major>;
    using disc_samples_t = Eigen::Matrix<util::disc_param_t, Eigen::Dynamic, Eigen::Dynamic>;

    cont_samples_t cont_samples;
    disc_samples_t disc_samples;
    std::string name;
    double warmup_time = 0;
    double sampling_time = 0;

    MCMCResult() =default;
    MCMCResult(size_t n_samples,
               size_t n_cont_params,
               size_t n_disc_params)
        : cont_samples(n_samples, n_cont_params)
        , disc_samples(n_samples, n_disc_params)
    {}
};

} // namespace ppl


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

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

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

#ifndef BOOST_GEOMETRY_EXTENSIONS_ALGORITHMS_REMOVE_MARKED_HPP
#define BOOST_GEOMETRY_EXTENSIONS_ALGORITHMS_REMOVE_MARKED_HPP

#include <boost/range.hpp>

#include <boost/geometry/multi/core/tags.hpp>

#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/interior_rings.hpp>
#include <boost/geometry/geometries/concepts/check.hpp>
#include <boost/geometry/util/math.hpp>

#include <boost/geometry/algorithms/area.hpp>
#include <boost/geometry/algorithms/distance.hpp>
#include <boost/geometry/algorithms/perimeter.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace remove_marked
{

template <typename Range, typename MarkMap>
struct range_remove_marked
{
    typedef typename strategy::side::services::default_strategy
    <
        typename cs_tag<Range>::type
    >::type side_strategy_type;

    typedef typename coordinate_type<Range>::type coordinate_type;

    static inline void apply(Range const& range_in, ring_identifier id,
                    Range& range_out, MarkMap const& mark_map)
    {
        typename MarkMap::const_iterator mit = mark_map.find(id);
        if (mit == mark_map.end())
        {
            range_out = range_in;
            return;
        }
        typedef typename MarkMap::mapped_type bit_vector_type;

        if (boost::size(range_in) != boost::size(mit->second))
        {
            throw std::runtime_error("ERROR in size of mark_map");
            return;
        }

        range_out.clear();

        typename boost::range_iterator<bit_vector_type const>::type bit = boost::begin(mit->second);
        for (typename boost::range_iterator<Range const>::type it = boost::begin(range_in);
            it != boost::end(range_in); ++it, ++bit)
        {
            bool const& marked = *bit;
            if (! marked)
            {
                range_out.push_back(*it);
            }
        }
    }
};

template <typename Polygon, typename MarkMap>
struct polygon_remove_marked
{
    static inline void apply(Polygon const& polygon_in, ring_identifier id,
                Polygon& polygon_out, MarkMap const& mark_map)
    {
        typedef typename geometry::ring_type<Polygon>::type ring_type;

        typedef range_remove_marked<ring_type, MarkMap> per_range;
        id.ring_index = -1;
        per_range::apply(exterior_ring(polygon_in), id, exterior_ring(polygon_out), mark_map);

        typename interior_return_type<Polygon const>::type rings_in
                    = interior_rings(polygon_in);
        typename interior_return_type<Polygon>::type rings_out
                    = interior_rings(polygon_out);

        rings_out.resize(boost::size(interior_rings(polygon_in)));
        BOOST_AUTO_TPL(out, boost::begin(rings_out));

        for (BOOST_AUTO_TPL(it, boost::begin(rings_in));
            it != boost::end(rings_in);
            ++it, ++out)
        {
            id.ring_index++;
            per_range::apply(*it, id, *out, mark_map);
        }
    }
};

template <typename MultiGeometry, typename MarkMap, typename SinglePolicy>
struct multi_remove_marked
{
    static inline void apply(MultiGeometry const& multi_in, ring_identifier id,
                MultiGeometry& multi_out, MarkMap const& mark_map)
    {
        id.multi_index = 0;

        multi_out.resize(boost::size(multi_in));

        typename boost::range_iterator<MultiGeometry>::type out = boost::begin(multi_out);
        for (typename boost::range_iterator<MultiGeometry const>::type
                it = boost::begin(multi_in);
            it != boost::end(multi_in);
            ++it, ++out)
        {
            SinglePolicy::apply(*it, id, *out, mark_map);
            id.multi_index++;
        }
    }
};

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

#ifndef DOXYGEN_NO_DISPATCH
namespace dispatch
{

template
<
    typename Tag,
    typename Geometry,
    typename MarkMap
>
struct remove_marked
{
    static inline void apply(Geometry const&, ring_identifier, Geometry&, MarkMap const&)
    {}
};

template <typename Ring, typename MarkMap>
struct remove_marked<ring_tag, Ring, MarkMap>
    : detail::remove_marked::range_remove_marked<Ring, MarkMap>
{};

template <typename Polygon, typename MarkMap>
struct remove_marked<polygon_tag, Polygon, MarkMap>
    : detail::remove_marked::polygon_remove_marked<Polygon, MarkMap>
{};

template <typename MultiPolygon, typename MarkMap>
struct remove_marked<multi_polygon_tag, MultiPolygon, MarkMap>
    : detail::remove_marked::multi_remove_marked
        <
            MultiPolygon,
            MarkMap,
            detail::remove_marked::polygon_remove_marked
            <
                typename boost::range_value<MultiPolygon>::type,
                MarkMap
            >
        >
{};

} // namespace dispatch
#endif

/*!
    \ingroup remove_marked
    \tparam Geometry geometry type
    \param geometry the geometry to make remove_marked
*/
template <typename Geometry, typename MarkMap>
inline void remove_marked(Geometry const& geometry_in, Geometry& geometry_out,
            MarkMap const& mark_map)
{
    concept::check<Geometry>();

    ring_identifier id;
    dispatch::remove_marked
        <
            typename tag<Geometry>::type,
            Geometry,
            MarkMap
        >::apply(geometry_in, id, geometry_out, mark_map);
}

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_EXTENSIONS_ALGORITHMS_REMOVE_MARKED_HPP


/** \file kernel_combined.hpp
    \brief Kernel functions that combine other kernels */
/*
-------------------------------------------------------------------------
   This file is part of BayesOpt, an efficient C++ library for
   Bayesian optimization.

   Copyright (C) 2011-2015 Ruben Martinez-Cantin <rmcantin@unizar.es>

   BayesOpt 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.

   BayesOpt 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 BayesOpt.  If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------------
*/

#ifndef  _KERNEL_COMBINED_HPP_
#define  _KERNEL_COMBINED_HPP_

#include <boost/numeric/ublas/vector_proxy.hpp>
#include "kernel_functors.hpp"

namespace bayesopt
{

  /**\addtogroup KernelFunctions */
  //@{

  /** \brief Abstract class for combined kernel.
   *  It allows combinations of other kernels (addition, product, etc.)
   */
  class CombinedKernel : public Kernel
  {
  public:
    virtual void init(size_t input_dim, Kernel* left, Kernel* right)
    {
      n_inputs = input_dim;
      this->left = left;
      this->right = right;
    };

    void setHyperParameters(const vectord &theta)
    {
      using boost::numeric::ublas::subrange;

      size_t n_lhs = left->nHyperParameters();
      size_t n_rhs = right->nHyperParameters();
      if (theta.size() != n_lhs + n_rhs)
	{
	  FILE_LOG(logERROR) << "Wrong number of kernel hyperparameters";
	  throw std::invalid_argument("Wrong number of kernel hyperparameters");
	}
      left->setHyperParameters(subrange(theta,0,n_lhs));
      right->setHyperParameters(subrange(theta,n_lhs,n_lhs+n_rhs));
    };

    vectord getHyperParameters()
    {
      using boost::numeric::ublas::subrange;

      size_t n_lhs = left->nHyperParameters();
      size_t n_rhs = right->nHyperParameters();
      vectord par(n_lhs + n_rhs);
      subrange(par,0,n_lhs) = left->getHyperParameters();
      subrange(par,n_lhs,n_lhs+n_rhs) = right->getHyperParameters();
      return par;
    };

    size_t nHyperParameters()
    {
      size_t n_lhs = left->nHyperParameters();
      size_t n_rhs = right->nHyperParameters();
      return n_lhs + n_rhs;
    };

    virtual ~CombinedKernel()
    {
      delete left;
      delete right;
    };

  protected:
    Kernel* left;
    Kernel* right;
  };

  //@}

} //namespace bayesopt

#endif


// Copyright (c) 2018 Graphcore Ltd. All rights reserved.

// Legacy code to test some BroadcastVectorInner vertices.
// This code is no longer used.

#define BOOST_TEST_MODULE ChannelMul2d

#include "../lib/popops/ExprOpUtil.hpp"
#include <boost/multi_array.hpp>
#include <functional>
#include <iostream>
#include <limits>
#include <poplar/Engine.hpp>
#include <poplibs_support/TestDevice.hpp>
#include <poplibs_test/Util.hpp>
#include <popops/codelets.hpp>
#include <poputil/TileMapping.hpp>
#include <poputil/VertexTemplates.hpp>

// Tolerances used in tests
#define FLOAT_REL_TOL 0.01
#define HALF_REL_TOL 0.1
#define FLOAT_ABS_TOL 1e-6
#define HALF_ABS_TOL 1e-5

using namespace poplar;
using namespace poplar::program;
using namespace poputil;
using namespace poplibs_test::util;
using namespace poplibs_support;

const OptionFlags options;

struct TestCase {
  poplar::Type type;
  std::vector<std::size_t> scaleLen;
  std::vector<std::size_t> actsLen;
};

struct TestCaseData {
  // These are all the scales/acts concatenated together.
  std::vector<double> allScales;
  std::vector<double> allActsIn;

  std::unique_ptr<char[]> rawAllScales;
  std::unique_ptr<char[]> rawAllActsIn;
  std::unique_ptr<char[]> rawAllActsOut;
};

static bool channelMul2DTests(const std::vector<TestCase> &cases) {

  const std::size_t overwriteLen = 32;

  auto device = createTestDevice(TEST_TARGET, 1, 1);
  const auto &target = device.getTarget();
  Graph graph(target);
  popops::addCodelets(graph);

  // One compute set, with a vertex for each test case.
  auto cs = graph.addComputeSet("cs");

  Sequence uploadProg;
  Sequence downloadProg;
  std::vector<std::pair<std::string, char *>> tmap;
  std::vector<TestCaseData> tcData(cases.size());

  for (std::size_t i = 0; i < cases.size(); ++i) {
    const auto &tc = cases[i];

    // Check the actsLen's are ok.
    if (tc.actsLen.size() != tc.scaleLen.size())
      return false;

    for (unsigned a = 0; a < tc.actsLen.size(); ++a) {
      if (tc.actsLen[a] % tc.scaleLen[a] != 0) {
        return false;
      }
    }

    std::size_t totalScaleLen =
        std::accumulate(tc.scaleLen.begin(), tc.scaleLen.end(), 0);
    std::size_t totalActsLen =
        std::accumulate(tc.actsLen.begin(), tc.actsLen.end(), 0);

    std::cout << "Test case [" << i << "]: "
              << " scaleLen.size(): " << tc.scaleLen.size()
              << " actsLen.size(): " << tc.actsLen.size()
              << " totalScaleLen: " << totalScaleLen
              << " totalActsLen: " << totalActsLen
              << " type: " << tc.type.toString() << "\n";

    std::string suffix = "_" + std::to_string(i);

    auto allScales =
        graph.addVariable(tc.type, {totalScaleLen}, "allScales" + suffix);
    auto allActsIn =
        graph.addVariable(tc.type, {totalActsLen}, "allActsIn" + suffix);
    auto allActsOut = graph.addVariable(tc.type, {totalActsLen + overwriteLen},
                                        "allActsOut" + suffix);
    graph.setTileMapping(allScales, 0);
    graph.setTileMapping(allActsIn, 0);
    graph.setTileMapping(allActsOut, 0);

    std::string templateVertexName =
        templateVertex("popops::BroadcastVectorInner2D",
                       popops::expr::BinaryOpType::MULTIPLY, tc.type);

    auto v = graph.addVertex(cs, templateVertexName);

    // Connect the acts and scale subvectors.
    graph.setFieldSize(v["data"], tc.actsLen.size());
    graph.setFieldSize(v["out"], tc.actsLen.size());
    graph.setFieldSize(v["B"], tc.actsLen.size());

    std::size_t actsPos = 0;
    std::size_t scalePos = 0;
    std::vector<unsigned> workList;
    workList.push_back(tc.actsLen.size() - 1);
    for (unsigned a = 0; a < tc.actsLen.size(); ++a) {
      graph.connect(v["data"][a],
                    allActsIn.slice(actsPos, actsPos + tc.actsLen[a]));
      graph.connect(v["out"][a],
                    allActsOut.slice(actsPos, actsPos + tc.actsLen[a]));
      graph.connect(v["B"][a],
                    allScales.slice(scalePos, scalePos + tc.scaleLen[a]));

      actsPos += tc.actsLen[a];
      scalePos += tc.scaleLen[a];

      auto actsBlockCount = tc.actsLen[a] / tc.scaleLen[a];
      uint16_t actsBlockCount16 = actsBlockCount;

      if (actsBlockCount16 != actsBlockCount)
        return false;
      workList.push_back(tc.scaleLen[a]);
      workList.push_back(actsBlockCount16);
    }
    graph.setTileMapping(v, 0);
    auto workListTensor =
        graph.addConstant(UNSIGNED_SHORT, {workList.size()}, workList.data());
    graph.setTileMapping(workListTensor, 0);
    graph.connect(v["workList"], workListTensor);

    tcData[i].rawAllScales = allocateHostMemoryForTensor(
        allScales, "allScale" + suffix, graph, uploadProg, downloadProg, tmap);
    tcData[i].rawAllActsIn = allocateHostMemoryForTensor(
        allActsIn, "allActsIn" + suffix, graph, uploadProg, downloadProg, tmap);
    tcData[i].rawAllActsOut =
        allocateHostMemoryForTensor(allActsOut, "allActsOut" + suffix, graph,
                                    uploadProg, downloadProg, tmap);

    tcData[i].allScales.resize(totalScaleLen);
    tcData[i].allActsIn.resize(totalActsLen);

    std::mt19937 randomEngine;
    writeRandomValues(target, allScales.elementType(),
                      tcData[i].allScales.data(),
                      tcData[i].allScales.data() + tcData[i].allScales.size(),
                      -2., 2., randomEngine);
    writeRandomValues(target, allActsIn.elementType(),
                      tcData[i].allActsIn.data(),
                      tcData[i].allActsIn.data() + tcData[i].allActsIn.size(),
                      -2., 2., randomEngine);

    copy(target, tcData[i].allScales.data(), tcData[i].allScales.size(),
         allScales.elementType(), tcData[i].rawAllScales.get());
    copy(target, tcData[i].allActsIn.data(), tcData[i].allActsIn.size(),
         allActsIn.elementType(), tcData[i].rawAllActsIn.get());
    // Set actsOut to 1, to verify there are no overwrites.
    std::vector<double> ones(allActsOut.numElements(), 1.0);
    copy(target, ones.data(), ones.size(), allActsOut.elementType(),
         tcData[i].rawAllActsOut.get());
  }

  std::cout << "Executing engine\n";

  auto prog = Execute(cs);
  Engine engine(graph, Sequence(uploadProg, prog, downloadProg), options);

  attachStreams(engine, tmap);

  device.bind([&](const Device &d) {
    engine.load(d);
    engine.run(0);
  });

  std::cout << "Checking results\n";

  // Check the results for each test case.

  for (std::size_t i = 0; i < tcData.size(); ++i) {
    const auto &tc = cases[i];

    std::cout << "Checking case [" << i << "]\n";

    // Convert back to double.
    std::vector<double> allActsOut(tcData[i].allActsIn.size() + overwriteLen,
                                   0.0);

    copy(target, tc.type, tcData[i].rawAllActsOut.get(), allActsOut.data(),
         allActsOut.size());

    // Calculate the correct answer.
    std::vector<double> allActsOutRef(allActsOut.size(), 1.0);

    std::size_t actsPos = 0;
    std::size_t scalePos = 0;
    for (unsigned a = 0; a < tc.actsLen.size(); ++a) {
      for (std::size_t k = 0; k < tc.actsLen[a]; ++k) {
        allActsOutRef[actsPos + k] =
            tcData[i].allActsIn[actsPos + k] *
            tcData[i].allScales[scalePos + (k % tc.scaleLen[a])];
      }
      actsPos += tc.actsLen[a];
      scalePos += tc.scaleLen[a];
    }

    const double absoluteTolerance =
        tc.type == FLOAT ? FLOAT_ABS_TOL : HALF_ABS_TOL;
    const double relativeTolerance =
        tc.type == FLOAT ? FLOAT_REL_TOL : HALF_REL_TOL;

    auto matchesModel = checkIsClose(
        "out", allActsOut.data(), {allActsOut.size()}, allActsOutRef.data(),
        allActsOut.size(), relativeTolerance, absoluteTolerance);
    if (!matchesModel)
      return false;
  }

  return true;
}

const bool isNotSim = !isSimulator(TEST_TARGET);

BOOST_AUTO_TEST_CASE(ChannelMul2DTiny) {
  std::vector<TestCase> cases = {
      {HALF, {1, 4, 8, 5}, {15, 12, 32, 15}},
      {FLOAT, {1, 4, 8, 5}, {15, 12, 32, 15}},
  };
  BOOST_TEST(channelMul2DTests(cases));
}

BOOST_AUTO_TEST_CASE(ChannelMul2DSmall,
                     *boost::unit_test::precondition(enableIfNotSim())) {
  std::vector<TestCase> cases = {
      {HALF, {1, 4, 8, 12, 16}, {480, 480, 480, 480, 480}},
      {HALF, {1, 4, 8, 5, 8}, {15, 12, 40, 15, 168}},
      {FLOAT, {1, 4, 8, 12, 16}, {480, 480, 480, 480, 480}},
      {FLOAT, {1, 4, 8, 5, 8}, {15, 12, 40, 15, 168}},
  };
  BOOST_TEST(channelMul2DTests(cases));
}

std::size_t maxBlockCount() {
  // This is the maximum acts_block_count the vertex supports.
  return 4095;
}

BOOST_AUTO_TEST_CASE(ChannelMul2DLarge1_half,
                     *boost::unit_test::precondition(enableIfNotSim())) {
  std::vector<TestCase> cases = {
      {HALF, {1, 1}, {maxBlockCount(), 80}},
  };
  BOOST_TEST(channelMul2DTests(cases));
}

BOOST_AUTO_TEST_CASE(ChannelMul2DLarge8_half,
                     *boost::unit_test::precondition(enableIfNotSim())) {
  std::vector<TestCase> cases = {
      {HALF, {8, 8}, {8000, 80}},
  };
  BOOST_TEST(channelMul2DTests(cases));
}

BOOST_AUTO_TEST_CASE(ChannelMul2DLarge1_float,
                     *boost::unit_test::precondition(enableIfNotSim())) {
  std::vector<TestCase> cases = {
      {FLOAT, {1, 1}, {maxBlockCount(), 80}},
  };
  BOOST_TEST(channelMul2DTests(cases));
}

BOOST_AUTO_TEST_CASE(ChannelMul2DLarge8_float,
                     *boost::unit_test::precondition(enableIfNotSim())) {
  std::vector<TestCase> cases = {
      {FLOAT, {8, 8}, {8000, 80}},
  };
  BOOST_TEST(channelMul2DTests(cases));
}


#include <functional>
#include <thread>
#include <random>
#include <Eigen/Core>
#include "sdf/internal/nanoflann.hpp"

namespace sdf {

// Min number of items to allow multithreading
const int MULTITHREAD_MIN_ITEMS = 50;

// Parallel for
void maybe_parallel_for(std::function<void(int&)> loop_content,
                        int loop_max = MULTITHREAD_MIN_ITEMS,
                        int num_threads = std::thread::hardware_concurrency());

// Get a seeded mersenne twister 19937
std::mt19937& get_rng();

}  // namespace sdf

namespace nanoflann {
namespace {
// Static KD-tree adaptor using Eigen::Ref, so we can pass in
// e.g. blocks and Maps without copying
template <class MatrixType, int DIM,
          class Distance = nanoflann::metric_L2_Simple,
          typename IndexType = int>
struct KDTreeEigenRefAdaptor {
    typedef KDTreeEigenRefAdaptor<MatrixType, DIM, Distance> self_t;
    typedef typename MatrixType::Scalar num_t;
    typedef
        typename Distance::template traits<num_t, self_t>::distance_t metric_t;
    typedef KDTreeSingleIndexAdaptor<metric_t, self_t, DIM, IndexType> index_t;
    index_t* index = nullptr;
    explicit KDTreeEigenRefAdaptor(Eigen::Ref<const MatrixType> mat,
                                   bool build = true,
                                   const int leaf_max_size = 10)
        : m_data_matrix(mat), leaf_max_size(leaf_max_size) {
        if (build) rebuild();
    }
    ~KDTreeEigenRefAdaptor() {
        if (index) delete index;
    }

    // Rebuild the KD tree from scratch. Call if data updated.
    void rebuild() {
        if (index) delete index;
        index = new index_t(
            DIM, *this,
            nanoflann::KDTreeSingleIndexAdaptorParams(leaf_max_size));
        index->buildIndex();
    }

    Eigen::Ref<const MatrixType> m_data_matrix;
    const int leaf_max_size;
    /// Query for the num_closest closest points to a given point (entered as
    /// query_point[0:dim-1]).
    inline void query(const num_t* query_point, const size_t num_closest,
                      IndexType* out_indices, num_t* out_distances_sq) const {
        nanoflann::KNNResultSet<typename MatrixType::Scalar, IndexType>
            resultSet(num_closest);
        resultSet.init(out_indices, out_distances_sq);
        index->findNeighbors(resultSet, query_point, nanoflann::SearchParams());
    }
    /// Query for the closest points to a given point (entered as
    /// query_point[0:dim-1]).
    inline IndexType closest(const num_t* query_point) const {
        IndexType out_indices;
        num_t out_distances_sq;
        query(query_point, 1, &out_indices, &out_distances_sq);
        return out_indices;
    }
    const self_t& derived() const { return *this; }
    self_t& derived() { return *this; }
    inline size_t kdtree_get_point_count() const {
        return m_data_matrix.rows();
    }
    /// Returns the distance between the vector "p1[0:size-1]" and the data
    /// point with index "idx_p2" stored in the class:
    inline num_t kdtree_distance(const num_t* p1, const size_t idx_p2,
                                 size_t size) const {
        num_t s = 0;
        for (size_t i = 0; i < size; i++) {
            const num_t d = p1[i] - m_data_matrix.coeff(idx_p2, i);
            s += d * d;
        }
        return s;
    }
    /// Returns the dim'th component of the idx'th point in the class:
    inline num_t kdtree_get_pt(const size_t idx, int dim) const {
        return m_data_matrix.coeff(idx, dim);
    }
    /// Optional bounding-box computation: return false to default to a standard
    /// bbox computation loop.
    template <class BBOX>
    bool kdtree_get_bbox(BBOX&) const {
        return false;
    }
};
}  // namespace
}  // namespace nanoflann


#ifndef DXF_TYPES_HPP
#define DXF_TYPES_HPP

/// SYSTEM
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/segment.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/strategies/transform/matrix_transformers.hpp>
#include <boost/geometry/strategies/transform/inverse_transformer.hpp>

#include <cmath>

#include <vector>
#include <algorithm>

namespace cslibs_boost_geometry {
namespace types {

class Mask {
public:
    Mask(const unsigned int _size) :
        mask(new bool[_size]),
        size(_size)
    {
        std::fill(mask, mask + _size, true);
    }

    bool& operator[](const unsigned int i)
    {
        return mask[i];
    }

    void resize(const unsigned int _size)
    {
        if(mask != nullptr) {
            delete [] mask;
            mask = new bool[_size];
        }

        size = _size;
        std::fill(mask, mask + _size, true);
    }

    virtual ~Mask()
    {
        if(mask != nullptr)
            delete [] mask;
    }

private:
    bool        *mask;
    unsigned int size;
};

/**
 * @brief Type definition for point sets.
 */
template<typename PointT>
struct PointSet {
    typedef std::vector<PointT>
    type;
};
/**
 * @brief Type definition of a polygon.
 */
template<typename PointT>
struct Polygon {
    typedef boost::geometry::model::polygon<PointT>
    type;
};

/**
 * @brief Type definition for a line.
 */
template<typename PointT>
struct Line {
    typedef boost::geometry::model::segment<PointT>
    type;
};
/**
 * @brief Type definition for a line set.
 */
template<typename PointT>
struct LineSet {
    typedef std::vector<boost::geometry::model::segment<PointT> >
    type;

    static const boost::geometry::model::segment<PointT>& getSegment(const typename type::const_iterator& it) {
        return *it;
    }

    static boost::geometry::model::segment<PointT>& getSegment(const typename type::iterator& it) {
        return *it;
    }
};
/**
 * @brief Type definition for an indexed line set.
 */
template<typename PointT>
struct IndexedLineSet {
    typedef std::vector<const boost::geometry::model::segment<PointT>* >
    type;

    static const boost::geometry::model::segment<PointT>& getSegment(const typename type::const_iterator& it) {
        return **it;
    }
};

/**
 * @brief Type definition for a box.
 */
template<typename PointT>
struct Box {
    typedef boost::geometry::model::box<PointT>
    type;
};

/**
 * @brief Type definition for point sets that
 *        can be declared invalid.
 */
template<typename PointT>
struct IntersectionResult {
    typedef IntersectionResult<PointT>
    type;

    IntersectionResult() :
        valid(false)
    {
    }

    typename PointSet<PointT>::type result;
    bool                            valid;
};
/**
 * @brief Type definition for a set of
 *        invalidatable point sets.
 */
template<typename PointT>
struct IntersectionResultSet {
    typedef std::vector<IntersectionResult<PointT> >
    type;
};

/**
 * @brief Type defintion for a translation.
 */
template<typename PointT>
struct Translation {
#if BOOST_VERSION >= 105500
    typedef boost::geometry::strategy::transform::translate_transformer<double, 2, 2>
#else
    typedef boost::geometry::strategy::transform::translate_transformer<PointT, PointT>
#endif
    type;
};

template<typename PointT>
struct Rotation {
#if BOOST_VERSION >= 105500
    typedef boost::geometry::strategy::transform::rotate_transformer<boost::geometry::radian, double, 2, 2>
#else
    typedef boost::geometry::strategy::transform::rotate_transformer<PointT, PointT,
                                                                     boost::geometry::radian>
#endif
    type;
};

/**
 * @brief Invert a translation.
 * @param translation   the translation to be inverted
 * @return              the inverted translation
 */
template<typename PointT, typename TransT>
TransT invert(const TransT &translation)
{
#if BOOST_VERSION >= 105500
    return boost::geometry::strategy::transform::inverse_transformer<double, 2, 2>::inverse_transformer(translation);
#else
    return boost::geometry::strategy::transform::inverse_transformer<PointT, PointT>::inverse_transformer(translation);
#endif
}

struct periodic {
    inline static double sin(const double rad)
    {
        return std::sin(rad);
    }

    inline static double cos(const double rad)
    {
        return std::cos(rad);
    }

    inline static void sin_cos(const double x,
                               double &sinx,
                               double &cosx)
    {
        sinx = std::sin(x);
        cosx = std::cos(x);
    }
};

struct periodicApprox {
    inline static double angleClamp(const double rad)
    {
        double rad_(rad);
        while (rad_ < -M_PI)
            rad_ += 2*M_PI;

        while (rad_ >= M_PI)
            rad_ -= 2*M_PI;

        return rad_;
    }

    inline static double sin(const double rad)
    {
        double angle = angleClamp(rad);
        return fastersinfull(angle);

    }

    inline static double cos(const double rad)
    {
        double angle = angleClamp(rad);
        return fastercosfull(angle);
    }

    inline static void sin_cos(const double rad,
                               double &sinx,
                               double &cosx)
    {
        double angle = angleClamp(rad);
        sinx = fastersinfull(angle);
        cosx = fastercosfull(angle);
    }

    ///__________________ FAST MATH _________________///

    static inline float
    fastsin (float x)
    {
      static const float fouroverpi = 1.2732395447351627f;
      static const float fouroverpisq = 0.40528473456935109f;
      static const float q = 0.78444488374548933f;
      union { float f; uint32_t i; } p = { 0.20363937680730309f };
      union { float f; uint32_t i; } r = { 0.015124940802184233f };
      union { float f; uint32_t i; } s = { -0.0032225901625579573f };

      union { float f; uint32_t i; } vx = { x };
      uint32_t sign = vx.i & 0x80000000;
      vx.i = vx.i & 0x7FFFFFFF;

      float qpprox = fouroverpi * x - fouroverpisq * x * vx.f;
      float qpproxsq = qpprox * qpprox;

      p.i |= sign;
      r.i |= sign;
      s.i ^= sign;

      return q * qpprox + qpproxsq * (p.f + qpproxsq * (r.f + qpproxsq * s.f));
    }

    static inline float
    fastersin (float x)
    {
      static const float fouroverpi = 1.2732395447351627f;
      static const float fouroverpisq = 0.40528473456935109f;
      static const float q = 0.77633023248007499f;
      union { float f; uint32_t i; } p = { 0.22308510060189463f };

      union { float f; uint32_t i; } vx = { x };
      uint32_t sign = vx.i & 0x80000000;
      vx.i &= 0x7FFFFFFF;

      float qpprox = fouroverpi * x - fouroverpisq * x * vx.f;

      p.i |= sign;

      return qpprox * (q + p.f * qpprox);
    }

    static inline float
    fastsinfull (float x)
    {
      static const float twopi = 6.2831853071795865f;
      static const float invtwopi = 0.15915494309189534f;

      int k = x * invtwopi;
      float half = (x < 0) ? -0.5f : 0.5f;
      return fastsin ((half + k) * twopi - x);
    }

    static inline float
    fastersinfull (float x)
    {
      static const float twopi = 6.2831853071795865f;
      static const float invtwopi = 0.15915494309189534f;

      int k = x * invtwopi;
      float half = (x < 0) ? -0.5f : 0.5f;
      return fastersin ((half + k) * twopi - x);
    }

    static inline float
    fastcosfull (float x)
    {
      static const float halfpi = 1.5707963267948966f;
      return fastsinfull (x + halfpi);
    }

    static inline float
    fastercosfull (float x)
    {
      static const float halfpi = 1.5707963267948966f;
      return fastersinfull (x + halfpi);
    }
};

/// PREDEFINED TYPES
typedef boost::geometry::model::d2::point_xy<double> Point2d;
typedef boost::geometry::model::d2::point_xy<float>  Point2f;
typedef boost::geometry::model::d2::point_xy<int>    Point2i;
typedef Point2i                                      Dim2i;
typedef Point2f                                      Dim2f;
typedef Point2d                                      Dim2d;
typedef Point2i                                      Vec2i;
typedef Point2f                                      Vec2f;
typedef Point2d                                      Vec2d;
typedef Line<Point2d>::type                          Line2d;
typedef Line<Point2f>::type                          Line2f;
typedef Line<Point2i>::type                          Line2i;
typedef Box<Point2d>::type                           Box2d;
typedef Box<Point2f>::type                           Box2f;
typedef Box<Point2i>::type                           Box2i;
typedef PointSet<Point2i>::type                      PointSet2i;
typedef PointSet<Point2f>::type                      PointSet2f;
typedef PointSet<Point2d>::type                      PointSet2d;
typedef Polygon<Point2i>::type                       Polygon2i;
typedef Polygon<Point2f>::type                       Polygon2f;
typedef Polygon<Point2d>::type                       Polygon2d;
typedef IntersectionResultSet<Point2i>::type         Intersection2iResultSet;
typedef IntersectionResultSet<Point2f>::type         Intersection2fResultSet;
typedef IntersectionResultSet<Point2d>::type         Intersection2dResultSet;
typedef IntersectionResult<Point2i>::type            Intersection2iResult;
typedef IntersectionResult<Point2f>::type            Intersection2fResult;
typedef IntersectionResult<Point2d>::type            Intersection2dResult;
typedef LineSet<Point2i>::type                       Line2iSet;
typedef LineSet<Point2f>::type                       Line2fSet;
typedef LineSet<Point2d>::type                       Line2dSet;
typedef Translation<Point2i>::type                   Translation2i;
typedef Translation<Point2f>::type                   Translation2f;
typedef Translation<Point2d>::type                   Translation2d;
typedef Rotation<Point2i>::type                      Rotation2i;
typedef Rotation<Point2f>::type                      Rotation2f;
typedef Rotation<Point2d>::type                      Rotation2d;
typedef std::vector<const Line2i*>                   Line2iPtrSet;
typedef std::vector<const Line2f*>                   Line2fPtrSet;
typedef std::vector<const Line2d*>                   Line2dPtrSet;
}
}
#endif // DXF_TYPES_HPP


// Copyright 2014-2015 SDL plc
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//     http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
   \file

   Feature expectations for the expectation semiring.

   the relationship between FeatureWeight (with nonnegative feature values only)
   and ExpectationWeight is, for a linear derivation (single path):

   FeatureWeight(cost, [1=val1,...])

   is the same as

   ExpectationWeight(cost=-log(prob), [1=-log(val1*prob)=-log(val1)+cost,...])

   times in both cases adds the costs, and the values accumulate in the same way
   modulo the above isomorphism (exception: FeatureWeight with allowed negative
   values would not be isomorphic; we would need to introduce a sign bit to the
   ExpectationWeight's feature values)

   we could have kept the interpretations the same, but this way is more
   computationally efficient (because FeatureWeight plus selects the best path,
   while ExpectationWeight sums probabilities/expectations of things)

   think of ExpectationWeight as representing disjoint events.

   the sum of two ExpectationWeight is then a more probable single event.

   the feature vectors' values are -log(p(event)*E[count of feature|event]) - joint, not conditional

   (the overall probability is also a float value that's -log(prob))

   so adding two ExpectationWeight just separately (-LogPlus) adds the probs and
   expectation vectors

   further, the default value for weight[feature_id] is +infinity, not 0 as it
   is for featureweight

   the times (product) simply concatenates the events together (multiplying the
   two probabilities), and the resulting expectations are counts of features over
   the event (so the product event is less likely, but has more features on it, so
   higher conditional expectation)

   but because we don't store expectations conditional on the event, but rather
   the joint expectation in the overall probability distribution, this means that
   we have (a*b)[i]=-log(prob(a)*prob(b)*E[feature i|a*b]

   where E[feature i|a*b]) := E[feature i|a]+E[feature i|b])

   Now, times in detail:

   the algorithm for C=A*B is:

   costC = costA+costB
   fC[i] = costA+costB+logplus(fA[i]-costA, fB[i]-costB)
   =(probs)probA*probB*(pA[i]/probA+pB[i]/probB) = pA[i]*probB + pB[i]*probA
   = logplus(fA[i]+costB, fB[i]+costA)

   or to add C += B

   fC[i] = logplus(costB + fC[i], costC + fB[i])

   in case fC[i] = fB[i] (same map)

   fC[i] = logplus(costB + fC[i], costC + fC[i]) =(prob) probB*pC[i] + probC*pC[i] = (probB+probC)*pC[i]
   = logplus(costB, costC) + fC[i]

   in case C==B (so further costB == costC), you can take logplus(costC, costC) = costC-log(2)

   graehl thinks it really simplifies thinking about expectations if we use
   conditional (log) expectations in the feature values map. however, it's
   currently correct so don't touch it.

   \author Markus Dreyer

*/

#ifndef HYP__HYPERGRAPH_EXPECTATION_WEIGHT_HPP
#define HYP__HYPERGRAPH_EXPECTATION_WEIGHT_HPP
#pragma once

#include <sdl/Hypergraph/FeatureWeight.hpp>
#include <sdl/Hypergraph/Weight.hpp>
#include <sdl/Util/Constants.hpp>
#include <sdl/Util/LogHelper.hpp>
#include <sdl/Util/LogMath.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>
#include <cassert>
#include <iostream>
#include <stdexcept>
#include <string>

namespace sdl {
namespace Hypergraph {

/// ExpectationWeight is a type of FeatureWeight, where we take the
/// sum of values rather than the min:
typedef FeatureWeightTpl<FeatureValue, Features, Expectation> ExpectationWeight;

/**
   Adds another weight to this weight (using semiring
   plus). This is the version for Expectation policy.
*/
template <class FloatT, class MapT, class SumPolicy>
void FeatureWeightTpl<FloatT, MapT, SumPolicy>::plusBy(FeatureWeightTpl<FloatT, MapT, Expectation> const& b) {
  checkSumPolicy<Expectation>();
  if (b.isZero()) {
    // adding zero to this means do nothing. the expectations must all be zero too.
  } else if (isZero()) {
    *this = b;
  } else if (&b == this) {
    // initially, I thought the pmap equal by pointer case would handle this, but
    // i was wrong. ownMap() only copies if pointer wasn't unique. but the
    // pointer will be unique if the objects are identical.
    const FloatT neglog2 = -std::log((FloatT)2);
    this->value_ += neglog2;
    if (pMap_) {
      ownMap();
      for (iterator i = pMap_->begin(), e = pMap_->end(); i != e; ++i)
        i->second += neglog2;  // in prob space, we're adding p1+p2
    }
  } else {
    Util::neglogPlusBy(b.value_, this->value_);
    assert(!isZero());  // adding two nonzero things can never give a zero result
    if (pMap_ == b.pMap_) {  // maps equal by pointer. doesn't imply probabilities are the same too
      if (pMap_) {
        ownMap();
        const FloatT neglog2 = -std::log((FloatT)2);
        for (iterator i = pMap_->begin(), e = pMap_->end(); i != e; ++i)
          i->second += neglog2;  // in prob space, we're adding p1+p2
        // by multiplying by 2 (since the maps are the same, the joint probs are all the same)
      }
    } else {
      // add (joint) expectations:
      if (!b.empty()) {
        ownMap();
        MapT& thismap = *pMap_;
        MapT* bmap = b.pMap_.get();
        assert(&thismap != bmap);
        if (bmap)
          for (typename MapT::const_iterator i = bmap->begin(), e = bmap->end(); i != e; ++i)
            Util::mapAddNeglogPlus(thismap, i->first, i->second);
      }
    }
  }
}

/**
   Multiplies this weight by another weight (using semiring
   times). This is the version for Expectation policy.
*/
template <class FloatT, class MapT, class SumPolicy>
void FeatureWeightTpl<FloatT, MapT, SumPolicy>::timesBy(FeatureWeightTpl<FloatT, MapT, Expectation> const& b) {
  checkSumPolicy<Expectation>();
  if (isZero()) {
  } else if (b.isZero()) {
    setZero();
  } else if (b.isOne()) {
  } else if (isOne()) {
    *this = b;
  } else {
    FloatT const aval = this->value_;
    FloatT const bval = b.value_;
    this->value_ = aval + bval;  // pa' = pa*pb
    if (pMap_ == b.pMap_) {  // special (rare) case to avoid modifying map while reading from it
      ownMap();  // note: this must occur after the test above
      // fC[i] <= logplus(costB + fC[i], costC + fB[i]) = logplus(costB, costC) + fC[i]
      FloatT const aplusbval = Util::neglogPlus(aval, bval);  // we're setting (in prob space) v'=v*(pa+pb)
      SDL_DEBUG_ALWAYS(Hypergraph.ExpectationWeight, "same map for a*b; result[i] = a[i] + neglogplus("
                                                         << aval << ", " << bval << ") = " << aplusbval);
      for (typename MapT::iterator i = pMap_->begin(), e = pMap_->end(); i != e; ++i)
        i->second += aplusbval;  // *=(pa+pb)
    } else {
      assert(this != &b);
      ownMap();
      // fC[i] = logplus(costB + fC[i], costC + fB[i])
      // multiply A expectations by probB
      for (typename MapT::iterator i = pMap_->begin(), e = pMap_->end(); i != e; ++i) i->second += bval;
      // then add (B expectations multiplied by probA).
      if (!b.empty()) {
        MapT& thismap = *pMap_;
        MapT const* bmap = b.pMap_.get();
        assert(&thismap != bmap);
        if (bmap)
          for (typename MapT::const_iterator i = bmap->begin(), e = bmap->end(); i != e; ++i)
            Util::mapAddNeglogPlus(thismap, i->first, i->second + aval);
      }
    }
    assert(!isZero());  // we shouldn't ever underflow by adding two non-zero probs in logspace
  }
}

template <class FloatT, class MapT>
inline FeatureWeightTpl<FloatT, MapT, Expectation> plus(FeatureWeightTpl<FloatT, MapT, Expectation> const& w1,
                                                        FeatureWeightTpl<FloatT, MapT, Expectation> const& w2) {
  FeatureWeightTpl<FloatT, MapT, Expectation> w3(w1, true);
  w3.plusBy(w2);
  return w3;
}

/// result has p=w1.p*w2.p and feats=w1.p*w2.feats+w2.p*w1.feats
template <class FloatT, class MapT>
inline FeatureWeightTpl<FloatT, MapT, Expectation> times(FeatureWeightTpl<FloatT, MapT, Expectation> const& w1,
                                                         FeatureWeightTpl<FloatT, MapT, Expectation> const& w2) {
  FeatureWeightTpl<FloatT, MapT, Expectation> w3(w1, true);
  w3.timesBy(w2);
  return w3;
}

}}

#endif


#pragma once

#include "log.hpp"

#include <hnsw/distance.hpp>

#include <boost/optional.hpp>

#include <algorithm>
#include <chrono>
#include <cmath>
#include <random>
#include <string>
#include <utility>
#include <vector>

using random_t = std::minstd_rand;

using vector_t = std::vector<float>;
using dataset_t = std::vector<std::pair<std::string, vector_t>>;

inline void shuffle(dataset_t &vectors, random_t &random) {
    std::shuffle(vectors.begin(), vectors.end(), random);
}

inline void normalize(dataset_t &vectors) {
    for (auto &v: vectors) {
        float coef = 1.0f / std::sqrt(hnsw::detail::dot_product(v.second.data(), v.second.data(), v.second.size()));

        for (auto &x: v.second) {
            x *= coef;
        }
    }
}

inline size_t get_control_size(const dataset_t &vectors, boost::optional<size_t> size) {
    if (size) {
        return *size;
    } else {
        return std::min(vectors.size(), std::max<size_t>(1, vectors.size() / 100));
    }
}

inline void split_dataset(dataset_t &main, dataset_t &control, size_t control_size) {
    control.assign(main.begin(), main.begin() + control_size);

    dataset_t new_main(main.begin() + control_size, main.end());
    main = std::move(new_main);
}