recursive-set 3.0.0 → 5.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
@@ -1,37 +1,33 @@
1
1
  # RecursiveSet
2
2
 
3
- > High-performance, mutable set implementation for TypeScript – modeled after ZFC set theory.
4
-
5
- Supports recursive nesting, strict structural equality, and includes all classic set operations (union, intersection, difference, powerset, cartesian product). **Designed for Theoretical Computer Science, Graphs, and FSMs.**
3
+ > **High-Performance ZFC Set Implementation for TypeScript**
4
+ >
5
+ > Mutable, strictly typed, and optimized for cache locality.
6
6
 
7
7
  [![MIT License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
8
8
  [![npm version](https://img.shields.io/npm/v/recursive-set.svg)](https://www.npmjs.com/package/recursive-set)
9
9
 
10
10
  ---
11
11
 
12
- ## Features
12
+ ## 🚀 What is this?
13
13
 
14
- * **Strict Value Equality:** Mathematical sets behave mathematically. `{a, b}` is equal to `{b, a}`.
15
- * **Tuples First:** Includes a strongly typed `Tuple` class for ordered pairs (e.g., edges, transitions), solving JS Array reference pitfalls.
16
- * **Homogeneous by Default:** Generic typing (`RecursiveSet<T>`) enforces clean data structures.
17
- * **Recursive:** Sets can contain sets (of sets...). Ideal for Power Sets and Von Neumann Ordinals.
18
- * **Copy-on-Write:** **O(1) cloning** via structural sharing (powered by persistent Red-Black Trees).
19
- * **Lean \& Mean:** No implicit overhead. Cycle checking is left to the user to allow maximum performance.
14
+ A mathematical set implementation designed for **Theoretical Computer Science**, **SAT-Solvers**, and **Graph Theory**. Unlike native JavaScript `Set`, `RecursiveSet` enforces **Structural Equality** (ZFC semantics) and supports deep nesting.
20
15
 
21
- ---
16
+ **v5.0.0 Update:** Now featuring **"Freeze-on-Hash"** lifecycle management.
17
+ * **Safety First**: Sets automatically become **immutable** (frozen) once used as a key or member of another set. No more corrupted hash codes!
18
+ * **High Performance**: Backed by **Sorted Arrays** and FNV-1a hashing. 5x - 10x faster than tree-based implementations for typical *N* < 1000.
19
+ * **O(1) Equality Checks**: Aggressive caching allows for instant comparisons of deep structures.
22
20
 
23
- ## Implementation Details
21
+ ---
24
22
 
25
- This library enforces **Strict ZFC Semantics**, differing from native JavaScript `Set`:
23
+ ## Features
26
24
 
27
- - **Extensionality:** Two sets are equal if they contain the same elements.
28
- - `new RecursiveSet(new RecursiveSet(1)).equals(new RecursiveSet(new RecursiveSet(1)))` is `true`.
29
- - **No Hidden References:** Plain JavaScript Arrays and Objects are **rejected** to prevent reference-equality confusion.
30
- - Use `Tuple` for ordered sequences.
31
- - Use `RecursiveSet` for collections.
32
- - **Performance:** Powered by **Functional Red-Black Trees**.
33
- - Insertion/Lookup: **O(log n)**.
34
- - Cloning: **O(1)**.
25
+ * **🔢 Strict Structural Equality:** `{1, 2}` is equal to `{2, 1}`.
26
+ * **❄️ Freeze-on-Hash:** Mutable during construction, immutable during usage. Prevents subtle reference bugs.
27
+ * **📦 Deeply Recursive:** Sets can contain Sets. Ideal for Power Sets.
28
+ * **📐 Tuples & Arrays:** Native support for `Tuple` class or standard JS Arrays `[a, b]` as elements.
29
+ * **🔒 Type Safe:** Fully strict TypeScript implementation. No `any` casts.
30
+ * **🛡️ Deterministic:** Hashing is order-independent for Sets and order-dependent for Sequences.
35
31
 
36
32
  ---
37
33
 
@@ -43,155 +39,120 @@ npm install recursive-set
43
39
 
44
40
  ---
45
41
  ## Quickstart
42
+
43
+ ### 1. Basic Usage
46
44
  ```typescript
47
45
  import { RecursiveSet, Tuple } from "recursive-set";
48
46
 
49
- // 1. Sets of primitives
47
+ // Sets of primitives
50
48
  const states = new RecursiveSet<string>();
51
49
  states.add("q0").add("q1");
52
50
 
53
- // 2. Sets of Sets (Partitioning)
51
+ // Sets of Sets (Partitioning)
54
52
  const partition = new RecursiveSet<RecursiveSet<string>>();
55
53
  partition.add(states); // {{q0, q1}}
56
54
 
57
- // 3. Tuples (Ordered Pairs / Edges)
58
- const edge = new Tuple("q0", "q1"); // (q0, q1)
55
+ // Tuples (Ordered Pairs / Edges)
56
+ const edge = new Tuple("q0", "q1");
57
+ // or simply: const edge = ["q0", "q1"];
58
+
59
59
  const transitions = new RecursiveSet<Tuple<[string, string]>>();
60
60
  transitions.add(edge);
61
61
 
62
- console.log(partition.toString()); // {{q0, q1}}
63
- console.log(transitions.toString()); // {(q0, q1)}
64
- ```
65
-
66
- ---
67
-
68
- ## API Reference
69
-
70
- ### Constructor
71
-
72
- ```typescript
73
- // T must be explicit or inferred. No default 'unknown'.
74
- new RecursiveSet<T>(...elements: Array<T | RecursiveSet<T>>)
62
+ console.log(partition.toString()); // {{q0, q1}}
75
63
  ```
76
64
 
77
- ### Methods
78
-
79
- **Mutation:**
80
- * `add(element: T | RecursiveSet<T>): this` – Add element. **Throws on NaN or plain Object/Array.**
81
- * `remove(element: T | RecursiveSet<T>): this` – Remove element.
82
- * `clear(): this` – Remove all elements.
83
-
84
- **Snapshot:**
85
- - `clone(): RecursiveSet<T>` – Creates a shallow copy in **O(1)** time (Copy-on-Write).
86
-
87
- **Set Operations:**
88
- - `union(other: RecursiveSet<T>): RecursiveSet<T>` – A ∪ B
89
- - `intersection(other: RecursiveSet<T>): RecursiveSet<T>` – A ∩ B
90
- - `difference(other: RecursiveSet<T>): RecursiveSet<T>` – A \ B
91
- - `symmetricDifference(other: RecursiveSet<T>): RecursiveSet<T>` – A △ B
92
-
93
- **Advanced Operations:**
94
- - `powerset(): RecursiveSet<RecursiveSet<T>>` – 𝒫(A)
95
- - `cartesianProduct<U>(other: RecursiveSet<U>): RecursiveSet<Tuple<[T, U]>>` – A × B (Returns Tuples!)
96
-
97
- **Predicates:**
98
- - `has(element: T | RecursiveSet<T>): boolean` – Check membership
99
- - `isSubset(other: RecursiveSet<T>): boolean` – Check if ⊆
100
- - `isSuperset(other: RecursiveSet<T>): boolean` – Check if ⊇
101
- - `equals(other: RecursiveSet<T>): boolean` – Structural equality
102
- - `isEmpty(): boolean` – Check if set is empty
103
-
104
- **Properties:**
105
- - `size: number` – Cardinality |A|
106
- - `toString(): string` – Pretty print with ∅ and {}
65
+ ### 2. The Lifecycle (Mutable -> Frozen)
107
66
 
108
- ### Tuple Class
109
-
110
- Helper for structural value equality of sequences.
67
+ **New in v5:** To ensure mathematical correctness, a set cannot be modified once it has been hashed (e.g., added to another set).
111
68
 
112
69
  ```typescript
113
- const t1 = new Tuple(1, 2);
114
- const t2 = new Tuple(1, 2);
115
- // In JS: [1,2] !== [1,2]
116
- // In RecursiveSet: t1 equals t2 (Structural Equality)
117
- ```
118
- ---
119
-
120
- ## Examples
70
+ const A = new RecursiveSet(1, 2);
71
+ const B = new RecursiveSet(A);
72
+ // B hashes A to store it.
73
+ // A is now FROZEN to ensure B's integrity.
121
74
 
122
- ### Basic Usage
75
+ console.log(B.has(A)); // true
123
76
 
124
- ```typescript
125
- const s1 = new RecursiveSet(1, 2, 3);
126
- const s2 = new RecursiveSet(2, 3, 4);
77
+ try {
78
+ A.add(3); // 💥 Throws Error: Cannot add() to a frozen RecursiveSet
79
+ } catch (e) {
80
+ console.log("A is immutable now!");
81
+ }
127
82
 
128
- console.log(s1.union(s2)); // {1, 2, 3, 4}
129
- console.log(s1.intersection(s2)); // {2, 3}
130
- console.log(s1.difference(s2)); // {1}
83
+ // Fix: Create a mutable copy ("Forking")
84
+ const C = A.mutableCopy();
85
+ C.add(3); // Works!
131
86
  ```
132
87
 
133
- ### Backtracking with O(1) Clone
134
-
135
- ```typescript
136
- const state = new RecursiveSet("init");
137
- // ... perform some operations ...
138
-
139
- // Create a checkpoint (O(1))
140
- const checkpoint = state.clone();
88
+ ---
141
89
 
142
- state.add("newState");
143
- // If this path fails, simply revert:
144
- // state = checkpoint; (conceptually)
145
- ```
90
+ ## API Reference
146
91
 
147
- ### Power Set
92
+ ### Constructor
148
93
 
149
94
  ```typescript
150
- const set = new RecursiveSet(1, 2);
151
- const power = set.powerset();
152
-
153
- console.log(power.toString()); // {∅, {1}, {2}, {1, 2}}
95
+ // Create empty or with initial elements
96
+ // Elements are automatically sorted and deduplicated.
97
+ new RecursiveSet<T>(...elements: T[])
154
98
  ```
155
99
 
156
- ### Cartesian Product \& Tuples
157
100
 
158
- ```typescript
159
- const A = new RecursiveSet(1, 2);
160
- const B = new RecursiveSet("x", "y");
161
-
162
- // A × B = {(1, x), (1, y), (2, x), (2, y)}
163
- const product = A.cartesianProduct(B);
101
+ ### Methods
164
102
 
165
- // Result contains strongly typed Tuples
166
- for (const tuple of product) {
167
- console.log(tuple.get(0), tuple.get(1)); // 1 "x"
168
- }
169
- ```
103
+ **Lifecycle Management:**
104
+ * `mutableCopy(): RecursiveSet<T>` – Creates a fresh, mutable clone of the set (O(N)). Use this if you need to modify a frozen set.
105
+ * `clone(): RecursiveSet<T>` Alias for mutableCopy.
170
106
 
107
+ **Mutation:**
108
+ * `add(element: T): this` – Insert element (O(N) worst case, O(1) append).
109
+ * `remove(element: T): this` – Remove element.
110
+ * `clear(): this` – Reset set.
111
+
112
+ **Set Operations (Immutable results):**
113
+ * `union(other: RecursiveSet<T>): RecursiveSet<T>` – $A \cup B$
114
+ * `intersection(other: RecursiveSet<T>): RecursiveSet<T>` – $A \cap B$
115
+ * `difference(other: RecursiveSet<T>): RecursiveSet<T>` – $A \setminus B$
116
+ * `symmetricDifference(other: RecursiveSet<T>): RecursiveSet<T>` – $A \triangle B$
117
+ * `powerset(): RecursiveSet<RecursiveSet<T>>` – $\mathcal{P}(A)$
118
+ * `cartesianProduct<U>(other: RecursiveSet<U>): RecursiveSet<Tuple<[T, U]>>` – $A \times B$
119
+
120
+ **Predicates (Fast):**
121
+ * `has(element: T): boolean` – **O(log N)** lookup (Binary Search).
122
+ * `equals(other: RecursiveSet<T>): boolean` – **O(1)** via Hash-Cache (usually).
123
+ * `isSubset(other: RecursiveSet<T>): boolean` – Check if $A \subseteq B$.
124
+ * `isSuperset(other: RecursiveSet<T>): boolean` – Check if $A \supseteq B$.
125
+ * `isEmpty(): boolean` – Check if $|A| = 0$.
171
126
 
172
- ### Strictness (Breaking Changes in V3)
127
+ **Properties:**
128
+ * `size: number` – Cardinality.
129
+ * `hashCode: number` – The cached hash. Accessing this property freezes the set.
130
+ * `isFrozen: boolean` – Check if the set is read-only.
173
131
 
174
- ```typescript
175
- const s = new RecursiveSet<number>();
132
+ ---
176
133
 
177
- // Error: Plain Arrays not supported (Reference Ambiguity)
178
- // s.add([1, 2]);
134
+ ## Performance Notes
179
135
 
180
- // Correct: Use Tuple
181
- s.add(new Tuple(1, 2));
136
+ **Why Sorted Arrays?**
137
+ For sets with $N < 1000$ (common in logic puzzles, N-Queens, graphs), the overhead of allocating tree nodes (v2/v3) dominates runtime. Sorted Arrays exploit **CPU Cache Lines**.
182
138
 
183
- // Error: NaN is not supported
184
- // s.add(NaN);
185
- ```
139
+ | Operation | Complexity | Real World (Small N) |
140
+ | :--- | :--- | :--- |
141
+ | **Lookup** | $O(\log N)$ | 🚀 Instant |
142
+ | **Equality** | $O(N)$ / $O(1)$* | ⚡ Instant (Hash Match) |
143
+ | **Insert** | $O(N)$ | Fast (Native `splice` / `memmove`) |
144
+ | **Iteration** | $O(N)$ | 🚀 Native Array Speed |
186
145
 
146
+ *\*Equality is O(1) if hashes differ (99% case), O(N) if hash collision occurs.*
187
147
 
188
148
  ---
189
149
 
190
- ## Use Cases
150
+ ## Breaking Changes in v5.0
191
151
 
192
- * **Finite State Machine (FSM):** States as Sets, Transitions as Tuples.
193
- * **Graph Theory:** Edges as Tuples `(u, v)`, Nodes as Sets.
194
- * **Formal Languages:** Alphabets, Grammars, Power Sets.
152
+ 1. **Freeze-on-Hash Semantics:** To guarantee mathematical correctness, sets now transition to an **immutable state** once their `hashCode` is computed (which happens automatically when added to another `RecursiveSet` or used as a Map key).
153
+ * *Old Behavior:* Modifying a hashed set was possible but resulted in corrupted hash codes and lookup failures.
154
+ * *New Behavior:* Calling `add()`, `remove()` or `clear()` on a hashed set throws an `Error`.
155
+ * *Migration:* Use `mutableCopy()` to create a modifiable clone if you need to evolve a state that has already been stored.
195
156
 
196
157
  ---
197
158
 
@@ -203,7 +164,8 @@ Contributions are welcome!
203
164
  git clone https://github.com/cstrerath/recursive-set.git
204
165
  npm install
205
166
  npm run build
206
- npx tsx test.ts
167
+ npx tsx test/test.ts
168
+ npx tsx test/nqueens.ts
207
169
  ```
208
170
 
209
171
  ---
@@ -221,5 +183,4 @@ See [LICENSE](LICENSE) for details.
221
183
 
222
184
  Inspired by:
223
185
  * Zermelo-Fraenkel set theory (ZFC)
224
- * Formal Language Theory requirements
225
- * Powered by [functional-red-black-tree](https://github.com/mikolalysenko/functional-red-black-tree)
186
+ * Formal Language Theory requirements