lcsim-contrib/src/main/java/org/lcsim/contrib/onoprien/util
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
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