#ifndef _ADD_CPP_
#define _ADD_CPP_

#include <stddef.h> //size_t
#include <iostream> //cout
#include <stdio.h> //printf

//Eigen
#ifdef USE_EIGEN
#include <Eigen/Dense>
#endif //USE_EIGEN

#include "add.h"

#ifdef USE_CUDA
/* includes CUDA kernel */
#include "cuda/gpuadd.cuh"
#else
#include "cpuadd.h"
#endif //USE_CUDA

void add(double *A, double *B, double *C, size_t Am, size_t An)
{

#ifdef USE_EIGEN
  std::cout << "But before, print a dummy matrix to show that Eigen is used in the Mex and binary case:" << std::endl;
  Eigen::MatrixXd my_dummy_matrix(2,2);
  my_dummy_matrix(0,0) = 3;
  my_dummy_matrix(1,0) = 2.5;
  my_dummy_matrix(0,1) = -1;
  my_dummy_matrix(1,1) = my_dummy_matrix(1,0) + my_dummy_matrix(0,1);
  std::cout << "my_dummy_matrix" << my_dummy_matrix << std::endl << std::endl;
#endif //USE_EIGEN

  std::cout << "Ok, now really add the two values:" << std::endl;
#ifdef USE_CUDA
  std::cout << "With CUDA support" << std::endl;
  gpuadd(A, B, C, Am, An);
#else
  std::cout << "Without CUDA support" << std::endl;
	cpuadd(A, B, C, Am, An);
#endif

}

#endif


// Copyright (c) 2014-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "chainparams.h"
#include "validation.h"
#include "net.h"

#include "test/test_bitcoin.h"

#include <boost/signals2/signal.hpp>
#include <boost/test/unit_test.hpp>

BOOST_FIXTURE_TEST_SUITE(main_tests, TestingSetup)

static int Halvings2Height(int nHalvings, const Consensus::Params& consensusParams)
{
    if (consensusParams.BCXHeight > 0) {
        if (nHalvings < 3) {
            return nHalvings * consensusParams.nSubsidyHalvingInterval;
        } else if (nHalvings == 3) {
            return 1154444;
        } else {
            return 1154443 + (nHalvings - 3) * consensusParams.nBCXSubsidyHalvingInterval;
        }
    } else {
       return nHalvings * consensusParams.nSubsidyHalvingInterval;
    }
}

static void TestBlockSubsidyHalvings(const Consensus::Params& consensusParams)
{
    const int maxHalvings = 64;
    const CAmount nInitialSubsidy = 50 * COIN * BTC_2_BCX_RATE;
    const int bcx2btcHalvingRate = consensusParams.nBCXSubsidyHalvingInterval / consensusParams.nSubsidyHalvingInterval;

    CAmount nPreviousSubsidy = nInitialSubsidy * 2; // for height == 0
    BOOST_CHECK_EQUAL(nPreviousSubsidy, nInitialSubsidy * 2);
    for (int nHalvings = 0; nHalvings < maxHalvings; nHalvings++) {
        const int nHeight = Halvings2Height(nHalvings, consensusParams);
        CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams);
        if (0 < consensusParams.BCXHeight && consensusParams.BCXHeight <= nHeight) {
            nSubsidy = nSubsidy * bcx2btcHalvingRate;
            BOOST_CHECK((nSubsidy == nPreviousSubsidy / 2) || ((nSubsidy + 2) == nPreviousSubsidy / 2));
        } else {
            BOOST_CHECK_EQUAL(nSubsidy, nPreviousSubsidy / 2);
        }
        BOOST_CHECK(nSubsidy <= nInitialSubsidy);
        nPreviousSubsidy = nSubsidy;
    }
    BOOST_CHECK_EQUAL(GetBlockSubsidy(Halvings2Height(maxHalvings, consensusParams), consensusParams), 0);
}

static void TestBlockSubsidyHalvings(int nSubsidyHalvingInterval)
{
    Consensus::Params consensusParams;
    consensusParams.nSubsidyHalvingInterval = nSubsidyHalvingInterval;
    consensusParams.BCXHeight = 0;
    TestBlockSubsidyHalvings(consensusParams);
}

BOOST_AUTO_TEST_CASE(block_subsidy_test)
{
    const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
    TestBlockSubsidyHalvings(chainParams->GetConsensus()); // As in main
    TestBlockSubsidyHalvings(150); // As in regtest
    TestBlockSubsidyHalvings(1000); // Just another interval
}

BOOST_AUTO_TEST_CASE(subsidy_limit_test)
{
    const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
    const CAmount nInitialSubsidy = 50 * COIN * BTC_2_BCX_RATE;
    CAmount nSum = 0;
    for (int nHeight = 0; nHeight < 30000000; nHeight++) {
        CAmount nSubsidy = GetBlockSubsidy(nHeight, chainParams->GetConsensus());
        if (nSubsidy == 0) {
            break;
        }

        if (nHeight != chainParams->GetConsensus().BCXHeight) {
            BOOST_CHECK(nSubsidy <= nInitialSubsidy);
        }
        nSum += nSubsidy;
        BOOST_CHECK(MoneyRange(nSum));
    }
    BOOST_CHECK_EQUAL(nSum, 2099999986350000ULL);
}

bool ReturnFalse() { return false; }
bool ReturnTrue() { return true; }

BOOST_AUTO_TEST_CASE(test_combiner_all)
{
    boost::signals2::signal<bool (), CombinerAll> Test;
    BOOST_CHECK(Test());
    Test.connect(&ReturnFalse);
    BOOST_CHECK(!Test());
    Test.connect(&ReturnTrue);
    BOOST_CHECK(!Test());
    Test.disconnect(&ReturnFalse);
    BOOST_CHECK(Test());
    Test.disconnect(&ReturnTrue);
    BOOST_CHECK(Test());
}
BOOST_AUTO_TEST_SUITE_END()


#ifndef SIMULATION_HPP
#define SIMULATION_HPP

#include <core/graph.hpp>
#include <core/stack.hpp>
#include <core/graph_data.hpp>

// TODO move these guys to visitor?
#include "typecheck.hpp"
#include "numbering.hpp"
#include "push.hpp"
#include "fetch.hpp"
#include "pull.hpp"
#include "select.hpp"

#include "concatenate.hpp"
#include "integrate.hpp"

#include "init.hpp"
#include "reset.hpp"

#include <Eigen/SparseCholesky>

struct simulation {

  // TODO non-member, pass to step?
  vec3 gravity = {0, -9.81, 0};

  template<class F>
  static void with_auto_resize( const F& f ) {
    while (true) {
      try{
        f();
        return;
      } catch(stack::overflow& e) {
        e.who.grow();
        // std::clog << "stack reserve: " << e.who.capacity()
        //         << " -> " << e.size << std::endl;
        // std::clog << "overflow" << std::endl;
      }
    }
  }

  graph_data init_pos, init_vel;

  void init(graph& g) {
    const std::size_t n = num_vertices(g);

    // save init pos/vel
    with_auto_resize([&] {
        init_pos.reset(n);
        init_vel.reset(n);

        struct init vis{init_pos, init_vel};
        for(unsigned v : g.vertices() ) {
          g[v].apply( vis, v, g);
        }

      });

  }

  void reset(graph& g) const {

    struct reset vis{init_pos, init_vel};

    for(unsigned v : g.vertices() ) {
      g[v].apply( vis, v);
    }

  }

  void step(graph& g, real dt) const {

    std::vector<unsigned> order;
    g.sort( std::back_inserter(order) );

    const std::size_t n = num_vertices(g);

    // propagate positions
    graph_data pos;
    with_auto_resize([&] {
        pos.reset(n);

        for(unsigned v : order) {
          g[v].apply( typecheck(), v, g);
          g[v].apply( push(pos), v, g);
        }
      });

    // TODO update graph according to collision detection
    // TODO reorder graph if it changed

    // number dofs
    std::vector<chunk> chunks(n);
    std::size_t offset = 0;

    std::vector<triplet> primal, dual;
    std::size_t primal_offset = 0, dual_offset = 0;

    struct select vis{primal, dual, primal_offset, dual_offset, chunks};

    for(unsigned v : order) {
      g[v].apply( numbering(chunks, offset, pos), v, g);

      // TODO merge visitors?
      g[v].apply(vis , v);
    }

    const std::size_t total_dim = offset;

    // primal dual selection matrices
    cmat P, Q;

    P.resize(total_dim, primal_offset);
    P.setFromTriplets(primal.begin(), primal.end());

    Q.resize(total_dim, dual_offset);
    Q.setFromTriplets(dual.begin(), dual.end());

    // fetch masks/jacobians
    graph_data mask;

    std::vector<triplet> jacobian, diagonal;
    std::vector< std::vector<triplet> > elements;

    with_auto_resize([&] {
        mask.reset(n);
        jacobian.clear();
        diagonal.clear();

        fetch vis(jacobian, diagonal, mask, elements, chunks, pos);
        vis.dt = dt;

        for(unsigned v : reverse(order)) {
          g[v].apply( vis, v, g);
        }

      });

    // concatenate/assemble
    rmat L(total_dim, total_dim), D(total_dim, total_dim);

    L.setFromTriplets(jacobian.begin(), jacobian.end());

    // pull gradients/geometric stiffness
    vec momentum, force;
    graph_data work;

    std::vector<triplet> gs;

    with_auto_resize([&] {
        work.reset(n);

        force = vec::Zero(total_dim);
        momentum = vec::Zero(total_dim);

        gs.clear();

        pull vis(force, momentum, L, gs, work, pos, chunks, gravity);

        for(unsigned v : reverse(order)) {
          g[v].apply(vis, v, g);
        }

      });

    std::cout << "force: " << force.transpose() << std::endl;
    std::cout << "momentum: " << momentum.transpose() << std::endl;

    // // add geometric stiffness TODO scale by dt
    // std::copy(gs.begin(), gs.end(), std::back_inserter(diagonal));

    D.setFromTriplets(diagonal.begin(), diagonal.end());

    std::cout << "diagonal: " << D << std::endl;

    rmat concat;
    concatenate(concat, L);

    // collapse matrix on source dofs only
    // concat = concat * select;

    // TODO select compliant/master source dofs for easy matrix
    // splitting:

    // H = master.transpose() * K * master
    // J = compliant.transpose() * K * master
    // C = compliant.transpose() * K * compliant

    std::cout << "mapping concatenation: " << std::endl;
    std::cout << concat << std::endl;

    const cmat LP = concat * P;
    const cmat LQ = concat * Q;

    const rmat H = LP.transpose() * D * LP;
    std::cout << "H: " << H << std::endl;

    vec rhs = vec::Zero(primal_offset + dual_offset);

    rhs.head(primal_offset) += LP.transpose() * momentum;
    rhs.head(primal_offset) += dt * (LP.transpose() * force);

    std::cout << "rhs: " << rhs.head(primal_offset).transpose() << std::endl;

    if( dual_offset ) {
      const rmat J = LQ.transpose() * D * LP;
      const rmat C = LQ.transpose() * D * LQ;

      rhs.tail(dual_offset) -= (LQ.transpose() * force) / dt;

      std::cout << "J: " << J << std::endl;
      std::cout << "C: " << C << std::endl;

      std::cout << "rhs: " << rhs.tail(dual_offset).transpose() << std::endl;
    }

    Eigen::SimplicialLDLT<cmat> inv;

    inv.compute(H.transpose());
    std::cout << "ldlt: " << (inv.info() == Eigen::Success) << std::endl;

    // compute velocities
    const vec velocities = inv.solve(rhs.head(primal_offset));

    for(unsigned v : order) {
      g[v].apply( integrate{chunks, velocities, dt}, v, g);
    }

  }

};

#endif


//
//  main.cpp
//  Delaunay
//
//  Created by toru ito on 2017/03/22.
//  Copyright © 2017年 toru ito. All rights reserved.
//

#include <cstdio>
#include <cstdlib>

#include <iostream>
#include <string>

#include <GLFW/glfw3.h>
#include <OpenGL/gl3.h>

#include <Eigen/Core>
#include <Eigen/Geometry>

#include "ShaderProgram.hpp"
#include "ShapeData.hpp"
#include "Delaunay.hpp"

int main(int argc, const char * argv[]) {

    GLFWwindow* window;

    // Initialize the library
    if (!glfwInit())
        return -1;

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    // Create a windowed mode window and its OpenGL context
    window = glfwCreateWindow( 500, 500, "Delaunay", NULL, NULL );
    if (!window) {
        glfwTerminate();
        return -1;
    }

    // Make the window's context current
    glfwMakeContextCurrent(window);

    glEnable(GL_DEPTH_TEST);

    std::vector< Eigen::Vector2d > PointList;
    std::vector< std::vector< unsigned int > > IndexList;

    double x, y;
    int k;

    srand( 2 );
    //srand((unsigned int)time(NULL));

    for( k = 0; k < 100; ++k ) {
        x = static_cast< double >( rand() ) / static_cast< double >( RAND_MAX );
        y = static_cast< double >( rand() ) / static_cast< double >( RAND_MAX );
        PointList.push_back( Eigen::Vector2d( x, y ) );
    }

    Delaunay delaunay;
    delaunay.SetPoint( &PointList );
    delaunay.Triangulation();
    delaunay.GetResult( &PointList, &IndexList );

    ShaderProgram triangleProgram, wireframeProgram;
    ShapeData triangle, wireframe;

    //triangle
    if( !triangleProgram.InitProgram() )
        return -1;

    triangle.SetIndex( IndexList );
    triangle.SetVertex( PointList );
    triangle.InitDraw();
    triangle.SetDrawMode( GL_TRIANGLES );

    triangleProgram.SetColor( 0.0f, 0.0f, 1.0f );
    triangleProgram.SetDataMatrix( &triangle, Eigen::Vector3f( 0.0f, 0.0f, 0.0f ) );

    //wireframe
    std::vector< std::vector< unsigned int > > WireframeIndexList;
    std::vector< unsigned int > WireframeIndexs;
    unsigned int i, j;

    WireframeIndexs.resize( 2 );

    for( auto indexs : IndexList ) {
        for( i = 0; i < 3; ++i ) {
            j = ( i + 1 ) % 3;
            WireframeIndexs[0] = indexs[i];
            WireframeIndexs[1] = indexs[j];
            WireframeIndexList.push_back( WireframeIndexs );
        }
    }

    if( !wireframeProgram.InitProgram() )
        return -1;

    wireframe.SetIndex( WireframeIndexList );
    wireframe.SetVertex( PointList );
    wireframe.InitDraw();
    wireframe.SetDrawMode( GL_LINES );

    wireframeProgram.SetColor( 1.0f, 1.0f, 1.0f );
    wireframeProgram.SetDataMatrix( &wireframe, Eigen::Vector3f( 0.0f, 0.0f, -0.1f ) );

    // Loop until the user closes the window
    while( !glfwWindowShouldClose( window ) ) {

        // Render
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

        triangleProgram.UseProgram();
        triangle.Draw();

        wireframeProgram.UseProgram();
        wireframe.Draw();

        glBindVertexArray( 0 );

        // Swap front and back buffers
        glfwSwapBuffers( window );

        // Poll for and process events
        glfwPollEvents();

    }

    triangle.DeleteDraw();
    triangleProgram.DeleteProgram();

    wireframe.DeleteDraw();
    wireframeProgram.DeleteProgram();

    glfwTerminate();

    return 0;
}


/*
 * MIT License
 *
 * Copyright (c) 2020 Robert Grupp
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "xregImgSimMetric2DGradImgOCL.h"

#include <boost/compute/utility/source.hpp>

#include "xregAssert.h"
#include "xregNormDist.h"

namespace
{

// This should actually be set based on the size of the input data (and the GPU HW)
const std::size_t kSOBEL_PROJ_DIMS_WORK_UNIT_DIV = 1;
const std::size_t kSMOOTH_PROJ_DIMS_WORK_UNIT_DIV = 1;

const char* kGRAD_NCC_OPENCL_SRC = BOOST_COMPUTE_STRINGIZE_SOURCE(

__kernel void GaussianKernel(__global float* src_imgs,
                             __global float* smooth_imgs,
                             __global float* smooth_kernel,
                             const uint kernel_width,
                             const uint num_imgs,
                             const uint img_nr,
                             const uint img_nc,
                             const uint proj_off)
{
  const uint row_inc = get_global_size(0);
  const uint col_inc = get_global_size(1);
  const uint img_idx = get_global_id(2);
  const uint img_len = img_nr * img_nc;

  if (img_idx < num_imgs)
  {
    const uint img_nr_minus_1 = img_nr - 1;
    const uint img_nc_minus_1 = img_nc - 1;

    const int kernel_half_width = (int) (kernel_width / 2);

    // this is typically from the ray caster buffer, which can be split up amongst different sim
    // metrics and thus why we need projection offset.
    __global float* cur_src_img = src_imgs + ((proj_off + img_idx) * img_len);

    // this buffer is local to this sim metric and we can just use the beginning section; no need
    // to worry about projection offset
    __global float* cur_smooth_img = smooth_imgs + (img_idx * img_len);

    for (uint row_idx = get_global_id(0); row_idx < img_nr; row_idx += row_inc)
    {
      __global float* smooth_row = cur_smooth_img + (row_idx * img_nc);

      for (uint col_idx = get_global_id(1); col_idx < img_nc; col_idx += col_inc)
      {
        float cur_sum = 0;

        uint kern_idx = 0;
        for (int kr = -kernel_half_width; kr <= kernel_half_width; ++kr)
        {
          const int conv_r_no_clamp = ((int)row_idx) + kr;

          // clamp row used in image
          const uint conv_r_clamp = (conv_r_no_clamp >= 0) ?
                                      ((conv_r_no_clamp < (int)img_nr) ? (uint)conv_r_no_clamp : img_nr_minus_1) : 0u;

          __global float* cur_conv_row = cur_src_img + (conv_r_clamp * img_nc);

          for (int kc = -kernel_half_width; kc <= kernel_half_width; ++kc, ++kern_idx)
          {
            const int conv_c_no_clamp = ((int)col_idx) + kc;

            // clamp column used in image
            const uint conv_c_clamp = (conv_c_no_clamp >= 0) ?
                                      ((conv_c_no_clamp < (int)img_nc) ? (uint)conv_c_no_clamp : img_nc_minus_1) : 0u;

            cur_sum += cur_conv_row[conv_c_clamp] * smooth_kernel[kern_idx];
          }
        }

        smooth_row[col_idx] = cur_sum;
      }
    }
  }
}

__kernel void SobelKernel(__global float* src_imgs,
                          __global float* grad_x_imgs,
                          __global float* grad_y_imgs,
                          const uint num_imgs,
                          const uint img_nr,
                          const uint img_nc,
                          const uint proj_off)
{
  const uint row_inc = get_global_size(0);
  const uint col_inc = get_global_size(1);
  const uint img_idx = get_global_id(2);
  const uint img_len = img_nr * img_nc;

  if (img_idx < num_imgs)
  {
    const uint img_nr_minus_1 = img_nr - 1;
    const uint img_nc_minus_1 = img_nc - 1;

    // this is typically from the ray caster buffer, which can be split up amongst different sim
    // metrics and thus why we need projection offset.
    __global float* cur_src_img = src_imgs + ((proj_off + img_idx) * img_len);

    // this buffer is local to this sim metric and we can just use the beginning section; no need
    // to worry about projection offset
    __global float* cur_grad_x_img = grad_x_imgs + (img_idx * img_len);
    __global float* cur_grad_y_img = grad_y_imgs + (img_idx * img_len);

    for (uint row_idx = get_global_id(0); row_idx < img_nr; row_idx += row_inc)
    {
      const uint cur_row_off  = row_idx * img_nc;
      const uint prev_row_off = (row_idx > 0) ? (cur_row_off - img_nc) : cur_row_off;
      const uint next_row_off = (row_idx < img_nr_minus_1) ? (cur_row_off + img_nc) : cur_row_off;

      __global float* cur_src_row  = cur_src_img + cur_row_off;
      __global float* prev_src_row = cur_src_img + prev_row_off;
      __global float* next_src_row = cur_src_img + next_row_off;

      __global float* grad_x_row = cur_grad_x_img + cur_row_off;
      __global float* grad_y_row = cur_grad_y_img + cur_row_off;

      for (uint col_idx = get_global_id(1); col_idx < img_nc; col_idx += col_inc)
      {
        const uint prev_col = (col_idx > 0) ? (col_idx - 1) : 0;
        const uint next_col = (col_idx < img_nc_minus_1) ? (col_idx + 1) : col_idx;

        // x-direction
        grad_x_row[col_idx] = -prev_src_row[prev_col] + prev_src_row[next_col] +
                              -(2.0f * cur_src_row[prev_col]) + (2.0f * cur_src_row[next_col]) +
                              -next_src_row[prev_col] + next_src_row[next_col];

        // y-direction
        grad_y_row[col_idx] = -prev_src_row[prev_col] - (2.0f * prev_src_row[col_idx]) - prev_src_row[next_col]
                              + next_src_row[prev_col] + (2.0f * next_src_row[col_idx]) + next_src_row[next_col];
      }
    }
  }
}

);

}  // un-named

xreg::ImgSimMetric2DGradImgOCL::ImgSimMetric2DGradImgOCL(const boost::compute::device& dev)
  : ImgSimMetric2DOCL(dev)
{ }

xreg::ImgSimMetric2DGradImgOCL::ImgSimMetric2DGradImgOCL(const boost::compute::context& ctx,
                                                         const boost::compute::command_queue& queue)
  : ImgSimMetric2DOCL(ctx, queue)
{ }

void xreg::ImgSimMetric2DGradImgOCL::allocate_resources()
{
  namespace bc = boost::compute;

  ImgSimMetric2DOCL::allocate_resources();

  const auto itk_size = this->fixed_img_->GetLargestPossibleRegion().GetSize();
  const size_type img_num_rows = itk_size[1];
  const size_type img_num_cols = itk_size[0];

  // compile kernels

  bc::program prog = bc::program::create_with_source(kGRAD_NCC_OPENCL_SRC, this->ctx_);

  try
  {
    prog.build();
  }
  catch (bc::opencl_error &)
  {
    std::cerr << "OpenCL Kernel Compile Error (ImgSimMetric2DGradImgOCL):\n"
              << prog.build_log() << std::endl;
    throw;
  }

  const size_type num_pix_per_img = this->num_pix_per_proj();
  const size_type max_buf_size    = num_pix_per_img * this->num_mov_imgs_;

  if (smooth_img_kernel_rad_)
  {
    // width must be odd
    xregASSERT(smooth_img_kernel_rad_ & 1);

    // Doh... the radius is actually diameter/width
    const size_type tot_kernel_len = smooth_img_kernel_rad_ * smooth_img_kernel_rad_;

    // This is the OpenCV formula; mainly using to get results consistent
    // with the CPU implementation that uses cv::GaussianBlur
    const float sigma = ((((smooth_img_kernel_rad_ - 1) * 0.5f) - 1.0f) * 0.3f) + 0.8f;

    // Create distribution with mean at the kernel center index
    NormalDist2DIndep norm_dist(0, 0, sigma, sigma);

    float kern_sum = 0;
    std::vector<float> tmp_host_kern;
    tmp_host_kern.reserve(tot_kernel_len);

    const int kernel_half_width = static_cast<int>(smooth_img_kernel_rad_) / 2;

    for (int r = -kernel_half_width; r <= kernel_half_width; ++r)
    {
      for (int c = -kernel_half_width; c <= kernel_half_width; ++c)
      {
        const float cur_val = norm_dist(r,c);
        tmp_host_kern.push_back(cur_val);

        kern_sum += cur_val;
      }
    }
    xregASSERT(tmp_host_kern.size() == tot_kernel_len);

    // normalize so kernel has sum of 1
    std::transform(tmp_host_kern.begin(), tmp_host_kern.end(), tmp_host_kern.begin(),
                   [kern_sum] (const float& x)
                   {
                     return x / kern_sum;
                   });

    smooth_kernel_dev_buf_.reset(new DevBuf(this->ctx_));
    smooth_kernel_dev_buf_->assign(tmp_host_kern.begin(), tmp_host_kern.end(),
                                   this->queue_);

    mov_smooth_dev_buf_.reset(new DevBuf(this->ctx_));
    mov_smooth_dev_buf_->resize(max_buf_size, this->queue_);

    smooth_krnl_ = prog.create_kernel("GaussianKernel");

    // smooth the fixed image
    smooth_krnl_.set_arg(0, *this->fixed_img_ocl_buf_);
    smooth_krnl_.set_arg(1, *mov_smooth_dev_buf_);
    smooth_krnl_.set_arg(2, *smooth_kernel_dev_buf_);
    smooth_krnl_.set_arg(3, bc::uint_(smooth_img_kernel_rad_));
    smooth_krnl_.set_arg(4, bc::uint_(1));
    smooth_krnl_.set_arg(5, bc::uint_(img_num_rows));
    smooth_krnl_.set_arg(6, bc::uint_(img_num_cols));
    smooth_krnl_.set_arg(7, bc::uint_(0));

    smooth_ocl_kernel_global_size_[0] = img_num_rows / kSMOOTH_PROJ_DIMS_WORK_UNIT_DIV;
    smooth_ocl_kernel_global_size_[1] = img_num_cols / kSMOOTH_PROJ_DIMS_WORK_UNIT_DIV;
    smooth_ocl_kernel_global_size_[2] = 1 ;
    this->queue_.enqueue_nd_range_kernel(smooth_krnl_, 3, 0, smooth_ocl_kernel_global_size_.data(), 0).wait();
  }

  sobel_krnl_ = prog.create_kernel("SobelKernel");

  fixed_grad_x_dev_buf_ = std::make_shared<DevBuf>(this->ctx_);
  fixed_grad_y_dev_buf_ = std::make_shared<DevBuf>(this->ctx_);

  mov_grad_x_dev_buf_ = std::make_shared<DevBuf>(this->ctx_);
  mov_grad_y_dev_buf_ = std::make_shared<DevBuf>(this->ctx_);

  fixed_grad_x_dev_buf_->resize(num_pix_per_img, this->queue_);
  fixed_grad_y_dev_buf_->resize(num_pix_per_img, this->queue_);

  mov_grad_x_dev_buf_->resize(max_buf_size, this->queue_);
  mov_grad_y_dev_buf_->resize(max_buf_size, this->queue_);

  // compute fixed image sobel grads
  sobel_krnl_.set_arg(0, smooth_img_kernel_rad_ ? *mov_smooth_dev_buf_ :
                                                  *this->fixed_img_ocl_buf_);
  sobel_krnl_.set_arg(1, *fixed_grad_x_dev_buf_);
  sobel_krnl_.set_arg(2, *fixed_grad_y_dev_buf_);
  sobel_krnl_.set_arg(3, bc::uint_(1));
  sobel_krnl_.set_arg(4, bc::uint_(img_num_rows));
  sobel_krnl_.set_arg(5, bc::uint_(img_num_cols));
  sobel_krnl_.set_arg(6, bc::uint_(0));

  sobel_ocl_kernel_global_size_[0] = img_num_rows / kSOBEL_PROJ_DIMS_WORK_UNIT_DIV;
  sobel_ocl_kernel_global_size_[1] = img_num_cols / kSOBEL_PROJ_DIMS_WORK_UNIT_DIV;
  sobel_ocl_kernel_global_size_[2] = 1 ;
  this->queue_.enqueue_nd_range_kernel(sobel_krnl_, 3, 0, sobel_ocl_kernel_global_size_.data(), 0).wait();

  // setup some kernel arguments that will not change
  sobel_krnl_.set_arg(1, *mov_grad_x_dev_buf_);
  sobel_krnl_.set_arg(2, *mov_grad_y_dev_buf_);
  // image dimensions do not change

  if (smooth_img_kernel_rad_)
  {
    smooth_krnl_.set_arg(0, *this->mov_imgs_buf_);
  }
  else
  {
    sobel_krnl_.set_arg(0, *this->mov_imgs_buf_);
  }
}

xreg::size_type xreg::ImgSimMetric2DGradImgOCL::smooth_img_before_sobel_kernel_radius() const
{
  return smooth_img_kernel_rad_;
}

void xreg::ImgSimMetric2DGradImgOCL::set_smooth_img_before_sobel_kernel_radius(const size_type r)
{
  smooth_img_kernel_rad_ = r;
}

void xreg::ImgSimMetric2DGradImgOCL::compute_sobel_grads()
{
  namespace bc = boost::compute;

  // this should be called by the compute method of the sub-class
  //this->pre_compute();

  sobel_ocl_kernel_global_size_[2]  = this->num_mov_imgs_;
  smooth_ocl_kernel_global_size_[2] = this->num_mov_imgs_;

  // smooth moving images
  if (smooth_img_kernel_rad_)
  {
    smooth_krnl_.set_arg(4, bc::uint_(this->num_mov_imgs_));
    smooth_krnl_.set_arg(7, bc::uint_(this->proj_off_));

    this->queue_.enqueue_nd_range_kernel(smooth_krnl_, 3, 0, smooth_ocl_kernel_global_size_.data(), 0).wait();

    sobel_krnl_.set_arg(6, bc::uint_(0));
  }
  else
  {
    sobel_krnl_.set_arg(6, bc::uint_(this->proj_off_));
  }

  // compute the sobel of moving images
  sobel_krnl_.set_arg(3, bc::uint_(this->num_mov_imgs_));

  this->queue_.enqueue_nd_range_kernel(sobel_krnl_, 3, 0, sobel_ocl_kernel_global_size_.data(), 0).wait();
}


#include "include/MMVII_all.h"

// #include <Eigen/Dense>

namespace MMVII
{

/* ========================== */
/*          ::                */
/* ========================== */

/// Computation to get the point we have at end of iterating a rectangle
template <const int Dim> cPtxd<int,Dim> CalPEnd(const cPtxd<int,Dim> & aP0,const cPtxd<int,Dim> & aP1)
{
    cPtxd<int,Dim> aRes = aP0;
    aRes[Dim-1] = aP1[Dim-1] ;
    return aRes;
}

/* ========================== */
/*   cBorderPixBoxIterator    */
/* ========================== */

template <const int Dim>
  cBorderPixBoxIterator<Dim>::cBorderPixBoxIterator(tBPB & aBPB,const  tPt & aP0) :
      cPixBoxIterator<Dim>  (aBPB.PB(),aP0),
      mBPB                  (&aBPB)
{
}

template <const int Dim>
  cBorderPixBoxIterator<Dim>  & cBorderPixBoxIterator<Dim>::operator ++(int)
{
   return ++(*this);
}

template <const int Dim>
  cBorderPixBoxIterator<Dim>  & cBorderPixBoxIterator<Dim>::operator ++()
{
    //cPixBoxIterator<Dim>::operator ++ ();
    tPBI::operator ++ ();

    mBPB->IncrPt(tPBI::mPCur);
    return *this;
}

/* ========================== */
/*      cBorderPixBox         */
/* ========================== */

template <const int Dim>
  cBorderPixBox<Dim>::cBorderPixBox(const tPB & aPB,const tPt & aSz) :
    mPB     (aPB),
    mSz     (aSz),
    mBoxInt (mPB.Dilate(-mSz)),
    mX0     (mBoxInt.P0().x()),
    mX1     (mBoxInt.P1().x()),
    mBegin  (*this,mPB.P0()),
    mEnd    (*this,CalPEnd(mPB.P0(),mPB.P1()))
{
}
template <const int Dim>
  cBorderPixBox<Dim>::cBorderPixBox(const tPB & aPB,int aSz) :
      cBorderPixBox<Dim>(aPB,tPt::PCste(aSz))
{
}

template <const int Dim>
  cBorderPixBox<Dim>::cBorderPixBox(const cBorderPixBox<Dim> & aBPB) :
    cBorderPixBox<Dim>(mPB,mSz)
{
}

template <const int Dim>  cPixBox<Dim> &   cBorderPixBox<Dim>::PB() {return mPB;}

template <const int Dim>  void cBorderPixBox<Dim>::IncrPt(tPt & aP)
{
   if  ((aP.x()==mX0) && (mBoxInt.Inside(aP)))
      aP.x() = mX1;

}

/* ========================== */
/*          cPtxd             */
/* ========================== */

int NbPixVign(const int & aVign){return 1+2*aVign;}

template <const int Dim> int NbPixVign(const cPtxd<int,Dim> & aVign)
{
   int aRes = NbPixVign(aVign[0]);
   for (int aD=1 ;aD<Dim ; aD++)
       aRes *= NbPixVign(aVign[aD]);
   return aRes;
}

// static const int VeryBig = 1e9;
// const cPt2di  ThePSupImage( VeryBig, VeryBig);
// const cPt2di  ThePInfImage(-VeryBig,-VeryBig);

cPt2di  TAB4Corner[4] = {{1,1},{-1,1},{-1,-1},{1,-1}};

template <class Type,const int Dim> cPtxd<Type,Dim>  cPtxd<Type,Dim>::PCste(const Type & aVal)
{
   cPtxd<Type,Dim> aRes;
   for (int aK=0 ; aK<Dim; aK++)
       aRes.mCoords[aK]= aVal;
   return aRes;
}

template <class Type,const int Dim> cPtxd<Type,Dim>  cPtxd<Type,Dim>::FromPtInt(const cPtxd<int,Dim> & aPInt)
{
   cPtxd<Type,Dim> aRes;
   for (int aK=0 ; aK<Dim; aK++)
       aRes.mCoords[aK]= aPInt[aK];
   return aRes;
}

/*
void ff()
{
    cPtxd<double,3>::FromPtInt(cPt3di(0,0,0));
}
*/

template <class Type,const int Dim> cPtxd<Type,Dim>  cPtxd<Type,Dim>::PRand()
{
   cPtxd<Type,Dim> aRes;
   for (int aK=0 ; aK<Dim; aK++)
       aRes.mCoords[aK]= tNumTrait<Type>::RandomValue();
   return aRes;
}

template <class Type,const int Dim> cPtxd<Type,Dim>  cPtxd<Type,Dim>::PRandC()
{
   cPtxd<Type,Dim> aRes;
   for (int aK=0 ; aK<Dim; aK++)
       aRes.mCoords[aK]= RandUnif_C();
   return aRes;
}

template <class Type,const int Dim> cPtxd<Type,Dim>  cPtxd<Type,Dim>::PRandUnit()
{
   cPtxd<Type,Dim> aRes = PRandC();
   while (NormInf(aRes)<1e-2)
        aRes = PRandC();
   return VUnit(aRes);
}

template <class Type,const int Dim> cPtxd<Type,Dim>  cPtxd<Type,Dim>::PRandInSphere()
{
   cPtxd<Type,Dim> aRes = PRandC();
   while (Norm2(aRes)>1.0)
        aRes = PRandC();
   return aRes;
}

template <class Type,const int Dim> cPtxd<Type,Dim>
      cPtxd<Type,Dim>::PRandUnitDiff(const cPtxd<Type,Dim>& aP0,const Type & aDist)
{
   cPtxd<Type,Dim> aRes = PRandUnit();
   while (NormInf(aRes-aP0)<aDist)
        aRes = PRandUnit();
   return aRes;
}

template <class Type,const int Dim>
   typename cPtxd<Type,Dim>::tBigNum cPtxd<Type,Dim>::MinSqN2(const std::vector<tPt> & aVecPts,bool SVP) const
{
   if (aVecPts.empty())
   {
       MMVII_INTERNAL_ASSERT_medium(SVP,"MinSqN2 on empty vect, no def");
       return -1;
   }
   tBigNum aRes = SqN2(aVecPts[0]-*this);
   for (size_t  aKPts=1 ; aKPts<aVecPts.size() ; aKPts++)
       aRes = std::min(aRes, SqN2(aVecPts[aKPts]-*this));
   return aRes;
}

template <class Type,const int Dim> double NormK(const cPtxd<Type,Dim> & aPt,double anExp)
{
   double aRes = pow(std::abs(aPt[0]),anExp);
   for (int aD=1 ; aD<Dim; aD++)
      aRes += pow(std::abs(aPt[aD]),anExp);
   return pow(aRes,1/anExp);
}

template <class Type,const int Dim> double Norm2(const cPtxd<Type,Dim> & aPt)
{
   double aRes = Square(aPt[0]);
   for (int aD=1 ; aD<Dim; aD++)
      aRes += Square(aPt[aD]);
   return sqrt(aRes);
}

template <class Type,const int Dim> Type Norm1(const cPtxd<Type,Dim> & aPt)
{
   Type aRes = std::abs(aPt[0]);
   for (int aD=1 ; aD<Dim; aD++)
      aRes += std::abs(aPt[aD]);
   return aRes;
}

template <class Type,const int Dim> Type NormInf(const cPtxd<Type,Dim> & aPt)
{
   Type aRes = std::abs(aPt[0]);
   for (int aD=1 ; aD<Dim; aD++)
      aRes = std::max(aRes,std::abs(aPt[aD]));
   return aRes;
}

template <class Type,const int Dim> Type MinAbsCoord(const cPtxd<Type,Dim> & aPt)
{
   Type aRes = std::abs(aPt[0]);
   for (int aD=1 ; aD<Dim; aD++)
      aRes = std::min(aRes,std::abs(aPt[aD]));
   return aRes;
}

template <class T,const int Dim>
   typename  tNumTrait<T>::tBig Scal(const cPtxd<T,Dim> &aP1,const cPtxd<T,Dim> & aP2)
{
   typename tNumTrait<T>::tBig  aRes = aP1[0]*aP2[0];
   for (int aD=1 ; aD<Dim; aD++)
      aRes +=  aP1[aD]*aP2[aD];
   return aRes;
}

template <class T,const int Dim>  T Cos(const cPtxd<T,Dim> &aP1,const cPtxd<T,Dim> & aP2)
{
   return T(Scal(aP1,aP2)) / (Norm2(aP1)*Norm2(aP2));
}

template <class Type,const int Dim> std::ostream & operator << (std::ostream & OS,const cPtxd<Type,Dim> &aP)
{
    OS << "[" << aP.x();
    for (int aD=1; aD<Dim; aD++)
       OS << "," << aP[aD];
    OS << "]" ;
    return OS;
}

template<class T,const int Dim> cPtxd<T,Dim>  VUnit(const cPtxd<T,Dim> & aP)
{
   return aP / T(Norm2(aP));  // Check by 0 is made in operator
}

template <class Type,const int DimOut,const int DimIn> cPtxd<Type,DimOut> CastDim(const cPtxd<Type,DimIn> & aPt)
{
    MMVII_INTERNAL_ASSERT_tiny(DimIn==DimOut,"CastDim : different dim");

    return  cPtxd<Type,DimOut>(aPt.PtRawData());

}

template <const int Dim>  class cAllocNeighourhood
{

   public :
      typedef  cPtxd<int,Dim>    tPt;
      typedef  std::vector<tPt>  tVecPt;
      typedef  std::vector<tVecPt>  tVVPt;

      static  const tVVPt & AllocTabGrowNeigh(int aDMax)
      {
           static  tVVPt aRes;;
           if (int(aRes.size()) > aDMax) return aRes;

           aRes = tVVPt(1+aDMax,tVecPt());

           for (const auto & aP :  cPixBox(tPt::PCste(-aDMax),tPt::PCste(aDMax+1)))
               aRes.at(NormInf(aP)).push_back(aP);

           return aRes;
      }

      static const tVecPt &  Alloc(int aNbPix)
      {
// StdOut() <<  "----------------======================\n";
            static  std::vector<tVecPt> aBufRes(Dim);
            MMVII_INTERNAL_ASSERT_tiny((aNbPix>0)&&(aNbPix<=Dim),"Bad Nb in neighbourhood");

            // If alreay computed all fine
            tVecPt & aRes = aBufRes[aNbPix-1];
            if (! aRes.empty()) return aRes;

            // Usee full neighboor
            cPixBox<Dim> aPB(tPt::PCste(-1),tPt::PCste(2));
            for (const auto & aP : aPB)
            {
                int aN = Norm1(aP);
                if ((aN>0) && (aN<=aNbPix))
                {
                    aRes.push_back(aP);
                    // StdOut() << aP << "\n";
                }
            }

            return aRes;
      }
};

template <const int Dim>  const std::vector<cPtxd<int,Dim>> & AllocNeighbourhood(int aNbVois)
{
   return  cAllocNeighourhood<Dim>::Alloc(aNbVois);
}

template <const int Dim>  const std::vector<std::vector<cPtxd<int,Dim>>> & TabGrowNeigh(int aDistMax)
{
   return  cAllocNeighourhood<Dim>::AllocTabGrowNeigh(aDistMax);
}

/*
template <class Type> std::ostream & operator << (std::ostream & OS,const cPtxd<Type,1> &aP)
{ return  OS << "[" << aP.x() << "]"; }
template <class Type> std::ostream & operator << (std::ostream & OS,const cPtxd<Type,2> &aP)
{ return  OS << "[" << aP.x() << "," << aP.y() << "]"; }
template <class Type> std::ostream & operator << (std::ostream & OS,const cPtxd<Type,3> &aP)
{ return  OS << "[" << aP.x() << "," << aP.y() << "," << aP.z()<< "]"; }
template <class Type> std::ostream & operator << (std::ostream & OS,const cPtxd<Type,4> &aP)
{ return  OS << "[" << aP.x() << "," << aP.y() << "," << aP.z() << "," << aP.t() << "]"; }
*/

/* ========================== */
/*          ::                */
/* ========================== */
    //  To test Error_Handler mecanism

static std::string MesNegSz="Negative size in rect object";
/*
static std::string  TestErHandler;
void TestBenchRectObjError(const std::string & aType,const std::string &  aMes,const char * aFile,int aLine)
{
   TestErHandler = aMes;
}
*/

/* ========================== */
/*          cPixBox           */
/* ========================== */

template <const int Dim>   cPixBox<Dim>::cPixBox(const cPtxd<int,Dim> & aP0,const cPtxd<int,Dim> & aP1,bool AllowEmpty) :
     cTplBox<int,Dim>(aP0,aP1,AllowEmpty),
     mBegin  (*this,aP0),
     mEnd    (*this,CalPEnd(aP0,aP1))
{
}

template <const int Dim>   cPixBox<Dim>::cPixBox(const cPixBox<Dim> & aR) :
   cPixBox<Dim>(aR.mP0,aR.mP1,true)
{
}

template <const int Dim> cPixBox<Dim>  cPixBox<Dim>::BoxWindow(const tPt & aC,int aSz)
{
    return cPixBox<Dim>(aC-cPtxd<int,Dim>::PCste(aSz),aC+cPtxd<int,Dim>::PCste(aSz+1));
}

template <const int Dim> cPixBox<Dim>  cPixBox<Dim>::BoxWindow(int aSz)
{
    return BoxWindow(cPtxd<int,Dim>::PCste(0),aSz);
}

template <const int Dim>   cPixBox<Dim>::cPixBox(const cTplBox<int,Dim> & aR) :
   cPixBox<Dim>(aR.P0(),aR.P1(),true)
{
}

template <const int Dim> tINT8  cPixBox<Dim>::IndexeLinear(const tPt & aP) const
{
   tINT8 aRes = 0;
   for (int aK=0 ; aK<Dim ; aK++)
      aRes += tINT8(aP[aK]-tBox::mP0[aK]) * tINT8(tBox::mSzCum[aK]);
   return aRes;
}

template <const int Dim> bool  cPixBox<Dim>::SignalAtFrequence(const tPt & anIndex,double aFreq) const
{
   return MMVII::SignalAtFrequence(IndexeLinear(anIndex),aFreq,this->mNbElem-1);
}

template <const int Dim> cPtxd<int,Dim>  cPixBox<Dim>::FromIndexeLinear(tINT8  anIndexe) const
{
   tPt aRes;
   for (int aK=0 ; aK<Dim ; aK++)
   {
      aRes[aK]  = tBox::mP0[aK] + anIndexe % tBox::mSz[aK];
      anIndexe /= tBox::mSz[aK];
   }
   return aRes;
}

template <const int Dim> int cPixBox<Dim>::Interiority(const int  aCoord,int aD) const
{
   return std::min(aCoord-tBox::mP0[aD],tBox::mP1[aD]-1-aCoord);
}

template <const int Dim> int cPixBox<Dim>::Interiority(const tPt &  aP,int aD) const
{
   return Interiority(aP[aD],aD);
}

template <const int Dim> int cPixBox<Dim>::Interiority(const tPt& aP  ) const
{
   int aRes = Interiority(aP,0);
   for (int aD=1 ; aD<Dim ; aD++)
       aRes = std::min(aRes,Interiority(aP,aD));

   return aRes;
}

template <const int Dim> int cPixBox<Dim>::WinInteriority(const tPt& aP,const tPt& aWin,int aD) const
{
   return Interiority(aP,aD)-aWin[aD];
}

template <const int Dim> cBorderPixBox<Dim>  cPixBox<Dim>::Border(int aSz) const
{
   return  cBorderPixBox<Dim>(*this,aSz);
}

template <const int Dim> cPtxd<int,Dim>  cPixBox<Dim>::CircNormProj(const tPt & aPt) const
{
    cPtxd<int,Dim>  aRes;
    for (int aD=0 ; aD<Dim ; aD++)
       aRes[aD] = tBox::mP0[aD] + mod(aPt[aD]-tBox::mP0[aD],tBox::mSz[aD]);
    return aRes;
}

/* ========================== */
/*       cParseBoxInOut       */
/* ========================== */

template <const int Dim> cParseBoxInOut<Dim>::cParseBoxInOut(const tBox & aBoxGlob,const tBox & aBoxIndexe) :
    mBoxGlob (aBoxGlob),
    mBoxIndex (aBoxIndexe)
{
}
        // static tThis  CreateFromMem(const tBox&, double AvalaibleMem);

template <const int Dim> cParseBoxInOut<Dim> cParseBoxInOut<Dim>::CreateFromSize(const tBox & aBox,const tPt & aSzTile)
{
   return  tThis(aBox,tBox(tPt::PCste(0),CByC2P(aBox.Sz(),aSzTile,DivSup<int>)));
}
template <const int Dim> cParseBoxInOut<Dim> cParseBoxInOut<Dim>::CreateFromSizeCste(const tBox & aBox,int aSz)
{
    return CreateFromSize(aBox,tPt::PCste(aSz));
}

template <const int Dim> cParseBoxInOut<Dim> cParseBoxInOut<Dim>::CreateFromSzMem(const tBox & aBox,double aSzMem)
{
    return CreateFromSizeCste(aBox,round_ni(pow(aSzMem,1/double(Dim))));
}

template <const int Dim>  const cPixBox<Dim> &  cParseBoxInOut<Dim>::BoxIndex() const
{
      return  mBoxIndex;
}

template <const int Dim>  cPtxd<int,Dim>  cParseBoxInOut<Dim>::Index2Glob(const tPt & anIndex) const
{
      return  CByC1P(mBoxGlob.FromNormaliseCoord(mBoxIndex.ToNormaliseCoord(anIndex)),round_ni);
}

template <const int Dim>  cPixBox<Dim> cParseBoxInOut<Dim>::BoxOut(const tPt & anIndex) const
{
      return  cPixBox<Dim>
              (
                   Index2Glob(anIndex),
                   Index2Glob(anIndex+tPt::PCste(1))
              );
}

template <const int Dim>  cPixBox<Dim> cParseBoxInOut<Dim>::BoxIn(const tPt & anIndex,const tPt& aDil) const
{
   return mBoxGlob.Inter(BoxOut(anIndex).Dilate(aDil));
}

template <const int Dim>  cPixBox<Dim> cParseBoxInOut<Dim>::BoxIn(const tPt & anIndex,int aDil) const
{
   return mBoxGlob.Inter(BoxOut(anIndex).Dilate(tPt::PCste(aDil)));
}

/* ========================== */
/*          cTplBox           */
/* ========================== */

template <class Type,const int Dim>
   cTplBox<Type,Dim>::cTplBox
   (
       const cPtxd<Type,Dim> & aP0,
       const cPtxd<Type,Dim> & aP1,
       bool AllowEmpty
   ) :
       mP0  (aP0),
       mP1  (aP1),
       mSz  (aP0),
       mNbElem (1)
{
    //for (int aK=Dim-1 ; aK>=0 ; aK--)
    for (int aK=0 ; aK<Dim ; aK++)
    {
       mSz[aK] = mP1[aK] - mP0[aK];
       if (AllowEmpty)
       {
          // make coherent but empty
          if (mSz[aK] <0)
          {
             mP1[aK] = mP0[aK];
             mSz[aK] = 0;
          }
       }
       else
       {
          MMVII_INTERNAL_ASSERT_strong(mSz[aK]>0,MesNegSz);
       }
       mSzCum[aK] = mNbElem;
       mNbElem *= mSz[aK];
    }
}

template <class Type,const int Dim>
   cTplBox<Type,Dim>::cTplBox
   (
       const cPtxd<Type,Dim> & aSz,
       bool AllowEmpty
   ) :
     cTplBox<Type,Dim>(tPt::PCste(0),aSz,AllowEmpty)
{
}

template <class Type,const int Dim> bool  cTplBox<Type,Dim>::IsEmpty() const
{
   return mNbElem == 0;
}

template <class Type,const int Dim> cTplBox<Type,Dim>  cTplBox<Type,Dim>::Empty()
{
   return  cTplBox<Type,Dim>(tPt::PCste(0),true);
}

template <class Type,const int Dim> cTplBox<Type,Dim>  cTplBox<Type,Dim>::Inter(const tBox & aBox)const
{
  return tBox(PtSupEq(mP0,aBox.mP0),PtInfEq(mP1,aBox.mP1),true);
}

template <class Type,const int Dim> cTplBox<Type,Dim>  cTplBox<Type,Dim>::Sup(const tBox & aB2)const
{
  if (IsEmpty() && aB2.IsEmpty())
     return  Empty();
  if (IsEmpty())     return aB2;
  if (aB2.IsEmpty()) return *this;

  return tBox(PtInfEq(mP0,aB2.mP0),PtSupEq(mP1,aB2.mP1),true);
}

template <class Type,const int Dim> cTplBox<Type,Dim>  cTplBox<Type,Dim>::Dilate(const tPt & aPt) const
{
   return tBox(mP0-aPt,mP1+aPt);
}

template <class Type,const int Dim> cTplBox<Type,Dim>  cTplBox<Type,Dim>::Dilate(const Type & aVal) const
{
   return Dilate(tPt::PCste(aVal));
}

template <class Type,const int Dim> cTplBox<Type,Dim>  cTplBox<Type,Dim>::ScaleCentered(const Type & aVal) const
{
   tPt aMil = (mP0+mP1)/Type(2.0);
   tPt anAmpl = (mP1-mP0)* Type(aVal/2.0);
   return tBox(aMil-anAmpl,aMil+anAmpl);
}

template <class Type,const int Dim> void cTplBox<Type,Dim>::AssertSameArea(const cTplBox<Type,Dim> & aR2) const
{
    MMVII_INTERNAL_ASSERT_strong((*this)==aR2,"Rect obj were expected to have identic area");
}
template <class Type,const int Dim> void cTplBox<Type,Dim>::AssertSameSz(const cTplBox<Type,Dim> & aR2) const
{
    MMVII_INTERNAL_ASSERT_strong(Sz()==aR2.Sz(),"Rect obj were expected to have identic size");
}

template <class Type,const int Dim> bool cTplBox<Type,Dim>::operator == (const tBox & aR2) const
{
    return (mP0==aR2.mP0) && (mP1==aR2.mP1);
}

template <class Type,const int Dim> bool cTplBox<Type,Dim>::IncludedIn(const tBox & aR2) const
{
    return SupEq(mP0,aR2.mP0) && InfEq(mP1,aR2.mP1) ;
}

template <class Type,const int Dim> cTplBox<Type,Dim> cTplBox<Type,Dim>::Translate(const cPtxd<Type,Dim> & aTr) const
{
   return cTplBox<Type,Dim>(mP0+aTr,mP1+aTr);
}

template <class Type,const int Dim> cPtxd<Type,Dim>  cTplBox<Type,Dim>::FromNormaliseCoord(const cPtxd<double,Dim> & aPN) const
{
    // MMVII_INTERNAL_ASSERT_strong(false,"To Change
    cPtxd<Type,Dim> aRes;
    for (int aK=0 ; aK<Dim ; aK++)
    {
        // aRes[aK] = mP0[aK] + round_down(mSz[aK]*aPN[aK]);
        aRes[aK] = mP0[aK] + tBaseNumTrait<Type>::RoundNearestToType(mSz[aK]*aPN[aK]);
    }
    return aRes;
    // return Proj(aRes);
}

template <class Type,const int Dim> void  cTplBox<Type,Dim>::Corners(tCorner & aRes) const
{
    for (size_t aKpt=0; aKpt<(1<<Dim)  ; aKpt++)
    {
        for (size_t aD=0 ; aD<Dim ; aD++)
        {
            aRes[aKpt][aD] = (aKpt & (1<<aD)) ? mP0[aD] : mP1[aD];
        }
    }
}

template <class Type,const int Dim> cPtxd<double,Dim>  cTplBox<Type,Dim>::ToNormaliseCoord(const cPtxd<Type,Dim> & aP) const
{
    cPtxd<double,Dim> aRes;
    for (int aK=0 ; aK<Dim ; aK++)
    {
        aRes[aK] = (aP[aK]-mP0[aK]) / double(mSz[aK]);
    }
    return aRes;
}

template <class Type,const int Dim>  cPtxd<double,Dim>  cTplBox<Type,Dim>::RandomNormalised()
{
   cPtxd<double,Dim>  aRes;
   for (int aK=0 ; aK<Dim ; aK++)
   {
        aRes[aK] = RandUnif_0_1();
   }
   return aRes;
}

template <class Type,const int Dim> cPtxd<Type,Dim>   cTplBox<Type,Dim>::GeneratePointInside() const
{
/*
   cPtxd<double,Dim> aP0 = RandomNormalised();
   cPtxd<Type,Dim>  aP1 = FromNormaliseCoord(aP0);
   cPtxd<Type,Dim> aP2 = Proj(aP1);
StdOut() << "BOX " << mP0 << " " << mP1 << "\n";
StdOut() <<  aP0 << aP1 << aP2 << "\n"; getchar();
   return aP2;
*/
   return Proj(FromNormaliseCoord(RandomNormalised()));
}

template <class Type,const int Dim> cTplBox<Type,Dim>  cTplBox<Type,Dim>::GenerateRectInside(double aPowSize) const
{
    cPtxd<Type,Dim> aP0;
    cPtxd<Type,Dim> aP1;
    for (int aK=0 ; aK<Dim ; aK++)
    {
        double aSzRed = pow(RandUnif_0_1(),aPowSize);
        double aX0 = (1-aSzRed) * RandUnif_0_1();
        double aX1 = aX0 + aSzRed;
        // int aI0 = round_down(aX0*mSz[aK]);
        // int aI1 = round_down(aX1*mSz[aK]);
        Type aI0 =  tBaseNumTrait<Type>::RoundNearestToType(aX0*mSz[aK]);
        Type aI1 =  tBaseNumTrait<Type>::RoundNearestToType(aX1*mSz[aK]);
        aI1 = std::min((mP1[aK]-1),std::max(aI1,aI0+1));
        aI0  = std::max((mP0[aK]),std::min(aI0,aI1-1));
        aP0[aK] = aI0;
        aP1[aK] = aI1;

    }
    return cTplBox<Type,Dim>(aP0,aP1);
}

cBox2dr ToR(const cBox2di & aBox)
{
   return cBox2dr(ToR(aBox.P0()),ToR(aBox.P1()));
}

cBox2di ToI(const cBox2dr & aBox)
{
    return cBox2di(Pt_round_down(aBox.P0()),Pt_round_up(aBox.P1()));
}

cBox2dr operator * (const cBox2dr & aBox,double aScale)
{
    return cBox2dr(aBox.P0()*aScale,aBox.P1()*aScale);
}

//        x0,y1        x1,y1
//        x0,y0        x1,y0

template <class Type>
    void CornersTrigo(typename cTplBox<Type,2>::tCorner & aRes,const  cTplBox<Type,2>& aBox)
{
   aRes[0] = cPtxd<Type,2>(aBox.P1().x(),aBox.P1().y());
   aRes[1] = cPtxd<Type,2>(aBox.P0().x(),aBox.P1().y());
   aRes[2] = cPtxd<Type,2>(aBox.P0().x(),aBox.P0().y());
   aRes[3] = cPtxd<Type,2>(aBox.P1().x(),aBox.P0().y());
}

/* ========================== */
/*       cTpxBoxOfPts         */
/* ========================== */

template <class Type,const int Dim>   cTplBoxOfPts<Type,Dim>::cTplBoxOfPts() :
   mNbPts (0),
   mP0    (tPt::PCste(0)),
   mP1    (tPt::PCste(0))
{
}

template <class Type,const int Dim>  int  cTplBoxOfPts<Type,Dim>::NbPts() const {return mNbPts;}

template <class Type,const int Dim>  const cPtxd<Type,Dim> &  cTplBoxOfPts<Type,Dim>::P0() const
{
   MMVII_INTERNAL_ASSERT_medium(mNbPts,"cTplBoxOfPts<Type,Dim>::P0()")
   return  mP0;
}

template <class Type,const int Dim>  const cPtxd<Type,Dim> &  cTplBoxOfPts<Type,Dim>::P1() const
{
   MMVII_INTERNAL_ASSERT_medium(mNbPts,"cTplBoxOfPts<Type,Dim>::P1()")
   return  mP1;
}

template <class Type,const int Dim>  cTplBox<Type,Dim>  cTplBoxOfPts<Type,Dim>::CurBox() const
{
    return  cTplBox<Type,Dim>(mP0,mP1);
}

template <class Type,const int Dim>  void  cTplBoxOfPts<Type,Dim>::Add(const tPt & aP)
{
   if (mNbPts==0)
   {
      mP0 = aP;
      mP1 = aP;
   }
   else
   {
      SetInfEq(mP0,aP);
      SetSupEq(mP1,aP);
   }
   mNbPts++;
}

template <class Type,const int Dim>  cPtxd<int,Dim> Pt_round_down(const cPtxd<Type,Dim>  aP)
{
   return ICByC1P(aP,round_down);
}
template <class Type,const int Dim>  cPtxd<int,Dim> Pt_round_up(const cPtxd<Type,Dim>  aP)
{
   return ICByC1P(aP,round_up);
}
template <class Type,const int Dim>  cPtxd<int,Dim> Pt_round_ni(const cPtxd<Type,Dim>  aP)
{
   return ICByC1P(aP,round_ni);
}

template <class Type> bool WindInside4BL(const cBox2di & aBox,const cPtxd<Type,2> & aPt,const  cPt2di & aSzW)
{
   return
	   (aPt.x() >= aBox.P0().x() + aSzW.x())
       &&  (aPt.y() >= aBox.P0().y() + aSzW.y())
       &&  (aPt.x() <  aBox.P1().x() - aSzW.x()-1)
       &&  (aPt.y() <  aBox.P1().y() - aSzW.y()-1) ;
}

/* ========================== */
/*       INSTANTIATION        */
/* ========================== */

template void CornersTrigo(typename cTplBox<tREAL8,2>::tCorner & aRes,const  cTplBox<tREAL8,2>&);
template void CornersTrigo(typename cTplBox<tINT4,2>::tCorner & aRes,const  cTplBox<tINT4,2>&);

template  bool WindInside4BL(const cBox2di & aBox,const cPtxd<tINT4,2> & aPt,const  cPt2di & aSzW);
template  bool WindInside4BL(const cBox2di & aBox,const cPtxd<tREAL8,2> & aPt,const  cPt2di & aSzW);

#define MACRO_INSTATIATE_PTXD_2DIM(TYPE,DIMIN,DIMOUT)\
template  cPtxd<TYPE,DIMOUT> CastDim<TYPE,DIMOUT,DIMIN>(const cPtxd<TYPE,DIMIN> & aPt);

#define MACRO_INSTATIATE_PTXD(TYPE,DIM)\
MACRO_INSTATIATE_PTXD_2DIM(TYPE,DIM,1);\
MACRO_INSTATIATE_PTXD_2DIM(TYPE,DIM,2);\
MACRO_INSTATIATE_PTXD_2DIM(TYPE,DIM,3);\
MACRO_INSTATIATE_PTXD_2DIM(TYPE,DIM,4);\
template  std::ostream & operator << (std::ostream & OS,const cPtxd<TYPE,DIM> &aP);\
template  cPtxd<TYPE,DIM> cPtxd<TYPE,DIM>::PCste(const TYPE&);\
template  cPtxd<TYPE,DIM> cPtxd<TYPE,DIM>::PRand();\
template  cPtxd<TYPE,DIM> cPtxd<TYPE,DIM>::PRandC();\
template  cPtxd<TYPE,DIM> cPtxd<TYPE,DIM>::PRandUnit();\
template  cPtxd<TYPE,DIM> cPtxd<TYPE,DIM>::PRandInSphere();\
template typename cPtxd<TYPE,DIM>::tBigNum cPtxd<TYPE,DIM>::MinSqN2(const std::vector<tPt> &,bool SVP) const;\
template  cPtxd<TYPE,DIM>  cPtxd<TYPE,DIM>::PRandUnitDiff(const cPtxd<TYPE,DIM>& ,const TYPE&);\
template  double NormK(const cPtxd<TYPE,DIM> & aPt,double anExp);\
template  double Norm2(const cPtxd<TYPE,DIM> & aPt);\
template  TYPE Norm1(const cPtxd<TYPE,DIM> & aPt);\
template  TYPE NormInf(const cPtxd<TYPE,DIM> & aPt);\
template  TYPE MinAbsCoord(const cPtxd<TYPE,DIM> & aPt);\
template  typename  tNumTrait<TYPE>::tBig Scal(const cPtxd<TYPE,DIM> &,const cPtxd<TYPE,DIM> &);\
template  TYPE Cos(const cPtxd<TYPE,DIM> &,const cPtxd<TYPE,DIM> &);\
template  cPtxd<TYPE,DIM>  VUnit(const cPtxd<TYPE,DIM> & aP);\
template  cPtxd<TYPE,DIM>  cPtxd<TYPE,DIM>::FromPtInt(const cPtxd<int,DIM> & aPInt);

// template  cPtxd<TYPE,DIM>  PCste(const DIM & aVal);

#define MACRO_INSTATIATE_POINT(DIM)\
MACRO_INSTATIATE_PTXD(tINT4,DIM)\
MACRO_INSTATIATE_PTXD(tREAL4,DIM)\
MACRO_INSTATIATE_PTXD(tREAL8,DIM)\
MACRO_INSTATIATE_PTXD(tREAL16,DIM)

#define MACRO_INSTATIATE_PRECT_DIM(DIM)\
MACRO_INSTATIATE_POINT(DIM)\
template const std::vector<std::vector<cPtxd<int,DIM>>> & TabGrowNeigh(int);\
template const std::vector<cPtxd<int,DIM>> & AllocNeighbourhood(int);\
template cPtxd<int,DIM> Pt_round_down(const cPtxd<double,DIM>  aP);\
template cPtxd<int,DIM> Pt_round_up(const cPtxd<double,DIM>  aP);\
template cPtxd<int,DIM> Pt_round_ni(const cPtxd<double,DIM>  aP);\
template class cBorderPixBoxIterator<DIM>;\
template class cBorderPixBox<DIM>;\
template class cTplBox<tINT4,DIM>;\
template class cTplBoxOfPts<tINT4,DIM>;\
template  class cParseBoxInOut<DIM>;\
template class cTplBox<tREAL8,DIM>;\
template class cTplBoxOfPts<tREAL8,DIM>;\
template class cPixBox<DIM>;\
template  int NbPixVign(const cPtxd<int,DIM> & aVign);\
template class cDataGenUnTypedIm<DIM>;\
template <> const cPixBox<DIM> cPixBox<DIM>::TheEmptyBox(cPtxd<int,DIM>::PCste(0),cPtxd<int,DIM>::PCste(0),true);

/*
void F()
{
   cPtxd<tINT4,2>::T_PCste(2);
}
*/

MACRO_INSTATIATE_PRECT_DIM(1)
MACRO_INSTATIATE_PRECT_DIM(2)
MACRO_INSTATIATE_PRECT_DIM(3)
MACRO_INSTATIATE_POINT(4)

};


/*
 * MIT License
 *
 * Copyright (c) 2018 Tech Solutions Malta LTD
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <boost/test/unit_test.hpp>
#include <graphene/chain/access_layer.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>

#include <graphene/chain/queue_objects.hpp>

#include "../common/database_fixture.hpp"

#include <cinttypes>

using namespace graphene::chain;
using namespace graphene::chain::test;

BOOST_FIXTURE_TEST_SUITE( dascoin_tests, database_fixture )

BOOST_FIXTURE_TEST_SUITE( queue_tests, database_fixture )

BOOST_AUTO_TEST_CASE( convert_dascoin_cycles_precision_test )
{
  auto amount = db.cycles_to_dascoin(10000, 200);
  BOOST_CHECK_EQUAL(amount.value, 5000 * DASCOIN_DEFAULT_ASSET_PRECISION);

  amount = db.dascoin_to_cycles(5000 * DASCOIN_DEFAULT_ASSET_PRECISION, 200);
  BOOST_CHECK_EQUAL(amount.value, 10000);
}

BOOST_AUTO_TEST_CASE( convert_dascoin_cycles_test )
{
  // TODO: do not use hardcoded values.
  share_type amount = db.cycles_to_dascoin(10000, 300);
  BOOST_CHECK_EQUAL(amount.value, 333333333);

  amount = db.dascoin_to_cycles(333333333, 300);
  BOOST_CHECK_EQUAL(amount.value, 9999);
}

BOOST_AUTO_TEST_CASE( dascoin_reward_amount_regression_test )
{ try {

  share_type amount = 600000 * DASCOIN_DEFAULT_ASSET_PRECISION;
  BOOST_CHECK( amount > INT32_MAX );
  BOOST_CHECK( amount < INT64_MAX );
  BOOST_CHECK( amount == 600000 * DASCOIN_DEFAULT_ASSET_PRECISION );
  do_op(update_queue_parameters_operation(get_license_issuer_id(), {true}, {600}, {amount}));

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_CASE( update_queue_parameters_unit_test )
{ try {

  // Before HARDFORK_BLC_58_TIME, we submit reward amount divided by 10:
  do_op(update_queue_parameters_operation(get_license_issuer_id(), {true}, {600},
                                          {200 * DASCOIN_DEFAULT_ASSET_PRECISION}));

  const auto& params = get_chain_parameters();
  BOOST_CHECK_EQUAL( params.enable_dascoin_queue, true );
  BOOST_CHECK_EQUAL( params.reward_interval_time_seconds, 600 );
  BOOST_CHECK_EQUAL( params.dascoin_reward_amount.value, 2000 * DASCOIN_DEFAULT_ASSET_PRECISION );

  generate_blocks( HARDFORK_BLC_58_TIME );

  // After HARDFORK_BLC_58_TIME, reward amount is submitted with precision of 5:
  do_op(update_queue_parameters_operation(get_license_issuer_id(), {true}, {600},
                                          {2000 * DASCOIN_DEFAULT_ASSET_PRECISION}));

  const auto& params2 = get_chain_parameters();
  BOOST_CHECK_EQUAL( params2.dascoin_reward_amount.value, 2000 * DASCOIN_DEFAULT_ASSET_PRECISION );

  // TODO: handle negative cases

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_CASE( issue_chartered_license_unit_test )
{ try {
  VAULT_ACTOR(first)

  auto lic_typ = *(_dal.get_license_type("standard_charter"));

  do_op(issue_license_operation(get_license_issuer_id(), first_id, lic_typ.id,
      10, 200, db.head_block_time()));

  auto result_vec = *_dal.get_queue_submissions_with_pos(first_id).result;
  BOOST_CHECK_EQUAL( result_vec.size(), 1 );

  BOOST_CHECK_EQUAL( result_vec[0].position, 0 );

  auto rqo = result_vec[0].submission;
  BOOST_CHECK_EQUAL( rqo.origin, "charter_license" );
  BOOST_CHECK( rqo.license.valid() );
  BOOST_CHECK( *rqo.license == lic_typ.id );
  BOOST_CHECK( rqo.account == first_id );
  BOOST_CHECK_EQUAL( rqo.amount.value, 1210 );
  BOOST_CHECK_EQUAL( rqo.frequency.value, 200 );

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_CASE( submit_cycles_operation_test )
{ try {
  VAULT_ACTOR(first)

  adjust_frequency(200);

  adjust_cycles(first_id, 200);
  variant v;
  auto locked = *(_dal.get_license_type("standard_locked"));
  fc::to_variant(locked.id, v);
  const share_type bonus_percent = 50;
  share_type frequency_lock = 200;
  const time_point_sec issue_time = db.head_block_time();

  // This will fail, frequency cannot be zero:
  do_op(issue_license_operation(get_license_issuer_id(), first_id, locked.id,
                                bonus_percent, frequency_lock, issue_time));

  // Error: wrong frequency.
  GRAPHENE_REQUIRE_THROW(
    do_op(submit_cycles_to_queue_operation(first_id, 100, 730, v.as_string())),
    fc::exception
  );

  do_op(submit_cycles_to_queue_operation(first_id, 100, 200, v.as_string()));

  auto result_vec = *_dal.get_queue_submissions_with_pos(first_id).result;
  BOOST_CHECK_EQUAL( result_vec.size(), 1 );

  BOOST_CHECK_EQUAL( result_vec[0].position, 0 );

  auto rqo = result_vec[0].submission;
  BOOST_CHECK_EQUAL( rqo.origin, "user_submit" );
  BOOST_CHECK( rqo.license.valid() );
  BOOST_CHECK( rqo.account == first_id );
  BOOST_CHECK_EQUAL( rqo.amount.value, 100 );
  BOOST_CHECK_EQUAL( rqo.frequency.value, 200 );

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_CASE( basic_submit_reserved_cycles_to_queue_test )
{ try {
  VAULT_ACTOR(first)
  VAULT_ACTOR(second)
  VAULT_ACTOR(third)
  VAULT_ACTOR(fourth)

  adjust_dascoin_reward(500 * DASCOIN_DEFAULT_ASSET_PRECISION);
  adjust_frequency(200);

  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), first_id, 200, 200, ""));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), second_id, 400, 200, ""));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), third_id, 200, 200, ""));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), fourth_id, 600, 200, ""));

  // Queue looks like this:
  // 200 --> 400 --> 200 --> 600

  BOOST_CHECK_EQUAL( _dal.get_reward_queue_size(), 4 );

  toggle_reward_queue(true);

  // Wait for the cycles to be distributed:
  generate_blocks(db.head_block_time() + fc::seconds(get_chain_parameters().reward_interval_time_seconds));

  // Dascoin amounts shoud be:
  // 100, 200, 100, 100

  BOOST_CHECK_EQUAL( get_balance(first_id, get_dascoin_asset_id()), 100 * DASCOIN_DEFAULT_ASSET_PRECISION );
  BOOST_CHECK_EQUAL( get_balance(second_id, get_dascoin_asset_id()), 200 * DASCOIN_DEFAULT_ASSET_PRECISION );
  BOOST_CHECK_EQUAL( get_balance(third_id, get_dascoin_asset_id()), 100 * DASCOIN_DEFAULT_ASSET_PRECISION );
  BOOST_CHECK_EQUAL( get_balance(fourth_id, get_dascoin_asset_id()), 100 * DASCOIN_DEFAULT_ASSET_PRECISION );

  BOOST_CHECK_EQUAL( _dal.get_reward_queue_size(), 1 );

  // Wait for the rest if the cycles to be distributed:
  generate_blocks(db.head_block_time() + fc::seconds(get_chain_parameters().reward_interval_time_seconds));

  BOOST_CHECK_EQUAL( get_balance(fourth_id, get_dascoin_asset_id()), 300 * DASCOIN_DEFAULT_ASSET_PRECISION );

  BOOST_CHECK_EQUAL( _dal.get_reward_queue_size(), 0 );

} FC_LOG_AND_RETHROW() }
/*
BOOST_AUTO_TEST_CASE( basic_submit_cycles_to_queue_test )
{ try {
  VAULT_ACTORS((first)(second)(third)(fourth))

  adjust_dascoin_reward(500 * DASCOIN_DEFAULT_ASSET_PRECISION);
  adjust_frequency(200);

  adjust_cycles(first_id, 200);
  adjust_cycles(second_id, 400);
  adjust_cycles(third_id, 200);
  adjust_cycles(fourth_id, 600);

  do_op(submit_cycles_to_queue_operation(first_id, 200, 200, "TEST"));
  do_op(submit_cycles_to_queue_operation(second_id, 400, 200, "TEST"));
  do_op(submit_cycles_to_queue_operation(third_id, 200, 200, "TEST"));
  do_op(submit_cycles_to_queue_operation(fourth_id, 600, 200, "TEST"));

  // Queue looks like this:
  // 200 --> 400 --> 200 --> 600

  BOOST_CHECK_EQUAL( _dal.get_reward_queue_size(), 4 );

  toggle_reward_queue(true);

  // Wait for the cycles to be distributed:
  generate_blocks(db.head_block_time() + fc::seconds(get_chain_parameters().reward_interval_time_seconds));

  // Dascoin amounts shoud be:
  // 100, 200, 100, 100

  BOOST_CHECK_EQUAL( get_balance(first_id, get_dascoin_asset_id()), 100 * DASCOIN_DEFAULT_ASSET_PRECISION );
  BOOST_CHECK_EQUAL( get_balance(second_id, get_dascoin_asset_id()), 200 * DASCOIN_DEFAULT_ASSET_PRECISION );
  BOOST_CHECK_EQUAL( get_balance(third_id, get_dascoin_asset_id()), 100 * DASCOIN_DEFAULT_ASSET_PRECISION );
  BOOST_CHECK_EQUAL( get_balance(fourth_id, get_dascoin_asset_id()), 100 * DASCOIN_DEFAULT_ASSET_PRECISION );

  BOOST_CHECK_EQUAL( _dal.get_reward_queue_size(), 1 );

  // Wait for the cycles to be distributed:
  generate_blocks(db.head_block_time() + fc::seconds(get_chain_parameters().reward_interval_time_seconds));

  BOOST_CHECK_EQUAL( get_balance(fourth_id, get_dascoin_asset_id()), 300 * DASCOIN_DEFAULT_ASSET_PRECISION );

  BOOST_CHECK_EQUAL( _dal.get_reward_queue_size(), 0 );

} FC_LOG_AND_RETHROW() }
*/
BOOST_AUTO_TEST_CASE( basic_chartered_license_to_queue_test )
{
  const auto& issue =  [&](account_id_type account_id, const string& license_name, share_type bonus_percentage,
                           share_type frequency_lock)
  {
    auto lic_typ = *(_dal.get_license_type(license_name));
    do_op(issue_license_operation(get_license_issuer_id(), account_id, lic_typ.id,
        bonus_percentage, frequency_lock, db.head_block_time()));
  };

  try {

  VAULT_ACTORS((first)(second)(third)(fourth))

  adjust_dascoin_reward(5000 * DASCOIN_DEFAULT_ASSET_PRECISION);
  adjust_frequency(200);

  issue(first_id, "standard_charter", 100, 200);  // 1100 + 1 * 1100 = 2200 cycles
  issue(second_id, "standard_charter", 300, 200);  // 1100 + 3 * 1100 = 4400 cycles
  issue(third_id, "standard_charter", 100, 200);  // 1100 + 1 * 1100 = 2200 cycles
  issue(fourth_id, "standard_charter", 500, 200);  // 1100 + 5 * 1100 = 6600 cycles

  // Queue looks like this:
  // 2200 --> 4400 --> 2200 --> 6600

  BOOST_CHECK_EQUAL( _dal.get_reward_queue_size(), 4 );

  toggle_reward_queue(true);

  // Wait for the cycles to be distributed:
  generate_blocks(db.head_block_time() + fc::seconds(get_chain_parameters().reward_interval_time_seconds));

  // Dascoin amounts shoud be:
  // 1100, 2200, 1100, 600

  BOOST_CHECK_EQUAL( get_balance(first_id, get_dascoin_asset_id()), 1100 * DASCOIN_DEFAULT_ASSET_PRECISION );
  BOOST_CHECK_EQUAL( get_balance(second_id, get_dascoin_asset_id()), 2200 * DASCOIN_DEFAULT_ASSET_PRECISION );
  BOOST_CHECK_EQUAL( get_balance(third_id, get_dascoin_asset_id()), 1100 * DASCOIN_DEFAULT_ASSET_PRECISION );
  BOOST_CHECK_EQUAL( get_balance(fourth_id, get_dascoin_asset_id()), 600 * DASCOIN_DEFAULT_ASSET_PRECISION );

  BOOST_CHECK_EQUAL( _dal.get_reward_queue_size(), 1 );

  // Wait for the rest if the cycles to be distributed:
  generate_blocks(db.head_block_time() + fc::seconds(get_chain_parameters().reward_interval_time_seconds));

  BOOST_CHECK_EQUAL( get_balance(first_id, get_dascoin_asset_id()), 1100 * DASCOIN_DEFAULT_ASSET_PRECISION );
  BOOST_CHECK_EQUAL( get_balance(second_id, get_dascoin_asset_id()), 2200 * DASCOIN_DEFAULT_ASSET_PRECISION );
  BOOST_CHECK_EQUAL( get_balance(third_id, get_dascoin_asset_id()), 1100 * DASCOIN_DEFAULT_ASSET_PRECISION );
  BOOST_CHECK_EQUAL( get_balance(fourth_id, get_dascoin_asset_id()), 3300 * DASCOIN_DEFAULT_ASSET_PRECISION );

  BOOST_CHECK_EQUAL( _dal.get_reward_queue_size(), 0 );

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_CASE( submit_cycles_to_queue_by_license_operation_test )
{ try {
  VAULT_ACTOR(vault)

  auto standard_locked = *(_dal.get_license_type("standard_locked"));
  auto manager_locked = *(_dal.get_license_type("manager_locked"));
  const share_type bonus_percent = 10;
  share_type frequency_lock = 200;
  const time_point_sec issue_time = db.head_block_time();

  // Error: no issued license.
  GRAPHENE_REQUIRE_THROW( do_op(submit_cycles_to_queue_by_license_operation(vault_id, 100, standard_locked.id, 200, "TEST")), fc::exception );

  do_op(issue_license_operation(get_license_issuer_id(), vault_id, manager_locked.id, bonus_percent, frequency_lock, issue_time));

  // Error: issued other license.
  GRAPHENE_REQUIRE_THROW( do_op(submit_cycles_to_queue_by_license_operation(vault_id, 100, standard_locked.id, 200, "TEST")), fc::exception );

  // Error: cannot submit zero cycles.
  GRAPHENE_REQUIRE_THROW( do_op(submit_cycles_to_queue_by_license_operation(vault_id, 0, manager_locked.id, 200, "TEST")), fc::exception );

  // Error: not enough cycles on the balance.
  GRAPHENE_REQUIRE_THROW( do_op(submit_cycles_to_queue_by_license_operation(vault_id, 2 * DASCOIN_BASE_MANAGER_CYCLES, manager_locked.id, 200, "TEST")), fc::exception );

  // Error: frequency (20) is not equal to license's frequency.
  GRAPHENE_REQUIRE_THROW( do_op(submit_cycles_to_queue_by_license_operation(vault_id, 1000, manager_locked.id, 20, "TEST")), fc::exception );

  do_op(submit_cycles_to_queue_by_license_operation(vault_id, 1000, manager_locked.id, 200, "TEST"));

  const auto& license_information_obj = (*vault.license_information)(db);
  const auto& license_history = license_information_obj.history;
  const auto& license_record = license_history[0];
  const uint32_t remaining_cycles = static_cast<uint32_t>(DASCOIN_BASE_MANAGER_CYCLES * 1.1) - 1000;
  BOOST_CHECK_EQUAL( license_record.amount.value, remaining_cycles );

  const auto& balance = get_cycle_balance(vault_id);
  BOOST_CHECK_EQUAL( balance.value, remaining_cycles );

  // Error: not enough cycles on the balance:
  GRAPHENE_REQUIRE_THROW( do_op(submit_cycles_to_queue_by_license_operation(vault_id, remaining_cycles + 1, manager_locked.id, 200, "TEST")), fc::exception );

  do_op(submit_cycles_to_queue_by_license_operation(vault_id, 1000, manager_locked.id, 200, "TEST"));

  const auto& license_information_obj2 = (*vault.license_information)(db);
  const auto& license_history2 = license_information_obj2.history;
  const auto& license_record2 = license_history2[0];
  BOOST_CHECK_EQUAL( license_record2.amount.value, remaining_cycles - 1000 );

  const auto& balance2 = get_cycle_balance(vault_id);
  BOOST_CHECK_EQUAL( balance2.value, remaining_cycles - 1000 );

  // Issue 200 cycles:
  do_op(issue_cycles_to_license_operation(get_cycle_issuer_id(), vault_id, manager_locked.id, 200, "foo", "bar"));

  // Submit 100:
  do_op(submit_cycles_to_queue_by_license_operation(vault_id, 100, manager_locked.id, 200, "TEST"));

  const auto& license_information_obj3 = (*vault.license_information)(db);
  const auto& license_history3 = license_information_obj3.history;
  const auto& license_record3 = license_history3[0];

  // Cycles are first submitted from non_upgradeable_amount:
  BOOST_CHECK_EQUAL( license_record3.non_upgradeable_amount.value, 100 );
  const auto& balance3 = get_cycle_balance(vault_id);
  BOOST_CHECK_EQUAL( balance3.value, remaining_cycles - 900 );

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_CASE( submit_cycles_to_queue_operation_test )
{ try {
  VAULT_ACTOR(vault)

  auto standard_locked = *(_dal.get_license_type("standard_locked"));
  auto manager_locked = *(_dal.get_license_type("manager_locked"));
  variant vs;
  variant vm;
  fc::to_variant(standard_locked.id, vs);
  fc::to_variant(manager_locked.id, vm);

  const share_type bonus_percent = 10;
  share_type frequency_lock = 200;
  const time_point_sec issue_time = db.head_block_time();

  // Error: no issued license.
  GRAPHENE_REQUIRE_THROW( do_op(submit_cycles_to_queue_operation(vault_id, 100, 200, vs.as_string())), fc::exception );

  do_op(issue_license_operation(get_license_issuer_id(), vault_id, manager_locked.id, bonus_percent, frequency_lock, issue_time));

  // Error: issued other license.
  GRAPHENE_REQUIRE_THROW( do_op(submit_cycles_to_queue_operation(vault_id, 100, 200, vs.as_string())), fc::exception );

  // Error: cannot submit zero cycles.
  GRAPHENE_REQUIRE_THROW( do_op(submit_cycles_to_queue_operation(vault_id, 0, 200, vm.as_string())), fc::exception );

  // Error: not enough cycles on the balance.
  GRAPHENE_REQUIRE_THROW( do_op(submit_cycles_to_queue_operation(vault_id, 2 * DASCOIN_BASE_MANAGER_CYCLES, 200, vm.as_string())), fc::exception );

  // Error: frequency (20) is not equal to license's frequency.
  GRAPHENE_REQUIRE_THROW( do_op(submit_cycles_to_queue_operation(vault_id, 1000, 20, vm.as_string())), fc::exception );

  do_op(submit_cycles_to_queue_operation(vault_id, 1000, 200, vm.as_string()));

  const auto& license_information_obj = (*vault.license_information)(db);
  const auto& license_history = license_information_obj.history;
  const auto& license_record = license_history[0];
  const uint32_t remaining_cycles = static_cast<uint32_t>(DASCOIN_BASE_MANAGER_CYCLES * 1.1) - 1000;
  BOOST_CHECK_EQUAL( license_record.amount.value, remaining_cycles );

  const auto& balance = get_cycle_balance(vault_id);
  BOOST_CHECK_EQUAL( balance.value, remaining_cycles );

  // Error: not enough cycles on the balance:
  GRAPHENE_REQUIRE_THROW( do_op(submit_cycles_to_queue_operation(vault_id, remaining_cycles + 1, 200, vm.as_string())), fc::exception );

  do_op(submit_cycles_to_queue_operation(vault_id, 1000, 200, vm.as_string()));

  const auto& license_information_obj2 = (*vault.license_information)(db);
  const auto& license_history2 = license_information_obj2.history;
  const auto& license_record2 = license_history2[0];
  BOOST_CHECK_EQUAL( license_record2.amount.value, remaining_cycles - 1000 );

  const auto& balance2 = get_cycle_balance(vault_id);
  BOOST_CHECK_EQUAL( balance2.value, remaining_cycles - 1000 );

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_CASE( historic_sum_test )
{ try {
  VAULT_ACTORS((first)(second)(third)(fourth))

  // No coins minted at the start:
  BOOST_CHECK_EQUAL(get_dynamic_global_properties().total_dascoin_minted.value, 0);

  // Submit four reserve batches of 100 Dascoin:
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), first_id, 200, 200, ""));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), second_id, 200, 200, ""));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), third_id, 200, 200, ""));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), fourth_id, 200, 200, ""));

  // Check historic sums for all elements:
  // Should be: minted = 0; first: 100; second 200; third 300; fourth 400;
  BOOST_CHECK_EQUAL(get_dynamic_global_properties().total_dascoin_minted.value, 0);
  auto queue = _dal.get_reward_queue();
  BOOST_CHECK_EQUAL(queue[0].historic_sum.value, 100 * DASCOIN_DEFAULT_ASSET_PRECISION);
  BOOST_CHECK_EQUAL(queue[1].historic_sum.value, 200 * DASCOIN_DEFAULT_ASSET_PRECISION);
  BOOST_CHECK_EQUAL(queue[2].historic_sum.value, 300 * DASCOIN_DEFAULT_ASSET_PRECISION);
  BOOST_CHECK_EQUAL(queue[3].historic_sum.value, 400 * DASCOIN_DEFAULT_ASSET_PRECISION);

  // Adjust reward to 100 coin:
  adjust_dascoin_reward(200 * DASCOIN_DEFAULT_ASSET_PRECISION);
  adjust_frequency(200);

  // Wait for the first distribution interval:
  toggle_reward_queue(true);
  generate_blocks(db.head_block_time() + fc::seconds(get_chain_parameters().reward_interval_time_seconds));

  // Check historic sums for all elements:
  // Should be: minted = 200; third 300; fourth 400;
  BOOST_CHECK_EQUAL(get_dynamic_global_properties().total_dascoin_minted.value, 200 * DASCOIN_DEFAULT_ASSET_PRECISION);
  queue = _dal.get_reward_queue();
  BOOST_CHECK_EQUAL(queue[0].historic_sum.value, 300 * DASCOIN_DEFAULT_ASSET_PRECISION);
  BOOST_CHECK_EQUAL(queue[1].historic_sum.value, 400 * DASCOIN_DEFAULT_ASSET_PRECISION);

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_CASE( get_time_on_queue_test )
{ try {
  VAULT_ACTORS((first)(second)(third)(fourth))

  // Create the following queue:
  // Previuously minted: 400
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), first_id, 800, 200, "test"));

  // 100 -> 200 -> 100 -> 300
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), first_id, 100, 200, "test"));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), second_id, 400, 200, "test"));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), third_id, 200, 200, "test"));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), fourth_id, 600, 200, "test"));

  // Adjust reward to 100 coin:
  adjust_dascoin_reward(100 * DASCOIN_DEFAULT_ASSET_PRECISION);

  const auto& dgpo = get_dynamic_global_properties();
  const auto& gpo = get_global_properties();

  auto reward_amount = gpo.parameters.dascoin_reward_amount;
  auto reward_interval = gpo.parameters.reward_interval_time_seconds;

  // Wait for 4 intervals:
  toggle_reward_queue(true);
  generate_blocks(db.head_block_time() + fc::seconds(reward_interval));
  generate_blocks(db.head_block_time() + fc::seconds(reward_interval));
  generate_blocks(db.head_block_time() + fc::seconds(reward_interval));
  generate_blocks(db.head_block_time() + fc::seconds(reward_interval));

  BOOST_CHECK_EQUAL(dgpo.total_dascoin_minted.value, 400 * DASCOIN_DEFAULT_ASSET_PRECISION);

  auto queue = _dal.get_reward_queue();
  vector<uint32_t> times;
  times.reserve(queue.size());
  for(const auto& el: queue)
    times.emplace_back(get_time_on_queue(el.historic_sum, dgpo.total_dascoin_minted, reward_amount, reward_interval));

  BOOST_CHECK_EQUAL(times[0], 0);
  BOOST_CHECK_EQUAL(times[1], 2 * reward_interval);
  BOOST_CHECK_EQUAL(times[2], 3 * reward_interval);
  BOOST_CHECK_EQUAL(times[3], 6 * reward_interval);

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_CASE(submission_number_test)
{ try {
  VAULT_ACTORS((first)(second)(third)(fourth))

  BOOST_CHECK_EQUAL(db.get_dynamic_global_properties().last_minted_submission_num, 0);
  BOOST_CHECK_EQUAL(db.get_dynamic_global_properties().max_queue_submission_num, 0);

  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), first_id, 200, 200, "test"));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), second_id, 200, 200, "test"));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), third_id, 200, 200, "test"));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), fourth_id, 200, 200, "test"));

  // Now we have 4 submissions on the queue, 0 minted:
  auto last = db.get_dynamic_global_properties().last_minted_submission_num;
  auto max = db.get_dynamic_global_properties().max_queue_submission_num;
  BOOST_CHECK_EQUAL(last, 0);
  BOOST_CHECK_EQUAL(max, 4);
  BOOST_CHECK_EQUAL(_dal.get_reward_queue_size(), max-last);

  auto queue = _dal.get_reward_queue();
  BOOST_CHECK_EQUAL(queue[0].number, 1);
  BOOST_CHECK_EQUAL(queue[1].number, 2);
  BOOST_CHECK_EQUAL(queue[2].number, 3);
  BOOST_CHECK_EQUAL(queue[3].number, 4);

  // Mint two elements from the queue:
  adjust_dascoin_reward(100 * DASCOIN_DEFAULT_ASSET_PRECISION);
  toggle_reward_queue(true);
  auto reward_interval = get_global_properties().parameters.reward_interval_time_seconds;
  generate_blocks(db.head_block_time() + fc::seconds(reward_interval));
  generate_blocks(db.head_block_time() + fc::seconds(reward_interval));

  // Now we have 2 submissions on the queue, 2 minted:
  last = db.get_dynamic_global_properties().last_minted_submission_num;
  max = db.get_dynamic_global_properties().max_queue_submission_num;
  BOOST_CHECK_EQUAL(last, 2);
  BOOST_CHECK_EQUAL(max, 4);
  BOOST_CHECK_EQUAL(_dal.get_reward_queue_size(), max-last);

  queue = _dal.get_reward_queue();
  BOOST_CHECK_EQUAL(queue[0].number, 3);
  BOOST_CHECK_EQUAL(queue[1].number, 4);

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_CASE(get_queue_by_page_test)
{ try {
  VAULT_ACTORS((first)(second)(third)(fourth))

  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), first_id, 200, 200, "test"));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), second_id, 200, 200, "test"));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), third_id, 200, 200, "test"));
  do_op(submit_reserve_cycles_to_queue_operation(get_cycle_issuer_id(), fourth_id, 200, 200, "test"));

  // Now we have 4 submissions on the queue
  // Get the first queue entry
  auto queue = _dal.get_reward_queue_by_page(0, 1);
  BOOST_CHECK_EQUAL(queue.size(), 1);
  BOOST_CHECK_EQUAL(queue[0].number, 1);

  // Get first two entries from the queue
  queue = _dal.get_reward_queue_by_page(0, 2);
  BOOST_CHECK_EQUAL(queue.size(), 2);
  BOOST_CHECK_EQUAL(queue[0].number, 1);
  BOOST_CHECK_EQUAL(queue[1].number, 2);

  // Get the third entry
  queue = _dal.get_reward_queue_by_page(2, 1);
  BOOST_CHECK_EQUAL(queue.size(), 1);
  BOOST_CHECK_EQUAL(queue[0].number, 3);

  // Get from invalid position
  GRAPHENE_REQUIRE_THROW(_dal.get_reward_queue_by_page(4, 2), fc::exception);

  // Get invalid amount
  GRAPHENE_REQUIRE_THROW(_dal.get_reward_queue_by_page(1, 5), fc::exception);

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()


// Copyright David Abrahams 2002. Permission to copy, use,
// modify, sell and distribute this software is granted provided this
// copyright notice appears in all copies. This software is provided
// "as is" without express or implied warranty, and with no claim as
// to its suitability for any purpose.
#include <boost/static_assert.hpp>
#include <boost/python/detail/if_else.hpp>
#include <boost/type_traits/same_traits.hpp>

    typedef char c1;
    typedef char c2[2];
    typedef char c3[3];
    typedef char c4[4];

template <unsigned size>
struct choose
{
    typedef typename boost::python::detail::if_<
        (sizeof(c1) == size)
    >::template then<
        c1
    >::template elif<
        (sizeof(c2) == size)
    >::template then<
        c2
    >::template elif<
        (sizeof(c3) == size)
    >::template then<
        c3
    >::template elif<
        (sizeof(c4) == size)
    >::template then<
        c4
    >::template else_<void*>::type type;
};

int main()
{
    BOOST_STATIC_ASSERT((boost::is_same<choose<1>::type,c1>::value));
    BOOST_STATIC_ASSERT((boost::is_same<choose<2>::type,c2>::value));
    BOOST_STATIC_ASSERT((boost::is_same<choose<3>::type,c3>::value));
    BOOST_STATIC_ASSERT((boost::is_same<choose<4>::type,c4>::value));
    BOOST_STATIC_ASSERT((boost::is_same<choose<5>::type,void*>::value));
    return 0;
}


// Copyright 2017 Peter Dimov.
//
// 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 <boost/mp11/detail/config.hpp>

#if !defined( BOOST_MP11_HAS_CXX14_CONSTEXPR )

int main() {}

#else

#include <boost/mp11/algorithm.hpp>
#include <boost/core/lightweight_test.hpp>

using boost::mp11::mp_size_t;
using boost::mp11::mp_with_index;

struct F
{
    template<std::size_t I> constexpr std::size_t operator()( mp_size_t<I> ) const
    {
        return I;
    }
};

#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)

int main()
{
    constexpr std::size_t i = mp_with_index<64>( 57, F{} );
    STATIC_ASSERT( i == 57 );
}

#endif


#include <sstream>

#include "geometric/bounding_box.hpp"
#include "geometric/coordinate.hpp"

using namespace nepomuk::geometric;

// make sure we get a new main function here
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(wgs84_bbox)
{
    WGS84Coordinate coordinate(makeLatLonFromDouble<FixedLongitude>(0),
                               makeLatLonFromDouble<FixedLatitude>(0));

    WGS84Coordinate coordinate2(makeLatLonFromDouble<FixedLongitude>(45),
                                makeLatLonFromDouble<FixedLatitude>(60));

    WGS84BoundingBox bbox(coordinate, coordinate2);

    WGS84Coordinate coordinate3(makeLatLonFromDouble<FixedLongitude>(45),
                                makeLatLonFromDouble<FixedLatitude>(59));

    WGS84Coordinate coordinate4(makeLatLonFromDouble<FixedLongitude>(46),
                                makeLatLonFromDouble<FixedLatitude>(61));

    BOOST_CHECK(bbox.contains(coordinate3));
    BOOST_CHECK(!bbox.contains(coordinate4));
    BOOST_CHECK_EQUAL(bbox.width(), 45);
    BOOST_CHECK_EQUAL(bbox.height(), 60);
}

BOOST_AUTO_TEST_CASE(mercator_bbox)
{
    WGS84Coordinate coordinate(makeLatLonFromDouble<FixedLongitude>(0),
                               makeLatLonFromDouble<FixedLatitude>(0));

    WGS84Coordinate coordinate2(makeLatLonFromDouble<FixedLongitude>(180),
                                makeLatLonFromDouble<FixedLatitude>(90));

    MercatorBoundingBox bbox(coordinate, coordinate2);

    WGS84Coordinate coordinate3(makeLatLonFromDouble<FixedLongitude>(0),
                                makeLatLonFromDouble<FixedLatitude>(0));

    WGS84Coordinate coordinate4(makeLatLonFromDouble<FixedLongitude>(-0.1),
                                makeLatLonFromDouble<FixedLatitude>(-0.1));

    BOOST_CHECK(bbox.contains(coordinate3));
    BOOST_CHECK(!bbox.contains(coordinate4));
    BOOST_CHECK_EQUAL(bbox.width(), 128);
    BOOST_CHECK_EQUAL(bbox.height(), 128);

    MercatorCoordinate ll(coordinate);
    MercatorCoordinate ur(coordinate2);
    std::swap(ll.latitude, ur.latitude);
    MercatorBoundingBox bbox2(ll, ur);

    BOOST_CHECK(bbox2.contains(coordinate3));
    BOOST_CHECK(!bbox2.contains(coordinate4));
    BOOST_CHECK_EQUAL(bbox2.width(), 128);
    BOOST_CHECK_EQUAL(bbox2.height(), 128);
}


/**************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basegfx.hxx"
// autogenerated file with codegen.pl

#include "preextstl.h"
#include "gtest/gtest.h"
#include "postextstl.h"

#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/curve/b2dcubicbezier.hxx>
#include <basegfx/curve/b2dbeziertools.hxx>
#include <basegfx/range/b2dpolyrange.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
#include <basegfx/polygon/b2dpolygonclipper.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/numeric/ftools.hxx>

#include <boost/bind.hpp>

using namespace ::basegfx;

// FIXME:
#define RUN_OLD_FAILING_TESTS 0

namespace basegfx2d
{

class genericclipper : public ::testing::Test
{
protected:
    B2DPolygon aSelfIntersecting;
    B2DPolygon aShiftedRectangle;

public:
    // initialise your test code values here.
    virtual void SetUp()
    {
        aSelfIntersecting.append(B2DPoint(0,  0));
        aSelfIntersecting.append(B2DPoint(0,  100));
        aSelfIntersecting.append(B2DPoint(75, 100));
        aSelfIntersecting.append(B2DPoint(75, 50));
        aSelfIntersecting.append(B2DPoint(25, 50));
        aSelfIntersecting.append(B2DPoint(25, 150));
        aSelfIntersecting.append(B2DPoint(100,150));
        aSelfIntersecting.append(B2DPoint(100,0));
        aSelfIntersecting.setClosed(true);

        aShiftedRectangle = tools::createPolygonFromRect(
            B2DRange(0,90,20,150));
    }

    virtual void TearDown()
    {}

    void validate(const char* pName,
                  const char* pValidSvgD,
                  B2DPolyPolygon (*pFunc)(const B2DPolyPolygon&, const B2DPolyPolygon&))
    {
        const B2DPolyPolygon aSelfIntersect(
            tools::prepareForPolygonOperation(aSelfIntersecting));
        const B2DPolyPolygon aRect(
            tools::prepareForPolygonOperation(aShiftedRectangle));
#if defined(VERBOSE)
        fprintf(stderr, "%s input LHS - svg:d=\"%s\"\n",
                pName, rtl::OUStringToOString(
                    basegfx::tools::exportToSvgD(
                        aSelfIntersect, true, true, false),
                    RTL_TEXTENCODING_UTF8).getStr() );
        fprintf(stderr, "%s input RHS - svg:d=\"%s\"\n",
                pName, rtl::OUStringToOString(
                    basegfx::tools::exportToSvgD(
                        aRect, true, true, false),
                    RTL_TEXTENCODING_UTF8).getStr() );
#endif

        const B2DPolyPolygon aRes=
            pFunc(aSelfIntersect, aRect);

#if defined(VERBOSE)
        fprintf(stderr, "%s - svg:d=\"%s\"\n",
                pName, rtl::OUStringToOString(
                    basegfx::tools::exportToSvgD(aRes, true, true, false),
                    RTL_TEXTENCODING_UTF8).getStr() );
#endif

        rtl::OUString aValid=rtl::OUString::createFromAscii(pValidSvgD);

        ASSERT_TRUE(basegfx::tools::exportToSvgD(aRes, true, true, false) == aValid) << pName;
    }
};

TEST_F(genericclipper, validateOr)
{
#if RUN_OLD_FAILING_TESTS
    const char* pValid="m0 0h100v150h-75v-50h-5v50h-20v-50-10zm75 10v-50h-50v50z";
    validate("validateOr", pValid, &tools::solvePolygonOperationOr);
#endif
}

TEST_F(genericclipper, validateXor)
{
#if RUN_OLD_FAILING_TESTS
    const char* pValid="m0 0h100v150h-75v-50h-5v50h-20v-50-10zm0 10h20v-10h-20zm75 10v-50h-50v50z";
    validate("validateXor", pValid, &tools::solvePolygonOperationXor);
#endif
}

TEST_F(genericclipper, validateAnd)
{
    const char* pValid="m0 100v-10h20v10z";
    validate("validateAnd", pValid, &tools::solvePolygonOperationAnd);
}

TEST_F(genericclipper, validateDiff)
{
#if RUN_OLD_FAILING_TESTS
    const char* pValid="m0 90v-90h100v150h-75v-50h-5v-10zm55 10v-50h-50v50z";
    validate("validateDiff", pValid, &tools::solvePolygonOperationDiff);
#endif
}

// -----------------------------------------------------------------------------

} // namespace basegfx2d


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#include <nt2/reduction/include/functions/dot.hpp>
#include <nt2/include/functions/load.hpp>
#include <nt2/sdk/functor/meta/call.hpp>
#include <nt2/sdk/complex/complex.hpp>
#include <nt2/sdk/complex/dry.hpp>
#include <boost/simd/sdk/simd/native.hpp>
#include <boost/simd/sdk/simd/io.hpp>

#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>
#include <nt2/sdk/unit/tests/ulp.hpp>
#include <nt2/include/constants/zero.hpp>
#include <nt2/include/constants/mzero.hpp>
#include <nt2/include/constants/one.hpp>
#include <nt2/include/constants/mzero.hpp>
#include <nt2/include/constants/inf.hpp>
#include <nt2/include/constants/minf.hpp>
#include <nt2/include/constants/nan.hpp>
#include <nt2/include/constants/i.hpp>
#include <nt2/include/constants/pi.hpp>
#include <nt2/include/constants/pio_2.hpp>
#include <nt2/include/functions/conj.hpp>
#include <nt2/include/functions/extract.hpp>
#include <nt2/include/functions/multiplies.hpp>
#include <nt2/sdk/meta/cardinal_of.hpp>

NT2_TEST_CASE_TPL (dot_cplx, NT2_SIMD_REAL_TYPES)
{
  using nt2::dot;
  using nt2::tag::dot_;
  using boost::simd::native;
  using boost::simd::splat;

  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef std::complex<T>               cT;
  typedef native<T,ext_t>               vT;
  typedef native<cT,ext_t>              vcT;

  NT2_TEST_TYPE_IS( typename nt2::meta::call<dot_(vcT, vcT)>::type
                  , cT
                  );
  vcT a[] = {nt2::Zero<vcT>(), nt2::One<vcT>(), nt2::I<vcT>(), nt2::Pi<vcT>(),
             nt2::Pio_2<vcT>(), nt2::Inf<vcT>(), nt2::Minf<vcT>(), nt2::Nan<vcT>(),
             vcT(nt2::Zero<vT>(),nt2::Zero<vT>()),vcT(nt2::Inf<vT>(),nt2::Zero<vT>()),
             vcT(nt2::Minf<vT>(),nt2::Zero<vT>()),vcT(nt2::Nan<vT>(),nt2::Zero<vT>()),
             vcT(nt2::Zero<vT>(),nt2::Inf<vT>()), vcT(nt2::Inf<vT>(),nt2::Inf<vT>()),
             vcT(nt2::Minf<vT>(),nt2::Inf<vT>()), vcT(nt2::Nan<vT>(),nt2::Inf<vT>()),
             vcT(nt2::Zero<vT>(),nt2::Minf<vT>()),vcT(nt2::Inf<vT>(),nt2::Minf<vT>()),
             vcT(nt2::Minf<vT>(),nt2::Minf<vT>()),vcT(nt2::Nan<vT>(),nt2::Minf<vT>()),
             vcT(nt2::Zero<vT>(),nt2::Nan<vT>()), vcT(nt2::Inf<vT>(),nt2::Nan<vT>()),
             vcT(nt2::Minf<vT>(),nt2::Nan<vT>()), vcT(nt2::Nan<vT>(),nt2::Nan<vT>()),
             vcT(nt2::Zero<vT>(),nt2::Pi <vT>()), vcT(nt2::Inf<vT>(),nt2::Pi <vT>()),
             vcT(nt2::Minf<vT>(),nt2::Pi <vT>()), vcT(nt2::Nan<vT>(),nt2::Pi <vT>()),
  };
  size_t N =  sizeof(a)/sizeof(vcT);
  for(size_t i=0; i < N; i++)
  {
    vcT a1 = a[i];
    cT aa1 = nt2::extract<0>(a1);
    for(size_t j=0; j < N; j++)
    {
      vcT a2 = a[j];
      cT aa2 = nt2::extract<0>(a2);
      NT2_TEST_ULP_EQUAL(nt2::dot(a1, a2),nt2::multiplies(nt2::dot(aa1, aa2), T(nt2::meta::cardinal_of<vcT>::value)), 0.5);
    }
  }
}