//
//  Averaging.h
//  Qdos2 - Data Series Averaging Template Functions
//
//  Janet Barnes
//  Oct 1999
//

#ifndef QDOS_AVERAGE_H
#define QDOS_AVERAGE_H

#include "Algorithm.h"
#include <complex>
#ifdef QT_DEFINED
#include <qapplication.h>
#include <qprogressdialog.h>
#include <qmessagebox.h>
#endif

namespace QDOS {

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ range_index
// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// ----------------------------------------------------------- range_index

template <class Tx>
pair<int,int> range_index( const NumericSequence<Tx>& x_data, const Tx& x_value, const Tx& interval )
{
// Returns a pair of integers, such that
// x_data[result.first] >= x_value - 0.5*interval
// x_data[result.second] > x_value + 0.5*interval
// if result.first == result.second, then the requested interval lies outside
// of x_data
    Tx half_interval=0.5*interval;
    pair<int,int> res=make_pair(0,0);
    typename NumericSequence<Tx>::const_iterator itr=\
        lower_bound(x_data.begin(),x_data.end(),x_value-half_interval);
    res.first=distance(x_data.begin(), itr);
    itr=upper_bound(x_data.begin(),x_data.end(),x_value+half_interval);
    res.second=distance(x_data.begin(), itr);
    return res;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ initialise_sum
// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// ------------------------------------------------------------ initialise_sum
inline void initialise_sum(double& sum)
{ sum=0.;}

inline void initialise_sum(double_complex& sum)
{ sum=0;}

template<class T>
void initialise_sum(T& sum)
{ sum=T();}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ data_average
// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// ------------------------------------------------------------ data_average
template <class Tx, class Ty>
bool data_average( const NumericSequence<Tx>& x_data, const NumericSequence<Ty>& y_data,\
    const Tx& x_value, Ty& y_value, const Tx& interval)
{
// calculate the average value of a function y=f(x) (as given by values of y,
// y_data, at values of x, x_data ) at x_value, over a range of values
// spanning interval units along the x-axis
// Returns true if average calculated, false if no points exist from
// which to calculate average
    bool res;
    Ty sum;
    initialise_sum(sum);
    pair<int,int> range_idx = range_index( x_data, x_value, interval);
    if (res=range_idx.first!=range_idx.second)
    {
        sum=accumulate((y_data.begin()+range_idx.first),\
            (y_data.begin()+range_idx.second),sum);

        int denom=range_idx.second-range_idx.first;
        y_value=sum/denom;
    }
    return res;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++ series_data_average
// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// ------------------------------------------------------ series_data_average
template<class Tx, class Ty>
vector<bool> series_data_average( const NumericSequence<Tx>& x_data, \
    const NumericSequence<Ty>& y_data, int num, const Tx& start_centre_value, \
    NumericSequence<Tx>& x_average_data, NumericSequence<Ty>& y_average_data, \
    const Tx& averaging_interval_size)
{
// Calculate the average of a function y=f(x) (as defined by values
// of y, y_data, at values of x, x_data) at num points, starting
// at start_centre_value, where the averages are taken over an interval
// of averaging_interval_size along the x-axis. Exclude points for
// which there is no data to average

    data_is_ok(x_data,y_data);

    x_average_data.assign_regular(num,start_centre_value,\
        averaging_interval_size);
    y_average_data.resize(num,y_data[0]);

    vector<bool> mask(num,false);

#ifdef QT_DEFINED
     QProgressDialog progress_dlg("Averaging,,,","Abort",num,0,"progress",TRUE);
     progress_dlg.setCaption("MultiJoin");
     progress_dlg.show();
     qApp->processEvents();
#endif
     for(register int i=0;i<num;i++)
     {
#ifdef QT_DEFINED
	 progress_dlg.setProgress(i);
	 qApp->processEvents();
	 if(progress_dlg.wasCancelled())
	 {
	   switch(QMessageBox::warning(0,"MultiJoin","Abort?",
	     QMessageBox::No | QMessageBox::Default,
	     QMessageBox::Yes, QMessageBox::Cancel | QMessageBox::Escape))
           {
	     case QMessageBox::Yes:
	      cout << "Boxcar AverageLinear Aborted" << endl;
	      throw QDOS::Exception("MultiJoin Aborted");
	      break;

	     case QMessageBox::No:
	     case QMessageBox::Cancel:
	     default:
	        progress_dlg.reset();
		progress_dlg.setLabelText("Averaging...");
		progress_dlg.setProgress(i);
		progress_dlg.show();
		qApp->processEvents();

		break;
	   }
	 }
#endif

        mask[i]=data_average(x_data,y_data,x_average_data[i],\
            y_average_data[i],averaging_interval_size);
    }

    vector<bool> emask(!mask);

    x_average_data.erase(emask);
    y_average_data.erase(emask);

    return mask;


}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++ series_data_average
// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// ------------------------------------------------------ series_data_average
template<class Tx, class Ty>
vector<bool> series_data_average( const NumericSequence<Tx>& x_data, \
    const NumericSequence<Ty>& y_data, int num, const Tx& start_centre_value, \
    NumericSequence<Tx>& x_average_data, NumericSequence<Ty>& y_average_data, \
    const Tx& averaging_interval_size, const Ty& missing_data_value )
{
// Calculate the average of a function y=f(x) (as defined by values
// of y, y_data, at values of x, x_data) at num points, starting
// at start_centre_value, where the averages are taken over an interval
// of averaging_interval_size along the x-axis. Set y_average_data to
// missing_data_value for those points which have no data to average

    data_is_ok(x_data,y_data);

    x_average_data.assign_regular(num,start_centre_value,\
        averaging_interval_size);
    y_average_data.resize(num,y_data[0]);

    vector<bool> mask(num,false);

#ifdef QT_DEFINED
     QProgressDialog progress_dlg("Averaging,,,","Abort",num,0,"progress",TRUE);
     progress_dlg.setCaption("MultiJoin");
     progress_dlg.show();
     qApp->processEvents();
#endif
     for(register int i=0;i<num;i++)
     {
#ifdef QT_DEFINED
	 progress_dlg.setProgress(i);
	 qApp->processEvents();
	 if(progress_dlg.wasCancelled())
	 {
	   switch(QMessageBox::warning(0,"MultiJoin","Abort?",
	     QMessageBox::No | QMessageBox::Default,
	     QMessageBox::Yes, QMessageBox::Cancel | QMessageBox::Escape))
           {
	     case QMessageBox::Yes:
	      cout << "Boxcar AverageLinear Aborted" << endl;
	      throw QDOS::Exception("MultiJoin Aborted");
	      break;

	     case QMessageBox::No:
	     case QMessageBox::Cancel:
	     default:
	        progress_dlg.reset();
		progress_dlg.setLabelText("Averaging...");
		progress_dlg.setProgress(i);
		progress_dlg.show();
		qApp->processEvents();

		break;
	   }
	 }
#endif
        mask[i]=data_average(x_data,y_data,x_average_data[i],\
            y_average_data[i],averaging_interval_size);
        if (!mask[i])
            y_average_data[i]=missing_data_value;

     }
     return mask;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++ series_data_average
// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// ------------------------------------------------------ series_data_average
template<class Tx, class Ty>
vector<bool> series_data_average( const NumericSequence<Tx>& x_data, \
    const NumericSequence<Ty>& y_data, NumericSequence<Tx>& x_average_data, \
    NumericSequence<Ty>& y_average_data, const Tx& averaging_interval_size)
{
// Calculate the average of a function y=f(x) (as defined by values
// of y, y_data, at values of x, x_data) at x_average_data where the averages
// are taken over intervals of averaging_interval_size along the x-axis
// Exclude points for which there is no data to average

    data_is_ok(x_data,y_data);

    int num=x_average_data.size();

    y_average_data.resize(num,y_data[0]);

    vector<bool> mask(num,false);
#ifdef QT_DEFINED
     QProgressDialog progress_dlg("Averaging,,,","Abort",num,0,"progress",TRUE);
     progress_dlg.setCaption("MultiJoin");
     progress_dlg.show();
     qApp->processEvents();
#endif
     for(register int i=0;i<num;i++)
     {
#ifdef QT_DEFINED
	 progress_dlg.setProgress(i);
	 qApp->processEvents();
	 if(progress_dlg.wasCancelled())
	 {
	   switch(QMessageBox::warning(0,"MultiJoin","Abort?",
	     QMessageBox::No | QMessageBox::Default,
	     QMessageBox::Yes, QMessageBox::Cancel | QMessageBox::Escape))
           {
	     case QMessageBox::Yes:
	      cout << "Boxcar AverageLinear Aborted" << endl;
	      throw QDOS::Exception("MultiJoin Aborted");
	      break;

	     case QMessageBox::No:
	     case QMessageBox::Cancel:
	     default:
	        progress_dlg.reset();
		progress_dlg.setLabelText("Averaging...");
		progress_dlg.setProgress(i);
		progress_dlg.show();
		qApp->processEvents();

		break;
	   }
	 }
#endif
        mask[i]=data_average(x_data,y_data,x_average_data[i],\
            y_average_data[i],averaging_interval_size);
     }

     vector<bool> emask(!mask);

     x_average_data.erase(emask);
     y_average_data.erase(emask);
     return mask;


}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++ series_data_average
// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// ------------------------------------------------------ series_data_average
template<class Tx, class Ty>
vector<bool> series_data_average( const NumericSequence<Tx>& x_data, \
    const NumericSequence<Ty>& y_data, NumericSequence<Tx>& x_average_data, \
    NumericSequence<Ty>& y_average_data, const Tx& averaging_interval_size, \
    const Ty& missing_data_value )
{
// Calculate the average of a function y=f(x) (as defined by values
// of y, y_data, at values of x, x_data) at x_average_data where the averages
// are taken over intervals of averaging_interval_size along the x-axis
// Set y_average_data to missing_data_value for those points which have no data
// to average
    data_is_ok(x_data,y_data);

    int num=x_average_data.size();

    y_average_data.resize(num,y_data[0]);

    vector<bool> mask(num,false);
#ifdef QT_DEFINED
     QProgressDialog progress_dlg("Averaging,,,","Abort",num,0,"progress",TRUE);
     progress_dlg.setCaption("MultiJoin");
     progress_dlg.show();
     qApp->processEvents();
#endif
     for(register int i=0;i<num;i++)
     {
#ifdef QT_DEFINED
	 progress_dlg.setProgress(i);
	 qApp->processEvents();
	 if(progress_dlg.wasCancelled())
	 {
	   switch(QMessageBox::warning(0,"MultiJoin","Abort?",
	     QMessageBox::No | QMessageBox::Default,
	     QMessageBox::Yes, QMessageBox::Cancel | QMessageBox::Escape))
           {
	     case QMessageBox::Yes:
	      cout << "Boxcar AverageLinear Aborted" << endl;
	      throw QDOS::Exception("MultiJoin Aborted");
	      break;

	     case QMessageBox::No:
	     case QMessageBox::Cancel:
	     default:
	        progress_dlg.reset();
		progress_dlg.setLabelText("Averaging...");
		progress_dlg.setProgress(i);
		progress_dlg.show();
		qApp->processEvents();

		break;
	   }
	 }
#endif
        mask[i]=data_average(x_data,y_data,x_average_data[i],\
            y_average_data[i],averaging_interval_size);
        if (!mask[i])
            y_average_data[i]=missing_data_value;
     }
     return mask;

}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++ series_data_average
// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// ------------------------------------------------------ series_data_average
template<class Tx, class Ty>
vector<bool> series_data_average( const NumericSequence<Tx>& x_data, \
    const NumericSequence<Ty>& y_data, NumericSequence<Tx>& x_average_data, \
    NumericSequence<Ty>& y_average_data, const Tx& averaging_interval_size, \
    bool (*pinterp_fn)(const NumericSequence<Tx>&, const NumericSequence<Ty>&,
    const Tx&,Ty&))
{
// Calculate the average of a function y=f(x) (as defined by values
// of y, y_data, at values of x, x_data) at x_average_data where the averages
// are taken over intervals of averaging_interval_size along the x-axis
// Use interpolation function named in pinterp_fn to interpolate those points which
// have no data to average
    data_is_ok(x_data,y_data);
    int num=x_average_data.size();
    y_average_data.resize(num);
    vector<bool> mask(num,false);
    if(pinterp_fn==NULL)
      pinterp_fn=linear_interp;
#ifdef QT_DEFINED
     QProgressDialog progress_dlg("Averaging,,,","Abort",num,0,"progress",TRUE);
     progress_dlg.setCaption("MultiJoin");
     progress_dlg.show();
     qApp->processEvents();
#endif
     for(register int i=0;i<num;i++)
     {
#ifdef QT_DEFINED
	 progress_dlg.setProgress(i);
	 qApp->processEvents();
	 if(progress_dlg.wasCancelled())
	 {
	   switch(QMessageBox::warning(0,"MultiJoin","Abort?",
	     QMessageBox::No | QMessageBox::Default,
	     QMessageBox::Yes, QMessageBox::Cancel | QMessageBox::Escape))
           {
	     case QMessageBox::Yes:
	      cout << "Boxcar AverageLinear Aborted" << endl;
	      throw QDOS::Exception("MultiJoin Aborted");
	      break;

	     case QMessageBox::No:
	     case QMessageBox::Cancel:
	     default:
	        progress_dlg.reset();
		progress_dlg.setLabelText("Averaging...");
		progress_dlg.setProgress(i);
		progress_dlg.show();
		qApp->processEvents();

		break;
	   }
	 }
#endif
        mask[i]=data_average(x_data,y_data,x_average_data[i],\
            y_average_data[i],averaging_interval_size);
        if (!mask[i])
          (*pinterp_fn)(x_data,y_data,x_average_data[i],y_average_data[i]);
     }
     return mask;

}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++ series_data_average
// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// ------------------------------------------------------ series_data_average
template<class Tx, class Ty>
vector<bool> series_data_average( const NumericSequence<Tx>& x_data, \
    const NumericSequence<Ty>& y_data, NumericSequence<Tx>& x_average_data, \
    NumericSequence<Ty>& y_average_data,\
    const NumericSequence<Tx>& averaging_interval_sizes, \
    const Ty& missing_data_value)
{
// Calculate the average of a function y=f(x) (as defined by values
// of y, y_data, at values of x, x_data) at x_average_data, where the
// averages are taken over an interval of averaging_interval_sizes along the
// x-axis. Set y_average_data to missing_data_value for those points which
// have no data to average
    data_is_ok(x_data,y_data);

    int num=x_average_data.size();

    if (averaging_interval_sizes.size()!=num)
        throw DataNotConformant();

    y_average_data.resize(num,y_data[0]);

    vector<bool> mask(num,false);
#ifdef QT_DEFINED
     QProgressDialog progress_dlg("Averaging,,,","Abort",num,0,"progress",TRUE);
     progress_dlg.setCaption("MultiJoin");
     progress_dlg.show();
     qApp->processEvents();
#endif
     for(register int i=0;i<num;i++)
     {
#ifdef QT_DEFINED
	 progress_dlg.setProgress(i);
	 qApp->processEvents();
	 if(progress_dlg.wasCancelled())
	 {
	   switch(QMessageBox::warning(0,"MultiJoin","Abort?",
	     QMessageBox::No | QMessageBox::Default,
	     QMessageBox::Yes, QMessageBox::Cancel | QMessageBox::Escape))
           {
	     case QMessageBox::Yes:
	      cout << "Boxcar AverageLinear Aborted" << endl;
	      throw QDOS::Exception("MultiJoin Aborted");
	      break;

	     case QMessageBox::No:
	     case QMessageBox::Cancel:
	     default:
	        progress_dlg.reset();
		progress_dlg.setLabelText("Averaging...");
		progress_dlg.setProgress(i);
		progress_dlg.show();
		qApp->processEvents();

		break;
	   }
	 }
#endif
        mask[i]=data_average(x_data,y_data,x_average_data[i],\
            y_average_data[i],averaging_interval_sizes[i]);
        if (!mask[i])
            y_average_data[i]=missing_data_value;
     }
     return mask;
}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++ series_data_average
// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// ------------------------------------------------------ series_data_average
template<class Tx, class Ty>
vector<bool> series_data_average( const NumericSequence<Tx>& x_data, \
    const NumericSequence<Ty>& y_data, NumericSequence<Tx>& x_average_data, \
    NumericSequence<Ty>& y_average_data,\
    const NumericSequence<Tx>& averaging_interval_sizes)
{
// Calculate the average of a function y=f(x) (as defined by values
// of y, y_data, at values of x, x_data) at x_average_data, where the
// averages are taken over an interval of averaging_interval_sizes along the
// x-axis. Exclude points for which there is no data to average.

    data_is_ok(x_data,y_data);

    int num=x_average_data.size();

    if(averaging_interval_sizes.size()!=num)
        throw DataNotConformant();

    y_average_data.resize(num,y_data[0]);

    vector<bool> mask(num,false);
#ifdef QT_DEFINED
     QProgressDialog progress_dlg("Averaging,,,","Abort",num,0,"progress",TRUE);
     progress_dlg.setCaption("MultiJoin");
     progress_dlg.show();
     qApp->processEvents();
#endif
     for(register int i=0;i<num;i++)
     {
#ifdef QT_DEFINED
	 progress_dlg.setProgress(i);
	 qApp->processEvents();
	 if(progress_dlg.wasCancelled())
	 {
	   switch(QMessageBox::warning(0,"MultiJoin","Abort?",
	     QMessageBox::No | QMessageBox::Default,
	     QMessageBox::Yes, QMessageBox::Cancel | QMessageBox::Escape))
           {
	     case QMessageBox::Yes:
	      cout << "Boxcar AverageLinear Aborted" << endl;
	      throw QDOS::Exception("MultiJoin Aborted");
	      break;

	     case QMessageBox::No:
	     case QMessageBox::Cancel:
	     default:
	        progress_dlg.reset();
		progress_dlg.setLabelText("Averaging...");
		progress_dlg.setProgress(i);
		progress_dlg.show();
		qApp->processEvents();

		break;
	   }
	 }
#endif
        mask[i]=data_average(x_data,y_data,x_average_data[i],\
            y_average_data[i],averaging_interval_sizes[i]);
     }

     vector<bool> emask(!mask);

     x_average_data.erase(emask);
     y_average_data.erase(emask);
     return mask;
}


}  // end namespace QDOS
#endif
