/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
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.
*/
package org.geogebra.common.kernel.algos;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.math3.distribution.BinomialDistribution;
import org.apache.commons.math3.distribution.HypergeometricDistribution;
import org.apache.commons.math3.distribution.IntegerDistribution;
import org.apache.commons.math3.distribution.PascalDistribution;
import org.apache.commons.math3.distribution.PoissonDistribution;
import org.apache.commons.math3.distribution.ZipfDistribution;
import org.apache.commons.math3.util.Cloner;
import org.geogebra.common.awt.GColor;
import org.geogebra.common.euclidian.draw.DrawBarGraph;
import org.geogebra.common.euclidian.draw.DrawBarGraph.DrawType;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.arithmetic.NumberValue;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoBoolean;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoElement.FillType;
import org.geogebra.common.kernel.geos.GeoList;
import org.geogebra.common.kernel.geos.GeoNumberValue;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.statistics.AlgoUsingUniqueAndFrequency;
import org.geogebra.common.util.debug.Log;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Bar chart algorithm.
*
* @author G. Sturr
*
*/
public class AlgoBarChart extends AlgoUsingUniqueAndFrequency
implements DrawInformationAlgo {
private Map<Integer, HashMap<Integer, Object>> tags = new HashMap<Integer, HashMap<Integer, Object>>();
/** Bar chart from expression **/
public static final int TYPE_BARCHART_EXPRESSION = 0;
/** Bar chart from raw data and given width **/
public static final int TYPE_BARCHART_RAWDATA = 1;
/** Bar chart from (values,frequencies) **/
public static final int TYPE_BARCHART_FREQUENCY_TABLE = 2;
/** Bar chart from (values,frequencies) with given width **/
public static final int TYPE_BARCHART_FREQUENCY_TABLE_WIDTH = 3;
/** Stick graph **/
public static final int TYPE_STICKGRAPH = 10;
/** Step graph **/
public static final int TYPE_STEPGRAPH = 20;
/** Graph of a discrete probability distribution **/
public static final int TYPE_BARCHART_BINOMIAL = 40;
public static final int TYPE_BARCHART_PASCAL = 41;
public static final int TYPE_BARCHART_POISSON = 42;
public static final int TYPE_BARCHART_HYPERGEOMETRIC = 43;
public static final int TYPE_BARCHART_BERNOULLI = 44;
public static final int TYPE_BARCHART_ZIPF = 45;
// largest possible number of rectangles
private static final int MAX_RECTANGLES = 10000;
// output
private GeoNumeric sum;
// input
private GeoNumberValue a, b, p1, p2, p3;
private GeoList list1, list2;
// local fields
private GeoElement ageo, bgeo, widthGeo, isCumulative, isHorizontal, p1geo,
p2geo, p3geo, hasJoin, pointType;
private int type;
private int N; // # of intervals
private double[] yval; // y value (= min) in interval 0 <= i < N
private double[] leftBorder; // leftBorder (x val) of interval 0 <= i < N
private String[] value; // value string for each bar
private double barWidth;
private double freqMax;
private double dataSize;
private String toolTipText;
// flag to determine if result sum measures area or length
private boolean isAreaSum = true;
/******************************************************
* BarChart[<interval start>,<interval stop>, <list of heights>]
*
* @param cons
* @param label
* @param a
* @param b
* @param list1
*/
public AlgoBarChart(Construction cons, String label, GeoNumberValue a,
GeoNumberValue b, GeoList list1) {
super(cons);
type = TYPE_BARCHART_EXPRESSION;
this.a = a;
this.b = b;
this.list1 = list1;
ageo = a.toGeoElement();
bgeo = b.toGeoElement();
// output
sum = new GeoNumeric(cons) {
@Override
public String getTooltipText(final boolean colored,
final boolean alwaysOn) {
return toolTipText;
}
};
setInputOutput(); // for AlgoElement
compute();
sum.setDrawable(true);
sum.setLabel(label);
}
/******************************************************
* BarChart[<a>,<b>, <list of raw data>, <bar width>]
*
* @param cons
* @param label
* @param list1
* @param width
*/
public AlgoBarChart(Construction cons, String label, GeoList list1,
GeoNumeric width) {
this(cons, list1, null, width, null, null, null, TYPE_BARCHART_RAWDATA);
sum.setLabel(label);
}
public AlgoBarChart(Construction cons, String label, GeoList list1,
GeoNumeric width, GeoNumeric scale) {
this(cons, list1, null, width, null, null, null, scale,
TYPE_BARCHART_RAWDATA);
sum.setLabel(label);
}
/******************************************************
* BarChart[<a>,<b>, <list of raw data>, <bar width>] (no label)
*
* @param cons
* @param list1
* @param width
*/
public AlgoBarChart(Construction cons, GeoList list1, GeoNumeric width) {
this(cons, list1, null, width, null, null, null, TYPE_BARCHART_RAWDATA);
}
/******************************************************
* BarChart[<a>,<b>, <list of values>, <list of frequencies>]
*
* @param cons
* @param label
* @param list1
* @param list2
*/
public AlgoBarChart(Construction cons, String label, GeoList list1,
GeoList list2) {
this(cons, list1, list2, null, null, null, null,
TYPE_BARCHART_FREQUENCY_TABLE);
sum.setLabel(label);
}
/******************************************************
* BarChart[<a>,<b>, <list of values>, <list of frequencies>] (no label)
*
* @param cons
* @param list1
* @param list2
*/
public AlgoBarChart(Construction cons, GeoList list1, GeoList list2) {
this(cons, list1, list2, null, null, null, null,
TYPE_BARCHART_FREQUENCY_TABLE);
}
/******************************************************
* BarChart[<a>,<b>, <list of values>, <list of frequencies>, <bar width>]
*
* @param cons
* @param label
* @param list1
* @param list2
* @param width
*/
public AlgoBarChart(Construction cons, String label, GeoList list1,
GeoList list2, GeoNumberValue width) {
this(cons, list1, list2, width, null, null, null,
TYPE_BARCHART_FREQUENCY_TABLE_WIDTH);
sum.setLabel(label);
}
/******************************************************
* BarChart[<a>,<b>, <list of values>, <list of frequencies>, <bar width>]
* (no label)
*
* @param cons
* @param list1
* @param list2
* @param width
*/
public AlgoBarChart(Construction cons, GeoList list1, GeoList list2,
GeoNumberValue width) {
this(cons, list1, list2, width, null, null, null,
TYPE_BARCHART_FREQUENCY_TABLE_WIDTH);
}
/******************************************************
* General constructor with label
*
* @param cons
* @param label
* @param list1
* @param list2
* @param width
* @param isHorizontal
* @param join
* @param pointType
* @param type
*
*/
public AlgoBarChart(Construction cons, String label, GeoList list1,
GeoList list2, GeoNumberValue width, GeoBoolean isHorizontal,
GeoBoolean join, GeoNumeric pointType, int type) {
this(cons, list1, list2, width, isHorizontal, join, pointType, type);
sum.setLabel(label);
}
private GeoNumeric scale;
/******************************************************
* General constructor
*
* @param cons
* @param list1
* @param list2
* @param width
* @param isHorizontal
* @param join
* @param showStepJump
* @param showPoints
* @param pointType
* @param type
*/
public AlgoBarChart(Construction cons, GeoList list1, GeoList list2,
GeoNumberValue width, GeoBoolean isHorizontal, GeoBoolean join,
GeoNumeric pointType, int type) {
this(cons, list1, list2, width, isHorizontal, join, pointType, null,
type);
}
/******************************************************
* General constructor
*
* @param cons
* @param list1
* @param list2
* @param width
* @param isHorizontal
* @param join
* @param showStepJump
* @param showPoints
* @param pointType
* @param scale
* @param type
*/
public AlgoBarChart(Construction cons, GeoList list1, GeoList list2,
GeoNumberValue width, GeoBoolean isHorizontal, GeoBoolean join,
GeoNumeric pointType, GeoNumeric scale, int type) {
super(cons);
this.type = type;
this.list1 = list1;
this.list2 = list2;
if (width != null) {
widthGeo = width.toGeoElement();
}
this.isHorizontal = isHorizontal;
this.hasJoin = join;
this.pointType = pointType;
this.scale = scale;
sum = new GeoNumeric(cons) {
@Override
public String getTooltipText(final boolean colored,
final boolean alwaysOn) {
return toolTipText;
}
};
setInputOutput(); // for AlgoElement
compute();
sum.setDrawable(true);
}
/******************************************************
* Discrete distribution bar chart
*
* @param cons
* @param label
* @param p1
* @param p2
* @param p3
* @param isCumulative
* @param type
*/
public AlgoBarChart(Construction cons, String label, GeoNumberValue p1,
GeoNumberValue p2, GeoNumberValue p3, GeoBoolean isCumulative,
int type) {
super(cons);
this.type = type;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
p1geo = p1.toGeoElement();
if (p2 != null) {
p2geo = p2.toGeoElement();
}
if (p3 != null) {
p3geo = p3.toGeoElement();
}
this.isCumulative = isCumulative;
sum = new GeoNumeric(cons) {
@Override
public String getTooltipText(final boolean colored,
final boolean alwaysOn) {
return toolTipText;
}
};
setInputOutput(); // for AlgoElement
compute();
sum.setDrawable(true);
sum.setLabel(label);
if (yval == null) {
yval = new double[0];
leftBorder = new double[0];
}
}
/**
* @param cons
* @param p1
* @param p2
* @param p3
* @param isCumulative
* @param type
* @param a
* @param b
* @param vals
* @param borders
* @param N
*/
protected AlgoBarChart(GeoNumberValue p1, GeoNumberValue p2,
GeoNumberValue p3, GeoBoolean isCumulative, int type,
GeoNumberValue a, GeoNumberValue b, double[] vals, double[] borders,
int N) {
super(p1.getConstruction(), false);
this.type = type;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
p1geo = p1.toGeoElement();
if (p2 != null) {
p2geo = p2.toGeoElement();
}
if (p3 != null) {
p3geo = p3.toGeoElement();
}
this.isCumulative = isCumulative;
this.a = a;
this.b = b;
this.yval = vals;
this.leftBorder = borders;
this.N = N;
}
// ==================================================
// Copy constructors
// ==================================================
private AlgoBarChart(Construction cons, GeoNumberValue a, GeoNumberValue b,
double[] vals, double[] borders, int N) {
super(cons, false);
type = TYPE_BARCHART_EXPRESSION;
this.a = a;
this.b = b;
this.yval = vals;
this.leftBorder = borders;
this.N = N;
}
private AlgoBarChart(Construction cons, GeoNumeric width, double[] vals,
double[] borders, int N) {
super(cons, false);
type = TYPE_BARCHART_RAWDATA;
this.widthGeo = width;
this.yval = vals;
this.leftBorder = borders;
this.N = N;
}
private AlgoBarChart(Construction cons, double[] vals, double[] borders,
int N) {
super(cons, false);
type = TYPE_BARCHART_FREQUENCY_TABLE;
this.yval = vals;
this.leftBorder = borders;
this.N = N;
}
private AlgoBarChart(Construction cons, GeoNumberValue width, double[] vals,
double[] borders, int N) {
super(cons, false);
type = TYPE_BARCHART_FREQUENCY_TABLE_WIDTH;
widthGeo = width.toGeoElement();
this.yval = vals;
this.leftBorder = borders;
this.N = N;
}
// ======================================================
// InputOutput
// ======================================================
// for AlgoElement
@Override
@SuppressFBWarnings({ "SF_SWITCH_FALLTHROUGH",
"missing break is deliberate" })
protected void setInputOutput() {
ArrayList<GeoElement> list = new ArrayList<GeoElement>();
switch (type) {
default:
// do nothing
break;
case TYPE_BARCHART_EXPRESSION:
input = new GeoElement[3];
input[0] = ageo;
input[1] = bgeo;
input[2] = list1;
break;
case TYPE_BARCHART_RAWDATA:
createHelperAlgos(list1, scale);
// fall through
case TYPE_BARCHART_FREQUENCY_TABLE:
case TYPE_BARCHART_FREQUENCY_TABLE_WIDTH:
list.add(list1);
if (list2 != null) {
list.add(list2);
}
if (widthGeo != null) {
list.add(widthGeo);
}
if (scale != null) {
list.add(scale);
}
input = new GeoElement[list.size()];
input = list.toArray(input);
break;
case TYPE_STICKGRAPH:
list.add(list1);
if (list2 != null) {
list.add(list2);
}
if (isHorizontal != null) {
list.add(isHorizontal);
}
input = new GeoElement[list.size()];
input = list.toArray(input);
break;
case TYPE_STEPGRAPH:
list.add(list1);
if (list2 != null) {
list.add(list2);
}
if (hasJoin != null) {
list.add(hasJoin);
}
if (pointType != null) {
list.add(pointType);
}
input = new GeoElement[list.size()];
input = list.toArray(input);
break;
case TYPE_BARCHART_BERNOULLI:
case TYPE_BARCHART_BINOMIAL:
case TYPE_BARCHART_PASCAL:
case TYPE_BARCHART_HYPERGEOMETRIC:
case TYPE_BARCHART_POISSON:
case TYPE_BARCHART_ZIPF:
ArrayList<GeoElement> inputList = new ArrayList<GeoElement>();
inputList.add(p1geo);
if (p2geo != null) {
inputList.add(p2geo);
}
if (p3geo != null) {
inputList.add(p3geo);
}
if (isCumulative != null) {
inputList.add(isCumulative);
}
input = new GeoElement[inputList.size()];
input = inputList.toArray(input);
break;
}
setOutputLength(1);
setOutput(0, sum);
setDependencies(); // done by AlgoElement
}
// ======================================================
// Getters/Setters
// ======================================================
@Override
public Commands getClassName() {
return Commands.BarChart;
}
/**
* @return the resulting sum
*/
public GeoNumeric getSum() {
return sum;
}
/**
* @return the isCumulative
*/
public GeoElement getIsCumulative() {
return isCumulative;
}
/**
* @return maximum frequency of a bar chart
*/
public double getFreqMax() {
freqMax = 0.0;
for (int k = 0; k < yval.length; ++k) {
freqMax = Math.max(yval[k], freqMax);
}
return freqMax;
}
/**
* @return y values (heights) of a bar chart
*/
public double[] getYValue() {
return yval;
}
/**
* @return left class borders of a bar chart
*/
public double[] getLeftBorder() {
return leftBorder;
}
/**
* @return values of a bar chart formatted as string (for frequency tables)
*/
public String[] getValue() {
return value;
}
/**
* @return type of the bar chart
*/
public int getType() {
return type;
}
/**
* @return lower bound for sums
*/
public GeoNumberValue getA() {
return a == null ? new GeoNumeric(cons, Double.NaN) : a;
}
/**
* @return upper bound for sums
*/
public GeoNumberValue getB() {
return b == null ? new GeoNumeric(cons, Double.NaN) : b;
}
/**
* @return list of function values
*/
public double[] getValues() {
return yval;
}
/**
* number of intervals
*
* @return number of intervals
*/
public int getIntervals() {
return N;
}
/**
* @return bar width
*/
public double getWidth() {
return barWidth;
}
/**
* @return discrete graph parameter p1
*/
public NumberValue getP1() {
return p1;
}
/**
* @return discrete graph parameter p2
*/
public GeoNumberValue getP2() {
return p2;
}
/**
* @return discrete graph parameter p3
*/
public GeoNumberValue getP3() {
return p3;
}
/**
* @return the type of graph to draw
*/
public DrawType getDrawType() {
// case 1: step graphs
if (type == TYPE_STEPGRAPH) {
if ((hasJoin != null && ((GeoBoolean) hasJoin).getBoolean())) {
return DrawType.STEP_GRAPH_CONTINUOUS;
}
return DrawType.STEP_GRAPH_JUMP;
}
// case 2: cumulative discrete probability
else if (isCumulative != null
&& ((GeoBoolean) isCumulative).getBoolean()) {
return DrawType.STEP_GRAPH_CONTINUOUS;
// case 3: all other types use either horizontal or vertical bars
} else if (isHorizontal != null
&& ((GeoBoolean) isHorizontal).getBoolean()) {
return DrawType.HORIZONTAL_BAR;
} else {
return DrawType.VERTICAL_BAR;
}
}
/**
* @return true if points are drawn with the graph
*/
public boolean hasPoints() {
return (type == TYPE_STICKGRAPH || type == TYPE_STEPGRAPH);
}
/**
* @return point style
*/
public int getPointType() {
if (type == TYPE_STICKGRAPH) {
return DrawBarGraph.POINT_LEFT;
}
if (pointType == null) {
return DrawBarGraph.POINT_NONE;
}
int p = (int) ((GeoNumeric) pointType).getDouble();
if (p < -2 || p > 2) {
p = DrawBarGraph.POINT_NONE;
}
return p;
}
// ======================================================
// Compute
// ======================================================
@Override
public void compute() {
isAreaSum = true;
switch (type) {
default:
// do nothing
break;
case TYPE_BARCHART_FREQUENCY_TABLE:
case TYPE_BARCHART_FREQUENCY_TABLE_WIDTH:
computeWithFrequency();
break;
case TYPE_STICKGRAPH:
case TYPE_STEPGRAPH:
isAreaSum = false;
if (list1 == null || !list1.isDefined()) {
sum.setUndefined();
return;
}
if (list1.getGeoElementForPropertiesDialog().isGeoPoint()) {
computeFromPointList(list1);
} else {
if (list2 == null) {
sum.setUndefined();
return;
}
barWidth = 0.0;
computeFromValueFrequencyLists(list1, list2);
}
break;
case TYPE_BARCHART_EXPRESSION:
computeWithExp();
break;
case TYPE_BARCHART_RAWDATA:
computeWithRawData();
break;
case TYPE_BARCHART_BINOMIAL:
case TYPE_BARCHART_POISSON:
case TYPE_BARCHART_HYPERGEOMETRIC:
case TYPE_BARCHART_PASCAL:
case TYPE_BARCHART_ZIPF:
if (!prepareDistributionLists()) {
sum.setUndefined();
return;
}
barWidth = -1;
computeWithFrequency();
break;
}
}
public void computeWithExp() {
GeoElement geo; // temporary var
if (!(ageo.isDefined() && bgeo.isDefined() && list1.isDefined())) {
sum.setUndefined();
return;
}
N = list1.size();
double ad = a.getDouble();
double bd = b.getDouble();
double ints = list1.size();
if (ints < 1) {
sum.setUndefined();
return;
} else if (ints > MAX_RECTANGLES) {
N = MAX_RECTANGLES;
} else {
N = (int) Math.round(ints);
}
barWidth = (bd - ad) / N;
if (yval == null || yval.length < N) {
yval = new double[N];
leftBorder = new double[N];
}
value = new String[N];
double ySum = 0;
for (int i = 0; i < N; i++) {
leftBorder[i] = ad + i * barWidth;
geo = list1.get(i);
if (geo.isGeoNumeric()) {
yval[i] = ((GeoNumeric) geo).getDouble();
} else {
yval[i] = 0;
}
value[i] = kernel.format(ad + i * barWidth / 2,
StringTemplate.defaultTemplate);
ySum += yval[i];
}
// calc area of rectangles
sum.setValue(ySum * barWidth);
dataSize = ySum;
}
public void computeWithRawData() {
if (widthGeo == null || !widthGeo.isDefined()) {
sum.setUndefined();
return;
}
barWidth = ((GeoNumeric) widthGeo).getDouble();
if (barWidth < 0) {
sum.setUndefined();
return;
}
computeFromValueFrequencyLists(algoFreq.getValue(),
algoFreq.getResult());
}
public void computeWithFrequency() {
if (list1 == null || !list1.isDefined()) {
sum.setUndefined();
return;
}
if (!list2.isDefined() || list1.size() == 0
|| list1.size() != list2.size()) {
sum.setUndefined();
return;
}
if (list1.size() == 0 || list1.size() != list2.size()) {
sum.setUndefined();
return;
}
if (type == TYPE_BARCHART_FREQUENCY_TABLE_WIDTH) {
if (widthGeo == null || !widthGeo.isDefined()) {
sum.setUndefined();
return;
}
barWidth = ((GeoNumeric) widthGeo).getDouble();
if (barWidth < 0) {
sum.setUndefined();
return;
}
} else {
barWidth = -1;
}
computeFromValueFrequencyLists(list1, list2);
}
private void computeFromValueFrequencyLists(GeoList xList, GeoList yList) {
if (barWidth < 0) {
if (xList.size() > 1) {
double x1, x2;
if (xList.get(1).isGeoNumeric()) {
x1 = xList.get(0).evaluateDouble();
x2 = xList.get(1).evaluateDouble();
} else {
// use integers 1,2,3 ... for non-numeric data
x1 = 1;
x2 = 2;
}
if (!Double.isNaN(x1) && !Double.isNaN(x2)) {
barWidth = x2 - x1;
} else {
sum.setUndefined();
return;
}
} else {
barWidth = 0.5;
}
}
N = xList.size();
if (yval == null || yval.length < N) {
yval = new double[N];
leftBorder = new double[N];
}
value = new String[N];
for (int i = 0; i < N; i++) {
value[i] = xList.get(i)
.toValueString(StringTemplate.defaultTemplate);
}
double ySum = 0;
double x = 0;
if (yList.size() < N) {
sum.setUndefined();
return;
}
for (int i = 0; i < N; i++) {
if (xList.get(i).isGeoNumeric()) {
x = xList.get(i).evaluateDouble();
} else {
// use integers 1,2,3 ... to position non-numeric data
x = i + 1;
}
if (!Double.isNaN(x)) {
leftBorder[i] = x - barWidth / 2;
} else {
sum.setUndefined();
return;
}
// frequencies
double y = yList.get(i).evaluateDouble();
if (!Double.isNaN(y)) {
yval[i] = y;
ySum += y;
} else {
sum.setUndefined();
return;
}
}
// set the sum
if (isAreaSum) {
// sum = total area
sum.setValue(Math.abs(ySum) * barWidth);
} else {
// sum = total length
sum.setValue(Math.abs(ySum));
}
dataSize = ySum;
}
/**
* Computes stick or step graph from a list of points
*
* @param list1
*/
private void computeFromPointList(GeoList list1) {
N = list1.size();
if (yval == null || yval.length < N) {
yval = new double[N];
leftBorder = new double[N];
}
value = new String[N];
double ySum = 0;
for (int i = 0; i < N; i++) {
GeoElement geo = list1.get(i);
Coords coords = ((GeoPointND) geo).getCoordsInD3();
double x = coords.getX();
if (!Double.isNaN(x)) {
leftBorder[i] = x - barWidth / 2;
} else {
sum.setUndefined();
return;
}
value[i] = kernel.format(x, StringTemplate.defaultTemplate);
double y = coords.getY();
if (!Double.isNaN(y)) {
yval[i] = y;
ySum += y;
} else {
sum.setUndefined();
return;
}
}
// sum = total length
sum.setValue(ySum);
}
// ======================================================
// Probability Distributions
// ======================================================
/**
* Prepares list1 and list2 for use with probability distribution bar charts
*/
private boolean prepareDistributionLists() {
IntegerDistribution dist = null;
int first = 0, last = 0;
try {
// get the distribution and the first, last list values for given
// distribution type
switch (type) {
default:
// do nothing
break;
case TYPE_BARCHART_BINOMIAL:
if (!(p1geo.isDefined() && p2geo.isDefined())) {
return false;
}
int n = (int) Math.round(p1.getDouble());
double p = p2.getDouble();
dist = new BinomialDistribution(n, p);
first = 0;
last = n;
break;
case TYPE_BARCHART_PASCAL:
if (!(p1geo.isDefined() && p2geo.isDefined())) {
return false;
}
n = (int) Math.round(p1.getDouble());
p = p2.getDouble();
dist = new PascalDistribution(n, p);
first = 0;
last = (int) Math.max(1, (kernel).getXmax() + 1);
break;
case TYPE_BARCHART_ZIPF:
if (!(p1geo.isDefined() && p2geo.isDefined())) {
return false;
}
n = (int) Math.round(p1.getDouble());
p = p2.getDouble();
dist = new ZipfDistribution(n, p);
first = 0;
last = n;
break;
case TYPE_BARCHART_POISSON:
if (!p1geo.isDefined()) {
return false;
}
double lambda = p1.getDouble();
dist = new PoissonDistribution(lambda);
first = 0;
last = (int) Math.max(1, kernel.getXmax() + 1);
break;
case TYPE_BARCHART_HYPERGEOMETRIC:
if (!(p1geo.isDefined() && p2geo.isDefined()
&& p3geo.isDefined())) {
return false;
}
int pop = (int) p1.getDouble();
int successes = (int) p2.getDouble();
int sample = (int) p3.getDouble();
dist = new HypergeometricDistribution(pop, successes,
sample);
first = Math.max(0, successes + sample - pop);
last = Math.min(successes, sample);
break;
}
// load class list and probability list
loadDistributionLists(first, last, dist);
}
catch (Exception e) {
Log.debug(e.getMessage());
return false;
}
return true;
}
/**
* Utility method, creates and loads list1 and list2 with classes and
* probabilities for the probability distribution bar charts
*/
private void loadDistributionLists(int first, int last,
IntegerDistribution dist) throws Exception {
boolean oldSuppress = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
if (list1 == null) {
list1 = new GeoList(cons);
} else {
list1.clear();
}
if (list2 == null) {
list2 = new GeoList(cons);
} else {
list2.clear();
}
double prob;
double cumProb = 0;
for (int i = first; i <= last; i++) {
list1.addNumber(i, this);
prob = dist.probability(i);
cumProb += prob;
if (isCumulative != null
&& ((GeoBoolean) isCumulative).getBoolean()) {
list2.addNumber(cumProb, this);
} else {
list2.addNumber(prob, this);
}
}
cons.setSuppressLabelCreation(oldSuppress);
}
// ======================================================
// Copy
// ======================================================
@Override
public DrawInformationAlgo copy() {
int N1 = this.getIntervals();
switch (this.getType()) {
case TYPE_BARCHART_EXPRESSION:
return new AlgoBarChart(cons,
(GeoNumberValue) getA().deepCopy(kernel),
(GeoNumberValue) getB().deepCopy(kernel),
Cloner.clone(getValues()), Cloner.clone(getLeftBorder()),
N1);
case TYPE_BARCHART_FREQUENCY_TABLE:
return new AlgoBarChart(kernel.getConstruction(),
Cloner.clone(getValues()), Cloner.clone(getLeftBorder()),
N1);
case TYPE_BARCHART_FREQUENCY_TABLE_WIDTH:
return new AlgoBarChart(cons,
(GeoNumberValue) getA().deepCopy(kernel),
Cloner.clone(getValues()), Cloner.clone(getLeftBorder()),
N1);
default: // TYPE_BARCHART_RAWDATA
return new AlgoBarChart(cons,
(GeoNumberValue) widthGeo.deepCopy(kernel),
Cloner.clone(getValues()), Cloner.clone(getLeftBorder()),
N1);
}
}
@Override
public void remove() {
super.remove();
if (isProtectedInput()) {
return;
}
removeHelperAlgos();
}
public void setBarColor(GColor color, int numBar) {
if (color == null) {
if (tags.containsKey(numBar)) {
tags.get(numBar).remove(0);
}
return;
}
if (tags.containsKey(numBar)) {
tags.get(numBar).put(0, color);
} else {
HashMap<Integer, Object> hm = new HashMap<Integer, Object>();
hm.put(0, color);
tags.put(numBar, hm);
}
}
public GColor getBarColor(int numBar) {
HashMap<Integer, Object> hm = tags.get(numBar);
if (hm != null) {
return (GColor) hm.get(0);
}
return null;
}
public void setBarAlpha(double alpha, int numBar) {
if (alpha == -1) {
if (tags.containsKey(numBar)) {
tags.get(numBar).remove(1);
}
return;
}
if (tags.containsKey(numBar)) {
tags.get(numBar).put(1, alpha);
} else {
HashMap<Integer, Object> hm = new HashMap<Integer, Object>();
hm.put(1, alpha);
tags.put(numBar, hm);
}
}
/**
*
* @param numBar
* bar number
* @return -1 if not set, otherwise alpha (between 0 and 1)
*/
public double getBarAlpha(int numBar) {
HashMap<Integer, Object> hm = tags.get(numBar);
if (hm != null && hm.get(1) != null) {
return ((Double) hm.get(1)).doubleValue();
}
return -1;
}
public void setBarFillType(FillType fillType, int numBar) {
if (tags.containsKey(numBar)) {
tags.get(numBar).put(2, fillType);
} else {
HashMap<Integer, Object> hm = new HashMap<Integer, Object>();
hm.put(2, fillType);
tags.put(numBar, hm);
}
}
public FillType getBarFillType(int numBar) {
return getBarFillType(numBar, FillType.STANDARD);
}
public FillType getBarFillType(int numBar, FillType fallback) {
HashMap<Integer, Object> hm = tags.get(numBar);
if (hm != null) {
if (hm.get(2) == null) {
return fallback;
}
return (FillType) hm.get(2);
}
return fallback;
}
public void setBarSymbol(String symbol, int numBar) {
if (symbol == null) {
if (tags.containsKey(numBar)) {
tags.get(numBar).remove(3);
}
return;
}
if (tags.containsKey(numBar)) {
tags.get(numBar).put(3, symbol);
} else {
HashMap<Integer, Object> hm = new HashMap<Integer, Object>();
hm.put(3, symbol);
tags.put(numBar, hm);
}
}
public String getBarSymbol(int numBar) {
HashMap<Integer, Object> hm = tags.get(numBar);
if (hm != null) {
return (String) hm.get(3);
}
return null;
}
public void setBarImage(String image, int numBar) {
if (image == null) {
if (tags.containsKey(numBar)) {
tags.get(numBar).remove(4);
}
return;
}
if (tags.containsKey(numBar)) {
tags.get(numBar).put(4, image);
} else {
HashMap<Integer, Object> hm = new HashMap<Integer, Object>();
hm.put(4, image);
tags.put(numBar, hm);
}
}
public String getBarImage(int numBar) {
HashMap<Integer, Object> hm = tags.get(numBar);
if (hm != null) {
return (String) hm.get(4);
}
return null;
}
public void setBarHatchDistance(int distance, int numBar) {
if (distance == -1) {
if (tags.containsKey(numBar)) {
tags.get(numBar).remove(5);
}
return;
}
if (tags.containsKey(numBar)) {
tags.get(numBar).put(5, distance);
} else {
HashMap<Integer, Object> hm = new HashMap<Integer, Object>();
hm.put(5, distance);
tags.put(numBar, hm);
}
}
public int getBarHatchDistance(int numBar) {
HashMap<Integer, Object> hm = tags.get(numBar);
if (hm != null && hm.get(5) != null) {
return ((Integer) hm.get(5)).intValue();
}
return -1;
}
public void setBarHatchAngle(int angle, int numBar) {
if (angle == -1) {
if (tags.containsKey(numBar)) {
tags.get(numBar).remove(6);
}
return;
}
if (tags.containsKey(numBar)) {
tags.get(numBar).put(6, angle);
} else {
HashMap<Integer, Object> hm = new HashMap<Integer, Object>();
hm.put(6, angle);
tags.put(numBar, hm);
}
}
public int getBarHatchAngle(int numBar) {
HashMap<Integer, Object> hm = tags.get(numBar);
if (hm != null && hm.get(6) != null) {
return ((Integer) hm.get(6)).intValue();
}
return -1;
}
public void barXml(StringBuilder sb) {
sb.append("\t<tags>\n");
for (int i = 1; i <= N; i++) {
if (getBarColor(i) != null) {
sb.append("\t\t<tag key=\"barColor\"");
sb.append(" barNumber=\"");
sb.append(i);
sb.append("\" value=\"");
sb.append(GColor.getColorString(getBarColor(i)));
sb.append("\" />\n");
}
double barAlpha = getBarAlpha(i);
if (barAlpha != -1) {
sb.append("\t\t<tag key=\"barAlpha\" barNumber=\"");
sb.append(i);
sb.append("\" value=\"");
sb.append(barAlpha);
sb.append("\"/>\n");
}
if (getBarHatchDistance(i) != -1) {
sb.append("\t\t<tag key=\"barHatchDistance\" barNumber=\"");
sb.append(i);
sb.append("\" value=\"");
sb.append(getBarHatchDistance(i)
);
sb.append("\"/>\n");
}
if (getBarHatchAngle(i) != -1) {
sb.append("\t\t<tag key=\"barHatchAngle\" barNumber=\"");
sb.append(i);
sb.append("\" value=\"");
sb.append(getBarHatchAngle(i));
sb.append("\"/>\n");
}
if (getBarFillType(i) != FillType.STANDARD) {
sb.append("\t\t<tag key=\"barFillType\" barNumber=\"");
sb.append(i);
sb.append("\" value=\"");
sb.append(getBarFillType(i).ordinal());
sb.append("\"/>\n");
}
if (getBarImage(i) != null) {
sb.append("\t\t<tag key=\"barImage\" barNumber=\"");
sb.append(i);
sb.append("\" value=\"");
sb.append(getBarImage(i));
sb.append("\"/>\n");
}
if (getBarSymbol(i) != null) {
sb.append("\t\t<tag key=\"barSymbol\" barNumber=\"");
sb.append(i);
sb.append("\" value=\"");
sb.append(getBarSymbol(i));
sb.append("\"/>\n");
}
}
sb.append("\t</tags>\n");
}
public void setToolTipText(int index) {
int freq = (int) yval[index];
double percent = 100 * freq / dataSize;
StringBuilder sb = new StringBuilder();
sb.append(getLoc().getMenu("Value"));
sb.append(" = ");
sb.append(value[index]);
sb.append("<br>");
sb.append(getLoc().getMenu("Count"));
sb.append(" = ");
sb.append(kernel.format(freq, StringTemplate.defaultTemplate));
sb.append("<br>");
sb.append(kernel.format(percent, StringTemplate.defaultTemplate));
sb.append("%");
toolTipText = sb.toString();
}
}