// 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 show_diag ()
{
	init_diag ();
	if (mw.size() != 0)
	{
		diag->add_events (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
		diag->signal_realize ();
		display_x->show ();
		display_y->show ();
		diag->queue_draw ();
	}
	else fehler (_("No data"));
	return;
}

void eval_data ()
{
	Gdk::RGBA color;
	std::string option;
	int opt_page;
	m_record xval, yval;

	if (mw.size() == 0)
	{
		fehler (_("No data"));
		return;
	}
	diag->add_events (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
	diag->signal_realize ();
		
	evaloptions->set_current_page (0);
	color.set_rgba(diag->funktcol_r, diag->funktcol_g, diag->funktcol_b);
	diag_funktcolor->set_rgba(color);
	if (project->funktion_x != _("Time")) evaloption_vol->set_sensitive (false);
	else evaloption_vol->set_sensitive (true);
	evaloption_vol->set_state(eval_titvolumen);
	evaloption_xreziproksw->set_state(eval_xreziprok);
	evaloption_yreziproksw->set_state(eval_yreziprok);
	evaloption_xlogsw->set_state(eval_xlog);
	evaloption_ylogsw->set_state(eval_ylog);
	if (project->funktion_x != _("DC Voltage")) evaloption_xtherm->set_sensitive (false);
	else evaloption_xtherm->set_sensitive (true);
	if (eval_xtherm == CuNi) evaloption_xtherm->set_active_text("Cu/CuNi");
	else if (eval_xtherm == NiCr) evaloption_xtherm->set_active_text("Ni/NiCr");
	else evaloption_xtherm->set_active_text("none");
	if (project->funktion_y != _("DC Voltage")) evaloption_ytherm->set_sensitive (false);
	else evaloption_ytherm->set_sensitive (true);
	if (eval_ytherm == CuNi) evaloption_ytherm->set_active_text("Cu/CuNi");
	else if (eval_ytherm == NiCr) evaloption_ytherm->set_active_text("Ni/NiCr");
	else evaloption_ytherm->set_active_text(_("none"));
	evaloption_volspeed->set_value (60.0 * tit_volspeed);	// ml/sec -> ml/min
	evaloption_reglin->set_state(eval_reglin);
	evaloption_diff->set_state(eval_diff);
	evaloption_integration->set_state(eval_integration);
	evaloption_minpeakscale->set_value(min_peakscale);
	evaloption_mindiffscale->set_value(min_diffscale);
	evaloption_show_baseline->set_state(diag->show_baseline);
	evaloption_show_peakarea->set_state(diag->show_peakarea);
	evaloption_show_peaklimits->set_state(diag->show_peaklimits);
	show_evaloptions_integration (1);
	/////////////////////////////////////////
	evaloptions_dialog-> show ();
		
	//////////////////////	Accept options  ///////////////////////
	int result = evaloptions_dialog->run();
	switch(result)
  	{
    	case(Gtk::RESPONSE_ACCEPT):
    	{
			color = diag_funktcolor->get_rgba();
			diag->funktcol_r = color.get_red();
			diag->funktcol_g = color.get_green();
			diag->funktcol_b = color.get_blue();
			eval_titvolumen = evaloption_vol->get_state();
			eval_xreziprok = evaloption_xreziproksw->get_state();
			eval_yreziprok = evaloption_yreziproksw->get_state();
			eval_xlog = evaloption_xlogsw->get_state();
			eval_ylog = evaloption_ylogsw->get_state();
			option = evaloption_xtherm->get_active_text();
			if (option == "Cu/CuNi") eval_xtherm = CuNi;
			else if (option == "Ni/NiCr") eval_xtherm = NiCr;
			else eval_xtherm = no;
			option = evaloption_ytherm->get_active_text();
			if (option == "Cu/CuNi") eval_ytherm = CuNi;
			else if (option == "Ni/NiCr") eval_ytherm = NiCr;
			else eval_ytherm = no;
			tit_volspeed = evaloption_volspeed->get_value () / 60.0;	// ml/min -> ml/sec
			if ((tit_volspeed == 0) || (project->funktion_x != _("Time"))) eval_titvolumen = false;
			eval_reglin = evaloption_reglin->get_state();
			eval_diff = evaloption_diff->get_state();
			eval_integration = evaloption_integration->get_state();
			diag->show_baseline = evaloption_show_baseline->get_state();
			diag->show_peakarea = evaloption_show_peakarea->get_state();
			diag->show_peaklimits = evaloption_show_peaklimits->get_state();
			min_peakscale = evaloption_minpeakscale->get_value ();
			min_diffscale = evaloption_mindiffscale->get_value ();
			break;
    	}
    	case(Gtk::RESPONSE_CANCEL):
    	{
			
			break;
    	}
		case(Gtk::RESPONSE_DELETE_EVENT):	//Reset
    	{
			if (!dialog_ok (_("Reset options of current page?"))) break;
			opt_page = evaloptions->get_current_page ();
			switch (opt_page)
			{
				case 0:		// Reset Transformation
				{
					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;
				}
				case 1:		// Reset functions
				{
					diag->funktcol_r = 0.0;
					diag->funktcol_g = 0.0;
					diag->funktcol_b = 1.0;
					eval_reglin = false;
					eval_integration = false;
					diag->show_baseline = true;
					diag->show_peakarea = true;
					diag->show_peaklimits = true;
					eval_diff = false;
					min_peakscale = 0.1;
					min_diffscale = 5.0;
				}
			}
			break;
    	}
	}
	evaloptions_dialog->hide();
	
	if (eval_reglin) lin_regression ();
	if (eval_integration) peak_integration ();
	if (eval_diff) diff_funktion ();
	show_diag();
	if ((eval_reglin) || (eval_integration) || (eval_diff)) show_evalresults ();
	display_x->show ();
	display_y->show ();
	return;
}

void show_evaloptions_integration (int page)
{
	bool int_opt;

	int_opt = evaloption_integration->get_state ();
	if (int_opt) integration_options->show();
	else integration_options->hide();
	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;
}

mw_data trafo (mw_data val)
{
	mw_data data;

	data.x = val.x;
	data.y = val.y;
	if (eval_titvolumen)
	{
		if (project->unit_x == "min") data.x = 60.0 * data.x;
		if (project->unit_x == "h") data.x = 3600.0 * data.x;
		data.x = data.x * tit_volspeed;
	}
	if (eval_xtherm == CuNi)
	{
		if (project->unit_x == "V") data.x = 1000.0 * data.x;
		if (project->unit_x == "uV") data.x = 0.001 * data.x;
		data.x = trafo_cuni (data.x);
	}
	if (eval_ytherm == CuNi)
	{
		if (project->unit_y == "V") data.y = 1000.0 * data.y;
		if (project->unit_y == "uV") data.y = 0.001 * data.y;
		data.y = trafo_cuni (data.y);
	}
	if (eval_xtherm == NiCr)
	{
		if (project->unit_x == "V") data.x = 1000.0 * data.x;
		if (project->unit_x == "uV") data.x = 0.001 * data.x;
		data.x = trafo_nicr (data.x);
	}
	if (eval_ytherm == NiCr)
	{
		if (project->unit_y == "V") data.y = 1000 * data.y;
		if (project->unit_y == "uV") data.y = 0.001 * data.y;
		data.y = trafo_nicr (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 ()
{
	if ((project->funktion_y != "pH"))
	{
		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;
	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)
	{
		fehler (_("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 * 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 = - diag->xrange*reg_lin (m_av).a0 / reg_lin (m_av).a1; //maximum corrected by linear regression of slopes
			gauss = peakreg_gauss (peak.mpos);	// calculate gauss model of peak
			if (gauss.mpos == i) // regression with gauss model succeeded
			{
				peak.max.y = diag->yrange*gauss.max.y;
			}
			else	// regression with gauss model failed
			{
				peak.max.y = diag->yrange*val.y;
			}
			peak = get_peakarea (peak);	// add baseline value at val.x to min_peakheight
			if (val.y > (min_peakheight + (yb_a1 * val.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));
		yb = yb_a1 * val.x + yb_a0;
		if (k==mi) break; else val2 = trafo(mw.at(k+1));
		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
		peak.area *= diag->yrange;
	}
	
	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;
	double dx,dy,dxy;
	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 for maxima
	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();					//calculate triangular 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);
			av.push_back (pxy);
		}
		dx = diag->xrange*(av.at(mp+1).x - av.at(mp-1).x);	//calculate derivatives
		dy = diag->yrange*(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 = diag->xrange*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);
					for (int k=0; k<np; k++)			//logarithmic transformation
					{
						if (dp.at(k).y == 0) break; else dp.at(k).y = log(abs(dp.at(k).y));	// y>0 !
					}
					pc = poly2_reg (dp);				//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
					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);
		else k++;
	}
	ndiff = diff.size();
	return;
}


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

	
	tabs = Pango::TabArray (3, true);
	tabs.set_tab (0, Pango::TAB_LEFT, 70);
	tabs.set_tab (1, Pango::TAB_LEFT, 140);
	tabs.set_tab (2, Pango::TAB_LEFT, 240);
	tabs.set_tab (3, Pango::TAB_LEFT, 320);
	if (eval_reglin)
	{
		eval_funktion->set_text (_("Linear Regression"));
		project->evalresults = _("Slope: ")
		                            + to_string(mw_slope) + "\n" +
		                            _("Intercept: ")
		                            + to_string(mw_axis) + "\n";
		eval_result_items = Gtk::TextBuffer::create();
		eval_result_items->set_text (project->evalresults);
		eval_results_view->set_buffer(eval_result_items);
	}
	else if (eval_diff)
	{
		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 (diff.at(i).max.x, 4) +"\n";
		}		
		eval_result_items = Gtk::TextBuffer::create();
		eval_result_items->set_text (project->evalresults);
		eval_results_view->set_buffer(eval_result_items);
	}
	if (eval_integration)
	{
		eval_funktion->set_text (_("Peak-Integration"));
		if (peaks.size() == 0) project->evalresults = _("No Peaks found");
		else
		{
			project->evalresults = _("Number");
			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";
		}
		eval_result_items = Gtk::TextBuffer::create();
		eval_result_items->set_text (project->evalresults);
		eval_results_view->set_buffer(eval_result_items);
		eval_results_view->set_tabs (tabs);
	}
	
	eval_results_view->set_top_margin (20);
	eval_results_view->set_left_margin (20);
	eval_results_view->set_right_margin (20);
	eval_results_view->set_bottom_margin (30);
	eval_layout = eval_results_view->create_pango_layout(project->evalresults);	
	eval_layout->get_pixel_size(text_width, text_height);
	win_width = text_width + 100;
	win_height = text_height + 100;
	if (win_width < min_width) win_width = min_width;
	if (win_height < min_height) win_height = min_height;
	eval_results->resize (win_width,win_height);
	
	eval_results->show ();
	int result = eval_results->run();
	if (result == Gtk::RESPONSE_CLOSE) eval_results->hide();
	
	return;
}


std::string num_str (double x, int n)
{
	std::string str;
	double xn;
	int pos;
	
	xn = x * pow (10,n);
	xn = round (xn) / pow (10,n);
	str = to_string (xn);
	pos = str.find(',');		//localconv ?!
	str.resize(pos + n+1);
	
	return str;
}
