#ifndef SEQUENTIAL_LINE_SEARCH_PREFERENTIAL_BAYESIAN_OPTIMIZER_HPP
#define SEQUENTIAL_LINE_SEARCH_PREFERENTIAL_BAYESIAN_OPTIMIZER_HPP

#include <Eigen/Core>
#include <memory>
#include <sequential-line-search/acquisition-function.hpp>
#include <sequential-line-search/current-best-selection-strategy.hpp>
#include <sequential-line-search/kernel-type.hpp>
#include <utility>
#include <vector>

namespace sequential_line_search
{
    class PreferenceRegressor;
    class PreferenceDataManager;

    std::vector<Eigen::VectorXd> GenerateRandomPoints(const int num_dims);

    /// \brief Optimizer class for performing preferential Bayesian optimization with discrete choice.
    ///
    /// \details This optimizer requests discrete choice queries. In the current implementation, the number of choices
    /// for each query is fixed to two (i.e., pairwise comparison). This class assumes that the search space is [0,
    /// 1]^{D}.
    class PreferentialBayesianOptimizer
    {
    public:
        /// \brief Construct an optimizer instance.
        ///
        /// \param use_map_hyperparams When this is set true, the optimizer always perform the MAP estimation for the
        /// GPR kernel hyperparameters. When this is set false, the optimizer performs the MAP estimation only for
        /// goodness values.
        PreferentialBayesianOptimizer(
            const int                 num_dims,
            const bool                use_map_hyperparams   = true,
            const KernelType          kernel_type           = KernelType::ArdMatern52Kernel,
            const AcquisitionFuncType acquisition_func_type = AcquisitionFuncType::ExpectedImprovement,
            const std::function<std::vector<Eigen::VectorXd>(const int)>& initial_query_generator =
                GenerateRandomPoints,
            const CurrentBestSelectionStrategy current_best_selection_strategy =
                CurrentBestSelectionStrategy::LargestExpectValue);

        /// \brief Specify (kernel and other) hyperparameter values.
        ///
        /// \details When the MAP estimation is enabled, the specified kernel hyperparameters will be used as the median
        /// of the prior distribution and the initial solution of the estimation. When the MAP estimation is not
        /// enabled, these values will be directly used.
        void SetHyperparams(const double kernel_signal_var            = 0.500,
                            const double kernel_length_scale          = 0.500,
                            const double noise_level                  = 0.005,
                            const double kernel_hyperparams_prior_var = 0.250,
                            const double btl_scale                    = 0.010);

        /// \brief Submit the result of the user's selection and update the internal surrogate model.
        ///
        /// \param num_map_estimation_iters The number of iterations for the MAP estimation. When a non-positive value
        /// (e.g., 0) is specified, this is heuristically set.
        void SubmitFeedbackData(const int option_index, const int num_map_estimation_iters = 0);

        /// \brief Determine the preferential query for the next iteration by using an acquisition function.
        void DetermineNextQuery(const int num_global_search_iters = 0, const int num_local_search_iters = 0);

        /// \brief Get the current options.
        const std::vector<Eigen::VectorXd>& GetCurrentOptions() const { return m_current_options; }

        /// \brief Get the point that has the highest value among the observed points.
        Eigen::VectorXd GetMaximizer() const;

        double GetPreferenceValueMean(const Eigen::VectorXd& point) const;
        double GetPreferenceValueStdev(const Eigen::VectorXd& point) const;
        double GetAcquisitionFuncValue(const Eigen::VectorXd& point) const;

        const Eigen::MatrixXd& GetRawDataPoints() const;

        void DampData(const std::string& directory_path) const;

        /// \brief Set the hyperparameter in the GP-UCB algorithm.
        ///
        /// \details This hyperparameter controls the trade-off of exploration and exploitation. Specifically, this
        /// hyperparameter corresponds to the square root of the beta in [Srinivas et al. ICML '10].
        ///
        /// If the acquisition function is not GP-UCB, this value will not be used.
        void SetGaussianProcessUpperConfidenceBoundHyperparam(const double hyperparam)
        {
            m_gaussian_process_upper_confidence_bound_hyperparam = hyperparam;
        }

    private:
        const bool m_use_map_hyperparams;

        const CurrentBestSelectionStrategy m_current_best_selection_strategy;

        std::shared_ptr<PreferenceRegressor>   m_regressor;
        std::shared_ptr<PreferenceDataManager> m_data;

        /// \details In the case of using pairwise comparison, the number of options is always two.
        std::vector<Eigen::VectorXd> m_current_options;

        double m_kernel_signal_var;
        double m_kernel_length_scale;
        double m_noise_level;
        double m_kernel_hyperparams_prior_var;
        double m_btl_scale;

        const KernelType          m_kernel_type;
        const AcquisitionFuncType m_acquisition_func_type;

        double m_gaussian_process_upper_confidence_bound_hyperparam;
    };
} // namespace sequential_line_search

#endif // SEQUENTIAL_LINE_SEARCH_PREFERENTIAL_BAYESIAN_OPTIMIZER_HPP


// created by Mark O. Brown
#include "stdafx.h"
#include "PictureControl.h"
#include <algorithm>
#include <numeric>
#include <boost/lexical_cast.hpp>
#include <qlayout.h>

PictureControl::PictureControl(bool histogramOption, Qt::TransformationMode mode)
	: histOption(histogramOption), /*QWidget(), */transformationMode(mode)
	, slider(Qt::Vertical, RangeSlider::DoubleHandles)
{
	active = true;
	if ( histOption ){
		horData.resize ( 1 );
		vertData.resize ( 1 );
		updatePlotData ( );
	}
	repaint ();
}

void PictureControl::updatePlotData ( ){
	if ( !histOption ){
		return;
	}
	horData[ 0 ].resize ( mostRecentImage_m.getCols ( ) );
	vertData[ 0 ].resize ( mostRecentImage_m.getRows ( ) );
	unsigned count = 0;

	std::vector<long> dataRow;
	for ( auto& data : horData[ 0 ] ){
		data.x = count;
		// integrate the column
		double p = 0.0;
		for ( auto row : range ( mostRecentImage_m.getRows ( ) ) ){
			p += mostRecentImage_m ( row, count );
		}
		count++;
		dataRow.push_back ( p );
	}
	count = 0;
	auto avg = std::accumulate ( dataRow.begin ( ), dataRow.end ( ), 0.0 ) / dataRow.size ( );
	for ( auto& data : horData[ 0 ] ){
		data.y = dataRow[ count++ ] - avg;
	}
	count = 0;
	std::vector<long> dataCol;
	for ( auto& data : vertData[ 0 ] ){
		data.x = count;
		// integrate the row
		double p = 0.0;
		for ( auto col : range ( mostRecentImage_m.getCols ( ) ) ){
			p += mostRecentImage_m ( count, col );
		}
		count++;
		dataCol.push_back ( p );
	}
	count = 0;
	auto avgCol = std::accumulate ( dataCol.begin ( ), dataCol.end ( ), 0.0 ) / dataCol.size ( );
	for ( auto& data : vertData[ 0 ] ){
		data.y = dataCol[ count++ ] - avgCol;
	}
}

/*
* initialize all controls associated with single picture.
*/
void PictureControl::initialize(std::string name, int width, int height, IChimeraQtWindow* parent, int picScaleFactorIn){
	QVBoxLayout* layout = new QVBoxLayout(this);
	layout->setContentsMargins(0, 0, 0, 0);
	picScaleFactor = picScaleFactorIn;
	if ( width < 100 ){
		thrower ( "Pictures must be greater than 100 in width because this is the size of the max/min"
									 "controls." );
	}
	if ( height < 100 ){
		thrower ( "Pictures must be greater than 100 in height because this is the minimum height "
									 "of the max/min controls." );
	}
	QHBoxLayout* layout1 = new QHBoxLayout(this);
	layout1->setContentsMargins(0, 0, 0, 0);
	coordinatesText = new QLabel("Coordinates: ", this);
	coordinatesDisp = new QLabel("", this);
	valueText = new QLabel("; Value: ", this);
	valueDisp = new QLabel("", this);
	layout1->addWidget(new QLabel(qstr(name),this));
	layout1->addWidget(coordinatesText);
	layout1->addWidget(coordinatesDisp);
	layout1->addWidget(valueText);
	layout1->addWidget(valueDisp);
	layout1->addStretch();

	maxWidth = width;
	maxHeight = height;
	QHBoxLayout* layout2 = new QHBoxLayout(this);
	layout2->setContentsMargins(0, 0, 0, 0);
	pic.setStyle(plotStyle::DensityPlotWithHisto);
	pic.init(parent,"");
	pic.plot->setMinimumSize(400, 350);
	pictureObject = new ImageLabel (parent);
	//connect (pictureObject, &ImageLabel::mouseReleased, [this](QMouseEvent* event) {handleMouse (event); });
	std::vector<unsigned char> data (20000);
	for (auto& pt : data){
		pt = rand () % 255;
	}
	slider.setRange(0, 4096);
	slider.setMaximumWidth(80);
	slider.setMaxLength(800);
	parent->connect (&slider, &RangeSliderIntg::smlValueChanged, [this]() {redrawImage (); });
	parent->connect (&slider, &RangeSliderIntg::lrgValueChanged, [this]() {redrawImage (); });
	layout2->addWidget(pic.plot, 1);
	layout2->addWidget(&slider, 0);

	connect(pic.plot, &QCustomPlot::mouseMove, [this](QMouseEvent* event) {
		handleMouse(event); });

	layout->addLayout(layout1);
	layout->addLayout(layout2);
	layout->addStretch();
}

bool PictureControl::isActive(){
	return active;
}

void PictureControl::setSliderPositions(unsigned min, unsigned max){
	slider.upperSpinBox()->setValue(max);
	slider.lowerSpinBox()->setValue(min);
	//sliderMin.setValue ( min );
	//sliderMax.setValue ( max );
}

/*
 * Used during initialization & when used when transitioning between 1 and >1 pictures per repetition.
 * Sets the unscaled background area and the scaled area.
 */
void PictureControl::setPictureArea( QPoint loc, int width, int height ){
	//// this is important for the control to know where it should draw controls.
	//auto& sBA = scaledBackgroundArea;
	//auto& px = loc.rx (), & py = loc.ry ();
	//unscaledBackgroundArea = { px, py, px + width, py + height };
	//// reserve some area for the texts.
	//unscaledBackgroundArea.setRight (unscaledBackgroundArea.right () - 100);
	//sBA = unscaledBackgroundArea;
	///*
	//sBA.left *= width;
	//sBA.right *= width;
	//sBA.top *= height;
	//sBA.bottom *= height;*/
	//if ( horGraph ){
	//	//horGraph->setControlLocation ( { scaledBackgroundArea.left, scaledBackgroundArea.bottom },
	//	//							   scaledBackgroundArea.right - scaledBackgroundArea.left, 65 );
	//}
	//if ( vertGraph ){
	//	//vertGraph->setControlLocation ( { scaledBackgroundArea.left - 65, scaledBackgroundArea.bottom },
	//	//							      65, scaledBackgroundArea.bottom - scaledBackgroundArea.top );
	//}
	//double widthPicScale;
	//double heightPicScale;
	//auto& uIP = unofficialImageParameters;
	//double w_to_h_ratio = double (uIP.width ()) / uIP.height ();
	//double sba_w = sBA.right () - sBA.left ();
	//double sba_h = sBA.bottom () - sBA.top ();
	//if (w_to_h_ratio > sba_w/sba_h){
	//	widthPicScale = 1;
	//	heightPicScale = (1.0/ w_to_h_ratio) * (sba_w / sba_h);
	//}
	//else{
	//	heightPicScale = 1;
	//	widthPicScale = w_to_h_ratio / (sba_w / sba_h);
	//}

	//unsigned long picWidth = unsigned long( (sBA.right () - sBA.left())*widthPicScale );
	//unsigned long picHeight = (sBA.bottom() - sBA.top())*heightPicScale;
	//QPoint mid = { (sBA.left () + sBA.right ()) / 2, (sBA.top () + sBA.bottom ()) / 2 };
	//pictureArea.setLeft(mid.x() - picWidth / 2);
	//pictureArea.setRight(mid.x() + picWidth / 2);
	//pictureArea.setTop(mid.y() - picHeight / 2);
	//pictureArea.setBottom(mid.y() + picHeight / 2);
	//
	//if (pictureObject){
	//	pictureObject->setGeometry (px, py, width, height);
	//	pictureObject->raise ();
	//}
}

/* used when transitioning between single and multiple pictures. It sets it based on the background size, so make
 * sure to change the background size before using this.
 * ********/
void PictureControl::setSliderControlLocs (QPoint pos, int height){
	//sliderMin.reposition ( pos, height);
	//pos.rx() += 25;
	//sliderMax.reposition ( pos, height );
}

/* used when transitioning between single and multiple pictures. It sets it based on the background size, so make
* sure to change the background size before using this.
* ********/

/*
 * change the colormap used for a given picture.
 */
void PictureControl::updatePalette( QVector<QRgb> palette ){
	imagePalette = palette;
}

/*
 * called when the user changes either the min or max edit.
 */
void PictureControl::handleEditChange( int id ){
	//if ( id == sliderMax.getEditId() ){
	//	sliderMax.handleEdit ( );
	//}
	//if ( id == sliderMin.getEditId() ){
	//	sliderMin.handleEdit ( );
	//}
}

std::pair<unsigned, unsigned> PictureControl::getSliderLocations(){
	return { slider.getSilderSmlVal(), slider.getSilderLrgVal() };
}

/*
 * Recalculate the grid of pixels, which needs to be done e.g. when changing number of pictures or re-sizing the
 * picture. Does not draw the grid.
 */
void PictureControl::recalculateGrid(imageParameters newParameters){
	// not strictly necessary.
	grid.clear();
	// find the maximum dimension.
	unofficialImageParameters = newParameters;
	double widthPicScale;
	double heightPicScale;
	/*if (unofficialImageParameters.width ()> unofficialImageParameters.height())
	{
		widthPicScale = 1;
		heightPicScale = double(unofficialImageParameters.height()) / unofficialImageParameters.width();
	}
	else
	{
		heightPicScale = 1;
		widthPicScale = double(unofficialImageParameters.width()) / unofficialImageParameters.height();
	}*/
	auto& uIP = unofficialImageParameters;
	double w_to_h_ratio = double (uIP.width ()) / uIP.height ();
	auto& sBA = scaledBackgroundArea;
	double sba_w = sBA.right () - sBA.left ();
	double sba_h = sBA.bottom () - sBA.top ();
	if (w_to_h_ratio > sba_w / sba_h){
		widthPicScale = 1;
		heightPicScale = (1.0 / w_to_h_ratio) * (sba_w / sba_h);
	}
	else{
		heightPicScale = 1;
		widthPicScale = w_to_h_ratio / (sba_w / sba_h);
	}

	long width = long((scaledBackgroundArea.right () - scaledBackgroundArea.left ())*widthPicScale);
	long height = long((scaledBackgroundArea.bottom () - scaledBackgroundArea.top ())*heightPicScale);
	QPoint mid = { (scaledBackgroundArea.left () + scaledBackgroundArea.right ()) / 2,
				  (scaledBackgroundArea.top () + scaledBackgroundArea.bottom ()) / 2 };
	pictureArea.setLeft (mid.x () - width / 2);
	pictureArea.setRight (mid.x () + width / 2);
	pictureArea.setTop (mid.y () - height / 2);
	pictureArea.setBottom (mid.y () + height / 2);

	grid.resize(newParameters.width());
	for (unsigned colInc = 0; colInc < grid.size(); colInc++){
		grid[colInc].resize(newParameters.height());
		for (unsigned rowInc = 0; rowInc < grid[colInc].size(); rowInc++){
			// for all 4 pictures...
			grid[colInc][rowInc].setLeft(int(pictureArea.left()
											 + (double)(colInc+1) * (pictureArea.right () - pictureArea.left ())
											 / (double)grid.size( ) + 2));
			grid[colInc][rowInc].setRight(int(pictureArea.left()
				+ (double)(colInc + 2) * (pictureArea.right () - pictureArea.left ()) / (double)grid.size() + 2));
			grid[colInc][rowInc].setTop(int(pictureArea.top ()
				+ (double)(rowInc)* (pictureArea.bottom () - pictureArea.top ()) / (double)grid[colInc].size()));
			grid[colInc][rowInc].setBottom(int(pictureArea.top()
				+ (double)(rowInc + 1)* (pictureArea.bottom () - pictureArea.top ()) / (double)grid[colInc].size()));
		}
	}
}

/*
 * sets the state of the picture and changes visibility of controls depending on that state.
 */
void PictureControl::setActive( bool activeState )
{
	if (!coordinatesText || !coordinatesDisp)	{
		return;
	}
	active = activeState;
	if (!active){
		this->hide();
		slider.hide();
		coordinatesText->hide();
		coordinatesDisp->hide();
		valueText->hide();
		valueDisp->hide();
	}
	else{
		this->show();
		slider.show();
		coordinatesText->show();
		coordinatesDisp->show();
		valueText->show();
		valueDisp->show();
	}
}

/*
 * redraws the background and image.
 */
void PictureControl::redrawImage(){
	if ( active && mostRecentImage_m.size ( ) != 0 ){
		drawBitmap (mostRecentImage_m, mostRecentAutoscaleInfo, mostRecentSpecialMinSetting,
			mostRecentSpecialMaxSetting, mostRecentGrids, mostRecentPicNum, true);
	}
}

void PictureControl::resetStorage(){
	mostRecentImage_m = Matrix<long>(0,0);
}

void PictureControl::setSoftwareAccumulationOption ( softwareAccumulationOption opt ){
	saOption = opt;
	accumPicData.clear ( );
	accumNum = 0;
}

/*
  Version of this from the Basler camera control Code. I will consolidate these shortly.
*/
void PictureControl::drawBitmap ( const Matrix<long>& picData, std::tuple<bool, int, int> autoScaleInfo,
								  bool specialMin, bool specialMax, std::vector<atomGrid> grids, unsigned pictureNumber,
								  bool includingAnalysisMarkers ){
	mostRecentImage_m = picData;
	mostRecentPicNum = pictureNumber;
	mostRecentGrids = grids;

	auto minColor = slider.getSilderSmlVal( );
	auto maxColor = slider.getSilderLrgVal( );
	mostRecentAutoscaleInfo = autoScaleInfo;
	int pixelsAreaWidth = pictureArea.right () - pictureArea.left () + 1;
	int pixelsAreaHeight = pictureArea.bottom () - pictureArea.top () + 1;
	int dataWidth = grid.size ( );
	// first element containst whether autoscaling or not.
	//long colorRange;
	//if ( std::get<0> ( autoScaleInfo ) ){
	//	// third element contains max, second contains min.
	//	colorRange = std::get<2> ( autoScaleInfo ) - std::get<1> ( autoScaleInfo );
	//	minColor = std::get<1> ( autoScaleInfo );
	//}
	//else{
	//	colorRange = maxColor - minColor;
	//	minColor = minColor;
	//	//colorRange = sliderMax.getValue ( ) - sliderMin.getValue ( );
	//	//minColor = sliderMin.getValue ( );
	//}
	// assumes non-zero size...
	if ( grid.size ( ) == 0 ){
		thrower  ( "Tried to draw bitmap without setting grid size!" );
	}
	int dataHeight = grid[ 0 ].size ( );
	int totalGridSize = dataWidth * dataHeight;
	if ( picData.size ( ) != totalGridSize ){
		thrower  ( "Picture data didn't match grid size!" );
	}

	int width = picData.getCols();
	int height = picData.getRows();
	std::vector<plotDataVec> ddvec(height);
	for (size_t idx = 0; idx < height; idx++)
	{
		ddvec[idx].reserve(width);
		for (size_t idd = 0; idd < width; idd++)
		{
			ddvec[idx].push_back(dataPoint{ 0,double(picData(idx,idd)),0 }); // x,y,err
		}
	}
	pic.setData(ddvec);

	//float yscale = ( 256.0f ) / (float) colorRange;
	//std::vector<uchar> dataArray2 ( dataWidth * dataHeight, 255 );
	//int iTemp;
	//double dTemp = 1;
	//const int picPaletteSize = 256;
	//for (int heightInc = 0; heightInc < dataHeight; heightInc++){
	//	for (int widthInc = 0; widthInc < dataWidth; widthInc++){
	//		dTemp = ceil (yscale * double(picData (heightInc, widthInc) - minColor));
	//		if (dTemp <= 0)	{
	//			// raise value to zero which is the floor of values this parameter can take.
	//			iTemp = 1;
	//		}
	//		else if (dTemp >= picPaletteSize - 1)	{
	//			// round to maximum value.
	//			iTemp = picPaletteSize - 2;
	//		}
	//		else{
	//			// no rounding or flooring to min or max needed.
	//			iTemp = (int)dTemp;
	//		}
	//		// store the value.
	//		dataArray2[widthInc + heightInc * dataWidth] = (unsigned char)iTemp;
	//	}
	//}
	//int sf = picScaleFactor;
	//QImage img (sf * dataWidth, sf * dataHeight, QImage::Format_Indexed8);
	//img.setColorTable (imagePalette);
	//img.fill (0);
	//for (auto rowInc : range(dataHeight)){
	//	std::vector<uchar> singleRow (sf * dataWidth);
	//	for (auto val : range (dataWidth)){
	//		for (auto rep : range (sf)) {
	//			singleRow[sf * val + rep] = dataArray2[rowInc * dataWidth + val];
	//		}
	//	}
	//	for (auto repRow : range (sf)){
	//		memcpy (img.scanLine (rowInc * sf + repRow), singleRow.data(), img.bytesPerLine ());
	//	}
	//}
	// need to convert to an rgb format in order to draw on top. drawing on top using qpainter isn't supported with the
	// indexed format.
	//img = img.convertToFormat (QImage::Format_RGB888);
	//QPainter painter;
	//painter.begin (&img);
	//drawDongles (painter, grids, pictureNumber, includingAnalysisMarkers);
	//painter.end ();
	//// seems like this doesn't *quite* work for some reason, hence the extra number here to adjust
	//if (img.width () / img.height () > (pictureObject->width () / pictureObject->height ())-0.1)	{
	//	pictureObject->setPixmap (QPixmap::fromImage (img).scaledToWidth (pictureObject->width (), transformationMode));
	//}
	//else {
	//	pictureObject->setPixmap (QPixmap::fromImage (img).scaledToHeight (pictureObject->height (), transformationMode));
	//}
	// //update this with the new picture.
	//setHoverValue ( );
}

void PictureControl::setHoverValue( ){
	int loc = (grid.size( ) - 1 - selectedLocation.column) * grid.size( ) + selectedLocation.row;
	if ( loc >= mostRecentImage_m.size( ) )	{
		return;
	}
	valueDisp->setText( cstr( mostRecentImage_m.data[loc] ) );
}

void PictureControl::handleMouse (QMouseEvent* event){
	auto vec = pic.handleMousePosOnCMap(event);
	coordinatesDisp->setText("( " + qstr(int(vec[0])) + " , " + qstr(int(vec[1])) + " )");
	valueDisp->setText(qstr(int(vec[2])));
}

/*
 * draw the grid which outlines where each pixel is.  Especially needs to be done when selecting pixels and no picture
 * is displayed.
 */
void PictureControl::drawGrid(QPainter& painter){
	if (!active){
		return;
	}
	if (grid.size() != 0){
		// hard set to 5000. Could easily change this to be able to see finer grids. Tested before and 5000 seems
		// reasonable.
		if (grid.size() * grid.front().size() > 5000){
			return;
		}
	}
	// draw rectangles indicating where the pixels are.
	for (unsigned columnInc = 0; columnInc < grid.size(); columnInc++){
		for (unsigned rowInc = 0; rowInc < grid[columnInc].size(); rowInc++){
			unsigned pixelRow = picScaleFactor * grid[columnInc][rowInc].top();
			unsigned pixelColumn = picScaleFactor * grid[columnInc][rowInc].left();
			QRect rect = QRect (QPoint (pixelColumn, pixelRow),
						 QPoint (pixelColumn + picScaleFactor - 2, pixelRow + picScaleFactor - 2));
			painter.drawRect (rect);
		}
	}
}

/*
 * draws the circle which denotes the selected pixel that the user wants to know the counts for.
 */
void PictureControl::drawCircle(coordinate selectedLocation, QPainter& painter){
	if (grid.size() == 0){
		// this hasn't been set yet, presumably this got called by the camera window as the camera window
		// was drawing itself before the control was initialized.
		return;
	}
	if (!active){
		// don't draw anything if the window isn't active.
		return;
	}
	QRect smallRect( selectedLocation.column * picScaleFactor, selectedLocation.row * picScaleFactor,
					 picScaleFactor-1, picScaleFactor-1 );
	painter.drawEllipse (smallRect);
}

void PictureControl::drawPicNum( unsigned picNum, QPainter& painter ){
	QFont font = painter.font ();
	// I think this is the font height in pixels on the pixmap basically.
	font.setPointSize (20);
	painter.setFont (font);
	painter.setPen (Qt::white);
	painter.drawText (QPoint ( int(picScaleFactor)/5, picScaleFactor), cstr (picNum));
}

void PictureControl::drawAnalysisMarkers( std::vector<atomGrid> gridInfo, QPainter& painter ){
	if ( !active ){
		return;
	}
	painter.setPen (Qt::white);
	//std::vector<COLORREF> colors = { RGB( 100, 100, 100 ), RGB( 0, 100, 0 ), RGB( 0, 0, 100), RGB( 100, 0, 0 ) };
	unsigned gridCount = 0;
	for ( auto atomGrid : gridInfo ){
		if ( atomGrid.topLeftCorner == coordinate( 0, 0 ) ){
			// atom grid is empty, not to be used.
			unsigned count = 1;
		}
		else {
			// use the atom grid.
			unsigned count = 1;
			for ( auto columnInc : range( atomGrid.width ) ){
				for ( auto rowInc : range( atomGrid.height ) ){
					unsigned pixelRow = picScaleFactor*(atomGrid.topLeftCorner.row + rowInc * atomGrid.pixelSpacing);
					unsigned pixelColumn = picScaleFactor * (atomGrid.topLeftCorner.column
														 + columnInc * atomGrid.pixelSpacing);
					QRect rect = QRect (QPoint(pixelColumn, pixelRow),
										QPoint (pixelColumn+picScaleFactor-2, pixelRow + picScaleFactor - 2));
					painter.drawRect (rect);
					painter.drawText (rect, Qt::AlignCenter, cstr(count++));
				}
			}
		}
		gridCount++;
	}
}

void PictureControl::drawDongles (QPainter& painter, std::vector<atomGrid> grids, unsigned pictureNumber,
	bool includingAnalysisMarkers){
	drawPicNum (pictureNumber, painter);
	if (includingAnalysisMarkers) {
		drawAnalysisMarkers (  grids, painter );
	}
	painter.setPen (Qt::red);
	drawCircle (selectedLocation, painter);
}

void PictureControl::setTransformationMode (Qt::TransformationMode mode) {
	transformationMode = mode;
}

void PictureControl::setSliderSize(int size)
{
	slider.setMaxLength(size);
}


// Copyright Paul Bristow 2006, 2007.
// Copyright John Maddock 2006, 2007.

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

// test_triangular.cpp

#include <pch.hpp>

#ifdef _MSC_VER
#  pragma warning(disable: 4127) // conditional expression is constant.
#  pragma warning(disable: 4305) // truncation from 'long double' to 'float'
#endif

#include <boost/math/concepts/real_concept.hpp> // for real_concept
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp> // Boost.Test
#include <boost/test/floating_point_comparison.hpp>

#include <boost/math/distributions/triangular.hpp>
using boost::math::triangular_distribution;
#include <boost/math/tools/test.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#include "test_out_of_range.hpp"

#include <iostream>
#include <iomanip>
using std::cout;
using std::endl;
using std::scientific;
using std::fixed;
using std::left;
using std::right;
using std::setw;
using std::setprecision;
using std::showpos;
#include <limits>
using std::numeric_limits;

template <class RealType>
void check_triangular(RealType lower, RealType mode, RealType upper, RealType x, RealType p, RealType q, RealType tol)
{
  BOOST_CHECK_CLOSE_FRACTION(
    ::boost::math::cdf(
    triangular_distribution<RealType>(lower, mode, upper),   // distribution.
    x),  // random variable.
    p,    // probability.
    tol);   // tolerance.
  BOOST_CHECK_CLOSE_FRACTION(
    ::boost::math::cdf(
    complement(
    triangular_distribution<RealType>(lower, mode, upper), // distribution.
    x)),    // random variable.
    q,    // probability complement.
    tol);  // tolerance.
  BOOST_CHECK_CLOSE_FRACTION(
    ::boost::math::quantile(
    triangular_distribution<RealType>(lower,mode, upper),  // distribution.
    p),   // probability.
    x,  // random variable.
    tol);  // tolerance.
  BOOST_CHECK_CLOSE_FRACTION(
    ::boost::math::quantile(
    complement(
    triangular_distribution<RealType>(lower, mode, upper),  // distribution.
    q)),     // probability complement.
    x,                                             // random variable.
    tol);  // tolerance.
} // void check_triangular

template <class RealType>
void test_spots(RealType)
{
  // Basic sanity checks:
  //
  // Some test values were generated for the triangular distribution
  // using the online calculator at
  // http://espse.ed.psu.edu/edpsych/faculty/rhale/hale/507Mat/statlets/free/pdist.htm
  //
  // Tolerance is just over 5 epsilon expressed as a fraction:
  RealType tolerance = boost::math::tools::epsilon<RealType>() * 5; // 5 eps as a fraction.
  RealType tol5eps = boost::math::tools::epsilon<RealType>() * 5; // 5 eps as a fraction.

  cout << "Tolerance for type " << typeid(RealType).name()  << " is " << tolerance << "." << endl;

  using namespace std; // for ADL of std::exp;

  // Tests on construction
  // Default should be 0, 0, 1
  BOOST_CHECK_EQUAL(triangular_distribution<RealType>().lower(), -1);
  BOOST_CHECK_EQUAL(triangular_distribution<RealType>().mode(), 0);
  BOOST_CHECK_EQUAL(triangular_distribution<RealType>().upper(), 1);
  BOOST_CHECK_EQUAL(support(triangular_distribution<RealType>()).first, triangular_distribution<RealType>().lower());
  BOOST_CHECK_EQUAL(support(triangular_distribution<RealType>()).second, triangular_distribution<RealType>().upper());

  if (std::numeric_limits<RealType>::has_quiet_NaN == true)
  {
  BOOST_CHECK_THROW( // duff parameter lower.
    triangular_distribution<RealType>(static_cast<RealType>(std::numeric_limits<RealType>::quiet_NaN()), 0, 0),
    std::domain_error);

  BOOST_CHECK_THROW( // duff parameter mode.
    triangular_distribution<RealType>(0, static_cast<RealType>(std::numeric_limits<RealType>::quiet_NaN()), 0),
    std::domain_error);

  BOOST_CHECK_THROW( // duff parameter upper.
    triangular_distribution<RealType>(0, 0, static_cast<RealType>(std::numeric_limits<RealType>::quiet_NaN())),
    std::domain_error);
  } // quiet_NaN tests.

  BOOST_CHECK_THROW( // duff parameters upper < lower.
    triangular_distribution<RealType>(1, 0, -1),
    std::domain_error);

  BOOST_CHECK_THROW( // duff parameters upper == lower.
    triangular_distribution<RealType>(0, 0, 0),
    std::domain_error);
  BOOST_CHECK_THROW( // duff parameters mode < lower.
    triangular_distribution<RealType>(0, -1, 1),
    std::domain_error);

  BOOST_CHECK_THROW( // duff parameters mode > upper.
    triangular_distribution<RealType>(0, 2, 1),
    std::domain_error);

  // Tests for PDF
  // // triangular_distribution<RealType>() default is (0, 0, 1), mode == lower.
  BOOST_CHECK_CLOSE_FRACTION( // x == lower == mode
    pdf(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(0)),
    static_cast<RealType>(2),
    tolerance);

  BOOST_CHECK_CLOSE_FRACTION( // x == upper
    pdf(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(1)),
    static_cast<RealType>(0),
    tolerance);

  BOOST_CHECK_CLOSE_FRACTION( // x > upper
    pdf(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(-1)),
    static_cast<RealType>(0),
    tolerance);
  BOOST_CHECK_CLOSE_FRACTION( // x < lower
    pdf(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(2)),
    static_cast<RealType>(0),
    tolerance);

  BOOST_CHECK_CLOSE_FRACTION( // x < lower
    pdf(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(2)),
    static_cast<RealType>(0),
    tolerance);

  // triangular_distribution<RealType>() (0, 1, 1) mode == upper
  BOOST_CHECK_CLOSE_FRACTION( // x == lower
    pdf(triangular_distribution<RealType>(0, 1, 1), static_cast<RealType>(0)),
    static_cast<RealType>(0),
    tolerance);

  BOOST_CHECK_CLOSE_FRACTION( // x == upper
    pdf(triangular_distribution<RealType>(0, 1, 1), static_cast<RealType>(1)),
    static_cast<RealType>(2),
    tolerance);

  BOOST_CHECK_CLOSE_FRACTION( // x > upper
    pdf(triangular_distribution<RealType>(0, 1, 1), static_cast<RealType>(-1)),
    static_cast<RealType>(0),
    tolerance);
  BOOST_CHECK_CLOSE_FRACTION( // x < lower
    pdf(triangular_distribution<RealType>(0, 1, 1), static_cast<RealType>(2)),
    static_cast<RealType>(0),
    tolerance);

  BOOST_CHECK_CLOSE_FRACTION( // x < middle so Wiki says special case pdf = 2 * x
    pdf(triangular_distribution<RealType>(0, 1, 1), static_cast<RealType>(0.25)),
    static_cast<RealType>(0.5),
    tolerance);

  BOOST_CHECK_CLOSE_FRACTION( // x < middle so Wiki says special case cdf = x * x
    cdf(triangular_distribution<RealType>(0, 1, 1), static_cast<RealType>(0.25)),
    static_cast<RealType>(0.25 * 0.25),
    tolerance);

  // triangular_distribution<RealType>() (0, 0.5, 1) mode == middle.
  BOOST_CHECK_CLOSE_FRACTION( // x == lower
    pdf(triangular_distribution<RealType>(0, 0.5, 1), static_cast<RealType>(0)),
    static_cast<RealType>(0),
    tolerance);

  BOOST_CHECK_CLOSE_FRACTION( // x == upper
    pdf(triangular_distribution<RealType>(0, 0.5, 1), static_cast<RealType>(1)),
    static_cast<RealType>(0),
    tolerance);

  BOOST_CHECK_CLOSE_FRACTION( // x > upper
    pdf(triangular_distribution<RealType>(0, 0.5, 1), static_cast<RealType>(-1)),
    static_cast<RealType>(0),
    tolerance);
  BOOST_CHECK_CLOSE_FRACTION( // x < lower
    pdf(triangular_distribution<RealType>(0, 0.5, 1), static_cast<RealType>(2)),
    static_cast<RealType>(0),
    tolerance);

  BOOST_CHECK_CLOSE_FRACTION( // x == mode
    pdf(triangular_distribution<RealType>(0, 0.5, 1), static_cast<RealType>(0.5)),
    static_cast<RealType>(2),
    tolerance);

  BOOST_CHECK_CLOSE_FRACTION( // x == half mode
    pdf(triangular_distribution<RealType>(0, 0.5, 1), static_cast<RealType>(0.25)),
    static_cast<RealType>(1),
    tolerance);
  BOOST_CHECK_CLOSE_FRACTION( // x == half mode
    pdf(triangular_distribution<RealType>(0, 0.5, 1), static_cast<RealType>(0.75)),
    static_cast<RealType>(1),
    tolerance);

  if(std::numeric_limits<RealType>::has_infinity)
  { // BOOST_CHECK tests for infinity using std::numeric_limits<>::infinity()
    // Note that infinity is not implemented for real_concept, so these tests
    // are only done for types, like built-in float, double.. that have infinity.
    // Note that these assume that  BOOST_MATH_OVERFLOW_ERROR_POLICY is NOT throw_on_error.
    // #define BOOST_MATH_OVERFLOW_ERROR_POLICY == throw_on_error would give a throw here.
    // #define BOOST_MATH_DOMAIN_ERROR_POLICY == throw_on_error IS defined, so the throw path
    // of error handling is tested below with BOOST_CHECK_THROW tests.

    BOOST_CHECK_THROW( // x == infinity NOT OK.
      pdf(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(std::numeric_limits<RealType>::infinity())),
      std::domain_error);

    BOOST_CHECK_THROW( // x == minus infinity not OK too.
      pdf(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(-std::numeric_limits<RealType>::infinity())),
      std::domain_error);
  }
  if(std::numeric_limits<RealType>::has_quiet_NaN)
  { // BOOST_CHECK tests for NaN using std::numeric_limits<>::has_quiet_NaN() - should throw.
    BOOST_CHECK_THROW(
      pdf(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(std::numeric_limits<RealType>::quiet_NaN())),
      std::domain_error);
    BOOST_CHECK_THROW(
      pdf(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(-std::numeric_limits<RealType>::quiet_NaN())),
      std::domain_error);
  } // test for x = NaN using std::numeric_limits<>::quiet_NaN()

  // cdf
  BOOST_CHECK_EQUAL( // x < lower
    cdf(triangular_distribution<RealType>(0, 1, 1), static_cast<RealType>(-1)),
    static_cast<RealType>(0) );
  BOOST_CHECK_CLOSE_FRACTION( // x == lower
    cdf(triangular_distribution<RealType>(0, 1, 1), static_cast<RealType>(0)),
    static_cast<RealType>(0),
    tolerance);
  BOOST_CHECK_CLOSE_FRACTION( // x == upper
    cdf(triangular_distribution<RealType>(0, 1, 1), static_cast<RealType>(1)),
    static_cast<RealType>(1),
    tolerance);
   BOOST_CHECK_EQUAL( // x > upper
    cdf(triangular_distribution<RealType>(0, 1, 1), static_cast<RealType>(2)),
    static_cast<RealType>(1));

  BOOST_CHECK_CLOSE_FRACTION( // x == mode
    cdf(triangular_distribution<RealType>(-1, 0, 1), static_cast<RealType>(0)),
    //static_cast<RealType>((mode - lower) / (upper - lower)),
    static_cast<RealType>(0.5),    // (0 --1) / (1 -- 1) = 0.5
    tolerance);
  BOOST_CHECK_CLOSE_FRACTION(
    cdf(triangular_distribution<RealType>(0, 1, 1), static_cast<RealType>(0.9L)),
    static_cast<RealType>(0.81L),
    tolerance);

  BOOST_CHECK_CLOSE_FRACTION(
    cdf(triangular_distribution<RealType>(-1, 0, 1), static_cast<RealType>(-1)),
    static_cast<RealType>(0),
    tolerance);
  BOOST_CHECK_CLOSE_FRACTION(
    cdf(triangular_distribution<RealType>(-1, 0, 1), static_cast<RealType>(-0.5L)),
    static_cast<RealType>(0.125L),
    tolerance);
  BOOST_CHECK_CLOSE_FRACTION(
    cdf(triangular_distribution<RealType>(-1, 0, 1), static_cast<RealType>(0)),
    static_cast<RealType>(0.5),
    tolerance);
  BOOST_CHECK_CLOSE_FRACTION(
    cdf(triangular_distribution<RealType>(-1, 0, 1), static_cast<RealType>(+0.5L)),
    static_cast<RealType>(0.875L),
    tolerance);
  BOOST_CHECK_CLOSE_FRACTION(
    cdf(triangular_distribution<RealType>(-1, 0, 1), static_cast<RealType>(1)),
    static_cast<RealType>(1),
    tolerance);

   // cdf complement
  BOOST_CHECK_EQUAL( // x < lower
    cdf(complement(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(-1))),
    static_cast<RealType>(1));
  BOOST_CHECK_EQUAL( // x == lower
    cdf(complement(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(0))),
    static_cast<RealType>(1));

  BOOST_CHECK_EQUAL( // x == mode
    cdf(complement(triangular_distribution<RealType>(-1, 0, 1), static_cast<RealType>(0))),
    static_cast<RealType>(0.5));

  BOOST_CHECK_EQUAL( // x == mode
    cdf(complement(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(0))),
    static_cast<RealType>(1));
  BOOST_CHECK_EQUAL( // x == mode
    cdf(complement(triangular_distribution<RealType>(0, 1, 1), static_cast<RealType>(1))),
    static_cast<RealType>(0));

  BOOST_CHECK_EQUAL( // x > upper
    cdf(complement(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(2))),
    static_cast<RealType>(0));
  BOOST_CHECK_EQUAL( // x == upper
    cdf(complement(triangular_distribution<RealType>(0, 0, 1), static_cast<RealType>(1))),
    static_cast<RealType>(0));

  BOOST_CHECK_CLOSE_FRACTION( // x = -0.5
    cdf(complement(triangular_distribution<RealType>(-1, 0, 1), static_cast<RealType>(-0.5))),
    static_cast<RealType>(0.875L),
    tolerance);

  BOOST_CHECK_CLOSE_FRACTION( // x = +0.5
    cdf(complement(triangular_distribution<RealType>(-1, 0, 1), static_cast<RealType>(0.5))),
    static_cast<RealType>(0.125),
    tolerance);

  triangular_distribution<RealType> triang; // Using typedef == triangular_distribution<double> tristd;
  triangular_distribution<RealType> tristd(0, 0.5, 1); // 'Standard' triangular distribution.

  BOOST_CHECK_CLOSE_FRACTION( // median of Standard triangular is sqrt(mode/2) if c > 1/2 else 1 - sqrt((1-c)/2)
    median(tristd),
    static_cast<RealType>(0.5),
    tolerance);
  triangular_distribution<RealType> tri011(0, 1, 1); // Using default RealType double.
  triangular_distribution<RealType> tri0q1(0, 0.25, 1); // mode is near bottom.
  triangular_distribution<RealType> tri0h1(0, 0.5, 1); // Equilateral triangle - mode is the middle.
  triangular_distribution<RealType> trim12(-1, -0.5, 2); // mode is negative.

  BOOST_CHECK_CLOSE_FRACTION(cdf(tri0q1, 0.02L), static_cast<RealType>(0.0016L), tolerance);
  BOOST_CHECK_CLOSE_FRACTION(cdf(tri0q1, 0.5L), static_cast<RealType>(0.66666666666666666666666666666666666666666666667L), tolerance);
  BOOST_CHECK_CLOSE_FRACTION(cdf(tri0q1, 0.98L), static_cast<RealType>(0.9994666666666666666666666666666666666666666666L), tolerance);

  // quantile
  BOOST_CHECK_CLOSE_FRACTION(quantile(tri0q1, static_cast<RealType>(0.0016L)), static_cast<RealType>(0.02L), tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(tri0q1, static_cast<RealType>(0.66666666666666666666666666666666666666666666667L)), static_cast<RealType>(0.5), tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0q1, static_cast<RealType>(0.3333333333333333333333333333333333333333333333333L))), static_cast<RealType>(0.5), tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(tri0q1, static_cast<RealType>(0.999466666666666666666666666666666666666666666666666L)), static_cast<RealType>(98) / 100, 10 * tol5eps);

  BOOST_CHECK_CLOSE_FRACTION(pdf(trim12, 0), static_cast<RealType>(0.533333333333333333333333333333333333333333333L), tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(cdf(trim12, 0), static_cast<RealType>(0.466666666666666666666666666666666666666666667L), tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(cdf(complement(trim12, 0)), static_cast<RealType>(1 - 0.466666666666666666666666666666666666666666667L), tol5eps);

  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0q1, static_cast<RealType>(1 - 0.999466666666666666666666666666666666666666666666L))), static_cast<RealType>(0.98L), 10 * tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0h1, static_cast<RealType>(1))), static_cast<RealType>(0), tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0h1, static_cast<RealType>(0.5))), static_cast<RealType>(0.5), tol5eps); // OK
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0h1, static_cast<RealType>(1 - 0.02L))), static_cast<RealType>(0.1L), tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0h1, static_cast<RealType>(1 - 0.98L))), static_cast<RealType>(0.9L), tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0h1, 0)), static_cast<RealType>(1), tol5eps);

  RealType xs [] = {0, 0.01L, 0.02L, 0.05L, 0.1L, 0.2L, 0.3L, 0.4L, 0.5L, 0.6L, 0.7L, 0.8L, 0.9L, 0.95L, 0.98L, 0.99L, 1};

  const triangular_distribution<RealType>& distr = triang;
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(distr, 1.)), static_cast<RealType>(-1), tol5eps);
  const triangular_distribution<RealType>* distp = &triang;
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(*distp, 1.)), static_cast<RealType>(-1), tol5eps);

  const triangular_distribution<RealType>* dists [] = {&tristd, &tri011, &tri0q1, &tri0h1, &trim12};
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(*dists[1], 1.)), static_cast<RealType>(0), tol5eps);

   for (int i = 0; i < 5; i++)
  {
    const triangular_distribution<RealType>* const dist = dists[i];
    // cout << "Distribution " << i << endl;
    BOOST_CHECK_CLOSE_FRACTION(quantile(*dists[i], 0.5L), quantile(complement(*dist, 0.5L)),  tol5eps);
    BOOST_CHECK_CLOSE_FRACTION(quantile(*dists[i], 0.98L), quantile(complement(*dist, 1.L - 0.98L)),tol5eps);
    BOOST_CHECK_CLOSE_FRACTION(quantile(*dists[i], 0.98L), quantile(complement(*dist, 1.L - 0.98L)),tol5eps);
  } // for i

   // quantile complement
  for (int i = 0; i < 5; i++)
  {
    const triangular_distribution<RealType>* const dist = dists[i];
    //cout << "Distribution " << i << endl;
    BOOST_CHECK_EQUAL(quantile(complement(*dists[i], 1.)), quantile(*dists[i], 0.));
    for (unsigned j = 0; j < sizeof(xs) /sizeof(RealType); j++)
    {
      RealType x = xs[j];
      BOOST_CHECK_CLOSE_FRACTION(quantile(*dists[i], x), quantile(complement(*dist, 1 - x)),  tol5eps);
    } // for j
  } // for i

  check_triangular(
    static_cast<RealType>(0),       // lower
    static_cast<RealType>(0.5),     // mode
    static_cast<RealType>(1),       // upper
    static_cast<RealType>(0.5),     // x
    static_cast<RealType>(0.5),     // p
    static_cast<RealType>(1 - 0.5), // q
    tolerance);

  // Some Not-standard triangular tests.
  check_triangular(
    static_cast<RealType>(-1),    // lower
    static_cast<RealType>(0),     // mode
    static_cast<RealType>(1),     // upper
    static_cast<RealType>(0),     // x
    static_cast<RealType>(0.5),   // p
    static_cast<RealType>(1 - 0.5), // q = 1 - p
    tolerance);

  check_triangular(
    static_cast<RealType>(1),       // lower
    static_cast<RealType>(1),       // mode
    static_cast<RealType>(3),       // upper
    static_cast<RealType>(2),    // x
    static_cast<RealType>(0.75),     // p
    static_cast<RealType>(1 - 0.75), // q = 1 - p
    tolerance);

  check_triangular(
    static_cast<RealType>(-1),    // lower
    static_cast<RealType>(1),       // mode
    static_cast<RealType>(2),     // upper
    static_cast<RealType>(1),     // x
    static_cast<RealType>(0.66666666666666666666666666666666666666666667L),   // p
    static_cast<RealType>(0.33333333333333333333333333333333333333333333L), // q = 1 - p
    tolerance);
  tolerance = (std::max)(
    boost::math::tools::epsilon<RealType>(),
    static_cast<RealType>(boost::math::tools::epsilon<double>())) * 10; // 10 eps as a fraction.
  cout << "Tolerance (as fraction) for type " << typeid(RealType).name()  << " is " << tolerance << "." << endl;
  triangular_distribution<RealType> tridef; // (-1, 0, 1) // default
  RealType x = static_cast<RealType>(0.5);
  using namespace std; // ADL of std names.
  // mean:
  BOOST_CHECK_CLOSE_FRACTION(
    mean(tridef), static_cast<RealType>(0), tolerance);
  // variance:
  BOOST_CHECK_CLOSE_FRACTION(
    variance(tridef), static_cast<RealType>(0.16666666666666666666666666666666666666666667L), tolerance);
  // was 0.0833333333333333333333333333333333333333333L

  // std deviation:
  BOOST_CHECK_CLOSE_FRACTION(
    standard_deviation(tridef), sqrt(variance(tridef)), tolerance);
  // hazard:
  BOOST_CHECK_CLOSE_FRACTION(
    hazard(tridef, x), pdf(tridef, x) / cdf(complement(tridef, x)), tolerance);
  // cumulative hazard:
  BOOST_CHECK_CLOSE_FRACTION(
    chf(tridef, x), -log(cdf(complement(tridef, x))), tolerance);
  // coefficient_of_variation:
  if (mean(tridef) != 0)
  {
  BOOST_CHECK_CLOSE_FRACTION(
    coefficient_of_variation(tridef), standard_deviation(tridef) / mean(tridef), tolerance);
  }
  // mode:
  BOOST_CHECK_CLOSE_FRACTION(
    mode(tridef), static_cast<RealType>(0), tolerance);
  // skewness:
  BOOST_CHECK_CLOSE_FRACTION(
    median(trim12), static_cast<RealType>(-0.13397459621556151), tolerance);
  BOOST_CHECK_EQUAL(
    skewness(tridef), static_cast<RealType>(0));
  // kurtosis:
  BOOST_CHECK_CLOSE_FRACTION(
    kurtosis_excess(tridef), kurtosis(tridef) - static_cast<RealType>(3L), tolerance);
  // kurtosis excess = kurtosis - 3;
  BOOST_CHECK_CLOSE_FRACTION(
    kurtosis_excess(tridef), static_cast<RealType>(-0.6), tolerance); // for all distributions.

  if(std::numeric_limits<RealType>::has_infinity)
  { // BOOST_CHECK tests for infinity using std::numeric_limits<>::infinity()
    // Note that infinity is not implemented for real_concept, so these tests
    // are only done for types, like built-in float, double.. that have infinity.
    // Note that these assume that BOOST_MATH_OVERFLOW_ERROR_POLICY is NOT throw_on_error.
    // #define BOOST_MATH_OVERFLOW_ERROR_POLICY == throw_on_error would give a throw here.
    // #define BOOST_MATH_DOMAIN_ERROR_POLICY == throw_on_error IS defined, so the throw path
    // of error handling is tested below with BOOST_CHECK_THROW tests.

    using boost::math::policies::policy;
    using boost::math::policies::domain_error;
    using boost::math::policies::ignore_error;

    // Define a (bad?) policy to ignore domain errors ('bad' arguments):
    typedef policy<domain_error<ignore_error> > inf_policy; // domain error returns infinity.
    triangular_distribution<RealType, inf_policy> tridef_inf(-1, 0., 1);
    // But can't use BOOST_CHECK_EQUAL(?, quiet_NaN)
    using boost::math::isnan;
    BOOST_CHECK((isnan)(pdf(tridef_inf, std::numeric_limits<RealType>::infinity())));
  } // test for infinity using std::numeric_limits<>::infinity()
  else
  { // real_concept case, does has_infinfity == false, so can't check it throws.
    // cout << std::numeric_limits<RealType>::infinity() << ' '
    // << (boost::math::fpclassify)(std::numeric_limits<RealType>::infinity()) << endl;
    // value of std::numeric_limits<RealType>::infinity() is zero, so FPclassify is zero,
    // so (boost::math::isfinite)(std::numeric_limits<RealType>::infinity()) does not detect infinity.
    // so these tests would never throw.
    //BOOST_CHECK_THROW(pdf(tridef, std::numeric_limits<RealType>::infinity()),  std::domain_error);
    //BOOST_CHECK_THROW(pdf(tridef, std::numeric_limits<RealType>::quiet_NaN()),  std::domain_error);
    // BOOST_CHECK_THROW(pdf(tridef, boost::math::tools::max_value<RealType>() * 2),  std::domain_error); // Doesn't throw.
    BOOST_CHECK_EQUAL(pdf(tridef, boost::math::tools::max_value<RealType>()), 0);
  }
  // Special cases:
  BOOST_CHECK(pdf(tridef, -1) == 0);
  BOOST_CHECK(pdf(tridef, 1) == 0);
  BOOST_CHECK(cdf(tridef, 0) == 0.5);
  BOOST_CHECK(pdf(tridef, 1) == 0);
  BOOST_CHECK(cdf(tridef, 1) == 1);
  BOOST_CHECK(cdf(complement(tridef, -1)) == 1);
  BOOST_CHECK(cdf(complement(tridef, 1)) == 0);
  BOOST_CHECK(quantile(tridef, 1) == 1);
  BOOST_CHECK(quantile(complement(tridef, 1)) == -1);

  BOOST_CHECK_EQUAL(support(trim12).first, trim12.lower());
  BOOST_CHECK_EQUAL(support(trim12).second, trim12.upper());

  // Error checks:
  if(std::numeric_limits<RealType>::has_quiet_NaN)
  { // BOOST_CHECK tests for quiet_NaN (not for real_concept, for example - see notes above).
    BOOST_CHECK_THROW(triangular_distribution<RealType>(0, std::numeric_limits<RealType>::quiet_NaN()), std::domain_error);
    BOOST_CHECK_THROW(triangular_distribution<RealType>(0, -std::numeric_limits<RealType>::quiet_NaN()), std::domain_error);
  }
  BOOST_CHECK_THROW(triangular_distribution<RealType>(1, 0), std::domain_error); // lower > upper!

  check_out_of_range<triangular_distribution<RealType> >(-1, 0, 1);
} // template <class RealType>void test_spots(RealType)

BOOST_AUTO_TEST_CASE( test_main )
{
  //  double toleps = std::numeric_limits<double>::epsilon(); // 5 eps as a fraction.
  double tol5eps = std::numeric_limits<double>::epsilon() * 5; // 5 eps as a fraction.
  // double tol50eps = std::numeric_limits<double>::epsilon() * 50; // 50 eps as a fraction.
  double tol500eps = std::numeric_limits<double>::epsilon() * 500; // 500 eps as a fraction.

  // Check that can construct triangular distribution using the two convenience methods:
  using namespace boost::math;
  triangular triang; // Using typedef
  // == triangular_distribution<double> triang;

  BOOST_CHECK_EQUAL(triang.lower(), -1); // Check default.
  BOOST_CHECK_EQUAL(triang.mode(), 0);
  BOOST_CHECK_EQUAL(triang.upper(), 1);

  triangular tristd (0, 0.5, 1); // Using typedef

  BOOST_CHECK_EQUAL(tristd.lower(), 0);
  BOOST_CHECK_EQUAL(tristd.mode(), 0.5);
  BOOST_CHECK_EQUAL(tristd.upper(), 1);

  //cout << "X range from " << range(tristd).first << " to " << range(tristd).second << endl;
  //cout << "Supported from "<< support(tristd).first << ' ' << support(tristd).second << endl;

  BOOST_CHECK_EQUAL(support(tristd).first, tristd.lower());
  BOOST_CHECK_EQUAL(support(tristd).second, tristd.upper());

  triangular_distribution<> tri011(0, 1, 1); // Using default RealType double.
  // mode is upper
  BOOST_CHECK_EQUAL(tri011.lower(), 0); // Check defaults again.
  BOOST_CHECK_EQUAL(tri011.mode(), 1); // Check defaults again.
  BOOST_CHECK_EQUAL(tri011.upper(), 1);
  BOOST_CHECK_EQUAL(mode(tri011), 1);

  BOOST_CHECK_EQUAL(pdf(tri011, 0), 0);
  BOOST_CHECK_EQUAL(pdf(tri011, 0.1), 0.2);
  BOOST_CHECK_EQUAL(pdf(tri011, 0.5), 1);
  BOOST_CHECK_EQUAL(pdf(tri011, 0.9), 1.8);
  BOOST_CHECK_EQUAL(pdf(tri011, 1), 2);

  BOOST_CHECK_EQUAL(cdf(tri011, 0), 0);
  BOOST_CHECK_CLOSE_FRACTION(cdf(tri011, 0.1), 0.01, tol5eps);
  BOOST_CHECK_EQUAL(cdf(tri011, 0.5), 0.25);
  BOOST_CHECK_EQUAL(cdf(tri011, 0.9), 0.81);
  BOOST_CHECK_EQUAL(cdf(tri011, 1), 1);
  BOOST_CHECK_EQUAL(cdf(tri011, 9), 1);
  BOOST_CHECK_EQUAL(mean(tri011), 0.666666666666666666666666666666666666666666666666667);
  BOOST_CHECK_EQUAL(variance(tri011), 1./18.);

  triangular tri0h1(0, 0.5, 1); // Equilateral triangle - mode is the middle.
  BOOST_CHECK_EQUAL(tri0h1.lower(), 0);
  BOOST_CHECK_EQUAL(tri0h1.mode(), 0.5);
  BOOST_CHECK_EQUAL(tri0h1.upper(), 1);
  BOOST_CHECK_EQUAL(mean(tri0h1), 0.5);
  BOOST_CHECK_EQUAL(mode(tri0h1), 0.5);
  BOOST_CHECK_EQUAL(pdf(tri0h1, -1), 0);
  BOOST_CHECK_EQUAL(cdf(tri0h1, -1), 0);
  BOOST_CHECK_EQUAL(pdf(tri0h1, 1), 0);
  BOOST_CHECK_EQUAL(pdf(tri0h1, 999), 0);
  BOOST_CHECK_EQUAL(cdf(tri0h1, 999), 1);
  BOOST_CHECK_EQUAL(cdf(tri0h1, 1), 1);
  BOOST_CHECK_CLOSE_FRACTION(cdf(tri0h1, 0.1), 0.02, tol5eps);
  BOOST_CHECK_EQUAL(cdf(tri0h1, 0.5), 0.5);
  BOOST_CHECK_CLOSE_FRACTION(cdf(tri0h1, 0.9), 0.98, tol5eps);

  BOOST_CHECK_CLOSE_FRACTION(quantile(tri0h1, 0.), 0., tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(tri0h1, 0.02), 0.1, tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(tri0h1, 0.5), 0.5, tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(tri0h1, 0.98), 0.9, tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(tri0h1, 1.), 1., tol5eps);

  triangular tri0q1(0, 0.25, 1); // mode is near bottom.
  BOOST_CHECK_CLOSE_FRACTION(cdf(tri0q1, 0.02), 0.0016, tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(cdf(tri0q1, 0.5), 0.66666666666666666666666666666666666666666666667, tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(cdf(tri0q1, 0.98), 0.99946666666666661, tol5eps);

  BOOST_CHECK_CLOSE_FRACTION(quantile(tri0q1, 0.0016), 0.02, tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(tri0q1, 0.66666666666666666666666666666666666666666666667), 0.5, tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0q1, 0.3333333333333333333333333333333333333333333333333)), 0.5, tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(tri0q1, 0.99946666666666661), 0.98, 10 * tol5eps);

  triangular trim12(-1, -0.5, 2); // mode is negative.
  BOOST_CHECK_CLOSE_FRACTION(pdf(trim12, 0), 0.533333333333333333333333333333333333333333333, tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(cdf(trim12, 0), 0.466666666666666666666666666666666666666666667, tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(cdf(complement(trim12, 0)), 1 - 0.466666666666666666666666666666666666666666667, tol5eps);

  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0q1, 1 - 0.99946666666666661)), 0.98, 10 * tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0h1, 1.)), 0., tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0h1, 0.5)), 0.5, tol5eps); // OK
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0h1, 1. - 0.02)), 0.1, tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0h1, 1. - 0.98)), 0.9, tol5eps);
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(tri0h1, 0)), 1., tol5eps);

  double xs [] = {0., 0.01, 0.02, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 0.98, 0.99, 1.};

  const triangular_distribution<double>& distr = tristd;
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(distr, 1.)), 0., tol5eps);
  const triangular_distribution<double>* distp = &tristd;
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(*distp, 1.)), 0., tol5eps);

  const triangular_distribution<double>* dists [] = {&tristd, &tri011, &tri0q1, &tri0h1, &trim12};
  BOOST_CHECK_CLOSE_FRACTION(quantile(complement(*dists[1], 1.)), 0., tol5eps);

  for (int i = 0; i < 5; i++)
  {
    const triangular_distribution<double>* const dist = dists[i];
    cout << "Distribution " << i << endl;
    BOOST_CHECK_EQUAL(quantile(complement(*dists[i], 1.)), quantile(*dists[i], 0.));
    BOOST_CHECK_CLOSE_FRACTION(quantile(*dists[i], 0.5), quantile(complement(*dist, 0.5)),  tol5eps); // OK
    BOOST_CHECK_CLOSE_FRACTION(quantile(*dists[i], 0.98), quantile(complement(*dist, 1. - 0.98)),tol5eps);
    // cout << setprecision(17) <<  median(*dist) << endl;
  }

  cout << showpos << setprecision(2) << endl;

  //triangular_distribution<double>& dist = trim12;
  for (unsigned i = 0; i < sizeof(xs) /sizeof(double); i++)
  {
    double x = xs[i] * (trim12.upper() - trim12.lower()) + trim12.lower();
    double dx = cdf(trim12, x);
    double cx = cdf(complement(trim12, x));
    //cout << fixed << showpos << setprecision(3)
    //  << xs[i] << ", " << x << ",  " << pdf(trim12, x) << ",  " << dx << ",  " << cx << ",, " ;

    BOOST_CHECK_CLOSE_FRACTION(cx, 1 - dx, tol500eps); // cx == 1 - dx

    // << setprecision(2) << scientific << cr - x << ", " // difference x - quan(cdf)
    // << setprecision(3) << fixed
    // << quantile(trim12, dx) << ", "
    // << quantile(complement(trim12, 1 - dx)) << ", "
    // << quantile(complement(trim12, cx)) << ", "
    // << endl;
    BOOST_CHECK_CLOSE_FRACTION(quantile(trim12, dx), quantile(complement(trim12, 1 - dx)), tol500eps);
  }
  cout << endl;
  // Basic sanity-check spot values.
  // (Parameter value, arbitrarily zero, only communicates the floating point type).
  test_spots(0.0F); // Test float. OK at decdigits = 0 tolerance = 0.0001 %
  test_spots(0.0); // Test double. OK at decdigits 7, tolerance = 1e07 %
  #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
    test_spots(0.0L); // Test long double.
  #if !BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x0582))
    test_spots(boost::math::concepts::real_concept(0.)); // Test real concept.
  #endif
  #else
     std::cout << "<note>The long double tests have been disabled on this platform "
        "either because the long double overloads of the usual math functions are "
        "not available at all, or because they are too inaccurate for these tests "
        "to pass.</note>" << std::cout;
  #endif

} // BOOST_AUTO_TEST_CASE( test_main )

/*

Output:

Autorun "i:\boost-06-05-03-1300\libs\math\test\Math_test\debug\test_triangular.exe"
Running 1 test case...
Distribution 0
Distribution 1
Distribution 2
Distribution 3
Distribution 4
Tolerance for type float is 5.96046e-007.
Tolerance for type double is 1.11022e-015.
Tolerance for type long double is 1.11022e-015.
Tolerance for type class boost::math::concepts::real_concept is 1.11022e-015.
*** No errors detected

*/


#include <fstream>
#include <iostream>
#include <experimental/filesystem>

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/io.hpp>

#include "../tasks.hh"
#include "../util/split.hh"
#include "../classes/BinnedTypedMatrix.hh"

namespace fs = std::experimental::filesystem;

using namespace EMC;

using namespace boost::numeric;

int EMC::apply(bool forceOverwrite, std::string inputVectorFileName, std::string outputVectorFileName, std::string matrixFileName) {

	if (!forceOverwrite && fs::exists(outputVectorFileName)) {
		std::cout << "Output file already exists, use -f to force overwrite." << std::endl;
		return 1;
	}

	std::ifstream matFile(matrixFileName);
	BinnedTypedMatrix responseMatrix = BinnedTypedMatrix::readFromFile(matFile);
	matFile.close();

	std::vector<double> inputBinCenter;
	std::vector<double> inputBinValue;
	std::ifstream inputVectorFile(inputVectorFileName);
	while (!inputVectorFile.eof()) {
		std::string line;
		std::getline(inputVectorFile, line );

		if (!line.empty()) {
			try {
				auto splitLine = split(split(split(line, ';'), ','), ' ');
				double binCntr = std::stod(splitLine[0]);
				double binValue = std::stod(splitLine[1]);
				inputBinCenter.push_back(binCntr);
				inputBinValue.push_back(binValue);
			} catch(...) { }
		}
	}
	inputVectorFile.close();

	ublas::vector<ValueError> inputVector(responseMatrix.columnCount);
	std::vector<double> inputVectorBinWidth(responseMatrix.columnCount, 0);

	for (unsigned int i = 0; i < inputBinCenter.size() && inputBinCenter[i] < responseMatrix.columnIndex.back() ; i++) {
		int fillPosition = -1;
		while ( (inputBinCenter[i] > responseMatrix.columnIndex.at(fillPosition + 1)) ) {
			fillPosition++;
		}

		//std::cout << inputBinCenter[i] << " --> [" << responseMatrix.columnIndex.at(fillPosition) << ", " << responseMatrix.columnIndex.at(fillPosition + 1) << "]" << std::endl;

		if (fillPosition >= 0) {
			inputVector[fillPosition].value += inputBinValue[i];
			if (i == 0) {
				inputVectorBinWidth[fillPosition] += inputBinCenter[i+1] - inputBinCenter[i];
			} else {
				inputVectorBinWidth[fillPosition] += inputBinCenter[i] - inputBinCenter[i-1];
			}
		}
	}

	for (unsigned int i=0; i<responseMatrix.columnCount; i++) {
		inputVector[i].err_sq = inputVector[i].value;
		inputVector[i].value /= inputVectorBinWidth[i];
		inputVector[i].err_sq /= (inputVectorBinWidth[i]*inputVectorBinWidth[i]);
		std::cout << "inputVectorBinWidth[i] = " << inputVectorBinWidth[i] << std::endl;
	}

	ublas::vector<ValueError> outputVector = ublas::prod(responseMatrix.m, inputVector);

    std::ofstream outputFstep(outputVectorFileName + ".step");

    for (unsigned int i=0; i<responseMatrix.rowCount; i++) {
    	outputFstep << responseMatrix.rowIndex[i]   << " " << outputVector[i].value << std::endl;
    	outputFstep << responseMatrix.rowIndex[i+1] << " " << outputVector[i].value << std::endl;
	}

    outputFstep.close();

    std::ofstream outputFcntr(outputVectorFileName + ".cntr");

    for (unsigned int i=0; i<responseMatrix.rowCount; i++) {
    	outputFcntr << (0.5*(responseMatrix.rowIndex[i] + responseMatrix.rowIndex[i+1]))   << " " << outputVector[i].value << " " << sqrt(outputVector[i].err_sq) << std::endl;
	}

    outputFcntr.close();

    return 0;
}


#include "drake/math/barycentric.h"

#include <cmath>
#include <memory>
#include <set>
#include <vector>

#include <Eigen/Dense>
#include <gtest/gtest.h>

#include "drake/common/symbolic.h"
#include "drake/common/test_utilities/eigen_matrix_compare.h"

namespace drake {
namespace math {
namespace {

using Eigen::MatrixXd;
using Eigen::VectorXd;
using Eigen::VectorXi;
using Eigen::Vector2d;
using Eigen::Vector3d;
using Eigen::Vector3i;

GTEST_TEST(BarycentricTest, GetMeshPoints) {
  // Create a mesh in 3 (input) dimensions, with the second dimension being a
  // singleton.
  const int kNumInputs = 3;
  BarycentricMesh<double> bary{{{0.0, 1.0},  // BR
                                {2.0},       // BR
                                {3.0, 4.0}}};

  EXPECT_EQ(bary.get_input_size(), kNumInputs);
  EXPECT_EQ(*(bary.get_input_grid()[2].rbegin()), 4.0);
  EXPECT_EQ(bary.get_num_mesh_points(), 4);

  Vector3d point;
  bary.get_mesh_point(0, &point);
  EXPECT_TRUE(CompareMatrices(point, Vector3d{0, 2, 3}));
  bary.get_mesh_point(1, &point);
  EXPECT_TRUE(CompareMatrices(point, Vector3d{1, 2, 3}));
  bary.get_mesh_point(2, &point);
  EXPECT_TRUE(CompareMatrices(point, Vector3d{0, 2, 4}));
  bary.get_mesh_point(3, &point);
  EXPECT_TRUE(CompareMatrices(point, Vector3d{1, 2, 4}));

  // Test the alternative call signature.
  EXPECT_TRUE(CompareMatrices(bary.get_mesh_point(0), Vector3d{0, 2, 3}));

  // Test the batch retrieval.
  const MatrixXd points = bary.get_all_mesh_points();
  EXPECT_EQ(points.cols(), 4);
  EXPECT_TRUE(CompareMatrices(points.col(3), Vector3d{1, 2, 4}));
}

GTEST_TEST(BarycentricTest, EvalWeights) {
  // Create a mesh in 3 (input) dimensions, with the second dimension being a
  // singleton.
  BarycentricMesh<double> bary{{{0.0, 1.0},  // BR
                                {2.0},       // BR
                                {3.0, 4.0}}};

  VectorXi indices(3);
  VectorXd weights(3);

  Vector3d sample{1, 2, 3.1};
  bary.EvalBarycentricWeights(sample, &indices, &weights);
  EXPECT_TRUE(CompareMatrices(indices, Vector3i{3, 1, 0}));
  EXPECT_TRUE(CompareMatrices(weights, Vector3d{.1, .9, 0}, 1e-8));

  // Off the grid (in singleton dimension) should not change things.
  sample[1] = 3.0;
  bary.EvalBarycentricWeights(sample, &indices, &weights);
  EXPECT_TRUE(CompareMatrices(indices, Vector3i{3, 1, 0}));
  EXPECT_TRUE(CompareMatrices(weights, Vector3d{.1, .9, 0}, 1e-8));

  // Off the grid to the right.
  sample[0] = 1.5;
  bary.EvalBarycentricWeights(sample, &indices, &weights);
  EXPECT_TRUE(CompareMatrices(indices, Vector3i{3, 1, 1}));
  EXPECT_TRUE(CompareMatrices(weights, Vector3d{.1, .9, 0}, 1e-8));

  // Test a different face.
  sample = Vector3d{0., 2.0, 3.4};
  bary.EvalBarycentricWeights(sample, &indices, &weights);
  EXPECT_TRUE(CompareMatrices(indices, Vector3i{2, 0, 0}));
  EXPECT_TRUE(CompareMatrices(weights, Vector3d{.4, .6, 0.}, 1e-8));

  // Off the grid to the left should not change things.
  sample[0] = -1.5;
  bary.EvalBarycentricWeights(sample, &indices, &weights);
  EXPECT_TRUE(CompareMatrices(indices, Vector3i{2, 0, 0}));
  EXPECT_TRUE(CompareMatrices(weights, Vector3d{.4, .6, 0}, 1e-8));

  // Smack in the middle.
  sample = Vector3d{.5, 2., 3.5};
  bary.EvalBarycentricWeights(sample, &indices, &weights);
  EXPECT_TRUE(CompareMatrices(indices, Vector3i{3, 2, 0}));
  EXPECT_TRUE(CompareMatrices(weights, Vector3d{.5, 0, .5}, 1e-8));
}

GTEST_TEST(BarycentricTest, EvalTest) {
  BarycentricMesh<double> bary{{{0.0, 1.0},  // BR
                                {0.0, 1.0}}};

  MatrixXd mesh = Eigen::RowVector4d{1., 2., 3., 4.};

  Vector1d value;
  double tol = 1e-8;
  // Check grid points.
  bary.Eval(mesh, Vector2d{0., 0.}, &value);
  EXPECT_NEAR(value[0], 1., tol);
  bary.Eval(mesh, Vector2d{1., 0.}, &value);
  EXPECT_NEAR(value[0], 2., tol);
  bary.Eval(mesh, Vector2d{0., 1.}, &value);
  EXPECT_NEAR(value[0], 3., tol);
  bary.Eval(mesh, Vector2d{1., 1.}, &value);
  EXPECT_NEAR(value[0], 4., tol);

  // Check the middle.
  bary.Eval(mesh, Vector2d{.5, .5}, &value);
  EXPECT_NEAR(value[0], 2.5, 1e-8);

  // Check the two faces.
  bary.Eval(mesh, Vector2d{.75, .25}, &value);
  EXPECT_NEAR(value[0], 2.25, 1e-8);
  bary.Eval(mesh, Vector2d{.25, .75}, &value);
  EXPECT_NEAR(value[0], 2.75, 1e-8);

  // Lift a corner and check again.
  mesh(0, 2) = 10.;
  bary.Eval(mesh, Vector2d{.25, .75}, &value);
  EXPECT_NEAR(value[0], 6.25, 1e-8);

  // Test the alternative call signature.
  EXPECT_NEAR(bary.Eval(mesh, Vector2d{.25, .75})[0], 6.25, 1e-8);
}

GTEST_TEST(BarycentricTest, EvalSymbolicTest) {
  BarycentricMesh<double> bary{{{0.0, 1.0},  // BR
                                {0.0, 1.0}}};

  using symbolic::Variable;
  using symbolic::Expression;
  Variable a{"a"}, b{"b"}, c{"c"}, d{"d"};
  RowVector4<Expression> mesh;
  mesh << a, b, c, d;

  Vector1<Expression> value;
  // Check grid points.
  bary.EvalWithMixedScalars<Expression>(mesh, Vector2d{0., 0.}, &value);
  EXPECT_TRUE(value[0].EqualTo(a));
  bary.EvalWithMixedScalars<Expression>(mesh, Vector2d{1., 0.}, &value);
  EXPECT_TRUE(value[0].EqualTo(b));
  bary.EvalWithMixedScalars<Expression>(mesh, Vector2d{0., 1.}, &value);
  EXPECT_TRUE(value[0].EqualTo(c));
  bary.EvalWithMixedScalars<Expression>(mesh, Vector2d{1., 1.}, &value);
  EXPECT_TRUE(value[0].EqualTo(d));

  // Check the middle.
  bary.EvalWithMixedScalars<Expression>(mesh, Vector2d{.5, .5}, &value);
  EXPECT_TRUE(value[0].EqualTo(.5 * a + .5 * d));

  // Test the alternative call signature.
  EXPECT_TRUE(
      bary.EvalWithMixedScalars<Expression>(mesh, Vector2d{0., 0.})[0].EqualTo(
          a));
}

GTEST_TEST(BarycentricTest, MultidimensionalOutput) {
  BarycentricMesh<double> bary{{{0.0, 1.0},  // BR
                                {0.0, 1.0}}};

  const int kNumOutputs = 2;
  MatrixXd mesh(kNumOutputs, bary.get_num_mesh_points());
  mesh << 1., 2., 3., 4.,  // BR
      5., 6., 7., 8.;

  Vector2d value;
  // Check the two faces.
  bary.Eval(mesh, Vector2d{.75, .25}, &value);
  EXPECT_TRUE(CompareMatrices(value, Vector2d{2.25, 6.25}, 1e-8));
  bary.Eval(mesh, Vector2d{.25, .75}, &value);
  EXPECT_TRUE(CompareMatrices(value, Vector2d{2.75, 6.75}, 1e-8));
}

Vector1d my_sine(const Eigen::Ref<const Vector1d>& x) {
  return Vector1d(std::sin(x[0]));
}

// Build a BarycentricMesh from a function pointer.
GTEST_TEST(BarycentricTest, FromVectorFunc) {
  BarycentricMesh<double>::Coordinates x_values{0, .1, .2, 5, 204};

  BarycentricMesh<double> bary({x_values});

  MatrixXd mesh_values = bary.MeshValuesFrom(&my_sine);

  // Check that it evaluates correctly on the grid (the interpolation is
  // verified with the other tests).
  Vector1d y_value;
  for (const auto& x : x_values) {
    bary.Eval(mesh_values, Vector1d(x), &y_value);
    EXPECT_EQ(y_value[0], std::sin(x));
  }
}

// Build a BarycentricMesh from a lambda expression.
GTEST_TEST(BarycentricTest, FromLambda) {
  BarycentricMesh<double>::Coordinates x_values{0, .1, .2, 5, 204};

  BarycentricMesh<double> bary({x_values});

  MatrixXd mesh_values = bary.MeshValuesFrom(
      [](const auto& x) { return Vector1d(std::sin(x[0])); });

  // Check that it evaluates correctly on the grid (the interpolation is
  // verified with the other tests).
  Vector1d y_value;
  for (const auto& x : x_values) {
    bary.Eval(mesh_values, Vector1d(x), &y_value);
    EXPECT_EQ(y_value[0], std::sin(x));
  }
}

}  // namespace
}  // namespace math
}  // namespace drake


#pragma once

#include <iostream>

#include <chrono>
#include <functional>
#include <memory>
#include <optional>

#include <boost/hana.hpp>

#include "SignalPt.hpp"

namespace chrono = std::chrono;
using namespace std::chrono_literals;

template <typename Clock = chrono::steady_clock>
struct PIDState {
  chrono::time_point<Clock> time;
  double errSum;
  double error;
  double ctrlVal;
};

template <typename Clock = chrono::steady_clock>
auto pid_algebra(double kp, double ki, double kd) {
  return [kp, ki, kd](PIDState<Clock> prev,
                      SignalPt<double, Clock> errSigl) -> PIDState<Clock> {
    const chrono::duration<double> deltaT =
        errSigl.time - prev.time;
    if (deltaT <= chrono::seconds{0}) return prev;
    const auto errSum = prev.errSum + (errSigl.value * deltaT.count());
    const auto dErr = (errSigl.value - prev.error) / deltaT.count();
    const auto ctrlVal = kp * errSigl.value + ki * errSum + kd * dErr;

    return {errSigl.time, errSum, errSigl.value, ctrlVal};
  };
}


//---------------------------------------------------------------------------//
//!
//! \file   DataGen_FreeGasElasticMarginalBetaFunction.hpp
//! \author Alex Robinson
//! \brief  Free gas elastic marginal beta function definition
//!
//---------------------------------------------------------------------------//

// Boost Includes
#include <boost/bind.hpp>

// Trilinos Includes
#include <Teuchos_Tuple.hpp>

// FRENSIE Includes
#include "DataGen_FreeGasElasticMarginalBetaFunction.hpp"
#include "Utility_SearchAlgorithms.hpp"
#include "Utility_KinematicHelpers.hpp"
#include "Utility_ComparePolicy.hpp"
#include "Utility_ContractException.hpp"

namespace DataGen{

// Constructor
FreeGasElasticMarginalBetaFunction::FreeGasElasticMarginalBetaFunction(
	  const Teuchos::RCP<Utility::OneDDistribution>&
	  zero_temp_elastic_cross_section,
          const Teuchos::RCP<MonteCarlo::NuclearScatteringAngularDistribution>&
	  cm_scattering_distribution,
	  const double A,
	  const double kT,
	  const double E )
  : d_alpha_gkq_set( 1e-4, 0.0, 10000 ),
    d_beta_gkq_set( 1e-4, 0.0, 10000 ),
    d_sab_function( zero_temp_elastic_cross_section,
		    cm_scattering_distribution,
		    A,
		    kT ),
    d_E( E ),
    d_A( A ),
    d_kT( kT ),
    d_beta_min( 0.0 ),
    d_norm_constant( 1.0 ),
    d_cached_cdf_values()
{
  // Make sure the values are valid
  testPrecondition( A > 0.0 );
  testPrecondition( kT > 0.0 );
  testPrecondition( E > 0.0 );

  updateCachedValues();
}

// Set the beta and energy values
void FreeGasElasticMarginalBetaFunction::setIndependentVariables(
							       const double E )
{
  // Make sure the energy is valid
  testPrecondition( E > 0.0 );

  d_E = E;

  updateCachedValues();
}

// Get the lower beta limit
double FreeGasElasticMarginalBetaFunction::getBetaMin() const
{
  return d_beta_min;
}

// Get the normalization constant
double FreeGasElasticMarginalBetaFunction::getNormalizationConstant() const
{
  return d_norm_constant;
}

// Evaluate the marginal PDF
double FreeGasElasticMarginalBetaFunction::operator()( const double beta )
{
  // Make sure the beta value is valid
  testPrecondition( beta >= d_beta_min );

  return integratedSAlphaBetaFunction( beta )/d_norm_constant;
}

// Evaluate the marginal CDF
double FreeGasElasticMarginalBetaFunction::evaluateCDF( const double beta )
{
  // Make sure the beta value is valid
  testPrecondition( beta >= d_beta_min );

  // Find the nearest cached evaluation of cdf
  std::list<Utility::Pair<double,double> >::iterator lower_cdf_point =
    d_cached_cdf_values.begin();

  lower_cdf_point = Utility::Search::binaryLowerBound<Utility::FIRST>(
						     lower_cdf_point,
						     d_cached_cdf_values.end(),
						     beta );

  // Calculate the cdf value
  double cdf_value, cdf_value_error;

  d_beta_gkq_set.integrateAdaptively<15>( *this,
					 lower_cdf_point->first,
					 beta,
					 cdf_value,
					 cdf_value_error );

  cdf_value += lower_cdf_point->second;

  // Cache the new cdf value
  Utility::Pair<double,double> new_cdf_point( beta, cdf_value );

  d_cached_cdf_values.insert( ++lower_cdf_point, new_cdf_point );

  // Return the calculated cdf value
  return cdf_value;
}

// Update the cached values
void FreeGasElasticMarginalBetaFunction::updateCachedValues()
{
  d_beta_min = Utility::calculateBetaMin( d_E, d_kT );
  std::cout << "beta min: " << d_beta_min << std::endl;
  // Calculate the norm constant
  double norm_constant_error;

  boost::function<double (double beta)> d_integrated_sab_function =
    boost::bind<double>( &FreeGasElasticMarginalBetaFunction::integratedSAlphaBetaFunction, boost::ref( *this ), _1 );

  d_beta_gkq_set.integrateAdaptively<15>( d_integrated_sab_function,
  					 400.0,
  					 540.0,
  					 d_norm_constant,
  					 norm_constant_error );

  // Teuchos::Tuple<double,3> points_of_interest =
  //   Teuchos::tuple( d_beta_min, 515.0, -d_beta_min );

  // d_beta_gkq_set.integrateAdaptivelyWynnEpsilon( d_integrated_sab_function,
  // 						points_of_interest(),
  // 						d_norm_constant,
  // 						norm_constant_error );

  // Make sure the norm constant is non-zero
  testPostcondition( d_norm_constant > 0.0 );

  // Reset the cached cdf values
  d_cached_cdf_values.clear();

  Utility::Pair<double,double> cdf_point( d_beta_min, 0.0 );
  d_cached_cdf_values.push_back( cdf_point );

  cdf_point( std::numeric_limits<double>::infinity(), 1.0 );

  d_cached_cdf_values.push_back( cdf_point );
}

// Function that represents the integral of S(alpha,beta) over all alpha
double FreeGasElasticMarginalBetaFunction::integratedSAlphaBetaFunction(
							    const double beta )
{
  // Make sure beta is valid
  testPrecondition( beta >= d_beta_min );

  double alpha_min = Utility::calculateAlphaMin( d_E, beta, d_A, d_kT );
  double alpha_max = Utility::calculateAlphaMax( d_E, beta, d_A, d_kT );

  double function_value, function_value_error;

  boost::function<double (double alpha)> sab_function_wrapper =
    boost::bind<double>( boost::ref( d_sab_function ), _1, beta, d_E );

  // d_alpha_gkq_set.integrateAdaptively<15>( sab_function_wrapper,
  // 					  alpha_min,
  // 					  alpha_max,
  // 					  function_value,
  // 					  function_value_error );

  if( beta < 0.0 && beta > d_beta_min )
  {
    Teuchos::Tuple<double,3> points_of_interest =
      Teuchos::tuple( alpha_min, -beta, alpha_max );
    std::cout << points_of_interest() << std::endl;
    d_beta_gkq_set.integrateAdaptivelyWynnEpsilon( sab_function_wrapper,
						  points_of_interest(),
						  function_value,
						  function_value_error );
  }
  else
  {
    d_alpha_gkq_set.integrateAdaptively<15>( sab_function_wrapper,
					    alpha_min,
					    alpha_max,
					    function_value,
					    function_value_error );
  }

  std::cout << beta << " " << function_value << std::endl;

  // Make sure the return value is valid
  testPostcondition(!Teuchos::ScalarTraits<double>::isnaninf(function_value));

  return function_value;
}

} // end DataGen namespace

//---------------------------------------------------------------------------//
// end DataGen_FreeGasElasticMarginalBetaFunction.cpp
//---------------------------------------------------------------------------//


///////////////////////////////////////////////////////////////////////////////
//  Copyright 2013 Nikhar Agrawal
//  Copyright 2013 Christopher Kormanyos
//  Copyright 2013 John Maddock
//  Copyright 2013 Paul Bristow
//  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_BERNOULLI_2013_05_30_HPP_
  #define _BOOST_BERNOULLI_2013_05_30_HPP_

  #include <boost/array.hpp>
  #include <boost/cstdint.hpp>
  #include "detail/bernoulli_b2n.hpp"

  namespace boost { namespace math {

  template <class T, class Policy>
  inline T bernoulli_b2n(const int i, const Policy &pol)
  {
    if(i<0)
      policies::raise_domain_error<T>("boost::math::bernoulli<%1%>", "Index should be >= 0 but got %1%", T(i), pol);
    const int i_2 = 2 * i;

    return boost::math::detail::bernoulli_number_imp<T,Policy>(i_2, pol);
  }

  template <class T>
  inline T bernoulli_b2n(const int i)
  {
    return boost::math::bernoulli_b2n<T>(i, policies::policy<>());
  }

  template <class T, class OutputIterator, class Policy>
  inline OutputIterator bernoulli_b2n(int start_index,
                                      unsigned number_of_bernoullis_b2n,
                                      OutputIterator out_it,
                                      const Policy& pol)
  {
    return boost::math::detail::bernoulli_series_imp<T, OutputIterator, Policy>(start_index,
                                                                                number_of_bernoullis_b2n,
                                                                                out_it,
                                                                                pol);
  }

  template <class T, class OutputIterator>
  inline OutputIterator bernoulli_b2n(int start_index,
                                      unsigned number_of_bernoullis_b2n,
                                      OutputIterator out_it)
  {
    return boost::math::bernoulli_b2n<T, OutputIterator>(start_index,
                                                         number_of_bernoullis_b2n,
                                                         out_it,
                                                         policies::policy<>());
  }

} } // namespace boost::math

#endif // _BOOST_BERNOULLI_2013_05_30_HPP_


/* boost random/piecewise_linear_distribution.hpp header file
 *
 * Copyright Steven Watanabe 2011
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * See http://www.boost.org for most recent version including documentation.
 *
 * $Id: piecewise_linear_distribution.hpp 83381 2013-03-09 22:55:05Z eric_niebler $
 */

#ifndef BOOST_RANDOM_PIECEWISE_LINEAR_DISTRIBUTION_HPP_INCLUDED
#define BOOST_RANDOM_PIECEWISE_LINEAR_DISTRIBUTION_HPP_INCLUDED

#include <vector>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <boost/assert.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/discrete_distribution.hpp>
#include <boost/random/detail/config.hpp>
#include <boost/random/detail/operators.hpp>
#include <boost/random/detail/vector_io.hpp>

#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
#include <initializer_list>
#endif

#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>

namespace boost {
namespace random {

/**
 * The class @c piecewise_linear_distribution models a \random_distribution.
 */
template<class RealType = double>
class piecewise_linear_distribution {
public:
    typedef std::size_t input_type;
    typedef RealType result_type;

    class param_type {
    public:

        typedef piecewise_linear_distribution distribution_type;

        /**
         * Constructs a @c param_type object, representing a distribution
         * that produces values uniformly distributed in the range [0, 1).
         */
        param_type()
        {
            _weights.push_back(RealType(1));
            _weights.push_back(RealType(1));
            _intervals.push_back(RealType(0));
            _intervals.push_back(RealType(1));
        }
        /**
         * Constructs a @c param_type object from two iterator ranges
         * containing the interval boundaries and weights at the boundaries.
         * If there are fewer than two boundaries, then this is equivalent to
         * the default constructor and the distribution will produce values
         * uniformly distributed in the range [0, 1).
         *
         * The values of the interval boundaries must be strictly
         * increasing, and the number of weights must be the same as
         * the number of interval boundaries.  If there are extra
         * weights, they are ignored.
         */
        template<class IntervalIter, class WeightIter>
        param_type(IntervalIter intervals_first, IntervalIter intervals_last,
                   WeightIter weight_first)
          : _intervals(intervals_first, intervals_last)
        {
            if(_intervals.size() < 2) {
                _intervals.clear();
                _weights.push_back(RealType(1));
                _weights.push_back(RealType(1));
                _intervals.push_back(RealType(0));
                _intervals.push_back(RealType(1));
            } else {
                _weights.reserve(_intervals.size());
                for(std::size_t i = 0; i < _intervals.size(); ++i) {
                    _weights.push_back(*weight_first++);
                }
            }
        }
#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
        /**
         * Constructs a @c param_type object from an initializer_list
         * containing the interval boundaries and a unary function
         * specifying the weights at the boundaries.  Each weight is
         * determined by calling the function at the corresponding point.
         *
         * If the initializer_list contains fewer than two elements,
         * this is equivalent to the default constructor and the
         * distribution will produce values uniformly distributed
         * in the range [0, 1).
         */
        template<class T, class F>
        param_type(const std::initializer_list<T>& il, F f)
          : _intervals(il.begin(), il.end())
        {
            if(_intervals.size() < 2) {
                _intervals.clear();
                _weights.push_back(RealType(1));
                _weights.push_back(RealType(1));
                _intervals.push_back(RealType(0));
                _intervals.push_back(RealType(1));
            } else {
                _weights.reserve(_intervals.size());
                for(typename std::vector<RealType>::const_iterator
                    iter = _intervals.begin(), end = _intervals.end();
                    iter != end; ++iter)
                {
                    _weights.push_back(f(*iter));
                }
            }
        }
#endif
        /**
         * Constructs a @c param_type object from Boost.Range ranges holding
         * the interval boundaries and the weights at the boundaries.  If
         * there are fewer than two interval boundaries, this is equivalent
         * to the default constructor and the distribution will produce
         * values uniformly distributed in the range [0, 1).  The
         * number of weights must be equal to the number of
         * interval boundaries.
         */
        template<class IntervalRange, class WeightRange>
        param_type(const IntervalRange& intervals_arg,
                   const WeightRange& weights_arg)
          : _intervals(boost::begin(intervals_arg), boost::end(intervals_arg)),
            _weights(boost::begin(weights_arg), boost::end(weights_arg))
        {
            if(_intervals.size() < 2) {
                _weights.clear();
                _weights.push_back(RealType(1));
                _weights.push_back(RealType(1));
                _intervals.clear();
                _intervals.push_back(RealType(0));
                _intervals.push_back(RealType(1));
            }
        }

        /**
         * Constructs the parameters for a distribution that approximates a
         * function.  The range of the distribution is [xmin, xmax).  This
         * range is divided into nw equally sized intervals and the weights
         * are found by calling the unary function f on the boundaries of the
         * intervals.
         */
        template<class F>
        param_type(std::size_t nw, RealType xmin, RealType xmax, F f)
        {
            std::size_t n = (nw == 0) ? 1 : nw;
            double delta = (xmax - xmin) / n;
            BOOST_ASSERT(delta > 0);
            for(std::size_t k = 0; k < n; ++k) {
                _weights.push_back(f(xmin + k*delta));
                _intervals.push_back(xmin + k*delta);
            }
            _weights.push_back(f(xmax));
            _intervals.push_back(xmax);
        }

        /**  Returns a vector containing the interval boundaries. */
        std::vector<RealType> intervals() const { return _intervals; }

        /**
         * Returns a vector containing the probability densities
         * at all the interval boundaries.
         */
        std::vector<RealType> densities() const
        {
            RealType sum = static_cast<RealType>(0);
            for(std::size_t i = 0; i < _intervals.size() - 1; ++i) {
                RealType width = _intervals[i + 1] - _intervals[i];
                sum += (_weights[i] + _weights[i + 1]) * width / 2;
            }
            std::vector<RealType> result;
            result.reserve(_weights.size());
            for(typename std::vector<RealType>::const_iterator
                iter = _weights.begin(), end = _weights.end();
                iter != end; ++iter)
            {
                result.push_back(*iter / sum);
            }
            return result;
        }

        /** Writes the parameters to a @c std::ostream. */
        BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm)
        {
            detail::print_vector(os, parm._intervals);
            detail::print_vector(os, parm._weights);
            return os;
        }

        /** Reads the parameters from a @c std::istream. */
        BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, parm)
        {
            std::vector<RealType> new_intervals;
            std::vector<RealType> new_weights;
            detail::read_vector(is, new_intervals);
            detail::read_vector(is, new_weights);
            if(is) {
                parm._intervals.swap(new_intervals);
                parm._weights.swap(new_weights);
            }
            return is;
        }

        /** Returns true if the two sets of parameters are the same. */
        BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
        {
            return lhs._intervals == rhs._intervals
                && lhs._weights == rhs._weights;
        }
        /** Returns true if the two sets of parameters are different. */
        BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)

    private:
        friend class piecewise_linear_distribution;

        std::vector<RealType> _intervals;
        std::vector<RealType> _weights;
    };

    /**
     * Creates a new @c piecewise_linear_distribution that
     * produces values uniformly distributed in the range [0, 1).
     */
    piecewise_linear_distribution()
    {
        default_init();
    }
    /**
     * Constructs a piecewise_linear_distribution from two iterator ranges
     * containing the interval boundaries and the weights at the boundaries.
     * If there are fewer than two boundaries, then this is equivalent to
     * the default constructor and creates a distribution that
     * produces values uniformly distributed in the range [0, 1).
     *
     * The values of the interval boundaries must be strictly
     * increasing, and the number of weights must be equal to
     * the number of interval boundaries.  If there are extra
     * weights, they are ignored.
     *
     * For example,
     *
     * @code
     * double intervals[] = { 0.0, 1.0, 2.0 };
     * double weights[] = { 0.0, 1.0, 0.0 };
     * piecewise_constant_distribution<> dist(
     *     &intervals[0], &intervals[0] + 3, &weights[0]);
     * @endcode
     *
     * produces a triangle distribution.
     */
    template<class IntervalIter, class WeightIter>
    piecewise_linear_distribution(IntervalIter first_interval,
                                  IntervalIter last_interval,
                                  WeightIter first_weight)
      : _intervals(first_interval, last_interval)
    {
        if(_intervals.size() < 2) {
            default_init();
        } else {
            _weights.reserve(_intervals.size());
            for(std::size_t i = 0; i < _intervals.size(); ++i) {
                _weights.push_back(*first_weight++);
            }
            init();
        }
    }
#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
    /**
     * Constructs a piecewise_linear_distribution from an
     * initializer_list containing the interval boundaries
     * and a unary function specifying the weights.  Each
     * weight is determined by calling the function at the
     * corresponding interval boundary.
     *
     * If the initializer_list contains fewer than two elements,
     * this is equivalent to the default constructor and the
     * distribution will produce values uniformly distributed
     * in the range [0, 1).
     */
    template<class T, class F>
    piecewise_linear_distribution(std::initializer_list<T> il, F f)
      : _intervals(il.begin(), il.end())
    {
        if(_intervals.size() < 2) {
            default_init();
        } else {
            _weights.reserve(_intervals.size());
            for(typename std::vector<RealType>::const_iterator
                iter = _intervals.begin(), end = _intervals.end();
                iter != end; ++iter)
            {
                _weights.push_back(f(*iter));
            }
            init();
        }
    }
#endif
    /**
     * Constructs a piecewise_linear_distribution from Boost.Range
     * ranges holding the interval boundaries and the weights.  If
     * there are fewer than two interval boundaries, this is equivalent
     * to the default constructor and the distribution will produce
     * values uniformly distributed in the range [0, 1).  The
     * number of weights must be equal to the number of
     * interval boundaries.
     */
    template<class IntervalsRange, class WeightsRange>
    piecewise_linear_distribution(const IntervalsRange& intervals_arg,
                                  const WeightsRange& weights_arg)
      : _intervals(boost::begin(intervals_arg), boost::end(intervals_arg)),
        _weights(boost::begin(weights_arg), boost::end(weights_arg))
    {
        if(_intervals.size() < 2) {
            default_init();
        } else {
            init();
        }
    }
    /**
     * Constructs a piecewise_linear_distribution that approximates a
     * function.  The range of the distribution is [xmin, xmax).  This
     * range is divided into nw equally sized intervals and the weights
     * are found by calling the unary function f on the interval boundaries.
     */
    template<class F>
    piecewise_linear_distribution(std::size_t nw,
                                  RealType xmin,
                                  RealType xmax,
                                  F f)
    {
        if(nw == 0) { nw = 1; }
        RealType delta = (xmax - xmin) / nw;
        _intervals.reserve(nw + 1);
        for(std::size_t i = 0; i < nw; ++i) {
            RealType x = xmin + i * delta;
            _intervals.push_back(x);
            _weights.push_back(f(x));
        }
        _intervals.push_back(xmax);
        _weights.push_back(f(xmax));
        init();
    }
    /**
     * Constructs a piecewise_linear_distribution from its parameters.
     */
    explicit piecewise_linear_distribution(const param_type& parm)
      : _intervals(parm._intervals),
        _weights(parm._weights)
    {
        init();
    }

    /**
     * Returns a value distributed according to the parameters of the
     * piecewise_linear_distribution.
     */
    template<class URNG>
    RealType operator()(URNG& urng) const
    {
        std::size_t i = _bins(urng);
        bool is_in_rectangle = (i % 2 == 0);
        i = i / 2;
        uniform_real<RealType> dist(_intervals[i], _intervals[i+1]);
        if(is_in_rectangle) {
            return dist(urng);
        } else if(_weights[i] < _weights[i+1]) {
            return (std::max)(dist(urng), dist(urng));
        } else {
            return (std::min)(dist(urng), dist(urng));
        }
    }

    /**
     * Returns a value distributed according to the parameters
     * specified by param.
     */
    template<class URNG>
    RealType operator()(URNG& urng, const param_type& parm) const
    {
        return piecewise_linear_distribution(parm)(urng);
    }

    /** Returns the smallest value that the distribution can produce. */
    result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const
    { return _intervals.front(); }
    /** Returns the largest value that the distribution can produce. */
    result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const
    { return _intervals.back(); }

    /**
     * Returns a vector containing the probability densities
     * at the interval boundaries.
     */
    std::vector<RealType> densities() const
    {
        RealType sum = static_cast<RealType>(0);
        for(std::size_t i = 0; i < _intervals.size() - 1; ++i) {
            RealType width = _intervals[i + 1] - _intervals[i];
            sum += (_weights[i] + _weights[i + 1]) * width / 2;
        }
        std::vector<RealType> result;
        result.reserve(_weights.size());
        for(typename std::vector<RealType>::const_iterator
            iter = _weights.begin(), end = _weights.end();
            iter != end; ++iter)
        {
            result.push_back(*iter / sum);
        }
        return result;
    }
    /**  Returns a vector containing the interval boundaries. */
    std::vector<RealType> intervals() const { return _intervals; }

    /** Returns the parameters of the distribution. */
    param_type param() const
    {
        return param_type(_intervals, _weights);
    }
    /** Sets the parameters of the distribution. */
    void param(const param_type& parm)
    {
        std::vector<RealType> new_intervals(parm._intervals);
        std::vector<RealType> new_weights(parm._weights);
        init(new_intervals, new_weights);
        _intervals.swap(new_intervals);
        _weights.swap(new_weights);
    }

    /**
     * Effects: Subsequent uses of the distribution do not depend
     * on values produced by any engine prior to invoking reset.
     */
    void reset() { _bins.reset(); }

    /** Writes a distribution to a @c std::ostream. */
    BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(
        os, piecewise_linear_distribution, pld)
    {
        os << pld.param();
        return os;
    }

    /** Reads a distribution from a @c std::istream */
    BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(
        is, piecewise_linear_distribution, pld)
    {
        param_type parm;
        if(is >> parm) {
            pld.param(parm);
        }
        return is;
    }

    /**
     * Returns true if the two distributions will return the
     * same sequence of values, when passed equal generators.
     */
    BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(
        piecewise_linear_distribution, lhs,  rhs)
    {
        return lhs._intervals == rhs._intervals && lhs._weights == rhs._weights;
    }
    /**
     * Returns true if the two distributions may return different
     * sequences of values, when passed equal generators.
     */
    BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(piecewise_linear_distribution)

private:

    /// @cond \show_private

    void init(const std::vector<RealType>& intervals_arg,
              const std::vector<RealType>& weights_arg)
    {
        std::vector<RealType> bin_weights;
        bin_weights.reserve((intervals_arg.size() - 1) * 2);
        for(std::size_t i = 0; i < intervals_arg.size() - 1; ++i) {
            RealType width = intervals_arg[i + 1] - intervals_arg[i];
            RealType w1 = weights_arg[i];
            RealType w2 = weights_arg[i + 1];
            bin_weights.push_back((std::min)(w1, w2) * width);
            bin_weights.push_back(std::abs(w1 - w2) * width / 2);
        }
        typedef discrete_distribution<std::size_t, RealType> bins_type;
        typename bins_type::param_type bins_param(bin_weights);
        _bins.param(bins_param);
    }

    void init()
    {
        init(_intervals, _weights);
    }

    void default_init()
    {
        _intervals.clear();
        _intervals.push_back(RealType(0));
        _intervals.push_back(RealType(1));
        _weights.clear();
        _weights.push_back(RealType(1));
        _weights.push_back(RealType(1));
        init();
    }

    discrete_distribution<std::size_t, RealType> _bins;
    std::vector<RealType> _intervals;
    std::vector<RealType> _weights;

    /// @endcond
};

}
}

#endif