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 +93 -132
- package/dist/cjs/index.js +398 -209
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.d.ts +24 -29
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +395 -203
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,37 +1,33 @@
|
|
|
1
1
|
# RecursiveSet
|
|
2
2
|
|
|
3
|
-
> High-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
> **High-Performance ZFC Set Implementation for TypeScript**
|
|
4
|
+
>
|
|
5
|
+
> Mutable, strictly typed, and optimized for cache locality.
|
|
6
6
|
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
[](https://www.npmjs.com/package/recursive-set)
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## 🚀 What is this?
|
|
13
13
|
|
|
14
|
-
|
|
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
|
-
|
|
21
|
+
---
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
## Features
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
//
|
|
47
|
+
// Sets of primitives
|
|
50
48
|
const states = new RecursiveSet<string>();
|
|
51
49
|
states.add("q0").add("q1");
|
|
52
50
|
|
|
53
|
-
//
|
|
51
|
+
// Sets of Sets (Partitioning)
|
|
54
52
|
const partition = new RecursiveSet<RecursiveSet<string>>();
|
|
55
53
|
partition.add(states); // {{q0, q1}}
|
|
56
54
|
|
|
57
|
-
//
|
|
58
|
-
const edge = new Tuple("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());
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
|
114
|
-
const
|
|
115
|
-
//
|
|
116
|
-
//
|
|
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
|
-
|
|
75
|
+
console.log(B.has(A)); // true
|
|
123
76
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
83
|
+
// Fix: Create a mutable copy ("Forking")
|
|
84
|
+
const C = A.mutableCopy();
|
|
85
|
+
C.add(3); // Works!
|
|
131
86
|
```
|
|
132
87
|
|
|
133
|
-
|
|
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
|
-
|
|
143
|
-
// If this path fails, simply revert:
|
|
144
|
-
// state = checkpoint; (conceptually)
|
|
145
|
-
```
|
|
90
|
+
## API Reference
|
|
146
91
|
|
|
147
|
-
###
|
|
92
|
+
### Constructor
|
|
148
93
|
|
|
149
94
|
```typescript
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
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
|
-
|
|
175
|
-
const s = new RecursiveSet<number>();
|
|
132
|
+
---
|
|
176
133
|
|
|
177
|
-
|
|
178
|
-
// s.add([1, 2]);
|
|
134
|
+
## Performance Notes
|
|
179
135
|
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
184
|
-
|
|
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
|
-
##
|
|
150
|
+
## Breaking Changes in v5.0
|
|
191
151
|
|
|
192
|
-
|
|
193
|
-
*
|
|
194
|
-
*
|
|
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
|