jhplot.H2D.java Source code

Java tutorial

Introduction

Here is the source code for jhplot.H2D.java

Source

/**
*    Copyright (C)  DataMelt project. The jHPLot package by S.Chekanov and Work.ORG
*    All rights reserved.
*
*    This program is free software; you can redistribute it and/or modify it under the terms
*    of the GNU General Public License as published by the Free Software Foundation; either
*    version 3 of the License, or any later version.
*
*    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, see <http://www.gnu.org/licenses>.
*
*    Additional permission under GNU GPL version 3 section 7:
*    If you have received this program as a library with written permission from the DataMelt team,
*    you can link or combine this library with your non-GPL project to convey the resulting work.
*    In this case, this library should be considered as released under the terms of
*    GNU Lesser public license (see <https://www.gnu.org/licenses/lgpl.html>),
*    provided you include this license notice and a URL through which recipients can access the
*    Corresponding Source.
**/
package jhplot;

import root.*;
import hep.aida.*;
import hep.aida.ref.histogram.*;
import hep.io.root.interfaces.TH2;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import cern.jet.random.AbstractDistribution;
import jhplot.gui.HelpBrowser;

/**
 * Histogram in two dimensions (2D). 
 * Main class to create a 2D histogram. Each bin in 2D is characterized by 2 indexes, i and j.
 * This class is a direct extension of the H1D used for 1D case.
 * Use the get() method to  access JAIDA Histogram2D histogram class.
 * 
 * @author S.Chekanov
 * 
 */

public class H2D extends DrawOptions implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private Histogram2D h1;

    private double minX;

    private double maxX;

    private double minY;

    private double maxY;

    private int binsX;

    private int binsY;

    //   private int region = 0;

    private IAxis xAx;

    private IAxis yAy;

    /**
     * Create 2D histogram
     * 
     * @param title
     *            Title
     * @param binsX
     *            Number of bins in X
     * @param minX
     *            Min value in X
     * @param maxX
     *            Max value in X
     * @param binsY
     *            Number of bins in Y
     * @param minY
     *            Min value in Y
     * @param maxY
     *            Max value in Y
     */
    public H2D(String title, int binsX, double minX, double maxX, int binsY, double minY, double maxY) {

        is3D = true;
        this.title = title;
        this.binsX = binsX;
        this.minX = minX;
        this.maxX = maxX;
        this.binsY = binsY;
        this.minY = minY;
        this.maxY = maxY;

        /*
         * // cannot treat different binnings yet int Ibin = this.binsX; if
         * (this.binsY > this.binsX) Ibin = this.binsY; this.binsX = Ibin;
         * this.binsY = Ibin;
         */
        xAx = new FixedAxis(this.binsX, this.minX, this.maxX);
        yAy = new FixedAxis(this.binsY, this.minY, this.maxY);
        h1 = new Histogram2D(this.title, this.title, xAx, yAy);

    }

    /**
     * Create H2D histogram from JAIDA IHistogram2D class
     * 
     * @param h1
     *            IHistogram2D histogram from JAIDA
     */
    public H2D(IHistogram2D h1) {
        setStyle("h");
        is3D = true;
        this.h1 = (Histogram2D) h1;
        setTitle(h1.title());
        xAx = h1.xAxis();
        yAy = h1.yAxis();
        this.minX = xAx.lowerEdge();
        this.maxX = xAx.upperEdge();
        this.minY = yAy.lowerEdge();
        this.maxY = yAy.upperEdge();
        this.binsX = xAx.bins();
        this.binsY = yAy.bins();

    }

    /**
     * Create H2D histogram from Cloud2D.
     * 
     * @param c2d
     *            Cloud2D
     * @param binX number of bins in X
     * 
     * @param binY number of bins in Y          
     */
    public H2D(Cloud2D c2d, int binX, int binY) {
        setStyle("h");
        is3D = true;
        this.title = c2d.title();
        setTitle(c2d.title());
        this.minX = c2d.lowerEdgeX();
        this.maxX = c2d.upperEdgeX();
        this.minY = c2d.lowerEdgeY();
        this.maxY = c2d.upperEdgeY();
        this.binsX = binX;
        this.binsY = binY;
        xAx = new FixedAxis(this.binsX, this.minX, this.maxX);
        yAy = new FixedAxis(this.binsY, this.minY, this.maxY);
        h1 = new Histogram2D(this.title, this.title, xAx, yAy);
        fill(c2d);
    }

    /**
     * Create a 2D histogram using variable size bins in X and Y
     * 
     * @param title
     *            - bin titles
     * @param edgesX
     *            - array with bin edges in X
     * @param edgesY
     *            - array with bin edges in Y
     * */

    public H2D(String title, double[] edgesX, double[] edgesY) {

        is3D = true;
        this.title = title;
        this.binsX = edgesX.length - 1;
        this.binsY = edgesY.length - 1;
        this.minX = edgesX[0];
        this.maxX = edgesX[edgesX.length - 1];
        this.minY = edgesY[0];
        this.maxY = edgesY[edgesY.length - 1];
        xAx = new VariableAxis(edgesX);
        yAy = new VariableAxis(edgesY);
        h1 = new Histogram2D(this.title, this.title, xAx, yAy);
    }

    /**
     * Define H2D in terms of axis
     * 
     * @param title
     *            title
     * @param xAx
     *            Axis for X
     * @param yAy
     *            Axis for Y
     */

    public H2D(String title, IAxis xAx, IAxis yAy) {
        is3D = true;
        this.title = title;
        this.binsX = xAx.bins();
        this.minX = xAx.lowerEdge();
        this.maxX = xAx.upperEdge();
        this.binsY = yAy.bins();
        this.minY = yAy.lowerEdge();
        this.maxY = yAy.upperEdge();

        h1 = new Histogram2D(this.title, this.title, xAx, yAy);

    }

    /**
     * Create a H2D histogram from JAIDA histogram
     * 
     * @param h1
     *            Histogram2D histogram
     */
    public H2D(Histogram2D h1) {

        is3D = true;
        this.h1 = h1;
        setTitle(h1.title());
        xAx = h1.xAxis();
        yAy = h1.yAxis();
        this.minX = xAx.lowerEdge();
        this.maxX = xAx.upperEdge();
        this.minY = yAy.lowerEdge();
        this.maxY = yAy.upperEdge();
        this.binsX = xAx.bins();
        this.binsY = yAy.bins();
    }

    /**
     * Get the JAIDA Histogram2D
     * 
     * @return Histogram2D
     */
    public Histogram2D get() {

        return h1;

    }

    /**
     * Create H2D histogram from JAIDA TH1 histogram class
     * 
     * @param h1t
     *            TH1 histogram from JAIDA
     */
    public H2D(TH2 h2t) {

        h1 = Converter.convert(h2t, h2t.getTitle());

    }

    /**
     * Set RMS on Y
     * 
     * @param rmsY
     *            RMS on Y
     */
    public void setRmsY(double rmsY) {
        h1.setRmsY(rmsY);
    }

    /**
     * Set mean on Y
     * 
     * @param mean
     *            on Y
     */
    public void setMeanY(double mean) {
        h1.setMeanY(mean);
    }

    /**
     * Get RMS on Y
     */
    public double getRmsY() {
        return h1.rmsY();
    }

    /**
     * Get mean on Y
     */
    public double getMeanY() {
        return h1.meanY();
    }

    /**
     * Get mean on X.
     */
    public double getMeanX() {
        return h1.meanX();
    }

    /**
     * Get RMS on X
     */
    public double getRmsX() {
        return h1.rmsX();
    }

    /**
     * Set all entries.
     * 
     * @param entries
     *            entries
     */
    public void setNEntries(int entries) {
        h1.setNEntries(entries);
    }

    /**
     * Set in-range entries.
     * 
     * @param entries
     *            entries
     */
    public void setValidEntries(int entries) {
        h1.setValidEntries(entries);
    }

    /**
     * Set RMS on X.
     */
    public void setRmsX(double rmsX) {
        h1.setRmsX(rmsX);
    }

    /**
     * Set mean on X.
     * 
     * @param mean
     *            on X
     */
    public void setMeanX(double mean) {
        h1.setMeanX(mean);
    }

    /**
     * Set content of H2D histogram
     * 
     * @param heights
     *            heights
     * @param errors
     *            errors
     */
    public void setContents(double[][] heights, double[][] errors) {
        h1.setContents(heights, errors, null, null, null, null, null);
    }

    /**
     * Set the content of the whole Histogram at once. This is a convenience
     * method for saving/restoring Histograms. Of the arguments below the
     * heights array cannot be null. The errors array should in general be
     * non-null, but this depends on the specific binner. The entries array can
     * be null, in which case the entry of a bin is taken to be the integer part
     * of the height. If the means array is null, the mean is defaulted to the
     * geometric center of the bin. If the rms array is null, the rms is taken
     * to be the bin width over the root of 12.
     * 
     * 
     * @param heights
     *            The bin heights
     * @param errors
     *            The bin errors
     * @param entries
     *            The bin entries
     * @param meanXs
     *            The means of the bin along the x axis
     * @param rmsXs
     *            The rmss of the bin along the x axis
     * @param meanYs
     *            The means of the bin along the y axis
     * @param rmsYs
     *            The rmss of the bin along the y axis
     * 
     */
    public void setContents(double[][] heights, double[][] errors, int[][] entries, double[][] meanXs,
            double[][] rmsXs, double[][] meanYs, double[][] rmsYs) {
        h1.setContents(heights, errors, entries, meanXs, rmsXs, meanYs, rmsYs);
    }

    /**
     * Fill H2D histogram assuming all weights are unity.
     * 
     * @param value1
     *            value in X
     * @param value2
     *            value in Y
     */
    public void fill(double value1, double value2) {

        h1.fill(value1, value2);

    }

    /** Fill the histogram with random numbers from Gaussian (Normal) distribution.
    * Seed is taken from time. 
    * @param TotNumber  number generated events
    * @param meanX mean of the gaussian in X 
    * @param sdX   standard deviation in X 
    * @param meanY mean of the gaussian in Y 
    * @param sdY   standard deviation in Y 
    */
    public void fillGauss(int TotNumber, double meanX, double sdX, double meanY, double sdY) {
        java.util.Random random = new java.util.Random();
        for (int i = 0; i < TotNumber; i++)
            h1.fill(sdX * random.nextGaussian() + meanX, sdY * random.nextGaussian() + meanY);

    }

    /** Fill the histogram with random numbers from fralt distribution.
    * Seed is taken from time. 
    * Using mean=0 and width=1 will give a flat distribution between 0 and 1.  
    * @param TotNumber  number generated events
    * @param meanX mean of the distribution in X 
    * @param widthX width of the distribution  in X
    * @param meanY mean of the distribution in Y 
    * @param widthY width of the distribution  in Y 
    */
    public void fillRnd(int TotNumber, double meanX, double widthX, double meanY, double widthY) {
        java.util.Random random = new java.util.Random();
        for (int i = 0; i < TotNumber; i++)
            h1.fill(widthX * random.nextDouble() + meanX, widthY * random.nextDouble() + meanY);

    }

    /**
    * Fill the histogram with random numbers.
    * Random generators are taken from cern.jet.random.*.
    * Examples: Beta, Binominal, Poisson, BreitWigner,ChiSquare,Empirical
    * Exponential, Gamma, Hyperbolic, Logarithmic, Normal, NegativeBinomial
    * 
    * @param TotNumber  number generated events
    * @param random1 generator for X
    * @param random1 generator for Y
    */

    public void fill(int TotNumber, AbstractDistribution random1, AbstractDistribution random2) {
        for (int i = 0; i < TotNumber; i++)
            h1.fill(random1.nextDouble(), random2.nextDouble());

    }

    /**
     * Set the error on this bin.
     * 
     * @param indexX
     *            the bin number (0...N-1) or OVERFLOW or UNDERFLOW.
     * @param indexY
     *            the bin number (0...N-1) or OVERFLOW or UNDERFLOW.
     * @param error
     *            the error.
     */
    public void setBinError(int indexX, int indexY, double error) {
        h1.setBinError(indexX, indexY, error);
    }

    /**
     * Total height of the corresponding bin.
     * 
     * @param indexX
     *            The x bin number in the external representation: (0...N-1) or
     *            OVERFLOW or UNDERFLOW.
     * @param indexY
     *            The y bin number in the external representation: (0...N-1) or
     *            OVERFLOW or UNDERFLOW.
     * @return The bin height for the corresponding bin.
     * 
     */
    public double binHeight(int indexX, int indexY) {
        return h1.binHeight(indexX, indexY);
    }

    /**
     * Get the number of entries in the underflow and overflow bins.
     * 
     * @return The number of entries outside the range of the Histogram.
     * 
     */
    public int extraEntries() {
        return h1.extraEntries();
    }

    /**
     * Get the sum of the bin heights for all the entries outside the
     * Histogram's range.
     * 
     * @return The sum of the out of range bin's heights.
     * 
     */
    public double sumExtraBinHeights() {
        return h1.sumExtraBinHeights();
    }

    /**
     * Get the sum of the bin heights for all the entries, in-range and
     * out-range ones.
     * 
     * @return The sum of all the bin's heights.
     * 
     */
    public double sumAllBinHeights() {
        return h1.sumAllBinHeights();
    }

    /**
     * Get the number of bins in X
     * 
     * @return binsX number of bins in X
     */
    public int getBinsX() {

        return h1.xAxis().bins();
    }

    /**
     * Get the number of bins in Y
     * 
     * @return binsY number of bins in Y
     */
    public int getBinsY() {
        return h1.yAxis().bins();
    }

    /**
     * Get min value of axis in X
     * 
     * @return min value of X axis
     */
    public double getMinX() {
        return h1.xAxis().lowerEdge();
    }

    /**
     * Get max value of axis in X
     * 
     * @return max value of X axis
     */
    public double getMaxX() {
        return h1.xAxis().upperEdge();
    }

    /**
     * Get max value of Y axis
     * 
     * @return max value of Y axis
     */
    public double getMaxY() {
        return h1.yAxis().upperEdge();
    }

    /**
     * Get min value of Y axis
     * 
     * @return min value of X axis
     */
    public double getMinY() {
        return h1.yAxis().lowerEdge();
    }

    /**
     * Get underflow entries in Y
     * 
     * @return underflow in Y
     */
    public int getUnderflowEntriesY() {
        return h1.binEntriesY(IAxis.UNDERFLOW_BIN);
    }

    /**
     * Get underflow height in Y
     * 
     * @return underflow height in Y
     */
    public double getUnderflowHeightY() {
        return h1.binHeightY(IAxis.UNDERFLOW_BIN);
    }

    /**
     * Get underflow height in X
     * 
     * @return underflow height in X
     */
    public double getUnderflowHeightX() {
        return h1.binHeightX(IAxis.UNDERFLOW_BIN);
    }

    /**
     * Get underflow entries in X
     * 
     * @return underflow in X
     */
    public int getUnderflowEntriesX() {
        return h1.binEntriesY(IAxis.UNDERFLOW_BIN);
    }

    /**
     * Get overflow entries in Y
     * 
     * @return overflow in Y
     */
    public int getOverflowEntriesY() {
        return h1.binEntriesY(IAxis.OVERFLOW_BIN);
    }

    /**
     * Get overflow height in Y
     * 
     * @return overflow in Y
     */
    public double getOverflowHeightY() {
        return h1.binHeightY(IAxis.OVERFLOW_BIN);
    }

    /**
     * Get overflow entries in Y
     * 
     * @return overflow in Y
     */
    public int getOverflowEntriesX() {
        return h1.binEntriesX(IAxis.OVERFLOW_BIN);
    }

    /**
     * Get overflow entries in Y
     * 
     * @return overflow in Y
     */
    public double getOverflowHeightX() {
        return h1.binHeightX(IAxis.OVERFLOW_BIN);
    }

    /**
     * Get lower edge of the bin in X
     * 
     * @param index
     *            of the bin
     * @return lower edge of the bin
     */
    public double getLowerEdgeX(int index) {
        return h1.xAxis().binLowerEdge(index);

    }

    /**
     * Various manipulations with histograms (+,-,*,/). Note: no new object will
     * be created.
     * 
     * @param a
     *            H2D histogram.
     * @param title
     *            New Title
     * 
     * @return same H2D object but modified
     */

    public H2D oper(H2D a, String title, String what) {

        IAnalysisFactory af = IAnalysisFactory.create();
        IHistogramFactory hf = af.createHistogramFactory(af.createTreeFactory().create());

        // first check them
        if (what.equals("+")) {
            IHistogram2D hnew = hf.add(title, get(), a.get());
            return new H2D(hnew);
        }

        if (what.equals("-")) {
            IHistogram2D hnew = hf.subtract(title, get(), a.get());
            return new H2D(hnew);
        }

        if (what.equals("*")) {
            IHistogram2D hnew = hf.multiply(title, get(), a.get());
            return new H2D(hnew);
        }

        if (what.equals("/")) {
            IHistogram2D hnew = hf.divide(title, get(), a.get());
            return new H2D(hnew);
        }

        ErrorMessage("Operation \"" + what + "\" is not implemented");

        return this;

    }

    /**
     * Scale the histogram.
     * 
     * @param title
     *            New title
     * @param scaleFactor
     *            Scale factor
     */

    public void scale(String title, double scaleFactor) {

        this.title = title;
        h1.scale(scaleFactor);
    }

    /**
     * Scale the histogram and return scaled object.
     * 
     * @param title
     *            New title
     * @param scaleFactor
     *            Scale factor
     **/
    public H2D operScale(String title, double scaleFactor) {
        scale(title, scaleFactor);
        return this;
    }

    /**
     * Get upper edge of the bin in X
     * 
     * @param index
     *            of the bin
     * @return lower edge of the bin
     */
    public double getUpperEdgeX(int index) {
        return h1.xAxis().binUpperEdge(index);

    }

    /**
     * Get upper edge of the bin in Y
     * 
     * @param index
     *            of the bin
     * @return lower edge of the bin
     */
    public double getUpperEdgeY(int index) {
        return h1.yAxis().binUpperEdge(index);

    }

    /**
     * Get lower edge of the bins in Y
     * 
     * @param index
     *            of the bin
     * @return lower edge of the bin
     */
    public double getLowerEdgeY(int index) {
        return h1.yAxis().binLowerEdge(index);

    }

    /**
     * Calculate complete statistics for this histogram for the X or Y
     * Unlike other methods (like mean or rms), it performs calculations
     * on the existing histogram, thus the method is somewhat slow.
     * It return mean, error on the mean, RMS, variance, standard deviation. <p>
     * The key for the output <b>map are: mean, error, rms, variance, stddev etc. Print the keys of this
     * map to get the full access to statistics</b>.
     * 
     * 
     * @return map representing histogram statistics
     */
    public Map<String, Double> getStat() {

        Histogram2D h = get();

        Map<String, Double> tmp = new HashMap<String, Double>();

        tmp.put("meanX", h.meanX());
        tmp.put("meanY", h.meanY());
        tmp.put("rmsX", h.rmsX());
        tmp.put("rmsY", h.rmsX());
        tmp.put("entries", (double) h.allEntries());

        return tmp;
    }

    /**
     * Get number of all entries
     * 
     * @return Number of all entries
     */

    public int allEntries() {
        return h1.allEntries();

    }

    /**
     * Number of in-range entries in the histogram
     * 
     * @return Number of in-range entries
     */

    public int entries() {
        return h1.entries();

    }

    /**
     * Get IAxis in X
     * 
     * @return axis in X
     */

    public IAxis getAxisX() {
        return h1.xAxis();

    }

    /**
     * Get IAxis in Y
     * 
     * @return axis in Y
     */

    public IAxis getAxisY() {
        return h1.yAxis();

    }

    public void clear() {
        h1.reset();
    }

    /**
     * Get bin heights as 2D array. Note 0 index bin means 1st bin etc.
     * @return bin heights as 2D array
     */
    public double[][] binHeights() {
        binsX = h1.xAxis().bins();
        binsY = h1.yAxis().bins();
        double[][] newHeights = new double[binsX][binsY];
        for (int i = 0; i < binsX; i++) {
            for (int j = 0; j < binsY; j++) {
                newHeights[i][j] = h1.binHeight(i, j);
            }
        }

        return newHeights;
    }

    /**
     * Get errors on heights as 2D array. Note 0 index bin means 1st bin etc.
     * @return errors on heights as 2D array
     */
    public double[][] binErrors() {
        binsX = h1.xAxis().bins();
        binsY = h1.yAxis().bins();
        double[][] errors = new double[binsX][binsY];
        for (int i = 0; i < binsX; i++) {
            for (int j = 0; j < binsY; j++) {
                errors[i][j] = h1.binError(i, j);
            }
        }

        return errors;
    }

    /**
     * Get mean position for X axis as 2D array. Note 0 index bin means 1st bin etc.
     * @return X mean positions as 2D array
     */
    public double[][] binMeansX() {
        binsX = h1.xAxis().bins();
        binsY = h1.yAxis().bins();
        double[][] xx = new double[binsX][binsY];
        for (int i = 0; i < binsX; i++) {
            for (int j = 0; j < binsY; j++) {
                xx[i][j] = h1.binMeanX(i, j);
            }
        }
        return xx;
    }

    /**
     * Get lower edges for X bins as 1D array. Note 0 index bin means 1st bin etc.
     * @return lower edges for X positions as 1D array
     */
    public double[] getLowerEdgesX() {
        binsX = h1.xAxis().bins();
        double[] xx = new double[binsX];
        for (int i = 0; i < binsX; i++) {
            xx[i] = h1.xAxis().binLowerEdge(i);
        }
        return xx;
    }

    /**
     * Get lower edges for Y bins as 1D array. Note 0 index bin means 1st bin etc.
     * @return lower edges for Y positions as 1D array
     */
    public double[] getLowerEdgesY() {
        binsY = h1.yAxis().bins();
        double[] xx = new double[binsY];
        for (int i = 0; i < binsY; i++) {
            xx[i] = h1.yAxis().binLowerEdge(i);
        }
        return xx;
    }

    /**
     * Get mean position for Y axis as 2D array. Note 0 index bin means 1st bin etc.
     * @return Y mean positions as 2D array
     */
    public double[][] binMeansY() {
        binsX = h1.xAxis().bins();
        binsY = h1.yAxis().bins();
        double[][] xx = new double[binsX][binsY];
        for (int i = 0; i < binsX; i++) {
            for (int j = 0; j < binsY; j++) {
                xx[i][j] = h1.binMeanY(i, j);
            }
        }
        return xx;
    }

    /**
     * Get bin mean position at the location (i,i)
     * @param i bin position in X
     * @param j bin position in Y 
     * @return bin mean in X and Y (2D array).
     */
    public double[] binMeans(int i, int j) {
        double[] center = new double[2];
        center[0] = h1.binMeanX(i, j);
        center[1] = h1.binMeanY(i, j);
        return center;
    }

    /**
     * Get exact copy of the current histogram. This means it makes a new
     * object.
     * 
     * @param newtitle
     *            New title
     * @return a new copy of the histogram
     */

    public H2D copy(String newtitle) {
        xAx = h1.xAxis();
        yAy = h1.yAxis();
        this.binsX = xAx.bins();
        this.binsY = yAy.bins();

        int ibinsX = binsX + 2;
        int ibinsY = binsY + 2;

        double[][] newHeights = new double[ibinsX][ibinsY];
        double[][] newErrors = new double[ibinsX][ibinsY];
        double[][] newMeansX = new double[ibinsX][ibinsY];
        double[][] newRmssX = new double[ibinsX][ibinsY];
        double[][] newMeansY = new double[ibinsX][ibinsY];
        double[][] newRmssY = new double[ibinsX][ibinsY];
        int[][] newEntries = new int[ibinsX][ibinsY];

        newHeights[0][0] = getUnderflowHeightX();
        newHeights[ibinsX - 1][ibinsY - 1] = getOverflowHeightY();

        for (int i = 0; i < ibinsX - 1; i++) {
            for (int j = 0; j < ibinsY - 1; j++) {
                newHeights[i + 1][j + 1] = h1.binHeight(i, j);
                newErrors[i + 1][j + 1] = h1.binError(i, j);
                newEntries[i + 1][j + 1] = h1.binEntries(i, j);
                newMeansX[i + 1][j + 1] = h1.binMeanX(i, j);
                newRmssX[i + 1][j + 1] = h1.binRmsX(i, j);
                newMeansY[i + 1][j + 1] = h1.binMeanY(i, j);
                newRmssY[i + 1][j + 1] = h1.binRmsY(i, j);
            }
        }

        H2D hnew = new H2D(newtitle, xAx, yAy);
        hnew.setContents(newHeights, newErrors, newEntries, newMeansX, newMeansY, newRmssX, newRmssY);
        hnew.setMeanX(h1.meanX());
        hnew.setMeanY(h1.meanX());
        hnew.setRmsX(h1.rmsX());
        hnew.setRmsY(h1.rmsY());
        hnew.setNEntries(entries());
        return hnew;
    }

    /**
     * Get a density distribution dividing each bin of the histogram by the bin
     * width and the total number of heights for all bins.
     * 
     * @return density distribution
     */
    public H2D getDensity() {

        int ibinsX = binsX + 2;
        int ibinsY = binsY + 2;

        double[][] newHeights = new double[ibinsX][ibinsY];
        double[][] newErrors = new double[ibinsX][ibinsY];
        double[][] newMeansX = new double[ibinsX][ibinsY];
        double[][] newRmssX = new double[ibinsX][ibinsY];
        double[][] newMeansY = new double[ibinsX][ibinsY];
        double[][] newRmssY = new double[ibinsX][ibinsY];
        int[][] newEntries = new int[ibinsX][ibinsY];

        newHeights[0][0] = getUnderflowHeightX();
        newHeights[ibinsX - 1][ibinsY - 1] = getOverflowHeightY();
        double sum = sumAllBinHeights();

        for (int i = 0; i < ibinsX - 1; i++) {
            double w1 = sum * (xAx.binUpperEdge(i) - xAx.binLowerEdge(i));
            for (int j = 0; j < ibinsY - 1; j++) {
                double ww = w1 * (yAy.binUpperEdge(j) - yAy.binLowerEdge(j));
                newHeights[i + 1][j + 1] = h1.binHeight(i, j) / ww;
                newErrors[i + 1][j + 1] = h1.binError(i, j) / ww;
                newEntries[i + 1][j + 1] = h1.binEntries(i, j);
                newMeansX[i + 1][j + 1] = h1.binMeanX(i, j);
                newRmssX[i + 1][j + 1] = h1.binRmsX(i, j);
                newMeansY[i + 1][j + 1] = h1.binMeanY(i, j);
                newRmssY[i + 1][j + 1] = h1.binRmsY(i, j);
            }
        }

        H2D hnew = new H2D(getTitle(), xAx, yAy);
        hnew.setContents(newHeights, newErrors, newEntries, newMeansX, newMeansY, newRmssX, newRmssY);
        hnew.setMeanX(h1.meanX());
        hnew.setMeanY(h1.meanY());
        hnew.setRmsX(h1.rmsX());
        hnew.setRmsY(h1.rmsY());
        hnew.setNEntries(entries());
        return hnew;
    }

    /**
     * Number of entries in the corresponding bin (i.e. the number of times fill
     * was called for this bin).
     * 
     * @param indexX
     *            the x bin number in the external representation: (0...N-1) or
     *            OVERFLOW or UNDERFLOW.
     * @param indexY
     *            the y bin number in the external representation: (0...N-1) or
     *            OVERFLOW or UNDERFLOW.
     * @return The number of entries for the corresponding bin.
     * 
     */
    public int binEntries(int indexX, int indexY) {
        return h1.binEntries(indexX, indexY);
    }

    /**
     * Error of the corresponding bin.
     * 
     * @param indexX
     *            the x bin number in the external representation: (0...N-1) or
     *            OVERFLOW or UNDERFLOW.
     * @param indexY
     *            the y bin number in the external representation: (0...N-1) or
     *            OVERFLOW or UNDERFLOW.
     * @return errors for the corresponding bin.
     * 
     */
    public double binError(int indexX, int indexY) {
        return h1.binError(indexX, indexY);
    }

    /**
     * Fill H2D histogram with weights
     * 
     * @param value1
     *            value in X
     * @param value2
     *            value in Y
     * @param weight
     *            weight
     */
    public void fill(double value1, double value2, double weight) {
        h1.fill(value1, value2, weight);
    }

    /**
     * Fill H2D histogram from 2 arrays (X amd Y). Assume weights =1.
     * 
     * @param value1
     *            array with values in X
     * @param value2
     *            array with values in Y
     */
    public void fill(double[] value1, double[] value2) {

        if (value1.length != value2.length) {
            ErrorMessage("Different size of input arrays");
            return;
        }
        ;

        for (int i = 0; i < value1.length; i++)
            h1.fill(value1[i], value2[i]);

    }

    /**
     * Fill H2D histogram from Cloud2D. 
     * 
     * @param c2d input Cloud2D
     *            array with values in X
     *            
     */
    public void fill(Cloud2D c2d) {

        for (int i = 0; i < c2d.entries(); i++)
            h1.fill(c2d.valueX(i), c2d.valueY(i), c2d.weight(i));

    }

    /**
     * Fill H2D histogram from 2 arrays. Assume weights =1.
     * 
     * @param value1
     *            array with values in X
     * @param value2
     *            array with values in Y
     */
    public void fill(P0D value1, P0D value2) {

        if (value1.size() != value2.size()) {
            ErrorMessage("Different size of input arrays");
            return;
        }
        ;

        for (int i = 0; i < value1.size(); i++)
            h1.fill(value1.getQuick(i), value2.getQuick(i));

    }

    /**
     * Fill H2D histogram from 2 arrays. Assume weights =1.
     * 
     * @param value1
     *            array with values in X
     * @param value2
     *            array with values in Y
     */
    public void fill(P0I value1, P0I value2) {
        if (value1.size() != value2.size()) {
            ErrorMessage("Different size of input arrays");
            return;
        }
        ;

        for (int i = 0; i < value1.size(); i++)
            h1.fill(value1.getQuick(i), value2.getQuick(i));

    }

    /**
     * Fill H2D histogram from arrays. Weights are defined by the third array. Sizes of the arrays must be the same.
     * 
     * @param value1
     *            array with values in X
     * @param value2
     *            array with values in Y
     * @param weights
     *            weights
     */
    public void fill(double[] value1, double[] value2, double[] weights) {

        if (value1.length != value2.length) {
            ErrorMessage("Different size of input arrays");
            return;
        }

        if (value1.length != weights.length) {
            ErrorMessage("Different size of input weight");
            return;
        }

        for (int i = 0; i < value1.length; i++) {
            h1.fill(value1[i], value2[i], weights[i]);

        }

    }

    /**
     * Convert a coordinate X on the axis to a bin number
     * 
     * @param x
     *            coordinate on axis
     * @return index of the corresponding bin
     */
    public int findBinX(double x) {
        return xAx.coordToIndex(x);

    }

    /**
     * Convert a coordinate Y on the axis to a bin number
     * 
     * @param y
     *            coordinate on axis
     * @return index of the corresponding bin
     */
    public int findBinY(double y) {
        return yAy.coordToIndex(y);

    }

    /**
     * Integrate histogram in the range. The integral is computed as the sum of
     * bin contents in the range.
     * 
     * @param xMin
     *            Min value for X integration (included to integration)
     * @param xMax
     *            Max index for X integration (included to integration)
     * @param yMin
     *            Min index for Y integration (included to integration)
     * @param yMax
     *            Max index for Y integration (included to integration)
     *@param timesBinWidth
     *            If true, the integral is the sum of the bin contents
     *            multiplied by the bin width in x.
     * 
     * @return integral (sum of all heights)
     */

    public double integralRange(double xMin, double xMax, double yMin, double yMax, boolean timesBinWidth) {

        int i1 = findBinX(xMin);
        int i2 = findBinX(xMax);
        int i3 = findBinY(yMin);
        int i4 = findBinY(yMax);
        return integral(i1, i2, i3, i4, false);
    }

    /**
     * Return q probability distribution derived from a histogram. The histogram
     * is scaled by the sum of all contents.
     * 
     * @return probability distribution
     */

    public H2D getProbability() {
        H2D h2d = this.copy(getTitle());
        h2d.scale(1.0 / h2d.sumAllBinHeights());
        return h2d;

    }

    /**
     * Make a copy of the data holder
     * 
     * @return New data holder
     */
    public H2D copy() {
        return copy(getTitle());

    }

    /**
     * Scale the histogram.
     * 
     * @param scaleFactor
     *            Scale factor
     */

    public void scale(double scaleFactor) {

        h1.scale(scaleFactor);
    }

    /**
     * Integrate histogram between two indices. The integral is computed as the
     * sum of bin contents in the range.
     * 
     * @param BinMinX
     *            Min index for X integration (included to integration, start
     *            from 1)
     * @param BinMaxX
     *            Max index for X integration (included to integration)
     * @param BinMinY
     *            Min index for Y integration (included to integration, start
     *            from 1)
     * @param BinMaxY
     *            Max index for Y integration (included to integration)
     * 
     * @return integral (sum of all heights)
     */

    public double integral(int BinMinX, int BinMaxX, int BinMinY, int BinMaxY) {

        return integral(BinMinX, BinMaxX, BinMinY, BinMaxY, false);
    }

    /**
     * Integrate histogram between two indices. The integral is computed as the
     * sum of bin contents in the range if the last parameter is false. If it is
     * true, he integral is the sum of the bin contents multiplied by the bin
     * width in x.
     * 
     * @param BinMinX
     *            Min index for X integration (included to integration, start
     *            from 1)
     * @param BinMaxX
     *            Max index for X integration (included to integration)
     * @param BinMinY
     *            Min index for Y integration (included to integration, start
     *            from 1)
     * @param BinMaxY
     *            Max index for Y integration (included to integration)
     *@param timesBinWidth
     *            If true, the integral is the sum of the bin contents
     *            multiplied by the bin width in x.
     * 
     * @return integral (sum of all heights)
     */

    public double integral(int BinMinX, int BinMaxX, int BinMinY, int BinMaxY, boolean timesBinWidth) {

        if (BinMinX > BinMaxX) {
            ErrorMessage("Wrong bin number for X!");
            return -1;
        }

        if (BinMinY > BinMaxY) {
            ErrorMessage("Wrong bin number for Y!");
            return -1;
        }

        int bX = xAx.bins();
        int bY = yAy.bins();

        if (BinMinX < 1 || BinMaxX > bX) {
            ErrorMessage("Wrong bin number for X!");
            return -1;
        }
        if (BinMinY < 1 || BinMaxY > bY) {
            ErrorMessage("Wrong bin number for Y!");
            return -1;
        }

        double sum = 0.0;

        if (timesBinWidth == false) {
            for (int i = BinMinX - 1; i < BinMaxX; i++) {
                for (int j = BinMinY - 1; j < BinMaxY; j++) {
                    sum += h1.binHeight(i, j);

                }
            }

        } else {

            for (int i = BinMinX - 1; i < BinMaxX; i++) {
                double w1 = xAx.binUpperEdge(i) - xAx.binLowerEdge(i);
                for (int j = BinMinY - 1; j < BinMaxY; j++) {
                    double w2 = yAy.binUpperEdge(j) - yAy.binLowerEdge(j);
                    sum += (h1.binHeight(i, j) * w1 * w2);

                }
            }

        }
        return sum;
    }

    /**
    * Compare the histogram with a function. The comparison tests  hypotheses that
    * the histogram represent identical distribution with a function using Pearson's chi-squared test. 
    * The number chi2/ndf gives the estimate (values close to 1 indicates
    * similarity between 2 histograms.). the function and histogram are identical if chi2=0.
    * Chi2/ndf and p-value probability is 1. Maken sure that  statistical errors are included correctly. 
    * Data with zero errors will be ignored.  
    * @param f1 
    *            function to compare to.  
    * @return map with the result. It gives Chi2, gives number
    *         of degrees of freedom (ndf), probability
    *         ("quality", or p-value).
    */

    public Map<String, Double> compareChi2(F2D f1) {

        Map<String, Double> tmp = new HashMap<String, Double>();

        int bins1x = get().xAxis().bins();
        int bins1y = get().yAxis().bins();

        double sum1 = 0;
        double nDf = 0;

        for (int i = 0; i < bins1x; i++) {
            double xx1 = get().xAxis().binLowerEdge(i);
            double xx2 = get().xAxis().binUpperEdge(i);
            double d1 = xx2 - xx1;
            double xx = xx1 + 0.5 * d1;
            for (int j = 0; j < bins1y; j++) {
                double y1 = get().yAxis().binLowerEdge(j);
                double y2 = get().yAxis().binUpperEdge(j);
                double d2 = y1 - y2;
                double yy = y1 + 0.5 * d2;

                double bin1 = binHeight(i, j);
                double e1 = binError(i, j);
                double ff = f1.eval(xx, yy);
                if (e1 != 0) {
                    sum1 = sum1 + ((ff - bin1) * (ff - bin1) / (e1 * e1));
                    nDf++;
                }
            }
        }

        double chi2 = sum1;
        tmp.put("chi2", chi2);
        tmp.put("ndf", (double) nDf);

        org.apache.commons.math3.distribution.ChiSquaredDistribution chi2Distribution = new org.apache.commons.math3.distribution.ChiSquaredDistribution(
                nDf);
        double prob = chi2Distribution.cumulativeProbability(chi2);
        tmp.put("p-value", 1.0 - prob);
        return tmp;

    }

    /**
     * Compare two 2D  histograms. Comparison of two histograms test hypotheses that
     * two histograms represent identical distributions. It calculates Chi2
     * between 2 histograms taking into account errors on the heights of the
     * bins. The number chi2/ndf gives the estimate (values close to 1 indicates
     * similarity between 2 histograms.) Two histograms are identical if chi2=0.
     * Chi2/ndf]. Probability (p-value) is
     * 1. Make sure that both histograms have error (or set them to
     * small values).
     * 
     * @param h2
     *            second histogram to compare
     * @return results. It gives Chi2,  the  number
     *         of degrees of freedom (ndf), and probability
     *         ("quality", or p-value).
     */
    public Map<String, Double> compareChi2(H2D h2) {

        Map<String, Double> tmp = new HashMap<String, Double>();

        int bins1x = get().xAxis().bins();
        int bins2x = h2.get().xAxis().bins();

        if (bins1x != bins2x) {
            System.out.println("Different histograms! Please use histograms with the same bin numbers in X");
            return tmp;
        }

        int bins1y = get().yAxis().bins();
        int bins2y = h2.get().yAxis().bins();

        if (bins1y != bins2y) {
            System.out.println("Different histograms! Please use histograms with the same bin numbers in Y");
            return tmp;
        }

        double chi2 = 0;
        int nDf = 0;

        double sum1 = 0;
        double sum2 = 0;
        double sumw1 = 0;
        double sumw2 = 0;

        for (int i = 0; i < bins1x; i++) {
            for (int j = 0; j < bins1y; j++) {

                double bin1 = binHeight(i, j);
                double bin2 = h2.binHeight(i, j);
                double e1 = binError(i, j);
                double e2 = h2.binError(i, j);

                if (e1 > 0) {
                    bin1 *= bin1 / (e1 * e1);

                } else
                    bin1 = 0;

                if (e2 > 0) {
                    bin2 *= bin2 / (e2 * e2);
                } else
                    bin2 = 0;

                // sum contents
                sum1 += bin1;
                sum2 += bin2;
                sumw1 += e1 * e1;
                sumw2 += e2 * e2;

            }
        }

        //double sum = sum1 + sum2;

        if (sumw1 <= 0 || sumw2 <= 0) {
            System.out.println("Cannot compare histograms with all zero errors");
            return tmp;
        }

        if (sum1 == 0 || sum2 == 0) {
            System.out.println("One histogram is empty!");
            return tmp;
        }

        for (int i = 0; i < bins1x; i++) {
            for (int j = 0; j < bins1y; j++) {
                double bin1 = binHeight(i, j);
                double bin2 = h2.binHeight(i, j);
                double e1 = binError(i, j);
                double e2 = h2.binError(i, j);

                // System.out.println(Double.toString(bin1)+" - "+Double.toString(bin2));

                if (e1 > 0)
                    bin1 *= bin1 / (e1 * e1);
                else
                    bin1 = 0;

                if (e2 > 0)
                    bin2 *= bin2 / (e2 * e2);
                else
                    bin2 = 0;

                double binsum = bin1 + bin2;
                double delta = sum2 * bin1 - sum1 * bin2;

                if (binsum > 0) {
                    chi2 += delta * delta / binsum;
                    System.out.println(chi2);
                    nDf++;
                }

            }
        }

        chi2 /= (sum1 * sum2);
        tmp.put("chi2", chi2);
        tmp.put("ndf", (double) nDf);

        org.apache.commons.math3.distribution.ChiSquaredDistribution chi2Distribution = new org.apache.commons.math3.distribution.ChiSquaredDistribution(
                nDf);
        double prob = chi2Distribution.cumulativeProbability(chi2);
        tmp.put("p-value", 1.0 - prob);

        return tmp;

    }

    /**
     * Generate error message
     * 
     * @param a
     *            Message
     */
    private void ErrorMessage(String a) {
        jhplot.utils.Util.ErrorMessage(a);
    }

    /**
     * Show online documentation.
     */
    public void doc() {

        String a = this.getClass().getName();
        a = a.replace(".", "/") + ".html";
        new HelpBrowser(HelpBrowser.JHPLOT_HTTP + a);

    }

}