/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy 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
* (at your option) any later version.
*
* Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.math;
import icy.type.TypeUtil;
import icy.type.collection.array.ArrayUtil;
import java.util.Arrays;
/**
* @author Stephane
*/
public class Histogram
{
protected final int[] bins;
protected final double minValue;
protected final double maxValue;
protected final boolean integer;
protected final double dataToBin;
protected final double binWidth;
/**
* Create a histogram for the specified value range and the desired number of bins.
*
* @param minValue
* minimum value
* @param maxValue
* maximum value
* @param nbBin
* number of desired bins (should be > 0).
* @param integer
* If true the input value are considered as integer values.<br>
* Bins number is then clamped to values range to not bias the histogram.<br>
* In this case the effective number of bins can differ from the desired one.
*/
public Histogram(double minValue, double maxValue, int nbBin, boolean integer)
{
super();
this.minValue = minValue;
this.maxValue = maxValue;
this.integer = integer;
final double range = (maxValue - minValue);
double bw;
if (integer)
{
if (nbBin > range)
bw = 1d;
else
{
bw = (range + 1d) / nbBin;
bw = Math.max(1d, Math.floor(bw));
}
bins = new int[(int) Math.ceil((range + 1d) / bw)];
}
else
{
bw = range / nbBin;
bins = new int[nbBin];
}
binWidth = bw;
// data to bin index conversion ratio
if (range > 0)
dataToBin = (bins.length - 1) / range;
else
dataToBin = 0d;
}
/**
* Reset histogram
*/
public void reset()
{
Arrays.fill(bins, 0);
}
/**
* Add the value to the histogram
*/
public void addValue(double value)
{
final int index = (int) ((value - minValue) * dataToBin);
if ((index >= 0) && (index < bins.length))
bins[index]++;
}
/**
* Add the specified array of values to the histogram
*
* @param signed
* false if the input array should be interpreted as unsigned values<br>
* (integer type only)
*/
public void addValues(Object array, boolean signed)
{
switch (ArrayUtil.getDataType(array))
{
case BYTE:
addValues((byte[]) array, signed);
break;
case SHORT:
addValues((short[]) array, signed);
break;
case INT:
addValues((int[]) array, signed);
break;
case LONG:
addValues((long[]) array, signed);
break;
case FLOAT:
addValues((float[]) array);
break;
case DOUBLE:
addValues((double[]) array);
break;
}
}
/**
* Add the specified byte array to the histogram
*/
public void addValues(byte[] array, boolean signed)
{
if (signed)
{
for (byte value : array)
bins[(int) ((value - minValue) * dataToBin)]++;
}
else
{
for (byte value : array)
bins[(int) ((TypeUtil.unsign(value) - minValue) * dataToBin)]++;
}
}
/**
* Add the specified short array to the histogram
*/
public void addValues(short[] array, boolean signed)
{
if (signed)
{
for (short value : array)
bins[(int) ((value - minValue) * dataToBin)]++;
}
else
{
for (short value : array)
bins[(int) ((TypeUtil.unsign(value) - minValue) * dataToBin)]++;
}
}
/**
* Add the specified int array to the histogram
*/
public void addValues(int[] array, boolean signed)
{
if (signed)
{
for (int value : array)
bins[(int) ((value - minValue) * dataToBin)]++;
}
else
{
for (int value : array)
bins[(int) ((TypeUtil.unsign(value) - minValue) * dataToBin)]++;
}
}
/**
* Add the specified long array to the histogram
*/
public void addValues(long[] array, boolean signed)
{
if (signed)
{
for (long value : array)
bins[(int) ((value - minValue) * dataToBin)]++;
}
else
{
for (long value : array)
bins[(int) ((TypeUtil.unsign(value) - minValue) * dataToBin)]++;
}
}
/**
* Add the specified float array to the histogram
*/
public void addValues(float[] array)
{
for (float value : array)
bins[(int) ((value - minValue) * dataToBin)]++;
}
/**
* Add the specified double array to the histogram
*/
public void addValues(double[] array)
{
for (double value : array)
bins[(int) ((value - minValue) * dataToBin)]++;
}
/**
* Get bin index from data value
*/
protected int toBinIndex(double value)
{
return (int) Math.round((value - minValue) * dataToBin);
}
/**
* Returns the minimum allowed value of the histogram.
*/
public double getMinValue()
{
return minValue;
}
/**
* Returns the maximum allowed value of the histogram.
*/
public double getMaxValue()
{
return maxValue;
}
/**
* Returns true if the input value are integer values only.<br>
* This is used to adapt the bin number.
*/
public boolean isIntegerType()
{
return integer;
}
/**
* Returns the number of bins of the histogram.
*/
public int getBinNumber()
{
return bins.length;
}
/**
* Return the width of a bin
*/
public double getBinWidth()
{
return binWidth;
}
/**
* Returns the size of the specified bin (number of element in the bin)
*/
public int getBinSize(int index)
{
if ((index < 0) || (index >= bins.length))
return 0;
return bins[index];
}
/**
* Returns bins of histogram
*/
public int[] getBins()
{
return bins;
}
}