package basics.math;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import basics.testing.Test;
import basics.utl.NumUtl;

public class BasicStatistics implements Serializable
{
private static final long serialVersionUID = 1L;
double                    m_max            = Double.MIN_VALUE;
double                    m_min            = Double.MAX_VALUE;
double                    m_sum            = 0.0D;
long                      m_counter        = 0;
List<Double>              sources          = new ArrayList<Double>();
public static final int   MAX_RESOLUTION   = 12;

public BasicStatistics(double initialValue)
{
   m_sum = initialValue;
   m_counter = 1;
}

public BasicStatistics()
{
}

public void init(double sum, long counter)
{
   m_sum = sum;
   m_counter = counter;
   m_max = getAverage();
   m_min = getAverage();
}

public double getSum()
{
   return m_sum;
}

public long getCount()
{
   return m_counter;
}

public synchronized void add(double v)
{
   sources.add(v);
   m_sum = NumUtl.round(m_sum + v, MAX_RESOLUTION);
   m_counter += 1.0;
   m_max = v > m_max ? v : m_max;
   m_min = v < m_min ? v : m_min;
}

public double getMin()
{
   return m_min;
}

public double getMax()
{
   return m_max;
}

public double getMax(int precision)
{
   return NumUtl.round(m_max, precision);
}

/**
 * note that this can't be used after serializing/desializing the object, because it does not store
 * the source values. Sqrt(calcVariance()) gives the RmsError (Standardabweichung)
 * @return
 */
public double calcVariance()
{
   double elements = sources.size();
   if(elements < 1)
      return 0;
   double a = getAverage();
   double x = 0D;
   for(Double d : sources)
   {
      double v = (d - a);
      x += (v * v);
   }
   return NumUtl.round(x / elements, MAX_RESOLUTION);
}

public double calcVariance(int precision)
{
   return NumUtl.round(calcVariance(), precision);
}

public double calcRmsError()
{
   return NumUtl.round(Math.sqrt(calcVariance()), MAX_RESOLUTION);
}

public double calcRmsError(int precision)
{
   return NumUtl.round(Math.sqrt(calcVariance()), precision);
}

public double getAverage()
{
   double v = (double)m_counter;
   return v == 0D ? 0D : NumUtl.round(m_sum / v, MAX_RESOLUTION);
}

public double getAverage(int precision)
{
   return NumUtl.round(getAverage(), precision);
}

public synchronized double getAverage(int precision, boolean setAvg)
{
   double v = getAverage(precision);
   if(setAvg)
   {
      m_sum = v;
      m_counter = 1;
   }
   return v;
}

public synchronized long getAverage(boolean setAvg)
{
   double v = getAverage();
   if(setAvg)
   {
      m_sum = v;
      m_counter = 1;
   }
   return (long)v;
}

// public void writeObject(ObjectOutputStream out) throws IOException
// {
// out.writeDouble(m_sum);
// out.writeLong(m_counter);
// out.writeDouble(m_max);
// out.writeDouble(m_min);
// }
//
// public void readObject(ObjectInputStream in) throws IOException,
// ClassNotFoundException
// {
// m_sum = in.readDouble();
// m_counter = in.readLong();
// m_max = in.readDouble();
// m_min = in.readDouble();
// }
public String toString()
{
   int digits = 3;
   StringBuilder s = new StringBuilder();
   s.append("[");
   if(m_min == Double.MAX_VALUE)
      s.append("-");
   else
      s.append(NumUtl.round(m_min, digits));
   s.append(" ");
   s.append(NumUtl.round(getAverage(), digits));
   s.append(" ");
   if(m_max == Double.MIN_VALUE)
      s.append("-");
   else
      s.append(NumUtl.round(m_max, digits));
   s.append(" N:");
   s.append(m_counter);
   s.append(" S:");
   s.append(NumUtl.round(m_sum, digits));
   s.append(" E:");
   double v = calcRmsError();
   s.append(NumUtl.round(v, digits));
   s.append("]");
   return s.toString();
}

public static void unittest()
{
   BasicStatistics b = new BasicStatistics();
   b.add(2.20);
   Test.assertTrue(b.getSum() == 2.2D, "wrong sum: " + (b.getSum()));
   b.add(4.40);
   Test.assertTrue(b.getSum() == 6.6D, "wrong sum: " + (b.getSum()));
   b.add(6.60);
   Test.assertTrue(b.getCount() == 3, "wrong count: " + b.getCount());
   Test.assertTrue(b.getSum() == 13.2, "wrong sum: " + b.getSum());
   Test.assertTrue(b.getAverage() == 4.4, "wrong average: " + b.getAverage());
   Test.assertTrue(b.getMax() == 6.6, "wrong max: " + b.getMax());
   Test.assertTrue(b.getMin() == 2.2, "wrong min: " + b.getMin());
   //   Test.printout("var " + b.calcVariance(1));
   //   Test.printout("rms " + b.calcRmsError(1));
}
}
