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 CHANGED
@@ -1,35 +1,19 @@
1
1
  # RecursiveSet
2
2
 
3
- > **High-Performance ZFC Set Implementation for TypeScript**
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
- [![MIT License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
8
- [![npm version](https://img.shields.io/npm/v/recursive-set.svg)](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
- * **🔢 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.
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
- **New in v5:** To ensure mathematical correctness, a set cannot be modified once it has been hashed (e.g., added to another set).
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
- A.add(3); // 💥 Throws Error: Cannot add() to a frozen RecursiveSet
79
- } catch (e) {
80
- console.log("A is immutable now!");
60
+ A.add(3); // throws
61
+ } catch {
62
+ console.log("A is frozen and cannot be mutated.");
81
63
  }
82
64
 
83
- // Fix: Create a mutable copy ("Forking")
65
+ // “Fork” for mutation
84
66
  const C = A.mutableCopy();
85
- C.add(3); // Works!
67
+ C.add(3);
86
68
  ```
87
69
 
88
70
  ---
89
71
 
90
- ## API Reference
72
+ ## Supported element types
91
73
 
92
- ### Constructor
74
+ To keep value semantics predictable and prevent accidental mutation via arbitrary objects, `RecursiveSet` validates inputs and supports:
93
75
 
94
- ```typescript
95
- // Create empty or with initial elements
96
- // Elements are automatically sorted and deduplicated.
97
- new RecursiveSet<T>(...elements: T[])
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
- ### Methods
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
- **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.
87
+ Recommendation for SAT / hot loops: represent frequently compared “small composite values” as `Tuple` to benefit from cached hashing and immutability.
106
88
 
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.
89
+ ---
111
90
 
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$
91
+ ## API (selected)
119
92
 
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$.
93
+ ### Construction
126
94
 
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.
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
- ## Performance Notes
102
+ - `add(element: T): this`
103
+ - `remove(element: T): this`
104
+ - `clear(): this`
135
105
 
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**.
106
+ ### Copying
138
107
 
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 |
108
+ - `mutableCopy(): RecursiveSet<T>` mutable shallow copy (use after freezing)
109
+ - `clone(): RecursiveSet<T>` alias for `mutableCopy()`
145
110
 
146
- *\*Equality is O(1) if hashes differ (99% case), O(N) if hash collision occurs.*
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
- ## Breaking Changes in v5.0
120
+ ### Predicates & properties
151
121
 
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.
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
- ## Contributing
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
- Contributions are welcome!
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
- Inspired by:
185
- * Zermelo-Fraenkel set theory (ZFC)
186
- * Formal Language Theory requirements
161
+ MIT License © 2025 Christian Strerath. See `LICENSE`.