81 lines
2.1 KiB
TypeScript
81 lines
2.1 KiB
TypeScript
/// SPDX-License-Identifier: GPL-3.0-or-later
|
|
/// SPDX-FileCopyrightText: Copyright © 2016-2023 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
|
|
|
// Bags and Deltas (which are Bags where item-counts can be negative).
|
|
|
|
import { Value, Set, Dictionary, KeyedDictionary, KeyedSet } from '@preserves/core';
|
|
|
|
export enum ChangeDescription {
|
|
PRESENT_TO_ABSENT = -1,
|
|
ABSENT_TO_ABSENT = 0,
|
|
ABSENT_TO_PRESENT = 1,
|
|
PRESENT_TO_PRESENT = 2,
|
|
}
|
|
|
|
export class Bag<T, V extends Value<T> = Value<T>> {
|
|
_items: KeyedDictionary<V, number, T>;
|
|
|
|
constructor(s?: KeyedSet<V, T>) {
|
|
this._items = new KeyedDictionary();
|
|
if (s) s.forEach((v) => this._items.set(v, 1));
|
|
}
|
|
|
|
get(key: V): number {
|
|
return this._items.get(key, 0) as number;
|
|
}
|
|
|
|
change(key: V, delta: number, clamp: boolean = false): ChangeDescription {
|
|
let oldCount = this.get(key);
|
|
let newCount = oldCount + delta;
|
|
if (clamp) {
|
|
newCount = Math.max(0, newCount);
|
|
}
|
|
|
|
if (newCount === 0) {
|
|
this._items.delete(key);
|
|
return (oldCount === 0)
|
|
? ChangeDescription.ABSENT_TO_ABSENT
|
|
: ChangeDescription.PRESENT_TO_ABSENT;
|
|
} else {
|
|
this._items.set(key, newCount);
|
|
return (oldCount === 0)
|
|
? ChangeDescription.ABSENT_TO_PRESENT
|
|
: ChangeDescription.PRESENT_TO_PRESENT;
|
|
}
|
|
}
|
|
|
|
clear() {
|
|
this._items = new KeyedDictionary();
|
|
}
|
|
|
|
includes(key: V): boolean {
|
|
return this._items.has(key);
|
|
}
|
|
|
|
get size(): number {
|
|
return this._items.size;
|
|
}
|
|
|
|
keys(): IterableIterator<V> {
|
|
return this._items.keys();
|
|
}
|
|
|
|
entries(): IterableIterator<[V, number]> {
|
|
return this._items.entries();
|
|
}
|
|
|
|
forEach(f: (count: number, value: V) => void) {
|
|
this._items.forEach((c, v) => f(c, v));
|
|
}
|
|
|
|
snapshot(): KeyedDictionary<V, number, T> {
|
|
return this._items.clone();
|
|
}
|
|
|
|
clone(): Bag<T, V> {
|
|
const b = new Bag<T, V>();
|
|
b._items = this._items.clone();
|
|
return b;
|
|
}
|
|
}
|