package com.googlecode.totallylazy.collections;
import com.googlecode.totallylazy.functions.Function1;
import com.googlecode.totallylazy.functions.Function2;
import com.googlecode.totallylazy.functions.Callables;
import com.googlecode.totallylazy.Option;
import com.googlecode.totallylazy.Pair;
import com.googlecode.totallylazy.predicates.Predicate;
import com.googlecode.totallylazy.predicates.Predicates;
import com.googlecode.totallylazy.Sequences;
import com.googlecode.totallylazy.annotations.multimethod;
import java.util.Iterator;
import java.util.NoSuchElementException;
import static com.googlecode.totallylazy.Sequences.sequence;
public class HashTreeMap<K, V> extends AbstractMap<K, V> {
private final PersistentSortedMap<Integer, PersistentMap<K, V>> hash;
private final PersistentMap<K, V> emptyBucket = ListMap.<K, V>emptyListMap();
private HashTreeMap(PersistentSortedMap<Integer, PersistentMap<K, V>> hash) {
this.hash = hash;
}
public static <K, V> HashTreeMapFactory<K, V> factory() {
return HashTreeMapFactory.factory();
}
public static <K, V> HashTreeMap<K, V> hashTreeMap(PersistentSortedMap<Integer, PersistentMap<K, V>> map) {
return new HashTreeMap<K, V>(map);
}
public static <K, V> HashTreeMap<K, V> hashTreeMap() {
return hashTreeMap(PersistentSortedMap.constructors.<Integer, PersistentMap<K, V>>sortedMap());
}
public static <K, V> HashTreeMap<K, V> hashTreeMap(Iterable<? extends Pair<K, V>> values) {
return HashTreeMap.<K,V>factory().map(values);
}
@Override
public PersistentMap<K, V> empty() {
return hashTreeMap();
}
@Override
public boolean isEmpty() {
return hash.isEmpty();
}
@Override
public Pair<K, V> head() throws NoSuchElementException {
return iterator().next();
}
@Override
public Option<Pair<K, V>> headOption() {
if(isEmpty()) return Option.none();
return Option.some(head());
}
@Override
public PersistentMap<K, V> cons(Pair<K, V> head) {
return insert(head.first(), head.second());
}
@Override
public PersistentMap<K, V> tail() throws NoSuchElementException {
return delete(head().first());
}
@Override
public Option<V> lookup(final K key) {
return hash.lookup(key.hashCode()).flatMap(PersistentMap.functions.<K, V>get(key));
}
@Override
public PersistentMap<K, V> insert(K key, V value) {
int hashCode = key.hashCode();
return hashTreeMap(hash.insert(hashCode, hash.lookup(hashCode).getOrElse(emptyBucket).insert(key, value)));
}
@Override
public PersistentMap<K, V> delete(K key) {
int hashCode = key.hashCode();
PersistentMap<K, V> bucket = hash.lookup(hashCode).getOrElse(emptyBucket).delete(key);
if(bucket.isEmpty()) return hashTreeMap(hash.delete(hashCode));
return hashTreeMap(hash.insert(hashCode, bucket));
}
@Override
public <S> S fold(S seed, Function2<? super S, ? super Pair<K, V>, ? extends S> callable) {
return toSequence().fold(seed, callable);
}
@Override
public Iterator<Pair<K, V>> iterator() {
return hash.values().flatMap(Sequences.<Pair<K, V>>identity()).iterator();
}
@Override
public boolean contains(final Object other) {
return hash.lookup(other.hashCode()).map(PersistentMap.functions.<K, V>contains(other)).getOrElse(false);
}
@Override
public boolean exists(Predicate<? super K> predicate) {
return toSequence().exists(Predicates.<K>first(predicate));
}
@Override
public int size() {
return toSequence().size();
}
@Override
public int hashCode() {
return toSequence().hashCode();
}
@multimethod
public boolean equals(HashTreeMap<K, V> obj) {
return toSequence().equals(obj.toSequence());
}
@Override
public String toString() {
return toSequence().toString("");
}
}