/* =========================================================================
   Copyright (c) 2010-2014, Institute for Microelectronics,
                            Institute for Analysis and Scientific Computing,
                            TU Wien.
   Portions of this software are copyright by UChicago Argonne, LLC.

                            -----------------
                  ViennaCL - The Vienna Computing Library
                            -----------------

   Project Head:    Karl Rupp                   rupp@iue.tuwien.ac.at

   (A list of authors and contributors can be found in the PDF manual)

   License:         MIT (X11), see file LICENSE in the base directory
============================================================================= */

/* Computation of eigenvalues of a symmetric, tridiagonal matrix using
 * bisection.
 */

#ifndef NDEBUG
  #define NDEBUG
#endif

// includes, system
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

// includes, project

#include "viennacl/scalar.hpp"
#include "viennacl/vector.hpp"
#include "viennacl/matrix.hpp"

#include "viennacl/linalg/bisect_gpu.hpp"
#include "viennacl/linalg/bisect.hpp"
#include "viennacl/linalg/tql2.hpp"

#include <examples/benchmarks/benchmark-utils.hpp>
//#include <Eigen/Eigenvalues>
//using namespace Eigen;

#define EPS 10.0e-4

typedef float NumericT;

////////////////////////////////////////////////////////////////////////////////
/// \brief initInputData   Initialize the diagonal and superdiagonal elements of
///                        the matrix
/// \param diagonal        diagonal elements of the matrix
/// \param superdiagonal   superdiagonal elements of the matrix
/// \param mat_size        Dimension of the matrix
///
void
initInputData(viennacl::vector<NumericT> &diagonal, viennacl::vector<NumericT> &superdiagonal, const unsigned int mat_size)
{

  srand(time(NULL));

#define RANDOM_VALUES 0
  if (RANDOM_VALUES == true)
  {
    // Initialize diagonal and superdiagonal elements with random values
    for (unsigned int i = 0; i < mat_size; ++i)
    {
        diagonal[i] =      static_cast<NumericT>(2.0 * (((double)rand()
                                     / (double) RAND_MAX) - 0.5));
        superdiagonal[i] = static_cast<NumericT>(2.0 * (((double)rand()
                                     / (double) RAND_MAX) - 0.5));
    }
  }
  else
  {
    // Initialize diagonal and superdiagonal elements with modulo values
    // This will cause in many multiple eigenvalues.
    for (unsigned int i = 0; i < mat_size; ++i)
    {
       diagonal[i] = ((NumericT)(i % 37)) - 4.5f;
       superdiagonal[i] = ((NumericT)(i % 5)) - 4.5f;
    }
  }
  // the first element of s is used as padding on the device (thus the
  // whole vector is copied to the device but the kernels are launched
  // with (s+1) as start address
  superdiagonal[0] = 0.0f;
}

////////////////////////////////////////////////////////////////////////////////
//! Run a simple test
////////////////////////////////////////////////////////////////////////////////
bool
runTest(const int mat_size, std::vector<double> &av_time_all, unsigned int time_index)
    {
    bool bResult = false;
    viennacl::vector<NumericT> diagonal(mat_size);
    viennacl::vector<NumericT> superdiagonal(mat_size);
    viennacl::vector<NumericT> eigenvalues_bisect(mat_size);
    std::vector<NumericT> eigenvalues_bisect_cpu(mat_size);

    // -------Start the bisection algorithm------------
    std::cout << "Matrix size: " << mat_size << std::endl;

    unsigned int iterations = 10;
    double time_all     = 0.0;

    for(unsigned int i = 0; i < iterations; i++)
    {
      initInputData(diagonal, superdiagonal, mat_size);

      Timer timer;
      timer.start();

      // bisection - gpu
      bResult = viennacl::linalg::bisect(diagonal, superdiagonal, eigenvalues_bisect);
      viennacl::backend::finish();     // sync
      //---Run the tql algorithm-----------------------------------
     // viennacl::linalg::tql1<NumericT>(mat_size, diagonal, superdiagonal);
     // bResult = true;

      // Run the bisect algorithm for CPU only
      //eigenvalues_bisect_cpu = viennacl::linalg::bisect(diagonal, superdiagonal);
      // bResult = true;

      time_all     += timer.get() * 1000;
      if (bResult == false)
       return false;

    }

    std::cout << "Time: \t" << time_all / (double)iterations << "ms" << std::endl << std::endl;

    av_time_all[time_index] = time_all / (double)iterations;

  return bResult;

}

////////////////////////////////////////////////////////////////////////////////
// Program main
////////////////////////////////////////////////////////////////////////////////
int
main(int argc, char **argv)
{
    bool test_result = true;
    unsigned int time_index = 0;
    std::vector<double> av_time_all(500);
    std::vector<unsigned int> mat_sizes(500);

    for( unsigned int mat_size = 16;
         mat_size < 600;
         mat_size = mat_size * 1.15, time_index++)
      {
      test_result = runTest(mat_size, av_time_all, time_index);
      mat_sizes[time_index] = mat_size;

      if(test_result == true)
      {
        //std::cout << "Success!" << std::endl << std::endl;
      }
      else
      {
        std::cout << "---FAIL---" << std::endl;
        exit(EXIT_FAILURE);
      }
    }

    std::cout << "Times" << std::endl;
    for(unsigned int i = 0; i < time_index; i++)
    {
      std::cout <<  mat_sizes[i] << "\t" << av_time_all[i] << std::endl;
    }

}


#define BOOST_TEST_MODULE VectorFractionsTest

#include "VectorFractions.hpp"
#include <vector>
#include <tuple>

#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include <boost/algorithm/string.hpp>

BOOST_AUTO_TEST_SUITE(VectorFractionsSuite)

using namespace com::github::nimelo;
using namespace std;

BOOST_AUTO_TEST_CASE(ShouldCorrectlyReturnStats)
{
    // Arrange
    vector<int> elements = { -4, 3, -9, 0, 4 , 1 };

    VectorFractions<int> vf(elements);

    // Act
    auto stats = vf.getStats();

    // Assert
    BOOST_CHECK_EQUAL(2, std::get<0>(stats));
    BOOST_CHECK_EQUAL(1, std::get<1>(stats));
    BOOST_CHECK_EQUAL(3, std::get<2>(stats));
}

BOOST_AUTO_TEST_CASE(ShouldCorrectlyReturnFractions)
{
    // Arrange
    vector<int> elements = { -4, 3, -9, 0, 4 , 1 };

    VectorFractions<int> vf(elements);

    // Act
    auto fractions = vf.getFractions();

    // Assert
    BOOST_CHECK_CLOSE(2.0 / 6, std::get<0>(fractions), 0.001);
    BOOST_CHECK_CLOSE(1.0 / 6, std::get<1>(fractions), 0.001);
    BOOST_CHECK_CLOSE(3.0 / 6, std::get<2>(fractions), 0.001);
}

BOOST_AUTO_TEST_SUITE_END()


#include <iostream>

#include <Eigen/Sparse>
#include <Eigen/Dense>

#include <unsupported/Eigen/SparseExtra> // For reading MatrixMarket files

#include <amgcl/backend/eigen.hpp>
#include <amgcl/make_solver.hpp>
#include <amgcl/solver/bicgstab.hpp>
#include <amgcl/amg.hpp>
#include <amgcl/coarsening/smoothed_aggregation.hpp>
#include <amgcl/relaxation/spai0.hpp>

int main(int argc, char *argv[]) {
    if (argc < 2) {
        std::cerr << "Usage: " << argv[0] << " <matrix.mm>" << std::endl;
        return 1;
    }

    // Read sparse matrix from MatrixMarket format.
    // In general this should come pre-assembled.
    Eigen::SparseMatrix<double, Eigen::RowMajor> A;
    Eigen::loadMarket(A, argv[1]);

    // Use vector of ones as RHS for simplicity:
    Eigen::VectorXd f = Eigen::VectorXd::Constant(A.rows(), 1.0);

    // Zero initial approximation:
    Eigen::VectorXd x = Eigen::VectorXd::Zero(A.rows());

    // Setup the solver:
    typedef amgcl::make_solver<
        amgcl::amg<
            amgcl::backend::eigen<double>,
            amgcl::coarsening::smoothed_aggregation,
            amgcl::relaxation::spai0
            >,
        amgcl::solver::bicgstab<amgcl::backend::eigen<double> >
        > Solver;

    Solver solve(A);
    std::cout << solve << std::endl;

    // Solve the system for the given RHS:
    int    iters;
    double error;
    std::tie(iters, error) = solve(f, x);

    std::cout << iters << " " << error << std::endl;
}


#include <Eigen/Dense>
#include <iostream>
#include "mc_weights.c"
#include "mc_bias.c"
using namespace Eigen;

class weights
{
public:
	Eigen::Matrix<float,LAYER_1_WEIGHT_ROWS, LAYER_1_WEIGHT_COLS> layer_1_weight;
	Eigen::Matrix<float,LAYER_2_WEIGHT_ROWS, LAYER_2_WEIGHT_COLS> layer_2_weight;
	Eigen::Matrix<float,LAYER_3_WEIGHT_ROWS, LAYER_3_WEIGHT_COLS> layer_3_weight;

	weights (){
		layer_1_weight << LAYER_1_WEIGHT;
		layer_2_weight << LAYER_2_WEIGHT;
		layer_3_weight << LAYER_3_WEIGHT;
	}
};


#include "opencv2/opencv.hpp"
#include "opencv2/core/utility.hpp"

#include "opencv2/core/core_c.h" // shame, but needed for using dlib
#include <dlib/image_processing.h>
#include <dlib/opencv/cv_image.h>
#include <dlib/all/source.cpp>
//using namespace dlib; // ** don't ever try to**

using namespace cv; // this has to go later than the dlib includes

#include <iostream>
#include <vector>
using namespace std;

#include "profile.h"
#include "frontalizer.h"

struct FrontalizerImpl : public Frontalizer
{
    const dlib::shape_predictor &sp;
    const bool DEBUG_IMAGES;
    const double symBlend;
    const int symThresh;
    const int crop;

    Mat mdl;
    Mat_<double> eyemask;
    vector<Point3d> pts3d;

    FrontalizerImpl(const dlib::shape_predictor &sp, int crop, int symThreshold, double symBlend, bool debug_images)
        : sp(sp)
          , crop(crop)
          , symThresh(symThreshold)
          , symBlend(symBlend)
          , DEBUG_IMAGES(debug_images)
    {
        PROFILEX("Frontalizer")

            // model is rotated 90� already, but still in col-major, right hand coords
            FileStorage fs("data/mdl.yml.gz", FileStorage::READ);
        fs["mdl"] >> mdl;
        fs["eyemask"] >> eyemask;
        blur(eyemask,eyemask,Size(4,4));

        //// if you want to see the 3d model ..
        //Mat ch[3];
        //split(mdl, ch);
        //Mat_<double> depth;
        //normalize(ch[1], depth, -100);
        //imshow("head1", depth);

        // get 2d reference points from image
        vector<Point2d> pts2d;
        Mat meanI = imread("data/reference_320_320.png", 0);
        getkp2d(meanI, pts2d, Rect(80,80, 160,160));

        // get 3d reference points from model
        for(size_t k=0; k<pts2d.size(); k++)
        {
            Vec3d pm = mdl.at<Vec3d>(int(pts2d[k].y), int(pts2d[k].x));
            Point3d p(pm[0], pm[2], -pm[1]);
            pts3d.push_back(p);
        }
    }

    //
    // mostly stolen from Roy Shilkrot's HeadPosePnP
    //
    Mat pnp(const Size &s, vector<Point2d> &pts2d) const
    {
        PROFILEX("pnp")
            // camMatrix based on img size
            int max_d = std::max(s.width,s.height);
        Mat camMatrix = (Mat_<double>(3,3) <<
                max_d,   0, s.width/2.0,
                0,	 max_d, s.height/2.0,
                0,   0,	    1.0);

        // 2d -> 3d correspondence
        Mat rvec,tvec;
        solvePnP(pts3d, pts2d, camMatrix, Mat(1,4,CV_64F,0.0), rvec, tvec, false, SOLVEPNP_EPNP);
        cerr << "rot " << rvec.t() *180/CV_PI << endl;
        cerr << "tra " << tvec.t() << endl;
        // get 3d rot mat
        Mat rotM(3, 3, CV_64F);
        Rodrigues(rvec, rotM);

        // push tvec to transposed Mat
        Mat rotMT = rotM.t();
        rotMT.push_back(tvec.reshape(1, 1));

        // transpose back, and multiply
        return camMatrix * rotMT.t();
    }

    //! expects grayscale img
    void getkp2d(const Mat &I, vector<Point2d> &pts2d, const Rect &r) const
    {
        PROFILEX("getkp2d");
        dlib::rectangle rec(r.x, r.y, r.x+r.width, r.y+r.height);
        dlib::full_object_detection shape = sp(dlib::cv_image<uchar>(I), rec);

        for(size_t k=0; k<shape.num_parts(); k++)
        {
            Point2d p(shape.part(k).x(), shape.part(k).y());
            pts2d.push_back(p);
        }
    }

    inline Mat1d project_vec(const Mat &KP, const Vec3d & p) const
    {
        return  KP * (Mat1d)Matx41d(p[0], p[2], -p[1], 1.0); // swizzle to left-handed coords (from matlab's)
    }
    inline Mat1d project_vec(const Mat &KP, int row, int col) const
    {
        return  project_vec(KP, mdl.at<Vec3d>(row, col));
    }

    //
    // thanks again, Haris. i wouldn't be anywhere without your mind here.
    //
    MatResults project3d(const Mat & test, const Mat & testColor) const
    {
        PROFILEX("project3d");

        MatResults result = MatResults();

        int mid = mdl.cols/2;
        int midi = test.cols/2;
        Rect R(mid-crop/2,mid-crop/2,crop,crop);
        Rect Ri(midi-crop/2,midi-crop/2,crop,crop);

        cout << "R: " << R << endl;
        cout << "Ri: " << Ri << endl;
        cout << "mdl.size(): " << mdl.size() << endl;

        // get landmarks
        vector<Point2d> pts2d;
        getkp2d(test, pts2d, Ri);

        // get pose mat for our landmarks
        Mat KP = pnp(test.size(), pts2d);
        // Store the projection matrix.
        result.matrices.push_back(KP);

        // project img to head, count occlusions
        Mat_<uchar> test2(mdl.size(),127);
        Mat_<uchar> counts(mdl.size(),0);
        Mat test2Color(mdl.rows, mdl.cols, CV_8UC3);
        for (int i=R.y; i<R.y+R.height; i++)
        {
            PROFILEX("proj_1");
            for (int j=R.x; j<R.x+R.width; j++)
            {
                Mat1d p = project_vec(KP, i, j);
                int x = int(p(0) / p(2));
                int y = int(p(1) / p(2));
                if (y < 0 || y > test.rows - 1) continue;
                if (x < 0 || x > test.cols - 1) continue;
                // stare hard at the coord transformation ;)
                test2(i, j) = test.at<uchar>(y, x);
                // Copy a color pixel (BGR)
                test2Color.at<Vec3b>(i, j) = testColor.at<Vec3b>(y, x);
                // each point used more than once is occluded
                counts(y, x) ++;
            }
        }

        // project the occlusion counts in the same way
        Mat_<uchar> counts1(mdl.size(),0);
        for (int i=R.y; i<R.y+R.height; i++)
        {
            PROFILEX("proj_2");
            for (int j=R.x; j<R.x+R.width; j++)
            {
                Mat1d p = project_vec(KP, i, j);
                int x = int(p(0) / p(2));
                int y = int(p(1) / p(2));
                if (y < 0 || y > test.rows - 1) continue;
                if (x < 0 || x > test.cols - 1) continue;
                counts1(i, j) = counts(y, x);
            }
        }
        blur(counts1, counts1, Size(9,9));
        counts1 -= eyemask;
        counts1 -= eyemask;

        // count occlusions in left & right half
        Rect left (0,  0,mid,counts1.rows);
        Rect right(mid,0,mid,counts1.rows);
        double sleft=sum(counts1(left))[0];
        double sright=sum(counts1(right))[0];

        // fix occlusions with soft symmetry
        Mat_<double> weights;
        Mat_<uchar> sym = test2.clone();
        Mat symColor = test2Color.clone();
        if (abs(sleft-sright)>symThresh)
        {
            PROFILEX("proj_3");

            // make weights
            counts1.convertTo(weights,CV_64F);

            Point p,P;
            double m,M;
            int eyeMask_y_offset = -5;
            minMaxLoc(weights,&m,&M,&p,&P);

            double *wp = weights.ptr<double>();
            for (size_t i=0; i<weights.total(); ++i)
                wp[i] = (1.0 - 1.0 / exp(symBlend+(wp[i]/M)));
            // cerr << weights(Rect(mid,mid,6,6)) << endl;

            for (int i=R.y; i<R.y+R.height; i++)
            {
                if (sleft-sright>symThresh) // left side needs fixing
                {
                    for (int j=R.x; j<mid; j++)
                    {
                        int k = mdl.cols-j-1;
                        sym(i,j) = test2(i,j) * (1-weights(i,j)) + test2(i,k) * (weights(i,j));

                        if( i + eyeMask_y_offset >= 0 && eyemask.at<double>(i + eyeMask_y_offset ,j) == 0 ){
                            // symColor.at<Vec3b>(i, j) = test2Color.at<Vec3b>(i,j) * (1-weights(i,j)) + test2Color.at<Vec3b>(i,k) * (weights(i,j));
                            symColor.at<Vec3b>(i, j) = test2Color.at<Vec3b>(i,k);
                        }

                        //                        symColor.at<Vec3b>(i, j) = test2Color.at<Vec3b>(i,j) * (1-weights(i,j)) + test2Color.at<Vec3b>(i,k) * (weights(i,j));
                    }
                }
                if (sright-sleft>symThresh) // right side needs fixing
                {
                    for (int j=mid; j<R.x+R.width; j++)
                    {
                        int k = mdl.cols-j-1;
                        sym(i,j) = test2(i,j) * (1-weights(i,j)) + test2(i,k) * (weights(i,j));

                        if( i + eyeMask_y_offset >= 0 && eyemask.at<double>(i + eyeMask_y_offset ,j) == 0 ){
                            // symColor.at<Vec3b>(i, j) = test2Color.at<Vec3b>(i,j) * (1-weights(i,j)) + test2Color.at<Vec3b>(i,k) * (weights(i,j));
                            symColor.at<Vec3b>(i, j) = test2Color.at<Vec3b>(i,k);
                        }

                        //                        symColor.at<Vec3b>(i, j) = test2Color.at<Vec3b>(i,j) * (1-weights(i,j)) + test2Color.at<Vec3b>(i,k) * (weights(i,j));
                    }
                }
            }
        }

        if (DEBUG_IMAGES)
        {
            cerr << (sleft-sright) << "\t" << (abs(sleft-sright)>symThresh) << endl;
            imshow("proj",test2);
            if (abs(sleft-sright)>symThresh)
                imshow("weights", weights);
            Mat t = test.clone();
            rectangle(t,Ri,Scalar(255));
            for (size_t i=0; i<pts2d.size(); i++)
                circle(t, pts2d[i], 1, Scalar(0));
            imshow("test3",t);
            imshow("eyemask", eyemask);
        }

        Mat gray;
        Mat colorImg = symColor(R);

        sym.convertTo(gray,CV_8U);

        result.matrices.push_back(sym(R));
        result.matrices.push_back(colorImg);

        return result;
    }

    Rect getFaceRectBy2DPoints(const vector<Point2d> &pts2d, int padding) const{
        int top_y, bottom_y, left_x, right_x, width, height;

        // Set initial values.
        top_y = bottom_y = pts2d[0].y;
        left_x = right_x = pts2d[0].x;
        width = height = 0;

        for (size_t i=0; i<pts2d.size(); i++){
            if(pts2d[i].x < left_x)
                left_x = pts2d[i].x;

            if(pts2d[i].x > right_x)
                right_x = pts2d[i].x;

            if(pts2d[i].y < top_y)
                top_y = pts2d[i].y;

            if(pts2d[i].y > bottom_y)
                bottom_y = pts2d[i].y;
        }

        width = right_x - left_x;
        height = bottom_y - top_y;

        return Rect(left_x - (padding/2), top_y - (padding/2), width + padding, height + padding);
    };

    Mat getFaceMask(const Rect &faceRect) const{
        Mat_<uchar> mask(faceRect.size(),0);

        int center_x = faceRect.width / 2;
        int center_y = faceRect.height / 2;
        int axis_x = (faceRect.width / 2) - (faceRect.width / 20);
        int axis_y = (faceRect.height / 2) + (faceRect.height / 10);
        int angle = 0;
        int start_angle = 0;
        int end_angle = 360;
        Scalar color(255, 255, 255);
        int thickness = -1;

        cout << "faceRect.size(): " << faceRect.size() << endl;
        cout << "faceRect.width: " << faceRect.width << endl;
        cout << "faceRect.height: " << faceRect.height << endl;
        cout << "center_x: " << center_x << endl;
        cout << "center_y: " << center_y << endl;
        cout << "axis_x: " << axis_x << endl;
        cout << "axis_y: " << axis_y << endl;

        ellipse(mask, Point(center_x, center_y), Size(axis_x, axis_y), angle,
                start_angle, end_angle, color, thickness);

        return mask;
    };

    //
    //! 2d eye-alignment
    // Not tested when called from the Python code.
    //
    Mat align2d(const Mat &img) const
    {
        cout << "perfoming 2d eye-alignment" << endl;
        PROFILEX("align2d");

        Mat test;
        resize(img, test, Size(250,250), INTER_CUBIC);

        // get landmarks
        vector<Point2d> pts2d;
        getkp2d(test, pts2d, Rect(0, 0, test.cols, test.rows));

        Point2d eye_l = (pts2d[37] + pts2d[38] + pts2d[40] + pts2d[41]) * 0.25; // left eye center
        Point2d eye_r = (pts2d[43] + pts2d[44] + pts2d[46] + pts2d[47]) * 0.25; // right eye center

        double eyeXdis = eye_r.x - eye_l.x;
        double eyeYdis = eye_r.y - eye_l.y;
        double angle   = atan(eyeYdis/eyeXdis);
        double degree  = angle*180/CV_PI;
        double scale   = 44.0 / eyeXdis; // scale to lfw eye distance

        Mat res;
        Point2f center(test.cols/2, test.rows/2);
        Mat rot = getRotationMatrix2D(center, degree, scale);
        cerr << rot << endl;
        //rot.at<float>(1,2) += eye_l.y -
        warpAffine(test, res, rot, Size(), INTER_CUBIC, BORDER_CONSTANT, Scalar(127));

        if (DEBUG_IMAGES)
        {
            Mat t = test.clone();
            for (size_t i=0; i<pts2d.size(); i++)
                circle(t, pts2d[i], 1, Scalar(0));
            circle(t, eye_l, 3, Scalar(255));
            circle(t, eye_r, 3, Scalar(255));
            imshow("test2",t);
            imshow("testr",res);
        }
        return res;
    }

    static Ptr<Frontalizer> create(const dlib::shape_predictor &sp, int crop, int symThreshold, double symBlend, bool write)
    {
        return makePtr<FrontalizerImpl>(sp, crop, symThreshold, symBlend, write);
    }

};

Mat performFrontalization(bool write, bool facedet, bool align2d, bool project3d, int crop, int sym,
        double blend, char * imagepath, char * cascadepath, char * dlibpath, bool debugMode, char * outputpath,
        int crop_x_offset=0, int crop_y_offset=0, int crop_width=0, int crop_height=0){

    Mat projectionMatrix;

    if(debugMode){

        cout << "write, " << write << endl;
        cout << "facedet, " << facedet << endl;
        cout << "align2d, " << align2d << endl;
        cout << "project3d, " << project3d << endl;
        cout << "crop, " << crop << endl;
        cout << "crop_width, " << crop_width << endl;
        cout << "crop_height, " << crop_height << endl;
        cout << "crop_x_offset, " << crop_x_offset << endl;
        cout << "crop_y_offset, " << crop_y_offset << endl;
        cout << "sym, " << sym << endl;
        cout << "blend, " << blend << endl;
        cout << "imagepath, " << imagepath << endl;
        cout << "cascadepath, " << cascadepath << endl;
        cout << "dlibpath, " << dlibpath << endl;
        cout << "debugMode, " << debugMode << endl;
        cout << "outputpath, " << outputpath << endl;

    }

    string dlib_path = string(dlibpath);
    string casc_path = string(cascadepath);
    string path = string(imagepath);

    if(debugMode){
        cout << "dlib_path, " << dlib_path << endl;
        cout << "casc_path, " << casc_path << endl;
        cout << "path, " << path << endl;
    }

    dlib::shape_predictor sp;
    dlib::deserialize(dlib_path) >> sp;

    FrontalizerImpl front(sp,crop,sym,blend,debugMode);
    CascadeClassifier casc(casc_path + "haarcascade_frontalface_alt.xml");
    CascadeClassifier cascp(casc_path + "haarcascade_profileface.xml");

    //
    // !!!
    // if write is enabled,
    // please run this on a **copy** of your img folder,
    //  since this will just replace the images
    //  with the frontalized version !
    // !!!
    //
    if (! write && debugMode)
    {
        namedWindow("orig", 0);
        namedWindow("orig_color", 0);
        namedWindow("front", 0);
    }

    vector<String> str;
    glob(path, str, true);
    for (size_t i=0; i<str.size(); i++)
    {
        cerr << str[i] << endl;
        Mat in = imread(str[i], 0);
        Mat inColor = imread(str[i], CV_LOAD_IMAGE_COLOR);

        if (! write && debugMode)
        {
            imshow("orig", in);
            imshow("orig_color", inColor);
        }
        if (facedet && !casc.empty())
        {
            vector<Rect> rects;
            casc.detectMultiScale(in, rects, 1.3, 4);
            if (rects.size() > 0)
            {
                cerr << "frontal " << rects[0] << endl;
                in = in(rects[0]);
                inColor = inColor(rects[0]);
            }
            else
            {
                cascp.detectMultiScale(in, rects, 1.3, 4);
                if (rects.size() > 0)
                {
                    cerr << "profile_l" << endl;
                    in = in(rects[0]);
                    inColor = inColor(rects[0]);
                }
                else
                {
                    flip(in,in,1);
                    cascp.detectMultiScale(in, rects, 1.3, 4);
                    if (rects.size() > 0)
                    {
                        cerr << "profile_r" << endl;
                        in = in(rects[0]);
                        inColor = inColor(rects[0]);
                    }
                }
            }
        }

        if (align2d)
            in = front.align2d(in);

        Mat out = in;
        Mat outColor = in;

        if (project3d){
            MatResults matRes = front.project3d(in, inColor);

            projectionMatrix = matRes.matrices[0];
            out = matRes.matrices[1];
            outColor = matRes.matrices[2];

            if(debugMode){
                cout << "projection matrix :" << projectionMatrix << endl;
            }
        }

        // Further cropping of the result, if required.
        if( (crop_width != 0 && crop_width + (crop_x_offset * 2) <= crop) &&
                (crop_height != 0 && crop_height + (crop_y_offset * 2) <= crop) ){
            Rect R(crop_x_offset ,crop_y_offset , crop_width, crop_height);
            outColor = outColor(R);
        }

        if (! write && debugMode)
        {
            imshow("front_color", outColor);

            if (waitKey() == 27) break;
        }
        else
        {
            if(outputpath){
                string filename(str[i]);

                const size_t last_slash_idx = filename.find_last_of("\\/");

                if (std::string::npos != last_slash_idx) {
                    filename.erase(0, last_slash_idx + 1);
                }

                string fullName = outputpath + string("/") + filename;

                imwrite(fullName, outColor);
            }
            else {
                imwrite(str[i], outColor);
            }
        }
    }

    return projectionMatrix;
}

extern "C"{

//////////////////////////////////////////////////////////////////////////////////////////
// This is a wrapper function that can be called from Python, which is heavily based    //
// on a modified version of the frontzlization code from this project:                  //
// https://github.com/berak/uniform-lbp                                                 //
//////////////////////////////////////////////////////////////////////////////////////////
char * frontalize(bool write, bool facedet, bool align2d, bool project3d, int crop, int sym,
        double blend, char * imagepath, char * cascadepath, char * dlibpath, bool debugMode,
        char * outputpath, int crop_x_offset=0, int crop_y_offset=0, int crop_width=0, int crop_height=0){

    if(debugMode){

        cout << "in C++ code - frontalize" << endl;
        cout << "write: " << write << endl;
        cout << "facedet: " << facedet << endl;
        cout << "align2d: " << align2d << endl;
        cout << "project3d: " << project3d << endl;
        cout << "crop: " << crop << endl;
        cout << "sym: " << sym << endl;
        cout << "blend: " << blend << endl;
        cout << "imagepath: " << imagepath << endl;
        cout << "cascadepath: " << cascadepath << endl;
        cout << "dlibpath: " << dlibpath << endl;
        cout << "debugMode: " << debugMode << endl;
        cout << "outputpath: " << outputpath << endl;
    }

    Mat projMat = performFrontalization(write, facedet, align2d, project3d, crop, sym,
            blend, imagepath, cascadepath, dlibpath, debugMode, outputpath, crop_x_offset,
            crop_y_offset, crop_width, crop_height);

    // Create a string with a specific format so we can
    // return the projection matrix to the calling Python code.
    char rowBuffer[10000];
    char colBuffer[10000];

    sprintf(rowBuffer, "%d", projMat.rows);
    sprintf(colBuffer, "%d", projMat.cols);

    string rowsStr = string(rowBuffer);
    string colStr = string(colBuffer);

    std::ostringstream o;
    o << projMat;
    string resultAsString  = rowsStr + "_" + colStr + "_" + o.str();

    char * result = (char*)malloc(strlen(resultAsString.c_str()) + 1);
    strcpy(result, resultAsString.c_str());

    return result;
}

}


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

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

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

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

 /*!

    @ingroup group-bitwise
    Function object implementing ilog2 capabilities

    Returns the integer part of the base 2
    logarithm of the input.

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

    @code
    as_integer_t<T> r = ilog2(x);
    @endcode

    The code is similar to

    @code
    as_integer_t<T> r = toints(log2(x));
    @endcode

    @par Note
    This function asserts for negative or null values.

    @see  twopower, exponent

                 type associated to the input.
  **/
  const boost::dispatch::functor<tag::ilog2_> ilog2 = {};
} }
#endif

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

#endif


#ifndef SPIRAL_GRID_HPP
#define SPIRAL_GRID_HPP

#include <array>
#include <unordered_map>
#include <vector>

#include <boost/container_hash/hash.hpp>

#include "distributions/angular_gauss.hpp"
#include "types/types.hpp"

class SpiralGrid {
    int points_amount;
    double max_value;
    AngularGauss* distr;

    // points is the grid in Cartesian coordinates.
    std::vector<CoordsOfPoint> points;

    // values is an evaluated function on grid, value in i'th position
    // corresponds to the i'th point in points vector
    std::vector<double> values;

    void GenerateGrid(void);
    void EvaluateFunc(void);
    double EvaluateFuncRoutine(int lower_bound, int upper_bound);

public:
    // SpiralGrid creates new spiral grid with N points and function distr on it
    SpiralGrid(int N, AngularGauss* disrt);

    // Func returns function on grid
    const AngularGauss* Func(void) const;
    // MaxValue returns the maximum value of the evaluated function the spiral grid
    double MaxValue(void) const;
    // Values returns vector of evaluated function's values on spiral grid
    const std::vector<double>& Values(void) const;

    // CalcIntegralInsideIsoline calculates surface integral of function on
    // spiral grid
    double CalcIntegralInsideIsoline(double isoline_value);
};

#endif // SPIRAL_GRID_HPP


/*
 * from https://github.com/libigl/libigl/blob/master/include/igl/list_to_matrix.cpp
 * by R. Falque
 * 03/07/2019
 */

#ifndef EIGEN_LIST_TO_MATRIX_HPP
#define EIGEN_LIST_TO_MATRIX_HPP

#include <iostream>
#include <Eigen/Core>
#include <vector>

template <typename T, typename Derived>
inline bool list_to_matrix(const std::vector<std::vector<T > > & V,Eigen::PlainObjectBase<Derived>& M)
{
  // number of rows
  int m = V.size();
  if(m == 0)
  {
    M.resize(0,0);
    return true;
  }
  // number of columns

  /* BEGIN OF EDIT: REMOVE CHECK FOR ALL ROWS HAVING THE SAME VALUE */
  int n = V[0].size();
  /* END OF EDIT */

  assert(n != -1);
  // Resize output
  M.resize(m,n);

  // Loop over rows
  for(int i = 0;i<m;i++)
  {
    // Loop over cols
    for(int j = 0;j<n;j++)
    {
      M(i,j) = V[i][j];
    }
  }

  return true;
};

template <typename T, typename Derived>
inline bool list_to_matrix(
  const std::vector<std::vector<T > > & V,
  const int n,
  const T & padding,
  Eigen::PlainObjectBase<Derived>& M)
{
  const int m = V.size();
  M.resize(m,n);
  for(int i = 0;i<m;i++)
  {
    const auto & row = V[i];
    if(row.size()>n)
    {
      return false;
    }
    int j = 0;
    for(;j<row.size();j++)
    {
      M(i,j) = row[j];
    }
    for(;j<n;j++)
    {
      M(i,j) = padding;
    }
  }
  return true;
};

template <typename T, typename Derived>
inline bool list_to_matrix(const std::vector<T > & V,Eigen::PlainObjectBase<Derived>& M)
{
  // number of rows
  int m = V.size();
  if(m == 0)
  {
    //fprintf(stderr,"Error: list_to_matrix() list is empty()\n");
    //return false;
    if(Derived::ColsAtCompileTime == 1)
    {
      M.resize(0,1);
    }else if(Derived::RowsAtCompileTime == 1)
    {
      M.resize(1,0);
    }else
    {
      M.resize(0,0);
    }
    return true;
  }
  // Resize output
  if(Derived::RowsAtCompileTime == 1)
  {
    M.resize(1,m);
  }else
  {
    M.resize(m,1);
  }

  // Loop over rows
  for(int i = 0;i<m;i++)
  {
    M(i) = V[i];
  }

  return true;
};

#endif


//==================================================================================================
/*
  Copyright 2017 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)
*/
//==================================================================================================
//! [none_of]
#include <boost/simd/algorithm/none_of.hpp>
#include <boost/simd/function/is_gtz.hpp>
#include <iostream>
#include <vector>

int main()
{
  namespace bs =  boost::simd;
  std::vector<float> d{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};

  auto rt = bs::none_of( d.data(), d.data()+7, bs::is_gtz );
  std::cout <<  "rt " << rt << "\n";
  d[1] = 1.0f;
  auto rf = bs::none_of( d.data(), d.data()+7, bs::is_gtz);
  std::cout << "rf " << rf << "\n";

  return 0;
}
//! [none_of]


#include <common.h>
#include <boost/phoenix.hpp>
#include <iostream>
#include <numeric>
#include <vector>

int main()
{
    using namespace boost::phoenix::arg_names;

    auto numbers = std::vector<int>{21, 5, 62, 42, 53};
    std::partition(numbers.begin(), numbers.end(), arg1 <= 42);

    print(numbers, "Partitioned numbers around 42: ");

    auto sum = std::accumulate(numbers.cbegin(), numbers.cend(), 0,
                               arg1 + arg2 * arg2 / 2);
    std::cout << "Sum of half-squares: " << sum << '\n';
}


#pragma once

#include "Material.hpp"
#include "Vec3.hpp"
#include "Ray.hpp"
#include "Color.hpp"

#include <boost\optional.hpp>

#ifdef USE_AMP
#include <amp_math.h>
#endif

namespace Smurf {
    enum ActiveMaterial { ActiveMatte, ActiveGlossy };

    struct RayHit {
        RayHit() : depth{0}, hitPoint{0.0, 0.0, 0.0}, tMin{0.0}  { }
        RayHit(double tMin) : tMin{tMin} { }
        RayHit(double tMin, Vec3<double> hitPoint, int depth) : depth{depth}, hitPoint{hitPoint}, tMin{tMin} { }

        int depth;
        Vec3<double> hitPoint;
        double tMin;
    };

    struct GeometricObject {
        GeometricObject() : color{0.7F, 0.65F, 1.0F} { }
        GeometricObject(Color color) : color{color} { }
        GeometricObject(Color color, Matte material) : color{ color }, matte{ material }, active{ ActiveMaterial::ActiveMatte } { }
        GeometricObject(Color color, Glossy material) : color{ color }, glossy{ material }, active{ ActiveMaterial::ActiveGlossy } { }
        virtual boost::optional<RayHit> onRayCast(const Ray& ray) = 0;
        virtual ~GeometricObject() { }
        // Probably replace this later
        Color color;
        Matte matte;
        Glossy glossy;
        ActiveMaterial active;
    };

    class Plane : public GeometricObject {
    public:
        Plane() : point{0.0, 0.0, 0.0}, normal{0.0, 1.0, 0.0} { }
        Plane(const Vec3<double>& point, const Vec3<double>& normal, Color color) : GeometricObject{color},
                                                                                point{point},
                                                                                normal{normal} { }
        Plane(const Vec3<double>& point, const Vec3<double>& normal, Color color, Matte matte) : GeometricObject{ color, matte },
                                                                                point{ point },
                                                                                normal{ normal } { }
        Plane(const Vec3<double>& point, const Vec3<double>& normal, Color color, Glossy glossy) : GeometricObject{ color, glossy },
                                                                                point{ point },
                                                                                normal{ normal } { }
        boost::optional<RayHit> onRayCast(const Ray& ray) override {
            auto t = (point - ray.origin) * normal / (ray.direction * normal);
            if (t > 0) {
                RayHit hit(t);
                return boost::optional<RayHit>(hit);
            }
            // Didn't hit
            return boost::optional<RayHit>();
        }
        #ifdef USE_AMP
        const Vec3<double>& getPoint() {
            return point;
        }
        const Vec3<double>& getNormal() {
            return normal;
        }
        #endif
    private:
        Vec3<double> point;
        Vec3<double> normal;
    };

    class Sphere : public GeometricObject {
    public:
        Sphere() : center{0.0, 0.0, 0.0}, radius{1.0} { }
        Sphere(const Vec3<double>& center, double radius) : center{center},
                                                            radius{radius} { }
        Sphere(const Vec3<double>& center, double radius, Matte material) : GeometricObject{ {0.0F, 0.0F, 0.0F}, material },
                                                                            center{ center },
                                                                            radius{ radius } { }
        Sphere(const Vec3<double>& center, double radius, Glossy material) : GeometricObject{ { 0.0F, 0.0F, 0.0F }, material },
                                                                            center{ center },
                                                                            radius{ radius } { }
        boost::optional<RayHit> onRayCast(const Ray& ray) override {
            auto temp = ray.origin - center;
            auto a = ray.direction * ray.direction;
            auto b = ray.direction * (2.0 * temp);
            auto c = temp * temp - radius * radius;
            auto discriminant = b * b - (4.0 * a * c);

            // Didn't hit
            if (discriminant < 0.0) return boost::optional<RayHit>();

            auto e = sqrt(discriminant);
            auto quadraticDenominator = 2.0 * a;

            auto finalize = [](double tMin) -> boost::optional<RayHit> {
                RayHit hit(tMin);
                return boost::optional<RayHit>(hit);
            };

            auto t = (-b - e) / quadraticDenominator;
            if (t > 0) return finalize(t);

            t = (-b + e) / quadraticDenominator;
            if (t > 0) return finalize(t);

            // Didn't hit
            return boost::optional<RayHit>();
        }
        #ifdef USE_AMP
        const Vec3<double>& getCenter() {
            return center;
        }
        double getRadius() {
            return radius;
        }
        #endif
    private:
        Vec3<double> center;
        double radius;
    };

    class Rectangle : public GeometricObject {
    public:
        Rectangle() : point{0.0, 0.0, 0.0}, a{5.0, 0.0, 0.0}, b{0.0, -5.0, 0.0}, normal{0.0, 0.0, 1.0} { }
        Rectangle(Vec3<double> point, Vec3<double> a, Vec3<double> b, Vec3<double> normal) : point{point}, a{a}, b{b}, normal{normal} { }
        Rectangle(Vec3<double> point, Vec3<double> a, Vec3<double> b, Vec3<double> normal, Matte material) : GeometricObject{ { 0.0F, 0.0F, 0.0F }, material }, point{ point }, a{ a }, b{ b }, normal{ normal } { }
        Rectangle(Vec3<double> point, Vec3<double> a, Vec3<double> b, Vec3<double> normal, Glossy material) : GeometricObject{ { 0.0F, 0.0F, 0.0F }, material }, point{ point }, a{ a }, b{ b }, normal{ normal } { }

        boost::optional<RayHit> onRayCast(const Ray& ray) override {
            double t = (point - ray.origin) * normal / (ray.direction * normal);
            if (t <= 0) return {};

            auto temp = ray.origin + t * ray.direction;
            auto tempDir = temp - point;

            auto tempDirDotSide = tempDir * a;
            if (tempDirDotSide > a.lengthSquared() || tempDirDotSide < 0.0) {
                return {};
            }

            tempDirDotSide = tempDir * b;
            if (tempDirDotSide > b.lengthSquared() || tempDirDotSide < 0.0) {
                return {};
            }

            return {{t}};
        }
        #ifdef USE_AMP
        const Vec3<double>& getPoint() {
            return point;
        }
        const Vec3<double>& getA() {
            return a;
        }
        const Vec3<double>& getB() {
            return b;
        }
        const Vec3<double>& getNormal() {
            return normal;
        }
        #endif
    private:
        Vec3<double> point;
        Vec3<double> a, b;
        Vec3<double> normal;
    };
    #ifdef USE_AMP

    struct g_Plane {
    public:
        g_Plane() restrict(cpu, amp) : point{ 0.0, 0.0, 0.0 }, normal{ 0.0, 1.0, 0.0 }, matte{}, active{ ActiveMaterial::ActiveMatte } { }
        g_Plane(const Vec3<double>& point, const Vec3<double>& normal, const Matte& material) restrict(cpu, amp) : point{ point },
                                                                                normal{ normal },
                                                                                matte{ material },
                                                                                active{ ActiveMaterial::ActiveMatte } { }
        g_Plane(const Vec3<double>& point, const Vec3<double>& normal, const Glossy& material) restrict(cpu, amp) : point{ point },
                                                                                normal{ normal },
                                                                                glossy{ material },
                                                                                active{ ActiveMaterial::ActiveGlossy } { }
        Vec3<double> point;
        Vec3<double> normal;
        ActiveMaterial active;
        Matte matte;
        Glossy glossy;
    };

    struct g_Sphere {
        g_Sphere() restrict(amp) : center{ 0.0, 0.0, 0.0 }, radius{ 1.0 }, matte{}, active{ ActiveMaterial::ActiveMatte } { }
        g_Sphere(const Vec3<double>& center, double radius, const Matte& material) restrict(cpu, amp) : center{center},
                                                                                radius{radius},
                                                                                matte{material},
                                                                                active{ ActiveMaterial::ActiveMatte } { }
        g_Sphere(const Vec3<double>& center, double radius, const Glossy& material) restrict(cpu, amp) : center{ center },
                                                                                radius{ radius },
                                                                                glossy{ material },
                                                                                active{ ActiveMaterial::ActiveGlossy } { }
        ActiveMaterial active;
        Vec3<double> center;
        double radius;
        Matte matte;
        Glossy glossy;
    };

    struct g_Rectangle {
        g_Rectangle() restrict(cpu, amp) : point{0.0, 0.0, 0.0}, a{5.0, 0.0, 0.0}, b{0.0, -5.0, 0.0}, normal{0.0, 0.0, 1.0} { }
        g_Rectangle(const Vec3<double>& point, const Vec3<double>& a, const Vec3<double>& b, const Vec3<double>& normal, const Matte& matte) restrict(cpu, amp) : point{ point }, a{ a }, b{ b }, normal{ normal }, matte{ matte }, active{ ActiveMaterial::ActiveMatte } { }
        g_Rectangle(const Vec3<double>& point, const Vec3<double>& a, const Vec3<double>& b, const Vec3<double>& normal, const Glossy& glossy) restrict(cpu, amp) : point{ point }, a{ a }, b{ b }, normal{ normal }, glossy{ glossy }, active{ ActiveMaterial::ActiveGlossy } { }
        g_Rectangle(const g_Rectangle& other) restrict(cpu, amp) : point{ other.point }, a{ other.a }, b{ other.b }, normal{ other.normal }, matte{ other.matte }, glossy{ other.glossy }, active{ other.active } { }
        g_Rectangle(g_Rectangle&& other) restrict(cpu, amp) : point{static_cast<Vec3<double>&&>(other.point)},
                                                              a{static_cast<Vec3<double>&&>(other.a)},
                                                              b{static_cast<Vec3<double>&&>(other.b)},
                                                              normal{static_cast<Vec3<double>&&>(other.normal)},
                                                              matte{static_cast<Matte&&>(other.matte)},
                                                              glossy{ static_cast<Glossy&&>(other.glossy)},
                                                              active{ static_cast<ActiveMaterial&&>(other.active) } { }
        g_Rectangle& operator=(const g_Rectangle& other) restrict(cpu, amp) {
            point = other.point;
            a = other.a;
            b = other.b;
            normal = other.normal;
            active = other.active;
            return *this;
        }

        ActiveMaterial active;
        Vec3<double> point;
        Vec3<double> a, b;
        Vec3<double> normal;
        Matte matte;
        Glossy glossy;
    };

    struct g_RayHit {
        g_RayHit() restrict(amp) : hasHit{false} { }
        g_RayHit(double tMin, const Vec3<double>& normal, ActiveMaterial active) restrict(amp) : tMin{ tMin }, normal{ normal }, hasHit{ true }, active{ active } { }

        operator bool() const restrict(amp) {
            return hasHit;
        }

        Ray ray;
        int depth;
        ActiveMaterial active;
        Vec3<double> hitPoint;
        Vec3<double> normal;
        Matte matte;
        Glossy glossy;
        double tMin;
        bool hasHit;
    };

    struct g_ShadowRayHit {
        g_ShadowRayHit() restrict(amp) : hasHit{false} { }
        g_ShadowRayHit(float t) restrict(amp) : t{t}, hasHit{true} { }

        operator bool() const restrict(amp) {
            return hasHit;
        }

        float t;
        bool hasHit;
    };

    namespace OnRayCastAspect {
        static float const GetEpsilon() restrict(cpu, amp) {
            return 0.0001F;
        }

        g_RayHit onRayCast(const g_Plane& plane, const Ray& ray) restrict(amp) {
            auto t = (plane.point - ray.origin) * plane.normal / (ray.direction * plane.normal);
            if (t > GetEpsilon()) {
                g_RayHit hit{t, plane.normal, plane.active};
                return hit;
            }
            // Didn't hit
            return {};
        }

        g_ShadowRayHit onShadowRayCast(const g_Plane& plane, const Ray& ray) restrict(amp) {
            auto t = static_cast<float>((plane.point - ray.origin) * plane.normal / (ray.direction * plane.normal));
            if (t > GetEpsilon()) {
                return {t};
            }
            return {};
        }

        g_RayHit onRayCast(const g_Sphere& sphere, const Ray& ray) restrict(amp) {
            auto temp = ray.origin - sphere.center;
            auto a = ray.direction * ray.direction;
            auto b = ray.direction * (2.0 * temp);
            auto c = temp * temp - sphere.radius * sphere.radius;
            auto discriminant = b * b - (4.0 * a * c);

            // Didn't hit
            if (discriminant < 0.0) return {};

            auto e = Concurrency::fast_math::sqrt(static_cast<float>(discriminant));
            auto quadraticDenominator = 2.0 * a;

            auto finalize = [](double tMin, const Vec3<double>& normal, const g_Sphere& sphere) restrict(amp) {
                return g_RayHit{tMin, normal, sphere.active};
            };

            auto t = (-b - e) / quadraticDenominator;
            if (t > GetEpsilon()) return finalize(t, (temp + t * ray.direction).normalizeAndReturn(), sphere);

            t = (-b + e) / quadraticDenominator;
            if (t > GetEpsilon()) return finalize(t, (temp + t * ray.direction).normalizeAndReturn(), sphere);

            // Didn't hit
            return {};
        }

        g_ShadowRayHit onShadowRayCast(const g_Sphere& sphere, const Ray& ray) restrict(amp) {
            auto temp = ray.origin - sphere.center;
            auto a = ray.direction * ray.direction;
            auto b = ray.direction * (2.0 * temp);
            auto c = temp * temp - sphere.radius * sphere.radius;
            auto discriminant = b * b - (4.0 * a * c);

            // Didn't hit
            if (discriminant < 0.0) return {};

            auto e = Concurrency::fast_math::sqrt(static_cast<float>(discriminant));
            auto quadraticDenominator = 2.0 * a;

            auto t = static_cast<float>((-b - e) / quadraticDenominator);
            if (t > GetEpsilon()) return {t};

            t = static_cast<float>((-b + e) / quadraticDenominator);
            if (t > GetEpsilon()) return {t};

            // Didn't hit
            return {};
        }

        g_RayHit onRayCast(const g_Rectangle& rect, const Ray& ray) restrict(amp) {
            double t = (rect.point - ray.origin) * rect.normal / (ray.direction * rect.normal);

            // Didn't hit
            if (t <= 0) return {};

            auto temp = ray.origin + t * ray.direction;
            auto tempDir = temp - rect.point;

            auto tempDirDotSide = tempDir * rect.a;
            if (tempDirDotSide > rect.a.lengthSquared() || tempDirDotSide < 0.0) {
                // Didn't hit
                return {};
            }

            tempDirDotSide = tempDir * rect.b;
            if (tempDirDotSide > rect.b.lengthSquared() || tempDirDotSide < 0.0) {
                // Didn't hit
                return {};
            }

            return {t, rect.normal, rect.active};
        }

        g_ShadowRayHit onShadowRayCast(const g_Rectangle& rect, const Ray& ray) restrict(amp) {
            double t = (rect.point - ray.origin) * rect.normal / (ray.direction * rect.normal);

            // Didn't hit
            if (t <= 0) return {};

            auto temp = ray.origin + t * ray.direction;
            auto tempDir = temp - rect.point;

            auto tempDirDotSide = tempDir * rect.a;
            if (tempDirDotSide > rect.a.lengthSquared() || tempDirDotSide < 0.0) {
                // Didn't hit
                return {};
            }

            tempDirDotSide = tempDir * rect.b;
            if (tempDirDotSide > rect.b.lengthSquared() || tempDirDotSide < 0.0) {
                // Didn't hit
                return {};
            }

            return {static_cast<float>(t)};
        }
    } // namespace OnRayCastAspect
    #endif
} // namespace Smurf


//==================================================================================================
/*
  Copyright 2017 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)
*/
//==================================================================================================
//! [predicates]
#include <boost/simd/predicates.hpp>
#include <boost/simd/pack.hpp>
#include <boost/simd/function/enumerate.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/mindenormal.hpp>
#include <iostream>

namespace bs =  boost::simd;
using pack_ft =  bs::pack <float, 8>;

int main()
{
  pack_ft p = {0.0f, 1.0f, -1.0f, 2.5f, -2.5f, bs::Nan<float>(), bs::Minf<float>(), bs::Mindenormal<float>()};
  std::cout << " p =  " << p << std::endl
            <<  " -> bs::is_finite(p) =    " << bs::is_finite(p)   << std::endl
            <<  " -> bs::is_inf(p) =       " << bs::is_inf(p)      << std::endl
            <<  " -> bs::is_denormal(p) =  " << bs::is_denormal(p) << std::endl
            <<  " -> bs::is_flint(p) =     " << bs::is_flint(p)    << std::endl
            <<  " -> bs::is_odd(p) =       " << bs::is_odd(p)      << std::endl
            <<  " -> bs::is_nan(p) =       " << bs::is_nan(p)      << std::endl;
  return 0;
}
//! [predicates]


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

#include <boost/test/unit_test.hpp>
#include "armnnOnnxParser/IOnnxParser.hpp"
#include  "ParserPrototxtFixture.hpp"

BOOST_AUTO_TEST_SUITE(OnnxParser)

struct SimpleDepthConv2DFixture : public armnnUtils::ParserPrototxtFixture<armnnOnnxParser::IOnnxParser>
{
    SimpleDepthConv2DFixture()
    {
        m_Prototext = R"(
                   ir_version: 3
                   producer_name:  "CNTK"
                   producer_version:  "2.5.1"
                   domain:  "ai.cntk"
                   model_version: 1
                   graph {
                     name:  "CNTKGraph"
                     input {
                        name: "Input"
                        type {
                          tensor_type {
                            elem_type: 1
                            shape {
                              dim {
                                dim_value: 1
                              }
                              dim {
                                dim_value: 3
                              }
                              dim {
                                dim_value: 2
                              }
                              dim {
                                dim_value: 2
                              }
                            }
                          }
                        }
                      }
                      input {
                        name: "Weight"
                        type {
                          tensor_type {
                            elem_type: 1
                            shape {
                              dim {
                                dim_value: 3
                              }
                              dim {
                                dim_value: 1
                              }
                              dim {
                                dim_value: 2
                              }
                              dim {
                                dim_value: 2
                              }
                            }
                          }
                        }
                      }
                      initializer {
                          dims: 3
                          dims: 1
                          dims: 2
                          dims: 2
                          data_type: 1
                          float_data: 1
                          float_data: 1
                          float_data: 1
                          float_data: 1
                          float_data: 2
                          float_data: 2
                          float_data: 2
                          float_data: 2
                          float_data: 3
                          float_data: 3
                          float_data: 3
                          float_data: 3
                          name: "Weight"
                        }
                      node {
                         input: "Input"
                         input: "Weight"
                         output: "Output"
                         name: "Convolution"
                         op_type: "Conv"
                         attribute {
                           name: "kernel_shape"
                           ints: 2
                           ints: 2
                           type: INTS
                         }
                         attribute {
                           name: "strides"
                           ints: 1
                           ints: 1
                           type: INTS
                         }
                         attribute {
                           name: "auto_pad"
                           s: "VALID"
                           type: STRING
                         }
                         attribute {
                           name: "group"
                           i: 3
                           type: INT
                         }
                         attribute {
                           name: "dilations"
                           ints: 1
                           ints: 1
                           type: INTS
                         }
                         doc_string: ""
                         domain: ""
                       }
                      output {
                          name: "Output"
                          type {
                             tensor_type {
                               elem_type: 1
                               shape {
                                   dim {
                                       dim_value: 1
                                   }
                                   dim {
                                       dim_value: 3
                                   }
                                   dim {
                                       dim_value: 1
                                   }
                                   dim {
                                       dim_value: 1
                                   }
                               }
                            }
                        }
                        }
                    }
                   opset_import {
                      version: 7
                    })";
        Setup();
    }
};

BOOST_FIXTURE_TEST_CASE(ValidDepthConvTest, SimpleDepthConv2DFixture)
{
    RunTest<4>({{"Input", { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}}},
               {{"Output", { 10, 52, 126 }}});
}

BOOST_AUTO_TEST_SUITE_END()