recursive-set 7.0.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,18 +3,18 @@
3
3
  [![MIT License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
4
4
  [![npm version](https://img.shields.io/npm/v/recursive-set.svg)](https://www.npmjs.com/package/recursive-set)
5
5
 
6
- High-performance set implementation for TypeScript with **value semantics** (structural equality) and controlled mutability via “freeze-on-hash”.
6
+ High-performance collection library for TypeScript supporting **value semantics** (deep equality) and recursive structures. Version 8 introduces a new hash-based architecture optimized for high-throughput workloads like SAT solvers and graph algorithms.
7
7
 
8
8
  ## Overview
9
9
 
10
- `RecursiveSet` is a mathematical set designed for workloads in theoretical computer science (SAT solvers, graph algorithms, ZFC-style constructions) where deep nesting and structural equality matter (e.g., `{1,2} = {2,1}`).
10
+ `RecursiveSet` provides mathematical sets and maps where equality is determined by structure/content rather than object reference (e.g., `{1, 2}` is equal to `{2, 1}`).
11
11
 
12
- Key design points:
12
+ **Key Architectural Features:**
13
13
 
14
- - Structural equality (ZFC-like semantics) for nested sets and sequences.
15
- - Mutable during construction; becomes immutable once hashed (“freeze-on-hash”).
16
- - Sorted-array backing for good cache locality on small to medium `N`.
17
- - Bulk loading and merge-scan set operations for speed.
14
+ - **Open Addressing:** Uses linear probing with a load factor of 0.75 for cache-efficient lookups.
15
+ - **Backshift Deletion:** Maintains probe chain integrity without "tombstones," preventing performance degradation over time.
16
+ - **Zero-Allocation Hashing:** Uses static buffers and raw bitwise operations to hash numbers without triggering the Garbage Collector.
17
+ - **Structure of Arrays (SoA):** Data is stored in flat arrays to maximize CPU cache locality.
18
18
 
19
19
  ## Installation
20
20
 
@@ -22,186 +22,157 @@ Key design points:
22
22
  npm install recursive-set
23
23
  ```
24
24
 
25
-
26
25
  ## Quickstart
27
26
 
28
- ### Efficient Construction (Bulk Loading)
27
+ ### Working with Sets
29
28
 
30
- Instead of adding elements one by one, use `fromArray` for maximum performance:
29
+ Sets automatically deduplicate elements based on their value or structure.
31
30
 
32
31
  ```ts
33
- import { RecursiveSet, Tuple } from "recursive-set";
32
+ import { RecursiveSet } from "recursive-set";
33
+
34
+ // Primitive values
35
+ const numbers = new RecursiveSet(1, 2, 3);
36
+ numbers.add(1); // No effect, 1 is already present
34
37
 
35
- // Fast: Bulk load sorts and deduplicates in one go
36
- const states = RecursiveSet.fromArray(["q0", "q1", "q2"]);
38
+ // Recursive structures (Sets of Sets)
39
+ const setA = new RecursiveSet(1, 2);
40
+ const setB = new RecursiveSet(2, 1);
41
+ const metaSet = new RecursiveSet<RecursiveSet<number>>();
37
42
 
38
- // Sets of Sets (partitioning)
39
- const partition = new RecursiveSet<RecursiveSet<string>>();
40
- partition.add(states); // {{q0, q1, q2}}
43
+ metaSet.add(setA);
44
+ metaSet.add(setB);
41
45
 
42
- console.log(partition.toString()); // {{q0, q1, q2}}
46
+ console.log(metaSet.size); // 1, because setA equals setB
43
47
  ```
44
48
 
49
+ ### Working with Maps
45
50
 
46
- ### Working with Tuples \& Structures
51
+ `RecursiveMap` allows using complex objects (like Tuples or Sets) as keys.
47
52
 
48
53
  ```ts
49
- // Tuples (ordered pairs / edges) represent structural values
50
- // They are immutable and cached by default.
54
+ import { RecursiveMap, Tuple } from "recursive-set";
55
+
56
+ const transitions = new RecursiveMap<Tuple<[string, string]>, number>();
51
57
  const edge = new Tuple("q0", "q1");
52
58
 
53
- const transitions = new RecursiveSet<Tuple<[string, string]>>();
54
- transitions.add(edge);
55
- ```
59
+ transitions.set(edge, 1);
56
60
 
61
+ // Retrieval using a new, structurally identical key
62
+ console.log(transitions.get(new Tuple("q0", "q1"))); // 1
63
+ ```
57
64
 
58
- ### Lifecycle (mutablefrozen)
65
+ ### Lifecycle (MutableFrozen)
59
66
 
60
- Accessing `hashCode` freezes the set to prevent hash corruption.
67
+ To guarantee hash stability, collections become **immutable** (frozen) once their hash code is computed or they are inserted into another collection.
61
68
 
62
69
  ```ts
63
- const A = new RecursiveSet(1, 2);
64
- const B = new RecursiveSet(A); // hashing B may hash A -> A becomes frozen
65
-
66
- console.log(B.has(A)); // true
70
+ const A = new RecursiveSet(1);
71
+ const B = new RecursiveSet(A); // Accessing A's hash to store it in B freezes A.
67
72
 
68
73
  try {
69
- A.add(3); // throws after A is frozen
70
- } catch {
71
- console.log("A is frozen and cannot be mutated.");
74
+ A.add(2); // Throws Error: Frozen Set modified.
75
+ } catch (e) {
76
+ // Expected behavior
72
77
  }
73
78
 
74
- // “Fork” for mutation
79
+ // Use mutableCopy to continue editing
75
80
  const C = A.mutableCopy();
76
- C.add(3);
77
- ```
78
-
79
-
80
- ## Contracts
81
-
82
- This library optimizes for raw throughput. Using it correctly requires strict adherence to these rules:
83
-
84
- 1. **Finite numbers only:** Do not insert `NaN`, `Infinity`, or `-Infinity`. Comparison logic uses fast arithmetic (`a - b`).
85
- 2. **No mutation:** Do not mutate arrays/tuples/objects after insertion.
86
- 3. **Type consistency:** Avoid mixing distinct structure types (e.g., `Array` vs `Tuple`) in the same set for the same logical role, as hash-collision edge cases may treat them as equal for performance reasons.
87
-
88
- Violating the contract can break sorted order invariants, hashing assumptions, and equality semantics (garbage in → garbage out).
89
-
90
- ### Freeze-on-hash rule
91
-
92
- - A set is mutable until `hashCode` is accessed.
93
- - After hashing, mutation methods throw; use `mutableCopy()` to continue editing.
94
-
95
-
96
- ### Tuple vs Array
97
-
98
- - `Tuple` is an immutable container: it makes a defensive copy and freezes its internal storage via `Object.freeze()` (shallow immutability).
99
- - Plain `Array` values are supported as ordered sequences, but they are not frozen by the library.
100
-
101
- **Recommendation:** For hot loops (like SAT solvers), represent frequently compared “small composite values” as `Tuple` to benefit from cached hashing and immutability.
102
-
103
- ## API
104
-
105
- ### Types
106
-
107
- ```ts
108
- export type Primitive = number | string;
109
- export type Value =
110
- | Primitive
111
- | RecursiveSet<any>
112
- | Tuple<any>
113
- | ReadonlyArray<Value>;
81
+ C.add(2);
114
82
  ```
115
83
 
84
+ ## Contracts & Invariants
116
85
 
117
- ### Construction
86
+ This library optimizes for raw speed and assumes strict adherence to the following contracts. Violating them leads to undefined behavior.
118
87
 
119
- ```ts
120
- new RecursiveSet<T>(...elements: T[])
121
- ```
88
+ 1. **Finite Numbers Only:** `NaN` and `Infinity` are **strictly forbidden**. They break strict equality checks and integer optimization paths.
89
+ 2. **Strict Value Semantics:** Plain JavaScript objects (`{}`) are **not supported**. Keys must implement the `Structural` interface (provide `equals`, `hashCode`, and `toString`).
90
+ 3. **Hash Quality:** The $O(1)$ performance guarantee relies on a good distribution. Returning a constant `hashCode` (e.g., `42`) forces all elements into a single bucket, degrading performance to $O(N)$.
91
+ 4. **Deterministic Visualization:** Custom `toString()` implementations **must** utilize `compareVisualLogic` for nested structures. Failing to do so results in unstable string output.
92
+ 5. **Immutability:** Once an object is added to a collection, its `hashCode` **must not change**.
93
+ 6. **No Circular Dependencies:** A `RecursiveSet` cannot contain itself, directly or indirectly. Runtime checks are omitted for performance; creating a cycle will cause a Stack Overflow during hashing.
122
94
 
123
- Elements are sorted and deduplicated on construction.
95
+ ## API Reference
124
96
 
125
- ### Bulk loading
97
+ ### Core Types
126
98
 
127
99
  ```ts
128
- RecursiveSet.fromArray<T>(elements: T[]): RecursiveSet<T>
129
- ```
130
-
131
- Sorts once and deduplicates (typically much faster than many `.add()` calls).
100
+ type Primitive = number | string;
101
+ type Value = Primitive | Structural;
132
102
 
133
- ### Unsafe creation
134
-
135
- ```ts
136
- RecursiveSet.fromSortedUnsafe<T>(sortedUnique: T[]): RecursiveSet<T>
103
+ interface Structural {
104
+ readonly hashCode: number;
105
+ equals(other: unknown): boolean;
106
+ toString(): string;
107
+ }
137
108
  ```
138
109
 
139
- **Trusted bypass:** Assumes the input array is already strictly sorted (by internal `compare`) and contains no duplicates. Use only when you can guarantee invariants externally.
140
-
141
- ### Mutation (only while unfrozen)
110
+ ### RecursiveSet
142
111
 
143
- - `add(element: T): this`
144
- - `remove(element: T): this`
145
- - `clear(): this`
112
+ #### Construction
146
113
 
114
+ - `new RecursiveSet<T>(...elements: T[])`: Creates a set from the given arguments.
147
115
 
148
- ### Copying
116
+ #### Mutation (Unfrozen state only)
149
117
 
150
- - `mutableCopy(): RecursiveSet<T>` mutable shallow copy (use after freezing)
151
- - `clone(): RecursiveSet<T>` alias for `mutableCopy()`
118
+ - `add(element: T): void`: Adds an element ($O(1)$ amortized).
119
+ - `remove(element: T): void`: Removes an element ($O(1)$ amortized).
152
120
 
121
+ #### Set Operations
153
122
 
154
- ### Set operations (return new sets)
123
+ All operations return a new `RecursiveSet` instance.
155
124
 
156
- All operations below return new `RecursiveSet` instances:
157
-
158
- - `union(other): RecursiveSet<T | U>`
125
+ - `union(other): RecursiveSet<T>`
159
126
  - `intersection(other): RecursiveSet<T>`
160
- - `difference(other): RecursiveSet<T>`
127
+ - `difference(other): RecursiveSet<T>` ($A \setminus B$)
161
128
  - `symmetricDifference(other): RecursiveSet<T>`
162
- - `powerset(): RecursiveSet<RecursiveSet<T>>` (guarded; throws if too large)
163
129
  - `cartesianProduct<U>(other): RecursiveSet<Tuple<[T, U]>>`
130
+ - `powerset(): RecursiveSet<RecursiveSet<T>>` (Throws if size > 20)
164
131
 
165
-
166
- ### Predicates \& properties
132
+ #### Properties
167
133
 
168
134
  - `has(element: T): boolean`
169
- - `equals(other: RecursiveSet<Value>): boolean`
170
- - `compare(other: RecursiveSet<Value>): number`
135
+ - `equals(other: unknown): boolean`
171
136
  - `isSubset(other): boolean`
172
137
  - `isSuperset(other): boolean`
173
- - `isEmpty(): boolean`
138
+ - `mutableCopy(): RecursiveSet<T>`: Returns a shallow mutable clone.
174
139
  - `size: number`
175
- - `hashCode: number` computes and caches hash; freezes the set
176
- - `isFrozen: boolean`
140
+ - `hashCode: number`: Computes hash and freezes the set.
177
141
 
142
+ ### RecursiveMap
178
143
 
179
- ### Ordering rules
144
+ A hash map supporting `Value` keys.
180
145
 
181
- Internal ordering is deterministic by design:
146
+ - `set(key: K, value: V): void`
147
+ - `get(key: K): V | undefined`
148
+ - `delete(key: K): boolean`
149
+ - `has(key: K): boolean`
150
+ - `mutableCopy(): RecursiveMap<K, V>`
182
151
 
183
- - Type order: `number` < `string` < sequence (`Array`/`Tuple`) < `RecursiveSet`.
184
- - Sequences compare by length first, then lexicographically element-by-element.
185
- - Sets compare by cached hash first, then by structural comparison on collision.
152
+ ### Tuple
186
153
 
154
+ An immutable, hashable sequence of values. Useful for composite keys.
155
+
156
+ - `new Tuple(...elements: T[])`
157
+ - `get(index: number): T[index]`
158
+ - `length: number`
187
159
 
188
160
  ## Credits
189
161
 
190
162
  This library was developed as a student research project under the supervision of **[Karl Stroetmann](https://github.com/karlstroetmann/)**.
191
163
 
192
- Special thanks for his architectural guidance towards homogeneous sets and for contributing the "Merge Scan" & "Bulk Loading" optimization concepts that form the high-performance core of this engine.
164
+ Special thanks for his architectural guidance on homogeneous sets and the theoretical foundations required for high-performance set engines.
193
165
 
194
166
  ## Contributing
195
167
 
196
168
  ```bash
197
- git clone https://github.com/cstrerath/recursive-set.git
169
+ git clone [https://github.com/cstrerath/recursive-set.git](https://github.com/cstrerath/recursive-set.git)
198
170
  npm install
199
171
  npm run build
200
172
  npx tsx test/test.ts
201
173
  npx tsx test/nqueens.ts
202
174
  ```
203
175
 
204
-
205
176
  ## License
206
177
 
207
- MIT License © 2025 Christian Strerath. See `LICENSE`
178
+ MIT License © 2025 Christian Strerath. See `LICENSE`.