// ---------------------------------------------------------------------
//
// Copyright (C) 1999 - 2017 by the deal.II authors
//
// This file is part of the deal.II library.
//
// The deal.II library is free software; you can use it, redistribute
// it, and/or modify it under the terms of the GNU Lesser General
// Public License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// The full text of the license can be found in the file LICENSE at
// the top level of the deal.II distribution.
//
// ---------------------------------------------------------------------

#ifndef dealii_histogram_h
#define dealii_histogram_h


#include <deal.II/base/config.h>
#include <deal.II/base/exceptions.h>
#include <deal.II/lac/vector.h>
#include <vector>
#include <string>

DEAL_II_NAMESPACE_OPEN


/**
 * This class provides some facilities to generate 2d and 3d histograms. It is
 * used by giving it one or several data sets and a rule how to break the
 * range of values therein into intervals (e.g. linear spacing or logarithmic
 * spacing of intervals). The values are then sorted into the different
 * intervals and the number of values in each interval is stored for output
 * later. In case only one data set was given, the resulting histogram will be
 * a 2d one, while it will be a 3d one if more than one data set was given.
 * For more than one data set, the same intervals are used for each of them
 * anyway, to make comparison easier.
 *
 *
 * <h3>Ways to generate the intervals</h3>
 *
 * At present, the following schemes for interval spacing are implemented:
 * <ul>
 * <li> Linear spacing: The intervals are distributed in constant steps
 * between the minimum and maximum values of the data.
 * <li> Logarithmic spacing: The intervals are distributed in constant steps
 * between the minimum and maximum values of the logs of the values. This
 * scheme is only useful if the data has only positive values. Negative and
 * zero values are sorted into the leftmost interval.
 * </ul>
 *
 * To keep programs extensible, you can use the two functions @p
 * get_interval_spacing_names and @p parse_interval_spacing, which always give
 * you a complete list of spacing formats presently supported and are able to
 * generate the respective value of the @p enum. If you use them, you can
 * write your program in a way such that it only needs to be recompiled to
 * take effect of newly added formats, without changing your code.
 *
 *
 * <h3>Output formats</h3>
 *
 * At present, only GNUPLOT output is supported.
 *
 *
 * @ingroup textoutput
 * @author Wolfgang Bangerth, 1999
 */
class Histogram
{
public:
  /**
   * Definition of several ways to arrange the spacing of intervals.
   */
  enum IntervalSpacing
  {
    /**
     * Space intervals linearly.
     */
    linear,
    /**
     * Space intervals logarithmically.
     */
    logarithmic
  };


  /**
   * Take several lists of values, each on to produce one histogram that will
   * then be arrange one behind each other.
   *
   * Using several data sets at once allows to compare them more easily, since
   * the intervals into which the data is sorted is the same for all data
   * sets.
   *
   * The histograms will be arranged such that the computed intervals of the
   * <tt>values[i][j]</tt> form the x-range, and the number of values in each
   * interval will be the y-range (for 2d plots) or the z-range (for 3d
   * plots). For 3d plots, the @p y_values parameter is used to assign each
   * data set a value in the y direction, which is the depth coordinate in the
   * resulting plot. For 2d plots, the @p y_values are ignored.
   *
   * If you give only one data set, i.e. <tt>values.size()==1</tt>, then the
   * resulting histogram will be a 2d one.
   *
   * @p n_intervals denotes the number of intervals into which the data will
   * be sorted; @p interval_spacing denotes the way the bounds of the
   * intervals are computed. Refer to the general documentation for more
   * information on this.
   */
  template <typename number>
  void evaluate (const std::vector<Vector<number> > &values,
                 const std::vector<double>                   &y_values,
                 const unsigned int                           n_intervals,
                 const IntervalSpacing                        interval_spacing = linear);

  /**
   * This function is only a wrapper to the above one in case you have only
   * one data set.
   */
  template <typename number>
  void evaluate (const Vector<number>          &values,
                 const unsigned int             n_intervals,
                 const IntervalSpacing          interval_spacing = linear);

  /**
   * Write the histogram computed by the @p evaluate function to a stream in a
   * format suitable to the GNUPLOT program. The function generates 2d or 3d
   * histograms.
   */
  void write_gnuplot (std::ostream &out) const;

  /**
   * Return allowed names for the interval spacing as string. At present this
   * is "linear|logarithmic".
   */
  static std::string get_interval_spacing_names ();

  /**
   * Get a string containing one of the names returned by the above function
   * and return the respective value of @p IntervalSpacing. Throw an error if
   * the string is no valid one.
   */
  static IntervalSpacing parse_interval_spacing (const std::string &name);

  /**
   * Determine an estimate for the memory consumption (in bytes) of this
   * object.
   */
  std::size_t memory_consumption () const;

  /**
   * Exception.
   */
  DeclExceptionMsg (ExcEmptyData,
                    "Your input argument to this function does not appear to "
                    "have any data in it.");
  /**
   * Exception.
   */
  DeclException2 (ExcIncompatibleArraySize,
                  int, int,
                  << "The two array sizes " << arg1 << " and " << arg2
                  << " must match, but don't.");
  /**
   * Exception.
   */
  DeclException1 (ExcInvalidName,
                  std::string,
                  << "The given name <" << arg1
                  << "> does not match any of the known formats.");

private:
  /**
   * Structure denoting one interval.
   */
  struct Interval
  {
    /**
     * Constructor. Sets the bounds and sets the number of values in this
     * interval to zero.
     */
    Interval (const double left_point,
              const double right_point);

    /**
     * Determine an estimate for the memory consumption (in bytes) of this
     * object.
     */
    std::size_t memory_consumption () const;

    /**
     * Left bound of the interval.
     */
    double       left_point;

    /**
     * Right bound of the interval.
     */
    double       right_point;

    /**
     * Number of values in this interval.
     */
    unsigned int content;
  };

  /**
   * "Less-than" operation which finds the minimal positive value by sorting
   * zero and negative value to be larger than the largest positive number.
   * Used to find the lower bound of the leftmost interval in the logarithmic
   * case interval spacing scheme.
   *
   * Return @p true, if (<tt>n1<n2</tt>, and (<tt>n1>0</tt> or
   * <tt>n2<0</tt>)), or (n2<n1 and n1>0 and n2<=0). This in effect sorts all
   * negative numbers to be larger than the largest positive number.
   */
  template <typename number>
  static bool logarithmic_less (const number n1,
                                const number n2);

  /**
   * Vector holding one set of intervals for each data set given to the @p
   * evaluate function.
   */
  std::vector<std::vector<Interval> > intervals;

  /**
   * Values for the depth axis of 3d histograms. Stored in the @p evaluate
   * function.
   */
  std::vector<double>            y_values;
};


DEAL_II_NAMESPACE_CLOSE

#endif
