diff --git a/bag.ts b/bag.ts new file mode 100644 index 0000000..53ed2dc --- /dev/null +++ b/bag.ts @@ -0,0 +1,77 @@ +// Bags and Deltas (which are Bags where item-counts can be negative). + +import { Value, Set, Dictionary, DefaultPointer } from 'preserves'; + +export enum ChangeDescription { + PRESENT_TO_ABSENT = -1, + ABSENT_TO_ABSENT = 0, + ABSENT_TO_PRESENT = 1, + PRESENT_TO_PRESENT = 2, +} + +export class Bag { + _items: Dictionary; + + constructor(s?: Set) { + this._items = new Dictionary(); + if (s) s.forEach((v) => this._items.set(v, 1)); + } + + get(key: Value): number { + return this._items.get(key, 0) as number; + } + + change(key: Value, 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 Dictionary(); + } + + includes(key: Value): boolean { + return this._items.has(key); + } + + get size(): number { + return this._items.size; + } + + keys(): IterableIterator> { + return this._items.keys(); + } + + entries(): IterableIterator<[Value, number]> { + return this._items.entries(); + } + + forEach(f: (count: number, value: Value) => void) { + this._items.forEach(f); + } + + snapshot(): Dictionary { + return this._items.clone(); + } + + clone(): Bag { + const b = new Bag(); + b._items = this._items.clone(); + return b; + } +}