/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2009 IITP RAS
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * 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
 *
 * Authors: Pavel Boyko <boyko@iitp.ru>
 * Corrections and extensions: Timo Bingmann <tbns@idlebox.net>
 */

#ifndef AVERAGE_H
#define AVERAGE_H
#include <cmath>
#include <ostream>
#include <limits>

namespace ns3 {

/// Simple average, min, max and std. deviation calculator
template <typename T = double>
class Average
{
public:
  Average ()
    : m_size (0), m_min (std::numeric_limits<T>::max ()), m_max (0),
      m_avg (0), m_avg2 (0) 
  {
  }

  /// Add new sample
  void Update (T const & x)
  {
    m_min = std::min (x, m_min);
    m_max = std::max (x, m_max);
    m_avg = (m_size * m_avg + x) / (m_size + 1);
    m_avg2 = (m_size * m_avg2 + x * x) / (m_size + 1);
    m_size++;
  }
  /// Reset statistics
  void Reset ()
  {
    m_size = 0;
    m_min = std::numeric_limits<T>::max ();
    m_max = 0;
    m_avg = 0;
    m_avg2 = 0;
  }

  ///\name Sample statistics
  //\{
  /// Sample size
  uint32_t Count   () const { return m_size; }
  /// Minimum
  T        Min     () const { return m_min; }
  /// Maximum
  T        Max     () const { return m_max; }
  /// Sample average
  double   Avg     () const { return m_avg; }
  /// Estimate of mean, alias to Avg
  double   Mean    () const { return Avg (); }
  /// Unbiased estimate of variance
  double   Var     () const { return Count() / (double)(Count() - 1) * (m_avg2 - m_avg*m_avg); }
  /// Standard deviation
  double   Stddev  () const { return sqrt (Var ());}
  //\}
  
  /** 
   * \name Error of the mean estimates
   *  
   * Note that estimates are valid for 
   *   - uncorrelated measurements, 
   *   - normal distribution and
   *   - large enough sample size.
   */
  //\{
  /// Margin of error of the mean for 90% confidence level 
  double   Error90() const { return 1.645 * sqrt (Var () / Count ()); }
  /// Margin of error of the mean for 95% confidence level 
  double   Error95() const { return 1.960 * sqrt (Var () / Count ()); }
  /// Margin of error of the mean for 99% confidence level 
  double   Error99() const { return 2.576 * sqrt (Var () / Count ()); }
  //\}

private:
  uint32_t m_size;
  T      m_min, m_max;
  double m_avg, m_avg2;
};

/// Print avg (err) [min, max]
template <typename T>
std::ostream & operator<< (std::ostream & os, Average<T> const & x)
{
  if (x.Count () != 0)
    os << x.Avg () << " (" << x.Stddev () << ") [" << x.Min () << ", " << x.Max () << "]";
  else
    os << "NA"; // not available
  return os;
}
}
#endif /* AVERAGE_H */
