UNPKG

@rimbu/sorted

Version:

Immutable SortedMap and SortedSet implementations for TypeScript

1,063 lines (859 loc) 28.2 kB
import { Arr, Entry, RimbuError, Token } from '@rimbu/base'; import { type ArrayNonEmpty, IndexRange, OptLazy, OptLazyOr, Range, type RelatedTo, type ToJSON, TraverseState, Update, } from '@rimbu/common'; import { Stream, type StreamSource } from '@rimbu/stream'; import { isEmptyStreamSourceInstance } from '@rimbu/stream/custom'; import type { SortedMap } from '@rimbu/sorted/map'; import type { SortedMapBuilder, SortedMapContext, } from '@rimbu/sorted/map-custom'; import { SortedEmpty, SortedIndex, SortedNonEmptyBase, innerDeleteMax, innerDeleteMin, innerDropInternal, innerGetAtIndex, innerMutateGetFromLeft, innerMutateGetFromRight, innerMutateGiveToLeft, innerMutateGiveToRight, innerMutateJoinLeft, innerMutateJoinRight, innerMutateSplitRight, innerNormalizeDownsizeChild, innerNormalizeIncreaseChild, innerStreamSliceIndex, innerTakeInternal, leafDeleteMax, leafDeleteMin, leafMutateGetFromLeft, leafMutateGetFromRight, leafMutateGiveToLeft, leafMutateGiveToRight, leafMutateJoinLeft, leafMutateJoinRight, leafMutateSplitRight, } from '@rimbu/sorted/common'; export class SortedMapEmpty<K = any, V = any> extends SortedEmpty implements SortedMap<K, V> { _NonEmptyType!: SortedMap.NonEmpty<K, V>; constructor(readonly context: SortedMapContext<K>) { super(); } streamRange(): Stream<readonly [K, V]> { return Stream.empty(); } streamKeys(): Stream<K> { return Stream.empty(); } streamValues(): Stream<V> { return Stream.empty(); } streamSliceIndex(): Stream<readonly [K, V]> { return Stream.empty(); } minKey<O>(otherwise?: OptLazy<O>): O { return OptLazy(otherwise) as O; } minValue<O>(otherwise?: OptLazy<O>): O { return OptLazy(otherwise) as O; } maxKey<O>(otherwise?: OptLazy<O>): O { return OptLazy(otherwise) as O; } maxValue<O>(otherwise?: OptLazy<O>): O { return OptLazy(otherwise) as O; } get<_, O>(key: any, otherwise?: OptLazy<O>): O { return OptLazy(otherwise) as O; } hasKey(): false { return false; } findIndex(): number { return -1; } getKeyAtIndex<O>(index: number, otherwise?: OptLazy<O>): O { return OptLazy(otherwise) as O; } getValueAtIndex<O>(index: number, otherwise?: OptLazy<O>): O { return OptLazy(otherwise) as O; } set(key: K, value: V): SortedMap.NonEmpty<K, V> { return this.context.leaf([[key, value]]); } addEntry(entry: readonly [K, V]): SortedMap.NonEmpty<K, V> { return this.context.leaf([entry]); } addEntries(entries: StreamSource<readonly [K, V]>): SortedMap.NonEmpty<K, V> { return this.context.from(entries); } removeKey(): SortedMap<K, V> { return this; } removeKeys(): SortedMap<K, V> { return this; } removeKeyAndGet(): undefined { return undefined; } modifyAt( atKey: K, options: { ifNew?: OptLazyOr<V, Token>; } ): SortedMap<K, V> { if (undefined !== options.ifNew) { const value = OptLazyOr<V, Token>(options.ifNew, Token); if (Token === value) return this; return this.context.leaf([[atKey, value]]); } return this; } mapValues<V2>(): SortedMap<K, V2> { return this as any; } updateAt(): SortedMap<K, V> { return this; } slice(): SortedMap<K, V> { return this; } toBuilder(): SortedMapBuilder<K, V> { return this.context.builder(); } toString(): string { return `SortedMap()`; } toJSON(): ToJSON<any[]> { return { dataType: this.context.typeTag, value: [], }; } } export abstract class SortedMapNode<K, V> extends SortedNonEmptyBase<readonly [K, V], SortedMapNode<K, V>> implements SortedMap.NonEmpty<K, V> { _NonEmptyType!: SortedMap.NonEmpty<K, V>; abstract get context(): SortedMapContext<K>; abstract get size(): number; abstract stream(options?: { reversed?: boolean; }): Stream.NonEmpty<readonly [K, V]>; abstract streamSliceIndex( range: IndexRange, options?: { reversed?: boolean } ): Stream<readonly [K, V]>; abstract forEach( f: (entry: readonly [K, V], index: number, halt: () => void) => void, options?: { state?: TraverseState } ): void; abstract get<U, O>(key: RelatedTo<K, U>, otherwise?: OptLazy<O>): V | O; abstract findIndex(key: K): number; abstract addInternal( entry: readonly [K, V], hash?: number ): SortedMapNode<K, V>; abstract modifyAtInternal( atKey: K, options: { ifNew?: OptLazyOr<V, Token>; ifExists?: ((currentEntry: V, remove: Token) => V | Token) | V; } ): SortedMapNode<K, V>; abstract getInsertIndexOf(key: K): number; abstract mapValues<V2>( mapFun: (value: V, key: K) => V2 ): SortedMapNode<K, V2>; abstract toArray(): ArrayNonEmpty<readonly [K, V]>; abstract normalize(): SortedMap<K, V>; abstract min(): readonly [K, V]; abstract max(): readonly [K, V]; asNormal(): this { return this; } getSliceRange(range: Range<K>): { startIndex: number; endIndex: number } { const { start, end } = Range.getNormalizedRange(range); let startIndex = 0; let endIndex = this.size - 1; if (undefined !== start) { const [startValue, startInclude] = start; startIndex = this.getInsertIndexOf(startValue); if (startIndex < 0) { startIndex = SortedIndex.next(startIndex); } else if (!startInclude) { startIndex++; } } if (undefined !== end) { const [endValue, endInclude] = end; endIndex = this.getInsertIndexOf(endValue); if (endIndex < 0) endIndex = SortedIndex.prev(endIndex); else if (!endInclude) endIndex--; } return { startIndex, endIndex }; } streamKeys(options: { reversed?: boolean } = {}): Stream.NonEmpty<K> { return this.stream(options).map(Entry.first); } streamValues(options: { reversed?: boolean } = {}): Stream.NonEmpty<V> { return this.stream(options).map(Entry.second); } streamRange( keyRange: Range<K>, options: { reversed?: boolean } = {} ): Stream<readonly [K, V]> { const { startIndex, endIndex } = this.getSliceRange(keyRange); return this.streamSliceIndex( { start: [startIndex, true], end: [endIndex, true], }, options ); } minKey(): K { return this.min()[0]; } minValue(): V { return this.min()[1]; } maxKey(): K { return this.max()[0]; } maxValue(): V { return this.max()[1]; } hasKey<UK>(key: RelatedTo<K, UK>): boolean { const token = Symbol(); return token !== this.get(key, token); } getKeyAtIndex<O>(index: number, otherwise?: OptLazy<O>): K | O { const token = Symbol(); const result = this.getAtIndex(index, token); if (token === result) return OptLazy(otherwise) as O; return result[0]; } getValueAtIndex<O>(index: number, otherwise?: OptLazy<O>): V | O { const token = Symbol(); const result = this.getAtIndex(index, token); if (token === result) return OptLazy(otherwise) as O; return result[1]; } addEntry(entry: readonly [K, V]): SortedMap.NonEmpty<K, V> { return this.addInternal(entry).normalize().assumeNonEmpty(); } addEntries(entries: StreamSource<readonly [K, V]>): SortedMap.NonEmpty<K, V> { if (isEmptyStreamSourceInstance(entries)) return this; const builder = this.toBuilder(); builder.addEntries(entries); return builder.build() as SortedMap.NonEmpty<K, V>; } modifyAt( atKey: K, options: { ifNew?: OptLazyOr<V, Token>; ifExists?: ((currentEntry: V, remove: Token) => V | Token) | V; } ): SortedMap<K, V> { return this.modifyAtInternal(atKey, options).normalize(); } set(key: K, value: V): SortedMap.NonEmpty<K, V> { return this.addEntry([key, value]); } updateAt<U>( key: RelatedTo<K, U>, update: Update<V> ): SortedMap.NonEmpty<K, V> { if (!this.context.isValidKey(key)) return this; return this.modifyAt(key, { ifExists: (value): V => Update(value, update), }).assumeNonEmpty(); } removeKey<UK>(key: RelatedTo<K, UK>): SortedMap<K, V> { if (!this.context.isValidKey(key)) return this; return this.modifyAt(key, { ifExists: (_, remove): typeof remove => remove, }); } removeKeys<UK>(keys: StreamSource<RelatedTo<K, UK>>): SortedMap<K, V> { if (isEmptyStreamSourceInstance(keys)) return this; const builder = this.toBuilder(); builder.removeKeys(keys); return builder.build(); } removeKeyAndGet<UK>(key: RelatedTo<K, UK>): [SortedMap<K, V>, V] | undefined { if (!this.context.isValidKey(key)) return undefined; const token = Symbol(); let currentValue: V | typeof token = token; const newMap = this.modifyAt(key, { ifExists: (value, remove): V | typeof remove => { currentValue = value; return remove; }, }); if (token === currentValue) { return undefined; } return [newMap, currentValue]; } filter( pred: (entry: readonly [K, V], index: number, halt: () => void) => boolean, options: { negate?: boolean } = {} ): SortedMap<K, V> { const builder = this.context.builder<K, V>(); builder.addEntries(this.stream().filter(pred, options)); if (builder.size === this.size) return this; return builder.build(); } take(amount: number): SortedMap<K, V> | any { if (amount === 0) return this.context.empty(); if (amount >= this.size || -amount > this.size) return this; if (amount < 0) return this.drop(this.size + amount); return this.takeInternal(amount).normalize(); } drop(amount: number): SortedMap<K, V> { if (amount === 0) return this; if (amount >= this.size || -amount > this.size) return this.context.empty(); if (amount < 0) return this.take(this.size + amount); return this.dropInternal(amount).normalize(); } sliceIndex(range: IndexRange): SortedMap<K, V> { const indexRange = IndexRange.getIndicesFor(range, this.size); if (indexRange === 'empty') return this.context.empty(); if (indexRange === 'all') return this; const [start, end] = indexRange; return this.drop(start).take(end - start + 1); } slice(range: Range<K>): SortedMap<K, V> { const { startIndex, endIndex } = this.getSliceRange(range); return this.sliceIndex({ start: [startIndex, true], end: [endIndex, true], }); } toBuilder(): SortedMapBuilder<K, V> { return this.context.createBuilder<K, V>(this); } toString(): string { return this.stream().join({ start: 'SortedMap(', sep: ', ', end: ')', valueToString: (entry) => `${entry[0]} -> ${entry[1]}`, }); } toJSON(): ToJSON<(readonly [K, V])[]> { return { dataType: this.context.typeTag, value: this.toArray(), }; } } export class SortedMapLeaf<K, V> extends SortedMapNode<K, V> { constructor( readonly context: SortedMapContext<K>, public entries: readonly (readonly [K, V])[] ) { super(); } copy(entries: readonly (readonly [K, V])[]): SortedMapLeaf<K, V> { if (entries === this.entries) return this; return this.context.leaf(entries); } get size(): number { return this.entries.length; } stream( options: { reversed?: boolean } = {} ): Stream.NonEmpty<readonly [K, V]> { return Stream.fromArray(this.entries, options) as Stream.NonEmpty<[K, V]>; } streamSliceIndex( range: IndexRange, options: { reversed?: boolean } = {} ): Stream<readonly [K, V]> { const { reversed = false } = options; return Stream.fromArray(this.entries, { range, reversed }); } min(): readonly [K, V] { return this.entries[0]; } max(): readonly [K, V] { return Arr.last(this.entries); } get<UK, O>(key: RelatedTo<K, UK>, otherwise?: OptLazy<O>): V | O { if (!this.context.isValidKey(key)) return OptLazy(otherwise) as O; const index = this.context.findIndex(key, this.entries); if (index < 0) return OptLazy(otherwise) as O; return this.entries[index][1]; } findIndex(key: K): number { if (!this.context.comp.isComparable(key)) return -1; const index = this.context.findIndex(key, this.entries); return index < 0 ? -1 : index; } getAtIndex<O>(index: number, otherwise?: OptLazy<O>): readonly [K, V] | O { if (index >= this.size || -index > this.size) return OptLazy(otherwise) as O; if (index < 0) return this.getAtIndex(this.size + index, otherwise); return this.entries[index]; } forEach( f: (entry: readonly [K, V], index: number, halt: () => void) => void, options: { state?: TraverseState } = {} ): void { const { state = TraverseState() } = options; if (state.halted) return; Arr.forEach(this.entries, f, state); } mapValues<V2>(mapFun: (value: V, key: K) => V2): SortedMapLeaf<K, V2> { const newEntries = this.entries.map((entry): [K, V2] => { const newValue = mapFun(entry[1], entry[0]); return [entry[0], newValue]; }); return this.context.leaf(newEntries); } toArray(): ArrayNonEmpty<[K, V]> { return this.entries.slice() as ArrayNonEmpty<[K, V]>; } // internal methods getInsertIndexOf(key: K): number { return this.context.findIndex(key, this.entries); } addInternal(entry: readonly [K, V]): SortedMapNode<K, V> { const index = this.context.findIndex(entry[0], this.entries); if (index >= 0) { const currentEntry = this.entries[index]; if (Object.is(currentEntry[1], entry[1])) return this; const newEntries = Arr.update(this.entries, index, entry); return this.copy(newEntries); } const insertIndex = SortedIndex.next(index); const newEntries = Arr.insert(this.entries, insertIndex, entry); return this.copy(newEntries); } modifyAtInternal( key: K, options: { ifNew?: OptLazyOr<V, Token>; ifExists?: ((currentEntry: V, remove: Token) => V | Token) | V; } ): SortedMapNode<K, V> { const entryIndex = this.context.findIndex(key, this.entries); if (entryIndex >= 0) { if (undefined === options.ifExists) return this; const currentEntry = this.entries[entryIndex]; const currentValue = currentEntry[1]; const newValue = options.ifExists instanceof Function ? options.ifExists(currentValue, Token) : options.ifExists; if (Object.is(newValue, currentValue)) return this; if (Token === newValue) { const newEntries = Arr.splice(this.mutateEntries, entryIndex, 1); return this.copy(newEntries); } const newEntries = Arr.update(this.entries, entryIndex, [ key, newValue, ] as [K, V]); return this.copy(newEntries); } if (undefined === options.ifNew) return this; const newValue = OptLazyOr<V, Token>(options.ifNew, Token); if (Token === newValue) return this; const insertIndex = SortedIndex.next(entryIndex); const newEntries = Arr.insert(this.entries, insertIndex, [ key, newValue, ] as [K, V]); return this.copy(newEntries); } takeInternal(amount: number): SortedMapLeaf<K, V> { return this.context.leaf(this.entries.slice(0, amount)); } dropInternal(amount: number): SortedMapLeaf<K, V> { return this.context.leaf(this.entries.slice(amount)); } deleteMin(): [readonly [K, V], SortedMapLeaf<K, V>] { return leafDeleteMin<SortedMapLeaf<K, V>, readonly [K, V]>(this); } deleteMax(): [readonly [K, V], SortedMapLeaf<K, V>] { return leafDeleteMax<SortedMapLeaf<K, V>, readonly [K, V]>(this); } mutateSplitRight(index?: number): [readonly [K, V], SortedMapLeaf<K, V>] { return leafMutateSplitRight<SortedMapLeaf<K, V>, readonly [K, V]>( this, index ); } mutateGiveToLeft( left: SortedMapLeaf<K, V>, toLeft: readonly [K, V] ): [readonly [K, V], SortedMapLeaf<K, V>] { return leafMutateGiveToLeft(this, left, toLeft); } mutateGiveToRight( right: SortedMapLeaf<K, V>, toRight: readonly [K, V] ): [readonly [K, V], SortedMapLeaf<K, V>] { return leafMutateGiveToRight(this, right, toRight); } mutateGetFromLeft( left: SortedMapLeaf<K, V>, toMe: readonly [K, V] ): [readonly [K, V], SortedMapLeaf<K, V>] { return leafMutateGetFromLeft(this, left, toMe); } mutateGetFromRight( right: SortedMapLeaf<K, V>, toMe: readonly [K, V] ): [readonly [K, V], SortedMapLeaf<K, V>] { return leafMutateGetFromRight(this, right, toMe); } mutateJoinLeft(left: SortedMapLeaf<K, V>, entry: readonly [K, V]): void { return leafMutateJoinLeft(this, left, entry); } mutateJoinRight(right: SortedMapLeaf<K, V>, entry: readonly [K, V]): void { return leafMutateJoinRight(this, right, entry); } normalize(): SortedMap<K, V> { if (this.entries.length === 0) return this.context.empty(); if (this.entries.length <= this.context.maxEntries) return this; const size = this.size; const [upEntry, rightNode] = this.mutateSplitRight(); return this.context.inner([upEntry], [this, rightNode], size); } } export class SortedMapInner<K, V> extends SortedMapNode<K, V> { constructor( readonly context: SortedMapContext<K>, public entries: readonly (readonly [K, V])[], public children: readonly SortedMapNode<K, V>[], readonly size: number ) { super(); } get mutateChildren(): SortedMapNode<K, V>[] { return this.children as SortedMapNode<K, V>[]; } copy( entries: readonly (readonly [K, V])[] = this.entries, children: readonly SortedMapNode<K, V>[] = this.children, size: number = this.size ): SortedMapInner<K, V> { if ( entries === this.entries && children === this.children && size === this.size ) return this; return this.context.inner(entries, children, size); } stream( options: { reversed?: boolean } = {} ): Stream.NonEmpty<readonly [K, V]> { const token = Symbol(); return Stream.zipAll( token, Stream.fromArray(this.children, options), Stream.fromArray(this.entries, options) ).flatMap(([child, e]): Stream.NonEmpty<readonly [K, V]> => { if (token === child) RimbuError.throwInvalidStateError(); if (token === e) return child.stream(options); return child.stream(options).append(e); }) as Stream.NonEmpty<readonly [K, V]>; } streamSliceIndex( range: IndexRange, options: { reversed?: boolean } = {} ): Stream<readonly [K, V]> { const { reversed = false } = options; return innerStreamSliceIndex<readonly [K, V]>(this, range, reversed); } min(): readonly [K, V] { return this.children[0].min(); } max(): readonly [K, V] { return Arr.last(this.children).max(); } get<UK, O>(key: RelatedTo<K, UK>, otherwise?: OptLazy<O>): V | O { if (!this.context.isValidKey(key)) return OptLazy(otherwise) as O; const index = this.context.findIndex(key, this.entries); if (index >= 0) return this.entries[index][1]; const childIndex = SortedIndex.next(index); const child = this.children[childIndex]; return child.get(key, otherwise); } findIndex(key: K): number { if (!this.context.comp.isComparable(key)) return -1; const index = this.context.findIndex(key, this.entries); if (index >= 0) return ( this.children.slice(0, index + 1).reduce((x, y) => x + y.size, 0) + index ); const childIndex = SortedIndex.next(index); const child = this.children[childIndex]; const index$ = child.findIndex(key); if (index$ >= 0) { return ( index$ + (this.children.slice(0, childIndex).reduce((x, y) => x + y.size, 0) - index - 1) ); } return -1; } getAtIndex<O>(index: number, otherwise?: OptLazy<O>): readonly [K, V] | O { return innerGetAtIndex<readonly [K, V], O>(this, index, otherwise); } forEach( f: (entry: readonly [K, V], index: number, halt: () => void) => void, options: { state?: TraverseState } = {} ): void { const { state = TraverseState() } = options; let i = -1; const entryLength = this.entries.length; const { halt } = state; while (!state.halted && i < entryLength) { if (i >= 0) f(this.entries[i], state.nextIndex(), halt); else { const childIndex = SortedIndex.next(i); this.children[childIndex].forEach(f, { state }); } i = SortedIndex.next(i); } } mapValues<V2>(mapFun: (value: V, key: K) => V2): SortedMapInner<K, V2> { const newEntries = this.entries.map((entry): [K, V2] => { const newValue = mapFun(entry[1], entry[0]); return [entry[0], newValue]; }); const newChildren = this.children.map( (child): SortedMapNode<K, V2> => child.mapValues(mapFun) ); return this.context.inner(newEntries, newChildren, this.size); } toArray(): ArrayNonEmpty<readonly [K, V]> { let i = -1; let result: (readonly [K, V])[] = []; while (i < this.entries.length) { if (i >= 0) result.push(this.entries[i]); else { const childIndex = SortedIndex.next(i); result = result.concat(this.children[childIndex].toArray()); } i = SortedIndex.next(i); } return result as ArrayNonEmpty<[K, V]>; } // internal methods getInsertIndexOf(key: K): number { let index = 0; for (let i = 0; i < this.entries.length; i++) { const comp = this.context.comp.compare(key, this.entries[i][0]); const child = this.children[i]; if (comp < 0) { const insertIndex = child.getInsertIndexOf(key); if (insertIndex < 0) return -index + insertIndex; return index + insertIndex; } index += child.size + 1; if (comp === 0) return index - 1; } const insertIndex = Arr.last(this.children).getInsertIndexOf(key); if (insertIndex < 0) return -index + insertIndex; return index + insertIndex; } addInternal(entry: readonly [K, V]): SortedMapInner<K, V> { const entryIndex = this.context.findIndex(entry[0], this.entries); if (entryIndex >= 0) { const newEntries = Arr.update( this.entries, entryIndex, (currentEntry): readonly [K, V] => { if (Object.is(currentEntry[1], entry[1])) return currentEntry; return entry; } ); return this.copy(newEntries); } const childIndex = SortedIndex.next(entryIndex); const child = this.children[childIndex]; const newChild = child.addInternal(entry); if (newChild === child) return this; const newSize = this.size + newChild.size - child.size; if (newChild.entries.length <= this.context.maxEntries) { // no need to shift const newChildren = Arr.update(this.children, childIndex, newChild); return this.copy(undefined, newChildren, newSize); } return this.normalizeDownsizeChild(childIndex, newChild, newSize); } modifyAtInternal( key: K, options: { ifNew?: OptLazyOr<V, Token>; ifExists?: ((currentEntry: V, remove: Token) => V | Token) | V; } ): SortedMapInner<K, V> { const entryIndex = this.context.findIndex(key, this.entries); if (entryIndex >= 0) { if (undefined === options.ifExists) return this; const currentEntry = this.entries[entryIndex]; const currentValue = currentEntry[1]; const newValue = options.ifExists instanceof Function ? options.ifExists(currentValue, Token) : options.ifExists; if (Object.is(newValue, currentValue)) return this; if (Token === newValue) { // remove inner entry const leftChild = this.children[entryIndex]; const rightChild = this.children[entryIndex + 1]; if (leftChild.entries.length >= rightChild.entries.length) { const [max, newLeft] = leftChild.deleteMax(); const newEntries = Arr.update(this.entries, entryIndex, max); const newSelf = this.copy(newEntries); return newSelf.normalizeIncreaseChild( entryIndex, newLeft, this.size - 1 ); } const [min, newRight] = rightChild.deleteMin(); const newEntries = Arr.update(this.entries, entryIndex, min); const newSelf = this.copy(newEntries); return newSelf.normalizeIncreaseChild( entryIndex + 1, newRight, this.size - 1 ); } // update inner entry const newEntry: [K, V] = [key, newValue]; const newEntries = Arr.update(this.entries, entryIndex, newEntry); return this.copy(newEntries); } const childIndex = SortedIndex.next(entryIndex); const child = this.children[childIndex]; const newChild = child.modifyAtInternal(key, options); const newSize = this.size + newChild.size - child.size; if (newChild.entries.length < this.context.minEntries) { return this.normalizeIncreaseChild(childIndex, newChild, newSize); } if (newChild.entries.length > this.context.maxEntries) { return this.normalizeDownsizeChild(childIndex, newChild, newSize); } const newChildren = Arr.update(this.children, childIndex, newChild); return this.copy( undefined, newChildren, this.size + newChild.size - child.size ); } takeInternal(amount: number): SortedMapNode<K, V> { return innerTakeInternal<SortedMapInner<K, V>, readonly [K, V]>( this, amount ); } dropInternal(amount: number): SortedMapNode<K, V> { return innerDropInternal<SortedMapInner<K, V>, readonly [K, V]>( this, amount ); } deleteMin(): [readonly [K, V], SortedMapInner<K, V>] { return innerDeleteMin<SortedMapInner<K, V>, readonly [K, V]>(this); } deleteMax(): [readonly [K, V], SortedMapInner<K, V>] { return innerDeleteMax<SortedMapInner<K, V>, readonly [K, V]>(this); } mutateSplitRight(index?: number): [readonly [K, V], SortedMapInner<K, V>] { return innerMutateSplitRight<SortedMapInner<K, V>, readonly [K, V]>( this, index ); } mutateGiveToLeft( left: SortedMapInner<K, V>, toLeft: readonly [K, V] ): [readonly [K, V], SortedMapInner<K, V>] { return innerMutateGiveToLeft(this, left, toLeft); } mutateGiveToRight( right: SortedMapInner<K, V>, toRight: readonly [K, V] ): [readonly [K, V], SortedMapInner<K, V>] { return innerMutateGiveToRight(this, right, toRight); } mutateGetFromLeft( left: SortedMapInner<K, V>, toMe: readonly [K, V] ): [readonly [K, V], SortedMapInner<K, V>] { return innerMutateGetFromLeft(this, left, toMe); } mutateGetFromRight( right: SortedMapInner<K, V>, toMe: readonly [K, V] ): [readonly [K, V], SortedMapInner<K, V>] { return innerMutateGetFromRight(this, right, toMe); } mutateJoinLeft(left: SortedMapInner<K, V>, entry: readonly [K, V]): void { return innerMutateJoinLeft(this, left, entry); } mutateJoinRight(right: SortedMapInner<K, V>, entry: readonly [K, V]): void { return innerMutateJoinRight(this, right, entry); } normalizeDownsizeChild( childIndex: number, newChild: SortedMapNode<K, V>, newSize: number ): SortedMapInner<K, V> { return innerNormalizeDownsizeChild<SortedMapInner<K, V>, readonly [K, V]>( this, childIndex, newChild, newSize ); } normalizeIncreaseChild( childIndex: number, newChild: SortedMapNode<K, V>, newSize: number ): SortedMapInner<K, V> { return innerNormalizeIncreaseChild<SortedMapInner<K, V>, readonly [K, V]>( this, childIndex, newChild, newSize ); } normalize(): SortedMap<K, V> { if (this.entries.length === 0) return this.children[0].normalize(); if (this.entries.length <= this.context.maxEntries) return this; const size = this.size; const [upEntry, rightNode] = this.mutateSplitRight(); return this.copy([upEntry], [this, rightNode], size); } }