http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/core/Utils.java
----------------------------------------------------------------------
diff --git a/samoa-api/src/main/java/org/apache/samoa/moa/core/Utils.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/core/Utils.java
new file mode 100644
index 0000000..e45db02
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/core/Utils.java
@@ -0,0 +1,1982 @@
+/*
+ *    Utils.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.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.lang.reflect.Array;
+import java.net.URL;
+import java.text.BreakIterator;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Random;
+import java.util.Vector;
+
+/**
+ * Class implementing some simple utility methods.
+ * 
+ * @author Eibe Frank
+ * @author Yong Wang
+ * @author Len Trigg
+ * @author Julien Prados
+ * @version $Revision: 8080 $
+ */
+public final class Utils {
+
+  /** The natural logarithm of 2. */
+  public static double log2 = Math.log(2);
+
+  /** The small deviation allowed in double comparisons. */
+  public static double SMALL = 1e-6;
+
+  /**
+   * Tests if the given value codes "missing".
+   * 
+   * @param val
+   *          the value to be tested
+   * @return true if val codes "missing"
+   */
+  public static boolean isMissingValue(double val) {
+
+    return Double.isNaN(val);
+  }
+
+  /**
+   * Returns the value used to code a missing value. Note that equality tests 
on this value will always return false, so
+   * use isMissingValue(double val) for testing..
+   * 
+   * @return the value used as missing value.
+   */
+  public static double missingValue() {
+
+    return Double.NaN;
+  }
+
+  /**
+   * Casting an object without "unchecked" compile-time warnings. Use only 
when absolutely necessary (e.g. when using
+   * clone()).
+   */
+  @SuppressWarnings("unchecked")
+  public static <T> T cast(Object x) {
+    return (T) x;
+  }
+
+  /**
+   * Returns the correlation coefficient of two double vectors.
+   * 
+   * @param y1
+   *          double vector 1
+   * @param y2
+   *          double vector 2
+   * @param n
+   *          the length of two double vectors
+   * @return the correlation coefficient
+   */
+  public static final double correlation(double y1[], double y2[], int n) {
+
+    int i;
+    double av1 = 0.0, av2 = 0.0, y11 = 0.0, y22 = 0.0, y12 = 0.0, c;
+
+    if (n <= 1) {
+      return 1.0;
+    }
+    for (i = 0; i < n; i++) {
+      av1 += y1[i];
+      av2 += y2[i];
+    }
+    av1 /= (double) n;
+    av2 /= (double) n;
+    for (i = 0; i < n; i++) {
+      y11 += (y1[i] - av1) * (y1[i] - av1);
+      y22 += (y2[i] - av2) * (y2[i] - av2);
+      y12 += (y1[i] - av1) * (y2[i] - av2);
+    }
+    if (y11 * y22 == 0.0) {
+      c = 1.0;
+    } else {
+      c = y12 / Math.sqrt(Math.abs(y11 * y22));
+    }
+
+    return c;
+  }
+
+  /**
+   * Removes all occurrences of a string from another string.
+   * 
+   * @param inString
+   *          the string to remove substrings from.
+   * @param substring
+   *          the substring to remove.
+   * @return the input string with occurrences of substring removed.
+   */
+  public static String removeSubstring(String inString, String substring) {
+
+    StringBuffer result = new StringBuffer();
+    int oldLoc = 0, loc = 0;
+    while ((loc = inString.indexOf(substring, oldLoc)) != -1) {
+      result.append(inString.substring(oldLoc, loc));
+      oldLoc = loc + substring.length();
+    }
+    result.append(inString.substring(oldLoc));
+    return result.toString();
+  }
+
+  /**
+   * Replaces with a new string, all occurrences of a string from another 
string.
+   * 
+   * @param inString
+   *          the string to replace substrings in.
+   * @param subString
+   *          the substring to replace.
+   * @param replaceString
+   *          the replacement substring
+   * @return the input string with occurrences of substring replaced.
+   */
+  public static String replaceSubstring(String inString, String subString,
+      String replaceString) {
+
+    StringBuffer result = new StringBuffer();
+    int oldLoc = 0, loc = 0;
+    while ((loc = inString.indexOf(subString, oldLoc)) != -1) {
+      result.append(inString.substring(oldLoc, loc));
+      result.append(replaceString);
+      oldLoc = loc + subString.length();
+    }
+    result.append(inString.substring(oldLoc));
+    return result.toString();
+  }
+
+  /**
+   * Pads a string to a specified length, inserting spaces on the left as 
required. If the string is too long,
+   * characters are removed (from the right).
+   * 
+   * @param inString
+   *          the input string
+   * @param length
+   *          the desired length of the output string
+   * @return the output string
+   */
+  public static String padLeft(String inString, int length) {
+
+    return fixStringLength(inString, length, false);
+  }
+
+  /**
+   * Pads a string to a specified length, inserting spaces on the right as 
required. If the string is too long,
+   * characters are removed (from the right).
+   * 
+   * @param inString
+   *          the input string
+   * @param length
+   *          the desired length of the output string
+   * @return the output string
+   */
+  public static String padRight(String inString, int length) {
+
+    return fixStringLength(inString, length, true);
+  }
+
+  /**
+   * Pads a string to a specified length, inserting spaces as required. If the 
string is too long, characters are
+   * removed (from the right).
+   * 
+   * @param inString
+   *          the input string
+   * @param length
+   *          the desired length of the output string
+   * @param right
+   *          true if inserted spaces should be added to the right
+   * @return the output string
+   */
+  private static/* @pure@ */String fixStringLength(String inString, int length,
+      boolean right) {
+
+    if (inString.length() < length) {
+      while (inString.length() < length) {
+        inString = (right ? inString.concat(" ") : " ".concat(inString));
+      }
+    } else if (inString.length() > length) {
+      inString = inString.substring(0, length);
+    }
+    return inString;
+  }
+
+  /**
+   * Rounds a double and converts it into String.
+   * 
+   * @param value
+   *          the double value
+   * @param afterDecimalPoint
+   *          the (maximum) number of digits permitted after the decimal point
+   * @return the double as a formatted string
+   */
+  public static/* @pure@ */String doubleToString(double value, int 
afterDecimalPoint) {
+
+    StringBuffer stringBuffer;
+    double temp;
+    int dotPosition;
+    long precisionValue;
+
+    temp = value * Math.pow(10.0, afterDecimalPoint);
+    if (Math.abs(temp) < Long.MAX_VALUE) {
+      precisionValue = (temp > 0) ? (long) (temp + 0.5)
+          : -(long) (Math.abs(temp) + 0.5);
+      if (precisionValue == 0) {
+        stringBuffer = new StringBuffer(String.valueOf(0));
+      } else {
+        stringBuffer = new StringBuffer(String.valueOf(precisionValue));
+      }
+      if (afterDecimalPoint == 0) {
+        return stringBuffer.toString();
+      }
+      dotPosition = stringBuffer.length() - afterDecimalPoint;
+      while (((precisionValue < 0) && (dotPosition < 1)) ||
+          (dotPosition < 0)) {
+        if (precisionValue < 0) {
+          stringBuffer.insert(1, '0');
+        } else {
+          stringBuffer.insert(0, '0');
+        }
+        dotPosition++;
+      }
+      stringBuffer.insert(dotPosition, '.');
+      if ((precisionValue < 0) && (stringBuffer.charAt(1) == '.')) {
+        stringBuffer.insert(1, '0');
+      } else if (stringBuffer.charAt(0) == '.') {
+        stringBuffer.insert(0, '0');
+      }
+      int currentPos = stringBuffer.length() - 1;
+      while ((currentPos > dotPosition) &&
+          (stringBuffer.charAt(currentPos) == '0')) {
+        stringBuffer.setCharAt(currentPos--, ' ');
+      }
+      if (stringBuffer.charAt(currentPos) == '.') {
+        stringBuffer.setCharAt(currentPos, ' ');
+      }
+
+      return stringBuffer.toString().trim();
+    }
+    return new String("" + value);
+  }
+
+  /**
+   * Rounds a double and converts it into a formatted decimal-justified 
String. Trailing 0's are replaced with spaces.
+   * 
+   * @param value
+   *          the double value
+   * @param width
+   *          the width of the string
+   * @param afterDecimalPoint
+   *          the number of digits after the decimal point
+   * @return the double as a formatted string
+   */
+  public static/* @pure@ */String doubleToString(double value, int width,
+      int afterDecimalPoint) {
+
+    String tempString = doubleToString(value, afterDecimalPoint);
+    char[] result;
+    int dotPosition;
+
+    if ((afterDecimalPoint >= width)
+        || (tempString.indexOf('E') != -1)) { // Protects sci notation
+      return tempString;
+    }
+
+    // Initialize result
+    result = new char[width];
+    for (int i = 0; i < result.length; i++) {
+      result[i] = ' ';
+    }
+
+    if (afterDecimalPoint > 0) {
+      // Get position of decimal point and insert decimal point
+      dotPosition = tempString.indexOf('.');
+      if (dotPosition == -1) {
+        dotPosition = tempString.length();
+      } else {
+        result[width - afterDecimalPoint - 1] = '.';
+      }
+    } else {
+      dotPosition = tempString.length();
+    }
+
+    int offset = width - afterDecimalPoint - dotPosition;
+    if (afterDecimalPoint > 0) {
+      offset--;
+    }
+
+    // Not enough room to decimal align within the supplied width
+    if (offset < 0) {
+      return tempString;
+    }
+
+    // Copy characters before decimal point
+    for (int i = 0; i < dotPosition; i++) {
+      result[offset + i] = tempString.charAt(i);
+    }
+
+    // Copy characters after decimal point
+    for (int i = dotPosition + 1; i < tempString.length(); i++) {
+      result[offset + i] = tempString.charAt(i);
+    }
+
+    return new String(result);
+  }
+
+  /**
+   * Returns the basic class of an array class (handles multi-dimensional 
arrays).
+   * 
+   * @param c
+   *          the array to inspect
+   * @return the class of the innermost elements
+   */
+  public static Class getArrayClass(Class c) {
+    if (c.getComponentType().isArray())
+      return getArrayClass(c.getComponentType());
+    else
+      return c.getComponentType();
+  }
+
+  /**
+   * Returns the dimensions of the given array. Even though the parameter is 
of type "Object" one can hand over primitve
+   * arrays, e.g. int[3] or double[2][4].
+   * 
+   * @param array
+   *          the array to determine the dimensions for
+   * @return the dimensions of the array
+   */
+  public static int getArrayDimensions(Class array) {
+    if (array.getComponentType().isArray())
+      return 1 + getArrayDimensions(array.getComponentType());
+    else
+      return 1;
+  }
+
+  /**
+   * Returns the dimensions of the given array. Even though the parameter is 
of type "Object" one can hand over primitve
+   * arrays, e.g. int[3] or double[2][4].
+   * 
+   * @param array
+   *          the array to determine the dimensions for
+   * @return the dimensions of the array
+   */
+  public static int getArrayDimensions(Object array) {
+    return getArrayDimensions(array.getClass());
+  }
+
+  /**
+   * Returns the given Array in a string representation. Even though the 
parameter is of type "Object" one can hand over
+   * primitve arrays, e.g. int[3] or double[2][4].
+   * 
+   * @param array
+   *          the array to return in a string representation
+   * @return the array as string
+   */
+  public static String arrayToString(Object array) {
+    String result;
+    int dimensions;
+    int i;
+
+    result = "";
+    dimensions = getArrayDimensions(array);
+
+    if (dimensions == 0) {
+      result = "null";
+    }
+    else if (dimensions == 1) {
+      for (i = 0; i < Array.getLength(array); i++) {
+        if (i > 0)
+          result += ",";
+        if (Array.get(array, i) == null)
+          result += "null";
+        else
+          result += Array.get(array, i).toString();
+      }
+    }
+    else {
+      for (i = 0; i < Array.getLength(array); i++) {
+        if (i > 0)
+          result += ",";
+        result += "[" + arrayToString(Array.get(array, i)) + "]";
+      }
+    }
+
+    return result;
+  }
+
+  /**
+   * Tests if a is equal to b.
+   * 
+   * @param a
+   *          a double
+   * @param b
+   *          a double
+   */
+  public static/* @pure@ */boolean eq(double a, double b) {
+
+    return (a - b < SMALL) && (b - a < SMALL);
+  }
+
+  /**
+   * Checks if the given array contains any non-empty options.
+   * 
+   * @param options
+   *          an array of strings
+   * @exception Exception
+   *              if there are any non-empty options
+   */
+  public static void checkForRemainingOptions(String[] options)
+      throws Exception {
+
+    int illegalOptionsFound = 0;
+    StringBuffer text = new StringBuffer();
+
+    if (options == null) {
+      return;
+    }
+    for (int i = 0; i < options.length; i++) {
+      if (options[i].length() > 0) {
+        illegalOptionsFound++;
+        text.append(options[i] + ' ');
+      }
+    }
+    if (illegalOptionsFound > 0) {
+      throw new Exception("Illegal options: " + text);
+    }
+  }
+
+  /**
+   * Checks if the given array contains the flag "-Char". Stops searching at 
the first marker "--". If the flag is
+   * found, it is replaced with the empty string.
+   * 
+   * @param flag
+   *          the character indicating the flag.
+   * @param options
+   *          the array of strings containing all the options.
+   * @return true if the flag was found
+   * @exception Exception
+   *              if an illegal option was found
+   */
+  public static boolean getFlag(char flag, String[] options)
+      throws Exception {
+
+    return getFlag("" + flag, options);
+  }
+
+  /**
+   * Checks if the given array contains the flag "-String". Stops searching at 
the first marker "--". If the flag is
+   * found, it is replaced with the empty string.
+   * 
+   * @param flag
+   *          the String indicating the flag.
+   * @param options
+   *          the array of strings containing all the options.
+   * @return true if the flag was found
+   * @exception Exception
+   *              if an illegal option was found
+   */
+  public static boolean getFlag(String flag, String[] options)
+      throws Exception {
+
+    int pos = getOptionPos(flag, options);
+
+    if (pos > -1)
+      options[pos] = "";
+
+    return (pos > -1);
+  }
+
+  /**
+   * Gets an option indicated by a flag "-Char" from the given array of 
strings. Stops searching at the first marker
+   * "--". Replaces flag and option with empty strings.
+   * 
+   * @param flag
+   *          the character indicating the option.
+   * @param options
+   *          the array of strings containing all the options.
+   * @return the indicated option or an empty string
+   * @exception Exception
+   *              if the option indicated by the flag can't be found
+   */
+  public static/* @non_null@ */String getOption(char flag, String[] options)
+      throws Exception {
+
+    return getOption("" + flag, options);
+  }
+
+  /**
+   * Gets an option indicated by a flag "-String" from the given array of 
strings. Stops searching at the first marker
+   * "--". Replaces flag and option with empty strings.
+   * 
+   * @param flag
+   *          the String indicating the option.
+   * @param options
+   *          the array of strings containing all the options.
+   * @return the indicated option or an empty string
+   * @exception Exception
+   *              if the option indicated by the flag can't be found
+   */
+  public static/* @non_null@ */String getOption(String flag, String[] options)
+      throws Exception {
+
+    String newString;
+    int i = getOptionPos(flag, options);
+
+    if (i > -1) {
+      if (options[i].equals("-" + flag)) {
+        if (i + 1 == options.length) {
+          throw new Exception("No value given for -" + flag + " option.");
+        }
+        options[i] = "";
+        newString = new String(options[i + 1]);
+        options[i + 1] = "";
+        return newString;
+      }
+      if (options[i].charAt(1) == '-') {
+        return "";
+      }
+    }
+
+    return "";
+  }
+
+  /**
+   * Gets the index of an option or flag indicated by a flag "-Char" from the 
given array of strings. Stops searching at
+   * the first marker "--".
+   * 
+   * @param flag
+   *          the character indicating the option.
+   * @param options
+   *          the array of strings containing all the options.
+   * @return the position if found, or -1 otherwise
+   */
+  public static int getOptionPos(char flag, String[] options) {
+    return getOptionPos("" + flag, options);
+  }
+
+  /**
+   * Gets the index of an option or flag indicated by a flag "-String" from 
the given array of strings. Stops searching
+   * at the first marker "--".
+   * 
+   * @param flag
+   *          the String indicating the option.
+   * @param options
+   *          the array of strings containing all the options.
+   * @return the position if found, or -1 otherwise
+   */
+  public static int getOptionPos(String flag, String[] options) {
+    if (options == null)
+      return -1;
+
+    for (int i = 0; i < options.length; i++) {
+      if ((options[i].length() > 0) && (options[i].charAt(0) == '-')) {
+        // Check if it is a negative number
+        try {
+          Double.valueOf(options[i]);
+        } catch (NumberFormatException e) {
+          // found?
+          if (options[i].equals("-" + flag))
+            return i;
+          // did we reach "--"?
+          if (options[i].charAt(1) == '-')
+            return -1;
+        }
+      }
+    }
+
+    return -1;
+  }
+
+  /**
+   * Quotes a string if it contains special characters.
+   * 
+   * The following rules are applied:
+   * 
+   * A character is backquoted version of it is one of <tt>" ' % \ \n \r 
\t</tt> .
+   * 
+   * A string is enclosed within single quotes if a character has been 
backquoted using the previous rule above or
+   * contains <tt>{ }</tt> or is exactly equal to the strings <tt>, ? space or 
""</tt> (empty string).
+   * 
+   * A quoted question mark distinguishes it from the missing value which is 
represented as an unquoted question mark in
+   * arff files.
+   * 
+   * @param string
+   *          the string to be quoted
+   * @return the string (possibly quoted)
+   * @see #unquote(String)
+   */
+  public static/* @pure@ */String quote(String string) {
+    boolean quote = false;
+
+    // backquote the following characters
+    if ((string.indexOf('\n') != -1) || (string.indexOf('\r') != -1) ||
+        (string.indexOf('\'') != -1) || (string.indexOf('"') != -1) ||
+        (string.indexOf('\\') != -1) ||
+        (string.indexOf('\t') != -1) || (string.indexOf('%') != -1) ||
+        (string.indexOf('\u001E') != -1)) {
+      string = backQuoteChars(string);
+      quote = true;
+    }
+
+    // Enclose the string in 's if the string contains a recently added
+    // backquote or contains one of the following characters.
+    if ((quote == true) ||
+        (string.indexOf('{') != -1) || (string.indexOf('}') != -1) ||
+        (string.indexOf(',') != -1) || (string.equals("?")) ||
+        (string.indexOf(' ') != -1) || (string.equals(""))) {
+      string = ("'".concat(string)).concat("'");
+    }
+
+    return string;
+  }
+
+  /**
+   * unquotes are previously quoted string (but only if necessary), i.e., it 
removes the single quotes around it.
+   * Inverse to quote(String).
+   * 
+   * @param string
+   *          the string to process
+   * @return the unquoted string
+   * @see #quote(String)
+   */
+  public static String unquote(String string) {
+    if (string.startsWith("'") && string.endsWith("'")) {
+      string = string.substring(1, string.length() - 1);
+
+      if ((string.indexOf("\\n") != -1) || (string.indexOf("\\r") != -1) ||
+          (string.indexOf("\\'") != -1) || (string.indexOf("\\\"") != -1) ||
+          (string.indexOf("\\\\") != -1) ||
+          (string.indexOf("\\t") != -1) || (string.indexOf("\\%") != -1) ||
+          (string.indexOf("\\u001E") != -1)) {
+        string = unbackQuoteChars(string);
+      }
+    }
+
+    return string;
+  }
+
+  /**
+   * Converts carriage returns and new lines in a string into \r and \n. 
Backquotes the following characters: ` " \ \t
+   * and %
+   * 
+   * @param string
+   *          the string
+   * @return the converted string
+   * @see #unbackQuoteChars(String)
+   */
+  public static/* @pure@ */String backQuoteChars(String string) {
+
+    int index;
+    StringBuffer newStringBuffer;
+
+    // replace each of the following characters with the backquoted version
+    char charsFind[] = { '\\', '\'', '\t', '\n', '\r', '"', '%',
+        '\u001E' };
+    String charsReplace[] = { "\\\\", "\\'", "\\t", "\\n", "\\r", "\\\"", 
"\\%",
+        "\\u001E" };
+    for (int i = 0; i < charsFind.length; i++) {
+      if (string.indexOf(charsFind[i]) != -1) {
+        newStringBuffer = new StringBuffer();
+        while ((index = string.indexOf(charsFind[i])) != -1) {
+          if (index > 0) {
+            newStringBuffer.append(string.substring(0, index));
+          }
+          newStringBuffer.append(charsReplace[i]);
+          if ((index + 1) < string.length()) {
+            string = string.substring(index + 1);
+          } else {
+            string = "";
+          }
+        }
+        newStringBuffer.append(string);
+        string = newStringBuffer.toString();
+      }
+    }
+
+    return string;
+  }
+
+  /**
+   * Converts carriage returns and new lines in a string into \r and \n.
+   * 
+   * @param string
+   *          the string
+   * @return the converted string
+   */
+  public static String convertNewLines(String string) {
+    int index;
+
+    // Replace with \n
+    StringBuffer newStringBuffer = new StringBuffer();
+    while ((index = string.indexOf('\n')) != -1) {
+      if (index > 0) {
+        newStringBuffer.append(string.substring(0, index));
+      }
+      newStringBuffer.append('\\');
+      newStringBuffer.append('n');
+      if ((index + 1) < string.length()) {
+        string = string.substring(index + 1);
+      } else {
+        string = "";
+      }
+    }
+    newStringBuffer.append(string);
+    string = newStringBuffer.toString();
+
+    // Replace with \r
+    newStringBuffer = new StringBuffer();
+    while ((index = string.indexOf('\r')) != -1) {
+      if (index > 0) {
+        newStringBuffer.append(string.substring(0, index));
+      }
+      newStringBuffer.append('\\');
+      newStringBuffer.append('r');
+      if ((index + 1) < string.length()) {
+        string = string.substring(index + 1);
+      } else {
+        string = "";
+      }
+    }
+    newStringBuffer.append(string);
+    return newStringBuffer.toString();
+  }
+
+  /**
+   * Reverts \r and \n in a string into carriage returns and new lines.
+   * 
+   * @param string
+   *          the string
+   * @return the converted string
+   */
+  public static String revertNewLines(String string) {
+    int index;
+
+    // Replace with \n
+    StringBuffer newStringBuffer = new StringBuffer();
+    while ((index = string.indexOf("\\n")) != -1) {
+      if (index > 0) {
+        newStringBuffer.append(string.substring(0, index));
+      }
+      newStringBuffer.append('\n');
+      if ((index + 2) < string.length()) {
+        string = string.substring(index + 2);
+      } else {
+        string = "";
+      }
+    }
+    newStringBuffer.append(string);
+    string = newStringBuffer.toString();
+
+    // Replace with \r
+    newStringBuffer = new StringBuffer();
+    while ((index = string.indexOf("\\r")) != -1) {
+      if (index > 0) {
+        newStringBuffer.append(string.substring(0, index));
+      }
+      newStringBuffer.append('\r');
+      if ((index + 2) < string.length()) {
+        string = string.substring(index + 2);
+      } else {
+        string = "";
+      }
+    }
+    newStringBuffer.append(string);
+
+    return newStringBuffer.toString();
+  }
+
+  /**
+   * Returns the secondary set of options (if any) contained in the supplied 
options array. The secondary set is defined
+   * to be any options after the first "--". These options are removed from 
the original options array.
+   * 
+   * @param options
+   *          the input array of options
+   * @return the array of secondary options
+   */
+  public static String[] partitionOptions(String[] options) {
+
+    for (int i = 0; i < options.length; i++) {
+      if (options[i].equals("--")) {
+        options[i++] = "";
+        String[] result = new String[options.length - i];
+        for (int j = i; j < options.length; j++) {
+          result[j - i] = options[j];
+          options[j] = "";
+        }
+        return result;
+      }
+    }
+    return new String[0];
+  }
+
+  /**
+   * The inverse operation of backQuoteChars(). Converts back-quoted carriage 
returns and new lines in a string to the
+   * corresponding character ('\r' and '\n'). Also "un"-back-quotes the 
following characters: ` " \ \t and %
+   * 
+   * @param string
+   *          the string
+   * @return the converted string
+   * @see #backQuoteChars(String)
+   */
+  public static String unbackQuoteChars(String string) {
+
+    int index;
+    StringBuffer newStringBuffer;
+
+    // replace each of the following characters with the backquoted version
+    String charsFind[] = { "\\\\", "\\'", "\\t", "\\n", "\\r", "\\\"", "\\%",
+        "\\u001E" };
+    char charsReplace[] = { '\\', '\'', '\t', '\n', '\r', '"', '%',
+        '\u001E' };
+    int pos[] = new int[charsFind.length];
+    int curPos;
+
+    String str = new String(string);
+    newStringBuffer = new StringBuffer();
+    while (str.length() > 0) {
+      // get positions and closest character to replace
+      curPos = str.length();
+      index = -1;
+      for (int i = 0; i < pos.length; i++) {
+        pos[i] = str.indexOf(charsFind[i]);
+        if ((pos[i] > -1) && (pos[i] < curPos)) {
+          index = i;
+          curPos = pos[i];
+        }
+      }
+
+      // replace character if found, otherwise finished
+      if (index == -1) {
+        newStringBuffer.append(str);
+        str = "";
+      }
+      else {
+        newStringBuffer.append(str.substring(0, pos[index]));
+        newStringBuffer.append(charsReplace[index]);
+        str = str.substring(pos[index] + charsFind[index].length());
+      }
+    }
+
+    return newStringBuffer.toString();
+  }
+
+  /**
+   * Split up a string containing options into an array of strings, one for 
each option.
+   * 
+   * @param quotedOptionString
+   *          the string containing the options
+   * @return the array of options
+   * @throws Exception
+   *           in case of an unterminated string, unknown character or a parse 
error
+   */
+  public static String[] splitOptions(String quotedOptionString) throws 
Exception {
+
+    Vector<String> optionsVec = new Vector<String>();
+    String str = new String(quotedOptionString);
+    int i;
+
+    while (true) {
+
+      // trimLeft
+      i = 0;
+      while ((i < str.length()) && (Character.isWhitespace(str.charAt(i))))
+        i++;
+      str = str.substring(i);
+
+      // stop when str is empty
+      if (str.length() == 0)
+        break;
+
+      // if str start with a double quote
+      if (str.charAt(0) == '"') {
+
+        // find the first not anti-slached double quote
+        i = 1;
+        while (i < str.length()) {
+          if (str.charAt(i) == str.charAt(0))
+            break;
+          if (str.charAt(i) == '\\') {
+            i += 1;
+            if (i >= str.length())
+              throw new Exception("String should not finish with \\");
+          }
+          i += 1;
+        }
+        if (i >= str.length())
+          throw new Exception("Quote parse error.");
+
+        // add the founded string to the option vector (without quotes)
+        String optStr = str.substring(1, i);
+        optStr = unbackQuoteChars(optStr);
+        optionsVec.addElement(optStr);
+        str = str.substring(i + 1);
+      } else {
+        // find first whiteSpace
+        i = 0;
+        while ((i < str.length()) && (!Character.isWhitespace(str.charAt(i))))
+          i++;
+
+        // add the founded string to the option vector
+        String optStr = str.substring(0, i);
+        optionsVec.addElement(optStr);
+        str = str.substring(i);
+      }
+    }
+
+    // convert optionsVec to an array of String
+    String[] options = new String[optionsVec.size()];
+    for (i = 0; i < optionsVec.size(); i++) {
+      options[i] = (String) optionsVec.elementAt(i);
+    }
+    return options;
+  }
+
+  /**
+   * Joins all the options in an option array into a single string, as might 
be used on the command line.
+   * 
+   * @param optionArray
+   *          the array of options
+   * @return the string containing all options.
+   */
+  public static String joinOptions(String[] optionArray) {
+
+    String optionString = "";
+    for (int i = 0; i < optionArray.length; i++) {
+      if (optionArray[i].equals("")) {
+        continue;
+      }
+      boolean escape = false;
+      for (int n = 0; n < optionArray[i].length(); n++) {
+        if (Character.isWhitespace(optionArray[i].charAt(n))) {
+          escape = true;
+          break;
+        }
+      }
+      if (escape) {
+        optionString += '"' + backQuoteChars(optionArray[i]) + '"';
+      } else {
+        optionString += optionArray[i];
+      }
+      optionString += " ";
+    }
+    return optionString.trim();
+  }
+
+  /**
+   * Computes entropy for an array of integers.
+   * 
+   * @param counts
+   *          array of counts
+   * @return - a log2 a - b log2 b - c log2 c + (a+b+c) log2 (a+b+c) when 
given array [a b c]
+   */
+  public static/* @pure@ */double info(int counts[]) {
+
+    int total = 0;
+    double x = 0;
+    for (int j = 0; j < counts.length; j++) {
+      x -= xlogx(counts[j]);
+      total += counts[j];
+    }
+    return x + xlogx(total);
+  }
+
+  /**
+   * Tests if a is smaller or equal to b.
+   * 
+   * @param a
+   *          a double
+   * @param b
+   *          a double
+   */
+  public static/* @pure@ */boolean smOrEq(double a, double b) {
+
+    return (a - b < SMALL);
+  }
+
+  /**
+   * Tests if a is greater or equal to b.
+   * 
+   * @param a
+   *          a double
+   * @param b
+   *          a double
+   */
+  public static/* @pure@ */boolean grOrEq(double a, double b) {
+
+    return (b - a < SMALL);
+  }
+
+  /**
+   * Tests if a is smaller than b.
+   * 
+   * @param a
+   *          a double
+   * @param b
+   *          a double
+   */
+  public static/* @pure@ */boolean sm(double a, double b) {
+
+    return (b - a > SMALL);
+  }
+
+  /**
+   * Tests if a is greater than b.
+   * 
+   * @param a
+   *          a double
+   * @param b
+   *          a double
+   */
+  public static/* @pure@ */boolean gr(double a, double b) {
+
+    return (a - b > SMALL);
+  }
+
+  /**
+   * Returns the kth-smallest value in the array.
+   * 
+   * @param array
+   *          the array of integers
+   * @param k
+   *          the value of k
+   * @return the kth-smallest value
+   */
+  public static double kthSmallestValue(int[] array, int k) {
+
+    int[] index = new int[array.length];
+
+    for (int i = 0; i < index.length; i++) {
+      index[i] = i;
+    }
+
+    return array[index[select(array, index, 0, array.length - 1, k)]];
+  }
+
+  /**
+   * Returns the kth-smallest value in the array
+   * 
+   * @param array
+   *          the array of double
+   * @param k
+   *          the value of k
+   * @return the kth-smallest value
+   */
+  public static double kthSmallestValue(double[] array, int k) {
+
+    int[] index = new int[array.length];
+
+    for (int i = 0; i < index.length; i++) {
+      index[i] = i;
+    }
+
+    return array[index[select(array, index, 0, array.length - 1, k)]];
+  }
+
+  /**
+   * Returns the logarithm of a for base 2.
+   * 
+   * @param a
+   *          a double
+   * @return the logarithm for base 2
+   */
+  public static/* @pure@ */double log2(double a) {
+
+    return Math.log(a) / log2;
+  }
+
+  /**
+   * 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;
+  }
+
+  /**
+   * Returns index of maximum element in a given array of integers. First 
maximum is returned.
+   * 
+   * @param ints
+   *          the array of integers
+   * @return the index of the maximum element
+   */
+  public static/* @pure@ */int maxIndex(int[] ints) {
+
+    int maximum = 0;
+    int maxIndex = 0;
+
+    for (int i = 0; i < ints.length; i++) {
+      if ((i == 0) || (ints[i] > maximum)) {
+        maxIndex = i;
+        maximum = ints[i];
+      }
+    }
+
+    return maxIndex;
+  }
+
+  /**
+   * Computes the mean for an array of doubles.
+   * 
+   * @param vector
+   *          the array
+   * @return the mean
+   */
+  public static/* @pure@ */double mean(double[] vector) {
+
+    double sum = 0;
+
+    if (vector.length == 0) {
+      return 0;
+    }
+    for (int i = 0; i < vector.length; i++) {
+      sum += vector[i];
+    }
+    return sum / (double) vector.length;
+  }
+
+  /**
+   * Returns index of minimum element in a given array of integers. First 
minimum is returned.
+   * 
+   * @param ints
+   *          the array of integers
+   * @return the index of the minimum element
+   */
+  public static/* @pure@ */int minIndex(int[] ints) {
+
+    int minimum = 0;
+    int minIndex = 0;
+
+    for (int i = 0; i < ints.length; i++) {
+      if ((i == 0) || (ints[i] < minimum)) {
+        minIndex = i;
+        minimum = ints[i];
+      }
+    }
+
+    return minIndex;
+  }
+
+  /**
+   * Returns index of minimum element in a given array of doubles. First 
minimum is returned.
+   * 
+   * @param doubles
+   *          the array of doubles
+   * @return the index of the minimum element
+   */
+  public static/* @pure@ */int minIndex(double[] doubles) {
+
+    double minimum = 0;
+    int minIndex = 0;
+
+    for (int i = 0; i < doubles.length; i++) {
+      if ((i == 0) || (doubles[i] < minimum)) {
+        minIndex = i;
+        minimum = doubles[i];
+      }
+    }
+
+    return minIndex;
+  }
+
+  /**
+   * Normalizes the doubles in the array by their sum.
+   * 
+   * @param doubles
+   *          the array of double
+   * @exception IllegalArgumentException
+   *              if sum is Zero or NaN
+   */
+  public static void normalize(double[] doubles) {
+
+    double sum = 0;
+    for (int i = 0; i < doubles.length; i++) {
+      sum += doubles[i];
+    }
+    normalize(doubles, sum);
+  }
+
+  /**
+   * Normalizes the doubles in the array using the given value.
+   * 
+   * @param doubles
+   *          the array of double
+   * @param sum
+   *          the value by which the doubles are to be normalized
+   * @exception IllegalArgumentException
+   *              if sum is zero or NaN
+   */
+  public static void normalize(double[] doubles, double sum) {
+
+    if (Double.isNaN(sum)) {
+      throw new IllegalArgumentException("Can't normalize array. Sum is NaN.");
+    }
+    if (sum == 0) {
+      // Maybe this should just be a return.
+      throw new IllegalArgumentException("Can't normalize array. Sum is 
zero.");
+    }
+    for (int i = 0; i < doubles.length; i++) {
+      doubles[i] /= sum;
+    }
+  }
+
+  /**
+   * Converts an array containing the natural logarithms of probabilities 
stored in a vector back into probabilities.
+   * The probabilities are assumed to sum to one.
+   * 
+   * @param a
+   *          an array holding the natural logarithms of the probabilities
+   * @return the converted array
+   */
+  public static double[] logs2probs(double[] a) {
+
+    double max = a[maxIndex(a)];
+    double sum = 0.0;
+
+    double[] result = new double[a.length];
+    for (int i = 0; i < a.length; i++) {
+      result[i] = Math.exp(a[i] - max);
+      sum += result[i];
+    }
+
+    normalize(result, sum);
+
+    return result;
+  }
+
+  /**
+   * Returns the log-odds for a given probabilitiy.
+   * 
+   * @param prob
+   *          the probabilitiy
+   * 
+   * @return the log-odds after the probability has been mapped to 
[Utils.SMALL, 1-Utils.SMALL]
+   */
+  public static/* @pure@ */double probToLogOdds(double prob) {
+
+    if (gr(prob, 1) || (sm(prob, 0))) {
+      throw new IllegalArgumentException("probToLogOdds: probability must " +
+          "be in [0,1] " + prob);
+    }
+    double p = SMALL + (1.0 - 2 * SMALL) * prob;
+    return Math.log(p / (1 - p));
+  }
+
+  /**
+   * Rounds a double to the next nearest integer value. The JDK version of it 
doesn't work properly.
+   * 
+   * @param value
+   *          the double value
+   * @return the resulting integer value
+   */
+  public static/* @pure@ */int round(double value) {
+
+    int roundedValue = value > 0
+        ? (int) (value + 0.5)
+        : -(int) (Math.abs(value) + 0.5);
+
+    return roundedValue;
+  }
+
+  /**
+   * Rounds a double to the next nearest integer value in a probabilistic 
fashion (e.g. 0.8 has a 20% chance of being
+   * rounded down to 0 and a 80% chance of being rounded up to 1). In the 
limit, the average of the rounded numbers
+   * generated by this procedure should converge to the original double.
+   * 
+   * @param value
+   *          the double value
+   * @param rand
+   *          the random number generator
+   * @return the resulting integer value
+   */
+  public static int probRound(double value, Random rand) {
+
+    if (value >= 0) {
+      double lower = Math.floor(value);
+      double prob = value - lower;
+      if (rand.nextDouble() < prob) {
+        return (int) lower + 1;
+      } else {
+        return (int) lower;
+      }
+    } else {
+      double lower = Math.floor(Math.abs(value));
+      double prob = Math.abs(value) - lower;
+      if (rand.nextDouble() < prob) {
+        return -((int) lower + 1);
+      } else {
+        return -(int) lower;
+      }
+    }
+  }
+
+  /**
+   * Rounds a double to the given number of decimal places.
+   * 
+   * @param value
+   *          the double value
+   * @param afterDecimalPoint
+   *          the number of digits after the decimal point
+   * @return the double rounded to the given precision
+   */
+  public static/* @pure@ */double roundDouble(double value, int 
afterDecimalPoint) {
+
+    double mask = Math.pow(10.0, (double) afterDecimalPoint);
+
+    return (double) (Math.round(value * mask)) / mask;
+  }
+
+  /**
+   * Sorts a given array of integers in ascending order and returns an array 
of integers with the positions of the
+   * elements of the original array in the sorted array. The sort is stable. 
(Equal elements remain in their original
+   * order.)
+   * 
+   * @param array
+   *          this array is not changed by the method!
+   * @return an array of integers with the positions in the sorted array.
+   */
+  public static/* @pure@ */int[] sort(int[] array) {
+
+    int[] index = new int[array.length];
+    int[] newIndex = new int[array.length];
+    int[] helpIndex;
+    int numEqual;
+
+    for (int i = 0; i < index.length; i++) {
+      index[i] = i;
+    }
+    quickSort(array, index, 0, array.length - 1);
+
+    // Make sort stable
+    int i = 0;
+    while (i < index.length) {
+      numEqual = 1;
+      for (int j = i + 1; ((j < index.length)
+      && (array[index[i]] == array[index[j]])); j++) {
+        numEqual++;
+      }
+      if (numEqual > 1) {
+        helpIndex = new int[numEqual];
+        for (int j = 0; j < numEqual; j++) {
+          helpIndex[j] = i + j;
+        }
+        quickSort(index, helpIndex, 0, numEqual - 1);
+        for (int j = 0; j < numEqual; j++) {
+          newIndex[i + j] = index[helpIndex[j]];
+        }
+        i += numEqual;
+      } else {
+        newIndex[i] = index[i];
+        i++;
+      }
+    }
+    return newIndex;
+  }
+
+  /**
+   * Sorts a given array of doubles in ascending order and returns an array of 
integers with the positions of the
+   * elements of the original array in the sorted array. NOTE THESE CHANGES: 
the sort is no longer stable and it doesn't
+   * use safe floating-point comparisons anymore. Occurrences of Double.NaN 
are treated as Double.MAX_VALUE
+   * 
+   * @param array
+   *          this array is not changed by the method!
+   * @return an array of integers with the positions in the sorted array.
+   */
+  public static/* @pure@ */int[] sort(/* @non_null@ */double[] array) {
+
+    int[] index = new int[array.length];
+    array = (double[]) array.clone();
+    for (int i = 0; i < index.length; i++) {
+      index[i] = i;
+      if (Double.isNaN(array[i])) {
+        array[i] = Double.MAX_VALUE;
+      }
+    }
+    quickSort(array, index, 0, array.length - 1);
+    return index;
+  }
+
+  /**
+   * Sorts a given array of doubles in ascending order and returns an array of 
integers with the positions of the
+   * elements of the original array in the sorted array. The sort is stable 
(Equal elements remain in their original
+   * order.) Occurrences of Double.NaN are treated as Double.MAX_VALUE
+   * 
+   * @param array
+   *          this array is not changed by the method!
+   * @return an array of integers with the positions in the sorted array.
+   */
+  public static/* @pure@ */int[] stableSort(double[] array) {
+
+    int[] index = new int[array.length];
+    int[] newIndex = new int[array.length];
+    int[] helpIndex;
+    int numEqual;
+
+    array = (double[]) array.clone();
+    for (int i = 0; i < index.length; i++) {
+      index[i] = i;
+      if (Double.isNaN(array[i])) {
+        array[i] = Double.MAX_VALUE;
+      }
+    }
+    quickSort(array, index, 0, array.length - 1);
+
+    // Make sort stable
+
+    int i = 0;
+    while (i < index.length) {
+      numEqual = 1;
+      for (int j = i + 1; ((j < index.length) && Utils.eq(array[index[i]],
+          array[index[j]])); j++)
+        numEqual++;
+      if (numEqual > 1) {
+        helpIndex = new int[numEqual];
+        for (int j = 0; j < numEqual; j++)
+          helpIndex[j] = i + j;
+        quickSort(index, helpIndex, 0, numEqual - 1);
+        for (int j = 0; j < numEqual; j++)
+          newIndex[i + j] = index[helpIndex[j]];
+        i += numEqual;
+      } else {
+        newIndex[i] = index[i];
+        i++;
+      }
+    }
+
+    return newIndex;
+  }
+
+  /**
+   * Computes the variance for an array of doubles.
+   * 
+   * @param vector
+   *          the array
+   * @return the variance
+   */
+  public static/* @pure@ */double variance(double[] vector) {
+
+    double sum = 0, sumSquared = 0;
+
+    if (vector.length <= 1) {
+      return 0;
+    }
+    for (int i = 0; i < vector.length; i++) {
+      sum += vector[i];
+      sumSquared += (vector[i] * vector[i]);
+    }
+    double result = (sumSquared - (sum * sum / (double) vector.length)) /
+        (double) (vector.length - 1);
+
+    // We don't like negative variance
+    if (result < 0) {
+      return 0;
+    } else {
+      return result;
+    }
+  }
+
+  /**
+   * Computes the sum of the elements of an array of doubles.
+   * 
+   * @param doubles
+   *          the array of double
+   * @return the sum of the elements
+   */
+  public static/* @pure@ */double sum(double[] doubles) {
+
+    double sum = 0;
+
+    for (int i = 0; i < doubles.length; i++) {
+      sum += doubles[i];
+    }
+    return sum;
+  }
+
+  /**
+   * Computes the sum of the elements of an array of integers.
+   * 
+   * @param ints
+   *          the array of integers
+   * @return the sum of the elements
+   */
+  public static/* @pure@ */int sum(int[] ints) {
+
+    int sum = 0;
+
+    for (int i = 0; i < ints.length; i++) {
+      sum += ints[i];
+    }
+    return sum;
+  }
+
+  /**
+   * Returns c*log2(c) for a given integer value c.
+   * 
+   * @param c
+   *          an integer value
+   * @return c*log2(c) (but is careful to return 0 if c is 0)
+   */
+  public static/* @pure@ */double xlogx(int c) {
+
+    if (c == 0) {
+      return 0.0;
+    }
+    return c * Utils.log2((double) c);
+  }
+
+  /**
+   * Partitions the instances around a pivot. Used by quicksort and 
kthSmallestValue.
+   * 
+   * @param array
+   *          the array of doubles to be sorted
+   * @param index
+   *          the index into the array of doubles
+   * @param l
+   *          the first index of the subset
+   * @param r
+   *          the last index of the subset
+   * 
+   * @return the index of the middle element
+   */
+  private static int partition(double[] array, int[] index, int l, int r) {
+
+    double pivot = array[index[(l + r) / 2]];
+    int help;
+
+    while (l < r) {
+      while ((array[index[l]] < pivot) && (l < r)) {
+        l++;
+      }
+      while ((array[index[r]] > pivot) && (l < r)) {
+        r--;
+      }
+      if (l < r) {
+        help = index[l];
+        index[l] = index[r];
+        index[r] = help;
+        l++;
+        r--;
+      }
+    }
+    if ((l == r) && (array[index[r]] > pivot)) {
+      r--;
+    }
+
+    return r;
+  }
+
+  /**
+   * Partitions the instances around a pivot. Used by quicksort and 
kthSmallestValue.
+   * 
+   * @param array
+   *          the array of integers to be sorted
+   * @param index
+   *          the index into the array of integers
+   * @param l
+   *          the first index of the subset
+   * @param r
+   *          the last index of the subset
+   * 
+   * @return the index of the middle element
+   */
+  private static int partition(int[] array, int[] index, int l, int r) {
+
+    double pivot = array[index[(l + r) / 2]];
+    int help;
+
+    while (l < r) {
+      while ((array[index[l]] < pivot) && (l < r)) {
+        l++;
+      }
+      while ((array[index[r]] > pivot) && (l < r)) {
+        r--;
+      }
+      if (l < r) {
+        help = index[l];
+        index[l] = index[r];
+        index[r] = help;
+        l++;
+        r--;
+      }
+    }
+    if ((l == r) && (array[index[r]] > pivot)) {
+      r--;
+    }
+
+    return r;
+  }
+
+  /**
+   * Implements quicksort according to Manber's "Introduction to Algorithms".
+   * 
+   * @param array
+   *          the array of doubles to be sorted
+   * @param index
+   *          the index into the array of doubles
+   * @param left
+   *          the first index of the subset to be sorted
+   * @param right
+   *          the last index of the subset to be sorted
+   */
+  // @ requires 0 <= first && first <= right && right < array.length;
+  // @ requires (\forall int i; 0 <= i && i < index.length; 0 <= index[i] &&
+  // index[i] < array.length);
+  // @ requires array != index;
+  // assignable index;
+  private static void quickSort(/* @non_null@ */double[] array, /* @non_null@ 
*/int[] index,
+      int left, int right) {
+
+    if (left < right) {
+      int middle = partition(array, index, left, right);
+      quickSort(array, index, left, middle);
+      quickSort(array, index, middle + 1, right);
+    }
+  }
+
+  /**
+   * Implements quicksort according to Manber's "Introduction to Algorithms".
+   * 
+   * @param array
+   *          the array of integers to be sorted
+   * @param index
+   *          the index into the array of integers
+   * @param left
+   *          the first index of the subset to be sorted
+   * @param right
+   *          the last index of the subset to be sorted
+   */
+  // @ requires 0 <= first && first <= right && right < array.length;
+  // @ requires (\forall int i; 0 <= i && i < index.length; 0 <= index[i] &&
+  // index[i] < array.length);
+  // @ requires array != index;
+  // assignable index;
+  private static void quickSort(/* @non_null@ */int[] array, /* @non_null@ 
*/int[] index,
+      int left, int right) {
+
+    if (left < right) {
+      int middle = partition(array, index, left, right);
+      quickSort(array, index, left, middle);
+      quickSort(array, index, middle + 1, right);
+    }
+  }
+
+  /**
+   * Implements computation of the kth-smallest element according to Manber's 
"Introduction to Algorithms".
+   * 
+   * @param array
+   *          the array of double
+   * @param index
+   *          the index into the array of doubles
+   * @param left
+   *          the first index of the subset
+   * @param right
+   *          the last index of the subset
+   * @param k
+   *          the value of k
+   * 
+   * @return the index of the kth-smallest element
+   */
+  // @ requires 0 <= first && first <= right && right < array.length;
+  private static int select(/* @non_null@ */double[] array, /* @non_null@ 
*/int[] index,
+      int left, int right, int k) {
+
+    if (left == right) {
+      return left;
+    } else {
+      int middle = partition(array, index, left, right);
+      if ((middle - left + 1) >= k) {
+        return select(array, index, left, middle, k);
+      } else {
+        return select(array, index, middle + 1, right, k - (middle - left + 
1));
+      }
+    }
+  }
+
+  /**
+   * Converts a File's absolute path to a path relative to the user (ie start) 
directory. Includes an additional
+   * workaround for Cygwin, which doesn't like upper case drive letters.
+   * 
+   * @param absolute
+   *          the File to convert to relative path
+   * @return a File with a path that is relative to the user's directory
+   * @exception Exception
+   *              if the path cannot be constructed
+   */
+  public static File convertToRelativePath(File absolute) throws Exception {
+    File result;
+    String fileStr;
+
+    result = null;
+
+    // if we're running windows, it could be Cygwin
+    if (File.separator.equals("\\")) {
+      // Cygwin doesn't like upper case drives -> try lower case drive
+      try {
+        fileStr = absolute.getPath();
+        fileStr = fileStr.substring(0, 1).toLowerCase()
+            + fileStr.substring(1);
+        result = createRelativePath(new File(fileStr));
+      } catch (Exception e) {
+        // no luck with Cygwin workaround, convert it like it is
+        result = createRelativePath(absolute);
+      }
+    }
+    else {
+      result = createRelativePath(absolute);
+    }
+
+    return result;
+  }
+
+  /**
+   * Converts a File's absolute path to a path relative to the user (ie start) 
directory.
+   * 
+   * @param absolute
+   *          the File to convert to relative path
+   * @return a File with a path that is relative to the user's directory
+   * @exception Exception
+   *              if the path cannot be constructed
+   */
+  protected static File createRelativePath(File absolute) throws Exception {
+    File userDir = new File(System.getProperty("user.dir"));
+    String userPath = userDir.getAbsolutePath() + File.separator;
+    String targetPath = (new File(absolute.getParent())).getPath()
+        + File.separator;
+    String fileName = absolute.getName();
+    StringBuffer relativePath = new StringBuffer();
+    // relativePath.append("."+File.separator);
+    // System.err.println("User dir "+userPath);
+    // System.err.println("Target path "+targetPath);
+
+    // file is in user dir (or subdir)
+    int subdir = targetPath.indexOf(userPath);
+    if (subdir == 0) {
+      if (userPath.length() == targetPath.length()) {
+        relativePath.append(fileName);
+      } else {
+        int ll = userPath.length();
+        relativePath.append(targetPath.substring(ll));
+        relativePath.append(fileName);
+      }
+    } else {
+      int sepCount = 0;
+      String temp = new String(userPath);
+      while (temp.indexOf(File.separator) != -1) {
+        int ind = temp.indexOf(File.separator);
+        sepCount++;
+        temp = temp.substring(ind + 1, temp.length());
+      }
+
+      String targetTemp = new String(targetPath);
+      String userTemp = new String(userPath);
+      int tcount = 0;
+      while (targetTemp.indexOf(File.separator) != -1) {
+        int ind = targetTemp.indexOf(File.separator);
+        int ind2 = userTemp.indexOf(File.separator);
+        String tpart = targetTemp.substring(0, ind + 1);
+        String upart = userTemp.substring(0, ind2 + 1);
+        if (tpart.compareTo(upart) != 0) {
+          if (tcount == 0) {
+            tcount = -1;
+          }
+          break;
+        }
+        tcount++;
+        targetTemp = targetTemp.substring(ind + 1, targetTemp.length());
+        userTemp = userTemp.substring(ind2 + 1, userTemp.length());
+      }
+      if (tcount == -1) {
+        // then target file is probably on another drive (under windows)
+        throw new Exception("Can't construct a path to file relative to user "
+            + "dir.");
+      }
+      if (targetTemp.indexOf(File.separator) == -1) {
+        targetTemp = "";
+      }
+      for (int i = 0; i < sepCount - tcount; i++) {
+        relativePath.append(".." + File.separator);
+      }
+      relativePath.append(targetTemp + fileName);
+    }
+    // System.err.println("new path : "+relativePath.toString());
+    return new File(relativePath.toString());
+  }
+
+  /**
+   * Implements computation of the kth-smallest element according to Manber's 
"Introduction to Algorithms".
+   * 
+   * @param array
+   *          the array of integers
+   * @param index
+   *          the index into the array of integers
+   * @param left
+   *          the first index of the subset
+   * @param right
+   *          the last index of the subset
+   * @param k
+   *          the value of k
+   * 
+   * @return the index of the kth-smallest element
+   */
+  // @ requires 0 <= first && first <= right && right < array.length;
+  private static int select(/* @non_null@ */int[] array, /* @non_null@ */int[] 
index,
+      int left, int right, int k) {
+
+    if (left == right) {
+      return left;
+    } else {
+      int middle = partition(array, index, left, right);
+      if ((middle - left + 1) >= k) {
+        return select(array, index, left, middle, k);
+      } else {
+        return select(array, index, middle + 1, right, k - (middle - left + 
1));
+      }
+    }
+  }
+
+  /**
+   * Breaks up the string, if wider than "columns" characters.
+   * 
+   * @param s
+   *          the string to process
+   * @param columns
+   *          the width in columns
+   * @return the processed string
+   */
+  public static String[] breakUp(String s, int columns) {
+    Vector<String> result;
+    String line;
+    BreakIterator boundary;
+    int boundaryStart;
+    int boundaryEnd;
+    String word;
+    String punctuation;
+    int i;
+    String[] lines;
+
+    result = new Vector<String>();
+    punctuation = " .,;:!?'\"";
+    lines = s.split("\n");
+
+    for (i = 0; i < lines.length; i++) {
+      boundary = BreakIterator.getWordInstance();
+      boundary.setText(lines[i]);
+      boundaryStart = boundary.first();
+      boundaryEnd = boundary.next();
+      line = "";
+
+      while (boundaryEnd != BreakIterator.DONE) {
+        word = lines[i].substring(boundaryStart, boundaryEnd);
+        if (line.length() >= columns) {
+          if (word.length() == 1) {
+            if (punctuation.indexOf(word.charAt(0)) > -1) {
+              line += word;
+              word = "";
+            }
+          }
+          result.add(line);
+          line = "";
+        }
+        line += word;
+        boundaryStart = boundaryEnd;
+        boundaryEnd = boundary.next();
+      }
+      if (line.length() > 0)
+        result.add(line);
+    }
+
+    return result.toArray(new String[result.size()]);
+  }
+
+  /**
+   * Creates a new instance of an object given it's class name and (optional) 
arguments to pass to it's setOptions
+   * method. If the object implements OptionHandler and the options parameter 
is non-null, the object will have it's
+   * options set. Example use:
+   * <p>
+   * 
+   * <code> <pre>
+   * String classifierName = Utils.getOption('W', options);
+   * Classifier c = (Classifier)Utils.forName(Classifier.class,
+   *                                          classifierName,
+   *                                          options);
+   * setClassifier(c);
+   * </pre></code>
+   * 
+   * @param classType
+   *          the class that the instantiated object should be assignable to 
-- an exception is thrown if this is not
+   *          the case
+   * @param className
+   *          the fully qualified class name of the object
+   * @param options
+   *          an array of options suitable for passing to setOptions. May be 
null. Any options accepted by the object
+   *          will be removed from the array.
+   * @return the newly created object, ready for use.
+   * @exception Exception
+   *              if the class name is invalid, or if the class is not 
assignable to the desired class type, or the
+   *              options supplied are not acceptable to the object
+   */
+  public static Object forName(Class<?> classType,
+      String className,
+      String[] options) throws Exception {
+
+    Class<?> c = null;
+    try {
+      c = Class.forName(className);
+    } catch (Exception ex) {
+      throw new Exception("Can't find class called: " + className);
+    }
+    if (!classType.isAssignableFrom(c)) {
+      throw new Exception(classType.getName() + " is not assignable from "
+          + className);
+    }
+    Object o = c.newInstance();
+    /*
+     * if ((o instanceof OptionHandler) && (options != null)) {
+     * ((OptionHandler)o).setOptions(options);
+     * Utils.checkForRemainingOptions(options); }
+     */
+    return o;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/LearningCurve.java
----------------------------------------------------------------------
diff --git 
a/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/LearningCurve.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/LearningCurve.java
new file mode 100644
index 0000000..427e01d
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/LearningCurve.java
@@ -0,0 +1,132 @@
+package org.apache.samoa.moa.evaluation;
+
+/*
+ * #%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;
+import org.apache.samoa.moa.core.DoubleVector;
+import org.apache.samoa.moa.core.Measurement;
+import org.apache.samoa.moa.core.StringUtils;
+
+/**
+ * Class that stores and keeps the history of evaluation measurements.
+ * 
+ * @author Richard Kirkby ([email protected])
+ * @version $Revision: 7 $
+ */
+public class LearningCurve extends AbstractMOAObject {
+
+  private static final long serialVersionUID = 1L;
+
+  protected List<String> measurementNames = new ArrayList<String>();
+
+  protected List<double[]> measurementValues = new ArrayList<double[]>();
+
+  public LearningCurve(String orderingMeasurementName) {
+    this.measurementNames.add(orderingMeasurementName);
+  }
+
+  public String getOrderingMeasurementName() {
+    return this.measurementNames.get(0);
+  }
+
+  public void insertEntry(LearningEvaluation learningEvaluation) {
+    Measurement[] measurements = learningEvaluation.getMeasurements();
+    Measurement orderMeasurement = Measurement.getMeasurementNamed(
+        getOrderingMeasurementName(), measurements);
+    if (orderMeasurement == null) {
+      throw new IllegalArgumentException();
+    }
+    DoubleVector entryVals = new DoubleVector();
+    for (Measurement measurement : measurements) {
+      entryVals.setValue(addMeasurementName(measurement.getName()),
+          measurement.getValue());
+    }
+    double orderVal = orderMeasurement.getValue();
+    int index = 0;
+    while ((index < this.measurementValues.size())
+        && (orderVal > this.measurementValues.get(index)[0])) {
+      index++;
+    }
+    this.measurementValues.add(index, entryVals.getArrayRef());
+  }
+
+  public int numEntries() {
+    return this.measurementValues.size();
+  }
+
+  protected int addMeasurementName(String name) {
+    int index = this.measurementNames.indexOf(name);
+    if (index < 0) {
+      index = this.measurementNames.size();
+      this.measurementNames.add(name);
+    }
+    return index;
+  }
+
+  public String headerToString() {
+    StringBuilder sb = new StringBuilder();
+    boolean first = true;
+    for (String name : this.measurementNames) {
+      if (!first) {
+        sb.append(',');
+      } else {
+        first = false;
+      }
+      sb.append(name);
+    }
+    return sb.toString();
+  }
+
+  public String entryToString(int entryIndex) {
+    StringBuilder sb = new StringBuilder();
+    double[] vals = this.measurementValues.get(entryIndex);
+    for (int i = 0; i < this.measurementNames.size(); i++) {
+      if (i > 0) {
+        sb.append(',');
+      }
+      if ((i >= vals.length) || Double.isNaN(vals[i])) {
+        sb.append('?');
+      } else {
+        sb.append(Double.toString(vals[i]));
+      }
+    }
+    return sb.toString();
+  }
+
+  @Override
+  public void getDescription(StringBuilder sb, int indent) {
+    sb.append(headerToString());
+    for (int i = 0; i < numEntries(); i++) {
+      StringUtils.appendNewlineIndented(sb, indent, entryToString(i));
+    }
+  }
+
+  public double getMeasurement(int entryIndex, int measurementIndex) {
+    return this.measurementValues.get(entryIndex)[measurementIndex];
+  }
+
+  public String getMeasurementName(int measurementIndex) {
+    return this.measurementNames.get(measurementIndex);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/LearningEvaluation.java
----------------------------------------------------------------------
diff --git 
a/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/LearningEvaluation.java
 
b/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/LearningEvaluation.java
new file mode 100644
index 0000000..993667d
--- /dev/null
+++ 
b/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/LearningEvaluation.java
@@ -0,0 +1,64 @@
+package org.apache.samoa.moa.evaluation;
+
+/*
+ * #%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.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.samoa.moa.AbstractMOAObject;
+import org.apache.samoa.moa.core.Measurement;
+import org.apache.samoa.moa.learners.Learner;
+
+/**
+ * Class that stores an array of evaluation measurements.
+ * 
+ * @author Richard Kirkby ([email protected])
+ * @version $Revision: 7 $
+ */
+public class LearningEvaluation extends AbstractMOAObject {
+
+  private static final long serialVersionUID = 1L;
+
+  protected Measurement[] measurements;
+
+  public LearningEvaluation(Measurement[] measurements) {
+    this.measurements = measurements.clone();
+  }
+
+  public LearningEvaluation(Measurement[] evaluationMeasurements,
+      LearningPerformanceEvaluator cpe, Learner model) {
+    List<Measurement> measurementList = new LinkedList<Measurement>();
+    measurementList.addAll(Arrays.asList(evaluationMeasurements));
+    measurementList.addAll(Arrays.asList(cpe.getPerformanceMeasurements()));
+    measurementList.addAll(Arrays.asList(model.getModelMeasurements()));
+    this.measurements = measurementList.toArray(new 
Measurement[measurementList.size()]);
+  }
+
+  public Measurement[] getMeasurements() {
+    return this.measurements.clone();
+  }
+
+  @Override
+  public void getDescription(StringBuilder sb, int indent) {
+    Measurement.getMeasurementsDescription(this.measurements, sb, indent);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/LearningPerformanceEvaluator.java
----------------------------------------------------------------------
diff --git 
a/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/LearningPerformanceEvaluator.java
 
b/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/LearningPerformanceEvaluator.java
new file mode 100644
index 0000000..7a0a8af
--- /dev/null
+++ 
b/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/LearningPerformanceEvaluator.java
@@ -0,0 +1,59 @@
+package org.apache.samoa.moa.evaluation;
+
+/*
+ * #%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.MOAObject;
+import org.apache.samoa.moa.core.Example;
+import org.apache.samoa.moa.core.Measurement;
+
+/**
+ * Interface implemented by learner evaluators to monitor the results of the 
learning process.
+ * 
+ * @author Richard Kirkby ([email protected])
+ * @version $Revision: 7 $
+ */
+public interface LearningPerformanceEvaluator<E extends Example> extends 
MOAObject {
+
+  /**
+   * Resets this evaluator. It must be similar to starting a new evaluator 
from scratch.
+   * 
+   */
+  public void reset();
+
+  /**
+   * Adds a learning result to this evaluator.
+   * 
+   * @param example
+   *          the example to be classified
+   * @param classVotes
+   *          an array containing the estimated membership probabilities of 
the test instance in each class
+   * @return an array of measurements monitored in this evaluator
+   */
+  public void addResult(E example, double[] classVotes);
+
+  /**
+   * Gets the current measurements monitored by this evaluator.
+   * 
+   * @return an array of measurements monitored by this evaluator
+   */
+  public Measurement[] getPerformanceMeasurements();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/MeasureCollection.java
----------------------------------------------------------------------
diff --git 
a/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/MeasureCollection.java
 
b/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/MeasureCollection.java
new file mode 100644
index 0000000..42e6d49
--- /dev/null
+++ 
b/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/MeasureCollection.java
@@ -0,0 +1,262 @@
+package org.apache.samoa.moa.evaluation;
+
+/*
+ * #%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.HashMap;
+
+import org.apache.samoa.moa.AbstractMOAObject;
+import org.apache.samoa.moa.cluster.Clustering;
+import org.apache.samoa.moa.core.DataPoint;
+
+public abstract class MeasureCollection extends AbstractMOAObject {
+  private String[] names;
+  private ArrayList<Double>[] values;
+  private ArrayList<Double>[] sortedValues;
+  private ArrayList<String> events;
+
+  private double[] minValue;
+  private double[] maxValue;
+  private double[] sumValues;
+  private boolean[] enabled;
+  private boolean[] corrupted;
+  private double time;
+  private boolean debug = true;
+  private MembershipMatrix mm = null;
+
+  private HashMap<String, Integer> map;
+
+  private int numMeasures = 0;
+
+  public MeasureCollection() {
+    names = getNames();
+    numMeasures = names.length;
+    map = new HashMap<String, Integer>(numMeasures);
+    for (int i = 0; i < names.length; i++) {
+      map.put(names[i], i);
+    }
+    values = (ArrayList<Double>[]) new ArrayList[numMeasures];
+    sortedValues = (ArrayList<Double>[]) new ArrayList[numMeasures];
+    maxValue = new double[numMeasures];
+    minValue = new double[numMeasures];
+    sumValues = new double[numMeasures];
+    corrupted = new boolean[numMeasures];
+    enabled = getDefaultEnabled();
+    time = 0;
+    events = new ArrayList<String>();
+
+    for (int i = 0; i < numMeasures; i++) {
+      values[i] = new ArrayList<Double>();
+      sortedValues[i] = new ArrayList<Double>();
+      maxValue[i] = Double.MIN_VALUE;
+      minValue[i] = Double.MAX_VALUE;
+      corrupted[i] = false;
+      sumValues[i] = 0.0;
+    }
+
+  }
+
+  protected abstract String[] getNames();
+
+  public void addValue(int index, double value) {
+    if (Double.isNaN(value)) {
+      if (debug)
+        System.out.println("NaN for " + names[index]);
+      corrupted[index] = true;
+    }
+    if (value < 0) {
+      if (debug)
+        System.out.println("Negative value for " + names[index]);
+    }
+
+    values[index].add(value);
+    sumValues[index] += value;
+    if (value < minValue[index])
+      minValue[index] = value;
+    if (value > maxValue[index])
+      maxValue[index] = value;
+  }
+
+  protected void addValue(String name, double value) {
+    if (map.containsKey(name)) {
+      addValue(map.get(name), value);
+    }
+    else {
+      System.out.println(name + " is not a valid measure key, no value added");
+    }
+  }
+
+  // add an empty entry e.g. if evaluation crashed internally
+  public void addEmptyValue(int index) {
+    values[index].add(Double.NaN);
+    corrupted[index] = true;
+  }
+
+  public int getNumMeasures() {
+    return numMeasures;
+  }
+
+  public String getName(int index) {
+    return names[index];
+  }
+
+  public double getMaxValue(int index) {
+    return maxValue[index];
+  }
+
+  public double getMinValue(int index) {
+    return minValue[index];
+  }
+
+  public double getLastValue(int index) {
+    if (values[index].size() < 1)
+      return Double.NaN;
+    return values[index].get(values[index].size() - 1);
+  }
+
+  public double getMean(int index) {
+    if (corrupted[index] || values[index].size() < 1)
+      return Double.NaN;
+
+    return sumValues[index] / values[index].size();
+  }
+
+  private void updateSortedValues(int index) {
+    // naive implementation of insertion sort
+    for (int i = sortedValues[index].size(); i < values[index].size(); i++) {
+      double v = values[index].get(i);
+      int insertIndex = 0;
+      while (!sortedValues[index].isEmpty() && insertIndex < 
sortedValues[index].size()
+          && v > sortedValues[index].get(insertIndex))
+        insertIndex++;
+      sortedValues[index].add(insertIndex, v);
+    }
+    // for (int i = 0; i < sortedValues[index].size(); i++) {
+    // System.out.print(sortedValues[index].get(i)+" ");
+    // }
+    // System.out.println();
+  }
+
+  public void clean(int index) {
+    sortedValues[index].clear();
+  }
+
+  public double getMedian(int index) {
+    updateSortedValues(index);
+    int size = sortedValues[index].size();
+
+    if (size > 0) {
+      if (size % 2 == 1)
+        return sortedValues[index].get((int) (size / 2));
+      else
+        return (sortedValues[index].get((size - 1) / 2) + 
sortedValues[index].get((size - 1) / 2 + 1)) / 2.0;
+    }
+    return Double.NaN;
+  }
+
+  public double getLowerQuartile(int index) {
+    updateSortedValues(index);
+    int size = sortedValues[index].size();
+    if (size > 11) {
+      return sortedValues[index].get(Math.round(size * 0.25f));
+    }
+    return Double.NaN;
+  }
+
+  public double getUpperQuartile(int index) {
+    updateSortedValues(index);
+    int size = sortedValues[index].size();
+    if (size > 11) {
+      return sortedValues[index].get(Math.round(size * 0.75f - 1));
+    }
+    return Double.NaN;
+  }
+
+  public int getNumberOfValues(int index) {
+    return values[index].size();
+  }
+
+  public double getValue(int index, int i) {
+    if (i >= values[index].size())
+      return Double.NaN;
+    return values[index].get(i);
+  }
+
+  public ArrayList<Double> getAllValues(int index) {
+    return values[index];
+  }
+
+  public void setEnabled(int index, boolean value) {
+    enabled[index] = value;
+  }
+
+  public boolean isEnabled(int index) {
+    return enabled[index];
+  }
+
+  public double getMeanRunningTime() {
+    if (values[0].size() != 0)
+      return (time / 10e5 / values[0].size());
+    else
+      return 0;
+  }
+
+  protected boolean[] getDefaultEnabled() {
+    boolean[] defaults = new boolean[numMeasures];
+    for (int i = 0; i < defaults.length; i++) {
+      defaults[i] = true;
+    }
+    return defaults;
+  }
+
+  protected abstract void evaluateClustering(Clustering clustering, Clustering 
trueClustering,
+      ArrayList<DataPoint> points) throws Exception;
+
+  /*
+   * Evaluate Clustering
+   * 
+   * return Time in milliseconds
+   */
+  public double evaluateClusteringPerformance(Clustering clustering, 
Clustering trueClustering,
+      ArrayList<DataPoint> points) throws Exception {
+    long start = System.nanoTime();
+    evaluateClustering(clustering, trueClustering, points);
+    long duration = System.nanoTime() - start;
+    time += duration;
+    duration /= 10e5;
+    return duration;
+  }
+
+  public void getDescription(StringBuilder sb, int indent) {
+
+  }
+
+  public void addEventType(String type) {
+    events.add(type);
+  }
+
+  public String getEventType(int index) {
+    if (index < events.size())
+      return events.get(index);
+    else
+      return null;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/MembershipMatrix.java
----------------------------------------------------------------------
diff --git 
a/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/MembershipMatrix.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/MembershipMatrix.java
new file mode 100644
index 0000000..4387c04
--- /dev/null
+++ 
b/samoa-api/src/main/java/org/apache/samoa/moa/evaluation/MembershipMatrix.java
@@ -0,0 +1,149 @@
+package org.apache.samoa.moa.evaluation;
+
+/*
+ * #%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.HashMap;
+
+import org.apache.samoa.moa.cluster.Clustering;
+import org.apache.samoa.moa.core.DataPoint;
+
+public class MembershipMatrix {
+
+  HashMap<Integer, Integer> classmap;
+  int cluster_class_weights[][];
+  int cluster_sums[];
+  int class_sums[];
+  int total_entries;
+  int class_distribution[];
+  int total_class_entries;
+  int initalBuildTimestamp = -1;
+
+  public MembershipMatrix(Clustering foundClustering, ArrayList<DataPoint> 
points) {
+    classmap = Clustering.classValues(points);
+    // int lastID = classmap.size()-1;
+    // classmap.put(-1, lastID);
+    int numClasses = classmap.size();
+    int numCluster = foundClustering.size() + 1;
+
+    cluster_class_weights = new int[numCluster][numClasses];
+    class_distribution = new int[numClasses];
+    cluster_sums = new int[numCluster];
+    class_sums = new int[numClasses];
+    total_entries = 0;
+    total_class_entries = points.size();
+    for (int p = 0; p < points.size(); p++) {
+      int worklabel = classmap.get((int) points.get(p).classValue());
+      // real class distribution
+      class_distribution[worklabel]++;
+      boolean covered = false;
+      for (int c = 0; c < numCluster - 1; c++) {
+        double prob = 
foundClustering.get(c).getInclusionProbability(points.get(p));
+        if (prob >= 1) {
+          cluster_class_weights[c][worklabel]++;
+          class_sums[worklabel]++;
+          cluster_sums[c]++;
+          total_entries++;
+          covered = true;
+        }
+      }
+      if (!covered) {
+        cluster_class_weights[numCluster - 1][worklabel]++;
+        class_sums[worklabel]++;
+        cluster_sums[numCluster - 1]++;
+        total_entries++;
+      }
+
+    }
+
+    initalBuildTimestamp = points.get(0).getTimestamp();
+  }
+
+  public int getClusterClassWeight(int i, int j) {
+    return cluster_class_weights[i][j];
+  }
+
+  public int getClusterSum(int i) {
+    return cluster_sums[i];
+  }
+
+  public int getClassSum(int j) {
+    return class_sums[j];
+  }
+
+  public int getClassDistribution(int j) {
+    return class_distribution[j];
+  }
+
+  public int getClusterClassWeightByLabel(int cluster, int classLabel) {
+    return cluster_class_weights[cluster][classmap.get(classLabel)];
+  }
+
+  public int getClassSumByLabel(int classLabel) {
+    return class_sums[classmap.get(classLabel)];
+  }
+
+  public int getClassDistributionByLabel(int classLabel) {
+    return class_distribution[classmap.get(classLabel)];
+  }
+
+  public int getTotalEntries() {
+    return total_entries;
+  }
+
+  public int getNumClasses() {
+    return classmap.size();
+  }
+
+  public boolean hasNoiseClass() {
+    return classmap.containsKey(-1);
+  }
+
+  @Override
+  public String toString() {
+    StringBuffer sb = new StringBuffer();
+    sb.append("Membership Matrix\n");
+    for (int i = 0; i < cluster_class_weights.length; i++) {
+      for (int j = 0; j < cluster_class_weights[i].length; j++) {
+        sb.append(cluster_class_weights[i][j] + "\t ");
+      }
+      sb.append("| " + cluster_sums[i] + "\n");
+    }
+    // sb.append("-----------\n");
+    for (int i = 0; i < class_sums.length; i++) {
+      sb.append(class_sums[i] + "\t ");
+    }
+    sb.append("| " + total_entries + "\n");
+
+    sb.append("Real class distribution \n");
+    for (int i = 0; i < class_distribution.length; i++) {
+      sb.append(class_distribution[i] + "\t ");
+    }
+    sb.append("| " + total_class_entries + "\n");
+
+    return sb.toString();
+  }
+
+  public int getInitalBuildTimestamp() {
+    return initalBuildTimestamp;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-samoa/blob/9b178f63/samoa-api/src/main/java/org/apache/samoa/moa/learners/Learner.java
----------------------------------------------------------------------
diff --git a/samoa-api/src/main/java/org/apache/samoa/moa/learners/Learner.java 
b/samoa-api/src/main/java/org/apache/samoa/moa/learners/Learner.java
new file mode 100644
index 0000000..8905ea5
--- /dev/null
+++ b/samoa-api/src/main/java/org/apache/samoa/moa/learners/Learner.java
@@ -0,0 +1,130 @@
+package org.apache.samoa.moa.learners;
+
+/*
+ * #%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.instances.InstancesHeader;
+import org.apache.samoa.moa.MOAObject;
+import org.apache.samoa.moa.core.Example;
+import org.apache.samoa.moa.core.Measurement;
+import org.apache.samoa.moa.options.OptionHandler;
+
+/**
+ * Learner interface for incremental learning models.
+ * 
+ * @author Richard Kirkby ([email protected])
+ * @version $Revision: 7 $
+ */
+public interface Learner<E extends Example> extends MOAObject, OptionHandler {
+
+  /**
+   * Gets whether this learner needs a random seed. Examples of methods that 
needs a random seed are bagging and
+   * boosting.
+   * 
+   * @return true if the learner needs a random seed.
+   */
+  public boolean isRandomizable();
+
+  /**
+   * Sets the seed for random number generation.
+   * 
+   * @param s
+   *          the seed
+   */
+  public void setRandomSeed(int s);
+
+  /**
+   * Gets whether training has started.
+   * 
+   * @return true if training has started
+   */
+  public boolean trainingHasStarted();
+
+  /**
+   * Gets the sum of the weights of the instances that have been used by this 
learner during the training in
+   * <code>trainOnInstance</code>
+   * 
+   * @return the weight of the instances that have been used training
+   */
+  public double trainingWeightSeenByModel();
+
+  /**
+   * Resets this learner. It must be similar to starting a new learner from 
scratch.
+   * 
+   */
+  public void resetLearning();
+
+  /**
+   * Trains this learner incrementally using the given example.
+   * 
+   * @param inst
+   *          the instance to be used for training
+   */
+  public void trainOnInstance(E example);
+
+  /**
+   * Predicts the class memberships for a given instance. If an instance is 
unclassified, the returned array elements
+   * must be all zero.
+   * 
+   * @param inst
+   *          the instance to be classified
+   * @return an array containing the estimated membership probabilities of the 
test instance in each class
+   */
+  public double[] getVotesForInstance(E example);
+
+  /**
+   * Gets the current measurements of this learner.
+   * 
+   * @return an array of measurements to be used in evaluation tasks
+   */
+  public Measurement[] getModelMeasurements();
+
+  /**
+   * Gets the learners of this ensemble. Returns null if this learner is a 
single learner.
+   * 
+   * @return an array of the learners of the ensemble
+   */
+  public Learner[] getSublearners();
+
+  /**
+   * Gets the model if this learner.
+   * 
+   * @return the copy of this learner
+   */
+  public MOAObject getModel();
+
+  /**
+   * Sets the reference to the header of the data stream. The header of the 
data stream is extended from WEKA
+   * <code>Instances</code>. This header is needed to know the number of 
classes and attributes
+   * 
+   * @param ih
+   *          the reference to the data stream header
+   */
+  public void setModelContext(InstancesHeader ih);
+
+  /**
+   * Gets the reference to the header of the data stream. The header of the 
data stream is extended from WEKA
+   * <code>Instances</code>. This header is needed to know the number of 
classes and attributes
+   * 
+   * @return the reference to the data stream header
+   */
+  public InstancesHeader getModelContext();
+
+}

Reply via email to