UNPKG

effi-find

Version:

Creates a function to choose an Item from a list in O(1)

193 lines (192 loc) 6.82 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.chooserFactory = void 0; const fluent_iterable_1 = require("@codibre/fluent-iterable"); function getGCD(value1, value2) { if (value1 === undefined) return value2; while (value2 !== 0) { const temp = value2; value2 = value1 % value2; value1 = temp; } if (value1 === 0) { throw new RangeError('There is no GCD'); } return value1; } const empty = Symbol('empty'); const INFINITY_BOUNDS = new Map([ [-1, Symbol('NEGATIVE_INFINITY')], [1, Symbol('POSITIVE_INFINITY')], ]); function prepareValue(value, multiplier) { if (value === undefined) return undefined; return Math.round(value * multiplier); } class NormalizedList { constructor(list, gcd, multiplier, init, end, halfDelimited, lowerBound, upperBound) { this.list = list; this.gcd = gcd; this.multiplier = multiplier; this.init = init; this.end = end; this.halfDelimited = halfDelimited; this.lowerBound = lowerBound; this.upperBound = upperBound; this.multiplierSymbol = Symbol('multiplier'); this.current = 0; this.pivot = empty; this.multiple = 0; this.iterator = this.list[Symbol.iterator](); } [Symbol.iterator]() { return this; } next() { if (this.item === undefined) this.item = this.iterator.next(); if (this.item.done) return { done: true, value: undefined }; const item = this.item.value; if (!item) return { done: false, value: item }; const { value } = item; const init = value[this.init]; const end = item.value[this.end]; if (end === undefined || init === undefined) { const halfDelimited = value[this.halfDelimited]; delete this.item.value[this.init]; delete this.item.value[this.end]; delete this.item.value[this.halfDelimited]; this.item = this.iterator.next(); return { done: false, value: { value, [this.multiplierSymbol]: halfDelimited ? INFINITY_BOUNDS.get(halfDelimited) : empty, }, }; } if (this.pivot === empty || this.pivot !== value) { this.pivot = value; this.current = init; this.multiple = Math.floor((init - this.lowerBound) / this.gcd); } if (end < this.current) { delete this.item.value[this.init]; delete this.item.value[this.end]; this.item = this.iterator.next(); } const result = { done: false, value: { value, [this.multiplierSymbol]: this.multiple, }, }; this.current += this.gcd; this.multiple++; return result; } } function getItem(base, initSymbol, endSymbol, init, multiplier, end) { const item = base; item[initSymbol] = prepareValue(item[init], multiplier); item[endSymbol] = prepareValue(item[end], multiplier); return item; } function partitionRange(original, list, init, end, precision = 2) { const initSymbol = Symbol(init.toString()); const endSymbol = Symbol(end.toString()); const halfDelimited = Symbol('halfDelimited'); const multiplier = 10 ** precision; let upperBound; let gcd; let lowerBound; let lowestBound; let highestBound; for (const item of original) { const current = getItem(item, initSymbol, endSymbol, init, multiplier, end); if (!upperBound) upperBound = current[initSymbol]; lowerBound = upperBound; upperBound = current[initSymbol]; if (upperBound !== undefined && upperBound > lowerBound) { gcd = getGCD(gcd, upperBound - lowerBound); } lowerBound = upperBound ?? Number.NEGATIVE_INFINITY; upperBound = current[endSymbol] ?? Number.POSITIVE_INFINITY; if (lowerBound === Number.NEGATIVE_INFINITY && upperBound === Number.POSITIVE_INFINITY) { continue; } else if (lowerBound === Number.NEGATIVE_INFINITY) { current[halfDelimited] = -1; } else if (upperBound === Number.POSITIVE_INFINITY) { current[halfDelimited] = 1; } else { if (lowestBound === undefined || lowestBound > lowerBound) { lowestBound = lowerBound; } if (highestBound === undefined || highestBound < upperBound) { highestBound = upperBound; } if (upperBound > lowerBound) { gcd = getGCD(gcd, upperBound - lowerBound); } } } if (gcd === undefined) { throw new RangeError('Could not find GCD'); } return new NormalizedList(list, gcd, multiplier, initSymbol, endSymbol, halfDelimited, lowestBound ?? Number.MIN_SAFE_INTEGER, highestBound ?? Number.MAX_SAFE_INTEGER); } function chooserFactory(list, ...keys) { let normalized = (0, fluent_iterable_1.fluent)(list).map((value) => ({ value, })); const getters = []; const prepared = keys.map((key) => { if (Array.isArray(key)) { const iterator = partitionRange(list, normalized, key[0], key[1], key[2]); const result = iterator.multiplierSymbol; const { gcd, multiplier, lowerBound, upperBound } = iterator; normalized = iterator; getters.push((x) => { if (x === undefined) return empty; const inScale = x * multiplier; if (inScale < lowerBound) return INFINITY_BOUNDS.get(-1); if (inScale > upperBound) return INFINITY_BOUNDS.get(1); return Math.floor((inScale - lowerBound) / gcd); }); return result; } getters.push(fluent_iterable_1.identity); return (x) => x.value[key]; }); const tree = (0, fluent_iterable_1.fluent)(normalized).toObjectChainReduce(() => [], (acc, { value }) => { acc.push(value); return acc; }, ...prepared); return ((...args) => { let result = tree; for (let i = 0; i < args.length; i++) { const item = args[i]; const getter = getters[i] ?? fluent_iterable_1.identity; result = result[getter(item)]; if (result === undefined) break; } return result ?? []; }); } exports.chooserFactory = chooserFactory;