Commit in lcsim-contrib/src/main/java/org/lcsim/contrib/onoprien/util on MAIN
WeightedList.java+443added 1.1
WeightedTable.java+423added 1.1
+866
2 added files
Redesign and extend MCTruth to support CAT analysis

lcsim-contrib/src/main/java/org/lcsim/contrib/onoprien/util
WeightedList.java added at 1.1
diff -N WeightedList.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ WeightedList.java	9 Feb 2009 03:57:42 -0000	1.1
@@ -0,0 +1,443 @@
+package org.lcsim.contrib.onoprien.util;
+
+import java.util.*;
+
+/**
+ * A list of elements with a <tt>double</tt> weight assigned to each element.
+ * <p>
+ * If an element is added to the list without explicitly specifying the weight, the weight is assumed to be 1.
+ * <p>
+ * The list is backed by an <tt>ArrayList</tt>. If none of the methods that explicitly specify
+ * the weight have been called for an objet of this class, it is almost identical in terms of
+ * memory and speed to an object of <tt>ArrayList</tt> type.
+ *
+ * @author D. Onoprienko
+ * @version $Id: WeightedList.java,v 1.1 2009/02/09 03:57:42 onoprien Exp $
+ */
+public class WeightedList<E> extends ArrayList<E> {
+  
+// -- Private parts :  ---------------------------------------------------------
+  
+  private double[] _w = null;
+  
+// -- Constructors :  ----------------------------------------------------------
+
+  /** Constructs an empty list with an initial capacity of ten. */
+  public WeightedList() {
+    super();
+  }
+
+  /**
+   * Constructs a list containing the elements of the specified collection, in
+   * the order they are returned by the collection's iterator.
+   */
+  public WeightedList(Collection<? extends E> c) {
+    super(c);
+  }
+
+  /** Constructs an empty list with the specified initial capacity. */
+  public WeightedList(int initialCapacity) {
+    super(initialCapacity);
+  }
+
+  /**
+   * Constructs a list containing the elements of the specified collection, in
+   * the order they are returned by the collection's iterator, with the specified weights.
+   * The supplied double array will be owned by this list.
+   */
+  public WeightedList(Collection<? extends E> c, double[] weights) {
+    super(c);
+    int s = weights.length;
+    if (c.size() != s) throw new IllegalArgumentException();
+    if (s != 0) _w = weights;
+  }
+
+  /** Constructs a list that initially contains a single element with the specified weight. */
+  public WeightedList(E element, double weight) {
+    super(1);
+    super.add(element);
+    _w = new double[] {weight};
+  }
+  
+// -- Getters:  ----------------------------------------------------------------
+
+  /** Returns weight of the element with the specified index. */
+  public double getWeight(int index) {
+    return _w[index];
+  }
+
+  /** Returns weight of the specified element. */
+  public double getWeight(E element) {
+    int index = indexOf(element);
+    return (index == -1) ? 0. : getWeight(index);
+  }
+
+  /** Returns <tt>true</tt> if any of the elements in the list has been explicitly assigned weight. */
+  public boolean isWeighted() {
+    return (_w != null);
+  }
+  
+// -- Modifiers :  -------------------------------------------------------------
+
+  /** Appends the specified element to the end of this list, with unit weight. */
+  public boolean add(E element) {
+    if (_w == null) {
+      return super.add(element);
+    } else {
+      return add(element, 1.);
+    }
+  }
+
+  /** Appends the specified element to the end of this list, with the specified weight. */
+  public boolean add(E element, double weight) {
+    int index = size();
+    if (_w == null) {
+      _w = new double[2*index+1];
+      Arrays.fill(_w, 0, index, 1.);
+    } else if (index == _w.length) {
+      _w = Arrays.copyOf(_w, _w.length*2);
+    }
+    _w[index] = weight;
+    return super.add(element);
+  }
+
+  /** Inserts the specified element at the specified position in this list, with unit weight. */
+  public void add(int index, E element) {
+    if (_w == null) {
+      super.add(index, element);
+    } else {
+      add(index, element, 1.);
+    }
+  }
+
+  /** Inserts the specified element at the specified position in this list, with the specified weight. */
+  public void add(int index, E element, double weight) {
+    super.add(index, element);
+    int s = size();
+    if (_w == null) {
+      _w = new double[2*s];
+      Arrays.fill(_w, 0, index, 1.);
+      _w[index] = weight;
+      Arrays.fill(_w, index+1, s, 1.);
+    } else if (_w.length < s) {
+      double[] u = new double[2*s];
+      for (int i=0; i<index; i++) u[i]=_w[i];
+      u[index] = weight;
+      for (int i=index+1; i<s; i++) u[i] = _w[i-1];
+      _w = u;
+    } else {
+      for (int i = s-1; i>index; i--) _w[i] = _w[i-1];
+      _w[index] = weight;
+    }
+  }
+
+  /**
+   * Adds weight to one of the elements. If this list contains the specified element,
+   * the weight of its first occurance will be increased by <tt>weight</tt>. Otherwise,
+   * the element will be appended to the list with the specified weight.
+   *
+   * @return  Previous weight of the specified element (zero if the element was not in this list).
+   */
+  public double addWeight(E element, double weight) {
+    int index = indexOf(element);
+    if (index == -1) {
+      add(element, weight);
+      return 0.;
+    } else {
+      if (_w == null) {
+        _w = new double[size()];
+        Arrays.fill(_w, 1.);
+      }
+      double out = _w[index];
+      _w[index] += weight;
+      return out;
+    }
+  }
+
+  /**
+   * Appends all of the elements in the specified collection to the end of this list, with unit weight.
+   * Elements are appended in the order that they are returned by the specified collection's Iterator.
+   */
+  public boolean addAll(Collection<? extends E> c) {
+    if (_w != null) {
+      int newSize = size()+c.size();
+      if (_w.length < newSize) _w = Arrays.copyOf(_w, newSize);
+      Arrays.fill(_w, size(), newSize, 1.);
+    }
+    return super.addAll(c);
+  }
+
+  /**
+   * Inserts all of the elements in the specified collection into this list,
+   *  with unit weight, starting at the specified position.
+   */
+  public boolean addAll(int index, Collection<? extends E> c) {
+    if (_w != null) {
+      int oldSize = size();
+      int difSize = c.size();
+      int newSize = oldSize + difSize;
+      if (_w.length < newSize) {
+        double[] u = new double[newSize];
+        for (int i=0; i<index; i++) u[i] = _w[i];
+        Arrays.fill(u, index, index+difSize, 1.);
+        for (int i = index; i<oldSize; i++) u[i+difSize] = _w[i];
+        _w = u;
+      } else {
+        for (int i=oldSize-1; i>=index; i--) _w[i+difSize] = _w[i];
+        Arrays.fill(_w, index, index+difSize, 1.);
+      }
+    }
+    return super.addAll(c);
+  }
+
+  /** Removes all elements from this list. */
+  public void clear() {
+    super.clear();
+    _w = null;
+  }
+
+  /** Removes the element at the specified position in this list. */
+  public E remove(int index) {
+    E out = super.remove(index);
+    if (_w != null) {
+      double newSize = size()-1;
+      if (newSize == 0) {
+        _w = null;
+      } else {
+        for (int i=index; i<newSize; i++) _w[i] = _w[i+1];
+      }
+    }
+    return out;
+  }
+
+  /** Removes the first occurrence of the specified element from this list, if it is present. */
+  public boolean remove(Object o) {
+    int index = indexOf(o);
+    if (index == -1) {
+      return false;
+    } else {
+      remove(index);
+      return true;
+    }
+  }
+
+  /** Removes all of this list's elements that are also contained in the specified collection. */
+  public boolean removeAll(Collection<?> c) {
+    if (_w == null) {
+      return super.removeAll(c);
+    } else {
+      boolean out = false;
+      for (Object o : c) {
+        out |= remove(o);
+      }
+      return out;
+    }
+  }
+
+  /** Retains only the elements in this list that are contained in the specified collection. */
+  public boolean retainAll(Collection<?> c) {
+    if (_w != null) {
+      int newIndex = 0;
+      int s=size();
+      double[] u = new double[_w.length];
+      for (int oldIndex=0; oldIndex<s; oldIndex++) {
+        if (c.contains(get(oldIndex))) {
+          u[newIndex++] = _w[oldIndex];
+        }
+      }
+      _w = u;
+    }
+    return super.retainAll(c);
+  }
+
+  /**
+   * Removes from this list all of the elements whose index is between <tt>fromIndex</tt>,
+   * inclusive, and <tt>toIndex</tt>, exclusive.
+   */
+  protected void removeRange(int fromIndex, int toIndex) {
+    int d = toIndex - fromIndex;
+    int newSize = size() - d;
+    super.removeRange(fromIndex, toIndex);
+    if (_w != null) {
+      for (int i=fromIndex; i<newSize; i++) _w[i] = _w[i+d];
+    }
+  }
+
+  /** Replaces the element at the specified position in this list with the specified element, with unit weight. */
+  public E set(int index, E element) {
+    if (_w != null) {
+      _w[index] = 1.;
+    }
+    return super.set(index, element);
+  }
+
+  /** Replaces the element at the specified position in this list with the specified element, with the specified weight. */
+  public E set(int index, E element, double weight) {
+    if (_w == null) {
+      _w = new double[size()];
+      Arrays.fill(_w, 1.);
+    }
+    _w[index] = weight;
+    return super.set(index, element);
+  }
+
+// -- Capacity :  --------------------------------------------------------------
+
+  /**
+   * Increases the capacity of this <tt>WeightedList</tt> instance, if necessary, to ensure that it can hold
+   * at least the number of elements specified by the minimum capacity argument.
+   */
+  public void ensureCapacity(int minCapacity) {
+    super.ensureCapacity(minCapacity);
+    if (_w != null && _w.length < minCapacity) _w = Arrays.copyOf(_w, Math.max(size(), minCapacity));
+  }
+
+  /** Trims the capacity of this <tt>WeightedList</tt> instance to be the list's current size. */
+  public void trimToSize() {
+    super.trimToSize();
+    if (_w != null && _w.length > size()) _w = Arrays.copyOf(_w, size());
+  }
+
+// -- Object methods :  --------------------------------------------------------
+
+  /** Returns a shallow copy of this <tt>WeightedList</tt> instance. */
+  public WeightedList<E> clone() {
+    WeightedList<E> newList = new WeightedList<E>(this);
+    newList._w = (_w == null) ? null : Arrays.copyOf(_w, size());
+    return newList;
+  }
+
+  /** Returns a string representation of this list. */
+  public String toString() {
+    StringBuilder out = new StringBuilder();
+    for (int i=0; i<size(); i++) {
+      out.append(get(i) +" weight "+ getWeight(i) +"\n");
+    }
+    return out.toString();
+  }
+
+// -- Iterators :  -------------------------------------------------------------
+
+  /**
+   * Returns an iterator over the elements in this list in proper sequence.
+   * The iterator cannot be used to modify this list if the weight has been explicitly
+   * specified for any of its elements.
+   */
+  public Iterator<E> iterator() {
+    return new WeightedListIterator<E>(super.listIterator());
+  }
+
+  /**
+   * Returns a list iterator over the elements in this list in proper sequence.
+   * The iterator cannot be used to modify this list if the weight has been explicitly
+   * specified for any of its elements.
+   */
+  public ListIterator<E> listIterator() {
+    return new WeightedListIterator<E>(super.listIterator());
+  }
+
+  /**
+   * Returns a list iterator over the elements in this list in proper sequence,
+   * starting at the specified position in this list.
+   * The iterator cannot be used to modify this list if the weight has been explicitly
+   * specified for any of its elements.
+   */
+  public ListIterator<E> listIterator(int index) {
+    return new WeightedListIterator<E>(super.listIterator(index));
+  }
+
+// -- Views :  -----------------------------------------------------------------
+
+  /**
+   * Returns a view of the portion of this list between the specified <tt>fromIndex</tt>,
+   * inclusive, and <tt>toIndex</tt>, exclusive.
+   * This method is only supported if this list is unweighted (no weight has been explicitly
+   * specified for any of its elements). If the list is weighted,
+   * <tt>UnsupportedOperationException</tt> will be thrown.
+   */
+  public List<E> subList(int fromIndex, int toIndex) {
+    if (_w == null) {
+      return super.subList(fromIndex, toIndex);
+    } else {
+      throw new UnsupportedOperationException(); // FIXME
+    }
+  }
+
+// -- Sorting :  ---------------------------------------------------------------
+
+  /** Sorts this list in decreasing weight order. */
+  public void sort() {
+    if (_w != null) {
+      int s = size();
+      ArrayList<Pair> temp = new ArrayList<Pair>(s);
+      for (int i=0; i<s; i++) temp.add(new Pair(get(i), _w[i]));
+      Collections.sort(temp);
+      for (int i=0; i<s; i++) {
+        Pair p = temp.get(i);
+        super.set(i, p.element);
+        _w[i] = p.weight;
+      }
+    }
+  }
+
+// -- Iterator class :  --------------------------------------------------------
+
+  private class WeightedListIterator<E> implements ListIterator<E> {
+
+    private ListIterator<E> _it;
+
+    WeightedListIterator(ListIterator<E> it) {_it = it;}
+
+    public boolean hasNext() {return _it.hasNext();}
+
+    public E next() {return _it.next();}
+
+    public boolean hasPrevious() {return _it.hasPrevious();}
+
+    public E previous() {return _it.previous();}
+
+    public int nextIndex() {return _it.nextIndex();}
+
+    public int previousIndex() {return _it.previousIndex();}
+
+    public void remove() {
+      if (_w == null) {
+        _it.remove();
+      } else {
+        throw new UnsupportedOperationException();
+      }
+    }
+
+    public void set(E e) {
+      if (_w == null) {
+        _it.set(e);
+      } else {
+        throw new UnsupportedOperationException();
+      }
+    }
+
+    public void add(E e) {
+      if (_w == null) {
+        _it.add(e);
+      } else {
+        throw new UnsupportedOperationException();
+      }
+    }
+
+  }
+
+// -- Pair class for sorting :  ------------------------------------------------
+
+  private class Pair implements Comparable<Pair> {
+    E element;
+    double weight;
+    Pair(E element, double weight) {
+      this.element = element;
+      this.weight = weight;
+    }
+    public int compareTo(Pair p) {
+      return Double.compare(p.weight, this.weight);
+    }
+  }
+
+}

lcsim-contrib/src/main/java/org/lcsim/contrib/onoprien/util
WeightedTable.java added at 1.1
diff -N WeightedTable.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ WeightedTable.java	9 Feb 2009 03:57:42 -0000	1.1
@@ -0,0 +1,423 @@
+package org.lcsim.contrib.onoprien.util;
+
+import java.util.*;
+
+import org.lcsim.event.RelationalTable;
+
+/**
+ * Implementation of {@link RelationalTable} that allows independent ordering of 
+ * TO and FROM objects, and {@link WeightedList} output.
+ * Based on Tony Johnson's <tt>BaseRelationalTable</tt>.
+ *
+ * @author D. Onoprienko
+ * @version $Id: WeightedTable.java,v 1.1 2009/02/09 03:57:42 onoprien Exp $
+ */
+public class WeightedTable<F,T> implements RelationalTable<F, T> {
+
+// -- Private parts :  ---------------------------------------------------------
+
+  private final static double WEIGHT_ONE = 1.0;
+  private final static double WEIGHT_ZERO = 0.0;
+   
+  private final boolean _oneFrom;
+  private final boolean _oneTo;
+  private final boolean _weighted;
+   
+  private Map<Relation<F, T>, Double> _weightedRelations;
+  private Set<Relation<F, T>> _unweightedRelations;
+
+  private Map<F, Set<T>> _fromMultiMap;
+  private Map<T, Set<F>> _toMultiMap;
+
+  private Map<F, T> _fromMap;
+  private Map<T, F> _toMap;
+
+  private final Comparator<F> _compFrom;
+  private final Comparator<T> _compTo;
+
+// -- Constructors :  ----------------------------------------------------------
+
+  /** Creates an empty ManyToMany relational table with weights. */
+  public WeightedTable() {
+    this(Mode.MANY_TO_MANY, Weighting.WEIGHTED);
+  }
+
+  public WeightedTable(Mode mode, Weighting weighting) {
+    this(mode, weighting, null, null);
+  }
+
+  public WeightedTable(Mode mode, Weighting weighting, Comparator<F> compFrom, Comparator<T> compTo) {
+    
+    _oneFrom = (mode == Mode.ONE_TO_MANY) || (mode == Mode.ONE_TO_ONE);
+    _oneTo = (mode == Mode.MANY_TO_ONE) || (mode == Mode.ONE_TO_ONE);
+    _weighted = (weighting == Weighting.WEIGHTED);
+
+    if (_weighted) {
+      _weightedRelations = new HashMap<Relation<F, T>, Double>();
+    } else {
+      _unweightedRelations = new HashSet<Relation<F, T>>();
+    }
+
+    if (_oneTo) {
+      _fromMap = new HashMap<F, T>();
+    } else {
+      _fromMultiMap = new HashMap<F, Set<T>>();
+    }
+
+    if (_oneFrom) {
+      _toMap = new HashMap<T, F>();
+    } else {
+      _toMultiMap = new HashMap<T, Set<F>>();
+    }
+
+    _compFrom = compFrom;
+    _compTo = compTo;
+  }
+  
+  
+// -- Modifiers :  -------------------------------------------------------------
+
+  public boolean add(F from, T to) {
+    return add(from, to, WEIGHT_ONE);
+  }
+   
+  public boolean add(F from, T to, double weight) {
+
+    if (from == null || to == null) {
+      throw new IllegalArgumentException("Argument cannot be null");
+    }
+    if (!_weighted && weight != WEIGHT_ONE) {
+      throw new IllegalArgumentException("Weight must be 1 for unweighted relational table");
+    }
+
+    boolean result = false;
+
+    if (_oneTo) {
+      T oldTo = _fromMap.put(from, to);
+      if (oldTo != null) {
+        removeRelation(from, oldTo);
+        result |= true;
+      }
+    }
+    if (_oneFrom) {
+      F oldFrom = _toMap.put(to, from);
+      if (oldFrom != null) {
+        removeRelation(oldFrom, to);
+        result |= true;
+      }
+    }
+
+    // This must be done before the multimaps below to ensure ordering
+    result |= addRelation(from, to, weight);
+
+    if (!_oneTo) {
+      Set<T> toList = _fromMultiMap.get(from);
+      if (toList == null) {
+        if (_compTo == null) {
+          toList = _weighted ?  new TreeSet<T>(new FromWeightComparator(from)) : new HashSet<T>() ;
+        } else {
+          toList = new TreeSet<T>(_compTo);
+        }
+        _fromMultiMap.put(from, toList);
+      }
+      toList.add(to);
+    }
+
+    if (!_oneFrom) {
+      Set<F> fromList = _toMultiMap.get(to);
+      if (fromList == null) {
+        if (_compFrom == null) {
+          fromList = _weighted ? new TreeSet<F>(new ToWeightComparator(to)) : new HashSet<F>() ;
+        } else {
+          fromList = new TreeSet<F>(_compFrom);
+        }
+        _toMultiMap.put(to, fromList);
+      }
+      fromList.add(from);
+    }
+
+    return result;
+  }
+   
+  public boolean remove(F from, T to) {
+
+    boolean result = removeRelation(from, to);
+    if (result) {
+      if (_oneTo) {
+        _fromMap.remove(from);
+      } else {
+        Set<T> toList = _fromMultiMap.get(from);
+        toList.remove(from);
+      }
+
+      if (_oneFrom) {
+        _toMap.remove(to);
+      } else {
+        Set<T> fromList = _fromMultiMap.get(to);
+        fromList.remove(to);
+      }
+    }
+    return result;
+  }
+
+
+// -- Getters :  ---------------------------------------------------------------
+   
+  public T to(F from) {
+    if (_oneTo) {
+      return _fromMap.get(from);
+    } else {
+      Set<T> all = allFrom(from);
+      if (all.size() > 1) {
+        throw new IllegalArgumentException();
+      }
+      return all.size() == 1 ? all.iterator().next() : null;
+    }
+  }
+   
+  public F from(T to) {
+    if (_oneFrom) {
+      return _toMap.get(to);
+    } else {
+      Set<F> all = allTo(to);
+      if (all.size() > 1) {
+        throw new IllegalArgumentException();
+      }
+      return all.size() == 1 ? all.iterator().next() : null;
+    }
+  }
+   
+  public Set<T> allFrom(F from) {
+    if (_oneTo) {
+      T to = _fromMap.get(from);
+      return to == null ? Collections.<T>emptySet() : Collections.singleton(to);
+    } else {
+      Set<T> result = _fromMultiMap.get(from);
+      return result == null ? Collections.<T>emptySet() : Collections.unmodifiableSet(result);
+    }
+  }
+   
+  public Map<T, Double> allFromWithWeights(F from) {
+    if (_oneTo) {
+      T to = _fromMap.get(from);
+      return to == null ? Collections.<T, Double>emptyMap() : Collections.singletonMap(to, weightFromTo(from, to));
+    } else {
+      Set<T> toList = _fromMultiMap.get(from);
+      if (toList == null || toList.isEmpty()) {
+        return Collections.<T, Double>emptyMap();
+      }
+      Map<T, Double> result = new LinkedHashMap<T, Double>();
+      for (T to : toList) {
+        result.put(to, weightFromTo(from, to));
+      }
+      return result;
+    }
+  }
+   
+  public Set<F> allTo(T to) {
+    if (_oneFrom) {
+      F from = _toMap.get(to);
+      return from == null ? Collections.<F>emptySet() : Collections.singleton(from);
+    } else {
+      Set<F> result = _toMultiMap.get(to);
+      return result == null ? Collections.<F>emptySet() : Collections.unmodifiableSet(result);
+    }
+  }
+   
+  public Map<F, Double> allToWithWeights(T to) {
+    if (_oneFrom) {
+      F from = _toMap.get(to);
+      return from == null ? Collections.<F, Double>emptyMap() : Collections.singletonMap(from, weightFromTo(from, to));
+    } else {
+      Set<F> fromList = _toMultiMap.get(to);
+      if (fromList == null || fromList.isEmpty()) {
+        return Collections.<F, Double>emptyMap();
+      }
+      Map<F, Double> result = new LinkedHashMap<F, Double>();
+      for (F from : fromList) {
+        result.put(from, weightFromTo(from, to));
+      }
+      return result;
+    }
+  }
+   
+  public double weightFromTo(F from, T to) {
+    Relation relation = new Relation(from, to);
+    if (!_weighted) {
+      return _unweightedRelations.contains(relation) ? WEIGHT_ONE : WEIGHT_ZERO;
+    } else {
+      Double d = _weightedRelations.get(relation);
+      return d == null ? WEIGHT_ZERO : d.doubleValue();
+    }
+  }
+   
+  public Mode getMode() {
+    if (_oneFrom) {
+      if (_oneTo) {
+        return Mode.ONE_TO_ONE;
+      } else {
+        return Mode.ONE_TO_MANY;
+      }
+    } else {
+      if (_oneTo) {
+        return Mode.MANY_TO_ONE;
+      } else {
+        return Mode.MANY_TO_MANY;
+      }
+    }
+  }
+
+  public Weighting getWeighting() {
+    return _weighted ? Weighting.WEIGHTED : Weighting.UNWEIGHTED;
+  }
+
+  public int size() {
+    return relations().size();
+  }
+  
+  public WeightedList<T> listFrom(F from) {
+    if (_oneTo) {
+      T to = _fromMap.get(from);
+      if (to == null) {
+        return new WeightedList<T>(0);
+      } else {
+        WeightedList<T> out = new WeightedList<T>(1);
+        out.add(to, weightFromTo(from, to));
+        return out;
+      }
+    } else {
+      Set<T> toList = _fromMultiMap.get(from);
+      if (toList == null || toList.isEmpty()) {
+        return new WeightedList<T>(0);
+      }
+      WeightedList<T> out = new WeightedList<T>(toList.size());
+      for (T to : toList) {
+        out.add(to, weightFromTo(from, to));
+      }
+      return out;
+    }
+  }
+
+  public WeightedList<F> listTo(T to) {
+    if (_oneFrom) {
+      F from = _toMap.get(to);
+      if (from == null) {
+        return new WeightedList<F>(0);
+      } else {
+        WeightedList<F> out = new WeightedList<F>(1);
+        out.add(from, weightFromTo(from, to));
+        return out;
+      }
+    } else {
+      Set<F> fromList = _toMultiMap.get(to);
+      if (fromList == null || fromList.isEmpty()) {
+        return new WeightedList<F>(0);
+      }
+      WeightedList<F> out = new WeightedList<F>(fromList.size());
+      for (F from : fromList) {
+        out.add(from, weightFromTo(from, to));
+      }
+      return out;
+    }
+  }
+
+  /** Returns a set of all <tt>FROM</tt> objects in this table. */
+  public Set<F> fromSet() {
+    if (_oneTo) {
+      return Collections.unmodifiableSet(_fromMap.keySet());
+    } else {
+      return Collections.unmodifiableSet(_fromMultiMap.keySet());
+    }
+  }
+
+  /** Returns a set of all <tt>TO</tt> objects in this table. */
+  public Set<T> toSet() {
+    if (_oneFrom) {
+      return Collections.unmodifiableSet(_toMap.keySet());
+    } else {
+      return Collections.unmodifiableSet(_toMultiMap.keySet());
+    }
+  }
+
+
+// -- Helper methods :  --------------------------------------------------------
+
+  private Set<Relation<F, T>> relations() {
+    return _weighted ? _weightedRelations.keySet() : _unweightedRelations ;
+  }
+
+  private boolean removeRelation(F from, T to) {
+    Relation relation = new Relation(from, to);
+    return relations().remove(relation);
+  }
+
+  private boolean addRelation(F from, T to, double weight) {
+    Relation relation = new Relation<F, T>(from, to);
+    return !_weighted ? !_unweightedRelations.add(relation) : _weightedRelations.put(relation, weight) != null;
+  }
+
+
+// -- Relation class :  --------------------------------------------------------
+   
+  private static class Relation<F, T> {
+
+    private F from;
+    private T to;
+
+    Relation(F from, T to) {
+      this.from = from;
+      this.to = to;
+    }
+
+    F getFrom() {
+      return from;
+    }
+
+    T getTo() {
+      return to;
+    }
+
+    public int hashCode() {
+      return to.hashCode() + 37 * from.hashCode();
+    }
+
+    public boolean equals(Object obj) {
+      if (obj instanceof Relation) {
+        Relation that = (Relation) obj;
+        return this.from.equals(that.from) && this.to.equals(that.to);
+      } else {
+        return false;
+      }
+    }
+  }
+
+  
+// -- Comparators :  -----------------------------------------------------------
+
+  private class FromWeightComparator implements Comparator<T> {
+    private F from;
+    FromWeightComparator(F from) {
+      this.from = from;
+    }
+    public int compare(T o1, T o2) {
+      double w1 = weightFromTo(from, o1);
+      double w2 = weightFromTo(from, o2);
+      int out = Double.compare(w2, w1);
+      return (out == 0) ? o1.hashCode() - o2.hashCode() : out;
+    }
+  }
+
+  private class ToWeightComparator implements Comparator<F> {
+    private T to;
+    ToWeightComparator(T to) {
+      this.to = to;
+    }
+    public int compare(F o1, F o2) {
+      double w1 = weightFromTo(o1, to);
+      double w2 = weightFromTo(o2, to);
+      int out = Double.compare(w2, w1);
+      return (out == 0) ? o1.hashCode() - o2.hashCode() : out;
+    }
+  }
+
+}
\ No newline at end of file
CVSspam 0.2.8