// -*- c++ -*-

#ifndef COLVARGRID_H
#define COLVARGRID_H

#include <iostream>
#include <iomanip>
#include <cmath>

#include "colvar.h"
#include "colvarmodule.h"
#include "colvarvalue.h"
#include "colvarparse.h"

/// \brief Grid of values of a function of several collective
/// variables \param T The data type
///
/// Only scalar colvars supported so far
template <class T> class colvar_grid : public colvarparse {

protected:

  /// Number of dimensions
  size_t nd;

  /// Number of points along each dimension
  std::vector<int> nx;

  /// Cumulative number of points along each dimension
  std::vector<int> nxc;

  /// \brief Multiplicity of each datum (allow the binning of
  /// non-scalar types)
  size_t mult;

  /// Total number of grid points
  size_t nt;

  /// Low-level array of values
  std::vector<T> data;

  /// Newly read data (used for count grids, when adding several grids read from disk)
  std::vector<size_t> new_data;

  /// Colvars collected in this grid
  std::vector<colvar *> cv;

  /// Get the low-level index corresponding to an index
  inline size_t address (std::vector<int> const &ix) const
  {
    size_t addr = 0;
    for (size_t i = 0; i < nd; i++) {
      addr += ix[i]*nxc[i];
      if (cvm::debug()) {
        if (ix[i] >= nx[i])
          cvm::fatal_error ("Error: exceeding bounds in colvar_grid.\n");
      }
    }
    return addr;
  }

public:

  /// Lower boundaries of the colvars in this grid
  std::vector<colvarvalue> lower_boundaries;

  /// Upper boundaries of the colvars in this grid
  std::vector<colvarvalue> upper_boundaries;

  /// Whether some colvars are periodic
  std::vector<bool>        periodic;

  /// Widths of the colvars in this grid
  std::vector<cvm::real>   widths;

  /// True if this is a count grid related to another grid of data
  bool has_parent_data;

  /// Whether this grid has been filled with data or is still empty
  bool has_data;

  /// Return the number of colvars
  inline size_t number_of_colvars() const
  {
    return nd;
  }

  /// Return the number of points in the i-th direction, if provided, or
  /// the total number
  inline size_t number_of_points (int const icv = -1) const
  {
    if (icv < 0) {
      return nt;
    } else {
      return nx[icv];
    }
  }

  /// Get the sizes in each direction
  inline std::vector<int> const & sizes() const
  {
    return nx;
  }

  /// Set the sizes in each direction
  inline void set_sizes (std::vector<int> const &new_sizes)
  {
    nx = new_sizes;
  }

  /// Return the multiplicity of the type used
  inline size_t multiplicity() const
  {
    return mult;
  }

  /// \brief Allocate data (allow initialization also after construction)
  void create (std::vector<int> const &nx_i,
               T const &t = T(),
               size_t const &mult_i = 1)
  {
    mult = mult_i;
    nd = nx_i.size();
    nxc.resize (nd);
    nx = nx_i;

    nt = mult;
    for (int i = nd-1; i >= 0; i--) {
      if (nx_i[i] <= 0)
        cvm::fatal_error ("Error: providing an invalid number of points, "+
                          cvm::to_str (nx_i[i])+".\n");
      nxc[i] = nt;
      nt *= nx[i];
    }

    data.reserve (nt);
    data.assign (nt, t);
  }

  /// \brief Allocate data (allow initialization also after construction)
  void create()
  {
    create (this->nx, T(), this->mult);
  }

  /// \brief Reset data (in case the grid is being reused)
  void reset (T const &t = T())
  {
    data.assign (nt, t);
  }


  /// Default constructor
  colvar_grid() : has_data (false)
  {
    save_delimiters = false;
    nd = nt = 0;
  }

  /// Destructor
  virtual ~colvar_grid()
  {}

  /// \brief "Almost copy-constructor": only copies configuration
  /// parameters from another grid, but doesn't reallocate stuff;
  /// create() must be called after that;
  colvar_grid (colvar_grid<T> const &g) : has_data (false),
                                          nd (g.nd),
                                          mult (g.mult),
                                          cv (g.cv),
                                          lower_boundaries (g.lower_boundaries),
                                          upper_boundaries (g.upper_boundaries),
                                          periodic (g.periodic),
                                          widths (g.widths),
                                          data()
  {
    save_delimiters = false;
  }

  /// \brief Constructor from explicit grid sizes \param nx_i Number
  /// of grid points along each dimension \param t Initial value for
  /// the function at each point (optional) \param mult_i Multiplicity
  /// of each value
  colvar_grid (std::vector<int> const &nx_i,
               T const &t = T(),
               size_t const &mult_i = 1) : has_data (false)
  {
    save_delimiters = false;
    this->create (nx_i, t, mult_i);
  }

  /// \brief Constructor from a vector of colvars
  colvar_grid (std::vector<colvar *> const &colvars,
               T const &t = T(),
               size_t const &mult_i = 1,
               bool margin = false)
    : cv (colvars), has_data (false)
  {
    save_delimiters = false;

    std::vector<int> nx_i;

    if (cvm::debug())
      cvm::log ("Allocating a grid for "+cvm::to_str (colvars.size())+
                " collective variables.\n");

    for (size_t i =  0; i < cv.size(); i++) {

      if (cv[i]->type() != colvarvalue::type_scalar) {
        cvm::fatal_error ("Colvar grids can only be automatically "
                          "constructed for scalar variables.  "
                          "ABF and histogram can not be used; metadynamics "
                          "can be used with useGrids disabled.\n");
      }

      if (cv[i]->width <= 0.0) {
        cvm::fatal_error ("Tried to initialize a grid on a "
                          "variable with negative or zero width.\n");
      }

      if (!cv[i]->tasks[colvar::task_lower_boundary] || !cv[i]->tasks[colvar::task_upper_boundary]) {
        cvm::fatal_error ("Tried to initialize a grid on a "
                          "variable with undefined boundaries.\n");
      }

      widths.push_back (cv[i]->width);
      periodic.push_back (cv[i]->periodic_boundaries());

      if (margin) {
        if (periodic[i]) {
          // Shift the grid by half the bin width (values at edges instead of center of bins)
          lower_boundaries.push_back (cv[i]->lower_boundary.real_value - 0.5 * widths[i]);
          upper_boundaries.push_back (cv[i]->upper_boundary.real_value - 0.5 * widths[i]);
        } else {
          // Make this grid larger by one bin width
          lower_boundaries.push_back (cv[i]->lower_boundary.real_value - 0.5 * widths[i]);
          upper_boundaries.push_back (cv[i]->upper_boundary.real_value + 0.5 * widths[i]);
        }
      } else {
        lower_boundaries.push_back (cv[i]->lower_boundary);
        upper_boundaries.push_back (cv[i]->upper_boundary);
      }


      { 
        cvm::real nbins = ( upper_boundaries[i].real_value -
                            lower_boundaries[i].real_value ) / widths[i];
        int nbins_round = (int)(nbins+0.5);
        
        if (std::fabs (nbins - cvm::real (nbins_round)) > 1.0E-10) {
          cvm::log ("Warning: grid interval ("+
                    cvm::to_str (lower_boundaries[i], cvm::cv_width, cvm::cv_prec)+" - "+
                    cvm::to_str (upper_boundaries[i], cvm::cv_width, cvm::cv_prec)+
                    ") is not commensurate to its bin width ("+
                    cvm::to_str (widths[i], cvm::cv_width, cvm::cv_prec)+").\n");
          upper_boundaries[i].real_value = lower_boundaries[i].real_value +
            (nbins_round * widths[i]);
        }

        if (cvm::debug())
          cvm::log ("Number of points is "+cvm::to_str ((int) nbins_round)+
                    " for the colvar no. "+cvm::to_str (i+1)+".\n");
        
        nx_i.push_back (nbins_round);
      }

    }

    create (nx_i, t, mult_i);
  }


  /// Wrap an index vector around periodic boundary conditions
  /// also checks validity of non-periodic indices
  inline void wrap (std::vector<int> & ix) const
  {
    for (size_t i = 0; i < nd; i++) {
      if (periodic[i]) {
        ix[i] = (ix[i] + nx[i]) % nx[i]; //to ensure non-negative result
      } else {
        if (ix[i] < 0 || ix[i] >= nx[i])
          cvm::fatal_error ("Trying to wrap illegal index vector (non-PBC): "
                            + cvm::to_str (ix));
      }
    }
  }

  /// \brief Report the bin corresponding to the current value of variable i
  inline int current_bin_scalar(int const i) const
  {
    return value_to_bin_scalar (cv[i]->value(), i);
  }

  /// \brief Use the lower boundary and the width to report which bin
  /// the provided value is in
  inline int value_to_bin_scalar (colvarvalue const &value, const int i) const
  {
    return (int) std::floor ( (value.real_value - lower_boundaries[i].real_value) / widths[i] );
  }

  /// \brief Same as the standard version, but uses another grid definition
  inline int value_to_bin_scalar (colvarvalue const &value,
                                  colvarvalue const &new_offset,
                                  cvm::real   const &new_width) const
  {
    return (int) std::floor ( (value.real_value - new_offset.real_value) / new_width );
  }

  /// \brief Use the two boundaries and the width to report the
  /// central value corresponding to a bin index
  inline colvarvalue bin_to_value_scalar (int const &i_bin, int const i) const
  {
    return lower_boundaries[i].real_value + widths[i] * (0.5 + i_bin);
  }
  
  /// \brief Same as the standard version, but uses different parameters
  inline colvarvalue bin_to_value_scalar (int const &i_bin,
                                          colvarvalue const &new_offset,
                                          cvm::real const &new_width) const
  {
    return new_offset.real_value + new_width * (0.5 + i_bin);
  }

  /// Set the value at the point with index ix
  inline void set_value (std::vector<int> const &ix,
                         T const &t,
                         size_t const &imult = 0)
  { 
    data[this->address (ix)+imult] = t;
    has_data = true;
  }


  /// \brief Get the binned value indexed by ix, or the first of them
  /// if the multiplicity is larger than 1
  inline T const & value (std::vector<int> const &ix,
                          size_t const &imult = 0) const
  {
    return data[this->address (ix) + imult];
  }


  /// \brief Add a constant to all elements (fast loop)
  inline void add_constant (T const &t)
  {
    for (size_t i = 0; i < nt; i++) 
      data[i] += t;
    has_data = true;
  }

  /// \brief Multiply all elements by a scalar constant (fast loop)
  inline void multiply_constant (cvm::real const &a)
  {
    for (size_t i = 0; i < nt; i++) 
      data[i] *= a;
  }

 
  /// \brief Get the bin indices corresponding to the provided values of
  /// the colvars
  inline std::vector<int> const get_colvars_index (std::vector<colvarvalue> const &values) const
  {
    std::vector<int> index = new_index();
    for (size_t i = 0; i < nd; i++) {
      index[i] = value_to_bin_scalar (values[i], i);
    }
    return index;
  }

  /// \brief Get the bin indices corresponding to the current values
  /// of the colvars
  inline std::vector<int> const get_colvars_index() const
  {
    std::vector<int> index = new_index();
    for (size_t i = 0; i < nd; i++) {
      index[i] = current_bin_scalar (i);
    }
    return index;
  }

  /// \brief Get the minimal distance (in number of bins) from the
  /// boundaries; a negative number is returned if the given point is
  /// off-grid
  inline cvm::real bin_distance_from_boundaries (std::vector<colvarvalue> const &values)
  {
    cvm::real minimum = 1.0E+16;
    for (size_t i = 0; i < nd; i++) {

      if (periodic[i]) continue;

      cvm::real dl = std::sqrt (cv[i]->dist2 (values[i], lower_boundaries[i])) / widths[i];
      cvm::real du = std::sqrt (cv[i]->dist2 (values[i], upper_boundaries[i])) / widths[i];

      if (values[i].real_value < lower_boundaries[i])
        dl *= -1.0;
      if (values[i].real_value > upper_boundaries[i])
        du *= -1.0;

      if (dl < minimum) 
        minimum = dl;
      if (du < minimum) 
        minimum = du;
    }

    return minimum;
  }


  /// \brief Add data from another grid of the same type
  /// 
  /// Note: this function maps other_grid inside this one regardless
  /// of whether it fits or not.
  void map_grid (colvar_grid<T> const &other_grid)
  {
    if (other_grid.multiplicity() != this->multiplicity())
      cvm::fatal_error ("Error: trying to merge two grids with values of "
                        "different multiplicity.\n");

    std::vector<colvarvalue> const &gb  = this->lower_boundaries;
    std::vector<cvm::real> const &gw    = this->widths;
    std::vector<colvarvalue> const &ogb = other_grid.lower_boundaries;
    std::vector<cvm::real> const &ogw   = other_grid.widths;

    std::vector<int> ix = this->new_index();
    std::vector<int> oix = other_grid.new_index();

    if (cvm::debug())
      cvm::log ("Remapping grid...\n");
    for ( ; this->index_ok (ix); this->incr (ix)) {

      for (size_t i = 0; i < nd; i++) {
        oix[i] =
          value_to_bin_scalar (bin_to_value_scalar (ix[i], gb[i], gw[i]),
                               ogb[i],
                               ogw[i]);
      }

      if (! other_grid.index_ok (oix)) {
        continue;
      }

      for (size_t im = 0; im < mult; im++) {
        this->set_value (ix, other_grid.value (oix, im), im);
      }
    }

    has_data = true;
    if (cvm::debug())
      cvm::log ("Remapping done.\n");
  }

  /// \brief Add data from another grid of the same type, AND
  /// identical definition (boundaries, widths)
  void add_grid (colvar_grid<T> const &other_grid,
                 cvm::real scale_factor = 1.0)
  {
    if (other_grid.multiplicity() != this->multiplicity())
      cvm::fatal_error ("Error: trying to sum togetehr two grids with values of "
                        "different multiplicity.\n");
    if (scale_factor != 1.0) 
      for (size_t i = 0; i < data.size(); i++) {
        data[i] += scale_factor * other_grid.data[i];
      }
    else 
      // skip multiplication if possible
      for (size_t i = 0; i < data.size(); i++) {
        data[i] += other_grid.data[i];
      }
    has_data = true;
  }

  /// \brief Return the value suitable for output purposes (so that it
  /// may be rescaled or manipulated without changing it permanently)
  virtual inline T value_output (std::vector<int> const &ix,
                                 size_t const &imult = 0)
  {
    return value (ix, imult);
  }

  /// \brief Get the value from a formatted output and transform it
  /// into the internal representation (the two may be different,
  /// e.g. when using colvar_grid_count)
  virtual inline void value_input (std::vector<int> const &ix,
                                   T const &t,
                                   size_t const &imult = 0,
                                   bool add = false)
  {
    if ( add )
      data[address (ix) + imult] += t;
    else
      data[address (ix) + imult] = t;
    has_data = true;
  }

  //   /// Get the pointer to the binned value indexed by ix
  //   inline T const *value_p (std::vector<int> const &ix)
  //   {
  //     return &(data[address (ix)]);
  //   }

  /// \brief Get the index corresponding to the "first" bin, to be
  /// used as the initial value for an index in looping
  inline std::vector<int> const new_index() const
  {
    return std::vector<int> (nd, 0);
  }

  /// \brief Check that the index is within range in each of the
  /// dimensions
  inline bool index_ok (std::vector<int> const &ix) const
  {
    for (size_t i = 0; i < nd; i++) {
      if ( (ix[i] < 0) || (ix[i] >= int (nx[i])) )
        return false;
    }
    return true;
  }

  /// \brief Increment the index, in a way that will make it loop over
  /// the whole nd-dimensional array
  inline void incr (std::vector<int> &ix) const
  {
    for (int i = ix.size()-1; i >= 0; i--) {

      ix[i]++;

      if (ix[i] >= nx[i]) {

        if (i > 0) {
          ix[i] = 0;
          continue;
        } else {
          // this is the last iteration, a non-valid index is being
          // set for the outer index, which will be caught by
          // index_ok()
          ix[0] = nx[0];
          return;
        }
      } else {
        return;
      }
    }
  }

  /// \brief Write the grid parameters (number of colvars, boundaries, width and number of points)
  std::ostream & write_params (std::ostream &os)
  {
    os << "grid_parameters {\n  n_colvars " << nd << "\n";
      
    os << "  lower_boundaries ";
    for (size_t i = 0; i < nd; i++) 
      os << " " << lower_boundaries[i];
    os << "\n";

    os << "  upper_boundaries ";
    for (size_t i = 0; i < nd; i++) 
      os << " " << upper_boundaries[i];
    os << "\n";

    os << "  widths ";
    for (size_t i = 0; i < nd; i++) 
      os << " " << widths[i];
    os << "\n";

    os << "  sizes ";
    for (size_t i = 0; i < nd; i++) 
      os << " " << nx[i];
    os << "\n";

    os << "}\n";
    return os;
  } 


  bool parse_params (std::string const &conf)
  {
    std::vector<int> old_nx = nx;
    std::vector<colvarvalue> old_lb = lower_boundaries;

    {
      size_t nd_in = 0;
      colvarparse::get_keyval (conf, "n_colvars", nd_in, nd, colvarparse::parse_silent);
      if (nd_in != nd)
        cvm::fatal_error ("Error: trying to read data for a grid "
                          "that contains a different number of colvars ("+
                          cvm::to_str (nd_in)+") than the grid defined "
                          "in the configuration file ("+cvm::to_str (nd)+
                          ").\n");
    }

    colvarparse::get_keyval (conf, "lower_boundaries",
                             lower_boundaries, lower_boundaries, colvarparse::parse_silent);

    colvarparse::get_keyval (conf, "upper_boundaries",
                             upper_boundaries, upper_boundaries, colvarparse::parse_silent);

    colvarparse::get_keyval (conf, "widths", widths, widths, colvarparse::parse_silent);

    colvarparse::get_keyval (conf, "sizes", nx, nx, colvarparse::parse_silent);

    bool new_params = false;
    for (size_t i = 0; i < nd; i++) {
      if ( (old_nx[i] != nx[i]) ||
           (std::sqrt (cv[i]->dist2 (old_lb[i],
                                     lower_boundaries[i])) > 1.0E-10) ) {
        new_params = true;
      }
    }

    // reallocate the array in case the grid params have just changed
    if (new_params) {
      data.resize (0);
      this->create (nx, T(), mult);
    }

    return true;
  }

  /// \brief Check that the grid information inside (boundaries,
  /// widths, ...) is consistent with the current setting of the
  /// colvars
  void check_consistency()
  {
    for (size_t i = 0; i < nd; i++) {
      if ( (std::sqrt (cv[i]->dist2 (cv[i]->lower_boundary,
                                     lower_boundaries[i])) > 1.0E-10) || 
           (std::sqrt (cv[i]->dist2 (cv[i]->upper_boundary,
                                     upper_boundaries[i])) > 1.0E-10) || 
           (std::sqrt (cv[i]->dist2 (cv[i]->width,
                                     widths[i])) > 1.0E-10) ) {
        cvm::fatal_error ("Error: restart information for a grid is "
                          "inconsistent with that of its colvars.\n");
      }
    }
  }


  /// \brief Check that the grid information inside (boundaries,
  /// widths, ...) is consistent with the one of another grid
  void check_consistency (colvar_grid<T> const &other_grid)
  {
    for (size_t i = 0; i < nd; i++) {
      // we skip dist2(), because periodicities and the like should
      // matter: boundaries should be EXACTLY the same (otherwise,
      // map_grid() should be used)
      if ( (std::fabs (other_grid.lower_boundaries[i] -
                       lower_boundaries[i]) > 1.0E-10) || 
           (std::fabs (other_grid.upper_boundaries[i] -
                       upper_boundaries[i]) > 1.0E-10) || 
           (std::fabs (other_grid.widths[i] -
                       widths[i]) > 1.0E-10) || 
           (data.size() != other_grid.data.size()) ) {
      cvm::fatal_error ("Error: inconsistency between "
                        "two grids that are supposed to be equal, "
                        "aside from the data stored.\n");
    }
  }
}
  

/// \brief Write the grid data without labels, as they are
/// represented in memory
/// \param buf_size Number of values per line
  std::ostream & write_raw (std::ostream &os,
                            size_t const buf_size = 3)
  {
    std::streamsize const w = os.width();
    std::streamsize const p = os.precision();

    std::vector<int> ix = new_index();
    size_t count = 0;
    for ( ; index_ok (ix); incr (ix)) {
      for (size_t imult = 0; imult < mult; imult++) {
        os << " "
           << std::setw (w) << std::setprecision (p)
           << value_output (ix, imult);
        if (((++count) % buf_size) == 0)
          os << "\n";
      }
    }
    // write a final newline only if buffer is not empty
    if ((count % buf_size) != 0)
      os << "\n";

    return os;
  }

/// \brief Read data written by colvar_grid::write_raw()
std::istream & read_raw (std::istream &is)
{
  size_t const start_pos = is.tellg();
    
  for (std::vector<int> ix = new_index(); index_ok (ix); incr (ix)) {
    for (size_t imult = 0; imult < mult; imult++) {
      T new_value;
      if (is >> new_value) {
        value_input (ix, new_value, imult);
      } else {
        is.clear();
        is.seekg (start_pos, std::ios::beg);
        is.setstate (std::ios::failbit);
        return is;
      }
    }
  }

  has_data = true;
  return is;
}

/// \brief To be called after colvar_grid::read_raw() returns an error
void read_raw_error()
{
  cvm::fatal_error ("Error: failed to read all of the grid points from file.  Possible explanations: grid parameters in the configuration (lowerBoundary, upperBoundary, width) are different from those in the file, or the file is corrupt/incomplete.\n");
}

/// \brief Write the grid in a format which is both human readable
/// and suitable for visualization e.g. with gnuplot
void write_multicol (std::ostream &os)
{
  std::streamsize const w = os.width();
  std::streamsize const p = os.precision();

  // Data in the header: nColvars, then for each
  // xiMin, dXi, nPoints, periodic

  os << std::setw (2) << "# " << nd << "\n";
  for (size_t i = 0; i < nd; i++) {
    os << "# "
       << std::setw (10) << lower_boundaries[i]
       << std::setw (10) << widths[i]
       << std::setw (10) << nx[i] << "  "
       << periodic[i] << "\n";
  }

  for (std::vector<int> ix = new_index(); index_ok (ix); incr (ix) ) {

    if (ix.back() == 0) {
      // if the last index is 0, add a new line to mark the new record
      os << "\n";
    }

    for (size_t i = 0; i < nd; i++) {
      os << " "
         << std::setw (w) << std::setprecision (p)
         << bin_to_value_scalar (ix[i], i);
    }
    os << " ";
    for (size_t imult = 0; imult < mult; imult++) {
      os << " "
         << std::setw (w) << std::setprecision (p)
         << value_output (ix, imult);
    }
    os << "\n";
  }
}

/// \brief Read a grid written by colvar_grid::write_multicol()
/// Adding data if add is true, replacing if false
std::istream & read_multicol (std::istream &is, bool add = false)
{
  // Data in the header: nColvars, then for each
  // xiMin, dXi, nPoints, periodic

  std::string   hash;
  cvm::real     lower, width, x;
  size_t        n, periodic;
  bool          remap;
  std::vector<T>        new_value;
  std::vector<int>      nx_read;
  std::vector<int>      bin;

  if ( cv.size() != nd ) {
    cvm::fatal_error ("Cannot read grid file: missing reference to colvars.");
  }

  if ( !(is >> hash) || (hash != "#") ) {
    cvm::fatal_error ("Error reading grid at position "+
                      cvm::to_str (is.tellg())+" in stream (read \"" + hash + "\")\n");
  }

  is >> n;
  if ( n != nd ) {
    cvm::fatal_error ("Error reading grid: wrong number of collective variables.\n");
  }

  nx_read.resize (n);
  bin.resize (n);
  new_value.resize (mult);

  if (this->has_parent_data && add) {
    new_data.resize (data.size());
  }

  remap = false;
  for (size_t i = 0; i < nd; i++ ) {
    if ( !(is >> hash) || (hash != "#") ) {
      cvm::fatal_error ("Error reading grid at position "+
                        cvm::to_str (is.tellg())+" in stream (read \"" + hash + "\")\n");
    }

    is >> lower >> width >> nx_read[i] >> periodic;


    if ( (std::fabs (lower - lower_boundaries[i].real_value) > 1.0e-10) ||
         (std::fabs (width - widths[i] ) > 1.0e-10) ||
         (nx_read[i] != nx[i]) ) {
      cvm::log ("Warning: reading from different grid definition (colvar "
                + cvm::to_str (i+1) + "); remapping data on new grid.\n");
      remap = true;
    }
  }

  if ( remap ) {
    // re-grid data
    while (is.good()) {
      bool end_of_file = false;

      for (size_t i = 0; i < nd; i++ ) {
        if ( !(is >> x) ) end_of_file = true;
        bin[i] = value_to_bin_scalar (x, i);
      }
      if (end_of_file) break;	

      for (size_t imult = 0; imult < mult; imult++) {
        is >> new_value[imult];
      }

      if ( index_ok(bin) ) {
        for (size_t imult = 0; imult < mult; imult++) {
          value_input (bin, new_value[imult], imult, add);
        }
      }
    }
  } else {
    // do not re-grid the data but assume the same grid is used
    for (std::vector<int> ix = new_index(); index_ok (ix); incr (ix) ) {
      for (size_t i = 0; i < nd; i++ ) {
        is >> x;
      }
      for (size_t imult = 0; imult < mult; imult++) {
        is >> new_value[imult];
        value_input (ix, new_value[imult], imult, add);
      }
    }
  }
  has_data = true;
  return is;
}

};



/// \brief Colvar_grid derived class to hold counters in discrete
/// n-dim colvar space
class colvar_grid_count : public colvar_grid<size_t>
{
public:

  /// Default constructor
  colvar_grid_count();

  /// Destructor
  virtual inline ~colvar_grid_count()
  {}

  /// Constructor
  colvar_grid_count (std::vector<int> const &nx_i,
                     size_t const           &def_count = 0);

  /// Constructor from a vector of colvars
  colvar_grid_count (std::vector<colvar *>  &colvars,
                     size_t const           &def_count = 0);

  /// Increment the counter at given position
  inline void incr_count (std::vector<int> const &ix)
  {
    ++(data[this->address (ix)]);
  }

  /// \brief Get the binned count indexed by ix from the newly read data 
  inline size_t const & new_count (std::vector<int> const &ix,
                                   size_t const &imult = 0)
  {
    return new_data[address (ix) + imult];
  }

  /// \brief Read the grid from a restart
  std::istream & read_restart (std::istream &is);

  /// \brief Write the grid to a restart
  std::ostream & write_restart (std::ostream &os);

  /// \brief Get the value from a formatted output and transform it
  /// into the internal representation (it may have been rescaled or
  /// manipulated)
  virtual inline void value_input (std::vector<int> const &ix,
                                   size_t const &t,
                                   size_t const &imult = 0,
                                   bool add = false)
  {
    if (add) {
      data[address (ix)] += t;
      if (this->has_parent_data) {
        // save newly read data for inputting parent grid
        new_data[address (ix)] = t;
      }
    } else {
      data[address (ix)] = t;
    }
    has_data = true;
  }
};


/// Class for accumulating a scalar function on a grid
class colvar_grid_scalar : public colvar_grid<cvm::real>
{
public:

  /// \brief Provide the associated sample count by which each binned value
  /// should be divided
  colvar_grid_count *samples;

  /// Default constructor
  colvar_grid_scalar();

  /// Copy constructor (needed because of the grad pointer)
  colvar_grid_scalar (colvar_grid_scalar const &g);

  /// Destructor
  ~colvar_grid_scalar();

  /// Constructor from specific sizes arrays
  colvar_grid_scalar (std::vector<int> const &nx_i);

  /// Constructor from a vector of colvars
  colvar_grid_scalar (std::vector<colvar *> &colvars,
                      bool margin = 0);

  /// Accumulate the value
  inline void acc_value (std::vector<int> const &ix,
                         cvm::real const &new_value,
                         size_t const &imult = 0)
  {
    // only legal value of imult here is 0
    data[address (ix)] += new_value;
    if (samples)
      samples->incr_count (ix);
    has_data = true;
  }

  /// Return the gradient of the scalar field from finite differences
  inline const cvm::real * gradient_finite_diff ( const std::vector<int> &ix0 )
  {
    cvm::real A0, A1;
    std::vector<int> ix;
    if (nd != 2) cvm::fatal_error ("Finite differences available in dimension 2 only.");
    for (int n = 0; n < nd; n++) {
      ix = ix0;
      A0 = data[address (ix)];
      ix[n]++; wrap (ix);
      A1 = data[address (ix)];
      ix[1-n]++; wrap (ix);
      A1 += data[address (ix)];
      ix[n]--; wrap (ix);
      A0 += data[address (ix)];
      grad[n] = 0.5 * (A1 - A0) / widths[n];
    }
    return grad; 
  }

  /// \brief Return the value of the function at ix divided by its
  /// number of samples (if the count grid is defined)
  virtual cvm::real value_output (std::vector<int> const &ix,
                                  size_t const &imult = 0)
  {
    if (imult > 0)
      cvm::fatal_error ("Error: trying to access a component "
                        "larger than 1 in a scalar data grid.\n");
    if (samples)
      return (samples->value (ix) > 0) ?
        (data[address (ix)] / cvm::real (samples->value (ix))) :
        0.0;
    else
      return data[address (ix)];
  }

  /// \brief Get the value from a formatted output and transform it
  /// into the internal representation (it may have been rescaled or
  /// manipulated)
  virtual void value_input (std::vector<int> const &ix,
                            cvm::real const &new_value,
                            size_t const &imult = 0,
                            bool add = false)
  {
    if (imult > 0)
      cvm::fatal_error ("Error: trying to access a component "
                        "larger than 1 in a scalar data grid.\n");
    if (add) {
      if (samples)
        data[address (ix)] += new_value * samples->new_count (ix);
      else
        data[address (ix)] += new_value;
    } else {
      if (samples)
        data[address (ix)] = new_value * samples->value (ix);
      else
        data[address (ix)] = new_value;
    }
    has_data = true;
  }

  /// \brief Read the grid from a restart
  std::istream & read_restart (std::istream &is);

  /// \brief Write the grid to a restart
  std::ostream & write_restart (std::ostream &os);

  /// \brief Return the highest value
  inline cvm::real maximum_value()
  {
    cvm::real max = data[0];
    for (size_t i = 0; i < nt; i++) {
      if (data[i] > max) max = data[i];
    }
    return max;
  }

  /// \brief Return the lowest value
  inline cvm::real minimum_value()
  {
    cvm::real min = data[0];
    for (size_t i = 0; i < nt; i++) {
      if (data[i] < min) min = data[i];
    }
    return min;
  }

private:
  // gradient
  cvm::real * grad;
};



/// Class for accumulating the gradient of a scalar function on a grid
class colvar_grid_gradient : public colvar_grid<cvm::real>
{
public:

  /// \brief Provide the sample count by which each binned value
  /// should be divided
  colvar_grid_count *samples;

  /// Default constructor
  colvar_grid_gradient();

  /// Destructor
  virtual inline ~colvar_grid_gradient()
  {}

  /// Constructor from specific sizes arrays
  colvar_grid_gradient (std::vector<int> const &nx_i);

  /// Constructor from a vector of colvars
  colvar_grid_gradient (std::vector<colvar *>  &colvars);

  /// \brief Accumulate the gradient
  inline void acc_grad (std::vector<int> const &ix, cvm::real const *grads) {
    for (size_t imult = 0; imult < mult; imult++) {
      data[address (ix) + imult] += grads[imult];
    }
    if (samples)
      samples->incr_count (ix);
  }

  /// \brief Accumulate the gradient based on the force (i.e. sums the
  /// opposite of the force)
  inline void acc_force (std::vector<int> const &ix, cvm::real const *forces) {
    for (size_t imult = 0; imult < mult; imult++) {
      data[address (ix) + imult] -= forces[imult];
    }
    if (samples)
      samples->incr_count (ix);
  }

  /// \brief Return the value of the function at ix divided by its
  /// number of samples (if the count grid is defined)
  virtual inline cvm::real value_output (std::vector<int> const &ix,
                                         size_t const &imult = 0)
  {
    if (samples)
      return (samples->value (ix) > 0) ?
        (data[address (ix) + imult] / cvm::real (samples->value (ix))) :
        0.0;
    else
      return data[address (ix) + imult];
  }

  /// \brief Get the value from a formatted output and transform it
  /// into the internal representation (it may have been rescaled or
  /// manipulated)
  virtual inline void value_input (std::vector<int> const &ix,
                                   cvm::real const &new_value,
                                   size_t const &imult = 0,
                                   bool add = false)
  {
    if (add) {
      if (samples)
        data[address (ix) + imult] += new_value * samples->new_count (ix);
      else
        data[address (ix) + imult] += new_value;
    } else {
      if (samples)
        data[address (ix) + imult] = new_value * samples->value (ix);
      else
        data[address (ix) + imult] = new_value;
    }
    has_data = true;
  }


  /// \brief Read the grid from a restart
  std::istream & read_restart (std::istream &is);

  /// \brief Write the grid to a restart
  std::ostream & write_restart (std::ostream &os);

  /// Compute and return average value for a 1D gradient grid
  inline cvm::real average()
  {
    size_t n = 0;

    if (nd != 1 || nx[0] == 0) {
      return 0.0;
    }

    cvm::real sum = 0.0;
    std::vector<int> ix = new_index();
    if (samples) {
      for ( ; index_ok (ix); incr (ix)) {
        if ( (n = samples->value (ix)) )
          sum += value (ix) / n;
      }
    } else {
      for ( ; index_ok (ix); incr (ix)) {
        sum += value (ix);
      }
    }
    return (sum / cvm::real (nx[0]));
  }

  /// \brief If the grid is 1-dimensional, integrate it and write the
  /// integral to a file
  void write_1D_integral (std::ostream &os);

};


#endif

