Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/DeterministicFiniteAutomaton.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/DeterministicFiniteAutomaton.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/DeterministicFiniteAutomaton.java	(revision 922)
@@ -0,0 +1,79 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+
+/**
+ * <p>
+ * Implements a Deterministic Finite Automata (DFA) capable of random session generation. It is a
+ * special case of a first-order Markov model, where the transition probability is equally high for
+ * all following states.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class DeterministicFiniteAutomaton extends FirstOrderMarkovModel {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new DeterministicFiniteAutomaton.
+     * </p>
+     * 
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     */
+    public DeterministicFiniteAutomaton(Random r) {
+        super(r);
+    }
+
+    /**
+     * <p>
+     * Calculates the proability of the next state. Each of the following states in the automaton is
+     * equally probable.
+     * </p>
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getProbability(java.util.List,
+     *      de.ugoe.cs.autoquest.eventcore.Event)
+     */
+    @Override
+    public double getProbability(List<Event> context, Event symbol) {
+        if (context == null) {
+            throw new IllegalArgumentException("context must not be null");
+        }
+        if (symbol == null) {
+            throw new IllegalArgumentException("symbol must not be null");
+        }
+        double result = 0.0d;
+
+        List<Event> contextCopy;
+        if (context.size() >= trieOrder) {
+            contextCopy =
+                new LinkedList<Event>(context.subList(context.size() - trieOrder + 1,
+                                                      context.size()));
+        }
+        else {
+            contextCopy = new LinkedList<Event>(context);
+        }
+
+        Collection<Event> followers = trie.getFollowingSymbols(contextCopy);
+
+        if (followers.size() != 0 && followers.contains(symbol)) {
+            result = 1.0d / followers.size();
+        }
+
+        return result;
+    }
+
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/FirstOrderMarkovModel.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/FirstOrderMarkovModel.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/FirstOrderMarkovModel.java	(revision 922)
@@ -0,0 +1,256 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.util.StringTools;
+import de.ugoe.cs.util.console.Console;
+import edu.uci.ics.jung.graph.Graph;
+import edu.uci.ics.jung.graph.SparseMultigraph;
+import edu.uci.ics.jung.graph.util.EdgeType;
+
+import Jama.Matrix;
+
+/**
+ * <p>
+ * Implements first-order Markov models. The implementation is based on {@link HighOrderMarkovModel}
+ * and restricts the Markov order to 1. In comparison to {@link HighOrderMarkovModel}, more
+ * calculations are possible with first-order models, e.g., the calculation of the entropy (
+ * {@link #calcEntropy()}).
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class FirstOrderMarkovModel extends HighOrderMarkovModel implements IDotCompatible {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Maximum number of iterations when calculating the stationary distribution as the limit of
+     * multiplying the transmission matrix with itself.
+     * </p>
+     */
+    final static int MAX_STATDIST_ITERATIONS = 1000;
+
+    /**
+     * <p>
+     * Constructor. Creates a new FirstOrderMarkovModel.
+     * </p>
+     * 
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     */
+    public FirstOrderMarkovModel(Random r) {
+        super(1, r);
+    }
+
+    /**
+     * <p>
+     * Generates the transmission matrix of the Markov model.
+     * </p>
+     * 
+     * @return transmission matrix
+     */
+    private Matrix getTransmissionMatrix() {
+        List<Event> knownSymbols = new ArrayList<Event>(trie.getKnownSymbols());
+        int numStates = knownSymbols.size();
+        Matrix transmissionMatrix = new Matrix(numStates, numStates);
+
+        for (int i = 0; i < numStates; i++) {
+            Event currentSymbol = knownSymbols.get(i);
+            List<Event> context = new ArrayList<Event>();
+            context.add(currentSymbol);
+            for (int j = 0; j < numStates; j++) {
+                Event follower = knownSymbols.get(j);
+                double prob = getProbability(context, follower);
+                transmissionMatrix.set(i, j, prob);
+            }
+        }
+        return transmissionMatrix;
+    }
+
+    /**
+     * <p>
+     * Calculates the entropy of the model. To make it possible that the model is stationary, a
+     * transition from {@link Event#ENDEVENT} to {@link Event#STARTEVENT} is added.
+     * </p>
+     * 
+     * @return entropy of the model or NaN if it could not be calculated
+     */
+    public double calcEntropy() {
+        Matrix transmissionMatrix = getTransmissionMatrix();
+        List<Event> knownSymbols = new ArrayList<Event>(trie.getKnownSymbols());
+        int numStates = knownSymbols.size();
+
+        List<Integer> startIndexList = new LinkedList<Integer>();
+        List<Integer> endIndexList = new LinkedList<Integer>();
+        for (int i = 0; i < knownSymbols.size(); i++) {
+            String id = knownSymbols.get(i).getId();
+            if (id.equals(Event.STARTEVENT.getId()) ||
+                id.contains(Event.STARTEVENT.getId() + "-=-"))
+            {
+                startIndexList.add(i);
+            }
+            if (id.equals(Event.ENDEVENT.getId()) || id.contains("-=-" + Event.ENDEVENT.getId())) {
+                endIndexList.add(i);
+            }
+        }
+
+        if (startIndexList.isEmpty()) {
+            Console
+                .printerrln("Error calculating entropy. Initial state of markov chain not found.");
+            return Double.NaN;
+        }
+        if (endIndexList.isEmpty()) {
+            Console.printerrln("Error calculating entropy. End state of markov chain not found.");
+            return Double.NaN;
+        }
+        for (Integer i : endIndexList) {
+            for (Integer j : startIndexList) {
+                transmissionMatrix.set(i, j, 1);
+            }
+        }
+
+        // Calculate stationary distribution by raising the power of the
+        // transmission matrix.
+        // The rank of the matrix should fall to 1 and each two should be the
+        // vector of the stationory distribution.
+        int iter = 0;
+        int rank = transmissionMatrix.rank();
+        Matrix stationaryMatrix = (Matrix) transmissionMatrix.clone();
+        while (iter < MAX_STATDIST_ITERATIONS && rank > 1) {
+            stationaryMatrix = stationaryMatrix.times(stationaryMatrix);
+            rank = stationaryMatrix.rank();
+            iter++;
+        }
+
+        if (rank != 1) {
+            Console.traceln(Level.FINE, "rank: " + rank);
+            Console.printerrln("Unable to calculate stationary distribution.");
+            return Double.NaN;
+        }
+
+        double entropy = 0.0;
+        for (int i = 0; i < numStates; i++) {
+            for (int j = 0; j < numStates; j++) {
+                if (transmissionMatrix.get(i, j) != 0 && transmissionMatrix.get(i, j) != 1) {
+                    double tmp = stationaryMatrix.get(0, i);
+                    tmp *= transmissionMatrix.get(i, j);
+                    tmp *= Math.log(transmissionMatrix.get(i, j)) / Math.log(2);
+                    entropy -= tmp;
+                }
+            }
+        }
+        return entropy;
+    }
+
+    /**
+     * <p>
+     * The dot represenation of {@link FirstOrderMarkovModel}s is its graph representation with the
+     * states as nodes and directed edges weighted with transition probabilities.
+     * </p>
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IDotCompatible#getDotRepresentation()
+     */
+    @Override
+    public String getDotRepresentation() {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("digraph model {" + StringTools.ENDLINE);
+
+        List<Event> knownSymbols = new ArrayList<Event>(trie.getKnownSymbols());
+        for (Event symbol : knownSymbols) {
+            final String thisSaneId = symbol.getId().replace("\"", "\\\"").replaceAll("[\r\n]", "");
+            stringBuilder.append(" " + knownSymbols.indexOf(symbol) + " [label=\"" + thisSaneId +
+                "\"];" + StringTools.ENDLINE);
+            List<Event> context = new ArrayList<Event>();
+            context.add(symbol);
+            Collection<Event> followers = trie.getFollowingSymbols(context);
+            for (Event follower : followers) {
+                stringBuilder.append(" " + knownSymbols.indexOf(symbol) + " -> " +
+                    knownSymbols.indexOf(follower) + " ");
+                stringBuilder.append("[label=\"" + getProbability(context, follower) + "\"];" +
+                    StringTools.ENDLINE);
+            }
+        }
+        stringBuilder.append('}' + StringTools.ENDLINE);
+        return stringBuilder.toString();
+    }
+
+    /**
+     * <p>
+     * Returns a {@link Graph} representation of the model with the states as nodes and directed
+     * edges weighted with transition probabilities.
+     * </p>
+     * 
+     * @return {@link Graph} of the model
+     */
+    public Graph<String, MarkovEdge> getGraph() {
+        Graph<String, MarkovEdge> graph = new SparseMultigraph<String, MarkovEdge>();
+
+        List<Event> knownSymbols = new ArrayList<Event>(trie.getKnownSymbols());
+
+        for (Event symbol : knownSymbols) {
+            String from = symbol.getId();
+            List<Event> context = new ArrayList<Event>();
+            context.add(symbol);
+
+            Collection<Event> followers = trie.getFollowingSymbols(context);
+
+            for (Event follower : followers) {
+                String to = follower.getId();
+                MarkovEdge prob = new MarkovEdge(getProbability(context, follower));
+                graph.addEdge(prob, from, to, EdgeType.DIRECTED);
+            }
+        }
+        return graph;
+    }
+
+    /**
+     * Inner class used for the {@link Graph} representation of the model.
+     * 
+     * @author Steffen Herbold
+     * @version 1.0
+     */
+    static public class MarkovEdge {
+        /**
+         * <p>
+         * Weight of the edge, i.e., its transition probability.
+         * </p>
+         */
+        double weight;
+
+        /**
+         * <p>
+         * Constructor. Creates a new MarkovEdge.
+         * </p>
+         * 
+         * @param weight
+         *            weight of the edge, i.e., its transition probability
+         */
+        MarkovEdge(double weight) {
+            this.weight = weight;
+        }
+
+        /**
+         * <p>
+         * The weight of the edge as {@link String}.
+         * </p>
+         */
+        public String toString() {
+            return "" + weight;
+        }
+    }
+
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/HighOrderMarkovModel.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/HighOrderMarkovModel.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/HighOrderMarkovModel.java	(revision 922)
@@ -0,0 +1,84 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+
+/**
+ * <p>
+ * Implements high-order Markov models.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class HighOrderMarkovModel extends TrieBasedModel {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new HighOrderMarkovModel with a defined Markov order.
+     * </p>
+     * 
+     * @param maxOrder
+     *            Markov order of the model
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     */
+    public HighOrderMarkovModel(int maxOrder, Random r) {
+        super(maxOrder, r);
+    }
+
+    /**
+     * <p>
+     * Calculates the probability of the next Event being symbol based on the order of the Markov
+     * model. The order is defined in the constructor {@link #HighOrderMarkovModel(int, Random)}.
+     * </p>
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getProbability(java.util.List,
+     *      de.ugoe.cs.autoquest.eventcore.Event)
+     */
+    @Override
+    public double getProbability(List<Event> context, Event symbol) {
+        if (context == null) {
+            throw new IllegalArgumentException("context must not be null");
+        }
+        if (symbol == null) {
+            throw new IllegalArgumentException("symbol must not be null");
+        }
+        double result = 0.0d;
+
+        List<Event> contextCopy;
+        if (context.size() >= trieOrder) {
+            contextCopy =
+                new LinkedList<Event>(context.subList(context.size() - trieOrder + 1,
+                                                      context.size()));
+        }
+        else {
+            contextCopy = new LinkedList<Event>(context);
+        }
+
+        Collection<Event> followers = trie.getFollowingSymbols(contextCopy);
+        int sumCountFollowers = 0; // N(s\sigma')
+        for (Event follower : followers) {
+            sumCountFollowers += trie.getCount(contextCopy, follower);
+        }
+
+        int countSymbol = trie.getCount(contextCopy, symbol);
+        if (sumCountFollowers != 0) {
+            result = ((double) countSymbol / sumCountFollowers);
+        }
+
+        return result;
+    }
+
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IDotCompatible.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IDotCompatible.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IDotCompatible.java	(revision 922)
@@ -0,0 +1,22 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+/**
+ * <p>
+ * Models implementing this interface provide a graph representation of themselves as a
+ * {@link String} with Dot syntax.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public interface IDotCompatible {
+
+    /**
+     * <p>
+     * Returns a Dot representation of the model.
+     * </p>
+     * 
+     * @return string with Dot syntax that describes the model.
+     */
+    public abstract String getDotRepresentation();
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IMemory.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IMemory.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IMemory.java	(revision 922)
@@ -0,0 +1,40 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.List;
+
+/**
+ * <p>
+ * This interface defines basic functions for classes that implement a memory about the recent
+ * events of a sequences.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ * 
+ * @param <T>
+ *            Type of the sequence elements that are memorized.
+ */
+public interface IMemory<T> {
+
+    /**
+     * Adds an element to the end of the memory.
+     * 
+     * @param element
+     *            Element to be added.
+     */
+    public void add(T element);
+
+    /**
+     * <p>
+     * Returns the last <code>num</code> memorized elements. If the history is shorter than
+     * <code>num</code>, the length of the returned {@link java.util.List} may be less than
+     * <code>num</code>.
+     * </p>
+     * 
+     * @param num
+     *            Number of states from the end of the memory to be returned.
+     * @return {@link java.util.List} of memorized elements.
+     */
+    public List<T> getLast(int num);
+
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IStochasticProcess.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IStochasticProcess.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IStochasticProcess.java	(revision 922)
@@ -0,0 +1,179 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+
+/**
+ * <p>
+ * This interface defines the functionalities provided by stochastic processes.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public interface IStochasticProcess extends Serializable {
+
+    /**
+     * <p>
+     * Returns the probability, that the next event is {@code symbol} given the last events are
+     * {@code context}. The last element of {@code context} is the most recent in the history, the
+     * first element is the oldest.
+     * </p>
+     * 
+     * @param context
+     *            recently observed symbols
+     * @param symbol
+     *            event for which the probability is calculated
+     * @return probabilty the {@code symbol} is the next event, given the last events
+     *         {@code context}
+     * @throws IllegalArgumentException
+     *             thrown if context or symbol is null
+     */
+    double getProbability(List<Event> context, Event symbol);
+
+    /**
+     * <p>
+     * Returns the probabilitiy that a given sequence is generated by the stochastic process.
+     * </p>
+     * 
+     * @param sequence
+     *            sequences of which the probability is calculated
+     * @return probability of the sequences; 1.0 if sequence is empty or null
+     * @throws IllegalArgumentException
+     *             thrown if sequence is null
+     */
+    double getProbability(List<Event> sequence);
+
+    /**
+     * <p>
+     * Generates a random sequence of events. The sequence starts with {@link Event#STARTEVENT} and
+     * finishes with {@link Event#ENDEVENT}.
+     * </p>
+     * 
+     * @return randomly generated sequence
+     */
+    public List<Event> randomSequence();
+
+    /**
+     * <p>
+     * Generates a random sequence of events. The sequence starts with {@link Event#STARTEVENT} and
+     * finishes with
+     * <ul>
+     * <li>{@link Event#ENDEVENT} if validEnd==true.</li>
+     * <li>b) if a generated sequences reaches {@link Event#ENDEVENT} before maxLength, the sequence
+     * finishes and is shorter than maxLenght. Otherwise, the sequence finishes as soon as maxLength
+     * is reached and the final event of the sequence must not be {@link Event#ENDEVENT}.</li>
+     * </ul>
+     * </p>
+     * 
+     * @param maxLength
+     *            maximum length of the generated sequence
+     * @param validEnd
+     *            if true, only sequences that finish with {@link Event#ENDEVENT} are generated
+     * @return randomly generated sequence
+     * 
+     */
+    public List<Event> randomSequence(int maxLength, boolean validEnd);
+
+    /**
+     * <p>
+     * Generates all sequences of a given length are possible, i.e., have a positive probability.<br>
+     * All states are used as possible starting states.
+     * </p>
+     * 
+     * @param length
+     *            length of the generated sequences
+     * @return generated sequences
+     * @see #generateSequences(int, boolean)
+     * @throws IllegalArgumentException
+     *             thrown if length is less than or equal to 0
+     */
+    public Collection<List<Event>> generateSequences(int length);
+
+    /**
+     * <p>
+     * Generates all sequences of given length that can are possible, i.e., have positive
+     * probability.<br>
+     * If {@code fromStart==true}, all generated sequences start in {@link Event#STARTEVENT}.
+     * Otherwise this method is the same as {@link #generateSequences(int)}.
+     * </p>
+     * 
+     * @param length
+     *            length of the generated sequences
+     * @param fromStart
+     *            if true, all generated sequences start with {@link Event#STARTEVENT}
+     * @return generated sequences
+     * @throws IllegalArgumentException
+     *             thrown if length is less than or equal to 0
+     */
+    public Collection<List<Event>> generateSequences(int length, boolean fromStart);
+
+    /**
+     * <p>
+     * Generates all sequences starting with {@link Event#STARTEVENT} and finishing with
+     * {@link Event#ENDEVENT} of a given length. It is possible that no such sequence exists with
+     * the defined length and the returned set is empty. If {@code length} is less than 2 the
+     * returned set is always empty.
+     * </p>
+     * 
+     * @param length
+     * @return generated sequences
+     * @throws IllegalArgumentException
+     *             thrown if length is less than or equal to 0
+     */
+    public Collection<List<Event>> generateValidSequences(int length);
+
+    /**
+     * <p>
+     * Returns the number of states known by the stochastic process, i.e., the size of its alphabet.
+     * </p>
+     * 
+     * @return number of states
+     */
+    public int getNumSymbols();
+
+    /**
+     * <p>
+     * Returns a string representation of all known states.
+     * </p>
+     * 
+     * @return string representation for all known states
+     */
+    public String[] getSymbolStrings();
+
+    /**
+     * <p>
+     * Returns the number of states the process would have if it would be flattened through
+     * state-splitting to a first-order Markov model.
+     * </p>
+     * <p>
+     * If it is not possible to flatten the model, -1 is returned.
+     * </p>
+     * 
+     * @return number of states an equivalent FOM would have; -1 if not available
+     */
+    public int getNumFOMStates();
+
+    /**
+     * <p>
+     * Returns the number of transitions the process would have if it would be flattened through
+     * state-splitting to a first-order Markov model.
+     * </p>
+     * 
+     * @return number of transitions an equivalent FOM would have; -1 if not available
+     */
+    public int getNumTransitions();
+
+    /**
+     * <p>
+     * Returns all states known by the stochastic process, i.e., its {@link Event}s.
+     * </p>
+     * 
+     * @return events known by the process
+     */
+    public Collection<Event> getEvents();
+
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IncompleteMemory.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IncompleteMemory.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IncompleteMemory.java	(revision 922)
@@ -0,0 +1,93 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * <p>
+ * Implements a round-trip buffered memory of a specified length that can be used to remember the
+ * recent history. Every event that happend longer ago than the length of the memory is forgotten,
+ * hence the memory is incomplete.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ * 
+ * @param <T>
+ *            Type which is memorized.
+ */
+public class IncompleteMemory<T> implements IMemory<T> {
+
+    /**
+     * <p>
+     * Maximum length of the memory.
+     * </p>
+     */
+    private int length;
+
+    /**
+     * <p>
+     * Internal storage of the history.
+     * </p>
+     */
+    private List<T> history;
+
+    /**
+     * <p>
+     * Constructor. Creates a new IncompleteMemory.
+     * </p>
+     * 
+     * @param length
+     *            number of recent events that are remembered
+     * @throws IllegalArgumentException
+     *             This exception is thrown if the length is smaller than 1
+     */
+    public IncompleteMemory(int length) {
+        if (length < 1) {
+            throw new IllegalArgumentException("Length of IncompleteMemory must be at least 1.");
+        }
+        this.length = length;
+        history = new LinkedList<T>();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IMemory#add(java.lang.Object)
+     */
+    @Override
+    public void add(T state) {
+        if (history.size() == length) {
+            history.remove(0);
+        }
+        history.add(state);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IMemory#getLast(int)
+     */
+    @Override
+    public List<T> getLast(int num) {
+        if (num < 1) {
+            return new LinkedList<T>();
+        }
+        else {
+            return new LinkedList<T>(history.subList(Math.max(0, history.size() - num),
+                                                     history.size())); // defensive copy
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the current length of the memory. This can be less than {@link #length}, if the
+     * overall history is less than {@link #length}.
+     * </p>
+     * 
+     * @return length of the current memory
+     */
+    public int getLength() {
+        return history.size();
+    }
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/ModelFlattener.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/ModelFlattener.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/ModelFlattener.java	(revision 922)
@@ -0,0 +1,131 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.StringEventType;
+
+/**
+ * <p>
+ * This class provides functions to create flattened first-order Markov models from
+ * {@link HighOrderMarkovModel}s and {@link PredictionByPartialMatch} models through state
+ * splitting.
+ * </p>
+ * <p>
+ * If possible, the normal high-order markov model should be used, as the Events may be broken by
+ * the flattener, as, e.g., the information {@link ReplayableEvent}s contain is not preserved.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class ModelFlattener {
+
+    private static final String EVENT_SEPARATOR = "-=-";
+
+    Trie<Event> firstOrderTrie;
+
+    /**
+     * <p>
+     * Takes a {@link HighOrderMarkovModel} and returns a {@link FirstOrderMarkovModel} that
+     * conserves the high-order memory through state splitting.
+     * </p>
+     * 
+     * @param model
+     *            model that is flattened
+     * @return flattened first-order Markov model
+     */
+    public FirstOrderMarkovModel flattenHighOrderMarkovModel(HighOrderMarkovModel model) {
+        int markovOrder = model.trieOrder - 1;
+        FirstOrderMarkovModel firstOrderModel = new FirstOrderMarkovModel(new Random());
+        firstOrderModel.trieOrder = 2;
+        if (markovOrder == 1) {
+            firstOrderModel.trie = new Trie<Event>(model.trie);
+            firstOrderModel.trieOrder = 2;
+        }
+        else {
+            firstOrderTrie = new Trie<Event>();
+            TrieNode<Event> rootNode = model.trie.find(null);
+            generateFirstOrderTrie(rootNode, new LinkedList<String>(), markovOrder);
+            firstOrderTrie.updateKnownSymbols();
+            firstOrderModel.trie = firstOrderTrie;
+        }
+
+        return firstOrderModel;
+    }
+
+    /**
+     * <p>
+     * <b>This method is not available yet and always return null!</b>
+     * </p>
+     * <p>
+     * Takes a {@link PredictionByPartialMatch} model and returns a {@link FirstOrderMarkovModel}
+     * that conserves the high-order memory through state splitting.
+     * </p>
+     * 
+     * @param model
+     *            model that is flattened
+     * @return flattened first-order Markov model
+     */
+    public FirstOrderMarkovModel flattenPredictionByPartialMatch(PredictionByPartialMatch model) {
+        // TODO implement method
+        return null;
+    }
+
+    /**
+     * <p>
+     * Converts all nodes of the given depth into first-order node through state-splitting. For each
+     * node at the given depth a new node is created and appropriate transitions will be added.
+     * </p>
+     * <p>
+     * This method traverses through the tree recursively. If the recursion reaches the desired
+     * depth in the tree, node are added.
+     * </p>
+     * 
+     * @param currentNode
+     *            node whose sub-trie is currently traversed
+     * @param parentIDs
+     *            ID strings of the ancestors of the currentNode
+     * @param depth
+     *            depth to go - NOT the current depth.
+     */
+    private void generateFirstOrderTrie(TrieNode<Event> currentNode,
+                                        List<String> parentIDs,
+                                        int depth)
+    {
+        for (TrieNode<Event> child : currentNode.getChildren()) {
+            String currentId = child.getSymbol().getId();
+            if (depth > 1) {
+                List<String> childParentIDs = new LinkedList<String>(parentIDs);
+                childParentIDs.add(currentId);
+                generateFirstOrderTrie(child, childParentIDs, depth - 1);
+
+            }
+            else {
+                StringBuilder firstOrderID = new StringBuilder();
+                for (String parentID : parentIDs) {
+                    firstOrderID.append(parentID + EVENT_SEPARATOR);
+                }
+                firstOrderID.append(currentId);
+                TrieNode<Event> firstOrderNode =
+                    firstOrderTrie.getChildCreate(new Event(new StringEventType(firstOrderID
+                        .toString())));
+                firstOrderNode.setCount(child.getCount());
+                for (TrieNode<Event> transitionChild : child.getChildren()) {
+                    StringBuilder transitionID = new StringBuilder();
+                    for (String parentID : parentIDs.subList(1, parentIDs.size())) {
+                        transitionID.append(parentID + EVENT_SEPARATOR);
+                    }
+                    transitionID.append(currentId + EVENT_SEPARATOR);
+                    transitionID.append(transitionChild.getSymbol().getId());
+                    TrieNode<Event> firstOrderTransitionChild =
+                        firstOrderNode.getChildCreate(new Event(new StringEventType(transitionID
+                            .toString())));
+                    firstOrderTransitionChild.setCount(transitionChild.getCount());
+                }
+            }
+        }
+    }
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/PredictionByPartialMatch.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/PredictionByPartialMatch.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/PredictionByPartialMatch.java	(revision 922)
@@ -0,0 +1,198 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+
+/**
+ * <p>
+ * Implements Prediction by Partial Match (PPM) based on the following formula (LaTeX-style
+ * notation):<br>
+ * P_{PPM}(X_n|X_{n-1},...,X_{n-k}) = \sum_{i=k}^min escape^{k-i} P_{MM^i}(X_n
+ * |X_{n-1},...,X_{n-i})(1-escape)+escape^(k-min)P(X_n|X_{n-i},... ,X_{n-min})<br>
+ * P_{MM^i} denotes the probability in an i-th order Markov model.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * 
+ */
+public class PredictionByPartialMatch extends TrieBasedModel {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Minimum order of the Markov model.
+     * </p>
+     */
+    protected int minOrder;
+
+    /**
+     * <p>
+     * Probability to use a lower-order Markov model
+     * </p>
+     */
+    protected double probEscape;
+
+    /**
+     * <p>
+     * Constructor. Creates a new PredictionByPartialMatch model with a given Markov order and a
+     * default escape probability of 0.1.
+     * </p>
+     * 
+     * @param markovOrder
+     *            Markov order of the model
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     */
+    public PredictionByPartialMatch(int markovOrder, Random r) {
+        this(markovOrder, r, 0.1);
+    }
+
+    /**
+     * <p>
+     * Creates a new PredictionByPartialMatch model with a given Markov order and escape
+     * probability.
+     * </p>
+     * 
+     * @param markovOrder
+     *            Markov order of the model
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     * @param probEscape
+     *            escape probability used by the model
+     */
+    public PredictionByPartialMatch(int markovOrder, Random r, double probEscape) {
+        this(markovOrder, 0, r, probEscape);
+    }
+
+    /**
+     * <p>
+     * Creates a new PredictionByPartialMatch model with a given Markov order and escape
+     * probability.
+     * </p>
+     * 
+     * @param markovOrder
+     *            Markov order of the model
+     * @param minOrder
+     *            minimum order of the model; if this order is reached, there is no escape
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     * @param probEscape
+     *            escape probability used by the model
+     * @throws IllegalArgumentException
+     *             thrown if minOrder is less than 0 or greater than markovOrder or probEscape is
+     *             not in the interval (0,1)
+     */
+    public PredictionByPartialMatch(int markovOrder, int minOrder, Random r, double probEscape) {
+        super(markovOrder, r);
+        if (minOrder < 0) {
+            throw new IllegalArgumentException("minOrder must be greather than or equal to 0");
+        }
+        if (minOrder > markovOrder) {
+            throw new IllegalArgumentException(
+                                                "minOrder must be less than or equal to markovOrder");
+        }
+        if (probEscape <= 0.0 || probEscape >= 1.0) {
+            throw new IllegalArgumentException("probEscape must be in the interval (0,1)");
+        }
+        this.probEscape = probEscape;
+        this.minOrder = minOrder;
+    }
+
+    /**
+     * <p>
+     * Sets the escape probability of the model.
+     * </p>
+     * 
+     * @param probEscape
+     *            new escape probability
+     */
+    public void setProbEscape(double probEscape) {
+        this.probEscape = probEscape;
+    }
+
+    /**
+     * <p>
+     * Returns the escape probability of the model.
+     * </p>
+     * 
+     * @return escape probability of the model
+     */
+    public double getProbEscape() {
+        return probEscape;
+    }
+
+    /**
+     * <p>
+     * Calculates the probability of the next event based on the formula:<br>
+     * P_{PPM}(X_n|X_{n-1},...,X_{n-k}) = \sum_{i=k}^min escape^{k-i} P_{MM^i}(X_n
+     * |X_{n-1},...,X_{n-i})(1-escape)+escape^(k-min)P(X_n|X_{n-i},... ,X_{n-min})<br>
+     * P_{MM^i} denotes the probability in an i-th order Markov model.
+     * </p>
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getProbability(java.util.List,
+     *      de.ugoe.cs.autoquest.eventcore.Event)
+     */
+    @Override
+    public double getProbability(List<Event> context, Event symbol) {
+        if (context == null) {
+            throw new IllegalArgumentException("context must not be null");
+        }
+        if (symbol == null) {
+            throw new IllegalArgumentException("symbol must not be null");
+        }
+        double result = 0.0d;
+        double resultCurrentContex = 0.0d;
+        double resultShorterContex = 0.0d;
+
+        List<Event> contextCopy;
+        if (context.size() >= trieOrder) {
+            contextCopy =
+                new LinkedList<Event>(context.subList(context.size() - trieOrder + 1,
+                                                      context.size()));
+        }
+        else {
+            contextCopy = new LinkedList<Event>(context);
+        }
+
+        Collection<Event> followers = trie.getFollowingSymbols(contextCopy); // \Sigma'
+        int sumCountFollowers = 0; // N(s\sigma')
+        for (Event follower : followers) {
+            sumCountFollowers += trie.getCount(contextCopy, follower);
+        }
+
+        int countSymbol = trie.getCount(contextCopy, symbol); // N(s\sigma)
+        if (sumCountFollowers == 0) {
+            resultCurrentContex = 0.0;
+        }
+        else {
+            resultCurrentContex = (double) countSymbol / sumCountFollowers;
+        }
+        if (contextCopy.size() > minOrder) {
+            resultCurrentContex *= (1 - probEscape);
+            contextCopy.remove(0);
+            if (contextCopy.size() >= minOrder) {
+                double probSuffix = getProbability(contextCopy, symbol);
+
+                if (followers.size() == 0) {
+                    resultShorterContex = probSuffix;
+                }
+                else {
+                    resultShorterContex = probEscape * probSuffix;
+                }
+            }
+        }
+        result = resultCurrentContex + resultShorterContex;
+
+        return result;
+    }
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/Trie.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/Trie.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/Trie.java	(revision 922)
@@ -0,0 +1,466 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.util.StringTools;
+
+import edu.uci.ics.jung.graph.DelegateTree;
+import edu.uci.ics.jung.graph.Graph;
+import edu.uci.ics.jung.graph.Tree;
+
+/**
+ * <p>
+ * This class implements a <it>trie</it>, i.e., a tree of sequences that the occurence of
+ * subsequences up to a predefined length. This length is the trie order.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * 
+ * @param <T>
+ *            Type of the symbols that are stored in the trie.
+ * 
+ * @see TrieNode
+ */
+public class Trie<T> implements IDotCompatible, Serializable {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Collection of all symbols occuring in the trie.
+     * </p>
+     */
+    private Collection<T> knownSymbols;
+
+    /**
+     * <p>
+     * Reference to the root of the trie.
+     * </p>
+     */
+    private final TrieNode<T> rootNode;
+
+    /**
+     * <p>
+     * Contructor. Creates a new Trie.
+     * </p>
+     */
+    public Trie() {
+        rootNode = new TrieNode<T>();
+        knownSymbols = new LinkedHashSet<T>();
+    }
+
+    /**
+     * <p>
+     * Copy-Constructor. Creates a new Trie as the copy of other. The other trie must noch be null.
+     * </p>
+     * 
+     * @param other
+     *            Trie that is copied
+     */
+    public Trie(Trie<T> other) {
+        if (other == null) {
+            throw new IllegalArgumentException("other trie must not be null");
+        }
+        rootNode = new TrieNode<T>(other.rootNode);
+        knownSymbols = new LinkedHashSet<T>(other.knownSymbols);
+    }
+
+    /**
+     * <p>
+     * Returns a collection of all symbols occuring in the trie.
+     * </p>
+     * 
+     * @return symbols occuring in the trie
+     */
+    public Collection<T> getKnownSymbols() {
+        return new LinkedHashSet<T>(knownSymbols);
+    }
+
+    /**
+     * <p>
+     * Trains the current trie using the given sequence and adds all subsequence of length
+     * {@code maxOrder}.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence whose subsequences are added to the trie
+     * @param maxOrder
+     *            maximum length of the subsequences added to the trie
+     */
+    public void train(List<T> sequence, int maxOrder) {
+        if (maxOrder < 1) {
+            return;
+        }
+        IncompleteMemory<T> latestActions = new IncompleteMemory<T>(maxOrder);
+        int i = 0;
+        for (T currentEvent : sequence) {
+            latestActions.add(currentEvent);
+            knownSymbols.add(currentEvent);
+            i++;
+            if (i >= maxOrder) {
+                add(latestActions.getLast(maxOrder));
+            }
+        }
+        int sequenceLength = sequence.size();
+        for (int j = maxOrder - 1; j > 0; j--) {
+            add(sequence.subList(sequenceLength - j, sequenceLength));
+        }
+    }
+
+    /**
+     * <p>
+     * Adds a given subsequence to the trie and increases the counters accordingly.
+     * </p>
+     * 
+     * @param subsequence
+     *            subsequence whose counters are increased
+     * @see TrieNode#add(List)
+     */
+    protected void add(List<T> subsequence) {
+        if (subsequence != null && !subsequence.isEmpty()) {
+            knownSymbols.addAll(subsequence);
+            subsequence = new LinkedList<T>(subsequence); // defensive copy!
+            T firstSymbol = subsequence.get(0);
+            TrieNode<T> node = getChildCreate(firstSymbol);
+            node.add(subsequence);
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the child of the root node associated with the given symbol or creates it if it does
+     * not exist yet.
+     * </p>
+     * 
+     * @param symbol
+     *            symbol whose node is required
+     * @return node associated with the symbol
+     * @see TrieNode#getChildCreate(Object)
+     */
+    protected TrieNode<T> getChildCreate(T symbol) {
+        return rootNode.getChildCreate(symbol);
+    }
+
+    /**
+     * <p>
+     * Returns the child of the root node associated with the given symbol or null if it does not
+     * exist.
+     * </p>
+     * 
+     * @param symbol
+     *            symbol whose node is required
+     * @return node associated with the symbol; null if no such node exists
+     * @see TrieNode#getChild(Object)
+     */
+    protected TrieNode<T> getChild(T symbol) {
+        return rootNode.getChild(symbol);
+    }
+
+    /**
+     * <p>
+     * Returns the number of occurences of the given sequence.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence whose number of occurences is required
+     * @return number of occurences of the sequence
+     */
+    public int getCount(List<T> sequence) {
+        int count = 0;
+        TrieNode<T> node = find(sequence);
+        if (node != null) {
+            count = node.getCount();
+        }
+        return count;
+    }
+
+    /**
+     * <p>
+     * Returns the number of occurences of the given prefix and a symbol that follows it.<br>
+     * Convenience function to simplify usage of {@link #getCount(List)}.
+     * </p>
+     * 
+     * @param sequence
+     *            prefix of the sequence
+     * @param follower
+     *            suffix of the sequence
+     * @return number of occurences of the sequence
+     * @see #getCount(List)
+     */
+    public int getCount(List<T> sequence, T follower) {
+        List<T> tmpSequence = new LinkedList<T>(sequence);
+        tmpSequence.add(follower);
+        return getCount(tmpSequence);
+
+    }
+
+    /**
+     * <p>
+     * Searches the trie for a given sequence and returns the node associated with the sequence or
+     * null if no such node is found.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence that is searched for
+     * @return node associated with the sequence
+     * @see TrieNode#find(List)
+     */
+    public TrieNode<T> find(List<T> sequence) {
+        if (sequence == null || sequence.isEmpty()) {
+            return rootNode;
+        }
+        List<T> sequenceCopy = new LinkedList<T>(sequence);
+        TrieNode<T> result = null;
+        TrieNode<T> node = getChild(sequenceCopy.get(0));
+        if (node != null) {
+            sequenceCopy.remove(0);
+            result = node.find(sequenceCopy);
+        }
+        return result;
+    }
+
+    /**
+     * <p>
+     * Returns a collection of all symbols that follow a given sequence in the trie. In case the
+     * sequence is not found or no symbols follow the sequence the result will be empty.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence whose followers are returned
+     * @return symbols following the given sequence
+     * @see TrieNode#getFollowingSymbols()
+     */
+    public Collection<T> getFollowingSymbols(List<T> sequence) {
+        Collection<T> result = new LinkedList<T>();
+        TrieNode<T> node = find(sequence);
+        if (node != null) {
+            result = node.getFollowingSymbols();
+        }
+        return result;
+    }
+
+    /**
+     * <p>
+     * Returns the longest suffix of the given context that is contained in the tree and whose
+     * children are leaves.
+     * </p>
+     * 
+     * @param context
+     *            context whose suffix is searched for
+     * @return longest suffix of the context
+     */
+    public List<T> getContextSuffix(List<T> context) {
+        List<T> contextSuffix;
+        if (context != null) {
+            contextSuffix = new LinkedList<T>(context); // defensive copy
+        }
+        else {
+            contextSuffix = new LinkedList<T>();
+        }
+        boolean suffixFound = false;
+
+        while (!suffixFound) {
+            if (contextSuffix.isEmpty()) {
+                suffixFound = true; // suffix is the empty word
+            }
+            else {
+                TrieNode<T> node = find(contextSuffix);
+                if (node != null) {
+                    if (!node.getFollowingSymbols().isEmpty()) {
+                        suffixFound = true;
+                    }
+                }
+                if (!suffixFound) {
+                    contextSuffix.remove(0);
+                }
+            }
+        }
+
+        return contextSuffix;
+    }
+
+    /**
+     * <p>
+     * Helper class for graph visualization of a trie.
+     * </p>
+     * 
+     * @author Steffen Herbold
+     * @version 1.0
+     */
+    static public class Edge {}
+
+    /**
+     * <p>
+     * Helper class for graph visualization of a trie.
+     * </p>
+     * 
+     * @author Steffen Herbold
+     * @version 1.0
+     */
+    static public class TrieVertex {
+
+        /**
+         * <p>
+         * Id of the vertex.
+         * </p>
+         */
+        private String id;
+
+        /**
+         * <p>
+         * Contructor. Creates a new TrieVertex.
+         * </p>
+         * 
+         * @param id
+         *            id of the vertex
+         */
+        protected TrieVertex(String id) {
+            this.id = id;
+        }
+
+        /**
+         * <p>
+         * Returns the id of the vertex.
+         * </p>
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return id;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns a {@link Graph} representation of the trie.
+     * </p>
+     * 
+     * @return {@link Graph} representation of the trie
+     */
+    protected Tree<TrieVertex, Edge> getGraph() {
+        DelegateTree<TrieVertex, Edge> graph = new DelegateTree<TrieVertex, Edge>();
+        rootNode.getGraph(null, graph);
+        return graph;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IDotCompatible#getDotRepresentation()
+     */
+    public String getDotRepresentation() {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("digraph model {" + StringTools.ENDLINE);
+        rootNode.appendDotRepresentation(stringBuilder);
+        stringBuilder.append('}' + StringTools.ENDLINE);
+        return stringBuilder.toString();
+    }
+
+    /**
+     * <p>
+     * Returns the string representation of the root node.
+     * </p>
+     * 
+     * @see TrieNode#toString()
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return rootNode.toString();
+    }
+
+    /**
+     * <p>
+     * Returns the number of symbols contained in the trie.
+     * </p>
+     * 
+     * @return number of symbols contained in the trie
+     */
+    public int getNumSymbols() {
+        return knownSymbols.size();
+    }
+
+    /**
+     * <p>
+     * Returns the number of trie nodes that are ancestors of a leaf. This is the equivalent to the
+     * number of states a first-order markov model would have.
+     * <p>
+     * 
+     * @return number of trie nodes that are ancestors of leafs.
+     */
+    public int getNumLeafAncestors() {
+        List<TrieNode<T>> ancestors = new LinkedList<TrieNode<T>>();
+        rootNode.getLeafAncestors(ancestors);
+        return ancestors.size();
+    }
+
+    /**
+     * <p>
+     * Returns the number of trie nodes that are leafs.
+     * </p>
+     * 
+     * @return number of leafs in the trie
+     */
+    public int getNumLeafs() {
+        return rootNode.getNumLeafs();
+    }
+
+    /**
+     * <p>
+     * Updates the list of known symbols by replacing it with all symbols that are found in the
+     * child nodes of the root node. This should be the same as all symbols that are contained in
+     * the trie.
+     * </p>
+     */
+    public void updateKnownSymbols() {
+        knownSymbols = new HashSet<T>();
+        for (TrieNode<T> node : rootNode.getChildren()) {
+            knownSymbols.add(node.getSymbol());
+        }
+    }
+
+    /**
+     * <p>
+     * Two Tries are defined as equal, if their {@link #rootNode} are equal.
+     * </p>
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @SuppressWarnings("rawtypes")
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (other instanceof Trie) {
+            return rootNode.equals(((Trie) other).rootNode);
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int multiplier = 17;
+        int hash = 42;
+        if (rootNode != null) {
+            hash = multiplier * hash + rootNode.hashCode();
+        }
+        return hash;
+    }
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/TrieBasedModel.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/TrieBasedModel.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/TrieBasedModel.java	(revision 922)
@@ -0,0 +1,402 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.usageprofiles.Trie.Edge;
+import de.ugoe.cs.autoquest.usageprofiles.Trie.TrieVertex;
+import edu.uci.ics.jung.graph.Tree;
+
+/**
+ * <p>
+ * Implements a skeleton for stochastic processes that can calculate probabilities based on a trie.
+ * The skeleton provides all functionalities of {@link IStochasticProcess} except
+ * {@link IStochasticProcess#getProbability(List, Event)}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public abstract class TrieBasedModel implements IStochasticProcess {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * The order of the trie, i.e., the maximum length of subsequences stored in the trie.
+     * </p>
+     */
+    protected int trieOrder;
+
+    /**
+     * <p>
+     * Trie on which the probability calculations are based.
+     * </p>
+     */
+    protected Trie<Event> trie = null;
+
+    /**
+     * <p>
+     * Random number generator used by probabilistic sequence generation methods.
+     * </p>
+     */
+    protected final Random r;
+
+    /**
+     * <p>
+     * Constructor. Creates a new TrieBasedModel that can be used for stochastic processes with a
+     * Markov order less than or equal to {@code markovOrder}.
+     * </p>
+     * 
+     * @param markovOrder
+     *            Markov order of the model
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     * @throws IllegalArgumentException
+     *             thrown if markovOrder is less than 0 or the random number generator r is null
+     */
+    public TrieBasedModel(int markovOrder, Random r) {
+        super();
+        if (markovOrder < 0) {
+            throw new IllegalArgumentException("markov order must not be less than 0");
+        }
+        if (r == null) {
+            throw new IllegalArgumentException("random number generator r must not be null");
+        }
+        this.trieOrder = markovOrder + 1;
+        this.r = r;
+    }
+
+    /**
+     * <p>
+     * Trains the model by generating a trie from which probabilities are calculated. The trie is
+     * newly generated based solely on the passed sequences. If an existing model should only be
+     * updated, use {@link #update(Collection)} instead.
+     * </p>
+     * 
+     * @param sequences
+     *            training data
+     * @throws IllegalArgumentException
+     *             thrown is sequences is null
+     */
+    public void train(Collection<List<Event>> sequences) {
+        trie = null;
+        update(sequences);
+    }
+
+    /**
+     * <p>
+     * Trains the model by updating the trie from which the probabilities are calculated. This
+     * function updates an existing trie. In case no trie exists yet, a new trie is generated and
+     * the function behaves like {@link #train(Collection)}.
+     * </p>
+     * 
+     * @param sequences
+     *            training data
+     * @throws IllegalArgumentException
+     *             thrown is sequences is null
+     */
+    public void update(Collection<List<Event>> sequences) {
+        if (sequences == null) {
+            throw new IllegalArgumentException("sequences must not be null");
+        }
+        if (trie == null) {
+            trie = new Trie<Event>();
+        }
+        for (List<Event> sequence : sequences) {
+            List<Event> currentSequence = new LinkedList<Event>(sequence); // defensive
+                                                                           // copy
+            currentSequence.add(0, Event.STARTEVENT);
+            currentSequence.add(Event.ENDEVENT);
+
+            trie.train(currentSequence, trieOrder);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#randomSequence()
+     */
+    @Override
+    public List<Event> randomSequence() {
+        return randomSequence(Integer.MAX_VALUE, true);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#randomSequence()
+     */
+    @Override
+    public List<Event> randomSequence(int maxLength, boolean validEnd) {
+        List<Event> sequence = new LinkedList<Event>();
+        if (trie != null) {
+            boolean endFound = false;
+            while (!endFound) { // outer loop for length checking
+                sequence = new LinkedList<Event>();
+                IncompleteMemory<Event> context = new IncompleteMemory<Event>(trieOrder - 1);
+                context.add(Event.STARTEVENT);
+
+                while (!endFound && sequence.size() <= maxLength) {
+                    double randVal = r.nextDouble();
+                    double probSum = 0.0;
+                    List<Event> currentContext = context.getLast(trieOrder);
+                    for (Event symbol : trie.getKnownSymbols()) {
+                        probSum += getProbability(currentContext, symbol);
+                        if (probSum >= randVal) {
+                            if (!(Event.STARTEVENT.equals(symbol) || Event.ENDEVENT.equals(symbol)))
+                            {
+                                // only add the symbol the sequence if it is not
+                                // START or END
+                                context.add(symbol);
+                                sequence.add(symbol);
+                            }
+                            endFound =
+                                (Event.ENDEVENT.equals(symbol)) ||
+                                    (!validEnd && sequence.size() == maxLength);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        return sequence;
+    }
+
+    /**
+     * <p>
+     * Returns a Dot representation of the internal trie.
+     * </p>
+     * 
+     * @return dot representation of the internal trie
+     */
+    public String getTrieDotRepresentation() {
+        if (trie == null) {
+            return "";
+        }
+        else {
+            return trie.getDotRepresentation();
+        }
+    }
+
+    /**
+     * <p>
+     * Returns a {@link Tree} of the internal trie that can be used for visualization.
+     * </p>
+     * 
+     * @return {@link Tree} depicting the internal trie
+     */
+    public Tree<TrieVertex, Edge> getTrieGraph() {
+        if (trie == null) {
+            return null;
+        }
+        else {
+            return trie.getGraph();
+        }
+    }
+
+    /**
+     * <p>
+     * The string representation of the model is {@link Trie#toString()} of {@link #trie}.
+     * </p>
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        if (trie == null) {
+            return "";
+        }
+        else {
+            return trie.toString();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getNumStates()
+     */
+    @Override
+    public int getNumSymbols() {
+        if (trie == null) {
+            return 0;
+        }
+        else {
+            return trie.getNumSymbols();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getStateStrings()
+     */
+    @Override
+    public String[] getSymbolStrings() {
+        if (trie == null) {
+            return new String[0];
+        }
+        String[] stateStrings = new String[getNumSymbols()];
+        int i = 0;
+        for (Event symbol : trie.getKnownSymbols()) {
+            if (symbol.toString() == null) {
+                stateStrings[i] = "null";
+            }
+            else {
+                stateStrings[i] = symbol.toString();
+            }
+            i++;
+        }
+        return stateStrings;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getEvents()
+     */
+    @Override
+    public Collection<Event> getEvents() {
+        if (trie == null) {
+            return new HashSet<Event>();
+        }
+        else {
+            return trie.getKnownSymbols();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#generateSequences(int)
+     */
+    @Override
+    public Collection<List<Event>> generateSequences(int length) {
+        return generateSequences(length, false);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#generateSequences(int, boolean)
+     */
+    @Override
+    public Set<List<Event>> generateSequences(int length, boolean fromStart) {
+        Set<List<Event>> sequenceSet = new LinkedHashSet<List<Event>>();
+        if (length < 1) {
+            throw new IllegalArgumentException(
+                                                "Length of generated subsequences must be at least 1.");
+        }
+        if (length == 1) {
+            if (fromStart) {
+                List<Event> subSeq = new LinkedList<Event>();
+                subSeq.add(Event.STARTEVENT);
+                sequenceSet.add(subSeq);
+            }
+            else {
+                for (Event event : getEvents()) {
+                    List<Event> subSeq = new LinkedList<Event>();
+                    subSeq.add(event);
+                    sequenceSet.add(subSeq);
+                }
+            }
+            return sequenceSet;
+        }
+        Collection<Event> events = getEvents();
+        Collection<List<Event>> seqsShorter = generateSequences(length - 1, fromStart);
+        for (Event event : events) {
+            for (List<Event> seqShorter : seqsShorter) {
+                Event lastEvent = event;
+                if (getProbability(seqShorter, lastEvent) > 0.0) {
+                    List<Event> subSeq = new ArrayList<Event>(seqShorter);
+                    subSeq.add(lastEvent);
+                    sequenceSet.add(subSeq);
+                }
+            }
+        }
+        return sequenceSet;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#generateValidSequences (int)
+     */
+    @Override
+    public Collection<List<Event>> generateValidSequences(int length) {
+        // check for min-length implicitly done by generateSequences
+        Collection<List<Event>> allSequences = generateSequences(length, true);
+        Collection<List<Event>> validSequences = new LinkedHashSet<List<Event>>();
+        for (List<Event> sequence : allSequences) {
+            if (sequence.size() == length &&
+                Event.ENDEVENT.equals(sequence.get(sequence.size() - 1)))
+            {
+                validSequences.add(sequence);
+            }
+        }
+        return validSequences;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getProbability(java.util .List)
+     */
+    @Override
+    public double getProbability(List<Event> sequence) {
+        if (sequence == null) {
+            throw new IllegalArgumentException("sequence must not be null");
+        }
+        double prob = 1.0;
+        List<Event> context = new LinkedList<Event>();
+        for (Event event : sequence) {
+            prob *= getProbability(context, event);
+            context.add(event);
+        }
+        return prob;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getNumFOMStates()
+     */
+    @Override
+    public int getNumFOMStates() {
+        if (trie == null) {
+            return 0;
+        }
+        else {
+            return trie.getNumLeafAncestors();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getNumTransitions()
+     */
+    @Override
+    public int getNumTransitions() {
+        if (trie == null) {
+            return 0;
+        }
+        else {
+            return trie.getNumLeafs();
+        }
+    }
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/TrieNode.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/TrieNode.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/TrieNode.java	(revision 922)
@@ -0,0 +1,438 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.usageprofiles.Trie.Edge;
+import de.ugoe.cs.autoquest.usageprofiles.Trie.TrieVertex;
+import de.ugoe.cs.util.StringTools;
+import edu.uci.ics.jung.graph.DelegateTree;
+import edu.uci.ics.jung.graph.Tree;
+
+/**
+ * <p>
+ * This class implements a node of a trie. Each node is associated with a symbol and has a counter.
+ * The counter marks the number of occurences of the sequence defined by the path from the root of
+ * the trie to this node.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * 
+ * @param <T>
+ *            Type of the symbols that are stored in the trie.
+ * @see Trie
+ */
+class TrieNode<T> implements Serializable {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Counter for the number of occurences of the sequence.
+     * </p>
+     */
+    private int count;
+
+    /**
+     * <p>
+     * Symbol associated with the node.
+     * </p>
+     */
+    private final T symbol;
+
+    /**
+     * <p>
+     * Child nodes of this node. If the node is a leaf this collection is empty.
+     * </p>
+     */
+    private Collection<TrieNode<T>> children;
+
+    /**
+     * <p>
+     * Constructor. Creates a new TrieNode without a symbol associated.<br>
+     * <b>This constructor should only be used to create the root node of the trie!</b>
+     * </p>
+     */
+    TrieNode() {
+        this.symbol = null;
+        count = 0;
+        children = new LinkedList<TrieNode<T>>();
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new TrieNode. The symbol must not be null.
+     * </p>
+     * 
+     * @param symbol
+     *            symbol associated with the trie node
+     */
+    TrieNode(T symbol) {
+        if (symbol == null) {
+            throw new IllegalArgumentException(
+                                                "symbol must not be null. null is reserved for root node!");
+        }
+        this.symbol = symbol;
+        count = 0;
+        children = new LinkedList<TrieNode<T>>();
+    }
+
+    /**
+     * <p>
+     * Copy-Constructor. Creates a new TrieNode as copy of other. Other must not be null.
+     * </p>
+     * 
+     * @param other
+     */
+    TrieNode(TrieNode<T> other) {
+        if (other == null) {
+            throw new IllegalArgumentException("other must not be null");
+        }
+        symbol = other.symbol;
+        count = other.count;
+        children = new LinkedList<TrieNode<T>>();
+        for (TrieNode<T> child : other.children) {
+            children.add(new TrieNode<T>(child));
+        }
+    }
+
+    /**
+     * <p>
+     * Adds a given subsequence to the trie and increases the counters accordingly.
+     * </p>
+     * 
+     * @param subsequence
+     *            subsequence whose counters are increased
+     * @see Trie#add(List)
+     */
+    public void add(List<T> subsequence) {
+        if (!subsequence.isEmpty()) {
+            if (!symbol.equals(subsequence.get(0))) { // should be guaranteed by
+                                                      // the
+                                                      // recursion/TrieRoot!
+                throw new AssertionError("Invalid trie operation!");
+            }
+            count++;
+            subsequence.remove(0);
+            if (!subsequence.isEmpty()) {
+                T nextSymbol = subsequence.get(0);
+                getChildCreate(nextSymbol).add(subsequence);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the symbol associated with the node.
+     * </p>
+     * 
+     * @return symbol associated with the node
+     */
+    public T getSymbol() {
+        return symbol;
+    }
+
+    /**
+     * <p>
+     * Returns the number of occurences of the sequence represented by the node.
+     * </p>
+     * 
+     * @return number of occurences of the sequence represented by the node
+     */
+    public int getCount() {
+        return count;
+    }
+
+    /**
+     * <p>
+     * Returns the child of the node associated with the given symbol or creates it if it does not
+     * exist yet.
+     * </p>
+     * 
+     * @param symbol
+     *            symbol whose node is required
+     * @return node associated with the symbol
+     * @see Trie#getChildCreate(Object)
+     */
+    protected TrieNode<T> getChildCreate(T symbol) {
+        TrieNode<T> node = getChild(symbol);
+        if (node == null) {
+            node = new TrieNode<T>(symbol);
+            children.add(node);
+        }
+        return node;
+    }
+
+    /**
+     * <p>
+     * Returns the child of the node associated with the given symbol or null if it does not exist.
+     * </p>
+     * 
+     * @param symbol
+     *            symbol whose node is required
+     * @return node associated with the symbol; null if no such node exists
+     * @see Trie#getChild(Object)
+     */
+    protected TrieNode<T> getChild(T symbol) {
+        for (TrieNode<T> child : children) {
+            if (child.getSymbol().equals(symbol)) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * <p>
+     * Returns all children of this node.
+     * </p>
+     * 
+     * @return children of this node
+     */
+    protected Collection<TrieNode<T>> getChildren() {
+        return children;
+    }
+
+    /**
+     * <p>
+     * Searches the sub-trie of this trie node for a given sequence and returns the node associated
+     * with the sequence or null if no such node is found.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence that is searched for
+     * @return node associated with the sequence
+     * @see Trie#find(List)
+     */
+    public TrieNode<T> find(List<T> subsequence) {
+        TrieNode<T> result = null;
+        if (subsequence.isEmpty()) {
+            result = this;
+        }
+        else {
+            TrieNode<T> node = getChild(subsequence.get(0));
+            if (node != null) {
+                subsequence.remove(0);
+                result = node.find(subsequence);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * <p>
+     * Returns a collection of all symbols that follow a this node, i.e., the symbols associated
+     * with the children of this node.
+     * </p>
+     * 
+     * @return symbols follow this node
+     * @see TrieNode#getFollowingSymbols()
+     */
+    public Collection<T> getFollowingSymbols() {
+        Collection<T> followingSymbols = new LinkedList<T>();
+        for (TrieNode<T> child : children) {
+            followingSymbols.add(child.getSymbol());
+        }
+        return followingSymbols;
+    }
+
+    /**
+     * <p>
+     * The string representation of a node is {@code symbol.toString()#count}
+     * </p>
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        String str = symbol.toString() + " #" + count;
+        if (!children.isEmpty()) {
+            str += StringTools.ENDLINE + children.toString();
+        }
+        return str;
+    }
+
+    /**
+     * <p>
+     * Generates a {@link Tree} represenation of the trie.
+     * </p>
+     * 
+     * @param parent
+     *            parent vertex in the generated tree
+     * @param graph
+     *            complete tree
+     */
+    void getGraph(TrieVertex parent, DelegateTree<TrieVertex, Edge> graph) {
+        TrieVertex currentVertex;
+        if (isRoot()) {
+            currentVertex = new TrieVertex("root");
+            graph.addVertex(currentVertex);
+        }
+        else {
+            currentVertex = new TrieVertex(getSymbol().toString() + "#" + getCount());
+            graph.addChild(new Edge(), parent, currentVertex);
+        }
+        for (TrieNode<T> node : children) {
+            node.getGraph(currentVertex, graph);
+        }
+    }
+
+    /**
+     * <p>
+     * Appends the current node to the dot representation of the trie.
+     * </p>
+     * 
+     * @param stringBuilder
+     *            {@link StringBuilder} to which the dot representation is appended
+     */
+    void appendDotRepresentation(StringBuilder stringBuilder) {
+        String thisSaneId;
+        if (isRoot()) {
+            thisSaneId = "root";
+        }
+        else {
+            thisSaneId =
+                symbol.toString().replace("\"", "\\\"").replaceAll("[\r\n]", "") + "#" + count;
+        }
+        stringBuilder.append(" " + hashCode() + " [label=\"" + thisSaneId + "\"];" +
+            StringTools.ENDLINE);
+        for (TrieNode<T> childNode : children) {
+            stringBuilder.append(" " + hashCode() + " -> " + childNode.hashCode() + ";" +
+                StringTools.ENDLINE);
+        }
+        for (TrieNode<T> childNode : children) {
+            childNode.appendDotRepresentation(stringBuilder);
+        }
+    }
+
+    /**
+     * <p>
+     * Checks if the node is a leaf.
+     * </p>
+     * 
+     * @return true if the node is a leaf, false otherwise.
+     */
+    protected boolean isLeaf() {
+        return children.isEmpty();
+    }
+
+    /**
+     * <p>
+     * Checks if the node is the root.
+     * </p>
+     * 
+     * @return true if the node is the root of the trie, false otherwise
+     */
+    protected boolean isRoot() {
+        return symbol == null;
+    }
+
+    /**
+     * <p>
+     * Recursive methods that collects all nodes that are ancestors of leafs and stores them in the
+     * set.
+     * </p>
+     * 
+     * @param ancestors
+     *            set of all ancestors of leafs
+     */
+    protected void getLeafAncestors(List<TrieNode<T>> ancestors) {
+        boolean isAncestor = false;
+        for (TrieNode<T> child : children) {
+            child.getLeafAncestors(ancestors);
+            isAncestor |= child.isLeaf();
+        }
+        if (isAncestor) {
+            ancestors.add(this);
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the number of descendants of this node that are leafs. This does not only include
+     * direct children of this node, but all leafs in the sub-trie with this node as root.
+     * </p>
+     * 
+     * @return number of leafs in this sub-trie
+     */
+    protected int getNumLeafs() {
+        int numLeafs = 0;
+        for (TrieNode<T> child : children) {
+            if (child.isLeaf()) {
+                numLeafs++;
+            }
+            else {
+                numLeafs += child.getNumLeafs();
+            }
+        }
+        return numLeafs;
+    }
+
+    /**
+     * <p>
+     * Sets the {@link #count} of this node.
+     * </p>
+     * <p>
+     * This function should only be used sparingly and very carefully! The count is usually
+     * maintained automatically by the training procedures.
+     * </p>
+     * 
+     * @param count
+     *            new count
+     */
+    protected void setCount(int count) {
+        this.count = count;
+    }
+
+    /**
+     * <p>
+     * Two TrieNodes are defined as equal, if their {@link #count}, {@link #symbol}, and
+     * {@link #children} are equal.
+     * </p>
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @SuppressWarnings("rawtypes")
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (other instanceof TrieNode) {
+            TrieNode otherNode = (TrieNode) other;
+            if (symbol == null) {
+                return count == otherNode.count && otherNode.symbol == null &&
+                    children.equals(otherNode.children);
+            }
+            else {
+                return count == otherNode.count && symbol.equals(((TrieNode) other).symbol) &&
+                    children.equals(((TrieNode) other).children);
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int multiplier = 17;
+        int hash = 42;
+        if (symbol != null) {
+            hash = multiplier * hash + symbol.hashCode();
+        }
+        hash = multiplier * hash + count;
+        hash = multiplier * hash + children.hashCode();
+        return hash;
+    }
+}
