// Copyright (C) 2015, Pawel Tomulik <ptomulik@meil.pw.edu.pl>
//
// 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)
#define BOOST_TEST_MODULE test_txpl_vm_eval_binary_xor

#include <txpl/test_config.hpp>
#include <boost/test/unit_test.hpp>

#ifndef TXPL_TEST_SKIP_VM_EVAL_BINARY_XOR

#include <txpl/vm/eval_binary_op.hpp>
#include <txpl/vm/basic_types.hpp>
#include <txpl/vm/value.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/get.hpp>

using namespace txpl::vm;
typedef basic_types<>::char_type   char_type;
typedef basic_types<>::int_type    int_type;
typedef basic_types<>::bool_type   bool_type;
typedef basic_types<>::real_type   real_type;
typedef basic_types<>::string_type string_type;
typedef basic_types<>::regex_type  regex_type;
typedef basic_types<>::blank_type  blank_type;
typedef array<value<> >            array_type;
typedef object<value<> >           object_type;

BOOST_AUTO_TEST_CASE(char__xor__char)
{
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = char_type{'\0'};
    const value<> v2 = char_type{'\0'};
    r = blank_type();
    BOOST_CHECK(boost::apply_visitor(op, v1, v2));
    int_type x = int_type{654};
    BOOST_CHECK_NO_THROW(x = boost::get<int_type>(r));
    BOOST_CHECK(x == (char_type{'\0'} ^ char_type{'\0'}));
  }
}
BOOST_AUTO_TEST_CASE(char__xor__int)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = char_type{'\0'};
    const value<> v2 = int_type{0};
    r = blank_type();
    BOOST_CHECK(boost::apply_visitor(op, v1, v2));
    int_type x = int_type{654};
    BOOST_CHECK_NO_THROW(x = boost::get<int_type>(r));
    BOOST_CHECK(x == (char_type{'\0'} ^ int_type{0}));
  }
}
BOOST_AUTO_TEST_CASE(char__xor__bool)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = char_type{'\0'};
    const value<> v2 = bool_type{false};
    r = blank_type();
    BOOST_CHECK(boost::apply_visitor(op, v1, v2));
    int_type x = int_type{654};
    BOOST_CHECK_NO_THROW(x = boost::get<int_type>(r));
    BOOST_CHECK(x == (char_type{'\0'} ^ bool_type{false}));
  }
}
BOOST_AUTO_TEST_CASE(char__xor__real)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = char_type{'\0'};
    const value<> v2 = real_type{0.0};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(char__xor__string)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = char_type{'a'};
    const value<> v2 = string_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(char__xor__regex)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = char_type{'a'};
    const value<> v2 = regex_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(char__xor__array)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = char_type{'a'};
    const value<> v2 = array_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(char__xor__object)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = char_type{'a'};
    const value<> v2 = object_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}

BOOST_AUTO_TEST_CASE(int__xor__char)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = int_type{0};
    const value<> v2 = char_type{'\0'};
    r = blank_type();
    BOOST_CHECK(boost::apply_visitor(op, v1, v2));
    int_type x = int_type{654};
    BOOST_CHECK_NO_THROW(x = boost::get<int_type>(r));
    BOOST_CHECK(x == (int_type{0} ^ char_type{'\0'}));
  }
}
BOOST_AUTO_TEST_CASE(int__xor__int)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = int_type{0x1700};
    const value<> v2 = int_type{0x0134};
    r = blank_type();
    BOOST_CHECK(boost::apply_visitor(op, v1, v2));
    int_type x = int_type{654};
    BOOST_CHECK_NO_THROW(x = boost::get<int_type>(r));
    BOOST_CHECK(x == (int_type{0x1700} ^ int_type{0x0134}));
  }
}
BOOST_AUTO_TEST_CASE(int__xor__bool)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = int_type{0};
    const value<> v2 = bool_type{false};
    r = blank_type();
    BOOST_CHECK(boost::apply_visitor(op, v1, v2));
    int_type x = int_type{654};
    BOOST_CHECK_NO_THROW(x = boost::get<int_type>(r));
    BOOST_CHECK(x == (int_type{0} ^ bool_type{false}));
  }
}
BOOST_AUTO_TEST_CASE(int__xor__real)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = int_type{0};
    const value<> v2 = real_type{0.0};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(int__xor__string)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = int_type{0};
    const value<> v2 = string_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(int__xor__regex)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = int_type{0};
    const value<> v2 = regex_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(int__xor__array)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = int_type{0};
    const value<> v2 = array_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(int__xor__object)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = int_type{0};
    const value<> v2 = object_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}

BOOST_AUTO_TEST_CASE(bool__xor__char)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = bool_type{false};
    const value<> v2 = char_type{'\0'};
    r = blank_type();
    BOOST_CHECK(boost::apply_visitor(op, v1, v2));
    int_type x = int_type{654};
    BOOST_CHECK_NO_THROW(x = boost::get<int_type>(r));
    BOOST_CHECK(x == (char_type{false} ^ bool_type{'\0'}));
  }
}
BOOST_AUTO_TEST_CASE(bool__xor__int)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = bool_type{false};
    const value<> v2 = int_type{0};
    r = blank_type();
    BOOST_CHECK(boost::apply_visitor(op, v1, v2));
    int_type x = int_type{654};
    BOOST_CHECK_NO_THROW(x = boost::get<int_type>(r));
    BOOST_CHECK(x == (bool_type{false} ^ int_type{0}));
  }
}
BOOST_AUTO_TEST_CASE(bool__xor__bool)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = bool_type{false};
    const value<> v2 = bool_type{false};
    r = blank_type();
    BOOST_CHECK(boost::apply_visitor(op, v1, v2));
    int_type x = int_type{654};
    BOOST_CHECK_NO_THROW(x = boost::get<int_type>(r));
    BOOST_CHECK(x == (bool_type{false} ^ bool_type{false}));
  }
}
BOOST_AUTO_TEST_CASE(bool__xor__real)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = bool_type{false};
    const value<> v2 = real_type{0.0};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(bool__xor__string)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = bool_type{false};
    const value<> v2 = string_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(bool__xor__regex)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = bool_type{false};
    const value<> v2 = regex_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(bool__xor__array)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = bool_type{false};
    const value<> v2 = array_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(bool__xor__object)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = bool_type{false};
    const value<> v2 = object_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}

BOOST_AUTO_TEST_CASE(real__xor__char)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = real_type{0.0};
    const value<> v2 = char_type{'\0'};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(real__xor__int)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = real_type{0.0};
    const value<> v2 = int_type{0};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(real__xor__bool)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = real_type{0.0};
    const value<> v2 = bool_type{false};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(real__xor__real)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = real_type{0.0};
    const value<> v2 = real_type{0.0};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(real__xor__string)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = real_type{0.0};
    const value<> v2 = string_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(real__xor__regex)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = real_type{0.0};
    const value<> v2 = regex_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(real__xor__array)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = real_type{0.0};
    const value<> v2 = array_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(real__xor__object)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = real_type{0.0};
    const value<> v2 = object_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}

BOOST_AUTO_TEST_CASE(string__xor__char)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = string_type();
    const value<> v2 = char_type{'\0'};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(string__xor__int)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = string_type();
    const value<> v2 = int_type{0};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(string__xor__bool)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = string_type();
    const value<> v2 = bool_type{false};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(string__xor__real)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = string_type();
    const value<> v2 = real_type{0.0};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(string__xor__string)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = string_type();
    const value<> v2 = string_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(string__xor__regex)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = string_type();
    const value<> v2 = regex_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(string__xor__array)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = string_type();
    const value<> v2 = array_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(string__xor__object)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = string_type();
    const value<> v2 = object_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}

BOOST_AUTO_TEST_CASE(regex__xor__char)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = regex_type();
    const value<> v2 = char_type{'\0'};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(regex__xor__int)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = regex_type();
    const value<> v2 = int_type{0};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(regex__xor__bool)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = regex_type();
    const value<> v2 = bool_type{false};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(regex__xor__real)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = regex_type();
    const value<> v2 = real_type{0.0};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(regex__xor__string)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = regex_type();
    const value<> v2 = string_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(regex__xor__regex)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = regex_type();
    const value<> v2 = regex_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(regex__xor__array)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = regex_type();
    const value<> v2 = array_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(regex__xor__object)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = regex_type();
    const value<> v2 = object_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}

BOOST_AUTO_TEST_CASE(array__xor__char)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = array_type();
    const value<> v2 = char_type{'\0'};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(array__xor__int)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = array_type();
    const value<> v2 = int_type{0};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(array__xor__bool)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = array_type();
    const value<> v2 = bool_type{false};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(array__xor__real)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = array_type();
    const value<> v2 = real_type{0.0};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(array__xor__string)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = array_type();
    const value<> v2 = string_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(array__xor__regex)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = array_type();
    const value<> v2 = regex_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(array__xor__array)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = array_type();
    const value<> v2 = array_type();
    r = blank_type();
    BOOST_CHECK(boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<array_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(array__xor__object)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = array_type();
    const value<> v2 = object_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}

BOOST_AUTO_TEST_CASE(object__xor__char)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = object_type();
    const value<> v2 = char_type{'\0'};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(object__xor__int)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = object_type();
    const value<> v2 = int_type{0};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(object__xor__bool)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = object_type();
    const value<> v2 = bool_type{false};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(object__xor__real)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = object_type();
    const value<> v2 = real_type{0.0};
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(object__xor__string)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = object_type();
    const value<> v2 = string_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(object__xor__regex)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = object_type();
    const value<> v2 = regex_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(object__xor__array)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = object_type();
    const value<> v2 = array_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}
BOOST_AUTO_TEST_CASE(object__xor__object)
{
  using namespace txpl::vm;
  value<> r;
  auto op = eval_binary_op<op_t::xor_>(r);
  {
    const value<> v1 = object_type();
    const value<> v2 = object_type();
    r = blank_type();
    BOOST_CHECK(!boost::apply_visitor(op, v1, v2));
    BOOST_CHECK_NO_THROW(boost::get<blank_type>(r));
  }
}

#else
BOOST_AUTO_TEST_CASE(dummy)
{
  BOOST_CHECK(true);
}
#endif


#include <boost/config/warning_disable.hpp>
#include <boost/bind.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_real.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_increment_actor.hpp>
#include <string>
#include <map>
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <equation_system/clientp.hpp>

namespace System {
using namespace std;
using namespace BOOST_SPIRIT_CLASSIC_NS;
namespace clientp
{

namespace {
	void sad_little_message(int line_no, string input){
		cerr << "Input line : " << line_no << endl;
		cerr << "Received : " << input << endl;
		cerr << "Please express equations using arithmetic combinations +/- of one or more terms, " << endl;
		cerr << "terms consisting of products expressed with *, powers with ^ and powers/indices" << endl;
		cerr << "expressed within parenthesis for constants k and variables y (1-based index) eg.," << endl;
		cerr << " " << endl;
		cerr << "1.0*k(717)*y(516)-1.0*k(416)*y(1)*y(392)-1.0*k(718)*y(1)^(1/2)*y(517)" << endl;
		cerr << " " << endl;
		throw std::invalid_argument( "Received bad equation format on stdin" );
	}
}

template <typename T>
void update(std::map<T,T> &x, const T& key, const T& val){

	x[key]+=val;
}

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

using qi::float_;
using qi::double_;
using qi::phrase_parse;
using qi::_1;
using ascii::space;
using phoenix::push_back;

template <typename Iterator>
	bool parse_csv(Iterator first, Iterator last, std::vector<float>& k)
	{
		bool r = phrase_parse(first, last,

				//  grammar for csv files
				(
				 //*(float_ >> ',' | float_ [push_back(phoenix::ref(k),_1)])
				 *(float_ [push_back(phoenix::ref(k),_1)])
				)
				,
				space);

		return r;

	}

template <typename Iterator>
	bool parse_k_vals(Iterator first, Iterator last, std::vector<float>& w)
	{
		bool r = phrase_parse(first, last,

				//  grammar for k constants
				(
				 ("k(" >> float_[push_back(phoenix::ref(w), _1)] >> ')')
				)
				,
				//skip
				space | '*' | float_ >> '*' | '-'>>float_ >> '*'| '+' >> float_ >> '*'| "y(" >> float_ >> ')');

		return r;
	}

template <typename Iterator>
	bool parse_constants(Iterator first, Iterator last, std::vector<float>& v)
	{
		bool r = phrase_parse(first, last,

				//  grammar for numerical constants & sign
				(
				 (float_[push_back(phoenix::ref(v), _1)] >> '*')
				)
				,
				//skip
				space);
		return r;
	}
template <typename Iterator>
	bool parse_y_vals(Iterator first, Iterator last, std::map<float,float>& x)
	{
		float base =1.0;
		float exp = 0.0;
		bool r = phrase_parse(first, last,

				//  grammar for y variables
				//  this looks like hell
				(
				 *("y(" >> float_[phoenix::ref(base)=_1] >> ")^(" >> float_[phoenix::ref(exp)=_1] >> '/' >> float_[phoenix::ref(exp)/=_1]\
					 [boost::bind(&update<float>,boost::ref(x),boost::ref(base),boost::ref(exp))] >> ')' |
					 "y(" >> float_[phoenix::ref(base)=_1, boost::bind(&update<float>,boost::ref(x),boost::ref(base),1.0f)] >> ')')
				)
				,
				//skip
				space | '*' | float_ >> '*' | '-'>>float_ >> '*'| '+' >> float_ >> '*'| "k(" >> float_ >> ')');

		return r;
	}

template <typename Iterator>
	bool parse_csv(Iterator first, Iterator last, std::vector<double>& k)
	{
		bool r = phrase_parse(first, last,

				//  grammar for csv files
				(
				 //*(double_ >> ',' | double_ [push_back(phoenix::ref(k),_1)])
				 *(double_ [push_back(phoenix::ref(k),_1)])
				)
				,
				space);

		return r;

	}

template <typename Iterator>
	bool parse_k_vals(Iterator first, Iterator last, std::vector<double>& w)
	{
		bool r = phrase_parse(first, last,

				//  grammar for k constants
				(
				 ("k(" >> double_[push_back(phoenix::ref(w), _1)] >> ')')
				)
				,
				//skip
				space | '*' | double_ >> '*' | '-'>>double_ >> '*'| '+' >> double_ >> '*'| "y(" >> double_ >> ')');

		return r;
	}

template <typename Iterator>
	bool parse_constants(Iterator first, Iterator last, std::vector<double>& v)
	{
		bool r = phrase_parse(first, last,

				//  grammar for numerical constants & sign
				(
				 (double_[push_back(phoenix::ref(v), _1)] >> '*')
				)
				,
				//skip
				space);
		return r;
	}
template <typename Iterator>
	bool parse_y_vals(Iterator first, Iterator last, std::map<double,double>& x)
	{
		double base =1.0;
		double exp = 0.0;
		bool r = phrase_parse(first, last,

				//  grammar for y variables
				//  this looks like hell
				(
				 *("y(" >> double_[phoenix::ref(base)=_1] >> ")^(" >> double_[phoenix::ref(exp)=_1] >> '/' >> double_[phoenix::ref(exp)/=_1]\
					 [boost::bind(&update<double>,boost::ref(x),boost::ref(base),boost::ref(exp))] >> ')' |
					 "y(" >> double_[phoenix::ref(base)=_1, boost::bind(&update<double>,boost::ref(x),boost::ref(base),1.0f)] >> ')')
				)
				,
				//skip
				space | '*' | double_ >> '*' | '-'>>double_ >> '*'| '+' >> double_ >> '*'| "k(" >> double_ >> ')');

		return r;
	}
}
}


#include "numpy_utils.hpp"
#include <boost/python.hpp>
#include <iostream>
#include "cdist.cuh"
namespace py = boost::python;

py::object sqdistGPU(py::object x) {
  x = np_mod.attr("array")(x, "float32");
  int xdim = py::extract<int>(x.attr("shape")[0]);
  float* xdata = getPointer<float>(x);
  float* zdata = sqdistWrapper(xdata, xdata, xdim, xdim);
  return toNdarray2<float>(zdata, xdim, xdim);
}

int pyMean(py::object x) {
  x = np_mod.attr("array")(x, "float32");
  int xdim = py::extract<int>(x.attr("shape")[0]);
  float* xdata = getPointer<float>(x);
  printf("dimension is %i\n", xdim);
  int mean = 0;
  for(int i = 0; i < xdim; ++i) {
    mean += xdata[i];
    printf("idx %i, mean value is %d\n", i, mean);
  }
  mean = mean/(float) xdim;
  return mean;
}

BOOST_PYTHON_MODULE(test_boost) {
    np_mod = py::import("numpy");
    py::def("cpp_mean", &pyMean, (py::arg("x")));
    py::def("cpp_sqdist", &sqdistGPU, (py::arg("x")));
}


/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/

#include <algorithm>
#include <fstream>
#include <queue>
#include <sysexits.h>
#include <unordered_map>
#include <vector>

#include <limits.h>

#include <boost/tokenizer.hpp>

#include "common/functional_maps.hpp"
#include "common/CachingJoiner.hpp"
#include "w2v/distance.hpp"
#include "w2v/TokenSequence.hpp"
#include "w2v/WordProjections.hpp"

namespace w2v
{

static std::function<std::string(const std::string&)> lowercaser = [] (const std::string& s) -> std::string
{
	std::string result(s);
	std::transform(result.begin(), result.end(), result.begin(), ::tolower);
	return result;
};

static std::pair<real, word_pair_error_code> distance(WordProjections& projections, const TokenSequence& tokenSequence_i, const std::string& vocabLookupKey)
{
    return distance(projections, tokenSequence_i.getTokenArray(), tokenSequence_i.getTokenCount(), vocabLookupKey.c_str());
}

static std::pair<real, word_pair_error_code> lowercaseDistance(WordProjections& projections, const std::vector<std::string>& tokenSequence_i, const std::string& joined_i_lower, const std::string& vocabLookupKey, std::unordered_map<std::string, TokenSequence>& tokenSequenceCache, std::unordered_map<std::string, std::string>& lowercaseCache)
{
    const TokenSequence& ts_i = tokenSequenceCache.emplace(joined_i_lower, tokenSequence_i).first->second;
    std::string vocabLookupKey_lower = common::emplace(lowercaseCache, vocabLookupKey, lowercaser).first->second;
    return distance(projections, ts_i, vocabLookupKey_lower);
}

static void printDistanceTable(WordProjections& projections, const std::vector<std::vector<std::string> >& tokenSequences, std::ostream& out=std::cout, std::ostream& err=std::cerr, const std::string& rowSep="\n", const std::string& colSep="\t")
{
    common::CachingJoiner tableKeyJoiner(" ");
    common::CachingJoiner vocabKeyJoiner("_");
    std::unordered_map<std::string, TokenSequence> tokenSequenceCache;
    std::unordered_map<std::string, std::string> lowercaseCache;
    std::unordered_map<std::string, std::unordered_map<std::string, real> > table;
    size_t comparisons = 0;
    out << "FIRST" << colSep << "SECOND" << colSep << "DIST" << rowSep;
    for (const std::vector<std::string>& tokenSequence_i : tokenSequences)
    {
        const std::string& joined_i = tableKeyJoiner(tokenSequence_i);
        std::unordered_map<std::string, real>& row = table[joined_i];
        for (const std::vector<std::string>& tokenSequence_j : tokenSequences)
        {
            const std::string& joined_j = tableKeyJoiner(tokenSequence_j);
            auto colIter = row.find(joined_j);
            if (colIter == row.end())
            {
                if (joined_i == joined_j)
                {
                    const real dist = 0;
                    row[joined_j] = dist;
                    out << joined_i << colSep << joined_j << colSep << dist << rowSep;
                    ++comparisons;
                }
                else
                {
                    const TokenSequence& ts_i = tokenSequenceCache.emplace(joined_i, tokenSequence_i).first->second;
                    const std::string& vocabLookupKey = vocabKeyJoiner(tokenSequence_j);

                    const std::pair<real, word_pair_error_code> distResults = distance(projections, ts_i, vocabLookupKey);
                    if (distResults.second == OK)
                    {
                        const real dist = distResults.first;
                        row[joined_j] = dist;
                        out << joined_i << colSep << joined_j << colSep << dist << rowSep;
                        ++comparisons;
                    }
                    else if (distResults.second == SECOND)
                    {
                        //                        err << "\"" << joined_j << "\" not found in dictionary; Trying lowercase version.\n";
						const std::string& joined_i_lower = common::emplace(lowercaseCache, joined_i, lowercaser).first->second;
                        const std::pair<real, word_pair_error_code> distResults = lowercaseDistance(projections, tokenSequence_i, joined_i_lower, vocabLookupKey, tokenSequenceCache, lowercaseCache);
                        if (distResults.second == OK)
                        {
							std::string joined_j_lower = common::emplace(lowercaseCache, joined_j, lowercaser).first->second;
                            const real dist = distResults.second;
                            row[joined_j] = dist;
                            out << joined_i_lower << colSep << joined_j_lower << colSep << dist << rowSep;
                            ++comparisons;
                        }
                        else
                        {
                            //                            err << "Lowercased \"" << joined_j_lower << "\" lookup failed.\n";
                        }
                    }
                    else if (distResults.second == FIRST)
                    {
                        //                        err << "\"" << joined_i << "\" not found in dictionary; Trying lowercase version.\n";
                        const std::string& joined_i_lower = common::emplace(lowercaseCache, joined_i, lowercaser).first->second;
                        const std::pair<real, word_pair_error_code> distResults = lowercaseDistance(projections, tokenSequence_i, joined_i_lower, vocabLookupKey, tokenSequenceCache, lowercaseCache);
                        if (distResults.second == OK)
                        {
                            std::string joined_j_lower = common::emplace(lowercaseCache, joined_j, lowercaser).first->second;
                            const real dist = distResults.second;
                            row[joined_j] = dist;
                            out << joined_i_lower << colSep << joined_j_lower << colSep << dist << rowSep;
                            ++comparisons;
                        }
                        else
                        {
                            //                            err << "Lowercased \"" << joined_i << "\" lookup failed.\n";
                            // Break out of loop for second word in word pair
                            break;
                        }
                    }
                    else
                    {
                        err << "An undentified error occured for word pair {\"" << joined_i << "\", \"" << joined_j << "\"}.\n";
                    }
                }
            }
            else
            {
                err << "Already computed distance between \"" << joined_i << "\" and \"" << joined_j << "\"; Skipping.\n";
            }
        }
    }
    std::cerr << "Checked " << comparisons << " word pairs.\n";
}

//static int printProjections(const WordProjections& projections, std::ostream& out=std::cout, std::ostream& err=std::cerr, const std::string& rowSep="\n", const std::string& colSep="\t")
//{
//    for (size_t vocabIdx = 0; vocabIdx < projections.getVocabSize(); ++vocabIdx)
//    {
//        const char* const vocabSymbol = projections.getVocabSymbol(vocabIdx);
//        out << vocabSymbol;
//
//        ArrayIterator<real> projectionStart = projections.getProjectionVectorBegin(vocabIdx);
//		const ArrayIterator<real> projectionEnd = projections.getProjectionVectorBegin(vocabIdx + 1);
//		while (projectionStart != projectionEnd)
//		{
//			out << colSep << *projectionStart;
//
//			++projectionStart;
//		}
//
//		 out << rowSep;
//    }
//
//    return EXIT_SUCCESS;
//}

static std::vector<std::vector<std::string> > readTokens(std::istream& in)
{
    std::vector<std::vector<std::string> > result;

    std::string line;
    while (std::getline(in,line))
    {
        if(line.find_first_not_of(' ') != std::string::npos)
        {
            boost::char_separator<char> sep(" ");
            typedef boost::token_iterator_generator<boost::char_separator<char> >::type Iter;
            Iter beg = boost::make_token_iterator<std::string>(line.begin(), line.end(), sep);
            Iter end = boost::make_token_iterator<std::string>(line.end(), line.end(), sep);
            std::vector<std::string> tokenSequence(beg, end);
            result.push_back(tokenSequence);
        }
    }

    return result;
}

static int printDistanceTable(const char* tokensPath, WordProjections& projections)
{
    int result = EXIT_FAILURE;

    std::cerr << "Reading tokens from \"" << tokensPath << "\"...\n";
    std::ifstream tokensFile(tokensPath);
    if (tokensFile.is_open())
    {
        std::vector<std::vector<std::string> > tokenSequences = readTokens(tokensFile);
        tokensFile.close();
        std::cerr << "Read in " << tokenSequences.size() << " token sequence(s).\n";
        printDistanceTable(projections, tokenSequences);
        //                            std::map<std::string, std::map<std::string, real> > distances = createDistanceTable(projections, tokenSequences);
        //                            std::cerr << "Created a table with " << distances.size() << " rows.\n";
        //                            print(distances, std::cout);

        result = EXIT_SUCCESS;
    }
    else
    {
        std::cerr << "Tokens file not found.\n";
        result = EX_NOINPUT;
    }

    return result;
}

static int printDistanceTable(std::istream& in, WordProjections& projections)
{
    std::vector<std::vector<std::string> > tokenSequences = readTokens(in);
    std::cerr << "Read in " << tokenSequences.size() << " token sequence(s).\n";
    printDistanceTable(projections, tokenSequences);
    //                        std::map<std::string, std::map<std::string, real> > distances = createDistanceTable(projections, tokenSequences);
    //                        std::cerr << "Created a table with " << distances.size() << " rows.\n";
    //						print(distances, std::cout);

    return EXIT_SUCCESS;
}

static void printUsage(std::ostream& out, const char* executableName)
{
	out << "Usages:\n"
	<< executableName << " PROJECTION_FILE\n"
	<< executableName << " PROJECTION_FILE < TOKENS...\n"
	<< executableName << " PROJECTION_FILE TOKENS_FILE\nwhere PROJECTION_FILE contains word projections in binary format\n";
}

}

int main(int argc, char **argv)
{
    int result = EXIT_FAILURE;

    if (argc < 2)
    {
        w2v::printUsage(std::cerr, argv[0]);
        result = EX_USAGE;
    }
    else
    {
        const char* projectionsPath = argv[1];
        std::FILE* projectionsFile = std::fopen(projectionsPath, "rb");
        if (projectionsFile == NULL)
        {
            std::cerr << "Projections file not found.\n";
            result = EX_NOINPUT;
        }
        else
        {
//			std::cerr << "sizeof(real)" << sizeof(real) << "\n";
//			std::cerr << std::numeric_limits<float>::is_iec559 << "\n";

            std::cerr << "Reading word projections from path \"" << projectionsPath << "\"...\n";
            long long vocabSize;
            std::fscanf(projectionsFile, "%lld", &vocabSize);
            std::cerr << vocabSize << " words in total.\n";
            long long dimensionality;
            std::fscanf(projectionsFile, "%lld", &dimensionality);
            std::cerr << "Dimensionality is " << dimensionality << ".\n";
            w2v::WordProjections projections(vocabSize, dimensionality);
            projections.load(projectionsFile);
            std::fclose(projectionsFile);

            if (argc > 2)
            {
                const char* tokensPath = argv[2];
                result = w2v::printDistanceTable(tokensPath, projections);
            }
            else if (isatty(fileno(stdin)))
            {
                // Read interactive console input
                consoleDistance(projections);
                result = EXIT_SUCCESS;
            }
            else
            {
                result = w2v::printDistanceTable(std::cin, projections);
            }
//			result = w2v::printProjections(projections);
        }

        return result;
    }
}


/**
 * Author: Aravinth Panchadcharam
 * Email: me@aravinth.info
 * Date: 07/06/15.
 * Project: Gesture Recogntion for Human-Robot Interaction
 */

#include <boost/log/trivial.hpp>
#include "test.h"

test::test()
{
    BOOST_LOG_TRIVIAL(info) << "Test Runner Initiated";

    //    run();
    //    grtDataToJson();
    //    testNullRejection();
    //        testHandsUp();
//    externalRangeTest();
    savePipeline();
}

void test::testHandsUp(){
    //--------------------- Load Datasets --------------------------//

    ClassificationData trainingData;
    ClassificationData testData;

    if( !trainingData.loadDatasetFromFile("../-train/1-1-2-3-4.txt") ){
        BOOST_LOG_TRIVIAL(error) <<"Failed to load training data!\n";
    }

    if( !testData.loadDatasetFromFile("../-train/1-5.txt") ){
        BOOST_LOG_TRIVIAL(error) <<"Failed to load training data!\n";
    }

    //--------------------- Init Classifiers ------------------------//

    ANBC anbc;
    anbc.setNullRejectionCoeff(5);
    anbc.enableScaling(true);
    anbc.enableNullRejection(true);

    //--------------------- Init and Train Pipeline -----------------------//

    GestureRecognitionPipeline pipeline;
    pipeline.setClassifier(anbc);

    if( !pipeline.train( trainingData ) ){
        BOOST_LOG_TRIVIAL(error) << "Failed to train classifier!\n";
    }

    //--------------------- Test Accuracy ---------------------------//

    double accuracy = 0;

    for(UINT i=0; i<testData.getNumSamples(); i++){

        UINT classLabel = testData[i].getClassLabel();
        vector< double > inputVector = testData[i].getSample();

        if( !pipeline.predict( inputVector )){
            BOOST_LOG_TRIVIAL(error) << "Failed to perform prediction for test sampel: " << i <<"\n";
        }

        UINT predictedClassLabel = pipeline.getPredictedClassLabel();

        if( classLabel == predictedClassLabel) accuracy++;

        //        BOOST_LOG_TRIVIAL(debug) << "TestSample: " << i <<  " ClassLabel: " << classLabel << " PredictedClassLabel: " << predictedClassLabel;
    }

    BOOST_LOG_TRIVIAL(info) << "Test Accuracy testHandsUp : " << accuracy/double(testData.getNumSamples())*100.0 << "%";
}

void test::testNullRejection(){

    //--------------------- Load Datasets --------------------------//

    ClassificationData trainingData;
    ClassificationData testData;

    if( !trainingData.loadDatasetFromFile("../-test/1-1-2-3.txt") ){
        BOOST_LOG_TRIVIAL(error) <<"Failed to load training data!\n";
    }

    if( !testData.loadDatasetFromFile("../-train/0.txt") ){
        BOOST_LOG_TRIVIAL(error) <<"Failed to load training data!\n";
    }

    //--------------------- Init Classifiers ------------------------//

    ANBC anbc;
    anbc.setNullRejectionCoeff(3);
    anbc.enableScaling(true);
    anbc.enableNullRejection(true);

    //--------------------- Init and Train Pipeline -----------------------//

    GestureRecognitionPipeline pipeline;
    pipeline.setClassifier(anbc);

    if( !pipeline.train( trainingData ) ){
        BOOST_LOG_TRIVIAL(error) << "Failed to train classifier!\n";
    }

    vector< MinMax > ranges = trainingData.getRanges();

    cout << "The ranges of the dataset are: \n";
    for(UINT j=0; j<ranges.size(); j++){
        cout << "Dimension: " << j << " Min: " << ranges[j].minValue << " Max: " << ranges[j].maxValue << endl;
    }

    //--------------------- Test Accuracy ---------------------------//

    double accuracy = 0;

    UINT classLabel = trainingData[0].getClassLabel();

    for(UINT i=0; i<testData.getNumSamples(); i++){

        vector< double > inputVector = testData[i].getSample();

        if( !pipeline.predict( inputVector )){
            BOOST_LOG_TRIVIAL(error) << "Failed to perform prediction for test sampel: " << i <<"\n";
        }

        UINT predictedClassLabel = pipeline.getPredictedClassLabel();

        if( predictedClassLabel == 0 ) accuracy++;

        BOOST_LOG_TRIVIAL(debug) << "TestSample: " << i << " PredictedClassLabel: " << predictedClassLabel << " classLabel: " << classLabel;
    }

    BOOST_LOG_TRIVIAL(info) << "Test Accuracy testNullRejection :" << accuracy/double(testData.getNumSamples())*100.0 << "%";

}

void test::run(){

    //--------------------- Load Datasets --------------------------//

    ClassificationData trainingData;
    ClassificationData testData;

    if( !trainingData.loadDatasetFromFile(TRAINING_DATASET) ){
        BOOST_LOG_TRIVIAL(error) <<"Failed to load training data!\n";
    }

    if( !testData.loadDatasetFromFile(TEST_DATASET) ){
        BOOST_LOG_TRIVIAL(error) <<"Failed to load training data!\n";
    }

    //        testData = trainingData.partition(80);

    //--------------------- Init Classifiers ------------------------//

    ANBC anbc;
    anbc.setNullRejectionCoeff(10);
    anbc.enableScaling(true);
    anbc.enableNullRejection(true);

    //--------------------- Init and Train Pipeline -----------------------//

    GestureRecognitionPipeline pipeline;
    pipeline.setClassifier(anbc);
    pipeline.addPostProcessingModule(ClassLabelFilter(5,10));
    pipeline.addPostProcessingModule(ClassLabelChangeFilter());

    if( !pipeline.train( trainingData ) ){
        BOOST_LOG_TRIVIAL(error) << "Failed to train classifier!\n";
    }

    //--------------------- Test Accuracy ---------------------------//

    double accuracy = 0;

    for(UINT i=0; i<testData.getNumSamples(); i++){

        UINT classLabel = testData[i].getClassLabel();
        vector< double > inputVector = testData[i].getSample();

        if( !pipeline.predict( inputVector )){
            BOOST_LOG_TRIVIAL(error) << "Failed to perform prediction for test sampel: " << i <<"\n";
        }

        UINT predictedClassLabel = pipeline.getPredictedClassLabel();
        UINT unprocessedClassLabel = pipeline.getUnProcessedPredictedClassLabel();
        vector< double > classLikelihoods = pipeline.getClassLikelihoods();
        vector< double > classDistances = pipeline.getClassDistances();

        if( classLabel == unprocessedClassLabel ) accuracy++;

        //        BOOST_LOG_TRIVIAL(debug) << i <<  " : " << classLabel << " : "<< unprocessedClassLabel;

        BOOST_LOG_TRIVIAL(debug) << "TestSample: " << i <<  " ClassLabel: " << classLabel
        << " PredictedClassLabel: " << predictedClassLabel << " unprocessedClassLabel: " << unprocessedClassLabel;

    }

    BOOST_LOG_TRIVIAL(info) << "Test Accuracy: " << accuracy/double(testData.getNumSamples())*100.0 << "%";
}

void test::grtDataToJson(){
    ClassificationData testData;
    if( !testData.loadDatasetFromFile("../-train/2-6.txt") ){
        BOOST_LOG_TRIVIAL(error) <<"Failed to load training data!\n";
    }

    for(UINT i=0; i<testData.getNumSamples(); i++){

        std::string lx = "\"" + std::to_string(testData[i].getSample()[0]) + "\",";
        std::string ly = "\"" + std::to_string(testData[i].getSample()[1]) + "\",";
        std::string lz = "\"" + std::to_string(testData[i].getSample()[2]) + "\"]";

        std::string rx = "\"" + std::to_string(testData[i].getSample()[3]) + "\",";
        std::string ry = "\"" + std::to_string(testData[i].getSample()[4]) + "\",";
        std::string rz = "\"" + std::to_string(testData[i].getSample()[5]) + "\"]";

        std::string right = "{\"RIGHT\":[" +  rx + ry + rz ;
        std::string left = ", \"LEFT\":[" +  lx + ly + lz + "},";
        std::cout << right + left + "\n";
    }

}

void test::externalRangeTest(){
    //--------------------- Load Datasets --------------------------//

    ClassificationData trainingData;
    ClassificationData testData;

    if( !trainingData.loadDatasetFromFile("../-test/1-1-ext.txt") ){
        BOOST_LOG_TRIVIAL(error) <<"Failed to load training data!\n";
    }

    //--------------------- Init Classifiers ------------------------//

    ANBC anbc;
    anbc.setNullRejectionCoeff(3);
    anbc.enableScaling(true);
    anbc.enableNullRejection(true);

    //--------------------- Init and Train Pipeline -----------------------//

    GestureRecognitionPipeline pipeline;
    pipeline.setClassifier(anbc);

    if( !pipeline.train( trainingData ) ){
        BOOST_LOG_TRIVIAL(error) << "Failed to train classifier!\n";
    }

    trainingData.enableExternalRangeScaling(true);

    //--------------------- Test -----------------------//
    vector< MinMax > ranges = trainingData.getClassData(1).getRanges();

    cout << "The ranges of the dataset are: \n";
    for(UINT j=0; j<ranges.size(); j++){
        cout << "Dimension: " << j << " Min: " << ranges[j].minValue << " Max: " << ranges[j].maxValue << endl;
    }

//    vector< MinMax > externalRange;
//    MinMax d1(-1000,1000);
//    MinMax d2(-1000,1000);
//    MinMax d3(-1000,1000);
//    MinMax d4(-1000,1000);
//    MinMax d5(-1000,1000);
//    MinMax d6(-1000,1000);
//
//    externalRange.push_back(d1);
//    externalRange.push_back(d2);
//    externalRange.push_back(d3);
//    externalRange.push_back(d4);
//    externalRange.push_back(d5);
//    externalRange.push_back(d6);
//
//    trainingData.setExternalRanges(externalRange);
//    trainingData.enableExternalRangeScaling(true);
//
//    trainingData.saveDatasetToFile("extRange.txt");

}

void test::savePipeline(){
    GestureRecognitionPipeline pipeline;
    ANBC anbc;
    ClassificationData trainingData;
    TrainingDataRecordingTimer trainingTimer;

    anbc.setNullRejectionCoeff(1);
    anbc.enableScaling(true);
    anbc.enableNullRejection(true);

    pipeline.setClassifier(anbc);
    pipeline.addPostProcessingModule(ClassLabelFilter(30,60));
    pipeline.addPostProcessingModule(ClassLabelChangeFilter());

    trainingData.setNumDimensions(6);
    trainingData.setDatasetName("both-hand-static-gesture");
    trainingData.setInfoText("Gesture Recognition For Human-Robot Interaction by Aravinth Panchadcharam <me@aravinth.info>");

    pipeline.savePipelineToFile("pipeline.txt");

}


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

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

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

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

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CLOSEST_FEATURE_POINT_TO_RANGE_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CLOSEST_FEATURE_POINT_TO_RANGE_HPP

#include <utility>

#include <boost/range.hpp>

#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/core/closure.hpp>
#include <boost/geometry/strategies/distance.hpp>
#include <boost/geometry/util/math.hpp>

namespace geofeatures_boost {} namespace boost = geofeatures_boost; namespace geofeatures_boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace closest_feature
{

// returns the segment (pair of iterators) that realizes the closest
// distance of the point to the range
template
<
    typename Point,
    typename Range,
    closure_selector Closure,
    typename Strategy
>
class point_to_point_range
{
protected:
    typedef typename geofeatures_boost::range_iterator<Range const>::type iterator_type;

    template <typename Distance>
    static inline void apply(Point const& point,
                             iterator_type first,
                             iterator_type last,
                             Strategy const& strategy,
                             iterator_type& it_min1,
                             iterator_type& it_min2,
                             Distance& dist_min)
    {
        BOOST_GEOMETRY_ASSERT( first != last );

        Distance const zero = Distance(0);

        iterator_type it = first;
        iterator_type prev = it++;
        if (it == last)
        {
            it_min1 = it_min2 = first;
            dist_min = strategy.apply(point, *first, *first);
            return;
        }

        // start with first segment distance
        dist_min = strategy.apply(point, *prev, *it);
        iterator_type prev_min_dist = prev;

        // check if other segments are closer
        for (++prev, ++it; it != last; ++prev, ++it)
        {
            Distance dist = strategy.apply(point, *prev, *it);
            if (geometry::math::equals(dist, zero))
            {
                dist_min = zero;
                it_min1 = prev;
                it_min2 = it;
                return;
            }
            else if (dist < dist_min)
            {
                dist_min = dist;
                prev_min_dist = prev;
            }
        }

        it_min1 = it_min2 = prev_min_dist;
        ++it_min2;
    }

public:
    typedef typename std::pair<iterator_type, iterator_type> return_type;

    template <typename Distance>
    static inline return_type apply(Point const& point,
                                    iterator_type first,
                                    iterator_type last,
                                    Strategy const& strategy,
                                    Distance& dist_min)
    {
        iterator_type it_min1, it_min2;
        apply(point, first, last, strategy, it_min1, it_min2, dist_min);

        return std::make_pair(it_min1, it_min2);
    }

    static inline return_type apply(Point const& point,
                                    iterator_type first,
                                    iterator_type last,
                                    Strategy const& strategy)
    {
        typename strategy::distance::services::return_type
            <
                Strategy,
                Point,
                typename geofeatures_boost::range_value<Range>::type
            >::type dist_min;

        return apply(point, first, last, strategy, dist_min);
    }

    template <typename Distance>
    static inline return_type apply(Point const& point,
                                    Range const& range,
                                    Strategy const& strategy,
                                    Distance& dist_min)
    {
        return apply(point,
                     geofeatures_boost::begin(range),
                     geofeatures_boost::end(range),
                     strategy,
                     dist_min);
    }

    static inline return_type apply(Point const& point,
                                    Range const& range,
                                    Strategy const& strategy)
    {
        return apply(point, geofeatures_boost::begin(range), geofeatures_boost::end(range), strategy);
    }
};

// specialization for open ranges
template <typename Point, typename Range, typename Strategy>
class point_to_point_range<Point, Range, open, Strategy>
    : point_to_point_range<Point, Range, closed, Strategy>
{
private:
    typedef point_to_point_range<Point, Range, closed, Strategy> base_type;
    typedef typename base_type::iterator_type iterator_type;

    template <typename Distance>
    static inline void apply(Point const& point,
                             iterator_type first,
                             iterator_type last,
                             Strategy const& strategy,
                             iterator_type& it_min1,
                             iterator_type& it_min2,
                             Distance& dist_min)
    {
        BOOST_GEOMETRY_ASSERT( first != last );

        base_type::apply(point, first, last, strategy,
                         it_min1, it_min2, dist_min);

        iterator_type it_back = --last;
        Distance const zero = Distance(0);
        Distance dist = strategy.apply(point, *it_back, *first);

        if (geometry::math::equals(dist, zero))
        {
            dist_min = zero;
            it_min1 = it_back;
            it_min2 = first;
        }
        else if (dist < dist_min)
        {
            dist_min = dist;
            it_min1 = it_back;
            it_min2 = first;
        }
    }

public:
    typedef typename std::pair<iterator_type, iterator_type> return_type;

    template <typename Distance>
    static inline return_type apply(Point const& point,
                                    iterator_type first,
                                    iterator_type last,
                                    Strategy const& strategy,
                                    Distance& dist_min)
    {
        iterator_type it_min1, it_min2;

        apply(point, first, last, strategy, it_min1, it_min2, dist_min);

        return std::make_pair(it_min1, it_min2);
    }

    static inline return_type apply(Point const& point,
                                    iterator_type first,
                                    iterator_type last,
                                    Strategy const& strategy)
    {
        typedef typename strategy::distance::services::return_type
            <
                Strategy,
                Point,
                typename geofeatures_boost::range_value<Range>::type
            >::type distance_return_type;

        distance_return_type dist_min;

        return apply(point, first, last, strategy, dist_min);
    }

    template <typename Distance>
    static inline return_type apply(Point const& point,
                                    Range const& range,
                                    Strategy const& strategy,
                                    Distance& dist_min)
    {
        return apply(point,
                     geofeatures_boost::begin(range),
                     geofeatures_boost::end(range),
                     strategy,
                     dist_min);
    }

    static inline return_type apply(Point const& point,
                                    Range const& range,
                                    Strategy const& strategy)
    {
        return apply(point, geofeatures_boost::begin(range), geofeatures_boost::end(range), strategy);
    }
};

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

}} // namespace geofeatures_boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CLOSEST_FEATURE_POINT_TO_RANGE_HPP


/*
  Author(s):      Robert I A Patterson
  Project:        sweepc (population balance solver)
  Sourceforge:    http://sourceforge.net/projects/mopssuite

  Copyright (C) 2011 Robert I A Patterson.

  File purpose:
    Implementation of constant additive coagulation kernel
	for the hybrid particle-number and particle model

  Licence:
    This file is part of "sweepc".

    sweepc is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

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

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

  Contact:
    Dr Markus Kraft
    Dept of Chemical Engineering
    University of Cambridge
    New Museums Site
    Pembroke Street
    Cambridge
    CB2 3RA
    UK

    Email:       mk306@cam.ac.uk
    Website:     http://como.cheng.cam.ac.uk
*/

#include "swp_hybrid_constcoag.h"

#include "swp_params.h"
#include "swp_cell.h"
#include "swp_mechanism.h"
#include <boost/random/uniform_01.hpp>

using namespace Sweep::Processes;

const double Sweep::Processes::HybridConstantCoagulation::s_MajorantFactor = 1.5;

/**
 * Main way of building a new coagulation object
 * @param[in] mech      Mechanism to which coagulation will belong
 *
 */
Sweep::Processes::HybridConstantCoagulation::HybridConstantCoagulation(const Sweep::Mechanism &mech)
: Coagulation(mech)
{
    m_name = "ConstantCoagulation";
}

// Stream-reading constructor.
Sweep::Processes::HybridConstantCoagulation::HybridConstantCoagulation(std::istream &in, const Sweep::Mechanism &mech)
: Coagulation(mech)
{
    m_name = "ConstantCoagulation";
    Deserialize(in, mech);
}

// Returns the rate of the process for the given system.
double Sweep::Processes::HybridConstantCoagulation::Rate(double t, const Cell &sys,
                                                        const Geometry::LocalGeometry1d &local_geom) const
{
    // Get the number of particles in the system.
    unsigned int n = sys.ParticleCount() + sys.Particles().GetTotalParticleNumber();

    return A() * n * (n - 1) * s_MajorantFactor / sys.SampleVolume() / 2;
}

/**
 * Number of terms in the expression for the sum of the majorant
 * kernel over all particle pairs.
 */
unsigned int Sweep::Processes::HybridConstantCoagulation::TermCount() const { return TYPE_COUNT; }

/**
 * Calculate the terms in the sum of the majorant kernel over all particle
 * pairs, placing each term in successive positions of the sequence
 * beginning at iterm and return the sum of the terms added to that
 * vector.
 *
 * @param[in]     t            Time for which rates are requested
 * @param[in]     sys          Details of the particle population and environment
 * @param[in]     local_geom   Position information
 * @param[in,out] iterm        Pointer to start of sequence to hold the rate terms, returned as one past the end.
 */
double Sweep::Processes::HybridConstantCoagulation::RateTerms(double t, const Cell &sys,
                                                             const Geometry::LocalGeometry1d &local_geom,
                                                             fvector::iterator &iterm) const
{
    return *(iterm++) = Rate(t, sys, local_geom);
}

/*!
 *
 *
 * \param[in]       t           Time
 * \param[in,out]   sys         System to update
 * \param[in]       local_geom  Details of local phsyical layout
 * \param[in]       iterm       Process term responsible for this event
 * \param[in,out]   rng         Random number generator
 *
 * \return      0 on success, otherwise negative.
 */
int HybridConstantCoagulation::Perform(double t, Sweep::Cell &sys,
                             const Geometry::LocalGeometry1d& local_geom,
                             unsigned int iterm,
                             Sweep::rng_type &rng) const
{
	PartPtrVector dummy;
    // Select properties by which to choose particles.
    // Note we need to choose 2 particles.  One particle must be chosen
    // uniformly and one with probability proportional
    // to particle mass.

    int ip1 = -1, ip2 = -1;
    unsigned int index1 = 0, index2 = 0;

    // Hybrid particle model flags
    bool hybrid_flag = m_mech->IsHybrid();
    bool ip1_flag = false;
    bool ip2_flag = false;
    bool coag_in_place = false;

    double n_incep = sys.Particles().GetTotalParticleNumber();
    double n_other = sys.ParticleCount();
    double n_total = n_incep + n_other;

    boost::uniform_01<rng_type&, double> unifDistrib(rng);
    double alpha1 = unifDistrib() * n_total;
    double alpha2 = unifDistrib() * n_total;
    if (n_total <= 0)
        return -1;

    if (n_total < 2) // if there are < 2 SPs but incepting class has weight >= 2, we can still act
        return 1;
    else
    {
        if (hybrid_flag)
        {
            // Particle 1 is picked uniformly. Here, the
            // incepting class has multiple weight 1 particles
            // Account for this by selecting this by default and
            // switching with probability n_other/n_total
            ip1 = -2;
            ip2 = -2;
            if (n_other >= alpha1)
            {
                ip1 = sys.Particles().Select_usingGivenRand(iUniform, alpha1, rng);
            }
        }
        else
        {
            ip1 = sys.Particles().Select(rng);
            ip2 = sys.Particles().Select(rng);
        }
    }

    // Choose and get first particle, then update it.
    Particle *sp1 = NULL;
    double dsp1 = 0.0;
    bool must_switch = false;

    // Is this an incepting class particle?
    if (hybrid_flag && ip1 == -2)
    {
        // Note don't need to add it to the ensemble unless coagulation is successful
        index1 = m_mech->SetRandomParticle(sys.Particles(), t, alpha1 - n_other, iUniform, rng);
		if (index1 == 0)
			return -1;
        sp1 = sys.Particles().GetPNParticleAt(index1)->Clone();
        sp1->SetTime(t);
        ip1_flag = true;                                                                // Flag sp1 as an incepting class particle
    }
    else
    {
        if (ip1 >= 0) {
            sp1 = sys.Particles().At(ip1);
        }
        else {
            // Failed to choose a particle.
            return -1;
        }
    }

    // Choose and get unique second particle, then update it.  Note, we are allowed to do
    // this even if the first particle was invalidated.
    unsigned int guard = 0;
    if (!hybrid_flag)
    {
        while ((ip2 == ip1) && (++guard < 1000))
        {
            ip2 = sys.Particles().Select(rng);
        }
    }
    else
    {
        ip2 = ip1;
        bool unsuitableChoice = true;
        unsigned int n_index1 = sys.Particles().NumberAtIndex(index1);
        while (unsuitableChoice && (++guard < 1000))
        {
            alpha2 = unifDistrib() * n_total;
            if (alpha2 <= n_incep)
            {
                index2 = m_mech->SetRandomParticle(sys.Particles(), t, alpha2, iUniform, rng);
				if (index2 == 0)
				{
					if (ip1_flag)
					{
						delete sp1;
						sp1 = NULL;
					}
					return -1;
				}
                if (!((index2 == index1) && (n_index1 == 1)))
                {
                    unsuitableChoice = false;
                    ip2 = -2;
                }
            }
            else
            {
                ip2 = sys.Particles().Select_usingGivenRand(iUniform, alpha2 - n_incep, rng);
                if (!(ip2 == ip1))
                    unsuitableChoice = false;
            }
        }
    }

    // Choose and get second particle, then update it.
    Particle *sp2 = NULL;
    double dsp2 = 0.0;

    // Is this an incepting class particle?
    if (hybrid_flag && ip2 == -2)
    {
        // Note don't need to add it to the ensemble unless coagulation is successful
        sp2 = sys.Particles().GetPNParticleAt(index2)->Clone();
        sp2->SetTime(t);
        ip2_flag = true;                                                             // Flag sp2 as an incepting class particle
    }
    else
    {
        if ((ip2 >= 0) && (ip2 != ip1)) {
            sp2 = sys.Particles().At(ip2);
        }
        else {
            // Failed to select a unique particle.
            return -1;
        }
    }

    //Calculate the majorant rate before updating the particles
    const double majk = MajorantKernel(*sp1, *sp2, sys, Default);

    //Update the particles
    if (t > sp1->LastUpdateTime())
        m_mech->UpdateParticle(*sp1, sys, t, ip1, rng, dummy);

    // Check that particle is still valid.  If not,
    // remove it and cease coagulating.
    if (!sp1->IsValid()) {
        if (!ip1_flag)
        {
            // Must remove first particle now.
            sys.Particles().Remove(ip1);
        }
        else
        {
            // Particle sp1 is not in the ensemble, must manually delete it
            delete sp1;
            sp1 = NULL;
        }
        // Invalidating the index tells this routine not to perform coagulation.
        ip1 = -1;
        return 0;
    }

    if (t > sp2->LastUpdateTime())
        m_mech->UpdateParticle(*sp2, sys, t, ip2, rng, dummy);

    // Check validity of particles after update.
    if (!sp2->IsValid()) {
        // Tell the ensemble to update particle one before we confuse things
        // by removing particle 2
        if (!ip1_flag)
            sys.Particles().Update(ip1);

        if (!ip2_flag)
        {
            // Must remove second particle now.
            sys.Particles().Remove(ip2);
        }
        else
        {
            // Particle sp2 is not in the ensemble, must manually delete it
            delete sp2;
            sp2 = NULL;
        }

        // Invalidating the index tells this routine not to perform coagulation.
        ip2 = -1;

        return 0;
    }

    // Check that both the particles are still valid.
    if ((ip1 != -1) && (ip2 != -1)) {
        // Must check for ficticious event now by comparing the original
        // majorant rate and the current (after updates) true rate.

        double truek = CoagKernel(*sp1, *sp2, sys);

        if (!Fictitious(majk, truek, rng)) {
            if (ip1_flag)
            {
                sys.Particles().UpdateTotalsWithIndex(index1, -1.0);
                sys.Particles().UpdateNumberAtIndex(index1, -1);
                sys.Particles().UpdateTotalParticleNumber(-1);
                unsigned int index12 = index1 + index2;
                // Allow for coagulation in place if the combined particle is small enough
				if ((m_mech->CoagulateInList()) && ip2_flag && (index12 < sys.Particles().GetHybridThreshold()))
                {
                    coag_in_place = true;
                    sys.Particles().UpdateTotalsWithIndex(index12, 1.0);
                    sys.Particles().UpdateNumberAtIndex(index12, 1);
                    sys.Particles().UpdateTotalParticleNumber(1);
                    if (sp1 != NULL)
                    {
                        delete sp1;
                        sp1 = NULL;
                    }
                }
                else
                {
                    // otherwise add the particle to the ensemble
                    ip1 = sys.Particles().Add(*sp1, rng, ip2, true);
                }
            }
            if (ip2_flag)
            {
                sys.Particles().UpdateTotalsWithIndex(index2, -1.0);
                sys.Particles().UpdateNumberAtIndex(index2, -1);
                sys.Particles().UpdateTotalParticleNumber(-1);
            }
            if (!coag_in_place)
                JoinParticles(t, ip1, sp1, ip2, sp2, sys, rng);
            if (ip2_flag && sp2 != NULL)
            {
                delete sp2;
                sp2 = NULL;
            }
        } else {
            if (!ip1_flag)
                sys.Particles().Update(ip1);
            else if (sp1 != NULL)
            {
                delete sp1;
                sp1 = NULL;
            }
            if (!ip2_flag)
                sys.Particles().Update(ip2);
            else if (sp2 != NULL)
            {
                delete sp2;
                sp2 = NULL;
            }
            return 1; // Ficticious event.
        }
    } else {
        // One or both particles were invalidated on update,
        // but that's not a problem.  Information on the update
        // of valid particles must be propagated into the binary
        // tree
        if (ip1 != -1)
        {
            if (!ip1_flag)
                sys.Particles().Update(ip1);
        }
        if (ip2 != -1 && !ip2_flag)
            sys.Particles().Update(ip2);

        if (ip1_flag && sp1 != NULL)
        {
            delete sp1;
            sp1 = NULL;
        }
        if (ip2_flag && sp2 != NULL)
        {
            delete sp2;
            sp2 = NULL;
        }
    }

    if (ip1_flag && sp1 != NULL && ip1 == -2)
    {
        delete sp1;
        sp1 = NULL;
    }

    if (ip2_flag && sp2 != NULL)
    {
        delete sp2;
        sp2 = NULL;
    }

    return 0;
}

/**
 * Calculate the coagulation kernel between two particles in a given environment.
 * Note that the details of the environment are not currently used.
 *
 *@param[in]    sp1         First particle
 *@param[in]    sp2         Second particle
 *@param[in]    sys         Details of the environment
 *
 *@return       Value of kernel
 */
double Sweep::Processes::HybridConstantCoagulation::CoagKernel(const Particle &sp1,
                                                              const Particle &sp2,
                                                              const Cell& sys) const
{
    return A();
}

/**
 * Calculate the majorant kernel between two particles in a given environment.
 * Note that the details of the environment are not currently used.
 *
 *@param[in]    sp1         First particle
 *@param[in]    sp2         Second particle
 *@param[in]    sys         Details of the environment
 *@param[in]    maj         Unused flag to indicate which majorant kernel is required
 *
 *@return       Value of majorant kernel
 */
double Sweep::Processes::HybridConstantCoagulation::MajorantKernel(const Particle &sp1,
                                                                  const Particle &sp2,
                                                                  const Cell& sys,
                                                                  const MajorantType maj) const
{
    return CoagKernel(sp1, sp2, sys) * s_MajorantFactor;
}


#include "wordnet.h"

#include <random>
#include <iterator>
#include <boost/graph/random.hpp>

template<typename Iter, typename RandomGenerator>
Iter select_randomly(Iter start, Iter end, RandomGenerator& g) {
    std::uniform_int_distribution<> dis(0, std::distance(start, end) - 1);
    std::advance(start, dis(g));
    return start;
}

template<typename Iter>
Iter select_randomly(Iter start, Iter end) {
    static std::random_device rd;
    static std::mt19937 gen(rd());
    return select_randomly(start, end, gen);
}

std::string random_q(const wnb::wordnet& wn, std::map<std::string, std::string>& q, int n_options){
    static std::random_device rd;
    static std::mt19937 gen(rd());
    for (int i = 0; i<n_options; ++i)
    {
        auto item = boost::random_vertex(wn.wordnet_graph(), gen);
        auto synset = wn.wordnet_graph()[item];
        auto lemma = *select_randomly(synset.words.begin(), synset.words.end());
        if (q.find(lemma) == q.end()) {
            auto found_example = synset.gloss.find(";");
            q[lemma] = found_example != std::string::npos ? synset.gloss.substr(0, found_example) : synset.gloss;
        }
        else {
            --i;
        }
    }

    auto chosen = *select_randomly(q.begin(), q.end());
    return chosen.first;
}


// Copyright (c) 2015-2020 Daniel Cooke
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

#include "median_base_quality.hpp"

#include <algorithm>
#include <iterator>

#include <boost/variant.hpp>
#include <boost/optional.hpp>

#include "core/tools/read_assigner.hpp"
#include "core/types/allele.hpp"
#include "io/variant/vcf_record.hpp"
#include "io/variant/vcf_spec.hpp"
#include "utils/maths.hpp"
#include "utils/append.hpp"
#include "../facets/samples.hpp"
#include "../facets/alleles.hpp"
#include "../facets/read_assignments.hpp"

namespace octopus { namespace csr {

const std::string MedianBaseQuality::name_ = "BQ";

std::unique_ptr<Measure> MedianBaseQuality::do_clone() const
{
    return std::make_unique<MedianBaseQuality>(*this);
}

Measure::ValueType MedianBaseQuality::get_value_type() const
{
    return int {};
}

namespace {

bool is_canonical(const VcfRecord::NucleotideSequence& allele) noexcept
{
    return !(allele == vcfspec::missingValue || allele == vcfspec::deleteMaskAllele);
}

bool has_called_alt_allele(const VcfRecord& call, const VcfRecord::SampleName& sample)
{
    if (!call.has_genotypes()) return true;
    const auto& genotype = get_genotype(call, sample);
    return std::any_of(std::cbegin(genotype), std::cend(genotype),
                       [&] (const auto& allele) { return allele != call.ref() && is_canonical(allele); });
}

bool is_evaluable(const VcfRecord& call, const VcfRecord::SampleName& sample)
{
    return has_called_alt_allele(call, sample);
}

auto median_base_quality(const ReadRefSupportSet& reads, const Allele& allele)
{
    Measure::Optional<Measure::ValueType> result {};
    if (!is_indel(allele)) {
        std::vector<AlignedRead::BaseQuality> base_qualities {};
        base_qualities.reserve(reads.size() * sequence_size(allele));
        for (const auto& read : reads) {
            if (overlaps(read.get(), allele)) {
                utils::append(copy_base_qualities(read, mapped_region(allele)), base_qualities);
            }
        }
        if (!base_qualities.empty()) {
            result = static_cast<int>(maths::median(base_qualities));
        }
    }
    return result;
}

} // namespace

Measure::ResultType MedianBaseQuality::do_evaluate(const VcfRecord& call, const FacetMap& facets) const
{
    const auto& samples = get_value<Samples>(facets.at("Samples"));
    const auto& alleles = get_value<Alleles>(facets.at("Alleles"));
    const auto& assignments = get_value<ReadAssignments>(facets.at("ReadAssignments")).alleles;
    const auto num_alleles = call.num_alt() + 1;
    Array<Array<Optional<ValueType>>> result(samples.size(), Array<Optional<ValueType>>(num_alleles));
    for (std::size_t s {0}; s < samples.size(); ++s) {
        const auto& sample_alleles = get(alleles, call, samples[s]);
        assert(sample_alleles.size() == num_alleles);
        const auto& support = assignments.at(samples[s]);
        for (std::size_t a {0}; a < num_alleles; ++a) {
            if (sample_alleles[a]) {
                const auto& allele = *sample_alleles[a];
                const auto support_set_itr = support.find(allele);
                if (support_set_itr != std::cend(support)) {
                    result[s][a] = median_base_quality(support_set_itr->second, allele);
                }
            }
        }
    }
    return result;
}

Measure::ResultCardinality MedianBaseQuality::do_cardinality() const noexcept
{
    return ResultCardinality::samples_and_alleles;
}

const std::string& MedianBaseQuality::do_name() const
{
    return name_;
}

std::string MedianBaseQuality::do_describe() const
{
    return "Median base quality of reads supporting each allele";
}

std::vector<std::string> MedianBaseQuality::do_requirements() const
{
    return {"Samples", "Alleles", "ReadAssignments"};
}

boost::optional<Measure::Aggregator> MedianBaseQuality::do_aggregator() const noexcept
{
    return Measure::Aggregator::min_tail;
}

} // namespace csr
} // namespace octopus


#define BOOST_TEST_MODULE AggregateData AggregateDataTest
#define BOOST_TEST_DYN_LINK
#define GOOGLE_STRIP_LOG 4
#include "../src/AggregateData.h"
#include "../src/Statistic.h"
#include <boost/test/unit_test.hpp>
#include <iostream>
#include <list>

BOOST_AUTO_TEST_CASE( findTime )
{
    AggregateData aggData;
    aggData.loadCurrentCostData( "data/input/current_cost/10July.csv" );

    BOOST_CHECK_EQUAL( aggData.findTime(1310252400), 0 );
    BOOST_CHECK_EQUAL( aggData.findTime(1310255190), 427);
    BOOST_CHECK_EQUAL( aggData.findTime(1310338795), 12988);
}

BOOST_AUTO_TEST_CASE( findSpike )
{
    AggregateData aggData;
    aggData.loadCurrentCostData( "data/input/current_cost/10July.csv" );

    std::list<AggregateData::FoundSpike> foundSpikes = aggData.findSpike(Statistic<Sample_t>(245));

/*    for (std::list<AggregateData::FoundSpike>::iterator spike = foundSpikes.begin();
            spike != foundSpikes.end();
            spike++) {
        std::cout << *spike << std::endl;
    }
*/

    BOOST_CHECK_EQUAL( foundSpikes.front().timestamp , 1310253570);

    std::cout << "done" << std::endl;
}


//	Copyright (c) 2013, Andre Gaschler, Quirin Fischer
//	All rights reserved.
//
//	Redistribution and use in source and binary forms, with or without
// modification,
//	are permitted provided that the following conditions are met:
//
//	* Redistributions of source code must retain the above copyright notice,
// this
//	  list of conditions and the following disclaimer.
//
//	* Redistributions in binary form must reproduce the above copyright
// notice, this
//	  list of conditions and the following disclaimer in the documentation
// and/or
//	  other materials provided with the distribution.
//
//	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND
//	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED
//	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
//	DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR
//	ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES
//	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES;
//	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
// AND ON
//	ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
//	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS
//	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "Decimator.hh"

#include <cmath>
#include <limits>
#include <iostream>
#include <sstream>
#include <stdexcept>

#include "SimpleOptimizer.hh"
// for debugging
#include <Eigen/Eigenvalues>

namespace boundingmesh
{
Decimator::Decimator(DecimationDirection direction)
	: direction_(direction), optimizer_(new SimpleOptimizer)
{}

Decimator::Decimator(std::unique_ptr<OptimizerInterface> optimizer,
	DecimationDirection direction)
	: target_vertices_(default_target_vertices),
	maximum_error_(default_maximum_error),
	direction_(direction),
	optimizer_(std::move(optimizer))
{}

Decimator::~Decimator()
{}

Real Decimator::currentError()
{
	return current_error_;
}

Real Decimator::nextError()
{
	if (queue_.size() == 0) return 0;
	const EdgeContraction& contraction = queue_.first();
	return contraction.cost();
}

void Decimator::setTargetVertices(int target_vertices)
{
	target_vertices_ = target_vertices;
	target_vertices_used_ = true;
}

void Decimator::unsetTargetVertices()
{
	target_vertices_used_ = false;
}

void Decimator::setMaximumError(Real maximum_error)
{
	maximum_error_ = maximum_error;
	maximum_error_used_ = true;
}

void Decimator::unsetMaximumError()
{
	maximum_error_used_ = false;
}

void Decimator::setDirection(DecimationDirection direction)
{
	direction_ = direction;

	if (result_mesh_ != NULL) recomputeQueue();
}

void Decimator::setMetric(Metric metric)
{
	metric_generator_.setMetric(metric);

	if (result_mesh_ != NULL) recomputeQueue();
}

void Decimator::setInitialization(Initialization initialization)
{
	metric_generator_.setInitialization(initialization);

	if (result_mesh_ != NULL) recomputeQueue();
}

void Decimator::setMesh(const TriMesh& mesh)
{
	result_mesh_ = std::make_shared<TriMesh>(mesh);
	metric_generator_.setMesh(result_mesh_);

	recomputeQueue();
}

::std::shared_ptr<TriMesh> Decimator::getMesh()
{
	return result_mesh_;
}

void Decimator::recomputeQueue()
{
	queue_ = ContractionQueue();
	for (unsigned int i = 0; i < result_mesh_->n_edges(); ++i)
		queue_.insert(computeEdgeContraction(result_mesh_->edge_handle(i)));
}

::std::shared_ptr<TriMesh> Decimator::compute(ComputeCallback callback)
{
	if (queue_.size() == 0) {
		std::cout << "Bad mesh, no edges in list" << std::endl;
		result_mesh_.reset(new TriMesh());
		return result_mesh_;
	}

	if (!target_vertices_used_ && !maximum_error_used_)
		return result_mesh_;

	// Greedy decimation, repeatedly contract best edge
	while ((queue_.size() > 0) &&
		(!target_vertices_used_ ||
			result_mesh_->n_vertices() > target_vertices_) &&
			(!maximum_error_used_ || queue_.first().cost() < maximum_error_)) {
		if (callback != NULL)
			callback(result_mesh_->n_vertices(), queue_.first().cost());
		const EdgeContraction contraction = queue_.first();
		executeEdgeContraction(contraction);
	}
	cleanAndRenumber();
	return result_mesh_;
}

::std::shared_ptr<TriMesh> Decimator::doContractions(unsigned int n)
{
	if (queue_.size() <= n) {
		std::cerr << "Warning: Cannot perform this many contractions" << std::endl;
		return ::std::make_shared<TriMesh>();
	}
	for (unsigned int i = 0; i < n; ++i) {
		const EdgeContraction& contraction = queue_.first();
		if (maximum_error_used_ && contraction.cost() > maximum_error_)
			break;
		executeEdgeContraction(contraction);
	}
	cleanAndRenumber();
	return result_mesh_;
}

void Decimator::executeEdgeContraction(const EdgeContraction& contraction)
{
	current_error_ = contraction.cost();

	TriMesh::HalfedgeHandle edge = result_mesh_->halfedge_handle(contraction.edge(), 0);
	metric_generator_.contractEdge(contraction.edge());
	auto v0 = result_mesh_->to_vertex_handle(edge);
	auto v1 = result_mesh_->from_vertex_handle(edge);

	// First, remove edge, vertices and surrounding triangles
	std::set<TriMesh::EdgeHandle> edges_to_remove;
	std::vector<TriMesh::VertexHandle*> hole_border;
	collectRemovalData(v0, v1, edges_to_remove, hole_border);
	collectRemovalData(v1, v0, edges_to_remove, hole_border);

	for (std::set<TriMesh::EdgeHandle>::iterator it = edges_to_remove.begin(); it != edges_to_remove.end(); ++it) {
		queue_.remove(*it);
	}

	result_mesh_->delete_vertex(v0);
	result_mesh_->delete_vertex(v1);

	// Then insert new vertex and connect it correctly
	TriMesh::VertexHandle new_vertex_index = result_mesh_->add_vertex(contraction.new_point());
	for (unsigned int i = 0; i < hole_border.size(); ++i) {
		result_mesh_->add_face(new_vertex_index, hole_border[i][0], hole_border[i][1]);
		delete[] hole_border[i];
	}

	//const TriMesh::Vertex& new_vertex = result_mesh_->vertex(new_vertex_index);
	std::set<TriMesh::EdgeHandle> edges_to_add;
	for (TriMesh::VertexFaceIter vf_it = result_mesh_->vf_iter(new_vertex_index); vf_it.is_valid(); ++vf_it) {
		for (TriMesh::FaceEdgeIter fe_it = result_mesh_->fe_iter(*vf_it); fe_it.is_valid(); ++fe_it) {
			edges_to_add.insert(fe_it);
		}
	}
	for (std::set<TriMesh::EdgeHandle>::iterator it = edges_to_add.begin(); it != edges_to_add.end(); ++it) {
		queue_.insert(computeEdgeContraction(*it));
	}
}

void Decimator::collectRemovalData(TriMesh::VertexHandle vertex_index,
	TriMesh::VertexHandle other_index,
	std::set<TriMesh::EdgeHandle>& edges_to_remove,
	std::vector<TriMesh::VertexHandle*>& hole_border)
{
	const TriMesh::Vertex& vertex = result_mesh_->vertex(vertex_index);
	// Collects data to remove a patch of triangles around a vertex.
	// Is called for both vertices of an edge, with vertex_index and other_index
	// swapped
	for (TriMesh::VertexFaceIter vf_it = result_mesh_->vf_iter(vertex_index); vf_it.is_valid(); ++vf_it) {
		const TriMesh::Face& triangle = result_mesh_->face(vf_it);
		bool is_shared_triangle = false;
		for (TriMesh::FaceEdgeIter fe_it = result_mesh_->fe_iter(*vf_it); fe_it.is_valid(); ++fe_it) {
			edges_to_remove.insert(fe_it);

			TriMesh::HalfedgeHandle edge = result_mesh_->halfedge_handle(fe_it, 0);
			auto v0 = result_mesh_->to_vertex_handle(edge);

			if (v0 == other_index)
				is_shared_triangle = true;
		}

		if (!is_shared_triangle) {

			auto vh = result_mesh_->fv_iter(*vf_it);
			auto v0 = *vh; vh++;
			auto v1 = *vh; vh++;
			auto v2 = *vh;

			TriMesh::VertexHandle* border_edge = new TriMesh::VertexHandle[2];
			if (v0 == vertex_index) {
				border_edge[0] = v1;
				border_edge[1] = v2;
			} else if (v1 == vertex_index) {
				border_edge[0] = v2;
				border_edge[1] = v0;
			} else if (v2 == vertex_index) {
				border_edge[0] = v0;
				border_edge[1] = v1;
			}
			hole_border.push_back(border_edge);
		}
	}
}

EdgeContraction Decimator::computeEdgeContraction(TriMesh::EdgeHandle edge_index)
{
	TriMesh::HalfedgeHandle hEdge = result_mesh_->halfedge_handle(edge_index, 0);
	auto v0 = result_mesh_->to_vertex_handle(hEdge);
	auto v1 = result_mesh_->from_vertex_handle(hEdge);

	const TriMesh::Vertex& vertex_1 = result_mesh_->vertex(v0);
	const TriMesh::Vertex& vertex_2 = result_mesh_->vertex(v1);

	Matrix44 qem = metric_generator_.getErrorMetric(edge_index);
	TriMesh::Point new_point = TriMesh::Point();

#ifndef NDEBUG
	// Debugging: error matrices shoud be positive semidefinite
	Eigen::SelfAdjointEigenSolver<Matrix44> es;
	es.compute(qem);
	if (es.eigenvalues()(0) < -epsilon || es.eigenvalues()(1) < -epsilon ||
		es.eigenvalues()(2) < -epsilon || es.eigenvalues()(3) < -epsilon) {
		std::cerr << "Warning: Bad metric eigenvalues: "
			<< es.eigenvalues().transpose() << std::endl
			<< "Metric:" << std::endl
			<< qem << std::endl;
	}
#endif

	// Detect edges with one vertex on a border edge
	int border_vertices = 0;
	for (TriMesh::VertexEdgeIter ve_it = result_mesh_->ve_iter(v0); ve_it.is_valid(); ++ve_it) {
		if (result_mesh_->is_boundary(*ve_it)) {
			new_point = result_mesh_->point(v0);
			border_vertices++;
			break;
		}
	}
	for (TriMesh::VertexEdgeIter ve_it = result_mesh_->ve_iter(v1); ve_it.is_valid(); ++ve_it) {
		if (result_mesh_->is_boundary(*ve_it)) {
			new_point = result_mesh_->point(v1);
			border_vertices++;
			break;
		}
	}

	if (border_vertices == 2 && result_mesh_->is_boundary(edge_index)) {
		/* Edge connects two borders, collapsing this would result in bad topology
		Current situation:
				___________(Border)
				...../|....
				..../.|....
				.../..|<--This edge
				...\..|....
				....\.|....
				_____\|____(Border)
		Would result in:
				___      __(Border)
				...\    /..
				....\  /...
				.....\/<--New Point
				...../\....
				..../  \...
				___/    \__(Border)
		*/
		return EdgeContraction(edge_index, TriMesh::Point(),
			std::numeric_limits<Real>::max(), Matrix44::Zero());
	}

	Vector3 tmp_vec3 = Vector3(new_point[0], new_point[1], new_point[2]);
	if (direction_ == Any) {
		if (border_vertices == 0 || border_vertices == 2) {
#ifndef NDEBUG
			Real unused_cost;
#endif
			assert(optimizer_->optimize(qem, {}, false, &tmp_vec3, &unused_cost));
			new_point = TriMesh::Point(tmp_vec3[0], tmp_vec3[1], tmp_vec3[2]);
		}
		// For border_vertices == 1 the new point is already found

		Vector4 new_point_homogeneous;
		new_point_homogeneous << tmp_vec3, 1;
		Real cost = new_point_homogeneous.transpose() * qem * new_point_homogeneous;
		return EdgeContraction(edge_index, new_point, cost, qem);
	}

	// collect constraint planes
	std::vector<Plane> constraints;
	for (TriMesh::VertexFaceIter vf_it = result_mesh_->vf_iter(v0); vf_it.is_valid(); ++vf_it) {
		auto vh = result_mesh_->fv_iter(*vf_it);
		auto tmp = result_mesh_->point(vh++);
		auto v0 = Vector3(tmp[0], tmp[1],tmp[2]);
		tmp = result_mesh_->point(vh++);
		auto v1 = Vector3(tmp[0], tmp[1], tmp[2]);
		tmp = result_mesh_->point(vh);
		auto v2 = Vector3(tmp[0], tmp[1], tmp[2]);
		Plane p = Plane(v0, v1, v2);
		constraints.push_back(p);
	}

	for (TriMesh::VertexFaceIter vf_it = result_mesh_->vf_iter(v1); vf_it.is_valid(); ++vf_it) {
		bool shared_triangle = false;
		for (TriMesh::FaceVertexIter fv_it = result_mesh_->fv_iter(vf_it); fv_it.is_valid(); ++fv_it) {
			if (*fv_it == v0) {
				shared_triangle = true;
				break;
			}
		}
		if (!shared_triangle) {
			auto vh = result_mesh_->fv_iter(*vf_it);
			auto tmp = result_mesh_->point(vh++);
			auto v0 = Vector3(tmp[0], tmp[1], tmp[2]);
			tmp = result_mesh_->point(vh++);
			auto v1 = Vector3(tmp[0], tmp[1], tmp[2]);
			tmp = result_mesh_->point(vh);
			auto v2 = Vector3(tmp[0], tmp[1], tmp[2]);
			Plane p = Plane(v0, v1, v2);
			constraints.push_back(p);
		}
	}

	// if one point is on the border, check if decimation is allowed
	if (border_vertices == 1) {
		bool valid_solution = true;
		Vector4 new_point_homogeneous;
		new_point_homogeneous << tmp_vec3, 1;
		for (unsigned int i = 0; i < constraints.size(); ++i) {
			if (direction_ == Outward &&
				constraints[i].vector().transpose() * new_point_homogeneous +
				epsilon <
				0) {
				valid_solution = false;
				break;
			} else if (direction_ == Inward &&
				constraints[i].vector().transpose() * new_point_homogeneous -
				epsilon >
				0) {
				valid_solution = false;
				break;
			}
		}
		if (valid_solution) {
			Real cost = new_point_homogeneous.transpose() * qem * new_point_homogeneous;
			return EdgeContraction(edge_index, new_point, cost, qem);
		} else
			return EdgeContraction(edge_index, TriMesh::Point(),
				std::numeric_limits<Real>::max(),
				Matrix44::Zero());
	}

	Real minimal_cost;
	bool found = optimizer_->optimize(
		qem, constraints, (direction_ == DecimationDirection::Outward),
		&tmp_vec3, &minimal_cost
	);
	new_point = TriMesh::Point(tmp_vec3[0], tmp_vec3[1], tmp_vec3[2]);

	if (found)
		return EdgeContraction(edge_index, new_point, minimal_cost, qem);
	else
		return EdgeContraction(edge_index, TriMesh::Point(),
			std::numeric_limits<Real>::max(), Matrix44::Zero());
}

void Decimator::cleanAndRenumber()
{
	ContractionQueue new_queue;
	int next_index = 0;
	for (std::set<ContractionIndex>::iterator it = queue_.indices_.begin(); it != queue_.indices_.end(); ++it) {
		/*
		The iteration over the queue_.indices_ set is ordered by index.
		Since the indices of deleted edges are missing, we can just put the position
		in the list as new index.
		This changes the edge indices just like Mesh::cleanAndRenumber
		Example:
		Set of indices: {1, 3, 5, 6, 7}
		Renumbered: 	{1, 2, 3, 4, 5}
		*/
		ContractionIterator contraction_it = it->contraction();
		new_queue.insert(EdgeContraction(
			TriMesh::EdgeHandle(next_index), contraction_it->new_point(),
			contraction_it->cost(),
			contraction_it->qem())
		);
		next_index++;
	}
	swap(queue_, new_queue);
	metric_generator_.cleanAndRenumber();
	result_mesh_->garbage_collection();
}

}  // namespace boundingmesh


#include "OccR.hpp"
#include "ContactPoint.hpp"
#include "OccContactShape.hpp"
#include "ContactShapeDistance.hpp"
#include "WhichGeometer.hpp"
#include "RuntimeException.hpp"
#include <limits>
#include <iostream>
#include <boost/typeof/typeof.hpp>

#undef DEBUG_MESSAGES
#include "debug.h"

OccR::OccR(const ContactPoint& contact1,
           const ContactPoint& contact2,
           const DistanceCalculatorType& distance_calculator) :
  NewtonEulerFrom3DLocalFrameR(),
  _contact1(contact1),
  _contact2(contact2),
  _geometer(),
  _normalFromFace1(false),
  _offsetp1(false),
  _offset(0.1)
{
  switch (Type::value(distance_calculator))
  {
  case Type::OccDistanceType:
    this->_geometer = ask<WhichGeometer<OccDistanceType> >(contact1.contactShape());
    break;
  case Type::CadmbtbDistanceType:
    this->_geometer = ask<WhichGeometer<CadmbtbDistanceType> >(contact1.contactShape());
    break;
  default:
    RuntimeException::selfThrow("OccR: Unknown distance calculator");
  }
  this->_contact2.contactShape().accept(*this->_geometer);
}

void OccR::computeh(double time, BlockVector& q0, SiconosVector& y)
{
  this->_contact2.contactShape().accept(*this->_geometer);

  ContactShapeDistance& dist = this->_geometer->answer;

  DEBUG_PRINTF("---->%g P1=(%g, %g, %g) P2=(%g,%g,%g) N=(%g, %g, %g)\n", dist.value,
               dist.x1, dist.y1, dist.z1,
               dist.x2, dist.y2, dist.z2,
               dist.nx, dist.ny, dist.nz);

  double& X1 = dist.x1;
  double& Y1 = dist.y1;
  double& Z1 = dist.z1;

  double& X2 = dist.x2;
  double& Y2 = dist.y2;
  double& Z2 = dist.z2;

  double& n1x = dist.nx;
  double& n1y = dist.ny;
  double& n1z = dist.nz;

  if(_offsetp1)
  {
    _Pc1->setValue(0, X1+_offset*n1x);
    _Pc1->setValue(1, Y1+_offset*n1y);
    _Pc1->setValue(2, Z1+_offset*n1z);
    _Pc2->setValue(0, X2);
    _Pc2->setValue(1, Y2);
    _Pc2->setValue(2, Z2);
  }
  else
  {
    _Pc1->setValue(0, X1);
    _Pc1->setValue(1, Y1);
    _Pc1->setValue(2, Z1);
    _Pc2->setValue(0, X2-_offset*n1x);
    _Pc2->setValue(1, Y2-_offset*n1y);
    _Pc2->setValue(2, Z2-_offset*n1z);
  }

  /* cf comments from O. Bonnefon */
  _Nc->setValue(0, n1x);
  _Nc->setValue(1, n1y);
  _Nc->setValue(2, n1z);

  dist.value -= _offset;

  y.setValue(0, dist.value);

}