/*
$Id: ElementContainer.hh 2295 2011-08-10 02:57:13Z jisuzuki $
*/


#ifndef ELEMENTCONTAINER
#define ELEMENTCONTAINER

#include <algorithm>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/string.hpp>
#include <nexus/napi.h>
#include <sstream>

#include "Header.hh"
#include "HeaderBase.hh"
#include "CppToPython.hh"
#include "StlMapDouble.hh"
#include "NeutronWriteBinaryData.hh"
#include "NeutronReadBinaryData.hh"
#include "SimpleIOVectorToTextFile.hh"
#include "DoubleBinArrange.hh"

//!std::vector<Double> container with their name tag.
/*!
  This class is used as a data container.
  The STL vectors, "std::vector<Double>", and each its name tag
  are stored in this class object.

  Comparing with NeutronVectorS,
  this class has some methods which is
  convenient to use std::vector<Double>.
  Since this class can not store array-objects,
  this class is more effective than "NeutronVectorS".
  This class is also designed to use this class
  from Python environment.
  Array objects of C++ are not convenient to use,
  because special functions on the Python
  are needed to access C++ arrays.
  But STL-vector of C++ is a little easy to use on the
  Python environment, because STL-vector can be converted
  to Python-list object easily, and in some cases,
  STL-vector can be accessed directly
  on the Python with SWIG-interface.

  "Key" is the name tag assigned to each std::vector<Double>.
  Each std::vector<Double> stored in this class object can be extracted
  with the "Key" or "IndexNumber".
  If you want to know their names registered in this storage,
  use "DumpName()" and you can see their names
  on the standard-output.

  When the destructor of this class is called,
  all data containers and a header object installed
  in this class will deleted completely.
  Copy constructor of this class can also work completely
  on Python.
*/

class ElementContainer
{
private:

  // If the vector named "Error-key" in the argument contains
  // a negative value element, this method returns "true".
  bool IsMaskData( std::vector<Double> &v, const std::vector<Double> *vp );

  //std::vector< std::vector< Double >* > v;
  //std::vector< std::string > s;
  StlMapDouble M;
  /*!< @brief strage area of ElementContainer data container.

    It is a std::map of keys/std::vector<double>, and each vetor<double> fields
    contains various data.
   */
  HeaderBase *header;
  HeaderBase *UnitHeader;

  std::string Xkey, Ykey, Ekey;

  void _Add( const std::string &Key, std::vector< Double > &value, std::string Unit="None" )
  {  M.Add( Key, value ); UnitHeader -> Add( Key, Unit ); }


  /** Formatter2; performs rebinning of myself or makes new vectors
   * from righthand side object r with rebinning
   *
   * @param rx pointer to a const X std::vector in r
   * @param ry a reference of the pointer to a const Y std::vector in r
   * @param re a reference of the pointer to a const Err std::vector in r
   */
  void Formatter2(
    const std::vector<Double> *rx, const std::vector<Double> *&ry,
    const std::vector<Double> *&re);

  friend class boost::serialization::access;
  /** serialize method used from boost::serialization::access
   *
   */
  template <class Archive>
  void serialize(Archive &ar, const unsigned int ver);

  friend class WriteNeXusFile;
  /** called from WriteNEXusFile::WriteData()
   *
   */
  template <class Write>
  void NXwrite(Write &W) const ;

  friend class ReadNeXusFile;
  /** called from ReadNeXusFile::ReadData()
   *
   */
  template <class Read>
  void NXread(Read &R) ;

  bool HistFlag;

protected:

public:
  ElementContainer();
  /*!< @brief Constructor.

  If the header object is not required,
   use this method.*/
  ElementContainer( const HeaderBase &Header );
  /*!< @brief Constructor.

  The header object is received and held
    in this storage. */

  ElementContainer( const ElementContainer &ob );
  /*!< @brief Override the copy constructor. */

  ~ElementContainer();
  /*!< @brief Destructor. Delete all objects stored in this storage.*/

  ElementContainer &operator=( const ElementContainer &ob );
  /*!< @brief Override the operator of "=".  */

  ElementContainer &operator+=(const ElementContainer &r );
  /**< += operation of ElementContainers with rebinning and error propagation */

  ElementContainer &MaskedPlus(const ElementContainer &r );
  ElementContainer &MaskedSub( const ElementContainer &r );
  ElementContainer &MaskedMul( const ElementContainer &r );
  ElementContainer &MaskedDiv( const ElementContainer &r );


  ElementContainer &operator+=( const std::pair<Double, Double> &r );
  /**< += operation of a value (with error) */

  ElementContainer &operator+=( const Double r ) {
    return *this += std::make_pair(r, 0.0);
  }
  /**< += operation of a value (without error) */

  ElementContainer operator+(const ElementContainer &r ) const {
    ElementContainer result(*this);
    return result += r;
  }
  /*!< @brief Override the operator of "+".

    The returned ElementContainer should delete in your codes.
   */

  ElementContainer operator+(const std::pair<Double, Double> &r ) const {
    ElementContainer result(*this);
    return result += r;
  }
  /**< + operation of a ElementContainer and a value (with error) */

  ElementContainer operator+(const Double r ) const {
    ElementContainer result(*this);
    return result += r;
  }
  /**< + operation of a value (without error) */


  ElementContainer &operator-=(const ElementContainer &r );
  /**< -= operation of ElementContainers with rebinning and error propagation */

  ElementContainer &operator-=( const std::pair<Double, Double> &r );
  /**< -= operation of a value (with error) */

  ElementContainer &operator-=( const Double r ) {
    return *this -= std::make_pair(r, 0.0);
  }
  /**< -= operation of a value (without error) */

  ElementContainer operator-( ElementContainer &r ) const {
    ElementContainer result(*this);
    return result -= r;
  }
  /*!< @brief See the comment of "operator+". */

  ElementContainer operator-(const std::pair<Double, Double> &r ) const {
    ElementContainer result(*this);
    return result -= r;
  }
  /**< subtract(-) operation of a ElementContainer and a value (with error) */

  ElementContainer operator-(const Double r ) const {
    ElementContainer result(*this);
    return result -= r;
  }
  /**< subtract(-) operation of a value (without error) */


  ElementContainer &operator*=(const ElementContainer &r );
  /**< *= operation of ElementContainers with rebinning and error propagation */

  ElementContainer &operator*=( const std::pair<Double, Double> &r );
  /**< *= operation of a value (with error) */

  ElementContainer &operator*=( const Double r ) {
    return *this *= std::make_pair(r, 0.0);
  }
  /**< *= operation of a value (without error) */

  ElementContainer operator*( ElementContainer &r ) const {
    ElementContainer result(*this);
    return result *= r;
  }
  /*!< @brief See the comment of "operator+". */

  ElementContainer operator*(const std::pair<Double, Double> &r ) const {
    ElementContainer result(*this);
    return result *= r;
  }
  /**< * operation of a ElementContainer and a value (with error) */

  ElementContainer operator*(const Double r ) const {
    ElementContainer result(*this);
    return result *= r;
  }
  /**< * operation of a value (without error) */


  ElementContainer &operator/=(const ElementContainer &r );
  /**< /= operation of ElementContainers with rebinning and error propagation */

  ElementContainer &operator/=( const std::pair<Double, Double> &r );
  /**< /= operation of a value (with error) */

  ElementContainer &operator/=( const Double r ) {
    return *this /= std::make_pair(r, 0.0);
  }
  /**< /= operation of a value (without error) */

  ElementContainer operator/( ElementContainer &r ) const {
    ElementContainer result(*this);
    return result /= r;
  }
  /*!< @brief See the comment of "operator+".

    If the denominator is zero, the Y-value and its error in returned object
    are set as 0.0 and 1.0, respectively.
   */

  ElementContainer operator/(const std::pair<Double, Double> &r ) const {
    ElementContainer result(*this);
    return result /= r;
  }
  /**< / operation of a ElementContainer and a value (with error) */

  ElementContainer operator/(const Double r ) const {
    ElementContainer result(*this);
    return result /= r;
  }
  /**< / operation of a value (without error) */

  bool isHist();

  void HistToScat();
  void ScatToHist();

  ElementContainer ReBin( ElementContainer &E, std::string &Key ){return ReBin( E.Put( Key ) );}
  /*!< @brief The number of bin and its width of the histogram are modified.

    The bin properties are set by the vector extracted from the arguments.
    The returned container has only three columns
    which have specified as x, y, and ye.
    The original data-container, "*this" is not changed,
    and the returned ElementContainer should delete in your codes.
  */
  ElementContainer ReBin( const std::vector<Double> &v );
  /*!< @brief Core of Rebin() method. (averaging mode)
    See the comment of "ReBin(ElementContainer*, std::string)".

    You can define the bin propertis of the histogram directory.
    @param v    reference to a reference std::vector
    @return    new ElementContainer with a X std::vector same with specified
        reference std::vector, Y and E std::vector re-binned from this ElementContainer.
   */

  ElementContainer ReBin( UInt4 n );
  ElementContainer Binning( UInt4 n );
  ElementContainer Averaging( UInt4 n );
  ElementContainer MergeWithWeight( ElementContainer &e );

  //ElementContainer ReBin( UInt4 n ){ ReBin( PrepareXbin( n ) ); }
  std::vector<Double> PrepareXbin( UInt4 n );

  ElementContainer Binning( const std::vector<Double> &v );
  /*!< @brief Core of Binning() method. (binning mode)

    @param v    reference to a reference std::vector
    @return    new ElementContainer with a X std::vector same with specified
        reference std::vector, Y and E std::vector re-binned from this ElementContainer.
   */
  ElementContainer Binning( ElementContainer &E, std::string &Key )
  {return Binning( E.Put( Key ) );}

  ElementContainer Averaging( const std::vector<Double> &v );
  /*!< @brief Core of Binning() method. (binning mode)

    @param v    reference to a reference std::vector
    @return    new ElementContainer with a X std::vector same with specified
        reference std::vector, Y and E std::vector re-binned from this ElementContainer.
   */
  ElementContainer Averaging( ElementContainer &E, std::string &Key )
  {return Averaging( E.Put( Key ) );}


  bool SetKeys( const std::string &X, const std::string &Y, const std::string &E );
  /*!< @brief Set keys, they are key-names.

   "X" is assigned to the x-value column name,
   and "Y" is assigned to the y-value column name,
   "E" is assigned to the y-value-error column name.
   The x, y and error column with their key should be installed
   by the methods of "Add(...)" before this method is called.
   The keys, "X", "Y" and "E" were set as "None" in the constructor.
  */

  const std::string &PutXKey() const { return Xkey; }
  /*!< @brief Return X-key in string. */

  const std::string &PutYKey() const { return Ykey; }
  /*!< @brief Return Y-key in string. */

  const std::string &PutEKey() const { return Ekey; }
  /*!< @brief Return Y-Error-key in string. */

  std::vector<Double> PutX();
  /*!< @brief Return the vector assigned to X-value. */

  std::vector<Double> PutY();
  /*!< @brief Return the vector assigned to Y-value. */

  std::vector<Double> PutE();
  /*!< @brief Return the vector assigned to the error of Y-value. */

  PyObject *PutXList(){ return PutList( PutXKey() ); }
  /*!< @brief Return the Python-List assigned to X-value. */

  PyObject *PutYList(){ return PutList( PutYKey() ); }
  /*!< @brief Return the Python-List assigned to Y-value. */

  PyObject *PutEList(){ return PutList( PutEKey() ); }
  /*!< @brief Return the Python-List assigned to E-value. */

  PyObject *PutHistKeyList();

  UInt4 PutTableSize() const { return M.Size(); }
  /*!< @brief Returns the number of vectors stored in this storage. */
  UInt4 PutSize() const { return PutTableSize(); }
  /*!< @brief Arias of UInt4 PutTableSize(). */
  UInt4 PutSize( UInt4 IndexNumber ) const { return (UInt4)(M(IndexNumber) -> size()); }
  /*!< @brief Returns the size of vectors assigned to the IndexNumber. */
  UInt4 PutSize( const std::string &Key ) const { return M.Size(Key); }
  /*!< @brief Returns the size of vectors assigned to the "Key".

    This method is left for keeping
    the compatibility with the old version frameworks.
   */
  Int8 PutIndexNumber( const std::string &Key ){return M.PutIndexNumber( Key );}
  /*!< @brief Returns the IndexNumber assigned to the "Key".

    This method will be return -1, when "Key" has not been
    registored. The type of the return value is Int8, because
    negative values should be returned.
   */
  std::string PutName( UInt4 IndexNumber );
  //{ return (M.PutKeyList())[IndexNumber]; }
  /*!< @brief Returns the "key" assigned to the "IndexNumber". */

  std::vector<std::string> PutKeys();
  PyObject *PutKeysList();

  HeaderBase PutUnitHeader(){return *UnitHeader;}
  HeaderBase PutHeader(){return *header;}
  /*!< @brief
    Returns the object of "HeaderBase"
    stored in this container.

    This method will duplicate the original object,
    and return it.
    If you want to access the method of
    the object installed in this class,
    use PutHeaderPointer().
  */
  HeaderBase *PutHeaderPointer(){ return header; }
  HeaderBase *PutUnitHeaderPointer(){ return UnitHeader; }
  void InputUnitHeader( const HeaderBase &uHeader );
  void InputHeader( const HeaderBase &Header );
  /*!< @brief Input header-object.

    If the header object has installed already,
    the object is deleted, and the given header-object
    will be duplicated and its copy object will be installed.
  */

  void ReSetUnit( std::string key, std::string unit ){ UnitHeader->OverWrite( key,unit); }

  void DumpKey();
  /*!< @brief Dump Key-name(s) with index-number(s) in table format
    to the standard-output. */

  void Add( const std::string &Key, std::vector< Double > value, const std::string &Unit="None" );
  /*!< @brief Append and install a vector object with "Key".

    If the type of second argument is not std::vector<Double>,
    the data object will be converted into vector<Double>
    in this method.*/

  void Add( const std::string &Key, std::vector< Float  > value, const std::string &Unit="None" );
  /*!< @brief Append and install a vector object with "Key".

    If the type of second argument is not std::vector<Double>,
    the data object will be converted into vector<Double>
    in this method.*/

  void Add( const std::string &Key, std::vector< UInt4  > value, const std::string &Unit="None" );
  /*!< @brief Append and install a vector object with "Key".

    If the type of second argument is not std::vector<Double>,
    the data object will be converted into vector<Double>
    in this method.*/

  void Add( const std::string &Key, std::vector< UInt2  > value, const std::string &Unit="None" );
  /*!< @brief Append and install a vector object with "Key".

    If the type of second argument is not std::vector<Double>,
    the data object will be converted into vector<Double>
    in this method.*/

  void Add( const std::string &Key, PyObject *value, const std::string &Unit="None" );
  /*!< @brief Append and install a Python-List object with "Key".

    If the type of "value" is not "Python-List",
    the error message will be shown by CppToPython.
  */

  void Add( const std::string &Key, Double *&Array, UInt4 ArraySize, const std::string &Unit="None" );
  /*!< @brief Append and install an array object with "Key".

   The array object will be converted into a vector<Double>
   in this method.*/

  void Add( const std::string &Key, Float *&Array, UInt4 ArraySize, const std::string &Unit="None" );
  /*!< @brief Append and install an array object with "Key".

   The array object will be converted into a vector<Double>
   in this method.*/

  void Add( const std::string &Key, UInt4 *&Array, UInt4 ArraySize, const std::string &Unit="None" );
  /*!< @brief Append and install an array object with "Key".

   The array object will be converted into a vector<Double>
   in this method.*/

  void Add( const std::string &Key, UInt2 *&Array, UInt4 ArraySize, const std::string &Unit="None" );
  /*!< @brief Append and install an array object with "Key".

   The array object will be converted into a vector<Double>
   in this method.*/


  void AddBlankVector( const std::string &Key, UInt4 Size=0, const std::string &Unit="None" );
  /*!< @brief Append a vector<Double> with its Key.

    The size of the vector is "Size",
    and its default size is zero.
    A vector object is created in this method,
    and appended to the container with "Key". This method
    is very useful for manipulating this class
    from the Python environment.*/

  std::vector<Double> Find( const std::string &Key ){ return Put(Key); }
  /*!< @brief Returns the pointer of the vector-object
    assigned to "Key". */
  std::vector<Double> Find( UInt4 IndexNumber ){ return Put(IndexNumber); }
  /*!< @brief Returns the pointer of the vector-object
    assigned to "IndexNumber". */
  Double Find( const std::string &Key, UInt4 Index ){return ( Put(Key) )[Index]; }
  /*!< @brief Returns Index-th Double value extracted from the vector
    named as "Key". */
  std::vector<Double> Put( UInt4 IndexNumber ){return M.Put( IndexNumber );}
  /*!< @brief Alias of Find(IndexNumber), returns a vector object. */
  std::vector<Double> Put( const std::string &Key ){return M.Put( Key );}
  /*!< @brief Alias of Find(Key), returns a vector object. */
  Double Put( const std::string &Key, UInt4 Index ){ return Find(Key,Index); }
  /*!< @brief Alias of Find(std::string,UInt4). */

  std::string PutUnit( const std::string &Key ) const;
  void SetUnit( const std::string &Key, const std::string &NewUnit );

  PyObject *PutList( const std::string &Key ){return __gCppToPython.VectorDoubleToList( Put( Key ) );}
  /*!< @brief
    Returns the Python-List object which is converted std::vector<Double>.

    The each element of the Python-List is equal to the vector
    returned by Put( Key ).
    This method is written on C++, the speed of converting
    from STL-vector object to Python-List object in this method
    is faster than that in Python environment.
   */

  std::vector<Double> ReduceColumn( const std::string &Key );
  PyObject *ReduceColumnList( const std::string &Key ){return __gCppToPython.
    VectorDoubleToList( ReduceColumn( Key ) );
  }

  UInt4 CheckKey( const std::string &Key ){return M.CheckKey( Key );}
  /*!< @brief
    This method returns zero, if the vector whose name is "Key" is not
    contained in the storage.

    The integer returned by this method expresses the number of
    the vectors assigned to "Key".
    But the value is not 0 or 1, the serious problems may occur
    in this class.
   */

  bool RenameKey( const std::string &OldKey, const std::string & NewKey );


  void Copy( const std::string &Old, const std::string &New );
  /*!< @brief Copy the vector object. The new vector object is created
    named as "x". Each value is copied
    from the "std::vector-Old" to the "std::vector-New".*/
  void Remove( const std::string &Key );
  /*!< @brief Remove a vector object whose name is "Key". */

  void Replace( const std::string &Key, const std::vector<Double> &value );
  void Replace( const std::string &Key, PyObject *value );
  /*!< @brief Replace the vector assigned to Key. */

  void AppendValue( const std::string &Key, Double value ){M( Key ) -> push_back( value );}
  /*!< @brief
    This method appends "value" to the target std::vector specified by "Key".

    The class method of STL-vector, append(Double), is used in this method.
   */

  void AppendValue( const std::string &Key, const std::vector<Double> &value );
  /*!< @brief
    This method appends "std::vector<Double> value"
    to the vector assigned to "Key".

    The std::vector "value" is connected
    to the end of the vector.
   */

  void SetValue( const std::string &Key, UInt4 Number, Double value );
  /*!< @brief
    This method can change the "Number"-th value of the target std::vector.
    The vector is extracted with "Key".

    If the value of the second argument, "Number", is larger than
    the size of target std::vector, a warning message will be shown.
   */


  /** performs std::transform() to a vector with specified key and function object
   *
   * @param Key     a key which specifies the target std::vector
   * @param F()     a function object
   * @param newunit new unit
   */
  template <class T>
  void Transform(const std::string &Key, T F, const std::string &newunit="None");

  //! reverse all std::vector
  void Reverse(void) { M.Reverse(); }

  void Dump();
  /*!< @brief Dump all information stored in this container
    to the standard output.

    If you want to convert this output into vector<std::string>,
    the vector object can be input into
    "ReceiveString(std::vector<std::string>)".
  */

  PyObject* PyDump();

  void dump() {DumpValue();}

  void DumpValue();
  void DumpFromVectorContainer();
  void DumpFromVectorContainer( UInt4 size );

  void Dump( UInt4 size );
  /*!< @brief
    The method of "Dump()" will dump whole information
    stored in this container.

    If the size of vectors are huge,
    "Dump()" will completely fill up your console with data.
    This method will shorten the size of std::vector shown in your console.
   */
  void DumpValue( UInt4 size );

  std::string PutXYEKeys();
  std::string PutMergedKey();
  std::vector<UInt4> PutSizeVector();
  std::vector<Double> PutMergedDataVector();

  void BuildElementContainer( std::string XYEKeys,
                              std::string MergedKey,
                              std::vector<UInt4> SizeVector,
                              std::vector<Double> MergedDataVector,
                              HeaderBase h,
                              HeaderBase uh );
  /*!< @brief Build a new ElementContainer instance from long std::vector
    Mergedkey and MergedDataVector.

    The object of HeaderBase, the fifth argument, will be deleted
    in this function. If the object extracted with "PutHeader()" is
    installed with this function, you should copy it before use this function.
   */

  std::vector<Double>* PutP( const std::string &Key ){return M( Key );}
  std::vector<Double>* operator()( const std::string &Key ){return M( Key );}
  /*!< @brief
    This operator returns the pointer of std::vector<Double>
    whose name is "Key".

    If you want to obtain vector objects,
    use "Put(std::string)". This method should only be used for the accessing
    methods in the vector-objects.
   */
  std::vector<Double>* operator()( UInt4 index ){return M( index );}
  /*!< @brief
    This operator returns the pointer of std::vector<Double>
    whose index-number is "index".

    If you want to obtain vector objects,
    use "Put(UInt4)". This method should only be used for the accessing
    methods in the vector-objects.
   */

  void AddToHeader( const std::string &Key, Int4   value ){ header->Add( Key, value ); }
  /*!< @brief
    Data objects can be installed into the header-object with
    its name-tag.

    The method of the header-object is executed
    in this method. See the manual of HeaderBase.
   */
  void AddToHeader( const std::string &Key, Double value ){ header->Add( Key, value ); }
  /*!< @brief
    Data objects can be installed into the header-object with
    its name-tag.

    The method of the header-object is executed
    in this method. See the manual of HeaderBase.
   */
  void AddToHeader( const std::string &Key, std::string value ){ header->Add( Key, value ); }
  /*!< @brief
    Data objects can be installed into the header-object with
    its name-tag.

    The method of the header-object is executed
    in this method. See the manual of HeaderBase.
   */
  void AddToHeader( const std::string &Key, std::vector<Int4>   value ){ header->Add( Key, value ); }
  /*!< @brief
    Data objects can be installed into the header-object with
    its name-tag.

    The method of the header-object is executed
    in this method. See the manual of HeaderBase.
   */
  void AddToHeader( const std::string &Key, std::vector<Double> value ){ header->Add( Key, value ); }
  /*!< @brief
    Data objects can be installed into the header-object with
    its name-tag.

    The method of the header-object is executed
    in this method. See the manual of HeaderBase.
   */
  void AddToHeader( const std::string &Key, std::vector<std::string> value ){ header->Add( Key, value ); }
  /*!< @brief
    Data objects can be installed into the header-object with
    its name-tag.

    The method of the header-object is executed
    in this method. See the manual of HeaderBase.
   */


  Double Sum( const std::string &key, const UInt4 i0, const UInt4 i1 ) const;
  /*!< @brief
    Returns the sum of each element of the target std::vector within the
    specified range [i0, i1).
  */

  Double Sum( const std::string &key ) const;
  /*!< @brief
    Returns the sum of each element of the target std::vector.
    The argument of this method is the key of the target std::vector.
   */

  Double Sum( void ) const;
  /*!< @brief
    Returns the sum of each element of the target std::vector.
    Ykey is selected as a target.
   */

  std::pair<Double, Double> Sum( const std::string &key, const std::string &err_key, UInt4 i0, UInt4 i1 ) const;
  /*!< @brief
    Returns Sum(key, err_key) within the range [i0, i1)
   */

  std::pair<Double, Double> Sum( const std::string &key, const std::string &err_key ) const;
  /*!< @brief
    Returns the sum of each element of the target std::vector and
    its error.

    The type of the rerurn value is std::pair<Double, Double>.
    The first element of the vector is the sum, and the second one
    is the its error. To calculate the sum and its error,
    this method requires you to input two keys.
    The first value of the return vector is equal to
    the return value of Sum(std::string).
   */

  std::pair<Double, Double> Sum2( void ) const ;
  /*!< @brief
    Returns Sum( Ykey, Ekey )
   */

  std::pair<Double, Double> Sum( Double ini, Double fin ) const;
  /*!< @brief
    Returns the sum of values of "Ykey" in the range between
    ini and fin of "Xkey" with errors.

    The type of the rerurn value is std::pair<Double, Double>.
    The first element of the vector is the sum, and the second one
    is the its error. To calculate the sum and its error,
    this method requires you to input two keys.
    ... arranged by Y.I.
   */
  Double Ave( const std::string &key );
  /*!< @brief
    Returns the average value
    of the target std::vector. The average value will be
    obtained without its error.
   */
  Double Ave( const std::string &key, const std::string &err );
  /*!< @brief
  Returns the average value
  of the target std::vector. The average value will be
  obtained with its error.
  */
  Double Min( const std::string &key );
  /*!< @brief Returns the minimal value contained in
   the target std::vector. */
  Double Max( const std::string &key );
  /*!< @brief Returns the maximum value contained in
   the target std::vector. */

  std::pair<Double, Double> Integrate();
  /*!< @brief
    This method obtains the area of the histogram
    defined with "Xkey", "Ykey" and "Ekey".

    The first element of the return std::vector is
    the area integrated in this method, and
    the second argument is its error value.
    Before calling this method, you should set
    "Xkey", "Ykey" and "Ekey" with "SetKeys(std::string,std::string,std::string)".
    arranged by Y.I.
   */
//  std::vector<Double> Integrate( UInt4 ini, UInt4 fin );
  /*!< @brief
    This method obtains the area of the histogram
    defined with "Xkey", "Ykey" and "Ekey".

    The first element of the return std::vector is
    the area integrated in this method, and
    the second argument is its error value.
    Before calling this method, you should set
    "Xkey", "Ykey" and "Ekey" with "SetKeys(std::string,std::string,std::string)".
    The arguments of this methods are the start and end index of
    the histogram. When the size of Y-vector is S, the indices
    should be set from zero to (S-1).
   */
  std::pair<Double, Double> Integrate( Double ini, Double fin );
  void SaveToBinFile( std::string key, std::string FileName );
  /*!< @brief
    histogram data whose name is "key" is saved to a binary file
    named as "FileName".
   */
  void ReadBinFile( std::string FileName, std::string key );
  /*!< @brief
    A binary file stored by "SaveToBinFile(std::string,std::string)" is read
    by this method.

    The format of the target file is a dump file of
    double type C-language array.
    The target file data will be stored
    into ElementContainer with "key".
   */
  void SaveTextFile( std::string FileName );
  void SaveTextFile( std::string FileName, Int4 prec );
  void SaveTextFile( std::string FileName, Char deli );
  void SaveTextFile( std::string FileName, Int4 prec, Char deli );
  void SaveHistTextFile( std::string FileName );
  void SaveHistTextFile( std::string FileName, Int4 prec );
  void SaveHistTextFile( std::string FileName, Char deli );
  void SaveHistTextFile( std::string FileName, Int4 prec, Char deli );
  /*!< @brief
    This method saves Histogram data defined by "keys" on a TEXT file
    named "FileName".
    by Y.I.
    and modified by T.I.
   */
  void LoadTextFile( std::string FileName );
  /*!< @brief
    This method saves Histogram data defined by "keys" on a TEXT file
    named "FileName".
    by Y.I.
   */


  bool safesum;    //!< if true, Sum() performs Kahan's Compensated Summation
  // http://en.wikipedia.org/wiki/Kahan_summation_algorithm
  // warning, Ave(std::string, std::string) doesn't performs this safe summation yet


  // developing methods
  ElementContainer Mul( Double d );
  ElementContainer &MulMySelf( Double d );
  ElementContainer Mul( Double d, Double e );
  ElementContainer &MulMySelf( Double d, Double e );
  ElementContainer Mul( std::pair<Double,Double> &d ){ return Mul(d.first,d.second); }
  ElementContainer &MulMySelf( std::pair<Double,Double> &d ){ return MulMySelf(d.first,d.second); }

  ElementContainer Plus( Double d );
  ElementContainer &PlusMySelf( Double d );
  ElementContainer Plus( Double d, Double e );
  ElementContainer &PlusMySelf( Double d, Double e );
  ElementContainer Plus( std::pair<Double,Double> &d ){ return Plus(d.first,d.second); }
  ElementContainer &PlusMySelf( std::pair<Double,Double> &d ){ return PlusMySelf(d.first,d.second); }

  ElementContainer Pow( Double d );
  ElementContainer &PowMySelf( Double d );

};

/////////////////////////////////////////////////////////
template <class Archive>
void ElementContainer::
serialize(Archive &ar, const unsigned int ver) {
  ar & boost::serialization::make_nvp("XKey", Xkey);
  ar & boost::serialization::make_nvp("YKey", Ykey);
  ar & boost::serialization::make_nvp("EKey", Ekey);

  ar & boost::serialization::make_nvp("Header", *header);
  ar & boost::serialization::make_nvp("UnitHeader", *UnitHeader);

  ar & boost::serialization::make_nvp("ElementContainerData", M);
}

/////////////////////////////////////////////////////////
template <class T>
void ElementContainer::
Transform(const std::string &Key, T F, const std::string &newunit) {
  if( CheckKey( Key ) != 1 ){
    std::cout << "ElementContainer::Transform(std::string)"
              << std::endl;
    std::cout << Key << " is not found in this container." << std::endl;
    return;
  }
  std::vector< Double > &vec = *M( Key );

  std::transform(vec.begin(), vec.end(), vec.begin(), F);
  ReSetUnit(Key, newunit);
}

////////////////////////////////////////////////
template <class Write>
void ElementContainer::
NXwrite(Write &W) const {
  W.WriteData("XKey", Xkey);
  W.WriteData("YKey", Ykey);
  W.WriteData("EKey", Ekey);

  if (!header->Empty())
  W.WriteData("Header", *header);
  if (!UnitHeader->Empty())
  W.WriteData("UnitHeader", *UnitHeader);

  W.WriteData("ElementContainerData", M.M);
}

////////////////////////////////////////////////
template <class Read>
void ElementContainer::
NXread(Read &R) {
  R.ReadData("XKey", Xkey);
  R.ReadData("YKey", Ykey);
  R.ReadData("EKey", Ekey);

  R.ReadData("ElementContainerData", M.M);

  NXname _name, _class;
  int _id, _status;
  while ((_status=R.GetNextEntry2(_name, _class, &_id))==1) {
//    std::cerr << "NEXTENTRY: " << _name << "," << _class << ", "
//              << _id << "," << _status << std::endl;
    if (strcmp(_name, "Header")==0)
      R.ReadData("Header", *header);
    else if (strcmp(_name, "UnitHeader")==0)
      R.ReadData("UnitHeader", *UnitHeader);
  }

}


class BinConverter{
private:
  std::vector<Double> lx;
  std::vector<Double> ly;
  std::vector<Double> le;

  const std::vector<Double> *rx;
  const std::vector<Double> *ry;
  const std::vector<Double> *re;

  std::vector<Double> *LBin;
  std::vector<Double> *RBin;


public:
  BinConverter( std::vector<Double> &LX, std::vector<Double> &LY, std::vector<Double> &LE,
    const std::vector<Double> *RX,
    const std::vector<Double> *RY,
    const std::vector<Double> *RE );
 ~BinConverter();


  Int4 prepare();

  DoubleBinArrange *al;
  DoubleBinArrange *ar;
  std::vector<Double> *NewBin;

};


#endif
