/*
==============================================================================
KratosStructuralApplication
A library based on:
Kratos
A General Purpose Software for Multi-Physics Finite Element Analysis
Version 1.0 (Released on march 05, 2007).

Copyright 2007
Pooyan Dadvand, Riccardo Rossi, Janosch Stascheit, Felix Nagel
pooyan@cimne.upc.edu
rrossi@cimne.upc.edu
janosch.stascheit@rub.de
nagel@sd.rub.de
- CIMNE (International Center for Numerical Methods in Engineering),
Gran Capita' s/n, 08034 Barcelona, Spain
- Ruhr-University Bochum, Institute for Structural Mechanics, Germany

Permission is hereby granted, free  of charge, to any person obtaining
a  copy  of this  software  and  associated  documentation files  (the
"Software"), to  deal in  the Software without  restriction, including
without limitation  the rights to  use, copy, modify,  merge, publish,
distribute,  sublicense and/or  sell copies  of the  Software,  and to
permit persons to whom the Software  is furnished to do so, subject to
the following condition:

Distribution of this code for  any  commercial purpose  is permissible
ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNERS.

The  above  copyright  notice  and  this permission  notice  shall  be
included in all copies or substantial portions of the Software.

THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT  SHALL THE AUTHORS OR COPYRIGHT HOLDERS  BE LIABLE FOR ANY
CLAIM, DAMAGES OR  OTHER LIABILITY, WHETHER IN AN  ACTION OF CONTRACT,
TORT  OR OTHERWISE, ARISING  FROM, OUT  OF OR  IN CONNECTION  WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

==============================================================================
*/
/* **************************************************************************************
*
*   Last Modified by:    $Author: hbui $
*   Date:                $Date: 23 Oct 2014 $
*   Revision:            $Revision: 1.1 $
*
* ***************************************************************************************/

// System includes
// External includes
#include <boost/timer.hpp>

// Project includes
#include "includes/define.h"
#include "custom_conditions/line_force_isogeometric.h"
#include "utilities/math_utils.h"
#include "utilities/openmp_utils.h"
#include "structural_application/structural_application_variables.h"
#include "structural_application/custom_utilities/sd_math_utils.h"
#include "structural_application/structural_application.h"
#include "isogeometric_application/custom_utilities/isogeometric_math_utils.h"
#include "isogeometric_application/isogeometric_application.h"

//#define ENABLE_PROFILING

namespace Kratos
{
//***********************************************************************************
//***********************************************************************************
// -------- //
//  PUBLIC  //
// -------- //

// Constructor
LineForceIsogeometric::LineForceIsogeometric()
{
}

// Constructor
LineForceIsogeometric::LineForceIsogeometric( IndexType NewId, GeometryType::Pointer pGeometry )
    : Condition( NewId, pGeometry )
{
    mpIsogeometricGeometry =
        boost::dynamic_pointer_cast<IsogeometricGeometryType>(pGetGeometry());

}

// Constructor
LineForceIsogeometric::LineForceIsogeometric( IndexType NewId, GeometryType::Pointer pGeometry,
                          PropertiesType::Pointer pProperties )
    : Condition( NewId, pGeometry, pProperties )
{
    mpIsogeometricGeometry =
        boost::dynamic_pointer_cast<IsogeometricGeometryType>(pGetGeometry());
}

//***********************************************************************************
//***********************************************************************************
Condition::Pointer LineForceIsogeometric::Create( IndexType NewId,
    GeometryType::Pointer pGeom,
    PropertiesType::Pointer pProperties ) const
{
return Condition::Pointer( new LineForceIsogeometric( NewId, pGeom, pProperties ) );
}

//***********************************************************************************
//***********************************************************************************
// Destructor
LineForceIsogeometric::~LineForceIsogeometric()
{
}

//***********************************************************************************
//***********************************************************************************
void LineForceIsogeometric::EquationIdVector( EquationIdVectorType& rResult,
    const ProcessInfo& rCurrentProcessInfo ) const
{
    KRATOS_TRY

    DofsVectorType ConditionalDofList;
    GetDofList(ConditionalDofList, rCurrentProcessInfo);

    if (rResult.size() != ConditionalDofList.size())
    rResult.resize(ConditionalDofList.size(), false);

    for(unsigned int i = 0; i < ConditionalDofList.size(); ++i)
    {
        rResult[i] = ConditionalDofList[i]->EquationId();
    }

    KRATOS_CATCH( "" )
}

//***********************************************************************************
//***********************************************************************************
void LineForceIsogeometric::GetDofList( DofsVectorType& ElementalDofList,
const ProcessInfo& rCurrentProcessInfo ) const
{
    const unsigned int dim = 3;

    ElementalDofList.resize( 0 );

    for ( unsigned int i = 0; i < GetGeometry().size(); ++i )
    {
        ElementalDofList.push_back( GetGeometry()[i].pGetDof( DISPLACEMENT_X ) );
        ElementalDofList.push_back( GetGeometry()[i].pGetDof( DISPLACEMENT_Y ) );
        ElementalDofList.push_back( GetGeometry()[i].pGetDof( DISPLACEMENT_Z ) );
    }
}

//***********************************************************************************
//***********************************************************************************
void LineForceIsogeometric::Initialize(const ProcessInfo& rCurrentProcessInfo)
{
    KRATOS_TRY

        ////////////////////Initialize geometry_data/////////////////////////////
        #ifdef ENABLE_PROFILING
        double start_compute = OpenMPUtils::GetCurrentTime();
        #endif

        // try to read the extraction operator from the elemental data
        Matrix ExtractionOperator;
        bool manual_initilization = false;
        if( this->Has( EXTRACTION_OPERATOR ) )
        {
            ExtractionOperator = this->GetValue( EXTRACTION_OPERATOR );
            manual_initilization = true;
        }
        else if( this->Has( EXTRACTION_OPERATOR_MCSR ) )
        {
            Matrix Temp = this->GetValue( EXTRACTION_OPERATOR_MCSR );

            // make a simple check
            if(Temp.size1() != 2)
                KRATOS_THROW_ERROR(std::logic_error, "Invalid MCSR matrix for extraction operator found at element", this->Id())

            // choose the best storage scheme based ratio between number of nonzeros and the full size of the matrix
            unsigned int size_ex_n = (unsigned int)(Temp(0, 0) - 1);
            unsigned int size_ex_nz = Temp.size2() - 1;
            if( ( (double)(size_ex_nz) ) / (size_ex_n * size_ex_n) < 0.2 )
                ExtractionOperator = IsogeometricMathUtils::MCSR2CSR(Temp);
            else
                ExtractionOperator = IsogeometricMathUtils::MCSR2MAT(Temp);
            manual_initilization = true;
        }
        else if( this->Has( EXTRACTION_OPERATOR_CSR_ROWPTR )
             && this->Has( EXTRACTION_OPERATOR_CSR_COLIND )
             && this->Has( EXTRACTION_OPERATOR_CSR_VALUES ) )
        {
            Vector rowPtr = this->GetValue( EXTRACTION_OPERATOR_CSR_ROWPTR ); // must be 0-base
            Vector colInd = this->GetValue( EXTRACTION_OPERATOR_CSR_COLIND ); // must be 0-base
            Vector values = this->GetValue( EXTRACTION_OPERATOR_CSR_VALUES );
            ExtractionOperator = IsogeometricMathUtils::Triplet2CSR(rowPtr, colInd, values);
            manual_initilization = true;
        }

//        KRATOS_WATCH(manual_initilization)
//        KRATOS_WATCH(ExtractionOperator)

        // initialize the geometry
        if(manual_initilization)
            mpIsogeometricGeometry->AssignGeometryData(
                this->GetValue(NURBS_KNOTS_1),
                this->GetValue(NURBS_KNOTS_2),
                this->GetValue(NURBS_KNOTS_3),
                this->GetValue(NURBS_WEIGHTS),
                ExtractionOperator,
                this->GetValue(NURBS_DEGREE_1),
                this->GetValue(NURBS_DEGREE_2),
                this->GetValue(NURBS_DEGREE_3),
                2 // only need to compute 2 integration rules
            );

        // integration rule
        if(this->Has( INTEGRATION_ORDER ))
        {
            if(this->GetValue(INTEGRATION_ORDER) == 1)
            {
                mThisIntegrationMethod = GeometryData::GI_GAUSS_1;
            }
            else if(this->GetValue(INTEGRATION_ORDER) == 2)
            {
                mThisIntegrationMethod = GeometryData::GI_GAUSS_2;
            }
            else if(this->GetValue(INTEGRATION_ORDER) == 3)
            {
                mThisIntegrationMethod = GeometryData::GI_GAUSS_3;
            }
            else if(this->GetValue(INTEGRATION_ORDER) == 4)
            {
                mThisIntegrationMethod = GeometryData::GI_GAUSS_4;
            }
            else if(this->GetValue(INTEGRATION_ORDER) == 5)
            {
                mThisIntegrationMethod = GeometryData::GI_GAUSS_5;
            }
            else
                KRATOS_THROW_ERROR(std::logic_error, "LineForceIsogeometric element does not support for integration rule", this->GetValue(INTEGRATION_ORDER))
        }
        else if(GetProperties().Has( INTEGRATION_ORDER ))
        {
            if(GetProperties()[INTEGRATION_ORDER] == 1)
            {
                mThisIntegrationMethod = GeometryData::GI_GAUSS_1;
            }
            else if(GetProperties()[INTEGRATION_ORDER] == 2)
            {
                mThisIntegrationMethod = GeometryData::GI_GAUSS_2;
            }
            else if(GetProperties()[INTEGRATION_ORDER] == 3)
            {
                mThisIntegrationMethod = GeometryData::GI_GAUSS_3;
            }
            else if(GetProperties()[INTEGRATION_ORDER] == 4)
            {
                mThisIntegrationMethod = GeometryData::GI_GAUSS_4;
            }
            else if(GetProperties()[INTEGRATION_ORDER] == 5)
            {
                mThisIntegrationMethod = GeometryData::GI_GAUSS_5;
            }
            else
                KRATOS_THROW_ERROR(std::logic_error, "LineForceIsogeometric element does not support for integration points", GetProperties()[INTEGRATION_ORDER])
        }
        else
            mThisIntegrationMethod = GeometryData::GI_GAUSS_1; // default method

        #ifdef ENABLE_PROFILING
        double end_compute = OpenMPUtils::GetCurrentTime();
        std::cout << "GenerateGeometryData for condition " << Id() << " completed: " << end_compute - start_compute << " s" << std::endl;
        #endif

    KRATOS_CATCH("")
}

//***********************************************************************************
//***********************************************************************************
void LineForceIsogeometric::CalculateLocalSystem( MatrixType& rLeftHandSideMatrix,
                                                VectorType& rRightHandSideVector,
                                                const ProcessInfo& rCurrentProcessInfo )
{
    //calculation flags
    bool CalculateStiffnessMatrixFlag = true;
    bool CalculateResidualVectorFlag = true;

    CalculateAll( rLeftHandSideMatrix, rRightHandSideVector, rCurrentProcessInfo,
                  CalculateStiffnessMatrixFlag, CalculateResidualVectorFlag );
}
//***********************************************************************************
//***********************************************************************************
void LineForceIsogeometric::CalculateDampingMatrix(MatrixType& rDampMatrix, const ProcessInfo& rCurrentProcessInfo)
{
    KRATOS_TRY
    rDampMatrix.resize(0,0,false);
    KRATOS_CATCH("")
}
//***********************************************************************************
//***********************************************************************************
void LineForceIsogeometric::CalculateAll( MatrixType& rLeftHandSideMatrix,
                                VectorType& rRightHandSideVector,
                                const ProcessInfo& rCurrentProcessInfo,
                                bool CalculateStiffnessMatrixFlag,
                                bool CalculateResidualVectorFlag )
{
    KRATOS_TRY

    const unsigned int number_of_nodes = GetGeometry().size();
    const unsigned int dim = 3;
    unsigned int MatSize = number_of_nodes * dim;

    //resizing as needed the LHS
    if ( CalculateStiffnessMatrixFlag == true )
    {
        if ( rLeftHandSideMatrix.size1() != MatSize )
            rLeftHandSideMatrix.resize( MatSize, MatSize, false );

        noalias( rLeftHandSideMatrix ) = ZeroMatrix( MatSize, MatSize ); //resetting LHS
    }

    //resizing as needed the RHS
    if ( CalculateResidualVectorFlag == true )
    {
        if ( rRightHandSideVector.size() != MatSize )
            rRightHandSideVector.resize( MatSize, false );

        noalias(rRightHandSideVector) = ZeroVector( MatSize ); //resetting RHS
    }

    //KRATOS_WATCH(typeid(*mpIsogeometricGeometry).name())

    //reading integration points
    const GeometryType::IntegrationPointsArrayType& integration_points =
                mpIsogeometricGeometry->IntegrationPoints(mThisIntegrationMethod);

    //calculating shape function values and local gradients
    GeometryType::ShapeFunctionsGradientsType DN_De;
    Matrix Ncontainer;

    mpIsogeometricGeometry->CalculateShapeFunctionsIntegrationPointsValuesAndLocalGradients(
        Ncontainer,
        DN_De,
        mThisIntegrationMethod
    );

    //loop over integration points
    Vector Load( dim );
    Vector temp( dim );
    for ( unsigned int PointNumber = 0; PointNumber < integration_points.size(); ++PointNumber )
    {
        // compute Load vector at integration point
        noalias( Load ) = ZeroVector( dim );
        for ( unsigned int n = 0; n < GetGeometry().size(); ++n )
        {
            noalias( temp ) = ( GetGeometry()[n] ).GetSolutionStepValue(FACE_LOAD);
//            KRATOS_WATCH(temp)

            for ( unsigned int i = 0; i < dim; ++i )
            {
                Load( i ) += temp( i ) * Ncontainer( PointNumber, n );
            }
        }

        // integration weight
        double IntegrationWeight = integration_points[PointNumber].Weight();

        // compute the tangential vector
        Vector t = ZeroVector( dim );
        for ( unsigned int n = 0; n < GetGeometry().size(); ++n )
        {
            t[0] += GetGeometry().GetPoint( n ).X0() * DN_De[PointNumber]( n, 0 );
            t[1] += GetGeometry().GetPoint( n ).Y0() * DN_De[PointNumber]( n, 0 );
            t[2] += GetGeometry().GetPoint( n ).Z0() * DN_De[PointNumber]( n, 0 );
        }

//        KRATOS_WATCH(t)
        //calculating length
        double dL = sqrt( t[0] * t[0] + t[1] * t[1] + t[2] * t[2] );

//        KRATOS_WATCH(Ncontainer)
//        KRATOS_WATCH(Load)
//        KRATOS_WATCH(IntegrationWeight)
//        KRATOS_WATCH(dL)

        // contribute to RHS vector
        if ( CalculateResidualVectorFlag == true )
        {
            for ( unsigned int prim = 0; prim < GetGeometry().size(); ++prim )
                for ( unsigned int i = 0; i < dim; ++i )
                    rRightHandSideVector( prim*dim + i ) +=
                        Ncontainer( PointNumber, prim ) * Load( i ) * IntegrationWeight * dL;
        }
    }
//    KRATOS_WATCH(rRightHandSideVector)

    KRATOS_CATCH( "" )
}

} // Namespace Kratos.


/*
 * rsxgltest - feedback1
 */

#define GL3_PROTOTYPES
#include <GL3/gl3.h>

#include "rsxgltest.h"
#include "math3d.h"
#include "sine_wave.h"

#include <stddef.h>
#include "points_vert.h"
#include "feedback1_frag.h"

#include <io/pad.h>

#include <math.h>
#include <Eigen/Geometry>

const char * rsxgltest_name = "feedback1";

struct sine_wave_t rgb_waves[3] = {
  { 0.5f,
    0.5f,
    1.0f
  },
  {
    0.5f,
    0.5f,
    1.5f
  },
  {
    0.5f,
    0.5f,
    2.5f
  }
};

struct sine_wave_t xyz_waves[3] = {
  { 0.5f,
    0.5f,
    1.0f / 4.0f
  },
  {
    0.5f,
    0.5f,
    1.5f / 4.0f
  },
  {
    0.5f,
    0.5f,
    2.5f / 4.0f
  }
};

GLuint buffers[2] = { 0,0 };
GLuint shaders[2] = { 0,0 };
GLuint program = 0;

GLint ProjMatrix_location = -1, TransMatrix_location = -1;

const GLuint npoints = 100;
const GLuint batchsize = 256;
const GLuint nbatch = (npoints / batchsize) + ((npoints % batchsize) ? 1 : 0);

GLint batchfirst[nbatch];
GLsizei batchcount[nbatch];
GLvoid const * batchoffsets[nbatch];

GLuint * client_elements = 0;
GLvoid const * client_batchindices[nbatch];
GLuint elements = 0;

#define DTOR(X) ((X)*0.01745329f)
#define RTOD(d) ((d)*57.295788f)

Eigen::Projective3f ProjMatrix(perspective(DTOR(54.3),1920.0 / 1080.0,0.1,1000.0));

Eigen::Affine3f ViewMatrixInv =
  Eigen::Affine3f(Eigen::Affine3f::Identity() *
		  (
		   Eigen::Translation3f(1.779,2.221,4.034) *
		   (
		    Eigen::AngleAxisf(DTOR(0),Eigen::Vector3f::UnitZ()) *
		    Eigen::AngleAxisf(DTOR(23.8),Eigen::Vector3f::UnitY()) *
		    Eigen::AngleAxisf(DTOR(-26.738),Eigen::Vector3f::UnitX())
		    )
		   )
		  ).inverse();

extern "C"
void
rsxgltest_pad(unsigned int,const padData * paddata)
{
  if(paddata -> BTN_UP) {
    tcp_printf("up\n");
  }
  else if(paddata -> BTN_DOWN) {
    tcp_printf("down\n");
  }
}

extern "C"
void
rsxgltest_init(int argc,const char ** argv)
{
  tcp_printf("%s\n",__PRETTY_FUNCTION__);

  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);

  // Set up us the program:
  shaders[0] = glCreateShader(GL_VERTEX_SHADER);
  shaders[1] = glCreateShader(GL_FRAGMENT_SHADER);

  program = glCreateProgram();

  glAttachShader(program,shaders[0]);
  glAttachShader(program,shaders[1]);

  // Supply shader binaries:
  //glShaderBinary(1,shaders,0,points_vpo,points_vpo_size);
  //glShaderBinary(1,shaders + 1,0,points_fpo,points_fpo_size);

  // Supply shader SOURCES!
  char szInfo[2048];

  const GLchar * shader_srcs[] = { (const GLchar *)points_vert, (const GLchar *)feedback1_frag };
  GLint shader_srcs_lengths[] = { points_vert_len, feedback1_frag_len };
  GLint compiled = 0;

  glShaderSource(shaders[0],1,shader_srcs,shader_srcs_lengths);
  glCompileShader(shaders[0]);

  glGetShaderiv(shaders[0],GL_COMPILE_STATUS,&compiled);
  tcp_printf("shader compile status: %i\n",compiled);

  glGetShaderInfoLog(shaders[0],2048,0,szInfo);
  tcp_printf("%s\n",szInfo);

  glShaderSource(shaders[1],1,shader_srcs + 1,shader_srcs_lengths + 1);
  glCompileShader(shaders[1]);

  glGetShaderiv(shaders[1],GL_COMPILE_STATUS,&compiled);
  tcp_printf("shader compile status: %i\n",compiled);

  glGetShaderInfoLog(shaders[1],2048,0,szInfo);
  tcp_printf("%s\n",szInfo);

  const char * varyings[] = {
    "gl_Position"
  };
  glTransformFeedbackVaryings(program,1,varyings,GL_SEPARATE_ATTRIBS);

  // Link the program for real:
  glLinkProgram(program);
  glValidateProgram(program);

  summarize_program("draw",program);

  GLint
    vertex_location = glGetAttribLocation(program,"position");

  ProjMatrix_location = glGetUniformLocation(program,"ProjMatrix");
  TransMatrix_location = glGetUniformLocation(program,"TransMatrix");

  tcp_printf("vertex_location: %i\n",vertex_location);
  tcp_printf("ProjMatrix_location: %i TransMatrix_location: %i\n",
	     ProjMatrix_location,TransMatrix_location);

  glUseProgram(program);

  glUniformMatrix4fv(ProjMatrix_location,1,GL_FALSE,ProjMatrix.data());

  // Set up us the vertex data:
  glGenBuffers(2,buffers);

  glBindBuffer(GL_ARRAY_BUFFER,buffers[0]);

  glBufferData(GL_ARRAY_BUFFER,sizeof(float) * 3 * 2 * npoints,0,GL_STATIC_DRAW);

  float * geometry = (float *)glMapBuffer(GL_ARRAY_BUFFER,GL_WRITE_ONLY);

  for(size_t i = 0;i < npoints;++i,geometry += 6) {
    geometry[0] = drand48() * 5.0 - 2.5;
    geometry[1] = drand48() * 5.0 - 2.5;
    geometry[2] = drand48() * 5.0 - 2.5;

    geometry[3] = 1.0;
    geometry[4] = 1.0;
    geometry[5] = 1.0;
  }

  glUnmapBuffer(GL_ARRAY_BUFFER);

  client_elements = (GLuint *)malloc(sizeof(GLuint) * npoints);
  for(size_t i = 0;i < npoints;++i) {
    client_elements[i] = i;
  }

  GLint first = 0;
  for(size_t i = 0;i < nbatch;++i) {
    batchfirst[i] = first;
    batchcount[i] = (i == (nbatch - 1)) ? (npoints % batchsize) : (batchsize);
    batchoffsets[i] = (GLvoid const*)((uint64_t)first * sizeof(GLuint));
    client_batchindices[i] = client_elements + first;
    first += batchsize;
  }

  glGenBuffers(1,&elements);

  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,elements);

  glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(GLuint) * npoints,0,GL_STATIC_DRAW);

  GLuint * pelements = (GLuint *)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER,GL_WRITE_ONLY);

  for(size_t i = 0;i < npoints;++i) {
    pelements[i] = i;
  }

  glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);

  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);

  glEnableVertexAttribArray(vertex_location);
  glVertexAttribPointer(vertex_location,3,GL_FLOAT,GL_FALSE,sizeof(float) * 3 * 2,0);
  glPointSize(1);

  glBindBuffer(GL_ARRAY_BUFFER,0);

  glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER,buffers[1]);

  const size_t nfeedback = sizeof(float) * 4 * npoints;
  GLfloat * pfeedback = (GLfloat *)malloc(nfeedback);
  GLfloat * ppfeedback = pfeedback;

  for(size_t i = 0;i < npoints;++i,ppfeedback += 4) {
    ppfeedback[0] = 3.0;
    ppfeedback[1] = 5.0;
    ppfeedback[2] = 7.0;
    ppfeedback[3] = (float)i;
  }

  glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER,nfeedback,pfeedback,GL_STATIC_DRAW);
  free(pfeedback);

  glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER,0,buffers[1],0,nfeedback);
}

extern "C"
int
rsxgltest_draw()
{
  static int frame = 0;

  float rgb[3] = {
    compute_sine_wave(rgb_waves,rsxgltest_elapsed_time),
    compute_sine_wave(rgb_waves + 1,rsxgltest_elapsed_time),
    compute_sine_wave(rgb_waves + 2,rsxgltest_elapsed_time)
  };

  glClearColor(0,0,0,1.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  float xyz[3] = {
    compute_sine_wave(xyz_waves,rsxgltest_elapsed_time),
    compute_sine_wave(xyz_waves + 1,rsxgltest_elapsed_time),
    compute_sine_wave(xyz_waves + 2,rsxgltest_elapsed_time)
  };

  Eigen::Affine3f rotmat =
    Eigen::Affine3f::Identity() *
    Eigen::AngleAxisf(DTOR(xyz[2]) * 360.0f,Eigen::Vector3f::UnitZ()) *
    Eigen::AngleAxisf(DTOR(xyz[1]) * 360.0f,Eigen::Vector3f::UnitY()) *
    Eigen::AngleAxisf(DTOR(xyz[0]) * 360.0f,Eigen::Vector3f::UnitX());

  glPointSize(4.0);

  {
    Eigen::Affine3f modelview = ViewMatrixInv * (Eigen::Affine3f::Identity() * Eigen::Translation3f(0,0,0) * rotmat);
    glUniformMatrix4fv(TransMatrix_location,1,GL_FALSE,modelview.data());

    //int which = frame % 6;
    int which = 0;

    if(which == 0) {
      report_glerror("Before transform feedback");

      glBeginTransformFeedback(GL_POINTS);
      report_glerror("glBeginTransformFeedback");

      glDrawArrays(GL_POINTS,0,npoints);
      report_glerror("glDrawArrays");

      glEndTransformFeedback();
      report_glerror("glEndTransformFeedback");

      // Dump the alleged feedback buffer:
      GLfloat * pfeedback = (GLfloat *)glMapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER,GL_READ_ONLY);

      if(pfeedback) {
	for(size_t i = 0;i < npoints;++i,pfeedback += 4) {
	  tcp_printf("%04i: %.3f, %.3f, %.3f, %.3f\n",i,pfeedback[0],pfeedback[1],pfeedback[2],pfeedback[3]);
	}
      }

      glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
    }
    else if(which == 1) {
      glMultiDrawArrays(GL_POINTS,batchfirst,batchcount,nbatch);
    }
    else if(which == 2) {
      glDrawElements(GL_POINTS,npoints,GL_UNSIGNED_INT,client_elements);
    }
    else if(which == 3) {
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,elements);
      glDrawElements(GL_POINTS,npoints,GL_UNSIGNED_INT,0);
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
    }
    else if(which == 4) {
      glMultiDrawElements(GL_POINTS,batchcount,GL_UNSIGNED_INT,client_batchindices,nbatch);
    }
    else if(which == 5) {
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,elements);
      glMultiDrawElements(GL_POINTS,batchcount,GL_UNSIGNED_INT,batchoffsets,nbatch);
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
    }
  }

  ++frame;

  return 1;
}

extern "C"
void
rsxgltest_exit()
{
  glDeleteShader(shaders[0]);
  glDeleteProgram(program);
  glDeleteShader(shaders[1]);
}


#include <limits>
#include <random>
#include <boost/test/unit_test.hpp>
#include "algorithms/data_structures/array/fast_sum_of_range.hpp"

BOOST_AUTO_TEST_SUITE(TestFastSumOfRanges)

BOOST_AUTO_TEST_CASE(invalid_args)
{
    {
        Algo::DS::Array::NumArray array({});
        BOOST_CHECK(0 == array.sumRange(1, 2));
        BOOST_CHECK(0 == array.sumRange(0, 0));
    }

    {
        Algo::DS::Array::NumArray array({1, 2, 3});
        BOOST_CHECK(0 == array.sumRange(-1, -2));
        BOOST_CHECK(0 == array.sumRange(-1, 0));
        BOOST_CHECK(0 == array.sumRange(3, 1));
        BOOST_CHECK(0 == array.sumRange(3, 4));
    }
}

BOOST_AUTO_TEST_CASE(rand_tests) {
    std::random_device rd;
    std::mt19937 gen(rd());

    std::uniform_int_distribution<size_t> sizeDist(1, 1000000);

    std::uniform_int_distribution<int> valuesDist(
                std::numeric_limits<int>::min(),
                std::numeric_limits<int>::max());

    for (size_t i = 0; i < 10; ++i) {
        std::vector<int> container(sizeDist(gen), 0);
        for (size_t j = 0; j < container.size(); ++j) {
            container[j] = valuesDist(gen);
        }

        Algo::DS::Array::NumArray array(container);

        std::uniform_int_distribution<int> indexesDist(
                    0, static_cast<int>(container.size() - 1));

        for (size_t j = 0; j < 100; ++j) {
            int iInd = indexesDist(gen);

            std::uniform_int_distribution<int> secondIndexDist(
                        iInd, static_cast<int>(container.size() - 1));

            int jInd = secondIndexDist(gen);

            int expected = 0;
            for (size_t k = static_cast<size_t>(iInd);
                 k <= static_cast<size_t>(jInd);
                 ++k)
            {
                expected += container[k];
            }

            BOOST_CHECK(expected == array.sumRange(iInd, jInd));
        }
    }
}

BOOST_AUTO_TEST_SUITE_END()


// Copyright (c) 2019 Graphcore Ltd. All rights reserved.
#define BOOST_TEST_MODULE PipelineQuadraticEpsilonTesto0

#include <../random_util.hpp>
#include <algorithm>
#include <boost/filesystem.hpp>
#include <boost/test/unit_test.hpp>
#include <map>
#include <tuple>
#include <vector>

#define protected public
#include <filereader.hpp>
#include <popart/builder.hpp>
#include <popart/dataflow.hpp>
#include <popart/devicemanager.hpp>
#include <popart/inputshapeinfo.hpp>
#include <popart/ndarraywrapper.hpp>
#include <popart/op/identity.hpp>
#include <popart/op/ipucopy.hpp>
#include <popart/op/l1.hpp>
#include <popart/op/restore.hpp>
#include <popart/op/stash.hpp>
#include <popart/sgd.hpp>
#include <popart/tensorinfo.hpp>
#include <popart/tensornames.hpp>
#include <popart/testdevice.hpp>
#undef protected

#include <popart/session.hpp>

// The tricky thing with testing continuous pipelining is that we don't
// have a baseline, as it is not exact SGD. But "theory predicts" that as
// learning rate -> 0, continuous pipelining approaches exact SGD.
//
// Model, initialized with weights uniform from [-1, +1], inputs uniform from
// [0,4].
//
//   input1              input2
//     |                   |
//    (Add) <- Weight     (Add) <- Weight
//     |                   |
//  (Sigmoid)           (Sigmoid)
//     |                   |
//    (Add) <- Weight     (Add) <- Weight
//     |                   |
//  (Sigmoid)           (Sigmoid)
//     |                   |
//    (Add) <- Weight     (Add) <- Weight
//     |                   |
//  (Sigmoid)           (Sigmoid)
//     \                   |
//      \                  |
//       \----------(Add)--|
//                    |
//                    |
//                    |
//                 finalOut
//                    |
//                  l1-loss
//
//
// Results:
//
// l-rate  | n-batches | SGD_delta | cont_delta | SGD_cont_error |
// 0.04      20          2.15029     2.14938      0.00147016
// 0.02      40          2.1499      2.14942      0.000760384
// 0.01      80          2.15091     2.15066      0.000388138
// 0.005     160           .             .        0.000195466
//
// where
//
// SGD_delta  =  |weights_start - weights_end|_1 for exact SGD
// cont_delta =  |weights_start - weights_end|_1 for continuous pipelining
// SGD_cont_error = |weights_end_cont - weights_end_SGD|_1
//
// Notice the linear scaling of the error with learning rate
// (quadratic per step)
//
// THIS TEST:
// confirm that the final line is reproduced, that is, when
// l-rate  = 0.005, n-batches = 160, the error is less than 0.0003.

BOOST_AUTO_TEST_CASE(QuadraticEpsilolTest0) {

  // useful for debugging : set this to true to get the final .dot Ir
  // description
  bool exportFinalDotFiles = false;

  // Store the initial and final weights from training
  class WeightPair {
  public:
    using Wts = std::vector<std::vector<float>>;
    WeightPair(Wts s, Wts e) : start(s), end(e) {}
    Wts start;
    Wts end;
  };

  using namespace popart;

  bool printStdOut = true;

  // Return {initial weights, updated weights}
  auto getResults = [exportFinalDotFiles](bool continuous) {
    // input stream samples weights are generated randomly
    int seed = 1011;
    DefaultRandomEngine eng(seed);
    UniformRealDistribution<float> fdis(-1.f, +1.f);

    int64_t batchSize      = 4;
    int64_t batchesPerStep = 160;
    int64_t sampleHeight   = 2;
    int64_t samplesPerStep = batchesPerStep * batchSize;
    std::vector<int64_t> sampleShape{sampleHeight, sampleHeight};
    std::vector<int64_t> weightShape = sampleShape;
    std::vector<int64_t> batchShape{batchSize, sampleHeight, sampleHeight};
    std::vector<int64_t> stepDataShape{
        batchesPerStep, batchSize, sampleHeight, sampleHeight};
    TensorInfo sampleInfo{"FLOAT", sampleShape};
    TensorInfo weightInfo = sampleInfo;
    TensorInfo batchInfo{"FLOAT", batchShape};
    TensorInfo stepDataInfo{"FLOAT", stepDataShape};
    int64_t sampleElms{sampleHeight * sampleHeight};
    int64_t batchElms    = sampleElms * batchSize;
    int64_t stepDataElms = batchElms * batchesPerStep;

    // The model: see above

    // number of Adds on each of the two branches.
    int nLayers = 3;

    auto builder     = Builder::create();
    auto aiOnnx      = builder->aiOnnxOpset9();
    auto aiGraphcore = builder->aiGraphcoreOpset1();
    auto input0      = builder->addInputTensor(batchInfo, "0tupni");
    auto input1      = builder->addInputTensor(batchInfo, "1tupni");

    // Storage for all layers
    std::vector<std::vector<float>> allWeights;
    std::vector<ConstVoidData> allWeightCvds;
    std::vector<TensorId> allWeightIds;
    std::vector<TensorId> allSigActIds;
    std::vector<std::vector<float>> w_readbacks;
    WeightsIO weightsRead;

    int nLayersAdded    = 0;
    auto insertAddLayer = [&allWeights,
                           &allWeightCvds,
                           &allWeightIds,
                           &allSigActIds,
                           sampleElms,
                           &weightsRead,
                           weightInfo,
                           sampleInfo,
                           &fdis,
                           &eng,
                           &builder,
                           &aiOnnx,
                           &nLayersAdded,
                           &w_readbacks,
                           exportFinalDotFiles](TensorId actInId) {
      w_readbacks.push_back(std::vector<float>(sampleElms, -99.0f));
      allWeights.push_back(std::vector<float>(sampleElms, 0));
      for (auto &x : allWeights.back()) {
        x = fdis(eng);
      }
      allWeightCvds.push_back({allWeights.back().data(), sampleInfo});
      allWeightIds.push_back(
          builder->addInitializedInputTensor(allWeightCvds.back()));
      TensorId addActOutId = "addAct" + std::to_string(nLayersAdded);
      TensorId sigActOutId = "sigAct" + std::to_string(nLayersAdded);
      auto addOut = aiOnnx.add({allWeightIds.back(), actInId}, addActOutId);
      auto sigOut = aiOnnx.sigmoid({addOut}, sigActOutId);
      allSigActIds.push_back(sigOut);

      weightsRead.insert(allWeightIds.back(),
                         {w_readbacks.back().data(), weightInfo});
      ++nLayersAdded;
    };

    // left branch (branch 0)
    insertAddLayer(input0);
    for (int i = 1; i < nLayers; ++i) {
      insertAddLayer(allSigActIds.back());
    }
    TensorId actFinal0 = allSigActIds.back();

    // right branch (branch 1)
    insertAddLayer(input1);
    for (int i = 1; i < nLayers; ++i) {
      insertAddLayer(allSigActIds.back());
    }
    TensorId actFinal1 = allSigActIds.back();

    // sum of the 2 branch outputs
    auto actFinal = aiOnnx.add({actFinal0, actFinal1}, "finalAct");

    float lambda  = 0.1;
    auto l1       = builder->aiGraphcoreOpset1().l1loss({actFinal}, lambda);
    auto proto    = builder->getModelProto();
    auto dataFlow = DataFlow(batchesPerStep);

    float learnRate = 0.005;
    auto optimizer  = ConstSGD(learnRate);

    SessionOptions userOptions;
    unsigned numIpus = 1;

    if (exportFinalDotFiles) {
      userOptions.dotChecks.insert("Final");
      userOptions.logDir = "./dotTestContinuous_" + std::to_string(continuous);
      boost::filesystem::create_directories(userOptions.logDir);
    }

    if (continuous == true) {
      userOptions.virtualGraphMode = VirtualGraphMode::Auto;
      userOptions.enablePipelining = true;
      numIpus                      = 3;
    }

    auto device = createTestDevice(TEST_TARGET, numIpus);

    auto session = popart::TrainingSession::createFromOnnxModel(
        proto,
        dataFlow,
        l1,
        optimizer,
        device,
        InputShapeInfo(),
        userOptions,
        popart::Patterns(PatternsLevel::Default));

    session->prepareDevice();

    if (continuous) {
      auto opSchedule =
          session->getIr().getOpSchedule({}, RequireOptimalSchedule::Yes);
      for (auto op : opSchedule) {
        auto stashOp = dynamic_cast<StashOp *>(op);
        if (stashOp) {
          BOOST_CHECK(stashOp->scheduledPreLoss == ScheduledPreLoss::Yes);
        }

        auto restoreOp = dynamic_cast<RestoreOp *>(op);
        if (restoreOp) {
          // we don't check that it is scheduled post-loss, as it is the backend
          // that controls where it is scheduled
        }
      }
    }

    // The samples (same for 0 and 1)
    std::vector<float> v_input_x(stepDataElms);

    // cumulative samples (accumulated over multiple steps).
    std::map<popart::TensorId, popart::IArray &> anchors = {};

    // write initial weights to host
    session->weightsFromHost();

    for (int i = 0; i < 3; ++i) {
      std::cout << "Iteration (call to run(...)) # " << i << std::endl;

      // generate new samples
      for (int i = 0; i < samplesPerStep; ++i) {
        for (int j = 0; j < sampleElms; ++j) {
          auto stepIndex       = i * sampleElms + j;
          v_input_x[stepIndex] = 1 + 2 * fdis(eng); // in range [0, 4]
        }
      }
      popart::NDArrayWrapper<float> input_x_wrapper(v_input_x.data(),
                                                    stepDataInfo);
      std::map<popart::TensorId, popart::IArray &> inputs = {
          {input1, input_x_wrapper}, {input0, input_x_wrapper}};
      popart::StepIO stepio(inputs, anchors);

      // process the samples
      session->run(stepio);
    }

    // read final weights back
    session->weightsToHost();
    session->readWeights(weightsRead);
    return WeightPair(allWeights, w_readbacks);
  };

  auto continuous = getResults(true);
  auto exact      = getResults(false);

  auto printResults = [](const WeightPair &wp) {
    auto nws = wp.start.size();
    for (auto layer = 0; layer < nws; ++layer) {
      const auto &baseline = wp.start[layer];
      std::cout << "layer " << layer << std::endl;
      for (int i = 0; i < baseline.size(); ++i) {
        std::cout << wp.start[layer][i] << " --> " << wp.end[layer][i]
                  << std::endl;
      }
    }
  };

  auto getDelta = [](const std::vector<std::vector<float>> &a,
                     const std::vector<std::vector<float>> &b) {
    float delta = 0;
    for (int layer = 0; layer < a.size(); ++layer) {
      for (int i = 0; i < a[layer].size(); ++i) {
        delta += std::abs(a[layer][i] - b[layer][i]);
      }
    }
    return delta;
  };

  if (printStdOut) {
    std::cout << "Exact: " << std::endl;
    printResults(exact);
    std::cout << "Continuous: " << std::endl;
    printResults(continuous);
  }

  auto delta_exact      = getDelta(exact.start, exact.end);
  auto delta_continuous = getDelta(continuous.start, continuous.end);
  auto delta_starts     = getDelta(continuous.start, exact.start);
  auto delta_ends       = getDelta(continuous.end, exact.end);

  // Obtained on 14 May 2020 on IPUModel:
  // --------------------------------------
  // delta exact 2.11625
  // delta continuous 2.1159
  // delta starts 0
  // delta ends 0.000577103

  if (printStdOut) {
    std::cout << "delta exact " << delta_exact << std::endl;
    std::cout << "delta continuous " << delta_continuous << std::endl;
    std::cout << "delta starts " << delta_starts << std::endl;
    std::cout << "delta ends " << delta_ends << std::endl;
  }

  BOOST_CHECK(delta_ends < 0.0006);
}


/*
 Copyright (C) 2016 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

#include "toplevelfixture.hpp"
#include "utilities.hpp"
#include <boost/test/unit_test.hpp>

#include <qle/termstructures/dynamicswaptionvolmatrix.hpp>

#include <ql/quotes/simplequote.hpp>
#include <ql/termstructures/volatility/swaption/swaptionvolmatrix.hpp>
#include <ql/time/calendars/target.hpp>

#include <ql/time/daycounters/actual365fixed.hpp>

using namespace QuantExt;
using namespace QuantLib;
using namespace boost::unit_test_framework;

namespace {

struct TestData {

    TestData() : origRefDate(20, Jan, 2016), atmVols(2, 2) {

        Settings::instance().evaluationDate() = origRefDate;

        optionTenors.push_back(3 * Months);
        optionTenors.push_back(5 * Years);
        swapTenors.push_back(1 * Years);
        swapTenors.push_back(2 * Years);
        //    sw 1Y                   sw 2Y
        atmVols[0][0] = 0.0050;
        atmVols[0][1] = 0.0060; // opt 3m
        atmVols[1][0] = 0.0100;
        atmVols[1][1] = 0.0160; // opt 2y

        // atm surface
        atmSurface = boost::make_shared<SwaptionVolatilityMatrix>(origRefDate, TARGET(), Following, optionTenors,
                                                                  swapTenors, atmVols, Actual365Fixed(), false, Normal);
    }

    SavedSettings backup;
    const Date origRefDate;
    boost::shared_ptr<SwaptionVolatilityStructure> atmSurface;
    std::vector<Period> optionTenors, swapTenors;
    Matrix atmVols;
};

} // anonymous namespace

BOOST_FIXTURE_TEST_SUITE(QuantExtTestSuite, qle::test::TopLevelFixture)

BOOST_AUTO_TEST_SUITE(DynamicSwaptionVolMatrixTest)

BOOST_AUTO_TEST_CASE(testConstantVariance) {

    BOOST_TEST_MESSAGE("Testing constant variance dynamics of "
                       "DynamicSwaptionVolatilityMatrix...");

    TestData d;

    boost::shared_ptr<SwaptionVolatilityStructure> dyn =
        boost::make_shared<DynamicSwaptionVolatilityMatrix>(d.atmSurface, 0, TARGET(), ConstantVariance);

    dyn->enableExtrapolation();

    Real tol = 1.0E-8;

    Real strike = 0.05; // dummy strike

    // initially we should get the same volatilities

    BOOST_CHECK_CLOSE(dyn->volatility(1 * Months, 1 * Years, strike),
                      d.atmSurface->volatility(1 * Months, 1 * Years, strike), tol);
    BOOST_CHECK_CLOSE(dyn->volatility(1 * Months, 2 * Years, strike),
                      d.atmSurface->volatility(1 * Months, 2 * Years, strike), tol);
    BOOST_CHECK_CLOSE(dyn->volatility(1 * Years, 1 * Years, strike),
                      d.atmSurface->volatility(1 * Years, 1 * Years, strike), tol);
    BOOST_CHECK_CLOSE(dyn->volatility(1 * Years, 2 * Years, strike),
                      d.atmSurface->volatility(1 * Years, 2 * Years, strike), tol);

    // move forward in time, we expect a constant surface

    Settings::instance().evaluationDate() = TARGET().advance(d.origRefDate, 5 * Months);

    BOOST_CHECK_CLOSE(dyn->volatility(0.1, 1.0, strike), d.atmSurface->volatility(0.1, 1.0, strike), tol);
    BOOST_CHECK_CLOSE(dyn->volatility(0.1, 2.0, strike), d.atmSurface->volatility(0.1, 2.0, strike), tol);
    BOOST_CHECK_CLOSE(dyn->volatility(1.0, 1.0, strike), d.atmSurface->volatility(1.0, 1.0, strike), tol);
    BOOST_CHECK_CLOSE(dyn->volatility(1.0, 2.0, strike), d.atmSurface->volatility(1.0, 2.0, strike), tol);

} // testConstantVariance

BOOST_AUTO_TEST_CASE(testForwardForwardVariance) {

    BOOST_TEST_MESSAGE("Testing forward forward variance dynamics of "
                       "DynamicSwaptionVolatilityMatrix");
    TestData d;

    boost::shared_ptr<SwaptionVolatilityStructure> dyn =
        boost::make_shared<DynamicSwaptionVolatilityMatrix>(d.atmSurface, 0, TARGET(), ForwardForwardVariance);

    dyn->enableExtrapolation();

    Real tol = 1.0E-8;

    Real strike = 0.05; // dummy strike

    // initially we should get the same volatilities again

    BOOST_CHECK_CLOSE(dyn->volatility(1 * Months, 1 * Years, strike),
                      d.atmSurface->volatility(1 * Months, 1 * Years, strike), tol);
    BOOST_CHECK_CLOSE(dyn->volatility(1 * Months, 2 * Years, strike),
                      d.atmSurface->volatility(1 * Months, 2 * Years, strike), tol);
    BOOST_CHECK_CLOSE(dyn->volatility(1 * Years, 1 * Years, strike),
                      d.atmSurface->volatility(1 * Years, 1 * Years, strike), tol);
    BOOST_CHECK_CLOSE(dyn->volatility(1 * Years, 2 * Years, strike),
                      d.atmSurface->volatility(1 * Years, 2 * Years, strike), tol);

    // move forward in time, we expect the forward forward variance

    Settings::instance().evaluationDate() = TARGET().advance(d.origRefDate, 5 * Months);
    Real tf = d.atmSurface->timeFromReference(Settings::instance().evaluationDate());

    BOOST_CHECK_CLOSE(dyn->blackVariance(0.1, 1.0, strike),
                      d.atmSurface->blackVariance(tf + 0.1, 1.0, strike) - d.atmSurface->blackVariance(tf, 1.0, strike),
                      tol);
    BOOST_CHECK_CLOSE(dyn->blackVariance(0.1, 2.0, strike),
                      d.atmSurface->blackVariance(tf + 0.1, 2.0, strike) - d.atmSurface->blackVariance(tf, 2.0, strike),
                      tol);
    BOOST_CHECK_CLOSE(dyn->blackVariance(1.0, 1.0, strike),
                      d.atmSurface->blackVariance(tf + 1.0, 1.0, strike) - d.atmSurface->blackVariance(tf, 1.0, strike),
                      tol);
    BOOST_CHECK_CLOSE(dyn->blackVariance(1.0, 2.0, strike),
                      d.atmSurface->blackVariance(tf + 1.0, 2.0, strike) - d.atmSurface->blackVariance(tf, 2.0, strike),
                      tol);

} // testForwardForwardVariance

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()


// Copyright 2019 Hans Dembinski
//
// 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)

// clang-format off

//[ getting_started_listing_04

#include <algorithm>           // std::max_element
#include <boost/format.hpp>    // only needed for printing
#include <boost/histogram.hpp> // make_histogram, integer, indexed
#include <iostream>            // std::cout, std::endl
#include <sstream>             // std::ostringstream

int main() {
  using namespace boost::histogram;
  using namespace boost::histogram::literals;

  /*
    We make a 3d histogram for color values (r, g, b) in an image. We assume the values
    are of type char. The value range then is [0, 256).
  */
  auto h = make_histogram(
    axis::integer<>(0, 256, "r"),
    axis::integer<>(0, 256, "g"),
    axis::integer<>(0, 256, "b")
  );

  /*
    We don't have real image data, so fill some fake data.
  */
  h(1, 2, 3);
  h(1, 2, 3);
  h(0, 1, 0);

  /*
    Now let's say we want to know which color is most common. We can use std::max_element on an
    indexed range for that.
  */
  auto ind = indexed(h);
  auto max_it = std::max_element(ind.begin(), ind.end());

  /*
    max_it is the iterator to the maximum counter. This is the special iterator from indexed_range,
    you can use it to access the cell index as well and the bin values. You need to
    double-dereference it to get to the cell count.
  */
  std::ostringstream os;
  os << boost::format("count=%i rgb=(%i, %i, %i)")
        % **max_it % max_it->bin(0_c) % max_it->bin(1) % max_it->bin(2);

  std::cout << os.str() << std::endl;

  assert(os.str() == "count=2 rgb=(1, 2, 3)");
}

//]


#ifndef slic3r_Line_hpp_
#define slic3r_Line_hpp_

#include <myinit.h>
#include "Point.hpp"
#include <boost/polygon/polygon.hpp>

namespace Slic3r {

class Line;
class Polyline;

class Line
{
    public:
    Point a;
    Point b;
    Line() {};
    explicit Line(Point _a, Point _b): a(_a), b(_b) {};
    std::string wkt() const;
    operator Polyline() const;
    void scale(double factor);
    void translate(double x, double y);
    void rotate(double angle, Point* center);
    void reverse();
    double length() const;
    Point* midpoint() const;
    void point_at(double distance, Point* point) const;
    Point point_at(double distance) const;
    bool coincides_with(const Line* line) const;
    double distance_to(const Point* point) const;
    double atan2_() const;
    double direction() const;
    Vector vector() const;

    #ifdef SLIC3RXS
    void from_SV(SV* line_sv);
    void from_SV_check(SV* line_sv);
    SV* to_AV();
    SV* to_SV_ref();
    SV* to_SV_clone_ref() const;
    SV* to_SV_pureperl() const;
    #endif
};

typedef std::vector<Line> Lines;

}

// start Boost
namespace boost { namespace polygon {
    template <>
    struct geometry_concept<Line> { typedef segment_concept type; };

    template <>
    struct segment_traits<Line> {
        typedef coord_t coordinate_type;
        typedef Point point_type;

        static inline point_type get(const Line& line, direction_1d dir) {
            return dir.to_int() ? line.b : line.a;
        }
    };
} }
// end Boost

#endif


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

// Copyright (c) 2010-2015 Barend Gehrels, Amsterdam, the Netherlands.

// This file was modified by Oracle on 2016.
// Modifications copyright (c) 2016, Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// 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)

#include <iostream>
#include <string>

#include "test_intersection.hpp"
#include <algorithms/test_overlay.hpp>
#include <algorithms/overlay/multi_overlay_cases.hpp>

#include <boost/geometry/algorithms/correct.hpp>
#include <boost/geometry/algorithms/intersection.hpp>

#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/multi_point.hpp>
#include <boost/geometry/geometries/multi_linestring.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>

#include <boost/geometry/io/wkt/read.hpp>

#define TEST_INTERSECTION(caseid, clips, points, area) \
    (test_one<Polygon, MultiPolygon, MultiPolygon>) \
    ( #caseid, caseid[0], caseid[1], clips, points, area)

#define TEST_INTERSECTION_IGNORE(caseid, clips, points, area) \
    (test_one<Polygon, MultiPolygon, MultiPolygon>) \
    ( #caseid, caseid[0], caseid[1], clips, points, area, ignore_validity)

template <typename Ring, typename Polygon, typename MultiPolygon>
void test_areal()
{
    ut_settings ignore_validity;
    ignore_validity.test_validity = false;

    test_one<Polygon, MultiPolygon, MultiPolygon>("simplex_multi",
        case_multi_simplex[0], case_multi_simplex[1],
        2, 12, 6.42);

    test_one<Polygon, MultiPolygon, MultiPolygon>("case_multi_no_ip",
        case_multi_no_ip[0], case_multi_no_ip[1],
        2, 8, 8.5);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_multi_2",
        case_multi_2[0], case_multi_2[1],
        3, 12, 5.9);

    test_one<Polygon, MultiPolygon, Polygon>("simplex_multi_mp_p",
        case_multi_simplex[0], case_single_simplex,
        2, 12, 6.42);

    test_one<Polygon, Ring, MultiPolygon>("simplex_multi_r_mp",
        case_single_simplex, case_multi_simplex[0],
        2, 12, 6.42);
    test_one<Ring, MultiPolygon, Polygon>("simplex_multi_mp_r",
        case_multi_simplex[0], case_single_simplex,
        2, 12, 6.42);

    test_one<Polygon, MultiPolygon, MultiPolygon>("case_58_multi_a",
        case_58_multi[0], case_58_multi[3],
        3, 12, 0.666666667);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_58_multi_b",
        case_58_multi[1], case_58_multi[2],
        1, 19, 11.16666666667);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_58_multi_b4",
        case_58_multi[4], case_58_multi[2],
        1, 13, 12.66666666);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_58_multi_b5",
        case_58_multi[5], case_58_multi[2],
        1, 13, 13.25);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_58_multi_b6",
        case_58_multi[6], case_58_multi[2],
        1, 13, 13.25);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_58_multi_b7",
        case_58_multi[7], case_58_multi[2],
        1, 16, 12.5);

    // Constructed cases for multi/touch/equal/etc
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_61_multi",
        case_61_multi[0], case_61_multi[1],
        0, 0, 0.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_62_multi",
        case_62_multi[0], case_62_multi[1],
        1, 5, 1.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_63_multi",
        case_63_multi[0], case_63_multi[1],
        1, 5, 1.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_64_multi",
        case_64_multi[0], case_64_multi[1],
        1, 5, 1.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_65_multi",
        case_65_multi[0], case_65_multi[1],
        1, 5, 1.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_65_multi_inv_a",
        case_65_multi[0], case_65_multi[3],
        0, 0, 0.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_65_multi_inv_b",
        case_65_multi[1], case_65_multi[2],
        2, 10, 3.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_72_multi",
        case_72_multi[0], case_72_multi[1],
        3, 14, 2.85);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_72_multi_inv_b",
        case_72_multi[1], case_72_multi[2],
        3, 16, 6.15);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_77_multi",
        case_77_multi[0], case_77_multi[1],
        5, 33, 9.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_78_multi",
        case_78_multi[0], case_78_multi[1],
        1, 16, 22.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_101_multi",
        case_101_multi[0], case_101_multi[1],
        4, 22, 4.75);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_102_multi",
        case_102_multi[0], case_102_multi[1],
        3, 26, 19.75);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_102_multi_inv_b",
        case_102_multi[1], case_102_multi[2],
        3, 25, 3.75, ignore_validity);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_107_multi",
        case_107_multi[0], case_107_multi[1],
        2, 10, 1.5);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_107_multi_inv_b",
        case_107_multi[1], case_107_multi[2],
        3, 13, 3.0);

#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS
    // One intersection is missing (by rescaling)
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_108_multi",
        case_108_multi[0], case_108_multi[1],
        5, 33, 7.5,
        ignore_validity);
#endif

    TEST_INTERSECTION(case_123_multi, 3, 13, 1.875);
    TEST_INTERSECTION(case_124_multi, 2, 13, 2.0625);
    TEST_INTERSECTION_IGNORE(case_125_multi, 3, 17, 2.1);

    // #1 needs self-turns to make valid
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_1",
        case_recursive_boxes_1[0], case_recursive_boxes_1[1],
        8, 97, 47.0, ignore_validity);

    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_2",
        case_recursive_boxes_2[0], case_recursive_boxes_2[1],
        1, 50, 90.0); // Area from SQL Server
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_3",
        case_recursive_boxes_3[0], case_recursive_boxes_3[1],
        19, 87, 12.5); // Area from SQL Server

    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_4",
        case_recursive_boxes_4[0], case_recursive_boxes_4[1],
        8, 179, 67.0, // Area from SQL Server
        ignore_validity);

    // Fixed by replacing handle_tangencies in less_by_segment_ratio sort order
    // Should contain 6 output polygons
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_6",
        case_recursive_boxes_6[0], case_recursive_boxes_6[1],
        6, 47, 19.0);

    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_7",
        case_recursive_boxes_7[0], case_recursive_boxes_7[1],
        2, 9, 1.5);

    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_8",
        case_recursive_boxes_8[0], case_recursive_boxes_8[1],
        3, 19, 3.75);

    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_9",
        case_recursive_boxes_9[0], case_recursive_boxes_9[1],
        5, 27, 4.25);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_10",
        case_recursive_boxes_10[0], case_recursive_boxes_10[1],
        2, 8, 0.75);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_11",
        case_recursive_boxes_11[0], case_recursive_boxes_11[1],
        2, 8, 1.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_12",
        case_recursive_boxes_12[0], case_recursive_boxes_12[1],
        1, 4, 0.5);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_13",
        case_recursive_boxes_13[0], case_recursive_boxes_13[1],
        0, 0, 0.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_14",
        case_recursive_boxes_14[0], case_recursive_boxes_14[1],
        0, 0, 0.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_15",
        case_recursive_boxes_15[0], case_recursive_boxes_15[1],
        1, 4, 0.5);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_16",
        case_recursive_boxes_16[0], case_recursive_boxes_16[1],
        9, 43, 10.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_17",
        case_recursive_boxes_17[0], case_recursive_boxes_17[1],
        7, -1, 7.75);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_18",
        case_recursive_boxes_18[0], case_recursive_boxes_18[1],
        0, 0, 0.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_19",
        case_recursive_boxes_19[0], case_recursive_boxes_19[1],
        0, 0, 0.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_20",
        case_recursive_boxes_20[0], case_recursive_boxes_20[1],
        2, 0, 1.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_21",
        case_recursive_boxes_21[0], case_recursive_boxes_21[1],
        1, 0, 0.5);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_22",
        case_recursive_boxes_22[0], case_recursive_boxes_22[1],
        0, 0, 0.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_23",
        case_recursive_boxes_23[0], case_recursive_boxes_23[1],
        1, 0, 0.5);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_24",
        case_recursive_boxes_24[0], case_recursive_boxes_24[1],
        1, 0, 0.5);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_25",
        case_recursive_boxes_25[0], case_recursive_boxes_25[1],
        1, 0, 0.5);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_26",
        case_recursive_boxes_26[0], case_recursive_boxes_26[1],
        1, 0, 2.5);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_27",
        case_recursive_boxes_27[0], case_recursive_boxes_27[1],
        1, 0, 0.5);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_28",
        case_recursive_boxes_28[0], case_recursive_boxes_28[1],
        2, 0, 1.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_29",
        case_recursive_boxes_29[0], case_recursive_boxes_29[1],
        5, 0, 3.75);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_30",
        case_recursive_boxes_30[0], case_recursive_boxes_30[1],
        4, 0, 6.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_31",
        case_recursive_boxes_31[0], case_recursive_boxes_31[1],
        2, 0, 2.5);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_32",
        case_recursive_boxes_32[0], case_recursive_boxes_32[1],
        2, 0, 1.75);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_33",
        case_recursive_boxes_33[0], case_recursive_boxes_33[1],
        3, 0, 2.0);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_34",
        case_recursive_boxes_34[0], case_recursive_boxes_34[1],
        2, 0, 17.25);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_35",
        case_recursive_boxes_35[0], case_recursive_boxes_35[1],
        1, 0, 20.0,
        ignore_validity);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_36",
        case_recursive_boxes_36[0], case_recursive_boxes_36[1],
        1, 0, 0.5);
    test_one<Polygon, MultiPolygon, MultiPolygon>("case_recursive_boxes_37",
        case_recursive_boxes_37[0], case_recursive_boxes_37[1],
        2, 0, 1.0);

    TEST_INTERSECTION(case_recursive_boxes_39, 3, 0, 3.00);
    TEST_INTERSECTION(case_recursive_boxes_40, 1, 0, 1.00);

    test_one<Polygon, MultiPolygon, MultiPolygon>("ggl_list_20120915_h2_a",
        ggl_list_20120915_h2[0], ggl_list_20120915_h2[1],
        2, 10, 6.0); // Area from SQL Server
    test_one<Polygon, MultiPolygon, MultiPolygon>("ggl_list_20120915_h2_b",
        ggl_list_20120915_h2[0], ggl_list_20120915_h2[2],
        2, 10, 6.0); // Area from SQL Server

    test_one<Polygon, MultiPolygon, MultiPolygon>("ticket_9081",
        ticket_9081[0], ticket_9081[1],
        2, 10, 0.0019812556);

    // qcc-arm reports 1.7791215549400884e-14
    test_one<Polygon, MultiPolygon, MultiPolygon>("ticket_11018",
        ticket_11018[0], ticket_11018[1],
        1, 4,
#ifdef BOOST_GEOMETRY_NO_ROBUSTNESS
        9.896437631745599e-09
#else
        1.7791170511070893e-14, ut_settings(0.001)
#endif

    );

    test_one<Polygon, MultiPolygon, MultiPolygon>("mysql_23023665_7",
        mysql_23023665_7[0], mysql_23023665_7[1],
        2, 11, 9.80505786783);

    test_one<Polygon, MultiPolygon, MultiPolygon>("mysql_23023665_12",
        mysql_23023665_12[0], mysql_23023665_12[1],
        1, -1, 11.812440191387557,
        ignore_validity);
}

template <typename Polygon, typename MultiPolygon, typename Box>
void test_areal_clip()
{
    static std::string const clip = "POLYGON((1 1,4 4))";
    test_one<Polygon, Box, MultiPolygon>("simplex_multi_mp_b", clip, case_multi_simplex[0],
        2, 11, 6.791666);
    test_one<Polygon, MultiPolygon, Box>("simplex_multi_b_mp", case_multi_simplex[0], clip,
        2, 11, 6.791666);
}

template <typename LineString, typename MultiLineString, typename Box>
void test_linear()
{
    typedef typename bg::point_type<MultiLineString>::type point;
    test_one<point, MultiLineString, MultiLineString>("case_multi_ml_ml_1",
        "MULTILINESTRING((0 0,1 1))", "MULTILINESTRING((0 1,1 0))",
        1, 1, 0.0);
    test_one<point, MultiLineString, MultiLineString>("case_multi_ml_ml_2",
        "MULTILINESTRING((0 0,1 1),(0.5 0,1.5 1))", "MULTILINESTRING((0 1,1 0),(0.5 1,1.5 0))",
        4, 4, 0.0);

    test_one<point, LineString, MultiLineString>("case_multi_l_ml",
        "LINESTRING(0 0,1 1)", "MULTILINESTRING((0 1,1 0),(0.5 1,1.5 0))",
        2, 2, 0.0);
    test_one<point, MultiLineString, LineString>("case_multi_ml_l",
        "MULTILINESTRING((0 1,1 0),(0.5 1,1.5 0))", "LINESTRING(0 0,1 1)",
        2, 2, 0.0);

    test_one<LineString, MultiLineString, Box>("case_multi_ml_b",
        "MULTILINESTRING((0 0,3 3)(1 0,4 3))", "POLYGON((1 1,3 2))",
        2, 4, 2.0 * std::sqrt(2.0));
    test_one<LineString, Box, MultiLineString>("case_multi_b_ml",
        "POLYGON((1 1,3 2))", "MULTILINESTRING((0 0,3 3)(1 0,4 3))",
        2, 4, 2.0 * std::sqrt(2.0));
}

template <typename P>
void test_point_output()
{
    typedef bg::model::box<P> box;
    typedef bg::model::linestring<P> linestring;
    typedef bg::model::polygon<P> polygon;
    typedef bg::model::multi_polygon<polygon> multi_polygon;

    test_point_output<multi_polygon, multi_polygon>(case_multi_simplex[0], case_multi_simplex[1], 10);
    test_point_output<linestring, multi_polygon>("linestring(4 0,0 4)", case_multi_simplex[0], 4);
    test_point_output<box, multi_polygon>("box(3 0,4 6)", case_multi_simplex[0], 8);
}

template <typename MultiPolygon, typename MultiLineString>
void test_areal_linear()
{
    typedef typename boost::range_value<MultiPolygon>::type Polygon;
    typedef typename boost::range_value<MultiLineString>::type LineString;
    typedef typename bg::point_type<Polygon>::type Point;
    typedef bg::model::ring<Point> Ring;

    test_one_lp<LineString, MultiPolygon, LineString>("case_mp_ls_1", case_multi_simplex[0], "LINESTRING(2 0,2 5)", 2, 4, 3.70);
    test_one_lp<LineString, Polygon, MultiLineString>("case_p_mls_1", case_single_simplex, "MULTILINESTRING((2 0,2 5),(3 0,3 5))", 2, 4, 7.5);
    test_one_lp<LineString, MultiPolygon, MultiLineString>("case_mp_mls_1", case_multi_simplex[0], "MULTILINESTRING((2 0,2 5),(3 0,3 5))", 4, 8, 6.8333333);
    test_one_lp<LineString, Ring, MultiLineString>("case_r_mls_1", case_single_simplex, "MULTILINESTRING((2 0,2 5),(3 0,3 5))", 2, 4, 7.5);
}

template <typename P>
void test_all()
{
    typedef bg::model::ring<P> ring;
    typedef bg::model::polygon<P> polygon;
    typedef bg::model::multi_polygon<polygon> multi_polygon;
    test_areal<ring, polygon, multi_polygon>();

#if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE)

    typedef bg::model::ring<P, false> ring_ccw;
    typedef bg::model::polygon<P, false> polygon_ccw;
    typedef bg::model::multi_polygon<polygon_ccw> multi_polygon_ccw;
    test_areal<ring_ccw, polygon_ccw, multi_polygon_ccw>();

    typedef bg::model::ring<P, true, false> ring_open;
    typedef bg::model::polygon<P, true, false> polygon_open;
    typedef bg::model::multi_polygon<polygon_open> multi_polygon_open;
    test_areal<ring_open, polygon_open, multi_polygon_open>();

    typedef bg::model::ring<P, false, false> ring_open_ccw;
    typedef bg::model::polygon<P, false, false> polygon_open_ccw;
    typedef bg::model::multi_polygon<polygon_open_ccw> multi_polygon_open_ccw;
    test_areal<ring_open_ccw, polygon_open_ccw, multi_polygon_open_ccw>();

    typedef bg::model::box<P> box;
    test_areal_clip<polygon, multi_polygon, box>();
    test_areal_clip<polygon_ccw, multi_polygon_ccw, box>();

    typedef bg::model::linestring<P> linestring;
    typedef bg::model::multi_linestring<linestring> multi_linestring;

    test_linear<linestring, multi_linestring, box>();
    test_areal_linear<multi_polygon, multi_linestring>();
#endif

    test_point_output<P>();
    // linear

}

int test_main(int, char* [])
{
    test_all<bg::model::d2::point_xy<double> >();

#if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE)
    test_all<bg::model::d2::point_xy<float> >();

#if defined(HAVE_TTMATH)
    std::cout << "Testing TTMATH" << std::endl;
    test_all<bg::model::d2::point_xy<ttmath_big> >();
#endif

#endif

    return 0;
}


#ifndef CX_SOLVES_HPP
#define CX_SOLVES_HPP

#include "print.hpp"
//#include "cx_modify.hpp"
#include "sub.hpp"
#include "../tools.hpp"
// #include "cx_multiply.hpp"
// #include "cx_special.hpp"

#include <mkl.h>

#include <boost/timer/timer.hpp>
#include <boost/chrono.hpp>

#include <vector>
#include <utility>
#include <complex>

namespace dqmc {
    namespace la {
	inline void solve_qr_col_piv(cx_mat&__restrict__ A,
				     const cx_mat&__restrict__ B,
				     cx_mat&__restrict__ X) {
	    using namespace std;
	    using namespace Eigen;

	    cx_vec_t taus(A.cols());
	    ivec iwork(A.cols()); iwork.setZero();

	    int lwork = -1;
	    int M = A.rows();
	    int N = A.cols();

	    perm_mat row_sort_perm = cx_row_sort(A, X);
	    lapack_int info = LAPACKE_zgeqp3( LAPACK_COL_MAJOR, M, N, X.data(),
					      M, iwork.data(), taus.data() );
	    ivec col_ids(A.cols());
	    for (int i = 0; i < A.cols(); ++i) { col_ids(i) = iwork(i) - 1; }
	    Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> lapack_col_perm(col_ids);

	    cx_mat_t upper = X.triangularView<Upper>();
	    lwork = -1;

	    info = LAPACKE_zungqr( LAPACK_COL_MAJOR, M, N, N,
				   X.data(), M, taus.data() );
	    // cout << "Reconstructed matrix " << endl
	    // 	 << row_sort_perm.transpose() * X * upper * lapack_col_perm.transpose() << endl << endl;
	    // solving
	    A = X.adjoint() * row_sort_perm * B;
	    X = lapack_col_perm * upper.triangularView<Upper>().solve(A);
	}
    }
}

#endif


#include "modules/bio_base/align_multigene.h"
#include "modules/io/io.h"
#include <boost/multi_array.hpp>

static double match_score(const dna_base& b1, const dna_base& b2)
{
	if (b1 == b2) return 0.0;
	return 1.0;
}

static const double insert_score = 2.5;  // Read has 'insert' not in original
static const double delete_score = 2.5;  // Read skips over base in original

struct choice
{
	choice() : score(0.0), which(-1) {}
	choice(double _score) : score(_score), which(-1) {}
	choice(double _score, int _which) : score(_score), which(_which) {}
	double score;
	int which;
};

choice choose(const choice& c1, const choice& c2)
{
	if (c2.score < c1.score)
		return c2;
	return c1;
}

choice choose(const choice& c1, const choice& c2, const choice& c3)
{ return choose(c1, choose(c2, c3)); }

choice choose(const choice& c1, const choice& c2, const choice& c3, const choice& c4)
{ return choose(c1, choose(c2, c3, c4)); }

double align_multigene(const dna_sequence& r, const dna_sequence& g1, const dna_sequence& g2, std::vector<align_info>& out)
{
	boost::multi_array<choice, 2> rg1(boost::extents[r.size()+1][g1.size()]);
	boost::multi_array<choice, 2> rg2(boost::extents[r.size()+1][g2.size()]);
	boost::multi_array<choice, 1> rs(boost::extents[r.size()+1]);
	// It's free to start 'r' anywhere
	for(size_t i = 0; i < g1.size(); i++)
		rg1[0][i] = 0;
	// Score for skipping parts of r
	for(size_t i = 1; i <= r.size(); i++)
		rg1[i][0] = insert_score * i;
	// Main body of rg1
	for(size_t i = 1; i <= r.size(); i++)
	{
		for(size_t j = 1; j < g1.size(); j++)
		{
			rg1[i][j] = choose(
				choice(rg1[i-1][j-1].score + match_score(r[i-1], g1[j-1]), 1),
				choice(rg1[i][j-1].score + delete_score, 2),
				choice(rg1[i-1][j].score + insert_score, 3)
				);
		}
	}
	// Calculate cost of various skip points
	for(size_t i = 0; i <= r.size(); i++)
	{
		rs[i] = choice(rg1[i][0].score, 0);
		for(size_t j = 1; j < g1.size(); j++)
			rs[i] = choose(rs[i], choice(rg1[i][j].score, j));
	}
	// Score of first alignment anywhere in g2
	for(size_t i = 0; i < g2.size(); i++)
		rg2[0][i] = 0;
	// Score carried across from other side
	for(size_t i = 1; i <= r.size(); i++)
		rg2[i][0] = choice(rs[i].score, 0);
	// Main body of rg2
	for(size_t i = 1; i <= r.size(); i++)
	{
		for(size_t j = 1; j < g2.size(); j++)
		{
			rg2[i][j] = choose(
				choice(rs[i].score, 0),
				choice(rg2[i-1][j-1].score + match_score(r[i-1], g2[j-1]), 1),
				choice(rg2[i][j-1].score + delete_score, 2),
				choice(rg2[i-1][j].score + insert_score, 3)
				);
		}
	}
	// Find the minimal result that fully consumes the read
	choice final(rg2[r.size()][0].score, 0);
	for(size_t i = 1; i < g2.size(); i++)
		final = choose(final, choice(rg2[r.size()][i].score, i));

	// Now travel backwards and generate output
	out.resize(r.size());
	int cur_seq = 1;
	int cur_pos = final.which;
	int cur_r = r.size();
	while(cur_r)
	{
		choice back = (cur_seq ? rg2[cur_r][cur_pos] : rg1[cur_r][cur_pos]);
		//printf("cur_seq = %d, cur_pos = %d, cur_r = %d, back.score = %f, back.which = %d\n",
		//	(int) cur_seq, (int) cur_pos, (int) cur_r, back.score, (int) back.which);
		switch(back.which)
		{
		case 0:
			cur_seq = 0;
			cur_pos = rs[cur_r].which;
			break;
		case 1:
			cur_r--;
			cur_pos--;
			out[cur_r].seq = cur_seq;
			out[cur_r].pos = cur_pos;
			break;
		case 2:
			cur_pos--;
			break;
		case 3:
			cur_r--;
			out[cur_r].seq = cur_seq;
			out[cur_r].pos = -1;
			break;
		default:
			throw io_exception("Invalid case in back.which");  // Should never happen, but just in case
		}
	}
	return final.score;
}

void print_multigene(const dna_sequence& r, const dna_sequence& g1, const dna_sequence& g2, const std::vector<align_info>& out, bool all)
{
	int cur_seq = 0;
	int cur_pos = 0;
	std::string topline;
	std::string botline;
	bool started = false;
	for(size_t i = 0; i < r.size(); i++)
	{
		// Deal with deletions
		if (out[i].pos == -1)
		{
			started = true;
			topline += '.';
			botline += char(r[i]);
			continue;
		}
		// Print sequence 1 bases missing in read
		while(cur_seq == 0 && (out[i].seq == 1 || (out[i].seq == 0 && cur_pos < out[i].pos)))
		{
			if (all || started)
			{
				topline += char(g1[cur_pos]);
				botline += ' ';
				if (!all)
				{
					topline += '-';
					botline += ' ';
					started = false;
				}
			}
			cur_pos++;
			if (cur_pos == (int) g1.size()) {cur_seq = 1; cur_pos = 0; topline += ' '; botline += ' ';}
		}
		// Print sequence 2 bases missing in read
		while(cur_seq == 1 && out[i].seq == 1 && cur_pos < out[i].pos)
		{
			if (all || started)
			{
				topline += char(g2[cur_pos]);
				botline += ' ';
				if (!all)
				{
					topline += '-';
					botline += ' ';
					started = false;
				}
			}
			cur_pos++;
		}
		// Print 'matched' base
		topline += char((out[i].seq ? g2[cur_pos] : g1[cur_pos]));
		cur_pos++;
		botline += char(r[i]);
		started = true;
	}
	if (all)
	{
		while(cur_seq == 0)
		{
			topline += char(g1[cur_pos++]);
			if (cur_pos == (int) g1.size()) {cur_seq = 1; cur_pos = 0; topline += ' ';}
		}
		while(cur_seq == 1 && cur_pos != (int) g2.size())
			topline += char(g2[cur_pos++]);
	}
	printf("%s\n%s\n", topline.c_str(), botline.c_str());
}


/// \file Unit tests for Box class in the NRLib Geometry library.

#include <boost/test/unit_test.hpp>

#include "nrlib/geometry/box.hpp"
#include "nrlib/geometry/line.hpp"

BOOST_AUTO_TEST_CASE(CornerIntersectionsTest)
{
  NRLib::Box unit_box(-1.0, -1.0, -1.0, 1.0, 1.0, 1.0);
  NRLib::Line line = NRLib::Line::InfiniteLine(NRLib::Point(0.0, 0.0, 0.0), NRLib::Point(1.0, 1.0, 1.0));

  // This does not work properly:
  // std::vector<NRLib::Point> intersections = unit_box.FindIntersections(line);
  // The results should be corners (-1, -1, -1) and (1, 1, 1)
}


////////////////////////////////////////////////////////////////////////////////
//  Copyright (c) 2011 Katelyn Kufahl
//
//  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)
////////////////////////////////////////////////////////////////////////////////

#if !defined(HPX_05A1C29B_DB73_463A_8C9D_B8EDC3B69F5E)
#define HPX_05A1C29B_DB73_463A_8C9D_B8EDC3B69F5E

#include <boost/config.hpp>
#include <boost/assert.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/moment.hpp>
#include <boost/cstdint.hpp>
#include <boost/atomic.hpp>

#include <hpx/performance_counters/parcels/count_and_time_data_point.hpp>
#include <hpx/util/high_resolution_timer.hpp>
#include <hpx/util/spinlock.hpp>

namespace hpx { namespace performance_counters { namespace parcels
{

class count_time_stats
{
    typedef hpx::util::spinlock mutex_type;
    typedef mutex_type::scoped_lock lock;
    typedef boost::accumulators::accumulator_set acc_set;

public:

    count_time_stats():
      count_time_stats_size(0)
    {}

    boost::int64_t size() const;
    void push_back(data_point const& x);

    double mean_time() const;
    double moment_time() const;
    double variance_time() const;

    double total_time() const;

    private:
    util::high_resolution_timer timer;
    boost::atomic<boost::int64_t> count_time_stats_size;

    // Create mutexes for accumulator functions.
    mutable mutex_type acc_mtx;

    // Create accumulator sets.
    acc_set < double,
        boost::accumulators::features< boost::accumulators::tag::mean > >
        mean_time_acc;

    acc_set < double,
        boost::accumulators::features< boost::accumulators::tag::moment<2> > >
        moment_time_acc;

    acc_set < double,
        boost::accumulators::features< boost::accumulators::tag::variance> >
        variance_time_acc;
};

inline void count_time_stats::push_back(count_and_time_data_point const& x)
{
    lock mtx(acc_mtx)

    ++count_time_stats_size;

    mean_time_acc(x.time);
    moment_time_acc(x.time);
    variance_time_acc(x.time);
}

inline boost::int64_t count_time_stats::size() const
{
    return count_time_stats_size.load();
}

inline double count_time_stats::mean_time() const
{
    lock mtx(acc_mtx);
    return boost::accumulators::extract::mean(mean_time_acc);
}

inline double count_time_stats::moment_time() const
{
    lock mtx(acc_mtx);
    return boost::accumulators::extract::moment<2>(moment_time_acc);
}

inline double count_time_stats::variance_time() const
{
    lock mtx(acc_mtx);
    return boost::accumulators::extract::variance(variance_time_acc);
}

}}}

#endif // HPX_05A1C29B_DB73_463A_8C9D_B8EDC3B69F5E


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

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

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_SIMD_FUNCTION_SPLIT_MULTIPLIES_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SIMD_FUNCTION_SPLIT_MULTIPLIES_HPP_INCLUDED
#include <boost/simd/detail/overload.hpp>

#include <boost/simd/meta/hierarchy/simd.hpp>
#include <boost/simd/function/simd/multiplies.hpp>
#include <boost/simd/function/simd/split.hpp>

namespace boost { namespace simd { namespace ext
{
   namespace bd = boost::dispatch;
   namespace bs = boost::simd;
   BOOST_DISPATCH_OVERLOAD(split_multiplies_
                          , (typename A0, typename A1, typename X, typename Y)
                          , bd::cpu_
                          , bs::pack_<bd::arithmetic_<A0>, X>
                          , bs::pack_<bd::arithmetic_<A0>, X>
                          , bs::pack_<bd::arithmetic_<A1>, Y>
                          , bs::pack_<bd::arithmetic_<A1>, Y>
                          )
   {
     BOOST_FORCEINLINE void operator()(A0 const& a0, A0 const& a1
                                      , A1& a2, A1& a3
 //                                      , typename std::enable_if<  //TODO
//                                        is_upgradable_to<A0,A1>::value
//                                        >::type* = 0
                                      ) const
      {
        A1 a00, a01, a10, a11;
        split(a0, a00, a01);
        split(a1, a10, a11);
        a2 = a00*a10;
        a3 = a01*a11;
      }
   };

} } }

#endif


#ifndef TEST_UTILS_H
#define TEST_UTILS_H

#include <vector>
#include <tuple>
#include <iostream>
#include <iomanip>

#include <boost/io/ios_state.hpp>

#include <Eigen/Sparse>

#include "SparseLinearSolvers.hpp"
#include "Converters.hpp"
#include "IO.hpp"

#include <dfesnippets/NumericUtils.hpp>

namespace cask {
  namespace test {

    using Td = Eigen::Triplet<double>;
    using Vd = Eigen::VectorXd;
    using Md = Eigen::SparseMatrix<double>;

    using MismatchT = std::vector<std::tuple<int, double, double>>;

    MismatchT check(
        const std::vector<double>& got,
        const std::vector<double>& exp
        )
    {
      std::vector<std::tuple<int, double, double>> mismatches;
      for (int i = 0; i < got.size(); i++) {
        if (!dfesnippets::numeric_utils::almost_equal(got[i], exp[i], 1E-8, 1E-11))
          mismatches.push_back(std::make_tuple(i, got[i], exp[i]));
      }

      return mismatches;
    }

    MismatchT check(
        const Eigen::VectorXd& got,
        const Eigen::VectorXd& exp)
    {
      return
        check(cask::converters::eigenVectorToStdVector(got),
            cask::converters::eigenVectorToStdVector(exp));
    }

    void print_mismatches(const MismatchT& mismatches) {
      boost::io::ios_all_saver guard(std::cout);
      if (!mismatches.empty()) {
        std::cerr << "Results didn't match" << std::endl;
        for (int i = 0; i < mismatches.size(); i++) {
          std::cerr << std::fixed << std::setprecision(10);
          std::cerr << "At " << std::get<0>(mismatches[i]);
          std::cerr << " got: " << std::get<1>(mismatches[i]);
          std::cerr << " exp: "  << std::get<2>(mismatches[i]) << std::endl;
        }
      }
    }

    struct SimpleVectorGenerator {
      std::tuple<Vd, Vd> operator()(Md mat, int m) {
        Eigen::VectorXd x(m);
        for (int i = 0; i < m; i++)
          x[i] = i * 0.25;
        return std::make_tuple(
            mat * x,
            x);
      }
    };

    Md one(int m) {
      std::vector<Td> nnzs;
      for (int i = 0; i < m; i++)
        nnzs.push_back(Td(i, i, 1));
      Md a(m, m);
      a.setFromTriplets(nnzs.begin(), nnzs.end());
      return a;
    }

    // functor to generate a matrix
    struct IdentityGenerator {
      Md operator()(int m) {
        return one(m);
      }
    };

    struct RandomGenerator {
      Md operator()(int m) {
        return one(m) * 2;
      }
    };

    struct EigenMatrixGenerator {
      Md matrix;
      EigenMatrixGenerator(Md _matrix) : matrix(_matrix) {}
      Md operator()(int m) {
        return matrix;
      }
    };

    struct EigenVectorGenerator {
      Vd vd;
      EigenVectorGenerator(Vd _vd) : vd(_vd) {}
      std::tuple<Vd, Vd> operator()(Md mat, int m) {
        std::cout << "Running eigen solver" << std::endl;
        cask::sparse_linear_solvers::EigenSolver es;
        Vd sol = es.solve(mat, vd);
        std::cout << "Eigen solver complete!" << std::endl;
        return std::make_tuple(vd, sol);
      }
    };

    template<typename MatrixGenerator, typename RhsGenerator>
      int test(int m, MatrixGenerator mg, RhsGenerator rhsg,
          cask::sparse_linear_solvers::Solver &solver) {
        Md a = mg(m);
        auto tpl =  rhsg(a, m);
        Vd b = std::get<0>(tpl);
        Vd exp = std::get<1>(tpl);

        std::cout << "Running DFE Solver" << std::endl;
        Vd sol = solver.solve(a, b);
        std::cout << "DFE solver complete!" << std::endl;

        auto mismatches = cask::test::check(sol, exp);
        if (mismatches.empty())
          return 0;
        cask::test::print_mismatches(mismatches);

        return 1;
      }

    int runTest(
        std::string path,
        cask::sparse_linear_solvers::Solver& solver) {
      cask::io::MmReader<double> m(path);
      auto eigenMatrix = cask::converters::tripletToEigen(m.mmreadMatrix(path));
      return test(eigenMatrix->rows(),
          EigenMatrixGenerator{*eigenMatrix},
          cask::test::SimpleVectorGenerator{},
          solver);
    }

    int runTest(
        std::string path,
        std::string vectorPath,
        cask::sparse_linear_solvers::Solver& solver) {
      cask::io::MmReader<double> m(path);
      auto eigenMatrix = cask::converters::tripletToEigen(m.mmreadMatrix(path));
      cask::io::MmReader<double> mv(vectorPath);
      auto eigenVector = cask::converters::stdvectorToEigen(mv.readVector());
      return test(eigenMatrix->rows(),
          EigenMatrixGenerator{*eigenMatrix},
          EigenVectorGenerator{eigenVector},
          solver);
    }

  }
}

#endif /* end of include guard: TEST_UTILS_H */


#include <random>
#include <iostream>
#include <cstdio>
#include <boost/dynamic_bitset.hpp>
#include <cassert>

#include "../src/factor.hpp"
#include "../src/common.hpp"
#include "../src/ctw.hpp"
#include "../src/cts.hpp"

const uint16_t dim = 5;
const uint16_t SIZE = dim*dim;

typedef Factor<ContextTree> FactoredContextTree;
typedef Factor<SwitchingTree> FactoredSwitchingTree;

std::random_device rd;

const char* grayscale = "_.:-=+*#%@";

void printScreen(uint8_t* screen) {
    size_t i = 0;
    for (size_t r = 0; r < dim; r++) {
        for (size_t c = 0; c < dim; c++) {
            uint8_t pixel = screen[i++];
            char code;
            for (uint8_t z=0; ; z++) {
                code = grayscale[z];
                if (pixel <= (z+1)*26) break;
            }
            printf("%c", code);
        }
        printf("\n");
    }
    printf("\n");
}

void randomScreen(std::mt19937& random, uint8_t* screen, uint8_t range) {
    std::uniform_int_distribution<uint8_t> uniform255(0, range);
    for (size_t i=0; i < SIZE; i++) {
        screen[i] = uniform255(random);
    }
}

double test(uint32_t times) {
    std::mt19937 random(rd());
    std::uniform_int_distribution<uint16_t> uniformN(0, SIZE-1);

    uint8_t* screen = new uint8_t[SIZE];
    randomScreen(random, screen, 0);
    printScreen(screen);

    history_t history(dim);

//    FactoredContextTree tree(dim, history, 4);
    FactoredSwitchingTree tree(dim, history, 4);

    int reportEvery = 1 + times / 50;

    double probability = 1.;

    for (size_t z=0; z < times; z++) {
        if (z % 10 == 0) {
//            screen[uniformN(random)]++;
        }

        history.reset(screen);
        for (size_t r = 1; r < dim; r++) {
            for (size_t c = 1; c < dim-1; c++) {
                uint8_t pixel = screen[r*dim+c];
                uint8_t mask = 1;
                for (size_t b = 0; b < 8; b++) {
                    bit_t bit = (mask & pixel) > 0;
                    tree.update(bit);
                    mask <<= 1;
                }
            }
        }

        double sumProb = 0.;
        history.reset(screen);
        probability = 1.;
        for (size_t r = 1; r < dim; r++) {
            for (size_t c = 1; c < dim-1; c++) {
                uint8_t pixel = screen[r*dim+c];
                uint8_t mask = 1;
                for (size_t b = 0; b < 8; b++) {
                    bit_t bit = (mask & pixel) > 0;
                    double prob = tree.prob(bit);
                    mask <<= 1;
                    history.push_back(0);
                    sumProb += prob;
                    probability *= prob;
                }
            }
        }

        if (z % reportEvery==0) {
           printf("n=%5lu P_=%.20f P=%.20f\n", z, sumProb / (8*SIZE), probability);
        }
    }
    delete[] screen;
    return probability;
}

int main(int argc, char *argv[]) {
    uint32_t times = argc>1 ? std::stoi(argv[1]) : 1;
    assert(test(times)>0.85);
}


#ifndef LIBKRIGING_BENCH_HPP
#define LIBKRIGING_BENCH_HPP

#include <armadillo>

#include "libKriging/OrdinaryKriging.hpp"
#include "libKriging/libKriging_exports.h"
// #include "covariance.h"

/** Ordinary kriging regression
 * @ingroup Regression
 */
class Bench {

public:
  struct OFNData {
    arma::mat histx;
  };

 public:
  int n;

  LIBKRIGING_EXPORT Bench(int _n);  // const std::string & covType);

  LIBKRIGING_EXPORT arma::mat SolveTri(const arma::mat& Xtri, const arma::vec& y);

  LIBKRIGING_EXPORT arma::mat CholSym(const arma::mat& Rsym);

  LIBKRIGING_EXPORT std::tuple<arma::mat, arma::mat> QR(const arma::mat& M);

  LIBKRIGING_EXPORT arma::mat InvSymPD(const arma::mat& Rsympd);

  LIBKRIGING_EXPORT double LogLik(OrdinaryKriging& ok, const arma::vec& theta);

  LIBKRIGING_EXPORT arma::vec LogLikGrad(OrdinaryKriging& ok, const arma::vec& theta);

  LIBKRIGING_EXPORT double Rosenbrock(arma::vec& x) ;
  LIBKRIGING_EXPORT arma::vec RosenbrockGrad(arma::vec& x) ;
  LIBKRIGING_EXPORT arma::mat OptimRosenbrock(arma::vec& x0) ;
};

#endif  // LIBKRIGING_BENCH_HPP