// eval.cxx
// This file is part of galvani.
// Copyright (C) 2020-2024 Burkard Lutz <b.lutz@online.de>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.


#include <glib.h>
#include <gtkmm.h>
#include <cmath>
#include <glibmm/i18n.h>

#include "eval.h"

using namespace std;


void Project::show_data_transformation ()
{
    if (project->funktion_x != _("Time")) main_win->data_transform_dialog->evaloption_vol.set_sensitive (false);
	else main_win->data_transform_dialog->evaloption_vol.set_sensitive (true);
	main_win->data_transform_dialog->evaloption_vol.set_active(eval_titvolumen);
	main_win->data_transform_dialog->evaloption_volspeed.set_sensitive (eval_titvolumen);
	main_win->data_transform_dialog->evaloption_xreziproksw.set_active(eval_xreziprok);
	main_win->data_transform_dialog->evaloption_yreziproksw.set_active(eval_yreziprok);
	main_win->data_transform_dialog->evaloption_xlogsw.set_active(eval_xlog);
	main_win->data_transform_dialog->evaloption_ylogsw.set_active(eval_ylog);

	// Temperature data transformation (x)
    main_win->data_transform_dialog->evaloption_xtherm.remove_all();
    main_win->data_transform_dialog->evaloption_xtherm.append (_("none"));
    main_win->data_transform_dialog->evaloption_xtherm.set_active_text (_("none"));
	if (project->funktion_x == _("DC Voltage"))
	{
	    main_win->data_transform_dialog->evaloption_xtherm.set_sensitive (true);
	    main_win->data_transform_dialog->evaloption_xtherm.append ("Cu/CuNi");
	    main_win->data_transform_dialog->evaloption_xtherm.append ("Ni/NiCr");
	    if (eval_xtherm == CuNi) main_win->data_transform_dialog->evaloption_xtherm.set_active_text("Cu/CuNi");
	    else if (eval_xtherm == NiCr) main_win->data_transform_dialog->evaloption_xtherm.set_active_text("Ni/NiCr");
	}
	else if (project->funktion_x == _("Resistance"))
	{
	    main_win->data_transform_dialog->evaloption_xtherm.set_sensitive (true);
        main_win->data_transform_dialog->evaloption_xtherm.append ("Pt100");
        if (eval_xtherm == Pt100) main_win->data_transform_dialog->evaloption_xtherm.set_active_text("Pt100");
	}
	else main_win->data_transform_dialog->evaloption_xtherm.set_sensitive (false);

	// Temperature data transformation (y)
    main_win->data_transform_dialog->evaloption_ytherm.remove_all();
    main_win->data_transform_dialog->evaloption_ytherm.append (_("none"));
    main_win->data_transform_dialog->evaloption_ytherm.set_active_text (_("none"));
	if (project->funktion_y == _("DC Voltage"))
	{
	    main_win->data_transform_dialog->evaloption_ytherm.set_sensitive (true);
	    main_win->data_transform_dialog->evaloption_ytherm.append ("Cu/CuNi");
	    main_win->data_transform_dialog->evaloption_ytherm.append ("Ni/NiCr");
	    if (eval_ytherm == CuNi) main_win->data_transform_dialog->evaloption_ytherm.set_active_text("Cu/CuNi");
	    else if (eval_ytherm == NiCr) main_win->data_transform_dialog->evaloption_ytherm.set_active_text("Ni/NiCr");
	}
	else if (project->funktion_y == _("Resistance"))
	{
	    main_win->data_transform_dialog->evaloption_ytherm.set_sensitive (true);
        main_win->data_transform_dialog->evaloption_ytherm.append ("Pt100");
        if (eval_ytherm == Pt100) main_win->data_transform_dialog->evaloption_ytherm.set_active_text("Pt100");
	}
	else main_win->data_transform_dialog->evaloption_ytherm.set_sensitive (false);


	// Time -> Volume
	main_win->data_transform_dialog->evaloption_volspeed.set_value (60.0 * tit_volspeed);	// ml/sec -> ml/min

    main_win->data_transform_dialog->set_visible (true);
    if (!main_win->data_transform_dialog->resp_loop->is_running ()) main_win->data_transform_dialog->resp_loop-> run();  // start loop to pause program until response is received
    return;
}

void Project::transform_data ()
{
    std::string option;
	m_record xval, yval;

	main_win->eval_function_dialog->do_reset_eval_functions (); //evaluation parameters reset

	//Read Parameter
	eval_titvolumen = main_win->data_transform_dialog->evaloption_vol.get_active();
	eval_xreziprok = main_win->data_transform_dialog->evaloption_xreziproksw.get_active();
	check_xreziprok ();
	eval_yreziprok = main_win->data_transform_dialog->evaloption_yreziproksw.get_active();
	check_yreziprok ();
	eval_xlog = main_win->data_transform_dialog->evaloption_xlogsw.get_active();
	check_xlog ();
	eval_ylog = main_win->data_transform_dialog->evaloption_ylogsw.get_active();
	check_ylog ();
	option = main_win->data_transform_dialog->evaloption_xtherm.get_active_text();
	if (option == "Cu/CuNi") eval_xtherm = CuNi;
	else if (option == "Ni/NiCr") eval_xtherm = NiCr;
	else if (option == "Pt100") eval_xtherm = Pt100;
	else eval_xtherm = no;
	option = main_win->data_transform_dialog->evaloption_ytherm.get_active_text();
	if (option == "Cu/CuNi") eval_ytherm = CuNi;
	else if (option == "Ni/NiCr") eval_ytherm = NiCr;
	else if (option == "Pt100") eval_ytherm = Pt100;
	else eval_ytherm = no;
	tit_volspeed = main_win->data_transform_dialog->evaloption_volspeed.get_value () / 60.0;	// ml/min -> ml/sec
	if ((tit_volspeed == 0) || (project->funktion_x != _("Time"))) eval_titvolumen = false;
	main_win->data_transform_dialog->close ();

	 // calculate functions
	if (eval_reglin) lin_regression ();
	if (eval_integration) peak_integration ();
	if (eval_diff) diff_funktion ();

	// show diagram and results if there are data
	if (mw.size() != 0)
	{
	    main_win->diag.show_diag();
        if ((eval_reglin) || (eval_integration) || (eval_diff)) show_evalresults ();
        main_win->display_x.show ();
        main_win->display_y.show ();
    }
    else main_win->diag.queue_draw ();
    if (main_win->data_transform_dialog->resp_loop->is_running ()) main_win->data_transform_dialog->resp_loop-> quit();     //stop loop

    return;
}

bool EvalData_Transform_Dialog::show_eval_volspeed (bool state)
{
    bool active;

    active = evaloption_vol.get_active ();
    evaloption_volspeed.set_sensitive (active);
    evaloption_vol.set_state (active);

    return true;
}


void EvalData_Transform_Dialog::cancel_data_transformation ()
{
    main_win->data_transform_dialog->close ();
    if (resp_loop->is_running ()) resp_loop-> quit();     //stop loop
    return;
}


void EvalData_Transform_Dialog::reset_data_transformation ()
{
    if (!main_win->msg_dialog->dialog_ok(_("Reset data transformation parameters?"))) return;
    eval_titvolumen = false;
	eval_xreziprok = false;
	eval_yreziprok = false;
	eval_xlog = false;
	eval_ylog = false;
	eval_xtherm = no;
	eval_ytherm = no;
	tit_volspeed = 0.0;

	main_win->data_transform_dialog->close ();
	if (resp_loop->is_running ()) resp_loop-> quit();     //stop loop

    return;
}

void Project::show_eval_functions()
{
    Gdk::RGBA color;

    color.set_rgba(main_win->diag.funktcol_r, main_win->diag.funktcol_g, main_win->diag.funktcol_b);
	main_win->options_dialog->diag_funktcolor.set_rgba(color);
	main_win->eval_function_dialog->evaloption_reglin.set_active(eval_reglin);
	main_win->eval_function_dialog->evaloption_diff.set_active(eval_diff);
	main_win->eval_function_dialog->evaloption_integration.set_active(eval_integration);
	main_win->eval_function_dialog->integration_options.set_visible (eval_integration);
	main_win->eval_function_dialog->evaloption_minpeakscale.set_value(min_peakscale);
	main_win->eval_function_dialog->evaloption_minpeakscale.set_sensitive(eval_integration);
	main_win->eval_function_dialog->evaloption_mindiffscale.set_value(min_diffscale);
	main_win->eval_function_dialog->evaloption_mindiffscale.set_sensitive (eval_diff);
	main_win->eval_function_dialog->evaloption_show_baseline.set_active(main_win->diag.show_baseline);
	main_win->eval_function_dialog->evaloption_show_peakarea.set_active(main_win->diag.show_peakarea);
	main_win->eval_function_dialog->evaloption_show_peaklimits.set_active(main_win->diag.show_peaklimits);

    main_win->eval_function_dialog->set_visible (true);
    if (!main_win->eval_function_dialog->resp_loop->is_running ()) main_win->eval_function_dialog->resp_loop-> run();  // start loop to pause program until response is received
    return;
}

void Project::evaluate_functions ()
{
    Gdk::RGBA color;
	std::string option;

	//read parameter
	color = main_win->options_dialog->diag_funktcolor.get_rgba();
	main_win->diag.funktcol_r = color.get_red();
	main_win->diag.funktcol_g = color.get_green();
	main_win->diag.funktcol_b = color.get_blue();
	eval_reglin = main_win->eval_function_dialog->evaloption_reglin.get_active();
	eval_diff = main_win->eval_function_dialog->evaloption_diff.get_active();
	eval_integration = main_win->eval_function_dialog->evaloption_integration.get_active();
	main_win->diag.show_baseline = main_win->eval_function_dialog->evaloption_show_baseline.get_active();
	main_win->diag.show_peakarea = main_win->eval_function_dialog->evaloption_show_peakarea.get_active();
	main_win->diag.show_peaklimits = main_win->eval_function_dialog->evaloption_show_peaklimits.get_active();
	min_peakscale = main_win->eval_function_dialog->evaloption_minpeakscale.get_value ();
	min_diffscale = main_win->eval_function_dialog->evaloption_mindiffscale.get_value ();
	main_win->eval_function_dialog->close ();

    // calculate functions
	if (eval_reglin) lin_regression ();
	if (eval_integration) peak_integration ();
	if (eval_diff) diff_funktion ();

	// show diagram and results
	main_win->diag.show_diag();
    if ((eval_reglin) || (eval_integration) || (eval_diff)) show_evalresults ();
    main_win->display_x.show ();
    main_win->display_y.show ();
    if (main_win->data_transform_dialog->resp_loop->is_running ()) main_win->data_transform_dialog->resp_loop-> quit();     //stop loop

    return;
}

void Eval_Function_Dialog::cancel_eval_functions ()
{
    main_win->eval_function_dialog->close ();
    if (resp_loop->is_running ()) resp_loop-> quit();     //stop loop
    return;
}

void Eval_Function_Dialog::do_reset_eval_functions ()
{
    main_win->diag.funktcol_r = 0.0;
	main_win->diag.funktcol_g = 0.0;
	main_win->diag.funktcol_b = 1.0;
	eval_reglin = false;
	eval_integration = false;
	main_win->diag.show_baseline = true;
	main_win->diag.show_peakarea = true;
	main_win->diag.show_peaklimits = true;
	eval_diff = false;
	min_peakscale = 0.1;
	min_diffscale = 5.0;

    return;
}

void Eval_Function_Dialog::reset_eval_functions ()
{
    if (!main_win->msg_dialog->dialog_ok(_("Reset evaluation functions?"))) return;
    do_reset_eval_functions ();
	main_win->eval_function_dialog->close ();
	if (resp_loop->is_running ()) resp_loop-> quit();     //stop loop

    return;
}

bool Eval_Function_Dialog::show_mindiffscale (bool state)
{
    bool active;

    active = evaloption_diff.get_active ();
    evaloption_mindiffscale.set_sensitive (active);
    evaloption_diff.set_state (active);
    evaloption_mindiffscale.set_value(min_diffscale);

    return true;
}

bool Eval_Function_Dialog::show_evaloptions_integration (bool state)
{
	bool int_opt, active;

    //integration options
	int_opt = evaloption_integration.get_active ();
	integration_options.set_visible (int_opt);
	evaloption_integration.set_state (int_opt);

	//minimum peak size
	active = evaloption_integration.get_active ();
    evaloption_minpeakscale.set_sensitive (active);
    evaloption_integration.set_state (active);
    evaloption_minpeakscale.set_value(min_peakscale);

    set_default_size (-1,-1);

	return true;
}


void check_xlog ()
{
	mw_data val;
	std::string error_str;

	for (unsigned long long int i=0; i<mi;i++)
	{
		val = mw.at(i);
		if (val.x <= 0)
		{
			if (eval_xlog)
			{
				error_str = "x -> log x : ";
				error_str = error_str + _("Operation cannot be executed");
				error_msg (error_str);
				eval_xlog = false;
				break;
			}
		}
	}
	return;
}

void check_xreziprok ()
{
	mw_data val;
	std::string error_str;

	for (unsigned long long int i=0; i<mi;i++)
	{
		val = mw.at(i);
		if (val.x == 0)
		{
			if (eval_xreziprok)
			{
				error_str = "x -> 1/x : ";
				error_str = error_str + _("Operation cannot be executed");
				error_msg (error_str);
				eval_xreziprok = false;
				break;
			}
		}
	}
	return;
}

void check_ylog ()
{
	mw_data val;
	std::string error_str;

	for (unsigned long long int i=0; i<mi;i++)
	{
		val = mw.at(i);
		if (val.y <= 0)
		{
			if (eval_ylog)
			{
				error_str = "y -> log y : ";
				error_str = error_str + _("Operation cannot be executed");
				error_msg (error_str);
				eval_ylog = false;
				break;
			}
		}
	}
	return;
}

void check_yreziprok ()
{
	mw_data val;
	std::string error_str;

	for (unsigned long long int i=0; i<mi;i++)
	{
		val = mw.at(i);
		if (val.y == 0)
		{
			if (eval_yreziprok)
			{
				error_str = "y -> 1/y : ";
				error_str = error_str + _("Operation cannot be executed");
				error_msg (error_str);
				eval_yreziprok = false;
				break;
			}
		}
	}
	return;
}


double trafo_log (double x)
{
	double result;

	result = log10 (x);
	return result;
}

double trafo_reziprok (double x)
{
	double result;

	result = 1/x;
	return result;
}

double trafo_cuni (double x) 	//Thermocouple voltage in mV
{
	double result;

	result = 25.8650796213774*x - 0.694899767425285*pow(x,2) + 0.026194980890306*pow(x,3);
	return result;
}

double trafo_nicr (double x)	//Thermocouple voltage in mV
{
	double result;

	result = 25.3500116061292*x - 0.397961523467875*pow(x,2) + 0.041291006439827*pow(x,3);
	return result;
}

double trafo_pt100 (double x)	//Thermal Resistance in Ohm
{
	double result;

	result = -249.6509 + 2.4571767*x + 1.54E-04*pow(x,2) + 0.0000023911275*pow(x,3);
	// deviation <= 0.01 °C (-10 - 110 °C)
	return result;
}

mw_data trafo (mw_data val)
{
	mw_data data;

	data.x = val.x;
	data.y = val.y;
	if (eval_titvolumen) data.x = data.x * tit_volspeed;
	if (eval_xtherm == CuNi) data.x = trafo_cuni (1000.0 * data.x);
	if (eval_ytherm == CuNi) data.y = trafo_cuni (1000.0 * data.y);
	if (eval_xtherm == NiCr) data.x = trafo_nicr (1000.0 * data.x);
	if (eval_ytherm == NiCr) data.y = trafo_nicr (1000.0 * data.y);
	if (eval_xtherm == Pt100) data.x = trafo_pt100 (data.x);
	if (eval_ytherm == Pt100) data.y = trafo_pt100 (data.y);
	if ((eval_xreziprok) && (data.x != 0)) data.x = 1/data.x;
	if ((eval_yreziprok) && (data.y != 0)) data.y = 1/data.y;
	if ((eval_xlog) && (data.x > 0)) data.x = log10(data.x);
	if ((eval_ylog) && (data.y > 0)) data.y = log10(data.y);

	return data;
}

void reset_evaloptions ()
{
	eval_titvolumen = false;
    tit_volspeed = 0.0;
	eval_xreziprok = false;
	eval_yreziprok = false;
	eval_xlog = false;
	eval_ylog = false;
	eval_reglin = false;
	eval_integration = false;
	eval_diff = false;
	eval_xtherm = no;
	eval_ytherm = no;
	main_win->diag.coord_saved = false;
	min_peakscale = 0.1;
	return;
}

std::string eval_funkt (int kanal, std::string funkt)
{
	if (kanal == 0)
	{
		if (eval_titvolumen) funkt = _("Volume");
		else if (eval_xtherm != no) funkt = _("Temperature");
		if (eval_xreziprok)
		{
			if (funkt == _("Resistance")) funkt = _("Conductivity");
			else if (funkt == _("Conductivity")) funkt = _("Resistance");
			else funkt.insert (0, "1/ ");
		}
		if (eval_xlog) funkt.insert (0, "log ");
	}
	else
	{
		if (eval_ytherm != no) funkt = _("Temperature");
		if (eval_yreziprok)
		{
			if (funkt == _("Resistance")) funkt = _("Conductivity");
			else if (funkt == _("Conductivity")) funkt = _("Resistance");
			else funkt.insert (0, "1/ ");
		}
		if (eval_ylog) funkt.insert (0, "log ");
	}

	return funkt;
}


std::string eval_unit (int kanal, std::string unit)
{
	if (kanal == 0)
	{
		if (eval_titvolumen) unit = "ml";
		else if (eval_xtherm != no) unit = "°C";
		if (eval_xreziprok)
		{
			if (unit == "Ohm") unit = "S";
			else if (unit == "S") unit = "Ohm";
			else unit.insert (0, "1/ ");
		}
		if (eval_xlog) unit.insert (0, "log ");
	}
	else
	{
		if (eval_ytherm != no) unit = "°C";
		if (eval_yreziprok)
		{
			if (unit == "Ohm") unit = "S";
			else if (unit == "S") unit = "Ohm";
			else unit.insert (0, "1/ ");
		}
		if (eval_ylog) unit.insert (0, "log ");
	}

	return unit;
}

void lin_regression ()	//linear regression of all measurement values
{
	double mw_x, mw_y, sxx, sxy;
	unsigned long long int n;
	double x,y;
	mw_data data;

	if (mw.size() == 0)
	{
		error_msg (_("Regression cannot be computed - no data available"));
		eval_reglin = false;
		return;
	}
	mw_x = mw_y = sxx = sxy = 0;
	n = 0;
	for (unsigned long long int i=0; i<mi;i++)
	{
		data = trafo (mw.at(i));
		x = data.x;
		y = data.y;
		mw_x += data.x;
		mw_y += data.y;
		n++;
	}
	mw_x = mw_x / n;
	mw_y = mw_y / n;

	for (unsigned long long int i=0; i<mi;i++)	//compute regression coefficient
	{
		data = trafo (mw.at(i));
		x = data.x;
		y = data.y;
		sxy += (x - mw_x) * (y - mw_y);
		sxx += pow ((x - mw_x), 2);
	}
	mw_slope = sxy / sxx;
	mw_axis = mw_y - mw_slope * mw_x;

	return;
}

polyreg_data reg_lin (std::vector<mw_data> &p)	//linear regression of data in array
{
	double mw_x, mw_y, sxx, sxy;
	int np;
	double x,y;
	polyreg_data coeff;

	np = p.size();
	mw_x = mw_y = sxx = sxy = 0;
	for (int i=0; i<np;i++)	//compute average
	{
		mw_x += p.at(i).x;
		mw_y += p.at(i).y;
	}
	mw_x = mw_x / np;
	mw_y = mw_y / np;
	for (int i=0;i<np;i++)	//compute regression coefficient
	{
		x = p.at(i).x;
		y = p.at(i).y;
		sxy += (x - mw_x) * (y - mw_y);
		sxx += pow ((x - mw_x), 2);
	}
	coeff.a1 = sxy / sxx;
	coeff.a0 = mw_y - coeff.a1 * mw_x;
	coeff.a2 = 0.0;

	return coeff;
}



polyreg_data poly2_reg (std::vector<mw_data> &p)
{
	double sx1,sx2,sy,sy1,sy2,syy,s11,s12,s22;
	int np;
	polyreg_data coeff;

	sx1=sx2=sy=sy1=sy2=syy=s11=s12=s22=0.0;
	np = p.size();
	for (int i=0; i<np;i++)
	{
		sx1 += p.at(i).x;
		sx2 += pow (p.at(i).x,2);
		sy += p.at(i).y;
	}
	sx1 /= np;
	sx2 /= np;
	sy /= np;
	for (int i=0;i<np;i++)
	{
		s11 += pow (p.at(i).x - sx1, 2);
		s12 += (p.at(i).x - sx1) * (pow(p.at(i).x,2) - sx2);
		s22 += pow ((pow(p.at(i).x,2) - sx2),2);
		sy1 += (p.at(i).y - sy) * (p.at(i).x - sx1);
		sy2 += (p.at(i).y - sy) * (pow(p.at(i).x,2) - sx2);
		syy += pow (p.at(i).y - sy, 2);
	}
	coeff.a1 = (s22*sy1 - s12*sy2) / (s11*s22 - s12*s12);
	coeff.a2 = (s11*sy2 - s12*sy1) / (s11*s22 - s12*s12);
	coeff.a0 = sy - coeff.a1*sx1 - coeff.a2*sx2;

	return coeff;
}

polyreg_data poly3_reg (std::vector<mw_data> &p)
{
	double s,sx1,sx2,sx3,sy,sy1,sy2,sy3,syy,s11,s12,s13,s22,s23,s33;
	double a11,a12,a13,a22,a23,a33;
	int np;
	polyreg_data coeff;

	sx1=sx2=sx3=sy=sy1=sy2=sy3=syy=s11=s12=s13=s22=s23=s33=0.0;
	np = p.size();
	for (int i=0; i<np;i++)
	{
		sx1 += p.at(i).x;
		sx2 += pow (p.at(i).x,2);
		sx3 += pow (p.at(i).x,3);
		sy += p.at(i).y;
	}
	sx1 /= np;
	sx2 /= np;
	sx3 /= np;
	sy /= np;
	for (int i=0;i<np;i++)
	{
		s11 += pow (p.at(i).x - sx1, 2);
		s12 += (p.at(i).x - sx1) * (pow(p.at(i).x,2) - sx2);
		s13 += (p.at(i).x - sx1) * (pow(p.at(i).x,3) - sx3);
		s22 += pow ((pow(p.at(i).x,2) - sx2),2);
		s23 += (pow(p.at(i).x,2) - sx2) * (pow(p.at(i).x,3) - sx3);
		s33 += pow ((pow(p.at(i).x,3) - sx3),2);
		sy1 += (p.at(i).y - sy) * (p.at(i).x - sx1);
		sy2 += (p.at(i).y - sy) * (pow(p.at(i).x,2) - sx2);
		sy3 += (p.at(i).y - sy) * (pow(p.at(i).x,3) - sx3);
		syy += pow (p.at(i).y - sy, 2);
	}
	a11 = s22*s33 - s23*s23;
	a12 = s23*s13 - s12*s33;
	a13 = s12*s23 - s22*s13;
	a22 = s11*s33 - s13*s13;
	a23 = s12*s13 - s11*s23;
	a33 = s11*s22 - s12*s12;
	s = s11*a11 + s12*a12 + s13*a13;
	coeff.a3 = (a13*sy1 + a23*sy2 + a33*sy3) / s;
	coeff.a2 = (a12*sy1 + a22*sy2 + a23*sy3) / s;
	coeff.a1 = (a11*sy1 + a12*sy2 + a13*sy3) / s;
	coeff.a0 = sy - coeff.a1*sx1 - coeff.a2*sx2 - coeff.a3*sx3;

	return coeff;
}


polyreg_data baseline ()
{
	std::vector<mw_data> p, q;		//arrays for data values
	mw_data pxy;
	polyreg_data coeff, init_slope, end_slope;

	p.clear();
	//fill array with first 5 values
	for (int i=0; i<5; i++)
	{
		pxy = trafo(mw.at(i));
		p.push_back (pxy);
	}
	init_slope = reg_lin (p);
	//fill array with last 5 values
	for (int i=mi-5; i<mi; i++)
	{
		pxy = trafo(mw.at(i));
		q.push_back (pxy);
	}
	end_slope = reg_lin (q);
	for (int i=0; i<5; i++) p.push_back (q.at(i));

	// return end_slope if init_slope is too different or =0. else init_ and end_slope
	if ((init_slope.a1 == 0) || (end_slope.a1 == 0)) coeff = end_slope;
	else if (log (abs(init_slope.a1/end_slope.a1)) < 1.0) coeff = reg_lin (p);
	else coeff = end_slope;

	return coeff;
}

void peak_integration ()			//integration of peaks
{
	yb_a0 = baseline().a0;		//get baseline parameters by linear regression
	yb_a1 = baseline().a1;
	find_peaks();
	peaks_area = 0;
	for (int n=0; n<peaks.size(); n++) peaks_area += peaks[n].area;		//compute whole peak area
	for (int n=0; n<peaks.size(); n++) peaks[n].percent = 100* peaks[n].area / peaks_area; //compute percent of area
	return;
}


void find_peaks ()			//find peaks in measurement data
{
	mw_data val,pxy;
	double min_peakheight;
	peak_data peak, gauss;
	std::vector<mw_data> p;		//array for data values
	std::vector<mw_data> m_av;	//array for averages slope
	int np;						//number of elements in p - must be odd
	int mp;						//center of data array

	peaks.clear();
	min_peakheight = min_peakscale / 100 * main_win->diag.yscale;
	np = 3;						//set size of p
	mp = trunc (np / 2);		//calculate center of p

	for (unsigned long long int i=np-1;i<mi-2*np;i++)
	{
		val = trafo(mw.at(i));
		m_av.clear();
		for (int j=0; j<np; j++)
		{
			p.clear();
			for (int n=0; n<np; n++)	// fill array with neighbours of value
			{
				pxy = trafo (mw.at(i-np+n+j+1));
				p.push_back (pxy);
			}
			pxy.x = p.at(mp).x;		//calculate slope
			pxy.y = reg_lin(p).a1;
			m_av.push_back (pxy);
		}

		if ((m_av.at(mp-1).y >0) && (m_av.at(mp).y >=0) && (m_av.at(mp+1).y <0))
			// peak-maximum found at center with peak slope > 0
		{
			peak.mpos = i;		//position of peak maximum in mw array
			peak.max.x = - reg_lin (m_av).a0 / reg_lin (m_av).a1; //maximum corrected by linear regression of slopes
			if ((peak.max.x < trafo(mw.at(i-1)).x) || (peak.max.x > trafo(mw.at(i+1)).x)) peak.max.x = val.x;
			// --> in this case correction is not valid
			peak.max.x *= main_win->diag.xrange;
			gauss = peakreg_gauss (peak.mpos);	// calculate gauss model of peak
			if (gauss.mpos == i) // regression with gauss model succeeded
			{
				peak.max.y = main_win->diag.yrange*gauss.max.y;
			}
			else	// regression with gauss model failed
			{
				peak.max.y = main_win->diag.yrange*val.y;
			}
			peak = get_peakarea (peak);
			// add baseline value at val.x to min_peakheight
			if (peak.max.y > (min_peakheight + (yb_a1 * peak.max.x + yb_a0)))		// if < min_peakheight -> forget peak
			{
				peaks.push_back (peak);
			}
			if (i<peak.xb2) i = peak.xb2;	// go to next peak: begin seeking at end of current peak
		}
	}

	return;
}


peak_data get_peakarea (peak_data peak)
{
	unsigned long long int i;
	double m;					//slope of curve at data value
	mw_data val, val2, pxy;
	std::vector<mw_data> p;		//array for data values
	std::vector<mw_data> m_av;	//array for averages slope
	int np;						//number of elements in p - must be odd
	int mp;						//center of data array

	peak.area = 0;
	np = 3;						//set size of p
	mp = trunc (np / 2);		//calculate center of p
	i = peak.mpos -1;	// start left to peak maximum in mw_array
	peak.xb1 = 0;		// set minimum value if i<np
	while (i>=np)			//seek beginning of peak
	{
		m_av.clear();
		val = trafo(mw.at(i));
		yb = yb_a1 * val.x + yb_a0;
		for (int j=0; j<np; j++)
		{
			p.clear();
			for (int n=0; n<np; n++)	// fill array with neighbours of value
			{
				pxy = trafo (mw.at(i-np+n+j+1));
				p.push_back (pxy);
			}
			pxy.x = p.at(mp).x;		//calculate slope
			pxy.y = reg_lin(p).a1;
			m_av.push_back (pxy);
		}
		peak.xb1 = i;	// position of peak beginning
		// add to peak area unless peak base is reached
		// kriteria for peak base: y = baseline or slope = 0
		if ((val.y <= yb) || ((m_av.at(mp-1).y < 0) && (m_av.at(mp).y >= 0) && (m_av.at(mp+1).y > 0))) break;
		else i--;
	}
	i = peak.mpos +1;	// start right to peak maximum in mw_array
	while (i<mi-np)			//seek end of peak
	{
		m_av.clear();
		val = trafo(mw.at(i));
		yb = yb_a1 * val.x + yb_a0;
		for (int j=0; j<np; j++)
		{
			p.clear();
			for (int n=0; n<np; n++)	// fill array with neighbours of value
			{
				pxy = trafo (mw.at(i-np+n+j+1));
				p.push_back (pxy);
			}
			pxy.x = p.at(mp).x;		//calculate slope
			pxy.y = reg_lin(p).a1;
			m_av.push_back (pxy);
		}
		peak.xb2 = i;	// position of peak end
		// add to peak area unless peak base is reached
		// kriteria for peak base: y = baseline or slope = 0
		if ((val.y <= yb) || ((m_av.at(mp-1).y < 0) && (m_av.at(mp).y >= 0) && (m_av.at(mp+1).y > 0))) break;
		else i++;
	}
	//calculate peak area
	for (int k=peak.xb1; k<peak.xb2; k++)
	{
		val = trafo(mw.at(k));
		val.x *= main_win->diag.xrange;
		val.y *= main_win->diag.yrange;
		yb = yb_a1 * val.x + yb_a0;
		if (k==mi) break;
		else
		{
		val2 = trafo(mw.at(k+1));
		val2.x *= main_win->diag.xrange;
		val2.y *= main_win->diag.yrange;
		peak.area += (val2.x - val.x) * abs(val.y - yb); // base rectangle
		peak.area += 0.5*(val2.x - val.x) * abs(val2.y - val.y); // upper triangle
		}
	}

	return peak;
}


peak_data peakreg_gauss (unsigned long long int max)
{
	polyreg_data pc;			//coefficients of polynom regression (maxima)
	peak_data peak;
	mw_data pxy;
	std::vector<mw_data> p;		//array for data values
	int np, mp;

	np = 5;						//set size of p
	p.clear();
	mp = trunc (np / 2);		//calculate center of p
	for (unsigned long long int k=max-mp;k<=max+mp;k++)	//fill array with neighbours of max ()
	{
		pxy = trafo (mw.at(k));
		if (pxy.y >0) pxy.y = log (pxy.y);		//logarithmic transformation
		else
		{
			peak.mpos = -1;	//error: to few data -> cannot calculate regression
			return peak;
		}
		p.push_back(pxy);
	}
	pc = poly2_reg (p);	//calculating gauss parameter for maximum by polynom regression
	peak.max.x = - (pc.a1 / (2*pc.a2));		//gauss parameter m is the corrected x-value of maximum
	peak.max.y = exp (pc.a0 - pow(pc.a1,2) / (4*pc.a2)); //gauss parameter P is the corrected y-value of maximum

	return peak;	// error bei peaktest max = 23!
}



mw_data mv_av (std::vector<mw_data> &p)		//calculate moving average
{
	int np;			//size of p
	int mp;			//center of p
	mw_data av;		//average of values in p

	av.y = 0.0;
	np = p.size();
	mp = trunc (np/2);
	for (int k=0; k<np; k++)
	{
		av.y += p.at(k).y;
	}
	av.y /= np;
	av.x = p.at(mp).x;	//x-value at center of p

	return av;
}

mw_data tr_av (std::vector<mw_data> &p)		//calculate triangular average
{
	int np;			//size of p
	int mp;			//center of p
	mw_data av;		//average of values in p
	int fi;		//coefficient of i-th element
	int sfi;		//sum of coefficients

	av.y = 0.0;
	np = p.size();
	mp = trunc (np/2);
	fi = 0;
	sfi = 0;
	for (int k=0; k<np; k++)
	{
		if (k<=mp) fi++; else fi--;		//increase fi until reaching center of p, then decrease
		sfi += fi;
		av.y += fi * p.at(k).y;			//each element weighted with its coefficient
	}
	av.y /= sfi;
	av.x = p.at(mp).x;	//x-value at center of p

	return av;
}


void diff_funktion ()			//calculate differential function
{
	int i=0;
	mw_data pxy, gpxy;
	double dx,dy,dxy, gdx, gdy, gdxy;
	double diff_maxval;
	double min_diffheight;
	diffmax diff_val;
	std::vector<mw_data> p;		//array for calculating differential data values
	std::vector<mw_data> av;	//array for triangular data averages
	std::vector<mw_data> dp;	//array of differential values for searching maxima
	std::vector<mw_data> gp;	//array of differential values for calculating gauss model
	int np;						//number of elements in p - must be odd
	int mp;						//center of p
	polyreg_data pc;			//coefficients of polynom regression (maxima)


	ndiff = 0;
	diff_min = diff_max = 0.0;
	min_diffheight = min_diffscale / 100; //minimum peak height in derivative
	diff.clear();
	dp.clear();
	np = 5;						//set size of p, must be at least =5
	mp = trunc (np / 2);		//calculate center of p
	for (unsigned long long int i=mp;i<mi-3*mp;i++)
	{
		av.clear();					//arrayF for calculation of averages
		for (int k=0;k<np;k++)
		{
			p.clear();
			for (int n=0; n<np; n++)	// fill array with neighbours of value
			{
				pxy = trafo (mw.at(i-mp+n+k));
				p.push_back (pxy);
			}
			pxy= mv_av(p);      //calculate averages
			av.push_back (pxy);
		}
		dx = av.at(mp+1).x - av.at(mp-1).x;	//calculate derivatives
		dy = av.at(mp+1).y - av.at(mp-1).y;
		if (dx !=0)
		{
			dxy = dy/dx;
			if (dxy < diff_min) diff_min = dxy;
			if (dxy > diff_max) diff_max = dxy;
			pxy.x = av.at(mp).x;
			pxy.y = dxy;
			if (dp.size() >= np) dp.erase(dp.begin());
			dp.push_back (pxy);
			if (dp.size() >= np)			//begin searching for maxima when dp has np elements
			{
				if ((dp.front().y < dp.at(1).y)  && (dp.at(mp-1).y < dp.at(mp).y) && (dp.at(mp).y > dp.at(mp+1).y) && (dp.at(mp+1).y > dp.back().y))
				// maximum found at center
				{
					diff_val.max = dp.at(mp);
					gp = dp;

					for (int k=0; k<np; k++)			//logarithmic transformation
					{
						if (gp.at(k).y == 0) break; else gp.at(k).y = log(abs(gp.at(k).y));	// y>0 !
					}
					pc = poly2_reg (gp);				//calculating gauss parameter for maximum by polynom regression

                    /*
                    gp.clear();
                    for (int k=0; k<np; k++)    //logarithmic transformation
                    {
                        gdx = trafo (mw.at(i+k -1)).x - trafo (mw.at(i+k -2)).x;
                        gdy = trafo (mw.at(i+k -1)).y - trafo (mw.at(i+k -2)).y;
                        gdxy = gdy/gdx;
                        gpxy.x = mw.at(i+k -1).x;
                        if (gdxy == 0) break; else gpxy.y = log (abs(gdxy));   // y>0 !
                        gp.push_back (gpxy);
                    }

					if (gp.size() < np) break; else pc = poly2_reg (gp);				//calculating gauss parameter for maximum by polynom regression
                    */
					diff_val.P = exp (pc.a0 - pow(pc.a1,2) / (4*pc.a2));
					diff_val.b = sqrt(abs(-1/(2*pc.a2)));		//argument > 0!
					diff_val.m = - (pc.a1 / (2*pc.a2));
					diff_val.max.x = diff_val.m;		//gauss parameter m is the corrected x-value of maximum
					diff_val.max.y = diff_val.P;		//gauss parameter P is the corrected y-value of maximum
					diff_val.xm = i;					//position of maximum in mw_array
					//if (dp.at(mp).y < min_diffheight * diff_val.P) break; // noise criteria in absolute scale ??

					diff.push_back (diff_val);			//save parameters of max in array
					ndiff++;
				}
			}
		}

	}

	//find maximal value in differential function
	diff_maxval = 0;
	for (int k=0; k<ndiff;k++)
	{
		if (diff.at(k).max.y > diff_maxval) diff_maxval =  diff.at(k).max.y;
	}
	//eliminate noise from maxima found
	int k=0;
	while (k<diff.size())
	{
		if (diff.at(k).max.y < min_diffheight * diff_maxval) diff.erase(diff.begin()+k); // noise criteria in relative scale
		else k++;
	}
	ndiff = diff.size();
	return;
}


void Project::show_evalresults ()
{
	int text_width, text_height, win_width, win_height;
	int min_width=250, min_height=150;
	Glib::RefPtr<Pango::Layout> eval_layout;


	main_win->eval_results->tabs = Pango::TabArray (3, true);
	main_win->eval_results->tabs.set_tab (0, Pango::TabAlign::LEFT, 70);
	main_win->eval_results->tabs.set_tab (1, Pango::TabAlign::LEFT, 140);
	main_win->eval_results->tabs.set_tab (2, Pango::TabAlign::LEFT, 240);
	main_win->eval_results->tabs.set_tab (3, Pango::TabAlign::LEFT, 320);
	if (eval_reglin)
	{
		main_win->eval_results->eval_funktion.set_text (_("Linear Regression"));
		project->evalresults = _("Slope: ")
		                            + to_string(mw_slope) + "\n" +
		                            _("Intercept: ")
		                            + to_string(mw_axis) + "\n";
		main_win->eval_results->eval_result_items = Gtk::TextBuffer::create();
		main_win->eval_results->eval_result_items->set_text (project->evalresults);
		main_win->eval_results->eval_results_view.set_buffer(main_win->eval_results->eval_result_items);
	}
	else if (eval_diff)
	{
		main_win->eval_results->eval_funktion.set_text (_("Differential Function"));
		if (ndiff == 0) project->evalresults = _("No Maxima found");
		else
		{
			project->evalresults = _("Maxima:");
			project->evalresults += "\n\n";
			for (int i=0; i<ndiff; i++)
				project->evalresults += " " + num_str (main_win->diag.xrange*diff.at(i).max.x, 4) + " " + main_win->diag.unit_x + "\n";
		}
		main_win->eval_results->eval_result_items = Gtk::TextBuffer::create();
		main_win->eval_results->eval_result_items->set_text (project->evalresults);
		main_win->eval_results->eval_results_view.set_buffer(main_win->eval_results->eval_result_items);
	}
	if (eval_integration)
	{
		main_win->eval_results->eval_funktion.set_text (_("Peak-Integration"));
		if (peaks.size() == 0) project->evalresults = _("No Peaks found");
		else
		{
			project->evalresults = _("Peak");
			project->evalresults += " \t";
			project->evalresults += "    ";
			project->evalresults +=_("Peak-Maximum");
			project->evalresults +=" \t";
			project->evalresults +=_("Peak Area");
			project->evalresults += " \t";
			project->evalresults +=_("Percent");
			project->evalresults += " \n\n";
			for (int i=0; i<peaks.size(); i++)
				project->evalresults += "  " + to_string(i+1) + "\t" + num_str (peaks[i].max.x, 4) + "\t" + num_str (peaks[i].max.y, 4) + "\t"
				+ num_str (peaks[i].area, 4) + "\t" + num_str (peaks[i].percent, 2) + "\n";
		}
		main_win->eval_results->eval_result_items = Gtk::TextBuffer::create();
		main_win->eval_results->eval_result_items->set_text (project->evalresults);
		main_win->eval_results->eval_results_view.set_buffer(main_win->eval_results->eval_result_items);
		main_win->eval_results->eval_results_view.set_tabs (main_win->eval_results->tabs);
	}

	eval_layout = main_win->eval_results->eval_results_view.create_pango_layout(project->evalresults);
	eval_layout->get_pixel_size(text_width, text_height);
	win_width = text_width + 300;
	win_height = text_height + 100;
	if (win_width < min_width) win_width = min_width;
	if (win_height < min_height) win_height = min_height;
	main_win->eval_results->set_default_size (win_width,win_height);
	main_win->eval_results->set_visible(true);

	return;
}

void EvalResults::close_results ()
{
    close ();
    return;
}


std::string num_str (double x, int n)
{
	std::string str, dezimal;
	double xn;
	int pos;
	struct lconv *lc  = localeconv();

	dezimal = lc->decimal_point;

	xn = x * pow (10,n);
	xn = round (xn) / pow (10,n);
	str = to_string (xn);
	pos = str.find(dezimal);
	str.resize(pos + n+1);

	return str;
}
