/*    Copyright (c) 2010-2019, Delft University of Technology
 *    All rigths reserved
 *
 *    This file is part of the Tudat. Redistribution and use in source and
 *    binary forms, with or without modification, are permitted exclusively
 *    under the terms of the Modified BSD license. You should have received
 *    a copy of the license with this file. If not, please or visit:
 *    http://tudat.tudelft.nl/LICENSE.
 *
 */

#include <boost/date_time/gregorian/gregorian.hpp>

#include "Tudat/Astrodynamics/BasicAstrodynamics/physicalConstants.h"
#include "Tudat/Astrodynamics/BasicAstrodynamics/timeConversions.h"
#include "Tudat/Basics/timeType.h"

namespace tudat
{
namespace basic_astrodynamics
{

//! Function to get the Julian day on J2000, in double precision.
template< >
double getJulianDayOnJ2000< double >( )
{
    return JULIAN_DAY_ON_J2000;
}

//! Function to get the Julian day on J2000, in long double precision.
template< >
long double getJulianDayOnJ2000< long double >( )
{
    return JULIAN_DAY_ON_J2000_LONG;
}

//! Function to get the Julian day on zero modified Julian day, in double precision.
template< >
double getJulianDayOnMjd0< double >( )
{
    return JULIAN_DAY_AT_0_MJD;
}

//! Function to get the Julian day on zero modified Julian day, in long double precision.
template< >
long double getJulianDayOnMjd0< long double >( )
{
    return JULIAN_DAY_AT_0_MJD_LONG;
}

//! Function to get the synchronization Julian day of TT, TCG, and TCB, in double precision.
template< >
double getTimeOfTaiSynchronizationJulianDay< double >( )
{
    return TAI_JULIAN_DAY_AT_TIME_SYNCHRONIZATION;
}

//! Function to get the synchronization Julian day of TT, TCG, and TCB, in long double precision.
template< >
long double getTimeOfTaiSynchronizationJulianDay< long double >( )
{
    return TAI_JULIAN_DAY_AT_TIME_SYNCHRONIZATION_LONG;
}

//! Function to get the difference between TDB and (TT, TCB and TCB) at TAI_JULIAN_DAY_AT_TIME_SYNCHRONIZATION,
//! in double precision.
template< >
double getTdbSecondsOffsetAtSynchronization< double >( )
{
    return TDB_SECONDS_OFFSET_AT_SYNCHRONIZATION;
}

//! Function to get the difference between TDB and (TT, TCB and TCB) at TAI_JULIAN_DAY_AT_TIME_SYNCHRONIZATION,
//! in long double precision.
template< >
long double getTdbSecondsOffsetAtSynchronization< long double >( )
{
    return TDB_SECONDS_OFFSET_AT_SYNCHRONIZATION_LONG;
}

//! Function to get the offset of TT from TAI (constant by definition), in double precision.
template< >
double getTTMinusTai< double >( )
{
    return TT_MINUS_TAI;
}

//! Function to get the offset of TT from TAI (constant by definition), in long double precision.
template< >
long double getTTMinusTai< long double >( )
{
    return TT_MINUS_TAI_LONG;
}

//! Function to get the offset of TT from TAI (constant by definition), in Time format.
template< >
Time getTTMinusTai< Time>( )
{
    return Time( TT_MINUS_TAI_LONG );
}

//! Function to convert julian day to gregorian calendar date.
boost::gregorian::date convertJulianDayToCalendarDate( const double julianDay )
{
    // Declare temporary variables.
    int L, M, N, P, Q;

    // Declare date variables.
    int day, month, year;

    // Execute algorithm.
    double shiftedJulianDay = julianDay + 0.5;
    if( shiftedJulianDay > 2299160 )    // after Oct 4, 1582
    {
        L = shiftedJulianDay + 68569;
        M = (4 * L) / 146097;
        L = L - ((146097 * M + 3) / 4);
        N = (4000 * (L + 1)) / 1461001;
        L = L - ((1461 * N) / 4) + 31;
        P = (80 * L) / 2447;
        day = int(L - (2447 * P) / 80);
        L = P / 11;
        month = int(P + 2 - 12 * L);
        year = int(100 * (M - 49) + N + L);
    }
    else
    {
        P = shiftedJulianDay + 1402;
        Q = (P - 1) / 1461;
        L = P - 1461 * Q;
        M = (L - 1) / 365 - L / 1461;
        N = L - 365 * M + 30;
        P = (80 * N) / 2447;
        day = int(N - (2447 * P) / 80);
        N = P / 11;
        month = int( P + 2 - 12 * N );
        year = int( 4 * Q + M + N - 4716 );
        if(year <= 0)
        {
            --year;
        }
    }
    // catch century/non-400 non-leap years
    if( year > 1599 && !( year % 100 )
            && ( year % 400 ) && month == 2 && day == 29 )
    {
        month = 3;
        day = 1;
    }

    // Create and return date object

    return boost::gregorian::date( year, month, static_cast< double >( day ) );
}

//! Function to determine whether the given year is a leap year (i.e. has 366 days)
bool isLeapYear( const int year )
{
    bool isLeapYear = 0;
    if( ( year % 4 == 0 ) && !( ( year % 100 == 0 ) && !( year % 400 == 0 ) ) )
    {
        isLeapYear = 1;
    }
    return isLeapYear;
}

//! Function that returns number of days in given month number
int getDaysInMonth( const int month,
                    const int year )
{
    // Declare number of days per month
    static const int daysPerMonth[ 12 ] =
    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    int numberOfDays = 0;

    // Check input consistency
    if( month < 1 || month > 12 )
    {
        throw std::runtime_error( "Error, month number " + std::to_string( month ) +
                                  " does not exist, value must be gretaer than 0 and smaller than 13" );
    }
    else
    {
        numberOfDays = daysPerMonth[ month - 1 ];

        // Check for leap year.
        if( month == 2 && isLeapYear( year ) )
        {
            numberOfDays++;
        }
    }
    return numberOfDays;
}

//! Determine number of full days that have passed in current year
int convertDayMonthYearToDayOfYear( const int day,
                                    const int month,
                                    const int year )
{
    return convertDayMonthYearToDayOfYear( boost::gregorian::date( year, month, day ) );
}

//! Determine number of full days that have passed in current year
int convertDayMonthYearToDayOfYear( const boost::gregorian::date calendarDate )
{
    // Calculate and return result (taking into account that this function should return 0 for first day, not 1)
    return ( calendarDate.day_of_year( ) - 1 );
}

//! Determine number of seconds into current day of given time
double calculateSecondsInCurrentJulianDay( const double julianDay )
{
    // Calculate and return result, taking into accoun the fact that julian day 0.0 is at 12:00 and result starts counting at 00:00
    return ( julianDay + 0.5 - std::floor( julianDay + 0.5 ) ) * physical_constants::JULIAN_DAY;
}

//! Function to create the calendar date from the year and the number of days in the year
boost::gregorian::date convertYearAndDaysInYearToDate( const int year, const int daysInYear )
{
    // Go to day 1 at 01-01 convention
    int daysLeft = daysInYear + 1;

    // Loop over all months (starting at January until month in which daysinYear is situated is found)
    int currentMonth = 1;
    int daysInCurrentMonth;
    bool isConverged = 0;
    while( !isConverged )
    {
        // Determine days in current month
        daysInCurrentMonth = getDaysInMonth( currentMonth, year );
        if( daysInCurrentMonth < daysLeft )
        {
            // Subtract days in current month from days left.
            daysLeft -= daysInCurrentMonth;

            // Update month and check consistency
            currentMonth++;
            if( currentMonth > 12 )
            {
                throw std::runtime_error(
                            "Error when converting year and days in year to date, month number has exceeded 12" );
            }
        }
        else
        {
            isConverged = 1;
        }
    }

    // Create date and return
    boost::gregorian::date date( year, currentMonth, daysLeft );

    if( date.day_of_year( ) != daysInYear + 1 )
    {
        throw std::runtime_error(
                    "Error when converting year and days in year to date, inconsistent output" );
    }

    return date;
}

//! Perform apprixmate conversion of TT to TDB
double approximateConvertTTtoTDB( const double ttSecondsSinceJ2000 )
{
    double ttCenturiesSinceJ2000 = ttSecondsSinceJ2000 / ( 100.0 * physical_constants::JULIAN_YEAR );
    return ttSecondsSinceJ2000 + 0.001657  * std::sin( 628.3076 * ttCenturiesSinceJ2000 + 6.2401 );
}

} // namespace basic_astrodynamics
} // namespace tudat


#include <boost/functional/hash.hpp>
#include <vector>
#include <cassert>

#include "memory.hh"

using namespace std;

static const double alpha = 1.0 / 8.0;
static const double slow_alpha = 1.0 / 256.0;
// static const double middle_alpha = 1.0 / 32.0;

void Memory::lost(const int lost) {
  // printf("lost: %d\n", lost);
  assert(lost==1);
  for (auto i=0; i<lost; i++) {
    _loss = 1;
    _loss_ewma = (1 - alpha) * _loss_ewma + alpha * 1;
    _slow_loss_ewma = (1 - slow_alpha) * _slow_loss_ewma + slow_alpha * 1;
  }
  _lost_since_last_time = lost;
}

void Memory::window(const double& s_window) {
  // printf("lost: %d\n", lost);
  _window = s_window;
  _window_ewma = (1 - alpha) * _window_ewma + alpha * s_window;
  _slow_window_ewma = (1 - slow_alpha) * _slow_window_ewma + slow_alpha * s_window;
}

void Memory::packets_received( const vector< remy::Packet > & packets, const unsigned int,
  const int largest_ack )
{
  for ( const auto &x : packets ) {
    /* This is already checked by Unicorn... */
    // if ( x.flow_id != flow_id ) {
    //   continue;
    // }
    // puts("I'm going through the freaking loop in RemyMemory when receiving a packet...");
    const double rtt = x.tick_received - x.tick_sent;
    _rtt = rtt;
    int pkt_outstanding = 1;
    if ( x.seq_num > largest_ack ) {
      pkt_outstanding = x.seq_num - largest_ack;
    }
    if (_min_rtt <= 0) {
      _min_rtt = rtt;
    }
    if ( _last_tick_sent == 0 || _last_tick_received == 0 ) {
      _last_tick_sent = x.tick_sent;
      _last_tick_received = x.tick_received;
      _min_rtt = rtt;
    } else {
      _send = (x.tick_sent - _last_tick_sent);
      _rec = (x.tick_received - _last_tick_received);
      _rec_send_ewma = (1 - alpha) * _rec_send_ewma + alpha * (x.tick_sent - _last_tick_sent);
      _rec_rec_ewma = (1 - alpha) * _rec_rec_ewma + alpha * (x.tick_received - _last_tick_received);

      _slow_rec_rec_ewma = (1 - slow_alpha) * _slow_rec_rec_ewma + slow_alpha * (x.tick_received - _last_tick_received);
      _slow_rec_send_ewma = (1 - slow_alpha) * _slow_rec_send_ewma + slow_alpha * (x.tick_sent - _last_tick_sent);

      _last_tick_sent = x.tick_sent;
      _last_tick_received = x.tick_received;

      _min_rtt = min( _min_rtt, rtt );
      _rtt_ratio = double( rtt ) / double( _min_rtt );
      _rtt_diff = rtt - _min_rtt;
      assert( _rtt_ratio >= 1.0 );
      assert( _rtt_diff >= 0 );
      _queueing_delay = _rec_rec_ewma * pkt_outstanding;

      _loss = 0;
      _loss_ewma = (1 - alpha) * _loss_ewma + alpha * 0;
      _slow_loss_ewma = (1 - slow_alpha) * _slow_loss_ewma + slow_alpha * 0;

      _rtt_ewma = (1 - alpha) * _rtt_ewma + alpha * rtt;
      _slow_rtt_ewma = (1 - slow_alpha) * _slow_rtt_ewma + slow_alpha * rtt;
    }
  }
}

string Memory::str( void ) const
{
  char tmp[ 256 ];
  snprintf( tmp, 256, "sewma=%f, rewma=%f, rttr=%f, slowrewma=%f, rttd=%f, qdelay=%f", _rec_send_ewma, _rec_rec_ewma, _rtt_ratio, _slow_rec_rec_ewma, _rtt_diff, _queueing_delay );
  return tmp;
}

string Memory::str( unsigned int num ) const
{
  char tmp[ 50 ];
  switch ( num ) {
    case 0:
      snprintf( tmp, 50, "sewma=%f ", _rec_send_ewma );
      break;
    case 1:
      snprintf( tmp, 50, "rewma=%f ", _rec_rec_ewma );
      break;
    case 2:
      snprintf( tmp, 50, "rttr=%f ", _rtt_ratio );
      break;
    case 3:
      snprintf( tmp, 50, "slowrewma=%f ", _slow_rec_rec_ewma );
      break;
    case 4:
      snprintf( tmp, 50, "rttd=%f ", _rtt_diff );
      break;
    case 5:
      snprintf( tmp, 50, "qdelay=%f ", _queueing_delay );
      break;
  }
  return tmp;
}

const Memory & MAX_MEMORY( void )
{
  static const Memory max_memory( { 163840, 163840, 163840, 163840, 163840, 163840 } );
  return max_memory;
}

RemyBuffers::Memory Memory::DNA( void ) const
{
  RemyBuffers::Memory ret;
  ret.set_rec_send_ewma( _rec_send_ewma );
  ret.set_rec_rec_ewma( _rec_rec_ewma );
  ret.set_rtt_ratio( _rtt_ratio );
  ret.set_slow_rec_rec_ewma( _slow_rec_rec_ewma );
  ret.set_rtt_diff( _rtt_diff );
  ret.set_queueing_delay( _queueing_delay );
  return ret;
}

/* If fields are missing in the DNA, we want to wildcard the resulting rule to match anything */
#define get_val_or_default( protobuf, field, limit ) \
  ( (protobuf).has_ ## field() ? (protobuf).field() : (limit) ? 0 : 163840 )

Memory::Memory( const bool is_lower_limit, const RemyBuffers::Memory & dna )
  : _rec_send_ewma( get_val_or_default( dna, rec_send_ewma, is_lower_limit ) ),
    _rec_rec_ewma( get_val_or_default( dna, rec_rec_ewma, is_lower_limit ) ),
    _rtt_ratio( get_val_or_default( dna, rtt_ratio, is_lower_limit ) ),
    _slow_rec_rec_ewma( get_val_or_default( dna, slow_rec_rec_ewma, is_lower_limit ) ),
    _rtt_diff( get_val_or_default( dna, rtt_diff, is_lower_limit ) ),
    _queueing_delay( get_val_or_default( dna, queueing_delay, is_lower_limit ) ),
    _slow_rec_send_ewma( 0 ),
    _loss(0), //FIXME: Should change "DNA" at some point
    _loss_ewma(0),
    _slow_loss_ewma(0),
    _last_tick_sent( 0 ),
    _last_tick_received( 0 ),
    _min_rtt( 0 ),
    _send(0),
    _rec(0),
    _lost_since_last_time(0),
    _rtt(0),
    _rtt_ewma(0),
    _slow_rtt_ewma(0),
    _window(0),
    _window_ewma(0),
    _slow_window_ewma(0)
{
  assert(false);
}

size_t hash_value( const Memory & mem )
{
  size_t seed = 0;
  boost::hash_combine( seed, mem._rec_send_ewma );
  boost::hash_combine( seed, mem._rec_rec_ewma );
  boost::hash_combine( seed, mem._rtt_ratio );
  boost::hash_combine( seed, mem._slow_rec_rec_ewma );
  boost::hash_combine( seed, mem._rtt_diff );
  boost::hash_combine( seed, mem._queueing_delay );

  return seed;
}


// Copyright (c) 2010  GeometryFactory Sarl (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 3 of the License,
// or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
//
// Author(s)     : Laurent Rineau

#ifndef CGAL_NO_DEPRECATED_CODE

#include <boost/optional.hpp>

// The following Mesh_cell_criteria
 and Mesh_facet_criteria

// implements models of Mesh(Cell|Facet)Criteria_3 (from specification of
// CGAL-3.7), that always return "not bad".

template <typename Tr>
struct Mesh_cell_criteria {
  typedef int Cell_quality;
  typedef boost::optional<Cell_quality> Cell_badness;

  Cell_badness operator()(const typename Tr::Cell_handle) const {
    return Cell_badness();
  }
};

template <typename Tr>
struct Mesh_facet_criteria {
  typedef int Facet_quality;
  typedef boost::optional<Facet_quality> Facet_badness;

  Facet_badness operator()(const typename Tr::Facet&) const {
    return Facet_badness();
  }

  Facet_badness operator()(const typename Tr::Cell_handle, int) const {
    return Facet_badness();
  }
};

// The following Mesh_new_cell_criteria
 and Mesh_new_facet_criteria

// implements models of Mesh(Cell|Facet)Criteria_3 (from specification of
// CGAL-3.8), that always return "not bad".

template <typename Tr>
struct Mesh_new_cell_criteria {
  typedef int Cell_quality;
  typedef boost::optional<Cell_quality> Is_cell_bad;

  Is_cell_bad operator()(const typename Tr::Cell_handle) const {
    return Is_cell_bad();
  }
};

template <typename Tr>
struct Mesh_new_facet_criteria {
  typedef int Facet_quality;
  typedef boost::optional<Facet_quality> Is_facet_bad;

  Is_facet_bad operator()(const typename Tr::Facet&) const {
    return Is_facet_bad();
  }

  Is_facet_bad operator()(const typename Tr::Cell_handle, int) const {
    return Is_facet_bad();
  }
};

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>

#include <CGAL/Mesh_triangulation_3.h>
#include <CGAL/Mesh_complex_3_in_triangulation_3.h>
#include <CGAL/Mesh_criteria_3.h>

#include <CGAL/Implicit_mesh_domain_3.h>
#include <CGAL/make_mesh_3.h>
#include <CGAL/remove_far_points_in_mesh_3.h>

// Domain
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::FT FT;
typedef K::Point_3 Point;
typedef FT (Function)(const Point&);
typedef CGAL::Implicit_mesh_domain_3<Function,K> Mesh_domain;

// To avoid verbose function and named parameters call
using namespace CGAL::parameters;

// Function
FT sphere_function (const Point& p)
{ return CGAL::squared_distance(p, Point(CGAL::ORIGIN))-1; }

template <typename Tr>
void test()
{
  typedef CGAL::Mesh_complex_3_in_triangulation_3
 C3t3;

  // Criteria
  typedef CGAL::Mesh_criteria_3 Mesh_criteria;
  typedef CGAL::Mesh_criteria_3 Mesh_new_criteria;

  // Domain (Warning: Sphere_3 constructor uses squared radius !)
  Mesh_domain domain(sphere_function, K::Sphere_3(CGAL::ORIGIN, 2.));

  // Mesh criteria
  Mesh_criteria criteria = Mesh_criteria(Mesh_facet_criteria
(),
                                         Mesh_cell_criteria
());
  Mesh_new_criteria new_criteria =
    Mesh_new_criteria(Mesh_new_facet_criteria
(),
                      Mesh_new_cell_criteria
());

  // Mesh generation
  C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria, no_exude(), no_perturb());
  CGAL::refine_mesh_3<C3t3>(c3t3, domain, new_criteria, no_exude(), no_perturb());
  CGAL::remove_far_points_in_mesh_3(c3t3);

  std::cout << "Number of vertices: "
            << c3t3.triangulation().number_of_vertices() << "\n"
            << " (should be a small number, because there are no criteria"
            << " on facets and cells)\n";
}

int main()
{
  std::cout << "==== Test sequential meshing ====" << std::endl;
  typedef CGAL::Mesh_triangulation_3<Mesh_domain>::type Tr;
  test
();

#ifdef CGAL_LINKED_WITH_TBB
  std::cout << "==== Test parallel meshing ====" << std::endl;
  typedef CGAL::Mesh_triangulation_3<
    Mesh_domain,
    CGAL::Kernel_traits<Mesh_domain>::Kernel,
    CGAL::Parallel_tag>::type TrP;
  test<TrP>();
#endif

  return 0;
}

#else // CGAL_NO_DEPRECATED_CODE
#include <iostream>
int main() {
  std::cerr << "CGAL_NO_DEPRECATED_CODE is defined. Nothing to test.\n";
  return 0;
}
#endif // CGAL_NO_DEPRECATED_CODE


//---------------------------------------------------------------------------//
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
//
// 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
//
// See http://boostorg.github.com/compute for more information.
//---------------------------------------------------------------------------//

#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_SCAN_ON_GPU_HPP
#define BOOST_COMPUTE_ALGORITHM_DETAIL_SCAN_ON_GPU_HPP

#include <boost/compute/kernel.hpp>
#include <boost/compute/detail/meta_kernel.hpp>
#include <boost/compute/command_queue.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/detail/iterator_range_size.hpp>
#include <boost/compute/memory/local_buffer.hpp>
#include <boost/compute/iterator/buffer_iterator.hpp>

namespace boost {
namespace compute {
namespace detail {

template<class InputIterator, class OutputIterator, class BinaryOperator>
class local_scan_kernel : public meta_kernel
{
public:
    local_scan_kernel(InputIterator first,
                      InputIterator last,
                      OutputIterator result,
                      bool exclusive,
                      BinaryOperator op)
        : meta_kernel("local_scan")
    {
        typedef typename std::iterator_traits<InputIterator>::value_type T;

        (void) last;

        bool checked = true;

        m_block_sums_arg = add_arg<T *>(memory_object::global_memory, "block_sums");
        m_scratch_arg = add_arg<T *>(memory_object::local_memory, "scratch");
        m_block_size_arg = add_arg<const cl_uint>("block_size");
        m_count_arg = add_arg<const cl_uint>("count");
        m_init_value_arg = add_arg<const T>("init");

        // work-item parameters
        *this <<
            "const uint gid = get_global_id(0);\n" <<
            "const uint lid = get_local_id(0);\n";

        // check against data size
        if(checked){
            *this <<
                "if(gid < count){\n";
        }

        // copy values from input to local memory
        if(exclusive){
            *this <<
                decl<const T>("local_init") << "= (gid == 0) ? init : 0;\n" <<
                "if(lid == 0){ scratch[lid] = local_init; }\n" <<
                "else { scratch[lid] = " << first[expr<cl_uint>("gid-1")] << "; }\n";
        }
        else{
            *this <<
                "scratch[lid] = " << first[expr<cl_uint>("gid")] << ";\n";
        }

        if(checked){
            *this <<
                "}\n"
                "else {\n" <<
                "    scratch[lid] = 0;\n" <<
                "}\n";
        }

        // wait for all threads to read from input
        *this <<
            "barrier(CLK_LOCAL_MEM_FENCE);\n";

        // perform scan
        *this <<
            "for(uint i = 1; i < block_size; i <<= 1){\n" <<
            "    " << decl<const T>("x") << " = lid >= i ? scratch[lid-i] : 0;\n" <<
            "    barrier(CLK_LOCAL_MEM_FENCE);\n" <<
            "    if(lid >= i){\n" <<
            "        scratch[lid] = " << op(var<T>("scratch[lid]"), var<T>("x")) << ";\n" <<
            "    }\n" <<
            "    barrier(CLK_LOCAL_MEM_FENCE);\n" <<
            "}\n";

        // copy results to output
        if(checked){
            *this <<
                "if(gid < count){\n";
        }

        *this <<
            result[expr<cl_uint>("gid")] << " = scratch[lid];\n";

        if(checked){
            *this << "}\n";
        }

        // store sum for the block
        if(exclusive){
            *this <<
                "if(lid == block_size - 1){\n" <<
                "    " << decl<T>("temp") << " = " << first[expr<cl_uint>("gid")] <<";\n"<<
                "    block_sums[get_group_id(0)] = " <<
                       op(var<T>("temp"), var<T>("scratch[lid]")) <<
                       ";\n" <<
                "}\n";
        }
        else {
            *this <<
                "if(lid == block_size - 1){\n" <<
                "    block_sums[get_group_id(0)] = scratch[lid];\n" <<
                "}\n";
        }
    }

    size_t m_block_sums_arg;
    size_t m_scratch_arg;
    size_t m_block_size_arg;
    size_t m_count_arg;
    size_t m_init_value_arg;
};

template<class T, class BinaryOperator>
class write_scanned_output_kernel : public meta_kernel
{
public:
    write_scanned_output_kernel(BinaryOperator op)
        : meta_kernel("write_scanned_output")
    {
        bool checked = true;

        m_output_arg = add_arg<T *>(memory_object::global_memory, "output");
        m_block_sums_arg = add_arg<const T *>(memory_object::global_memory, "block_sums");
        m_count_arg = add_arg<const cl_uint>("count");

        // work-item parameters
        *this <<
            "const uint gid = get_global_id(0);\n" <<
            "const uint block_id = get_group_id(0);\n";

        // check against data size
        if(checked){
            *this << "if(gid < count){\n";
        }

        // write output
        *this <<
            "output[gid] = " <<
                op(var<T>("block_sums[block_id]"), var<T>("output[gid] ")) << ";\n";

        if(checked){
            *this << "}\n";
        }
    }

    size_t m_output_arg;
    size_t m_block_sums_arg;
    size_t m_count_arg;
};

template<class InputIterator>
inline size_t pick_scan_block_size(InputIterator first, InputIterator last)
{
    size_t count = iterator_range_size(first, last);

    if(count == 0)        { return 0; }
    else if(count <= 1)   { return 1; }
    else if(count <= 2)   { return 2; }
    else if(count <= 4)   { return 4; }
    else if(count <= 8)   { return 8; }
    else if(count <= 16)  { return 16; }
    else if(count <= 32)  { return 32; }
    else if(count <= 64)  { return 64; }
    else if(count <= 128) { return 128; }
    else                  { return 256; }
}

template<class InputIterator, class OutputIterator, class T, class BinaryOperator>
inline OutputIterator scan_impl(InputIterator first,
                                InputIterator last,
                                OutputIterator result,
                                bool exclusive,
                                T init,
                                BinaryOperator op,
                                command_queue &queue)
{
    typedef typename
        std::iterator_traits<InputIterator>::value_type
        input_type;
    typedef typename
        std::iterator_traits<InputIterator>::difference_type
        difference_type;
    typedef typename
        std::iterator_traits<OutputIterator>::value_type
        output_type;

    const context &context = queue.get_context();
    const size_t count = detail::iterator_range_size(first, last);

    size_t block_size = pick_scan_block_size(first, last);
    size_t block_count = count / block_size;

    if(block_count * block_size < count){
        block_count++;
    }

    // zero block sums
    input_type zero;
    std::memset(&zero, 0, sizeof(input_type));

    std::vector<input_type> host_block_sums(block_count, zero);
    ::boost::compute::vector<input_type> block_sums(host_block_sums.begin(), host_block_sums.end(),
                                                    queue);

    // local scan
    local_scan_kernel<InputIterator, OutputIterator, BinaryOperator>
        local_scan_kernel(first, last, result, exclusive, op);

    ::boost::compute::kernel kernel = local_scan_kernel.compile(context);
    kernel.set_arg(local_scan_kernel.m_scratch_arg, local_buffer<input_type>(block_size));
    kernel.set_arg(local_scan_kernel.m_block_sums_arg, block_sums);
    kernel.set_arg(local_scan_kernel.m_block_size_arg, static_cast<cl_uint>(block_size));
    kernel.set_arg(local_scan_kernel.m_count_arg, static_cast<cl_uint>(count));
    kernel.set_arg(local_scan_kernel.m_init_value_arg, static_cast<output_type>(init));

    queue.enqueue_1d_range_kernel(kernel,
                                  0,
                                  block_count * block_size,
                                  block_size);

    // inclusive scan block sums
    if(block_count > 1){
        scan_impl(block_sums.begin(),
                  block_sums.end(),
                  block_sums.begin(),
                  false,
                  init,
                  op,
                  queue
        );
    }

    // add block sums to each block
    if(block_count > 1){
        write_scanned_output_kernel<input_type, BinaryOperator>
            write_output_kernel(op);
        kernel = write_output_kernel.compile(context);
        kernel.set_arg(write_output_kernel.m_output_arg, result.get_buffer());
        kernel.set_arg(write_output_kernel.m_block_sums_arg, block_sums);
        kernel.set_arg(write_output_kernel.m_count_arg, static_cast<cl_uint>(count));

        queue.enqueue_1d_range_kernel(kernel,
                                      block_size,
                                      block_count * block_size,
                                      block_size);
    }

    return result + static_cast<difference_type>(count);
}

template<class InputIterator, class OutputIterator, class T, class BinaryOperator>
inline OutputIterator dispatch_scan(InputIterator first,
                                    InputIterator last,
                                    OutputIterator result,
                                    bool exclusive,
                                    T init,
                                    BinaryOperator op,
                                    command_queue &queue)
{
    return scan_impl(first, last, result, exclusive, init, op, queue);
}

template<class InputIterator, class T, class BinaryOperator>
inline InputIterator dispatch_scan(InputIterator first,
                                   InputIterator last,
                                   InputIterator result,
                                   bool exclusive,
                                   T init,
                                   BinaryOperator op,
                                   command_queue &queue)
{
    typedef typename std::iterator_traits<InputIterator>::value_type value_type;

    if(first == result){
        // scan input in-place
        const context &context = queue.get_context();

        // make a temporary copy the input
        size_t count = iterator_range_size(first, last);
        vector<value_type> tmp(count, context);
        copy(first, last, tmp.begin(), queue);

        // scan from temporary values
        return scan_impl(tmp.begin(), tmp.end(), first, exclusive, init, op, queue);
    }
    else {
        // scan input to output
        return scan_impl(first, last, result, exclusive, init, op, queue);
    }
}

template<class InputIterator, class OutputIterator, class T, class BinaryOperator>
inline OutputIterator scan_on_gpu(InputIterator first,
                                  InputIterator last,
                                  OutputIterator result,
                                  bool exclusive,
                                  T init,
                                  BinaryOperator op,
                                  command_queue &queue)
{
    if(first == last){
        return result;
    }

    return dispatch_scan(first, last, result, exclusive, init, op, queue);
}

} // end detail namespace
} // end compute namespace
} // end boost namespace

#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_SCAN_ON_GPU_HPP


/*
Copyright 2011, 2012 Rogier van Dalen.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

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

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

/** \file
Define operators for Rime constants.

The named functions in rime:: will return a compile-time constant if the
result of the calculation is known at compile-time.
The overloaded operators only apply to Rime constants, and not, for example,
MPL constants: the behaviour of existing code does not change.
*/

#ifndef RIME_OPERATORS_HPP_INCLUDED
#define RIME_OPERATORS_HPP_INCLUDED

#include <type_traits>

#include <boost/utility/enable_if.hpp>
#include <boost/mpl/and.hpp>

#include "rime/core.hpp"

namespace rime {

/*
Produce something like the following:

namespace callable
{
    struct plus
    {
        template <class Left, class Right>
            auto operator() (Left const & left, Right const & right) const
        -> typename boost::enable_if <
            mpl::and_ <is_constant <Left>, is_constant <Right> >,
            constant <
                typename std::decay <decltype (Left::value + Right::value)
                    >::type,
                (Left::value + Right::value)
            >
        >::type
        {
            typedef typename std::decay <decltype (
                Left::value + Right::value)>::type ValueType;
            return constant <ValueType, Left::value + Right::value>();
        }

        template <class Left, class Right>
            auto operator() (Left const & left, Right const & right) const
        -> typename boost::disable_if <
            mpl::and_ <is_constant <Left>, is_constant <Right> >,
            decltype (get_value (left) + get_value (right))
        >::type
        {
            return get_value (left) + get_value (right);
        }
    };
}

static const callable::plus plus = {};

template <class Left, class Right>
    auto operator + (Left const & left, Right const & right)
-> typename boost::enable_if <
    mpl::and_ <is_constant <Left>, is_rime_<Right>,
        mpl::or_ <is_rime_constant <Left>, is_rime_constant <Right> > >,
    constant <
        typename std::decay <decltype (Left::value + Right::value)>::type,
            (Left::value + Right::value)
    >
>::type
{
    typedef typename std::decay <decltype (
        Left::value + Right::value)>::type ValueType;
    return constant <ValueType, Left::value + Right::value>();
}*/

#define RIME_DEFINE_BINARY_OPERATOR(name, operation) \
namespace callable \
{ \
    struct name \
    { \
        template <class Left, class Right> \
            auto operator() (Left const & left, Right const & right) const \
        -> typename boost::enable_if < \
            mpl::and_ <is_constant <Left>, is_constant <Right> >, \
            constant < \
                typename std::decay <decltype (( \
                    Left::value operation Right::value))>::type, \
                (Left::value operation Right::value) \
            > \
        >::type \
        { \
            typedef typename std::decay <decltype (( \
                Left::value operation Right::value))>::type ValueType; \
            return constant <ValueType, \
                (Left::value operation Right::value)>(); \
        } \
\
        template <class Left, class Right> \
            auto operator() (Left const & left, Right const & right) const \
        -> typename boost::disable_if < \
            mpl::and_ <is_constant <Left>, is_constant <Right> >, \
            decltype ((get_value (left) operation get_value (right))) \
        >::type \
        { \
            return get_value (left) operation get_value (right); \
        } \
    }; \
} \
\
static const callable::name name = callable::name(); \
\
template <class Left, class Right> \
    auto operator operation (Left const & left, Right const & right) \
-> typename boost::enable_if < \
    mpl::and_ <is_constant <Left>, is_constant <Right>, \
        mpl::or_ <is_rime_constant <Left>, is_rime_constant <Right> > >, \
    constant < \
        typename std::decay <decltype (( \
            Left::value operation Right::value))>::type, \
            (Left::value operation Right::value) \
    > \
>::type \
{ \
    typedef typename std::decay <decltype (( \
        Left::value operation Right::value))>::type ValueType; \
    return constant <ValueType, (Left::value operation Right::value)>(); \
}

RIME_DEFINE_BINARY_OPERATOR(plus, +)
RIME_DEFINE_BINARY_OPERATOR(minus, -)
RIME_DEFINE_BINARY_OPERATOR(times, *)
RIME_DEFINE_BINARY_OPERATOR(divides, /)
RIME_DEFINE_BINARY_OPERATOR(modulo, %)

RIME_DEFINE_BINARY_OPERATOR(bitwise_and, &)
RIME_DEFINE_BINARY_OPERATOR(bitwise_or, |)
RIME_DEFINE_BINARY_OPERATOR(bitwise_xor, ^)
RIME_DEFINE_BINARY_OPERATOR(shift_left, <<)
RIME_DEFINE_BINARY_OPERATOR(shift_right, >>)

RIME_DEFINE_BINARY_OPERATOR(equal_to, ==)
RIME_DEFINE_BINARY_OPERATOR(not_equal_to, !=)
RIME_DEFINE_BINARY_OPERATOR(less, <)
RIME_DEFINE_BINARY_OPERATOR(less_equal, <=)
RIME_DEFINE_BINARY_OPERATOR(greater, >)
RIME_DEFINE_BINARY_OPERATOR(greater_equal, >=)

#undef RIME_DEFINE_BINARY_OPERATOR

#define RIME_DEFINE_UNARY_OPERATOR(name, operation) \
namespace callable \
{ \
    struct name \
    { \
        template <class Argument> \
            auto operator() (Argument const & argument) const \
        -> typename boost::enable_if < \
            is_constant <Argument>, \
            constant < \
                typename std::decay <decltype ( \
                    operation Argument::value)>::type, \
                (operation Argument::value) \
            > \
        >::type \
        { \
            typedef typename std::decay <decltype ( \
                operation Argument::value)>::type ValueType; \
            return constant <ValueType, (operation Argument::value)>(); \
        } \
\
        template <class Argument> \
            auto operator() (Argument const & argument) const \
        -> typename boost::disable_if < \
            is_constant <Argument>, \
            decltype (operation get_value (argument)) \
        >::type \
        { \
            return operation get_value (argument); \
        } \
    }; \
} \
\
static const callable::name name = {}; \
\
template <class Argument> \
    auto operator operation (Argument const & argument) \
-> typename boost::enable_if < \
    is_constant <Argument>, \
    constant < \
        typename std::decay <decltype (operation Argument::value)>::type, \
            (operation Argument::value) \
    > \
>::type \
{ \
    typedef typename std::decay <decltype ( \
        operation Argument::value)>::type ValueType; \
    return constant <ValueType, (operation Argument::value)>(); \
}

RIME_DEFINE_UNARY_OPERATOR(negate, -)
RIME_DEFINE_UNARY_OPERATOR(not_, !)
RIME_DEFINE_UNARY_OPERATOR(bitwise_not, ~)

#undef RIME_DEFINE_UNARY_OPERATOR

/* "and" and "or" */
namespace callable {

    namespace detail {
        template <typename Constant, typename... Arguments>
            struct any_equal_constant;
        template <typename Constant>
            struct any_equal_constant <Constant> : mpl::false_ {};
        template <typename Constant, typename Head, typename...Tail>
            struct any_equal_constant <Constant, Head, Tail...>
        : mpl::or_ <
            equal_constant <Constant, Head>,
            any_equal_constant <Constant, Tail...>
        > {};

        template <typename Constant, typename... Arguments>
            struct all_equal_constant;
        template <typename Constant>
            struct all_equal_constant <Constant> : mpl::true_ {};
        template <typename Constant, typename Head, typename...Tail>
            struct all_equal_constant <Constant, Head, Tail...>
        : mpl::and_ <
            equal_constant <Constant, Head>,
            all_equal_constant <Constant, Tail...>
        > {};
    } // namespace detail

    template <typename Type> struct return_ {
        template <typename... Arguments>
            Type operator() (Arguments const &...) const
        { return Type(); }
    };

} // namespace callable

// If any one of the parameters is "overrideValue" (true or false), then the
// whole expression equals "overrideValue".
// If all parameters are "defaultValue" (or if there are zero parameters), then
// the whole expression equals "defaultValue".
#define RIME_DEFINE_BINARY_BOOLEAN_OPERATOR( \
    name, operation, overrideValue, defaultValue) \
namespace callable \
{ \
    namespace detail \
    { \
        template <typename Void, typename... Arguments> struct name##runtime; \
        template <> struct name##runtime <void> \
        { \
            bool operator() () const { return defaultValue; } \
        }; \
        \
        template <typename Head, typename... Tail> \
            struct name##runtime < \
                typename boost::disable_if <is_constant <Head> >::type, \
                Head, Tail...> \
        { \
            bool operator() (Head const & head, Tail const & ... tail) const \
            { \
                name##runtime <void, Tail...> child; \
                return head operation child (tail...); \
            } \
        }; \
        \
        template <typename Head, typename... Tail> \
            struct name##runtime < \
                typename boost::enable_if <is_constant <Head> >::type, \
                Head, Tail...> \
        { \
            bool operator() (Head const &, Tail const & ... tail) const \
            { \
                name##runtime <void, Tail...> child; \
                return child (tail...); \
            } \
        }; \
    } /* namespace detail */ \
    \
    class name \
    { \
        template <typename... Arguments> struct implementation \
        : mpl::if_ < \
            detail::any_equal_constant <bool_ <overrideValue>, Arguments...>, \
            return_ <bool_ <overrideValue> >, \
            typename mpl::if_ < \
                detail::all_equal_constant <bool_ <defaultValue>, \
                    Arguments...>, \
                return_ <bool_ <defaultValue> >, \
                detail::name##runtime <void, Arguments...> \
            >::type \
        >::type {}; \
        \
    public: \
        template <typename... Arguments> \
            auto operator() (Arguments const & ... arguments) const \
        -> decltype (implementation <Arguments...>() (arguments...)) \
        { \
            return implementation <Arguments...>() (arguments...); \
        } \
    }; \
} \
\
static const callable::name name = {}; \
\
template <class Left, class Right> \
    auto operator operation (Left const & left, Right const & right) \
-> typename boost::enable_if < \
    mpl::and_ <is_constant <Left>, is_constant <Right>, \
        mpl::or_ <is_rime_constant <Left>, is_rime_constant <Right> > >, \
    bool_ <(Left::value operation Right::value)> \
>::type \
{ \
    return bool_ <(Left::value operation Right::value)>(); \
}

RIME_DEFINE_BINARY_BOOLEAN_OPERATOR(or_, ||, true, false)
RIME_DEFINE_BINARY_BOOLEAN_OPERATOR(and_, &&, false, true)

#undef RIME_DEFINE_BINARY_BOOLEAN_OPERATOR

} // namespace rime

#endif  // RIME_OPERATORS_HPP_INCLUDED


//
// Created by Davide Caroselli on 04/09/16.
//

#include <iostream>
#include <random>
#include <fstream>
#include <fastalign/Corpus.h>
#include <fastalign/FastAligner.h>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <thread>
#include <cmath>

#ifdef _OPENMP
#include <thread>
#include <omp.h>
#endif

using namespace std;
using namespace mmt;
using namespace mmt::fastalign;

namespace {
    const size_t ERROR_IN_COMMAND_LINE = 1;
    const size_t GENERIC_ERROR = 2;
    const size_t SUCCESS = 0;

    struct args_t {
        string output_path;
        string model_path;
        string input_path;
        string source_lang;
        string target_lang;
        size_t buffer_size = 100000;
    };
} // namespace

namespace po = boost::program_options;
namespace fs = boost::filesystem;

class Sequence {
public:
    Sequence() : sum(0), sum2(0), count(0) {}

    void Add(score_t score) {
        if (!isnan(score)) {
            sum += score;
            sum2 += score * score;
            count++;
        }
    }

    double GetAverage() {
        return sum / count;
    }

    double GetStandardDeviation() {
        return sqrt((sum2 / count) - (GetAverage() * GetAverage()));
    }

private:
    double sum;
    double sum2;
    double count;
};

bool ParseArgs(int argc, const char *argv[], args_t *args) {
    po::options_description desc("Runs FastAlign model on a collection of parallel files and outputs scores in the "
                                 "output folder.\nThis script also prints to stdout the Average and Standard Deviation "
                                 "of the \"good\" and \"bad\" alignment distributions\n(the bad distribution is "
                                 "computed by shifting the target side of every batch by one position)");
    desc.add_options()
            ("help,h", "print this help message")
            ("model,m", po::value<string>()->required(), "the FastAlign model path")
            ("output,o", po::value<string>()->required(), "output folder for \"*.score\" files")
            ("source,s", po::value<string>()->required(), "source language")
            ("target,t", po::value<string>()->required(), "target language")
            ("input,i", po::value<string>()->required(), "input folder containing the parallel files collection")
            ("batch-size,b", po::value<size_t>(), "input batch size, expressed in number of lines");

    po::variables_map vm;
    try {
        po::store(po::parse_command_line(argc, argv, desc), vm);

        if (vm.count("help")) {
            std::cout << desc << std::endl;
            return false;
        }

        po::notify(vm);

        args->model_path = vm["model"].as<string>();
        args->input_path = vm["input"].as<string>();
        args->output_path = vm["output"].as<string>();
        args->source_lang = vm["source"].as<string>();
        args->target_lang = vm["target"].as<string>();

        if (vm.count("batch-size"))
            args->buffer_size = vm["batch-size"].as<size_t>();
    } catch (po::error &e) {
        std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
        std::cerr << desc << std::endl;
        return false;
    }

    return true;
}

void PrintScores(vector<alignment_t> &alignments, ofstream &out) {
    for (auto a = alignments.begin(); a != alignments.end(); ++a)
        out << a->score << endl;
}

void CollectScores(vector<alignment_t> &alignments, Sequence &seq) {
    for (auto a = alignments.begin(); a != alignments.end(); ++a)
        seq.Add(a->score);
}

void ShiftBatch(vector<pair<wordvec_t, wordvec_t>> &batch) {
    wordvec_t first = batch[0].second;
    for (size_t i = 1; i < batch.size(); ++i)
        batch[i - 1].second = batch[i].second;
    batch[batch.size() - 1].second = first;
}

void ScoreCorpus(FastAligner &aligner, Sequence &goodScores, Sequence &badScores,
                 const Corpus &corpus, size_t buffer_size, const string &outputPath) {
    CorpusReader reader(corpus, &aligner.GetVocabulary());

    vector<pair<wordvec_t, wordvec_t>> batch;
    vector<alignment_t> alignments;

    string scorePath = (fs::path(outputPath) / (corpus.GetName() + ".score")).string();
    ofstream scoreStream;
    scoreStream.open(scorePath.c_str(), ios_base::out);

    while (reader.Read(batch, buffer_size)) {
        aligner.GetAlignments(batch, alignments, GrowDiagonalFinalAnd);

        PrintScores(alignments, scoreStream);
        CollectScores(alignments, goodScores);
        alignments.clear();

        if (batch.size() > 1) {
            ShiftBatch(batch);

            aligner.GetAlignments(batch, alignments, GrowDiagonalFinalAnd);
            CollectScores(alignments, badScores);
            alignments.clear();
        }

        batch.clear();
    }
}

int main(int argc, const char *argv[]) {
    int threads = 1;

#ifdef _OPENMP
    threads = thread::hardware_concurrency();

    omp_set_dynamic(0);
    omp_set_num_threads(threads);
#endif

    args_t args;

    if (!ParseArgs(argc, argv, &args))
        return ERROR_IN_COMMAND_LINE;

    if (!fs::exists(args.input_path) || !fs::is_directory(args.input_path)) {
        cerr << "ERROR: input path is not a valid directory" << endl;
        return GENERIC_ERROR;
    }

    if (!fs::is_regular(args.model_path)) {
        cerr << "ERROR: model path is not a valid directory" << endl;
        return GENERIC_ERROR;
    }

    if (!fs::is_directory(args.output_path))
        fs::create_directories(args.output_path);

    vector<Corpus> corpora;
    Corpus::List(args.input_path, args.source_lang, args.target_lang, corpora);

    if (corpora.empty())
        exit(0);

    FastAligner aligner(args.model_path, threads);
    Sequence goodScores;
    Sequence badScores;

    // perform scoring of all corpora sequentially; multi-threading is used for each corpus
    for (auto corpus = corpora.begin(); corpus < corpora.end(); ++corpus) {
        ScoreCorpus(aligner, goodScores, badScores, *corpus, args.buffer_size, args.output_path);
    }

    cout << "good_avg=" << goodScores.GetAverage() << "\n";
    cout << "good_std_dev=" << goodScores.GetStandardDeviation() << "\n";
    cout << "bad_avg=" << badScores.GetAverage() << "\n";
    cout << "bad_std_dev=" << badScores.GetStandardDeviation() << "\n";

    return SUCCESS;
}


//=============================================================================================================
/**
 * @file     icp.cpp
 * @author   Ruben Dörfel <doerfelruben@aol.com>
 * @since    0.1.5
 * @date     July, 2020
 *
 * @section  LICENSE
 *
 * Copyright (C) 2020, Ruben Dörfel. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 * the following conditions are met:
 *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *       following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 *       the following disclaimer in the documentation and/or other materials provided with the distribution.
 *     * Neither the name of MNE-CPP authors 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 OWNER 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.
 *
 *
 * @brief    ICP class definition.
 *
 */

//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include "icp.h"
#include <iostream>

#include "fiff/fiff_coord_trans.h"
#include "mne/mne_project_to_surface.h"

//=============================================================================================================
// QT INCLUDES
//=============================================================================================================

#include <QSharedPointer>
#include <QDebug>

//=============================================================================================================
// EIGEN INCLUDES
//=============================================================================================================

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

//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================

using namespace MNELIB;
using namespace RTPROCESSINGLIB;
using namespace Eigen;
using namespace FIFFLIB;

//=============================================================================================================
// DEFINE GLOBAL METHODS
//=============================================================================================================

bool RTPROCESSINGLIB::performIcp(const MNEProjectToSurface::SPtr mneSurfacePoints,
                                 const Eigen::MatrixXf& matPointCloud,
                                 FiffCoordTrans& transFromTo,
                                 float& fRMSE,
                                 bool bScale,
                                 int iMaxIter,
                                 float fTol,
                                 const VectorXf& vecWeitgths)
/**
 * Follow notation of P.J. Besl and N.D. McKay, A Method for
 * Registration of 3-D Shapes, IEEE Trans. Patt. Anal. Machine Intell., 14,
 * 239 - 255, 1992.
 */
{
    if(matPointCloud.rows() == 0){
        qWarning() << "[RTPROCESSINGLIB::icp] Passed point cloud is empty.";
        return false;
    }

    // Initialization
    int iNP = matPointCloud.rows();             // The number of points
    float fMSEPrev,fMSE = 0.0;                  // The mean square error
    float fScale = 1.0;
    MatrixXf matP0 = matPointCloud;             // Initial Set of points
    MatrixXf matPk = matP0;                     // Transformed Set of points
    MatrixXf matYk(matPk.rows(),matPk.cols());  // Iterative closest points on the surface
    MatrixXf matDiff = matYk;
    VectorXf vecSE(matDiff.rows());
    Matrix4f matTrans;                          // the transformation matrix
    VectorXi vecNearest;                        // Triangle of the new point
    VectorXf vecDist;                           // The Distance between matX and matP

    // Initial transformation - From point cloud To surface
    FiffCoordTrans transICP = transFromTo;
    matPk = transICP.apply_trans(matPk);

    // Icp algorithm:
    for(int iIter = 0; iIter < iMaxIter; ++iIter) {

        // Step a: compute the closest point on the surface; eq 29
        if(!mneSurfacePoints->mne_find_closest_on_surface(matPk, iNP, matYk, vecNearest, vecDist)) {
            qWarning() << "[RTPROCESSINGLIB::icp] mne_find_closest_on_surface was not sucessfull.";
            return false;
        }

        // Step b: compute the registration; eq 30
        if(!fitMatchedPoints(matP0, matYk, matTrans, fScale, bScale, vecWeitgths)) {
            qWarning() << "[RTPROCESSINGLIB::icp] point cloud registration not succesfull";
        }

        // Step c: apply registration
        transICP.trans = matTrans;
        matPk = transICP.apply_trans(matP0);

        // step d: compute mean-square-error and terminate if below fTol
        vecDist = vecDist.cwiseProduct(vecDist);
        fMSE = vecDist.sum() / iNP;
        fRMSE = std::sqrt(fMSE);

        if(std::sqrt(std::fabs(fMSE - fMSEPrev)) < fTol) {
            transFromTo = transICP;
            qInfo() << "[RTPROCESSINGLIB::icp] ICP was succesfull and exceeded after " << iIter +1 << " Iterations with RMSE dist: " << fRMSE * 1000 << " mm.";
            return true;
        }
        fMSEPrev = fMSE;
        qInfo() << "[RTPROCESSINGLIB::icp] ICP iteration " << iIter + 1 << " with RMSE: " << fRMSE * 1000 << " mm.";
    }
    transFromTo = transICP;

    qWarning() << "[RTPROCESSINGLIB::icp] Maximum number of " << iMaxIter << " Iterations exceeded with RMSE: " << fRMSE * 1000 << " mm.";
    return true;
}

//=============================================================================================================

bool RTPROCESSINGLIB::fitMatchedPoints(const MatrixXf& matSrcPoint,
                                       const MatrixXf& matDstPoint,
                                       Eigen::Matrix4f& matTrans,
                                       float fScale,
                                       bool bScale,
                                       const VectorXf& vecWeitgths)
/**
 * Follow notation of P.J. Besl and N.D. McKay, A Method for
 * Registration of 3-D Shapes, IEEE Trans. Patt. Anal. Machine Intell., 14,
 * 239 - 255, 1992.
 *
 * The code is further adapted from MNE Python function _fitMatched_points(...).
 */
{
    // init values
    MatrixXf matP = matSrcPoint;
    MatrixXf matX = matDstPoint;
    VectorXf vecW = vecWeitgths;
    VectorXf vecMuP;                                // column wise mean - center of mass
    VectorXf vecMuX;                                // column wise mean - center of mass
    MatrixXf matDot;
    MatrixXf matSigmaPX;                            // cross-covariance
    MatrixXf matAij;                                // Anti-Symmetric matrix
    Vector3f vecDelta;                              // column vector, elements of matAij
    Matrix4f matQ = Matrix4f::Identity(4,4);
    Matrix3f matScale = Matrix3f::Identity(3,3);    // scaling matrix
    Matrix3f matRot = Matrix3f::Identity(3,3);
    Vector3f vecTrans;
    float fTrace = 0.0;
    fScale = 1.0;

    // test size of point clouds
    if(matSrcPoint.size() != matDstPoint.size()) {
        qWarning() << "[RTPROCESSINGLIB::fitMatched] Point clouds do not match.";
        return false;
    }

    // get center of mass
    if(vecWeitgths.isZero()) {
        vecMuP = matP.colwise().mean(); // eq 23
        vecMuX = matX.colwise().mean();
        matDot = matP.transpose() * matX;
        matDot = matDot / matP.rows();
    } else {
        vecW = vecWeitgths / vecWeitgths.sum();
        vecMuP = vecW.transpose() * matP;
        vecMuX = vecW.transpose() * matX;

        MatrixXf matXWeighted = matX;
        for(int i = 0; i < (vecW.size()); ++i) {
            matXWeighted.row(i) = matXWeighted.row(i) * vecW(i);
        }
        matDot = matP.transpose() * (matXWeighted);
    }

    // get cross-covariance
    matSigmaPX = matDot - (vecMuP * vecMuX.transpose());  // eq 24
    matAij = matSigmaPX - matSigmaPX.transpose();
    vecDelta(0) = matAij(1,2); vecDelta(1) = matAij(2,0); vecDelta(2) = matAij(0,1);
    fTrace = matSigmaPX.trace();
    matQ(0,0) = fTrace; // eq 25
    matQ.block(0,1,1,3) = vecDelta.transpose();
    matQ.block(1,0,3,1) = vecDelta;
    matQ.block(1,1,3,3) = matSigmaPX + matSigmaPX.transpose() - fTrace * MatrixXf::Identity(3,3);

    // unit eigenvector coresponding to maximum eigenvalue of matQ is selected as optimal rotation quaterions q0,q1,q2,q3
    SelfAdjointEigenSolver<MatrixXf> es(matQ);
    Vector4f vecEigVec = es.eigenvectors().col(matQ.cols()-1);  // only take last Eigen-Vector since this corresponds to the maximum Eigenvalue

    // quatRot(w,x,y,z)
    Quaternionf quatRot(vecEigVec(0),vecEigVec(1),vecEigVec(2),vecEigVec(3));
    quatRot.normalize();
    matRot = quatRot.matrix();

    // get scaling factor and matrix
    if(bScale) {
        MatrixXf matDevX = matX.rowwise() - vecMuX.transpose();
        MatrixXf matDevP = matP.rowwise() - vecMuP.transpose();
        matDevX = matDevX.cwiseProduct(matDevX);
        matDevP = matDevP.cwiseProduct(matDevP);

        if(!vecWeitgths.isZero()) {
            for(int i = 0; i < (vecW.size()); ++i) {
                matDevX.row(i) = matDevX.row(i) * vecW(i);
                matDevP.row(i) = matDevP.row(i) * vecW(i);
            }
        }
        // get scaling factor and set scaling matrix
        fScale = std::sqrt(matDevX.sum() / matDevP.sum());
        matScale *= fScale;
    }

    // get translation and Rotation
    vecTrans = vecMuX - fScale * matRot * vecMuP;
    matRot *= matScale;

    matTrans.block<3,3>(0,0) = matRot;
    matTrans.block<3,1>(0,3) = vecTrans;
    matTrans(3,3) = 1.0f;
    matTrans.block<1,3>(3,0) = MatrixXf::Zero(1,3);
    return true;
}

//=========================================================================================================

bool RTPROCESSINGLIB::discard3DPointOutliers(const QSharedPointer<MNELIB::MNEProjectToSurface> mneSurfacePoints,
                                             const MatrixXf& matPointCloud,
                                             const FiffCoordTrans& transFromTo,
                                             VectorXi& vecTake,
                                             MatrixXf& matTakePoint,
                                             float fMaxDist)
{
    // Initialization
    int iNP = matPointCloud.rows();               // The number of points
    MatrixXf matP = matPointCloud;                // Initial Set of points
    MatrixXf matYk(matPointCloud.rows(),matPointCloud.cols());  // Iterative losest points on the surface
    VectorXi vecNearest;                        // Triangle of the new point
    VectorXf vecDist;                           // The Distance between matX and matP

    // Initial transformation - From point cloud To surface
    matP = transFromTo.apply_trans(matP);

    int iDiscarded = 0;

    // discard outliers if necessary
    if(fMaxDist > 0.0) {
        if(!mneSurfacePoints->mne_find_closest_on_surface(matP, iNP, matYk, vecNearest, vecDist)) {
            qWarning() << "[RTPROCESSINGLIB::icp] mne_find_closest_on_surface was not sucessfull.";
            return false;
        }

        for(int i = 0; i < vecDist.size(); ++i) {
            if(std::fabs(vecDist(i)) < fMaxDist) {
                vecTake.conservativeResize(vecTake.size()+1);
                vecTake(vecTake.size()-1) = i;
                matTakePoint.conservativeResize(matTakePoint.rows()+1,3);
                matTakePoint.row(matTakePoint.rows()-1) = matPointCloud.row(i);
            } else {
                iDiscarded++;
            }
        }
    }
    qInfo() << "[RTPROCESSINGLIB::discardOutliers] " << iDiscarded << "digitizers discarded.";
    return true;
}

//=============================================================================================================
// DEFINE MEMBER METHODS
//=============================================================================================================


// Copyright 2005 The Trustees of Indiana University.

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

//  Authors: Douglas Gregor
//           Andrew Lumsdaine
#include <boost/graph/strong_components.hpp>
#include <boost/python.hpp>

namespace boost { namespace graph { namespace python {

using boost::python::object;

template<typename Graph>
int
strong_components
  (const Graph& g,
   vector_property_map<
     int,
     typename property_map<Graph, vertex_index_t>::const_type>* in_color)
{
  typedef typename property_map<Graph, vertex_index_t>::const_type
    VertexIndexMap;
  typedef vector_property_map<int, VertexIndexMap> ColorMap;

  ColorMap color =
    in_color? *in_color
    : ColorMap(num_vertices(g), get(vertex_index, g));

  return boost::strong_components(g, color);
}

} } } // end namespace boost::graph::python


/**
 * @file
 * @copyright 2020, Max Planck Gesellschaft.  All rights reserved.
 */
#pragma once

#include <Eigen/Eigen>
#include <cereal/archives/json.hpp>
#include <serialization_utils/cereal_eigen.hpp>

namespace trifinger_object_tracking
{
//! @brief Estimated pose of a tracked object.
struct ObjectPose
{
public:
    //! Position (x, y, z)
    Eigen::Vector3d position;
    //! Orientation quaternion (x, y, z, w)
    Eigen::Vector4d orientation;
    //! Confidence of the accuracy of the given pose.  Ranges from 0 to 1.
    double confidence = 0.0;

    //! For serialization with cereal.
    template <class Archive>
    void serialize(Archive& archive)
    {
        archive(cereal::make_nvp("px", position[0]),
                cereal::make_nvp("py", position[1]),
                cereal::make_nvp("pz", position[2]),
                cereal::make_nvp("qx", orientation[0]),
                cereal::make_nvp("qy", orientation[1]),
                cereal::make_nvp("qz", orientation[2]),
                cereal::make_nvp("qw", orientation[3]),
                CEREAL_NVP(confidence));
    }

    std::string to_string()
    {
        // use cereal JSON archive to get a nice string representation
        std::stringstream ss;
        {
            cereal::JSONOutputArchive archive(ss);
            this->serialize(archive);
        }

        return ss.str();
    }
};

}  // namespace trifinger_object_tracking


#ifndef ROSE499_DIFFDRIVE_HPP
#define ROSE499_DIFFDRIVE_HPP

#include <array>
#include <Eigen/Core>
#include <set>
#include "rose499/types.hpp"
#include "rose499/spline.hpp"
#include "rose499/world.hpp"

struct InvalidPathException{ };

struct DriveSystem
{
    typedef SimulatorTypes::ValueType ValueType;
    typedef std::array<ValueType, 3> StateType;

    void step(StateType x, StateType& dxdt, double t, ValueType turnControl, ValueType speedControl);

    double time() const;
    Eigen::Matrix<ValueType, 3, 1> const & state() const;
    Eigen::Matrix<ValueType, 3, 1> const & flow() const;
    void state(Eigen::Matrix<ValueType, 3, 1> x);
    void flow(Eigen::Matrix<ValueType, 3, 1> f);
    void time(double t);

    World::Polygon viewCone() const;

private:
    double mTime;
    Eigen::Matrix<ValueType, 3, 1> mState;
    Eigen::Matrix<ValueType, 3, 1> mFlow;
};

struct DriveController
{
    typedef DriveSystem::ValueType ValueType;
    typedef DriveSystem::StateType StateType;

    DriveController(DriveSystem &, Eigen::Matrix<ValueType, 2, 1> goal, ValueType goalRadius);

    void operator() (StateType x, StateType& dxdt, double t);

    void operatingPoint( Spline::ValueType );
    Spline::ValueType operatingPoint() const;
    void path( Spline const & );
    Spline const & path() const;
    Eigen::Matrix<ValueType, 2, 1> const & goal() const;

    std::ostream& printData(std::ostream&) const;
    std::ostream& printHeaders(std::ostream&) const;

    bool updateKnownWorld(std::list<std::pair<World::Box, int>> const & obstacles, bool forceReplan = false);
    void replan();

    DriveSystem const & system() const;

    virtual bool hasDiverged() const;

protected:
    virtual ValueType genSpeedControl(StateType x, double t);
    virtual ValueType genTurnControl(StateType x, double t);

    virtual std::ostream& printSpecificHeaders(std::ostream&) const;
    virtual std::ostream& printSpecificData(std::ostream&) const;

    double lastPlanTime;

private:
    int mPlanIndex;
    Spline mPath;
    Spline::ValueType mOperatingLambda;
    DriveSystem& mSystem;
    World::RTree mKnownWorld;
    std::set<int> mKnownBoxes;
    Eigen::Matrix<ValueType, 2, 1> mGoal;
    ValueType mGoalRadius;
};

namespace std
{
    std::ostream& operator << (std::ostream&, DriveController const &);
}

#endif


#ifndef HERMIT_SPIRIT_QI_ASCTIME_HPP
#define HERMIT_SPIRIT_QI_ASCTIME_HPP

#include <iostream>
#include <vector>
#include <string>
#include <boost/date_time/date_defs.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/gregorian_calendar.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace hermit {
  namespace spirit {
    namespace qi {
      template <typename InputIterator>
        class asctime :
          public boost::spirit::qi::grammar< InputIterator, boost::posix_time::ptime() > {
            public:
              asctime() : asctime::base_type( root ) {
                namespace qi = boost::spirit::qi;
                namespace dt = boost::date_time;
                namespace phx = boost::phoenix;
                root = ( wkday >> qi::omit[ +qi::standard::space ] >> date )[
                  qi::_pass = qi::_1 == phx::bind( &boost::gregorian::date::day_of_week, &phx::at_c< 0 >( qi::_2 ) ),
                  qi::_val = phx::construct< boost::posix_time::ptime >(
                    phx::at_c< 0 >( qi::_2 ), phx::at_c< 1 >( qi::_2 )
                  )
                ];
                date = ( month >> qi::omit[ +qi::standard::space ] >> dec2_p >> qi::omit[ +qi::standard::space ] >>
                         time >> qi::omit[ +qi::standard::space ] >> dec4_p )[
                  qi::_pass = qi::_2 <= phx::bind( boost::gregorian::gregorian_calendar::end_of_month_day, qi::_4, qi::_2 ),
                  phx::at_c< 0 >( qi::_val ) = phx::construct< boost::gregorian::date >( qi::_4, qi::_1, qi::_2 ),
                  phx::at_c< 1 >( qi::_val ) = qi::_3
                ];
                time = ( dec2_p >> ':' >> dec2_p >> ':' >> dec2_p )[
                  qi::_pass = qi::_1 < 24u && qi::_2 < 60u && qi::_3 < 60u,
                  qi::_val = phx::construct< boost::posix_time::time_duration >( qi::_1, qi::_2, qi::_3 )
                ];
                wkday.add( "Mon", dt::Monday )( "Tue", dt::Tuesday )( "Wed", dt::Wednesday )( "Thu", dt::Thursday )
                         ( "Fri", dt::Friday )( "Sat", dt::Saturday )( "Sun", dt::Sunday );
                month.add( "Jan", dt::Jan )( "Feb", dt::Feb )( "Mar", dt::Mar )( "Apr", dt::Apr )
                         ( "May", dt::May )( "Jun", dt::Jun )( "Jul", dt::Jul )( "Aug", dt::Aug )
                         ( "Sep", dt::Sep )( "Oct", dt::Oct )( "Nov", dt::Nov )( "Dec", dt::Dec );
              }
            private:
              boost::spirit::qi::uint_parser<unsigned int, 10, 2, 2> dece2_p;
              boost::spirit::qi::uint_parser<unsigned int, 10, 1, 2> dec2_p;
              boost::spirit::qi::uint_parser<unsigned int, 10, 1, 4> dec4_p;
              boost::spirit::qi::rule< InputIterator, boost::posix_time::ptime() > root;
              boost::spirit::qi::rule< InputIterator, boost::fusion::vector< boost::gregorian::date, boost::posix_time::time_duration >() > date;
              boost::spirit::qi::rule< InputIterator, boost::posix_time::time_duration() > time;
              boost::spirit::qi::symbols< char, boost::date_time::weekdays > wkday;
              boost::spirit::qi::symbols< char, boost::posix_time::time_duration > zone_name;
              boost::spirit::qi::symbols< char, boost::date_time::months_of_year > month;
      };
    }
  }
}

#endif


#include <algorithm>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <string>
#include <utility>
#include <vector>
#include <boost/date_time.hpp>

namespace gregorian = boost::gregorian;

template<typename It, typename BinaryPred>
auto group_range(It first, It last, BinaryPred pred) -> std::vector<std::pair<It, It>> {
    std::vector<std::pair<It, It>> result;

    auto not2 = [pred](auto const& a, auto const& b) -> bool {
        return !pred(a, b);
    };

    while (first != last) {
        auto found = std::adjacent_find(first, last, not2);
        if (found != last) {
            std::advance(found, 1);
        }
        result.emplace_back(first, found);
        first = found;
    }

    return result;
}

template<typename It, typename UnaryPred>
auto group_point(It first, It last, UnaryPred pred) -> std::vector<std::pair<It, It>> {
    std::vector<std::pair<It, It>> result;

    while (first != last) {
        auto found = std::find_if(first, last, pred);
        if (found != last) {
            std::advance(found, 1);
        }
        result.emplace_back(first, found);
        first = found;
    }

    return result;
}

template<typename It>
auto first_date_iter(std::pair<It, It>& pair) -> It& {
    return pair.first;
}

template<typename It>
auto first_date_iter(std::pair<It, It> const& pair) -> It const& {
    return pair.first;
}

template<typename It>
auto last_date_iter(std::pair<It, It>& pair) -> It& {
    return pair.second;
}

template<typename It>
auto last_date_iter(std::pair<It, It> const& pair) -> It const& {
    return pair.second;
}

template<typename It>
auto group_by_week(It first_date, It last_date) {
    return group_point(first_date, last_date,
        [](auto const& date) { return date.day_of_week() == gregorian::Saturday; });
}

template<typename It>
auto group_by_moth(It first_date, It last_date) {
    return group_range(first_date, last_date,
        [](auto const& a, auto const& b) { return a.month() == b.month(); });
}

auto create_dates(gregorian::day_iterator first_date, gregorian::date last_date) -> std::vector<gregorian::date> {
    std::vector<gregorian::date> dates;
    while (first_date != last_date) {
        dates.emplace_back(*first_date);
        ++first_date;
    }
    return dates;
}

template<typename It>
auto group_dates(It first, It last) {
    auto months = group_by_moth(first, last);
    std::vector<decltype(group_by_week(first_date_iter(months[0]), last_date_iter(months[0])))> weeks_in_months;
    for (auto const& month : months) {
        weeks_in_months.emplace_back(group_by_week(first_date_iter(month), last_date_iter(month)));
    }
    return weeks_in_months;
}

void print_week_title(size_t width) {
    char const *titles[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
    for (auto const& title : titles) {
        std::cout << std::setw(width) << title;
    }
}

template<typename It>
void print_week_dates(It first_date, It last_date, size_t width) {
    std::cout << std::string(first_date->day_of_week() * width, ' ');
    while (first_date != last_date) {
        std::cout << std::setw(width) << first_date->day();
        ++first_date;
    }
}

template<typename T>
void print_calendar(T const& weeks_in_months) {
    std::size_t const width = 5;
    for (auto const& month : weeks_in_months) {
        print_week_title(width);
        std::cout << '\n';
        for (auto const& week : month) {
            print_week_dates(first_date_iter(week), last_date_iter(week), width);
            std::cout << '\n';
        }
        std::cout << "\n\n";
    }
}

void print_year(int year) {
    gregorian::day_iterator it{ gregorian::date{ year, 1, 1 } };
    gregorian::date end{ year + 1, 1, 1 };

    auto dates = create_dates(it, end);
    auto weeks_in_months = group_dates(std::begin(dates), std::end(dates));
    print_calendar(weeks_in_months);
}

int main() {
    print_year(2015);
}


#define BOOST_TEST_MODULE hash_test
#include <stdio.h>
#include <toscore/log/Log.h>
#include <toscore/crypto/SHA3.h>
#include <toscore/common/Common.h>
#include <toscore/log/Log.h>
#include <toscore/crypto/Hash.h>
#include <secp256k1_sha256.h>
#include <toscore/crypto/FixedHash.h>
#include <toscore/common/vector_ref.h>
#include <test/tools/TosTestUtils.h>
#include <boost/test/included/unit_test.hpp>
#include <toscore/crypto/Base64.h>
using std::cout;
using namespace boost;
using namespace boost::unit_test;
using namespace dev;

  class HashAndBaseTest
  {
    public:

    void testHashWrite()
    {
      dev::Hash256 h;
      h.Write((const unsigned char*)"2",5);
    }

    void testHashFinalize()
    {
      dev::Hash256 h;
      dev::h256 hash;
      h.Finalize((unsigned char*)"5");
    }

    void testHashreset()
    {
      dev::Hash256 h;
      h.Reset();
    }

    h256 testsha256(const unsigned char * teststring)
    {
      h256 h;
      dev::bytesConstRef test=dev::bytesConstRef(teststring,16);
      h = dev::sha256(test);
      return h;
    }

     void testToBase64()
    {
        dev::bytesConstRef btc((const unsigned char *)"1", 16);
        dev::toBase64(btc);
    }

    bytes testFromBase64(const unsigned char *teststring)
    {
        bytes bts = dev::fromBase64("qqqqqqqq");
        return bts;
    }
  };

BOOST_FIXTURE_TEST_SUITE(hashTest, TestHelperFixture)

BOOST_FIXTURE_TEST_CASE(hashTestRun, TestHelperFixture)
{
  HashAndBaseTest h;
  h.testsha256((const unsigned char*)"w");
}

BOOST_AUTO_TEST_SUITE_END()


#include <vector>
#include <list>
#include <map>
#include <set>

#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstring>
#include <assert.h>
#include <boost/lexical_cast.hpp>

#define INF 1023123123
#define EPS 1e-11
#define LSOne(S) (S & (-S))

#define FORN(X,Y) for (int (X) = 0;(X) < (Y);++(X))
#define FORB(X,Y) for (int (X) = (Y);(X) >= 0;--(X))
#define REP(X,Y,Z) for (int (X) = (Y);(X) < (Z);++(X))
#define REPB(X,Y,Z) for (int (X) = (Y);(X) >= (Z);--(X))

#define SZ(Z) ((int)(Z).size())
#define ALL(W) (W).begin(), (W).end()
#define PB push_back

#define MP make_pair
#define A first
#define B second

#define FORIT(X,Y) for(typeof((Y).begin()) X = (Y).begin();X!=(Y).end();X++)

using namespace std;

typedef long long ll;
typedef double db;
typedef vector<int> vi;
typedef pair<int, int> ii;
typedef vector<ii> vii;

void print_case(int i)
{
   cout << "Case #" << i + 1 << ": ";
}

bool clashes(char c, char d)
{
   return c == d ||
      (c == 'O' && (d == 'R' || d == 'Y')) ||
      (c == 'G' && (d == 'B' || d == 'Y')) ||
      (c == 'V' && (d == 'R' || d == 'B')) ||
      (d == 'O' && (c == 'R' || c == 'Y')) ||
      (d == 'G' && (c == 'B' || c == 'Y')) ||
      (d == 'V' && (c == 'R' || c == 'B')) ||
      (d == 'V' && c == 'O') ||
      (d == 'O' && c == 'G') ||
      (d == 'V' && c == 'G') ||
      (c == 'V' && d == 'O') ||
      (c == 'O' && d == 'G') ||
      (c == 'V' && d == 'G');
}

string expand(char c)
{
   if (c == 'O') return "RY";
   if (c == 'V') return "RB";
   if (c == 'G') return "BY";
   return boost::lexical_cast<string>(c);
}

int to_index(char c)
{
   if (c == 'R') return 0;
   if (c == 'B') return 1;
   if (c == 'Y') return 2;
   return -1;
}

int main()
{
   cout.precision(12);
   int ntc;
   cin >> ntc;

   FORN(kk, ntc)
   {
      print_case(kk);
      int n;
      cin >> n;
      vector<char> stalls(n, '_');
      vector<pair<int, char>> horses(6);
      vector<pair<int, char>> feat(3);
      feat[0].second = 'R';
      feat[1].second = 'B';
      feat[2].second = 'Y';
      string names = "ROYGBV";
      FORN(i, 6)
      {
         cin >> horses[i].first;
         horses[i].second = names[i];
         string s = expand(names[i]);
         FORN(j, s.length())
         {
            feat[to_index(s[j])].first += horses[i].first;
         }
      }
      bool possible = true;

      auto m = max_element(feat.begin(), feat.end());
      int toAdd = to_index(m->second);
      int realAdd = -1;
      int realAddCnt = -1;
      FORN(j, 6)
      {
         if (clashes(feat[toAdd].second, horses[j].second) && horses[j].first > 0)
         {
            string s = expand(horses[j].second);
            int total = 0;
            FORN(k, s.length())
            {
               total += feat[to_index(s[k])].first;
            }
            if (total > realAddCnt)
            {
               realAdd = j;
               realAddCnt = total;
            }
         }
      }

      stalls[0] = horses[realAdd].second;
      horses[realAdd].first--;
      string s = expand(horses[realAdd].second);
      FORN(k, s.length())
      {
         feat[to_index(s[k])].first--;
      }

      REP(i, 1, n - 1)
      {
         int toAdd = -1;
         int toAddCnt = -1;
         FORN(j, 3)
         {
            if ((feat[j].first > toAddCnt || (feat[j].first == toAddCnt && clashes(feat[j].second, stalls[0]))) &&
               !clashes(feat[j].second, stalls[i - 1]))
            {
               toAdd = j;
               toAddCnt = feat[j].first;
            }
         }

         if (toAdd == -1)
         {
            possible = false;
            break;
         }
         else
         {
            int realAdd = -1;
            int realAddCnt = -1;
            FORN(j, 6)
            {
               if (clashes(feat[toAdd].second, horses[j].second) && horses[j].first > 0 && !clashes(horses[j].second, stalls[i - 1]))
               {
                  string s = expand(horses[j].second);
                  int total = 0;
                  FORN(k, s.length())
                  {
                     total += feat[to_index(s[k])].first;
                  }
                  if (total > realAddCnt)
                  {
                     realAdd = j;
                     realAddCnt = total;
                  }
               }
            }
            if (realAdd != -1)
            {
               stalls[i] = horses[realAdd].second;
               horses[realAdd].first--;
               string s = expand(horses[realAdd].second);
               FORN(k, s.length())
               {
                  feat[to_index(s[k])].first--;
               }
            }
            else
            {
               possible = false;
               break;
            }
         }
      }

      if (possible)
      {
         m = max_element(horses.begin(), horses.end());
         if (!clashes(stalls[n - 2], m->second) && !clashes(stalls[0], m->second))
         {
            stalls[n - 1] = m->second;
            m->first--;
         }
         else
         {
            possible = false;
         }
      }

      if (possible)
      {
         FORN(i, n)
         {
            cout << stalls[i];
         }
         cout << endl;
      }
      else
      {
         cout << "IMPOSSIBLE" << endl;
      }
   }
}


/* Copyright (c) 2017, United States Government, as represented by the
 * Administrator of the National Aeronautics and Space Administration.
 *
 * All rights reserved.
 *
 * The Astrobee platform is licensed under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

#include <interest_point/matching.h>

#include <Eigen/Geometry>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <string>
#include <vector>

class MatchingTest : public ::testing::Test {
 protected:
  virtual void SetUp() {
    // TEST_DIR is defined as this modules test directory. However
    // that is not where the test imagery is located.
    std::string data_dir = std::string(TEST_DIR) + "/../../sparse_mapping/test/data";
    image1 = cv::imread(data_dir + "/m0004000.jpg",
                        CV_LOAD_IMAGE_GRAYSCALE);
    image2 = cv::imread(data_dir + "/m0004025.jpg",
                        CV_LOAD_IMAGE_GRAYSCALE);

    // Decimate the image to speed things up for our unit tests
    cv::resize(image1, image1, cv::Size(), 0.7, 0.7, cv::INTER_AREA);
    cv::resize(image2, image2, cv::Size(), 0.7, 0.7, cv::INTER_AREA);
  }

  void DetectKeyPoints(std::string const& detector) {
    interest_point::FeatureDetector ipdetect(detector);
    ipdetect.Detect(image1, &keypoints1, &descriptor1);
    ipdetect.Detect(image2, &keypoints2, &descriptor2);
  }

  cv::Mat image1, image2;
  std::vector<cv::KeyPoint> keypoints1, keypoints2;
  cv::Mat descriptor1, descriptor2;
  std::vector<cv::DMatch> matches;
};

TEST_F(MatchingTest, SURF) {
  DetectKeyPoints("SURF");
  interest_point::FindMatches(descriptor1, descriptor2, &matches);
  EXPECT_NEAR(keypoints1.size(), 2023u, 10);
  EXPECT_NEAR(keypoints2.size(), 2047u, 10);
  EXPECT_EQ(descriptor1.cols, 64);
  // KNN matching seems to be variable
  EXPECT_NEAR(matches.size(), 798u, 10);
}

TEST_F(MatchingTest, ORGBRISK) {
  DetectKeyPoints("ORGBRISK");
  interest_point::FindMatches(descriptor1, descriptor2, &matches);
  EXPECT_LT(100u, keypoints1.size());
  EXPECT_LT(100u, keypoints2.size());
  EXPECT_EQ(64, descriptor1.cols);
  EXPECT_LT(50u, matches.size());
}


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

#include <boost/simd/include/functor.hpp>
#include <boost/simd/sdk/meta/cardinal_of.hpp>
#include <boost/simd/swar/functions/details/random_permute.hpp>
#include <boost/dispatch/meta/as.hpp>

namespace boost { namespace simd
{
  namespace tag
  {
    /*!
      @brief shuffle generic tag

      Represents the shuffle function in generic contexts.

      @par Models:
      Hierarchy
    **/
    struct shuffle_ : ext::unspecified_<shuffle_>
    {
      /// @brief Parent hierarchy
      typedef ext::unspecified_<shuffle_> parent;
      template<class... Args>
      static BOOST_FORCEINLINE BOOST_AUTO_DECLTYPE dispatch(Args&&... args)
      BOOST_AUTO_DECLTYPE_BODY( dispatching_shuffle_( ext::adl_helper(), static_cast<Args&&>(args)... ) )
    };
  }
  namespace ext
  {
    template<class Site>
    BOOST_FORCEINLINE generic_dispatcher<tag::shuffle_, Site> dispatching_shuffle_(adl_helper, boost::dispatch::meta::unknown_<Site>, ...)
    {
      return generic_dispatcher<tag::shuffle_, Site>();
    }
    template<class... Args>
    struct impl_shuffle_;
  }
} }

namespace
{
  /// INTERNAL ONLY - local shuffle functor type
  template<typename T>
  using shuffle_t = typename boost::dispatch
                                  ::make_functor< boost::simd::tag::shuffle_
                                                , T
                                                >::type;
}

namespace boost { namespace simd
{
  /*!
    @brief SIMD register shuffling

    Shuffle the elements of one SIMD register following a compile-time
    permutation pattern passed as a static list of integral constants.

    @par Semantic:

    Let's @c T be a SIMD register type of cardinal @c N and <tt>I1,...,In</tt> be
    @c N integral constant in \f$[-1, N[\f$. For any SIMD register @c v of type
    @c T, the following code :

    @code
    T r = shuffle<I1, ..., In>(v);
    @endcode

    is equivalent to

    @code
    r[0]   = I1 == -1 ? 0 : v[I1];
    r[1]   = I2 == -1 ? 0 : v[I2];
    ...
    r[N-1] = Ic == -1 ? 0 : v[In];
    @endcode

    @usage{shuffle_idx1.cpp}

    @tparam Indices Integral constant defining the permutation pattern
    @param  a0 First register to shuffle

    @return A SIMD register shuffled as per the permutation pattern
  **/
  template<int... Indices, typename A0>
  BOOST_FORCEINLINE
  auto shuffle(A0 const& a0)
      -> decltype(shuffle_t<A0>()(a0,details::random_permute_t<Indices...>()))
  {
    static_assert ( boost::simd::meta::cardinal_of<A0>::value == sizeof...(Indices)
                  , "Wrong number of permutation indices"
                  );

    return shuffle_t<A0>()(a0,details::random_permute_t<Indices...>());
  }

  /*!
    @brief SIMD register shuffling

    Shuffle the elements of two SIMD registers following a compile-time
    permutation pattern passed as a static list of integral constants.

    @par Semantic:

    Let's @c T be a SIMD register type of cardinal @c N and <tt>I1,...,In</tt> be
    @c N integral constant in \f$[-1, 2N-1[\f$. For any SIMD register @c v1 and @c v2
    of type @c T, the following code :

    @code
    T r = shuffle<I1, ..., In>(v1,v2);
    @endcode

    is equivalent to shuffling the agglomeration of @c v1 and @c v2 and extracting
    the lower part of the resulting vector.

    @code
    r[0]   = shuffle<I1,...,Ic>( combine(v1,v2) )[0]
    r[1]   = shuffle<I1,...,Ic>( combine(v1,v2) )[1]
    ...
    r[N-1] = shuffle<I1,...,Ic>( combine(v1,v2) )[N-1]
    @endcode

    @usage{shuffle_idx2.cpp}

    @tparam Indices Integral constant defining the permutation pattern
    @param  a0 First register to shuffle
    @param  a1 Second register to shuffle.

    @return A SIMD register shuffled as per the permutation pattern
  **/
  template<int... Indices, typename A0, typename A1>
  BOOST_FORCEINLINE
  auto shuffle(A0 const& a0, A1 const& a1)
      -> decltype(shuffle_t<A0>()(a0,a1,details::random_permute_t<Indices...>()))
  {
    static_assert (  boost::simd::meta::cardinal_of<A0>::value == sizeof...(Indices)
                  && boost::simd::meta::cardinal_of<A1>::value == sizeof...(Indices)
                  , "Wrong number of permutation indices"
                  );

    return shuffle_t<A0>()(a0,a1,details::random_permute_t<Indices...>());
  }

  /*!
    @brief SIMD register shuffling

    Shuffle the elements of one SIMD register following a compile-time
    permutation pattern passed as a @metafunction.

    @par Semantic:

    Let's @c T be a SIMD register type of cardinal @c N, @c Perm be a
    binary @metafunction. For any SIMD register @c v of type @c T, the following
    code:

    @code
    T r = shuffle<Perm>(v);
    @endcode

    is respectively equivalent to

    @code
    T r = shuffle< mpl::apply<Perm, int_<0>, int_<N> >::type::value
                 , ...
                 , mpl::apply<Perm, int_<C-1>, int_<N> >::type::value
                 >(v);
    @endcode

    @usage{shuffle_perm2.cpp}

    @tparam Perm Permutation pattern @metafunction
    @param  a0 First register to shuffle

    @return A SIMD register shuffled as per the permutation pattern
  **/
  template<typename Perm,typename A0>
  BOOST_FORCEINLINE
  auto shuffle(A0 const& a0)
      -> decltype(shuffle_t<A0>()(a0,boost::dispatch::meta::as_<Perm>()))
  {
    return shuffle_t<A0>()(a0,boost::dispatch::meta::as_<Perm>());
  }

  /*!
    @brief SIMD register shuffling

    Shuffle the elements of two SIMD registers following a compile-time
    permutation pattern passed as a @metafunction.

    @par Semantic:

    Let's @c T be a SIMD register type of cardinal @c N, @c Perm be a
    binary @metafunction. For any SIMD register @c v1 and  @c v2 of type @c T,
    the following code:

    @code
    T r = shuffle<Perm>(v1,v2);
    @endcode

    is equivalent to

    @code
    T r = shuffle< mpl::apply<Perm, int_<0>, int_<N> >::type::value
                 , ...
                 , mpl::apply<Perm, int_<C-1>, int_<N> >::type::value
                 >(v1,v2);
    @endcode

    @usage{shuffle_perm1.cpp}

    @tparam Perm Permutation pattern @metafunction
    @param  a0 First register to shuffle
    @param  a1 Second register to shuffle.

    @return A SIMD register shuffled as per the permutation pattern
  **/
  template<typename Perm, typename A0, typename A1>
  BOOST_FORCEINLINE
  auto shuffle(A0 const& a0, A1 const& a1)
      -> decltype(shuffle_t<A0>()(a0,a1,boost::dispatch::meta::as_<Perm>()))
  {
    return shuffle_t<A0>()(a0,a1,boost::dispatch::meta::as_<Perm>());
  }
} }

#endif


#include <elle/IntRange.hh>

#include <boost/algorithm/cxx11/any_of.hpp>

#include <elle/log.hh>
#include <elle/printf.hh>

ELLE_LOG_COMPONENT("elle.IntRange");

namespace elle
{
  IntRange::IntRange(int start, int size)
    : _start(start)
    , _end(start + size)
  {}

  bool
  IntRange::operator <(IntRange const& rhs) const
  {
    return this->start() < rhs.start() ||
           this->start() == rhs.start() && this->end() < rhs.end();
  }

  bool
  IntRange::operator >(IntRange const& rhs) const
  {
    return this->end() > rhs.end() ||
           this->end() == rhs.end() && this->start() > rhs.start();
  }

  bool
  IntRange::operator ==(IntRange const& rhs) const
  {
    return this->start() == rhs.start() && this->end() == rhs.end();
  }

  int
  IntRange::size() const
  {
    return this->_end - this->_start;
  }

  void
  IntRange::size(int size)
  {
    this->_end = this->_start + size;
  }

  bool
  IntRange::empty() const
  {
    return this->end() <= this->start();
  }

  bool
  IntRange::contains(IntRange const& other) const
  {
    return this->start() <= other.start() && this->end() >= other.end();
  }

  bool
  IntRange::contains(int pos) const
  {
    return pos >= this->start() && pos < this->end();
  }

  bool
  IntRange::overlaps(IntRange const& other) const
  {
    return this->contains(other.start()) || this->contains(other.end());
  }

  IntRanges
  IntRange::operator -(IntRange const& rhs) const
  {
    // Disjoint
    if (!rhs.overlaps(*this) && !this->contains(rhs))
      ELLE_DEBUG("disjoint")
        return *this;
    // Superset
    if (rhs.contains(*this))
      ELLE_DEBUG("superset")
        return IntRanges();
    // Prefix
    if (rhs.start() <= this->start())
      ELLE_DEBUG("prefix")
        return IntRange(rhs.end(), this->end() - rhs.end());
    // Suffix
    if (rhs.end() >= this->end())
      ELLE_DEBUG("suffix")
        return IntRange(this->start(), rhs.start() - this->start());
    // Right in the middle
    ELLE_DEBUG("right in the middle")
      return IntRanges{{
        IntRange{this->start(), rhs.start() - this->start()},
        IntRange{rhs.end(), this->end() - rhs.end()}
      }};
  }

  std::ostream&
  operator <<(std::ostream& output, IntRange const& range)
  {
    return elle::fprintf(output, "(%s, %s)", range.start(), range.size());
  }

  /*-------.
  | IntRanges |
  `-------*/

  IntRanges::IntRanges()
    : _ranges()
  {}

  static inline
  void
  emplace(std::set<IntRange>& set, IntRange range)
  {
#if GCC_VERSION_LTE(4, 7)
    set.insert(std::move(range));
#else
    set.emplace(std::move(range));
#endif
  }

  template <typename ... Args>
  static inline
  void
  emplace(std::set<IntRange>& set, Args&& ... args)
  {
#if GCC_VERSION_LTE(4, 7)
    set.insert(IntRange(std::forward<Args>(args)...));
#else
    set.emplace(std::forward<Args>(args)...);
#endif
  }

  IntRanges::IntRanges(IntRange range)
    : _ranges()
  {
    emplace(this->_ranges, std::move(range));
  }

  IntRanges::IntRanges(std::vector<IntRange> const& ranges)
    : _ranges()
  {
    for (auto const& range: ranges)
      emplace(this->_ranges, std::move(range));
  }

  IntRanges&
  IntRanges::operator += (IntRange range)
  {
    this->complete(range);
    return *this;
  }

  int
  IntRanges::next_free(int position) const
  {
    for (auto const& chunk: this->_ranges)
      if (chunk.end() >= position)
        if (chunk.start() > position)
          return position;
        else
          return chunk.end();
    return position;
  }

  bool
  IntRanges::contains(IntRange const& range) const
  {
    return boost::algorithm::any_of(this->_ranges,
                                    [&range] (auto const& chunk)
                                    {
                                      return chunk.contains(range);
                                    });
  }

  bool
  IntRanges::contains(int pos) const
  {
    return boost::algorithm::any_of(this->_ranges,
                                    [pos] (auto const& chunk)
                                    {
                                      return chunk.contains(pos);
                                    });
  }

  bool
  IntRanges::operator ==(IntRanges const& rhs) const
  {
    return this->_ranges == rhs._ranges;
  }

  IntRanges
  IntRanges::complete(IntRange range)
  {
    ELLE_TRACE_SCOPE("%s: complete with %s", *this, range);
    IntRanges res;
    boost::optional<IntRange> previous;
    for (auto current: this->_ranges)
    {
      ELLE_DUMP_SCOPE("%s: consider %s", *this, current);
      if (current.start() > range.end())
      {
        ELLE_DUMP("%s: past the end, stop", *this);
        break;
      }
      if (current.overlaps(range))
      {
        ELLE_DUMP("%s: overlap", *this);
        if (current.start() > range.start())
        {
          emplace(res._ranges, range.start(), current.start());
          this->_ranges.erase(current);
          current.start(range.start());
          // Merge with previous if needed.
          if (previous && previous.get().end() == current.start())
          {
            this->_ranges.erase(previous.get());
            current.start(previous.get().start());
          }
          emplace(this->_ranges, current);
        }
        range.start(current.end());
        ELLE_DUMP("%s: leftover is %s", *this, range);
        if (range.empty())
        {
          ELLE_DUMP("%s: leftover empty, done", *this);
          return res;
        }
      }
      else
        ELLE_DUMP("%s: skip %s", *this, current);
      previous = current;
    }
    emplace(res._ranges, range);
    if (previous && previous.get().end() == range.start())
    {
      this->_ranges.erase(previous.get());
      range.start(previous.get().start());
    }
    emplace(this->_ranges, range);
    ELLE_DUMP("%s: added %s", *this, range);
    return res;
  }

  IntRanges::iterator
  IntRanges::begin()
  {
    return this->_ranges.begin();
  }

  IntRanges::const_iterator
  IntRanges::begin() const
  {
    return this->_ranges.begin();
  }

  IntRanges::iterator
  IntRanges::end()
  {
    return this->_ranges.end();
  }

  IntRanges::const_iterator
  IntRanges::end() const
  {
    return this->_ranges.end();
  }

  std::ostream&
  operator <<(std::ostream& output, IntRanges const& ranges)
  {
    return output << ranges._ranges;
  }
}


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

  @copyright 2016 NumScale SAS

  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_FUNCTION_SHR_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_SHR_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

 /*!

    @ingroup group-operator
    Function object implementing shr capabilities

    Returns the first entry logically shifted right
    by value of the second entry which must be of integtral type.
    If the second entry is negative
    the result is not defined.

    @par semantic:
    For any given value @c x of type @c T,  @c n  of type @c I:

    @code
    auto r = shr(x, n);
    @endcode

    @see  shift_right, shift_left, rshl, rshr, rol, ror

  **/
  Value shr(Value const & x, IntegerValue const& n);
} }
#endif

#include <boost/simd/function/scalar/shr.hpp>
#include <boost/simd/function/simd/shr.hpp>

#endif