package hep.aida.ref;

import hep.aida.IAxis;
import hep.aida.IHistogram1D;
/**
A reference implementation of hep.aida.IHistogram1D.
The goal is to provide a clear implementation rather than the most efficient implementation.
However, performance seems fine - filling 1.2 * 10^6 points/sec, both using FixedAxis or VariableAxis.

@author Wolfgang Hoschek, Tony Johnson, and others.
@version 1.0, 23/03/2000
*/
public class Histogram1D extends AbstractHistogram1D implements IHistogram1D
{
	private double[] errors;
	private double[] heights;
	private int[] entries;
	private int nEntry; // total number of times fill called
	private double sumWeight; // Sum of all weights
	private double sumWeightSquared; // Sum of the squares of the weights
	private double mean, rms;
	/**
	 * Creates a variable-width histogram.
	 * Example: <tt>edges = (0.2, 1.0, 5.0)</tt> yields an axis with 2 in-range bins <tt>[0.2,1.0), [1.0,5.0)</tt> and 2 extra bins <tt>[-inf,0.2), [5.0,inf]</tt>.
	 * @param title The histogram title.
	 * @param edges the bin boundaries the axis shall have;
	 *        must be sorted ascending and must not contain multiple identical elements.
	 * @throws IllegalArgumentException if <tt>edges.length < 1</tt>.
	 */
	public Histogram1D(String title, double[] edges)
	{
		this(title,new VariableAxis(edges));
	}
	/**
	 * Creates a fixed-width histogram.
	 * 
	 * @param title The histogram title.
	 * @param bins The number of bins.
	 * @param min The minimum value on the X axis.
	 * @param max The maximum value on the X axis.
	 */
	public Histogram1D(String title, int bins, double min, double max)
	{
		this(title,new FixedAxis(bins,min,max));
	}
	/**
	 * Creates a histogram with the given axis binning.
	 * 
	 * @param title The histogram title.
	 * @param axis The axis description to be used for binning.
	 */
	public Histogram1D(String title, IAxis axis)
	{
		super(title);
		xAxis = axis;
		int bins = axis.bins();
		entries = new int[bins+2];
		heights = new double[bins+2];
		errors = new double[bins+2];
	}
	public int allEntries() // perhaps to be deleted (default impl. in superclass sufficient)
	{
		return nEntry;
	}
	public int binEntries(int index)
	{
		//return entries[xAxis.map(index)];
		return entries[map(index)];
	}
	public double binError(int index)
	{
		//return Math.sqrt(errors[xAxis.map(index)]);
		return Math.sqrt(errors[map(index)]);
	}
	public double binHeight(int index)
	{
		//return heights[xAxis.map(index)];
		return heights[map(index)];
	}
	public double equivalentBinEntries()
	{
		return sumWeight*sumWeight/sumWeightSquared;
	}
	public void fill(double x)
	{
		//int bin = xAxis.getBin(x);
		int bin = map(xAxis.coordToIndex(x));
		entries[bin]++;
		heights[bin]++;
		errors[bin]++;
		nEntry++;
		sumWeight++;
		sumWeightSquared++;
		mean += x;
		rms += x*x;
	}
	public void fill(double x, double weight)
	{
		//int bin = xAxis.getBin(x);
		int bin = map(xAxis.coordToIndex(x));
		entries[bin]++;
		heights[bin] += weight;
		errors[bin] += weight*weight;
		nEntry++;
		sumWeight += weight;
		sumWeightSquared += weight*weight;
		mean += x*weight;
		rms += x*weight*weight;
	}
	public double mean()
	{
		return mean/sumWeight;
	}
	public void reset()
	{
		 for (int i=0; i<entries.length; i++)
		 {
			 entries[i] = 0;
			 heights[i] = 0;
			 errors[i] = 0;
		 }
		 nEntry = 0;
		 sumWeight = 0;
		 sumWeightSquared = 0;
		 mean = 0;
		 rms = 0;
	}
	public double rms()
	{
		return Math.sqrt(rms/sumWeight - mean*mean/sumWeight/sumWeight);
	}
	/**
	 * Used internally for creating slices and projections
	 */
	void setContents(int[] entries, double[] heights, double[] errors)
	{
		this.entries = entries;
		this.heights = heights;
		this.errors = errors;
		
		for (int i=0; i<entries.length; i++) 
		{
			nEntry += entries[i];
			sumWeight += heights[i];
		}
		// TODO: Can we do anything sensible/useful with the other statistics?
		sumWeightSquared = Double.NaN;
		mean = Double.NaN;
		rms = Double.NaN;
	}
}
