import type { Eq } from "@principia/prelude/Eq";
import * as Ord from "@principia/prelude/Ord";
import { toNumber } from "@principia/prelude/Ordering";
import type { Either } from "../Either";
import type { Predicate, Refinement } from "../Function";
import type { NonEmptyArray } from "../NonEmptyArray";
import type { Option } from "../Option";
import { isSome, none, some } from "../Option";
import { empty } from "./constructors";
import { reduce_ } from "./foldable";
import { isEmpty, isNonEmpty, isOutOfBound_ } from "./guards";
import { chain_ } from "./monad";
/*
* -------------------------------------------
* Combinators
* -------------------------------------------
*/
export const append_ = (xs: ReadonlyArray, ys: ReadonlyArray): ReadonlyArray => {
const lenx = xs.length;
if (lenx === 0) {
return ys;
}
const leny = ys.length;
if (leny === 0) {
return xs;
}
const r = Array(lenx + leny);
for (let i = 0; i < lenx; i++) {
r[i] = xs[i];
}
for (let i = 0; i < leny; i++) {
r[i + lenx] = ys[i];
}
return r;
};
export const append = (ys: ReadonlyArray) => (xs: ReadonlyArray): ReadonlyArray => append_(xs, ys);
export const lookup_ = (i: number, as: ReadonlyArray): Option => (isOutOfBound_(i, as) ? none() : some(as[i]));
export const lookup = (i: number) => (as: ReadonlyArray): Option => lookup_(i, as);
export const scanl = (b: B, f: (b: B, a: A) => B) => (as: ReadonlyArray): ReadonlyArray => {
const l = as.length;
const r: Array = new Array(l + 1);
r[0] = b;
for (let i = 0; i < l; i++) {
r[i + 1] = f(r[i], as[i]);
}
return r;
};
export const scanr = (b: B, f: (a: A, b: B) => B) => (as: ReadonlyArray): ReadonlyArray => {
const l = as.length;
const r: Array = new Array(l + 1);
r[l] = b;
for (let i = l - 1; i >= 0; i--) {
r[i] = f(as[i], r[i + 1]);
}
return r;
};
export const cons_ = (head: A, tail: ReadonlyArray): NonEmptyArray => {
const len = tail.length;
const r = Array(len + 1);
for (let i = 0; i < len; i++) {
r[i + 1] = tail[i];
}
r[0] = head;
return (r as unknown) as NonEmptyArray;
};
export const cons = (tail: ReadonlyArray) => (head: A): NonEmptyArray => cons_(head, tail);
export const snoc_ = (init: ReadonlyArray, end: A): NonEmptyArray => {
const len = init.length;
const r = Array(len + 1);
for (let i = 0; i < len; i++) {
r[i] = init[i];
}
r[len] = end;
return (r as unknown) as NonEmptyArray;
};
export const snoc = (end: A) => (init: ReadonlyArray): NonEmptyArray => snoc_(init, end);
export const head = (as: ReadonlyArray): Option => (isEmpty(as) ? none() : some(as[0]));
export const tail = (as: ReadonlyArray): Option> => (isEmpty(as) ? none() : some(as.slice(1)));
export const last = (as: ReadonlyArray): Option => lookup_(as.length - 1, as);
export const init = (as: ReadonlyArray): Option> => {
const len = as.length;
return len === 0 ? none() : some(as.slice(0, len - 1));
};
export const takel = (n: number) => (as: ReadonlyArray): ReadonlyArray => as.slice(0, n);
export const taker = (n: number) => (as: ReadonlyArray): ReadonlyArray =>
isEmpty(as) ? empty() : as.slice(-n);
const spanIndexUncurry = (as: ReadonlyArray, predicate: Predicate): number => {
const l = as.length;
let i = 0;
for (; i < l; i++) {
if (!predicate(as[i])) {
break;
}
}
return i;
};
export const takelWhile: {
(refinement: Refinement): (as: ReadonlyArray) => ReadonlyArray;
(predicate: Predicate): (as: ReadonlyArray) => ReadonlyArray;
} = (predicate: Predicate) => (as: ReadonlyArray) => {
const i = spanIndexUncurry(as, predicate);
const init = Array(i);
for (let j = 0; j < i; j++) {
init[j] = as[j];
}
return init;
};
export interface Spanned {
readonly init: ReadonlyArray;
readonly rest: ReadonlyArray;
}
export const spanl: {
(refinement: Refinement): (as: ReadonlyArray) => Spanned;
(predicate: Predicate): (as: ReadonlyArray) => Spanned;
} = (predicate: Predicate) => (as: ReadonlyArray): Spanned => {
const i = spanIndexUncurry(as, predicate);
const init = Array(i);
for (let j = 0; j < i; j++) {
init[j] = as[j];
}
const l = as.length;
const rest = Array(l - i);
for (let j = i; j < l; j++) {
rest[j - i] = as[j];
}
return { init, rest };
};
export const dropl = (n: number) => (as: ReadonlyArray): ReadonlyArray => as.slice(n, as.length);
export const dropr = (n: number) => (as: ReadonlyArray): ReadonlyArray => as.slice(0, as.length - n);
export const droprWhile = (predicate: Predicate) => (as: ReadonlyArray): ReadonlyArray => {
const i = spanIndexUncurry(as, predicate);
const l = as.length;
const rest = Array(l - i);
for (let j = i; j < l; j++) {
rest[j - i] = as[j];
}
return rest;
};
export const findr: {
(refinement: Refinement): (as: ReadonlyArray) => Option;
(predicate: Predicate): (as: ReadonlyArray) => Option;
} = (predicate: Predicate) => (as: ReadonlyArray): Option => {
const len = as.length;
for (let i = 0; i < len; i++) {
if (predicate(as[i])) {
return some(as[i]);
}
}
return none();
};
export const findrMap = (f: (a: A) => Option) => (as: ReadonlyArray): Option => {
const len = as.length;
for (let i = 0; i < len; i++) {
const v = f(as[i]);
if (isSome(v)) {
return v;
}
}
return none();
};
export const findl: {
(refinement: Refinement): (as: ReadonlyArray) => Option;
(predicate: Predicate): (as: ReadonlyArray) => Option;
} = (predicate: Predicate) => (as: ReadonlyArray): Option => {
const len = as.length;
for (let i = len - 1; i >= 0; i--) {
if (predicate(as[i])) {
return some(as[i]);
}
}
return none();
};
export const findlMap = (f: (a: A) => Option) => (as: ReadonlyArray): Option => {
const len = as.length;
for (let i = len - 1; i >= 0; i--) {
const v = f(as[i]);
if (isSome(v)) {
return v;
}
}
return none();
};
export const findlIndex_ = (as: ReadonlyArray, predicate: Predicate): Option => {
const len = as.length;
for (let i = 0; i < len; i++) {
if (predicate(as[i])) {
return some(i);
}
}
return none();
};
export const findlIndex = (predicate: Predicate) => (as: ReadonlyArray): Option =>
findlIndex_(as, predicate);
export const findrIndex = (predicate: Predicate) => (as: ReadonlyArray): Option => {
const len = as.length;
for (let i = len - 1; i >= 0; i--) {
if (predicate(as[i])) {
return some(i);
}
}
return none();
};
export const unsafeInsertAt = (i: number, a: A, as: ReadonlyArray): ReadonlyArray => {
const xs = Array.from(as);
xs.splice(i, 0, a);
return xs;
};
export const unsafeUpdateAt = (i: number, a: A, as: ReadonlyArray): ReadonlyArray => {
if (as[i] === a) {
return as;
} else {
const xs = Array.from(as);
xs[i] = a;
return xs;
}
};
export const unsafeDeleteAt = (i: number, as: ReadonlyArray): ReadonlyArray => {
const xs = Array.from(as);
xs.splice(i, 1);
return xs;
};
export const insertAt_ = (as: ReadonlyArray, i: number, a: A): Option> =>
isOutOfBound_(i, as) ? none() : some(unsafeInsertAt(i, a, as));
export const insertAt = (i: number, a: A) => (as: ReadonlyArray): Option> => insertAt_(as, i, a);
export const updateAt_ = (as: ReadonlyArray, i: number, a: A): Option> =>
isOutOfBound_(i, as) ? none() : some(unsafeUpdateAt(i, a, as));
export const updateAt = (i: number, a: A) => (as: ReadonlyArray): Option> => updateAt_(as, i, a);
export const deleteAt_ = (as: ReadonlyArray, i: number): Option> =>
isOutOfBound_(i, as) ? none() : some(unsafeDeleteAt(i, as));
export const deleteAt = (i: number) => (as: ReadonlyArray): Option> => deleteAt_(as, i);
/**
* Apply a function to the element at the specified index, creating a new array, or returning `None` if the index is out
* of bounds
*
* @since 1.0.0
*/
export const modifyAt_ = (as: ReadonlyArray, i: number, f: (a: A) => A): Option> =>
isOutOfBound_(i, as) ? none() : some(unsafeUpdateAt(i, f(as[i]), as));
/**
* Apply a function to the element at the specified index, creating a new array, or returning `None` if the index is out
* of bounds
*
* @since 1.0.0
*/
export const modifyAt = (i: number, f: (a: A) => A) => (as: ReadonlyArray): Option> =>
modifyAt_(as, i, f);
export const reverse = (as: ReadonlyArray): ReadonlyArray => (isEmpty(as) ? as : as.slice().reverse());
export const rights = (as: ReadonlyArray>): ReadonlyArray => {
const rs: Array = [];
for (let i = 0; i < as.length; i++) {
const a = as[i];
if (a._tag === "Right") {
rs.push(a.right);
}
}
return rs;
};
export const lefts = (as: ReadonlyArray>): ReadonlyArray => {
const ls: Array = [];
for (let i = 0; i < as.length; i++) {
const a = as[i];
if (a._tag === "Left") {
ls.push(a.left);
}
}
return ls;
};
export const sort = (O: Ord.Ord) => (as: ReadonlyArray): ReadonlyArray =>
isEmpty(as) ? empty() : as.slice().sort((a, b) => toNumber(O.compare(a)(b)));
export const unzip = (as: ReadonlyArray): readonly [ReadonlyArray, ReadonlyArray] => {
const fa: Array = [];
const fb: Array = [];
for (let i = 0; i < as.length; i++) {
fa[i] = as[i][0];
fb[i] = as[i][1];
}
return [fa, fb];
};
export const elem = (E: Eq) => (a: A) => (as: ReadonlyArray): boolean => {
const predicate = (element: A) => E.equals(element)(a);
let i = 0;
const len = as.length;
for (; i < len; i++) {
if (predicate(as[i])) {
return true;
}
}
return false;
};
export const uniq = (E: Eq) => (as: ReadonlyArray): ReadonlyArray => {
const elemS = elem(E);
const out: Array = [];
const len = as.length;
let i = 0;
for (; i < len; i++) {
const a = as[i];
if (!elemS(a)(out)) {
out.push(a);
}
}
return len === out.length ? as : out;
};
export const sortBy = (ords: ReadonlyArray>) => (as: ReadonlyArray): ReadonlyArray => {
const M = Ord.getMonoid();
return sort(reduce_(ords, M.nat, (b, a) => M.combine_(a, b)))(as);
};
export const comprehension: {
(
input: readonly [ReadonlyArray, ReadonlyArray, ReadonlyArray, ReadonlyArray],
f: (a: A, b: B, c: C, d: D) => R,
g?: (a: A, b: B, c: C, d: D) => boolean
): ReadonlyArray;
(
input: readonly [ReadonlyArray, ReadonlyArray, ReadonlyArray],
f: (a: A, b: B, c: C) => R,
g?: (a: A, b: B, c: C) => boolean
): ReadonlyArray;
(
input: readonly [ReadonlyArray, ReadonlyArray],
f: (a: A, b: B) => R,
g?: (a: A, b: B) => boolean
): ReadonlyArray;
(input: readonly [ReadonlyArray], f: (a: A) => boolean, g?: (a: A) => R): ReadonlyArray;
} = (
input: ReadonlyArray>,
f: (...xs: ReadonlyArray) => R,
g: (...xs: ReadonlyArray) => boolean = () => true
): ReadonlyArray => {
const go = (scope: ReadonlyArray, input: ReadonlyArray>): ReadonlyArray => {
if (input.length === 0) {
return g(...scope) ? [f(...scope)] : empty();
} else {
return chain_(input[0], (x) => go(snoc_(scope, x), input.slice(1)));
}
};
return go(empty(), input);
};
export const union = (E: Eq) => (ys: ReadonlyArray) => (xs: ReadonlyArray): ReadonlyArray => {
const elemE = elem(E);
return append_(
xs,
ys.filter((a) => !elemE(a)(xs))
);
};
export const intersection = (E: Eq) => (ys: ReadonlyArray) => (xs: ReadonlyArray): ReadonlyArray => {
const elemE = elem(E);
return xs.filter((a) => elemE(a)(ys));
};
export const chop_ = (
as: ReadonlyArray,
f: (as: NonEmptyArray) => readonly [B, ReadonlyArray]
): ReadonlyArray => {
const result: Array = [];
let cs: ReadonlyArray = as;
while (isNonEmpty(cs)) {
const [b, c] = f(cs);
result.push(b);
cs = c;
}
return result;
};
export const chop = (f: (as: NonEmptyArray) => readonly [B, ReadonlyArray]) => (
as: ReadonlyArray
): ReadonlyArray => chop_(as, f);
export const splitAt = (n: number) => (as: ReadonlyArray): readonly [ReadonlyArray, ReadonlyArray] => [
as.slice(0, n),
as.slice(n)
];
export const chunksOf = (n: number) => (as: ReadonlyArray): ReadonlyArray> =>
as.length === 0 ? empty() : isOutOfBound_(n - 1, as) ? [as] : chop_(as, splitAt(n));
export const difference = (E: Eq) => (ys: ReadonlyArray) => (xs: ReadonlyArray): ReadonlyArray => {
const elemE = elem(E);
return xs.filter((a) => !elemE(a)(ys));
};
export const dropLeft_ = (as: ReadonlyArray, n: number): ReadonlyArray => as.slice(n, as.length);
export const dropLeft = (n: number) => (as: ReadonlyArray): ReadonlyArray => dropLeft_(as, n);
export const dropRight_ = (as: ReadonlyArray, n: number): ReadonlyArray => as.slice(0, as.length - n);
export const dropRight = (n: number) => (as: ReadonlyArray): ReadonlyArray => dropRight_(as, n);
export const dropLeftWhile_ = (as: ReadonlyArray, predicate: Predicate): ReadonlyArray => {
const i = spanIndexUncurry(as, predicate);
const l = as.length;
const rest = Array(l - i);
for (let j = i; j < l; j++) {
rest[j - i] = as[j];
}
return rest;
};
export const dropLeftWhile = (predicate: Predicate) => (as: ReadonlyArray): ReadonlyArray =>
dropLeftWhile_(as, predicate);