package hep.aida.ref.histogram;
import hep.aida.*;

/**
 * Fixed-width axis; A reference implementation of hep.aida.IAxis.
 *
 * @author The AIDA team @ SLAC.
 * @version $Id: FixedAxis.java,v 1.4 2002/10/28 06:42:11 tonyj Exp $
 */
public class FixedAxis implements IAxis
{
	private int bins;
	private double min;
	private double binWidth;
	private int xunder, xover;
	/**
	 * Create an Axis
	 * @param bins Number of bins
	 * @param min Minimum for axis
	 * @param max Maximum for axis
	 */
	public FixedAxis(int bins, double min, double max)
	{
		if (bins < 1) throw new IllegalArgumentException("bins="+bins);
		if (max <= min) throw new IllegalArgumentException("max <= min");
		
		// Note, for internal consistency we save only min and binWidth
		// and always use these quantities for all calculations. Due to 
		// rounding errors the return value from upperEdge is not necessarily
		// exactly equal to max
		
		this.bins = bins;
		this.min = min;
		this.binWidth =  (max - min)/bins;
		
		// our internal definition of overflow/underflow differs from
		// that of the outside world
		this.xunder = 0;
		this.xover = bins+1;
	}

        /**
         * Check if two Axis are equal.
         * @param o the Object to check
         * @return <code>true</code> if <code>o</code> is an instance of FixedAxis and
         *         it has the same number of bins, minimum and bin width.
         */
        public boolean equals(Object o)
        {
            if (o instanceof FixedAxis)
            {
                FixedAxis other = (FixedAxis) o;
                return this.bins == other.bins && this.min == other.min && this.binWidth == other.binWidth;
            }
            return false;
        }

        /**
         * Get the centre of a bin.
         * @param index the bin's index
         * @return the bin's centre
         */
        public double binCentre(int index)
	{
		return min + binWidth*index + binWidth/2;
	}

        /**
         * Get the number of bins in the Axis.
         * @return the number of bins.
         */
	public int bins()
	{
		return bins;
	}

        /**
         * Get the lower edge of a bin.
         * @param index the bin's index
         * @return the bin's lower edge. If <code>index</code> corresponds
         *         to the UNDERFLOW_BIN, Double.NEGATIVE_INFINITY is returned.
         *         If <code>index</code> corresponds to OVERFLOW_BIN, the
         *         upper edge of the axis is returned.
         */
	public double binLowerEdge(int index)
	{
            if (index == IAxis.UNDERFLOW_BIN) return Double.NEGATIVE_INFINITY;
            if (index == IAxis.OVERFLOW_BIN) return upperEdge();
            return min + binWidth*index;
	}

        /**
         * Get the upper edge of a bin.
         * @param index the bin's index
         * @return the bin's upper edge. If <code>index</code> corresponds
         *         to the OVERFLOW_BIN, Double.POSITIVE_INFINITY is returned.
         *         If <code>index</code> corresponds to UNDERFLOW_BIN, the
         *         lower edge of the axis is returned.
         */
        public double binUpperEdge(int index)
	{
		if (index == IAxis.UNDERFLOW_BIN) return min;
		if (index == IAxis.OVERFLOW_BIN) return Double.POSITIVE_INFINITY;
		return min + binWidth*(index+1);
	}

        /**
         * Get the bin width.
         * @param index the bin's index. For a Fixed Axis the bin's width is constant.
         * @return the bin's width.
         **/
        public double binWidth(int index)
	{
		return binWidth;
	}
        
        /**
         * Get the bin's index corresponding to an axis's value.
         * @param coord an axis value
         * @return the bin's index corresponding to <code>coord</code>
         */
	public int coordToIndex(double coord)
	{
                if (coord < min) return IAxis.UNDERFLOW_BIN;
		int index = (int) Math.floor((coord - min)/binWidth);
		if (index >= bins) return IAxis.OVERFLOW_BIN;
		return index;
	}
        
        /**
         * Get the Axis lower edge.
         * @return the axis lower edge
         */
	public double lowerEdge()
	{
		return min;
	}

        /**
         * Get the Axis upper edge.
         * @return the axis upper edge
         */        
        public double upperEdge()
	{
		return min + binWidth*bins;
	}
        
	/**
	 * This package private method is similar to coordToIndex except
	 * that it returns our internal definition for overflow/underflow
	 */
	int xgetBin(double coord)
	{
		if (coord < min) return xunder; 
		int index = (int) Math.floor((coord - min)/binWidth);
		if (index > bins) return xover; 
		return index+1;
	}
        
	/**
	 * Package private method to map from the external representation of bin
	 * number to our internal representation of bin number
	 */
	int xmap(int index)
	{
		if (index >= bins) throw new IllegalArgumentException("bin="+index);
		if (index >= 0) return index+1;
		if (index == IAxis.UNDERFLOW_BIN) return xunder;
		if (index == IAxis.OVERFLOW_BIN) return xover;
		throw new IllegalArgumentException("bin="+index);
	}
        
        public boolean isFixedBinning() {
            return true;
        }
}
