//////////////////////////////////////////////////////////////////
// example93.cpp
//
// Copyright (c) 2015 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include <iostream>
#include <limits>
#include <boost/integer.hpp>

// include headers to support safe integers
#include "../include/cpp.hpp"
#include "../include/exception.hpp"
#include "../include/safe_integer.hpp"
#include "../include/safe_integer_range.hpp"
#include "../include/safe_integer_literal.hpp"

// use same type promotion as used by the pic compiler
// target compiler XC8 supports:
using pic16_promotion = boost::numeric::cpp<
    8,  // char      8 bits
    16, // short     16 bits
    16, // int       16 bits
    16, // long      16 bits
    32  // long long 32 bits
>;

// ***************************
// 1. Specify exception policies so we will generate a
// compile time error whenever an operation MIGHT fail.

// ***************************
// generate compile time errors if operation could fail
using trap_policy = boost::numeric::loose_trap_policy;
// generate runtime errors if operation could fail
using exception_policy = boost::numeric::default_exception_policy;

// ***************************
// 2. Create a macro named literal an integral value
// that can be evaluated at compile time.
#define literal(n) make_safe_literal(n, void, void)

// 1st step=10ms; max speed=300rpm (based on 1MHz timer, 1.8deg steps)
#define C_MIN  literal((2500 << 8))
#define C0    literal((50000 << 8))
static_assert(
    boost::numeric::base_value(C0) < 0xffffff,
    "Largest step too long"
);
static_assert(
    boost::numeric::base_value(C_MIN) > 0,
    "Smallest step must be greater than zero"
);
static_assert(
    boost::numeric::base_value(C_MIN) < boost::numeric::base_value(C0),
    "Smallest step must be smaller than largest step"
);

// ***************************
// 3. Create special ranged types for the motor program
// These wiil guarantee that values are in the expected
// ranges and permit compile time determination of when
// exceptional conditions might occur.

using pic_register_t = boost::numeric::safe<
    uint8_t,
    pic16_promotion,
    trap_policy // use for compiling and running tests
>;

// number of steps
using step_t = boost::numeric::safe<
    std::int32_t,
    pic16_promotion,
    exception_policy
>;

// next end of step timer value in format 24.8
// where the .8 is the number of bits in the fractional part.
using ccpr_t = boost::numeric::safe<
    uint32_t,
    pic16_promotion,
    exception_policy
>;

// pulse length in format 24.8
// note: this value is constrainted to be a positive value. But
// we still need to make it a signed type. We get an arithmetic
// error when moving to a negative step number.
using c_t = boost::numeric::safe_signed_range<
    C_MIN,
    C0,
    pic16_promotion,
    exception_policy
>;

// index into phase table
using phase_ix_t = boost::numeric::safe_signed_range<
    0,
    3,
    pic16_promotion,
    exception_policy
>;

// settings for control value output
using phase_t = boost::numeric::safe<
    uint16_t,
    pic16_promotion,
    trap_policy
>;

// direction of rotation
using direction_t = boost::numeric::safe_signed_range<
    -1,
    +1,
    pic16_promotion,
    exception_policy
>;

// some number of microseconds
using microseconds = boost::numeric::safe<
    uint32_t,
    pic16_promotion,
    trap_policy
>;

// ***************************
// emulate PIC features on the desktop

// suppress special keyword used by XC8 compiler
#define interrupt

// emulate PIC special registers
pic_register_t INTCON;
pic_register_t CCP1IE;
pic_register_t CCP2IE;
pic_register_t PORTC;
pic_register_t TRISC;
pic_register_t T3CON;
pic_register_t T1CON;

pic_register_t CCPR2H;
pic_register_t CCPR2L;
pic_register_t CCPR1H;
pic_register_t CCPR1L;
pic_register_t CCP1CON;
pic_register_t CCP2CON;
pic_register_t TMR1H;
pic_register_t TMR1L;

// ***************************
// special checked type for bits - values restricted to 0 or 1
using safe_bit_t = boost::numeric::safe_unsigned_range<
    0,
    1,
    pic16_promotion,
    trap_policy
>;

// create type used to map PIC bit names to
// correct bit in PIC register
template<typename T, std::int8_t N>
struct bit {
    T & m_word;
    constexpr explicit bit(T & rhs) :
        m_word(rhs)
    {}
    // special functions for assignment of literal
    constexpr bit & operator=(decltype(literal(1))){
        m_word |= literal(1 << N);
        return *this;
    }
    constexpr bit & operator=(decltype(literal(0))){
        m_word &= ~literal(1 << N);
        return *this;
    }
    // operator to convert to 0 or 1
    constexpr operator safe_bit_t () const {
        return m_word >> literal(N) & literal(1);
    }
};

// define bits for T1CON register
struct  {
    bit<pic_register_t, 7> RD16{T1CON};
    bit<pic_register_t, 5> T1CKPS1{T1CON};
    bit<pic_register_t, 4> T1CKPS0{T1CON};
    bit<pic_register_t, 3> T1OSCEN{T1CON};
    bit<pic_register_t, 2> T1SYNC{T1CON};
    bit<pic_register_t, 1> TMR1CS{T1CON};
    bit<pic_register_t, 0> TMR1ON{T1CON};
} T1CONbits;

#include "motor3.c"

#include <chrono>
#include <thread>

microseconds to_microseconds(ccpr_t t){
    return (t + literal(128)) / literal(256);
}

// move motor to the indicated target position in steps
void test(step_t m){
    std::cout << "move motor to " << m << '\n';
    motor_run(m);
    std::cout
    << "step #" << ' '
    << "delay(us)(24.8)" << ' '
    << "delay(us)" << ' '
    << "CCPR" << ' '
    << "motor position" << '\n';
    while(busy()){
        std::this_thread::sleep_for(std::chrono::microseconds(to_microseconds(c)));
        c_t last_c = c;
        ccpr_t last_ccpr = ccpr;
        isr_motor_step();
        std::cout << i << ' '
        << last_c << ' '
        << to_microseconds(last_c) << ' '
        << std::hex << last_ccpr << std::dec << ' '
        << motor_position << '\n';
    };
}

int main(){
    std::cout << "start test\n";
    try{
        initialize();
        // move to the left before zero position
        test(literal(-10));
        // move motor to position 200
        test(literal(200));
        // move motor to position 200 again! Should result in no movement.
        test(literal(200));
        // move motor to position 1000
        test(literal(1000));
        // move back to position 0
        test(literal(0));
    }
    catch(std::exception & e){
        std::cout << e.what() << '\n';
        return 1;
    }
    catch(...){
        std::cout << "test interrupted\n";
        return 1;
    }
    std::cout << "end test\n";
    return 0;
}


#include <puppet/compiler/evaluation/operators/binary/modulo.hpp>
#include <puppet/compiler/evaluation/operators/binary/call_context.hpp>
#include <puppet/compiler/exceptions.hpp>
#include <boost/format.hpp>

using namespace std;
using namespace puppet::runtime;

namespace puppet { namespace compiler { namespace evaluation { namespace operators { namespace binary {

    descriptor modulo::create_descriptor()
    {
        binary::descriptor descriptor{ ast::binary_operator::modulo };

        descriptor.add("Integer", "Integer", [](call_context& context) {
            auto left = context.left().require<int64_t>();
            auto right = context.right().require<int64_t>();
            if (right == 0) {
                throw evaluation_exception("cannot divide by zero.", context.right_context(), context.context().backtrace());
            }
            return left % right;
        });
        return descriptor;
    }

}}}}}  // namespace puppet::compiler::evaluation::operators::binary


#include <stan/math/rev/mat.hpp>
#include <gtest/gtest.h>
#include <test/unit/math/rev/mat/fun/util.hpp>
#include <test/unit/math/rev/mat/util.hpp>

// TODO(carpenter): move this to test framework;  should be able to put
// all this GPU config into the functor
// TODO(carpenter): safer cleanup on resetting tuning_opts

#ifdef STAN_OPENCL
#include <boost/random/mersenne_twister.hpp>

#define EXPECT_MATRIX_NEAR(A, B, DELTA) \
  for (int i = 0; i < A.size(); i++)    \
    EXPECT_NEAR(stan::math::value_of(A(i)), stan::math::value_of(B(i)), DELTA);

boost::random::mt19937 rng;
#define MDIVIDE_OPENCL_OVERRIDE 0
#define MDIVIDE_CPU_OVERRIDE INT_MAX
TEST(AgradRevMatrix, mdivide_left_tri_val_cl) {
  int temp = stan::math::opencl_context.tuning_opts()
                 .tri_inverse_size_worth_transfer;
  using stan::math::matrix_d;
  using stan::math::matrix_v;
  using stan::math::mdivide_left_tri;
  int size = 111;
  matrix_v Av(size, size);
  matrix_d Ad(size, size);
  matrix_v I, I_cl;
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < i; j++) {
      Ad(i, j) = stan::math::uniform_rng(-5, 5, rng);
      Av(i, j) = Ad(i, j);
    }
    Ad(i, i) = 20.0;
    Av(i, i) = Ad(i, i);
    for (int j = i + 1; j < size; j++) {
      Ad(i, j) = 0.0;
      Av(i, j) = Ad(i, j);
    }
  }
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_OPENCL_OVERRIDE;
  I_cl = mdivide_left_tri<Eigen::Lower>(Av, Av);
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_CPU_OVERRIDE;
  I = mdivide_left_tri<Eigen::Lower>(Av, Av);
  EXPECT_MATRIX_NEAR(I, I_cl, 1.0E-12);

  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_OPENCL_OVERRIDE;
  I_cl = mdivide_left_tri<Eigen::Lower>(Av, Ad);
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_CPU_OVERRIDE;
  I = mdivide_left_tri<Eigen::Lower>(Av, Ad);
  EXPECT_MATRIX_NEAR(I, I_cl, 1.0E-12);

  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_OPENCL_OVERRIDE;
  I_cl = mdivide_left_tri<Eigen::Lower>(Ad, Av);
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_CPU_OVERRIDE;
  I = mdivide_left_tri<Eigen::Lower>(Ad, Av);
  EXPECT_MATRIX_NEAR(I, I_cl, 1.0E-12);

  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_OPENCL_OVERRIDE;
  matrix_v Av_inv_cl = mdivide_left_tri<Eigen::Lower>(Av);
  I_cl = Av * Av_inv_cl;
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_CPU_OVERRIDE;
  matrix_v Av_inv = mdivide_left_tri<Eigen::Lower>(Av);
  I = Av * Av_inv;
  EXPECT_MATRIX_NEAR(I, I_cl, 1.0E-12);

  for (int i = 0; i < size; i++) {
    for (int j = 0; j < i; j++) {
      Ad(i, j) = 0.0;
      Av(i, j) = Ad(i, j);
    }
    Ad(i, i) = 20.0;
    Av(i, i) = Ad(i, i);
    for (int j = i + 1; j < size; j++) {
      Ad(i, j) = stan::math::uniform_rng(-5, 5, rng);
      Av(i, j) = Ad(i, j);
    }
  }

  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_OPENCL_OVERRIDE;
  I_cl = mdivide_left_tri<Eigen::Upper>(Av, Av);
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_CPU_OVERRIDE;
  I = mdivide_left_tri<Eigen::Upper>(Av, Av);
  EXPECT_MATRIX_NEAR(I, I_cl, 1.0E-12);

  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_OPENCL_OVERRIDE;
  I_cl = mdivide_left_tri<Eigen::Upper>(Av, Ad);
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_CPU_OVERRIDE;
  I = mdivide_left_tri<Eigen::Upper>(Av, Ad);
  EXPECT_MATRIX_NEAR(I, I_cl, 1.0E-12);

  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_OPENCL_OVERRIDE;
  I_cl = mdivide_left_tri<Eigen::Upper>(Ad, Av);
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_CPU_OVERRIDE;
  I = mdivide_left_tri<Eigen::Upper>(Ad, Av);
  EXPECT_MATRIX_NEAR(I, I_cl, 1.0E-12);

  // restore default value
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = temp;
}

TEST(AgradRevMatrix, mdivide_left_tri_lower_grad_vv_cl) {
  int temp = stan::math::opencl_context.tuning_opts()
                 .tri_inverse_size_worth_transfer;

  using stan::math::matrix_d;
  using stan::math::matrix_v;
  using stan::math::mdivide_left_tri;
  using stan::math::multiply;
  int size = 256;

  matrix_v Av(size, size);
  matrix_v Bv(size, size);
  matrix_v Cv(size, size);
  matrix_v Cv_cl(size, size);
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < i; j++) {
      Av(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
    Av(i, i) = 20.0;
    for (int j = i + 1; j < size; j++) {
      Av(i, j) = 0.0;
    }
    for (int j = 0; j < size; j++) {
      Bv(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
  }
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_OPENCL_OVERRIDE;
  Cv_cl = mdivide_left_tri<Eigen::Lower>(Av, Bv);
  Cv_cl(0, 0).grad();
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_CPU_OVERRIDE;
  Cv = mdivide_left_tri<Eigen::Lower>(Av, Bv);
  Cv(0, 0).grad();
  EXPECT_MATRIX_NEAR(Cv, Cv_cl, 1.0E-12);
  EXPECT_MATRIX_NEAR(Cv.adj(), Cv_cl.adj(), 1.0E-12);
  stan::math::recover_memory();
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = temp;
}

TEST(AgradRevMatrix, mdivide_left_tri_lower_grad_dv_cl) {
  int temp = stan::math::opencl_context.tuning_opts()
                 .tri_inverse_size_worth_transfer;

  using stan::math::matrix_d;
  using stan::math::matrix_v;
  using stan::math::mdivide_left_tri;
  using stan::math::multiply;
  int size = 213;

  matrix_d Ad(size, size);
  matrix_v Bv(size, size);
  matrix_v Cv(size, size);
  matrix_v Cv_cl(size, size);
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < i; j++) {
      Ad(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
    Ad(i, i) = 20.0;
    for (int j = i + 1; j < size; j++) {
      Ad(i, j) = 0.0;
    }
    for (int j = 0; j < size; j++) {
      Bv(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
  }
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_OPENCL_OVERRIDE;
  Cv_cl = mdivide_left_tri<Eigen::Lower>(Ad, Bv);
  Cv_cl(0, 0).grad();
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_CPU_OVERRIDE;
  Cv = mdivide_left_tri<Eigen::Lower>(Ad, Bv);
  Cv(0, 0).grad();
  EXPECT_MATRIX_NEAR(Cv, Cv_cl, 1.0E-12);
  EXPECT_MATRIX_NEAR(Cv.adj(), Cv_cl.adj(), 1.0E-12);
  stan::math::recover_memory();
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = temp;
}

TEST(AgradRevMatrix, mdivide_left_tri_lower_grad_vd_cl) {
  int temp = stan::math::opencl_context.tuning_opts()
                 .tri_inverse_size_worth_transfer;

  using stan::math::matrix_d;
  using stan::math::matrix_v;
  using stan::math::mdivide_left_tri;
  using stan::math::multiply;
  int size = 250;

  matrix_d Av(size, size);
  matrix_v Bd(size, size);
  matrix_v Cv(size, size);
  matrix_v Cv_cl(size, size);
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < i; j++) {
      Av(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
    Av(i, i) = 20.0;
    for (int j = i + 1; j < size; j++) {
      Av(i, j) = 0.0;
    }
    for (int j = 0; j < size; j++) {
      Bd(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
  }
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_OPENCL_OVERRIDE;
  Cv_cl = mdivide_left_tri<Eigen::Lower>(Av, Bd);
  Cv_cl(0, 0).grad();
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_CPU_OVERRIDE;
  Cv = mdivide_left_tri<Eigen::Lower>(Av, Bd);
  Cv(0, 0).grad();
  EXPECT_MATRIX_NEAR(Cv, Cv_cl, 1.0E-12);
  EXPECT_MATRIX_NEAR(Cv.adj(), Cv_cl.adj(), 1.0E-12);
  stan::math::recover_memory();
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = temp;
}

TEST(AgradRevMatrix, mdivide_left_tri_upper_grad_vv_cl) {
  int temp = stan::math::opencl_context.tuning_opts()
                 .tri_inverse_size_worth_transfer;

  using stan::math::matrix_d;
  using stan::math::matrix_v;
  using stan::math::mdivide_left_tri;
  using stan::math::multiply;
  int size = 299;

  matrix_v Av(size, size);
  matrix_v Bv(size, size);
  matrix_v Cv(size, size);
  matrix_v Cv_cl(size, size);
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < i; j++) {
      Av(i, j) = 0.0;
    }
    Av(i, i) = 20.0;
    for (int j = i + 1; j < size; j++) {
      Av(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
    for (int j = 0; j < size; j++) {
      Bv(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
  }
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_OPENCL_OVERRIDE;
  Cv_cl = mdivide_left_tri<Eigen::Upper>(Av, Bv);
  Cv_cl(0, 0).grad();
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_CPU_OVERRIDE;
  Cv = mdivide_left_tri<Eigen::Upper>(Av, Bv);
  Cv(0, 0).grad();
  EXPECT_MATRIX_NEAR(Cv, Cv_cl, 1.0E-12);
  EXPECT_MATRIX_NEAR(Cv.adj(), Cv_cl.adj(), 1.0E-12);
  stan::math::recover_memory();
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = temp;
}

TEST(AgradRevMatrix, mdivide_left_tri_upper_grad_dv_cl) {
  int temp = stan::math::opencl_context.tuning_opts()
                 .tri_inverse_size_worth_transfer;

  using stan::math::matrix_d;
  using stan::math::matrix_v;
  using stan::math::mdivide_left_tri;
  using stan::math::multiply;
  int size = 301;

  matrix_d Ad(size, size);
  matrix_v Bv(size, size);
  matrix_v Cv(size, size);
  matrix_v Cv_cl(size, size);
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < i; j++) {
      Ad(i, j) = 0.0;
    }
    Ad(i, i) = 20.0;
    for (int j = i + 1; j < size; j++) {
      Ad(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
    for (int j = 0; j < size; j++) {
      Bv(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
  }
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_OPENCL_OVERRIDE;
  Cv_cl = mdivide_left_tri<Eigen::Upper>(Ad, Bv);
  Cv_cl(0, 0).grad();
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_CPU_OVERRIDE;
  Cv = mdivide_left_tri<Eigen::Upper>(Ad, Bv);
  Cv(0, 0).grad();
  EXPECT_MATRIX_NEAR(Cv, Cv_cl, 1.0E-12);
  EXPECT_MATRIX_NEAR(Cv.adj(), Cv_cl.adj(), 1.0E-12);
  stan::math::recover_memory();
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = temp;
}

TEST(AgradRevMatrix, mdivide_left_tri_upper_grad_vd_cl) {
  int temp = stan::math::opencl_context.tuning_opts()
                 .tri_inverse_size_worth_transfer;

  using stan::math::matrix_d;
  using stan::math::matrix_v;
  using stan::math::mdivide_left_tri;
  using stan::math::multiply;
  int size = 300;

  matrix_d Av(size, size);
  matrix_v Bd(size, size);
  matrix_v Cv(size, size);
  matrix_v Cv_cl(size, size);
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < i; j++) {
      Av(i, j) = 0.0;
    }
    Av(i, i) = 20.0;
    for (int j = i + 1; j < size; j++) {
      Av(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
    for (int j = 0; j < size; j++) {
      Bd(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
  }
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_OPENCL_OVERRIDE;
  Cv_cl = mdivide_left_tri<Eigen::Upper>(Av, Bd);
  Cv_cl(0, 0).grad();
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = MDIVIDE_CPU_OVERRIDE;
  Cv = mdivide_left_tri<Eigen::Upper>(Av, Bd);
  Cv(0, 0).grad();
  EXPECT_MATRIX_NEAR(Cv, Cv_cl, 1.0E-12);
  EXPECT_MATRIX_NEAR(Cv.adj(), Cv_cl.adj(), 1.0E-12);
  stan::math::recover_memory();
  stan::math::opencl_context.tuning_opts().tri_inverse_size_worth_transfer
      = temp;
}
#endif


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

  @copyright 2015 NumScale SAS
  @copyright 2015 J.T. Lapreste

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

#include <boost/simd/constant/allbits.hpp>
#include <boost/simd/function/is_nez.hpp>
#include <boost/simd/detail/dispatch/function/overload.hpp>
#include <boost/assert.hpp>
#include <boost/config.hpp>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  BOOST_DISPATCH_OVERLOAD ( mask2logical_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_< bd::arithmetic_<A0> >
                          )
  {
    auto operator() ( A0 a0) const BOOST_NOEXCEPT -> decltype(is_nez(a0))
    {
      BOOST_ASSERT_MSG((a0 != A0(0)) || (a0 != Allbits<A0>()), "Argument to mask2logical is not a valid logical mask");
      return is_nez(a0);
    }
  };
} } }

#endif


//
// Created by vlad on 14.11.16.
//

#pragma once

#include <boost/optional.hpp>

#include <vector>
#include <memory>

namespace rs {

struct point2d
{
	point2d() = default;

	point2d(double lat,
			double lon);

	double x() const;

	double y() const;

	double lat;
	double lon;
};

struct TaskPoint
{
	TaskPoint() = default;
	TaskPoint(point2d&& point,
			  std::size_t ts,
			  std::size_t te,
			  std::size_t dur);

	point2d point;
	std::size_t ts = 0;
	std::size_t te = 0;
	std::size_t dur = 0;
};

class Task {
public:
	Task(std::uint32_t id);

	std::uint32_t getId() const;

	float getCapacity() const;
	void setCapacity(float capacity);

	float getVolume() const;
	void setVolume(float volume);

	float getMaxSize() const;
	void setMaxsize(float maxsize);

	bool getVip() const;
	void setVip(bool vip);

	const std::vector<std::uint16_t>& getTypes() const;
	void setTypes(std::vector<std::uint16_t>&& types);

	const TaskPoint& getStart() const;
	void setStart(TaskPoint&& start);

	const TaskPoint& getEnd() const;
	void setEnd(TaskPoint&& end);

	std::uint32_t getExternalId() const;
	void setExternalId(const std::uint32_t& externalId);

private:
	std::uint32_t m_id;
	std::uint32_t m_externalId;
	bool m_vip;
	float m_capacity;
	float m_volume;
	float m_maxsize;
	TaskPoint m_start;
	TaskPoint m_end;
	std::vector<std::uint16_t> m_types;
};

bool isPointInZone(const point2d& pt, const std::vector<point2d>& workzone);

class Zone {
public:
	enum ZoneType
	{
		Circle,
		Polygon
	};

	Zone(std::uint32_t id);

	std::uint32_t getId() const;

	ZoneType getZoneType() const;

	bool isTaskInZone(const Task& task) const;

	const std::vector<point2d>& getPoints() const;

	std::uint32_t getExternalId() const;

	void setExternalId(const std::uint32_t& externalId);

	void setPolygonPoints(std::vector<point2d>&& points);

	void setCircle(double circleRadius, const point2d& circleCenter);

private:
	std::uint32_t m_id;
	std::uint32_t m_externalId;
	double m_circleRadius;
	ZoneType m_zoneType;
	boost::optional<point2d> m_circleCenter;
	std::vector <point2d> m_points;
};

class Vehicle {
public:
	Vehicle(std::uint32_t id);

	std::uint32_t getExternalId() const;
	void setExternalId(std::uint32_t externalId);

	std::uint32_t getId() const;
	void setId(std::uint32_t id);

	float getCapacity() const;
	void setCapacity(float capacity);

	float getLength() const;
	void setLength(float length);

	float getHeight() const;
	void setHeight(float height);

	float getWidth() const;
	void setWidth(float width);

	float getTripDuration() const;
	void setTripDuration(double tripDuration);

	float getTripLength() const;
	void setTripLength(double tripLength);

	std::vector<std::uint32_t> getWorkZones() const;
	void setWorkZones(std::vector<std::uint32_t>&& workZones);

	std::vector<std::uint32_t> getRestrictedZones() const;
	void setRestrictedZones(std::vector<std::uint32_t>&& restrictedZones);

	float getCostOfUsing() const;
	void setCostOfUsing(float costOfUsing);

	bool getCostPerKM() const;
	void setCostPerKM(bool costPerKM);

	std::uint16_t getTripMaxTasks() const;
	void setTripMaxTasks(std::uint32_t tripMaxTasks);

	const std::vector<std::uint32_t>& getWorkZonesExternalIds() const;
	const std::vector<std::uint32_t>& getRestrictedZonesExternalIds() const;

	const std::vector<uint16_t>& getTypes() const;
	void setTypes(const std::vector<uint16_t>& types);

	bool costPerKM() const;
	bool canTakeTask(const Task& task) const;
	float getMaxVolume() const;

private:
	std::uint32_t m_id;
	std::uint32_t m_externalId;
	std::uint32_t m_tripMaxTasks;
	double m_tripDuration;
	double m_tripLength;
	float m_capacity;
	float m_length;
	float m_height;
	float m_width;
	float m_costOfUsing;
	bool m_costPerKM;
	std::vector<uint16_t> m_types;
	std::vector<std::uint32_t> m_workZones;
	std::vector<std::uint32_t> m_restrictedZones;
};

}


#include <boost/hana/sum.hpp>


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

#include <nt2/core/functions/inner_fold.hpp>
#include <boost/fusion/include/pop_front.hpp>
#include <nt2/include/functions/scalar/numel.hpp>

namespace nt2 { namespace details
{
  template <class X, class N, class B, class U>
  BOOST_FORCEINLINE typename X::value_type
  inner_fold_step(X const& in, const std::size_t& p, N const& neutral, B const& bop, U const&)
  {
    typedef typename X::value_type   value_type;
    typedef typename X::extent_type  extent_type;
    extent_type ext = in.extent();

    std::size_t ibound  = boost::fusion::at_c<0>(ext);
    value_type out = neutral(nt2::meta::as_<value_type>());

    for(std::size_t i = 0; i < ibound; ++i)
    {
        out = bop(out, nt2::run(in, i+p, meta::as_<value_type>()));
    }

    return out;
  }
} }

namespace nt2 { namespace ext
{

  //============================================================================
  // Generates inner_fold
  //============================================================================
  NT2_FUNCTOR_IMPLEMENTATION( nt2::tag::inner_fold_, tag::cpu_, (A0)(A1)(A2)(A3)(A4)
                            , ((ast_< A0, nt2::container::domain>))
                              ((ast_< A1, nt2::container::domain>))
                              (unspecified_<A2>)
                              (unspecified_<A3>)
                              (unspecified_<A4>)
                            )
  {
    typedef void                                                               result_type;
    typedef typename A0::value_type                                            value_type;
    typedef typename boost::remove_reference<A1>::type::extent_type            extent_type;

    BOOST_FORCEINLINE result_type operator()(A0& out, A1& in, A2 const& neutral, A3 const& bop, A4 const& uop) const
    {
      extent_type ext = in.extent();
      std::size_t ibound  = boost::fusion::at_c<0>(ext);
      std::size_t obound =  nt2::numel(boost::fusion::pop_front(ext));

      for(std::size_t j = 0, k = 0; j < obound; ++j, k+=ibound)
      {
          nt2::run(out, j, details::inner_fold_step(in,k, neutral, bop, uop));
      }

    }

    };

  } }

#endif


/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center.

All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

// Blueberry
#include <berryISelectionService.h>
#include <berryIWorkbenchWindow.h>

#include "QmitkFiberClusteringView.h"

#include <mitkNodePredicateDataType.h>
#include <mitkNodePredicateDimension.h>
#include <mitkNodePredicateAnd.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
#include <mitkTractClusteringFilter.h>
#include <mitkFiberBundle.h>
#include <mitkClusteringMetricEuclideanMean.h>
#include <mitkClusteringMetricEuclideanMax.h>
#include <mitkClusteringMetricEuclideanStd.h>
#include <mitkClusteringMetricAnatomic.h>
#include <mitkClusteringMetricScalarMap.h>
#include <mitkClusteringMetricInnerAngles.h>
#include <mitkClusteringMetricLength.h>
#include <QMessageBox>
#include <boost/lexical_cast.hpp>

const std::string QmitkFiberClusteringView::VIEW_ID = "org.mitk.views.fiberclustering";
using namespace mitk;

QmitkFiberClusteringView::QmitkFiberClusteringView()
  : QmitkAbstractView()
  , m_Controls( nullptr )
{

}

// Destructor
QmitkFiberClusteringView::~QmitkFiberClusteringView()
{

}

void QmitkFiberClusteringView::CreateQtPartControl( QWidget *parent )
{
  // build up qt view, unless already done
  if ( !m_Controls )
  {
    // create GUI widgets from the Qt Designer's .ui file
    m_Controls = new Ui::QmitkFiberClusteringViewControls;
    m_Controls->setupUi( parent );

    connect( m_Controls->m_StartButton, SIGNAL(clicked()), this, SLOT(StartClustering()) );
    connect( m_Controls->m_TractBox, SIGNAL(currentIndexChanged(int)), this, SLOT(FiberSelectionChanged()) );

    mitk::TNodePredicateDataType<mitk::FiberBundle>::Pointer isFib = mitk::TNodePredicateDataType<mitk::FiberBundle>::New();
    m_Controls->m_TractBox->SetDataStorage(this->GetDataStorage());
    m_Controls->m_TractBox->SetPredicate(isFib);

    m_Controls->m_InCentroidsBox->SetDataStorage(this->GetDataStorage());
    m_Controls->m_InCentroidsBox->SetPredicate(isFib);
    m_Controls->m_InCentroidsBox->SetZeroEntryText("--");

    mitk::TNodePredicateDataType<mitk::Image>::Pointer imageP = mitk::TNodePredicateDataType<mitk::Image>::New();
    mitk::NodePredicateDimension::Pointer dimP = mitk::NodePredicateDimension::New(3);

    m_Controls->m_MapBox->SetDataStorage(this->GetDataStorage());
    m_Controls->m_MapBox->SetPredicate(mitk::NodePredicateAnd::New(imageP, dimP));

    m_Controls->m_ParcellationBox->SetDataStorage(this->GetDataStorage());
    m_Controls->m_ParcellationBox->SetPredicate(mitk::NodePredicateAnd::New(imageP, dimP));

    m_Controls->m_MetricBox6->setVisible(false);
    m_Controls->m_MetricWeight6->setVisible(false);

    FiberSelectionChanged();
  }

}

void QmitkFiberClusteringView::SetFocus()
{
  FiberSelectionChanged();
}

void QmitkFiberClusteringView::FiberSelectionChanged()
{
  if (m_Controls->m_TractBox->GetSelectedNode().IsNull())
    m_Controls->m_StartButton->setEnabled(false);
  else
    m_Controls->m_StartButton->setEnabled(true);
}

void QmitkFiberClusteringView::StartClustering()
{
  if (m_Controls->m_TractBox->GetSelectedNode().IsNull())
    return;

  mitk::DataNode::Pointer node = m_Controls->m_TractBox->GetSelectedNode();
  float clusterSize = m_Controls->m_ClusterSizeBox->value();

  mitk::FiberBundle::Pointer fib = dynamic_cast<mitk::FiberBundle*>(node->GetData());

  float max_d = 0;
  int i=1;
  std::vector< float > distances;
  while (max_d < fib->GetGeometry()->GetDiagonalLength()/2)
  {
    distances.push_back(clusterSize*i);
    max_d = clusterSize*i;
    ++i;
  }

  std::shared_ptr< mitk::TractClusteringFilter > clusterer = std::make_shared<mitk::TractClusteringFilter>();
  clusterer->SetDistances(distances);
  clusterer->SetTractogram(fib);
  if (m_Controls->m_InCentroidsBox->GetSelectedNode().IsNotNull())
  {
    mitk::FiberBundle::Pointer in_centroids = dynamic_cast<mitk::FiberBundle*>(m_Controls->m_InCentroidsBox->GetSelectedNode()->GetData());
    clusterer->SetInCentroids(in_centroids);
  }

  std::vector< mitk::ClusteringMetric* > metrics;
  if (m_Controls->m_MetricBox1->isChecked())
  {
    mitk::ClusteringMetricEuclideanMean* metric = new mitk::ClusteringMetricEuclideanMean();
    metric->SetScale(m_Controls->m_MetricWeight1->value());
    metrics.push_back(metric);
  }
  if (m_Controls->m_MetricBox2->isChecked())
  {
    mitk::ClusteringMetricEuclideanStd* metric = new mitk::ClusteringMetricEuclideanStd();
    metric->SetScale(m_Controls->m_MetricWeight2->value());
    metrics.push_back(metric);
  }
  if (m_Controls->m_MetricBox3->isChecked())
  {
    mitk::ClusteringMetricEuclideanMax* metric = new mitk::ClusteringMetricEuclideanMax();
    metric->SetScale(m_Controls->m_MetricWeight3->value());
    metrics.push_back(metric);
  }
  if (m_Controls->m_MetricBox6->isChecked())
  {
    mitk::ClusteringMetricInnerAngles* metric = new mitk::ClusteringMetricInnerAngles();
    metric->SetScale(m_Controls->m_MetricWeight6->value());
    metrics.push_back(metric);
  }
  if (m_Controls->m_MetricBox7->isChecked())
  {
    mitk::ClusteringMetricLength* metric = new mitk::ClusteringMetricLength();
    metric->SetScale(m_Controls->m_MetricWeight7->value());
    metrics.push_back(metric);
  }

  if (m_Controls->m_ParcellationBox->GetSelectedNode().IsNotNull() && m_Controls->m_MetricBox4->isChecked())
  {
    mitk::Image::Pointer mitk_map = dynamic_cast<mitk::Image*>(m_Controls->m_ParcellationBox->GetSelectedNode()->GetData());

    ShortImageType::Pointer itk_map = ShortImageType::New();
    mitk::CastToItkImage(mitk_map, itk_map);
    mitk::ClusteringMetricAnatomic* metric = new mitk::ClusteringMetricAnatomic();
    metric->SetParcellations({itk_map});
    metric->SetScale(m_Controls->m_MetricWeight4->value());
    metrics.push_back(metric);
  }
  if (m_Controls->m_MapBox->GetSelectedNode().IsNotNull() && m_Controls->m_MetricBox5->isChecked())
  {
    mitk::Image::Pointer mitk_map = dynamic_cast<mitk::Image*>(m_Controls->m_MapBox->GetSelectedNode()->GetData());

    FloatImageType::Pointer itk_map = FloatImageType::New();
    mitk::CastToItkImage(mitk_map, itk_map);
    mitk::ClusteringMetricScalarMap* metric = new mitk::ClusteringMetricScalarMap();
    metric->SetImages({itk_map});
    metric->SetScale(m_Controls->m_MetricWeight5->value());
    metrics.push_back(metric);
  }

  if (metrics.empty())
  {
    QMessageBox::warning(nullptr, "Warning", "No metric selected!");
    return;
  }

  clusterer->SetMetrics(metrics);
  clusterer->SetMergeDuplicateThreshold(m_Controls->m_MergeDuplicatesBox->value());
  clusterer->SetNumPoints(m_Controls->m_FiberPointsBox->value());
  clusterer->SetMaxClusters(m_Controls->m_MaxClustersBox->value());
  clusterer->SetMinClusterSize(m_Controls->m_MinFibersBox->value());
  clusterer->Update();
  std::vector<mitk::FiberBundle::Pointer> tracts = clusterer->GetOutTractograms();
  std::vector<mitk::FiberBundle::Pointer> centroids = clusterer->GetOutCentroids();

  unsigned int c = 0;
  for (auto f : tracts)
  {
    mitk::DataNode::Pointer new_node = mitk::DataNode::New();
    new_node->SetData(f);
    new_node->SetName("Cluster_" + boost::lexical_cast<std::string>(c));
    if (m_Controls->m_InCentroidsBox->GetSelectedNode().IsNotNull())
      this->GetDataStorage()->Add(new_node, m_Controls->m_InCentroidsBox->GetSelectedNode());
    else
      this->GetDataStorage()->Add(new_node, node);
    node->SetVisibility(false);

    if (m_Controls->m_CentroidsBox->isChecked())
    {
      mitk::DataNode::Pointer new_node2 = mitk::DataNode::New();
      new_node2->SetData(centroids.at(c));
      new_node2->SetName("Centroid_" + boost::lexical_cast<std::string>(c));
      this->GetDataStorage()->Add(new_node2, new_node);
    }
    ++c;
  }
}

void QmitkFiberClusteringView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList<mitk::DataNode::Pointer>& )
{

}


//
// Copyright © 2019 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//

#include <reference/workloads/ArgMinMax.hpp>

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(RefArgMinMax)

BOOST_AUTO_TEST_CASE(ArgMinTest)
{
    const armnn::TensorInfo inputInfo({ 1, 2, 3 } , armnn::DataType::Float32);
    const armnn::TensorInfo outputInfo({ 1, 3 }, armnn::DataType::Signed64);

    std::vector<float> inputValues({ 1.0f, 5.0f, 3.0f, 4.0f, 2.0f, 6.0f});
    std::vector<int64_t> outputValues(outputInfo.GetNumElements());
    std::vector<int64_t> expectedValues({ 0, 1, 0 });

    ArgMinMax(*armnn::MakeDecoder<float>(inputInfo, inputValues.data()),
               outputValues.data(),
               inputInfo,
               outputInfo,
               armnn::ArgMinMaxFunction::Min,
               -2);

    BOOST_CHECK_EQUAL_COLLECTIONS(outputValues.begin(),
                                  outputValues.end(),
                                  expectedValues.begin(),
                                  expectedValues.end());

}

BOOST_AUTO_TEST_CASE(ArgMaxTest)
{
    const armnn::TensorInfo inputInfo({ 1, 2, 3 } , armnn::DataType::Float32);
    const armnn::TensorInfo outputInfo({ 1, 3 }, armnn::DataType::Signed64);

    std::vector<float> inputValues({ 1.0f, 5.0f, 3.0f, 4.0f, 2.0f, 6.0f });
    std::vector<int64_t> outputValues(outputInfo.GetNumElements());
    std::vector<int64_t> expectedValues({ 1, 0, 1 });

    ArgMinMax(*armnn::MakeDecoder<float>(inputInfo, inputValues.data()),
               outputValues.data(),
               inputInfo,
               outputInfo,
               armnn::ArgMinMaxFunction::Max,
               -2);

    BOOST_CHECK_EQUAL_COLLECTIONS(outputValues.begin(),
                                  outputValues.end(),
                                  expectedValues.begin(),
                                  expectedValues.end());

}

BOOST_AUTO_TEST_SUITE_END()


#ifndef PYTHONIC_INCLUDE_NUMPY_SIGN_HPP
#define PYTHONIC_INCLUDE_NUMPY_SIGN_HPP

#include "pythonic/include/utils/functor.hpp"
#include "pythonic/include/types/ndarray.hpp"
#include "pythonic/include/utils/numpy_traits.hpp"
#include <boost/simd/function/sign.hpp>

PYTHONIC_NS_BEGIN

namespace numpy
{
#define NUMPY_NARY_FUNC_NAME sign
#define NUMPY_NARY_FUNC_SYM boost::simd::sign
#include "pythonic/include/types/numpy_nary_expr.hpp"
}
PYTHONIC_NS_END

#endif


/*
 * flex_prediction.cc
 *
 *  Copyright (C) 2013 Diamond Light Source
 *
 *  Author: James Parkhurst
 *
 *  This code is distributed under the BSD license, a copy of which is
 *  included in the root directory of this package.
 */
#include <boost/python.hpp>
#include <boost/python/def.hpp>
#include <cctbx/miller.h>
#include <scitbx/array_family/boost_python/flex_wrapper.h>
#include <scitbx/array_family/ref_reductions.h>
#include <scitbx/array_family/boost_python/ref_pickle_double_buffered.h>
#include <scitbx/array_family/boost_python/flex_pickle_double_buffered.h>
#include <dials/model/data/prediction.h>
#include <dials/array_family/scitbx_shared_and_versa.h>
#include <dials/error.h>

namespace dials { namespace af { namespace boost_python {

  using namespace boost::python;
  using namespace scitbx::af::boost_python;
  using dials::model::Prediction;
  using scitbx::vec2;
  using scitbx::vec3;

  typedef cctbx::miller::index<> MillerIndex;

  /** @returns An array of miller indices */
  static af::shared<MillerIndex> prediction_miller_index(
    const af::const_ref<Prediction> &obj) {
    af::shared<MillerIndex> result(obj.size(), MillerIndex());
    for (std::size_t i = 0; i < result.size(); ++i) {
      result[i] = obj[i].miller_index;
    }
    return result;
  }

  /** @returns An array of beam vectors */
  static af::shared<vec3<double> > prediction_beam_vector(
    const af::const_ref<Prediction> &obj) {
    af::shared<vec3<double> > result(obj.size(),
                                     af::init_functor_null<vec3<double> >());
    for (std::size_t i = 0; i < result.size(); ++i) {
      result[i] = obj[i].beam_vector;
    }
    return result;
  }

  /** @returns An array of panel numbers */
  static af::shared<int> prediction_panel(const af::const_ref<Prediction> &obj) {
    af::shared<int> result(obj.size(), af::init_functor_null<int>());
    for (std::size_t i = 0; i < result.size(); ++i) {
      result[i] = obj[i].panel;
    }
    return result;
  }

  /** @returns An array of entering flags */
  static af::shared<bool> prediction_entering(const af::const_ref<Prediction> &obj) {
    af::shared<bool> result(obj.size(), af::init_functor_null<bool>());
    for (std::size_t i = 0; i < result.size(); ++i) {
      result[i] = obj[i].entering;
    }
    return result;
  }

  /** @returns An array of crystal ids */
  static af::shared<int> prediction_crystal(const af::const_ref<Prediction> &obj) {
    af::shared<int> result(obj.size(), af::init_functor_null<int>());
    for (std::size_t i = 0; i < result.size(); ++i) {
      result[i] = obj[i].crystal;
    }
    return result;
  }

  /** @returns An array of pixel positions */
  static af::shared<vec3<double> > prediction_position_px(
    const af::const_ref<Prediction> &obj) {
    af::shared<vec3<double> > result(obj.size(),
                                     af::init_functor_null<vec3<double> >());
    for (std::size_t i = 0; i < result.size(); ++i) {
      result[i] = obj[i].position.px;
    }
    return result;
  }

  /** @returns An array of pixel xy coordinates */
  static af::shared<vec2<double> > prediction_position_px_xy(
    const af::const_ref<Prediction> &obj) {
    af::shared<vec2<double> > result(obj.size(),
                                     af::init_functor_null<vec2<double> >());
    for (std::size_t i = 0; i < result.size(); ++i) {
      result[i] = vec2<double>(obj[i].position.px[0], obj[i].position.px[1]);
    }
    return result;
  }

  /** @returns An array of frames */
  static af::shared<double> prediction_position_frame(
    const af::const_ref<Prediction> &obj) {
    af::shared<double> result(obj.size(), af::init_functor_null<double>());
    for (std::size_t i = 0; i < result.size(); ++i) {
      result[i] = obj[i].position.px[2];
    }
    return result;
  }

  /** @returns An array of millimeter positions */
  static af::shared<vec3<double> > prediction_position_mm(
    const af::const_ref<Prediction> &obj) {
    af::shared<vec3<double> > result(obj.size(),
                                     af::init_functor_null<vec3<double> >());
    for (std::size_t i = 0; i < result.size(); ++i) {
      result[i] = obj[i].position.mm;
    }
    return result;
  }

  /** @returns An array of millimeter xy coordinates */
  static af::shared<vec2<double> > prediction_position_mm_xy(
    const af::const_ref<Prediction> &obj) {
    af::shared<vec2<double> > result(obj.size(),
                                     af::init_functor_null<vec2<double> >());
    for (std::size_t i = 0; i < result.size(); ++i) {
      result[i] = vec2<double>(obj[i].position.mm[0], obj[i].position.mm[1]);
    }
    return result;
  }

  /** @returns An array of angles */
  static af::shared<double> prediction_position_angle(
    const af::const_ref<Prediction> &obj) {
    af::shared<double> result(obj.size(), af::init_functor_null<double>());
    for (std::size_t i = 0; i < result.size(); ++i) {
      result[i] = obj[i].position.mm[2];
    }
    return result;
  }

  /**
   * Class to convert predictions to strings for pickling
   */
  struct prediction_to_string : pickle_double_buffered::to_string {
    using pickle_double_buffered::to_string::operator<<;

    /** Initialise with the version */
    prediction_to_string() {
      unsigned int version = 1;
      *this << version;
    }

    /** Convert a single prediction instance to string */
    prediction_to_string &operator<<(const Prediction &val) {
      *this << val.miller_index[0] << val.miller_index[1] << val.miller_index[2]
            << val.beam_vector[0] << val.beam_vector[1] << val.beam_vector[2]
            << val.panel << val.entering << val.crystal << val.position.px[0]
            << val.position.px[1] << val.position.px[2] << val.position.mm[0]
            << val.position.mm[1] << val.position.mm[2];

      return *this;
    }
  };

  /**
   * Class to convert strings to predictions for unpickling
   */
  struct prediction_from_string : pickle_double_buffered::from_string {
    using pickle_double_buffered::from_string::operator>>;

    /** Initialise with the string */
    prediction_from_string(const char *str_ptr)
        : pickle_double_buffered::from_string(str_ptr) {
      *this >> version;
      DIALS_ASSERT(version == 1);
    }

    /** Convert a string to a single prediction instance */
    prediction_from_string &operator>>(Prediction &val) {
      *this >> val.miller_index[0] >> val.miller_index[1] >> val.miller_index[2]
        >> val.beam_vector[0] >> val.beam_vector[1] >> val.beam_vector[2] >> val.panel
        >> val.entering >> val.crystal >> val.position.px[0] >> val.position.px[1]
        >> val.position.px[2] >> val.position.mm[0] >> val.position.mm[1]
        >> val.position.mm[2];

      return *this;
    }

    unsigned int version;
  };

  void export_flex_prediction() {
    scitbx::af::boost_python::flex_wrapper<Prediction, return_internal_reference<> >::
      plain("prediction")
        .def("miller_index", &prediction_miller_index)
        .def("beam_vector", &prediction_beam_vector)
        .def("panel", &prediction_panel)
        .def("entering", &prediction_entering)
        .def("crystal", &prediction_crystal)
        .def("position_px", &prediction_position_px)
        .def("position_px_xy", &prediction_position_px_xy)
        .def("position_frame", &prediction_position_frame)
        .def("position_mm", &prediction_position_mm)
        .def("position_mm_xy", &prediction_position_mm_xy)
        .def("position_angle", &prediction_position_angle)
        .def_pickle(flex_pickle_double_buffered<Prediction,
                                                prediction_to_string,
                                                prediction_from_string>());
  }

}}}  // namespace dials::af::boost_python


// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
//    conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
//    used to endorse or promote products derived from this software without specific
//    prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT 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.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers

#include <atomic>
#include <boost/algorithm/string.hpp>
#include "wipeable_string.h"
#include "string_tools.h"
#include "string_tools_lexical.h"
#include "serialization/string.h"
#include "cryptonote_format_utils.h"
#include "cryptonote_config.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "ringct/rctSigs.h"

using namespace epee;

#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "cn"

// #define ENABLE_HASH_CASH_INTEGRITY_CHECK

using namespace crypto;

static const uint64_t valid_decomposed_outputs[] = {
  (uint64_t)1, (uint64_t)2, (uint64_t)3, (uint64_t)4, (uint64_t)5, (uint64_t)6, (uint64_t)7, (uint64_t)8, (uint64_t)9, // 1 piconero
  (uint64_t)10, (uint64_t)20, (uint64_t)30, (uint64_t)40, (uint64_t)50, (uint64_t)60, (uint64_t)70, (uint64_t)80, (uint64_t)90,
  (uint64_t)100, (uint64_t)200, (uint64_t)300, (uint64_t)400, (uint64_t)500, (uint64_t)600, (uint64_t)700, (uint64_t)800, (uint64_t)900,
  (uint64_t)1000, (uint64_t)2000, (uint64_t)3000, (uint64_t)4000, (uint64_t)5000, (uint64_t)6000, (uint64_t)7000, (uint64_t)8000, (uint64_t)9000,
  (uint64_t)10000, (uint64_t)20000, (uint64_t)30000, (uint64_t)40000, (uint64_t)50000, (uint64_t)60000, (uint64_t)70000, (uint64_t)80000, (uint64_t)90000,
  (uint64_t)100000, (uint64_t)200000, (uint64_t)300000, (uint64_t)400000, (uint64_t)500000, (uint64_t)600000, (uint64_t)700000, (uint64_t)800000, (uint64_t)900000,
  (uint64_t)1000000, (uint64_t)2000000, (uint64_t)3000000, (uint64_t)4000000, (uint64_t)5000000, (uint64_t)6000000, (uint64_t)7000000, (uint64_t)8000000, (uint64_t)9000000, // 1 micronero
  (uint64_t)10000000, (uint64_t)20000000, (uint64_t)30000000, (uint64_t)40000000, (uint64_t)50000000, (uint64_t)60000000, (uint64_t)70000000, (uint64_t)80000000, (uint64_t)90000000,
  (uint64_t)100000000, (uint64_t)200000000, (uint64_t)300000000, (uint64_t)400000000, (uint64_t)500000000, (uint64_t)600000000, (uint64_t)700000000, (uint64_t)800000000, (uint64_t)900000000,
  (uint64_t)1000000000, (uint64_t)2000000000, (uint64_t)3000000000, (uint64_t)4000000000, (uint64_t)5000000000, (uint64_t)6000000000, (uint64_t)7000000000, (uint64_t)8000000000, (uint64_t)9000000000,
  (uint64_t)10000000000, (uint64_t)20000000000, (uint64_t)30000000000, (uint64_t)40000000000, (uint64_t)50000000000, (uint64_t)60000000000, (uint64_t)70000000000, (uint64_t)80000000000, (uint64_t)90000000000,
  (uint64_t)100000000000, (uint64_t)200000000000, (uint64_t)300000000000, (uint64_t)400000000000, (uint64_t)500000000000, (uint64_t)600000000000, (uint64_t)700000000000, (uint64_t)800000000000, (uint64_t)900000000000,
  (uint64_t)1000000000000, (uint64_t)2000000000000, (uint64_t)3000000000000, (uint64_t)4000000000000, (uint64_t)5000000000000, (uint64_t)6000000000000, (uint64_t)7000000000000, (uint64_t)8000000000000, (uint64_t)9000000000000, // 1 monero
  (uint64_t)10000000000000, (uint64_t)20000000000000, (uint64_t)30000000000000, (uint64_t)40000000000000, (uint64_t)50000000000000, (uint64_t)60000000000000, (uint64_t)70000000000000, (uint64_t)80000000000000, (uint64_t)90000000000000,
  (uint64_t)100000000000000, (uint64_t)200000000000000, (uint64_t)300000000000000, (uint64_t)400000000000000, (uint64_t)500000000000000, (uint64_t)600000000000000, (uint64_t)700000000000000, (uint64_t)800000000000000, (uint64_t)900000000000000,
  (uint64_t)1000000000000000, (uint64_t)2000000000000000, (uint64_t)3000000000000000, (uint64_t)4000000000000000, (uint64_t)5000000000000000, (uint64_t)6000000000000000, (uint64_t)7000000000000000, (uint64_t)8000000000000000, (uint64_t)9000000000000000,
  (uint64_t)10000000000000000, (uint64_t)20000000000000000, (uint64_t)30000000000000000, (uint64_t)40000000000000000, (uint64_t)50000000000000000, (uint64_t)60000000000000000, (uint64_t)70000000000000000, (uint64_t)80000000000000000, (uint64_t)90000000000000000,
  (uint64_t)100000000000000000, (uint64_t)200000000000000000, (uint64_t)300000000000000000, (uint64_t)400000000000000000, (uint64_t)500000000000000000, (uint64_t)600000000000000000, (uint64_t)700000000000000000, (uint64_t)800000000000000000, (uint64_t)900000000000000000,
  (uint64_t)1000000000000000000, (uint64_t)2000000000000000000, (uint64_t)3000000000000000000, (uint64_t)4000000000000000000, (uint64_t)5000000000000000000, (uint64_t)6000000000000000000, (uint64_t)7000000000000000000, (uint64_t)8000000000000000000, (uint64_t)9000000000000000000, // 1 meganero
  (uint64_t)10000000000000000000ull
};

static std::atomic<unsigned int> default_decimal_point(CRYPTONOTE_DISPLAY_DECIMAL_POINT);

static std::atomic<uint64_t> tx_hashes_calculated_count(0);
static std::atomic<uint64_t> tx_hashes_cached_count(0);
static std::atomic<uint64_t> block_hashes_calculated_count(0);
static std::atomic<uint64_t> block_hashes_cached_count(0);

#define CHECK_AND_ASSERT_THROW_MES_L1(expr, message) {if(!(expr)) {MWARNING(message); throw std::runtime_error(message);}}

namespace cryptonote
{
  static inline unsigned char *operator &(ec_point &point) {
    return &reinterpret_cast<unsigned char &>(point);
  }
  static inline const unsigned char *operator &(const ec_point &point) {
    return &reinterpret_cast<const unsigned char &>(point);
  }

  uint64_t get_transaction_weight_clawback(const transaction &tx, size_t n_padded_outputs)
  {
    const uint64_t bp_base = 368;
    const size_t n_outputs = tx.vout.size();
    if (n_padded_outputs <= 2)
      return 0;
    size_t nlr = 0;
    while ((1u << nlr) < n_padded_outputs)
      ++nlr;
    nlr += 6;
    const size_t bp_size = 32 * (9 + 2 * nlr);
    CHECK_AND_ASSERT_THROW_MES_L1(n_outputs <= BULLETPROOF_MAX_OUTPUTS, "maximum number of outputs is " + std::to_string(BULLETPROOF_MAX_OUTPUTS) + " per transaction");
    CHECK_AND_ASSERT_THROW_MES_L1(bp_base * n_padded_outputs >= bp_size, "Invalid bulletproof clawback: bp_base " + std::to_string(bp_base) + ", n_padded_outputs "
        + std::to_string(n_padded_outputs) + ", bp_size " + std::to_string(bp_size));
    const uint64_t bp_clawback = (bp_base * n_padded_outputs - bp_size) * 4 / 5;
    return bp_clawback;
  }
  //---------------------------------------------------------------
}

namespace cryptonote
{

  //---------------------------------------------------------------
  crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx)
  {
    crypto::hash h = null_hash;
    std::ostringstream s;
    binary_archive<true> a(s);
    ::serialization::serialize(a, const_cast<transaction_prefix&>(tx));
    crypto::cn_fast_hash(s.str().data(), s.str().size(), h);
    return h;
  }

  //---------------------------------------------------------------
  bool expand_transaction_1(transaction &tx)
  {
 //  if ( !is_coinbase(tx))
    {
      rct::rctSig &rv = tx.rct_signatures;
      if (rv.type == rct::RCTTypeNull)
        return true;
      if (rv.outCommitments.size() != tx.vout.size())
      {
        MINFO("Failed to parse transaction from blob, bad outCommitments size in tx " << get_transaction_hash(tx));
        return false;
      }
      for (size_t n = 0; n < tx.rct_signatures.outCommitments.size(); ++n)
      {
        if (tx.vout[n].target.type() != typeid(txout_to_key))
        {
          MINFO("Unsupported output type in tx " << get_transaction_hash(tx));
          return false;
        }
        rv.outCommitments[n].otk = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
      }

      if (!tx.is_pruned())
      {
        //const bool bulletproof = rct::is_rct_bulletproof(rv.type);
     //   if (bulletproof)
        {
          if (rv.p.bulletproofs.size() != 1)
          {
            MINFO("Failed to parse transaction from blob, bad bulletproofs size in tx " << get_transaction_hash(tx));
            return false;
          }
          if (rv.p.bulletproofs[0].L.size() < 6)
          {
            MINFO("Failed to parse transaction from blob, bad bulletproofs L size in tx " << get_transaction_hash(tx));
            return false;
          }
          const size_t max_outputs = 1 << (rv.p.bulletproofs[0].L.size() - 6);
          if (max_outputs < tx.vout.size())
          {
            MINFO("Failed to parse transaction from blob, bad bulletproofs max outputs in tx " << get_transaction_hash(tx));
            return false;
          }
          const size_t n_amounts = tx.vout.size();
          CHECK_AND_ASSERT_MES(n_amounts == rv.outCommitments.size(), false, "Internal error filling out V");
          rv.p.bulletproofs[0].V.resize(n_amounts);
          for (size_t i = 0; i < n_amounts; ++i)
            rv.p.bulletproofs[0].V[i] = rct::scalarmultKey(rv.outCommitments[i].commitment, rct::INV_EIGHT);
        }
      }
    }
    return true;
  }

 transaction parse_tx_from_blob_entry(const tx_blob_entry & tb )
 {
    if(tb.prunable_hash==null_hash){
      return parse_tx_from_blob(tb.tx_blob);
    }
    else{
      auto tx = parse_tx_base_from_blob(tb.tx_blob);
      tx.set_prunable_hash(tb.prunable_hash);
      return tx;
    }
 }
  transaction parse_tx_from_blob(const blobdata_ref& tx_blob)
  {
    if(tx_blob.size() > CRYPTONOTE_MAX_TX_SIZE)
    {
      throw_and_log("WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected");
    }
    transaction tx{};
    std::stringstream ss;
    ss << tx_blob;
    binary_archive<false> ba(ss);
    bool r = ::serialization::serialize(ba, tx);
    if(!r)
      throw_and_log(std::string("Failed to parse transaction from blob") + string_tools::buff_to_hex(tx_blob));
    r = expand_transaction_1(tx);
    if(!r)
      throw_and_log(std::string("Failed to expand transaction data")+string_tools::buff_to_hex(tx_blob));
    tx.invalidate_hashes();
    return tx;
  }
  //---------------------------------------------------------------
  transaction parse_tx_base_from_blob(const blobdata_ref& tx_blob)
  {
    transaction tx{};
    std::stringstream ss;
    ss << tx_blob;
    binary_archive<false> ba(ss);
    bool r = tx.serialize_base(ba);
    if(!r)
      throw_and_log("Failed to parse transaction from blob"+ string_tools::buff_to_hex(tx_blob));

    r = expand_transaction_1(tx);
    if(!r)
      throw_and_log("Failed to expand transaction data"+ string_tools::buff_to_hex(tx_blob));

    tx.invalidate_hashes();
    return tx;
  }
  //---------------------------------------------------------------
  bool parse_and_validate_tx_prefix_from_blob(const blobdata_ref& tx_blob, transaction_prefix& tx)
  {
    std::stringstream ss;
    ss << tx_blob;
    binary_archive<false> ba(ss);
    bool r = ::serialization::serialize_noeof(ba, tx);
    CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction prefix from blob");
    return true;
  }

  //---------------------------------------------------------------
  bool generate_key_image_helper(const account_keys& ack,   const crypto::public_key& tx_public_key,  size_t oi, keypair& otk_p, crypto::key_image& ki)
  {
    crypto::key_derivation kA{};
    //rGa=rA
    //H(rA)+b
    const auto & a= ack.m_view_secret_key;
    const auto & b= ack.m_spend_secret_key;
    bool r = crypto::generate_key_derivation(tx_public_key, a, kA);
    if (!r)
    {
      MWARNING("key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
      memcpy(&kA, rct::identity().bytes, sizeof(kA));
    }

 //computes Hs(a*R || idx) + b
      crypto::derive_secret_key(kA, oi, b, otk_p.sec); //

      CHECK_AND_ASSERT_MES(crypto::secret_key_to_public_key(otk_p.sec, otk_p.pub), false, "Failed to derive public key");
    crypto::generate_key_image(otk_p.pub, otk_p.sec, ki);
    return true;

  }
  //---------------------------------------------------------------
  uint64_t power_integral(uint64_t a, uint64_t b)
  {
    if(b == 0)
      return 1;
    uint64_t total = a;
    for(uint64_t i = 1; i != b; i++)
      total *= a;
    return total;
  }
  //---------------------------------------------------------------
  bool parse_amount(uint64_t& amount, const std::string& str_amount_)
  {
    std::string str_amount = str_amount_;
    boost::algorithm::trim(str_amount);

    size_t point_index = str_amount.find_first_of('.');
    size_t fraction_size;
    if (std::string::npos != point_index)
    {
      fraction_size = str_amount.size() - point_index - 1;
      while (default_decimal_point < fraction_size && '0' == str_amount.back())
      {
        str_amount.erase(str_amount.size() - 1, 1);
        --fraction_size;
      }
      if (default_decimal_point < fraction_size)
        return false;
      str_amount.erase(point_index, 1);
    }
    else
    {
      fraction_size = 0;
    }

    if (str_amount.empty())
      return false;

    if (fraction_size < default_decimal_point)
    {
      str_amount.append(default_decimal_point - fraction_size, '0');
    }

    return string_tools::get_xtype_from_string(amount, str_amount);
  }

  //---------------------------------------------------------------
  uint64_t get_transaction_weight(const transaction &tx)
  {
     const auto w = tx.vin.size()+tx.vout.size();
    return w;
  }
  //---------------------------------------------------------------
  bool get_tx_fee(const transaction& tx, uint64_t & fee)
  {
      fee = tx.rct_signatures.txnFee;
      return true;
  }
  //---------------------------------------------------------------
  uint64_t get_tx_fee(const transaction& tx)
  {
    uint64_t r = 0;
    if(!get_tx_fee(tx, r))
      return 0;
    return r;
  }

  //---------------------------------------------------------------
  uint64_t get_block_height(const block& b)
  {
    CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, 0, "wrong miner tx in block: " << get_block_hash(b) << ", b.miner_tx.vin.size() != 1");
    CHECKED_GET_SPECIFIC_VARIANT(b.miner_tx.vin[0], const txin_gen, coinbase_in, 0);
    return coinbase_in.height;
  }
  //---------------------------------------------------------------
  bool check_inputs_types_supported(const transaction& tx)
  {
    for(const auto& in: tx.vin)
    {
      CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), false, "wrong variant type: "
        << in.type().name() << ", expected " << typeid(txin_to_key).name()
        << ", in transaction id=" << get_transaction_hash(tx));

    }
    return true;
  }
  //-----------------------------------------------------------------------------------------------
  bool check_outs_valid(const transaction& tx)
  {
    for(const tx_out& out: tx.vout)
    {
      CHECK_AND_ASSERT_MES(out.target.type() == typeid(txout_to_key), false, "wrong variant type: "
        << out.target.type().name() << ", expected " << typeid(txout_to_key).name()
        << ", in transaction id=" << get_transaction_hash(tx));

      if(!check_key(boost::get<txout_to_key>(out.target).key))
        return false;
    }
    return true;
  }
  //-----------------------------------------------------------------------------------------------
  bool check_money_overflow(const transaction& tx)
  {
    return check_inputs_overflow(tx) && check_outs_overflow(tx);
  }
  //---------------------------------------------------------------
  bool check_inputs_overflow(const transaction& tx)
  {
    uint64_t money = 0;
    for(const auto& in: tx.vin)
    {
      CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false);
      if(money > tokey_in.amount + money)
        return false;
      money += tokey_in.amount;
    }
    return true;
  }
  //---------------------------------------------------------------
  bool check_outs_overflow(const transaction& tx)
  {
    uint64_t money = 0;
    for(const auto& o: tx.vout)
    {
      if(money > o.amount + money)
        return false;
      money += o.amount;
    }
    if(tx.vout.size()>2)
      return false;
    return true;
  }
  //---------------------------------------------------------------
  uint64_t get_outs_money_amount(const transaction& tx)
  {
    uint64_t outputs_amount = 0;
    for(const auto& o: tx.vout)
      outputs_amount += o.amount;
    return outputs_amount;
  }
  //---------------------------------------------------------------
  std::string short_hash_str(const crypto::hash& h)
  {
    std::string res = string_tools::pod_to_hex(h);
    CHECK_AND_ASSERT_MES(res.size() == 64, res, "wrong hash256 with string_tools::pod_to_hex conversion");
    auto erased_pos = res.erase(8, 48);
    res.insert(8, "....");
    return res;
  }
  //---------------------------------------------------------------
  bool is_out_to_acc(const account_keys& acc, const txout_to_key& otk, const crypto::public_key& tx_pub_key, size_t output_index)
  {
    crypto::key_derivation derivation;
    bool r = crypto::generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation);
    CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
    crypto::public_key pk;
    r = crypto::derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
    CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key");
    if (pk == otk.key)
      return true;

    return false;
  }

  //---------------------------------------------------------------
  bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered)
  {
    crypto::public_key tx_pub_key = tx.tx_pub_key;
    if(null_pkey == tx_pub_key)
      return false;
    return lookup_acc_outs(acc, tx, tx_pub_key,  outs, money_transfered);
  }
  //---------------------------------------------------------------
  bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key,  std::vector<size_t>& outs, uint64_t& money_transfered)
  {

    money_transfered = 0;
    size_t i = 0;
    for(const tx_out& o:  tx.vout)
    {
      CHECK_AND_ASSERT_MES(o.target.type() ==  typeid(txout_to_key), false, "wrong type id in transaction out" );
      if(is_out_to_acc(acc, boost::get<txout_to_key>(o.target), tx_pub_key,  i))
      {
        outs.push_back(i);
        money_transfered += o.amount;
      }
      i++;
    }
    return true;
  }
  //---------------------------------------------------------------
  void get_blob_hash(const blobdata_ref& blob, crypto::hash& res)
  {
    cn_fast_hash(blob.data(), blob.size(), res);
  }
  //---------------------------------------------------------------
  void get_blob_hash(const blobdata& blob, crypto::hash& res)
  {
    cn_fast_hash(blob.data(), blob.size(), res);
  }
  //---------------------------------------------------------------
  void set_default_decimal_point(unsigned int decimal_point)
  {
    switch (decimal_point)
    {
      case 12:
      case 9:
      case 6:
      case 3:
      case 0:
        default_decimal_point = decimal_point;
        break;
      default:
        throw_and_log("Invalid decimal point specification: " << decimal_point);
    }
  }
  //---------------------------------------------------------------
  unsigned int get_default_decimal_point()
  {
    return default_decimal_point;
  }
  //---------------------------------------------------------------
  std::string get_unit(unsigned int decimal_point)
  {
    if (decimal_point == (unsigned int)-1)
      decimal_point = default_decimal_point;
    switch (decimal_point)
    {
      case 8:
        return "dfa";
      case 5:
        return "mdf";
      case 0:
        return "pdf";
      default:
        throw_and_log("Invalid decimal point specification: " << decimal_point);
    }
  }
  //---------------------------------------------------------------
  static void insert_money_decimal_point(std::string &s, unsigned int decimal_point)
  {
    if (decimal_point == (unsigned int)-1)
      decimal_point = default_decimal_point;
    if(s.size() < decimal_point+1)
    {
      s.insert(0, decimal_point+1 - s.size(), '0');
    }
    if (decimal_point > 0)
      s.insert(s.size() - decimal_point, ".");
  }
  //---------------------------------------------------------------
  std::string print_money(uint64_t amount, unsigned int decimal_point)
  {
    std::string s = std::to_string(amount);
    insert_money_decimal_point(s, decimal_point);
    return s;
  }
  //---------------------------------------------------------------
  std::string print_money(const boost::multiprecision::uint128_t &amount, unsigned int decimal_point)
  {
    std::stringstream ss;
    ss << amount;
    std::string s = ss.str();
    insert_money_decimal_point(s, decimal_point);
    return s;
  }
  //---------------------------------------------------------------
  crypto::hash get_blob_hash(const blobdata& blob)
  {
    crypto::hash h = null_hash;
    get_blob_hash(blob, h);
    return h;
  }
  //---------------------------------------------------------------
  crypto::hash get_blob_hash(const blobdata_ref& blob)
  {
    crypto::hash h = null_hash;
    get_blob_hash(blob, h);
    return h;
  }

  //---------------------------------------------------------------
  bool calculate_transaction_prunable_hash(const transaction& t, const cryptonote::blobdata_ref *blob, crypto::hash& res)
  {
    const unsigned int unprunable_size = t.unprunable_size;
    if (blob && unprunable_size)
    {
      CHECK_AND_ASSERT_MES(unprunable_size <= blob->size(), false, "Inconsistent transaction unprunable and blob sizes");
      cryptonote::get_blob_hash(blobdata_ref(blob->data() + unprunable_size, blob->size() - unprunable_size), res);
    }
    else
    {
      if(pruned())
        throw_and_log("cannot calculate purnnable hash from prunned tx");

      transaction &tt = const_cast<transaction&>(t);
      std::stringstream ss;
      binary_archive<true> ba(ss);
      const size_t inputs = t.vin.size();
      const size_t outputs = t.vout.size();
      const size_t mixin = t.vin.empty() ? 0 : t.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(t.vin[0]).key_offsets.size() - 1 : 0;
      bool r = tt.rct_signatures.p.serialize_rctsig_prunable(ba, t.rct_signatures.type, inputs, outputs, mixin);
      CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures prunable");
      cryptonote::get_blob_hash(ss.str(), res);
    }
    return true;
  }
  //---------------------------------------------------------------
  crypto::hash get_transaction_prunable_hash(const transaction& t, const cryptonote::blobdata_ref *blobdata)
  {
    crypto::hash res;
    if (t.is_prunable_hash_valid())
    {
      res = t.prunable_hash;
      ++tx_hashes_cached_count;
      return res;
    }

    ++tx_hashes_calculated_count;
    res = calculate_transaction_prunable_hash(t, blobdata);
    t.set_prunable_hash(res);
    return res;
  }
  //---------------------------------------------------------------
  crypto::hash get_pruned_transaction_hash(const transaction& t, const crypto::hash &pruned_data_hash)
  {

    // v2 transactions hash different parts together, than hash the set of those hashes
    crypto::hash hashes[3];

    // prefix
    hashes[0] = get_transaction_prefix_hash(t);

    transaction &tt = const_cast<transaction&>(t);

    // base rct
    {
      std::stringstream ss;
      binary_archive<true> ba(ss);
      const size_t inputs = t.vin.size();
      const size_t outputs = t.vout.size();
      bool r = tt.rct_signatures.serialize_rctsig_base(ba, inputs, outputs);
      CHECK_AND_ASSERT_THROW_MES(r, "Failed to serialize rct signatures base");
      hashes[1]=cryptonote::get_blob_hash(ss.str());
    }

    // prunable rct
    if (t.rct_signatures.type == rct::RCTTypeNull)
      hashes[2] = crypto::null_hash;
    else
      hashes[2] = pruned_data_hash;

    // the tx hash is the hash of the 3 hashes
    crypto::hash res = cn_fast_hash(hashes, sizeof(hashes));
    t.set_hash(res);
    return res;
  }
  //---------------------------------------------------------------
  crypto::hash calculate_transaction_hash(const transaction& t)
  {
    CHECK_AND_ASSERT_MES(!t.pruned, false, "Cannot calculate the hash of a pruned transaction");

    // v2 transactions hash different parts together, than hash the set of those hashes
    crypto::hash hashes[3];

    // prefix
    hashes[0]=get_transaction_prefix_hash(t);

    const blobdata blob = tx_to_blob(t);
    const unsigned int unprunable_size = t.unprunable_size;
    const unsigned int prefix_size = t.prefix_size;

    // base rct
    CHECK_AND_ASSERT_MES(prefix_size <= unprunable_size && unprunable_size <= blob.size(), false, "Inconsistent transaction prefix, unprunable and blob sizes "<<prefix_size<<","<<unprunable_size<<","<<blob.size());

     hashes[1] =cryptonote::get_blob_hash(blobdata_ref(blob.data() + prefix_size, unprunable_size - prefix_size));

    // prunable rct
    if (t.rct_signatures.type == rct::RCTTypeNull)
    {
      hashes[2] = crypto::null_hash;
    }
    else
    {
        if(tx.is_prunable_hash_valid()){
          hashes[2]=tx.prunable_hash;
        }
        else{
        cryptonote::blobdata_ref blobref(blob);
        hashes[2]=calculate_transaction_prunable_hash(t, &blobref);
      }
    }

    // the tx hash is the hash of the 3 hashes
    const auto h = cn_fast_hash(hashes, sizeof(hashes));
    return h;
  }
  //---------------------------------------------------------------
  crypto::hash get_transaction_hash(const transaction& t)
  {
    if (t.is_hash_valid())
    {
      ++tx_hashes_cached_count;
      return t.hash;
    }

    ++tx_hashes_calculated_count;
    res = calculate_transaction_hash(t);
    t.set_hash(res);
    return res;
  }

  //---------------------------------------------------------------
  blobdata get_block_hashing_blob(const block& b)
  {
    //header+tx
    const block_header & header=b;
    blobdata blob = t_serializable_object_to_blob(header);
    crypto::hash tree_root_hash = get_tx_tree_hash(b);
    blob.append(reinterpret_cast<const char*>(&tree_root_hash), sizeof(tree_root_hash));
    blob.append(tools::get_varint_data(b.tx_hashes.size()+1));
    return blob;
  }
  //---------------------------------------------------------------
  bool calculate_block_hash(const block& b, crypto::hash& res, const blobdata_ref *blob)
  {

    bool hash_result = get_object_hash(get_block_hashing_blob(b), res);
    if (!hash_result)
      return false;

    return hash_result;
  }
  //---------------------------------------------------------------
  bool get_block_hash(const block& b, crypto::hash& res)
  {
    if (b.is_hash_valid())
    {
#ifdef ENABLE_HASH_CASH_INTEGRITY_CHECK
      CHECK_AND_ASSERT_THROW_MES(!calculate_block_hash(b, res) || b.hash == res, "block hash cash integrity failure");
#endif
      res = b.hash;
      ++block_hashes_cached_count;
      return true;
    }
    ++block_hashes_calculated_count;
    bool ret = calculate_block_hash(b, res);
    if (!ret)
      return false;
    b.set_hash(res);
    return true;
  }
  //---------------------------------------------------------------
  crypto::hash get_block_hash(const block& b)
  {
    crypto::hash p = null_hash;
    get_block_hash(b, p);
    return p;
  }
  //---------------------------------------------------------------
  std::vector<uint64_t> relative_output_offsets_to_absolute(const std::vector<uint64_t>& off)
  {
    std::vector<uint64_t> res = off;
    for(size_t i = 1; i < res.size(); i++)
      res[i] += res[i-1];
    return res;
  }
  //---------------------------------------------------------------
  std::vector<uint64_t> absolute_output_offsets_to_relative(const std::vector<uint64_t>& off)
  {
    std::vector<uint64_t> res = off;
    if(!off.size())
      return res;
    std::sort(res.begin(), res.end());//just to be sure, actually it is already should be sorted
    for(size_t i = res.size()-1; i != 0; i--)
      res[i] -= res[i-1];

    return res;
  }
  //---------------------------------------------------------------
  block parse_block_from_blob(const blobdata_ref& b_blob)
  {

     if(b_blob.size() > 16*1024*1024)
    {
       throw std::runtime_error("too large block");
    }
    block b;
    std::stringstream ss;
    ss << b_blob;
    binary_archive<false> ba(ss);
    bool r = ::serialization::serialize(ba, b);
    if(!r)
    {
      std::ostringstream ost;
      ost<<"Failed to parse block from blob ";
      ost<<to_hex::string(to_byte_span(to_span(b_blob)));
     throw std::runtime_error(ost.str());
    }
    b.invalidate_hashes();
    b.miner_tx.invalidate_hashes();
    return b;
  }

  //---------------------------------------------------------------
  blobdata block_to_blob(const block& b)
  {
    return t_serializable_object_to_blob(b);
  }
  //---------------------------------------------------------------
  bool block_to_blob(const block& b, blobdata& b_blob)
  {
    return t_serializable_object_to_blob(b, b_blob);
  }
  //---------------------------------------------------------------
  blobdata tx_to_blob(const transaction& tx)
  {
    return t_serializable_object_to_blob(tx);
  }
  //---------------------------------------------------------------
  bool tx_to_blob(const transaction& tx, blobdata& b_blob)
  {
    return t_serializable_object_to_blob(tx, b_blob);
  }

  //---------------------------------------------------------------
  crypto::hash get_tx_tree_hash(const block& b)
  {
    std::vector<crypto::hash> tx_hashes;
    tx_hashes.reserve(1 + b.tx_hashes.size());
   const crypto::hash miner_h = get_transaction_hash(b.miner_tx);
    tx_hashes.push_back(miner_h);
    for(auto& th: b.tx_hashes)
      tx_hashes.push_back(th);

    crypto::hash h = null_hash;
    tree_hash(tx_hashes.data(), tx_hashes.size(), h);
    return h;
  }
  //---------------------------------------------------------------
  bool is_valid_decomposed_amount(uint64_t amount)
  {
    const uint64_t *begin = valid_decomposed_outputs;
    const uint64_t *end = valid_decomposed_outputs + sizeof(valid_decomposed_outputs) / sizeof(valid_decomposed_outputs[0]);
    return std::binary_search(begin, end, amount);
  }
  //---------------------------------------------------------------
  void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached)
  {
    tx_hashes_calculated = tx_hashes_calculated_count;
    tx_hashes_cached = tx_hashes_cached_count;
    block_hashes_calculated = block_hashes_calculated_count;
    block_hashes_cached = block_hashes_cached_count;
  }
  //---------------------------------------------------------------
  crypto::secret_key encrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
  {
    crypto::hash hash;
    crypto::cn_fast_hash(passphrase.data(), passphrase.size(), hash);
    sc_add((unsigned char*)key.data, (const unsigned char*)key.data, (const unsigned char*)hash.data);
    return key;
  }
  //---------------------------------------------------------------
  crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
  {
    crypto::hash hash;
    crypto::cn_fast_hash(passphrase.data(), passphrase.size(), hash);
    sc_sub((unsigned char*)key.data, (const unsigned char*)key.data, (const unsigned char*)hash.data);
    return key;
  }
}


//| This file is a part of the sferes2 framework.
//| Copyright 2009, ISIR / Universite Pierre et Marie Curie (UPMC)
//| Main contributor(s): Jean-Baptiste Mouret, mouret@isir.fr
//|
//| This software is a computer program whose purpose is to facilitate
//| experiments in evolutionary computation and evolutionary robotics.
//|
//| This software is governed by the CeCILL license under French law
//| and abiding by the rules of distribution of free software.  You
//| can use, modify and/ or redistribute the software under the terms
//| of the CeCILL license as circulated by CEA, CNRS and INRIA at the
//| following URL "http://www.cecill.info".
//|
//| As a counterpart to the access to the source code and rights to
//| copy, modify and redistribute granted by the license, users are
//| provided only with a limited warranty and the software's author,
//| the holder of the economic rights, and the successive licensors
//| have only limited liability.
//|
//| In this respect, the user's attention is drawn to the risks
//| associated with loading, using, modifying and/or developing or
//| reproducing the software by the user in light of its specific
//| status of free software, that may mean that it is complicated to
//| manipulate, and that also therefore means that it is reserved for
//| developers and experienced professionals having in-depth computer
//| knowledge. Users are therefore encouraged to load and test the
//| software's suitability as regards their requirements in conditions
//| enabling the security of their systems and/or data to be ensured
//| and, more generally, to use and operate it in the same conditions
//| as regards security.
//|
//| The fact that you are presently reading this means that you have
//| had knowledge of the CeCILL license and that you accept its terms.

#ifndef PNSGA_HPP_
#define PNSGA_HPP_

#include <algorithm>
#include <limits>

#include <boost/foreach.hpp>

#include <sferes/stc.hpp>
#include <sferes/parallel.hpp>
#include <sferes/ea/ea.hpp>
#include <sferes/fit/fitness.hpp>
#include <sferes/ea/dom_sort_basic.hpp>
#include <sferes/ea/common.hpp>
#include <sferes/ea/crowd.hpp>

#ifdef ENABLE_TIMING
#include "clock.hpp"
#endif

namespace sferes
{
namespace ea
{
namespace pnsga
{
SFERES_CLASS(prob_dom_f){
public:
    template<typename Indiv>
    inline bool operator() (const Indiv &ind, const std::vector<Indiv>&pop) const{
        dbg::trace trace("dom_sort_trace" , DBG_HERE);
        BOOST_FOREACH(Indiv i, pop){
            if (dominate_flag(i, ind) == 1)
                return false;
        }
        return true;
    }

    template<typename I1, typename I2>
    inline int dominate_flag(const I1& i1, const I2& i2) const
    {
        dbg::trace trace("dom_sort_trace" , DBG_HERE);
        assert(i1->fit().objs().size());
        assert(i2->fit().objs().size());
        assert(i1->fit().objs().size() == i2->fit().objs().size());

        size_t nb_objs = i1->fit().objs().size();
        assert(nb_objs);

        bool flag1 = false, flag2 = false;
        for (unsigned i = 0; i < nb_objs; ++i)
        {
            assert(i < Params::ea::obj_pressure_size());
            float pressure = Params::ea::obj_pressure(i);;
#ifdef GRADUAL
            if(i == 1) pressure = Params::ea::pressure;
#endif
            if (misc::rand<float>() > pressure)
                continue;
            float fi1 = i1->fit().obj(i);
            float fi2 = i2->fit().obj(i);
            if (fi1 > fi2)
                flag1 = true;
            else
                if (fi2 > fi1)
                    flag2 = true;
        }
        if (flag1 && !flag2)
            return 1;
        else
            if (!flag1 && flag2)
                return -1;
            else
                return 0;
    }
};
}
// Main class
SFERES_EA(Pnsga, Ea)
{
public:
    typedef boost::shared_ptr<crowd::Indiv<Phen> > indiv_t;
    typedef typename std::vector<indiv_t> pop_t;
    typedef typename pop_t::iterator it_t;
    typedef typename std::vector<std::vector<indiv_t> > front_t;

    void random_pop()
    {
        parallel::init();

        _parent_pop.resize(Params::pop::size);
        assert(Params::pop::size % 4 == 0);

        pop_t init_pop((size_t)(Params::pop::size * Params::pop::initial_aleat));
        parallel::p_for(parallel::range_t(0, init_pop.size()), random<crowd::Indiv<Phen> >(init_pop));
        _init_pop(init_pop);
    }

    void epoch()
    {
        //Clearing populations

        _mixed_pop.clear();
        _child_pop.clear();
        this->_pop.clear();
        _pareto_front.clear();

        //Parent selection
#ifdef ENABLE_TIMING
        _epoch_clock.resetAndStart();
        _parent_selection_clock.resetAndStart();
#endif
        _selection (_parent_pop, _child_pop);

#ifdef ENABLE_TIMING
        _parent_selection_clock.stop();
        _mutate_clock.resetAndStart();
#endif

        //Mutation (and crossover, if applicable)

        parallel::p_for(parallel::range_t(0, _child_pop.size()),
                mutate<crowd::Indiv<Phen> >(_child_pop));

#ifdef ENABLE_TIMING
        _mutate_clock.stop();
        _eval_clock.resetAndStart();
#endif

        //Evaluation
#ifndef EA_EVAL_ALL
        _eval_pop(_child_pop);
        _merge(_parent_pop, _child_pop, _mixed_pop);
#else
        _merge(_parent_pop, _child_pop, _mixed_pop);
        _eval_pop(_mixed_pop);
#endif

#ifdef ENABLE_TIMING
        _eval_clock.stop();
        _mod_clock.resetAndStart();
#endif

        //Apply modifiers (such as behavioral diversity)
        _apply_modifier(_mixed_pop);

#ifdef ENABLE_TIMING
        _mod_clock.stop();
        _survivor_selection_clock.resetAndStart();
#endif

#ifndef NDEBUG
        BOOST_FOREACH(indiv_t & ind, _mixed_pop)
        for (size_t i = 0; i < ind->fit().objs().size(); ++i)
        {
            assert(!std::isnan(ind->fit().objs()[i]));
        }
#endif
        _fill_nondominated_sort(_mixed_pop, _parent_pop);
        _convert_pop(_parent_pop, this->_pop);

#ifdef ENABLE_TIMING
        _survivor_selection_clock.stop();
        _epoch_clock.stop();
#endif
        assert(_parent_pop.size() == Params::pop::size);
        assert(_pareto_front.size() <= Params::pop::size * 2);
        assert(this->_pop.size() == Params::pop::size);
    }

    const std::vector<boost::shared_ptr<Phen> >& pareto_front() const {
        return _pareto_front;
    }

    const pop_t& mixed_pop() const{
        return _mixed_pop;
    }

    const pop_t& parent_pop() const{
        return _parent_pop;
    }

    const pop_t& child_pop() const{
        return _child_pop;
    }

    void init_pop(std::vector<boost::shared_ptr<Phen> > population){
        //        	std::cout << "Reading population: pnsga" << std::endl;
        _mixed_pop.clear();
        for (size_t i = 0; i < population.size(); ++i){
            indiv_t indiv (new crowd::Indiv<Phen>(*population[i]));
            _mixed_pop.push_back(indiv);
        }
        _init_pop(_mixed_pop);
        _convert_pop(_parent_pop, this->_pop);
    }

    void init_parent_pop(std::vector<boost::shared_ptr<Phen> > population){
        dbg::assertion(DBG_ASSERTION(population.size() <= Params::pop::size));
        _parent_pop.clear();
        for (size_t i = 0; i < Params::pop::size; ++i){
            indiv_t indiv (new crowd::Indiv<Phen>(*population[i]));
            _parent_pop.push_back(indiv);
        }
    }

    void set_gen(size_t gen){
        this->_gen = gen;
    }

    void best_pop(std::vector<boost::shared_ptr<Phen> >& population){
        //        	std::cout << "Reading population: pnsga" << std::endl;
        _mixed_pop.clear();
        for (size_t i = 0; i < Params::pop::size*2; ++i){
            indiv_t indiv (new crowd::Indiv<Phen>());
            indiv->gen() = population[0]->gen();
            _mixed_pop.push_back(indiv);
        }
        _init_pop(_mixed_pop);
        _convert_pop(_parent_pop, this->_pop);
    }

#ifdef ENABLE_TIMING
public:
    //Timing related functions
    double getEpochTime() const{
        return _epoch_clock.time();
    }

    double getParentSelectionTime() const{
        return _parent_selection_clock.time();
    }

    double getMutateTime() const{
        return _mutate_clock.time();
    }

    double getEvalTime() const{
        return _eval_clock.time();
    }

    double getModTime() const{
        return _mod_clock.time();
    }

    double getSurvivorSelectionTime() const{
        return _survivor_selection_clock.time();
    }

protected:
    //Timing related variables
    Clock _epoch_clock;
    Clock _parent_selection_clock;
    Clock _mutate_clock;
    Clock _eval_clock;
    Clock _mod_clock;
    Clock _survivor_selection_clock;
#endif

protected:

    std::vector<boost::shared_ptr<Phen> > _pareto_front;

    pop_t _parent_pop;
    pop_t _child_pop;
    pop_t _mixed_pop;

    void _init_pop(pop_t& init_pop)
    {
        _eval_pop(init_pop);
        _apply_modifier(init_pop);
        front_t fronts;
        _rank_crowd(init_pop, fronts);
        _fill_nondominated_sort(init_pop, _parent_pop);

    }

    void _update_pareto_front(const front_t& fronts)
    {
        _convert_pop(fronts.front(), _pareto_front);
    }

    void _convert_pop(const pop_t& pop1, std::vector<boost::shared_ptr<Phen> >& pop2)
    {
        pop2.resize(pop1.size());
        for (size_t i = 0; i < pop1.size(); ++i)
            pop2[i] = pop1[i];
    }

    void _eval_pop(pop_t& pop)
    {
        this->_eval.eval(pop, 0, pop.size(), this->_fit_proto);
    }

    void _apply_modifier(pop_t& pop)
    {
        _convert_pop(pop, this->_pop);
        this->apply_modifier();
    }

    void _fill_nondominated_sort(pop_t& mixed_pop, pop_t& new_pop)
    {
        assert(mixed_pop.size());
        front_t fronts;
#ifndef NDEBUG
        BOOST_FOREACH(indiv_t & ind, mixed_pop)
        for (size_t i = 0; i < ind->fit().objs().size(); ++i)
        {
            assert(!std::isnan(ind->fit().objs()[i]));
        }
#endif
        _rank_crowd(mixed_pop, fronts);
        new_pop.clear();

        // fill the i first layers
        size_t i;
        for (i = 0; i < fronts.size(); ++i)
            if (fronts[i].size() + new_pop.size() < Params::pop::size)
                new_pop.insert(new_pop.end(), fronts[i].begin(), fronts[i].end());
            else
                break;

        size_t size = Params::pop::size - new_pop.size();
        // sort the last layer
        if (new_pop.size() < Params::pop::size)
        {
            std::sort(fronts[i].begin(), fronts[i].end(), crowd::compare_crowd());
            for (size_t k = 0; k < size; ++k)
            {
                assert(i < fronts.size());
                new_pop.push_back(fronts[i][k]);
            }
        }
        assert(new_pop.size() == Params::pop::size);
    }

    //
    void _merge(const pop_t& pop1, const pop_t& pop2, pop_t& pop3)
    {
        assert(pop1.size());
        assert(pop2.size());
        pop3.clear();
        pop3.insert(pop3.end(), pop1.begin(), pop1.end());
        pop3.insert(pop3.end(), pop2.begin(), pop2.end());
        assert(pop3.size() == pop1.size() + pop2.size());
    }

    // --- tournament selection ---
    void _selection(pop_t& old_pop, pop_t& new_pop)
    {
        new_pop.resize(old_pop.size());
        std::vector<size_t> a1, a2;
        misc::rand_ind(a1, old_pop.size());
        misc::rand_ind(a2, old_pop.size());
        // todo : this loop could be parallelized
        for (size_t i = 0; i < old_pop.size(); i += 4)
        {
            const indiv_t& p1 = _tournament(old_pop[a1[i]], old_pop[a1[i + 1]]);
            const indiv_t& p2 = _tournament(old_pop[a1[i + 2]], old_pop[a1[i + 3]]);
            const indiv_t& p3 = _tournament(old_pop[a2[i]], old_pop[a2[i + 1]]);
            const indiv_t& p4 = _tournament(old_pop[a2[i + 2]], old_pop[a2[i + 3]]);
            assert(i + 3 < new_pop.size());
            p1->cross(p2, new_pop[i], new_pop[i + 1]);
            p3->cross(p4, new_pop[i + 2], new_pop[i + 3]);
        }
    }

    const indiv_t& _tournament(const indiv_t& i1, const indiv_t& i2)
    {
        pnsga::prob_dom_f<Params> p;
        int flag = p.dominate_flag(i1, i2);
        if (flag == 1)
            return i1;
        if (flag == -1)
            return i2;
        if (i1->crowd() > i2->crowd())
            return i1;
        if (i1->crowd() < i2->crowd())
            return i2;
        if (misc::flip_coin())
            return i1;
        else
            return i2;
    }

    // --- rank & crowd ---
    void _rank_crowd(pop_t& pop, front_t& fronts){
        std::vector<size_t> ranks;
#ifndef NDEBUG
        BOOST_FOREACH(indiv_t& ind, pop)
        for (size_t i = 0; i < ind->fit().objs().size(); ++i)
        { assert(!std::isnan(ind->fit().objs()[i])); }
#endif
        typename Params::ea::dom_sort_f()(pop, fronts, pnsga::prob_dom_f<Params>(), ranks);
        _update_pareto_front(fronts);
        parallel::p_for(parallel::range_t(0, fronts.size()),
                crowd::assign_crowd<indiv_t >(fronts));

        for (size_t i = 0; i < ranks.size(); ++i)
            pop[i]->set_rank(ranks[i]);
        parallel::sort(pop.begin(), pop.end(), crowd::compare_ranks());;
    }

};
}
}
#endif