type ReduceFunc = (aggregator: R, item: T) => R interface List { insert(at: number, item: T): List get?(at: number): T size(): number reduce(fn: ReduceFunc, aggregator: R): R } interface Item { compare(b: Item): number } function divide, R>( lower: number, upper: number, elements: List, item: T, onNew: (item: T, elements: List, lower: number) => R, onExists: (item: T, elements: List) => R, ): R { const step = (upper - lower); if (step < 1) { return onNew(item, elements, lower) } const half = step / 2 | 0; const idx = lower + half; const elm = elements.get(idx); const cmp = elm.compare(item); if (cmp < 0) { return divide(half ? (lower + half) : upper, upper, elements, item, onNew, onExists); } if (cmp > 0) { return divide(lower, half ? (upper - half) : lower, elements, item, onNew, onExists); } return onExists(elm, elements) } class Tuple { constructor(public result: A, public value: B) {} } export class SortedSetArray> { elements: List constructor(elements: List) { this.elements = elements; } size(): number { return this.elements.size(); } add(value: T): Tuple,T> { return divide( 0, this.elements.size(), this.elements, value, (value, elements, lower) => new Tuple(new SortedSetArray(elements.insert(lower, value)), value), (value, elements) => new Tuple(this, value) ); } has(value: T): boolean { return divide( 0, this.elements.size(), this.elements, value, () => false, () => true ); } union(b: SortedSetArray): SortedSetArray { return b.reduce((result: SortedSetArray, item: T): SortedSetArray => { return result.add(item).result; }, this); } reduce(fn: ReduceFunc, accumulator: R): R { return this.elements.reduce(fn, accumulator); } }