UNPKG

@effect-ts/system

Version:

Effect-TS is a zero dependency set of libraries to write highly productive, purely functional TypeScript at scale.

2,212 lines (1,791 loc) 82.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Node = exports.ListBuilder = exports.List = void 0; exports.adjust = adjust; exports.adjust_ = adjust_; exports.ap = ap; exports.ap_ = ap_; exports.append = append; exports.append_ = append_; exports.backwards = backwards; exports.builder = builder; exports.chain = chain; exports.chain_ = chain_; exports.compact = compact; exports.concat = concat; exports.concat_ = concat_; exports.contains = contains; exports.contains_ = contains_; exports.drop = drop; exports.dropLast = dropLast; exports.dropLast_ = dropLast_; exports.dropRepeats = dropRepeats; exports.dropRepeatsWith = dropRepeatsWith; exports.dropRepeatsWith_ = dropRepeatsWith_; exports.dropWhile = dropWhile; exports.dropWhile_ = dropWhile_; exports.drop_ = drop_; exports.empty = empty; exports.emptyPushable = emptyPushable; exports.equals = equals; exports.equalsWith = equalsWith; exports.equalsWith_ = equalsWith_; exports.equals_ = equals_; exports.every = every; exports.every_ = every_; exports.filter = filter; exports.filterMap = filterMap; exports.filterMap_ = filterMap_; exports.filterNot = filterNot; exports.filterNot_ = filterNot_; exports.filter_ = filter_; exports.find = find; exports.findIndex = findIndex; exports.findIndex_ = findIndex_; exports.findLast = findLast; exports.findLast_ = findLast_; exports.find_ = find_; exports.first = first; exports.flatten = flatten; exports.forEach = forEach; exports.forEach_ = forEach_; exports.from = from; exports.group = group; exports.groupWith = groupWith; exports.groupWith_ = groupWith_; exports.indexOf = indexOf; exports.indexOf_ = indexOf_; exports.insert = insert; exports.insertAll = insertAll; exports.insertAll_ = insertAll_; exports.insert_ = insert_; exports.intersperse = intersperse; exports.intersperse_ = intersperse_; exports.isEmpty = isEmpty; exports.isList = isList; exports.join = join; exports.join_ = join_; exports.last = last; exports.lastIndexOf = lastIndexOf; exports.lastIndexOf_ = lastIndexOf_; exports.list = list; exports.map = map; exports.map_ = map_; exports.none = none; exports.none_ = none_; exports.nth = nth; exports.nth_ = nth_; exports.of = of; exports.pair = pair; exports.pair_ = pair_; exports.partition = partition; exports.partitionMap = partitionMap; exports.partitionMap_ = partitionMap_; exports.partition_ = partition_; exports.pluck = pluck; exports.pluck_ = pluck_; exports.pop = pop; exports.prepend = prepend; exports.prepend_ = prepend_; exports.push_ = push_; exports.range = range; exports.range_ = range_; exports.reduce = reduce; exports.reduceRight = reduceRight; exports.reduceRight_ = reduceRight_; exports.reduceWhile = reduceWhile; exports.reduceWhile_ = reduceWhile_; exports.reduce_ = reduce_; exports.remove = remove; exports.remove_ = remove_; exports.repeat = repeat; exports.repeat_ = repeat_; exports.reverse = reverse; exports.scan = scan; exports.scan_ = scan_; exports.separate = separate; exports.size = size; exports.slice = slice; exports.slice_ = slice_; exports.some = some; exports.some_ = some_; exports.sortWith = sortWith; exports.sortWith_ = sortWith_; exports.splitAt = splitAt; exports.splitAt_ = splitAt_; exports.splitEvery = splitEvery; exports.splitEvery_ = splitEvery_; exports.splitWhen = splitWhen; exports.splitWhen_ = splitWhen_; exports.tail = tail; exports.take = take; exports.takeLast = takeLast; exports.takeLastWhile = takeLastWhile; exports.takeLastWhile_ = takeLastWhile_; exports.takeLast_ = takeLast_; exports.takeWhile = takeWhile; exports.takeWhile_ = takeWhile_; exports.take_ = take_; exports.times = times; exports.times_ = times_; exports.toArray = toArray; exports.unsafeFind = unsafeFind; exports.unsafeFindLast = unsafeFindLast; exports.unsafeFindLast_ = unsafeFindLast_; exports.unsafeFind_ = unsafeFind_; exports.unsafeFirst = unsafeFirst; exports.unsafeLast = unsafeLast; exports.unsafeNth = unsafeNth; exports.unsafeNth_ = unsafeNth_; exports.update = update; exports.update_ = update_; exports.zip = zip; exports.zipWith = zipWith; exports.zipWith_ = zipWith_; exports.zip_ = zip_; var _index = /*#__PURE__*/require("../../../Function/index.js"); var O = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../../Option/index.js")); var St = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../../../Structural/index.js")); var Tp = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Tuple/index.js")); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /** * Forked from https://github.com/funkia/list/blob/master/src/index.ts * * All credits to original authors. * * The implementation has been forked to adapt to the double standard pipeable/data first * available in the remaining modules and to remove the fantasy-land bias. */ const branchingFactor = 32; const branchBits = 5; const mask = 31; function elementEquals(a, b) { if (a === b) { return true; } else { return false; } } function createPath(depth, value) { let current = value; for (let i = 0; i < depth; ++i) { current = new Node(undefined, [current]); } return current; } // Array helper functions function copyArray(source) { const array = []; for (let i = 0; i < source.length; ++i) { array[i] = source[i]; } return array; } function pushElements(source, target, offset, amount) { for (let i = offset; i < offset + amount; ++i) { target.push(source[i]); } } function copyIndices(source, sourceStart, target, targetStart, length) { for (let i = 0; i < length; ++i) { target[targetStart + i] = source[sourceStart + i]; } } function arrayPrepend(value, array) { const newLength = array.length + 1; const result = new Array(newLength); result[0] = value; for (let i = 1; i < newLength; ++i) { result[i] = array[i - 1]; } return result; } /** * Create a reverse _copy_ of an array. */ function reverseArray(array) { return array.slice().reverse(); } function arrayFirst(array) { return array[0]; } function arrayLast(array) { return array[array.length - 1]; } const pathResult = { path: 0, index: 0, updatedOffset: 0 }; function getPath(index, offset, depth, sizes) { if (sizes === undefined && offset !== 0) { pathResult.updatedOffset = 0; index = handleOffset(depth, offset, index); } let path = index >> depth * branchBits & mask; if (sizes !== undefined) { while (sizes[path] <= index) { path++; } const traversed = path === 0 ? 0 : sizes[path - 1]; index -= traversed; pathResult.updatedOffset = offset; } pathResult.path = path; pathResult.index = index; return pathResult; } function updateNode(node, depth, index, offset, value) { const { index: newIndex, path, updatedOffset } = getPath(index, offset, depth, node.sizes); const array = copyArray(node.array); array[path] = depth > 0 ? updateNode(array[path], depth - 1, newIndex, updatedOffset, value) : value; return new Node(node.sizes, array); } class Node { constructor(sizes, array) { this.sizes = sizes; this.array = array; } } exports.Node = Node; function cloneNode({ array, sizes }) { return new Node(sizes === undefined ? undefined : copyArray(sizes), copyArray(array)); } // This array should not be mutated. Thus a dummy element is placed in // it. Thus the affix will not be owned and thus not mutated. const emptyAffix = [0]; // We store a bit field in list. From right to left, the first five // bits are suffix length, the next five are prefix length and the // rest is depth. The functions below are for working with the bits in // a sane way. const affixBits = 6; const affixMask = 0b111111; function getSuffixSize(l) { return l.bits & affixMask; } function getPrefixSize(l) { return l.bits >> affixBits & affixMask; } function getDepth(l) { return l.bits >> affixBits * 2; } function setPrefix(size, bits) { return size << affixBits | bits & ~(affixMask << affixBits); } function setSuffix(size, bits) { return size | bits & ~affixMask; } function setDepth(depth, bits) { return depth << affixBits * 2 | bits & (affixMask | affixMask << affixBits); } function incrementPrefix(bits) { return bits + (1 << affixBits); } function incrementSuffix(bits) { return bits + 1; } function incrementDepth(bits) { return bits + (1 << affixBits * 2); } function decrementDepth(bits) { return bits - (1 << affixBits * 2); } /* * Invariants that any list `l` should satisfy * * 1. If `l.root !== undefined` then `getSuffixSize(l) !== 0` and * `getPrefixSize(l) !== 0`. The invariant ensures that `first` and * `last` never have to look in the root and that they therefore * take O(1) time. * 2. If a tree or sub-tree does not have a size-table then all leaf * nodes in the tree are of size 32. */ /** * Represents a list of elements. */ class List { constructor(bits, offset, length, prefix, root, suffix) { this.bits = bits; this.offset = offset; this.length = length; this.prefix = prefix; this.root = root; this.suffix = suffix; } [Symbol.iterator]() { return new ForwardListIterator(this); } toJSON() { return toArray(this); } [St.equalsSym](that) { return that instanceof List && equalsWith_(this, that, St.equals); } get [St.hashSym]() { return St.hashIterator(this[Symbol.iterator]()); } } exports.List = List; function cloneList(l) { return new List(l.bits, l.offset, l.length, l.prefix, l.root, l.suffix); } class ListIterator { constructor(l, direction) { this.l = l; this.result = { done: false, value: undefined }; this.idx = direction === 1 ? -1 : l.length; this.prefixSize = getPrefixSize(l); this.middleSize = l.length - getSuffixSize(l); if (l.root !== undefined) { const depth = getDepth(l); this.stack = new Array(depth + 1); this.indices = new Array(depth + 1); let currentNode = l.root.array; for (let i = depth; 0 <= i; --i) { this.stack[i] = currentNode; const idx = direction === 1 ? 0 : currentNode.length - 1; this.indices[i] = idx; currentNode = currentNode[idx].array; } this.indices[0] -= direction; } } } class ForwardListIterator extends ListIterator { constructor(l) { super(l, 1); } nextInTree() { for (var i = 0; ++this.indices[i] === this.stack[i].length; ++i) { this.indices[i] = 0; } for (; 0 < i; --i) { this.stack[i - 1] = this.stack[i][this.indices[i]].array; } } next() { let newVal; const idx = ++this.idx; if (idx < this.prefixSize) { newVal = this.l.prefix[this.prefixSize - idx - 1]; } else if (idx < this.middleSize) { this.nextInTree(); newVal = this.stack[0][this.indices[0]]; } else if (idx < this.l.length) { newVal = this.l.suffix[idx - this.middleSize]; } else { this.result.done = true; } this.result.value = newVal; return this.result; } } class BackwardsListIterator extends ListIterator { constructor(l) { super(l, -1); } prevInTree() { for (var i = 0; this.indices[i] === 0; ++i) {// } --this.indices[i]; for (; 0 < i; --i) { const n = this.stack[i][this.indices[i]].array; this.stack[i - 1] = n; this.indices[i - 1] = n.length - 1; } } next() { let newVal; const idx = --this.idx; if (this.middleSize <= idx) { newVal = this.l.suffix[idx - this.middleSize]; } else if (this.prefixSize <= idx) { this.prevInTree(); newVal = this.stack[0][this.indices[0]]; } else if (0 <= idx) { newVal = this.l.prefix[this.prefixSize - idx - 1]; } else { this.result.done = true; } this.result.value = newVal; return this.result; } } /** * Returns an iterable that iterates backwards over the given list. * * @complexity O(1) */ function backwards(l) { return { [Symbol.iterator]() { return new BackwardsListIterator(l); } }; } function emptyPushable() { return new List(0, 0, 0, [], undefined, []); } /** Appends the value to the list by _mutating_ the list and its content. */ function push_(l, value) { const suffixSize = getSuffixSize(l); if (l.length === 0) { l.bits = setPrefix(1, l.bits); l.prefix = [value]; } else if (suffixSize < 32) { l.bits = incrementSuffix(l.bits); l.suffix.push(value); } else if (l.root === undefined) { l.root = new Node(undefined, l.suffix); l.suffix = [value]; l.bits = setSuffix(1, l.bits); } else { const newNode = new Node(undefined, l.suffix); const index = l.length - 1 - 32 + 1; let current = l.root; let depth = getDepth(l); l.suffix = [value]; l.bits = setSuffix(1, l.bits); if (index - 1 < branchingFactor ** (depth + 1)) { for (; depth >= 0; --depth) { const path = index >> depth * branchBits & mask; if (path < current.array.length) { current = current.array[path]; } else { current.array.push(createPath(depth - 1, newNode)); break; } } } else { l.bits = incrementDepth(l.bits); l.root = new Node(undefined, [l.root, createPath(depth, newNode)]); } } l.length++; return l; } /** * Creates a list of the given elements. * * @complexity O(n) */ function list(...elements) { const l = emptyPushable(); for (const element of elements) { push_(l, element); } return l; } /** * Creates an empty list. * * @complexity O(1) */ function empty() { return new List(0, 0, 0, emptyAffix, undefined, emptyAffix); } /** * Takes a single arguments and returns a singleton list that contains it. * * @complexity O(1) */ function of(a) { return list(a); } /** * Takes two arguments and returns a list that contains them. * * @complexity O(1) */ function pair(second) { return first => pair_(first, second); } /** * Takes two arguments and returns a list that contains them. * * @complexity O(1) */ function pair_(first, second) { return new List(2, 0, 2, emptyAffix, undefined, [first, second]); } function from(sequence) { const l = emptyPushable(); if (sequence.length > 0 && (sequence[0] !== undefined || 0 in sequence)) { for (let i = 0; i < sequence.length; ++i) { push_(l, sequence[i]); } } else if (Symbol.iterator in sequence) { const iterator = sequence[Symbol.iterator](); let cur; // tslint:disable-next-line:no-conditional-assignment while (!(cur = iterator.next()).done) { push_(l, cur.value); } } return l; } /** * Returns a list of numbers between an inclusive lower bound and an exclusive upper bound. * * @complexity O(n) */ function range(end) { return start => range_(start, end); } /** * Returns a list of numbers between an inclusive lower bound and an exclusive upper bound. * * @complexity O(n) */ function range_(start, end) { const list = emptyPushable(); for (let i = start; i < end; ++i) { push_(list, i); } return list; } /** * Returns a list of a given length that contains the specified value * in all positions. * * @complexity O(n) */ function repeat(times) { return value => repeat_(value, times); } /** * Returns a list of a given length that contains the specified value * in all positions. * * @complexity O(n) */ function repeat_(value, times) { const l = emptyPushable(); while (--times >= 0) { push_(l, value); } return l; } /** * Generates a new list by calling a function with the current index * `n` times. * * @complexity O(n) */ function times(times) { return func => times_(func, times); } /** * Generates a new list by calling a function with the current index * `n` times. * * @complexity O(n) */ function times_(func, times) { const l = emptyPushable(); for (let i = 0; i < times; i++) { push_(l, func(i)); } return l; } function nodeNthDense(node, depth, index) { let current = node; for (; depth >= 0; --depth) { current = current.array[index >> depth * branchBits & mask]; } return current; } function handleOffset(depth, offset, index) { index += offset; for (; depth >= 0; --depth) { index = index - (offset & mask << depth * branchBits); if ((index >> depth * branchBits & mask) !== 0) { break; } } return index; } function nodeNth(node, depth, offset, index) { let path; let current = node; while (current.sizes !== undefined) { path = index >> depth * branchBits & mask; while (current.sizes[path] <= index) { path++; } if (path !== 0) { index -= current.sizes[path - 1]; offset = 0; // Offset is discarded if the left spine isn't traversed } depth--; current = current.array[path]; } return nodeNthDense(current, depth, offset === 0 ? index : handleOffset(depth, offset, index)); } /** * Gets the nth element of the list. If `n` is out of bounds * `undefined` is returned. * * @complexity O(log(n)) */ function unsafeNth_(l, index) { return O.toUndefined(nth_(l, index)); } /** * Gets the nth element of the list. If `n` is out of bounds * `undefined` is returned. * * @complexity O(log(n)) */ function unsafeNth(index) { return l => unsafeNth_(l, index); } /** * Gets the nth element of the list. If `n` is out of bounds * `undefined` is returned. * * @complexity O(log(n)) */ function nth_(l, index) { if (index < 0 || l.length <= index) { return O.none; } const prefixSize = getPrefixSize(l); const suffixSize = getSuffixSize(l); if (index < prefixSize) { return O.some(l.prefix[prefixSize - index - 1]); } else if (index >= l.length - suffixSize) { return O.some(l.suffix[index - (l.length - suffixSize)]); } const { offset } = l; const depth = getDepth(l); return O.some(l.root.sizes === undefined ? nodeNthDense(l.root, depth, offset === 0 ? index - prefixSize : handleOffset(depth, offset, index - prefixSize)) : nodeNth(l.root, depth, offset, index - prefixSize)); } /** * Gets the nth element of the list. If `n` is out of bounds * `undefined` is returned. * * @complexity O(log(n)) */ function nth(index) { return l => nth_(l, index); } function setSizes(node, height) { let sum = 0; const sizeTable = []; for (let i = 0; i < node.array.length; ++i) { sum += sizeOfSubtree(node.array[i], height - 1); sizeTable[i] = sum; } node.sizes = sizeTable; return node; } /** * Returns the number of elements stored in the node. */ function sizeOfSubtree(node, height) { if (height !== 0) { if (node.sizes !== undefined) { return arrayLast(node.sizes); } else { // the node is leftwise dense so all all but the last child are full const lastSize = sizeOfSubtree(arrayLast(node.array), height - 1); return (node.array.length - 1 << height * branchBits) + lastSize; } } else { return node.array.length; } } // prepend & append function affixPush(a, array, length) { if (array.length === length) { array.push(a); return array; } else { const newArray = []; copyIndices(array, 0, newArray, 0, length); newArray.push(a); return newArray; } } /** * Prepends an element to the front of a list and returns the new list. * * @complexity O(1) */ function prepend_(l, value) { const prefixSize = getPrefixSize(l); if (prefixSize < 32) { return new List(incrementPrefix(l.bits), l.offset, l.length + 1, affixPush(value, l.prefix, prefixSize), l.root, l.suffix); } else { const newList = cloneList(l); prependNodeToTree(newList, reverseArray(l.prefix)); const newPrefix = [value]; newList.prefix = newPrefix; newList.length++; newList.bits = setPrefix(1, newList.bits); return newList; } } /** * Prepends an element to the front of a list and returns the new list. * * @complexity O(1) */ function prepend(value) { return l => prepend_(l, value); } /** * Traverses down the left edge of the tree and copies k nodes. * Returns the last copied node. * @param l * @param k The number of nodes to copy. Should always be at least 1. */ function copyLeft(l, k) { let currentNode = cloneNode(l.root); // copy root l.root = currentNode; // install copy of root for (let i = 1; i < k; ++i) { const index = 0; // go left if (currentNode.sizes !== undefined) { for (let i = 0; i < currentNode.sizes.length; ++i) { currentNode.sizes[i] += 32; } } const newNode = cloneNode(currentNode.array[index]); // Install the copied node currentNode.array[index] = newNode; currentNode = newNode; } return currentNode; } /** * Prepends an element to a node */ function nodePrepend(value, size, node) { const array = arrayPrepend(value, node.array); let sizes = undefined; if (node.sizes !== undefined) { sizes = new Array(node.sizes.length + 1); sizes[0] = size; for (let i = 0; i < node.sizes.length; ++i) { sizes[i + 1] = node.sizes[i] + size; } } return new Node(sizes, array); } /** * Prepends a node to a tree. Either by shifting the nodes in the root * left or by increasing the height */ function prependTopTree(l, depth, node) { let newOffset; if (l.root.array.length < branchingFactor) { // There is space in the root, there is never a size table in this // case newOffset = 32 ** depth - 32; l.root = new Node(undefined, arrayPrepend(createPath(depth - 1, node), l.root.array)); } else { // We need to create a new root l.bits = incrementDepth(l.bits); const sizes = l.root.sizes === undefined ? undefined : [32, arrayLast(l.root.sizes) + 32]; newOffset = depth === 0 ? 0 : 32 ** (depth + 1) - 32; l.root = new Node(sizes, [createPath(depth, node), l.root]); } return newOffset; } /** * Takes a list and a node tail. It then prepends the node to the tree * of the list. * @param l The subject for prepending. `l` will be mutated. Nodes in * the tree will _not_ be mutated. * @param node The node that should be prepended to the tree. */ function prependNodeToTree(l, array) { if (l.root === undefined) { if (getSuffixSize(l) === 0) { // ensure invariant 1 l.bits = setSuffix(array.length, l.bits); l.suffix = array; } else { l.root = new Node(undefined, array); } return l; } else { const node = new Node(undefined, array); const depth = getDepth(l); let newOffset = 0; if (l.root.sizes === undefined) { if (l.offset !== 0) { newOffset = l.offset - branchingFactor; l.root = prependDense(l.root, depth, l.offset, node); } else { // in this case we can be sure that the is not room in the tree // for the new node newOffset = prependTopTree(l, depth, node); } } else { // represents how many nodes _with size-tables_ that we should copy. let copyableCount = 0; // go down while there is size tables let nodesTraversed = 0; let currentNode = l.root; while (currentNode.sizes !== undefined && nodesTraversed < depth) { ++nodesTraversed; if (currentNode.array.length < 32) { // there is room if offset is > 0 or if the first node does not // contain as many nodes as it possibly can copyableCount = nodesTraversed; } currentNode = currentNode.array[0]; } if (l.offset !== 0) { const copiedNode = copyLeft(l, nodesTraversed); for (let i = 0; i < copiedNode.sizes.length; ++i) { copiedNode.sizes[i] += branchingFactor; } copiedNode.array[0] = prependDense(copiedNode.array[0], depth - nodesTraversed, l.offset, node); l.offset = l.offset - branchingFactor; return l; } else { if (copyableCount === 0) { l.offset = prependTopTree(l, depth, node); } else { let parent; let prependableNode; // Copy the part of the path with size tables if (copyableCount > 1) { parent = copyLeft(l, copyableCount - 1); prependableNode = parent.array[0]; } else { parent = undefined; prependableNode = l.root; } const path = createPath(depth - copyableCount, node); // add offset l.offset = 32 ** (depth - copyableCount + 1) - 32; const prepended = nodePrepend(path, 32, prependableNode); if (parent === undefined) { l.root = prepended; } else { parent.array[0] = prepended; } } return l; } } l.offset = newOffset; return l; } } /** * Prepends a node to a dense tree. The given `offset` is never zero. */ function prependDense(node, depth, offset, value) { // We're indexing down `offset - 1`. At each step `path` is either 0 or -1. const curOffset = offset >> depth * branchBits & mask; const path = (offset - 1 >> depth * branchBits & mask) - curOffset; if (path < 0) { return new Node(undefined, arrayPrepend(createPath(depth - 1, value), node.array)); } else { const array = copyArray(node.array); array[0] = prependDense(array[0], depth - 1, offset, value); return new Node(undefined, array); } } /** * Appends an element to the end of a list and returns the new list. * * @complexity O(n) */ function append_(l, value) { const suffixSize = getSuffixSize(l); if (suffixSize < 32) { return new List(incrementSuffix(l.bits), l.offset, l.length + 1, l.prefix, l.root, affixPush(value, l.suffix, suffixSize)); } const newSuffix = [value]; const newList = cloneList(l); appendNodeToTree(newList, l.suffix); newList.suffix = newSuffix; newList.length++; newList.bits = setSuffix(1, newList.bits); return newList; } /** * Appends an element to the end of a list and returns the new list. * * @complexity O(n) */ function append(value) { return l => append_(l, value); } /** * Gets the length of a list. * * @complexity `O(1)` */ function size(l) { return l.length; } /** * Returns the first element of the list. If the list is empty the * function returns undefined. * * @complexity O(1) */ function unsafeFirst(l) { return O.toUndefined(first(l)); } /** * Returns the first element of the list. If the list is empty the * function returns undefined. * * @complexity O(1) */ function first(l) { const prefixSize = getPrefixSize(l); return prefixSize !== 0 ? O.some(l.prefix[prefixSize - 1]) : l.length !== 0 ? O.some(l.suffix[0]) : O.none; } /** * Returns the last element of the list. If the list is empty the * function returns `undefined`. * * @complexity O(1) */ function unsafeLast(l) { return O.toUndefined(last(l)); } /** * Returns the last element of the list. If the list is empty the * function returns `undefined`. * * @complexity O(1) */ function last(l) { const suffixSize = getSuffixSize(l); return suffixSize !== 0 ? O.some(l.suffix[suffixSize - 1]) : l.length !== 0 ? O.some(l.prefix[0]) : O.none; } // map function mapArray(f, array) { const result = new Array(array.length); for (let i = 0; i < array.length; ++i) { result[i] = f(array[i]); } return result; } function mapNode(f, node, depth) { if (depth !== 0) { const { array } = node; const result = new Array(array.length); for (let i = 0; i < array.length; ++i) { result[i] = mapNode(f, array[i], depth - 1); } return new Node(node.sizes, result); } else { return new Node(undefined, mapArray(f, node.array)); } } function mapPrefix(f, prefix, length) { const newPrefix = new Array(length); for (let i = length - 1; 0 <= i; --i) { newPrefix[i] = f(prefix[i]); } return newPrefix; } function mapAffix(f, suffix, length) { const newSuffix = new Array(length); for (let i = 0; i < length; ++i) { newSuffix[i] = f(suffix[i]); } return newSuffix; } /** * Applies a function to each element in the given list and returns a * new list of the values that the function return. * * @complexity O(n) */ function map_(l, f) { return new List(l.bits, l.offset, l.length, mapPrefix(f, l.prefix, getPrefixSize(l)), l.root === undefined ? undefined : mapNode(f, l.root, getDepth(l)), mapAffix(f, l.suffix, getSuffixSize(l))); } /** * Applies a function to each element in the given list and returns a * new list of the values that the function return. * * @complexity O(n) */ function map(f) { return l => new List(l.bits, l.offset, l.length, mapPrefix(f, l.prefix, getPrefixSize(l)), l.root === undefined ? undefined : mapNode(f, l.root, getDepth(l)), mapAffix(f, l.suffix, getSuffixSize(l))); } /** * Extracts the specified property from each object in the list. */ function pluck_(l, key) { return map_(l, a => a[key]); } /** * Extracts the specified property from each object in the list. */ function pluck(key) { return l => pluck_(l, key); } // fold function foldlSuffix(f, acc, array, length) { for (let i = 0; i < length; ++i) { acc = f(acc, array[i]); } return acc; } function foldlPrefix(f, acc, array, length) { for (let i = length - 1; 0 <= i; --i) { acc = f(acc, array[i]); } return acc; } function foldlNode(f, acc, node, depth) { const { array } = node; if (depth === 0) { return foldlSuffix(f, acc, array, array.length); } for (let i = 0; i < array.length; ++i) { acc = foldlNode(f, acc, array[i], depth - 1); } return acc; } /** * Folds a function over a list. Left-associative. */ function reduce_(l, initial, f) { const suffixSize = getSuffixSize(l); const prefixSize = getPrefixSize(l); initial = foldlPrefix(f, initial, l.prefix, prefixSize); if (l.root !== undefined) { initial = foldlNode(f, initial, l.root, getDepth(l)); } return foldlSuffix(f, initial, l.suffix, suffixSize); } /** * Folds a function over a list. Left-associative. */ function reduce(initial, f) { return l => reduce_(l, initial, f); } /** * Folds a function over a list from left to right while collecting * all the intermediate steps in a resulting list. */ function scan_(l, initial, f) { return reduce_(l, push_(emptyPushable(), initial), (l2, a) => push_(l2, f(unsafeLast(l2), a))); } /** * Folds a function over a list from left to right while collecting * all the intermediate steps in a resulting list. */ function scan(initial, f) { return l => scan_(l, initial, f); } /** * Invokes a given callback for each element in the list from left to * right. Returns `undefined`. * * This function is very similar to map. It should be used instead of * `map` when the mapping function has side-effects. Whereas `map` * constructs a new list `forEach` merely returns `undefined`. This * makes `forEach` faster when the new list is unneeded. * * @complexity O(n) */ function forEach_(l, callback) { reduce_(l, undefined, (_, element) => callback(element)); } /** * Invokes a given callback for each element in the list from left to * right. Returns `undefined`. * * This function is very similar to map. It should be used instead of * `map` when the mapping function has side-effects. Whereas `map` * constructs a new list `forEach` merely returns `undefined`. This * makes `forEach` faster when the new list is unneeded. * * @complexity O(n) */ function forEach(callback) { return l => forEach_(l, callback); } function filter_(l, predicate) { return reduce_(l, emptyPushable(), (acc, a) => predicate(a) ? push_(acc, a) : acc); } function filter(predicate) { return l => reduce_(l, emptyPushable(), (acc, a) => predicate(a) ? push_(acc, a) : acc); } /** * Returns a new list that only contains the elements of the original * list for which the f returns `Some`. * * @complexity O(n) */ function filterMap_(l, f) { return reduce_(l, emptyPushable(), (acc, a) => { const fa = f(a); if (fa._tag === "Some") { push_(acc, fa.value); } return acc; }); } /** * Returns a new list that only contains the elements of the original * list for which the f returns `Some`. * * @complexity O(n) */ function filterMap(f) { return l => filterMap_(l, f); } /** * Filter out optional values */ function compact(fa) { return filterMap(x => x)(fa); } /** * Returns a new list that only contains the elements of the original * list for which the predicate returns `false`. * * @complexity O(n) */ function filterNot_(l, predicate) { return reduce_(l, emptyPushable(), (acc, a) => predicate(a) ? acc : push_(acc, a)); } /** * Returns a new list that only contains the elements of the original * list for which the predicate returns `false`. * * @complexity O(n) */ function filterNot(predicate) { return l => filterNot_(l, predicate); } function partition_(l, predicate) { return reduce_(l, Tp.tuple(emptyPushable(), emptyPushable()), (arr, a) => (predicate(a) ? push_(arr.get(0), a) : push_(arr.get(1), a), arr)); } function partition(predicate) { return l => partition_(l, predicate); } /** * Splits the list into two lists. One list that contains the lefts * and one contains the rights * * @complexity O(n) */ function partitionMap_(l, f) { return reduce_(l, Tp.tuple(emptyPushable(), emptyPushable()), (arr, a) => { const fa = f(a); if (fa._tag === "Left") { push_(arr.get(0), fa.left); } else { push_(arr.get(1), fa.right); } return arr; }); } /** * Splits the list into two lists. One list that contains the lefts * and one contains the rights * * @complexity O(n) */ function partitionMap(f) { return l => partitionMap_(l, f); } /** * Splits the list into two lists. One list that contains the lefts * and one contains the rights * * @complexity O(n) */ function separate(l) { return partitionMap_(l, _index.identity); } /** * Concats the strings in the list separated by a specified separator. */ function join_(l, separator) { return reduce_(l, "", (a, b) => a.length === 0 ? b : a + separator + b); } /** * Concats the strings in the list separated by a specified separator. */ function join(separator) { return l => join_(l, separator); } function foldrSuffix(f, initial, array, length) { let acc = initial; for (let i = length - 1; 0 <= i; --i) { acc = f(array[i], acc); } return acc; } function foldrPrefix(f, initial, array, length) { let acc = initial; for (let i = 0; i < length; ++i) { acc = f(array[i], acc); } return acc; } function foldrNode(f, initial, { array }, depth) { if (depth === 0) { return foldrSuffix(f, initial, array, array.length); } let acc = initial; for (let i = array.length - 1; 0 <= i; --i) { acc = foldrNode(f, acc, array[i], depth - 1); } return acc; } /** * Folds a function over a list. Right-associative. * * @complexity O(n) */ function reduceRight_(l, initial, f) { const suffixSize = getSuffixSize(l); const prefixSize = getPrefixSize(l); let acc = foldrSuffix(f, initial, l.suffix, suffixSize); if (l.root !== undefined) { acc = foldrNode(f, acc, l.root, getDepth(l)); } return foldrPrefix(f, acc, l.prefix, prefixSize); } /** * Folds a function over a list. Right-associative. * * @complexity O(n) */ function reduceRight(initial, f) { return l => reduceRight_(l, initial, f); } /** * Applies a list of functions to a list of values. */ function ap_(listF, l) { return flatten(map_(listF, f => map_(l, f))); } /** * Applies a list of functions to a list of values. */ function ap(l) { return listF => ap_(listF, l); } /** * Flattens a list of lists into a list. Note that this function does * not flatten recursively. It removes one level of nesting only. * * @complexity O(n * log(m)), where n is the length of the outer list and m the length of the inner lists. */ function flatten(nested) { return reduce_(nested, empty(), concat_); } /** * Maps a function over a list and concatenates all the resulting * lists together. */ function chain_(l, f) { return flatten(map_(l, f)); } /** * Maps a function over a list and concatenates all the resulting * lists together. */ function chain(f) { return l => chain_(l, f); } function foldlArrayCb(cb, state, array, from, to) { for (var i = from; i < to && cb(array[i], state); ++i) {// } return i === to; } function foldrArrayCb(cb, state, array, from, to) { for (var i = from - 1; to <= i && cb(array[i], state); --i) {// } return i === to - 1; } function foldlNodeCb(cb, state, node, depth) { const { array } = node; if (depth === 0) { return foldlArrayCb(cb, state, array, 0, array.length); } const to = array.length; for (let i = 0; i < to; ++i) { if (!foldlNodeCb(cb, state, array[i], depth - 1)) { return false; } } return true; } /** * This function is a lot like a fold. But the reducer function is * supposed to mutate its state instead of returning it. Instead of * returning a new state it returns a boolean that tells wether or not * to continue the fold. `true` indicates that the folding should * continue. */ function foldlCb(cb, state, l) { const prefixSize = getPrefixSize(l); if (!foldrArrayCb(cb, state, l.prefix, prefixSize, 0) || l.root !== undefined && !foldlNodeCb(cb, state, l.root, getDepth(l))) { return state; } const suffixSize = getSuffixSize(l); foldlArrayCb(cb, state, l.suffix, 0, suffixSize); return state; } function foldrNodeCb(cb, state, node, depth) { const { array } = node; if (depth === 0) { return foldrArrayCb(cb, state, array, array.length, 0); } for (let i = array.length - 1; 0 <= i; --i) { if (!foldrNodeCb(cb, state, array[i], depth - 1)) { return false; } } return true; } function foldrCb(cb, state, l) { const suffixSize = getSuffixSize(l); const prefixSize = getPrefixSize(l); if (!foldrArrayCb(cb, state, l.suffix, suffixSize, 0) || l.root !== undefined && !foldrNodeCb(cb, state, l.root, getDepth(l))) { return state; } const prefix = l.prefix; foldlArrayCb(cb, state, l.prefix, prefix.length - prefixSize, prefix.length); return state; } /** * Similar to `foldl`. But, for each element it calls the predicate function * _before_ the folding function and stops folding if it returns `false`. * * @category Folds * @example * const isOdd = (_acc:, x) => x % 2 === 1; * * const xs = L.list(1, 3, 5, 60, 777, 800); * foldlWhile(isOdd, (n, m) => n + m, 0, xs) //=> 9 * * const ys = L.list(2, 4, 6); * foldlWhile(isOdd, (n, m) => n + m, 111, ys) //=> 111 */ function foldlWhileCb(a, state) { if (state.predicate(state.result, a) === false) { return false; } state.result = state.f(state.result, a); return true; } function reduceWhile_(l, initial, predicate, f) { return foldlCb(foldlWhileCb, { predicate, f, result: initial }, l).result; } function reduceWhile(initial, predicate, f) { return l => reduceWhile_(l, initial, predicate, f); } function everyCb(value, state) { return state.result = state.predicate(value); } /** * Returns `true` if and only if the predicate function returns `true` * for all elements in the given list. * * @complexity O(n) */ function every_(l, predicate) { return foldlCb(everyCb, { predicate, result: true }, l).result; } /** * Returns `true` if and only if the predicate function returns `true` * for all elements in the given list. * * @complexity O(n) */ function every(predicate) { return l => every_(l, predicate); } function someCb(value, state) { return !(state.result = state.predicate(value)); } /** * Returns true if and only if there exists an element in the list for * which the predicate returns true. * * @complexity O(n) */ function some_(l, predicate) { return foldlCb(someCb, { predicate, result: false }, l).result; } /** * Returns true if and only if there exists an element in the list for * which the predicate returns true. * * @complexity O(n) */ function some(predicate) { return l => some_(l, predicate); } /** * Returns `true` if and only if the predicate function returns * `false` for every element in the given list. * * @complexity O(n) */ function none_(l, predicate) { return !some_(l, predicate); } /** * Returns `true` if and only if the predicate function returns * `false` for every element in the given list. * * @complexity O(n) */ function none(predicate) { return l => none_(l, predicate); } function findCb(value, state) { if (state.predicate(value)) { state.result = O.some(value); return false; } else { return true; } } /** * Returns the _first_ element for which the predicate returns `true`. * If no such element is found the function returns `undefined`. * * @complexity O(n) */ function unsafeFind_(l, predicate) { return O.toUndefined(find_(l, predicate)); } /** * Returns the _first_ element for which the predicate returns `true`. * If no such element is found the function returns `undefined`. * * @complexity O(n) */ function unsafeFind(predicate) { return l => unsafeFind_(l, predicate); } /** * Returns the _first_ element for which the predicate returns `true`. * If no such element is found the function returns `undefined`. * * @complexity O(n) */ function find_(l, predicate) { return foldlCb(findCb, { predicate, result: O.none }, l).result; } /** * Returns the _first_ element for which the predicate returns `true`. * If no such element is found the function returns `undefined`. * * @complexity O(n) */ function find(predicate) { return l => find_(l, predicate); } /** * Returns the _last_ element for which the predicate returns `true`. * If no such element is found the function returns `undefined`. * * @complexity O(n) */ function unsafeFindLast_(l, predicate) { return O.toUndefined(findLast_(l, predicate)); } /** * Returns the _last_ element for which the predicate returns `true`. * If no such element is found the function returns `undefined`. * * @complexity O(n) */ function unsafeFindLast(predicate) { return l => unsafeFindLast_(l, predicate); } /** * Returns the _last_ element for which the predicate returns `true`. * If no such element is found the function returns `undefined`. * * @complexity O(n) */ function findLast_(l, predicate) { return foldrCb(findCb, { predicate, result: O.none }, l).result; } /** * Returns the _last_ element for which the predicate returns `true`. * If no such element is found the function returns `undefined`. * * @complexity O(n) */ function findLast(predicate) { return l => findLast_(l, predicate); } function indexOfCb(value, state) { ++state.index; return !(state.found = elementEquals(value, state.element)); } /** * Returns the index of the _first_ element in the list that is equal * to the given element. If no such element is found `-1` is returned. * * @complexity O(n) */ function indexOf_(l, element) { const state = { element, found: false, index: -1 }; foldlCb(indexOfCb, state, l); return state.found ? state.index : -1; } /** * Returns the index of the _first_ element in the list that is equal * to the given element. If no such element is found `-1` is returned. * * @complexity O(n) */ function indexOf(element) { return l => indexOf_(l, element); } /** * Returns the index of the _last_ element in the list that is equal * to the given element. If no such element is found `-1` is returned. * * @complexity O(n) */ function lastIndexOf_(l, element) { const state = { element, found: false, index: 0 }; foldrCb(indexOfCb, state, l); return state.found ? l.length - state.index : -1; } /** * Returns the index of the _last_ element in the list that is equal * to the given element. If no such element is found `-1` is returned. * * @complexity O(n) */ function lastIndexOf(element) { return l => lastIndexOf_(l, element); } function findIndexCb(value, state) { ++state.index; return !(state.found = state.predicate(value)); } /** * Returns the index of the `first` element for which the predicate * returns true. If no such element is found the function returns * `-1`. * * @complexity O(n) */ function findIndex_(l, predicate) { const { found, index } = foldlCb(findIndexCb, { predicate, found: false, index: -1 }, l); return found ? index : -1; } /** * Returns the index of the `first` element for which the predicate * returns true. If no such element is found the function returns * `-1`. * * @complexity O(n) */ function findIndex(predicate) { return l => findIndex_(l, predicate); } const containsState = { element: undefined, result: false }; function containsCb(value, state) { return !(state.result = value === state.element); } /** * Returns `true` if the list contains the specified element. * Otherwise it returns `false`. * * @complexity O(n) */ function contains_(l, element) { containsState.element = element; containsState.result = false; return foldlCb(containsCb, containsState, l).result; } /** * Returns `true` if the list contains the specified element. * Otherwise it returns `false`. * * @complexity O(n) */ function contains(element) { return l => contains_(l, element); } function equalsCb(value2, state) { const { value } = state.iterator.next(); return state.equals = state.f(value, value2); } /** * Returns true if the two lists are equivalent. * * @complexity O(n) */ function equals_(l1, l2) { return equalsWith_(l1, l2, elementEquals); } /** * Returns true if the two lists are equivalent. * * @complexity O(n) */ function equals(l2) { return l1 => equals_(l1, l2); } /** * Returns true if the two lists are equivalent when comparing each * pair of elements with the given comparison function. * * @complexity O(n) */ function equalsWith_(l1, l2, f) { if (l1 === l2) { return true; } else if (l1.length !== l2.length) { return false; } else { const s = { iterator: l2[Symbol.iterator](), equals: true, f }; return foldlCb(equalsCb, s, l1).equals; } } /** * Returns true if the two lists are equivalent when comparing each * pair of elements with the given comparison function. * * @complexity O(n) */ function equalsWith(l2, f) { return l1 => equalsWith_(l1, l2, f); } // concat const eMax = 2; function createConcatPlan(array) { const sizes = []; let sum = 0; for (let i = 0; i < array.length; ++i) { sum += array[i].array.length; // FIXME: maybe only access array once sizes[i] = array[i].array.length; } const optimalLength = Math.ceil(sum / branchingFactor); let n = array.length; let i = 0; if (optimalLength + eMax >= n) { return undefined; // no rebalancing needed } while (optimalLength + eMax < n) { while (sizes[i] > branchingFactor - eMax / 2) { // Skip nodes that are already sufficiently balanced ++i; } // the node at this index is too short let remaining = sizes[i]; // number of elements to re-distribute do { const size = Math.min(remaining + sizes[i + 1], branchingFactor); sizes[i] = size; remaining = remaining - (size - sizes[i + 1]); ++i; } while (remaining > 0); // Shift nodes after for (let j = i; j <= n - 1; ++j) { sizes[j] = sizes[j + 1]; } --i; --n; } sizes.length = n; return sizes; } /** * Combines the children of three nodes into an array. The last child * of `left` and the first child of `right is ignored as they've been * concatenated into `center`. */ function concatNodeMerge(left, center, right) { const array = []; if (left !== undefined) { for (let i = 0; i < left.array.length - 1; ++i) { array.push(left.array[i]); } } for (let i = 0; i < center.array.length; ++i) { array.push(center.array[i]); } if (right !== undefined) { for (let i = 1; i < right.array.length; ++i) { array.push(right.array[i]); } } return array; } function executeConcatPlan(merged, plan, height) { const result = []; let sourceIdx = 0; // the current node we're copying from let offset = 0; // elements in source already used for (let toMove of plan) { let source = merged[sourceIdx].array; if (toMove === source.length && offset === 0) { // source matches target exactly, reuse source result.push(merged[sourceIdx]); ++sourceIdx; } else { const node = new Node(undefined, []); while (toMo