package fj.data;
import fj.Equal;
import fj.F;
import fj.F1Functions;
import fj.Hash;
import fj.Ord;
import fj.P;
import fj.P2;
import fj.P3;
import fj.Show;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import static fj.Function.compose;
import static fj.Function.flip;
import static fj.P.p;
import static fj.data.IterableW.join;
import static fj.data.List.iterableList;
/**
* An immutable, in-memory map, backed by a red-black tree.
*/
public final class TreeMap<K, V> implements Iterable<P2<K, V>> {
private final Set<P2<K, Option<V>>> tree;
private TreeMap(final Set<P2<K, Option<V>>> tree) {
this.tree = tree;
}
private static <K, V> Ord<P2<K, V>> ord(final Ord<K> keyOrd) {
return keyOrd.contramap(P2.__1());
}
/**
* Constructs an empty tree map.
*
* @param keyOrd An order for the keys of the tree map.
* @return an empty TreeMap with the given key order.
*/
public static <K, V> TreeMap<K, V> empty(final Ord<K> keyOrd) {
return new TreeMap<>(Set.empty(TreeMap.ord(keyOrd)));
}
@Override
public boolean equals(Object other) {
return Equal.equals0(TreeMap.class, this, other, () -> Equal.treeMapEqual(Equal.anyEqual(), Equal.anyEqual()));
}
@Override
public int hashCode() {
return Hash.treeMapHash(Hash.<K>anyHash(), Hash.<V>anyHash()).hash(this);
}
@Override
public String toString() {
return Show.treeMapShow(Show.<K>anyShow(), Show.<V>anyShow()).showS(this);
}
/**
* Constructs a tree map from the given elements.
*
* @param keyOrd An order for the keys of the tree map.
* @param p2s The elements to construct the tree map with.
* @return a TreeMap with the given elements.
*/
@SafeVarargs public static <K, V> TreeMap<K, V> treeMap(final Ord<K> keyOrd, final P2<K, V>... p2s) {
return arrayTreeMap(keyOrd, p2s);
}
/**
* Constructs a tree map from the given elements.
*
* @deprecated As of release 4.5, use {@link #iterableTreeMap(Ord, Iterable)}
*
* @param keyOrd An order for the keys of the tree map.
* @param list The elements to construct the tree map with.
* @return a TreeMap with the given elements.
*/
@Deprecated
public static <K, V> TreeMap<K, V> treeMap(final Ord<K> keyOrd, final List<P2<K, V>> list) {
return iterableTreeMap(keyOrd, list);
}
/**
* Constructs a tree map from the given elements.
*
* @param keyOrd An order for the keys of the tree map.
* @param it The elements to construct the tree map with.
* @return A TreeMap with the given elements.
*/
public static <K, V> TreeMap<K, V> iterableTreeMap(final Ord<K> keyOrd, final Iterable<P2<K, V>> it) {
TreeMap<K, V> tm = empty(keyOrd);
for (final P2<K, V> p2 : it) {
tm = tm.set(p2._1(), p2._2());
}
return tm;
}
/**
* Constructs a tree map from the given elements.
*
* @param keyOrd An order for the keys of the tree map.
* @param it The elements to construct the tree map with.
* @return A TreeMap with the given elements.
*/
public static <K, V> TreeMap<K, V> iteratorTreeMap(final Ord<K> keyOrd, final Iterator<P2<K, V>> it) {
return iterableTreeMap(keyOrd, () -> it);
}
/**
* Constructs a tree map from the given elements.
*
* @param keyOrd An order for the keys of the tree map.
* @param ps The elements to construct the tree map with.
* @return A TreeMap with the given elements.
*/
@SafeVarargs
public static <K, V> TreeMap<K, V> arrayTreeMap(final Ord<K> keyOrd, final P2<K, V>...ps) {
return iterableTreeMap(keyOrd, Array.array(ps));
}
/**
* Returns a potential value that the given key maps to.
*
* @param k The key to look up in the tree map.
* @return A potential value for the given key.
*/
public Option<V> get(final K k) {
return tree.lookup(p(k, Option.none())).bind(P2::_2);
}
/**
* Inserts the given key and value association into the tree map.
* If the given key is already mapped to a value, the old value is replaced with the given one.
*
* @param k The key to insert.
* @param v The value to insert.
* @return A new tree map with the given value mapped to the given key.
*/
public TreeMap<K, V> set(final K k, final V v) {
return new TreeMap<>(tree.insert(p(k, Option.some(v))));
}
/**
* Deletes the entry in the tree map that corresponds to the given key.
*
* @param k The key to delete from this tree map.
* @return A new tree map with the entry corresponding to the given key removed.
*/
public TreeMap<K, V> delete(final K k) {
return new TreeMap<>(tree.delete(p(k, Option.none())));
}
/**
* Returns the number of entries in this tree map.
*
* @return The number of entries in this tree map.
*/
public int size() {
return tree.size();
}
/**
* Determines if this tree map has any entries.
*
* @return <code>true</code> if this tree map has no entries, <code>false</code> otherwise.
*/
public boolean isEmpty() {
return tree.isEmpty();
}
/**
* Returns all values in this tree map.
*
* @return All values in this tree map.
*/
public List<V> values() {
return iterableList(join(tree.toList().map(compose(IterableW.wrap(), P2.__2()))));
}
/**
* Returns all keys in this tree map.
*
* @return All keys in this tree map.
*/
public List<K> keys() {
return tree.toList().map(P2.__1());
}
/**
* Determines if the given key value exists in this tree map.
*
* @param k The key value to look for in this tree map.
* @return <code>true</code> if this tree map contains the given key, <code>false</code> otherwise.
*/
public boolean contains(final K k) {
return tree.member(p(k, Option.none()));
}
/**
* Returns an iterator for this map's key-value pairs.
* This method exists to permit the use in a <code>for</code>-each loop.
*
* @return A iterator for this map's key-value pairs.
*/
public Iterator<P2<K, V>> iterator() {
return join(tree.toStream().map(P2.map2_(IterableW.wrap())
).map(P2.tuple(compose(IterableW.map(), P.p2())))).iterator();
}
/**
* A mutable map projection of this tree map.
*
* @return A new mutable map isomorphic to this tree map.
*/
public Map<K, V> toMutableMap() {
final F<K, P2<K, Option<V>>> fakePair = k -> p(k, Option.none());
final Comparator<K> comparator = tree.ord().contramap(fakePair).toComparator();
final Map<K, V> m = new java.util.TreeMap<>(comparator);
for (final P2<K, V> e : this) {
m.put(e._1(), e._2());
}
return m;
}
public Stream<P2<K, V>> toStream() {
return tree.toStream().map(p -> p.map2(o -> o.some()));
}
public Stream<P2<K, V>> toStreamReverse() {
return tree.toStreamReverse().map(p -> p.map2(o -> o.some()));
}
public List<P2<K, V>> toList() {
return tree.toList().map(p -> p.map2(o -> o.some()));
}
public List<P2<K, V>> toListReverse() {
return tree.toListReverse().map(p -> p.map2(o -> o.some()));
}
/**
* An immutable projection of the given mutable map.
*
* @param ord An order for the map's keys.
* @param m A mutable map to project to an immutable one.
* @return A new immutable tree map isomorphic to the given mutable map.
*/
public static <K, V> TreeMap<K, V> fromMutableMap(final Ord<K> ord, final Map<K, V> m) {
TreeMap<K, V> t = empty(ord);
for (final Map.Entry<K, V> e : m.entrySet()) {
t = t.set(e.getKey(), e.getValue());
}
return t;
}
/**
* Returns a first-class version of the get method for this TreeMap.
*
* @return a functional representation of this TreeMap.
*/
public F<K, Option<V>> get() {
return this::get;
}
/**
* Modifies the value for the given key, if present, by applying the given function to it.
*
* @param k The key for the value to modify.
* @param f A function with which to modify the value.
* @return A new tree map with the value for the given key transformed by the given function,
* paired with True if the map was modified, otherwise False.
*/
public P2<Boolean, TreeMap<K, V>> update(final K k, final F<V, V> f) {
final P2<Boolean, Set<P2<K, Option<V>>>> up =
tree.update(p(k, Option.none()), compose(P2.tuple(P.p2()), P2.map2_(Option.<V, V>map().f(f))));
return p(up._1(), new TreeMap<>(up._2()));
}
/**
* Modifies the value for the given key, if present, by applying the given function to it, or
* inserts the given value if the key is not present.
*
* @param k The key for the value to modify.
* @param f A function with which to modify the value.
* @param v A value to associate with the given key if the key is not already present.
* @return A new tree map with the value for the given key transformed by the given function.
*/
public TreeMap<K, V> update(final K k, final F<V, V> f, final V v) {
final P2<Boolean, TreeMap<K, V>> up = update(k, f);
return up._1() ? up._2() : set(k, v);
}
/**
* Splits this TreeMap at the given key. Returns a triple of:
* <ul>
* <li>A set containing all the values of this map associated with keys less than the given key.</li>
* <li>An option of a value mapped to the given key, if it exists in this map, otherwise None.
* <li>A set containing all the values of this map associated with keys greater than the given key.</li>
* </ul>
*
* @param k A key at which to split this map.
* @return Two sets and an optional value, where all elements in the first set are mapped to keys less than the given
* key in this map, all the elements in the second set are mapped to keys greater than the given key,
* and the optional value is the value associated with the given key if present, otherwise None.
*/
public P3<Set<V>, Option<V>, Set<V>> split(Ord<V> ord, final K k) {
final F<Set<P2<K, Option<V>>>, Set<V>> getSome = F1Functions.mapSet(F1Functions.o(Option.fromSome(), P2.__2()), ord);
return tree.split(p(k, Option.none())).map1(getSome).map3(getSome)
.map2(F1Functions.o(Option.join(), F1Functions.mapOption(P2.__2())));
}
/**
* Internal construction of a TreeMap from the given set.
* @param ord An order for the keys of the tree map.
* @param s The elements to construct the tree map with.
* @return a TreeMap with the given elements.
*/
private static <K, V> TreeMap<K, V> treeMap(Ord<K> ord, Set<P2<K, Option<V>>> s) {
TreeMap<K, V> empty = TreeMap.empty(ord);
TreeMap<K, V> tree = s.toList().foldLeft((tm, p2) -> {
Option<V> opt = p2._2();
if (opt.isSome()) {
return tm.set(p2._1(), opt.some());
}
return tm;
}, empty);
return tree;
}
/**
* Splits this TreeMap at the given key. Returns a triple of:
* <ul>
* <li>A tree map containing all the values of this map associated with keys less than the given key.</li>
* <li>An option of a value mapped to the given key, if it exists in this map, otherwise None.
* <li>A tree map containing all the values of this map associated with keys greater than the given key.</li>
* </ul>
*
* @param k A key at which to split this map.
* @return Two tree maps and an optional value, where all keys in the first tree map are mapped
* to keys less than the given key in this map, all the keys in the second tree map are mapped
* to keys greater than the given key, and the optional value is the value associated with the
* given key if present, otherwise None.
*/
public P3<TreeMap<K, V>, Option<V>, TreeMap<K, V>> splitLookup(final K k) {
P3<Set<P2<K, Option<V>>>, Option<P2<K, Option<V>>>, Set<P2<K, Option<V>>>> p3 = tree.split(p(k, get(k)));
Ord<K> o = tree.ord().contramap(k2 -> p(k2, Option.none()));
return p(treeMap(o, p3._1()), get(k), treeMap(o, p3._3()));
}
/**
* Maps the given function across the values of this TreeMap.
*
* @param f A function to apply to the values of this TreeMap.
* @return A new TreeMap with the values transformed by the given function.
*/
@SuppressWarnings("unchecked")
public <W> TreeMap<K, W> map(final F<V, W> f) {
final F<P2<K, Option<V>>, P2<K, Option<W>>> g = compose(p2 -> p(p2._1(), p2._2()), P2.map2_(F1Functions.mapOption(f)));
final F<K, P2<K, Option<V>>> coord = flip(P.<K, Option<V>>p2()).f(Option.none());
final Ord<K> o = tree.ord().contramap(coord);
return new TreeMap<>(tree.map(TreeMap.ord(o), g));
}
/**
* Returns the minimum (key, value) pair in the tree if the tree is not empty.
*/
public Option<P2<K, V>> min() {
return tree.min().map(p -> p(p._1(), p._2().some()));
}
/**
* Returns the minimum key in the tree if the tree is not empty.
*/
public Option<K> minKey() {
return tree.min().map(P2::_1);
}
/**
* Returns the maximum (key, value) pair in the tree if the tree is not empty.
*/
public Option<P2<K, V>> max() {
return tree.max().map(p -> p(p._1(), p._2().some()));
}
/**
* Returns the maximum key in the tree if the tree is not empty.
*/
public Option<K> maxKey() {
return tree.max().map(P2::_1);
}
/**
* The expression <code>t1.union(t2)</code> takes the left-biased union of <code>t1</code>
* and <code>t2</code>. It prefers <code>t1</code> when duplicate keys are encountered.
*
* @param t2 The other tree we wish to combine with this one
* @return The combined TreeMap
*/
public TreeMap<K, V> union(TreeMap<K, V> t2) {
// TODO This could be implemented more efficiently using "hedge union"
TreeMap<K, V> result = t2;
for(P2<K,V> p : this) {
result = result.set(p._1(), p._2());
}
return result;
}
/**
* The expression <code>t1.union(t2)</code> takes the left-biased union of <code>t1</code>
* and <code>t2</code>. It prefers <code>t1</code> when duplicate keys are encountered.
*
* @param t2 The other list/set of pairs we wish to combine with this one
* @return The combined TreeMap
*/
public TreeMap<K, V> union(Iterable<P2<K, V>> t2) {
TreeMap<K, V> result = this;
for(P2<K,V> p : t2) {
if(!this.contains(p._1())) {
result = result.set(p._1(), p._2());
}
}
return result;
}
}