/*
	File                 : FitTest.cpp
	Project              : LabPlot
	Description          : Tests for data fitting
	--------------------------------------------------------------------
	SPDX-FileCopyrightText: 2017 Alexander Semke <alexander.semke@web.de>
	SPDX-FileCopyrightText: 2018-2025 Stefan Gerlach <stefan.gerlach@uni.kn>

	SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "FitTest.h"
#include "backend/core/AbstractAspect.h"
#include "backend/core/AbstractColumn.h"
#include "backend/core/Project.h"
#include "backend/core/column/Column.h"
#include "backend/datasources/filters/AsciiFilter.h"
#include "backend/spreadsheet/Spreadsheet.h"
#include "backend/worksheet/Worksheet.h"
#include "backend/worksheet/plots/cartesian/Histogram.h"
#include "backend/worksheet/plots/cartesian/XYAnalysisCurvePrivate.h"
#include "backend/worksheet/plots/cartesian/XYCurvePrivate.h"
#include "backend/worksheet/plots/cartesian/XYFitCurve.h"

#include "backend/nsl/nsl_sf_stats.h"
#include "backend/nsl/nsl_stats.h"

// Print value and relative deviation from exact value
// also check with relative tolerance tol
void FitTest::printAndCheck(double value, double exact, double tol) {
	WARN(std::setprecision(15) << value << ' ' << (value - exact) / exact);
	FuzzyCompare(value, exact, tol);
}

// ##############################################################################
// #################  linear regression with NIST datasets ######################
// ##############################################################################
void FitTest::addCurve() {
	Project project;

	auto* ws = new Worksheet(QStringLiteral("Worksheet"));
	project.addChild(ws);

	auto* plot = new CartesianPlot(QStringLiteral("plot"));
	ws->addChild(plot);

	auto* sheet = new Spreadsheet(QStringLiteral("sheet"));
	project.addChild(sheet);
	sheet->setColumnCount(2);
	sheet->column(0)->setName(QStringLiteral("x"));
	sheet->column(1)->setName(QStringLiteral("y"));

	auto* curve = new XYCurve(QStringLiteral("curve"));
	plot->addChild(curve);

	curve->d_func()->setSelected(true);
	QCOMPARE(plot->currentCurve(), curve);

	plot->addFitCurve(); // Should not crash and curve should be assigned accordingly

	const auto& analysisCurves = plot->children<XYAnalysisCurve>();
	QCOMPARE(analysisCurves.count(), 1);

	QCOMPARE(analysisCurves.at(0)->d_func()->dataSourceCurve, curve);
}

void FitTest::testLinearNorris() {
	// NIST data for Norris dataset
	QVector<double> xData = {0.2,	337.4, 118.2, 884.6, 10.1,	226.5, 666.3, 996.3, 448.6, 777.0, 558.2, 0.4,	 0.6,  775.5, 666.9, 338.0, 447.5, 11.6,
							 556.0, 228.1, 995.8, 887.6, 120.2, 0.3,   0.3,	  556.8, 339.1, 887.2, 999.0, 779.0, 11.1, 118.3, 229.2, 669.1, 448.9, 0.5};
	QVector<double> yData = {0.1,	338.8, 118.1, 888.0, 9.2,	228.1, 668.5, 998.5, 449.1, 778.9, 559.2, 0.3,	 0.1,  778.1, 668.8, 339.3, 448.9, 10.8,
							 557.7, 228.3, 998.0, 888.8, 119.6, 0.3,   0.6,	  557.6, 339.3, 888.0, 998.5, 778.9, 10.2, 117.6, 228.9, 668.4, 449.2, 0.2};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 1;
	XYFitCurve::initFitData(fitData);
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	const int np = fitData.paramNames.size();
	QCOMPARE(np, 2);

	QCOMPARE(fitResult.paramValues.at(0), -0.262323073774029);
	QCOMPARE(fitResult.errorValues.at(0), 0.232818234301152);
	QCOMPARE(fitResult.paramValues.at(1), 1.00211681802045);
	QCOMPARE(fitResult.errorValues.at(1), 0.429796848199937e-3);

	QCOMPARE(fitResult.rsd, 0.884796396144373);
	QCOMPARE(fitResult.rsquare, 0.999993745883712);
	QCOMPARE(fitResult.sse, 26.6173985294224);
	QCOMPARE(fitResult.rms, 0.782864662630069);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 5436385.54083098
	FuzzyCompare(fitResult.fdist_F, 5436385.54079785, 1.e-9);
}

void FitTest::testLinearPontius() {
	// NIST data for Pontius dataset
	QVector<int> xData = {150000,  300000,	450000,	 600000,  750000,  900000,	1050000, 1200000, 1350000, 1500000, 1650000, 1800000, 1950000, 2100000,
						  2250000, 2400000, 2550000, 2700000, 2850000, 3000000, 150000,	 300000,  450000,  600000,	750000,	 900000,  1050000, 1200000,
						  1350000, 1500000, 1650000, 1800000, 1950000, 2100000, 2250000, 2400000, 2550000, 2700000, 2850000, 3000000};
	QVector<double> yData = {.11019,  .21956,  .32949,	.43899,	 .54803,  .65694,  .76562,	.87487,	 .98292,  1.09146, 1.20001, 1.30822, 1.41599, 1.52399,
							 1.63194, 1.73947, 1.84646, 1.95392, 2.06128, 2.16844, .11052,	.22018,	 .32939,  .43886,  .54798,	.65739,	 .76596,  .87474,
							 .98300,  1.09150, 1.20004, 1.30818, 1.41613, 1.52408, 1.63159, 1.73965, 1.84696, 1.95445, 2.06177, 2.16829};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 2;
	XYFitCurve::initFitData(fitData);
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	const int np = fitData.paramNames.size();
	QCOMPARE(np, 3);

	QCOMPARE(fitResult.paramValues.at(0), 0.673565789473684e-3);
	QCOMPARE(fitResult.errorValues.at(0), 0.107938612033077e-3);
	QCOMPARE(fitResult.paramValues.at(1), 0.732059160401003e-6);
	QCOMPARE(fitResult.errorValues.at(1), 0.157817399981659e-9);
	QCOMPARE(fitResult.paramValues.at(2), -0.316081871345029e-14);
	QCOMPARE(fitResult.errorValues.at(2), 0.486652849992036e-16);

	QCOMPARE(fitResult.rsd, 0.205177424076185e-3);
	QCOMPARE(fitResult.rsquare, 0.999999900178537);
	QCOMPARE(fitResult.sse, 0.155761768796992e-5);
	QCOMPARE(fitResult.rms, 0.420977753505385e-7);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 185330865.884471
	FuzzyCompare(fitResult.fdist_F, 185330865.995752, 1.e-9);
}

void FitTest::testLinearNoInt1() {
	// NIST data for NoInt1 dataset
	QVector<int> xData = {60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70};
	QVector<int> yData = {130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Integer);
	yDataColumn.replaceInteger(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*x");
	fitData.paramNames << QStringLiteral("b1");
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 1.;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max();
	// fitData.eps = 1.e-15;
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 1);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 2.07438016513166
	FuzzyCompare(fitResult.paramValues.at(0), 2.07438016528926, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.0165289256079047
	FuzzyCompare(fitResult.errorValues.at(0), 0.165289256198347e-1, 1.e-9);

	QCOMPARE(fitResult.rsd, 3.56753034006338);
	QCOMPARE(fitResult.sse, 127.272727272727);
	QCOMPARE(fitResult.rms, 12.7272727272727);
	QCOMPARE(fitResult.rsquare, 0.999365492298663);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 15750.2500000027
	QCOMPARE(fitResult.fdist_F, 15750.25);
}

void FitTest::testLinearNoInt1_2() {
	// NIST data for NoInt1 dataset
	QVector<int> xData = {60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70};
	QVector<int> yData = {130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Integer);
	yDataColumn.replaceInteger(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 1;
	XYFitCurve::initFitData(fitData);
	fitData.paramStartValues[0] = 0;
	fitData.paramFixed[0] = true;
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	const int np = fitData.paramNames.size();
	QCOMPARE(np, 2);

	QCOMPARE(fitResult.paramValues.at(0), 0.);
	QCOMPARE(fitResult.paramValues.at(1), 2.07438016528926);
	QCOMPARE(fitResult.errorValues.at(1), 0.165289256198347e-1);

	QCOMPARE(fitResult.rsd, 3.56753034006338);
	QCOMPARE(fitResult.sse, 127.272727272727);
	QCOMPARE(fitResult.rms, 12.7272727272727);
	QCOMPARE(fitResult.rsquare, 0.999365492298663);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 15760.25
	QCOMPARE(fitResult.fdist_F, 15750.25);
}

void FitTest::testLinearNoInt2() {
	// NIST data for NoInt2 dataset
	QVector<int> xData = {4, 5, 6};
	QVector<int> yData = {3, 4, 4};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Integer);
	yDataColumn.replaceInteger(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("c * x");
	fitData.paramNames << QStringLiteral("c");
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 1.;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max();
	// fitData.eps = 1.e-15;
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 1);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 0.727272727152573
	FuzzyCompare(fitResult.paramValues.at(0), 0.727272727272727, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.0420827316561797
	FuzzyCompare(fitResult.errorValues.at(0), 0.420827318078432E-01, 1.e-8);

	QCOMPARE(fitResult.rsd, 0.369274472937998);
	QCOMPARE(fitResult.sse, 0.272727272727273);
	QCOMPARE(fitResult.rms, 0.136363636363636);
	// can not detect that intercept is zero for a custom linear model
	DEBUG(std::setprecision(15) << fitResult.rsquare); // result: 0.590909090909091
	//	QCOMPARE(fitResult.rsquare, 0.993348115299335);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 2.88888888888889
	//	FuzzyCompare(fitResult.fdist_F, 298.666666666667, 1.);
}

void FitTest::testLinearNoInt2_2() {
	// NIST data for NoInt2 dataset
	QVector<int> xData = {4, 5, 6};
	QVector<int> yData = {3, 4, 4};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Integer);
	yDataColumn.replaceInteger(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 1;
	XYFitCurve::initFitData(fitData);
	fitData.paramStartValues[0] = 0;
	fitData.paramFixed[0] = true;
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	const int np = fitData.paramNames.size();
	QCOMPARE(np, 2);

	QCOMPARE(fitResult.paramValues.at(0), 0.);
	QCOMPARE(fitResult.paramValues.at(1), 0.727272727272727);
	QCOMPARE(fitResult.errorValues.at(1), 0.420827318078432e-1);

	QCOMPARE(fitResult.rsd, 0.369274472937998);
	QCOMPARE(fitResult.sse, 0.272727272727273);
	QCOMPARE(fitResult.rms, 0.136363636363636);
	QCOMPARE(fitResult.rsquare, 0.993348115299335);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 300.666666666667
	QCOMPARE(fitResult.fdist_F, 298.666666666667);
}

void FitTest::testLinearFilip() {
	// NIST data for Filip dataset
	QVector<double> xData = {-6.860120914, -4.324130045, -4.358625055, -4.358426747, -6.955852379, -6.661145254, -6.355462942, -6.118102026, -7.115148017,
							 -6.815308569, -6.519993057, -6.204119983, -5.853871964, -6.109523091, -5.79832982,	 -5.482672118, -5.171791386, -4.851705903,
							 -4.517126416, -4.143573228, -3.709075441, -3.499489089, -6.300769497, -5.953504836, -5.642065153, -5.031376979, -4.680685696,
							 -4.329846955, -3.928486195, -8.56735134,  -8.363211311, -8.107682739, -7.823908741, -7.522878745, -7.218819279, -6.920818754,
							 -6.628932138, -6.323946875, -5.991399828, -8.781464495, -8.663140179, -8.473531488, -8.247337057, -7.971428747, -7.676129393,
							 -7.352812702, -7.072065318, -6.774174009, -6.478861916, -6.159517513, -6.835647144, -6.53165267,  -6.224098421, -5.910094889,
							 -5.598599459, -5.290645224, -4.974284616, -4.64454848,	 -4.290560426, -3.885055584, -3.408378962, -3.13200249,	 -8.726767166,
							 -8.66695597,  -8.511026475, -8.165388579, -7.886056648, -7.588043762, -7.283412422, -6.995678626, -6.691862621, -6.392544977,
							 -6.067374056, -6.684029655, -6.378719832, -6.065855188, -5.752272167, -5.132414673, -4.811352704, -4.098269308, -3.66174277,
							 -3.2644011};
	QVector<double> yData = {0.8116, 0.9072, 0.9052, 0.9039, 0.8053, 0.8377, 0.8667, 0.8809, 0.7975, 0.8162, 0.8515, 0.8766, 0.8885, 0.8859,
							 0.8959, 0.8913, 0.8959, 0.8971, 0.9021, 0.909,	 0.9139, 0.9199, 0.8692, 0.8872, 0.89,	 0.891,	 0.8977, 0.9035,
							 0.9078, 0.7675, 0.7705, 0.7713, 0.7736, 0.7775, 0.7841, 0.7971, 0.8329, 0.8641, 0.8804, 0.7668, 0.7633, 0.7678,
							 0.7697, 0.77,	 0.7749, 0.7796, 0.7897, 0.8131, 0.8498, 0.8741, 0.8061, 0.846,	 0.8751, 0.8856, 0.8919, 0.8934,
							 0.894,	 0.8957, 0.9047, 0.9129, 0.9209, 0.9219, 0.7739, 0.7681, 0.7665, 0.7703, 0.7702, 0.7761, 0.7809, 0.7961,
							 0.8253, 0.8602, 0.8809, 0.8301, 0.8664, 0.8834, 0.8898, 0.8964, 0.8963, 0.9074, 0.9119, 0.9228};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 10;
	fitData.eps = 1.e-8;
	XYFitCurve::initFitData(fitData);
	const int np = fitData.paramNames.size();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 11);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: -1467.48962615175
	FuzzyCompare(fitResult.paramValues.at(0), -1467.48961422980, 2.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 298.084524514884
	FuzzyCompare(fitResult.errorValues.at(0), 298.084530995537, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -2772.1796150428
	FuzzyCompare(fitResult.paramValues.at(1), -2772.17959193342, 2.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 559.779853249694
	FuzzyCompare(fitResult.errorValues.at(1), 559.779865474950, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: -2316.37110148409
	FuzzyCompare(fitResult.paramValues.at(2), -2316.37108160893, 2.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 466.477561928144
	FuzzyCompare(fitResult.errorValues.at(2), 466.477572127796, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result: -1127.97395097195
	FuzzyCompare(fitResult.paramValues.at(3), -1127.97394098372, 2.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result: 227.204269523115
	FuzzyCompare(fitResult.errorValues.at(3), 227.204274477751, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(4)); // result: -354.478236951913
	FuzzyCompare(fitResult.paramValues.at(4), -354.478233703349, 2.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(4)); // result: 71.6478645361214
	FuzzyCompare(fitResult.errorValues.at(4), 71.6478660875927, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(5)); // result: -75.1242024539908
	FuzzyCompare(fitResult.paramValues.at(5), -75.1242017393757, 2.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(5)); // result: 15.289717547564
	FuzzyCompare(fitResult.errorValues.at(5), 15.2897178747400, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(6)); // result: -10.875318143236
	FuzzyCompare(fitResult.paramValues.at(6), -10.8753180355343, 2.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(6)); // result: 2.23691155110776
	FuzzyCompare(fitResult.errorValues.at(6), 2.23691159816033, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(7)); // result: -1.06221499687347
	FuzzyCompare(fitResult.paramValues.at(7), -1.06221498588947, 2.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(7)); // result: 0.221624317377432
	FuzzyCompare(fitResult.errorValues.at(7), 0.221624321934227, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(8)); // result: -0.0670191161850038
	FuzzyCompare(fitResult.paramValues.at(8), -0.670191154593408E-01, 2.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(8)); // result: 0.0142363760310402
	FuzzyCompare(fitResult.errorValues.at(8), 0.142363763154724E-01, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(9)); // result: -0.00246781081080665
	FuzzyCompare(fitResult.paramValues.at(9), -0.246781078275479E-02, 2.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(9)); // result: 0.000535617398555022
	FuzzyCompare(fitResult.errorValues.at(9), 0.535617408889821E-03, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(10)); // result: -4.02962529900222e-05
	FuzzyCompare(fitResult.paramValues.at(10), -0.402962525080404E-04, 2.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(10)); // result: 8.96632820770946e-06
	FuzzyCompare(fitResult.errorValues.at(10), 0.896632837373868E-05, 1.e-7);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.00334801050105949
	FuzzyCompare(fitResult.rsd, 0.334801051324544E-02, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.rsquare); // result: 0.996727416209443
	FuzzyCompare(fitResult.rsquare, 0.996727416185620, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.00079585137637953
	FuzzyCompare(fitResult.sse, 0.795851382172941E-03, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.rms); // result: 1.12091743152047e-05
	FuzzyCompare(fitResult.rms, 0.112091743968020E-04, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 2162.43960297897
	FuzzyCompare(fitResult.fdist_F, 2162.43954511489, 1.e-7);
}

void FitTest::testLinearWampler1() {
	// NIST data for Wampler1 dataset
	QVector<int> xData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	QVector<int> yData = {1,	  6,	  63,	  364,	  1365,	  3906,	   9331,	19608,	 37449,	  66430,  111111,
						  177156, 271453, 402234, 579195, 813616, 1118481, 1508598, 2000719, 2613660, 3368421};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Integer);
	yDataColumn.replaceInteger(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 5;
	XYFitCurve::initFitData(fitData);
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	const int np = fitData.paramNames.size();
	QCOMPARE(np, 6);

	for (int i = 0; i < np; i++) {
		const double paramValue = fitResult.paramValues.at(i);
		const double errorValue = fitResult.errorValues.at(i);
		QCOMPARE(paramValue, 1.0);
		QCOMPARE(errorValue, 0.0);
	}

	QCOMPARE(fitResult.rsd, 0.0);
	QCOMPARE(fitResult.rsquare, 1.0);
	QCOMPARE(fitResult.sse, 0.0);
	QCOMPARE(fitResult.rms, 0.0);
	QVERIFY(std::isinf(fitResult.fdist_F));
}

void FitTest::testLinearWampler1_custom() {
	// NIST data for Wampler1 dataset
	QVector<int> xData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	QVector<int> yData = {1,	  6,	  63,	  364,	  1365,	  3906,	   9331,	19608,	 37449,	  66430,  111111,
						  177156, 271453, 402234, 579195, 813616, 1118481, 1508598, 2000719, 2613660, 3368421};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Integer);
	yDataColumn.replaceInteger(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("B0+B1*x+B2*x^2+B3*x^3+B4*x^4+B5*x^5");
	fitData.paramNames << QStringLiteral("B0") << QStringLiteral("B1") << QStringLiteral("B2") << QStringLiteral("B3") << QStringLiteral("B4")
					   << QStringLiteral("B5");
	// fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	// start values
	fitData.paramStartValues << 1. << 1. << 1. << 1. << 1. << 1.;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 6);

	for (int i = 0; i < np; i++) {
		const double paramValue = fitResult.paramValues.at(i);
		const double errorValue = fitResult.errorValues.at(i);
		DEBUG(std::setprecision(15) << paramValue << ' ' << paramValue - 1.0);
		FuzzyCompare(paramValue, 1.0, 2.e-12);
		DEBUG(std::setprecision(15) << errorValue);
		FuzzyCompare(errorValue, 0.0, 5.e-12);
	}

	DEBUG(std::setprecision(15) << fitResult.rsd);
	FuzzyCompare(fitResult.rsd, 0.0, 5.e-12);
	QCOMPARE(fitResult.rsquare, 1.0);
	QCOMPARE(fitResult.sse, 0.0);
	QCOMPARE(fitResult.rms, 0.0);
	QVERIFY(std::isinf(fitResult.fdist_F));
}

void FitTest::testLinearWampler2() {
	// NIST data for Wampler2 dataset
	QVector<int> xData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	QVector<double> yData = {1.00000, 1.11111, 1.24992,	 1.42753,  1.65984,	 1.96875,  2.38336,	 2.94117,  3.68928,	 4.68559, 6.00000,
							 7.71561, 9.92992, 12.75603, 16.32384, 20.78125, 26.29536, 33.05367, 41.26528, 51.16209, 63.00000};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 5;
	XYFitCurve::initFitData(fitData);
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	qDebug() << "STATUS " << fitResult.status;
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	const int np = fitData.paramNames.size();
	QCOMPARE(np, 6);

	for (int i = 0; i < np; i++) {
		DEBUG(std::setprecision(15) << fitResult.paramValues.at(i));
	}
	QCOMPARE(fitResult.paramValues.at(0), 1.0);
	QCOMPARE(fitResult.paramValues.at(1), 0.1);
	QCOMPARE(fitResult.paramValues.at(2), 0.01);
	QCOMPARE(fitResult.paramValues.at(3), 0.001);
	QCOMPARE(fitResult.paramValues.at(4), 0.0001);
	QCOMPARE(fitResult.paramValues.at(5), 0.00001);
	for (int i = 0; i < np; i++)
		printAndCheck(fitResult.errorValues.at(i), 0., 1.e-14);

	printAndCheck(fitResult.rsd, 0., 1.e-14);
	QCOMPARE(fitResult.rsquare, 1.);
	printAndCheck(fitResult.sse, 0., 1.e-15);
	printAndCheck(fitResult.rms, 0., 1.e-15);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 2.44385217688297e+32
	QVERIFY(fitResult.fdist_F > 1.e+32);
}

void FitTest::testLinearWampler2_custom() {
	// NIST data for Wampler2 dataset
	QVector<int> xData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	QVector<double> yData = {1.00000, 1.11111, 1.24992,	 1.42753,  1.65984,	 1.96875,  2.38336,	 2.94117,  3.68928,	 4.68559, 6.00000,
							 7.71561, 9.92992, 12.75603, 16.32384, 20.78125, 26.29536, 33.05367, 41.26528, 51.16209, 63.00000};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("B0+B1*x+B2*x^2+B3*x^3+B4*x^4+B5*x^5");
	fitData.paramNames << QStringLiteral("B0") << QStringLiteral("B1") << QStringLiteral("B2") << QStringLiteral("B3") << QStringLiteral("B4")
					   << QStringLiteral("B5");
	// fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	// start values
	fitData.paramStartValues << 1. << 0.1 << 1.e-2 << 1.e-3 << 1.e-4 << 1.e-5;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 6);

	for (int i = 0; i < np; i++)
		printAndCheck(fitResult.errorValues.at(i), 0., 5.e-15);
	printAndCheck(fitResult.paramValues.at(0), 1.0, 1.e-15);
	printAndCheck(fitResult.paramValues.at(1), 0.1, 5.e-14);
	printAndCheck(fitResult.paramValues.at(2), 1.e-2, 2.e-13);
	printAndCheck(fitResult.paramValues.at(3), 1.e-3, 5.e-13);
	printAndCheck(fitResult.paramValues.at(4), 1.e-4, 2.e-13);
	printAndCheck(fitResult.paramValues.at(5), 1.e-5, 5.e-14);

	printAndCheck(fitResult.rsd, 0., 5.e-15);
	QCOMPARE(fitResult.rsquare, 1.);
	DEBUG(std::setprecision(15) << fitResult.sse); // result:
	QCOMPARE(fitResult.sse, 0.);
	DEBUG(std::setprecision(15) << fitResult.rms); // result:
	QCOMPARE(fitResult.rms, 0.);
	QVERIFY(std::isinf(fitResult.fdist_F));
}

void FitTest::testLinearWampler3() {
	// NIST data for Wampler3 dataset
	QVector<int> xData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	QVector<double> yData = {760.,	  -2042.,  2111.,	-1684.,	 3888.,	  1858.,	11379.,	  17560.,	39287.,	  64382.,  113159.,
							 175108., 273291., 400186., 581243., 811568., 1121004., 1506550., 2002767., 2611612., 3369180.};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 5;
	XYFitCurve::initFitData(fitData);
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	const int np = fitData.paramNames.size();
	QCOMPARE(np, 6);

	for (int i = 0; i < np; i++) {
		const double paramValue = fitResult.paramValues.at(i);
		QCOMPARE(paramValue, 1.0);
	}
	QCOMPARE(fitResult.errorValues.at(0), 2152.32624678170);
	QCOMPARE(fitResult.errorValues.at(1), 2363.55173469681);
	QCOMPARE(fitResult.errorValues.at(2), 779.343524331583);
	QCOMPARE(fitResult.errorValues.at(3), 101.475507550350);
	QCOMPARE(fitResult.errorValues.at(4), 5.64566512170752);
	QCOMPARE(fitResult.errorValues.at(5), 0.112324854679312);

	QCOMPARE(fitResult.rsd, 2360.14502379268);
	QCOMPARE(fitResult.rsquare, 0.999995559025820);
	QCOMPARE(fitResult.sse, 83554268.0000000);
	QCOMPARE(fitResult.rms, 5570284.53333333);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 675524.458247789
	FuzzyCompare(fitResult.fdist_F, 675524.458240122, 1.e-7);
}

void FitTest::testLinearWampler3_custom() {
	// enableInfoTrace(true);

	// NIST data for Wampler3 dataset
	QVector<int> xData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	QVector<double> yData = {760.,	  -2042.,  2111.,	-1684.,	 3888.,	  1858.,	11379.,	  17560.,	39287.,	  64382.,  113159.,
							 175108., 273291., 400186., 581243., 811568., 1121004., 1506550., 2002767., 2611612., 3369180.};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("B0+B1*x+B2*x^2+B3*x^3+B4*x^4+B5*x^5");
	fitData.paramNames << QStringLiteral("B0") << QStringLiteral("B1") << QStringLiteral("B2") << QStringLiteral("B3") << QStringLiteral("B4")
					   << QStringLiteral("B5");
	// fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	// start values
	fitData.paramStartValues << 1. << 1. << 1. << 1. << 1. << 1.;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 6);

	printAndCheck(fitResult.paramValues.at(0), 1., 2.e-6);
	printAndCheck(fitResult.paramValues.at(1), 1., 1.e-4);
	printAndCheck(fitResult.paramValues.at(2), 1., 2.e-5);
	printAndCheck(fitResult.paramValues.at(3), 1., 2.e-6);
	printAndCheck(fitResult.paramValues.at(4), 1., 1.e-7);
	printAndCheck(fitResult.paramValues.at(5), 1., 1.e-9);

	printAndCheck(fitResult.errorValues.at(0), 2152.32624678170, 2.e-8);
	printAndCheck(fitResult.errorValues.at(1), 2363.55173469681, 2.e-8);
	printAndCheck(fitResult.errorValues.at(2), 779.343524331583, 5.e-8);
	printAndCheck(fitResult.errorValues.at(3), 101.475507550350, 2.e-8);
	printAndCheck(fitResult.errorValues.at(4), 5.64566512170752, 5.e-8);
	printAndCheck(fitResult.errorValues.at(5), 0.112324854679312, 5.e-8);

	printAndCheck(fitResult.rsd, 2360.14502379268, 1.e-14);
	printAndCheck(fitResult.rsquare, 0.999995559025820, 1.e-15);
	printAndCheck(fitResult.sse, 83554268.0000000, 1.e-14);
	printAndCheck(fitResult.rms, 5570284.53333333, 1.e-14);
	printAndCheck(fitResult.fdist_F, 675524.458240122, 2.e-11);
}

void FitTest::testLinearWampler4() {
	// NIST data for Wampler4 dataset
	QVector<int> xData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	QVector<int> yData = {75901,  -204794, 204863, -204436, 253665, -200894, 214131,  -185192, 221249,	-138370, 315911,
						  -27644, 455253,  197434, 783995,	608816, 1370781, 1303798, 2205519, 2408860, 3444321};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Integer);
	yDataColumn.replaceInteger(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 5;
	XYFitCurve::initFitData(fitData);
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	const int np = fitData.paramNames.size();
	QCOMPARE(np, 6);

	printAndCheck(fitResult.paramValues.at(0), 1.0, 3.e-9);
	printAndCheck(fitResult.paramValues.at(1), 1.0, 5.e-9);
	printAndCheck(fitResult.paramValues.at(2), 1.0, 2.e-9);
	printAndCheck(fitResult.paramValues.at(3), 1.0, 1.e-9);
	printAndCheck(fitResult.paramValues.at(4), 1.0, 2.e-11);
	printAndCheck(fitResult.paramValues.at(5), 1.0, 1.e-12);

	QCOMPARE(fitResult.errorValues.at(0), 215232.624678170);
	QCOMPARE(fitResult.errorValues.at(1), 236355.173469681);
	QCOMPARE(fitResult.errorValues.at(2), 77934.3524331583);
	QCOMPARE(fitResult.errorValues.at(3), 10147.5507550350);
	QCOMPARE(fitResult.errorValues.at(4), 564.566512170752);
	QCOMPARE(fitResult.errorValues.at(5), 11.2324854679312);

	QCOMPARE(fitResult.rsd, 236014.502379268);
	QCOMPARE(fitResult.rsquare, 0.957478440825662);
	QCOMPARE(fitResult.sse, 835542680000.000);
	QCOMPARE(fitResult.rms, 55702845333.3333);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 67.5524458240122
	QCOMPARE(fitResult.fdist_F, 67.5524458240122);
}

void FitTest::testLinearWampler4_custom() {
	// NIST data for Wampler4 dataset
	QVector<int> xData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	QVector<int> yData = {75901,  -204794, 204863, -204436, 253665, -200894, 214131,  -185192, 221249,	-138370, 315911,
						  -27644, 455253,  197434, 783995,	608816, 1370781, 1303798, 2205519, 2408860, 3444321};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Integer);
	yDataColumn.replaceInteger(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("B0+B1*x+B2*x^2+B3*x^3+B4*x^4+B5*x^5");
	fitData.paramNames << QStringLiteral("B0") << QStringLiteral("B1") << QStringLiteral("B2") << QStringLiteral("B3") << QStringLiteral("B4")
					   << QStringLiteral("B5");
	// fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	// start values
	fitData.paramStartValues << 1. << 1. << 1. << 1. << 1. << 1.;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 6);

	QCOMPARE(fitResult.paramValues.at(0), 1.0);
	QCOMPARE(fitResult.paramValues.at(1), 1.0);
	QCOMPARE(fitResult.paramValues.at(2), 1.0);
	QCOMPARE(fitResult.paramValues.at(3), 1.0);
	QCOMPARE(fitResult.paramValues.at(4), 1.0);
	QCOMPARE(fitResult.paramValues.at(5), 1.0);

	printAndCheck(fitResult.errorValues.at(0), 215232.624678170, 5.e-9);
	printAndCheck(fitResult.errorValues.at(1), 236355.173469681, 1.e-7);
	printAndCheck(fitResult.errorValues.at(2), 77934.3524331583, 1.e-7);
	printAndCheck(fitResult.errorValues.at(3), 10147.5507550350, 1.e-7);
	printAndCheck(fitResult.errorValues.at(4), 564.566512170752, 1.e-7);
	printAndCheck(fitResult.errorValues.at(5), 11.2324854679312, 1.e-7);

	QCOMPARE(fitResult.rsd, 236014.502379268);
	QCOMPARE(fitResult.rsquare, 0.957478440825662);
	QCOMPARE(fitResult.sse, 835542680000.000);
	QCOMPARE(fitResult.rms, 55702845333.3333);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 67.5524458240122
	QCOMPARE(fitResult.fdist_F, 67.5524458240122);
}

void FitTest::testLinearWampler5() {
	// NIST data for Wampler5 dataset
	QVector<int> xData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	QVector<int> yData = {7590001,	 -20479994, 20480063,  -20479636, 25231365,	 -20476094, 20489331,  -20460392, 18417449,	 -20413570, 20591111,
						  -20302844, 18651453,	-20077766, 21059195,  -19666384, 26348481,	-18971402, 22480719,  -17866340, 10958421};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Integer);
	yDataColumn.replaceInteger(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 5;
	XYFitCurve::initFitData(fitData);
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	const int np = fitData.paramNames.size();
	QCOMPARE(np, 6);

	for (int i = 0; i < np; i++) {
		const double paramValue = fitResult.paramValues.at(i);
		DEBUG(std::setprecision(15) << paramValue);
		FuzzyCompare(paramValue, 1.0, 3.e-7);
	}
	QCOMPARE(fitResult.errorValues.at(0), 21523262.4678170);
	QCOMPARE(fitResult.errorValues.at(1), 23635517.3469681);
	QCOMPARE(fitResult.errorValues.at(2), 7793435.24331583);
	QCOMPARE(fitResult.errorValues.at(3), 1014755.07550350);
	QCOMPARE(fitResult.errorValues.at(4), 56456.6512170752);
	QCOMPARE(fitResult.errorValues.at(5), 1123.24854679312);

	QCOMPARE(fitResult.rsd, 23601450.2379268);
	QCOMPARE(fitResult.rsquare, 0.224668921574940E-02);
	QCOMPARE(fitResult.sse, 0.835542680000000E+16);
	QCOMPARE(fitResult.rms, 557028453333333.);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 0.00675524458240069
	QCOMPARE(fitResult.fdist_F, 0.675524458240122E-02);
}

void FitTest::testLinearWampler5_custom() {
	// NIST data for Wampler5 dataset
	QVector<int> xData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
	QVector<int> yData = {7590001,	 -20479994, 20480063,  -20479636, 25231365,	 -20476094, 20489331,  -20460392, 18417449,	 -20413570, 20591111,
						  -20302844, 18651453,	-20077766, 21059195,  -19666384, 26348481,	-18971402, 22480719,  -17866340, 10958421};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Integer);
	yDataColumn.replaceInteger(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("B0+B1*x+B2*x^2+B3*x^3+B4*x^4+B5*x^5");
	fitData.paramNames << QStringLiteral("B0") << QStringLiteral("B1") << QStringLiteral("B2") << QStringLiteral("B3") << QStringLiteral("B4")
					   << QStringLiteral("B5");
	// fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	// start values
	fitData.paramStartValues << 1. << 1. << 1. << 1. << 1. << 1.;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 6);

	for (int i = 0; i < np; i++)
		QCOMPARE(fitResult.paramValues.at(i), 1.0);

	printAndCheck(fitResult.errorValues.at(0), 21523262.4678170, 5.e-9);
	printAndCheck(fitResult.errorValues.at(1), 23635517.3469681, 1.e-7);
	printAndCheck(fitResult.errorValues.at(2), 7793435.24331583, 1.e-7);
	printAndCheck(fitResult.errorValues.at(3), 1014755.07550350, 1.e-7);
	printAndCheck(fitResult.errorValues.at(4), 56456.6512170752, 1.e-7);
	printAndCheck(fitResult.errorValues.at(5), 1123.24854679312, 1.e-7);

	QCOMPARE(fitResult.rsd, 23601450.2379268);
	QCOMPARE(fitResult.rsquare, 0.224668921574940E-02);
	QCOMPARE(fitResult.sse, 0.835542680000000E+16);
	QCOMPARE(fitResult.rms, 557028453333333.);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 0.00675524458240069
	QCOMPARE(fitResult.fdist_F, 0.675524458240122E-02);
}

/////////////////////////////////////////

// taken from https://en.wikipedia.org/wiki/Ordinary_least_squares
void FitTest::testLinearWP_OLS() {
	// data from  The World Almanac and Book of Facts, 1975
	QVector<double> xData = {1.47, 1.50, 1.52, 1.55, 1.57, 1.60, 1.63, 1.65, 1.68, 1.70, 1.73, 1.75, 1.78, 1.80, 1.83};
	QVector<double> yData = {52.21, 53.12, 54.48, 55.84, 57.20, 58.57, 59.93, 61.29, 63.11, 64.47, 66.28, 68.10, 69.92, 72.19, 74.46};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 2;
	XYFitCurve::initFitData(fitData);
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	const int np = fitData.paramNames.size();
	QCOMPARE(np, 3);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 128.812803578436
	FuzzyCompare(fitResult.paramValues.at(0), 128.8128, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 16.3082821390367
	FuzzyCompare(fitResult.errorValues.at(0), 16.3083, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.tdist_tValues.at(0)); // result: 7.89861264848368
	FuzzyCompare(fitResult.tdist_tValues.at(0), 7.8986, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.tdist_pValues.at(0)); // result: 4.28330815316414e-06
	FuzzyCompare(fitResult.tdist_pValues.at(0), 0., 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -143.16202286476
	FuzzyCompare(fitResult.paramValues.at(1), -143.1620, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 19.8331710430895
	FuzzyCompare(fitResult.errorValues.at(1), 19.8332, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.tdist_tValues.at(1)); // result: -7.21831231897945
	FuzzyCompare(fitResult.tdist_tValues.at(1), -7.2183, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.tdist_pValues.at(1)); // result: 1.05970640905074e-05
	FuzzyCompare(fitResult.tdist_pValues.at(1), 0., 2.e-5);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 61.9603254424724
	FuzzyCompare(fitResult.paramValues.at(2), 61.9603, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 6.00842899301227
	FuzzyCompare(fitResult.errorValues.at(2), 6.0084, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.tdist_tValues.at(2)); // result: 10.3122339490958
	FuzzyCompare(fitResult.tdist_tValues.at(2), 10.3122, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.tdist_pValues.at(2)); // result: 2.56647515320682e-07
	FuzzyCompare(fitResult.tdist_pValues.at(2), 0., 1.e-6);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.25158650082898
	FuzzyCompare(fitResult.rsd, 0.2516, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.rsquare); // result: 0.998904558436583
	FuzzyCompare(fitResult.rsquare, 0.9989, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.rsquareAdj); // result: 0.99860580164656
	FuzzyCompare(fitResult.rsquareAdj, 0.9987, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.759549208792447
	FuzzyCompare(fitResult.sse, 0.7595, 1.e-4);
	//	QCOMPARE(fitResult.rms, ???);	// result: 0.0632958
	DEBUG(std::setprecision(15) << fitResult.chisq_p); // result: 0.999996987409119
	//	FuzzyCompare(fitResult.chisq_p, ???, 1.e-8);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 5471.2433330734
	FuzzyCompare(fitResult.fdist_F, 5471.2, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0.0
	QCOMPARE(fitResult.fdist_p, 0.0);
	DEBUG(std::setprecision(15) << fitResult.logLik); // result: 1.0890247702592
	FuzzyCompare(fitResult.logLik, 1.0890, 3.e-5);
	DEBUG(std::setprecision(15) << fitResult.aic); // result: 5.82195045948161
	// not reproducable
	//	FuzzyCompare(fitResult.aic, 0.2548, 2.e-6);
	DEBUG(std::setprecision(15) << fitResult.bic); // result: 8.65415126389045
	// not reproducable
	//	FuzzyCompare(fitResult.bic, 0.3964, 2.e-6);
}

// from http://sia.webpopix.org/polynomialRegression1.html
void FitTest::testLinearR_lm2() {
	QVector<int> xData = {4,  4,  7,  7,  8,  9,  10, 10, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15,
						  15, 16, 16, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 20, 20, 20, 20, 20, 22, 23, 24, 24, 24, 24, 25};
	QVector<int> yData = {2,  10, 4,  22, 16, 10, 18, 26, 34, 17, 28, 14, 20, 24, 28, 26, 34, 34, 46, 26, 36, 60, 80, 20,  26,
						  54, 32, 40, 32, 40, 50, 42, 56, 76, 84, 36, 46, 68, 32, 48, 52, 56, 64, 66, 54, 70, 92, 93, 120, 85};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Integer);
	xDataColumn.replaceInteger(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Integer);
	yDataColumn.replaceInteger(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 2;
	XYFitCurve::initFitData(fitData);
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	const int np = fitData.paramNames.size();
	QCOMPARE(np, 3);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 2.47013778506623
	FuzzyCompare(fitResult.paramValues.at(0), 2.47014, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 14.8171647250237
	FuzzyCompare(fitResult.errorValues.at(0), 14.81716, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.tdist_tValues.at(0)); // result: 0.16670785746848
	FuzzyCompare(fitResult.tdist_tValues.at(0), 0.167, 2.e-3);
	DEBUG(std::setprecision(15) << fitResult.tdist_pValues.at(0)); // result: 0.868315075848582
	FuzzyCompare(fitResult.tdist_pValues.at(0), 0.868, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.913287614242592
	FuzzyCompare(fitResult.paramValues.at(1), 0.91329, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.03422044231195
	FuzzyCompare(fitResult.errorValues.at(1), 2.03422, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.tdist_tValues.at(1)); // result: 0.448961968548804
	FuzzyCompare(fitResult.tdist_tValues.at(1), 0.449, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.tdist_pValues.at(1)); // result: 0.655522449402813
	FuzzyCompare(fitResult.tdist_pValues.at(1), 0.656, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 0.0999593020698437
	FuzzyCompare(fitResult.paramValues.at(2), 0.09996, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.0659682106823392
	FuzzyCompare(fitResult.errorValues.at(2), 0.06597, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.tdist_tValues.at(2)); // result: 1.5152647166858
	FuzzyCompare(fitResult.tdist_tValues.at(2), 1.515, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.tdist_pValues.at(2)); // result: 0.136402432803739
	FuzzyCompare(fitResult.tdist_pValues.at(2), 0.136, 3.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 15.1760701243277
	FuzzyCompare(fitResult.rsd, 15.18, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.rsquare); // result: 0.66733081652621
	FuzzyCompare(fitResult.rsquare, 0.6673, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.rsquareAdj); // result: 0.645635000212702
	DEBUG(std::setprecision(15) << 1. - (1. - fitResult.rsquare) * (50. - 1.) / (50. - np)); // result: 0.65317468105924
	// reference calculates 1-(1-R^2)(n-1)/(n-p)
	FuzzyCompare(1. - (1. - fitResult.rsquare) * (50. - 1.) / (50. - np), 0.6532, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 10824.71590767
	FuzzyCompare(fitResult.sse, 10825, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.rms); // result: 230.313104418511
	//	QCOMPARE(fitResult.rms, ???);
	DEBUG(std::setprecision(15) << fitResult.logLik); // result: -205.386034235309
	FuzzyCompare(fitResult.logLik, -205.386, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.chisq_p); // result:
	//	FuzzyCompare(fitResult.chisq_p, ???, 1.e-8);
	DEBUG(std::setprecision(15) << fitResult.fdist_F); // result:
	// reference calculates sst/rms/np
	DEBUG(std::setprecision(15) << fitResult.sst / fitResult.rms / np); // result: 47.0938320858956
	FuzzyCompare(fitResult.sst / fitResult.rms / np, 47.14, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0
	QCOMPARE(fitResult.fdist_p, 0.); // exact: 5.852e-12
	DEBUG(std::setprecision(15) << fitResult.aic); // result: 418.772068470618
	FuzzyCompare(fitResult.aic, 418.7721, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.bic); // result: 426.42016049233
	FuzzyCompare(fitResult.bic, 426.4202, 1.e-7);
}

// ##############################################################################
// #############  non-linear regression with NIST datasets  #####################
// ##############################################################################

void FitTest::testNonLinearMisra1a() {
	// NIST data for Misra1a dataset
	QVector<double> xData = {77.6E0, 114.9E0, 141.1E0, 190.8E0, 239.9E0, 289.0E0, 332.8E0, 378.4E0, 434.8E0, 477.3E0, 536.8E0, 593.1E0, 689.1E0, 760.0E0};
	QVector<double> yData = {10.07E0, 14.73E0, 17.94E0, 23.93E0, 29.61E0, 35.18E0, 40.02E0, 44.82E0, 50.76E0, 55.05E0, 61.01E0, 66.40E0, 75.47E0, 81.78E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*(1-exp(-b2*x))");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 500. << 0.0001;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 238.942305251573
	FuzzyCompare(fitResult.paramValues.at(0), 2.3894212918E+02, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 2.70651225218243
	FuzzyCompare(fitResult.errorValues.at(0), 2.7070075241E+00, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000550155958419367
	FuzzyCompare(fitResult.paramValues.at(1), 5.5015643181E-04, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 7.26565480949189e-06
	FuzzyCompare(fitResult.errorValues.at(1), 7.2668688436E-06, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.101878763320394
	FuzzyCompare(fitResult.rsd, 1.0187876330E-01, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.124551388988316
	FuzzyCompare(fitResult.sse, 1.2455138894E-01, 1.e-9);
}

void FitTest::testNonLinearMisra1a_2() {
	// NIST data for Misra1a dataset
	QVector<double> xData = {77.6E0, 114.9E0, 141.1E0, 190.8E0, 239.9E0, 289.0E0, 332.8E0, 378.4E0, 434.8E0, 477.3E0, 536.8E0, 593.1E0, 689.1E0, 760.0E0};
	QVector<double> yData = {10.07E0, 14.73E0, 17.94E0, 23.93E0, 29.61E0, 35.18E0, 40.02E0, 44.82E0, 50.76E0, 55.05E0, 61.01E0, 66.40E0, 75.47E0, 81.78E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*(1-exp(-b2*x))");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 250. << 5.e-4;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 238.942305251573
	FuzzyCompare(fitResult.paramValues.at(0), 2.3894212918E+02, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 2.70651225218243
	FuzzyCompare(fitResult.errorValues.at(0), 2.7070075241E+00, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000550155958419367
	FuzzyCompare(fitResult.paramValues.at(1), 5.5015643181E-04, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 7.26565480949189e-06
	FuzzyCompare(fitResult.errorValues.at(1), 7.2668688436E-06, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.101878763320394
	FuzzyCompare(fitResult.rsd, 1.0187876330E-01, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.124551388988316
	FuzzyCompare(fitResult.sse, 1.2455138894E-01, 1.e-9);
}

void FitTest::testNonLinearMisra1a_3() {
	// NIST data for Misra1a dataset
	QVector<double> xData = {77.6E0, 114.9E0, 141.1E0, 190.8E0, 239.9E0, 289.0E0, 332.8E0, 378.4E0, 434.8E0, 477.3E0, 536.8E0, 593.1E0, 689.1E0, 760.0E0};
	QVector<double> yData = {10.07E0, 14.73E0, 17.94E0, 23.93E0, 29.61E0, 35.18E0, 40.02E0, 44.82E0, 50.76E0, 55.05E0, 61.01E0, 66.40E0, 75.47E0, 81.78E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*(1-exp(-b2*x))");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 2.3894212918E+02 << 5.5015643181E-04;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 238.942305251573
	FuzzyCompare(fitResult.paramValues.at(0), 2.3894212918E+02, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 2.70651225218243
	FuzzyCompare(fitResult.errorValues.at(0), 2.7070075241E+00, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000550155958419367
	FuzzyCompare(fitResult.paramValues.at(1), 5.5015643181E-04, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 7.26565480949189e-06
	FuzzyCompare(fitResult.errorValues.at(1), 7.2668688436E-06, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.101878763320394
	FuzzyCompare(fitResult.rsd, 1.0187876330E-01, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.124551388988316
	FuzzyCompare(fitResult.sse, 1.2455138894E-01, 1.e-9);
}

void FitTest::testNonLinearMisra1b() {
	// NIST data for Misra1b dataset
	QVector<double> xData = {77.6E0, 114.9E0, 141.1E0, 190.8E0, 239.9E0, 289.0E0, 332.8E0, 378.4E0, 434.8E0, 477.3E0, 536.8E0, 593.1E0, 689.1E0, 760.0E0};
	QVector<double> yData = {10.07E0, 14.73E0, 17.94E0, 23.93E0, 29.61E0, 35.18E0, 40.02E0, 44.82E0, 50.76E0, 55.05E0, 61.01E0, 66.40E0, 75.47E0, 81.78E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*(1-1/(1+b2*x/2)^2)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 500. << 0.0001;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 337.99775062098
	FuzzyCompare(fitResult.paramValues.at(0), 3.3799746163E+02, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 3.16358581006192
	FuzzyCompare(fitResult.errorValues.at(0), 3.1643950207E+00, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000390390523934039
	FuzzyCompare(fitResult.paramValues.at(1), 3.9039091287E-04, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 4.25373670682006e-06
	FuzzyCompare(fitResult.errorValues.at(1), 4.2547321834E-06, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0793014720259488
	FuzzyCompare(fitResult.rsd, 7.9301471998E-02, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0754646815857881
	FuzzyCompare(fitResult.sse, 7.5464681533E-02, 1.e-9);
}

void FitTest::testNonLinearMisra1b_2() {
	// NIST data for Misra1b dataset
	QVector<double> xData = {77.6E0, 114.9E0, 141.1E0, 190.8E0, 239.9E0, 289.0E0, 332.8E0, 378.4E0, 434.8E0, 477.3E0, 536.8E0, 593.1E0, 689.1E0, 760.0E0};
	QVector<double> yData = {10.07E0, 14.73E0, 17.94E0, 23.93E0, 29.61E0, 35.18E0, 40.02E0, 44.82E0, 50.76E0, 55.05E0, 61.01E0, 66.40E0, 75.47E0, 81.78E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*(1-1/(1+b2*x/2)^2)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 300. << 2.e-4;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 337.99775062098
	FuzzyCompare(fitResult.paramValues.at(0), 3.3799746163E+02, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 3.16358581006192
	FuzzyCompare(fitResult.errorValues.at(0), 3.1643950207E+00, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000390390523934039
	FuzzyCompare(fitResult.paramValues.at(1), 3.9039091287E-04, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 4.25373670682006e-06
	FuzzyCompare(fitResult.errorValues.at(1), 4.2547321834E-06, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0793014720259488
	FuzzyCompare(fitResult.rsd, 7.9301471998E-02, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0754646815857881
	FuzzyCompare(fitResult.sse, 7.5464681533E-02, 1.e-9);
}

void FitTest::testNonLinearMisra1b_3() {
	// NIST data for Misra1b dataset
	QVector<double> xData = {77.6E0, 114.9E0, 141.1E0, 190.8E0, 239.9E0, 289.0E0, 332.8E0, 378.4E0, 434.8E0, 477.3E0, 536.8E0, 593.1E0, 689.1E0, 760.0E0};
	QVector<double> yData = {10.07E0, 14.73E0, 17.94E0, 23.93E0, 29.61E0, 35.18E0, 40.02E0, 44.82E0, 50.76E0, 55.05E0, 61.01E0, 66.40E0, 75.47E0, 81.78E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*(1-1/(1+b2*x/2)^2)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 3.3799746163E+02 << 3.9039091287E-04;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 337.99775062098
	FuzzyCompare(fitResult.paramValues.at(0), 3.3799746163E+02, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 3.16358581006192
	FuzzyCompare(fitResult.errorValues.at(0), 3.1643950207E+00, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000390390523934039
	FuzzyCompare(fitResult.paramValues.at(1), 3.9039091287E-04, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 4.25373670682006e-06
	FuzzyCompare(fitResult.errorValues.at(1), 4.2547321834E-06, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0793014720259488
	FuzzyCompare(fitResult.rsd, 7.9301471998E-02, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0754646815857881
	FuzzyCompare(fitResult.sse, 7.5464681533E-02, 1.e-9);
}

void FitTest::testNonLinearMisra1c() {
	// NIST data for Misra1c dataset
	QVector<double> xData = {77.6E0, 114.9E0, 141.1E0, 190.8E0, 239.9E0, 289.0E0, 332.8E0, 378.4E0, 434.8E0, 477.3E0, 536.8E0, 593.1E0, 689.1E0, 760.0E0};
	QVector<double> yData = {10.07E0, 14.73E0, 17.94E0, 23.93E0, 29.61E0, 35.18E0, 40.02E0, 44.82E0, 50.76E0, 55.05E0, 61.01E0, 66.40E0, 75.47E0, 81.78E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*(1-1/sqrt(1+2*b2*x))");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 500. << 0.0001;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 636.427904767969
	FuzzyCompare(fitResult.paramValues.at(0), 6.3642725809E+02, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 4.66168062054875
	FuzzyCompare(fitResult.errorValues.at(0), 4.6638326572E+00, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000208136026420746
	FuzzyCompare(fitResult.paramValues.at(1), 2.0813627256E-04, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 1.77209416674174e-06
	FuzzyCompare(fitResult.errorValues.at(1), 1.7728423155E-06, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0584286153041661
	FuzzyCompare(fitResult.rsd, 5.8428615257E-02, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0409668370363468
	FuzzyCompare(fitResult.sse, 4.0966836971E-02, 1.e-8);
}

void FitTest::testNonLinearMisra1c_2() {
	// NIST data for Misra1c dataset
	QVector<double> xData = {77.6E0, 114.9E0, 141.1E0, 190.8E0, 239.9E0, 289.0E0, 332.8E0, 378.4E0, 434.8E0, 477.3E0, 536.8E0, 593.1E0, 689.1E0, 760.0E0};
	QVector<double> yData = {10.07E0, 14.73E0, 17.94E0, 23.93E0, 29.61E0, 35.18E0, 40.02E0, 44.82E0, 50.76E0, 55.05E0, 61.01E0, 66.40E0, 75.47E0, 81.78E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*(1-1/sqrt(1+2*b2*x))");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 600. << 2.e-4;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 636.427904767969
	FuzzyCompare(fitResult.paramValues.at(0), 6.3642725809E+02, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 4.66168062054875
	FuzzyCompare(fitResult.errorValues.at(0), 4.6638326572E+00, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000208136026420746
	FuzzyCompare(fitResult.paramValues.at(1), 2.0813627256E-04, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 1.77209416674174e-06
	FuzzyCompare(fitResult.errorValues.at(1), 1.7728423155E-06, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0584286153041661	(WIN: 0.0584286153252961)
	FuzzyCompare(fitResult.rsd, 5.8428615257E-02, 1.e-8);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0409668370363468
	FuzzyCompare(fitResult.sse, 4.0966836971E-02, 1.e-8);
}

void FitTest::testNonLinearMisra1c_3() {
	// NIST data for Misra1c dataset
	QVector<double> xData = {77.6E0, 114.9E0, 141.1E0, 190.8E0, 239.9E0, 289.0E0, 332.8E0, 378.4E0, 434.8E0, 477.3E0, 536.8E0, 593.1E0, 689.1E0, 760.0E0};
	QVector<double> yData = {10.07E0, 14.73E0, 17.94E0, 23.93E0, 29.61E0, 35.18E0, 40.02E0, 44.82E0, 50.76E0, 55.05E0, 61.01E0, 66.40E0, 75.47E0, 81.78E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*(1-1/sqrt(1+2*b2*x))");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 6.3642725809E+02 << 2.0813627256E-04;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 636.427904767969
	FuzzyCompare(fitResult.paramValues.at(0), 6.3642725809E+02, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 4.66168062054875
	FuzzyCompare(fitResult.errorValues.at(0), 4.6638326572E+00, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000208136026420746
	FuzzyCompare(fitResult.paramValues.at(1), 2.0813627256E-04, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 1.77209416674174e-06
	FuzzyCompare(fitResult.errorValues.at(1), 1.7728423155E-06, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0584286153041661
	FuzzyCompare(fitResult.rsd, 5.8428615257E-02, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0409668370363468
	FuzzyCompare(fitResult.sse, 4.0966836971E-02, 1.e-8);
}

void FitTest::testNonLinearMisra1d() {
	// NIST data for Misra1d dataset
	QVector<double> xData = {77.6E0, 114.9E0, 141.1E0, 190.8E0, 239.9E0, 289.0E0, 332.8E0, 378.4E0, 434.8E0, 477.3E0, 536.8E0, 593.1E0, 689.1E0, 760.0E0};
	QVector<double> yData = {10.07E0, 14.73E0, 17.94E0, 23.93E0, 29.61E0, 35.18E0, 40.02E0, 44.82E0, 50.76E0, 55.05E0, 61.01E0, 66.40E0, 75.47E0, 81.78E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*b2*x/(1+b2*x)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 500. << 0.0001;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 437.370039987725
	FuzzyCompare(fitResult.paramValues.at(0), 4.3736970754E+02, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 3.64772833062694
	FuzzyCompare(fitResult.errorValues.at(0), 3.6489174345E+00, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000302272976784709
	FuzzyCompare(fitResult.paramValues.at(1), 3.0227324449E-04, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.93256059733558e-06
	FuzzyCompare(fitResult.errorValues.at(1), 2.9334354479E-06, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.068568272134244
	FuzzyCompare(fitResult.rsd, 6.8568272111E-02, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.056419295321709 (new: 0.0564192953613416)
	FuzzyCompare(fitResult.sse, 5.6419295283E-02, 1.e-8);
}

void FitTest::testNonLinearMisra1d_2() {
	// NIST data for Misra1d dataset
	QVector<double> xData = {77.6E0, 114.9E0, 141.1E0, 190.8E0, 239.9E0, 289.0E0, 332.8E0, 378.4E0, 434.8E0, 477.3E0, 536.8E0, 593.1E0, 689.1E0, 760.0E0};
	QVector<double> yData = {10.07E0, 14.73E0, 17.94E0, 23.93E0, 29.61E0, 35.18E0, 40.02E0, 44.82E0, 50.76E0, 55.05E0, 61.01E0, 66.40E0, 75.47E0, 81.78E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*b2*x/(1+b2*x)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 450. << 3.e-4;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 437.370039987725
	FuzzyCompare(fitResult.paramValues.at(0), 4.3736970754E+02, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 3.64772833062694
	FuzzyCompare(fitResult.errorValues.at(0), 3.6489174345E+00, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000302272976784709
	FuzzyCompare(fitResult.paramValues.at(1), 3.0227324449E-04, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.93256059733558e-06
	FuzzyCompare(fitResult.errorValues.at(1), 2.9334354479E-06, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.068568272134244
	FuzzyCompare(fitResult.rsd, 6.8568272111E-02, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.056419295321709
	FuzzyCompare(fitResult.sse, 5.6419295283E-02, 1.e-8);
}

void FitTest::testNonLinearMisra1d_3() {
	// NIST data for Misra1d dataset
	QVector<double> xData = {77.6E0, 114.9E0, 141.1E0, 190.8E0, 239.9E0, 289.0E0, 332.8E0, 378.4E0, 434.8E0, 477.3E0, 536.8E0, 593.1E0, 689.1E0, 760.0E0};
	QVector<double> yData = {10.07E0, 14.73E0, 17.94E0, 23.93E0, 29.61E0, 35.18E0, 40.02E0, 44.82E0, 50.76E0, 55.05E0, 61.01E0, 66.40E0, 75.47E0, 81.78E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*b2*x/(1+b2*x)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 4.3736970754E+02 << 3.0227324449E-04;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 437.370039987725
	FuzzyCompare(fitResult.paramValues.at(0), 4.3736970754E+02, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 3.64772833062694
	FuzzyCompare(fitResult.errorValues.at(0), 3.6489174345E+00, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000302272976784709
	FuzzyCompare(fitResult.paramValues.at(1), 3.0227324449E-04, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.93256059733558e-06
	FuzzyCompare(fitResult.errorValues.at(1), 2.9334354479E-06, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.068568272134244
	FuzzyCompare(fitResult.rsd, 6.8568272111E-02, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.056419295321709
	FuzzyCompare(fitResult.sse, 5.6419295283E-02, 1.e-9);
}

void FitTest::testNonLinearMGH09() {
	// NIST data for MGH09 dataset
	QVector<double> xData = {4.000000E+00,
							 2.000000E+00,
							 1.000000E+00,
							 5.000000E-01,
							 2.500000E-01,
							 1.670000E-01,
							 1.250000E-01,
							 1.000000E-01,
							 8.330000E-02,
							 7.140000E-02,
							 6.250000E-02};
	QVector<double> yData = {1.957000E-01,
							 1.947000E-01,
							 1.735000E-01,
							 1.600000E-01,
							 8.440000E-02,
							 6.270000E-02,
							 4.560000E-02,
							 3.420000E-02,
							 3.230000E-02,
							 2.350000E-02,
							 2.460000E-02};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*(x^2 + b2*x)/(x^2 + x*b3 + b4)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3") << QStringLiteral("b4");
	// fitData.eps = 1.e-8;
	const int np = fitData.paramNames.size();
	// Start values 1
	fitData.paramStartValues << 2.5000000000E+01 << 3.9000000000E+01 << 4.1500000000E+01 << 3.9000000000E+01;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max()
							 << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max() << std::numeric_limits<double>::max()
							 << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 4);

	/* TODO: Does not find best result
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0));	// result:
	FuzzyCompare(fitResult.paramValues.at(0), 1.9280693458E-01, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0));	// result:
	FuzzyCompare(fitResult.errorValues.at(0), 1.1435312227E-02, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1));	// result:
	FuzzyCompare(fitResult.paramValues.at(1), 1.9128232873E-01, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1));	// result:
	FuzzyCompare(fitResult.errorValues.at(1), 1.9633220911E-01, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2));	// result:
	FuzzyCompare(fitResult.paramValues.at(2), 1.2305650693E-01, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2));	// result:
	FuzzyCompare(fitResult.errorValues.at(2), 8.0842031232E-02, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(3));	// result:
	FuzzyCompare(fitResult.paramValues.at(3), 1.3606233068E-01, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(3));	// result:
	FuzzyCompare(fitResult.errorValues.at(3), 9.0025542308E-02, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.rsd);	// result:
	FuzzyCompare(fitResult.rsd, 6.6279236551E-03, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse);	// result:
	FuzzyCompare(fitResult.sse, 3.0750560385E-04, 1.e-9);
	*/
}

void FitTest::testNonLinearMGH09_2() {
	// NIST data for MGH09 dataset
	QVector<double> xData = {4.000000E+00,
							 2.000000E+00,
							 1.000000E+00,
							 5.000000E-01,
							 2.500000E-01,
							 1.670000E-01,
							 1.250000E-01,
							 1.000000E-01,
							 8.330000E-02,
							 7.140000E-02,
							 6.250000E-02};
	QVector<double> yData = {1.957000E-01,
							 1.947000E-01,
							 1.735000E-01,
							 1.600000E-01,
							 8.440000E-02,
							 6.270000E-02,
							 4.560000E-02,
							 3.420000E-02,
							 3.230000E-02,
							 2.350000E-02,
							 2.460000E-02};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*(x^2 + b2*x)/(x^2 + x*b3 + b4)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3") << QStringLiteral("b4");
	// fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	// start values 2
	fitData.paramStartValues << 2.5000000000E-01 << 3.9000000000E-01 << 4.1500000000E-01 << 3.9000000000E-01;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max()
							 << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max() << std::numeric_limits<double>::max()
							 << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 4);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result:
	FuzzyCompare(fitResult.paramValues.at(0), 1.9280693458E-01, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result:
	FuzzyCompare(fitResult.errorValues.at(0), 1.1435312227E-02, 1.e-4);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result:
	FuzzyCompare(fitResult.paramValues.at(1), 1.9128232873E-01, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: (new: 0.196361378435283)
	FuzzyCompare(fitResult.errorValues.at(1), 1.9633220911E-01, 1.e-3);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result:
	FuzzyCompare(fitResult.paramValues.at(2), 1.2305650693E-01, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result:
	FuzzyCompare(fitResult.errorValues.at(2), 8.0842031232E-02, 1.e-4);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result:
	FuzzyCompare(fitResult.paramValues.at(3), 1.3606233068E-01, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result:
	FuzzyCompare(fitResult.errorValues.at(3), 9.0025542308E-02, 1.e-4);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result:
	FuzzyCompare(fitResult.rsd, 6.6279236551E-03, 1.e-8);
	DEBUG(std::setprecision(15) << fitResult.sse); // result:
	FuzzyCompare(fitResult.sse, 3.0750560385E-04, 1.e-8);
}

void FitTest::testNonLinearMGH09_3() {
	// NIST data for MGH09 dataset
	QVector<double> xData = {4.000000E+00,
							 2.000000E+00,
							 1.000000E+00,
							 5.000000E-01,
							 2.500000E-01,
							 1.670000E-01,
							 1.250000E-01,
							 1.000000E-01,
							 8.330000E-02,
							 7.140000E-02,
							 6.250000E-02};
	QVector<double> yData = {1.957000E-01,
							 1.947000E-01,
							 1.735000E-01,
							 1.600000E-01,
							 8.440000E-02,
							 6.270000E-02,
							 4.560000E-02,
							 3.420000E-02,
							 3.230000E-02,
							 2.350000E-02,
							 2.460000E-02};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*(x^2 + b2*x)/(x^2 + x*b3 + b4)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3") << QStringLiteral("b4");
	// fitData.eps = 1.e-8;
	const int np = fitData.paramNames.size();
	// exact start values
	fitData.paramStartValues << 1.9280693458E-01 << 1.9128232873E-01 << 1.2305650693E-01 << 1.3606233068E-01;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max()
							 << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max() << std::numeric_limits<double>::max()
							 << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 4);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result:
	FuzzyCompare(fitResult.paramValues.at(0), 1.9280693458E-01, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result:
	FuzzyCompare(fitResult.errorValues.at(0), 1.1435312227E-02, 1.e-5);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result:
	FuzzyCompare(fitResult.paramValues.at(1), 1.9128232873E-01, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result:
	FuzzyCompare(fitResult.errorValues.at(1), 1.9633220911E-01, 1.e-5);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result:
	FuzzyCompare(fitResult.paramValues.at(2), 1.2305650693E-01, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result:
	FuzzyCompare(fitResult.errorValues.at(2), 8.0842031232E-02, 1.e-5);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result:
	FuzzyCompare(fitResult.paramValues.at(3), 1.3606233068E-01, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result:
	FuzzyCompare(fitResult.errorValues.at(3), 9.0025542308E-02, 1.e-5);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result:
	FuzzyCompare(fitResult.rsd, 6.6279236551E-03, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result:
	FuzzyCompare(fitResult.sse, 3.0750560385E-04, 1.e-9);
}

void FitTest::testNonLinearMGH10() {
	// NIST data for MGH10 dataset
	QVector<double> xData = {5.000000E+01,
							 5.500000E+01,
							 6.000000E+01,
							 6.500000E+01,
							 7.000000E+01,
							 7.500000E+01,
							 8.000000E+01,
							 8.500000E+01,
							 9.000000E+01,
							 9.500000E+01,
							 1.000000E+02,
							 1.050000E+02,
							 1.100000E+02,
							 1.150000E+02,
							 1.200000E+02,
							 1.250000E+02};
	QVector<double> yData = {3.478000E+04,
							 2.861000E+04,
							 2.365000E+04,
							 1.963000E+04,
							 1.637000E+04,
							 1.372000E+04,
							 1.154000E+04,
							 9.744000E+03,
							 8.261000E+03,
							 7.030000E+03,
							 6.005000E+03,
							 5.147000E+03,
							 4.427000E+03,
							 3.820000E+03,
							 3.307000E+03,
							 2.872000E+03};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*exp(b2/(x+b3))");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3");
	// fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 2.0000000000E+00 << 4.0000000000E+05 << 2.5000000000E+04;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 3);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 0.00560963848336205 (Windows: 0.00560964247264364)
	FuzzyCompare(fitResult.paramValues.at(0), 5.6096364710E-03, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.000156888682057687
	FuzzyCompare(fitResult.errorValues.at(0), 1.5687892471E-04, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 6181.34604697191 (Windows: 6181.34545410281)
	FuzzyCompare(fitResult.paramValues.at(1), 6.1813463463E+03, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 23.3105479190063
	FuzzyCompare(fitResult.errorValues.at(1), 2.3309021107E+01, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 345.223624540718 (Windows: 345.223579103645)
	FuzzyCompare(fitResult.paramValues.at(2), 3.4522363462E+02, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.784915645388214
	FuzzyCompare(fitResult.errorValues.at(2), 7.8486103508E-01, 1.e-4);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 2.6009740064923	(Windows: 2.60097400662837, 2.60097400697918)
	FuzzyCompare(fitResult.rsd, 2.6009740065E+00, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 87.9458551718321	(FreeBSD: 87.9458551726946, Windows: 87.9458551810338)
	FuzzyCompare(fitResult.sse, 8.7945855171E+01, 1.e-9);
}

void FitTest::testNonLinearMGH10_2() {
	// NIST data for MGH10 dataset
	QVector<double> xData = {5.000000E+01,
							 5.500000E+01,
							 6.000000E+01,
							 6.500000E+01,
							 7.000000E+01,
							 7.500000E+01,
							 8.000000E+01,
							 8.500000E+01,
							 9.000000E+01,
							 9.500000E+01,
							 1.000000E+02,
							 1.050000E+02,
							 1.100000E+02,
							 1.150000E+02,
							 1.200000E+02,
							 1.250000E+02};
	QVector<double> yData = {3.478000E+04,
							 2.861000E+04,
							 2.365000E+04,
							 1.963000E+04,
							 1.637000E+04,
							 1.372000E+04,
							 1.154000E+04,
							 9.744000E+03,
							 8.261000E+03,
							 7.030000E+03,
							 6.005000E+03,
							 5.147000E+03,
							 4.427000E+03,
							 3.820000E+03,
							 3.307000E+03,
							 2.872000E+03};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*exp(b2/(x+b3))");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3");
	// fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 2.0000000000E-02 << 4.0000000000E+03 << 2.5000000000E+02;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 3);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 0.00560963848336205
	FuzzyCompare(fitResult.paramValues.at(0), 5.6096364710E-03, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.000156888586677272
	FuzzyCompare(fitResult.errorValues.at(0), 1.5687892471E-04, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 6181.34604697191
	FuzzyCompare(fitResult.paramValues.at(1), 6.1813463463E+03, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 23.3105167849511
	FuzzyCompare(fitResult.errorValues.at(1), 2.3309021107E+01, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 345.223624540718
	FuzzyCompare(fitResult.paramValues.at(2), 3.4522363462E+02, 2.e-7);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.784914752553929
	FuzzyCompare(fitResult.errorValues.at(2), 7.8486103508E-01, 1.e-4);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 2.6009740064923
	FuzzyCompare(fitResult.rsd, 2.6009740065E+00, 2.e-10);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 87.9458551718321
	FuzzyCompare(fitResult.sse, 8.7945855171E+01, 1.e-9);
}

void FitTest::testNonLinearMGH10_3() {
	// NIST data for MGH10 dataset
	QVector<double> xData = {5.000000E+01,
							 5.500000E+01,
							 6.000000E+01,
							 6.500000E+01,
							 7.000000E+01,
							 7.500000E+01,
							 8.000000E+01,
							 8.500000E+01,
							 9.000000E+01,
							 9.500000E+01,
							 1.000000E+02,
							 1.050000E+02,
							 1.100000E+02,
							 1.150000E+02,
							 1.200000E+02,
							 1.250000E+02};
	QVector<double> yData = {3.478000E+04,
							 2.861000E+04,
							 2.365000E+04,
							 1.963000E+04,
							 1.637000E+04,
							 1.372000E+04,
							 1.154000E+04,
							 9.744000E+03,
							 8.261000E+03,
							 7.030000E+03,
							 6.005000E+03,
							 5.147000E+03,
							 4.427000E+03,
							 3.820000E+03,
							 3.307000E+03,
							 2.872000E+03};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1*exp(b2/(x+b3))");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3");
	// fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 5.6096364710E-03 << 6.1813463463E+03 << 3.4522363462E+02;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max() << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 3);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 0.00560963848336205
	FuzzyCompare(fitResult.paramValues.at(0), 5.6096364710E-03, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.000156888348998521
	FuzzyCompare(fitResult.errorValues.at(0), 1.5687892471E-04, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 6181.34604697191
	FuzzyCompare(fitResult.paramValues.at(1), 6.1813463463E+03, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 23.310508065631
	FuzzyCompare(fitResult.errorValues.at(1), 2.3309021107E+01, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 345.223624540718
	FuzzyCompare(fitResult.paramValues.at(2), 3.4522363462E+02, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.784914424350028
	FuzzyCompare(fitResult.errorValues.at(2), 7.8486103508E-01, 1.e-4);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 2.6009740064923
	FuzzyCompare(fitResult.rsd, 2.6009740065E+00, 1.e-11);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 87.9458551718321
	FuzzyCompare(fitResult.sse, 8.7945855171E+01, 1.e-11);
}

void FitTest::testNonLinearRat43() {
	// NIST data for Rat43 dataset
	QVector<double> xData = {1.0E0, 2.0E0, 3.0E0, 4.0E0, 5.0E0, 6.0E0, 7.0E0, 8.0E0, 9.0E0, 10.0E0, 11.0E0, 12.0E0, 13.0E0, 14.0E0, 15.0E0};
	QVector<double> yData =
		{16.08E0, 33.83E0, 65.80E0, 97.20E0, 191.55E0, 326.20E0, 386.87E0, 520.53E0, 590.03E0, 651.92E0, 724.93E0, 699.56E0, 689.96E0, 637.56E0, 717.41E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1/pow(1 + exp(b2-b3*x); 1/b4)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3") << QStringLiteral("b4");
	fitData.eps = 1.e-9;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 1.0000000000E+02 << 1.0000000000E+01 << 1.0000000000E+00 << 1.0000000000E+00;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max()
							 << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max() << std::numeric_limits<double>::max()
							 << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 4);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 699.641340982193
	FuzzyCompare(fitResult.paramValues.at(0), 6.9964151270E+02, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 16.3022524293302
	FuzzyCompare(fitResult.errorValues.at(0), 1.6302297817E+01, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 5.2771555758844
	FuzzyCompare(fitResult.paramValues.at(1), 5.2771253025E+00, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.08290034908325
	FuzzyCompare(fitResult.errorValues.at(1), 2.0828735829E+00, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 0.759632366113637
	FuzzyCompare(fitResult.paramValues.at(2), 7.5962938329E-01, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.195664372178938
	FuzzyCompare(fitResult.errorValues.at(2), 1.9566123451E-01, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result: 1.27925748993867
	FuzzyCompare(fitResult.paramValues.at(3), 1.2792483859E+00, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result: 0.687627478905195
	FuzzyCompare(fitResult.errorValues.at(3), 6.8761936385E-01, 1.e-4);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 28.2624146626284
	FuzzyCompare(fitResult.rsd, 2.8262414662E+01, 1.e-10);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 8786.4049081859
	FuzzyCompare(fitResult.sse, 8.7864049080E+03, 1.e-10);
}

void FitTest::testNonLinearRat43_2() {
	// NIST data for Rat43 dataset
	QVector<double> xData = {1.0E0, 2.0E0, 3.0E0, 4.0E0, 5.0E0, 6.0E0, 7.0E0, 8.0E0, 9.0E0, 10.0E0, 11.0E0, 12.0E0, 13.0E0, 14.0E0, 15.0E0};
	QVector<double> yData =
		{16.08E0, 33.83E0, 65.80E0, 97.20E0, 191.55E0, 326.20E0, 386.87E0, 520.53E0, 590.03E0, 651.92E0, 724.93E0, 699.56E0, 689.96E0, 637.56E0, 717.41E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1/pow(1 + exp(b2-b3*x); 1/b4)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3") << QStringLiteral("b4");
	fitData.eps = 1.e-10;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 7.0000000000E+02 << 5.0000000000E+00 << 7.5000000000E-01 << 1.3000000000E+00;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max()
							 << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max() << std::numeric_limits<double>::max()
							 << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 4);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 699.641340982193
	FuzzyCompare(fitResult.paramValues.at(0), 6.9964151270E+02, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 16.3023134145464 (FreeBSD: 16.3023141645004)
	FuzzyCompare(fitResult.errorValues.at(0), 1.6302297817E+01, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 5.2771555758844
	FuzzyCompare(fitResult.paramValues.at(1), 5.2771253025E+00, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.08288970703906
	FuzzyCompare(fitResult.errorValues.at(1), 2.0828735829E+00, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 0.759632366113637
	FuzzyCompare(fitResult.paramValues.at(2), 7.5962938329E-01, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.195662802156063
	FuzzyCompare(fitResult.errorValues.at(2), 1.9566123451E-01, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result: 1.27925748993867
	FuzzyCompare(fitResult.paramValues.at(3), 1.2792483859E+00, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result: 0.687623222160242
	FuzzyCompare(fitResult.errorValues.at(3), 6.8761936385E-01, 1.e-5);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 28.2624146626284
	FuzzyCompare(fitResult.rsd, 2.8262414662E+01, 1.e-10);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 8786.4049081859
	FuzzyCompare(fitResult.sse, 8.7864049080E+03, 1.e-11);
}

void FitTest::testNonLinearRat43_3() {
	// NIST data for Rat43 dataset
	QVector<double> xData = {1.0E0, 2.0E0, 3.0E0, 4.0E0, 5.0E0, 6.0E0, 7.0E0, 8.0E0, 9.0E0, 10.0E0, 11.0E0, 12.0E0, 13.0E0, 14.0E0, 15.0E0};
	QVector<double> yData =
		{16.08E0, 33.83E0, 65.80E0, 97.20E0, 191.55E0, 326.20E0, 386.87E0, 520.53E0, 590.03E0, 651.92E0, 724.93E0, 699.56E0, 689.96E0, 637.56E0, 717.41E0};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1/pow(1 + exp(b2-b3*x); 1/b4)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3") << QStringLiteral("b4");
	// fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 6.9964151270E+02 << 5.2771253025E+00 << 7.5962938329E-01 << 1.2792483859E+00;
	fitData.paramLowerLimits << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max() << -std::numeric_limits<double>::max()
							 << -std::numeric_limits<double>::max();
	fitData.paramUpperLimits << std::numeric_limits<double>::max() << std::numeric_limits<double>::max() << std::numeric_limits<double>::max()
							 << std::numeric_limits<double>::max();
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 4);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 699.641340982193
	FuzzyCompare(fitResult.paramValues.at(0), 6.9964151270E+02, 1.e-8);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 16.3022905400761
	FuzzyCompare(fitResult.errorValues.at(0), 1.6302297817E+01, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 5.2771555758844
	FuzzyCompare(fitResult.paramValues.at(1), 5.2771253025E+00, 1.e-8);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.08289316520407
	FuzzyCompare(fitResult.errorValues.at(1), 2.0828735829E+00, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 0.759632366113637
	FuzzyCompare(fitResult.paramValues.at(2), 7.5962938329E-01, 1.e-8);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.195663312668779
	FuzzyCompare(fitResult.errorValues.at(2), 1.9566123451E-01, 1.e-4);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result: 1.27925748993867
	FuzzyCompare(fitResult.paramValues.at(3), 1.2792483859E+00, 1.e-8);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result: 0.687624541478887
	FuzzyCompare(fitResult.errorValues.at(3), 6.8761936385E-01, 1.e-5);

	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 28.2624146626284
	FuzzyCompare(fitResult.rsd, 2.8262414662E+01, 1.e-11);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 8786.4049081859
	FuzzyCompare(fitResult.sse, 8.7864049080E+03, 1.e-11);
}

// more tests

void FitTest::testNonLinearHahn1() {
	Spreadsheet spreadsheet(QStringLiteral("test"), false);
	AsciiFilter filter;

	// NIST data for Hahn1 dataset
	const QString& fileName = QFINDTESTDATA(QLatin1String("data/NIST/non-linear/Hahn1_data.dat"));

	auto properties = filter.properties();
	properties.headerEnabled = false;
	properties.simplifyWhitespaces = true;
	properties.skipEmptyParts = true;
	filter.setProperties(properties);
	filter.readDataFromFile(fileName, &spreadsheet, AbstractFileFilter::ImportMode::Replace);

	QCOMPARE(spreadsheet.rowCount(), 236);
	QCOMPARE(spreadsheet.columnCount(), 2);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(spreadsheet.column(1));
	fitCurve.setYDataColumn(spreadsheet.column(0));

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("(b1+b2*x+b3*x^2+b4*x^3) / (1+b5*x+b6*x^2+b7*x^3)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3") << QStringLiteral("b4") << QStringLiteral("b5")
					   << QStringLiteral("b6") << QStringLiteral("b7");
	fitData.eps = 1.e-8;
	const int np = fitData.paramNames.size();
	// first set of start values
	fitData.paramStartValues << 10. << -1. << 0.05 << -0.00001 << -0.05 << 0.001 << -0.000001;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 7);

	printAndCheck(fitResult.paramValues.at(0), 1.0776351733E+00, 1.e-2);
	printAndCheck(fitResult.errorValues.at(0), 1.7070154742E-01, 2.e-2);

	printAndCheck(fitResult.paramValues.at(1), -1.2269296921E-01, 1.e-2);
	printAndCheck(fitResult.errorValues.at(1), 1.2000289189E-02, 2.e-2);

	printAndCheck(fitResult.paramValues.at(2), 4.0863750610E-03, 2.e-3);
	printAndCheck(fitResult.errorValues.at(2), 2.2508314937E-04, 1.e-3);

	printAndCheck(fitResult.paramValues.at(3), -1.4262662514E-06, 1.e-3);
	printAndCheck(fitResult.errorValues.at(3), 2.7578037666E-07, 1.e-3);

	printAndCheck(fitResult.paramValues.at(4), -5.7609940901E-03, 1.e-3);
	printAndCheck(fitResult.errorValues.at(4), 2.4712888219E-04, 1.e-3);

	printAndCheck(fitResult.paramValues.at(5), 2.4053735503E-04, 1.e-3);
	printAndCheck(fitResult.errorValues.at(5), 1.0449373768E-05, 1.e-4);

	printAndCheck(fitResult.paramValues.at(6), -1.2314450199E-07, 1.e-4);
	printAndCheck(fitResult.errorValues.at(6), 1.3027335327E-08, 1.e-4);

	printAndCheck(fitResult.rsd, 8.1803852243E-02, 1.e-5);
	printAndCheck(fitResult.sse, 1.5324382854E+00, 2.e-5);
}

void FitTest::testNonLinearHahn1_2() {
	Spreadsheet spreadsheet(QStringLiteral("test"), false);
	AsciiFilter filter;

	// NIST data for Hahn1 dataset
	const QString& fileName = QFINDTESTDATA(QLatin1String("data/NIST/non-linear/Hahn1_data.dat"));

	auto properties = filter.properties();
	properties.headerEnabled = false;
	properties.simplifyWhitespaces = true;
	properties.skipEmptyParts = true;
	filter.setProperties(properties);
	filter.readDataFromFile(fileName, &spreadsheet, AbstractFileFilter::ImportMode::Replace);

	QCOMPARE(spreadsheet.rowCount(), 236);
	QCOMPARE(spreadsheet.columnCount(), 2);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(spreadsheet.column(1));
	fitCurve.setYDataColumn(spreadsheet.column(0));

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("(b1+b2*x+b3*x^2+b4*x^3) / (1+b5*x+b6*x^2+b7*x^3)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3") << QStringLiteral("b4") << QStringLiteral("b5")
					   << QStringLiteral("b6") << QStringLiteral("b7");
	// fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	// second start values
	fitData.paramStartValues << 1. << -0.1 << 0.005 << -0.000001 << -0.005 << 0.0001 << -0.0000001;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 7);

	printAndCheck(fitResult.paramValues.at(0), 1.0776351733E+00, 1.e-2);
	printAndCheck(fitResult.errorValues.at(0), 1.7070154742E-01, 2.e-2);

	printAndCheck(fitResult.paramValues.at(1), -1.2269296921E-01, 1.e-2);
	printAndCheck(fitResult.errorValues.at(1), 1.2000289189E-02, 2.e-2);

	printAndCheck(fitResult.paramValues.at(2), 4.0863750610E-03, 2.e-3);
	printAndCheck(fitResult.errorValues.at(2), 2.2508314937E-04, 1.e-3);

	printAndCheck(fitResult.paramValues.at(3), -1.4262662514E-06, 1.e-3);
	printAndCheck(fitResult.errorValues.at(3), 2.7578037666E-07, 1.e-3);

	printAndCheck(fitResult.paramValues.at(4), -5.7609940901E-03, 1.e-3);
	printAndCheck(fitResult.errorValues.at(4), 2.4712888219E-04, 1.e-3);

	printAndCheck(fitResult.paramValues.at(5), 2.4053735503E-04, 1.e-3);
	printAndCheck(fitResult.errorValues.at(5), 1.0449373768E-05, 1.e-4);

	printAndCheck(fitResult.paramValues.at(6), -1.2314450199E-07, 1.e-4);
	printAndCheck(fitResult.errorValues.at(6), 1.3027335327E-08, 1.e-4);

	printAndCheck(fitResult.rsd, 8.1803852243E-02, 1.e-5);
	printAndCheck(fitResult.sse, 1.5324382854E+00, 2.e-5);
}

void FitTest::testNonLinearHahn1_3() {
	Spreadsheet spreadsheet(QStringLiteral("test"), false);
	AsciiFilter filter;

	// NIST data for Hahn1 dataset
	const QString& fileName = QFINDTESTDATA(QLatin1String("data/NIST/non-linear/Hahn1_data.dat"));

	auto properties = filter.properties();
	properties.headerEnabled = false;
	properties.simplifyWhitespaces = true;
	properties.skipEmptyParts = true;
	filter.setProperties(properties);
	filter.readDataFromFile(fileName, &spreadsheet, AbstractFileFilter::ImportMode::Replace);

	QCOMPARE(spreadsheet.rowCount(), 236);
	QCOMPARE(spreadsheet.columnCount(), 2);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(spreadsheet.column(1));
	fitCurve.setYDataColumn(spreadsheet.column(0));

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("(b1+b2*x+b3*x^2+b4*x^3) / (1+b5*x+b6*x^2+b7*x^3)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3") << QStringLiteral("b4") << QStringLiteral("b5")
					   << QStringLiteral("b6") << QStringLiteral("b7");
	// fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	// exact start values
	fitData.paramStartValues << 1.0776351733E+00 << -1.2269296921E-01 << 4.0863750610E-03 << -1.4262662514E-06 << -5.7609940901E-03 << 2.4053735503E-04
							 << -1.2314450199E-07;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 7);

	printAndCheck(fitResult.paramValues.at(0), 1.0776351733E+00, 1.e-2);
	printAndCheck(fitResult.errorValues.at(0), 1.7070154742E-01, 2.e-2);

	printAndCheck(fitResult.paramValues.at(1), -1.2269296921E-01, 1.e-2);
	printAndCheck(fitResult.errorValues.at(1), 1.2000289189E-02, 2.e-2);

	printAndCheck(fitResult.paramValues.at(2), 4.0863750610E-03, 2.e-3);
	printAndCheck(fitResult.errorValues.at(2), 2.2508314937E-04, 1.e-3);

	printAndCheck(fitResult.paramValues.at(3), -1.4262662514E-06, 1.e-3);
	printAndCheck(fitResult.errorValues.at(3), 2.7578037666E-07, 1.e-3);

	printAndCheck(fitResult.paramValues.at(4), -5.7609940901E-03, 1.e-3);
	printAndCheck(fitResult.errorValues.at(4), 2.4712888219E-04, 1.e-3);

	printAndCheck(fitResult.paramValues.at(5), 2.4053735503E-04, 1.e-3);
	printAndCheck(fitResult.errorValues.at(5), 1.0449373768E-05, 1.e-4);

	printAndCheck(fitResult.paramValues.at(6), -1.2314450199E-07, 1.e-4);
	printAndCheck(fitResult.errorValues.at(6), 1.3027335327E-08, 1.e-4);

	printAndCheck(fitResult.rsd, 8.1803852243E-02, 1.e-9);
	printAndCheck(fitResult.sse, 1.5324382854E+00, 1.e-9);
}

void FitTest::testNonLinearBennett5() {
	Spreadsheet spreadsheet(QStringLiteral("test"), false);
	AsciiFilter filter;

	// NIST data for Hahn1 dataset
	const QString& fileName = QFINDTESTDATA(QLatin1String("data/NIST/non-linear/Bennett5_data.dat"));

	auto properties = filter.properties();
	properties.headerEnabled = false;
	properties.simplifyWhitespaces = true;
	properties.skipEmptyParts = true;
	filter.setProperties(properties);
	filter.readDataFromFile(fileName, &spreadsheet, AbstractFileFilter::ImportMode::Replace);

	QCOMPARE(spreadsheet.rowCount(), 154);
	QCOMPARE(spreadsheet.columnCount(), 2);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(spreadsheet.column(1));
	fitCurve.setYDataColumn(spreadsheet.column(0));

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1 * (b2+x)^(-1./b3)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3");
	fitData.eps = 1.e-8;
	fitData.maxIterations = 1000;
	const int np = fitData.paramNames.size();
	// first set of start values
	fitData.paramStartValues << -2000. << 50. << 0.8;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 3);

	printAndCheck(fitResult.paramValues.at(0), -2.5235058043E+03, 1.e-2);
	printAndCheck(fitResult.errorValues.at(0), 2.9715175411E+02, 2.e-2);

	printAndCheck(fitResult.paramValues.at(1), 4.6736564644E+01, 5.e-3);
	printAndCheck(fitResult.errorValues.at(1), 1.2448871856E+00, 2.e-2);

	printAndCheck(fitResult.paramValues.at(2), 9.3218483193E-01, 3.e-3);
	printAndCheck(fitResult.errorValues.at(2), 2.0272299378E-02, 1.e-2);

	printAndCheck(fitResult.rsd, 1.8629312528E-03, 5.e-5);
	printAndCheck(fitResult.sse, 5.2404744073E-04, 1.e-4);
}

void FitTest::testNonLinearBennett5_2() {
	Spreadsheet spreadsheet(QStringLiteral("test"), false);
	AsciiFilter filter;

	// NIST data for Hahn1 dataset
	const QString& fileName = QFINDTESTDATA(QLatin1String("data/NIST/non-linear/Bennett5_data.dat"));

	auto properties = filter.properties();
	properties.headerEnabled = false;
	properties.simplifyWhitespaces = true;
	properties.skipEmptyParts = true;
	filter.setProperties(properties);
	filter.readDataFromFile(fileName, &spreadsheet, AbstractFileFilter::ImportMode::Replace);

	QCOMPARE(spreadsheet.rowCount(), 154);
	QCOMPARE(spreadsheet.columnCount(), 2);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(spreadsheet.column(1));
	fitCurve.setYDataColumn(spreadsheet.column(0));

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1 * (b2+x)^(-1./b3)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3");
	fitData.eps = 1.e-8;
	fitData.maxIterations = 1000;
	const int np = fitData.paramNames.size();
	// second set of start values
	fitData.paramStartValues << -1500. << 45. << 0.85;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 3);

	printAndCheck(fitResult.paramValues.at(0), -2.5235058043E+03, 2.e-2);
	printAndCheck(fitResult.errorValues.at(0), 2.9715175411E+02, 2.e-2);

	printAndCheck(fitResult.paramValues.at(1), 4.6736564644E+01, 5.e-3);
	printAndCheck(fitResult.errorValues.at(1), 1.2448871856E+00, 2.e-2);

	printAndCheck(fitResult.paramValues.at(2), 9.3218483193E-01, 3.e-3);
	printAndCheck(fitResult.errorValues.at(2), 2.0272299378E-02, 1.e-2);

	printAndCheck(fitResult.rsd, 1.8629312528E-03, 1.e-4);
	printAndCheck(fitResult.sse, 5.2404744073E-04, 1.e-4);
}

void FitTest::testNonLinearBennett5_3() {
	Spreadsheet spreadsheet(QStringLiteral("test"), false);
	AsciiFilter filter;

	// NIST data for Hahn1 dataset
	const QString& fileName = QFINDTESTDATA(QLatin1String("data/NIST/non-linear/Bennett5_data.dat"));

	auto properties = filter.properties();
	properties.headerEnabled = false;
	properties.simplifyWhitespaces = true;
	properties.skipEmptyParts = true;
	filter.setProperties(properties);
	filter.readDataFromFile(fileName, &spreadsheet, AbstractFileFilter::ImportMode::Replace);

	QCOMPARE(spreadsheet.rowCount(), 154);
	QCOMPARE(spreadsheet.columnCount(), 2);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(spreadsheet.column(1));
	fitCurve.setYDataColumn(spreadsheet.column(0));

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("b1 * (b2+x)^(-1./b3)");
	fitData.paramNames << QStringLiteral("b1") << QStringLiteral("b2") << QStringLiteral("b3");
	fitData.eps = 1.e-8;
	fitData.maxIterations = 1000;
	const int np = fitData.paramNames.size();
	// exact start values
	fitData.paramStartValues << -2.5235058043E+03 << 4.6736564644E+01 << 9.3218483193E-01;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 3);

	printAndCheck(fitResult.paramValues.at(0), -2.5235058043E+03, 2.e-2);
	printAndCheck(fitResult.errorValues.at(0), 2.9715175411E+02, 2.e-2);

	printAndCheck(fitResult.paramValues.at(1), 4.6736564644E+01, 5.e-3);
	printAndCheck(fitResult.errorValues.at(1), 1.2448871856E+00, 2.e-2);

	printAndCheck(fitResult.paramValues.at(2), 9.3218483193E-01, 3.e-3);
	printAndCheck(fitResult.errorValues.at(2), 2.0272299378E-02, 1.e-2);

	printAndCheck(fitResult.rsd, 1.8629312528E-03, 1.e-4);
	printAndCheck(fitResult.sse, 5.2404744073E-04, 1.e-4);
}

//////////////////////////////////////////////////////

// https://bugs.kde.org/show_bug.cgi?id=393213
void FitTest::testNonLinearMichaelis_Menten() {
	// generic data
	QVector<double> xData = {0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0};
	QVector<double> yData = {0.0, 0.6, 0.65, 0.7, 0.75, 0.75, 0.8, 0.9, 0.85, 0.95, 0.9};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("Vm * x/(Km + x)");
	fitData.paramNames << QStringLiteral("Vm") << QStringLiteral("Km");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 1.0 << 1.0;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 0.945561082955744
	FuzzyCompare(fitResult.paramValues.at(0), 0.94556434933256, 1.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.0388724403232334
	FuzzyCompare(fitResult.errorValues.at(0), 0.0388803714011844, 3.e-4);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.159396945453528
	FuzzyCompare(fitResult.paramValues.at(1), 0.159400761666661, 3.e-5);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0388244491752518
	FuzzyCompare(fitResult.errorValues.at(1), 0.0388429738447119, 5.e-4);

	DEBUG(std::setprecision(15) << fitResult.rms); // result: 0.00280486748619082
	FuzzyCompare(fitResult.rms, 0.00280486748877263, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0529609996713697
	FuzzyCompare(fitResult.rsd, 0.0529609996957444, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0252438073757174
	FuzzyCompare(fitResult.sse, 0.0252438073989537, 1.e-9);
}
// ##############################################################################
// #########################  Fits with weights #################################
// ##############################################################################

// see http://gnuplot.sourceforge.net/demo_5.2/fit.html
void FitTest::testNonLinearGP_lcdemo() {
	// data from https://github.com/gnuplot/gnuplot/blob/master/demo/lcdemo.dat
	QVector<double> xData = {39.471, 40.091, 40.602, 41.058, 41.438, 41.880, 42.437, 42.836, 43.209, 43.599, 43.997, 44.313, 44.908,
							 45.169, 45.594, 45.743, 45.796, 45.816, 45.841, 45.876, 45.908, 45.959, 46.008, 46.040, 46.060, 46.096,
							 46.126, 46.149, 46.372, 46.625, 46.945, 47.326, 47.708, 48.095, 48.540, 48.927, 49.314};
	QVector<double> yData = {1.03307, 1.03246, 1.03197, 1.03153, 1.03117, 1.03074, 1.03021, 1.02982, 1.02946, 1.02907, 1.02867, 1.02833, 1.02765,
							 1.02735, 1.02683, 1.02661, 1.02650, 1.02644, 1.02634, 1.02623, 1.02611, 1.02592, 1.02561, 1.02526, 1.02506, 1.02500,
							 1.02496, 1.02494, 1.02474, 1.02452, 1.02425, 1.02393, 1.02361, 1.02329, 1.02293, 1.02262, 1.02231};
	QVector<double> yError = {0.010, 0.010, 0.010, 0.010, 0.010, 0.010, 0.010, 0.010, 0.010, 0.010, 0.010, 0.010, 0.010,
							  0.010, 0.010, 0.010, 0.010, 0.010, 0.010, 0.010, 0.010, 0.010, 0.001, 0.001, 0.001, 0.001,
							  0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	Column yErrorColumn(QStringLiteral("yerr"), AbstractColumn::ColumnMode::Double);
	yErrorColumn.replaceValues(0, yError);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);
	fitCurve.setYErrorColumn(&yErrorColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	// x > Tc : d + mh(x-Tc)
	// x < Tc : d + ml(x-Tc) + b tanh(g(Tc-x))
	fitData.model = QStringLiteral("d + theta(x-Tc)*mh*(x-Tc) + theta(Tc-x)*(ml*(x-Tc)+b*tanh(g*(Tc-x)))");
	fitData.paramNames << QStringLiteral("d") << QStringLiteral("Tc") << QStringLiteral("mh") << QStringLiteral("ml") << QStringLiteral("b")
					   << QStringLiteral("g");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 1.02 << 45. << -0.0005 << -0.0005 << 0.01002 << 1.0;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	fitData.yWeightsType = nsl_fit_weight_instrumental;
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 6);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 1.02499979307627 (Windows: 1.02561781433026)
	FuzzyCompare(fitResult.paramValues.at(0), 1.02499621370905, 1.e-3);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 4.81672854812941e-06
	// TODO	FuzzyCompare(fitResult.errorValues.at(0), 7.27819513635249e-06, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 46.0647953740441 (Windows: 45.4871250830364)
	FuzzyCompare(fitResult.paramValues.at(1), 46.0665367045608, 2.e-2);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 9.90288940612482
	// TODO	FuzzyCompare(fitResult.errorValues.at(1), 0.00159887430059728, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result:  -0.000835023995828296 (Windows: -0.000883960665773456)
	FuzzyCompare(fitResult.paramValues.at(2), -0.0008340717673769, 1.e-1);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result:
	// TODO	FuzzyCompare(fitResult.errorValues.at(2), , 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result: -0.000987547207638997
	FuzzyCompare(fitResult.paramValues.at(3), -0.00103152542276233, 1.e-1);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result:
	// TODO	FuzzyCompare(fitResult.errorValues.at(3), , 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(4)); // result: 0.00158880319355268
	FuzzyCompare(fitResult.paramValues.at(4), 0.00139548391000006, 1.5e-1);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(4)); // result:
	// TODO	FuzzyCompare(fitResult.errorValues.at(4), , 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(5)); // result: 6.34254053273612 (Windows: 4.45482397068125)
	//	FuzzyCompare(fitResult.paramValues.at(5), 6.92493866108287, 1.e-1);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(5)); // result:
	// TODO	FuzzyCompare(fitResult.errorValues.at(5), , 1.e-6);

	DEBUG(std::setprecision(15) << fitResult.rms); // result: 98.0672185899393
	//	FuzzyCompare(fitResult.rms, 0.000188776, 1.e-11);
	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 9.90288940612482
	//	FuzzyCompare(fitResult.rsd, 0.0137395924378767, 1.e-11);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 3040.08377628812
	//	FuzzyCompare(fitResult.sse, 0.00585206841112775, 1.e-11);
}

// see http://gnuplot.sourceforge.net/demo_5.2/fit.html
void FitTest::testLinearGP_PY_noerror() {
	// Pearson's data and York's weights
	QVector<double> xData = {0.0, 0.9, 1.8, 2.6, 3.3, 4.4, 5.2, 6.1, 6.5, 7.4};
	QVector<double> yData = {5.9, 5.4, 4.4, 4.6, 3.5, 3.7, 2.8, 2.8, 2.4, 1.5};
	//	QVector<double> xError = {1000.,1000.,500.,800.,200.,80.,60.,20.,1.8,1.0};
	//	QVector<double> yError = {1.0,1.8,4.,8.,20.,20.,70.,70.,100.,500.};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	//	Column yErrorColumn(QStringLiteral("yerr"), AbstractColumn::ColumnMode::Double);
	//	yErrorColumn.replaceValues(0, yError);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);
	//	fitCurve.setYErrorColumn(&yErrorColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("a1 + a2 * x");
	fitData.paramNames << QStringLiteral("a1") << QStringLiteral("a2");
	fitData.eps = 1.e-9;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 5. << -0.5;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}
	//	fitData.yWeightsType = nsl_fit_weight_instrumental;
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 5.76118518568804
	FuzzyCompare(fitResult.paramValues.at(0), 5.76118519043878, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.189485192391863
	FuzzyCompare(fitResult.errorValues.at(0), 0.189485195921141, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.539577274076964
	FuzzyCompare(fitResult.paramValues.at(1), -0.539577274983977, 1.e-8);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0421265487265946
	FuzzyCompare(fitResult.errorValues.at(1), 0.0421265483886995, 5.e-8);

	DEBUG(std::setprecision(15) << fitResult.rms); // result: 0.100082940279452
	QCOMPARE(fitResult.rms, 0.100082940279452);
	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.316358878932538
	QCOMPARE(fitResult.rsd, 0.316358878932538);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.800663522235619
	QCOMPARE(fitResult.sse, 0.800663522235619);
}

// see http://gnuplot.sourceforge.net/demo_5.2/fit.html
void FitTest::testLinearGP_PY_yerror_polynomial() {
	// Pearson's data and York's weights
	QVector<double> xData = {0.0, 0.9, 1.8, 2.6, 3.3, 4.4, 5.2, 6.1, 6.5, 7.4};
	QVector<double> yData = {5.9, 5.4, 4.4, 4.6, 3.5, 3.7, 2.8, 2.8, 2.4, 1.5};
	//	QVector<double> xError = {1000.,1000.,500.,800.,200.,80.,60.,20.,1.8,1.0};
	QVector<double> yError = {1.0, 1.8, 4., 8., 20., 20., 70., 70., 100., 500.};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	Column yErrorColumn(QStringLiteral("yerr"), AbstractColumn::ColumnMode::Double);
	yErrorColumn.replaceValues(0, yError);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);
	fitCurve.setYErrorColumn(&yErrorColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 1;
	XYFitCurve::initFitData(fitData);
	//	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 5. << -0.5;
	fitData.yWeightsType = nsl_fit_weight_direct;
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 6.10010931666575
	FuzzyCompare(fitResult.paramValues.at(0), 6.10010931635002, 1.e-10);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.424059452104775, i386: 0.424059429083679
	FuzzyCompare(fitResult.errorValues.at(0), 0.424059452104785, 1.e-10);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.610812956583933, i386: -0.610812954591566
	FuzzyCompare(fitResult.paramValues.at(1), -0.610812956537254, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0623409539388997, i386: 0.0623409508171503
	FuzzyCompare(fitResult.errorValues.at(1), 0.0623409539389024, 1.e-10);

	QCOMPARE(fitResult.rms, 4.29315093729054);
	QCOMPARE(fitResult.rsd, 2.07199202153158);
	QCOMPARE(fitResult.sse, 34.3452074983243);
	DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0.000101015996328551
	// TODO	QCOMPARE(fitResult.fdist_p, 3.51725605201025e-05);
}

// see http://gnuplot.sourceforge.net/demo_5.2/fit.html
void FitTest::testLinearGP_PY_yerror_custom() {
	// Pearson's data and York's weights
	QVector<double> xData = {0.0, 0.9, 1.8, 2.6, 3.3, 4.4, 5.2, 6.1, 6.5, 7.4};
	QVector<double> yData = {5.9, 5.4, 4.4, 4.6, 3.5, 3.7, 2.8, 2.8, 2.4, 1.5};
	//	QVector<double> xError = {1000.,1000.,500.,800.,200.,80.,60.,20.,1.8,1.0};
	QVector<double> yError = {1.0, 1.8, 4., 8., 20., 20., 70., 70., 100., 500.};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	Column yErrorColumn(QStringLiteral("yerr"), AbstractColumn::ColumnMode::Double);
	yErrorColumn.replaceValues(0, yError);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);
	fitCurve.setYErrorColumn(&yErrorColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("a1 + a2 * x");
	fitData.paramNames << QStringLiteral("a1") << QStringLiteral("a2");
	fitData.eps = 1.e-9;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 5. << -0.5;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}

	fitData.yWeightsType = nsl_fit_weight_direct;
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 6.10010932451396
	FuzzyCompare(fitResult.paramValues.at(0), 6.10010931635002, 1.e-8);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.424059453530443 (new: 0.424059492144904)
	FuzzyCompare(fitResult.errorValues.at(0), 0.424059452104785, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.610812957040808 (new: -0.610812955065274)
	FuzzyCompare(fitResult.paramValues.at(1), -0.610812956537254, 1.e-8);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0623409543258892 (new: 0.0623409596911536)
	FuzzyCompare(fitResult.errorValues.at(1), 0.0623409539389024, 1.e-7);

	QCOMPARE(fitResult.rms, 4.29315093729054);
	QCOMPARE(fitResult.rsd, 2.07199202153158);
	QCOMPARE(fitResult.sse, 34.3452074983243);
	DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0.000101015996328551
	// TODO	QCOMPARE(fitResult.fdist_p, 3.51725605201025e-05);
}

// see http://gnuplot.sourceforge.net/demo_5.2/fit.html
void FitTest::testLinearGP_PY_xyerror_polynomial() {
	// Pearson's data and York's weights
	QVector<double> xData = {0.0, 0.9, 1.8, 2.6, 3.3, 4.4, 5.2, 6.1, 6.5, 7.4};
	QVector<double> yData = {5.9, 5.4, 4.4, 4.6, 3.5, 3.7, 2.8, 2.8, 2.4, 1.5};
	QVector<double> xError = {1000., 1000., 500., 800., 200., 80., 60., 20., 1.8, 1.0};
	QVector<double> yError = {1.0, 1.8, 4., 8., 20., 20., 70., 70., 100., 500.};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	Column xErrorColumn(QStringLiteral("xerr"), AbstractColumn::ColumnMode::Double);
	xErrorColumn.replaceValues(0, xError);

	Column yErrorColumn(QStringLiteral("yerr"), AbstractColumn::ColumnMode::Double);
	yErrorColumn.replaceValues(0, yError);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);
	fitCurve.setXErrorColumn(&xErrorColumn);
	fitCurve.setYErrorColumn(&yErrorColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_basic;
	fitData.modelType = nsl_fit_model_polynomial;
	fitData.degree = 1;
	XYFitCurve::initFitData(fitData);
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 5. << -0.5;
	fitData.xWeightsType = nsl_fit_weight_direct;
	fitData.yWeightsType = nsl_fit_weight_direct;
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 5.3960522989993
	FuzzyCompare(fitResult.paramValues.at(0), 5.39749958415886, 3.e-4); // gnuplot result ("effective variance" method)
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.361458387983792
	FuzzyCompare(fitResult.errorValues.at(0), 0.361439886824914, 1.e-4); // -""-
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.463448925094435
	FuzzyCompare(fitResult.paramValues.at(1), -0.463744669606207, 1.e-3); // -""-
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0706627287153768
	FuzzyCompare(fitResult.errorValues.at(1), 0.0706637562101211, 1.e-4); // -""-

	DEBUG(std::setprecision(15) << fitResult.rms); // result: 1.49455608152396
	FuzzyCompare(fitResult.rms, 1.49417194665446, 1.e-3); // gnuplot result ("effective variance" method)
	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 1.22252038082151
	FuzzyCompare(fitResult.rsd, 1.22236326296828, 1.e-3); // -""-
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 11.9564486521917
	FuzzyCompare(fitResult.sse, 11.9533755732357, 1.e-3); // -""-
	DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0.00441031749154456
	// TODO	QCOMPARE(fitResult.fdist_p, 0.153296328355244);
}

// see http://gnuplot.sourceforge.net/demo_5.2/fit.html
void FitTest::testLinearGP_PY_xyerror_custom() {
	// Pearson's data and York's weights
	QVector<double> xData = {0.0, 0.9, 1.8, 2.6, 3.3, 4.4, 5.2, 6.1, 6.5, 7.4};
	QVector<double> yData = {5.9, 5.4, 4.4, 4.6, 3.5, 3.7, 2.8, 2.8, 2.4, 1.5};
	QVector<double> xError = {1000., 1000., 500., 800., 200., 80., 60., 20., 1.8, 1.0};
	QVector<double> yError = {1.0, 1.8, 4., 8., 20., 20., 70., 70., 100., 500.};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	Column xErrorColumn(QStringLiteral("xerr"), AbstractColumn::ColumnMode::Double);
	xErrorColumn.replaceValues(0, xError);

	Column yErrorColumn(QStringLiteral("yerr"), AbstractColumn::ColumnMode::Double);
	yErrorColumn.replaceValues(0, yError);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);
	fitCurve.setXErrorColumn(&xErrorColumn);
	fitCurve.setYErrorColumn(&yErrorColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("a1 + a2 * x");
	fitData.paramNames << QStringLiteral("a1") << QStringLiteral("a2");
	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 5. << -0.5;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}

	fitData.xWeightsType = nsl_fit_weight_direct;
	fitData.yWeightsType = nsl_fit_weight_direct;
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 5.39605223831693
	FuzzyCompare(fitResult.paramValues.at(0), 5.39749958415886, 3.e-4); // gnuplot result ("effective variance" method)
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.361458369696386
	FuzzyCompare(fitResult.errorValues.at(0), 0.361439886824914, 1.e-4); // -""-
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.463448913528306
	FuzzyCompare(fitResult.paramValues.at(1), -0.463744669606207, 1.e-3); // -""-
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0706627257036884
	FuzzyCompare(fitResult.errorValues.at(1), 0.0706637562101211, 1.e-4); // -""-

	DEBUG(std::setprecision(15) << fitResult.rms); // result: 1.49455596317092
	FuzzyCompare(fitResult.rms, 1.49417194665446, 1.e-3); // gnuplot result ("effective variance" method)
	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 1.22252033241616
	FuzzyCompare(fitResult.rsd, 1.22236326296828, 1.e-3); // -""-
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 11.9564477053674
	FuzzyCompare(fitResult.sse, 11.9533755732357, 1.e-3); // -""-
	DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0.00441031645455255
	// TODO	QCOMPARE(fitResult.fdist_p, 0.153296328355244);
}

// see http://gnuplot.sourceforge.net/demo_5.2/fit.html
void FitTest::testLinearGP_PY_xyerror_custom_instrumental_weight() {
	// Pearson's data and York's weights
	QVector<double> xData = {0.0, 0.9, 1.8, 2.6, 3.3, 4.4, 5.2, 6.1, 6.5, 7.4};
	QVector<double> yData = {5.9, 5.4, 4.4, 4.6, 3.5, 3.7, 2.8, 2.8, 2.4, 1.5};
	QVector<double> xError = {1000., 1000., 500., 800., 200., 80., 60., 20., 1.8, 1.0};
	QVector<double> yError = {1.0, 1.8, 4., 8., 20., 20., 70., 70., 100., 500.};

	// w_i -> s_i
	for (int i = 0; i < xError.size(); i++) {
		xError[i] = 1. / sqrt(xError[i]);
		yError[i] = 1. / sqrt(yError[i]);
	}

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	Column xErrorColumn(QStringLiteral("xerr"), AbstractColumn::ColumnMode::Double);
	xErrorColumn.replaceValues(0, xError);

	Column yErrorColumn(QStringLiteral("yerr"), AbstractColumn::ColumnMode::Double);
	yErrorColumn.replaceValues(0, yError);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);
	fitCurve.setXErrorColumn(&xErrorColumn);
	fitCurve.setYErrorColumn(&yErrorColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("a1 + a2 * x");
	fitData.paramNames << QStringLiteral("a1") << QStringLiteral("a2");
	//	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 5. << -0.5;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}

	fitData.xWeightsType = nsl_fit_weight_instrumental;
	fitData.yWeightsType = nsl_fit_weight_instrumental;
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 5.3960521880058
	FuzzyCompare(fitResult.paramValues.at(0), 5.39749958415886, 3.e-4); // gnuplot result ("effective variance" method)
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.361458367406729
	FuzzyCompare(fitResult.errorValues.at(0), 0.361439886824914, 1.e-4); // -""-
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.463448904801834
	FuzzyCompare(fitResult.paramValues.at(1), -0.463744669606207, 1.e-3); // -""-
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0706627229337577
	FuzzyCompare(fitResult.errorValues.at(1), 0.0706637562101211, 1.e-4); // -""-

	DEBUG(std::setprecision(15) << fitResult.rms); // result: 1.49455610878403
	FuzzyCompare(fitResult.rms, 1.49417194665446, 1.e-3); // gnuplot result ("effective variance" method)
	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 1.22252039197063
	FuzzyCompare(fitResult.rsd, 1.22236326296828, 1.e-3); // -""-
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 11.9564488702722
	FuzzyCompare(fitResult.sse, 11.9533755732357, 1.e-3); // -""-
	DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0.00441031773039329
	// TODO	QCOMPARE(fitResult.fdist_p, 0.153296328355244);
}

// see http://gnuplot.sourceforge.net/demo_5.2/fit.html
void FitTest::testLinearGP_PY_xyerror_custom_inverse_weight() {
	// Pearson's data and York's weights
	QVector<double> xData = {0.0, 0.9, 1.8, 2.6, 3.3, 4.4, 5.2, 6.1, 6.5, 7.4};
	QVector<double> yData = {5.9, 5.4, 4.4, 4.6, 3.5, 3.7, 2.8, 2.8, 2.4, 1.5};
	QVector<double> xError = {1000., 1000., 500., 800., 200., 80., 60., 20., 1.8, 1.0};
	QVector<double> yError = {1.0, 1.8, 4., 8., 20., 20., 70., 70., 100., 500.};

	// w_i -> 1/w_i
	for (int i = 0; i < xError.size(); i++) {
		xError[i] = 1. / xError[i];
		yError[i] = 1. / yError[i];
	}

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	Column xErrorColumn(QStringLiteral("xerr"), AbstractColumn::ColumnMode::Double);
	xErrorColumn.replaceValues(0, xError);

	Column yErrorColumn(QStringLiteral("yerr"), AbstractColumn::ColumnMode::Double);
	yErrorColumn.replaceValues(0, yError);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);
	fitCurve.setXErrorColumn(&xErrorColumn);
	fitCurve.setYErrorColumn(&yErrorColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("a1 + a2 * x");
	fitData.paramNames << QStringLiteral("a1") << QStringLiteral("a2");
	//	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 5. << -0.5;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}

	fitData.xWeightsType = nsl_fit_weight_inverse;
	fitData.yWeightsType = nsl_fit_weight_inverse;
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 5.39605219384715
	FuzzyCompare(fitResult.paramValues.at(0), 5.39749958415886, 3.e-4); // gnuplot result ("effective variance" method)
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.361458367208767
	FuzzyCompare(fitResult.errorValues.at(0), 0.361439886824914, 1.e-4); // -""-
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.463448906090293
	FuzzyCompare(fitResult.paramValues.at(1), -0.463744669606207, 1.e-3); // -""-
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0706627222052917
	FuzzyCompare(fitResult.errorValues.at(1), 0.0706637562101211, 1.e-4); // -""-

	DEBUG(std::setprecision(15) << fitResult.rms); // result: 1.49455610679827
	FuzzyCompare(fitResult.rms, 1.49417194665446, 1.e-3); // gnuplot result ("effective variance" method)
	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 1.22252039115847
	FuzzyCompare(fitResult.rsd, 1.22236326296828, 1.e-3); // -""-
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 11.9564488543861
	FuzzyCompare(fitResult.sse, 11.9533755732357, 1.e-3); // -""-
	DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0.00441031771299435
	// TODO	QCOMPARE(fitResult.fdist_p, 0.153296328355244);
}

// see https://bugs.kde.org/show_bug.cgi?id=408535
void FitTest::testNonLinear_yerror_zero_bug408535() {
	QVector<double> xData = {0.0,  320., 320., 360., 360., 400., 400., 440., 440., 480., 480., 520., 520., 560.,  560.,	 600.,	600., 640.,
							 640., 680., 680., 720., 720., 760., 760., 800., 800., 840., 840., 880., 880., 1200., 1200., 1200., 1200.};
	QVector<double> yData = {0.0,  160., 120., 190., 120., 210., 140., 220., 170., 230., 190., 240., 210., 250., 230., 270., 240., 260.,
							 270., 290., 240., 310., 270., 320., 290., 330., 290., 320., 300., 330., 310., 370., 390., 390., 400.};
	QVector<double> yError = {0.0, 15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15.,
							  15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15., 15.};

	// data source columns
	Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
	xDataColumn.replaceValues(0, xData);

	Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
	yDataColumn.replaceValues(0, yData);

	Column yErrorColumn(QStringLiteral("yerr"), AbstractColumn::ColumnMode::Double);
	yErrorColumn.replaceValues(0, yError);

	XYFitCurve fitCurve(QStringLiteral("fit"));
	fitCurve.setXDataColumn(&xDataColumn);
	fitCurve.setYDataColumn(&yDataColumn);
	fitCurve.setYErrorColumn(&yErrorColumn);

	// prepare the fit
	XYFitCurve::FitData fitData = fitCurve.fitData();
	fitData.modelCategory = nsl_fit_model_custom;
	XYFitCurve::initFitData(fitData);
	fitData.model = QStringLiteral("A*exp(-B/x)");
	fitData.paramNames << QStringLiteral("A") << QStringLiteral("B");
	//	fitData.eps = 1.e-12;
	const int np = fitData.paramNames.size();
	fitData.paramStartValues << 100. << 100.;
	for (int i = 0; i < np; i++) {
		fitData.paramLowerLimits << -std::numeric_limits<double>::max();
		fitData.paramUpperLimits << std::numeric_limits<double>::max();
	}

	fitData.yWeightsType = nsl_fit_weight_instrumental;
	fitCurve.setFitData(fitData);

	// perform the fit
	fitCurve.recalculate();
	const XYFitCurve::FitResult& fitResult = fitCurve.fitResult();

	// check the results
	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	QCOMPARE(np, 2);

	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 557.463875234563, Win: 557.463888957409
	FuzzyCompare(fitResult.paramValues.at(0), 557.463875234563, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 22.0490332546127, FreeBSD: 22.0490429285665
	FuzzyCompare(fitResult.errorValues.at(0), 22.0490332546127, 1.e-6);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 468.319724706721, Win: 468.319742522772
	FuzzyCompare(fitResult.paramValues.at(1), 468.319724706721, 1.e-7);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 26.5105791274139, FreeBSD: 26.510591238283
	FuzzyCompare(fitResult.errorValues.at(1), 26.5105791274139, 1.e-6);

	DEBUG(std::setprecision(15) << fitResult.rms); // result: 1.96172656494138
	FuzzyCompare(fitResult.rms, 1.96172656494138, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.rsd); // result: 1.4006164945985
	FuzzyCompare(fitResult.rsd, 1.4006164945985, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.sse); // result: 64.7369766430654
	FuzzyCompare(fitResult.sse, 64.7369766430654, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.rsquare); // result: 0.999740417228134
	FuzzyCompare(fitResult.rsquare, 0.999740417228134, 1.e-9);
	DEBUG(std::setprecision(15) << fitResult.rsquareAdj); // result: 0.999724193304893
	FuzzyCompare(fitResult.rsquareAdj, 0.999724193304893, 1.e-9);
}

void FitTest::testHistogramFit() {
	Project project;
	project.load(QFINDTESTDATA(QLatin1String("data/TestHistogramFit.lml")));

	auto* aspect = project.child<AbstractAspect>(0);
	QVERIFY(aspect != nullptr);
	QCOMPARE(aspect->name(), QLatin1String("Spreadsheet"));
	QVERIFY(aspect->type() == AspectType::Spreadsheet);
	aspect = project.child<AbstractAspect>(1);
	QVERIFY(aspect != nullptr);

	QCOMPARE(aspect->name(), QLatin1String("Worksheet - Spreadsheet"));
	QVERIFY(aspect->type() == AspectType::Worksheet);
	auto w = dynamic_cast<Worksheet*>(aspect);
	if (!w)
		return;

	auto plot = dynamic_cast<CartesianPlot*>(aspect->child<CartesianPlot>(0));
	QVERIFY(plot != nullptr);

	QCOMPARE(plot->name(), QLatin1String("Plot - Spreadsheet"));
	auto hist = dynamic_cast<Histogram*>(plot->child<Histogram>(0));
	QVERIFY(hist != nullptr);

	QCOMPARE(hist->name(), QLatin1String("2"));
	// Do the fit
	plot->addHistogramFit(hist, nsl_sf_stats_gaussian);

	auto fit = dynamic_cast<XYFitCurve*>(plot->child<XYFitCurve>(0));
	QVERIFY(fit != nullptr);

	QCOMPARE(fit->name(), i18n("Distribution Fit to '%1'", hist->name()));
	// get results
	const XYFitCurve::FitResult& fitResult = fit->fitResult();

	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	// ML results
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0));
	QCOMPARE(fitResult.paramValues.at(0), 1.);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1), 0.999776858937);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2));
	QCOMPARE(fitResult.paramValues.at(2), -0.0294045302042);

	/* LM results
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(0));
	QCOMPARE(fitResult.paramValues.at(0), 0.999829585605626);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(0));
	QCOMPARE(fitResult.errorValues.at(0), 0.0313884775124071);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1), 0.988871409810129);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(1));
	QCOMPARE(fitResult.errorValues.at(1), 0.0358652107488108);
	DEBUG(std::setprecision(15) << fitResult.paramValues.at(2));
	QCOMPARE(fitResult.paramValues.at(2), -0.138053471452345);
	DEBUG(std::setprecision(15) << fitResult.errorValues.at(2));
	QCOMPARE(fitResult.errorValues.at(2), 0.0358379697268546);

	DEBUG(std::setprecision(15) << fitResult.rms); // result:
	QCOMPARE(fitResult.rms, 0.000890023253844838);
	DEBUG(std::setprecision(15) << fitResult.rsd); // result:
	QCOMPARE(fitResult.rsd, 0.0298332575131318);
	DEBUG(std::setprecision(15) << fitResult.sse); // result:
	QCOMPARE(fitResult.sse, 0.0249206511076555);
	DEBUG(std::setprecision(15) << fitResult.rsquare); // result:
	QCOMPARE(fitResult.rsquare, 0.961753741845289);
	DEBUG(std::setprecision(15) << fitResult.rsquareAdj); // result:
	QCOMPARE(fitResult.rsquareAdj, 0.957504157605876);
	*/
}

void FitTest::testHistogramGaussianML() {
	Spreadsheet spreadsheet(QStringLiteral("test"), false);
	AsciiFilter filter;

	const QString& fileName = QFINDTESTDATA(QLatin1String("data/Gaussian.dat"));

	auto properties = filter.properties();
	properties.headerEnabled = false;
	filter.setProperties(properties);
	filter.readDataFromFile(fileName, &spreadsheet, AbstractFileFilter::ImportMode::Replace);

	QCOMPARE(spreadsheet.rowCount(), 1000);
	QCOMPARE(spreadsheet.columnCount(), 1);

	Worksheet worksheet(QStringLiteral("test"), false);
	auto* plot = new CartesianPlot(QStringLiteral("plot"));
	worksheet.addChild(plot);

	auto* hist = new Histogram(QStringLiteral("Histogram"));
	plot->addChild(hist);
	hist->setDataColumn(spreadsheet.column(0));

	// Do the fit
	plot->addHistogramFit(hist, nsl_sf_stats_gaussian);

	auto fit = dynamic_cast<XYFitCurve*>(plot->child<XYFitCurve>(0));
	QVERIFY(fit != nullptr);

	QCOMPARE(fit->name(), i18n("Distribution Fit to '%1'", hist->name()));
	// get results
	const XYFitCurve::FitResult& fitResult = fit->fitResult();

	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	WARN(std::setprecision(15) << fitResult.paramValues.at(0));
	QCOMPARE(fitResult.paramValues.at(0), 210.380459328);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1), 0.999776858937);
	WARN(std::setprecision(15) << fitResult.errorValues.at(1));
	QCOMPARE(fitResult.errorValues.at(1), 0.0223507017166885);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1) - fitResult.marginValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1) - fitResult.marginValues.at(1), 0.955917043549699);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1) + fitResult.marginValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1) + fitResult.marginValues.at(1), 1.0436366743251);

	WARN(std::setprecision(15) << fitResult.paramValues.at(2));
	QCOMPARE(fitResult.paramValues.at(2), -0.0294045302042);
	WARN(std::setprecision(15) << fitResult.errorValues.at(2));
	QCOMPARE(fitResult.errorValues.at(2), 0.0316157202617);
	WARN(std::setprecision(15) << fitResult.paramValues.at(2) - fitResult.marginValues.at(2));
	QCOMPARE(fitResult.paramValues.at(2) - fitResult.marginValues.at(2), -0.0914455198610062);
	WARN(std::setprecision(15) << fitResult.paramValues.at(2) + fitResult.marginValues.at(2));
	QCOMPARE(fitResult.paramValues.at(2) + fitResult.marginValues.at(2), 0.0326364594526431);
}

void FitTest::testHistogramExponentialML() {
	Spreadsheet spreadsheet(QStringLiteral("test"), false);
	AsciiFilter filter;

	const QString& fileName = QFINDTESTDATA(QLatin1String("data/Exponential.dat"));

	auto properties = filter.properties();
	properties.headerEnabled = false;
	filter.setProperties(properties);
	filter.readDataFromFile(fileName, &spreadsheet, AbstractFileFilter::ImportMode::Replace);

	QCOMPARE(spreadsheet.rowCount(), 100);
	QCOMPARE(spreadsheet.columnCount(), 1);

	Worksheet worksheet(QStringLiteral("test"), false);
	auto* plot = new CartesianPlot(QStringLiteral("plot"));
	worksheet.addChild(plot);

	auto* hist = new Histogram(QStringLiteral("Histogram"));
	plot->addChild(hist);
	hist->setDataColumn(spreadsheet.column(0));

	// Do the fit
	plot->addHistogramFit(hist, nsl_sf_stats_exponential);

	auto fit = dynamic_cast<XYFitCurve*>(plot->child<XYFitCurve>(0));
	QVERIFY(fit != nullptr);

	QCOMPARE(fit->name(), i18n("Distribution Fit to '%1'", hist->name()));
	// get results
	const XYFitCurve::FitResult& fitResult = fit->fitResult();

	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	WARN(std::setprecision(15) << fitResult.paramValues.at(0));
	QCOMPARE(fitResult.paramValues.at(0), 41.6543778722);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1), 1.93906050400681);
	WARN(std::setprecision(15) << fitResult.errorValues.at(1));
	QCOMPARE(fitResult.errorValues.at(1), 0.195884683568035);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1) - fitResult.marginValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1) - fitResult.marginValues.at(1), 1.5740096363284);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1) + fitResult.margin2Values.at(1));
	QCOMPARE(fitResult.paramValues.at(1) + fitResult.margin2Values.at(1), 2.34119114746796);
	WARN(std::setprecision(15) << fitResult.paramValues.at(2));
	QCOMPARE(fitResult.paramValues.at(2), 5.01032231564491);
}

void FitTest::testHistogramLaplaceML() {
	Spreadsheet spreadsheet(QStringLiteral("test"), false);
	AsciiFilter filter;

	const QString& fileName = QFINDTESTDATA(QLatin1String("data/Laplace.dat"));

	auto properties = filter.properties();
	properties.headerEnabled = false;
	filter.setProperties(properties);
	filter.readDataFromFile(fileName, &spreadsheet, AbstractFileFilter::ImportMode::Replace);

	QCOMPARE(spreadsheet.rowCount(), 100);
	QCOMPARE(spreadsheet.columnCount(), 1);

	Worksheet worksheet(QStringLiteral("test"), false);
	auto* plot = new CartesianPlot(QStringLiteral("plot"));
	worksheet.addChild(plot);

	auto* hist = new Histogram(QStringLiteral("Histogram"));
	plot->addChild(hist);
	hist->setDataColumn(spreadsheet.column(0));

	// Do the fit
	plot->addHistogramFit(hist, nsl_sf_stats_laplace);

	auto fit = dynamic_cast<XYFitCurve*>(plot->child<XYFitCurve>(0));
	QVERIFY(fit != nullptr);

	QCOMPARE(fit->name(), i18n("Distribution Fit to '%1'", hist->name()));
	// get results
	const XYFitCurve::FitResult& fitResult = fit->fitResult();

	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	WARN(std::setprecision(15) << fitResult.paramValues.at(0));
	QCOMPARE(fitResult.paramValues.at(0), 145.64147805241);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1), 1.99538835213796);
	WARN(std::setprecision(15) << fitResult.paramValues.at(2));
	QCOMPARE(fitResult.paramValues.at(2), 4.95890340967321);
}

void FitTest::testHistogramCauchyML() {
	Spreadsheet spreadsheet(QStringLiteral("test"), false);
	AsciiFilter filter;

	const QString& fileName = QFINDTESTDATA(QLatin1String("data/Cauchy.dat"));

	auto properties = filter.properties();
	properties.headerEnabled = false;
	filter.setProperties(properties);
	filter.readDataFromFile(fileName, &spreadsheet, AbstractFileFilter::ImportMode::Replace);

	QCOMPARE(spreadsheet.rowCount(), 1000);
	QCOMPARE(spreadsheet.columnCount(), 1);

	Worksheet worksheet(QStringLiteral("test"), false);
	auto* plot = new CartesianPlot(QStringLiteral("plot"));
	worksheet.addChild(plot);

	auto* hist = new Histogram(QStringLiteral("Histogram"));
	plot->addChild(hist);
	hist->setDataColumn(spreadsheet.column(0));

	// Do the fit
	plot->addHistogramFit(hist, nsl_sf_stats_cauchy_lorentz);

	auto fit = dynamic_cast<XYFitCurve*>(plot->child<XYFitCurve>(0));
	QVERIFY(fit != nullptr);

	QCOMPARE(fit->name(), i18n("Distribution Fit to '%1'", hist->name()));
	// get results
	const XYFitCurve::FitResult& fitResult = fit->fitResult();

	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	WARN(std::setprecision(15) << fitResult.paramValues.at(0));
	QCOMPARE(fitResult.paramValues.at(0), 34973.659700564);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1), 1.87876026823743);
	WARN(std::setprecision(15) << fitResult.paramValues.at(2));
	QCOMPARE(fitResult.paramValues.at(2), 5.09155507540282);
}

void FitTest::testHistogramLognormalML() {
	Spreadsheet spreadsheet(QStringLiteral("test"), false);
	AsciiFilter filter;

	const QString& fileName = QFINDTESTDATA(QLatin1String("data/Lognormal.dat"));

	auto properties = filter.properties();
	properties.headerEnabled = false;
	filter.setProperties(properties);
	filter.readDataFromFile(fileName, &spreadsheet, AbstractFileFilter::ImportMode::Replace);

	QCOMPARE(spreadsheet.rowCount(), 1000);
	QCOMPARE(spreadsheet.columnCount(), 1);

	Worksheet worksheet(QStringLiteral("test"), false);
	auto* plot = new CartesianPlot(QStringLiteral("plot"));
	worksheet.addChild(plot);

	auto* hist = new Histogram(QStringLiteral("Histogram"));
	plot->addChild(hist);
	hist->setDataColumn(spreadsheet.column(0));

	// Do the fit
	plot->addHistogramFit(hist, nsl_sf_stats_lognormal);

	auto fit = dynamic_cast<XYFitCurve*>(plot->child<XYFitCurve>(0));
	QVERIFY(fit != nullptr);

	QCOMPARE(fit->name(), i18n("Distribution Fit to '%1'", hist->name()));
	// get results
	const XYFitCurve::FitResult& fitResult = fit->fitResult();

	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	WARN(std::setprecision(15) << fitResult.paramValues.at(0));
	QCOMPARE(fitResult.paramValues.at(0), 4714813.91602);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1), 2.07876791650682);
	WARN(std::setprecision(15) << fitResult.paramValues.at(2));
	QCOMPARE(fitResult.paramValues.at(2), 5.01070949852617);
}

void FitTest::testHistogramPoissonML() {
	Spreadsheet spreadsheet(QStringLiteral("test"), false);
	AsciiFilter filter;

	const QString& fileName = QFINDTESTDATA(QLatin1String("data/Poisson.dat"));

	auto properties = filter.properties();
	properties.headerEnabled = false;
	filter.setProperties(properties);
	filter.readDataFromFile(fileName, &spreadsheet, AbstractFileFilter::ImportMode::Replace);

	QCOMPARE(spreadsheet.rowCount(), 100);
	QCOMPARE(spreadsheet.columnCount(), 1);

	Worksheet worksheet(QStringLiteral("test"), false);
	auto* plot = new CartesianPlot(QStringLiteral("plot"));
	worksheet.addChild(plot);

	auto* hist = new Histogram(QStringLiteral("Histogram"));
	plot->addChild(hist);
	hist->setDataColumn(spreadsheet.column(0));

	// Do the fit
	plot->addHistogramFit(hist, nsl_sf_stats_poisson);

	auto fit = dynamic_cast<XYFitCurve*>(plot->child<XYFitCurve>(0));
	QVERIFY(fit != nullptr);

	QCOMPARE(fit->name(), i18n("Distribution Fit to '%1'", hist->name()));
	// get results
	const XYFitCurve::FitResult& fitResult = fit->fitResult();

	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	WARN(std::setprecision(15) << fitResult.paramValues.at(0));
	QCOMPARE(fitResult.paramValues.at(0), 150.);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1), 9.55);
	WARN(std::setprecision(15) << fitResult.errorValues.at(1));
	QCOMPARE(fitResult.errorValues.at(1), 0.309030742807249);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1) - fitResult.marginValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1) - fitResult.marginValues.at(1), 8.95383732390823);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1) + fitResult.margin2Values.at(1));
	QCOMPARE(fitResult.paramValues.at(1) + fitResult.margin2Values.at(1), 10.1754213697868);
}

void FitTest::testHistogramBinomialML() {
	Spreadsheet spreadsheet(QStringLiteral("test"), false);
	AsciiFilter filter;

	const QString& fileName = QFINDTESTDATA(QLatin1String("data/Binomial.dat"));

	auto properties = filter.properties();
	properties.headerEnabled = false;
	filter.setProperties(properties);
	filter.readDataFromFile(fileName, &spreadsheet, AbstractFileFilter::ImportMode::Replace);

	QCOMPARE(spreadsheet.rowCount(), 100);
	QCOMPARE(spreadsheet.columnCount(), 1);

	Worksheet worksheet(QStringLiteral("test"), false);
	auto* plot = new CartesianPlot(QStringLiteral("plot"));
	worksheet.addChild(plot);

	auto* hist = new Histogram(QStringLiteral("Histogram"));
	plot->addChild(hist);
	hist->setDataColumn(spreadsheet.column(0));

	// Do the fit
	plot->addHistogramFit(hist, nsl_sf_stats_binomial);

	auto fit = dynamic_cast<XYFitCurve*>(plot->child<XYFitCurve>(0));
	QVERIFY(fit != nullptr);

	QCOMPARE(fit->name(), i18n("Distribution Fit to '%1'", hist->name()));
	// get results
	const XYFitCurve::FitResult& fitResult = fit->fitResult();

	QCOMPARE(fitResult.available, true);
	QCOMPARE(fitResult.valid, true);

	WARN(std::setprecision(15) << fitResult.paramValues.at(0));
	QCOMPARE(fitResult.paramValues.at(0), 270.);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1), 0.4931);
	WARN(std::setprecision(15) << fitResult.errorValues.at(1));
	QCOMPARE(fitResult.errorValues.at(1), 0.0499952387733072);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1) - fitResult.marginValues.at(1));
	QCOMPARE(fitResult.paramValues.at(1) - fitResult.marginValues.at(1), 0.482953993308348);
	WARN(std::setprecision(15) << fitResult.paramValues.at(1) + fitResult.margin2Values.at(1));
	QCOMPARE(fitResult.paramValues.at(1) + fitResult.margin2Values.at(1), 0.503287947118723);

	QCOMPARE(fitResult.paramValues.at(2), spreadsheet.rowCount());
}

QTEST_MAIN(FitTest)
