http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/DataPoint.java
----------------------------------------------------------------------
diff --git a/samoa-api/src/main/java/org/apache/samoa/moa/core/DataPoint.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/DataPoint.java
new file mode 100644
index 0000000..e4e1c61
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/core/DataPoint.java
@@ -0,0 +1,133 @@
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+import org.apache.samoa.instances.Attribute;
+import org.apache.samoa.instances.DenseInstance;
+import org.apache.samoa.instances.Instance;
+
+public class DataPoint extends DenseInstance {
+
+  private static final long serialVersionUID = 1L;
+
+  protected int timestamp;
+  private HashMap<String, String> measure_values;
+
+  protected int noiseLabel;
+
+  public DataPoint(Instance nextInstance, Integer timestamp) {
+    super(nextInstance);
+    this.setDataset(nextInstance.dataset());
+    this.timestamp = timestamp;
+    measure_values = new HashMap<String, String>();
+
+    Attribute classLabel = dataset().classAttribute();
+    noiseLabel = classLabel.indexOfValue("noise"); // -1 returned if there is 
no noise
+  }
+
+  public void updateWeight(int cur_timestamp, double decay_rate) {
+    setWeight(Math.pow(2, (-1.0) * decay_rate * (cur_timestamp - timestamp)));
+  }
+
+  public void setMeasureValue(String measureKey, double value) {
+    synchronized (measure_values) {
+      measure_values.put(measureKey, Double.toString(value));
+    }
+  }
+
+  public void setMeasureValue(String measureKey, String value) {
+    synchronized (measure_values) {
+      measure_values.put(measureKey, value);
+    }
+  }
+
+  public String getMeasureValue(String measureKey) {
+    if (measure_values.containsKey(measureKey))
+      synchronized (measure_values) {
+        return measure_values.get(measureKey);
+      }
+    else
+      return "";
+  }
+
+  public int getTimestamp() {
+    return timestamp;
+  }
+
+  public String getInfo(int x_dim, int y_dim) {
+    StringBuffer sb = new StringBuffer();
+    sb.append("<html><table>");
+    sb.append("<tr><td>Point</td><td>" + timestamp + "</td></tr>");
+    for (int i = 0; i < numAttributes() - 1; i++) { // m_AttValues.length
+      String label = "Dim " + i;
+      if (i == x_dim)
+        label = "<b>X</b>";
+      if (i == y_dim)
+        label = "<b>Y</b>";
+      sb.append("<tr><td>" + label + "</td><td>" + value(i) + "</td></tr>");
+    }
+    sb.append("<tr><td>Decay</td><td>" + weight() + "</td></tr>");
+    sb.append("<tr><td>True cluster</td><td>" + classValue() + "</td></tr>");
+    sb.append("</table>");
+    sb.append("<br>");
+    sb.append("<b>Evaluation</b><br>");
+    sb.append("<table>");
+
+    TreeSet<String> sortedset;
+    synchronized (measure_values) {
+      sortedset = new TreeSet<String>(measure_values.keySet());
+    }
+
+    Iterator miterator = sortedset.iterator();
+    while (miterator.hasNext()) {
+      String key = (String) miterator.next();
+      sb.append("<tr><td>" + key + "</td><td>" + measure_values.get(key) + 
"</td></tr>");
+    }
+
+    sb.append("</table></html>");
+    return sb.toString();
+  }
+
+  public double getDistance(DataPoint other) {
+    double distance = 0.0;
+    int numDims = numAttributes();
+    if (classIndex() != 0)
+      numDims--;
+
+    for (int i = 0; i < numDims; i++) {
+      double d = value(i) - other.value(i);
+      distance += d * d;
+    }
+    return Math.sqrt(distance);
+  }
+
+  public boolean isNoise() {
+    return (int) classValue() == noiseLabel;
+  }
+
+  public double getNoiseLabel() {
+    return noiseLabel;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/DoubleVector.java
----------------------------------------------------------------------
diff --git 
a/samoa-api/src/main/java/org/apache/samoa/moa/core/DoubleVector.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/DoubleVector.java
new file mode 100644
index 0000000..fbfc268
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/core/DoubleVector.java
@@ -0,0 +1,195 @@
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import org.apache.samoa.moa.AbstractMOAObject;
+
+/**
+ * Vector of double numbers with some utilities.
+ * 
+ * @author Richard Kirkby ([email protected])
+ * @version $Revision: 7 $
+ */
+public class DoubleVector extends AbstractMOAObject {
+
+  private static final long serialVersionUID = 1L;
+
+  protected double[] array;
+
+  public DoubleVector() {
+    this.array = new double[0];
+  }
+
+  public DoubleVector(double[] toCopy) {
+    this.array = new double[toCopy.length];
+    System.arraycopy(toCopy, 0, this.array, 0, toCopy.length);
+  }
+
+  public DoubleVector(DoubleVector toCopy) {
+    this(toCopy.getArrayRef());
+  }
+
+  public int numValues() {
+    return this.array.length;
+  }
+
+  public void setValue(int i, double v) {
+    if (i >= this.array.length) {
+      setArrayLength(i + 1);
+    }
+    this.array[i] = v;
+  }
+
+  public void addToValue(int i, double v) {
+    if (i >= this.array.length) {
+      setArrayLength(i + 1);
+    }
+    this.array[i] += v;
+  }
+
+  public void addValues(DoubleVector toAdd) {
+    addValues(toAdd.getArrayRef());
+  }
+
+  public void addValues(double[] toAdd) {
+    if (toAdd.length > this.array.length) {
+      setArrayLength(toAdd.length);
+    }
+    for (int i = 0; i < toAdd.length; i++) {
+      this.array[i] += toAdd[i];
+    }
+  }
+
+  public void subtractValues(DoubleVector toSubtract) {
+    subtractValues(toSubtract.getArrayRef());
+  }
+
+  public void subtractValues(double[] toSubtract) {
+    if (toSubtract.length > this.array.length) {
+      setArrayLength(toSubtract.length);
+    }
+    for (int i = 0; i < toSubtract.length; i++) {
+      this.array[i] -= toSubtract[i];
+    }
+  }
+
+  public void addToValues(double toAdd) {
+    for (int i = 0; i < this.array.length; i++) {
+      this.array[i] = this.array[i] + toAdd;
+    }
+  }
+
+  public void scaleValues(double multiplier) {
+    for (int i = 0; i < this.array.length; i++) {
+      this.array[i] = this.array[i] * multiplier;
+    }
+  }
+
+  // returns 0.0 for values outside of range
+  public double getValue(int i) {
+    return ((i >= 0) && (i < this.array.length)) ? this.array[i] : 0.0;
+  }
+
+  public double sumOfValues() {
+    double sum = 0.0;
+    for (double element : this.array) {
+      sum += element;
+    }
+    return sum;
+  }
+
+  public int maxIndex() {
+    int max = -1;
+    for (int i = 0; i < this.array.length; i++) {
+      if ((max < 0) || (this.array[i] > this.array[max])) {
+        max = i;
+      }
+    }
+    return max;
+  }
+
+  public void normalize() {
+    scaleValues(1.0 / sumOfValues());
+  }
+
+  public int numNonZeroEntries() {
+    int count = 0;
+    for (double element : this.array) {
+      if (element != 0.0) {
+        count++;
+      }
+    }
+    return count;
+  }
+
+  public double minWeight() {
+    if (this.array.length > 0) {
+      double min = this.array[0];
+      for (int i = 1; i < this.array.length; i++) {
+        if (this.array[i] < min) {
+          min = this.array[i];
+        }
+      }
+      return min;
+    }
+    return 0.0;
+  }
+
+  public double[] getArrayCopy() {
+    double[] aCopy = new double[this.array.length];
+    System.arraycopy(this.array, 0, aCopy, 0, this.array.length);
+    return aCopy;
+  }
+
+  public double[] getArrayRef() {
+    return this.array;
+  }
+
+  protected void setArrayLength(int l) {
+    double[] newArray = new double[l];
+    int numToCopy = this.array.length;
+    if (numToCopy > l) {
+      numToCopy = l;
+    }
+    System.arraycopy(this.array, 0, newArray, 0, numToCopy);
+    this.array = newArray;
+  }
+
+  public void getSingleLineDescription(StringBuilder out) {
+    getSingleLineDescription(out, numValues());
+  }
+
+  public void getSingleLineDescription(StringBuilder out, int numValues) {
+    out.append("{");
+    for (int i = 0; i < numValues; i++) {
+      if (i > 0) {
+        out.append("|");
+      }
+      out.append(StringUtils.doubleToString(getValue(i), 3));
+    }
+    out.append("}");
+  }
+
+  @Override
+  public void getDescription(StringBuilder sb, int indent) {
+    getSingleLineDescription(sb);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/Example.java
----------------------------------------------------------------------
diff --git a/samoa-api/src/main/java/org/apache/samoa/moa/core/Example.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/Example.java
new file mode 100644
index 0000000..96f9d21
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/core/Example.java
@@ -0,0 +1,30 @@
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+public interface Example<T extends Object> {
+
+  public T getData();
+
+  public double weight();
+
+  public void setWeight(double weight);
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/FastVector.java
----------------------------------------------------------------------
diff --git a/samoa-api/src/main/java/org/apache/samoa/moa/core/FastVector.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/FastVector.java
new file mode 100644
index 0000000..adbdceb
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/core/FastVector.java
@@ -0,0 +1,68 @@
+/*
+ *    FastVector.java
+
+ *
+ */
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import java.util.ArrayList;
+
+/**
+ * Simple extension of ArrayList. Exists for legacy reasons.
+ * 
+ * @author Eibe Frank ([email protected])
+ * @version $Revision: 8034 $
+ */
+public class FastVector<E> extends ArrayList<E> {
+
+  /**
+   * Adds an element to this vector. Increases its capacity if its not large 
enough.
+   * 
+   * @param element
+   *          the element to add
+   */
+  public final void addElement(E element) {
+    add(element);
+  }
+
+  /**
+   * Returns the element at the given position.
+   * 
+   * @param index
+   *          the element's index
+   * @return the element with the given index
+   */
+  public final E elementAt(int index) {
+    return get(index);
+  }
+
+  /**
+   * Deletes an element from this vector.
+   * 
+   * @param index
+   *          the index of the element to be deleted
+   */
+  public final void removeElementAt(int index) {
+    remove(index);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/GaussianEstimator.java
----------------------------------------------------------------------
diff --git 
a/samoa-api/src/main/java/org/apache/samoa/moa/core/GaussianEstimator.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/GaussianEstimator.java
new file mode 100644
index 0000000..ac3f958
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/core/GaussianEstimator.java
@@ -0,0 +1,125 @@
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import org.apache.samoa.moa.AbstractMOAObject;
+
+/**
+ * Gaussian incremental estimator that uses incremental method that is more 
resistant to floating point imprecision. for
+ * more info see Donald Knuth's "The Art of Computer Programming, Volume 2: 
Seminumerical Algorithms", section 4.2.2.
+ * 
+ * @author Richard Kirkby ([email protected])
+ * @version $Revision: 7 $
+ */
+public class GaussianEstimator extends AbstractMOAObject {
+
+  private static final long serialVersionUID = 1L;
+
+  protected double weightSum;
+
+  protected double mean;
+
+  protected double varianceSum;
+
+  public static final double NORMAL_CONSTANT = Math.sqrt(2 * Math.PI);
+
+  public void addObservation(double value, double weight) {
+    if (Double.isInfinite(value) || Double.isNaN(value)) {
+      return;
+    }
+    if (this.weightSum > 0.0) {
+      this.weightSum += weight;
+      double lastMean = this.mean;
+      this.mean += weight * (value - lastMean) / this.weightSum;
+      this.varianceSum += weight * (value - lastMean) * (value - this.mean);
+    } else {
+      this.mean = value;
+      this.weightSum = weight;
+    }
+  }
+
+  public void addObservations(GaussianEstimator obs) {
+    // Follows Variance Combination Rule in Section 2 of
+    // Brian Babcock, Mayur Datar, Rajeev Motwani, Liadan O'Callaghan:
+    // Maintaining variance and k-medians over data stream windows. PODS 2003:
+    // 234-243
+    //
+    if ((this.weightSum >= 0.0) && (obs.weightSum > 0.0)) {
+      double oldMean = this.mean;
+      this.mean = (this.mean * (this.weightSum / (this.weightSum + 
obs.weightSum)))
+          + (obs.mean * (obs.weightSum / (this.weightSum + obs.weightSum)));
+      this.varianceSum += obs.varianceSum + (this.weightSum * obs.weightSum / 
(this.weightSum + obs.weightSum) *
+          Math.pow(obs.mean - oldMean, 2));
+      this.weightSum += obs.weightSum;
+    }
+  }
+
+  public double getTotalWeightObserved() {
+    return this.weightSum;
+  }
+
+  public double getMean() {
+    return this.mean;
+  }
+
+  public double getStdDev() {
+    return Math.sqrt(getVariance());
+  }
+
+  public double getVariance() {
+    return this.weightSum > 1.0 ? this.varianceSum / (this.weightSum - 1.0)
+        : 0.0;
+  }
+
+  public double probabilityDensity(double value) {
+    if (this.weightSum > 0.0) {
+      double stdDev = getStdDev();
+      if (stdDev > 0.0) {
+        double diff = value - getMean();
+        return (1.0 / (NORMAL_CONSTANT * stdDev))
+            * Math.exp(-(diff * diff / (2.0 * stdDev * stdDev)));
+      }
+      return value == getMean() ? 1.0 : 0.0;
+    }
+    return 0.0;
+  }
+
+  public double[] estimatedWeight_LessThan_EqualTo_GreaterThan_Value(
+      double value) {
+    double equalToWeight = probabilityDensity(value) * this.weightSum;
+    double stdDev = getStdDev();
+    double lessThanWeight = stdDev > 0.0 ? org.apache.samoa.moa.core.Statistics
+        .normalProbability((value - getMean()) / stdDev)
+        * this.weightSum - equalToWeight
+        : (value < getMean() ? this.weightSum - equalToWeight : 0.0);
+    double greaterThanWeight = this.weightSum - equalToWeight
+        - lessThanWeight;
+    if (greaterThanWeight < 0.0) {
+      greaterThanWeight = 0.0;
+    }
+    return new double[] { lessThanWeight, equalToWeight, greaterThanWeight };
+  }
+
+  @Override
+  public void getDescription(StringBuilder sb, int indent) {
+    // TODO Auto-generated method stub
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/GreenwaldKhannaQuantileSummary.java
----------------------------------------------------------------------
diff --git 
a/samoa-api/src/main/java/org/apache/samoa/moa/core/GreenwaldKhannaQuantileSummary.java
 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/GreenwaldKhannaQuantileSummary.java
new file mode 100644
index 0000000..346c390
--- /dev/null
+++ 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/GreenwaldKhannaQuantileSummary.java
@@ -0,0 +1,281 @@
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import org.apache.samoa.moa.AbstractMOAObject;
+
+/**
+ * Class for representing summaries of Greenwald and Khanna quantiles.
+ * 
+ * @author Richard Kirkby ([email protected])
+ * @version $Revision: 7 $
+ */
+public class GreenwaldKhannaQuantileSummary extends AbstractMOAObject {
+
+  private static final long serialVersionUID = 1L;
+
+  protected static class Tuple implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public double v;
+
+    public long g;
+
+    public long delta;
+
+    public Tuple(double v, long g, long delta) {
+      this.v = v;
+      this.g = g;
+      this.delta = delta;
+    }
+
+    public Tuple(double v) {
+      this(v, 1, 0);
+    }
+  }
+
+  protected Tuple[] summary;
+
+  protected int numTuples = 0;
+
+  protected long numObservations = 0;
+
+  public GreenwaldKhannaQuantileSummary(int maxTuples) {
+    this.summary = new Tuple[maxTuples];
+  }
+
+  public void insert(double val) {
+    int i = findIndexOfTupleGreaterThan(val);
+    Tuple nextT = this.summary[i];
+    if (nextT == null) {
+      insertTuple(new Tuple(val, 1, 0), i);
+    } else {
+      insertTuple(new Tuple(val, 1, nextT.g + nextT.delta - 1), i);
+    }
+    if (this.numTuples == this.summary.length) {
+      // use method 1
+      deleteMergeableTupleMostFull();
+      // if (mergeMethod == 1) {
+      // deleteMergeableTupleMostFull();
+      // } else if (mergeMethod == 2) {
+      // deleteTupleMostFull();
+      // } else {
+      // long maxDelta = findMaxDelta();
+      // compress(maxDelta);
+      // while (numTuples == summary.length) {
+      // maxDelta++;
+      // compress(maxDelta);
+      // }
+      // }
+    }
+    this.numObservations++;
+  }
+
+  protected void insertTuple(Tuple t, int index) {
+    System.arraycopy(this.summary, index, this.summary, index + 1,
+        this.numTuples - index);
+    this.summary[index] = t;
+    this.numTuples++;
+  }
+
+  protected void deleteTuple(int index) {
+    this.summary[index] = new Tuple(this.summary[index + 1].v,
+        this.summary[index].g + this.summary[index + 1].g,
+        this.summary[index + 1].delta);
+    System.arraycopy(this.summary, index + 2, this.summary, index + 1,
+        this.numTuples - index - 2);
+    this.summary[this.numTuples - 1] = null;
+    this.numTuples--;
+  }
+
+  protected void deleteTupleMostFull() {
+    long leastFullness = Long.MAX_VALUE;
+    int leastFullIndex = 0;
+    for (int i = 1; i < this.numTuples - 1; i++) {
+      long fullness = this.summary[i].g + this.summary[i + 1].g
+          + this.summary[i + 1].delta;
+      if (fullness < leastFullness) {
+        leastFullness = fullness;
+        leastFullIndex = i;
+      }
+    }
+    if (leastFullIndex > 0) {
+      deleteTuple(leastFullIndex);
+    }
+  }
+
+  protected void deleteMergeableTupleMostFull() {
+    long leastFullness = Long.MAX_VALUE;
+    int leastFullIndex = 0;
+    for (int i = 1; i < this.numTuples - 1; i++) {
+      long fullness = this.summary[i].g + this.summary[i + 1].g
+          + this.summary[i + 1].delta;
+      if ((this.summary[i].delta >= this.summary[i + 1].delta)
+          && (fullness < leastFullness)) {
+        leastFullness = fullness;
+        leastFullIndex = i;
+      }
+    }
+    if (leastFullIndex > 0) {
+      deleteTuple(leastFullIndex);
+    }
+  }
+
+  public long getWorstError() {
+    long mostFullness = 0;
+    for (int i = 1; i < this.numTuples - 1; i++) {
+      long fullness = this.summary[i].g + this.summary[i].delta;
+      if (fullness > mostFullness) {
+        mostFullness = fullness;
+      }
+    }
+    return mostFullness;
+  }
+
+  public long findMaxDelta() {
+    long maxDelta = 0;
+    for (int i = 0; i < this.numTuples; i++) {
+      if (this.summary[i].delta > maxDelta) {
+        maxDelta = this.summary[i].delta;
+      }
+    }
+    return maxDelta;
+  }
+
+  public void compress(long maxDelta) {
+    long[] bandBoundaries = computeBandBoundaries(maxDelta);
+    for (int i = this.numTuples - 2; i >= 0; i--) {
+      if (this.summary[i].delta >= this.summary[i + 1].delta) {
+        int band = 0;
+        while (this.summary[i].delta < bandBoundaries[band]) {
+          band++;
+        }
+        long belowBandThreshold = Long.MAX_VALUE;
+        if (band > 0) {
+          belowBandThreshold = bandBoundaries[band - 1];
+        }
+        long mergeG = this.summary[i + 1].g + this.summary[i].g;
+        int childI = i - 1;
+        while (((mergeG + this.summary[i + 1].delta) < maxDelta)
+            && (childI >= 0)
+            && (this.summary[childI].delta >= belowBandThreshold)) {
+          mergeG += this.summary[childI].g;
+          childI--;
+        }
+        if (mergeG + this.summary[i + 1].delta < maxDelta) {
+          // merge
+          int numDeleted = i - childI;
+          this.summary[childI + 1] = new Tuple(this.summary[i + 1].v,
+              mergeG, this.summary[i + 1].delta);
+          // todo complete & test this multiple delete
+          System.arraycopy(this.summary, i + 2, this.summary,
+              childI + 2, this.numTuples - (i + 2));
+          for (int j = this.numTuples - numDeleted; j < this.numTuples; j++) {
+            this.summary[j] = null;
+          }
+          this.numTuples -= numDeleted;
+          i = childI + 1;
+        }
+      }
+    }
+  }
+
+  public double getQuantile(double quant) {
+    long r = (long) Math.ceil(quant * this.numObservations);
+    long currRank = 0;
+    for (int i = 0; i < this.numTuples - 1; i++) {
+      currRank += this.summary[i].g;
+      if (currRank + this.summary[i + 1].g > r) {
+        return this.summary[i].v;
+      }
+    }
+    return this.summary[this.numTuples - 1].v;
+  }
+
+  public long getTotalCount() {
+    return this.numObservations;
+  }
+
+  public double getPropotionBelow(double cutpoint) {
+    return (double) getCountBelow(cutpoint) / (double) this.numObservations;
+  }
+
+  public long getCountBelow(double cutpoint) {
+    long rank = 0;
+    for (int i = 0; i < this.numTuples; i++) {
+      if (this.summary[i].v > cutpoint) {
+        break;
+      }
+      rank += this.summary[i].g;
+    }
+    return rank;
+  }
+
+  public double[] getSuggestedCutpoints() {
+    double[] cutpoints = new double[this.numTuples];
+    for (int i = 0; i < this.numTuples; i++) {
+      cutpoints[i] = this.summary[i].v;
+    }
+    return cutpoints;
+  }
+
+  protected int findIndexOfTupleGreaterThan(double val) {
+    int high = this.numTuples, low = -1, probe;
+    while (high - low > 1) {
+      probe = (high + low) / 2;
+      if (this.summary[probe].v > val) {
+        high = probe;
+      } else {
+        low = probe;
+      }
+    }
+    return high;
+  }
+
+  public static long[] computeBandBoundaries(long maxDelta) {
+    ArrayList<Long> boundaryList = new ArrayList<Long>();
+    boundaryList.add(new Long(maxDelta));
+    int alpha = 1;
+    while (true) {
+      long boundary = (maxDelta - (2 << (alpha - 1)) - (maxDelta % (2 << 
(alpha - 1))));
+      if (boundary >= 0) {
+        boundaryList.add(new Long(boundary + 1));
+      } else {
+        break;
+      }
+      alpha++;
+    }
+    boundaryList.add(new Long(0));
+    long[] boundaries = new long[boundaryList.size()];
+    for (int i = 0; i < boundaries.length; i++) {
+      boundaries[i] = boundaryList.get(i).longValue();
+    }
+    return boundaries;
+  }
+
+  public void getDescription(StringBuilder sb, int indent) {
+    // TODO Auto-generated method stub
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/InputStreamProgressMonitor.java
----------------------------------------------------------------------
diff --git 
a/samoa-api/src/main/java/org/apache/samoa/moa/core/InputStreamProgressMonitor.java
 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/InputStreamProgressMonitor.java
new file mode 100644
index 0000000..33d55d0
--- /dev/null
+++ 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/InputStreamProgressMonitor.java
@@ -0,0 +1,131 @@
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+
+/**
+ * Class for monitoring the progress of reading an input stream.
+ * 
+ * @author Richard Kirkby ([email protected])
+ * @version $Revision: 7 $
+ */
+public class InputStreamProgressMonitor extends FilterInputStream implements 
Serializable {
+
+  /** The number of bytes to read in total */
+  protected int inputByteSize;
+
+  /** The number of bytes read so far */
+  protected int inputBytesRead;
+
+  public InputStreamProgressMonitor(InputStream in) {
+    super(in);
+    try {
+      this.inputByteSize = in.available();
+    } catch (IOException ioe) {
+      this.inputByteSize = 0;
+    }
+    this.inputBytesRead = 0;
+  }
+
+  public int getBytesRead() {
+    return this.inputBytesRead;
+  }
+
+  public int getBytesRemaining() {
+    return this.inputByteSize - this.inputBytesRead;
+  }
+
+  public double getProgressFraction() {
+    return ((double) this.inputBytesRead / (double) this.inputByteSize);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.io.InputStream#read()
+   */
+  @Override
+  public int read() throws IOException {
+    int c = this.in.read();
+    if (c > 0) {
+      this.inputBytesRead++;
+    }
+    return c;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.io.InputStream#read(byte[])
+   */
+  @Override
+  public int read(byte[] b) throws IOException {
+    int numread = this.in.read(b);
+    if (numread > 0) {
+      this.inputBytesRead += numread;
+    }
+    return numread;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.io.InputStream#read(byte[], int, int)
+   */
+  @Override
+  public int read(byte[] b, int off, int len) throws IOException {
+    int numread = this.in.read(b, off, len);
+    if (numread > 0) {
+      this.inputBytesRead += numread;
+    }
+    return numread;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.io.InputStream#skip(long)
+   */
+  @Override
+  public long skip(long n) throws IOException {
+    long numskip = this.in.skip(n);
+    if (numskip > 0) {
+      this.inputBytesRead += numskip;
+    }
+    return numskip;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.io.FilterInputStream#reset()
+   */
+  @Override
+  public synchronized void reset() throws IOException {
+    this.in.reset();
+    this.inputBytesRead = this.inputByteSize - this.in.available();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/InstanceExample.java
----------------------------------------------------------------------
diff --git 
a/samoa-api/src/main/java/org/apache/samoa/moa/core/InstanceExample.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/InstanceExample.java
new file mode 100644
index 0000000..3ae8985
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/core/InstanceExample.java
@@ -0,0 +1,51 @@
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import java.io.Serializable;
+
+import org.apache.samoa.instances.Instance;
+
+public class InstanceExample implements Example<Instance>, Serializable {
+
+  public Instance instance;
+
+  public InstanceExample(Instance inst)
+  {
+    this.instance = inst;
+  }
+
+  @Override
+  public Instance getData() {
+    return this.instance;
+  }
+
+  @Override
+  public double weight() {
+    return this.instance.weight();
+  }
+
+  @Override
+  public void setWeight(double w) {
+    this.instance.setWeight(w);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/Measurement.java
----------------------------------------------------------------------
diff --git a/samoa-api/src/main/java/org/apache/samoa/moa/core/Measurement.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/Measurement.java
new file mode 100644
index 0000000..8fc80c7
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/core/Measurement.java
@@ -0,0 +1,115 @@
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.samoa.moa.AbstractMOAObject;
+
+/**
+ * Class for storing an evaluation measurement.
+ * 
+ * @author Richard Kirkby ([email protected])
+ * @version $Revision: 7 $
+ */
+public class Measurement extends AbstractMOAObject {
+
+  private static final long serialVersionUID = 1L;
+
+  protected String name;
+
+  protected double value;
+
+  public Measurement(String name, double value) {
+    this.name = name;
+    this.value = value;
+  }
+
+  public String getName() {
+    return this.name;
+  }
+
+  public double getValue() {
+    return this.value;
+  }
+
+  public static Measurement getMeasurementNamed(String name,
+      Measurement[] measurements) {
+    for (Measurement measurement : measurements) {
+      if (name.equals(measurement.getName())) {
+        return measurement;
+      }
+    }
+    return null;
+  }
+
+  public static void getMeasurementsDescription(Measurement[] measurements,
+      StringBuilder out, int indent) {
+    if (measurements.length > 0) {
+      StringUtils.appendIndented(out, indent, measurements[0].toString());
+      for (int i = 1; i < measurements.length; i++) {
+        StringUtils.appendNewlineIndented(out, indent, 
measurements[i].toString());
+      }
+
+    }
+  }
+
+  public static Measurement[] averageMeasurements(Measurement[][] toAverage) {
+    List<String> measurementNames = new ArrayList<String>();
+    for (Measurement[] measurements : toAverage) {
+      for (Measurement measurement : measurements) {
+        if (measurementNames.indexOf(measurement.getName()) < 0) {
+          measurementNames.add(measurement.getName());
+        }
+      }
+    }
+    GaussianEstimator[] estimators = new 
GaussianEstimator[measurementNames.size()];
+    for (int i = 0; i < estimators.length; i++) {
+      estimators[i] = new GaussianEstimator();
+    }
+    for (Measurement[] measurements : toAverage) {
+      for (Measurement measurement : measurements) {
+        
estimators[measurementNames.indexOf(measurement.getName())].addObservation(measurement.getValue(),
 1.0);
+      }
+    }
+    List<Measurement> averagedMeasurements = new ArrayList<Measurement>();
+    for (int i = 0; i < measurementNames.size(); i++) {
+      String mName = measurementNames.get(i);
+      GaussianEstimator mEstimator = estimators[i];
+      if (mEstimator.getTotalWeightObserved() > 1.0) {
+        averagedMeasurements.add(new Measurement("[avg] " + mName,
+            mEstimator.getMean()));
+        averagedMeasurements.add(new Measurement("[err] " + mName,
+            mEstimator.getStdDev()
+                / Math.sqrt(mEstimator.getTotalWeightObserved())));
+      }
+    }
+    return averagedMeasurements.toArray(new 
Measurement[averagedMeasurements.size()]);
+  }
+
+  @Override
+  public void getDescription(StringBuilder sb, int indent) {
+    sb.append(getName());
+    sb.append(" = ");
+    sb.append(StringUtils.doubleToString(getValue(), 3));
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/MiscUtils.java
----------------------------------------------------------------------
diff --git a/samoa-api/src/main/java/org/apache/samoa/moa/core/MiscUtils.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/MiscUtils.java
new file mode 100644
index 0000000..89f1146
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/core/MiscUtils.java
@@ -0,0 +1,96 @@
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Random;
+
+/**
+ * Class implementing some utility methods.
+ * 
+ * @author Richard Kirkby ([email protected])
+ * @author Bernhard Pfahringer ([email protected])
+ * @version $Revision: 7 $
+ */
+public class MiscUtils {
+
+  public static int chooseRandomIndexBasedOnWeights(double[] weights,
+      Random random) {
+    double probSum = Utils.sum(weights);
+    double val = random.nextDouble() * probSum;
+    int index = 0;
+    double sum = 0.0;
+    while ((sum <= val) && (index < weights.length)) {
+      sum += weights[index++];
+    }
+    return index - 1;
+  }
+
+  public static int poisson(double lambda, Random r) {
+    if (lambda < 100.0) {
+      double product = 1.0;
+      double sum = 1.0;
+      double threshold = r.nextDouble() * Math.exp(lambda);
+      int i = 1;
+      int max = Math.max(100, 10 * (int) Math.ceil(lambda));
+      while ((i < max) && (sum <= threshold)) {
+        product *= (lambda / i);
+        sum += product;
+        i++;
+      }
+      return i - 1;
+    }
+    double x = lambda + Math.sqrt(lambda) * r.nextGaussian();
+    if (x < 0.0) {
+      return 0;
+    }
+    return (int) Math.floor(x);
+  }
+
+  public static String getStackTraceString(Exception ex) {
+    StringWriter stackTraceWriter = new StringWriter();
+    ex.printStackTrace(new PrintWriter(stackTraceWriter));
+    return "*** STACK TRACE ***\n" + stackTraceWriter.toString();
+  }
+
+  /**
+   * Returns index of maximum element in a given array of doubles. First 
maximum is returned.
+   * 
+   * @param doubles
+   *          the array of doubles
+   * @return the index of the maximum element
+   */
+  public static/* @pure@ */int maxIndex(double[] doubles) {
+
+    double maximum = 0;
+    int maxIndex = 0;
+
+    for (int i = 0; i < doubles.length; i++) {
+      if ((i == 0) || (doubles[i] > maximum)) {
+        maxIndex = i;
+        maximum = doubles[i];
+      }
+    }
+
+    return maxIndex;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/ObjectRepository.java
----------------------------------------------------------------------
diff --git 
a/samoa-api/src/main/java/org/apache/samoa/moa/core/ObjectRepository.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/ObjectRepository.java
new file mode 100644
index 0000000..092c0f8
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/core/ObjectRepository.java
@@ -0,0 +1,32 @@
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+/**
+ * Interface for object repositories.
+ * 
+ * @author Richard Kirkby ([email protected])
+ * @version $Revision: 7 $
+ */
+public interface ObjectRepository {
+
+  Object getObjectNamed(String string);
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/SerializeUtils.java
----------------------------------------------------------------------
diff --git 
a/samoa-api/src/main/java/org/apache/samoa/moa/core/SerializeUtils.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/SerializeUtils.java
new file mode 100644
index 0000000..ce884aa
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/core/SerializeUtils.java
@@ -0,0 +1,112 @@
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * Class implementing some serialize utility methods.
+ * 
+ * @author Richard Kirkby ([email protected])
+ * @version $Revision: 7 $
+ */
+public class SerializeUtils {
+
+  public static class ByteCountingOutputStream extends OutputStream {
+
+    protected int numBytesWritten = 0;
+
+    public int getNumBytesWritten() {
+      return this.numBytesWritten;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+      this.numBytesWritten++;
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+      this.numBytesWritten += len;
+    }
+
+    @Override
+    public void write(byte[] b) throws IOException {
+      this.numBytesWritten += b.length;
+    }
+  }
+
+  public static void writeToFile(File file, Serializable obj)
+      throws IOException {
+    ObjectOutputStream out = new ObjectOutputStream(new GZIPOutputStream(
+        new BufferedOutputStream(new FileOutputStream(file))));
+    out.writeObject(obj);
+    out.flush();
+    out.close();
+  }
+
+  public static Object readFromFile(File file) throws IOException,
+      ClassNotFoundException {
+    ObjectInputStream in = new ObjectInputStream(new GZIPInputStream(
+        new BufferedInputStream(new FileInputStream(file))));
+    Object obj = in.readObject();
+    in.close();
+    return obj;
+  }
+
+  public static Object copyObject(Serializable obj) throws Exception {
+    ByteArrayOutputStream baoStream = new ByteArrayOutputStream();
+    ObjectOutputStream out = new ObjectOutputStream(
+        new BufferedOutputStream(baoStream));
+    out.writeObject(obj);
+    out.flush();
+    out.close();
+    byte[] byteArray = baoStream.toByteArray();
+    ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(
+        new ByteArrayInputStream(byteArray)));
+    Object copy = in.readObject();
+    in.close();
+    return copy;
+  }
+
+  public static int measureObjectByteSize(Serializable obj) throws Exception {
+    ByteCountingOutputStream bcoStream = new ByteCountingOutputStream();
+    ObjectOutputStream out = new ObjectOutputStream(
+        new BufferedOutputStream(bcoStream));
+    out.writeObject(obj);
+    out.flush();
+    out.close();
+    return bcoStream.getNumBytesWritten();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/Statistics.java
----------------------------------------------------------------------
diff --git a/samoa-api/src/main/java/org/apache/samoa/moa/core/Statistics.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/Statistics.java
new file mode 100644
index 0000000..dde10b8
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/core/Statistics.java
@@ -0,0 +1,1107 @@
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+public class Statistics {
+
+  /** Some constants */
+  protected static final double MACHEP = 1.11022302462515654042E-16;
+  protected static final double MAXLOG = 7.09782712893383996732E2;
+  protected static final double MINLOG = -7.451332191019412076235E2;
+  protected static final double MAXGAM = 171.624376956302725;
+  protected static final double SQTPI = 2.50662827463100050242E0;
+  protected static final double SQRTH = 7.07106781186547524401E-1;
+  protected static final double LOGPI = 1.14472988584940017414;
+
+  protected static final double big = 4.503599627370496e15;
+  protected static final double biginv = 2.22044604925031308085e-16;
+
+  /*************************************************
+   * COEFFICIENTS FOR METHOD normalInverse() *
+   *************************************************/
+  /* approximation for 0 <= |y - 0.5| <= 3/8 */
+  protected static final double P0[] = {
+      -5.99633501014107895267E1,
+      9.80010754185999661536E1,
+      -5.66762857469070293439E1,
+      1.39312609387279679503E1,
+      -1.23916583867381258016E0,
+  };
+  protected static final double Q0[] = {
+      /* 1.00000000000000000000E0, */
+      1.95448858338141759834E0,
+      4.67627912898881538453E0,
+      8.63602421390890590575E1,
+      -2.25462687854119370527E2,
+      2.00260212380060660359E2,
+      -8.20372256168333339912E1,
+      1.59056225126211695515E1,
+      -1.18331621121330003142E0,
+  };
+
+  /*
+   * Approximation for interval z = sqrt(-2 log y ) between 2 and 8 i.e., y
+   * between exp(-2) = .135 and exp(-32) = 1.27e-14.
+   */
+  protected static final double P1[] = {
+      4.05544892305962419923E0,
+      3.15251094599893866154E1,
+      5.71628192246421288162E1,
+      4.40805073893200834700E1,
+      1.46849561928858024014E1,
+      2.18663306850790267539E0,
+      -1.40256079171354495875E-1,
+      -3.50424626827848203418E-2,
+      -8.57456785154685413611E-4,
+  };
+  protected static final double Q1[] = {
+      /* 1.00000000000000000000E0, */
+      1.57799883256466749731E1,
+      4.53907635128879210584E1,
+      4.13172038254672030440E1,
+      1.50425385692907503408E1,
+      2.50464946208309415979E0,
+      -1.42182922854787788574E-1,
+      -3.80806407691578277194E-2,
+      -9.33259480895457427372E-4,
+  };
+
+  /*
+   * Approximation for interval z = sqrt(-2 log y ) between 8 and 64 i.e., y
+   * between exp(-32) = 1.27e-14 and exp(-2048) = 3.67e-890.
+   */
+  protected static final double P2[] = {
+      3.23774891776946035970E0,
+      6.91522889068984211695E0,
+      3.93881025292474443415E0,
+      1.33303460815807542389E0,
+      2.01485389549179081538E-1,
+      1.23716634817820021358E-2,
+      3.01581553508235416007E-4,
+      2.65806974686737550832E-6,
+      6.23974539184983293730E-9,
+  };
+  protected static final double Q2[] = {
+      /* 1.00000000000000000000E0, */
+      6.02427039364742014255E0,
+      3.67983563856160859403E0,
+      1.37702099489081330271E0,
+      2.16236993594496635890E-1,
+      1.34204006088543189037E-2,
+      3.28014464682127739104E-4,
+      2.89247864745380683936E-6,
+      6.79019408009981274425E-9,
+  };
+
+  /**
+   * Computes standard error for observed values of a binomial random variable.
+   * 
+   * @param p
+   *          the probability of success
+   * @param n
+   *          the size of the sample
+   * @return the standard error
+   */
+  public static double binomialStandardError(double p, int n) {
+
+    if (n == 0) {
+      return 0;
+    }
+    return Math.sqrt((p * (1 - p)) / (double) n);
+  }
+
+  /**
+   * Returns chi-squared probability for given value and degrees of freedom. 
(The probability that the chi-squared
+   * variate will be greater than x for the given degrees of freedom.)
+   * 
+   * @param x
+   *          the value
+   * @param v
+   *          the number of degrees of freedom
+   * @return the chi-squared probability
+   */
+  public static double chiSquaredProbability(double x, double v) {
+
+    if (x < 0.0 || v < 1.0)
+      return 0.0;
+    return incompleteGammaComplement(v / 2.0, x / 2.0);
+  }
+
+  /**
+   * Computes probability of F-ratio.
+   * 
+   * @param F
+   *          the F-ratio
+   * @param df1
+   *          the first number of degrees of freedom
+   * @param df2
+   *          the second number of degrees of freedom
+   * @return the probability of the F-ratio.
+   */
+  public static double FProbability(double F, int df1, int df2) {
+
+    return incompleteBeta(df2 / 2.0, df1 / 2.0, df2 / (df2 + df1 * F));
+  }
+
+  /**
+   * Returns the area under the Normal (Gaussian) probability density 
function, integrated from minus infinity to
+   * <tt>x</tt> (assumes mean is zero, variance is one).
+   * 
+   * <pre>
+   *                            x
+   *                             -
+   *                   1        | |          2
+   *  normal(x)  = ---------    |    exp( - t /2 ) dt
+   *               sqrt(2pi)  | |
+   *                           -
+   *                          -inf.
+   * 
+   *             =  ( 1 + erf(z) ) / 2
+   *             =  erfc(z) / 2
+   * </pre>
+   * 
+   * where <tt>z = x/sqrt(2)</tt>. Computation is via the functions 
<tt>errorFunction</tt> and
+   * <tt>errorFunctionComplement</tt>.
+   * 
+   * @param a
+   *          the z-value
+   * @return the probability of the z value according to the normal pdf
+   */
+  public static double normalProbability(double a) {
+
+    double x, y, z;
+
+    x = a * SQRTH;
+    z = Math.abs(x);
+
+    if (z < SQRTH)
+      y = 0.5 + 0.5 * errorFunction(x);
+    else {
+      y = 0.5 * errorFunctionComplemented(z);
+      if (x > 0)
+        y = 1.0 - y;
+    }
+    return y;
+  }
+
+  /**
+   * Returns the value, <tt>x</tt>, for which the area under the Normal 
(Gaussian) probability density function
+   * (integrated from minus infinity to <tt>x</tt>) is equal to the argument 
<tt>y</tt> (assumes mean is zero, variance
+   * is one).
+   * <p>
+   * For small arguments <tt>0 < y < exp(-2)</tt>, the program computes <tt>z 
= sqrt( -2.0 * log(y) )</tt>; then the
+   * approximation is <tt>x = z - log(z)/z  - (1/z) P(1/z) / Q(1/z)</tt>. 
There are two rational functions P/Q, one for
+   * <tt>0 < y < exp(-32)</tt> and the other for <tt>y</tt> up to 
<tt>exp(-2)</tt>. For larger arguments,
+   * <tt>w = y - 0.5</tt>, and <tt>x/sqrt(2pi) = w + w**3 
R(w**2)/S(w**2))</tt>.
+   * 
+   * @param y0
+   *          the area under the normal pdf
+   * @return the z-value
+   */
+  public static double normalInverse(double y0) {
+
+    double x, y, z, y2, x0, x1;
+    int code;
+
+    final double s2pi = Math.sqrt(2.0 * Math.PI);
+
+    if (y0 <= 0.0)
+      throw new IllegalArgumentException();
+    if (y0 >= 1.0)
+      throw new IllegalArgumentException();
+    code = 1;
+    y = y0;
+    if (y > (1.0 - 0.13533528323661269189)) { /* 0.135... = exp(-2) */
+      y = 1.0 - y;
+      code = 0;
+    }
+
+    if (y > 0.13533528323661269189) {
+      y = y - 0.5;
+      y2 = y * y;
+      x = y + y * (y2 * polevl(y2, P0, 4) / p1evl(y2, Q0, 8));
+      x = x * s2pi;
+      return (x);
+    }
+
+    x = Math.sqrt(-2.0 * Math.log(y));
+    x0 = x - Math.log(x) / x;
+
+    z = 1.0 / x;
+    if (x < 8.0) /* y > exp(-32) = 1.2664165549e-14 */
+      x1 = z * polevl(z, P1, 8) / p1evl(z, Q1, 8);
+    else
+      x1 = z * polevl(z, P2, 8) / p1evl(z, Q2, 8);
+    x = x0 - x1;
+    if (code != 0)
+      x = -x;
+    return (x);
+  }
+
+  /**
+   * Returns natural logarithm of gamma function.
+   * 
+   * @param x
+   *          the value
+   * @return natural logarithm of gamma function
+   */
+  public static double lnGamma(double x) {
+
+    double p, q, w, z;
+
+    double A[] = {
+        8.11614167470508450300E-4,
+        -5.95061904284301438324E-4,
+        7.93650340457716943945E-4,
+        -2.77777777730099687205E-3,
+        8.33333333333331927722E-2
+    };
+    double B[] = {
+        -1.37825152569120859100E3,
+        -3.88016315134637840924E4,
+        -3.31612992738871184744E5,
+        -1.16237097492762307383E6,
+        -1.72173700820839662146E6,
+        -8.53555664245765465627E5
+    };
+    double C[] = {
+        /* 1.00000000000000000000E0, */
+        -3.51815701436523470549E2,
+        -1.70642106651881159223E4,
+        -2.20528590553854454839E5,
+        -1.13933444367982507207E6,
+        -2.53252307177582951285E6,
+        -2.01889141433532773231E6
+    };
+
+    if (x < -34.0) {
+      q = -x;
+      w = lnGamma(q);
+      p = Math.floor(q);
+      if (p == q)
+        throw new ArithmeticException("lnGamma: Overflow");
+      z = q - p;
+      if (z > 0.5) {
+        p += 1.0;
+        z = p - q;
+      }
+      z = q * Math.sin(Math.PI * z);
+      if (z == 0.0)
+        throw new ArithmeticException("lnGamma: Overflow");
+      z = LOGPI - Math.log(z) - w;
+      return z;
+    }
+
+    if (x < 13.0) {
+      z = 1.0;
+      while (x >= 3.0) {
+        x -= 1.0;
+        z *= x;
+      }
+      while (x < 2.0) {
+        if (x == 0.0)
+          throw new ArithmeticException("lnGamma: Overflow");
+        z /= x;
+        x += 1.0;
+      }
+      if (z < 0.0)
+        z = -z;
+      if (x == 2.0)
+        return Math.log(z);
+      x -= 2.0;
+      p = x * polevl(x, B, 5) / p1evl(x, C, 6);
+      return (Math.log(z) + p);
+    }
+
+    if (x > 2.556348e305)
+      throw new ArithmeticException("lnGamma: Overflow");
+
+    q = (x - 0.5) * Math.log(x) - x + 0.91893853320467274178;
+
+    if (x > 1.0e8)
+      return (q);
+
+    p = 1.0 / (x * x);
+    if (x >= 1000.0)
+      q += ((7.9365079365079365079365e-4 * p
+          - 2.7777777777777777777778e-3) * p
+          + 0.0833333333333333333333) / x;
+    else
+      q += polevl(p, A, 4) / x;
+    return q;
+  }
+
+  /**
+   * Returns the error function of the normal distribution. The integral is
+   * 
+   * <pre>
+   *                           x 
+   *                            -
+   *                 2         | |          2
+   *   erf(x)  =  --------     |    exp( - t  ) dt.
+   *              sqrt(pi)   | |
+   *                          -
+   *                           0
+   * </pre>
+   * 
+   * <b>Implementation:</b> For <tt>0 <= |x| < 1, erf(x) = x * 
P4(x**2)/Q5(x**2)</tt>; otherwise
+   * <tt>erf(x) = 1 - erfc(x)</tt>.
+   * <p>
+   * Code adapted from the <A 
HREF="http://www.sci.usq.edu.au/staff/leighb/graph/Top.html";> Java 2D Graph 
Package
+   * 2.4</A>, which in turn is a port from the <A 
HREF="http://people.ne.mediaone.net/moshier/index.html#Cephes";>Cephes
+   * 2.2</A> Math Library (C).
+   * 
+   * @param a
+   *          the argument to the function.
+   */
+  public static double errorFunction(double x) {
+    double y, z;
+    final double T[] = {
+        9.60497373987051638749E0,
+        9.00260197203842689217E1,
+        2.23200534594684319226E3,
+        7.00332514112805075473E3,
+        5.55923013010394962768E4
+    };
+    final double U[] = {
+        // 1.00000000000000000000E0,
+        3.35617141647503099647E1,
+        5.21357949780152679795E2,
+        4.59432382970980127987E3,
+        2.26290000613890934246E4,
+        4.92673942608635921086E4
+    };
+
+    if (Math.abs(x) > 1.0)
+      return (1.0 - errorFunctionComplemented(x));
+    z = x * x;
+    y = x * polevl(z, T, 4) / p1evl(z, U, 5);
+    return y;
+  }
+
+  /**
+   * Returns the complementary Error function of the normal distribution.
+   * 
+   * <pre>
+   *  1 - erf(x) =
+   * 
+   *                           inf. 
+   *                             -
+   *                  2         | |          2
+   *   erfc(x)  =  --------     |    exp( - t  ) dt
+   *               sqrt(pi)   | |
+   *                           -
+   *                            x
+   * </pre>
+   * 
+   * <b>Implementation:</b> For small x, <tt>erfc(x) = 1 - erf(x)</tt>; 
otherwise rational approximations are computed.
+   * <p>
+   * Code adapted from the <A 
HREF="http://www.sci.usq.edu.au/staff/leighb/graph/Top.html";> Java 2D Graph 
Package
+   * 2.4</A>, which in turn is a port from the <A 
HREF="http://people.ne.mediaone.net/moshier/index.html#Cephes";>Cephes
+   * 2.2</A> Math Library (C).
+   * 
+   * @param a
+   *          the argument to the function.
+   */
+  public static double errorFunctionComplemented(double a) {
+    double x, y, z, p, q;
+
+    double P[] = {
+        2.46196981473530512524E-10,
+        5.64189564831068821977E-1,
+        7.46321056442269912687E0,
+        4.86371970985681366614E1,
+        1.96520832956077098242E2,
+        5.26445194995477358631E2,
+        9.34528527171957607540E2,
+        1.02755188689515710272E3,
+        5.57535335369399327526E2
+    };
+    double Q[] = {
+        // 1.0
+        1.32281951154744992508E1,
+        8.67072140885989742329E1,
+        3.54937778887819891062E2,
+        9.75708501743205489753E2,
+        1.82390916687909736289E3,
+        2.24633760818710981792E3,
+        1.65666309194161350182E3,
+        5.57535340817727675546E2
+    };
+
+    double R[] = {
+        5.64189583547755073984E-1,
+        1.27536670759978104416E0,
+        5.01905042251180477414E0,
+        6.16021097993053585195E0,
+        7.40974269950448939160E0,
+        2.97886665372100240670E0
+    };
+    double S[] = {
+        // 1.00000000000000000000E0,
+        2.26052863220117276590E0,
+        9.39603524938001434673E0,
+        1.20489539808096656605E1,
+        1.70814450747565897222E1,
+        9.60896809063285878198E0,
+        3.36907645100081516050E0
+    };
+
+    if (a < 0.0)
+      x = -a;
+    else
+      x = a;
+
+    if (x < 1.0)
+      return 1.0 - errorFunction(a);
+
+    z = -a * a;
+
+    if (z < -MAXLOG) {
+      if (a < 0)
+        return (2.0);
+      else
+        return (0.0);
+    }
+
+    z = Math.exp(z);
+
+    if (x < 8.0) {
+      p = polevl(x, P, 8);
+      q = p1evl(x, Q, 8);
+    } else {
+      p = polevl(x, R, 5);
+      q = p1evl(x, S, 6);
+    }
+
+    y = (z * p) / q;
+
+    if (a < 0)
+      y = 2.0 - y;
+
+    if (y == 0.0) {
+      if (a < 0)
+        return 2.0;
+      else
+        return (0.0);
+    }
+    return y;
+  }
+
+  /**
+   * Evaluates the given polynomial of degree <tt>N</tt> at <tt>x</tt>. 
Evaluates polynomial when coefficient of N is
+   * 1.0. Otherwise same as <tt>polevl()</tt>.
+   * 
+   * <pre>
+   *                     2          N
+   * y  =  C  + C x + C x  +...+ C x
+   *        0    1     2          N
+   * 
+   * Coefficients are stored in reverse order:
+   * 
+   * coef[0] = C  , ..., coef[N] = C  .
+   *            N                   0
+   * </pre>
+   * 
+   * The function <tt>p1evl()</tt> assumes that <tt>coef[N] = 1.0</tt> and is 
omitted from the array. Its calling
+   * arguments are otherwise the same as <tt>polevl()</tt>.
+   * <p>
+   * In the interest of speed, there are no checks for out of bounds 
arithmetic.
+   * 
+   * @param x
+   *          argument to the polynomial.
+   * @param coef
+   *          the coefficients of the polynomial.
+   * @param N
+   *          the degree of the polynomial.
+   */
+  public static double p1evl(double x, double coef[], int N) {
+
+    double ans;
+    ans = x + coef[0];
+
+    for (int i = 1; i < N; i++)
+      ans = ans * x + coef[i];
+
+    return ans;
+  }
+
+  /**
+   * Evaluates the given polynomial of degree <tt>N</tt> at <tt>x</tt>.
+   * 
+   * <pre>
+   *                     2          N
+   * y  =  C  + C x + C x  +...+ C x
+   *        0    1     2          N
+   * 
+   * Coefficients are stored in reverse order:
+   * 
+   * coef[0] = C  , ..., coef[N] = C  .
+   *            N                   0
+   * </pre>
+   * 
+   * In the interest of speed, there are no checks for out of bounds 
arithmetic.
+   * 
+   * @param x
+   *          argument to the polynomial.
+   * @param coef
+   *          the coefficients of the polynomial.
+   * @param N
+   *          the degree of the polynomial.
+   */
+  public static double polevl(double x, double coef[], int N) {
+
+    double ans;
+    ans = coef[0];
+
+    for (int i = 1; i <= N; i++)
+      ans = ans * x + coef[i];
+
+    return ans;
+  }
+
+  /**
+   * Returns the Incomplete Gamma function.
+   * 
+   * @param a
+   *          the parameter of the gamma distribution.
+   * @param x
+   *          the integration end point.
+   */
+  public static double incompleteGamma(double a, double x)
+  {
+
+    double ans, ax, c, r;
+
+    if (x <= 0 || a <= 0)
+      return 0.0;
+
+    if (x > 1.0 && x > a)
+      return 1.0 - incompleteGammaComplement(a, x);
+
+    /* Compute x**a * exp(-x) / gamma(a) */
+    ax = a * Math.log(x) - x - lnGamma(a);
+    if (ax < -MAXLOG)
+      return (0.0);
+
+    ax = Math.exp(ax);
+
+    /* power series */
+    r = a;
+    c = 1.0;
+    ans = 1.0;
+
+    do {
+      r += 1.0;
+      c *= x / r;
+      ans += c;
+    } while (c / ans > MACHEP);
+
+    return (ans * ax / a);
+  }
+
+  /**
+   * Returns the Complemented Incomplete Gamma function.
+   * 
+   * @param a
+   *          the parameter of the gamma distribution.
+   * @param x
+   *          the integration start point.
+   */
+  public static double incompleteGammaComplement(double a, double x) {
+
+    double ans, ax, c, yc, r, t, y, z;
+    double pk, pkm1, pkm2, qk, qkm1, qkm2;
+
+    if (x <= 0 || a <= 0)
+      return 1.0;
+
+    if (x < 1.0 || x < a)
+      return 1.0 - incompleteGamma(a, x);
+
+    ax = a * Math.log(x) - x - lnGamma(a);
+    if (ax < -MAXLOG)
+      return 0.0;
+
+    ax = Math.exp(ax);
+
+    /* continued fraction */
+    y = 1.0 - a;
+    z = x + y + 1.0;
+    c = 0.0;
+    pkm2 = 1.0;
+    qkm2 = x;
+    pkm1 = x + 1.0;
+    qkm1 = z * x;
+    ans = pkm1 / qkm1;
+
+    do {
+      c += 1.0;
+      y += 1.0;
+      z += 2.0;
+      yc = y * c;
+      pk = pkm1 * z - pkm2 * yc;
+      qk = qkm1 * z - qkm2 * yc;
+      if (qk != 0) {
+        r = pk / qk;
+        t = Math.abs((ans - r) / r);
+        ans = r;
+      } else
+        t = 1.0;
+
+      pkm2 = pkm1;
+      pkm1 = pk;
+      qkm2 = qkm1;
+      qkm1 = qk;
+      if (Math.abs(pk) > big) {
+        pkm2 *= biginv;
+        pkm1 *= biginv;
+        qkm2 *= biginv;
+        qkm1 *= biginv;
+      }
+    } while (t > MACHEP);
+
+    return ans * ax;
+  }
+
+  /**
+   * Returns the Gamma function of the argument.
+   */
+  public static double gamma(double x) {
+
+    double P[] = {
+        1.60119522476751861407E-4,
+        1.19135147006586384913E-3,
+        1.04213797561761569935E-2,
+        4.76367800457137231464E-2,
+        2.07448227648435975150E-1,
+        4.94214826801497100753E-1,
+        9.99999999999999996796E-1
+    };
+    double Q[] = {
+        -2.31581873324120129819E-5,
+        5.39605580493303397842E-4,
+        -4.45641913851797240494E-3,
+        1.18139785222060435552E-2,
+        3.58236398605498653373E-2,
+        -2.34591795718243348568E-1,
+        7.14304917030273074085E-2,
+        1.00000000000000000320E0
+    };
+
+    double p, z;
+    int i;
+
+    double q = Math.abs(x);
+
+    if (q > 33.0) {
+      if (x < 0.0) {
+        p = Math.floor(q);
+        if (p == q)
+          throw new ArithmeticException("gamma: overflow");
+        i = (int) p;
+        z = q - p;
+        if (z > 0.5) {
+          p += 1.0;
+          z = q - p;
+        }
+        z = q * Math.sin(Math.PI * z);
+        if (z == 0.0)
+          throw new ArithmeticException("gamma: overflow");
+        z = Math.abs(z);
+        z = Math.PI / (z * stirlingFormula(q));
+
+        return -z;
+      } else {
+        return stirlingFormula(x);
+      }
+    }
+
+    z = 1.0;
+    while (x >= 3.0) {
+      x -= 1.0;
+      z *= x;
+    }
+
+    while (x < 0.0) {
+      if (x == 0.0) {
+        throw new ArithmeticException("gamma: singular");
+      } else if (x > -1.E-9) {
+        return (z / ((1.0 + 0.5772156649015329 * x) * x));
+      }
+      z /= x;
+      x += 1.0;
+    }
+
+    while (x < 2.0) {
+      if (x == 0.0) {
+        throw new ArithmeticException("gamma: singular");
+      } else if (x < 1.e-9) {
+        return (z / ((1.0 + 0.5772156649015329 * x) * x));
+      }
+      z /= x;
+      x += 1.0;
+    }
+
+    if ((x == 2.0) || (x == 3.0))
+      return z;
+
+    x -= 2.0;
+    p = polevl(x, P, 6);
+    q = polevl(x, Q, 7);
+    return z * p / q;
+  }
+
+  /**
+   * Returns the Gamma function computed by Stirling's formula. The polynomial 
STIR is valid for 33 <= x <= 172.
+   */
+  public static double stirlingFormula(double x) {
+
+    double STIR[] = {
+        7.87311395793093628397E-4,
+        -2.29549961613378126380E-4,
+        -2.68132617805781232825E-3,
+        3.47222221605458667310E-3,
+        8.33333333333482257126E-2,
+    };
+    double MAXSTIR = 143.01608;
+
+    double w = 1.0 / x;
+    double y = Math.exp(x);
+
+    w = 1.0 + w * polevl(w, STIR, 4);
+
+    if (x > MAXSTIR) {
+      /* Avoid overflow in Math.pow() */
+      double v = Math.pow(x, 0.5 * x - 0.25);
+      y = v * (v / y);
+    } else {
+      y = Math.pow(x, x - 0.5) / y;
+    }
+    y = SQTPI * y * w;
+    return y;
+  }
+
+  /**
+   * Returns the Incomplete Beta Function evaluated from zero to <tt>xx</tt>.
+   * 
+   * @param aa
+   *          the alpha parameter of the beta distribution.
+   * @param bb
+   *          the beta parameter of the beta distribution.
+   * @param xx
+   *          the integration end point.
+   */
+  public static double incompleteBeta(double aa, double bb, double xx) {
+
+    double a, b, t, x, xc, w, y;
+    boolean flag;
+
+    if (aa <= 0.0 || bb <= 0.0)
+      throw new ArithmeticException("ibeta: Domain error!");
+
+    if ((xx <= 0.0) || (xx >= 1.0)) {
+      if (xx == 0.0)
+        return 0.0;
+      if (xx == 1.0)
+        return 1.0;
+      throw new ArithmeticException("ibeta: Domain error!");
+    }
+
+    flag = false;
+    if ((bb * xx) <= 1.0 && xx <= 0.95) {
+      t = powerSeries(aa, bb, xx);
+      return t;
+    }
+
+    w = 1.0 - xx;
+
+    /* Reverse a and b if x is greater than the mean. */
+    if (xx > (aa / (aa + bb))) {
+      flag = true;
+      a = bb;
+      b = aa;
+      xc = xx;
+      x = w;
+    } else {
+      a = aa;
+      b = bb;
+      xc = w;
+      x = xx;
+    }
+
+    if (flag && (b * x) <= 1.0 && x <= 0.95) {
+      t = powerSeries(a, b, x);
+      if (t <= MACHEP)
+        t = 1.0 - MACHEP;
+      else
+        t = 1.0 - t;
+      return t;
+    }
+
+    /* Choose expansion for better convergence. */
+    y = x * (a + b - 2.0) - (a - 1.0);
+    if (y < 0.0)
+      w = incompleteBetaFraction1(a, b, x);
+    else
+      w = incompleteBetaFraction2(a, b, x) / xc;
+
+    /*
+     * Multiply w by the factor a b _ _ _ x (1-x) | (a+b) / ( a | (a) | (b) ) .
+     */
+
+    y = a * Math.log(x);
+    t = b * Math.log(xc);
+    if ((a + b) < MAXGAM && Math.abs(y) < MAXLOG && Math.abs(t) < MAXLOG) {
+      t = Math.pow(xc, b);
+      t *= Math.pow(x, a);
+      t /= a;
+      t *= w;
+      t *= gamma(a + b) / (gamma(a) * gamma(b));
+      if (flag) {
+        if (t <= MACHEP)
+          t = 1.0 - MACHEP;
+        else
+          t = 1.0 - t;
+      }
+      return t;
+    }
+    /* Resort to logarithms. */
+    y += t + lnGamma(a + b) - lnGamma(a) - lnGamma(b);
+    y += Math.log(w / a);
+    if (y < MINLOG)
+      t = 0.0;
+    else
+      t = Math.exp(y);
+
+    if (flag) {
+      if (t <= MACHEP)
+        t = 1.0 - MACHEP;
+      else
+        t = 1.0 - t;
+    }
+    return t;
+  }
+
+  /**
+   * Continued fraction expansion #1 for incomplete beta integral.
+   */
+  public static double incompleteBetaFraction1(double a, double b, double x) {
+
+    double xk, pk, pkm1, pkm2, qk, qkm1, qkm2;
+    double k1, k2, k3, k4, k5, k6, k7, k8;
+    double r, t, ans, thresh;
+    int n;
+
+    k1 = a;
+    k2 = a + b;
+    k3 = a;
+    k4 = a + 1.0;
+    k5 = 1.0;
+    k6 = b - 1.0;
+    k7 = k4;
+    k8 = a + 2.0;
+
+    pkm2 = 0.0;
+    qkm2 = 1.0;
+    pkm1 = 1.0;
+    qkm1 = 1.0;
+    ans = 1.0;
+    r = 1.0;
+    n = 0;
+    thresh = 3.0 * MACHEP;
+    do {
+      xk = -(x * k1 * k2) / (k3 * k4);
+      pk = pkm1 + pkm2 * xk;
+      qk = qkm1 + qkm2 * xk;
+      pkm2 = pkm1;
+      pkm1 = pk;
+      qkm2 = qkm1;
+      qkm1 = qk;
+
+      xk = (x * k5 * k6) / (k7 * k8);
+      pk = pkm1 + pkm2 * xk;
+      qk = qkm1 + qkm2 * xk;
+      pkm2 = pkm1;
+      pkm1 = pk;
+      qkm2 = qkm1;
+      qkm1 = qk;
+
+      if (qk != 0)
+        r = pk / qk;
+      if (r != 0) {
+        t = Math.abs((ans - r) / r);
+        ans = r;
+      } else
+        t = 1.0;
+
+      if (t < thresh)
+        return ans;
+
+      k1 += 1.0;
+      k2 += 1.0;
+      k3 += 2.0;
+      k4 += 2.0;
+      k5 += 1.0;
+      k6 -= 1.0;
+      k7 += 2.0;
+      k8 += 2.0;
+
+      if ((Math.abs(qk) + Math.abs(pk)) > big) {
+        pkm2 *= biginv;
+        pkm1 *= biginv;
+        qkm2 *= biginv;
+        qkm1 *= biginv;
+      }
+      if ((Math.abs(qk) < biginv) || (Math.abs(pk) < biginv)) {
+        pkm2 *= big;
+        pkm1 *= big;
+        qkm2 *= big;
+        qkm1 *= big;
+      }
+    } while (++n < 300);
+
+    return ans;
+  }
+
+  /**
+   * Continued fraction expansion #2 for incomplete beta integral.
+   */
+  public static double incompleteBetaFraction2(double a, double b, double x) {
+
+    double xk, pk, pkm1, pkm2, qk, qkm1, qkm2;
+    double k1, k2, k3, k4, k5, k6, k7, k8;
+    double r, t, ans, z, thresh;
+    int n;
+
+    k1 = a;
+    k2 = b - 1.0;
+    k3 = a;
+    k4 = a + 1.0;
+    k5 = 1.0;
+    k6 = a + b;
+    k7 = a + 1.0;
+    ;
+    k8 = a + 2.0;
+
+    pkm2 = 0.0;
+    qkm2 = 1.0;
+    pkm1 = 1.0;
+    qkm1 = 1.0;
+    z = x / (1.0 - x);
+    ans = 1.0;
+    r = 1.0;
+    n = 0;
+    thresh = 3.0 * MACHEP;
+    do {
+      xk = -(z * k1 * k2) / (k3 * k4);
+      pk = pkm1 + pkm2 * xk;
+      qk = qkm1 + qkm2 * xk;
+      pkm2 = pkm1;
+      pkm1 = pk;
+      qkm2 = qkm1;
+      qkm1 = qk;
+
+      xk = (z * k5 * k6) / (k7 * k8);
+      pk = pkm1 + pkm2 * xk;
+      qk = qkm1 + qkm2 * xk;
+      pkm2 = pkm1;
+      pkm1 = pk;
+      qkm2 = qkm1;
+      qkm1 = qk;
+
+      if (qk != 0)
+        r = pk / qk;
+      if (r != 0) {
+        t = Math.abs((ans - r) / r);
+        ans = r;
+      } else
+        t = 1.0;
+
+      if (t < thresh)
+        return ans;
+
+      k1 += 1.0;
+      k2 -= 1.0;
+      k3 += 2.0;
+      k4 += 2.0;
+      k5 += 1.0;
+      k6 += 1.0;
+      k7 += 2.0;
+      k8 += 2.0;
+
+      if ((Math.abs(qk) + Math.abs(pk)) > big) {
+        pkm2 *= biginv;
+        pkm1 *= biginv;
+        qkm2 *= biginv;
+        qkm1 *= biginv;
+      }
+      if ((Math.abs(qk) < biginv) || (Math.abs(pk) < biginv)) {
+        pkm2 *= big;
+        pkm1 *= big;
+        qkm2 *= big;
+        qkm1 *= big;
+      }
+    } while (++n < 300);
+
+    return ans;
+  }
+
+  /**
+   * Power series for incomplete beta integral. Use when b*x is small and x 
not too close to 1.
+   */
+  public static double powerSeries(double a, double b, double x) {
+
+    double s, t, u, v, n, t1, z, ai;
+
+    ai = 1.0 / a;
+    u = (1.0 - b) * x;
+    v = u / (a + 1.0);
+    t1 = v;
+    t = u;
+    n = 2.0;
+    s = 0.0;
+    z = MACHEP * ai;
+    while (Math.abs(v) > z) {
+      u = (n - b) * x / n;
+      t *= u;
+      v = t / (a + n);
+      s += v;
+      n += 1.0;
+    }
+    s += t1;
+    s += ai;
+
+    u = a * Math.log(x);
+    if ((a + b) < MAXGAM && Math.abs(u) < MAXLOG) {
+      t = gamma(a + b) / (gamma(a) * gamma(b));
+      s = s * t * Math.pow(x, a);
+    } else {
+      t = lnGamma(a + b) - lnGamma(a) - lnGamma(b) + u + Math.log(s);
+      if (t < MINLOG)
+        s = 0.0;
+      else
+        s = Math.exp(t);
+    }
+    return s;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/StringUtils.java
----------------------------------------------------------------------
diff --git a/samoa-api/src/main/java/org/apache/samoa/moa/core/StringUtils.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/StringUtils.java
new file mode 100644
index 0000000..6189f37
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/core/StringUtils.java
@@ -0,0 +1,96 @@
+package org.apache.samoa.moa.core;
+
+/*
+ * #%L
+ * SAMOA
+ * %%
+ * Copyright (C) 2014 - 2015 Apache Software Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import java.text.DecimalFormat;
+
+/**
+ * Class implementing some string utility methods.
+ * 
+ * @author Richard Kirkby ([email protected])
+ * @version $Revision: 7 $
+ */
+public class StringUtils {
+
+  public static final String newline = System.getProperty("line.separator");
+
+  public static String doubleToString(double value, int fractionDigits) {
+    return doubleToString(value, 0, fractionDigits);
+  }
+
+  public static String doubleToString(double value, int minFractionDigits,
+      int maxFractionDigits) {
+    DecimalFormat numberFormat = new DecimalFormat();
+    numberFormat.setMinimumFractionDigits(minFractionDigits);
+    numberFormat.setMaximumFractionDigits(maxFractionDigits);
+    return numberFormat.format(value);
+  }
+
+  public static void appendNewline(StringBuilder out) {
+    out.append(newline);
+  }
+
+  public static void appendIndent(StringBuilder out, int indent) {
+    for (int i = 0; i < indent; i++) {
+      out.append(' ');
+    }
+  }
+
+  public static void appendIndented(StringBuilder out, int indent, String s) {
+    appendIndent(out, indent);
+    out.append(s);
+  }
+
+  public static void appendNewlineIndented(StringBuilder out, int indent,
+      String s) {
+    appendNewline(out);
+    appendIndented(out, indent, s);
+  }
+
+  public static String secondsToDHMSString(double seconds) {
+    if (seconds < 60) {
+      return doubleToString(seconds, 2, 2) + 's';
+    }
+    long secs = (int) (seconds);
+    long mins = secs / 60;
+    long hours = mins / 60;
+    long days = hours / 24;
+    secs %= 60;
+    mins %= 60;
+    hours %= 24;
+    StringBuilder result = new StringBuilder();
+    if (days > 0) {
+      result.append(days);
+      result.append('d');
+    }
+    if ((hours > 0) || (days > 0)) {
+      result.append(hours);
+      result.append('h');
+    }
+    if ((hours > 0) || (days > 0) || (mins > 0)) {
+      result.append(mins);
+      result.append('m');
+    }
+    result.append(secs);
+    result.append('s');
+    return result.toString();
+  }
+}

Reply via email to