/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include "mitkCommandLineParser.h"
#include "mitkImage.h"
#include <mitkImageStatisticsCalculator.h>
#include <mitkExtendedLabelStatisticsImageFilter.h>
#include <mitkPlanarFigureMaskGenerator.h>
#include <mitkPlanarFigure.h>
#include "mitkIOUtil.h"
#include <iostream>
#include <usAny.h>
#include <fstream>
#include <itkImageRegionConstIterator.h>
#include "mitkImageAccessByItk.h"
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>
#include <boost/accumulators/statistics/count.hpp>
#include <boost/accumulators/statistics/moment.hpp>
#include <mitkImageMaskGenerator.h>
#include <mitkHotspotMaskGenerator.h>
#include <mitkIgnorePixelMaskGenerator.h>
#include <chrono>

struct statistics_res{
    double mean, variance, min, max, count, moment;
};

long getTimeInMs()
{
    std::chrono::milliseconds ms = std::chrono::duration_cast< std::chrono::milliseconds > (std::chrono::system_clock::now().time_since_epoch());
    long time = ms.count();
    return time;
}

//std::string printstats(mitk::ImageStatisticsCalculator::Statistics statisticsStruct)
//{
//   std::string res = "";
//   res += std::string("Entropy ") + std::to_string(statisticsStruct.GetEntropy()) + std::string("\n");
//   res += std::string("Kurtosis ") + std::to_string(statisticsStruct.GetKurtosis()) + std::string("\n");
//   res += std::string("MPP ") + std::to_string(statisticsStruct.GetMPP()) + std::string("\n");
//   res += std::string("Max ") + std::to_string(statisticsStruct.GetMax()) + std::string("\n");
//   res += std::string("Mean ") + std::to_string(statisticsStruct.GetMean()) + std::string("\n");
//   res += std::string("Median ") + std::to_string(statisticsStruct.GetMedian()) + std::string("\n");
//   res += std::string("Min ") + std::to_string(statisticsStruct.GetMin()) + std::string("\n");
//   res += std::string("N ") + std::to_string(statisticsStruct.GetN()) + std::string("\n");
//   res += std::string("RMS ") + std::to_string(statisticsStruct.GetRMS()) + std::string("\n");
//   res += std::string("Skewness ") + std::to_string(statisticsStruct.GetSkewness()) + std::string("\n");
//   res += std::string("Std ") + std::to_string(statisticsStruct.GetSigma()) + std::string("\n");
//   res += std::string("UPP ") + std::to_string(statisticsStruct.GetUPP()) + std::string("\n");
//   res += std::string("Uniformity ") + std::to_string(statisticsStruct.GetUniformity()) + std::string("\n");
//   res += std::string("Variance ") + std::to_string(statisticsStruct.GetVariance()) + std::string("\n");
//   vnl_vector<int> minIndex = statisticsStruct.GetMinIndex();
//   vnl_vector<int> maxIndex = statisticsStruct.GetMaxIndex();

//   res += "Min Index:  ";
//   for (auto it = minIndex.begin(); it != minIndex.end(); it++)
//   {
//       res += std::to_string(*it) + " ";
//   }
//   res += std::string("\n");

//   res += "Max Index:  ";
//   for (auto it = maxIndex.begin(); it != maxIndex.end(); it++)
//   {
//       res += std::to_string(*it) + " ";
//   }
//   res += std::string("\n");
//   return res;
//}

template <class T, class P>
void printMap(std::map<T, P> input)
{
    for (auto it = input.begin(); it != input.end(); ++it)
    {
        std::cout << it->first<< ": " << it->second<< std::endl;
    }
    std::cout << std::endl;
}

template < typename TPixel, unsigned int VImageDimension >
void get_statistics_boost(itk::Image<TPixel, VImageDimension>* itkImage, statistics_res& res){
    typedef itk::Image<TPixel, VImageDimension> ImageType;

    itk::ImageRegionConstIterator<ImageType> it(itkImage, itkImage->GetLargestPossibleRegion());

    int ctr=0;
    double sum=0;

    boost::accumulators::accumulator_set<double, boost::accumulators::stats<
            boost::accumulators::tag::mean,
            boost::accumulators::tag::variance,
            boost::accumulators::tag::min,
            boost::accumulators::tag::max,
            boost::accumulators::tag::count,
            boost::accumulators::tag::moment<2>> > acc;

    for (it.GoToBegin(); !it.IsAtEnd(); ++it)
    {
        acc(it.Get());
//        currentPixel = it.Get();
//        sum+=currentPixel;
//        ctr+=1;
    }

//    res.mean=(double)sum/(double)ctr;
    res.mean = boost::accumulators::mean(acc);
    res.variance = boost::accumulators::variance(acc);
    res.min = boost::accumulators::min(acc);
    res.max = boost::accumulators::max(acc);
    res.count = boost::accumulators::count(acc);
    res.moment = boost::accumulators::moment<2>(acc);

    std::cout << "sum: " << sum << " N: " <<  ctr << " mean: " << res.mean << std::endl;
}

void compute_statistics(std::string inputImageFile = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic3D.nrrd",
                        std::string outfname = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic3D_statistics_new.txt",
                        std::string outfname2 = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic3D_statistics_old.txt",
                        std::string maskImageFile = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic3D_someSegmentation.nrrd",
                        std::string pFfile = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic3D_rectangle.pf"                        )
{
    ofstream outFile;
    outFile.open(outfname);

    unsigned int timeStep = 0;

    mitk::Image::Pointer inputImage = mitk::IOUtil::LoadImage(inputImageFile);

    mitk::Image::Pointer maskImage;
    maskImage = mitk::IOUtil::LoadImage(maskImageFile);

    std::vector<mitk::BaseData::Pointer> loadedObjects;
    loadedObjects = mitk::IOUtil::Load(pFfile);
    mitk::BaseData::Pointer pf = loadedObjects[0];
    mitk::PlanarFigure::Pointer planarFigure = dynamic_cast<mitk::PlanarFigure*>(pf.GetPointer());

    mitk::ImageStatisticsCalculator::Pointer calculator = mitk::ImageStatisticsCalculator::New();
    mitk::ImageStatisticsCalculator::StatisticsContainer::Pointer result;
    calculator->SetInputImage(inputImage);
    calculator->SetNBinsForHistogramStatistics(100);

    long start_time;

    outFile << "Calculating Statistics on Pic3D" << std::endl << std::endl;

    outFile << "New Image Statistics Calculator" << std::endl;

    start_time = getTimeInMs();
    outFile << "1) unmasked: " << std::endl;
    calculator->SetMask(nullptr);
    result = calculator->GetStatistics(timeStep);
    outFile << result->GetAsString() << std::endl;
    outFile << "new image statistics calculator took: " << getTimeInMs()-start_time << " ms." << std::endl << std::endl;

    start_time = getTimeInMs();
    outFile << "2) planarfigure: " << std::endl;
    mitk::PlanarFigureMaskGenerator::Pointer planarFigMaskExtr = mitk::PlanarFigureMaskGenerator::New();
    planarFigMaskExtr->SetPlanarFigure(planarFigure);
    planarFigMaskExtr->SetInputImage(inputImage);
    calculator->SetMask(planarFigMaskExtr.GetPointer());
    result = calculator->GetStatistics(timeStep, 1);
    outFile << result->GetAsString() << std::endl;
    outFile << "new image statistics calculator took: " << getTimeInMs()-start_time << " ms." << std::endl << std::endl;

    start_time = getTimeInMs();
    outFile << "3) ignore pixel value mask: " << std::endl;
    mitk::IgnorePixelMaskGenerator::Pointer ignPixVal = mitk::IgnorePixelMaskGenerator::New();
    ignPixVal->SetInputImage(inputImage);
    ignPixVal->SetIgnoredPixelValue(0);
    calculator->SetMask(ignPixVal.GetPointer());
    result = calculator->GetStatistics(timeStep, 1);
    outFile << result->GetAsString() << std::endl;
    outFile << "new image statistics calculator took: " << getTimeInMs()-start_time << " ms." << std::endl << std::endl;

    start_time = getTimeInMs();
    outFile << "4) image mask: " << std::endl;
    mitk::ImageMaskGenerator::Pointer binaryImageMaskGen = mitk::ImageMaskGenerator::New();
    binaryImageMaskGen->SetImageMask(maskImage);
    calculator->SetMask(binaryImageMaskGen.GetPointer());
    result = calculator->GetStatistics(timeStep, 1);
    outFile << result->GetAsString() << std::endl;
    outFile << "new image statistics calculator took: " << getTimeInMs()-start_time << " ms." << std::endl << std::endl;

    start_time = getTimeInMs();
    outFile << "5) hotspot mask: " << std::endl;
    mitk::HotspotMaskGenerator::Pointer hotSpotMaskGen = mitk::HotspotMaskGenerator::New();
    hotSpotMaskGen->SetInputImage(inputImage);
    hotSpotMaskGen->SetHotspotRadiusInMM(10);
    hotSpotMaskGen->SetTimeStep(0);
    calculator->SetMask(hotSpotMaskGen.GetPointer());
    result = calculator->GetStatistics(timeStep, 1);
    outFile << result->GetAsString() << std::endl;
    outFile << "new image statistics calculator took: " << getTimeInMs()-start_time << " ms." << std::endl << std::endl;
    //mitk::IOUtil::SaveImage(hotSpotMaskGen->GetMask(), "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic2DplusTHotspot.nrrd");

    outFile << "6) all time steps (image mask): " << std::endl;
    if (inputImage->GetTimeSteps() > 1)
    {
        start_time = getTimeInMs();
        for (unsigned int i=0; i < inputImage->GetTimeSteps(); i++)
        {
            outFile << "timestep: " << i << std::endl;
            calculator->SetMask(binaryImageMaskGen.GetPointer());
            result = calculator->GetStatistics(i, 1);
            outFile << result->GetAsString() << std::endl;
            outFile << "new image statistics calculator took: " << getTimeInMs()-start_time << " ms." << std::endl << std::endl;
        }
    }
    else
    {
        outFile << "Input image has only 1 time step!" << std::endl;
    }

    outFile.flush();
    outFile.close();

    // -----------------------------------------------------------------------------------------------------------------------------------------------------------
//    outFile.open(outfname2);

//    std::cout << std::endl << std::endl << "calculating statistics old imgstatcalc: " << std::endl;
//    mitk::ImageStatisticsCalculator::Statistics statisticsStruct;
//    mitk::ImageStatisticsCalculator::Pointer calculator_old;

//    start_time = getTimeInMs();
//    outFile << "1) unmasked: " << std::endl;
//    calculator_old = mitk::ImageStatisticsCalculator::New();
//    calculator_old->SetHistogramBinSize(100);
//    calculator_old->SetImage(inputImage);
//    calculator_old->SetMaskingModeToNone();
//    calculator_old->ComputeStatistics(timeStep);
//    statisticsStruct = calculator_old->GetStatistics(timeStep);
//    outFile << printstats(statisticsStruct) << std::endl;
//    outFile << "old image statistics calculator took: " << getTimeInMs()-start_time << " ms." << std::endl << std::endl;

//    start_time = getTimeInMs();
//    outFile << "2) planarFigure: " << std::endl;
//    calculator_old = mitk::ImageStatisticsCalculator::New();
//    calculator_old->SetHistogramBinSize(100);
//    calculator_old->SetImage(inputImage);
//    calculator_old->SetPlanarFigure(planarFigure);
//    calculator_old->SetMaskingModeToPlanarFigure();
//    calculator_old->ComputeStatistics(timeStep);
//    statisticsStruct = calculator_old->GetStatistics(timeStep);
//    outFile << printstats(statisticsStruct) << std::endl;
//    outFile << "old image statistics calculator took: " << getTimeInMs()-start_time << " ms." << std::endl << std::endl;

//    start_time = getTimeInMs();
//    outFile << "3) IgnorePixelValue: " << std::endl;
//    calculator_old = mitk::ImageStatisticsCalculator::New();
//    calculator_old->SetHistogramBinSize(100);
//    calculator_old->SetImage(inputImage);
//    calculator_old->SetMaskingModeToNone();
//    calculator_old->SetDoIgnorePixelValue(true);
//    calculator_old->SetIgnorePixelValue(0);
//    calculator_old->ComputeStatistics(timeStep);
//    statisticsStruct = calculator_old->GetStatistics(timeStep);
//    outFile << printstats(statisticsStruct) << std::endl;
//    outFile << "old image statistics calculator took: " << getTimeInMs()-start_time << " ms." << std::endl << std::endl;
//    calculator_old->SetDoIgnorePixelValue(false);

//    start_time = getTimeInMs();
//    outFile << "4) Image Mask: " << std::endl;
//    calculator_old = mitk::ImageStatisticsCalculator::New();
//    calculator_old->SetHistogramBinSize(100);
//    calculator_old->SetImage(inputImage);
//    calculator_old->SetImageMask(maskImage);
//    calculator_old->SetMaskingModeToImage();
//    calculator_old->ComputeStatistics(timeStep);
//    statisticsStruct = calculator_old->GetStatistics(timeStep);
//    outFile << printstats(statisticsStruct) << std::endl;
//    outFile << "old image statistics calculator took: " << getTimeInMs()-start_time << " ms." << std::endl << std::endl;

//    start_time = getTimeInMs();
//    outFile << "5) Hotspot Mask: " << std::endl;
//    calculator_old = mitk::ImageStatisticsCalculator::New();
//    calculator_old->SetHistogramBinSize(100);
//    calculator_old->SetImage(inputImage);
//    calculator_old->SetMaskingModeToNone();
//    calculator_old->SetCalculateHotspot(true);
//    calculator_old->SetHotspotRadiusInMM(10);
//    calculator_old->ComputeStatistics(timeStep);
//    statisticsStruct = calculator_old->GetStatistics(timeStep).GetHotspotStatistics();
//    outFile << printstats(statisticsStruct) << std::endl;
//    outFile << "old image statistics calculator took: " << getTimeInMs()-start_time << " ms." << std::endl << std::endl;
//    calculator_old->SetCalculateHotspot(false);

//    outFile << "6) all time steps (image mask): " << std::endl;
//    if (inputImage->GetTimeSteps() > 1)
//    {
//        start_time = getTimeInMs();
//        calculator_old = mitk::ImageStatisticsCalculator::New();
//        calculator_old->SetHistogramBinSize(100);
//        calculator_old->SetImage(inputImage);
//        calculator_old->SetImageMask(maskImage);
//        calculator_old->SetMaskingModeToImage();
//        for (unsigned int i=0; i < inputImage->GetTimeSteps(); i++)
//        {
//            calculator_old->ComputeStatistics(i);
//            statisticsStruct = calculator_old->GetStatistics(i);
//            outFile << printstats(statisticsStruct) << std::endl;
//            outFile << "old image statistics calculator took: " << getTimeInMs()-start_time << " ms." << std::endl << std::endl;
//        }
//     }
//    else
//    {
//        outFile << "Input image has only 1 time step!" << std::endl;
//    }

//    outFile.flush();
//    outFile.close();
}

int main( int argc, char* argv[] )
{
    std::string inputImageFile = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic3D.nrrd";
    std::string outfname = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic3D_statistics_new.txt";
    std::string outfname2 = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic3D_statistics_old.txt";
    std::string maskImageFile = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic3D_someSegmentation.nrrd";
    std::string pFfile = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic3D_rectangle.pf";
    compute_statistics(inputImageFile, outfname, outfname2, maskImageFile, pFfile);

    inputImageFile = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic2DplusT.nrrd";
    outfname = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic2DplusT_statistics_new.txt";
    outfname2 = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic2DplusT_statistics_old.txt";
    maskImageFile = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic2DplusT_someSegmentation.nrrd";
    pFfile = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/Pic2DplusT_ellipse.pf";
    compute_statistics(inputImageFile, outfname, outfname2, maskImageFile, pFfile);

    inputImageFile = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/leber_ct.pic";
    outfname = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/leber_ct_statistics_new.txt";
    outfname2 = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/leber_ct_statistics_old.txt";
    maskImageFile = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/leber_ct_segmentation.nrrd";
    pFfile = "/home/fabian/MITK/MITK_platform_project/bin/MITK-superbuild/MITK-Data/leber_ct_PF.pf";
    compute_statistics(inputImageFile, outfname, outfname2, maskImageFile, pFfile);

    return EXIT_SUCCESS;
}


#ifndef INCLUDED_scheme_nest_maps_Rotation1DMap_HH
#define INCLUDED_scheme_nest_maps_Rotation1DMap_HH

#include "scheme/util/SimpleArray.hh"

#include <Eigen/Dense>

#include <boost/static_assert.hpp>
#include <iostream>
#include <vector>

namespace scheme { namespace nest { namespace pmap {

	template<
		int DIM=1,
		class Value=Eigen::Matrix3d,
		class Index=uint64_t,
		class Float=double
	>
	struct Rotation1DMap {
		BOOST_STATIC_ASSERT_MSG(DIM==1,"Rotation1D DIM must be == 1");

		static int const DIMENSION = DIM;
		typedef Value ValueType ;
		typedef Float FloatType ;
		typedef Index IndexType ;
		typedef util::SimpleArray<DIM,Index> Indices;
		typedef util::SimpleArray<DIM,Float> Params;
		typedef Eigen::Matrix<Float,3,1> V;
		typedef Eigen::Matrix<Float,3,3> M;

		Index nside;
		Float lb, ub;
		V axis, flipaxis;
		M flip;

		Rotation1DMap() : nside(1), lb(-M_PI), ub(M_PI), axis(0,0,1), flipaxis(0,0,0) {}
		Rotation1DMap( Params p, Params q, Indices i ) : lb(p[0]), ub(q[0]), nside(i[0]), axis(0,0,1), flipaxis(0,0,0) {}

		void set_axis(V a){ axis = a.normalized(); }

		void set_flip_axis(V a){
			BOOST_ASSERT( fabs(axis.dot(a)) < 0.00001 );
			a.normalize();
			flipaxis = a;
			flip = Eigen::AngleAxis<Float>(M_PI,a).toRotationMatrix();
		}

		///@brief sets value to parameters without change
		///@return false iff invalid parameters
		bool params_to_value(
			Params const & params,
			Index cell_index,
			Index resl,
			Value & value
		) const {
			BOOST_ASSERT( fabs(axis.norm()-1.0) < 0.00001 );
			BOOST_ASSERT( lb < ub );
			bool doflip = cell_index >= nside;
			cell_index -= doflip ? nside : 0;
			BOOST_ASSERT( cell_index < nside );
			Float ang = (Float)cell_index + params[0];
			ang *= (ub-lb) / (Float)nside;
			ang += lb;
			value = Eigen::AngleAxis<Float>( ang, axis ).matrix();
			value = value * ( doflip? flip : M::Identity() );
			BOOST_ASSERT( lb <= ang && ang <= ub );
			// std::cout << "SET " << doflip <<  std::endl;//axis.transpose() << " " << ang*180/M_PI << std::endl;
			// std::cout << value << std::endl;
			return true;
		}

		void get_axis_angle_and_flip( Value value, Eigen::Vector3d & ax, Float & ang, bool & doflip ) const {
			Eigen::AngleAxis<Float> aa(value);
			ang = aa.angle();
			ax = aa.axis();
			if( ax.dot(axis) < 0 ){ ax = -ax; ang = -ang; }
			doflip = false;
			if( fabs(ang) > 0.00001 && ax.dot(axis) < 0.999 ){
				doflip = true;
				value = value * flip.transpose();
				Eigen::AngleAxis<Float> aa(value);
				ax = aa.axis();
				ang = aa.angle();
				if( ax.dot(axis) < 0 ){ ax = -ax; ang = -ang; }
				// std::cout << ax.transpose() << " / " << axis.transpose() << " / " << ang*180/M_PI << std::endl;
				// std::cout << value << std::endl;
			}

		}

		///@brief sets params/cell_index from value
		///@note necessary for value lookup and neighbor lookup
		bool value_to_params(
			Value const & value0,
			Index /*resl*/,
			Params & params,
			Index & cell_index
		) const {
			M value = value0;
			BOOST_ASSERT( fabs(axis.norm()-1.0) < 0.00001 );
			BOOST_ASSERT( lb < ub );

			Float ang;
			bool doflip;
			Eigen::Vector3d vax;
			get_axis_angle_and_flip( value0, vax, ang, doflip );

			BOOST_ASSERT( fabs(ang) < 0.00001 || vax.dot(axis) > 0.99999 );
			if( ang >  M_PI ) ang -= 2.0*M_PI;
			if( ang < -M_PI ) ang += 2.0*M_PI;
			// std::cout << lb << " " << ang << " " << ub << " /  " << nside << " " << aa.axis().transpose() << " " << aa.angle() << std::endl;
			BOOST_ASSERT( lb <= ang && ang <= ub );
			Float p = (ang-lb)/(ub-lb) * (Float)nside;
			cell_index = (Index)p;
			params[0] = p - cell_index;
			BOOST_ASSERT( cell_index < nside );
			cell_index += doflip ? nside : 0;
			BOOST_ASSERT( 0 <= params[0] && params[0] <= 1 );
			return true;
		}

		///@brief get parameter space repr of Value for particular cell
		///@note necessary only for neighbor lookup
		void value_to_params_for_cell(
			Value const & value,
			Params & params
		) const {
			std::cerr << "Not Implemented" << std::endl;
			std::exit(-1);
		}

		///@brief return the cell_index of neighboring cells within radius of value
		///@note delta parameter is in "Parameter Space"
		template<class OutIter>
		void get_neighboring_cells(Value const & value, Float radius, OutIter out) const {
			std::cerr << "Not Implemented" << std::endl;
			std::exit(-1);
		}

		///@brief aka covering radius max distance from bin center to any value within bin
		Float bin_circumradius(Index resl) const {
			return (ub-lb) / nside / (Float)(1<<resl);
		}

		///@brief maximum distance from the bin center which must be within the bin
		Float bin_inradius(Index resl) const {
			return (ub-lb) / nside / (Float)(1<<resl);
		}

		///@brief cell size
		Index num_cells() const { return nside * ( flipaxis==V(0,0,0)? 1 : 2 ); }

	};

}}}

#endif


//
//  main.cpp
//  HRMeasure
//
//  Created by WuH on 2017/6/26.
//  Copyright © 2017年 WuH. All rights reserved.
//
#include "opencv2/core/core_c.h"
#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_processing/render_face_detections.h>
#include <dlib/image_processing.h>
#include <dlib/gui_widgets.h>
#include <dlib/image_io.h>
#include <dlib/image_processing.h>
#include <dlib/opencv/cv_image.h>

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <queue>
#include <cmath>
#include <time.h>
#include <chrono>
#include <pthread.h>
#include <armadillo>
#include <itpp/signal/fastica.h>
#include <stdlib.h>
#include "matplotlibcpp.h"

using namespace dlib;
using namespace cv;
using namespace std;
using namespace std::chrono;
using namespace arma;
using namespace itpp;
namespace plt = matplotlibcpp;

struct Frame {
public:
    cv::Mat frame;
    double time;
};

struct ROI {
public:
    cv::Mat roi;
    double time;
};

#define MAXT 201
milliseconds start_time;
bool isCalculating;
bool isDetecting;
pthread_t cal_thread;
pthread_t roi_thread;
std::queue<Frame> frames;

CascadeClassifier faces_cascade;

struct RGB_color
{
public:
    double r, g, b;
    RGB_color() {}
    RGB_color(double _r,double _g, double _b):r(_r), g(_g), b(_b) {}
    RGB_color(const RGB_color & A) {r = A.r, g = A.g, b = A.b;}
};

class Signal
{
private:
    RGB_color sig[MAXT];
    double times[MAXT];
    int nowT, headT;
    bool flag;
public:
    Signal() {nowT = headT = 0; flag = 0;}
    /* Insert avg. color of ROI */
    void insert(const RGB_color & a, const double & time)
    {
        sig[nowT] = a;
        times[nowT] = time;
        nowT = (nowT + 1) % MAXT;
        if(nowT == headT) headT = (headT + 1) % MAXT, flag = 1;
    }
    bool full() {return flag;}
    /* Calculate BVP */
    double calc_BVP()
    {
        /* filter */
        /* undo */
        /* SOBI */
        int nows = (nowT - headT + MAXT) % MAXT;
        if(nows < 100) return 0.0;
        double sel = -10000.0;
        int sel_i = 0;
        arma::mat tmp;
        rowvec even_times = linspace<rowvec>(times[headT], times[nowT - 1], nows);
        rowvec timesv = zeros<rowvec>(nows);
        for(int i = headT, j = 0; i != nowT; i = (i + 1) % MAXT, j++) timesv(j) = times[i];
        tmp.set_size(3, nows);
        for(int i = headT, j = 0; i != nowT; i = (i + 1) % MAXT, j++)
        {
            tmp(0, j) = sig[i].r;
            tmp(1, j) = sig[i].g;
            tmp(2, j) = sig[i].b;
        }

        std::vector<double> rgbx(nows), rgby(nows);
        for (int i = 0; i < nows; i++) {
            rgbx.at(i) = i;
            rgby.at(i) = tmp(0,i);
        }

        plt::plot(rgbx, rgby);
        plt::title("rgb red");
        plt::legend();
        plt::save("/Users/apple/Desktop/testpics/rgbred.png");

        for (int i = 0; i < nows; i++) {
            rgbx.at(i) = i;
            rgby.at(i) = tmp(1,i);
        }

        plt::figure();
        plt::plot(rgbx, rgby);
        plt::title("rgb green");
        plt::legend();
        plt::save("/Users/apple/Desktop/testpics/rgbgreen.png");

        for (int i = 0; i < nows; i++) {
            rgbx.at(i) = i;
            rgby.at(i) = tmp(2,i);
        }

        plt::figure();
        plt::plot(rgbx, rgby);
        plt::title("rgb blue");
        plt::legend();
        plt::save("/Users/apple/Desktop/testpics/rgbblue.png");

        for(int i = 0; i < 3; ++i)
        {
            double avg = mean(tmp.row(i)), std = stddev(tmp.row(i));
            tmp.row(i).transform([avg, std](double x) {return (x - avg) / (std + 0.0001);});
        }
        cout << "START SOBI!" << endl;
        arma::mat H;
        SOBI(tmp, 3, 20, H);
        cout << "END SOBI!" << endl;
        arma::mat sur = H * tmp;
        cx_mat raws;
        for(int i = 0; i < 3; ++i)
        {
            rowvec tmp3;
            interp1(timesv, sur.row(i), even_times, tmp3);
            sur.row(i) = tmp3;
            raws = fft(sur.row(i));
            double tmp2 = kurt(abs(raws));
            if(tmp2 > sel)
                sel = tmp2, sel_i = i;
        }
        rowvec sor = sur.row(sel_i);
        cout << "End Select" << endl;
        /* End of SOBI */
        /* Calculate BVP */
        raws = fft(sor);

        cout<<"&&&&&&&&&"<< abs(raws) <<endl;
        double ans = 0, BVP = 0;
        for(int i = 0; i < nows / 2 + 1; ++i)
        {
            if (abs(raws(i)) > ans && ((double)i) / (even_times[nows - 1] - even_times[0]) * 60 > 40
                        && ((double)i) / (even_times[nows - 1] - even_times[0]) * 60< 200)
                ans = abs(raws(i)), BVP = ((double)i) / (even_times[nows - 1] - even_times[0]) * 60;
        }

        cout << "BVP is: " << BVP << endl;
        return BVP;
        /* End of Calculate */
    }

    /* Helper Function */
    double sqr(double x)
    {
        return x * x;
    }
    double kurt(const rowvec & A)
    {
        int n = A.size();
        double B4 = 0.0, B2 = 0.0;
        for(int i = 0; i < n; ++i)
            B4 += sqr(sqr(A(i))), B2 += sqr(A(i));
        return B4 * n / (B2 * B2) - 3;
    }

    void stdcov(const arma::mat & X, int tau, arma::mat & C)
    {
        int N = X.n_cols, m = X.n_rows;
        arma::vec m1 = zeros<arma::vec>(m), m2 = zeros<arma::vec>(m);
        arma::mat R = X.cols(0, N - tau - 1) * X.cols(tau, N - 1).t() / (N - tau);
        for(int i = 0; i < m; ++i)
        {
            m1[i] = mean(X.row(i).cols(0, N - tau - 1));
            m2[i] = mean(X.row(i).cols(tau, N - 1));
        }
        C = R - m1 * m2.t();
        C = (C + C.t()) / 2;
    }
    void joint_diag(const arma::mat & A, double jthresh, cx_mat & V, cx_mat & D)
    {
        int m = A.n_rows, nm = A.n_cols;
        arma::mat b1 = zeros<arma::mat>(3, 3), b2 = zeros<arma::mat>(3, 3);
        b1 << 1 << 0 << 0 << endr << 0 << 1 << 1 << endr << 0 << 0 << 0 << endr;
        b2 << 0 << 0 << 0 << endr << 0 << 0 << 0 << endr << 0 << -1 << 1 << endr;
        cx_mat B = cx_mat(b1, b2);
        cx_mat Bt = B.t();
        cx_mat Ip = zeros<cx_mat>(1, nm);
        cx_mat Iq = zeros<cx_mat>(1, nm);
        cx_mat g = zeros<cx_mat>(3, m);
        cx_mat G = zeros<cx_mat>(2, 2);
        arma::vec ev = zeros<arma::vec>(3);
        arma::mat vcp = zeros<arma::mat>(3, 3);
        double c = 0;
        cx_double s = 0;
        V = eye<cx_mat>(m, m);
        D = zeros<cx_mat>(m, nm);
        D.set_real(A);
        for(int encore = 1; encore;)
        {
            encore = 0;
            for(int p = 0; p < m - 1; ++p)
                for(int q = p + 1; q < m; ++q)
                {
                    cx_rowvec t1 = zeros<cx_rowvec>(nm / m), t2 = zeros<cx_rowvec>(nm / m);
                    cx_rowvec t3 = zeros<cx_rowvec>(nm / m), t4 = zeros<cx_rowvec>(nm / m);
                    for(int i = p; i < nm; i += m) t1((i - p) / m) = D(p, i), t2((i - p) / m) = D(q, i);
                    for(int i = q; i < nm; i += m) t3((i - q) / m) = D(q, i), t4((i - q) / m) = D(p, i);
                    g = join_vert(t1 - t3, join_vert(t4, t2));
                    eig_sym(ev, vcp, real((B * (g * g.t())) * B.t()));
                    arma::vec angles = vcp.col(2);
                    if(angles[0] < 0)
                        angles = angles * (-1);
                    c = sqrt(0.5 + angles[0] / 2);
                    s = cx_double(angles[1], -angles[2]) * 0.5 / c;
                    if(abs(s) > jthresh)
                    {
                        encore = 1;
                        G << c << -conj(s) << endr << s << c << endr;
                        cx_mat tmp = join_horiz(V.col(p), V.col(q)) * G;
                        V.col(p) = tmp.col(0), V.col(q) = tmp.col(1);
                        tmp = G.t() * join_vert(D.row(p), D.row(q));
                        D.row(p) = tmp.row(0), D.row(q) = tmp.row(1);
                        for(int ip = p, iq = q; ip < nm && iq < nm; ip += m, iq += m)
                        {
                            cx_colvec dip = D.col(ip), diq = D.col(iq);
                            D.col(ip) = (dip * c) + (diq * s);
                            D.col(iq) = (diq * c) - (dip * conj(s));
                        }
                    }
                }
        }
    }
    void SOBI(const arma::mat & A, int n, int num_tau, arma::mat & H)
    {
        int N = A.n_cols, m = A.n_rows;
        double tiny = 1e-8;
        arma::mat Rx, tmp;
        stdcov(A, 0, Rx);
        arma::mat uu, vv;
        arma::vec dd;
        svd(uu, dd, vv, Rx);
        arma::mat d = diagmat(dd);
        arma::mat Q = sqrtmat_sympd(pinv(d)) * uu.t();
        arma::mat z = Q * A;
        arma::mat Rz = zeros<arma::mat>(n, num_tau * n);

        for(int i = 1; i <= num_tau; ++i)
        {
            stdcov(z, i - 1, tmp);
            Rz.cols((i - 1) * n, i * n - 1) = tmp.cols(0, n - 1);
        }
        cx_mat v, d2;
        joint_diag(Rz, tiny, v, d2);
        H = real(v.t()) * Q;
    }
}HR;

Scalar roiMean(cv::Mat roi){
    Scalar avg;
    Scalar std;
    cv::meanStdDev(roi, avg, std);
    return avg;
}
cv::Mat getROI(cv::Mat img, Rect FOI) {

    Rect ROI;
    ROI.x = FOI.x+FOI.width*0.5-FOI.width*0.25/2;
    ROI.y = FOI.y+FOI.height*0.18-FOI.height*0.15/2;
    ROI.width = FOI.width*0.25;
    ROI.height = FOI.height*0.15;

    Point p1(ROI.x, ROI.y);
    Point p2(ROI.x+ROI.width, ROI.y+ROI.height);
    cv::rectangle(img, p1, p2, Scalar(255, 0, 0));

    cv::Mat ROIm = img(ROI);

    return ROIm;
}

void saveROI(ROI roi_tmp){

    double r, g, b;
    Scalar mean = roiMean(roi_tmp.roi);
    r = mean[2];
    g = mean[1];
    b = mean[0];
    HR.insert(RGB_color(r, g, b), roi_tmp.time);
}

void * calculate(void * arg) {
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    while(true) {
        pthread_testcancel();
        cout << "Heart Rate" << HR.calc_BVP() << endl;
//        sleep(2);
    }
    return NULL;
}

void changeState() {
    isCalculating = !isCalculating;
}

void keyHandler(VideoCapture &cap) {
    char key = (char) cv::waitKey(10);
    if( key == 27 ) {
        pthread_cancel(roi_thread);
        if (isCalculating) {
            pthread_cancel(cal_thread);
        }
        cv::destroyAllWindows();
        cap.release();
    }
    if( key == 32 ) {
        changeState();
        if(isCalculating == true) {
            int succ = pthread_create(&cal_thread, NULL, calculate, NULL);
            if(succ == 0) {
                printf("start calculating in new thread\n");
            }
        }
        else {
            printf("stop calculating\n");
            pthread_cancel(cal_thread);
        }
    }
}

Rect getfacerect(cv::Mat frame){

    cv::Mat img = frame;
    cv::Mat img_gray;
    cv::cvtColor(img, img_gray, COLOR_BGR2GRAY);
    equalizeHist(img_gray, img_gray);

    std::vector<Rect> faces;
    faces_cascade.detectMultiScale(img_gray,faces,1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(50, 50) );

    Rect FOI = Rect(1,1,2,2);

    if(faces.size() > 1) {
        int maxsize = faces[0].area();

        FOI = faces[0];
        for ( size_t i = 1; i < faces.size(); ++i) {
            int tmp = faces[i].area();
            if( tmp > maxsize ) {
                maxsize = tmp;
                FOI = faces[i];
            }
        }
    }
    else if(faces.size() == 1) {
        FOI = faces[0];
    }

    return FOI;
}

void gettenface(Frame tenframe[]) {
    Rect facearea;
    int hasface = 0;

    for(int i = 0; i < 10; i++) {

        cv::Mat face = tenframe[i].frame;
        facearea = getfacerect(face);

        if(facearea != Rect(1,1,2,2)) {
            hasface = i;
            break;
        }
    }
    ROI roi_tmp;
    for(int i = hasface; i < 10; i++) {
        roi_tmp.time = tenframe[i].time;
        roi_tmp.roi = getROI(tenframe[i].frame, facearea);
        saveROI(roi_tmp);

        Point p1(facearea.x, facearea.y);
        Point p2(facearea.x+facearea.width, facearea.y+facearea.height);
        cv::rectangle(roi_tmp.roi, p1, p2, Scalar(255, 0, 0));

        char strchar[10];
        gcvt(roi_tmp.time, 15, strchar);
        string str = strchar;

        imwrite("/Users/apple/Library/Mobile\ Documents/com~apple~CloudDocs/Desktop/HRMeasure/roi"+str+".jpg", roi_tmp.roi);
    }
}
// In this function, take out the first frame in the queue and process it.
// Get the location of the top of eyebrow to calculate
// Call ROI calculate function in the end
ROI getRoiWithDlib(Frame frameparam) {

    cout << "getRoiWithDlib" << endl;

    dlib::frontal_face_detector face_detector = get_frontal_face_detector();

    shape_predictor sp;

    deserialize("/Users/apple/Library/Mobile\ Documents/com~apple~CloudDocs/Documents/ceca/projects/HRMeasure/shape_predictors/shape_predictor_68_face_landmarks.dat") >> sp;

    Frame frametmp = frameparam;

    cv::Mat frame = frametmp.frame;
    array2d<dlib::rgb_pixel> img;
    assign_image(img, dlib::cv_image<rgb_pixel>(frame));

    //pyramid_up(img);

    image_window win;
    win.clear_overlay();
    win.set_image(img);

    cout << "test5" <<endl;

    // Now tell the face detector to give us a list of bounding boxes
    // around all the faces in the image.
    std::vector<dlib::rectangle> dets = face_detector(img);

    cout << "test6" <<endl;

    // Choose the largest face to process
    unsigned long facenum = dets.size();

    cout << "face num" << facenum << endl;

    dlib::rectangle face;
    if(facenum > 1) {
        unsigned long maxsize = dets[0].area();

        face = dets[0];

        for(unsigned long j = 1; j < facenum; ++j) {
            unsigned long tmp = dets[j].area();
            if(tmp > maxsize) {
                maxsize = tmp;
                face = dets[j];
            }
         }
    }
    else if(facenum == 1) {
        face = dets[0];
    }

    full_object_detection shape = sp(img, face);

    dlib::point rightpoint = shape.part(20);
    dlib::point leftpoint = shape.part(23);
    Rect ROI;

    ROI.x = rightpoint.x();
    ROI.width = leftpoint.x() - rightpoint.x();

    if (rightpoint.y() <= leftpoint.y()) {
        ROI.y = 0.66 *face.top() + 0.33 * rightpoint.y();
        ROI.height = ROI.y - rightpoint.y();
    }
    else {
        ROI.y = 0.66 *face.top() + 0.33 * leftpoint.y();
        ROI.height = ROI.y - leftpoint.y();
    }

    struct ROI ROItmp;
    ROItmp.time = frametmp.time;
    ROItmp.roi = frame(ROI);

    // For debug
    Point p1(ROI.x, ROI.y);
    Point p2(ROI.x+ROI.width, ROI.y+ROI.height);
    cv::rectangle(frame, p1, p2, Scalar(255, 0, 0));
    imshow("frame2", frame);

    char strchar[10];
    gcvt(frametmp.time, 15, strchar);
    string str = strchar;

    imwrite("/Users/apple/Desktop/testpics/rois/roi"+str+".jpg", ROItmp.roi);
    // For debug

    return ROItmp;
}
void * processThreadDlib(void * arg) {
    while (frames.size() > 0) {
        cout << "frame num" << frames.size() << endl;
        Frame frame = frames.front();
        frames.pop();

        ROI roi_tmp;
        roi_tmp = getRoiWithDlib(frame);
        saveROI(roi_tmp);
    }
    return NULL;
}
void * processThreadTenFrames(void * arg) {
    while (frames.size() > 0) {
        //cout << "frame num" << frames.size() << endl;
        if(frames.size() > 10) {
            Frame tenframe[10];
            Frame tenframeout[10];
            for(int i = 0; i < 10; ++i) {
                tenframe[i] = frames.front();
                frames.pop();
            }
            gettenface(tenframe);
        }
    }
    return NULL;
}
int saveFrame() {
    VideoCapture cap(0);
    if(!cap.isOpened())
        return -1;

    while(cap.isOpened()) {
        Frame frame_in;
        cap >> frame_in.frame;

        milliseconds ms = duration_cast< milliseconds >(system_clock::now().time_since_epoch());
        frame_in.time = ((double)(ms.count() - start_time.count())) / 1000;

        frames.push(frame_in);
        imshow("frame", frame_in.frame);
        keyHandler(cap);
        if (isDetecting == false) {
            isDetecting = true;
            int out_thread;
            //out_thread = pthread_create(&roi_thread, NULL, processThreadTenFrames, NULL);
            out_thread = pthread_create(&roi_thread, NULL, processThreadDlib, NULL);
            if (out_thread == 0) {
                printf("start show thread\n");
            }
        }
    }
    return 1;
}

int main(int argc, const char * argv[]) {
    start_time = duration_cast< milliseconds >(system_clock::now().time_since_epoch());
    isCalculating = false;
    isDetecting = false;

    faces_cascade.load("/Users/apple/Library/Mobile\ Documents/com~apple~CloudDocs/Documents/ceca/projects/HRMeasure/Haarcascades/haarcascade_frontalface_alt2.xml");

    if(faces_cascade.empty()) {
        printf("fail loading cascade\n");
    }

    int openVideo = saveFrame();
    if( openVideo < 0 ) {
        printf("error open camera\n");
    }
    return 0;
}

//cv::Mat getFace(cv::Mat frame_in) {
//
//
//    cv::Mat img = frame_in;
//    cv::Mat img_gray;
//    cv::cvtColor(img, img_gray, COLOR_BGR2GRAY);
//    equalizeHist(img_gray, img_gray);
//
//    vector<Rect> faces;
//    faces_cascade.detectMultiScale(img_gray,faces,1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(50, 50) );
//
//    Rect FOI;
//
//    if(faces.size() > 1) {
//        int maxsize = faces[0].area();
//
//        FOI = faces[0];
//        for ( size_t i = 1; i < faces.size(); ++i) {
//            int tmp = faces[i].area();
//            if( tmp > maxsize ) {
//                maxsize = tmp;
//                FOI = faces[i];
//            }
//        }
//    }
//    else if(faces.size() == 1) {
//        FOI = faces[0];
//    }
//
//    cv::Mat ROI = getROI(img, FOI);
//    saveROI(ROI);
//
//    Point p1(FOI.x, FOI.y);
//    Point p2(FOI.x+FOI.width, FOI.y+FOI.height);
//    rectangle(img, p1, p2, Scalar(255, 0, 0));
//
//    return img;
//}


// File: vector_min_max.cpp

#include <iostream>
#include <cmath>
#include <boost/numeric/mtl/mtl.hpp>

int main(int, char**)
{
    using mtl::max; using std::pow; // to avoid possible ambiguity with ARPREC

    mtl::dense_vector<double>         v(100);

    for (unsigned i= 0; i < size(v); i++)
	v[i]= double(i+1) * pow(-1.0, int(i)); // Amb. in MSVC

    std::cout << "max(v) is " << max(v)<< "\n";

    std::cout << "min(v) is " <<  min(v)<< "\n";

    std::cout << "max<6>(v) is " <<  max<6>(v)<< "\n";

    return 0;
}


// Boost.Geometry

// Copyright (c) 2015, Oracle and/or its affiliates.

// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_EXPAND_EXPAND_BY_EPSILON_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_EXPAND_EXPAND_BY_EPSILON_HPP

#include <cstddef>
#include <algorithm>

#include <boost/type_traits/is_floating_point.hpp>

#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/core/coordinate_type.hpp>

#include <boost/geometry/util/math.hpp>

#include <boost/geometry/views/detail/indexed_point_view.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace expand
{

template
<
    typename Point,
    template <typename> class PlusOrMinus,
    std::size_t I = 0,
    std::size_t D = dimension<Point>::value,
    bool Enable = boost::is_floating_point
                    <
                        typename coordinate_type<Point>::type
                    >::value
>
struct corner_by_epsilon
{
    static inline void apply(Point & point)
    {
        typedef typename coordinate_type<Point>::type coord_type;
        coord_type const coord = get<I>(point);
        coord_type const eps = math::scaled_epsilon(coord);

        set<I>(point, PlusOrMinus<coord_type>()(coord, eps));

        corner_by_epsilon<Point, PlusOrMinus, I+1>::apply(point);
    }
};

template
<
    typename Point,
    template <typename> class PlusOrMinus,
    std::size_t I,
    std::size_t D
>
struct corner_by_epsilon<Point, PlusOrMinus, I, D, false>
{
    static inline void apply(Point const&) {}
};

template
<
    typename Point,
    template <typename> class PlusOrMinus,
    std::size_t D,
    bool Enable
>
struct corner_by_epsilon<Point, PlusOrMinus, D, D, Enable>
{
    static inline void apply(Point const&) {}
};

template
<
    typename Point,
    template <typename> class PlusOrMinus,
    std::size_t D
>
struct corner_by_epsilon<Point, PlusOrMinus, D, D, false>
{
    static inline void apply(Point const&) {}
};

} // namespace expand

template <typename Box>
inline void expand_by_epsilon(Box & box)
{
    typedef detail::indexed_point_view<Box, min_corner> min_type;
    min_type min_point(box);
    expand::corner_by_epsilon<min_type, std::minus>::apply(min_point);

    typedef detail::indexed_point_view<Box, max_corner> max_type;
    max_type max_point(box);
    expand::corner_by_epsilon<max_type, std::plus>::apply(max_point);
}

} // namespace detail
#endif // DOXYGEN_NO_DETAIL

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_EXPAND_EXPAND_BY_EPSILON_HPP


#include <string>
#include <unordered_map>

#include <boost/lexical_cast.hpp>

#include <frovedis.hpp>
#include <frovedis/ml/tree/tree.hpp>

double parse(const std::string& line) {
  return boost::lexical_cast<double>(line);
}

int main(int argc, char** argv) {
  frovedis::use_frovedis use(argc, argv);

  auto dataset = frovedis::make_rowmajor_matrix_load<double>("./train.mat");
  auto labels = frovedis::make_dvector_loadline("./train.label").map(parse);

  // iris dataset has no categorical feature
  std::unordered_map<size_t, size_t> categorical_features_info;
  // use a regressional impurity
  const auto impurity = frovedis::tree::impurity_type::Variance;
  const size_t max_depth = 4;
  const size_t max_bins = 32;

  const size_t num_iterations = 3;
  const auto loss = frovedis::tree::loss_type::LogLoss;
  const double learning_rate = 0.1;

  auto model = frovedis::gradient_boosted_trees::train_classifier<double>(
    dataset, labels,
    categorical_features_info,
    impurity, max_depth, max_bins,
    num_iterations, loss, learning_rate
  );

  std::cout << model << std::endl;
  model.save("./model");

  auto model2 = frovedis::make_gbts_by_load<double>("./model");

  auto testers = frovedis::make_rowmajor_matrix_local_load<double>("./test.mat");
  auto results = model2.predict(testers);

  std::cout << "-------- Prediction Results:: --------" << std::endl;
  for (const auto result: results) {
    std::cout << result << std::endl;
  }

  return 0;
}


#ifndef SKYLARK_LOCAL_COMPUTATIONS_HPP
#define SKYLARK_LOCAL_COMPUTATIONS_HPP

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

#include <unordered_map>
#include <unordered_set>
#include <queue>

extern "C" {

void EL_BLAS(sgemv)(const char*, const El::Int *, const El::Int *,
    const float *, const float *, const El::Int *,
    const float *, const El::Int *, const float *, float *, const El::Int *);

void EL_BLAS(dgemv)(const char*, const El::Int *, const El::Int *,
    const double *, const double *, const El::Int *,
    const double *, const El::Int *, const double *, double *, const El::Int *);

}

namespace skylark { namespace ml {

/**
 * Localized solution of Time-Dependent Personalized PageRank.
 *
 * For details on Time-Dependent PPR, the algorithm, the gaureentess of the
 * output, and the parameters, see:
 *
 * "Community Detection Using Time-Dependent PageRank"
 * by Haim Avron and Lior Horesh
 *
 * @tparam GraphType type of graph object. Needs to support the following:
 *                   GraphType::vertex_type - type of vertex.
 *                   GraphType::num_edges() - number of edges.
 *                   GraphType::deg(node) - debgree of a node.
 *                   GraphType::adjanct_begin(node),
 *                   GraphType::adjanct_end(node) -
 *                    begining and end iterators to adjancy container
 *                    container can be any type.
 * @tparam T datatype (e.g. double) for the function on nodes. Must be numeric.
 * @param G input graph
 * @param s seed function on nodes (i.e. map from node to numeric value).
 * @param y output function - or each node the function defines NX values
 *          for NX different time points in [0, gamma].
 * @param x NX-sized vector with the time values on which y reports the values.
 * @param alpha,gamma,epsilon, NX - parameters (see paper).
 */
template<typename GraphType, typename T>
void TimeDependentPPR(const GraphType& G,
    const std::unordered_map<typename GraphType::vertex_type, T>& s,
    std::unordered_map<typename GraphType::vertex_type, El::Matrix<T> *>& y,
    El::Matrix<T> &x, double alpha = 0.85, double gamma = 5.0,
    double epsilon = 0.001, int NX = 4) {

    typedef typename GraphType::vertex_type vertex_type;

    if (!El::Initialized())
        SKYLARK_THROW_EXCEPTION (
            base::skylark_exception()
               << base::error_msg("Elemental was not initialized") );

    // Find minimum N, caching it since it involves costly computations of
    // Bessel functions.
    static std::unordered_map<std::pair<double, double>, int,
                              utility::pair_hasher_t> Nmap;
    auto epsgamma = std::make_pair(epsilon, gamma);
    const double pi = boost::math::constants::pi<double>();
    if (Nmap.count(epsgamma) == 0) {
        int minN = 10;
        double C = 20.0 * std::sqrt(minN) * std::exp(-gamma/2);
        while (C * boost::math::cyl_bessel_i(minN, gamma) * pow(0.8, minN) >
            epsilon / (gamma * (1 + (2 / pi) * log(minN - 1))))
            minN++;
        Nmap[epsgamma] = minN;
    }
    int minN = Nmap[epsgamma];

    // N is taken to be the minimum multiple of NX that is bigger or equal
    // to minN.
    const El::Int N = minN % NX == 0 ? minN : (minN / NX + 1) * NX;
    const El::Int NR = N / NX;

    // Setup matrices associated with Chebyshev spectral diff
    // We cache them to keep costs low (can be crucial for
    // when finding the cluster is very fast).
    static std::unordered_map<std::pair<int, double>, El::Matrix<T>*,
                              utility::pair_hasher_t> Dmap;

    auto ngamma = std::make_pair(N, gamma);
    if (Dmap.count(ngamma) == 0) {
        El::Matrix<T> *D = new El::Matrix<T>(N, N);
        Dmap[ngamma] = D;

        El::Matrix<T> D0;
        nla::ChebyshevDiffMatrix(N, D0, x, 0, gamma);
        for(int i = 0; i < N; i++)
            D0.Set(i, i, D0.Get(i, i) + 1.0);

        El::Matrix<T> R(N, N);
        El::qr::Explicit(D0, R);

        for(int j = 0; j < N; j++)
            D->Set(N-1, j, D0.Get(j, N-1));

        El::Matrix<T> Q1, R1;
        base::ColumnView(Q1, D0, 0, N - 1);
        El::View(R1, R, 0, 0, N-1, N-1);

        El::Pseudoinverse(R1);

        El::Matrix<T> DU;
        base::RowView(DU, *D, 0, N-1);
        El::Gemm(El::NORMAL, El::TRANSPOSE, 1.0, R1, Q1, 0.0, DU);
    }

    const El::Matrix<T> *D_ = Dmap[ngamma];

    El::Matrix<T> x1;
    nla::ChebyshevPoints(N, x1, 0, gamma);
    x.Resize(NX, 1);
    for(int i = 0; i < NX; i++)
        x.Set(i, 0, x1.Get(i * NR, 0));

    // Constants for convergence.
    double LC = 1 + (2 / pi) * log(N - 1);
    double C = (alpha < 1) ?
        (1-alpha) * epsilon / ((1 - exp((alpha - 1) * gamma)) * LC) :
        epsilon / (gamma * LC);

    // From now on, do not use Elemental to avoid overheads.
    const T *D = D_->LockedBuffer();
    const T *u = D_->LockedBuffer() + N - 1;

    typedef std::pair<bool, T*> rypair_t;
    std::unordered_map<vertex_type, rypair_t> rymap;
    std::queue<vertex_type> violating;

    // Initialize non-zero functions, and their residual, which is not
    // fully computed yet (but we know that needs to be inserted into the queue).
    for(auto it = s.begin(); it != s.end(); it++) {
        const vertex_type &node = it->first;
        const T &v = it->second;

        T *ry = new T[N + NX];
        std::fill(ry, ry + N, -alpha * v);
        std::fill(ry + N, ry + N + NX, v);
        rymap[node] = rypair_t(true, ry);
        violating.push(node);
    }

    // Initialize to just zero for all nodes adjanct to seeds, that
    // are not seeds themselves. Residual is not fully computed yet.
    for(auto it = s.begin(); it != s.end(); it++) {
        const vertex_type &node = it->first;

        for(auto it = G.adjanct_begin(node); it != G.adjanct_end(node); it++) {
            const vertex_type &onode = *it;
            if (rymap.count(onode) == 0) {
                T *ry = new T[N + NX];
                std::fill(ry, ry + N + NX, 0);
                rymap[onode] = rypair_t(false, ry);
            }
        }
    }

    // Update the residual based on seeds
    for(auto it = s.begin(); it != s.end(); it++) {
        const vertex_type &node = it->first;

        T *ry = rymap[node].second;

        size_t deg = G.degree(node);
        T v = alpha * ry[N] / deg;
        for(auto it = G.adjanct_begin(node); it != G.adjanct_end(node); it++) {
            const vertex_type &onode = *it;
            size_t odeg = G.degree(onode);

            rypair_t& ryopair = rymap[onode];
            T *ro = ryopair.second;
            bool inq = false;
            double B = C * odeg;
            for(int j = 0; j < N; j++) {
                ro[j] += v;
                inq = inq || (std::abs(ro[j]) > B);
            }
            if (!ryopair.first && inq) {
                violating.push(onode);
                ryopair.first = true;
            }
        }
    }

    // Main loop
    T dyp[N];
    while(!violating.empty()) {
        vertex_type node = violating.front();
        violating.pop();

        // Solve locally, and update rymap[node].
        rypair_t& rpair = rymap[node];
        T *ry = rpair.second;

        // Compute correction to y, and the new residual.
        T done = 1.0, dzero = 0.0;
        El::Int ione = 1;
        if (std::is_same<T, float>::value)
            EL_BLAS(sgemv)("Normal", &N, &N, (float *)&done, (float *)D, &N,
                (float *)ry, &ione, (float *)&dzero, (float *)dyp, &ione);
        else
            EL_BLAS(dgemv)("Normal", &N, &N, (double *)&done, (double *)D, &N,
                (double *)ry, &ione, (double *)&dzero, (double *)dyp, &ione);
        for(int i = 0; i < NX; i++)
            ry[N + i] += dyp[i * NR];
        T v = dyp[N-1];
        for(int i = 0; i < N; i++)
            ry[i] = v * u[i * N];

        // No longer in queue.
        rpair.first = false;

        // Update residuals
        size_t deg = G.degree(node);
        for(auto it = G.adjanct_begin(node); it != G.adjanct_end(node); it++) {
            const vertex_type &onode = *it;
            size_t odeg = G.degree(onode);

            // Add it to rymap, if not already there.
            if (rymap.count(onode) == 0) {
                T *rynew = new T[N + NX];
                std::fill(rynew, rynew + N + NX, 0);
                rymap[onode] = rypair_t(false, rynew);
            }

            rypair_t& ryopair = rymap[onode];
            bool inq = false;
            T *ryo = ryopair.second;
            T c = alpha / deg;
            double B = C * odeg;
            for(int i = 0; i < N - 1; i++) {
                ryo[i] += c *  dyp[i];
                inq = inq || (std::abs(ryo[i]) > B);
            }
            inq = inq || (std::abs(ryo[N - 1]) > B);
            if (!ryopair.first && inq) {
                violating.push(onode);
                ryopair.first = true;
            }
        }
    }

    // Yank values to y, freeing other parts of ry in the process.
    y.clear();
    for(auto it = rymap.begin(); it != rymap.end(); it++) {
        if (it->second.second[N] != 0) {
            El::Matrix<T> *yv = new El::Matrix<T>(NX, 1);
            for(int i = 0; i < NX; i++)
                yv->Set(i, 0, it->second.second[N + i]);
            y[it->first] = yv;
        }
        delete it->second.second;
    }
}

/**
 * Find a local cluster in a graph using a set of seed nodes.
 *
 * Based on the algorithm in
 * "Community Detection Using Time-Dependent PageRank"
 * by Haim Avron and Lior Horesh
 *
 * @tparam GraphType type of graph object. Needs to support the following:
 *                   GraphType::vertex_type - type of vertex.
 *                   GraphType::num_edges() - number of edges.
 *                   GraphType::deg(node) - debgree of a node.
 *                   GraphType::adjanct_begin(node),
 *                   GraphType::adjanct_end(node) -
 *                    begining and end iterators to adjancy container
 *                    container can be any type.
 * @param G input graph
 * @param seeds seed nodes
 * @param cluster output cluster of nodes
 * @param alpha,gamma,epsilon, NX - parameters (see paper).
 * @param recursive - recursively run on output as seed until conductance
 *                    steps reducing.
 */
template<typename GraphType>
double FindLocalCluster(const GraphType& G,
    const std::unordered_set<typename GraphType::vertex_type>& seeds,
    std::unordered_set<typename GraphType::vertex_type>& cluster,
    double alpha = 0.85, double gamma = 5.0, double epsilon = 0.001, int NX = 4,
    bool recursive = false) {

    typedef typename GraphType::vertex_type vertex_type;
    double currentcond = -1;
    cluster = seeds;
    bool improve;
    El::Matrix<double> x;

    do {
        // Create seed set.
        std::unordered_map<vertex_type, double> s;
        for(auto it = cluster.begin(); it != cluster.end(); it++)
            s[*it] = 1.0 / cluster.size();

        // Run the diffusion
        std::unordered_map<vertex_type, El::Matrix<double>*> y;
        TimeDependentPPR(G, s, y, x, alpha, gamma, epsilon, NX);

        // Go over the y output at the different time samples,
        // find the best prefix and if better conductance, store it.
        improve = false;
        for (int t = 0; t < NX; t++) {
            // Sort (descending) the non-zero components based on their normalized
            // y values (normalized by degree).
            std::vector<std::pair<double, vertex_type> > vals(y.size());
            int i = 0;
            for(auto it = y.begin(); it != y.end(); it++) {
                vertex_type node = it->first;
                double val = - it->second->Get(t, 0) / G.degree(node);
                vals[i] = std::make_pair(val, node);
                i++;
            }
            std::sort(vals.begin(), vals.end());

            // Find the best prefix
            int volS = 0, cutS = 0;
            double bestcond = 1.0;
            int bestprefix = 0;
            int Gvol = G.num_edges();
            std::unordered_set<vertex_type> currentset;
            for (int i = 0; i < vals.size(); i++) {
                vertex_type node = vals[i].second;
                size_t deg = G.degree(node);
                volS += deg;
                for(auto it = G.adjanct_begin(node);
                    it != G.adjanct_end(node); it++) {
                    const vertex_type &onode = *it;
                    if (currentset.count(onode))
                        cutS--;
                    else
                        cutS++;
                }

                double condS =
                    static_cast<double>(cutS) / std::min(volS, Gvol - volS);
                if (condS < bestcond) {
                    bestcond = condS;
                    bestprefix = i;
                }
                currentset.insert(node);
            }

            if (currentcond == -1 || bestcond < 0.999999 * currentcond) {
                // We have a new best cluster - the best perfix.
                improve = true;
                cluster.clear();
                for(int i = 0; i <= bestprefix; i++)
                    cluster.insert(vals[i].second);
                currentcond = bestcond;
            }
        }

        // Clear y
        for(auto it = y.begin(); it != y.end(); it++)
            delete it->second;
    } while (recursive && improve);

    return currentcond;
}

} }   // namespace skylark::ml

#endif // SKYLARK_LOCAL_COMPUTATIONS_HPP


#include "bio/matrix_dependencies.h"
#include "bio/biobase_db.h"
#include "bio/biobase_filter.h"
#include "bio/biobase_data_traits.h"
USING_BIO_NS

#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/progress.hpp>
#include <boost/assign/list_of.hpp>
using namespace boost;
using namespace boost::assign;
using boost::unit_test::test_suite;

#include <gsl/gsl_math.h>

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

void
check_matrix_dependencies_arg(pssm_contingency_tables_t & contingency_tables)
{
	pssm_dependency_results_t results;
	calculate_dependencies(
		contingency_tables,
		results);

	//ordered set of results
	pssm_dependency_result_set result_set;
	build_result_set(results, result_set);

#ifdef VERBOSE
	unsigned max_to_show = 5;
	for (pssm_dependency_result_set::const_iterator i = result_set.begin();
		result_set.end() != i && 0 != max_to_show;
		++i, --max_to_show)
	{
		cout
			<< (*i)->first << " : " << (*i)->second.homogeneity_bayes_factor << '\n'
			<< contingency_tables[(*i)->first]
			<< '\n';
	}
#endif //VERBOSE
}

void
check_simple_contingency_dependencies_arg(base_pair_contingency_table_t first)
{
	//make the other entry in transpose of the first
	base_pair_contingency_table_t reverse = first;
	for (unsigned b1 = 0; 4 != b1; ++b1)
	{
		for (unsigned b2 = b1 + 1; 4 != b2; ++b2)
		{
			std::swap(reverse.data[b1][b2], reverse.data[b2][b1]);
		}
	}

	pssm_contingency_tables_t contingency_tables;
	contingency_tables[BasePair(0,1)] = first;
	contingency_tables[BasePair(1,0)] = reverse;

	check_matrix_dependencies_arg(contingency_tables);
}

void
check_simple_contingency_dependencies_table(base_pair_contingency_table_t::table_t data)
{
	base_pair_contingency_table_t first;
	first.data = data;
	check_simple_contingency_dependencies_arg(first);
}

void
check_simple_contingency_dependencies()
{
	cout << "******* check_simple_contingency_dependencies()" << endl;

	check_simple_contingency_dependencies_table(
		list_of
			(list_of(10)( 0)( 0)( 0))
			(list_of( 0)(10)( 0)( 0))
			(list_of( 0)( 0)(10)( 0))
			(list_of( 0)( 0)( 0)(10)));

	check_simple_contingency_dependencies_table(
		list_of
			(list_of(10)(10)(10)(10))
			(list_of(10)(10)(10)(10))
			(list_of(10)(10)(10)(10))
			(list_of(10)(10)(10)(10)));

	check_simple_contingency_dependencies_table(
		list_of
			(list_of(10)( 2)( 0)( 1))
			(list_of(10)( 2)( 0)( 1))
			(list_of(10)( 2)( 0)( 1))
			(list_of(10)( 2)( 0)( 1)));
}

struct MatrixResult
{
	const Matrix * matrix;
	BasePair base_pair;
	BasePairDependencyResults results;

	MatrixResult()
		: base_pair(0, 0)
	{
	}

	bool operator<(const MatrixResult & rhs) const
	{
		return results.homogeneity_bayes_factor < rhs.results.homogeneity_bayes_factor;
	}
};

struct MatrixResultLessThan
{
	double threshold;
	MatrixResultLessThan(double threshold) : threshold(threshold) { }
	bool operator()(const MatrixResult & result) { return result.results.homogeneity_bayes_factor < threshold; }
};

void
check_matrix_dependencies_arg(matrix_filter_it matrices_begin, matrix_filter_it matrices_end)
{
	//calculate the dependencies for all the matrices we can
	matrix_dependencies_map_t matrix_dependencies;
	std::set<MatrixResult> matrix_results;
	for (matrix_filter_it i = matrices_begin;
		i != matrices_end;
		++i)
	{
		if (i->second->align_descs.empty())
		{
			continue;
		}

		try
		{
#ifdef VERBOSE_CHECKING
			cout << i->second->get_name() << '\n';
#endif

			matrix_dependencies[i->second.get()] = MatrixDependencies(i->second.get());

			for (pssm_dependency_results_t::const_iterator j = matrix_dependencies[i->second.get()].results.begin();
				matrix_dependencies[i->second.get()].results.end() != j;
				++j)
			{
				MatrixResult result;
				result.matrix = i->second.get();
				result.base_pair = j->first;
				result.results = j->second;
				matrix_results.insert(result);
			}
		}
#ifdef VERBOSE_CHECKING
		catch (const std::exception & ex)
		{
			cout << ex.what() << endl;
		}
		catch (const std::string & ex)
		{
			cout << ex.c_str() << endl;
		}
		catch (const char * ex)
		{
			cout << ex << endl;
		}
		catch (...)
		{
			cout << "Unknown exception" << endl;
		}
#else
		catch (...)
		{
		}
#endif
	}

#ifdef VERBOSE_CHECKING
	const double threshold = 0.09;
	cout
		<< "Gathered " << matrix_results.size() << " results; "
		<< std::count_if(matrix_results.begin(), matrix_results.end(), MatrixResultLessThan(threshold))
		<< " are less than " << threshold << "\n";
	unsigned max_to_show = 100;
	for (std::set<MatrixResult>::const_iterator i = matrix_results.begin();
		matrix_results.end() != i && 0 != max_to_show;
		++i, --max_to_show)
	{
		cout
			<< i->matrix->get_name() << '\t'
			<< i->base_pair << '\t'
			<< i->results.homogeneity_bayes_factor << '\n';
		cout << matrix_dependencies[i->matrix].contingency_tables[i->base_pair] << '\n';
	}
#endif //VERBOSE_CHECKING
}

void
check_matrix_dependencies()
{
	cout << "******* check_matrix_dependencies()" << endl;

	const matrix_filter_it matrices_begin(BiobaseDb::singleton().get_matrices().begin(), BiobaseDb::singleton().get_matrices().end());
	const matrix_filter_it matrices_end(BiobaseDb::singleton().get_matrices().end(), BiobaseDb::singleton().get_matrices().end());

	check_matrix_dependencies_arg(matrices_begin, matrices_end);
}

void
check_crebatf_dependencies()
{
	cout << "******* check_crebatf_dependencies()" << endl;

	Matrix::map_t::iterator begin = BiobaseDb::singleton().get_matrices().find(TableLink(MATRIX_DATA, 981));
	Matrix::map_t::iterator end = begin; ++end;

	const matrix_filter_it matrices_begin(begin, end);
	const matrix_filter_it matrices_end(end, end);

	check_matrix_dependencies_arg(matrices_begin, matrices_end);
}

void
check_simple_sequence_dependencies()
{
	cout << "******* check_simple_sequence_dependencies()" << endl;

	pssm_source_matrix_t pssm_source;
	add_sequence_to_pssm_source("ca", pssm_source);
	add_sequence_to_pssm_source("cc", pssm_source);
	add_sequence_to_pssm_source("cg", pssm_source);
	add_sequence_to_pssm_source("ct", pssm_source);

	pssm_contingency_tables_t contingency_tables;
	count_frequencies(
		pssm_source,
		contingency_tables);

	check_matrix_dependencies_arg(contingency_tables);
}

void register_matrix_dependencies_tests(test_suite * test)
{
	test->add(BOOST_TEST_CASE(&check_simple_sequence_dependencies), 0);
	test->add(BOOST_TEST_CASE(&check_simple_contingency_dependencies), 0);
	test->add(BOOST_TEST_CASE(&check_matrix_dependencies), 0);
	test->add(BOOST_TEST_CASE(&check_crebatf_dependencies), 0);
}


#include <stan/math/prim/mat.hpp>
#include <stan/math/prim/scal.hpp>
#include <test/unit/math/prim/prob/vector_rng_test_helper.hpp>
#include <test/unit/math/prim/prob/util.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/math/distributions.hpp>
#include <gtest/gtest.h>
#include <limits>
#include <vector>

class BetaTestRig : public VectorRealRNGTestRig {
 public:
  BetaTestRig()
      : VectorRealRNGTestRig(10000, 10, {0.5, 1.0, 1.3, 2.0}, {1, 2, 3},
                             {-2.5, -1.7, -0.1, 0.0}, {-3, -2, -1, 0},
                             {0.25, 0.5, 1.0, 1.7, 2.1}, {1, 2, 3, 4},
                             {-2.7, -1.5, -0.5, 0.0}, {-3, -2, -1, 0}) {}

  template <typename T1, typename T2, typename T3, typename T_rng>
  auto generate_samples(const T1& alpha, const T2& beta, const T3&,
                        T_rng& rng) const {
    return stan::math::beta_rng(alpha, beta, rng);
  }

  std::vector<double> generate_quantiles(double alpha, double beta,
                                         double) const {
    std::vector<double> quantiles;
    double K = stan::math::round(2 * std::pow(N_, 0.4));
    boost::math::beta_distribution<> dist(alpha, beta);

    for (int i = 1; i < K; ++i) {
      double frac = i / K;
      quantiles.push_back(quantile(dist, frac));
    }
    quantiles.push_back(std::numeric_limits<double>::max());

    return quantiles;
  }
};

TEST(ProbDistributionsBeta, errorCheck) {
  check_dist_throws_all_types(BetaTestRig());
}

TEST(ProbDistributionsBeta, distributionTest) {
  check_quantiles_real_real(BetaTestRig());
}

TEST(ProbDistributionsBeta, error_check) {
  boost::random::mt19937 rng;
  EXPECT_NO_THROW(stan::math::beta_rng(2.0, 1.0, rng));
  EXPECT_NO_THROW(stan::math::beta_rng(1e-10, 1e-10, rng));

  EXPECT_THROW(stan::math::beta_rng(2.0, -1.0, rng), std::domain_error);
  EXPECT_THROW(stan::math::beta_rng(-2.0, 1.0, rng), std::domain_error);
  EXPECT_THROW(stan::math::beta_rng(-2.0, -1.0, rng), std::domain_error);
  EXPECT_THROW(stan::math::beta_rng(stan::math::positive_infinity(), 1.0, rng),
               std::domain_error);
  EXPECT_THROW(stan::math::beta_rng(2, stan::math::positive_infinity(), rng),
               std::domain_error);
}

TEST(ProbDistributionsBeta, chiSquareGoodnessFitTest) {
  boost::random::mt19937 rng;
  int N = 10000;
  int K = stan::math::round(2 * std::pow(N, 0.4));

  std::vector<double> samples;
  for (int i = 0; i < N; ++i) {
    samples.push_back(stan::math::beta_rng(2.0, 1.0, rng));
  }

  // Generate quantiles from boost's beta distribution
  boost::math::beta_distribution<> dist(2.0, 1.0);
  std::vector<double> quantiles;
  for (int i = 1; i < K; ++i) {
    double frac = static_cast<double>(i) / K;
    quantiles.push_back(quantile(dist, frac));
  }
  quantiles.push_back(std::numeric_limits<double>::max());

  // Assert that they match
  assert_matches_quantiles(samples, quantiles, 1e-6);
}

TEST(ProbDistributionsBeta, chiSquareGoodnessFitTestSmallParameters) {
  boost::random::mt19937 rng;
  int N = 10000;
  int K = stan::math::round(2 * std::pow(N, 0.4));

  std::vector<double> samples;
  for (int i = 0; i < N; ++i) {
    samples.push_back(stan::math::beta_rng(0.2, 0.3, rng));
  }

  // Generate quantiles from boost's beta distribution
  boost::math::beta_distribution<> dist(0.2, 0.3);
  std::vector<double> quantiles;
  for (int i = 1; i < K; ++i) {
    double frac = static_cast<double>(i) / K;
    quantiles.push_back(quantile(dist, frac));
  }
  quantiles.push_back(std::numeric_limits<double>::max());

  // Assert that they match
  assert_matches_quantiles(samples, quantiles, 1e-6);
}


#include <blitz/array.h>
#include <iostream>

BZ_USING_NAMESPACE(blitz)

BZ_DECLARE_STENCIL2(footprint,A,B)
    B = stenciltoapply(stencilargs);
BZ_END_STENCIL

template<class T_stencil>
void dumpStencil(const T_stencil& stencil)
{
    const int maxN = 5;

    Array<double,2> A(Range(-maxN,maxN),Range(-maxN,maxN)),
     B(Range(-maxN,maxN), Range(-maxN, maxN));

    A = 0.0;
    A(0,0) = 1;

    B = 0.0;

    applyStencil(stencil, A, B);

    B.reverseSelf(firstDim);
    B.reverseSelf(secondDim);

    using namespace blitz::tensor;

    int minrow = first(sum(B != 0.0, j));
    int maxrow = last(sum(B != 0.0, j));
    int mincol = first(sum(B(j,i) != 0.0, j));
    int maxcol = last(sum(B(j,i) != 0.0, j));

    // Code to dump HTML
		cout << "@html" << endl;
    cout << "
" << endl
         << "
	";

    for (int j=mincol; j <= maxcol; ++j)
        cout << "" << j << "	";

    cout << "
";

    for (int i=minrow; i <= maxrow; ++i)
    {
        cout << "
";

        if (minrow != maxrow)
            cout << "" << i << "	";
        else
            cout << "	";

        for (int j=mincol; j <= maxcol; ++j)
        {
            if (B(i,j) != 0.0)
            {
                if ((i == 0) && (j == 0))
                    cout << "";
                else
                    cout << "";
                cout << "" << B(i,j) << "	";
            }
            else
                cout << "	";
        }

        cout << "
" << endl;
    }
    cout << "
" << endl;
    cout << "@end html" << endl;

    // Code to dump TeX
		cout << "@tex" << endl;
		cout << "\\def\\tablevrule{\\vrule height 10pt depth 3pt}" << endl;
		cout << "\\vskip0.5\\baselineskip\\hskip\\itemindent" << endl;
		cout << "\\vbox{\\halign{\\hfill#\\quad&\\tablevrule#\\quad" << endl;
    for (int j=mincol; j <= maxcol; ++j)
        cout << "&\\hfill#\\quad";
    cout << "\\cr" << endl;
    cout << "&& ";
    for (int j=mincol; j < maxcol; ++j)
        cout << j << " & " ;
    cout << maxcol << " \\cr" << endl << "\\noalign{\\hrule}" << endl;
    for (int i=minrow; i <= maxrow; ++i)
    {
        if (minrow != maxrow)
            cout << i << " && ";
        else
            cout << " && ";
        for (int j=mincol; j <= maxcol; ++j)
        {
            if (B(i,j) != 0.0)
            {
                if ((i == 0) && (j == 0))
                    cout << "{\\bf " << B(i,j) << "} ";
                else
                    cout << " " << B(i,j) << " ";
            }
            if (j != maxcol)
                cout << " & ";
        }
        if (i != maxrow)
            cout << "\\cr" << endl;
    }
    cout <<  "\\cr}}" << endl;
    cout << "@end tex" << endl;

    // Code to dump !TeX && !HTML
		cout << "@ifnottex" << endl;
		cout << "@ifnothtml" << endl;
    cout << "@multitable {12345}";
    for (int j=mincol; j <= maxcol; ++j)
        cout << " {12345}";
    cout << endl << "@item @tab ";
    for (int j=mincol; j < maxcol; ++j)
        cout << j << " @tab " ;
    cout << maxcol << endl << "@item ";
    for (int i=minrow; i <= maxrow; ++i)
    {
        if (minrow != maxrow)
            cout << i << " @tab ";
        else
            cout << "@tab ";
        for (int j=mincol; j <= maxcol; ++j)
        {
            if (B(i,j) != 0.0)
            {
                if ((i == 0) && (j == 0))
                    cout << "@strong{" << B(i,j) << "} ";
                else
                    cout << " " << B(i,j) << " ";
            }
            if (j != maxcol)
                cout << "@tab ";
        }
        if (i != maxrow)
            cout << endl << "@item ";
    }
    cout << endl << "@end multitable" << endl;
		cout << "@end ifnothtml" << endl;
		cout << "@end ifnottex" << endl;

#if 0
    // Code to dump LaTeX
		cout << "latexcommand(" << endl;
    cout << "\\begin{tabular}{r|";
    for (int j=mincol; j <= maxcol; ++j)
        cout << "r";
    cout << "}" << endl;
    cout << " & ";
    for (int j=mincol; j < maxcol; ++j)
        cout << j << " & " ;
    cout << maxcol << " \\\\ \\hline" << endl;
    for (int i=minrow; i <= maxrow; ++i)
    {
        if (minrow != maxrow)
            cout << i << " & ";
        else
            cout << " & ";
        for (int j=mincol; j <= maxcol; ++j)
        {
            if (B(i,j) != 0.0)
            {
                if ((i == 0) && (j == 0))
                    cout << "{\\bf " << B(i,j) << "} ";
                else
                    cout << " " << B(i,j) << " ";
            }
            if (j != maxcol)
                cout << " & ";
        }
        if (i != maxrow)
            cout << " \\\\  " << endl;
    }
    cout << endl << "\\end{tabular}" << endl;
    cout << ")" << endl;
#endif
}

int main()
{
    dumpStencil(footprint());
}


#include "bio/contingency_homogeneity.h"
#include "bio/partition.h"
USING_BIO_NS

#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/progress.hpp>
#include <boost/array.hpp>
#include <boost/assign/list_of.hpp>
using namespace boost;
using namespace boost::assign;
using boost::unit_test::test_suite;

#include <iostream>
using namespace std;

//#define VERBOSE

typedef ContingencyTable<2,2> ode_table_t;
ode_table_t ode_table;
ode_table_t::lambda_vec_t ode_lambda;

typedef ContingencyTable<2,25> mobility_table_t;
mobility_table_t mobility_table;
mobility_table_t::lambda_vec_t mobility_lambda;

void
build_data()
{
	static bool data_built = false;
	if (! data_built)
	{
		ode_table.data =
			list_of
				(list_of( 9)(12))
				(list_of(78)(13));
		std::fill(ode_lambda.begin(), ode_lambda.end(), 1.0);

		mobility_table.data =
			list_of
				(list_of( 50)( 18))
				(list_of( 45)( 17))
				(list_of(  8)( 16))
				(list_of( 18)(  4))
				(list_of(  8)(  2))
				(list_of( 28)( 24))
				(list_of(174)(105))
				(list_of( 84)(109))
				(list_of(154)( 59))
				(list_of( 55)( 21))
				(list_of( 11)( 23))
				(list_of( 78)( 84))
				(list_of(110)(289))
				(list_of(223)(217))
				(list_of( 96)( 95))
				(list_of( 14)(  8))
				(list_of(150)( 49))
				(list_of(185)(175))
				(list_of(714)(348))
				(list_of(447)(198))
				(list_of(  3)(  6))
				(list_of( 42)(  8))
				(list_of( 72)( 69))
				(list_of(320)(201))
				(list_of(411)(246))
				;
		std::fill(mobility_lambda.begin(), mobility_lambda.end(), 1.0);

		data_built = true;
	}
}

template <class PartitionIt>
void
print_partitions(PartitionIt it)
{
	typedef std::vector<char> data_set_t;
	static data_set_t v;

	// initialize the set
	if (v.empty())
	{
		for (unsigned i = 0; i < 26; ++i)
		{
			v.push_back('a' + i);
		}
	}

	try
	{
		while (true)
		{
			std::cout << it << " : " << it.subsets() << " : ";

			std::auto_ptr<std::vector<data_set_t> > part = it[v];
			std::cout << *part << '\n';

			++it;
		}
	}
	catch (std::overflow_error&) //how the partition code determines there are no more partitions!
	{
	}
}

template <class PartitionIt>
unsigned
get_num_partitions(PartitionIt it)
{
	unsigned result = 0;
	try
	{
		while (true)
		{
			++result;

			++it;
		}
	}
	catch (std::overflow_error&) //how the partition code determines there are no more partitions!
	{
	}
	return result;
}

void
check_partitions()
{
	cout << "******* check_partitions()" << endl;

	const unsigned size = 4;

#ifdef VERBOSE
	//all partitions
	print_partitions(partition::iterator(size));
	cout << endl << endl;

	//the partitions of size = 3
	print_partitions(partition::iterator_k(size, 3));
#endif //VERBOSE

	BOOST_CHECK_EQUAL(5u, get_num_partitions(partition::iterator(3)));
	BOOST_CHECK_EQUAL(1u, get_num_partitions(partition::iterator_k(3, 3)));
	BOOST_CHECK_EQUAL(3u, get_num_partitions(partition::iterator_k(3, 2)));
	BOOST_CHECK_EQUAL(1u, get_num_partitions(partition::iterator_k(3, 1)));

	BOOST_CHECK_EQUAL(15u, get_num_partitions(partition::iterator(4)));
	BOOST_CHECK_EQUAL(1u, get_num_partitions(partition::iterator_k(4, 4)));
	BOOST_CHECK_EQUAL(6u, get_num_partitions(partition::iterator_k(4, 3)));
	BOOST_CHECK_EQUAL(7u, get_num_partitions(partition::iterator_k(4, 2)));
	BOOST_CHECK_EQUAL(1u, get_num_partitions(partition::iterator_k(4, 1)));
}

void
check_contingency_calculate_L()
{
	cout << "******* check_calculate_L()" << endl;

	build_data();

	BOOST_CHECK_CLOSE(
		3.9831225868046277e-027, //not verified except by correct calculation of bayes factor for ode data
		contingency_calculate_L(
			1,
			ode_table.data.begin(),
			ode_table.data.end(),
			ode_lambda.begin(),
			ode_lambda.end()),
		0.001);
	BOOST_CHECK_CLOSE(
		2.3831634926329567e-024, //not verified except by correct calculation of bayes factor for ode data
		contingency_calculate_L(
			2,
			ode_table.data.begin(),
			ode_table.data.end(),
			ode_lambda.begin(),
			ode_lambda.end()),
		0.001);
}

void
print_contingency_calculate_gamma_exponential_prior(
	unsigned m,
	unsigned k,
	double mean)
{
	ExponentialPrior exp_prior(1.0);

	cout
		<< "m = " << m
		<< ", k = " << k
		<< ", mean = " << mean
		<< ", result = " << contingency_calculate_gamma(m, k, &exp_prior)
		<< endl;
}

void
check_contingency_calculate_gamma_exponential_prior()
{
	cout << "******* contingency_calculate_gamma()" << endl;

	build_data();

#ifdef VERBOSE
	print_contingency_calculate_gamma_exponential_prior(1, 2, 1);
	print_contingency_calculate_gamma_exponential_prior(2, 2, 1);
#endif //VERBOSE

	ExponentialPrior exp_prior(1.0);

	BOOST_CHECK_CLOSE(
		0.59634736,
		contingency_calculate_gamma(1, 2, &exp_prior),
		0.001);
	BOOST_CHECK_CLOSE(
		0.40365264,
		contingency_calculate_gamma(2, 2, &exp_prior),
		0.001);
	BOOST_CHECK_CLOSE(
		0.59634736,
		contingency_calculate_gamma(3, 2, &exp_prior),
		0.001);
	BOOST_CHECK_CLOSE(
		1.4036526,
		contingency_calculate_gamma(4, 2, &exp_prior),
		0.001);
}

template <class ContingencyTable>
void
t_check_contingency_calculate_bayes_factor(
	const ContingencyTable & table,
	const typename ContingencyTable::lambda_vec_t & lambda,
	const gamma_vec_t & contingency_gamma_exponential_prior,
	double expected_bayes_factor)
{
#ifdef VERBOSE
	boost::progress_timer timer;
#endif

	const double bayes_factor =
		contingency_calculate_bayes_factor(
			contingency_gamma_exponential_prior.begin(),
			contingency_gamma_exponential_prior.end(),
			table.data.begin(),
			table.data.end(),
			lambda.begin(),
			lambda.end());

	const double bayes_factor_2 =
		contingency_calculate_L(1, table.data.begin(), table.data.end(), lambda.begin(), lambda.end())
			/ contingency_calculate_L(2, table.data.begin(), table.data.end(), lambda.begin(), lambda.end());

#ifdef VERBOSE
	cout << "Bayes factor: "
		<< bayes_factor
		<< endl;
	cout
		<< "L1(n) / L2(n): "
		<< bayes_factor_2
		<< endl;
#endif //VERBOSE

	BOOST_CHECK_CLOSE(bayes_factor, expected_bayes_factor, 0.001);
	BOOST_CHECK_CLOSE(bayes_factor_2, expected_bayes_factor, 0.001);
}

void
check_contingency_calculate_bayes_factor()
{
	cout << "******* check_contingency_calculate_bayes_factor()" << endl;

	build_data();

	ExponentialPrior exp_prior(1.0);

	gamma_vec_t contingency_gamma_exponential_prior;
	contingency_gamma_exponential_prior.push_back(
		contingency_calculate_gamma(1, 2, &exp_prior));
	contingency_gamma_exponential_prior.push_back(
		contingency_calculate_gamma(2, 2, &exp_prior));

	t_check_contingency_calculate_bayes_factor(
		ode_table,
		ode_lambda,
		contingency_gamma_exponential_prior,
		0.001671359);

	//takes too long and produces not a number
	if (false)
	{
		t_check_contingency_calculate_bayes_factor(
			mobility_table,
			mobility_lambda,
			contingency_gamma_exponential_prior,
			0.0);
	}
}

void register_contingency_homogeneity_tests(test_suite * test)
{
	test->add(BOOST_TEST_CASE(&check_contingency_calculate_gamma_exponential_prior), 0);
	test->add(BOOST_TEST_CASE(&check_partitions), 0);
	test->add(BOOST_TEST_CASE(&check_contingency_calculate_L), 0);
	test->add(BOOST_TEST_CASE(&check_contingency_calculate_bayes_factor), 0);
}


//          Copyright Carl Philipp Reh 2009 - 2016.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <fcppt/mpl/append.hpp>
#include <fcppt/config/external_begin.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/vector/vector10.hpp>
#include <type_traits>
#include <fcppt/config/external_end.hpp>

int
main()
{
	typedef
	boost::mpl::vector2<
		int,
		float
	>
	vec1;

	typedef
	boost::mpl::vector2<
		double,
		bool
	>
	vec2;

	typedef
	fcppt::mpl::append<
		vec1,
		vec2
	>::type
	appended;

	static_assert(
		std::is_same<
			boost::mpl::at_c<
				appended,
				0
			>::type,
			int
		>::value,
		""
	);

	static_assert(
		std::is_same<
			boost::mpl::at_c<
				appended,
				1
			>::type,
			float
		>::value,
		""
	);

	static_assert(
		std::is_same<
			boost::mpl::at_c<
				appended,
				2
			>::type,
			double
		>::value,
		""
	);

	static_assert(
		std::is_same<
			boost::mpl::at_c<
				appended,
				3
			>::type,
			bool
		>::value,
		""
	);
}


#include <iostream>
#include <Eigen/Dense>
#include <gflags/gflags.h>
#include "lcm/lcm-cpp.hpp"

DEFINE_string(file, "", "Log file name.");
DEFINE_int64(max_count, 5000, "Max number of messages to read.");

// A quick script to test the frequency of messages in an LCM log.
// Intended to be run with a log that has only one channel in it. Could
// easily be modified to first check against a desired channel name, see
// dispatcher_log_timing_test.cc for an example of this.
// Prints out the mean and standard deviation of the delta-times
int main(int argc, char* argv[]) {
  gflags::ParseCommandLineFlags(&argc, &argv, true);
  lcm::LogFile log(FLAGS_file, "r");

  auto event = log.readNextEvent();

  double time = event->timestamp;
  double last_time = time;
  event = log.readNextEvent();
  time = event->timestamp;

  Eigen::VectorXd times(FLAGS_max_count);
  Eigen::VectorXd dt(FLAGS_max_count);
  int count = 0;

  while (event != NULL &&
         count < FLAGS_max_count) {
    time = event->timestamp;
    times(count) = time;
    dt(count) = time - last_time;
    last_time = time;
    count++;
    event = log.readNextEvent();
  }

  if (count < FLAGS_max_count) {
    times.conservativeResize(count);
    dt.conservativeResize(count);
  }

  auto std_dev = std::sqrt(
      (dt.rowwise() - dt.colwise().mean()).squaredNorm()/(dt.size() - 1));
  std::cout << "dt (microseconds) mean: " << dt.mean() << " std_dev: " <<
      std_dev << std::endl;
}


#include "stdafx.h"

#include "problem.hpp"
#include "utility.hpp"

#include <fstream>
#include <utility>
#include <unordered_map>
#include <unordered_set>
#include <numeric>
#include <boost/algorithm/string.hpp>

struct advent_2017_13 : problem
{
	advent_2017_13() noexcept : problem(2017, 13) {
	}

protected:
	std::unordered_map<std::size_t, std::size_t> firewall;

	void prepare_input(std::ifstream& fin) override {
		for(std::string line; std::getline(fin, line); ) {
			std::size_t depth = std::stoull(line.substr(0, line.find(':')));
			std::size_t range = std::stoull(line.substr(line.find(' ')));
			firewall[depth] = (range - 1) * 2;
		}
	}

	enum { caught, severity };

	auto traverse_firewall(std::size_t delay) {
		return utility::transform_reduce(std::begin(firewall),
		                                 std::end(firewall),
		                                 std::make_pair(false, 0ull),
		                                 [](const auto& lhs, const auto& rhs) {
		                                     return std::make_pair(std::get<caught>(lhs) || std::get<caught>(rhs),
		                                                           std::get<severity>(lhs) + std::get<severity>(rhs));
		                                 },
		                                 [delay](const auto& layer) {
		                                     const auto [depth, range] = layer;
		                                     if((depth + delay) % range == 0) {
		                                         return std::make_pair(true, depth * ((range / 2) + 1));
		                                     } else {
		                                         return std::make_pair(false, 0ull);
		                                     }
		                                 }
		);
	}

	std::string part_1() override {
		auto result = traverse_firewall(0);
		return std::to_string(result.second);
	}

	std::string part_2() override {
		for(std::size_t delay = 0; ; ++delay) {
			auto result = traverse_firewall(delay);
			if(!result.first) {
				return std::to_string(delay);
			}
		}
	}
};

REGISTER_SOLVER(2017, 13);


/**
 * @file scd_test.cpp
 * @author Shikhar Bhardwaj
 *
 * Test file for SCD (stochastic coordinate descent).
 *
 * mlpack is free software; you may redistribute it and/or modify it under the
 * terms of the 3-clause BSD license.  You should have received a copy of the
 * 3-clause BSD license along with mlpack.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */
#include <mlpack/core.hpp>
#include <mlpack/core/optimizers/scd/scd.hpp>
#include <mlpack/core/optimizers/scd/descent_policies/greedy_descent.hpp>
#include <mlpack/core/optimizers/scd/descent_policies/cyclic_descent.hpp>
#include <mlpack/core/optimizers/parallel_sgd/sparse_test_function.hpp>
#include <mlpack/methods/logistic_regression/logistic_regression_function.hpp>
#include <mlpack/methods/softmax_regression/softmax_regression_function.hpp>

#include <boost/test/unit_test.hpp>
#include "test_tools.hpp"

using namespace std;
using namespace mlpack;
using namespace mlpack::math;
using namespace mlpack::optimization;
using namespace mlpack::optimization::test;
using namespace mlpack::regression;

BOOST_AUTO_TEST_SUITE(SCDTest);

/**
 * Test the correctness of the SCD implementation by using a dataset with a
 * precalculated minima.
 */
BOOST_AUTO_TEST_CASE(PreCalcSCDTest)
{
  arma::mat predictors("0 0 0.4; 0 0 0.6; 0 0.3 0; 0.2 0 0; 0.2 -0.5 0;");
  arma::Row<size_t> responses("1  1  0;");

  LogisticRegressionFunction<arma::mat> f(predictors, responses, 0.0001);

  SCD<> s(0.02, 60000, 1e-5);
  arma::mat iterate = f.InitialPoint();

  double objective = s.Optimize(f, iterate);

  BOOST_REQUIRE_LE(objective, 0.055);
}

/**
 * Test the correctness of the SCD implemenation by using the sparse test
 * function, with dijoint features which optimize to a precalculated minima.
 */
BOOST_AUTO_TEST_CASE(DisjointFeatureTest)
{
  // The test function for parallel SGD should work with SCD, as the gradients
  // of the individual functions are projections into the ith dimension.
  SparseTestFunction f;
  SCD<> s(0.4);

  arma::mat iterate = f.GetInitialPoint();

  double result = s.Optimize(f, iterate);

  // The final value of the objective function should be close to the optimal
  // value, that is the sum of values at the vertices of the parabolas.
  BOOST_REQUIRE_CLOSE(result, 123.75, 0.01);

  // The co-ordinates should be the vertices of the parabolas.
  BOOST_REQUIRE_CLOSE(iterate[0], 2, 0.02);
  BOOST_REQUIRE_CLOSE(iterate[1], 1, 0.02);
  BOOST_REQUIRE_CLOSE(iterate[2], 1.5, 0.02);
  BOOST_REQUIRE_CLOSE(iterate[3], 4, 0.02);
}

/**
 * Test the greedy descent policy.
 */
BOOST_AUTO_TEST_CASE(GreedyDescentTest)
{
  // In the sparse test function, the given point has the maximum gradient at
  // the feature with index 2.
  arma::mat point("1; 2; 3; 4;");

  SparseTestFunction f;

  GreedyDescent descentPolicy;

  BOOST_REQUIRE_EQUAL(descentPolicy.DescentFeature(0, point, f), 2);

  // Changing the point under consideration, so that the maximum gradient is at
  // index 1.
  point[1] = 10;

  BOOST_REQUIRE_EQUAL(descentPolicy.DescentFeature(0, point, f), 1);
}

/**
 * Test the cyclic descent policy.
 */
BOOST_AUTO_TEST_CASE(CyclicDescentTest)
{
  const size_t features = 10;
  struct DummyFunction
  {
    static size_t NumFeatures()
    {
      return features;
    }
  };

  DummyFunction dummy;

  CyclicDescent descentPolicy;

  for (size_t i = 0; i < 15; ++i)
  {
    BOOST_REQUIRE_EQUAL(descentPolicy.DescentFeature(i, arma::mat(), dummy), i %
        features);
  }
}

/**
 * Test the random descent policy.
 */
BOOST_AUTO_TEST_CASE(RandomDescentTest)
{
  const size_t features = 10;
  struct DummyFunction
  {
    static size_t NumFeatures()
    {
      return features;
    }
  };

  DummyFunction dummy;

  CyclicDescent descentPolicy;

  for (size_t i = 0; i < 100; ++i)
  {
    size_t j = descentPolicy.DescentFeature(i, arma::mat(), dummy);
    BOOST_REQUIRE_LT(j, features);
    BOOST_REQUIRE_GE(j, 0);
  }
}

/**
 * Test that LogisticRegressionFunction::PartialGradient() works as expected.
 */
BOOST_AUTO_TEST_CASE(LogisticRegressionFunctionPartialGradientTest)
{
  // Evaluate the gradient and feature gradient and equate.
  arma::mat predictors("0 0 0.4; 0 0 0.6; 0 0.3 0; 0.2 0 0; 0.2 -0.5 0;");
  arma::Row<size_t> responses("1  1  0;");

  LogisticRegressionFunction<arma::mat> f(predictors, responses, 0.0001);

  arma::mat testPoint(1, f.NumFeatures(), arma::fill::randu);

  arma::mat testGradient;

  f.Gradient(testPoint, testGradient);

  for (size_t i = 0; i < f.NumFeatures(); ++i)
  {
    arma::sp_mat fGrad;
    f.PartialGradient(testPoint, i, fGrad);

    CheckMatrices(testGradient.col(i), arma::mat(fGrad.col(i)));
  }
}

/**
 * Test that SoftmaxRegressionFunction::PartialGradient() works as expected.
 */
BOOST_AUTO_TEST_CASE(SoftmaxRegressionFunctionPartialGradientTest)
{
  const size_t points = 1000;
  const size_t inputSize = 10;
  const size_t numClasses = 5;

  // Initialize a random dataset.
  arma::mat data;
  data.randu(inputSize, points);

  // Create random class labels.
  arma::Row<size_t> labels(points);
  for (size_t i = 0; i < points; i++)
    labels(i) = RandInt(0, numClasses);

  // 2 objects for 2 terms in the cost function. Each term contributes towards
  // the gradient and thus need to be checked independently.
  SoftmaxRegressionFunction srf(data, labels, numClasses, 0);

  // Create a random set of parameters.
  arma::mat parameters;
  parameters.randu(numClasses, inputSize);

  // Get gradients for the current parameters.
  arma::mat gradient;
  srf.Gradient(parameters, gradient);

  // For each parameter.
  for (size_t j = 0; j < inputSize; j++)
  {
    // Get the gradient for this feature.
    arma::sp_mat fGrad;

    srf.PartialGradient(parameters, j, fGrad);

    CheckMatrices(gradient.col(j), arma::mat(fGrad.col(j)));
  }
}

BOOST_AUTO_TEST_SUITE_END();