/***************************************************************************

  PRunAsymmetry.cpp

  Author: Andreas Suter
  e-mail: andreas.suter@psi.ch

  $Id$

***************************************************************************/

/***************************************************************************
 *   Copyright (C) 2007-2013 by Andreas Suter                              *
 *   andreas.suter@psi.ch                                                  *
 *                                                                         *
 *   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, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_GOMP
#include <omp.h>
#endif

#include <stdio.h>

#include <iostream>
using namespace std;

#include <TString.h>
#include <TObjArray.h>
#include <TObjString.h>

#include "PMusr.h"
#include "PRunAsymmetry.h"

//--------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------
/**
 * <p>Constructor
 */
PRunAsymmetry::PRunAsymmetry() : PRunBase()
{
  fNoOfFitBins  = 0;

  // the 2 following variables are need in case fit range is given in bins, and since
  // the fit range can be changed in the command block, these variables need to be accessible
  fGoodBins[0] = -1;
  fGoodBins[1] = -1;
}

//--------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------
/**
 * <p>Constructor
 *
 * \param msrInfo pointer to the msr-file handler
 * \param rawData raw run data
 * \param runNo number of the run within the msr-file
 * \param tag tag showing what shall be done: kFit == fitting, kView == viewing
 */
PRunAsymmetry::PRunAsymmetry(PMsrHandler *msrInfo, PRunDataHandler *rawData, UInt_t runNo, EPMusrHandleTag tag) : PRunBase(msrInfo, rawData, runNo, tag)
{
  // the 2 following variables are need in case fit range is given in bins, and since
  // the fit range can be changed in the command block, these variables need to be accessible
  fGoodBins[0] = -1;
  fGoodBins[1] = -1;

  // check if alpha and/or beta is fixed --------------------

  PMsrParamList *param = msrInfo->GetMsrParamList();

  // check if alpha is given
  if (fRunInfo->GetAlphaParamNo() == -1) { // no alpha given
    cerr << endl << ">> PRunAsymmetry::PRunAsymmetry(): **ERROR** no alpha parameter given! This is needed for an asymmetry fit!";
    cerr << endl;
    fValid = false;
    return;
  }
  // check if alpha parameter is within proper bounds
  if ((fRunInfo->GetAlphaParamNo() < 0) || (fRunInfo->GetAlphaParamNo() > (Int_t)param->size())) {
    cerr << endl << ">> PRunAsymmetry::PRunAsymmetry(): **ERROR** alpha parameter no = " << fRunInfo->GetAlphaParamNo();
    cerr << endl << ">> This is out of bound, since there are only " << param->size() << " parameters.";
    cerr << endl;
    fValid = false;
    return;
  }
  // check if alpha is fixed
  Bool_t alphaFixedToOne = false;
  if (((*param)[fRunInfo->GetAlphaParamNo()-1].fStep == 0.0) &&
      ((*param)[fRunInfo->GetAlphaParamNo()-1].fValue == 1.0))
    alphaFixedToOne = true;

  // check if beta is given
  Bool_t betaFixedToOne = false;
  if (fRunInfo->GetBetaParamNo() == -1) { // no beta given hence assuming beta == 1
    betaFixedToOne = true;
  } else if ((fRunInfo->GetBetaParamNo() < 0) || (fRunInfo->GetBetaParamNo() > (Int_t)param->size())) { // check if beta parameter is within proper bounds
    cerr << endl << ">> PRunAsymmetry::PRunAsymmetry(): **ERROR** beta parameter no = " << fRunInfo->GetBetaParamNo();
    cerr << endl << ">> This is out of bound, since there are only " << param->size() << " parameters.";
    cerr << endl;
    fValid = false;
    return;
  } else { // check if beta is fixed
    if (((*param)[fRunInfo->GetBetaParamNo()-1].fStep == 0.0) &&
        ((*param)[fRunInfo->GetBetaParamNo()-1].fValue == 1.0))
      betaFixedToOne = true;
  }

  // set fAlphaBetaTag
  if (alphaFixedToOne && betaFixedToOne)       // alpha == 1, beta == 1
    fAlphaBetaTag = 1;
  else if (!alphaFixedToOne && betaFixedToOne) // alpha != 1, beta == 1
    fAlphaBetaTag = 2;
  else if (alphaFixedToOne && !betaFixedToOne) // alpha == 1, beta != 1
    fAlphaBetaTag = 3;
  else
    fAlphaBetaTag = 4;

  // calculate fData
  if (!PrepareData())
    fValid = false;
}

//--------------------------------------------------------------------------
// Destructor
//--------------------------------------------------------------------------
/**
 * <p>Destructor.
 */
PRunAsymmetry::~PRunAsymmetry()
{
  fForward.clear();
  fForwardErr.clear();
  fBackward.clear();
  fBackwardErr.clear();
}

//--------------------------------------------------------------------------
// CalcChiSquare (public)
//--------------------------------------------------------------------------
/**
 * <p>Calculate chi-square.
 *
 * <b>return:</b>
 * - chisq value
 *
 * \param par parameter vector iterated by minuit2
 */
Double_t PRunAsymmetry::CalcChiSquare(const std::vector<Double_t>& par)
{
  Double_t chisq = 0.0;
  Double_t diff = 0.0;
  Double_t asymFcnValue = 0.0;
  Double_t a, b, f;

  // calculate functions
  for (Int_t i=0; i<fMsrInfo->GetNoOfFuncs(); i++) {
    fFuncValues[i] = fMsrInfo->EvalFunc(fMsrInfo->GetFuncNo(i), *fRunInfo->GetMap(), par);
  }

  // calculate chi square
  Double_t time(1.0);
  Int_t i, N(static_cast<Int_t>(fData.GetValue()->size()));

  // In order not to have an IF in the next loop, determine the start and end bins for the fit range now
  Int_t startTimeBin = static_cast<Int_t>(ceil((fFitStartTime - fData.GetDataTimeStart())/fData.GetDataTimeStep()));
  if (startTimeBin < 0)
    startTimeBin = 0;
  Int_t endTimeBin = static_cast<Int_t>(floor((fFitEndTime - fData.GetDataTimeStart())/fData.GetDataTimeStep())) + 1;
  if (endTimeBin > N)
    endTimeBin = N;

  // Calculate the theory function once to ensure one function evaluation for the current set of parameters.
  // This is needed for the LF and user functions where some non-thread-save calculations only need to be calculated once
  // for a given set of parameters---which should be done outside of the parallelized loop.
  // For all other functions it means a tiny and acceptable overhead.
  asymFcnValue = fTheory->Func(time, par, fFuncValues);

  #ifdef HAVE_GOMP
  Int_t chunk = (endTimeBin - startTimeBin)/omp_get_num_procs();
  if (chunk < 10)
    chunk = 10;
  #pragma omp parallel for default(shared) private(i,time,diff,asymFcnValue,a,b,f) schedule(dynamic,chunk) reduction(+:chisq)
  #endif
  for (i=startTimeBin; i < endTimeBin; ++i) {
    time = fData.GetDataTimeStart() + (Double_t)i*fData.GetDataTimeStep();
    switch (fAlphaBetaTag) {
      case 1: // alpha == 1, beta == 1
        asymFcnValue = fTheory->Func(time, par, fFuncValues);
        break;
      case 2: // alpha != 1, beta == 1
        a = par[fRunInfo->GetAlphaParamNo()-1];
        f = fTheory->Func(time, par, fFuncValues);
        asymFcnValue = (f*(a+1.0)-(a-1.0))/((a+1.0)-f*(a-1.0));
        break;
      case 3: // alpha == 1, beta != 1
        b = par[fRunInfo->GetBetaParamNo()-1];
        f = fTheory->Func(time, par, fFuncValues);
        asymFcnValue = f*(b+1.0)/(2.0-f*(b-1.0));
        break;
      case 4: // alpha != 1, beta != 1
        a = par[fRunInfo->GetAlphaParamNo()-1];
        b = par[fRunInfo->GetBetaParamNo()-1];
        f = fTheory->Func(time, par, fFuncValues);
        asymFcnValue = (f*(a*b+1.0)-(a-1.0))/((a+1.0)-f*(a*b-1.0));
        break;
      default:
        asymFcnValue = 0.0;
        break;
    }
    diff = fData.GetValue()->at(i) - asymFcnValue;
    chisq += diff*diff / (fData.GetError()->at(i)*fData.GetError()->at(i));
  }

  return chisq;
}

//--------------------------------------------------------------------------
// CalcChiSquareExpected (public)
//--------------------------------------------------------------------------
/**
 * <p>Calculate expected chi-square. Currently not implemented since not clear what to be done.
 *
 * <b>return:</b>
 * - chisq value == 0.0
 *
 * \param par parameter vector iterated by minuit2
 */
Double_t PRunAsymmetry::CalcChiSquareExpected(const std::vector<Double_t>& par)
{
  return 0.0;
}

//--------------------------------------------------------------------------
// CalcMaxLikelihood (public)
//--------------------------------------------------------------------------
/**
 * <p>NOT IMPLEMENTED!!
 *
 * \param par parameter vector iterated by minuit2
 */
Double_t PRunAsymmetry::CalcMaxLikelihood(const std::vector<Double_t>& par)
{
  cout << endl << "PRunAsymmetry::CalcMaxLikelihood(): not implemented yet ..." << endl;

  return 1.0;
}

//--------------------------------------------------------------------------
// GetNoOfFitBins (public)
//--------------------------------------------------------------------------
/**
 * <p>Calculate the number of fitted bins for the current fit range.
 *
 * <b>return:</b> number of fitted bins.
 */
UInt_t PRunAsymmetry::GetNoOfFitBins()
{
  CalcNoOfFitBins();

  return fNoOfFitBins;
}

//--------------------------------------------------------------------------
// SetFitRangeBin (public)
//--------------------------------------------------------------------------
/**
 * <p>Allows to change the fit range on the fly. Used in the COMMAND block.
 * The syntax of the string is: FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]].
 * If only one pair of fgb/lgb is given, it is used for all runs in the RUN block section.
 * If multiple fgb/lgb's are given, the number N has to be the number of RUN blocks in
 * the msr-file.
 *
 * <p>nXY are offsets which can be used to shift, limit the fit range.
 *
 * \param fitRange string containing the necessary information.
 */
void PRunAsymmetry::SetFitRangeBin(const TString fitRange)
{
  TObjArray *tok = 0;
  TObjString *ostr = 0;
  TString str;
  Ssiz_t idx = -1;
  Int_t offset = 0;

  tok = fitRange.Tokenize(" \t");

  if (tok->GetEntries() == 3) { // structure FIT_RANGE fgb+n0 lgb-n1
    // handle fgb+n0 entry
    ostr = (TObjString*) tok->At(1);
    str = ostr->GetString();
    // check if there is an offset present
    idx = str.First("+");
    if (idx != -1) { // offset present
      str.Remove(0, idx+1);
      if (str.IsFloat()) // if str is a valid number, convert is to an integer
        offset = str.Atoi();
    }
    fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution;

    // handle lgb-n1 entry
    ostr = (TObjString*) tok->At(2);
    str = ostr->GetString();
    // check if there is an offset present
    idx = str.First("-");
    if (idx != -1) { // offset present
      str.Remove(0, idx+1);
      if (str.IsFloat()) // if str is a valid number, convert is to an integer
        offset = str.Atoi();
    }
    fFitEndTime = (fGoodBins[1] - offset - fT0s[0]) * fTimeResolution;
  } else if ((tok->GetEntries() > 3) && (tok->GetEntries() % 2 == 1)) { // structure FIT_RANGE fgb[+n00] lgb[-n01] [fgb[+n10] lgb[-n11] ... fgb[+nN0] lgb[-nN1]]
    Int_t pos = 2*(fRunNo+1)-1;

    if (pos + 1 >= tok->GetEntries()) {
      cerr << endl << ">> PRunSingleHisto::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'";
      cerr << endl << ">> will ignore it. Sorry ..." << endl;
    } else {
      // handle fgb+n0 entry
      ostr = (TObjString*) tok->At(pos);
      str = ostr->GetString();
      // check if there is an offset present
      idx = str.First("+");
      if (idx != -1) { // offset present
        str.Remove(0, idx+1);
        if (str.IsFloat()) // if str is a valid number, convert is to an integer
          offset = str.Atoi();
      }
      fFitStartTime = (fGoodBins[0] + offset - fT0s[0]) * fTimeResolution;

      // handle lgb-n1 entry
      ostr = (TObjString*) tok->At(pos+1);
      str = ostr->GetString();
      // check if there is an offset present
      idx = str.First("-");
      if (idx != -1) { // offset present
        str.Remove(0, idx+1);
        if (str.IsFloat()) // if str is a valid number, convert is to an integer
          offset = str.Atoi();
      }
      fFitEndTime = (fGoodBins[1] - offset - fT0s[0]) * fTimeResolution;
    }
  } else { // error
    cerr << endl << ">> PRunSingleHisto::SetFitRangeBin(): **ERROR** invalid FIT_RANGE command found: '" << fitRange << "'";
    cerr << endl << ">> will ignore it. Sorry ..." << endl;
  }

  // clean up
  if (tok) {
    delete tok;
  }
}

//--------------------------------------------------------------------------
// CalcNoOfFitBins (protected)
//--------------------------------------------------------------------------
/**
 * <p>Calculate the number of fitted bins for the current fit range.
 */
void PRunAsymmetry::CalcNoOfFitBins()
{
  // In order not having to loop over all bins and to stay consistent with the chisq method, calculate the start and end bins explicitly
  Int_t startTimeBin = static_cast<Int_t>(ceil((fFitStartTime - fData.GetDataTimeStart())/fData.GetDataTimeStep()));
  if (startTimeBin < 0)
    startTimeBin = 0;
  Int_t endTimeBin = static_cast<Int_t>(floor((fFitEndTime - fData.GetDataTimeStart())/fData.GetDataTimeStep())) + 1;
  if (endTimeBin > static_cast<Int_t>(fData.GetValue()->size()))
    endTimeBin = fData.GetValue()->size();

  if (endTimeBin > startTimeBin)
    fNoOfFitBins = endTimeBin - startTimeBin;
  else
    fNoOfFitBins = 0;
}

//--------------------------------------------------------------------------
// CalcTheory (protected)
//--------------------------------------------------------------------------
/**
 * <p>Calculate theory for a given set of fit-parameters.
 */
void PRunAsymmetry::CalcTheory()
{
  // feed the parameter vector
  std::vector<Double_t> par;
  PMsrParamList *paramList = fMsrInfo->GetMsrParamList();
  for (UInt_t i=0; i<paramList->size(); i++)
    par.push_back((*paramList)[i].fValue);

  // calculate functions
  for (Int_t i=0; i<fMsrInfo->GetNoOfFuncs(); i++) {
    fFuncValues[i] = fMsrInfo->EvalFunc(fMsrInfo->GetFuncNo(i), *fRunInfo->GetMap(), par);
  }

  // calculate asymmetry
  Double_t asymFcnValue = 0.0;
  Double_t a, b, f;
  Double_t time;
  for (UInt_t i=0; i<fData.GetValue()->size(); i++) {
    time = fData.GetDataTimeStart() + (Double_t)i*fData.GetDataTimeStep();
    switch (fAlphaBetaTag) {
      case 1: // alpha == 1, beta == 1
        asymFcnValue = fTheory->Func(time, par, fFuncValues);
        break;
      case 2: // alpha != 1, beta == 1
        a = par[fRunInfo->GetAlphaParamNo()-1];
        f = fTheory->Func(time, par, fFuncValues);
        asymFcnValue = (f*(a+1.0)-(a-1.0))/((a+1.0)-f*(a-1.0));
        break;
      case 3: // alpha == 1, beta != 1
        b = par[fRunInfo->GetBetaParamNo()-1];
        f = fTheory->Func(time, par, fFuncValues);
        asymFcnValue = f*(b+1.0)/(2.0-f*(b-1.0));
        break;
      case 4: // alpha != 1, beta != 1
        a = par[fRunInfo->GetAlphaParamNo()-1];
        b = par[fRunInfo->GetBetaParamNo()-1];
        f = fTheory->Func(time, par, fFuncValues);
        asymFcnValue = (f*(a*b+1.0)-(a-1.0))/((a+1.0)-f*(a*b-1.0));
        break;
      default:
        asymFcnValue = 0.0;
        break;
    }
    fData.AppendTheoryValue(asymFcnValue);
  }

  // clean up
  par.clear();
}

//--------------------------------------------------------------------------
// PrepareData (protected)
//--------------------------------------------------------------------------
/**
 * <p>Prepare data for fitting or viewing. What is already processed at this stage:
 * - get all needed forward/backward histograms
 * - get time resolution
 * - get start/stop fit time
 * - get t0's and perform necessary cross checks (e.g. if t0 of msr-file (if present) are consistent with t0 of the data files, etc.)
 * - add runs (if addruns are present)
 * - group histograms (if grouping is present)
 * - subtract background
 *
 * Error propagation for \f$ A_i = (f_i^{\rm c}-b_i^{\rm c})/(f_i^{\rm c}+b_i^{\rm c})\f$:
 * \f[ \Delta A_i = \pm\frac{2}{(f_i^{\rm c}+b_i^{\rm c})^2}\left[
 *     (b_i^{\rm c})^2 (\Delta f_i^{\rm c})^2 + 
 *      (\Delta b_i^{\rm c})^2 (f_i^{\rm c})^2\right]^{1/2}\f]
 *
 * <b>return:</b>
 * - true if everthing went smooth
 * - false, otherwise.
 */
Bool_t PRunAsymmetry::PrepareData()
{
  // get forward/backward histo from PRunDataHandler object ------------------------
  // get the correct run
  PRawRunData *runData = fRawData->GetRunData(*(fRunInfo->GetRunName()));
  if (!runData) { // run not found
    cerr << endl << ">> PRunAsymmetry::PrepareData(): **ERROR** Couldn't get run " << fRunInfo->GetRunName()->Data() << "!";
    cerr << endl;
    return false;
  }

  // keep the time resolution in (us)
  fTimeResolution = runData->GetTimeResolution()/1.0e3;
  cout.precision(10);
  cout << endl << ">> PRunAsymmetry::PrepareData(): time resolution=" << fixed << runData->GetTimeResolution() << "(ns)" << endl;

  // collect histogram numbers
  PUIntVector forwardHistoNo;
  PUIntVector backwardHistoNo;
  for (UInt_t i=0; i<fRunInfo->GetForwardHistoNoSize(); i++) {
    forwardHistoNo.push_back(fRunInfo->GetForwardHistoNo(i));

    if (!runData->IsPresent(forwardHistoNo[i])) {
      cerr << endl << ">> PRunAsymmetry::PrepareData(): **PANIC ERROR**:";
      cerr << endl << ">> forwardHistoNo found = " << forwardHistoNo[i] << ", which is NOT present in the data file!?!?";
      cerr << endl << ">> Will quit :-(";
      cerr << endl;
      // clean up
      forwardHistoNo.clear();
      backwardHistoNo.clear();
      return false;
    }
  }
  for (UInt_t i=0; i<fRunInfo->GetBackwardHistoNoSize(); i++) {
    backwardHistoNo.push_back(fRunInfo->GetBackwardHistoNo(i));

    if (!runData->IsPresent(backwardHistoNo[i])) {
      cerr << endl << ">> PRunAsymmetry::PrepareData(): **PANIC ERROR**:";
      cerr << endl << ">> backwardHistoNo found = " << backwardHistoNo[i] << ", which is NOT present in the data file!?!?";
      cerr << endl << ">> Will quit :-(";
      cerr << endl;
      // clean up
      forwardHistoNo.clear();
      backwardHistoNo.clear();
      return false;
    }
  }
  if (forwardHistoNo.size() != backwardHistoNo.size()) {
    cerr << endl << ">> PRunAsymmetry::PrepareData(): **PANIC ERROR**:";
    cerr << endl << ">> # of forward histograms different from # of backward histograms.";
    cerr << endl << ">> Will quit :-(";
    cerr << endl;
    // clean up
    forwardHistoNo.clear();
    backwardHistoNo.clear();
    return false;
  }

  // feed all T0's
  // first init T0's, T0's are stored as (forward T0, backward T0, etc.)
  fT0s.clear();
  fT0s.resize(2*forwardHistoNo.size());
  for (UInt_t i=0; i<fT0s.size(); i++) {
    fT0s[i] = -1.0;
  }

  // fill in the T0's from the msr-file (if present)
  for (UInt_t i=0; i<fRunInfo->GetT0BinSize(); i++) {
    fT0s[i] = fRunInfo->GetT0Bin(i);
  }

  // fill in the T0's from the data file, if not already present in the msr-file
  for (UInt_t i=0; i<forwardHistoNo.size(); i++) {
    if (fT0s[2*i] == -1.0) // i.e. not present in the msr-file, try the data file
      if (runData->GetT0Bin(forwardHistoNo[i]) > 0.0) {
        fT0s[2*i] = runData->GetT0Bin(forwardHistoNo[i]);
        fRunInfo->SetT0Bin(fT0s[2*i], 2*i);
      }
  }
  for (UInt_t i=0; i<backwardHistoNo.size(); i++) {
    if (fT0s[2*i+1] == -1.0) // i.e. not present in the msr-file, try the data file
      if (runData->GetT0Bin(backwardHistoNo[i]) > 0.0) {
        fT0s[2*i+1] = runData->GetT0Bin(backwardHistoNo[i]);
        fRunInfo->SetT0Bin(fT0s[2*i+1], 2*i+1);
      }
  }

  // fill in the T0's gaps, i.e. in case the T0's are NOT in the msr-file and NOT in the data file
  for (UInt_t i=0; i<forwardHistoNo.size(); i++) {
    if (fT0s[2*i] == -1.0) { // i.e. not present in the msr-file and data file, use the estimated T0
      fT0s[2*i] = runData->GetT0BinEstimated(forwardHistoNo[i]);
      fRunInfo->SetT0Bin(fT0s[2*i], 2*i);

      cerr << endl << ">> PRunAsymmetry::PrepareData(): **WARRNING** NO t0's found, neither in the run data nor in the msr-file!";
      cerr << endl << ">> run: " << fRunInfo->GetRunName()->Data();
      cerr << endl << ">> will try the estimated one: forward t0 = " << runData->GetT0BinEstimated(forwardHistoNo[i]);
      cerr << endl << ">> NO WARRANTY THAT THIS OK!! For instance for LEM this is almost for sure rubbish!";
      cerr << endl;
    }
  }
  for (UInt_t i=0; i<backwardHistoNo.size(); i++) {
    if (fT0s[2*i+1] == -1.0) { // i.e. not present in the msr-file and data file, use the estimated T0
      fT0s[2*i+1] = runData->GetT0BinEstimated(backwardHistoNo[i]);
      fRunInfo->SetT0Bin(fT0s[2*i+1], 2*i+1);

      cerr << endl << ">> PRunAsymmetry::PrepareData(): **WARRNING** NO t0's found, neither in the run data nor in the msr-file!";
      cerr << endl << ">> run: " << fRunInfo->GetRunName()->Data();
      cerr << endl << ">> will try the estimated one: backward t0 = " << runData->GetT0BinEstimated(backwardHistoNo[i]);
      cerr << endl << ">> NO WARRANTY THAT THIS OK!! For instance for LEM this is almost for sure rubbish!";
      cerr << endl;
    }
  }

  // check if t0 is within proper bounds
  for (UInt_t i=0; i<forwardHistoNo.size(); i++) {
    if ((fT0s[2*i] < 0) || (fT0s[2*i] > (Int_t)runData->GetDataBin(forwardHistoNo[i])->size())) {
      cerr << endl << ">> PRunAsymmetry::PrepareData(): **ERROR** t0 data bin (" << fT0s[2*i] << ") doesn't make any sense!";
      cerr << endl << ">> forwardHistoNo " << forwardHistoNo[i];
      cerr << endl;
      return false;
    }
    if ((fT0s[2*i+1] < 0) || (fT0s[2*i+1] > (Int_t)runData->GetDataBin(backwardHistoNo[i])->size())) {
      cerr << endl << ">> PRunAsymmetry::PrepareData(): **ERROR** t0 data bin (" << fT0s[2*i+1] << ") doesn't make any sense!";
      cerr << endl << ">> backwardHistoNo " << backwardHistoNo[i];
      cerr << endl;
      return false;
    }
  }

  // keep the histo of each group at this point (addruns handled below)
  vector<PDoubleVector> forward, backward;
  forward.resize(forwardHistoNo.size());   // resize to number of groups
  backward.resize(backwardHistoNo.size()); // resize to numer of groups
  for (UInt_t i=0; i<forwardHistoNo.size(); i++) {
    forward[i].resize(runData->GetDataBin(forwardHistoNo[i])->size());
    backward[i].resize(runData->GetDataBin(backwardHistoNo[i])->size());
    forward[i]  = *runData->GetDataBin(forwardHistoNo[i]);
    backward[i] = *runData->GetDataBin(backwardHistoNo[i]);
  }

  // check if addrun's are present, and if yes add data
  // check if there are runs to be added to the current one
  if (fRunInfo->GetRunNameSize() > 1) { // runs to be added present
    PRawRunData *addRunData;
    for (UInt_t i=1; i<fRunInfo->GetRunNameSize(); i++) {
      // get run to be added to the main one
      addRunData = fRawData->GetRunData(*(fRunInfo->GetRunName(i)));
      if (addRunData == 0) { // couldn't get run
        cerr << endl << ">> PRunAsymmetry::PrepareData(): **ERROR** Couldn't get addrun " << fRunInfo->GetRunName(i)->Data() << "!";
        cerr << endl;
        return false;
      }

      // get T0's of the to be added run
      PDoubleVector t0Add;

      // feed all T0's
      // first init T0's, T0's are stored as (forward T0, backward T0, etc.)
      t0Add.clear();
      t0Add.resize(2*forwardHistoNo.size());
      for (UInt_t j=0; j<t0Add.size(); j++) {
        t0Add[j] = -1.0;
      }

      // fill in the T0's from the msr-file (if present)
      for (Int_t j=0; j<fRunInfo->GetAddT0BinSize(i); j++) {
        t0Add[j] = fRunInfo->GetAddT0Bin(i, j);
      }

      // fill in the T0's from the data file, if not already present in the msr-file
      for (UInt_t j=0; j<forwardHistoNo.size(); j++) {
        if (t0Add[2*j] == -1.0) // i.e. not present in the msr-file, try the data file
          if (addRunData->GetT0Bin(forwardHistoNo[j]) > 0.0) {
            t0Add[2*j] = addRunData->GetT0Bin(forwardHistoNo[j]);
            fRunInfo->SetAddT0Bin(t0Add[2*j], i-1, 2*j);
          }
      }
      for (UInt_t j=0; j<backwardHistoNo.size(); j++) {
        if (t0Add[2*j+1] == -1.0) // i.e. not present in the msr-file, try the data file
          if (addRunData->GetT0Bin(backwardHistoNo[j]) > 0.0) {
            t0Add[2*j+1] = addRunData->GetT0Bin(backwardHistoNo[j]);
            fRunInfo->SetAddT0Bin(t0Add[2*j+1], i-1, 2*j+1);
          }
      }

      // fill in the T0's gaps, i.e. in case the T0's are NOT in the msr-file and NOT in the data file
      for (UInt_t j=0; j<forwardHistoNo.size(); j++) {
        if (t0Add[2*j] == -1.0) { // i.e. not present in the msr-file and data file, use the estimated T0
          t0Add[2*j] = addRunData->GetT0BinEstimated(forwardHistoNo[j]);
          fRunInfo->SetAddT0Bin(t0Add[2*j], i-1, 2*j);

          cerr << endl << ">> PRunAsymmetry::PrepareData(): **WARRNING** NO t0's found, neither in the run data nor in the msr-file!";
          cerr << endl << ">> run: " << fRunInfo->GetRunName(i)->Data();
          cerr << endl << ">> will try the estimated one: forward t0 = " << addRunData->GetT0BinEstimated(forwardHistoNo[j]);
          cerr << endl << ">> NO WARRANTY THAT THIS OK!! For instance for LEM this is almost for sure rubbish!";
          cerr << endl;
        }
      }
      for (UInt_t j=0; j<backwardHistoNo.size(); j++) {
        if (t0Add[2*j+1] == -1.0) { // i.e. not present in the msr-file and data file, use the estimated T0
          t0Add[2*j+1] = addRunData->GetT0BinEstimated(backwardHistoNo[j]);
          fRunInfo->SetAddT0Bin(t0Add[2*j+1], i-1, 2*j+1);

          cerr << endl << ">> PRunAsymmetry::PrepareData(): **WARRNING** NO t0's found, neither in the run data nor in the msr-file!";
          cerr << endl << ">> run: " << fRunInfo->GetRunName(i)->Data();
          cerr << endl << ">> will try the estimated one: backward t0 = " << runData->GetT0BinEstimated(backwardHistoNo[j]);
          cerr << endl << ">> NO WARRANTY THAT THIS OK!! For instance for LEM this is almost for sure rubbish!";
          cerr << endl;
        }
      }

      // add forward run
      UInt_t addRunSize;      
      for (UInt_t k=0; k<forwardHistoNo.size(); k++) { // fill each group
        addRunSize = addRunData->GetDataBin(forwardHistoNo[k])->size();
        for (UInt_t j=0; j<addRunData->GetDataBin(forwardHistoNo[k])->size(); j++) { // loop over the bin indices
          // make sure that the index stays in the proper range
          if ((j+(Int_t)t0Add[2*k]-(Int_t)fT0s[2*k] >= 0) && (j+(Int_t)t0Add[2*k]-(Int_t)fT0s[2*k] < addRunSize)) {
            forward[k][j] += addRunData->GetDataBin(forwardHistoNo[k])->at(j+(Int_t)t0Add[2*k]-(Int_t)fT0s[2*k]);
          }
        }
      }

      // add backward run
      for (UInt_t k=0; k<backwardHistoNo.size(); k++) { // fill each group
        addRunSize = addRunData->GetDataBin(backwardHistoNo[k])->size();
        for (UInt_t j=0; j<addRunData->GetDataBin(backwardHistoNo[k])->size(); j++) { // loop over the bin indices
          // make sure that the index stays in the proper range
          if ((j+(Int_t)t0Add[2*k+1]-(Int_t)fT0s[2*k+1] >= 0) && (j+(Int_t)t0Add[2*k+1]-(Int_t)fT0s[2*k+1] < addRunSize)) {
            backward[k][j] += addRunData->GetDataBin(backwardHistoNo[k])->at(j+(Int_t)t0Add[2*k+1]-(Int_t)fT0s[2*k+1]);
          }
        }
      }

      // clean up
      t0Add.clear();
    }
  }

  // set forward/backward histo data of the first group
  fForward.resize(forward[0].size());
  fBackward.resize(backward[0].size());
  for (UInt_t i=0; i<fForward.size(); i++) {
    fForward[i]  = forward[0][i];
    fBackward[i] = backward[0][i];
  }

  // group histograms, add all the remaining forward histograms of the group
  for (UInt_t i=1; i<forwardHistoNo.size(); i++) { // loop over the groupings
    for (UInt_t j=0; j<runData->GetDataBin(forwardHistoNo[i])->size(); j++) { // loop over the bin indices
      // make sure that the index stays within proper range
      if ((j+fT0s[2*i]-fT0s[0] >= 0) && (j+fT0s[2*i]-fT0s[0] < runData->GetDataBin(forwardHistoNo[i])->size())) {
        fForward[j] += forward[i][j+(Int_t)fT0s[2*i]-(Int_t)fT0s[0]];
      }
    }
  }

  // group histograms, add all the remaining backward histograms of the group
  for (UInt_t i=1; i<backwardHistoNo.size(); i++) { // loop over the groupings
    for (UInt_t j=0; j<runData->GetDataBin(backwardHistoNo[i])->size(); j++) { // loop over the bin indices
      // make sure that the index stays within proper range
      if ((j+fT0s[2*i+1]-fT0s[1] >= 0) && (j+fT0s[2*i+1]-fT0s[1] < runData->GetDataBin(backwardHistoNo[i])->size())) {
        fBackward[j] += backward[i][j+(Int_t)fT0s[2*i+1]-(Int_t)fT0s[1]];
      }
    }
  }

  // subtract background from histogramms ------------------------------------------
  if (fRunInfo->GetBkgFix(0) == PMUSR_UNDEFINED) { // no fixed background given
    if (fRunInfo->GetBkgRange(0) >= 0) {
      if (!SubtractEstimatedBkg())
        return false;
    } else { // no background given to do the job, try to estimate it
      fRunInfo->SetBkgRange(static_cast<Int_t>(fT0s[0]*0.1), 0);
      fRunInfo->SetBkgRange(static_cast<Int_t>(fT0s[0]*0.6), 1);
      fRunInfo->SetBkgRange(static_cast<Int_t>(fT0s[1]*0.1), 2);
      fRunInfo->SetBkgRange(static_cast<Int_t>(fT0s[1]*0.6), 3);
      cerr << endl << ">> PRunAsymmetry::PrepareData(): **WARNING** Neither fix background nor background bins are given!";
      cerr << endl << ">> Will try the following:";
      cerr << endl << ">> forward:  bkg start = " << fRunInfo->GetBkgRange(0) << ", bkg end = " << fRunInfo->GetBkgRange(1);
      cerr << endl << ">> backward: bkg start = " << fRunInfo->GetBkgRange(2) << ", bkg end = " << fRunInfo->GetBkgRange(3);
      cerr << endl << ">> NO WARRANTY THAT THIS MAKES ANY SENSE! Better check ...";
      cerr << endl;
      if (!SubtractEstimatedBkg())
        return false;
    }
  } else { // fixed background given
    if (!SubtractFixBkg())
      return false;
  }

  // everything looks fine, hence fill data set
  UInt_t histoNo[2] = {forwardHistoNo[0], backwardHistoNo[0]};
  Bool_t status;
  switch(fHandleTag) {
    case kFit:
      status = PrepareFitData(runData, histoNo);
      break;
    case kView:
      if (fMsrInfo->GetMsrPlotList()->at(0).fRRFPacking == 0)
        status = PrepareViewData(runData, histoNo);
      else
        status = PrepareRRFViewData(runData, histoNo);
      break;
    default:
      status = false;
      break;
  }

  // clean up
  forwardHistoNo.clear();
  backwardHistoNo.clear();

  return status;
}

//--------------------------------------------------------------------------
// SubtractFixBkg (private)
//--------------------------------------------------------------------------
/**
 * <p>Subtracts a fixed background from the raw data. The background is given
 * in units of (1/ns). The error propagation
 * is done the following way: it is assumed that the error of the background
 * is Poisson like, i.e. \f$\Delta\mathrm{bkg} = \sqrt{\mathrm{bkg}}\f$.
 *
 * Error propagation: 
 * \f[ \Delta f_i^{\rm c} = \pm\left[ (\Delta f_i)^2 + (\Delta \mathrm{bkg})^2 \right]^{1/2} =
 *      \pm\left[ f_i + \mathrm{bkg} \right]^{1/2}, \f]
 * where \f$ f_i^{\rm c} \f$ is the background corrected histogram, \f$ f_i \f$ the raw histogram
 * and \f$ \mathrm{bkg} \f$ the fix given background.
 *
 * <b>return:</b>
 * - true
 *
 */
Bool_t PRunAsymmetry::SubtractFixBkg()
{
  Double_t dval;
  for (UInt_t i=0; i<fForward.size(); i++) {
    if (fForward[i] != 0.0)
      dval = TMath::Sqrt(fForward[i]);
    else
      dval = 1.0;
    fForwardErr.push_back(dval);
    fForward[i] -= fRunInfo->GetBkgFix(0) * fTimeResolution * 1.0e3; // bkg per ns -> bkg per bin; 1.0e3: us -> ns
    if (fBackward[i] != 0.0)
      dval = TMath::Sqrt(fBackward[i]);
    else
      dval = 1.0;
    fBackwardErr.push_back(dval);
    fBackward[i] -= fRunInfo->GetBkgFix(1) * fTimeResolution * 1.0e3; // bkg per ns -> bkg per bin; 1.0e3: us -> ns
  }

  return true;
}

//--------------------------------------------------------------------------
// SubtractEstimatedBkg (private)
//--------------------------------------------------------------------------
/**
 * <p>Subtracts the background which is estimated from a given interval (typically before t0).
 *
 * The background corrected histogramms are:
 * \f$ f_i^{\rm c} = f_i - \mathrm{bkg} \f$, where \f$ f_i \f$ is the raw data histogram,
 * \f$ \mathrm{bkg} \f$ the background estimate, and \f$ f_i^{\rm c} \f$ background corrected
 * histogram. The error on \f$ f_i^{\rm c} \f$ is 
 * \f[ \Delta f_i^{\rm c} = \pm \sqrt{ (\Delta f_i)^2 + (\Delta \mathrm{bkg})^2 } =
 *    \pm \sqrt{f_i + (\Delta \mathrm{bkg})^2} \f]
 * The background error \f$ \Delta \mathrm{bkg} \f$ is
 * \f[ \Delta \mathrm{bkg} = \pm\frac{1}{N}\left[\sum_{i=0}^N (\Delta f_i)^2\right]^{1/2} =
 * \pm\frac{1}{N}\left[\sum_{i=0}^N f_i \right]^{1/2},\f]
 * where \f$N\f$ is the number of bins over which the background is formed.
 *
 * <b>return:</b>
 * - true
 */
Bool_t PRunAsymmetry::SubtractEstimatedBkg()
{
  Double_t beamPeriod = 0.0;

  // check if data are from PSI, RAL, or TRIUMF
  if (fRunInfo->GetInstitute()->Contains("psi"))
    beamPeriod = ACCEL_PERIOD_PSI;
  else if (fRunInfo->GetInstitute()->Contains("ral"))
    beamPeriod = ACCEL_PERIOD_RAL;
  else if (fRunInfo->GetInstitute()->Contains("triumf"))
    beamPeriod = ACCEL_PERIOD_TRIUMF;
  else
    beamPeriod = 0.0;

  // check if start and end are in proper order
  UInt_t start[2] = {fRunInfo->GetBkgRange(0), fRunInfo->GetBkgRange(2)};
  UInt_t end[2]   = {fRunInfo->GetBkgRange(1), fRunInfo->GetBkgRange(3)};
  for (UInt_t i=0; i<2; i++) {
    if (end[i] < start[i]) {
      cout << endl << "PRunAsymmetry::SubtractEstimatedBkg(): end = " << end[i] << " > start = " << start[i] << "! Will swap them!";
      UInt_t keep = end[i];
      end[i] = start[i];
      start[i] = keep;
    }
  }

  // calculate proper background range
  for (UInt_t i=0; i<2; i++) {
    if (beamPeriod != 0.0) {
      Double_t timeBkg = (Double_t)(end[i]-start[i])*(fTimeResolution*fRunInfo->GetPacking()); // length of the background intervall in time
      UInt_t fullCycles = (UInt_t)(timeBkg/beamPeriod); // how many proton beam cylces can be placed within the proposed background intervall
      // correct the end of the background intervall such that the background is as close as possible to a multiple of the proton cylce
      end[i] = start[i] + (UInt_t) ((fullCycles*beamPeriod)/(fTimeResolution*fRunInfo->GetPacking()));
      cout << "PRunAsymmetry::SubtractEstimatedBkg(): Background " << start[i] << ", " << end[i] << endl;
      if (end[i] == start[i])
        end[i] = fRunInfo->GetBkgRange(2*i+1);
    }
  }

  // check if start is within histogram bounds
  if ((start[0] < 0) || (start[0] >= fForward.size()) ||
      (start[1] < 0) || (start[1] >= fBackward.size())) {
    cerr << endl << ">> PRunAsymmetry::SubtractEstimatedBkg(): **ERROR** background bin values out of bound!";
    cerr << endl << ">> histo lengths (f/b)  = (" << fForward.size() << "/" << fBackward.size() << ").";
    cerr << endl << ">> background start (f/b) = (" << start[0] << "/" << start[1] << ").";
    return false;
  }

  // check if end is within histogram bounds
  if ((end[0] < 0) || (end[0] >= fForward.size()) ||
      (end[1] < 0) || (end[1] >= fBackward.size())) {
    cerr << endl << ">> PRunAsymmetry::SubtractEstimatedBkg(): **ERROR** background bin values out of bound!";
    cerr << endl << ">> histo lengths (f/b)  = (" << fForward.size() << "/" << fBackward.size() << ").";
    cerr << endl << ">> background end (f/b) = (" << end[0] << "/" << end[1] << ").";
    return false;
  }

  // calculate background
  Double_t bkg[2]    = {0.0, 0.0};
  Double_t errBkg[2] = {0.0, 0.0};

  // forward
  for (UInt_t i=start[0]; i<end[0]; i++)
    bkg[0] += fForward[i];
  errBkg[0] = TMath::Sqrt(bkg[0])/(end[0] - start[0] + 1);
  bkg[0] /= static_cast<Double_t>(end[0] - start[0] + 1);
  cout << endl << ">> estimated forward histo background: " << bkg[0];

  // backward
  for (UInt_t i=start[1]; i<end[1]; i++)
    bkg[1] += fBackward[i];
  errBkg[1] = TMath::Sqrt(bkg[1])/(end[0] - start[0] + 1);
  bkg[1] /= static_cast<Double_t>(end[1] - start[1] + 1);
  cout << endl << ">> estimated backward histo background: " << bkg[1] << endl;

  // correct error for forward, backward
  for (UInt_t i=0; i<fForward.size(); i++) {
    fForwardErr.push_back(TMath::Sqrt(fForward[i]+errBkg[0]*errBkg[0]));
    fBackwardErr.push_back(TMath::Sqrt(fBackward[i]+errBkg[1]*errBkg[1]));
  }

  // subtract background from data
  for (UInt_t i=0; i<fForward.size(); i++) {
    fForward[i]  -= bkg[0];
    fBackward[i] -= bkg[1];
  }

  fRunInfo->SetBkgEstimated(bkg[0], 0);
  fRunInfo->SetBkgEstimated(bkg[1], 1);

  return true;
}

//--------------------------------------------------------------------------
// PrepareFitData (protected)
//--------------------------------------------------------------------------
/**
 * <p>Take the pre-processed data (i.e. grouping and addrun are preformed) and form the asymmetry for fitting.
 * Before forming the asymmetry, the following checks will be performed:
 * -# check if data range is given, if not try to estimate one.
 * -# check that if a data range is present, that it makes any sense.
 * -# check that 'first good bin'-'t0' is the same for forward and backward histogram. If not adjust it.
 * -# pack data (rebin).
 * -# if packed forward size != backward size, truncate the longer one such that an asymmetry can be formed.
 * -# calculate the asymmetry: \f$ A_i = (f_i^c-b_i^c)/(f_i^c+b_i^c) \f$
 * -# calculate the asymmetry errors: \f$ \delta A_i = 2 \sqrt{(b_i^c)^2 (\delta f_i^c)^2 + (\delta b_i^c)^2 (f_i^c)^2}/(f_i^c+b_i^c)^2\f$
 *
 * \param runData raw run data needed to perform some crosschecks
 * \param histoNo histogram number (within a run). histoNo[0]: forward histogram number, histNo[1]: backward histogram number
 */
Bool_t PRunAsymmetry::PrepareFitData(PRawRunData* runData, UInt_t histoNo[2])
{
  // transform raw histo data. This is done the following way (for details see the manual):
  // first rebin the data, than calculate the asymmetry
  // first get start data, end data, and t0
  Int_t start[2] = {fRunInfo->GetDataRange(0), fRunInfo->GetDataRange(2)};
  Int_t end[2]   = {fRunInfo->GetDataRange(1), fRunInfo->GetDataRange(3)};
  Double_t t0[2] = {fT0s[0], fT0s[1]};
  Int_t offset = (Int_t)(10.0e-3/fTimeResolution); // needed in case first good bin is not given, default = 10ns

  // check if data range has been provided, and if not try to estimate them
  if (start[0] < 0) {
    start[0] = (Int_t)t0[0]+offset;
    fRunInfo->SetDataRange(start[0], 0);
    cerr << endl << ">> PRunAsymmetry::PrepareData(): **WARNING** data range (forward) was not provided, will try data range start = t0+" << offset << "(=10ns) = " << start[0] << ".";
    cerr << endl << ">> NO WARRANTY THAT THIS DOES MAKE ANY SENSE.";
    cerr << endl;
  }
  if (start[1] < 0) {
    start[1] = (Int_t)t0[1]+offset;
    fRunInfo->SetDataRange(start[1], 2);
    cerr << endl << ">> PRunAsymmetry::PrepareData(): **WARNING** data range (backward) was not provided, will try data range start = t0+" << offset << "(=10ns) = " << start[1] << ".";
    cerr << endl << ">> NO WARRANTY THAT THIS DOES MAKE ANY SENSE.";
    cerr << endl;
  }
  if (end[0] < 0) {
    end[0] = runData->GetDataBin(histoNo[0])->size();
    fRunInfo->SetDataRange(end[0], 1);
    cerr << endl << ">> PRunAsymmetry::PrepareData(): **WARNING** data range (forward) was not provided, will try data range end = " << end[0] << ".";
    cerr << endl << ">> NO WARRANTY THAT THIS DOES MAKE ANY SENSE.";
    cerr << endl;
  }
  if (end[1] < 0) {
    end[1] = runData->GetDataBin(histoNo[1])->size();
    fRunInfo->SetDataRange(end[1], 3);
    cerr << endl << ">> PRunAsymmetry::PrepareData(): **WARNING** data range (backward) was not provided, will try data range end = " << end[1] << ".";
    cerr << endl << ">> NO WARRANTY THAT THIS DOES MAKE ANY SENSE.";
    cerr << endl;
  }
  // check if start, end, and t0 make any sense
  // 1st check if start and end are in proper order
  for (UInt_t i=0; i<2; i++) {
    if (end[i] < start[i]) { // need to swap them
      Int_t keep = end[i];
      end[i] = start[i];
      start[i] = keep;
    }
    // 2nd check if start is within proper bounds
    if ((start[i] < 0) || (start[i] > (Int_t)runData->GetDataBin(histoNo[i])->size())) {
      cerr << endl << ">> PRunAsymmetry::PrepareFitData(): **ERROR** start data bin doesn't make any sense!";
      cerr << endl;
      return false;
    }
    // 3rd check if end is within proper bounds
    if ((end[i] < 0) || (end[i] > (Int_t)runData->GetDataBin(histoNo[i])->size())) {
      cerr << endl << ">> PRunAsymmetry::PrepareFitData(): **ERROR** end data bin doesn't make any sense!";
      cerr << endl;
      return false;
    }
    // 4th check if t0 is within proper bounds
    if ((t0[i] < 0) || (t0[i] > (Int_t)runData->GetDataBin(histoNo[i])->size())) {
      cerr << endl << ">> PRunAsymmetry::PrepareFitData(): **ERROR** t0 data bin doesn't make any sense!";
      cerr << endl;
      return false;
    }
  }

  // check that start-t0 is the same for forward as for backward, otherwise take max(start[i]-t0[i])
  if (fabs(static_cast<Double_t>(start[0])-t0[0]) > fabs(static_cast<Double_t>(start[1])-t0[1])){
    start[1] = static_cast<Int_t>(t0[1] + static_cast<Double_t>(start[0]) - t0[0]);
    end[1] = static_cast<Int_t>(t0[1] + static_cast<Double_t>(end[0]) - t0[0]);
    cerr << endl << ">> PRunAsymmetry::PrepareFitData **WARNING** needed to shift backward data range.";
    cerr << endl << ">> given: " << fRunInfo->GetDataRange(2) << ", " << fRunInfo->GetDataRange(3);
    cerr << endl << ">> used : " << start[1] << ", " << end[1];
    cerr << endl;
  }
  if (fabs(static_cast<Double_t>(start[0])-t0[0]) < fabs(static_cast<Double_t>(start[1])-t0[1])){
    start[0] = static_cast<Int_t>(t0[0] + static_cast<Double_t>(start[1]) - t0[1]);
    end[0] = static_cast<Int_t>(t0[0] + static_cast<Double_t>(end[1]) - t0[1]);
    cerr << endl << ">> PRunAsymmetry::PrepareFitData **WARNING** needed to shift forward data range.";
    cerr << endl << ">> given: " << fRunInfo->GetDataRange(0) << ", " << fRunInfo->GetDataRange(1);
    cerr << endl << ">> used : " << start[0] << ", " << end[0];
    cerr << endl;
  }

  // if fit range is given in bins (and not time), the fit start/end time can be calculated at this point now
  if (fRunInfo->IsFitRangeInBin()) {
    fFitStartTime = (fRunInfo->GetDataRange(0) + fRunInfo->GetFitRangeOffset(0) - fT0s[0]) * fTimeResolution; // (fgb+n0-t0)*dt
    fFitEndTime = (fRunInfo->GetDataRange(1) - fRunInfo->GetFitRangeOffset(1) - fT0s[0]) * fTimeResolution;   // (lgb-n1-t0)*dt
    // write these times back into the data structure. This way it is available when writting the log-file
    fRunInfo->SetFitRange(fFitStartTime, 0);
    fRunInfo->SetFitRange(fFitEndTime, 1);
  }

  // keep good bins for potential latter use
  fGoodBins[0] = start[0];
  fGoodBins[1] = end[0];

  // everything looks fine, hence fill packed forward and backward histo
  PRunData forwardPacked;
  PRunData backwardPacked;
  Double_t value = 0.0;
  Double_t error = 0.0;
  // forward
  for (Int_t i=start[0]; i<end[0]; i++) {
    if (fRunInfo->GetPacking() == 1) {
      forwardPacked.AppendValue(fForward[i]);
      forwardPacked.AppendErrorValue(fForwardErr[i]);
    } else { // packed data, i.e. fRunInfo->GetPacking() > 1
      if (((i-start[0]) % fRunInfo->GetPacking() == 0) && (i != start[0])) { // fill data
        // in order that after rebinning the fit does not need to be redone (important for plots)
        // the value is normalize to per bin
        value /= fRunInfo->GetPacking();
        forwardPacked.AppendValue(value);
        if (value == 0.0)
          forwardPacked.AppendErrorValue(1.0);
        else
          forwardPacked.AppendErrorValue(TMath::Sqrt(error)/fRunInfo->GetPacking());
        value = 0.0;
        error = 0.0;
      }
      value += fForward[i];
      error += fForwardErr[i]*fForwardErr[i];
    }
  }
  // backward
  for (Int_t i=start[1]; i<end[1]; i++) {
    if (fRunInfo->GetPacking() == 1) {
      backwardPacked.AppendValue(fBackward[i]);
      backwardPacked.AppendErrorValue(fBackwardErr[i]);
    } else { // packed data, i.e. fRunInfo->GetPacking() > 1
      if (((i-start[1]) % fRunInfo->GetPacking() == 0) && (i != start[1])) { // fill data
        // in order that after rebinning the fit does not need to be redone (important for plots)
        // the value is normalize to per bin
        value /= fRunInfo->GetPacking();
        backwardPacked.AppendValue(value);
        if (value == 0.0)
          backwardPacked.AppendErrorValue(1.0);
        else
          backwardPacked.AppendErrorValue(TMath::Sqrt(error)/fRunInfo->GetPacking());
        value = 0.0;
        error = 0.0;
      }
      value += fBackward[i];
      error += fBackwardErr[i]*fBackwardErr[i];
    }
  }

  // check if packed forward and backward hist have the same size, otherwise take the minimum size
  UInt_t noOfBins = forwardPacked.GetValue()->size();
  if (forwardPacked.GetValue()->size() != backwardPacked.GetValue()->size()) {
    if (forwardPacked.GetValue()->size() > backwardPacked.GetValue()->size())
      noOfBins = backwardPacked.GetValue()->size();
  }

  // form asymmetry including error propagation
  Double_t asym;
  Double_t f, b, ef, eb;
  // fill data time start, and step
  // data start at data_start-t0 shifted by (pack-1)/2
  fData.SetDataTimeStart(fTimeResolution*((Double_t)start[0]-t0[0]+(Double_t)(fRunInfo->GetPacking()-1)/2.0));
  fData.SetDataTimeStep(fTimeResolution*(Double_t)fRunInfo->GetPacking());
  for (UInt_t i=0; i<noOfBins; i++) {
    // to make the formulae more readable
    f  = forwardPacked.GetValue()->at(i);
    b  = backwardPacked.GetValue()->at(i);
    ef = forwardPacked.GetError()->at(i);
    eb = backwardPacked.GetError()->at(i);
    // check that there are indeed bins
    if (f+b != 0.0)
      asym = (f-b) / (f+b);
    else
      asym = 0.0;
    fData.AppendValue(asym);
    // calculate the error
    if (f+b != 0.0)
      error = 2.0/((f+b)*(f+b))*TMath::Sqrt(b*b*ef*ef+eb*eb*f*f);
    else
      error = 1.0;
    fData.AppendErrorValue(error);
  }

  CalcNoOfFitBins();

  // clean up
  fForward.clear();
  fForwardErr.clear();
  fBackward.clear();
  fBackwardErr.clear();

  return true;
}

//--------------------------------------------------------------------------
// PrepareViewData (protected)
//--------------------------------------------------------------------------
/**
 * <p>Take the pre-processed data (i.e. grouping and addrun are preformed) and form the asymmetry for view representation.
 * Before forming the asymmetry, the following checks will be performed:
 * -# check if view packing is whished.
 * -# check if data range is given, if not try to estimate one.
 * -# check that data range is present, that it makes any sense.
 * -# check that 'first good bin'-'t0' is the same for forward and backward histogram. If not adjust it.
 * -# pack data (rebin).
 * -# if packed forward size != backward size, truncate the longer one such that an asymmetry can be formed.
 * -# calculate the asymmetry: \f$ A_i = (\alpha f_i^c-b_i^c)/(\alpha \beta f_i^c+b_i^c) \f$
 * -# calculate the asymmetry errors: \f$ \delta A_i = 2 \sqrt{(b_i^c)^2 (\delta f_i^c)^2 + (\delta b_i^c)^2 (f_i^c)^2}/(f_i^c+b_i^c)^2\f$
 * -# calculate the theory vector.
 *
 * \param runData raw run data needed to perform some crosschecks
 * \param histoNo histogram number (within a run). histoNo[0]: forward histogram number, histNo[1]: backward histogram number
 */
Bool_t PRunAsymmetry::PrepareViewData(PRawRunData* runData, UInt_t histoNo[2])
{
  // check if view_packing is wished
  Int_t packing = fRunInfo->GetPacking();
  if (fMsrInfo->GetMsrPlotList()->at(0).fViewPacking > 0) {
    packing = fMsrInfo->GetMsrPlotList()->at(0).fViewPacking;
  }

  // feed the parameter vector
  std::vector<Double_t> par;
  PMsrParamList *paramList = fMsrInfo->GetMsrParamList();
  for (UInt_t i=0; i<paramList->size(); i++)
    par.push_back((*paramList)[i].fValue);

  // transform raw histo data. This is done the following way (for details see the manual):
  // first rebin the data, than calculate the asymmetry
  // first get start data, end data, and t0
  Int_t start[2] = {0, 0};
  Int_t end[2] = {0, 0};
  Double_t t0[2] = {fT0s[0], fT0s[1]};
  Int_t offset = (Int_t)(10.0e-3/fTimeResolution); // needed in case first good bin is not given, default = 10ns
  // check if data range has been provided, and if not try to estimate them
  if (fRunInfo->GetDataRange(0) < 0) {
    Int_t diff = offset;

    // calculate start position for plotting
    Int_t val = static_cast<Int_t>(t0[1])+diff-packing*((static_cast<Int_t>(t0[1])+diff)/packing);
    do {
      if (static_cast<Double_t>(val)+t0[1]-t0[0] < 0.0)
        val += packing;
    } while (static_cast<Double_t>(val) + t0[1] - t0[0] < 0.0);

    start[0] = val;
    start[1] = val + static_cast<Int_t>(t0[1] - t0[0]);

    cerr << endl << ">> PRunAsymmetry::PrepareViewData(): **WARNING** data range (forward/backward) not provided, will try data range start = t0_f/b+10ns.";
    cerr << endl << ">> NO WARRANTY THAT THIS DOES MAKE ANY SENSE.";
    cerr << endl;
  } else {
    // calculate the max(data range start[i]-t0[i])
    Int_t diff = fRunInfo->GetDataRange(0)-static_cast<Int_t>(t0[0]);
    if (abs(diff) < abs(fRunInfo->GetDataRange(2)-static_cast<Int_t>(t0[1])))
      diff = fRunInfo->GetDataRange(0)-static_cast<Int_t>(t0[0]);

    // calculate start position for plotting
    Int_t val = static_cast<Int_t>(t0[1])+diff-packing*((static_cast<Int_t>(t0[1])+diff)/packing);
    do {
      if (static_cast<Double_t>(val)+t0[1]-t0[0] < 0.0)
        val += packing;
    } while (static_cast<Double_t>(val) + t0[1] - t0[0] < 0.0);

    start[0] = val;
    start[1] = val + static_cast<Int_t>(t0[1] - t0[0]);
  }

  // make sure that there are equal number of rebinned bins in forward and backward
  UInt_t noOfBins0 = (runData->GetDataBin(histoNo[0])->size()-start[0])/packing;
  UInt_t noOfBins1 = (runData->GetDataBin(histoNo[1])->size()-start[1])/packing;
  if (noOfBins0 > noOfBins1)
    noOfBins0 = noOfBins1;
  end[0] = start[0] + noOfBins0 * packing;
  end[1] = start[1] + noOfBins0 * packing;

  // check if start, end, and t0 make any sense
  // 1st check if start and end are in proper order
  for (UInt_t i=0; i<2; i++) {
    if (end[i] < start[i]) { // need to swap them
      Int_t keep = end[i];
      end[i] = start[i];
      start[i] = keep;
    }
    // 2nd check if start is within proper bounds
    if ((start[i] < 0) || (start[i] > (Int_t)runData->GetDataBin(histoNo[i])->size())) {
      cerr << endl << ">> PRunAsymmetry::PrepareViewData(): **ERROR** start data bin doesn't make any sense!";
      cerr << endl;
      return false;
    }
    // 3rd check if end is within proper bounds
    if ((end[i] < 0) || (end[i] > (Int_t)runData->GetDataBin(histoNo[i])->size())) {
      cerr << endl << ">> PRunAsymmetry::PrepareViewData(): **ERROR** end data bin doesn't make any sense!";
      cerr << endl;
      return false;
    }
    // 4th check if t0 is within proper bounds
    if ((t0[i] < 0) || (t0[i] > (Int_t)runData->GetDataBin(histoNo[i])->size())) {
      cerr << endl << ">> PRunAsymmetry::PrepareViewData(): **ERROR** t0 data bin doesn't make any sense!";
      cerr << endl;
      return false;
    }
  }

  // if fit range is given in bins (and not time), the fit start/end time can be calculated at this point now
  if (fRunInfo->IsFitRangeInBin()) {
    fFitStartTime = (fRunInfo->GetDataRange(0) + fRunInfo->GetFitRangeOffset(0) - fT0s[0]) * fTimeResolution; // (fgb+n0-t0)*dt
    fFitEndTime = (fRunInfo->GetDataRange(1) - fRunInfo->GetFitRangeOffset(1) - fT0s[0]) * fTimeResolution;   // (lgb-n1-t0)*dt
  }

  // everything looks fine, hence fill packed forward and backward histo
  PRunData forwardPacked;
  PRunData backwardPacked;
  Double_t value = 0.0;
  Double_t error = 0.0;
  // forward
  for (Int_t i=start[0]; i<end[0]; i++) {
    if (packing == 1) {
      forwardPacked.AppendValue(fForward[i]);
      forwardPacked.AppendErrorValue(fForwardErr[i]);
    } else { // packed data, i.e. packing > 1
      if (((i-start[0]) % packing == 0) && (i != start[0])) { // fill data
        // in order that after rebinning the fit does not need to be redone (important for plots)
        // the value is normalize to per bin
        value /= packing;
        forwardPacked.AppendValue(value);
        if (value == 0.0)
          forwardPacked.AppendErrorValue(1.0);
        else
          forwardPacked.AppendErrorValue(TMath::Sqrt(error)/packing);
        value = 0.0;
        error = 0.0;
      }
      value += fForward[i];
      error += fForwardErr[i]*fForwardErr[i];
    }
  }

  // backward
  for (Int_t i=start[1]; i<end[1]; i++) {
    if (packing == 1) {
      backwardPacked.AppendValue(fBackward[i]);
      backwardPacked.AppendErrorValue(fBackwardErr[i]);
    } else { // packed data, i.e. packing > 1
      if (((i-start[1]) % packing == 0) && (i != start[1])) { // fill data
        // in order that after rebinning the fit does not need to be redone (important for plots)
        // the value is normalize to per bin
        value /= packing;
        backwardPacked.AppendValue(value);
        if (value == 0.0)
          backwardPacked.AppendErrorValue(1.0);
        else
          backwardPacked.AppendErrorValue(TMath::Sqrt(error)/packing);
        value = 0.0;
        error = 0.0;
      }
      value += fBackward[i];
      error += fBackwardErr[i]*fBackwardErr[i];
    }
  }

  // check if packed forward and backward hist have the same size, otherwise take the minimum size
  UInt_t noOfBins = forwardPacked.GetValue()->size();
  if (forwardPacked.GetValue()->size() != backwardPacked.GetValue()->size()) {
    if (forwardPacked.GetValue()->size() > backwardPacked.GetValue()->size())
      noOfBins = backwardPacked.GetValue()->size();
  }

  // form asymmetry including error propagation
  Double_t asym;
  Double_t f, b, ef, eb, alpha = 1.0, beta = 1.0;
  // set data time start, and step
  // data start at data_start-t0
  fData.SetDataTimeStart(fTimeResolution*((Double_t)start[0]-t0[0]+(Double_t)(packing-1)/2.0));
  fData.SetDataTimeStep(fTimeResolution*(Double_t)packing);

  // get the proper alpha and beta
  switch (fAlphaBetaTag) {
    case 1: // alpha == 1, beta == 1
      alpha = 1.0;
      beta  = 1.0;
      break;
    case 2: // alpha != 1, beta == 1
      alpha = par[fRunInfo->GetAlphaParamNo()-1];
      beta  = 1.0;
      break;
    case 3: // alpha == 1, beta != 1
      alpha = 1.0;
      beta  = par[fRunInfo->GetBetaParamNo()-1];
      break;
    case 4: // alpha != 1, beta != 1
      alpha = par[fRunInfo->GetAlphaParamNo()-1];
      beta  = par[fRunInfo->GetBetaParamNo()-1];
      break;
    default:
      break;
  }

  for (UInt_t i=0; i<forwardPacked.GetValue()->size(); i++) {
    // to make the formulae more readable
    f  = forwardPacked.GetValue()->at(i);
    b  = backwardPacked.GetValue()->at(i);
    ef = forwardPacked.GetError()->at(i);
    eb = backwardPacked.GetError()->at(i);
    // check that there are indeed bins
    if (f+b != 0.0)
      asym = (alpha*f-b) / (alpha*beta*f+b);
    else
      asym = 0.0;
    fData.AppendValue(asym);
    // calculate the error
    if (f+b != 0.0)
      error = 2.0/((f+b)*(f+b))*TMath::Sqrt(b*b*ef*ef+eb*eb*f*f);
    else
      error = 1.0;
    fData.AppendErrorValue(error);
  }

  CalcNoOfFitBins();

  // clean up
  fForward.clear();
  fForwardErr.clear();
  fBackward.clear();
  fBackwardErr.clear();

  // fill theory vector for kView
  // calculate functions
  for (Int_t i=0; i<fMsrInfo->GetNoOfFuncs(); i++) {
    fFuncValues[i] = fMsrInfo->EvalFunc(fMsrInfo->GetFuncNo(i), *fRunInfo->GetMap(), par);
  }

  // calculate theory
  Double_t time;
  UInt_t size = runData->GetDataBin(histoNo[0])->size();
  Double_t factor = 1.0;
  if (fData.GetValue()->size() * 10 > runData->GetDataBin(histoNo[0])->size()) {
    size = fData.GetValue()->size() * 10;
    factor = (Double_t)runData->GetDataBin(histoNo[0])->size() / (Double_t)size;
  }
  fData.SetTheoryTimeStart(fData.GetDataTimeStart());
  fData.SetTheoryTimeStep(fTimeResolution*factor);
  for (UInt_t i=0; i<size; i++) {
    time = fData.GetTheoryTimeStart() + (Double_t)i*fTimeResolution*factor;
    value = fTheory->Func(time, par, fFuncValues);
    if (fabs(value) > 10.0) {  // dirty hack needs to be fixed!!
      value = 0.0;
    }
    fData.AppendTheoryValue(value);
  }

  // clean up
  par.clear();

  return true;
}

//--------------------------------------------------------------------------
// PrepareRRFViewData (protected)
//--------------------------------------------------------------------------
/**
 * <p> Prepares the RRF data set for visual representation. This is done the following way:
 * -# make all necessary checks
 * -# build the asymmetry, \f$ A(t) \f$, WITHOUT packing.
 * -# \f$ A_R(t) = A(t) \cdot 2 \cos(\omega_R t + \phi_R) \f$
 * -# do the packing of \f$ A_R(t) \f$
 * -# calculate theory, \f$ T(t) \f$, as close as possible to the time resolution [compatible with the RRF frequency]
 * -# \f$ T_R(t) = T(t) \cdot 2 \cos(\omega_R t + \phi_R) \f$
 * -# do the packing of \f$ T_R(t) \f$
 * -# calculate the Kaiser FIR filter coefficients
 * -# filter \f$ T_R(t) \f$.
 *
 * \param runData raw run data needed to perform some crosschecks
 * \param histoNo array of the histo numbers form which to build the asymmetry
 */
Bool_t PRunAsymmetry::PrepareRRFViewData(PRawRunData* runData, UInt_t histoNo[2])
{
  // feed the parameter vector
  std::vector<Double_t> par;
  PMsrParamList *paramList = fMsrInfo->GetMsrParamList();
  for (UInt_t i=0; i<paramList->size(); i++)
    par.push_back((*paramList)[i].fValue);

  // ------------------------------------------------------------
  // 1. make all necessary checks
  // ------------------------------------------------------------

  // first get start data, end data, and t0
  Int_t start[2] = {0, 0};
  Int_t end[2] = {0, 0};
  Double_t t0[2] = {fT0s[0], fT0s[1]};
  UInt_t packing = fMsrInfo->GetMsrPlotList()->at(0).fRRFPacking;
  Int_t offset = (Int_t)(10.0e-3/fTimeResolution); // needed in case first good bin is not given, default = 10ns
  // check if data range has been provided, and if not try to estimate them
  if (fRunInfo->GetDataRange(0) < 0) {
    start[0] = static_cast<Int_t>(t0[0])+offset;
    cerr << endl << ">> PRunAsymmetry::PrepareRRFViewData(): **WARNING** data range (forward) was not provided, will try data range start = " << start[0] << ".";
    cerr << endl << ">> NO WARRANTY THAT THIS DOES MAKE ANY SENSE.";
    cerr << endl;
  } else if (fRunInfo->GetDataRange(2) < 0) {
    start[1] = static_cast<Int_t>(t0[1])+offset;
    cerr << endl << ">> PRunAsymmetry::PrepareRRFViewData(): **WARNING** data range (backward) was not provided, will try data range start = " << start[1] << ".";
    cerr << endl << ">> NO WARRANTY THAT THIS DOES MAKE ANY SENSE.";
    cerr << endl;
  } else {
    Int_t val = fRunInfo->GetDataRange(0)-packing*(fRunInfo->GetDataRange(0)/packing);
    do {
      if (fRunInfo->GetDataRange(2) - fRunInfo->GetDataRange(0) < 0)
        val += packing;
    } while (val + fRunInfo->GetDataRange(2) - fRunInfo->GetDataRange(0) < 0);

    start[0] = val;
    start[1] = val + fRunInfo->GetDataRange(2) - fRunInfo->GetDataRange(0);
  }

  // make sure that there are equal number of rebinned bins in forward and backward
  UInt_t noOfBins0 = runData->GetDataBin(histoNo[0])->size()-start[0];
  UInt_t noOfBins1 = runData->GetDataBin(histoNo[1])->size()-start[1];
  if (noOfBins0 > noOfBins1)
    noOfBins0 = noOfBins1;
  end[0] = start[0] + noOfBins0;
  end[1] = start[1] + noOfBins0;

  // check if start, end, and t0 make any sense
  // 1st check if start and end are in proper order
  for (UInt_t i=0; i<2; i++) {
    if (end[i] < start[i]) { // need to swap them
      Int_t keep = end[i];
      end[i] = start[i];
      start[i] = keep;
    }
    // 2nd check if start is within proper bounds
    if ((start[i] < 0) || (start[i] > (Int_t)runData->GetDataBin(histoNo[i])->size())) {
      cerr << endl << ">> PRunAsymmetry::PrepareRRFViewData(): **ERROR** start data bin doesn't make any sense!";
      cerr << endl;
      return false;
    }
    // 3rd check if end is within proper bounds
    if ((end[i] < 0) || (end[i] > (Int_t)runData->GetDataBin(histoNo[i])->size())) {
      cerr << endl << ">> PRunAsymmetry::PrepareRRFViewData(): **ERROR** end data bin doesn't make any sense!";
      cerr << endl;
      return false;
    }
    // 4th check if t0 is within proper bounds
    if ((t0[i] < 0) || (t0[i] > (Int_t)runData->GetDataBin(histoNo[i])->size())) {
      cerr << endl << ">> PRunAsymmetry::PrepareRRFViewData(): **ERROR** t0 data bin doesn't make any sense!";
      cerr << endl;
      return false;
    }
  }

  // ------------------------------------------------------------
  // 2. build the asymmetry [A(t)] WITHOUT packing.
  // ------------------------------------------------------------

  PDoubleVector forward, forwardErr;
  PDoubleVector backward, backwardErr;
  Double_t error = 0.0;
  // forward
  for (Int_t i=start[0]; i<end[0]; i++) {
    forward.push_back(fForward[i]);
    forwardErr.push_back(fForwardErr[i]);
  }
  // backward
  for (Int_t i=start[1]; i<end[1]; i++) {
    backward.push_back(fBackward[i]);
    backwardErr.push_back(fBackwardErr[i]);
  }

  // check if packed forward and backward hist have the same size, otherwise take the minimum size
  UInt_t noOfBins = forward.size();
  if (forward.size() != backward.size()) {
    if (forward.size() > backward.size())
      noOfBins = backward.size();
  }

  // form asymmetry including error propagation
  PDoubleVector asymmetry, asymmetryErr;
  Double_t asym;
  Double_t f, b, ef, eb, alpha = 1.0, beta = 1.0;

  // get the proper alpha and beta
  switch (fAlphaBetaTag) {
    case 1: // alpha == 1, beta == 1
      alpha = 1.0;
      beta  = 1.0;
      break;
    case 2: // alpha != 1, beta == 1
      alpha = par[fRunInfo->GetAlphaParamNo()-1];
      beta  = 1.0;
      break;
    case 3: // alpha == 1, beta != 1
      alpha = 1.0;
      beta  = par[fRunInfo->GetBetaParamNo()-1];
      break;
    case 4: // alpha != 1, beta != 1
      alpha = par[fRunInfo->GetAlphaParamNo()-1];
      beta  = par[fRunInfo->GetBetaParamNo()-1];
      break;
    default:
      break;
  }
//cout << endl << ">> alpha = " << alpha << ", beta = " << beta;

  for (UInt_t i=0; i<noOfBins; i++) {
    // to make the formulae more readable
    f  = forward[i];
    b  = backward[i];
    ef = forwardErr[i];
    eb = backwardErr[i];
    // check that there are indeed bins
    if (f+b != 0.0)
      asym = (alpha*f-b) / (alpha*beta*f+b);
    else
      asym = 0.0;
    asymmetry.push_back(asym);
    // calculate the error
    if (f+b != 0.0)
      error = 2.0/((f+b)*(f+b))*TMath::Sqrt(b*b*ef*ef+eb*eb*f*f);
    else
      error = 1.0;
    asymmetryErr.push_back(error);
  }

  // clean up
  fForward.clear();
  fForwardErr.clear();
  fBackward.clear();
  fBackwardErr.clear();


  // ------------------------------------------------------------
  // 3. A_R(t) = A(t) * 2 cos(w_R t + phi_R)
  // ------------------------------------------------------------

  // check which units shall be used
  Double_t gammaRRF = 0.0, wRRF = 0.0, phaseRRF = 0.0;
  Double_t time;

  switch (fMsrInfo->GetMsrPlotList()->at(0).fRRFUnit) {
    case RRF_UNIT_kHz:
      gammaRRF = TMath::TwoPi()*1.0e-3;
      break;
    case RRF_UNIT_MHz:
      gammaRRF = TMath::TwoPi();
      break;
    case RRF_UNIT_Mcs:
      gammaRRF = 1.0;
      break;
    case RRF_UNIT_G:
      gammaRRF = GAMMA_BAR_MUON*TMath::TwoPi();
      break;
    case RRF_UNIT_T:
      gammaRRF = GAMMA_BAR_MUON*TMath::TwoPi()*1.0e4;
      break;
    default:
      gammaRRF = TMath::TwoPi();
      break;
  }
  wRRF = gammaRRF * fMsrInfo->GetMsrPlotList()->at(0).fRRFFreq;
  phaseRRF = fMsrInfo->GetMsrPlotList()->at(0).fRRFPhase / 180.0 * TMath::Pi();

  for (UInt_t i=0; i<asymmetry.size(); i++) {
    time = fTimeResolution*(static_cast<Double_t>(start[0])-t0[0]+static_cast<Double_t>(i));
    asymmetry[i] *= 2.0*TMath::Cos(wRRF*time+phaseRRF);
  }

  // ------------------------------------------------------------
  // 4. do the packing of A_R(t)
  // ------------------------------------------------------------
  Double_t value = 0.0;
  error = 0.0;
  for (UInt_t i=0; i<asymmetry.size(); i++) {
    if ((i % packing == 0) && (i != 0)) {
      value /= packing;
      fData.AppendValue(value);
      fData.AppendErrorValue(TMath::Sqrt(error)/packing);

      value = 0.0;
      error = 0.0;
    }
    value += asymmetry[i];
    error += asymmetryErr[i]*asymmetryErr[i];
  }

  // set data time start, and step
  // data start at data_start-t0
  fData.SetDataTimeStart(fTimeResolution*(static_cast<Double_t>(start[0])-t0[0]+static_cast<Double_t>(packing-1)/2.0));
  fData.SetDataTimeStep(fTimeResolution*static_cast<Double_t>(packing));

  // ------------------------------------------------------------
  // 5. calculate theory [T(t)] as close as possible to the time resolution [compatible with the RRF frequency]
  // 6. T_R(t) = T(t) * 2 cos(w_R t + phi_R)
  // ------------------------------------------------------------
  UInt_t rebinRRF = static_cast<UInt_t>((TMath::Pi()/2.0/wRRF)/fTimeResolution); // RRF time resolution / data time resolution
  fData.SetTheoryTimeStart(fData.GetDataTimeStart());
  fData.SetTheoryTimeStep(TMath::Pi()/2.0/wRRF/rebinRRF); // = theory time resolution as close as possible to the data time resolution compatible with wRRF

/*
cout << endl << ">> rebinRRF = " << rebinRRF;
cout << endl << ">> theory time start = " << fData.GetTheoryTimeStart();
cout << endl << ">> theory time step  = " << fData.GetTheoryTimeStep();
cout << endl;
*/

  // calculate functions
  for (Int_t i=0; i<fMsrInfo->GetNoOfFuncs(); i++) {
    fFuncValues[i] = fMsrInfo->EvalFunc(fMsrInfo->GetFuncNo(i), *fRunInfo->GetMap(), par);
  }

  Double_t theoryValue;
  for (UInt_t i=0; i<asymmetry.size(); i++) {
    time = fData.GetTheoryTimeStart() + static_cast<Double_t>(i)*fData.GetTheoryTimeStep();
    theoryValue = fTheory->Func(time, par, fFuncValues);
    theoryValue *= 2.0*TMath::Cos(wRRF * time + phaseRRF);

    if (fabs(theoryValue) > 10.0) { // dirty hack needs to be fixed!!
      theoryValue = 0.0;
    }

    fData.AppendTheoryValue(theoryValue);
  }

  // ------------------------------------------------------------
  // 7. do the packing of T_R(t)
  // ------------------------------------------------------------

  PDoubleVector theo;
  Double_t dval = 0.0;
  for (UInt_t i=0; i<fData.GetTheory()->size(); i++) {
    if ((i % rebinRRF == 0) && (i != 0)) {
//cout << endl << "time = " << fData.GetTheoryTimeStart() + i * fData.GetTheoryTimeStep() << ", theory value = " << dval;
      theo.push_back(dval/rebinRRF);
      dval = 0.0;
    }
    dval += fData.GetTheory()->at(i);
  }
  fData.ReplaceTheory(theo);
  theo.clear();

  // set the theory time start and step size
  fData.SetTheoryTimeStart(fData.GetTheoryTimeStart()+static_cast<Double_t>(rebinRRF-1)*fData.GetTheoryTimeStep()/2.0);
  fData.SetTheoryTimeStep(rebinRRF*fData.GetTheoryTimeStep());

  // ------------------------------------------------------------
  // 8. calculate the Kaiser FIR filter coefficients
  // ------------------------------------------------------------
  CalculateKaiserFilterCoeff(wRRF, 60.0, 0.2); // w_c = wRRF, A = -20 log_10(delta), Delta w / w_c = (w_s - w_p) / (2 w_c)

  // ------------------------------------------------------------
  // 9. filter T_R(t)
  // ------------------------------------------------------------
  FilterTheo();

  // clean up
  par.clear();
  forward.clear();
  forwardErr.clear();
  backward.clear();
  backwardErr.clear();
  asymmetry.clear();
  asymmetryErr.clear();

  return true;
}
