Array.java
package church.lang;

import static church.lang.ArraySupport.*;
import static church.lang.Hash.$HashCode;
import static church.lang.JavaClasses.$Java_class;
import static church.lang.operators.Relational.$$equal;
import static church.lang.operators.Streams.$$encode;

@SuppressWarnings("unchecked")
public class Array {
    public static <T> T[] array(T... elements) {
        return array0(elements);
    }

    public static interface $CreateArray<T> {
        public T[] createArray(int length, java.util.function.IntFunction<T> generator);
    }

    public static <T> $CreateArray<T> createArray($Java_class<T> $L0) {
        return (length, generator) -> createArray0($L0.java_class(), length, generator);
    }

    public static <T> int length(T[] a) {
        return length0(a);
    }

    public static <T> int size(T[] a) {
        return length0(a);
    }

    public static <T> T get(T[] a, int i) {
        return get0(a, i);
    }

    public static <T> $$equal<T[]> $equal($$equal<T> $L0) {
        return (a, b) -> equal0($L0::$equal, a, b);
    }

    public static <T> $HashCode<T[]> hashCode($HashCode<T> $L0) {
        return a -> hashCode0($L0::hashCode, a);
    }

    public static interface $WriteElementsToStream<T, U> {
        public T writeElementsToStream(T stream0, U[] a);
    }

    public static <T, U> $WriteElementsToStream<T, U> writeElementsToStream($$encode<T, U> $L0, $$encode<T, String> $L1) {
        return (stream0, a) -> {
            int N       = length(a);
            T   stream1 = N == 0 ? stream0 : $L0.$encode(stream0, a[0]);
            return new Interval(1, N).reduce(stream1, s -> i -> $L0.$encode($L1.$encode(s, ", "), a[i]));
        };
    }

    public static interface $WriteEmbracedElementsToStream<T, U, V, W> {
        public T writeEmbracedElementsToStream(T stream0, U[] a, V openString, W closeString);
    }

    public static <T, U, V, W> $WriteEmbracedElementsToStream<T, U, V, W> writeEmbracedElementsToStream($$encode<T, V> $L0, $WriteElementsToStream<T, U> $L1, $$encode<T, W> $L2) {
        return (stream0, a, openString, closeString) -> {
            T stream1 = $L0.$encode(stream0, openString);
            T stream2 = $L1.writeElementsToStream(stream1, a);
            return $L2.$encode(stream2, closeString);
        };
    }

    public static <T, U> $$encode<T, U[]> $encode($WriteEmbracedElementsToStream<T, U, String, String> $L0) {
        return (stream0, a) -> $L0.writeEmbracedElementsToStream(stream0, a, "[", "]");
    }

}