/**
    @file Project_2.cpp

    @author Terence Henriod

    Project 2: Bayesion Minimum Error Classification

    @brief The driver program for use of a Bayesian Minimum Error Classifier to
           both classify randomly generated data and detect face (or at least
           skin-colored) regions in images.

    @version Original Code 1.00 (3/26/2014) - T. Henriod

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

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

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Compilation notes:
g++ -I /home/thenriod/Desktop/cpp_libs/Eigen_lib/ Project_1.cpp

*/

/*==============================================================================
=======     HEADER FILES     ===================================================
==============================================================================*/
#include <cmath>
#include <iostream>

#include "bayes_classifier.h"
#include "strict_gaussian_classifier.h"
#include "my_ppm.h"
#include <Eigen/Dense>  // -I /home/thenriod/Desktop/cpp_libs/Eigen_lib

using namespace std;

/*==============================================================================
=======     USER DEFINED TYPES     =============================================
==============================================================================*/
typedef struct
{
  string input_file_name;
  string output_file_base;
  string class_one_name;
  string class_two_name;
} BayesProblemData;

typedef struct
{
  string training_photo;
  string training_reference;
  string training_output;
  string photo_one;
  string reference_one;
  string output_one;
  string photo_two;
  string reference_two;
  string output_two;
} SkinProblemData;

/*==============================================================================
=======     CONSTANTS / MACROS     =============================================
==============================================================================*/
const string SKIN = "SKIN";
const string NOT_SKIN = "INANIMATE OBJECT";

/*==============================================================================
=======     GLOBAL VARIABLES     ===============================================
==============================================================================*/
  // none

/*==============================================================================
=======     FUNCTION PROTOTYPES     ============================================
==============================================================================*/

void solveBayesClassificationProblem( BayesProblemData& info );

int readData( vector<DataItem>& data, const string& input_file_name );

void solveImageProblem( SkinProblemData& info);

void trainStrictGaussianClassifiers( StrictGaussianClassifier& classifier_one,
                                     StrictGaussianClassifier& classifier_two,
                                     string& photo_file_name,
                                     string& reference_file_name );

void readPpmToDataVectors( vector<DataItem>& rb_picture_data,
                           vector<DataItem>& cb_cr_picture_data,
                          string& photo_file_name,
                          string& reference_file_name );

void classifyPhotoPixels( StrictGaussianClassifier& classifier,
                          vector<DataItem>& data,
                          string& output_file_name );

/*==============================================================================
=======     MAIN FUNCTION     ==================================================
==============================================================================*/

/**
main

The main driver

@param

@return

@pre
-#

@post
-#

@code
@endcode
*/

int main( int argc, char** argv )
{
  // variables
  BayesProblemData problem_info;
  SkinProblemData face_detection_info;

  string photo_file_name = "Training_1.ppm";
  string reference_file_name = "ref1.ppm";
  StrictGaussianClassifier skin_finder;

  // solve problem 1
  problem_info.input_file_name = "P1_data.txt";
  problem_info.output_file_base = "P1_solution";
  problem_info.class_one_name = "ONE";
  problem_info.class_two_name = "TWO";
  solveBayesClassificationProblem( problem_info );

  // solve problem 2
  problem_info.input_file_name = "P2_data.txt";
  problem_info.output_file_base = "P2_solution";
  solveBayesClassificationProblem( problem_info );

  // solve image problem
  face_detection_info.training_photo     = "Training_1.ppm";
  face_detection_info.training_reference = "ref1.ppm";
  face_detection_info.training_output    = "test_output.txt";
  face_detection_info.photo_one          = "Training_3.ppm";
  face_detection_info.reference_one      = "ref3.ppm";
  face_detection_info.output_one         = "output_1.txt";
  face_detection_info.photo_two          = "Training_6.ppm";
  face_detection_info.reference_two      = "ref6.ppm";
  face_detection_info.output_two         = "output_2.txt";

  skin_finder.set_class_name( SKIN );

  solveImageProblem( face_detection_info );

  // end program
  return 0;
}

/*==============================================================================
=======     FUNCTION IMPLEMENTATIONS     =======================================
==============================================================================*/
void solveBayesClassificationProblem( BayesProblemData& info )
{
  // variables
  vector<BayesClassifier> classifiers( 2 );
  vector<DataItem> problem_data;

  // read in the data
  readData( problem_data, info.input_file_name );

  // set the classifier class names
  classifiers[0].set_class_name( info.class_one_name );
  classifiers[1].set_class_name( info.class_two_name );

  // find the means of the training data
  classifiers[0].set_mean( problem_data );
  classifiers[1].set_mean( problem_data );

  // find the covariances of the training data
  classifiers[0].set_covariance( problem_data, classifiers[0].mean_vector() );
  classifiers[1].set_covariance( problem_data, classifiers[1].mean_vector() );

  // set the prior probabilities
  classifiers[0].set_prior_probability( 0.5 );
  classifiers[1].set_prior_probability( 0.5 );

  // perform the classifications with the equal prior probabilities
  BayesClassifier::performAnalysis( classifiers, problem_data,
                                    info.output_file_base + "_part1.txt" );

  // perform the classifications with the differing priors
  classifiers[0].set_prior_probability( 0.3 );
  classifiers[1].set_prior_probability( 0.7 );
  BayesClassifier::performAnalysis( classifiers, problem_data,
                                    info.output_file_base + "_part2.txt" );

  // no return - void
}

int readData( vector<DataItem>& data, const string& input_file_name )
{
  // variables
  fstream file;
  DataItem temp;
  char delimiter;

  // clear file stream object and open the file
  file.clear();
  file.open( input_file_name.c_str(), fstream::in );

  // prime the reading loop
  file >> temp.feature_vector(0) >> delimiter
       >> temp.feature_vector(1) >> delimiter
       >> temp.actual_class;

  // continue to read from the file while possible
  while( file.good() )
  {
    // store the recently read data
    data.push_back( temp );

    // attempt to read more data
    file >> temp.feature_vector(0) >> delimiter
         >> temp.feature_vector(1) >> delimiter
         >> temp.actual_class;
  }

  // return the data vector by reference
}

void solveImageProblem( SkinProblemData& info)
{
  // variables
  StrictGaussianClassifier rb_skin_finder;
  StrictGaussianClassifier cb_cr_skin_finder;
  string dummy = "CbCr_output.txt";

  vector<DataItem> rb_data;
  vector<DataItem> cb_cr_data;

  rb_skin_finder.set_class_name( SKIN );
  cb_cr_skin_finder.set_class_name( SKIN );

  // train the classifier
  trainStrictGaussianClassifiers( rb_skin_finder, cb_cr_skin_finder,
                                 info.training_photo,
                                 info.training_reference );

/*
readPpmToDataVectors( rb_data, cb_cr_data, info.training_photo, info.training_reference );

classifyPhotoPixels( rb_skin_finder, rb_data, info.training_output );

classifyPhotoPixels( cb_cr_skin_finder, cb_cr_data, dummy );
 */

  // perform classification on the first data set
  readPpmToDataVectors( rb_data, cb_cr_data, info.photo_one, info.reference_one );
  classifyPhotoPixels( rb_skin_finder, rb_data, info.output_one );
  dummy = "CbCr";
  dummy += info.output_one;
  classifyPhotoPixels( cb_cr_skin_finder, cb_cr_data, dummy );

  // clear the data vectors between runs
  rb_data.clear();
  cb_cr_data.clear();

  // perform classification on the second data set
  readPpmToDataVectors( rb_data, cb_cr_data, info.photo_two, info.reference_two );
  classifyPhotoPixels( rb_skin_finder, rb_data, info.output_two );
  dummy = "CbCr";
  dummy += info.output_two;
  classifyPhotoPixels( cb_cr_skin_finder, cb_cr_data, dummy );

  // no return - void
}

void trainStrictGaussianClassifiers( StrictGaussianClassifier& classifier_one,
                                     StrictGaussianClassifier& classifier_two,
                                     string& photo_file_name,
                                     string& reference_file_name )
{
  // variables
  vector<DataItem> rb_data;
  vector<DataItem> cb_cr_data;

  // read in the training data
  readPpmToDataVectors( rb_data, cb_cr_data, photo_file_name,
                        reference_file_name );

  // find the training mean
  classifier_one.set_mean( rb_data );
  classifier_two.set_mean( cb_cr_data );

  // find the covariance
  classifier_one.set_covariance( rb_data, classifier_one.mean_vector() );
  classifier_two.set_covariance( cb_cr_data, classifier_two.mean_vector() );

cout << "RB Classifier" << endl;
classifier_one.reportClassifierInfo();

cout << "CbCr Classifier" << endl;
classifier_two.reportClassifierInfo();

  // return the trained classifier by reference
}

void readPpmToDataVectors( vector<DataItem>& rb_picture_data,
                            vector<DataItem>& cb_cr_picture_data,
                            string& photo_file_name,
                            string& reference_file_name )
{
  // variables
  PpmImageData photo_image;
  PpmImageData reference_image;
  DataItem temp;
  int i = 0;
  int j = 0;
  int k = 0;
  double color_sum = 0;

  // read in the image file
  readPpmFile( &photo_image, photo_file_name.c_str() );

  // read in the reference file
  readPpmFile( &reference_image, reference_file_name.c_str() );

  // visit each row of pixels in the images
  for( i = 0, k = 0; i < photo_image.height; i++ )
  {
    // visit each pixel of the rows
    for( j = 0; j < photo_image.width; j++, k++ )
    {
      // convert the pixel color vector to a two dimensional one
      color_sum = photo_image.data[i][j].red + photo_image.data[i][j].green +
                  photo_image.data[i][j].blue;
      if( color_sum > 0 )
      {
        temp.feature_vector( 0 ) = photo_image.data[i][j].red / color_sum;
        temp.feature_vector( 1 ) = photo_image.data[i][j].green / color_sum;
      }
      else  // to not divide by 0
      {
        temp.feature_vector( 0 ) = 0;
        temp.feature_vector( 1 ) = 0;
      }

      // case: the reference image indicates the pixel in the photo is skin
      if( (reference_image.data[i][j].red + reference_image.data[i][j].green +
           reference_image.data[i][j].blue) > 0 )
      {
        // tag the data
        temp.actual_class = SKIN;
      }
      // case: the photo pixel is non-skin
      else
      {
        // tag the data
        temp.actual_class = NOT_SKIN;
      }

      // add the new item to the vector
      rb_picture_data.push_back( temp );

      // compute the Cb and Cr values for the alternative
      temp.feature_vector( 0 ) = ( -0.169 * photo_image.data[i][j].red ) +
                                 ( -0.332 * photo_image.data[i][j].green ) +
                                 ( 0.5 * photo_image.data[i][j].blue );
      temp.feature_vector( 1 ) = ( 0.5 * photo_image.data[i][j].red ) +
                                 ( -0.419 * photo_image.data[i][j].green ) +
                                 ( -0.081 * photo_image.data[i][j].blue );

      // add the new item to the new vector
      cb_cr_picture_data.push_back( temp );
    }
  }

  // deconstruct the image structs
  deconstructPpmImage( &photo_image );
  deconstructPpmImage( &reference_image );

  // return the data vector by reference
}

void classifyPhotoPixels( StrictGaussianClassifier& classifier,
                          vector<DataItem>& data,
                          string& output_file_name )
{
  // variables
  vector<double> skin_likelihood;
  int i = 0;
  int num_skin = 0;
  int num_other = 0;
  int num_correct_skin = 0;
  int num_correct_other = 0;
  int false_acceptance = 0;
  int false_rejection = 0;
  double threshold = 0;
  double overall_correct_rate = 0;
  double skin_correct_rate = 0;
  double other_correct_rate = 0;
  double false_acceptance_rate = 0;
  double false_rejection_rate = 0;
  fstream file;

  // prepare the file for the output summary
  file.clear();
  file.open( output_file_name.c_str(), fstream::out );
  file << "Threshold Used, "
       << "Number of items, "
       << "Correct Classifications, "
       << "Correct Classification Rate, "
       << "Number of Skin Items, "
       << "Correct Skin Classifications, "
       << "Correct Skin Classification Rate, "
       << "Number of Other Items, "
       << "Correct Other Classifications, "
       << "Correct Other Classification Rate, "
       << "False Acceptance Rate, "
       << "False Rejection Rate"<< endl;

  // find the likelihood of each test vector being a skin one
  for( i = 0; i < data.size(); i++ )
  {
     // compute the likelihood that the pixel is skin
     skin_likelihood.push_back(
         classifier.getGaussianProbability( data[i].feature_vector ) );
  }

  // perform the classifications at various acceptance thresholds
  for( threshold = 0.01; threshold < 1.0; threshold += 0.01 )
  {
    // set the decision threshold
    classifier.set_decision_threshold( threshold );

    // reset the counters
    num_skin = 0;
    num_other = 0;
    num_correct_skin = 0;
    num_correct_other = 0;
    false_acceptance = 0;
    false_rejection = 0;

    // for every skin-pixel likelihood
    for( i = 0; i < data.size(); i++ )
    {
       // case: the pixel is likely skin
       if( skin_likelihood[i] > threshold )
       {
          // mark the pixel as skin
          data[i].classified_as = SKIN;

          // case: the pixel was not correctly classified
          if( data[i].actual_class != SKIN  )
          {
            // count it
            false_acceptance++;
          }
          else
          {
             num_correct_skin++;
          }
       }
       // case: the skin pixel is not likely skin
       else
       {
          // mark the pixel as skin
          data[i].classified_as = NOT_SKIN;

          // case: the pixel was actually skin
          if( data[i].actual_class == SKIN )
          {
            // count it
            false_rejection++;
          }
          else
          {
            num_correct_other++;
          }
       }

       // case: the pixel was in fact skin
       if( data[i].actual_class == SKIN )
       {
         // count it
         num_skin++;
       }
       else
       {
         num_other++;
       }
    }

    // compute the summary statistics
    overall_correct_rate = (double)(num_correct_skin + num_correct_other) /
                           (double) data.size();
    skin_correct_rate = (double)((double)num_correct_skin / (double)num_skin);
    other_correct_rate = (double)((double)num_correct_other /
                         (double)num_other);
    false_acceptance_rate = (double) false_acceptance / (double) (data.size() - num_skin);  //false positive rate = false positive / negative
    false_rejection_rate = (double) false_rejection / (double) num_skin;    // false negative rate = false negative / positive

    // output the result data to the output file
    file << classifier.decision_threshold() << ", "
         << data.size() << ", "
         << (num_correct_skin + num_correct_other) << ", "
         << overall_correct_rate << ", "
         << num_skin << ", "
         << num_correct_skin << ", "
         << skin_correct_rate << ", "
         << num_other << ", "
         << num_correct_other << ", "
         << other_correct_rate << ", "
         << false_acceptance_rate << ", "
         << false_rejection_rate
         << endl;
  }

  // close the file
  file.close();

  // no return - void
}


#include <stan/math/fwd/scal.hpp>
#include <gtest/gtest.h>
#include <boost/math/special_functions/digamma.hpp>
#include <test/unit/math/fwd/scal/fun/nan_util.hpp>

TEST(AgradFwdLogRisingFactorial, Fvar) {
  using boost::math::digamma;
  using stan::math::fvar;
  using stan::math::log_rising_factorial;

  fvar<double> a(4.0, 1.0);
  fvar<double> x = log_rising_factorial(a, 1.0);
  EXPECT_FLOAT_EQ(std::log(4.0), x.val_);
  EXPECT_FLOAT_EQ(0.25, x.d_);

  // finite diff
  double eps = 1e-6;
  EXPECT_FLOAT_EQ((stan::math::log_rising_factorial(4.0 + eps, 1.0)
                   - stan::math::log_rising_factorial(4.0 - eps, 1.0))
                      / (2 * eps),
                  x.d_);

  fvar<double> c(-3.0, 2.0);

  EXPECT_THROW(log_rising_factorial(c, 2), std::domain_error);
  // EXPECT_THROW(log_rising_factorial(2, c), std::domain_error);
  EXPECT_THROW(log_rising_factorial(c, c), std::domain_error);

  x = log_rising_factorial(a, a);
  EXPECT_FLOAT_EQ(std::log(840.0), x.val_);
  EXPECT_FLOAT_EQ((2 * digamma(8) - digamma(4)), x.d_);

  x = log_rising_factorial(5, a);
  EXPECT_FLOAT_EQ(std::log(1680.0), x.val_);
  EXPECT_FLOAT_EQ(digamma(9), x.d_);

  // finite diff
  EXPECT_FLOAT_EQ((stan::math::log_rising_factorial(5.0, 4.0 + eps)
                   - stan::math::log_rising_factorial(5.0, 4.0 - eps))
                      / (2 * eps),
                  x.d_);
}

TEST(AgradFwdLogRisingFactorial, FvarFvarDouble) {
  using boost::math::digamma;
  using stan::math::fvar;
  using stan::math::log_rising_factorial;

  fvar<fvar<double> > x;
  x.val_.val_ = 4.0;
  x.val_.d_ = 1.0;

  fvar<fvar<double> > y;
  y.val_.val_ = 3.0;
  y.d_.val_ = 1.0;

  fvar<fvar<double> > a = log_rising_factorial(x, y);

  EXPECT_FLOAT_EQ(std::log(120.0), a.val_.val_);
  EXPECT_FLOAT_EQ(0.61666667, a.val_.d_);
  EXPECT_FLOAT_EQ(1.8727844, a.d_.val_);
  EXPECT_FLOAT_EQ(0.15354517, a.d_.d_);
}

struct log_rising_factorial_fun {
  template <typename T0, typename T1>
  inline typename boost::math::tools::promote_args<T0, T1>::type operator()(
      const T0 arg1, const T1 arg2) const {
    return log_rising_factorial(arg1, arg2);
  }
};

TEST(AgradFwdLogRisingFactorial, nan) {
  log_rising_factorial_fun log_rising_factorial_;
  test_nan_fwd(log_rising_factorial_, 3.0, 5.0, false);
}


//==================================================================================================
/*!
  @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_CONSTANT_SQRT_1O_5_HPP_INCLUDED
#define BOOST_SIMD_CONSTANT_SQRT_1O_5_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{
  /*!
    @ingroup group-constant

    Generate value \f$ \sqrt{\frac15}\f$

    @par Semantic:

    @code
    T r = Sqrt_1o_5<T>();
    @endcode

    is similar to:

    @code
    T r = sqrt(T(1)/T(5));
    @endcode

    @return The Sqrt_1o_5 constant for the proper type
  **/
  template<typename T> T Sqrt_1o_5();

  namespace functional
  {
    /*!
      @ingroup group-callable-constant
      Generate the  constant sqrt_1o_5.

      @return The Sqrt_1o_5 constant for the proper type
    **/
    const boost::dispatch::functor<tag::sqrt_1o_5_> sqrt_1o_5 = {};
  }
} }
#endif

#include <boost/simd/constant/definition/sqrt_1o_5.hpp>
#include <boost/simd/arch/common/scalar/constant/constant_value.hpp>
#include <boost/simd/arch/common/simd/constant/constant_value.hpp>

#endif


/*
 * Copyright 2015 Christoph Jud (christoph.jud@unibas.ch)
 *
 * 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 <fstream>
#include <iostream>
#include <iomanip>

//#include <boost/filesystem.hpp>
//namespace fs = boost::filesystem;

#include <Eigen/SVD>
#include <Eigen/Eigenvalues>

#include "GaussianProcess.h"
//#include "KernelFactory.h"
#include "MatrixIO.h"
#include "LAPACKUtils.h"

namespace gpr{

template< class TScalarType >
void GaussianProcess<TScalarType>::AddSample(const typename GaussianProcess<TScalarType>::VectorType &x,
                                             const typename GaussianProcess<TScalarType>::VectorType &y){
    if(m_SampleVectors.size() == 0){ // first call of AddSample defines dimensionality of input space
        m_InputDimension = x.size();
    }
    if(m_LabelVectors.size() == 0){ // first call of AddSample defines dimensionality of output space
        m_OutputDimension = y.size();
    }

    CheckInputDimension(x, "GaussianProcess::AddSample: ");
    CheckOutputDimension(y, "GaussianProcess::AddSample: ");

    m_SampleVectors.push_back(x);
    m_LabelVectors.push_back(y);
    m_Initialized = false;
}

template< class TScalarType >
typename GaussianProcess<TScalarType>::VectorType
GaussianProcess<TScalarType>::Predict(const typename GaussianProcess<TScalarType>::VectorType &x){
    Initialize();
    CheckInputDimension(x, "GaussianProcess::Predict: ");
    VectorType Kx;
    ComputeKernelVector(x, Kx);
    return (Kx.adjoint() * m_RegressionVectors).adjoint();
}

template< class TScalarType >
typename GaussianProcess<TScalarType>::VectorType
GaussianProcess<TScalarType>::PredictDerivative(const typename GaussianProcess<TScalarType>::VectorType &x,
                                                           typename GaussianProcess<TScalarType>::MatrixType &D){
    Initialize();
    CheckInputDimension(x, "GaussianProcess::PredictDerivative: ");
    VectorType Kx;
    ComputeKernelVector(x, Kx);
    MatrixType X;
    ComputeDifferenceMatrix(x, X);

    unsigned d = m_InputDimension;
    unsigned m = m_OutputDimension;
    D.resize(m_InputDimension, m_OutputDimension);
    for(unsigned i=0; i<m_OutputDimension; i++){
        D.col(i) = -X.transpose() * Kx.cwiseProduct(m_RegressionVectors.col(i));
    }
    return (Kx.adjoint() * m_RegressionVectors).adjoint(); // return point prediction
}

template< class TScalarType >
TScalarType
GaussianProcess<TScalarType>::operator()(const typename GaussianProcess<TScalarType>::VectorType & x,
                                         const typename GaussianProcess<TScalarType>::VectorType & y){
    Initialize();
    CheckInputDimension(x, "GaussianProcess::(): ");
    CheckInputDimension(y, "GaussianProcess::(): ");
    VectorType Kx;
    ComputeKernelVector(x, Kx);
    VectorType Ky;
    ComputeKernelVector(y, Ky);

    if(m_CoreMatrix.diagonalSize() == 0){
        ComputeCoreMatrix(m_CoreMatrix);
    }
    return (*m_Kernel)(x, y) - Kx.adjoint() * m_CoreMatrix * Ky;
}

template< class TScalarType >
TScalarType
GaussianProcess<TScalarType>::GetCredibleInterval(const typename GaussianProcess<TScalarType>::VectorType& x){
    Initialize();
    CheckInputDimension(x, "GaussianProcess::GetCredibleIntervall: ");

    // due to nummerical instabilities of the inversion of the kernel matrix
    // gp(x,x) might return negative values
    // therefore the maximum of zero and c is taken.
    TScalarType c = (*this)(x, x);
    if(debug && c<0) std::cout << "GaussianProcess::GetCredibleIntervall: prediction is instable. gp(x,x) = " << c << "." << std::endl;
    c = 2*std::sqrt(std::max(static_cast<TScalarType>(0.0),c));
    return c;
}

template< class TScalarType >
void GaussianProcess<TScalarType>::Initialize(){
    if(m_Initialized){
        return;
    }
    if(!(m_SampleVectors.size() > 0)){
        throw std::string("GaussianProcess::Initialize: no input samples defined during initialization");
    }
    if(!(m_LabelVectors.size() > 0)){
        throw std::string("GaussianProcess::Initialize: no ouput labels defined during initialization");
    }
    ComputeRegressionVectors();
    m_Initialized = true;
}

template< class TScalarType >
void GaussianProcess<TScalarType>::Save(std::string prefix){
    if(!m_Initialized){
        throw std::string("GaussianProcess::Save: gaussian process is not initialized.");
    }

    if(debug){
        std::cout << "GaussianProcess::Save: writing gaussian process: " << std::endl;
        std::cout << "\t " << prefix+"-RegressionVectors.txt" << std::endl;
        std::cout << "\t " << prefix+"-CoreMatrix.txt" << std::endl;
        std::cout << "\t " << prefix+"-SampleVectors.txt" << std::endl;
        std::cout << "\t " << prefix+"-LabelVectors.txt" << std::endl;
        std::cout << "\t " << prefix+"-ParameterFile.txt" << std::endl;
    }

    // save regression vectors
    WriteMatrix<MatrixType>(m_RegressionVectors, prefix+"-RegressionVectors.txt");

    // save regression vectors
    if(m_EfficientStorage) m_CoreMatrix.setZero(0,0);
    WriteMatrix<MatrixType>(m_CoreMatrix, prefix+"-CoreMatrix.txt");

    // save sample vectors
    MatrixType X = MatrixType::Zero(m_SampleVectors[0].size(), m_SampleVectors.size());
    for(unsigned i=0; i<m_SampleVectors.size(); i++){
        X.block(0,i,m_SampleVectors[0].size(),1) = m_SampleVectors[i];
    }
    WriteMatrix<MatrixType>(X, prefix+"-SampleVectors.txt");

    // save label vectors
    MatrixType Y = MatrixType::Zero(m_LabelVectors[0].size(), m_LabelVectors.size());
    for(unsigned i=0; i<m_LabelVectors.size(); i++){
        Y.block(0,i,m_LabelVectors[0].size(),1) = m_LabelVectors[i];
    }
    WriteMatrix<MatrixType>(Y, prefix+"-LabelVectors.txt");

    // save parameters
    // KernelType, #KernelParameters, KernelParameters, noise, InputDimension, OutputDimension
    std::ofstream parameter_outfile;
    parameter_outfile.open(std::string(prefix+"-ParameterFile.txt").c_str());

    parameter_outfile << m_Sigma << " " << m_InputDimension << " " << m_OutputDimension << " " << m_EfficientStorage << " " << debug << " ";

    parameter_outfile << std::setprecision(std::numeric_limits<TScalarType>::digits10 +1);
    parameter_outfile << m_Kernel->ToString();

    parameter_outfile.close();
}

template< class TScalarType >
void GaussianProcess<TScalarType>::ToString() const{
    std::cout << "---------------------------------------" << std::endl;
    std::cout << "Gaussian Process" << std::endl;
    std::cout << " - initialized:\t\t" << m_Initialized << std::endl;
    std::cout << " - # samples:\t\t" << m_SampleVectors.size() << std::endl;
    std::cout << " - # labels:\t\t" << m_LabelVectors.size() << std::endl;
    std::cout << " - noise:\t\t" << m_Sigma << std::endl;
    std::cout << " - input dimension:\t" << m_InputDimension << std::endl;
    std::cout << " - output dimension:\t" << m_OutputDimension << std::endl;
    std::cout << std::endl;
    std::cout << " - Kernel:" << std::endl;
    std::cout << "       - Type:\t\t" << m_Kernel->ToString() << std::endl;
    std::cout << "       - Parameter:\t";
    for(unsigned i=0; i<m_Kernel->GetStringParameters().size(); i++){
        std::cout << m_Kernel->GetStringParameters()[i] << ", ";
    }
    std::cout << std::endl;
    std::cout << "---------------------------------------" << std::endl;
}

template< class TScalarType >
bool GaussianProcess<TScalarType>::operator ==(const GaussianProcess<TScalarType> &b) const{
    if(this->debug) std::cout << "GaussianProcess::comparison: " << std::flush;

    if((this->m_RegressionVectors - b.m_RegressionVectors).norm() > 0){
        if(this->debug) std::cout << "regression vectors not equal." << std::endl;
        return false;
    }

    if(this->m_CoreMatrix.diagonalSize() != b.m_CoreMatrix.diagonalSize()){
        if(this->debug) std::cout << "core matrices not equal."  << std::endl;
        return false;
    }
    else{
        if(this->debug && (this->m_CoreMatrix - b.m_CoreMatrix).norm() > 0) std::cout << "core matrices error is " << (this->m_CoreMatrix - b.m_CoreMatrix).norm() << std::endl;
    }

    if(this->m_SampleVectors.size() != b.m_SampleVectors.size()){
        if(this->debug) std::cout << "number of sample vectors not equal." << std::endl;
        return false;
    }
    for(unsigned i=0; i<this->m_SampleVectors.size(); i++){
        if((this->m_SampleVectors[i] - b.m_SampleVectors[i]).norm()>0){
            if(this->debug) std::cout << "sample vectors not equal." << std::endl;
            return false;
        }
    }

    if(this->m_LabelVectors.size() != b.m_LabelVectors.size()){
        if(this->debug) std::cout << "number of label vectors not equal." << std::endl;
        return false;
    }
    for(unsigned i=0; i<this->m_LabelVectors.size(); i++){
        if((this->m_LabelVectors[i] - b.m_LabelVectors[i]).norm()>0) {
            if(this->debug) std::cout << "label vectors not equal." << std::endl;
            return false;
        }
    }
    if(*this->m_Kernel.get() != *b.m_Kernel.get()){
        if(this->debug) std::cout << "kernel not equal." << std::endl;
        return false;
    }
    if(this->m_Sigma != b.m_Sigma){
        if(this->debug) std::cout << "sigma not equal." << std::endl;
        return false;
    }
    if(this->m_Initialized != b.m_Initialized){
        if(this->debug) std::cout << "initialization state not equal." << std::endl;
        return false;
    }
    if(this->m_InputDimension != b.m_InputDimension){
        if(this->debug) std::cout << "input dimension not equal." << std::endl;
        return false;
    }
    if(this->m_OutputDimension != b.m_OutputDimension){
        if(this->debug) std::cout << "output dimension not equal." << std::endl;
        return false;
    }
    if(this->m_EfficientStorage!= b.m_EfficientStorage){
        if(this->debug) std::cout << "efficient storage setting not equal." << std::endl;
        return false;
    }
    if(this->debug != b.debug){
        if(this->debug) std::cout << "debug state not equal." << std::endl;
        return false;
    }
    if(this->debug) std::cout << "is equal!" << std::endl;
    return true;
}

template< class TScalarType >
void GaussianProcess<TScalarType>::ComputeKernelMatrix(typename GaussianProcess<TScalarType>::MatrixType &M) const{
    if(debug){
        std::cout << "GaussianProcess::ComputeKernelMatrix: building kernel matrix... ";
        std::cout.flush();
    }

    ComputeKernelMatrixInternal(M, m_SampleVectors);

    if(debug) std::cout << "[done]" << std::endl;
}

template< class TScalarType >
void GaussianProcess<TScalarType>::AddNoiseToKernelMatrix(typename GaussianProcess<TScalarType>::MatrixType &M) const{
    // add noise variance to diagonal
    if(M.rows() != M.cols()) throw std::string("GaussianProcess::AddNoiseToKernelMatrix: square matrix required.");
    for(unsigned i=0; i<M.rows(); i++){
        M(i,i) += m_Sigma*m_Sigma;
    }
}

template< class TScalarType >
void GaussianProcess<TScalarType>::ComputeKernelMatrixInternal(typename GaussianProcess<TScalarType>::MatrixType &M,
                                                               const typename GaussianProcess<TScalarType>::VectorListType& samples) const{
    unsigned n = samples.size();
    M.resize(n,n);

#pragma omp parallel for
    for(unsigned i=0; i<n; i++){
        for(unsigned j=i; j<n; j++){
            TScalarType v = (*m_Kernel)(samples[i],samples[j]);
            M(i,j) = v;
            M(j,i) = v;
        }
    }

    // check if matrix entries are finite
    if(!((M - M).array() == (M - M).array()).all()){
        throw std::string("GaussianProcess::ComputeKernelMatrixInternal: kernel matrix contains entries which are not finite.");
    }
}

template< class TScalarType >
TScalarType GaussianProcess<TScalarType>::ComputeKernelMatrixTrace() const{
    if(debug){
        std::cout << "GaussianProcess::ComputeKernelMatrixTrace: sum up diagonal elements of kernel matrix... ";
        std::cout.flush();
    }

    TScalarType trace = ComputeKernelMatrixTraceInternal(m_SampleVectors);

    if(debug) std::cout << "[done]" << std::endl;

    return trace;
}

template< class TScalarType >
TScalarType GaussianProcess<TScalarType>::ComputeKernelMatrixTraceInternal(const typename GaussianProcess<TScalarType>::VectorListType& samples) const{
    unsigned n = samples.size();
    TScalarType trace = 0;

    for(unsigned i=0; i<n; i++){
            trace += (*m_Kernel)(samples[i],samples[i]);
    }
    return trace;
}

template< class TScalarType >
typename GaussianProcess<TScalarType>::VectorType
GaussianProcess<TScalarType>::ComputeDerivativeKernelMatrixTrace() const{
    if(debug){
        std::cout << "GaussianProcess::ComputeDerivativeKernelMatrixTrace: sum up diagonal elements of derivative kernel matrix... ";
        std::cout.flush();
    }

    typename GaussianProcess<TScalarType>::VectorType trace = ComputeDerivativeKernelMatrixTraceInternal(m_SampleVectors);

    if(debug) std::cout << "[done]" << std::endl;

    return trace;
}

template< class TScalarType >
typename GaussianProcess<TScalarType>::VectorType
GaussianProcess<TScalarType>::ComputeDerivativeKernelMatrixTraceInternal(const typename GaussianProcess<TScalarType>::VectorListType& samples) const{
    typedef typename GaussianProcess<TScalarType>::VectorType VectorType;
    unsigned num_params = m_Kernel->GetNumberOfParameters();
    VectorType trace = VectorType::Zero(num_params);

    unsigned n = samples.size();
    for(unsigned i=0; i<n; i++){
            trace += m_Kernel->GetDerivative(samples[i], samples[i]);
    }
    return trace;
}

template< class TScalarType >
void GaussianProcess<TScalarType>::ComputeDerivativeKernelMatrix(typename GaussianProcess<TScalarType>::MatrixType &M) const{
    if(debug){
        std::cout << "GaussianProcess::ComputeDerivativeKernelMatrix: building kernel matrix... ";
        std::cout.flush();
    }

    ComputeDerivativeKernelMatrixInternal(M, m_SampleVectors);

    if(debug) std::cout << "[done]" << std::endl;
}

template< class TScalarType >
void GaussianProcess<TScalarType>::ComputeDerivativeKernelMatrixInternal(typename GaussianProcess<TScalarType>::MatrixType &M,
                                                                         const typename GaussianProcess<TScalarType>::VectorListType& samples) const{
    unsigned num_params = m_Kernel->GetNumberOfParameters();

    unsigned n = samples.size();
    M.resize(n*num_params,n);

#pragma omp parallel for
    for(unsigned i=0; i<n; i++){
        for(unsigned j=i; j<n; j++){
            typename GaussianProcess<TScalarType>::VectorType v;
            v = m_Kernel->GetDerivative(samples[i], samples[j]);

            if(v.rows() != num_params) throw std::string("GaussianProcess::ComputeDerivativeKernelMatrixInternal: dimension missmatch in derivative.");
            for(unsigned p=0; p<num_params; p++){

                //if(i+p*n >= M.rows() || j+p*n >= M.rows())  throw std::string("GaussianProcess::ComputeDerivativeKernelMatrix: dimension missmatch in derivative.");

                M(i + p*n, j) = v[p];
                M(j + p*n, i) = v[p];
            }
        }
    }
}

template< class TScalarType >
void GaussianProcess<TScalarType>::ComputeCoreMatrix(typename GaussianProcess<TScalarType>::MatrixType &C) const{
    MatrixType K;
    ComputeKernelMatrix(K);

    // add noise variance to diagonal
    AddNoiseToKernelMatrix(K);

    C = InvertKernelMatrix(K, m_InvMethod);

    if(debug){
        std::cout << "GaussianProcess::ComputeCoreMatrix: inversion error: " << (K*C - MatrixType::Identity(K.rows(),K.cols())).norm() << std::endl;
    }
}

template< class TScalarType >
typename GaussianProcess<TScalarType>::HighPrecisionType GaussianProcess<TScalarType>::ComputeCoreMatrixWithDeterminant(typename GaussianProcess<TScalarType>::MatrixType &C) const{
    typedef typename GaussianProcess<TScalarType>::HighPrecisionType HighPrecisionType;
    MatrixType K;
    ComputeKernelMatrix(K);

    // add noise variance to diagonal
    AddNoiseToKernelMatrix(K);

    C = InvertKernelMatrix(K, m_InvMethod);

    if(debug){
        std::cout << "GaussianProcess::ComputeCoreMatrix: inversion error: " << (K*C - MatrixType::Identity(K.rows(),K.cols())).norm() << std::endl;
        std::cout << "GaussianProcess::ComputeCoreMatrix: determinant of K: " << K.template cast<HighPrecisionType>().determinant() << std::endl;
    }
    return K.template cast<HighPrecisionType>().determinant();
}

template< class TScalarType >
typename GaussianProcess<TScalarType>::MatrixType GaussianProcess<TScalarType>::InvertKernelMatrix(const typename GaussianProcess<TScalarType>::MatrixType &K,
                                                      typename GaussianProcess<TScalarType>::InversionMethod inv_method,
                                                                                bool stable) const{
    // compute core matrix
    if(debug){
        std::cout << "GaussianProcess::InvertKernelMatrix: inverting kernel matrix... ";
        std::cout.flush();
    }

    typename GaussianProcess<TScalarType>::MatrixType core;

    switch(inv_method){
    // standard method: fast but not that accurate
    // Uses the LU decomposition with full pivoting for the inversion
    case FullPivotLU:{
        if(debug) std::cout << " (inversion method: FullPivotLU) " << std::flush;
        try{
            if(stable){
                core = K.inverse();
            }
            else{
                if(debug) std::cout << " (using lapack) " << std::flush;
                core = lapack::lu_invert<TScalarType>(K);
            }
        }
        catch(lapack::LAPACKException& e){
            core = K.inverse();
        }
    }
    break;

    // very accurate and very slow method, use it for small problems
    // Uses the two-sided Jacobi SVD decomposition
    case JacobiSVD:{
        if(debug) std::cout << " (inversion method: JacobiSVD) " << std::flush;
        Eigen::JacobiSVD<MatrixType> jacobisvd(K, Eigen::ComputeThinU | Eigen::ComputeThinV);
        if((jacobisvd.singularValues().real().array() < 0).any() && debug){
            std::cout << "GaussianProcess::InvertKernelMatrix: warning: there are negative eigenvalues.";
            std::cout.flush();
        }
        core = jacobisvd.matrixV() * VectorType(1/jacobisvd.singularValues().array()).asDiagonal() * jacobisvd.matrixU().transpose();
    }
    break;

    // accurate method and faster than Jacobi SVD.
    // Uses the bidiagonal divide and conquer SVD
    case BDCSVD:{
        if(debug) std::cout << " (inversion method: BDCSVD) " << std::flush;
#ifdef EIGEN_BDCSVD_H
        Eigen::BDCSVD<MatrixType> bdcsvd(K, Eigen::ComputeThinU | Eigen::ComputeThinV);
        if((bdcsvd.singularValues().real().array() < 0).any() && debug){
            std::cout << "GaussianProcess::InvertKernelMatrix: warning: there are negative eigenvalues.";
            std::cout.flush();
        }
        core = bdcsvd.matrixV() * VectorType(1/bdcsvd.singularValues().array()).asDiagonal() * bdcsvd.matrixU().transpose();
#else
        // this is checked, since BDCSVD is currently not in the newest release
        throw std::string("GaussianProcess::InvertKernelMatrix: BDCSVD is not supported by the provided Eigen library.");
#endif

    }
    break;

    // faster than the SVD method but less stable
    // computes the eigenvalues/eigenvectors of selfadjoint matrices
    case SelfAdjointEigenSolver:{
        if(debug) std::cout << " (inversion method: SelfAdjointEigenSolver) " << std::flush;
        try{
            core = lapack::chol_invert<TScalarType>(K);
        }
        catch(lapack::LAPACKException& e){
            Eigen::SelfAdjointEigenSolver<MatrixType> es;
            es.compute(K);
            VectorType eigenValues = es.eigenvalues().reverse();
            MatrixType eigenVectors = es.eigenvectors().rowwise().reverse();
            if((eigenValues.real().array() < 0).any() && debug){
                std::cout << "GaussianProcess::InvertKernelMatrix: warning: there are negative eigenvalues.";
                std::cout.flush();
            }
            core = eigenVectors * VectorType(1/eigenValues.array()).asDiagonal() * eigenVectors.transpose();
        }
    }
    break;
    }

    if(debug) std::cout << "[done]" << std::endl;
    return core;
}

template< class TScalarType >
void GaussianProcess<TScalarType>::ComputeLabelMatrix(typename GaussianProcess<TScalarType>::MatrixType &Y) const{
    ComputeLabelMatrixInternal(Y, m_LabelVectors);
}

template< class TScalarType >
void GaussianProcess<TScalarType>::ComputeLabelMatrixInternal(typename GaussianProcess<TScalarType>::MatrixType &Y,
                                                              const typename GaussianProcess<TScalarType>::VectorListType& labels) const{
    unsigned n = labels.size();
    if(!(n > 0)){
        throw std::string("GaussianProcess::ComputeLabelMatrixInternal: no ouput labels defined.");
    }
    unsigned d = labels[0].size();
    Y.resize(n,d);

#pragma omp parallel for
    for(unsigned i=0; i<n; i++){
        Y.block(i,0,1,d) = labels[i].adjoint();
    }
}

template< class TScalarType >
void GaussianProcess<TScalarType>::ComputeRegressionVectors(){

    // Computation of kernel matrix
    if(debug){
        std::cout << "GaussianProcess::ComputeRegressionVectors: calculating regression vectors... " << std::endl;
    }

    // compute the core matrix which is inv(K + sigma2 I)
    // This is a separate function call because it is also used if the core matrix
    // is not stored due to the efficient storage setting
    ComputeCoreMatrix(m_CoreMatrix);

    // calculate label matrix
    // TODO: if a mean support is implemented, the mean has to be subtracted from the labels!
    MatrixType Y;
    ComputeLabelMatrix(Y);

    // calculate regression vectors
    m_RegressionVectors = m_CoreMatrix * Y ; // inv(K + sigma2)*Y

    // deleting core matrix if the storage has to be handled efficiently
    // - it is not used for regression
    // - but it is needed to compute the credible interval
    if(m_EfficientStorage){
        m_CoreMatrix.setZero(0,0);
    }
    if(debug){
        std::cout << "GaussianProcess::ComputeRegressionVectors: calculating regression vectors [done]" << std::endl;
    }
}

template< class TScalarType >
void GaussianProcess<TScalarType>::ComputeKernelVector(const typename GaussianProcess<TScalarType>::VectorType &x,
                                                       typename GaussianProcess<TScalarType>::VectorType &Kx) const{
    if(!m_Initialized){
        throw std::string("GaussianProcess::ComputeKernelVectorInternal: gaussian process is not initialized.");
    }
    ComputeKernelVectorInternal(x, Kx, m_SampleVectors);
}

template< class TScalarType >
void GaussianProcess<TScalarType>::ComputeKernelVectorInternal(const typename GaussianProcess<TScalarType>::VectorType &x,
                                                               typename GaussianProcess<TScalarType>::VectorType &Kx,
                                                               const typename GaussianProcess<TScalarType>::VectorListType& samples) const{
    Kx.resize(samples.size());

#pragma omp parallel for
    for(unsigned i=0; i<Kx.size(); i++){
        Kx(i) = (*m_Kernel)(x, samples[i]);
    }
}

template< class TScalarType >
void GaussianProcess<TScalarType>::ComputeDifferenceMatrix(const typename GaussianProcess<TScalarType>::VectorType &x,
                                                           typename GaussianProcess<TScalarType>::MatrixType &X) const{
    unsigned n = m_SampleVectors.size();
    unsigned d = x.size();
    X.resize(n,d);

    for(unsigned i=0; i<n; i++){
        X.block(i,0,1,d) = (x - m_SampleVectors[i]).adjoint();
    }
}

template< class TScalarType >
void GaussianProcess<TScalarType>::CheckInputDimension(const typename GaussianProcess<TScalarType>::VectorType &x, std::string msg_prefix) const{
    if(x.size()!=m_InputDimension){
        std::stringstream error_msg;
        error_msg << msg_prefix << "dimension of input vector ("<< x.size() << ") does not correspond to the input dimension (" << m_InputDimension << ").";
        throw std::string(error_msg.str());
    }
}

template< class TScalarType >
void GaussianProcess<TScalarType>::CheckOutputDimension(const typename GaussianProcess<TScalarType>::VectorType &y, std::string msg_prefix) const{
    if(y.size()!=m_OutputDimension){
        std::stringstream error_msg;
        error_msg << msg_prefix << "dimension of output vector ("<< y.size() << ") does not correspond to the output dimension (" << m_OutputDimension << ").";
        throw std::string(error_msg.str());
    }
}

}

template class gpr::GaussianProcess<float>;
template class gpr::GaussianProcess<double>;


#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_mesh_processing/intersection.h>

#ifdef USE_SURFACE_MESH
#include "Kernel_type.h"
#include "Scene_surface_mesh_item.h"
#else
#include "Scene_polyhedron_item.h"
#include "Polyhedron_type.h"
#endif
#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>

#include "Scene_polylines_item.h"

#include <boost/foreach.hpp>

#include <QString>
#include <QAction>
#include <QMenu>
#include <QMainWindow>
#include <QApplication>
#include <QTime>
#include <QMessageBox>

#ifdef USE_SURFACE_MESH
typedef Scene_surface_mesh_item Scene_face_graph_item;
#else
typedef Scene_polyhedron_item Scene_face_graph_item;
#endif

using namespace CGAL::Three;
namespace PMP = CGAL::Polygon_mesh_processing;

class Polyhedron_demo_intersection_plugin :
  public QObject,
  public Polyhedron_demo_plugin_interface
{
  Q_OBJECT
  Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
  Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")

public:

  bool applicable(QAction*) const {
    return scene->selectionIndices().size() == 2 &&
      qobject_cast<Scene_face_graph_item*>(scene->item(scene->selectionIndices().first())) &&
      qobject_cast<Scene_face_graph_item*>(scene->item(scene->selectionIndices().last()));
  }

  QList<QAction*> actions() const {
    return QList<QAction*>() << actionPolyhedronIntersection_3;
  }

  void init(QMainWindow* mw, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) {
    this->scene = scene_interface;
    actionPolyhedronIntersection_3 = new QAction("Surface Intersection", mw);
    actionPolyhedronIntersection_3->setProperty("subMenuName", "Polygon Mesh Processing");
    if(actionPolyhedronIntersection_3) {
      connect(actionPolyhedronIntersection_3, SIGNAL(triggered()),
              this, SLOT(intersection()));
    }
  }

private:

  QAction*  actionPolyhedronIntersection_3;
  Scene_interface *scene;

public Q_SLOTS:
  void intersection();

}; // end class Polyhedron_demo_intersection_plugin

void Polyhedron_demo_intersection_plugin::intersection()
{
  Scene_face_graph_item* itemA = NULL;
  Q_FOREACH(CGAL::Three::Scene_interface::Item_id index, scene->selectionIndices())
  {
    Scene_face_graph_item* itemB =
      qobject_cast<Scene_face_graph_item*>(scene->item(index));

    if(itemB)
    {
      if (itemA==NULL)
      {
        itemA = itemB;
        continue;
      }

      QApplication::setOverrideCursor(Qt::WaitCursor);

      Scene_polylines_item* new_item = new Scene_polylines_item();
     // perform Boolean operation
      QTime time;
      time.start();

      try{
        PMP::surface_intersection(*itemA->polyhedron(),
                                  *itemB->polyhedron(),
                                  std::back_inserter(new_item->polylines),
                                  true);
      }
      catch(CGAL::Corefinement::Self_intersection_exception)
      {
        QMessageBox::warning((QWidget*)NULL,
          tr("Self-intersections Found"),
          tr("Some self-intersections were found amongst intersecting facets"));
        delete new_item;
        QApplication::restoreOverrideCursor();
        return;
      }

      QString name = tr("%1 intersection %2");

      new_item->setName(name.arg(itemA->name(), itemB->name()));
      std::cout << "ok (" << time.elapsed() << " ms)" << std::endl;

      if (new_item->polylines.empty())
        delete new_item;
      else{
        new_item->setColor(Qt::green);
        new_item->setRenderingMode(Wireframe);
        scene->addItem(new_item);
        new_item->invalidateOpenGLBuffers();
      }

      QApplication::restoreOverrideCursor();
    }
  }
}

#include "Surface_intersection_plugin.moc"


// Andrew Naplavkov

#ifndef BARK_GRID_HPP
#define BARK_GRID_HPP

#include <bark/geometry/geometry_ops.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/sum_kahan.hpp>
#include <boost/multi_array.hpp>

namespace bark {

/// Contiguous XY-coordinates container
class grid {
public:
    using value_type = double;

    grid(const geometry::box& ext, size_t rows, size_t cols)
        : data_{boost::extents[rows][cols][2]}
    {
        if (rows < 2 || cols < 2)
            std::fill(begin(), end(), NAN);
        else {
            auto dx = geometry::width(ext) / (cols - 1);
            auto dy = geometry::height(ext) / (rows - 1);
            auto min_x = geometry::left(ext);
            auto min_y = geometry::bottom(ext);
            kahan_accumulator acc_y(min_y);
            for (size_t row = 0; row < rows; ++row, acc_y(dy)) {
                kahan_accumulator acc_x(min_x);
                for (size_t col = 0; col < cols; ++col, acc_x(dx)) {
                    x(row, col) = boost::accumulators::sum_kahan(acc_x);
                    y(row, col) = boost::accumulators::sum_kahan(acc_y);
                }
            }
        }
    }

    size_t rows() const { return data_.shape()[0]; }
    size_t cols() const { return data_.shape()[1]; }
    value_type* begin() { return data_.data(); }
    value_type* end() { return data_.data() + data_.num_elements(); }
    value_type& x(size_t row, size_t col) { return data_[row][col][0]; }
    value_type& y(size_t row, size_t col) { return data_[row][col][1]; }

private:
    using sequence_type = boost::multi_array<value_type, 3>;
    using kahan_accumulator = boost::accumulators::accumulator_set<
        value_type,
        boost::accumulators::stats<boost::accumulators::tag::sum_kahan>>;

    sequence_type data_;
};

}  // namespace bark

#endif  // BARK_GRID_HPP


// Copyright 2020 Jan Feitsma (Falcons)
// SPDX-License-Identifier: Apache-2.0
/*
 * falconsCommonLegacy.hpp (previously cFalconsCommon.cpp)
 *
 *  Created on: Sep 11, 2014
 *      Author: Jan Feitsma
 */

#include "ext/falconsCommonLegacy.hpp"

#include <stdio.h>
#include <math.h>
#include <float.h>
#include <pwd.h>
#include <cstdio>
#include <string.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <unistd.h>

// TODO most of this stuff should be distributed to dedicated packages,
// for instance angle utilities, Position2D, Velocity2D etc. to geometry

// Source: http://math.stackexchange.com/questions/1201337/finding-the-angle-between-two-points
double angle_between_two_points_0_2pi(double x1, double y1, double x2, double y2)
{

    double angle = std::atan2(y2 - y1, x2 - x1);
    return project_angle_0_2pi(angle);
}

double project_angle_0_2pi(double angle)
{
    // sanity checks
    if ((angle > 100) || (angle < -100))
    {
        throw std::runtime_error("angle out of bounds");
    }
    while (angle < 0) angle += 2*M_PI;
    while (angle > 2*M_PI) angle -= 2*M_PI;
    return angle;
}

float project_angle_0_2pi(float angle)
{
    // sanity checks
    if ((angle > 100) || (angle < -100))
    {
        throw std::runtime_error("angle out of bounds");
    }
    while (angle < 0) angle += 2*M_PI;
    while (angle > 2*M_PI) angle -= 2*M_PI;
    return angle;
}

double project_angle_mpi_pi(double angle)
{
    // sanity checks
    if ((angle > 100) || (angle < -100))
    {
        throw std::runtime_error("angle out of bounds");
    }
    while (angle < -M_PI) angle += 2*M_PI;
    while (angle > M_PI) angle -= 2*M_PI;
    return angle;
}

float project_angle_mpi_pi(float angle)
{
    // sanity checks
    if ((angle > 100) || (angle < -100))
    {
        throw std::runtime_error("angle out of bounds");
    }
    while (angle < -M_PI) angle += 2*M_PI;
    while (angle > M_PI) angle -= 2*M_PI;
    return angle;
}

// position type transformations
Position2D getPosition2D( const geometry::Pose2D& pose)
{
    Position2D myPosition;
    myPosition.x= pose.x;
    myPosition.y= pose.y;
    return ( myPosition );
}

Position2D getPosition2D( const Point3D& point3d)
{
    Position2D myPosition;
    myPosition.x= (double) point3d.x;
    myPosition.y= (double) point3d.y;
    return ( myPosition );
}

// coordinate transformations
Velocity2D& Velocity2D::transform_fcs2rcs(const Position2D& robotpos)
{
    double angle = (M_PI_2 - robotpos.phi);
    Vector2D xynew = Vector2D(x, y).rotate(angle);
    x = xynew.x;
    y = xynew.y;
    // do not update vphi
    return (*this);
}

Velocity2D& Velocity2D::transform_rcs2fcs(const Position2D& robotpos)
{
    double angle = -(M_PI_2 - robotpos.phi);
    Vector2D xynew = Vector2D(x, y).rotate(angle);
    x = xynew.x;
    y = xynew.y;
    // do not update vphi
    return (*this);
}

Position2D& Position2D::transform_fcs2rcs(const Position2D& robotpos)
{
    // first ttranslate, then rotate
    double angle = (M_PI_2 - robotpos.phi);
    Vector2D xynew = (Vector2D(x, y) - robotpos.xy()).rotate(angle);
    x = xynew.x;
    y = xynew.y;
    phi = phi + angle;
    phi = project_angle_0_2pi(phi);
    return (*this);
}

Position2D& Position2D::transform_rcs2fcs(const Position2D& robotpos)
{
    // first rotate, then translate
    double angle = - (M_PI_2 - robotpos.phi);
    Vector2D xyrot = (Vector2D(x, y)).rotate(angle);
    x = xyrot.x + robotpos.x;
    y = xyrot.y + robotpos.y;
    phi = phi + angle;
    phi = project_angle_0_2pi(phi);
    return (*this);
}

Position2D& Position2D::transform_fcs2acs(bool playing_left_to_right)
{
    // no change in case we are playing left to right
    if (!playing_left_to_right)
    {
        // rotate by half a circle
        x = -x;
        y = -y;
        phi = project_angle_0_2pi(phi + M_PI);
    }
    return (*this);
}

Position2D& Position2D::transform_acs2fcs(bool playing_left_to_right)
{
    // no change in case we are playing left to right
    if (!playing_left_to_right)
    {
        // rotate by half a circle
        x = -x;
        y = -y;
        phi = project_angle_0_2pi(phi + M_PI);
    }
    return (*this);
}

Velocity2D& Velocity2D::transform_fcs2acs(bool playing_left_to_right)
{
    // no change in case we are playing left to right
    if (!playing_left_to_right)
    {
        // rotate by half a circle, xy only
        x = -x;
        y = -y;
    }
    return (*this);
}

Velocity2D& Velocity2D::transform_acs2fcs(bool playing_left_to_right)
{
    // no change in case we are playing left to right
    if (!playing_left_to_right)
    {
        // rotate by half a circle, xy only
        x = -x;
        y = -y;
    }
    return (*this);
}

// handy function to return output of a command
// source: http://stackoverflow.com/questions/478898/how-to-execute-a-command-and-get-output-of-command-within-c
std::string exec(const char* cmd) {
    boost::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
    if (!pipe) return "ERROR";
    char buffer[128];
    std::string result = "";
    while (!feof(pipe.get())) {
        if (fgets(buffer, 128, pipe.get()) != NULL)
            result += buffer;
    }
    boost::algorithm::trim(result);
    return result;
}

std::string getProcessId()
{
    return exec("getProcessId");
}

float restrictValue( float a, float minA, float maxA )  //returns restricted value position within given limits
{
    if( a > maxA )
        return maxA;
    if( a < minA )
        return minA;
    return a;
}

bool isValueInRange( float a, float minA, float maxA )  //check if value falls in min-max range
{
    if( a > maxA )
        return false;
    if( a < minA )
        return false;
    return true;
}

double calc_angle(double dX, double dY)
{
    // calculates the absolute angle
    // looking from X1,Y1 towards X2,Y2
    double S = 0.0;
    double th = 0.0;
    S = calc_hypothenusa(dX, dY);

    if (dY < 0) {
        if (dX == 0) {
            th = M_PI_2;
        } else {
            th = acos(-dX / S);
        }
    } else if (dY > 0) {
        if (dX == 0) {
            th = 3.0 / 2.0 * M_PI;
        } else {
            th = 2 * M_PI - acos(-dX / S);
        }
    } else if (dY == 0) {
        if (dX > 0) {
            th = 0;
        } else {
            th = M_PI;
        }
    }
    return th;
}

float calc_angle(float dX, float dY)
{
    // calculates the absolute angle
    // looking from X1,Y1 towards X2,Y2
    float S = 0.0;
    float th = 0.0;

    S = calc_hypothenusa(dX, dY);

    if (dY < 0) {
        if (dX == 0) {
            th = M_PI_2;
        } else {
            th = acos(-dX / S);
        }
    } else if (dY > 0) {
        if (dX == 0) {
            th = 3.0 / 2.0 * M_PI;
        } else {
            th = 2 * M_PI - acos(-dX / S);
        }
    } else if (dY == 0) {
        if (dX > 0) {
            th = 0;
        } else {
            th = M_PI;
        }
    }
    return th;
}

// transform a line segment (v1,v2)
// to a line equation a*x + b*y + c = 0
void calculate_line_equation(Vector2D v1, Vector2D v2, double &a, double &b, double &c)
{
    double dx = v2.x - v1.x;
    double dy = v2.y - v1.y;
    // dx*y - dx*v1.y - dy*x - dy*v1.x = 0
    a = -dy;
    b = dx;
    c = -1.0 * (dx*v1.y - dy*v1.x);
}

double calc_distance_point_line(Vector2D p, double a, double b, double c)
{
    if (a != 0 || b != 0)
    {
        return (a * p.x +  b * p.y + c) / sqrt(a*a + b*b);
    }
    else
    {
        return 0;
    }
}

// based on http://stackoverflow.com/a/385355
bool intersect(Vector2D const &a1, Vector2D const &a2, Vector2D const &b1, Vector2D const &b2, Vector2D &result)
{
    float x12 = a1.x - a2.x;
    float x34 = b1.x - b2.x;
    float y12 = a1.y - a2.y;
    float y34 = b1.y - b2.y;

    float c = x12 * y34 - y12 * x34;

    if (fabs(c) < 0.01)
    {
        // No intersection
        return false;
    }
    // Intersection
    float a = a1.x * a2.y - a1.y * a2.x;
    float b = b1.x * b2.y - b1.y * b2.x;

    result.x = (a * x34 - b * x12) / c;
    result.y = (a * y34 - b * y12) / c;

    return true;
}

double calc_hypothenusa(double dX, double dY)
{
    // Function to calculate the Hypothenusa (Schuine zijde)
    double S = 0.0;
    S = sqrt(pow(dX, 2) + pow(dY, 2));
    return S;
}

float calc_hypothenusa(float dX, float dY)
{
    // Function to calculate the Hypothenusa (Schuine zijde)
    float S = 0.0;
    S = sqrt(pow(dX, 2) + pow(dY, 2));
    return S;
}

double calc_distance( double x1, double y1, double x2, double y2)
{
    double dX=x2-x1;
    double dY=y2-y1;

    return calc_hypothenusa( dX, dY );
};

double calc_distance( Position2D p1, Position2D p2 )
{
    double dX=p2.x - p1.x;
    double dY=p2.y - p1.y;

    return calc_hypothenusa( dX, dY );
}

double calc_distance( Point2D p1, Point2D p2 )
{
    double dX=p2.x - p1.x;
    double dY=p2.y - p1.y;

    return calc_hypothenusa( dX, dY );
}

bool ignoreIfaName(std::string ifa_name)
{
    if (ifa_name == "enp0s31f6") return true; // the port on top-side of CPU-box, located most inward, is reserved for multiCam
    return false;
}

connectionType GetPrimaryIp(char* buffer, size_t buflen)
{
    struct ifaddrs * ifAddrStruct=NULL;
    struct ifaddrs * ifa=NULL;
    void * tmpAddrPtr=NULL;
    bool LANFound = false;
    connectionType retVal = connectionType::INVALID;

    assert(buflen >= INET_ADDRSTRLEN);
    getifaddrs(&ifAddrStruct);

    if(!LANFound)
    {
        for (ifa = ifAddrStruct; ((ifa != NULL) && (!LANFound)); ifa = ifa->ifa_next)
        {
            /* Filter IPV4 addresses */
            if (ifa->ifa_addr->sa_family == AF_INET)
            {

                tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
                char addressBuffer[INET_ADDRSTRLEN];
                inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);

                if((strstr(ifa->ifa_name, "en") != NULL) ||
                    (strstr(ifa->ifa_name, "eth") != NULL))
                {
                    if (!ignoreIfaName(ifa->ifa_name))
                    {
                        /* Copy IP address to output */
                        inet_ntop(AF_INET, tmpAddrPtr, buffer, buflen);

                        /* Alright meow! It is time to stop now Mack */
                        LANFound = true;
                        retVal = connectionType::LAN;
                    }
                }
            }
        }
    }

    for (ifa = ifAddrStruct; ((ifa != NULL) && (!LANFound)); ifa = ifa->ifa_next)
    {
        /* Filter IPV4 addresses */
        if (ifa->ifa_addr->sa_family == AF_INET)
        {

            tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);

            if(strstr(ifa->ifa_name, "wl") != NULL)
            {
                /* Copy IP address to output */
                inet_ntop(AF_INET, tmpAddrPtr, buffer, buflen);

                /* Alright meow! It is time to stop now Mack */
                LANFound = true;
                retVal = connectionType::WAN;
            }
        }
    }

    for (ifa = ifAddrStruct; ((ifa != NULL) && (!LANFound)); ifa = ifa->ifa_next)
    {
        /* Filter IPV4 addresses */
        if (ifa->ifa_addr->sa_family == AF_INET)
        {

            tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);

            if(strstr(ifa->ifa_name, "usb") != NULL)
            {
                /* Copy IP address to output */
                inet_ntop(AF_INET, tmpAddrPtr, buffer, buflen);

                /* Alright meow! It is time to stop now Mack */
                LANFound = true;
                retVal = connectionType::USB;
            }
        }
    }

    if(!LANFound)
    {
        for (ifa = ifAddrStruct; ((ifa != NULL) && (!LANFound)); ifa = ifa->ifa_next)
        {
            /* Filter IPV4 addresses */
            if (ifa->ifa_addr->sa_family == AF_INET)
            {

                tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
                char addressBuffer[INET_ADDRSTRLEN];
                inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);

                if(strstr(ifa->ifa_name, "lo") != NULL)
                {
                    /* Copy IP address to output */
                    inet_ntop(AF_INET, tmpAddrPtr, buffer, buflen);

                    /* Alright meow! It is time to stop now Mack */
                    LANFound = true;
                    retVal = connectionType::LOOPBACK;
                }
            }
        }
    }

    if(!LANFound)
    {
        printf("No adapter found for fetching an IP address");
    }

    if (ifAddrStruct!=NULL)
    {
        freeifaddrs(ifAddrStruct);
    }

    return retVal;
}

connectionType GetPrimaryConnectionType()
{
    struct ifaddrs * ifAddrStruct=NULL;
    struct ifaddrs * ifa=NULL;
    void * tmpAddrPtr=NULL;
    connectionType retVal = connectionType::INVALID;

    getifaddrs(&ifAddrStruct);

    if(retVal == connectionType::INVALID)
    {
        for (ifa = ifAddrStruct; ((ifa != NULL) && (retVal == connectionType::INVALID)); ifa = ifa->ifa_next)
        {
            /* Filter IPV4 addresses */
            if (ifa->ifa_addr->sa_family == AF_INET)
            {

                tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
                char addressBuffer[INET_ADDRSTRLEN];
                inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);

                if((strstr(ifa->ifa_name, "en") != NULL) ||
                    (strstr(ifa->ifa_name, "eth") != NULL))
                {
                    if (!ignoreIfaName(ifa->ifa_name))
                    {
                        /* Alright meow! It is time to stop now Mack */
                        retVal = connectionType::LAN;
                    }
                }
            }
        }
    }

    for (ifa = ifAddrStruct; ((ifa != NULL) && (retVal == connectionType::INVALID)); ifa = ifa->ifa_next)
    {
        /* Filter IPV4 addresses */
        if (ifa->ifa_addr->sa_family == AF_INET)
        {

            tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);

            if(strstr(ifa->ifa_name, "wl") != NULL)
            {
                /* Alright meow! It is time to stop now Mack */
                retVal = connectionType::WAN;
            }
        }
    }

    for (ifa = ifAddrStruct; ((ifa != NULL) && (retVal == connectionType::INVALID)); ifa = ifa->ifa_next)
    {
        /* Filter IPV4 addresses */
        if (ifa->ifa_addr->sa_family == AF_INET)
        {

            tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);

            if(strstr(ifa->ifa_name, "usb") != NULL)
            {
                /* Alright meow! It is time to stop now Mack */
                retVal = connectionType::USB;
            }
        }
    }

    if(retVal == connectionType::INVALID)
    {
        for (ifa = ifAddrStruct; ((ifa != NULL) && (retVal == connectionType::INVALID)); ifa = ifa->ifa_next)
        {
            /* Filter IPV4 addresses */
            if (ifa->ifa_addr->sa_family == AF_INET)
            {

                tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
                char addressBuffer[INET_ADDRSTRLEN];
                inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);

                if(strstr(ifa->ifa_name, "lo") != NULL)
                {
                    /* Alright meow! It is time to stop now Mack */
                    retVal = connectionType::LOOPBACK;
                }
            }
        }
    }

    if(retVal == connectionType::INVALID)
    {
        printf("No adapter found for fetching an IP address");
    }

    if (ifAddrStruct!=NULL)
    {
        freeifaddrs(ifAddrStruct);
    }

    return retVal;
}

/* Double comparison */
bool doubleIsEqual(double a, double b)
{
    return fabs(a - b) < FLT_EPSILON;
}

bool floatIsEqual(float a, float b)
{
    return fabs(a - b) < DBL_EPSILON;
}

// handy function to return output of a command
// source: http://stackoverflow.com/questions/478898/how-to-execute-a-command-and-get-output-of-command-within-c
std::string systemStdout(std::string cmd, int bufferLimit)
{
    std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
    if (!pipe) return "ERROR";
    char buffer[128];
    std::string result = "";
    while (!feof(pipe.get())) {
        if (fgets(buffer, 128, pipe.get()) != NULL)
            result += buffer;
    }
    boost::algorithm::trim(result);
    // limit string length
    if ((int)result.size() > bufferLimit)
    {
        result = result.substr(0, bufferLimit);
    }
    return result;
}


// Copyright 2020 ICLUE @ UIUC. All rights reserved.
#include <coxeter_properness/search.h>

#include <algorithm>
#include <climits>
#include <deque>
#include <numeric>
#include <ostream>
#include <cctype>
#include <stack>
#include <utility>
#include <vector>

#include <coxeter_properness/search.h>

#include <coxeter/constants.h>
#include <coxeter/coxtypes.h>
#include <coxeter/interactive.h>
#include <coxeter/type.h>

#include <boost/math/special_functions/binomial.hpp>

namespace coxeter_properness {

using interactive::CoxGroup;
using coxeter::CoxWord;

auto numLDescents(const CoxGroup& W, const CoxWord& w) -> int32_t {
  auto numDescents = 0;
  for (auto f1 = W.ldescent(w); f1 != 0; f1 &= (f1-1)) {
    // const auto s = bits::firstBit(f1);
    ++numDescents;
  }
  return numDescents;
}

auto choose(const int n, const int k) -> int32_t {
  if (n < k) return 0;
  return static_cast<int32_t>(boost::math::binomial_coefficient<double>(n, k));
}

auto maxw0(const CoxGroup& W, const CoxWord& w) -> int32_t {
  const auto n = W.rank();
  const auto dw = numLDescents(W, w);
  const auto& t = W.type();
  const auto& typeName = t.name();

  if (typeName == io::String("A"))  return choose(dw+1, 2);
  if (typeName == io::String("B")) return dw * dw;
  if (typeName == io::String("D")) return dw > 3 ? dw*(dw-1) : choose(dw+1, 2);
  if (typeName == io::String("E")) {
    const static std::vector<int32_t> m_e = {0, 1, 3, 6, 12, 20, 36, 63, 120};
    return m_e.at(dw);
  }
  if (typeName == io::String("F")) {
    const static std::vector<int32_t> m_f = {0, 1, 4, 9, 24};
    return m_f[dw];
  }
  if (typeName == io::String("G")) {
    const static std::vector<int32_t> m_g = {0, 1, 6};
    return m_g[dw];
  }
  if (typeName == io::String("H")) {
    const static std::vector<int32_t> m_h = {0, 1, 5, 15, 60};
    return m_h[dw];
  }
  if (typeName == io::String("I")) return dw == 2 ? n : dw;

  throw std::invalid_argument("no type matching");
}

bool proper(const CoxGroup& W, const CoxWord& w) {
  const auto n = W.rank();
  return w.length() <= n + maxw0(W, w);
}

Transformer::Transformer(const std::shared_ptr<CoxGroup>& W) : W(W) { }

bool Transformer::parse(const std::string& line, CoxWord* w) {
  elements.clear();
  for (const auto& x : line) {
    // This assumes that the rank of the group is < 10.
    if (std::isdigit(x)) {
      elements.push_back(x-'0');
    }
  }

  if (elements.empty()) return false;

  // Don't trust the coxeter code to handle dynamic resource allocation.
  rawWord.setLength(elements.size());
  for (int i = 0; i < elements.size(); ++i) {
    rawWord[i] = elements[i];
  }

  // W->reduced(*w, rawWord);
  // We can assume that the word is already reduced.
  *w = rawWord;
  return true;
}

}  // namespace coxeter_properness


//
// Created by erik on 9/23/16.
//

#include <cmath>
#include <boost/optional.hpp>
#include "HitTests.h"

using namespace spatacs;

boost::optional<physics::time_t> physics::intersect_origin(const MovingSphere& s)
{
    // x²(t) = r²(t): v² t² + 2 vo t + o² = a²t² + 2 ar t + r²
    // (v² - a²) t² + 2 (vo - ar)t + o² - r² = 0
    // t² + 2 (vo - ar)/(v²-a²) t + (o²-r²)/(v²-a²) = 0
    auto vo = dot(s.vel, s.pos);
    auto ar = s.exp * s.rad;
    auto v2 = dot(s.vel, s.vel);
    auto a2 = s.exp * s.exp;
    auto o2 = dot(s.pos, s.pos);
    auto r2 = s.rad * s.rad;

    // extreme case, need to consider to prevent div by zero
    if(v2 == a2)
    {
        if(vo == ar)
            return boost::none;

        return (r2 - o2) / (vo - ar);
    }

    auto p = 2.0 * (vo - ar) / (v2 - a2);
    auto q = (o2 - r2) / (v2 - a2);
    auto det = p*p/4.0 - q;
    if(det < decltype(det)(0))
    {
        return boost::none;
    }
    auto w = sqrt(det);
    if(-p/2.0 < w)
    {
        return -p/2.0 + w;
    } else
    {
        return -p/2.0 - w;
    }
}

boost::optional<physics::time_t> physics::intersect(MovingSphere s1, const MovingSphere& s2)
{
    s1.pos -= s2.pos;
    s1.vel -= s2.vel;
    s1.rad += s2.rad;
    s1.exp += s2.exp;
    return intersect_origin(s1);
}


/* -*- c++ -*- */
/*
 * Copyright 2013 Jiří Pinkava <j-pi@seznam.cz>.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include "noise_level_estimator2_impl.h"
#include <algorithm>
#include <boost/foreach.hpp>
#include <cmath>
#include <stdexcept>

#include <stdio.h>

namespace gr {
  namespace rstt {

    noise_level_estimator2::sptr
    noise_level_estimator2::make(float coverage, float chunk_size)
    {
      if (!(coverage > 0.))
        throw std::out_of_range("noise_level_estimator2 coverage must be > 0.");
      if (!(coverage <= 1.))
        throw std::out_of_range("noise_level_estimator2 coverage must be <= 1.");
      if (!(chunk_size > 0.))
        throw std::out_of_range("noise_level_estimator2 chunk_size must be > 0.");
      if (!(chunk_size <= coverage))
        throw std::out_of_range("noise_level_estimator2 chunk_size must be <= coverage");
      return sptr(new noise_level_estimator2_impl(coverage, chunk_size));
    }

    noise_model
    noise_level_estimator2_impl::estimate(const float *data, int data_items) const
    {
      const int coverage = data_items * this->coverage;
      const int chunk_size = data_items * this->chunk_size;

      const int ms_size = data_items - chunk_size + 1;
      float mean[ms_size];
      float mean2[ms_size];

      // moving average, first point
      mean[0] = 0.;
      mean2[0] = 0.;
      for (int i = 0; i < chunk_size; ++i) {
        mean[0] += data[i];
        mean2[0] += data[i] * data[i];
      }

      // moving average, other points
      for (int i = chunk_size, j = 1; i < data_items; ++i, ++j) {
       mean[j] = mean[j-1] - data[j-1] + data[i];
       mean2[j] = mean2[j-1] - (data[j-1] * data[j-1]) + (data[i] * data[i]);
      }

      for (int i = 0; i < ms_size; ++i) {
        mean[i] /= chunk_size;
        mean2[i] /= chunk_size;
      }

      // moving standard deviation (s^2)
      float s2[ms_size];
      for (int i = 0; i < ms_size; ++i) {
        s2[i] = mean2[i] - mean[i] * mean[i];
      }

      // get the average of lower bound values for mean and deviation
      std::sort(s2, s2 + ms_size);
      float s = 0.;
      for (int i = 0; i < coverage; ++i) {
        s += std::sqrt(s2[i]);
      }
      s /= coverage;

      std::sort(mean, mean + ms_size);
      float m = 0.;
      for (int i = 0; i < coverage; ++i) {
        m += mean[i];
      }
      m /= coverage;

      return noise_model(m, s);
    }

  } // namespace rstt
} // namespace gr


//  (C) Copyright John Maddock 2007.
//  Use, modification and distribution are 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 <boost/limits.hpp>
#include <vector>
#include "mp_t.hpp"

void write_table(unsigned max_exponent)
{
   mp_t max = ldexp(mp_t(1), (int)max_exponent);

   std::vector<mp_t> factorials;
   factorials.push_back(1);

   mp_t f(1);
   unsigned i = 1;

   while(f < max)
   {
      factorials.push_back(f);
      ++i;
      f *= i;
   }

   //
   // now write out the results to cout:
   //
   std::cout << std::scientific << std::setprecision(40);
   std::cout << "   static const std::array<T, " << factorials.size() << "> factorials = {\n";
   for(unsigned j = 0; j < factorials.size(); ++j)
      std::cout << "      " << factorials[j] << "L,\n";
   std::cout << "   };\n\n";
}

int main()
{
   write_table(16384/*std::numeric_limits<float>::max_exponent*/);
}


// vi: set et ts=4 sw=2 sts=2:

#ifndef HMAT_HMATRIX_DENSE_DATA_HPP
#define HMAT_HMATRIX_DENSE_DATA_HPP

#include "common.hpp"
#include "hmatrix_data.hpp"
#include <armadillo>

namespace hmat {

template <typename ValueType>
class HMatrixDenseData : public HMatrixData<ValueType> {
public:
  void apply(const arma::Mat<ValueType> &X, arma::Mat<ValueType> &Y,
             TransposeMode trans, ValueType alpha, ValueType beta) const
      override;

  void apply(const arma::subview<ValueType> &X, arma::subview<ValueType> &Y,
             TransposeMode trans, ValueType alpha, ValueType beta) const
      override;

  const arma::Mat<ValueType> &A() const;
  arma::Mat<ValueType> &A();

  int rows() const override;
  int cols() const override;
  int rank() const override;

  typename ScalarTraits<ValueType>::RealType frobeniusNorm() const override;

  double memSizeKb() const override;

private:
  arma::Mat<ValueType> m_A;
};
}

#include "hmatrix_dense_data_impl.hpp"

#endif


#include <algorithm>
#include <vector>
#include <list>
#include <queue>
#include <string>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <functional>
#include <iostream>
#include <sstream>
#include <cctype>
#include <cmath>
//#include <boost/multiprecision/cpp_int.hpp>

using namespace std;
//using namespace boost::multiprecision;

vector<vector<int>> g;
vector<bool> f;
int N, root;

int bitCount(int mask){
	int res = 0;
	while (mask){
		mask &= mask - 1;
		++res;
	}
	return res;
}

bool dfs(int prev, int cur, int mask){
	if (mask & (1 << cur))
		return false;

	f[cur] = true;

	int count = 0;
	for (int j : g[cur]){
		if (j != prev){
			if (mask & (1 << j)){
			}
			else{
				++count;
				if (!dfs(cur, j, mask))
					return false;
			}
		}
	}

	return count == 0 || count == 2;
}

bool calc(int mask){
	for (int i = 0; i < N; ++i){
		f.assign(N, false);
		if (dfs(-1, i, mask)){
			bool ok = true;
			for (int i = 0; i < N; ++i){
				if (!f[i] && !((1<<i) & mask)){
					ok = false;
					break;
				}
			}
			if (ok)
				return true;
		}
	}
	return false;
}

void comp(int tc){
	N;
	cin >> N;

	g.assign(N, vector<int>());

	for (int i = 0; i < N - 1; ++i){
		int a, b;
		cin >> a >> b;
		g[a - 1].push_back(b - 1);
		g[b - 1].push_back(a - 1);
	}

	int best = N;
	for (int i = 0; i < (1<<N); ++i){
		if (calc(i))
			best = min(best, bitCount(i));
	}

	cout << "Case #" << tc << ": " << best << endl;

}

int main(){
	int T;
	cin >> T;
	for (int tc = 1; tc <= T; ++tc){
		comp(tc);
	}
}


#ifndef Evaluate1D_hpp
#define Evaluate1D_hpp

#include <QVector>
#include <alglib/ap.h>
#include <alglib/interpolation.h>
#include <algorithm>
#include <cmath>
#include <QAlgorithm.hpp>
#include <UMF/CurveNormalization.hpp>

namespace UMF {

	class Evaluate1D : public QAlgorithm {

		Q_OBJECT

		QA_INPUT(QVector<double>, X)
		QA_OUTPUT(QVector<double>, Y)
		QA_PARAMETER(QVector<double>, Coefficients, QVector<double>())

		QA_CTOR_INHERIT

	public:
		typedef void(*EvalFuncType)(const alglib::real_1d_array&, const alglib::real_1d_array&, double&, void*);

	protected:
		EvalFuncType eval = Q_NULLPTR;

	public:
		void run() final;

		EvalFuncType getInternalFunc() const {return eval;};

		QVector<double> evaluate(QVector<double> C,
								 QVector<double> X);
	};

	class EvaluateGaussExp : public Evaluate1D {

		Q_OBJECT

		QA_IMPL_CREATE(EvaluateGaussExp)

	public:
		using Evaluate1D::Evaluate1D;

		void init();
	};

	class EvaluateGauss : public Evaluate1D {

		Q_OBJECT

		QA_IMPL_CREATE(EvaluateGauss)

	public:
		using Evaluate1D::Evaluate1D;

		void init();
	};

	class Fitting1D : public QAlgorithm {

		Q_OBJECT

		QA_INPUT(QVector<double>, X)
		QA_INPUT(QVector<double>, Y)
		QA_OUTPUT(QVector<double>, Coefficients)
		QA_OUTPUT(double, AvgError)
		QA_OUTPUT(double, AvgRelError)

		QA_CTOR_INHERIT

	protected:
		QSharedPointer<Evaluate1D> evaluator;

	public:
		void run();

	Q_SIGNALS:
		Q_SIGNAL void fittingReady(QVector<double> Parameters,
								   double min, double max,
								   double avgerr, double avgrelerr);
	};

	class FittingGauss: public Fitting1D {

		Q_OBJECT

		QA_IMPL_CREATE(FittingGauss)

	public:
		using Fitting1D::Fitting1D;

		void init();
	};

	class FittingGaussExp: public Fitting1D {

		Q_OBJECT

		QA_IMPL_CREATE(FittingGaussExp)

	public:
		using Fitting1D::Fitting1D;

		void init();
	};
}

#endif /* Evaluate1D_hpp */


// Copyright (c) 2021 Graphcore Ltd. All rights reserved.
#define BOOST_TEST_MODULE CholeskyTest
#include <poplibs_support/TestDevice.hpp>

#include <iostream>

#include <boost/test/data/monomorphic/generators/xrange.hpp>
#include <boost/test/data/test_case.hpp>
#include <boost/test/unit_test.hpp>

#include <poplar/Engine.hpp>
#include <poplin/Cholesky.hpp>
#include <poplin/MatMul.hpp>
#include <poplin/codelets.hpp>
#include <popops/codelets.hpp>
#include <poputil/TileMapping.hpp>
#include <poputil/exceptions.hpp>

using namespace poplar;
using namespace poplar::program;
using namespace poputil;
using namespace poplin;
using namespace poplibs_support;
namespace bu = boost::unit_test;
namespace bud = boost::unit_test::data;

namespace {

std::vector<float> createTriMat(std::size_t numBatches, std::size_t N,
                                bool lower, float batchIncrement = 100.0f) {
  float start = 1;
  std::vector<float> v(numBatches * N * N);
  for (std::size_t b = 0, k = 0; b < numBatches; ++b, k += N * N) {
    float f = start;
    for (std::size_t i = 0; i < N; i++) {
      for (std::size_t j = 0; j < N; j++, f += 1.0f) {
        // Generate the same values (tranposed)
        if (lower)
          v[k + i * N + j] = j > i ? 0 : f;
        else
          v[k + j * N + i] = j > i ? 0 : f;
      }
    }
    start += batchIncrement;
  }
  return v;
}

} // namespace

BOOST_DATA_TEST_CASE(CholeskyTest,
                     bud::make({20, 32}) * bud::make({16}) * bud::make({1, 2}) *
                         bud::make({true, false}),
                     N_, blockSize_, numBatches_, lower_) {
  std::size_t N = N_;
  std::size_t blockSize = blockSize_;
  std::size_t numBatches = numBatches_;
  bool lower = lower_;

  std::cout << "Running test: N = " << N << ", lower = " << lower
            << ", blockSize = " << blockSize << ", numBatches = " << numBatches
            << std::endl;

  auto device = createTestDevice(TEST_TARGET, 1, 4);
  auto &target = device.getTarget();

  Graph graph(target);
  popops::addCodelets(graph);
  poplin::addCodelets(graph);

  std::vector<float> Tv = createTriMat(numBatches, N, lower);

  Sequence prog;

  auto T = graph.addVariable(poplar::FLOAT, {numBatches, N, N}, "T");
  poputil::mapTensorLinearly(graph, T);
  graph.createHostWrite("T", T);

  auto A =
      lower ? poplin::matMulGrouped(graph, T, poplin::transposeGroupedMatrix(T),
                                    prog, T.elementType())
            : poplin::matMulGrouped(graph, poplin::transposeGroupedMatrix(T), T,
                                    prog, T.elementType());
  graph.createHostRead("A", A);

  poplar::OptionFlags options{{"blockSize", std::to_string(blockSize)}};
  auto matmulOptPairs = getCholeskyMatMulPrePlanParameters(
      A.elementType(), A.shape(), lower, options);

  std::set<MatMulPlanParams> params;
  for (auto &pair : matmulOptPairs)
    params.emplace(&target, pair.first, &pair.second);
  matmul::PlanningCache cache;
  preplanMatMuls(params, cache);
  BOOST_TEST(cache.size() == matmulOptPairs.size());

  if (N > blockSize) {
    BOOST_TEST(cache.size() > 0);
  } else {
    BOOST_TEST(cache.size() == 0);
  }

  poplar::DebugContext debugContext;

  auto T2 =
      poplin::cholesky(graph, A, lower, prog, debugContext, options, &cache);
  BOOST_TEST(cache.size() == matmulOptPairs.size());

  auto A2 =
      lower
          ? poplin::matMulGrouped(graph, T2, poplin::transposeGroupedMatrix(T2),
                                  prog, T2.elementType())
          : poplin::matMulGrouped(graph, poplin::transposeGroupedMatrix(T2), T2,
                                  prog, T2.elementType());
  graph.createHostRead("A2", A2);

  Engine eng(graph, prog);
  device.bind([&](const Device &d) {
    eng.load(d);
    eng.writeTensor("T", Tv.data(), Tv.data() + Tv.size());
    eng.run();

    std::vector<float> Av(Tv.size());
    eng.readTensor("A", Av.data(), Av.data() + Av.size());

    std::vector<float> A2v(Tv.size());
    eng.readTensor("A2", A2v.data(), A2v.data() + A2v.size());

    for (std::size_t i = 0; i < Av.size(); i++) {
      BOOST_REQUIRE_CLOSE(Av[i], A2v[i], 0.001f);
    }
  });
}


#include <Eigen/Core>
#include <boost/python.hpp>
#include <boost/numpy.hpp>

namespace boopy
{
  namespace bpn = boost::numpy;
  namespace bp = boost::python;

  struct Eigenvec_to_python_matrix
  {
    static PyObject* convert(Eigen::VectorXd const& v)
    {
      Py_intptr_t shape[1] = { v.size() };
      bpn::matrix result(bpn::zeros(1, shape, bpn::dtype::get_builtin<double>()));
      std::copy(v.data(), v.data()+v.size(), reinterpret_cast<double*>(result.get_data()));
      return bp::incref(result.ptr());
    }
  };

  struct Eigenvec_from_python_array
  {
    Eigenvec_from_python_array()
    {
      bp::converter::registry
	::push_back(&convertible,
		    &construct,
		    bp::type_id<Eigen::VectorXd>());
    }

    // Determine if obj_ptr can be converted in a Eigenvec
    static void* convertible(PyObject* obj_ptr)
    {

      try {
	bp::object obj(bp::handle<>(bp::borrowed(obj_ptr)));
	std::auto_ptr<bpn::ndarray>
	  array(new bpn::ndarray(bpn::from_object(obj,
						  bpn::dtype::get_builtin<double>(),
						  bpn::ndarray::V_CONTIGUOUS)));

	if( (array->get_nd()==1)
	    || ( (array->get_nd()==2) && (array->get_shape()[1]==1) ))
	  return array.release();
	else
	  return 0;
      } catch (bp::error_already_set & err) {
	bp::handle_exception();
	return 0;
      }
    }

    // Convert obj_ptr into a Eigenvec
    static void construct(PyObject* ,
			  bp::converter::rvalue_from_python_stage1_data* memory)
    {
      // Recover the pointer created in <convertible>
      std::auto_ptr<bpn::ndarray>
	array(reinterpret_cast<bpn::ndarray*>(memory->convertible));
      const int nrow = array->get_shape()[0];
      std::cout << "nrow = " << nrow << std::endl;

      // Get the memory where to create the vector
      void* storage
	= ((bp::converter::rvalue_from_python_storage<Eigen::VectorXd>*)memory)
	->storage.bytes;

      // Create the vector
      Eigen::VectorXd & res = * new (storage) Eigen::VectorXd(nrow);

      // Copy the data
      double * data = (double*)array->get_data();
      for(int i=0;i<nrow;++i)
	res[i] = data[i];

      // Stash the memory chunk pointer for later use by boost.python
      memory->convertible = storage;
    }
  };
}

Eigen::VectorXd test()
{
  Eigen::VectorXd v = Eigen::VectorXd::Random(5);
  std::cout << v.transpose() << std::endl;
  return v;
}

void test2( Eigen::VectorXd v )
{
  std::cout << "test2: dim = " << v.size() << " ||| v[0] = " << v[0] << std::endl;
}

BOOST_PYTHON_MODULE(libeigen)
{
  namespace bpn = boost::numpy;
  namespace bp = boost::python;

  bpn::initialize();
  bp::to_python_converter<Eigen::VectorXd,
			  boopy::Eigenvec_to_python_matrix>();
  boopy::Eigenvec_from_python_array();

  bp::def("test", test);
  bp::def("test2", test2);
}


/*
@copyright Louis Dionne 2015
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/assert.hpp>
#include <boost/hana/bool.hpp>
#include <boost/hana/config.hpp>
#include <boost/hana/maybe.hpp>
using namespace boost::hana;

//! [applicative]
template <char op>
constexpr auto function = nothing;

template <>
BOOST_HANA_CONSTEXPR_LAMBDA auto function<'+'> = just([](auto x, auto y) {
    return x + y;
});

template <>
BOOST_HANA_CONSTEXPR_LAMBDA auto function<'-'> = just([](auto x, auto y) {
    return x - y;
});

// and so on...

template <char n>
BOOST_HANA_CONSTEXPR_LAMBDA auto digit = if_(bool_<(n >= '0' && n <= '9')>,
    just(static_cast<int>(n - 48)),
    nothing
);

template <char x, char op, char y>
BOOST_HANA_CONSTEXPR_LAMBDA auto evaluate = ap(function<op>, digit<x>, digit<y>);

int main() {
    BOOST_HANA_CONSTEXPR_CHECK(evaluate<'1', '+', '2'> == just(1 + 2));

    BOOST_HANA_CONSTANT_CHECK(evaluate<'?', '+', '2'> == nothing);
    BOOST_HANA_CONSTANT_CHECK(evaluate<'1', '?', '2'> == nothing);
    BOOST_HANA_CONSTANT_CHECK(evaluate<'1', '+', '?'> == nothing);
    BOOST_HANA_CONSTANT_CHECK(evaluate<'?', '?', '?'> == nothing);

    BOOST_HANA_CONSTEXPR_CHECK(lift<Maybe>(123) == just(123));
}
//! [applicative]


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

#define BOOST_TEST_MODULE element_fp2_test

#include <chrono>
#include <boost/test/unit_test.hpp>

#include <nil/crypto3/algebra/curves/mnt4.hpp>
#include <nil/crypto3/algebra/fields/mnt4/base_field.hpp>
#include <nil/crypto3/algebra/fields/mnt4/scalar_field.hpp>
#include <nil/crypto3/algebra/fields/arithmetic_params/mnt4.hpp>
#include <nil/crypto3/algebra/curves/params/multiexp/mnt4.hpp>
#include <nil/crypto3/algebra/curves/params/wnaf/mnt4.hpp>
#include <nil/crypto3/algebra/curves/mnt6.hpp>
#include <nil/crypto3/algebra/fields/mnt6/base_field.hpp>
#include <nil/crypto3/algebra/fields/mnt6/scalar_field.hpp>
#include <nil/crypto3/algebra/fields/arithmetic_params/mnt6.hpp>
#include <nil/crypto3/algebra/curves/params/multiexp/mnt6.hpp>
#include <nil/crypto3/algebra/curves/params/wnaf/mnt6.hpp>
#include <nil/crypto3/algebra/random_element.hpp>

#include <nil/crypto3/zk/components/blueprint.hpp>

#include <nil/crypto3/zk/components/algebra/fields/element_fp2.hpp>

#include "arithmetic.hpp"

#include "../../verify_r1cs_scheme.hpp"

using namespace nil::crypto3;
using namespace nil::crypto3::zk;
using namespace nil::crypto3::algebra;

BOOST_AUTO_TEST_SUITE(field_element_arithmetic_component_test_suite)

BOOST_AUTO_TEST_CASE(field_element_mul_component_test_mnt4_case) {
    using curve_type = typename curves::mnt4<298>;
    using field_type = typename curve_type::template g2_type<>::field_type;
    using base_field_type = typename curve_type::base_field_type;

    std::size_t tries_quantity = 10;
    std::cout << "Starting element Fp2 mul component test for MNT4-298 " << tries_quantity << " times ..." << std::endl;
    auto begin = std::chrono::high_resolution_clock::now();

    for (std::size_t i = 0; i < tries_quantity; i++){
        typename field_type::value_type a_value = random_element<field_type>();
        typename field_type::value_type b_value = random_element<field_type>();

        components::blueprint<base_field_type> bp = test_field_element_mul<field_type,
            components::element_fp2,
            components::element_fp2_mul>(a_value, b_value);

        BOOST_CHECK(bp.is_satisfied());

        BOOST_CHECK(verify_component<typename curve_type::chained_on_curve_type>(bp));
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
    std::cout << "Element Fp2 mul component test for MNT4-298 finished, average time: " << elapsed.count() * 1e-9 / tries_quantity << std::endl;
}

BOOST_AUTO_TEST_CASE(field_element_squared_component_test_mnt4_case) {
    using curve_type = typename curves::mnt4<298>;
    using field_type = typename curve_type::template g2_type<>::field_type;
    using base_field_type = typename curve_type::base_field_type;

    std::size_t tries_quantity = 10;
    std::cout << "Starting element Fp2 squared component test for MNT4-298 " << tries_quantity << " times ..." << std::endl;
    auto begin = std::chrono::high_resolution_clock::now();

    for (std::size_t i = 0; i < tries_quantity; i++){
        typename field_type::value_type a_value = random_element<field_type>();

        components::blueprint<base_field_type> bp = test_field_element_squared<field_type,
            components::element_fp2,
            components::element_fp2_squared>(a_value);

        BOOST_CHECK(bp.is_satisfied());

        BOOST_CHECK(verify_component<typename curve_type::chained_on_curve_type>(bp));
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
    std::cout << "Element Fp2 squared component test for MNT4-298 finished, average time: " << elapsed.count() * 1e-9 / tries_quantity << std::endl;
}

BOOST_AUTO_TEST_SUITE_END()