import { PRNG } from "../rng/index.js"; import { Ok, result } from "./result.js"; export const zip = (xs: readonly A[], ys: readonly B[]): [A, B][] => { // based on Belt.Array.zip const lenX = xs.length; const lenY = ys.length; const len = lenX < lenY ? lenX : lenY; const s = new Array(len); for (let i = 0; i < len; i++) { s[i] = [xs[i], ys[i]]; } return s; }; // This is like map, but // accumulate([1,2,3], (a,b) => a + b) => [1, 3, 6] export const accumulate = ( items: readonly A[], fn: (x: A, y: A) => A ): A[] => { const len = items.length; if (len === 0) return []; const result = new Array(len); result[0] = items[0]; // handle the first element separately for (let i = 1; i < len; i++) { result[i] = fn(items[i], result[i - 1]); } return result; }; export const accumulateWithError = ( items: readonly A[], fn: (x: A, y: A) => result ): result => { const len = items.length; if (len === 0) return Ok([]); const results: A[] = new Array(len); results[0] = items[0]; // First element is directly assigned for (let i = 1; i < len; i++) { const r: result = fn(items[i], results[i - 1]); if (!r.ok) return r; // Early return on error results[i] = r.value; } return Ok(results); }; export const unzip = ( items: readonly (readonly [A, B])[] ): [A[], B[]] => { // based on Belt.Array.unzip const len = items.length; const a1 = new Array(len); const a2 = new Array(len); for (let i = 0; i < len; i++) { const [v1, v2] = items[i]; a1[i] = v1; a2[i] = v2; } return [a1, a2]; }; export const pairwise = ( items: readonly T[], fn: (v1: T, v2: T) => R ): R[] => { const result: R[] = []; for (let i = 1; i < items.length; i++) { result.push(fn(items[i - 1], items[i])); } return result; }; export const pairwiseWithError = ( items: readonly T[], fn: (v1: T, v2: T) => result ): result => { const result: R[] = []; for (let i = 1; i < items.length; i++) { const r: result = fn(items[i - 1], items[i]); if (!r.ok) return r; // Immediately return on error result.push(r.value); } return Ok(result); }; export const makeBy = (n: number, fn: (i: number) => T): T[] => { const result: T[] = []; for (let i = 0; i < n; i++) { result.push(fn(i)); } return result; }; export function shuffle(array: readonly T[], rng: PRNG): T[] { const shuffledArray = [...array]; for (let i = shuffledArray.length - 1; i > 0; i--) { const j = Math.floor(rng() * (i + 1)); [shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]]; } return shuffledArray; } export function isEqual(arr1: readonly T[], arr2: readonly T[]): boolean { // If lengths of the arrays are different, they are not equal if (arr1.length !== arr2.length) { return false; } for (let i = 0; i < arr1.length; i++) { if (arr1[i] !== arr2[i]) { return false; } } return true; } export function sample(array: readonly T[], rng: PRNG): T { const index = Math.floor(rng() * array.length); return array[index]; } export function sampleN(array: readonly T[], n: number, rng: PRNG): T[] { const size = Math.max(0, Math.floor(n)); if (size === 0) return []; if (size >= array.length) return shuffle(array, rng); const result: T[] = []; const indices = new Set(); while (indices.size < size) { const index = Math.floor(rng() * array.length); if (!indices.has(index)) { indices.add(index); result.push(array[index]); } } return result; }