/**
 * Partition an array into two, following a predicate.
 * @param input     - The Array to split.
 * @param predicate - The predicate by which to split.
 * @returns A tuple of two arrays, the first one containing all elements from `input` that match the predicate, the second one containing those that do not.
 */
export function partition<T>(input: Array<T>, predicate: (v: T) => boolean): [T[], T[]] {
    return input.reduce(
        (p: [Array<T>, Array<T>], cur: T) => {
            if (predicate(cur)) {
                p[0].push(cur);
            } else {
                p[1].push(cur);
            }
            return p;
        },
        [[], []],
    );
}

/**
 * Zips two Arrays to an array of pairs of elements with corresponding indices. Excessive elements are dropped.
 * @param a1 - First array to zip.
 * @param a2 - Second array to zip.
 *
 * @typeParam T - Type of elements contained in `a1`.
 * @typeParam U - Type of elements contained in `a2`.
 *
 * @returns The array of pairs that had the same index in their source array.
 */
export function zip<T, U>(a1: Array<T>, a2: Array<U>): Array<[T, U]> {
    if (a1.length <= a2.length) {
        return a1.map((e1, i) => [e1, a2[i]]);
    } else {
        return a2.map((e2, i) => [a1[i], e2]);
    }
}