/*
$Id: DoubleBinArrange.cc 2374 2012-04-19 01:57:28Z jisuzuki $
*/

#include "DoubleBinArrange.hh"
////////////////////////////////////////////////////////
DoubleBinArrange::
DoubleBinArrange(UInt4 _SourceBinNumber,
                 UInt4 _ResultBinNumber )
{
  SourceBinNumber = _SourceBinNumber;
  ResultBinNumber = _ResultBinNumber;
  MakeArray();
  AveFlag = 0;
  BinFlag = 0;
}
////////////////////////////////////////////////////////
DoubleBinArrange::
~DoubleBinArrange()
{
  delete CPercent;
  delete CNumber;
}
////////////////////////////////////////////////////////
void DoubleBinArrange::
SetHist( const std::vector<Double> &SourceBinBoundary,
         const std::vector<Double> &SourceValue,
         const std::vector<Double> &__SourceError,
         const std::vector<Double> &ResultBinBoundary )
{
  if( SourceValue.size() != __SourceError.size() ){
    std::cout << "The sizes of SourceValue and SourceError should be equal"
         << std::endl;
  }

  if( SourceValue.size()+1 != SourceBinBoundary.size() ){
    std::cout << "The set of vectors should be applied to "
         << "the histogram. "
         << "The size of SourceBinBoundary should be larger than "
         << "that of SourceValue by one."
         << std::endl;
  }

  SetSourceBinBoundary( SourceBinBoundary );
  SetResultBinBoundary( ResultBinBoundary );
  SetSourceValue( SourceValue );
  SourceError.resize(__SourceError.size());

  //std::transform(__SourceError.begin(), __SourceError.end(), SourceError.begin(), ::fabs);
  // This source is fixed by Jiro SUZUKI on 2012.04.19.
  // This bug was reported by Yasuhiro INAMURA.

  for( UInt4 i=0; i<__SourceError.size(); i++ ){
    SourceError[i] = __SourceError[i];
  }

  FormatBin();


}
////////////////////////////////////////////////////////
void DoubleBinArrange::
Averaging()
{
  if( AveFlag == 1 ){
    std::cout << "You have already executed \"Averaging()\", ";
    std::cout << "please extract the result and destruct this object." << std::endl;
    return;
  }
  if( BinFlag == 1 ){
    std::cout << "You have already executed \"Binning()\", ";
    std::cout << "please extract the result and destruct this object." << std::endl;
    return;
  }


  for( UInt4 b=0; b<ResultBinNumber; b++ ){
    UInt4 repeat = (UInt4)(CNumber->PutPointer(b)->size());
    std::vector<Double>::iterator P = CPercent->PutPointer(b)->begin();
    std::vector<UInt4> ::iterator N = CNumber ->PutPointer(b)->begin();
    Double A = 0.0;
    Double B = 0.0;
    for( UInt4 r=0; r<repeat; r++ ){
      if( SourceError[ N[r] ] != 0.0 ){
        A = A + P[r] * SourceValue[ N[r] ] /
            (Double)pow( (Double)SourceError[ N[r] ], 2.0 );
        B = B + P[r] /
            (Double)pow( (Double)SourceError[ N[r] ], 2.0 );
      }
    }

    if( B==0.0 ){
      ResultValue[b] = 0.0;
      ResultError[b] = 0.0;
    }
    else{
      ResultValue[b] = A / B;
      ResultError[b] = 1.0 / sqrt( fabs( B ) );
    }
  }
  AveFlag = 1;
}
////////////////////////////////////////////////////////
void DoubleBinArrange::
Binning( Int4 DefaultErrorValue )
{
  if( AveFlag == 1 ){
    std::cout << "You have already executed \"Averaging()\", ";
    std::cout << "please extract the result and destruct this object." << std::endl;
    return;
  }
  if( BinFlag == 1 ){
    std::cout << "You have already executed \"Binning()\", ";
    std::cout << "please extract the result and destruct this object." << std::endl;
    return;
  }

  NeutronVector< std::vector<UInt4>, HeaderBase >  index;
  NeutronVector< std::vector<Double>, HeaderBase > percent;

  for( unsigned int i=0; i<ResultBinNumber; i++ ){
    std::vector<UInt4> *u  = new std::vector<UInt4>();
    std::vector<Double> *d = new std::vector<Double>();
    index.AddPointer( u );
    percent.AddPointer( d );
  }

  for( unsigned int i=0; i<ResultBinNumber; i++ ){
    Double min = ResultBinMin[i];
    Double max = ResultBinMax[i];

    Int4 IniBin = SelectOrigBin( min );
    Int4 FinBin = SelectOrigBin( max );

    if( IniBin<0 && IniBin==FinBin ){ }
    else{
      if( IniBin < 0 ){ IniBin = 0; }
      if( FinBin < 0 ){ FinBin = SourceBinNumber-1; }
    }


    if( !( IniBin<0 && IniBin==FinBin ) ){
      for( Int4 j=IniBin; j<=FinBin; j++ ){
        double sourcebinmin = SourceBinMin[j];
        double sourcebinmax = SourceBinMax[j];
        double sourcewidth = sourcebinmax - sourcebinmin;
        double width = 0.0;

        if( sourcebinmin <= min && max <= sourcebinmax ){
          width = max-min;
        }
        else if( min <= sourcebinmin && max < sourcebinmax ){
          width = max - sourcebinmin;
        }
        else if( sourcebinmin < min && sourcebinmax <= max ){
          width = sourcebinmax - min;
        }
        else{
          width = sourcebinmax - sourcebinmin;
        }
        if (width!=0.0){ // Added by Y.I and T.I 2015.09.24 fixed the issue to set unnecessary mask.
          index.PutPointer(i)->push_back( j );
          percent.PutPointer(i)->push_back( width/sourcewidth );
        }
      }
    }
  }

  for( UInt4 i=0; i<ResultBinNumber; i++ ){
    std::vector<UInt4> *vi  = index.PutPointer(i);
    std::vector<Double> *vd = percent.PutPointer(i);
    double e = 0.0;
    Int4 Flag = -1;

    for( UInt4 j=0; j<vi->size(); j++ ){
      UInt4 SourceTarget   = ( *vi )[j];
      Double SourceRate = ( *vd )[j];


      /***************************/
      if( SourceError[SourceTarget] < 0.0 ){
        ResultValue[i] = ResultValue[i] + SourceValue[SourceTarget] * SourceRate;
        e = e +
            ( SourceError[SourceTarget] * SourceRate ) *
            ( SourceError[SourceTarget] * SourceRate );
        Flag = -2;
      }
      else{
        ResultValue[i] = ResultValue[i] + SourceValue[SourceTarget] * SourceRate;
        e = e +
            ( SourceError[SourceTarget] * SourceRate ) *
            ( SourceError[SourceTarget] * SourceRate );
        //Flag = 1;
      }
      /************************/
    }
    if( Flag == -2 ){
      ResultError[i] = -1.0 * sqrt( e );
    }
    else{
      ResultError[i] = sqrt( e );
      //ResultError[i] = (Double)DefaultErrorValue;
    }
    /*
    if( Flag == 1 ){
      ResultError[i] = sqrt( e );
    }
    else{
      ResultError[i] = (Double)DefaultErrorValue;
    }
    */
  }
  BinFlag = 1;
}
////////////////////////////////////////////////////////
Int4 DoubleBinArrange::
SelectOrigBin( Double value )
{
  if( value <  SourceBinMin[0] ){
    return -3;
  }

  if( value > SourceBinMax[SourceBinNumber-1] ){
    return -2;
  }

  for( UInt4 i=1; i<SourceBinNumber; i++ ){
    if( value < SourceBinMin[i] ){
      return i-1;
    }
  }

  if( value <= SourceBinMax[SourceBinNumber-1] ){
    return SourceBinNumber-1;
  }

  return -4;

}
////////////////////////////////////////////////////////
Int4 DoubleBinArrange::
SelectResultBin( Double value )
{
  if( value <  ResultBinMin[0] ) return -3;
  if( value >= ResultBinMax[ResultBinNumber-1] ) return -2;
  for( UInt4 i=1; i<ResultBinNumber; i++ )
    if( value < ResultBinMin[i] ) return i-1;
  return -4;
}
////////////////////////////////////////////////////////
void DoubleBinArrange::
FormatBin()
{
  // _e is the ambiguity on calculations with DBL_EPSILON for (Width / OrgWidth)
  // K = (Width / OrgWIdth), DBL_EPSILON = EPS
  // (delta-K)^2 = EPS^2 * { dK/d(Width) }^2 + EPS^2 * { dK/d(OrgWidth) }^2
  //             = EPS^2 * { (1/OrgWidth)^2  + Width^2 * ( -1.0/ (OrgWidth^2))^2 }
  //             = EPS^2 / (OrgWidth^2) * { 1 + K^2 }
  //  delta-K = EPS / OrgWidth * sqrt( 1 + K^2 )

  //Double _eps = (DBL_EPSILON * SourceBinNumber) / (SourceBinMax[0] - SourceBinMin[0]) * sqrt(2);
  //Double _eps_log10= log10(_eps);
  //Int4 _exp = 0.0;
  //if (_eps_log10 <= 0.0){
  //    _exp = int(_eps_log10);
  //}else{
  //    _exp = int(_eps_log10 + 1);
  //}
  //Double _e = pow(10, _exp);
  Double _e = 0.0;

  for( UInt4 bin=0; bin<ResultBinNumber; bin++){
    Int4 IniBin = SelectOrigBin( ResultBinMin[bin] );
    Int4 FinBin = SelectOrigBin( ResultBinMax[bin] );

    if( IniBin > -1 && FinBin > -1 ){
      ///////
      if( IniBin == FinBin ){
        Double OrigWidth = fabs( SourceBinMax[IniBin]-SourceBinMin[IniBin] );
        Double ResultWidth = fabs( ResultBinMax[bin]-ResultBinMin[bin] );
        Double p = ResultWidth / OrigWidth;
        Double _eps = ((std::numeric_limits<double>::epsilon()) * SourceBinNumber) / OrigWidth * sqrt(1.0 + p * p);
        Double _eps_log10 = log10(_eps);
        if (_eps_log10 <= 0.0) _e = pow(10, int(_eps_log10));
        else _e = pow(10, int(_eps_log10 + 1));
        if ( fabs(p - 1.0) < _e ) p = 1.0;
        if ( fabs(p) < _e ) p = 0.0;
        CPercent -> PutPointer(bin) -> push_back( p );
        //CPercent -> PutPointer(bin) -> push_back( ResultWidth / OrigWidth );
        CNumber  -> PutPointer(bin) -> push_back( (UInt4)IniBin );
      }
      ///////
      else{
        Double OrigWidthMin = fabs( SourceBinMax[IniBin]-SourceBinMin[IniBin] );
        Double OrigWidthMax = fabs( SourceBinMax[FinBin]-SourceBinMin[FinBin] );
        Double WidthMin = fabs( SourceBinMax[IniBin]-ResultBinMin[bin]);
        Double WidthMax = fabs( ResultBinMax[bin]   -SourceBinMin[FinBin]);

        Double p = WidthMin / OrigWidthMin ;
        Double _eps = ((std::numeric_limits<double>::epsilon()) * SourceBinNumber) / OrigWidthMin * sqrt(1.0 + p * p);
        Double _eps_log10 = log10(_eps);
        if (_eps_log10 <= 0.0) _e = pow(10, int(_eps_log10));
        else _e = pow(10, int(_eps_log10 + 1));
        if ( fabs(p - 1.0) < _e ) p = 1.0;
        if ( fabs(p) < _e ) p = 0.0;
        CPercent -> PutPointer(bin) -> push_back( p );
        //CPercent -> PutPointer(bin) -> push_back( WidthMin / OrigWidthMin );
        CNumber  -> PutPointer(bin) -> push_back( (UInt4)IniBin );

        for( Int4 b=IniBin+1; b<FinBin; b++ ){
          CPercent -> PutPointer(bin) -> push_back( 1.0 );
          CNumber  -> PutPointer(bin) -> push_back( b );
        }

        p = WidthMax / OrigWidthMax;
        if ( fabs(p - 1.0) < _e ) p = 1.0;
        if ( fabs(p) < _e ) p = 0.0;
        CPercent -> PutPointer(bin) -> push_back( p );
        //CPercent -> PutPointer(bin) -> push_back( WidthMax / OrigWidthMax );
        CNumber  -> PutPointer(bin) -> push_back( (UInt4)FinBin );
      }
      ///////
    }
  }
}
////////////////////////////////////////////////////////
void DoubleBinArrange::
SetSourceBinBoundary( const std::vector<Double> &array )
{
  SourceBinMin.assign(&array[0], &array[SourceBinNumber]);
//  SourceBinMax.assign(&array[1], &array[SourceBinNumber+1]);
  SourceBinMax.assign(array.begin()+1, array.begin()+SourceBinNumber+1); //BBT-20140501
}
////////////////////////////////////////////////////////
void DoubleBinArrange::
SetResultBinBoundary( const std::vector<Double> &array )
{
  ResultBinMin.assign(&array[0], &array[ResultBinNumber]);
//  ResultBinMax.assign(&array[1], &array[ResultBinNumber+1]);
  ResultBinMax.assign(array.begin()+1, array.begin()+ResultBinNumber+1); //BBT-20140501
}
////////////////////////////////////////////////////////
void DoubleBinArrange::
SetSourceValue( const std::vector<Double> &array )
{
  SourceValue = array;
}
////////////////////////////////////////////////////////
void DoubleBinArrange::
SetSourceError( const std::vector<Double> &array )
{
  SourceError = array;
}
////////////////////////////////////////////////////////
void DoubleBinArrange::
MakeArray()
{
  SourceBinMin.assign(SourceBinNumber, 0.0);
  SourceBinMax.assign(SourceBinNumber, 0.0);
  SourceValue.assign(SourceBinNumber, 0.0);
  SourceError.assign(SourceBinNumber, 0.0);

  ResultBinMin.assign(ResultBinNumber, 0.0);
  ResultBinMax.assign(ResultBinNumber, 0.0);
  ResultValue.assign(ResultBinNumber, 0.0);
  ResultError.assign(ResultBinNumber, 0.0);

  HeaderBase CPHeader;
  HeaderBase UIHeader;
  CPercent = new NeutronVector< std::vector<Double>, HeaderBase >( CPHeader );
  CNumber  = new NeutronVector< std::vector<UInt4>, HeaderBase >( UIHeader );

  for( UInt4 i=0; i<ResultBinNumber; i++ ){

    /*
    std::vector<UInt4>  *N = new std::vector<UInt4>;
    std::vector<Double> *P = new std::vector<Double>;
    N -> clear(); P -> clear();
    CPercent -> Add( P );
    CNumber  -> Add( N );
    */

    std::vector<UInt4>  N;
    std::vector<Double> P;
    N.clear();
    P.clear();
    CPercent -> Add( P );
    CNumber  -> Add( N );
  }
}
////////////////////////////////////////////////////////
std::vector<Double> &DoubleBinArrange::
PutResultBinMin()
{
  return ResultBinMin;
}
////////////////////////////////////////////////////////
std::vector<Double> &DoubleBinArrange::
PutResultBinMax()
{
  return ResultBinMax;
}
////////////////////////////////////////////////////////
std::vector<Double> DoubleBinArrange::
PutResultBin()
{
  std::vector<Double> v( ResultBinNumber );
  for( UInt4 i=0; i<ResultBinNumber; i++ ){
    v[i] = ( ResultBinMin[i] + ResultBinMax[i] ) / 2.0;
  }
  return v;
}
////////////////////////////////////////////////////////
std::vector<Double> DoubleBinArrange::
PutResultBinVector()
{
  std::vector<Double> v = ResultBinMin;
  v.push_back(ResultBinMax[ ResultBinNumber-1 ]);
  return v;
}
////////////////////////////////////////////////////////
std::vector<Double> &DoubleBinArrange::
PutResultValue()
{
  return ResultValue;
}
////////////////////////////////////////////////////////
std::vector<Double> &DoubleBinArrange::
PutResultError()
{
  return ResultError;
}
////////////////////////////////////////////////////////

