/*
 * EfficiencyBinner3D.java
 *
 * Created on July 22, 2002, 10:19 AM
 */

package hep.aida.ref.histogram.binner;

/**
 *
 * @author  The AIDA team at SLAC.
 *
 */
public class EfficiencyBinner3D implements Binner3D {
    
    private int[][][] entries;
    private double[][][] heights;
    private double[][][] plusErrors;
    private double[][][] minusErrors;
    private double[][][] meansX;
    private double[][][] rmssX;
    private double[][][] meansY;
    private double[][][] rmssY;
    private double[][][] meansZ;
    private double[][][] rmssZ;
    
    private int xBins;
    private int yBins;
    private int zBins;
    
    /**
     * Creates a new instance of EfficiencyBinner1D.
     */
    
    public EfficiencyBinner3D(int xBins, int yBins, int zBins) {
        if (xBins < 0) throw new IllegalArgumentException("Number of bins cannot be negative!!! "+xBins);
        if (yBins < 0) throw new IllegalArgumentException("Number of bins cannot be negative!!! "+yBins);
        if (zBins < 0) throw new IllegalArgumentException("Number of bins cannot be negative!!! "+zBins);
        setNumberOfBins(xBins, yBins, zBins);
    }
    
    public void fill( int xBin, int yBin, int zBin, double x, double y,double z, double weight) {
        if ( weight < 0 || weight > 1 ) throw new IllegalArgumentException("Wrong weight "+weight+" !! It has to be between 0 and 1");
        entries[xBin][yBin][zBin]++;
        heights[xBin][yBin][zBin]     += weight;
        meansX[xBin][yBin][zBin]      += x*weight;
        rmssX[xBin][yBin][zBin]       += x*x*weight;
        meansY[xBin][yBin][zBin]      += y*weight;
        rmssY[xBin][yBin][zBin]       += y*y*weight;
        meansZ[xBin][yBin][zBin]      += z*weight;
        rmssZ[xBin][yBin][zBin]       += z*z*weight;
    }
    
    public void clear() {
        createArrays( xBins, yBins, zBins);
    }
    
    private void setNumberOfBins(int xBins, int yBins, int zBins) {
        this.xBins = xBins;
        this.yBins = yBins;
        this.zBins = zBins;
        createArrays(xBins, yBins, zBins);
    }
    
    public int entries(int xBin, int yBin, int zBin) {
        return entries[xBin][yBin][zBin];
    }
    
    public double height(int xBin, int yBin, int zBin) {
        if ( entries(xBin,yBin,zBin) > 0 )
            return heights[xBin][yBin][zBin]/entries(xBin,yBin,zBin);
        return 0;
    }
    
    public double plusError(int xBin, int yBin, int zBin) {
        return EfficiencyBinnerUtils.H95CL(heights[xBin][yBin][zBin],entries[xBin][yBin][zBin],1);
    }
    
    public double minusError(int xBin, int yBin, int zBin) {
        return EfficiencyBinnerUtils.H95CL(heights[xBin][yBin][zBin],entries[xBin][yBin][zBin],2);
    }
    
    public double meanX(int xBin, int yBin, int zBin) {
        double h = heights[xBin][yBin][zBin];
        if ( h != 0 ) return meansX[xBin][yBin][zBin]/h;
        return 0;
    }
    
    public double meanY(int xBin, int yBin, int zBin) {
        double h = heights[xBin][yBin][zBin];
        if ( h != 0 ) return meansY[xBin][yBin][zBin]/h;
        return 0;
    }

    public double meanZ(int xBin, int yBin, int zBin) {
        double h = heights[xBin][yBin][zBin];
        if ( h != 0 ) return meansZ[xBin][yBin][zBin]/h;
        return 0;
    }
    
    public double rmsX(int xBin, int yBin, int zBin) {
        double h = heights[xBin][yBin][zBin];
        double m = meanX(xBin,yBin,zBin);
        if ( h != 0 ) return Math.sqrt( Math.abs(rmssX[xBin][yBin][zBin]/h - m*m) );
        return 0;
    }
    
    public double rmsY(int xBin, int yBin, int zBin) {
        double h = heights[xBin][yBin][zBin];
        double m = meanY(xBin,yBin,zBin);
        if ( h != 0 ) return Math.sqrt( Math.abs(rmssY[xBin][yBin][zBin]/h - m*m) );
        return 0;
    }
    
    public double rmsZ(int xBin, int yBin, int zBin) {
        double h = heights[xBin][yBin][zBin];
        double m = meanZ(xBin,yBin,zBin);
        if ( h != 0 ) return Math.sqrt( Math.abs(rmssZ[xBin][yBin][zBin]/h - m*m) );
        return 0;
    }

    public void setBinContent(int xBin, int yBin, int zBin, int entries, double height, double plusError, double minusError, double meanX, double rmsX, double meanY, double rmsY, double meanZ, double rmsZ) {
        heights[xBin][yBin][zBin] = height*entries;
        this.entries[xBin][yBin][zBin] = entries;
        meansX[xBin][yBin][zBin] = 0;
        rmssX[xBin][yBin][zBin] = 0;
        meansY[xBin][yBin][zBin] = 0;
        rmssY[xBin][yBin][zBin] = 0;
        meansZ[xBin][yBin][zBin] = 0;
        rmssZ[xBin][yBin][zBin] = 0;
        if ( heights[xBin][yBin][zBin] != 0 ) {
            meansX[xBin][yBin][zBin] = meanX*heights[xBin][yBin][zBin];
            rmssX[xBin][yBin][zBin] = rmsX*rmsX*heights[xBin][yBin][zBin] + meanX*meanX*heights[xBin][yBin][zBin];
            meansY[xBin][yBin][zBin] = meanY*heights[xBin][yBin][zBin];
            rmssY[xBin][yBin][zBin] = rmsY*rmsY*heights[xBin][yBin][zBin] + meanY*meanY*heights[xBin][yBin][zBin];
            meansZ[xBin][yBin][zBin] = meanZ*heights[xBin][yBin][zBin];
            rmssZ[xBin][yBin][zBin] = rmsZ*rmsZ*heights[xBin][yBin][zBin] + meanZ*meanZ*heights[xBin][yBin][zBin];
        }
    }
    
    public void scale( double scaleFactor ) {
        for ( int xBin = 0; xBin < xBins; xBin++ ) {
            for ( int yBin = 0; yBin < yBins; yBin++ ) {
                for ( int zBin = 0; zBin < zBins; zBin++ ) {
                    heights[xBin][yBin][zBin]     *= scaleFactor;
                    meansX[xBin][yBin][zBin]      *= scaleFactor;
                    rmssX[xBin][yBin][zBin]       *= scaleFactor;
                    meansY[xBin][yBin][zBin]      *= scaleFactor;
                    rmssY[xBin][yBin][zBin]       *= scaleFactor;
                    meansZ[xBin][yBin][zBin]      *= scaleFactor;
                    rmssZ[xBin][yBin][zBin]       *= scaleFactor;
                }
            }
        }
    }

    private void createArrays(int xBins, int yBins, int zBins) {
        entries = new int[xBins][yBins][zBins];
        heights = new double[xBins][yBins][zBins];
        plusErrors = new double[xBins][yBins][zBins];
        minusErrors = new double[xBins][yBins][zBins];
        meansX = new double[xBins][yBins][zBins];
        rmssX = new double[xBins][yBins][zBins];
        meansY = new double[xBins][yBins][zBins];
        rmssY = new double[xBins][yBins][zBins];
        meansZ = new double[xBins][yBins][zBins];
        rmssZ = new double[xBins][yBins][zBins];
    }
        
}

