HashMap.java
package church.util;

import java.util.function.Function;

import church.lang.ByteStream;

import static church.lang.Array.array;
import static church.lang.Collections.$Empty_set;
import static church.lang.Hash.$HashCode;
import static church.lang.operators.Relational.$$equal;
import static church.lang.operators.Streams.$$encode;
import static church.util.CollectionsSupport.*;
import static church.util.HashTable.hashTable4;

@SuppressWarnings("unchecked")
public class HashMap<K, V> {
    public final HashTable<Entry<K, V>, K> hashTable;

    public HashMap(HashTable<Entry<K, V>, K> hashTable) {
        this.hashTable = hashTable;
    }

    public static <K, V> HashMap<K, V> hashMap1(HashTable<Entry<K, V>, K> hashTable) {
        return new HashMap<>(hashTable);
    }

    public static interface $Map<T, U> {
        public HashMap<T, U> map(Entry<T, U>... elements);
    }

    public static <T, U> $Map<T, U> map($$equal<T> $L0, $HashCode<T> $L1) {
        return elements -> hashMap1(hashTable4(entry -> entry.key, $L0::$equal, $L1::hashCode, elements));
    }

    public static <T, U> $Empty_set<HashMap<T, U>> empty_set($Map<T, U> $L0) {
        return () -> $L0.map(array());
    }

    public static <T, U> int size(HashMap<T, U> map) {
        return map.hashTable.size();
    }

    public static <T, U> U get2(HashMap<T, U> map, T key, Function<T, U> defaultFactory) {
        return get0(map.hashTable, key, defaultFactory);
    }

    public static <T, U> U get(HashMap<T, U> map, T key) {
        return getOrFail(map.hashTable, key);
    }

    public static <T, U> boolean contains(HashMap<T, U> map, T key) {
        return map.hashTable.contains(key);
    }

    public static <T, U> LeftFold<HashMap<T, U>, Entry<T, U>> leftFold() {
        return leftLift(HashTable.leftFold(), m -> m.hashTable);
    }

    public static <T, U> RightFold<HashMap<T, U>, Entry<T, U>> rightFold() {
        return rightLift(HashTable.rightFold(), m -> m.hashTable);
    }

    public static interface $OutEntry<T, U, V> {
        public T outEntry(T stream, Entry<U, V> $0);
    }

    public static <T, U, V> $OutEntry<T, U, V> outEntry($$encode<T, V> $L0, $$encode<T, String> $L1, $$encode<T, U> $L2) {
        return (stream, $0) -> $L0.$encode($L1.$encode($L2.$encode(stream, $0.key), " -> "), $0.value);
    }

    public static <T, U> $$encode<ByteStream, HashMap<T, U>> $encode($OutEntry<ByteStream, T, U> $L0) {
        return (stream, hashMap) -> encode3(stream, leftFold(), hashMap, $L0::outEntry);
    }

}