recursive-set 5.0.2 → 6.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 +74 -99
- package/dist/cjs/index.js +262 -135
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.d.ts +57 -9
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +262 -135
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,35 +1,19 @@
|
|
|
1
1
|
# RecursiveSet
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
>
|
|
5
|
-
> Mutable, strictly typed, and optimized for cache locality.
|
|
3
|
+
High-performance, strictly typed set implementation for TypeScript with **value semantics** (structural equality) and controlled mutability via “freeze-on-hash”.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
[](https://www.npmjs.com/package/recursive-set)
|
|
5
|
+
## Overview
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
## 🚀 What is this?
|
|
13
|
-
|
|
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.
|
|
15
|
-
|
|
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.
|
|
7
|
+
`RecursiveSet` is a mathematical set designed for workloads in theoretical computer science (e.g., SAT solvers, graph algorithms, ZFC-style constructions) where deep nesting and structural equality matter (e.g., `{1,2} = {2,1}`).
|
|
20
8
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## Features
|
|
9
|
+
Key design points:
|
|
24
10
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
---
|
|
11
|
+
- Structural equality (ZFC-like semantics) for nested sets and sequences.
|
|
12
|
+
- Mutable during construction; becomes immutable once hashed (“freeze-on-hash”).
|
|
13
|
+
- Sorted-array backing for good cache locality on small to medium `N`.
|
|
14
|
+
- Deterministic hashing:
|
|
15
|
+
- Numbers use safe-integer splitting and IEEE-754 bit hashing.
|
|
16
|
+
- Float hashing enforces little-endian byte order via `DataView` for platform consistency.
|
|
33
17
|
|
|
34
18
|
## Installation
|
|
35
19
|
|
|
@@ -64,101 +48,102 @@ console.log(partition.toString()); // {{q0, q1}}
|
|
|
64
48
|
|
|
65
49
|
### 2. The Lifecycle (Mutable -> Frozen)
|
|
66
50
|
|
|
67
|
-
|
|
51
|
+
Accessing `hashCode` (directly or indirectly by inserting into another set) freezes the set to prevent hash corruption.
|
|
68
52
|
|
|
69
53
|
```typescript
|
|
70
54
|
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.
|
|
55
|
+
const B = new RecursiveSet(A); // hashing B may hash A -> A becomes frozen
|
|
74
56
|
|
|
75
57
|
console.log(B.has(A)); // true
|
|
76
58
|
|
|
77
59
|
try {
|
|
78
|
-
|
|
79
|
-
} catch
|
|
80
|
-
|
|
60
|
+
A.add(3); // throws
|
|
61
|
+
} catch {
|
|
62
|
+
console.log("A is frozen and cannot be mutated.");
|
|
81
63
|
}
|
|
82
64
|
|
|
83
|
-
//
|
|
65
|
+
// “Fork” for mutation
|
|
84
66
|
const C = A.mutableCopy();
|
|
85
|
-
C.add(3);
|
|
67
|
+
C.add(3);
|
|
86
68
|
```
|
|
87
69
|
|
|
88
70
|
---
|
|
89
71
|
|
|
90
|
-
##
|
|
72
|
+
## Supported element types
|
|
91
73
|
|
|
92
|
-
|
|
74
|
+
To keep value semantics predictable and prevent accidental mutation via arbitrary objects, `RecursiveSet` validates inputs and supports:
|
|
93
75
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
76
|
+
- `number` (excluding `NaN`)
|
|
77
|
+
- `string`
|
|
78
|
+
- `Tuple`
|
|
79
|
+
- plain `Array` (treated as an ordered sequence)
|
|
80
|
+
- `RecursiveSet`
|
|
99
81
|
|
|
82
|
+
## Tuple vs Array
|
|
100
83
|
|
|
101
|
-
|
|
84
|
+
- `Tuple` is an immutable container: it makes a defensive copy and freezes its internal storage via `Object.freeze()` (shallow immutability).
|
|
85
|
+
- Plain `Array` values are supported for performance and convenience, but they are not frozen by the library.
|
|
102
86
|
|
|
103
|
-
|
|
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.
|
|
87
|
+
Recommendation for SAT / hot loops: represent frequently compared “small composite values” as `Tuple` to benefit from cached hashing and immutability.
|
|
106
88
|
|
|
107
|
-
|
|
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.
|
|
89
|
+
---
|
|
111
90
|
|
|
112
|
-
|
|
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$
|
|
91
|
+
## API (selected)
|
|
119
92
|
|
|
120
|
-
|
|
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$.
|
|
93
|
+
### Construction
|
|
126
94
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
95
|
+
```typescript
|
|
96
|
+
new RecursiveSet<T>(...elements: T[])
|
|
97
|
+
```
|
|
98
|
+
Elements are sorted and deduplicated on construction.
|
|
131
99
|
|
|
132
|
-
|
|
100
|
+
### Mutation (only while unfrozen)
|
|
133
101
|
|
|
134
|
-
|
|
102
|
+
- `add(element: T): this`
|
|
103
|
+
- `remove(element: T): this`
|
|
104
|
+
- `clear(): this`
|
|
135
105
|
|
|
136
|
-
|
|
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**.
|
|
106
|
+
### Copying
|
|
138
107
|
|
|
139
|
-
|
|
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 |
|
|
108
|
+
- `mutableCopy(): RecursiveSet<T>` – mutable shallow copy (use after freezing)
|
|
109
|
+
- `clone(): RecursiveSet<T>` – alias for `mutableCopy()`
|
|
145
110
|
|
|
146
|
-
|
|
111
|
+
### Set operations (return new sets)
|
|
147
112
|
|
|
148
|
-
|
|
113
|
+
- `union(other): RecursiveSet<T>`
|
|
114
|
+
- `intersection(other): RecursiveSet<T>`
|
|
115
|
+
- `difference(other): RecursiveSet<T>`
|
|
116
|
+
- `symmetricDifference(other): RecursiveSet<T>`
|
|
117
|
+
- `powerset(): RecursiveSet<RecursiveSet<T>>` (guarded; throws if too large)
|
|
118
|
+
- `cartesianProduct<U>(other): RecursiveSet<Tuple<[T, U]>>`
|
|
149
119
|
|
|
150
|
-
|
|
120
|
+
### Predicates & properties
|
|
151
121
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
122
|
+
- `has(element: T): boolean` – binary search for larger sets
|
|
123
|
+
- `equals(other: RecursiveSet<T>): boolean`
|
|
124
|
+
- `isSubset(other): boolean`
|
|
125
|
+
- `isSuperset(other): boolean`
|
|
126
|
+
- `isEmpty(): boolean`
|
|
127
|
+
- `size: number`
|
|
128
|
+
- `hashCode: number` – computes and caches hash; freezes the set
|
|
129
|
+
- `isFrozen: boolean`
|
|
156
130
|
|
|
157
|
-
|
|
131
|
+
## Determinism & ordering rules
|
|
158
132
|
|
|
159
|
-
|
|
133
|
+
The internal ordering is deterministic across platforms:
|
|
134
|
+
|
|
135
|
+
- Type order: `number` < `string` < sequence (`Array`/`Tuple`) < `RecursiveSet`.
|
|
136
|
+
- Sequences compare lexicographically (then by length).
|
|
137
|
+
- Sets compare by cached hash first, then by structural comparison on collision.
|
|
138
|
+
|
|
139
|
+
## Breaking changes in v6
|
|
140
|
+
|
|
141
|
+
- Internal storage uses private class fields (no external access to internal arrays).
|
|
142
|
+
- Hashing uses `DataView` little-endian float hashing; hashes are not compatible with older versions.
|
|
143
|
+
- `Tuple` is immutable via defensive copy + `Object.freeze()` (shallow).
|
|
144
|
+
- Comparator type ordering is now deterministic: number < string < sequence < set.
|
|
160
145
|
|
|
161
|
-
|
|
146
|
+
## Contributing
|
|
162
147
|
|
|
163
148
|
```bash
|
|
164
149
|
git clone https://github.com/cstrerath/recursive-set.git
|
|
@@ -170,17 +155,7 @@ npx tsx test/nqueens.ts
|
|
|
170
155
|
|
|
171
156
|
---
|
|
172
157
|
|
|
173
|
-
## License
|
|
174
|
-
|
|
175
|
-
MIT License
|
|
176
|
-
© 2025 Christian Strerath
|
|
177
|
-
|
|
178
|
-
See [LICENSE](LICENSE) for details.
|
|
179
158
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
## Acknowledgments
|
|
159
|
+
## License
|
|
183
160
|
|
|
184
|
-
|
|
185
|
-
* Zermelo-Fraenkel set theory (ZFC)
|
|
186
|
-
* Formal Language Theory requirements
|
|
161
|
+
MIT License © 2025 Christian Strerath. See `LICENSE`.
|