/*
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 org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.arithmetic.NumberValue;
import org.geogebra.common.kernel.geos.GeoAngle;
import org.geogebra.common.kernel.geos.GeoAngle.AngleStyle;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoList;
import org.geogebra.common.kernel.geos.GeoNumeric;
/**
* Mean, variance, sum, sum of squares, standard deviation of a list adapted
* from AlgoListMin to replace AlgoMean, AlgoSum
*
* @author Michael Borcherds
* @version 2008-02-18
*/
public abstract class AlgoStats1D extends AlgoElement {
private GeoList geoList, geoList2; // input
private GeoNumeric Truncate; // input
private GeoNumeric result; // output
private int stat;
protected final static int STATS_MEAN = 0;
protected final static int STATS_VARIANCE = 1;
protected final static int STATS_SIGMAX = 2;
protected final static int STATS_SIGMAXX = 3;
protected final static int STATS_SD = 4;
protected final static int STATS_PRODUCT = 5;
protected final static int STATS_SXX = 6;
protected final static int STATS_SAMPLE_VARIANCE = 7;
protected final static int STATS_SAMPLE_SD = 8;
public AlgoStats1D(Construction cons, String label, GeoList geoList,
int stat) {
this(cons, label, geoList, null, null, stat);
}
protected AlgoStats1D(Construction cons, String label, GeoList geoList,
GeoNumeric Truncate, int stat) {
this(cons, geoList, null, Truncate, stat);
result.setLabel(label);
}
public AlgoStats1D(Construction cons, GeoList geoList, int stat) {
this(cons, geoList, null, null, stat);
}
public AlgoStats1D(Construction cons, String label, GeoList geoList,
GeoList geoList2, int stat) {
this(cons, label, geoList, geoList2, null, stat);
}
protected AlgoStats1D(Construction cons, String label, GeoList geoList,
GeoList geoList2, GeoNumeric Truncate, int stat) {
this(cons, geoList, geoList2, Truncate, stat);
result.setLabel(label);
}
public AlgoStats1D(Construction cons, GeoList geoList, GeoList geoList2,
int stat) {
this(cons, geoList, geoList2, null, stat);
}
/**
* @param cons
* @param geoList
* @param geoList2
* @param Truncate
* @param stat
*/
protected AlgoStats1D(Construction cons, GeoList geoList, GeoList geoList2,
GeoNumeric Truncate, int stat) {
super(cons);
this.geoList = geoList;
this.geoList2 = geoList2;
this.stat = stat;
this.Truncate = Truncate;
if (geoList.size() > 0 && geoList.get(0).isAngle()) {
result = new GeoAngle(cons);
// allow unbounded angles (from ggb44). This could break old files
// (unlikely)
((GeoAngle) result).setAngleStyle(AngleStyle.UNBOUNDED);
} else {
result = new GeoNumeric(cons);
}
setInputOutput();
compute();
}
@Override
protected void setInputOutput() {
ArrayList<GeoElement> inputList = new ArrayList<GeoElement>();
inputList.add(geoList);
if (geoList2 != null) {
inputList.add(geoList2);
}
if (Truncate != null) {
inputList.add(Truncate);
}
input = new GeoElement[inputList.size()];
inputList.toArray(input);
inputList.clear();
setOnlyOutput(result);
setDependencies(); // done by AlgoElement
}
public GeoNumeric getResult() {
return result;
}
@Override
public final void compute() {
// Application.debug("compute: " + geoList);
if (!geoList.isDefined()) {
result.setUndefined();
return;
}
if (geoList2 != null) {
if (!geoList2.isDefined()
// return undefined if we can't use number * freq or
// midpoint * freq
|| !(geoList.size() == geoList2.size()
|| geoList.size() == geoList2.size() + 1)) {
result.setUndefined();
return;
}
}
// eg SigmaXX[{1, 2, 3}, {2, 4, 8}]
// Sum[{1, 2, 3}, {2, 4, 8}]
// Product[{1, 2, 3}, {2, 4, 8}]
int truncate;
double size = geoList.size();
if (Truncate != null) {
if (!Truncate.isDefined()) {
// this change is done for AlgoProduct,
// and is probably useful for AlgoSum and other algos too
result.setUndefined();
return;
}
truncate = (int) Truncate.getDouble();
if (truncate == 0) {
result.setValue(0);
return;
}
if (truncate < 1 || truncate > size) {
result.setUndefined();
return;
}
size = truncate; // truncate the list
}
if (size == 0) {
switch (stat) {
case STATS_SIGMAX:
case STATS_SIGMAXX:
result.setValue(0);
return;
case STATS_PRODUCT:
result.setValue(1);
return;
default:
result.setUndefined();
return;
}
}
double sumVal = 0;
double sumSquares = 0;
double product = 1;
double sumFreq = 0;
double frequency = 1;
double var, mu;
GeoElement geo, geoFreq, geo2;
// list of numbers only, no frequencies
if (geoList2 == null) {
double val;
for (int i = 0; i < size; i++) {
geo = geoList.get(i);
if (geo instanceof NumberValue) {
val = ((NumberValue) geo).getDouble();
sumVal += val;
sumSquares += val * val;
product *= val;
} else {
result.setUndefined();
return;
}
}
}
// list of numbers with list of frequencies
else {
// if the number list is a list of classes, then we must use a
// midpoint
boolean useMidpoint = geoList.size() == geoList2.size() + 1;
size = useMidpoint ? size - 1 : size;
double val;
double val_by_freq;
for (int i = 0; i < size; i++) {
geo = geoList.get(i);
geoFreq = geoList2.get(i);
if (!(geo instanceof NumberValue)
|| !(geoFreq instanceof NumberValue)) {
result.setUndefined();
return;
}
val = ((NumberValue) geo).getDouble();
// compute midpoint value if needed
if (useMidpoint) {
geo2 = geoList.get(i + 1);
if (!(geo2 instanceof NumberValue)) {
result.setUndefined();
return;
}
val = (val + (((NumberValue) geo2).getDouble())) / 2;
}
frequency = ((NumberValue) geoFreq).getDouble();
// handle bad frequency
if (frequency < 0) {
result.setUndefined();
return;
}
val_by_freq = val * frequency;
sumVal += val_by_freq;
sumSquares += val * val_by_freq;
sumFreq += frequency;
product *= Math.pow(val, frequency);
}
size = sumFreq;
}
mu = sumVal / size;
switch (stat) {
default:
result.setValue(Double.NaN);
break;
case STATS_MEAN:
result.setValue(mu);
break;
case STATS_SD:
var = sumSquares / size - mu * mu;
result.setValue(Math.sqrt(var));
break;
case STATS_SAMPLE_SD:
var = (sumSquares - sumVal * sumVal / size) / (size - 1);
result.setValue(Math.sqrt(var));
break;
case STATS_VARIANCE:
var = sumSquares / size - mu * mu;
result.setValue(var);
break;
case STATS_SAMPLE_VARIANCE:
var = (sumSquares - sumVal * sumVal / size) / (size - 1);
result.setValue(var);
break;
case STATS_SXX:
var = sumSquares - (sumVal * sumVal) / size;
result.setValue(var);
break;
case STATS_SIGMAX:
result.setValue(sumVal);
break;
case STATS_SIGMAXX:
result.setValue(sumSquares);
break;
case STATS_PRODUCT:
result.setValue(product);
break;
}
}
}