recursive-set 1.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/LICENSE +21 -0
- package/README.md +214 -0
- package/package.json +45 -0
- package/src/index.ts +344 -0
- package/test.ts +115 -0
- package/tsconfig.json +19 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Christian Strerath
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# RecursiveSet
|
|
2
|
+
|
|
3
|
+
> Mutable, recursive set implementation for TypeScript – inspired by Cantor's and ZFC set theory.
|
|
4
|
+
|
|
5
|
+
Supports arbitrary nesting, detects cycles (Foundation axiom), and includes all classic set operations (union, intersection, difference, powerset etc.).
|
|
6
|
+
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://www.npmjs.com/package/recursive-set)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
✨ **Mutable, recursive sets** with arbitrary depth
|
|
15
|
+
🔍 **Extensional equality** (two sets are equal iff their elements are equal)
|
|
16
|
+
🛡️ **Cycle detection** (Foundation Axiom): prevents self-containing sets
|
|
17
|
+
🧮 **Classic set operations**: union, intersection, difference, symmetric difference
|
|
18
|
+
📐 **Power set and Cartesian product**
|
|
19
|
+
🎯 **TypeScript generics**: works with strings, numbers, objects, states, even sets of sets
|
|
20
|
+
🤖 **Ready for FSM**, mathematical, symbolic and practical use cases
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
npm install recursive-set
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Quickstart
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
import { RecursiveSet } from "recursive-set";
|
|
39
|
+
|
|
40
|
+
const q0 = "q0", q1 = "q1";
|
|
41
|
+
const eqClass = new RecursiveSet(q0, q1); // {q0, q1}
|
|
42
|
+
const classes = new RecursiveSet(eqClass);
|
|
43
|
+
|
|
44
|
+
classes.add(new RecursiveSet("q2", "q3")); // {{q0, q1}, {q2, q3}}
|
|
45
|
+
|
|
46
|
+
console.log(classes.toString()); // {{q0, q1}, {q2, q3}}
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## API Reference
|
|
53
|
+
|
|
54
|
+
### Constructor
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
new RecursiveSet<T>(...elements: Array<T | RecursiveSet<T>>)
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Methods
|
|
63
|
+
|
|
64
|
+
**Mutation:**
|
|
65
|
+
- `add(element: T | RecursiveSet<T>): this` – Add element (chainable)
|
|
66
|
+
- `remove(element: T | RecursiveSet<T>): this` – Remove element (chainable)
|
|
67
|
+
- `clear(): this` – Remove all elements (chainable)
|
|
68
|
+
|
|
69
|
+
**Set Operations:**
|
|
70
|
+
- `union(other: RecursiveSet<T>): RecursiveSet<T>` – A ∪ B
|
|
71
|
+
- `intersection(other: RecursiveSet<T>): RecursiveSet<T>` – A ∩ B
|
|
72
|
+
- `difference(other: RecursiveSet<T>): RecursiveSet<T>` – A \ B
|
|
73
|
+
- `symmetricDifference(other: RecursiveSet<T>): RecursiveSet<T>` – A △ B
|
|
74
|
+
|
|
75
|
+
**Advanced Operations:**
|
|
76
|
+
- `powerset(): RecursiveSet<RecursiveSet<T>>` – 𝒫(A)
|
|
77
|
+
- `cartesianProduct<U>(other: RecursiveSet<U>): RecursiveSet<RecursiveSet<T | U>>` – A × B
|
|
78
|
+
|
|
79
|
+
**Predicates:**
|
|
80
|
+
- `has(element: T | RecursiveSet<T>): boolean` – Check membership
|
|
81
|
+
- `isSubset(other: RecursiveSet<T>): boolean` – Check if ⊆
|
|
82
|
+
- `isSuperset(other: RecursiveSet<T>): boolean` – Check if ⊇
|
|
83
|
+
- `equals(other: RecursiveSet<T>): boolean` – Structural equality
|
|
84
|
+
- `isEmpty(): boolean` – Check if set is empty
|
|
85
|
+
|
|
86
|
+
**Properties:**
|
|
87
|
+
- `size: number` – Cardinality |A|
|
|
88
|
+
- `toString(): string` – Pretty print with ∅ and {}
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Examples
|
|
93
|
+
|
|
94
|
+
### Basic Usage
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
const s1 = new RecursiveSet(1, 2, 3);
|
|
99
|
+
const s2 = new RecursiveSet(2, 3, 4);
|
|
100
|
+
|
|
101
|
+
console.log(s1.union(s2)); // {1, 2, 3, 4}
|
|
102
|
+
console.log(s1.intersection(s2)); // {2, 3}
|
|
103
|
+
console.log(s1.difference(s2)); // {1}
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### FSM Equivalence Classes
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
const eq1 = new RecursiveSet("q0", "q1");
|
|
112
|
+
const eq2 = new RecursiveSet("q2", "q3");
|
|
113
|
+
const eqClasses = new RecursiveSet(eq1, eq2);
|
|
114
|
+
|
|
115
|
+
console.log(eqClasses.toString()); // {{q0, q1}, {q2, q3}}
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Power Set
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
const set = new RecursiveSet(1, 2);
|
|
124
|
+
const power = set.powerset();
|
|
125
|
+
|
|
126
|
+
console.log(power.toString()); // {∅, {1}, {2}, {1, 2}}
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Foundation Axiom (Cycle Detection)
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
const s = new RecursiveSet(1, 2);
|
|
135
|
+
try {
|
|
136
|
+
s.add(s); // ❌ Throws error
|
|
137
|
+
} catch (e) {
|
|
138
|
+
console.error(e.message); // "Foundation axiom violated..."
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Method Chaining
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
const set = new RecursiveSet(1, 2)
|
|
148
|
+
.add(3)
|
|
149
|
+
.add(4)
|
|
150
|
+
.remove(1);
|
|
151
|
+
|
|
152
|
+
console.log(set); // {2, 3, 4}
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Use Cases
|
|
159
|
+
|
|
160
|
+
- **Finite State Machine (FSM) minimization**: Equivalence classes of states
|
|
161
|
+
- **Set theory algorithms**: Implement mathematical proofs and algorithms
|
|
162
|
+
- **Graph algorithms**: Represent node sets and partitions
|
|
163
|
+
- **Compiler design**: Symbol tables, scope analysis
|
|
164
|
+
- **Type systems**: Type inference and unification
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Development
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# Clone repository
|
|
174
|
+
|
|
175
|
+
git clone https://github.com/<USERNAME>/recursive-set.git
|
|
176
|
+
cd recursive-set
|
|
177
|
+
|
|
178
|
+
# Install dependencies
|
|
179
|
+
|
|
180
|
+
npm install
|
|
181
|
+
|
|
182
|
+
# Build
|
|
183
|
+
|
|
184
|
+
npm run build
|
|
185
|
+
|
|
186
|
+
# Run tests
|
|
187
|
+
|
|
188
|
+
npx tsx test.ts
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Contributing
|
|
195
|
+
|
|
196
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT License
|
|
203
|
+
© 2025 Christian Strerath
|
|
204
|
+
|
|
205
|
+
See [LICENSE](LICENSE) for details.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Acknowledgments
|
|
210
|
+
|
|
211
|
+
Inspired by:
|
|
212
|
+
- Cantor's set theory
|
|
213
|
+
- Zermelo-Fraenkel set theory with the Axiom of Choice (ZFC)
|
|
214
|
+
- Practical needs in FSM algorithms and formal language theory
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "recursive-set",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Mutable recursive sets with ZFC axioms for TypeScript",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"watch": "tsc --watch",
|
|
17
|
+
"clean": "rm -rf dist",
|
|
18
|
+
"test": "tsx test.ts"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"set-theory",
|
|
22
|
+
"zfc",
|
|
23
|
+
"cantor",
|
|
24
|
+
"recursive",
|
|
25
|
+
"data-structure",
|
|
26
|
+
"typescript",
|
|
27
|
+
"fsm",
|
|
28
|
+
"formal-languages"
|
|
29
|
+
],
|
|
30
|
+
"author": "Christian Strerath",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/<USERNAME>/recursive-set.git"
|
|
35
|
+
},
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/<USERNAME>/recursive-set/issues"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/<USERNAME>/recursive-set#readme",
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^20.0.0",
|
|
42
|
+
"tsx": "^4.20.6",
|
|
43
|
+
"typescript": "^5.6.0"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module recursive-set
|
|
3
|
+
* A mutable recursive set implementation enforcing Cantor's ZFC axioms
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* RecursiveSet: Mutable set with arbitrary nesting depth
|
|
8
|
+
*
|
|
9
|
+
* Enforced ZFC Axioms (as class invariants):
|
|
10
|
+
* - Extensionality: Sets with same elements are equal
|
|
11
|
+
* - Foundation (Regularity): No membership cycles allowed
|
|
12
|
+
* - Power Set: Can construct 𝒫(A) for any set A
|
|
13
|
+
* - Union: Can construct A ∪ B for any sets A, B
|
|
14
|
+
* - Pairing: Can construct {a, b} for any elements a, b
|
|
15
|
+
*/
|
|
16
|
+
export class RecursiveSet<T = any> {
|
|
17
|
+
private _elements: Set<T | RecursiveSet<T>>;
|
|
18
|
+
|
|
19
|
+
constructor(...elements: Array<T | RecursiveSet<T>>) {
|
|
20
|
+
this._elements = new Set();
|
|
21
|
+
for (const el of elements) {
|
|
22
|
+
this._addElement(el);
|
|
23
|
+
}
|
|
24
|
+
this._checkInvariants();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Internal: Add element with cycle detection (Foundation axiom)
|
|
29
|
+
*/
|
|
30
|
+
private _addElement(el: T | RecursiveSet<T>): void {
|
|
31
|
+
if (el instanceof RecursiveSet) {
|
|
32
|
+
if (this._wouldCreateCycle(el)) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
"Foundation axiom violated: adding this element would create a membership cycle"
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
this._elements.add(el);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check if adding element would violate Foundation axiom
|
|
43
|
+
*/
|
|
44
|
+
private _wouldCreateCycle(element: RecursiveSet<T>): boolean {
|
|
45
|
+
const visited = new Set<RecursiveSet<any>>();
|
|
46
|
+
const toCheck: RecursiveSet<any>[] = [element];
|
|
47
|
+
|
|
48
|
+
while (toCheck.length > 0) {
|
|
49
|
+
const current = toCheck.pop()!;
|
|
50
|
+
if (current === this) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if (visited.has(current)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
visited.add(current);
|
|
57
|
+
|
|
58
|
+
for (const el of current._elements) {
|
|
59
|
+
if (el instanceof RecursiveSet) {
|
|
60
|
+
toCheck.push(el);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Verify class invariants (Design by Contract)
|
|
69
|
+
*/
|
|
70
|
+
private _checkInvariants(): void {
|
|
71
|
+
// Extensionality: enforced by Set semantics
|
|
72
|
+
// Foundation: enforced by _wouldCreateCycle
|
|
73
|
+
// Well-definedness: enforced by TypeScript type system
|
|
74
|
+
|
|
75
|
+
// Additional runtime checks can be added here
|
|
76
|
+
if (process.env.NODE_ENV === 'development') {
|
|
77
|
+
// More expensive checks only in development
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// === Mutable Operations ===
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Add element to this set (Pairing axiom)
|
|
85
|
+
* @returns this for method chaining
|
|
86
|
+
*/
|
|
87
|
+
add(element: T | RecursiveSet<T>): this {
|
|
88
|
+
this._addElement(element);
|
|
89
|
+
this._checkInvariants();
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Remove element from this set
|
|
95
|
+
* @returns this for method chaining
|
|
96
|
+
*/
|
|
97
|
+
remove(element: T | RecursiveSet<T>): this {
|
|
98
|
+
this._elements.delete(element);
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Remove all elements
|
|
104
|
+
* @returns this for method chaining
|
|
105
|
+
*/
|
|
106
|
+
clear(): this {
|
|
107
|
+
this._elements.clear();
|
|
108
|
+
return this;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// === Immutable Operations (return new sets) ===
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Union of two sets (Union axiom)
|
|
115
|
+
* Returns A ∪ B
|
|
116
|
+
*/
|
|
117
|
+
union(other: RecursiveSet<T>): RecursiveSet<T> {
|
|
118
|
+
const result = new RecursiveSet<T>();
|
|
119
|
+
result._elements = new Set([...this._elements, ...other._elements]);
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Intersection of two sets
|
|
125
|
+
* Returns A ∩ B
|
|
126
|
+
*/
|
|
127
|
+
intersection(other: RecursiveSet<T>): RecursiveSet<T> {
|
|
128
|
+
const result = new RecursiveSet<T>();
|
|
129
|
+
for (const el of this._elements) {
|
|
130
|
+
if (other.has(el)) {
|
|
131
|
+
result._elements.add(el);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Set difference
|
|
139
|
+
* Returns A \ B (elements in A but not in B)
|
|
140
|
+
*/
|
|
141
|
+
difference(other: RecursiveSet<T>): RecursiveSet<T> {
|
|
142
|
+
const result = new RecursiveSet<T>();
|
|
143
|
+
for (const el of this._elements) {
|
|
144
|
+
if (!other.has(el)) {
|
|
145
|
+
result._elements.add(el);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Symmetric difference
|
|
153
|
+
* Returns A △ B (elements in either but not both)
|
|
154
|
+
*/
|
|
155
|
+
symmetricDifference(other: RecursiveSet<T>): RecursiveSet<T> {
|
|
156
|
+
return this.union(other).difference(this.intersection(other));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Power set construction (Power Set axiom)
|
|
161
|
+
* Returns 𝒫(A) - set of all subsets
|
|
162
|
+
*/
|
|
163
|
+
powerset(): RecursiveSet<RecursiveSet<T>> {
|
|
164
|
+
const elements = Array.from(this._elements);
|
|
165
|
+
const subsets: RecursiveSet<T>[] = [];
|
|
166
|
+
|
|
167
|
+
// Generate all 2^n subsets
|
|
168
|
+
const n = elements.length;
|
|
169
|
+
for (let i = 0; i < (1 << n); i++) {
|
|
170
|
+
const subset = new RecursiveSet<T>();
|
|
171
|
+
for (let j = 0; j < n; j++) {
|
|
172
|
+
if (i & (1 << j)) {
|
|
173
|
+
subset._elements.add(elements[j]);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
subsets.push(subset);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return new RecursiveSet<RecursiveSet<T>>(...subsets);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Cartesian product
|
|
184
|
+
* Returns A × B as set of ordered pairs {{a}, {a,b}}
|
|
185
|
+
*/
|
|
186
|
+
cartesianProduct<U>(other: RecursiveSet<U>): RecursiveSet<RecursiveSet<T | U>> {
|
|
187
|
+
const pairs: RecursiveSet<T | U>[] = [];
|
|
188
|
+
|
|
189
|
+
for (const x of this._elements) {
|
|
190
|
+
for (const y of other._elements) {
|
|
191
|
+
// Kuratowski ordered pair: (x,y) := {{x}, {x,y}}
|
|
192
|
+
const pair = new RecursiveSet<T | U>(
|
|
193
|
+
new RecursiveSet<T | U>(x),
|
|
194
|
+
new RecursiveSet<T | U>(x, y)
|
|
195
|
+
);
|
|
196
|
+
pairs.push(pair);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return new RecursiveSet<RecursiveSet<T | U>>(...pairs);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// === Predicates ===
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Check membership (∈)
|
|
207
|
+
*/
|
|
208
|
+
has(element: T | RecursiveSet<T>): boolean {
|
|
209
|
+
return this._elements.has(element);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Check if subset (⊆)
|
|
214
|
+
*/
|
|
215
|
+
isSubset(other: RecursiveSet<T>): boolean {
|
|
216
|
+
for (const el of this._elements) {
|
|
217
|
+
if (!other.has(el)) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Check if superset (⊇)
|
|
226
|
+
*/
|
|
227
|
+
isSuperset(other: RecursiveSet<T>): boolean {
|
|
228
|
+
return other.isSubset(this);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Check if proper subset (⊂)
|
|
233
|
+
*/
|
|
234
|
+
isProperSubset(other: RecursiveSet<T>): boolean {
|
|
235
|
+
return this.isSubset(other) && !this.equals(other);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Check if empty set
|
|
240
|
+
*/
|
|
241
|
+
isEmpty(): boolean {
|
|
242
|
+
return this._elements.size === 0;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// === Extensionality (Equality) ===
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Structural equality (Extensionality axiom)
|
|
249
|
+
* Two sets are equal iff they have the same elements
|
|
250
|
+
*/
|
|
251
|
+
equals(other: RecursiveSet<T>): boolean {
|
|
252
|
+
if (this._elements.size !== other._elements.size) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
for (const el of this._elements) {
|
|
257
|
+
if (el instanceof RecursiveSet) {
|
|
258
|
+
// Deep comparison for nested sets
|
|
259
|
+
let found = false;
|
|
260
|
+
for (const otherEl of other._elements) {
|
|
261
|
+
if (otherEl instanceof RecursiveSet && el.equals(otherEl)) {
|
|
262
|
+
found = true;
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (!found) return false;
|
|
267
|
+
} else {
|
|
268
|
+
if (!other.has(el)) return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// === Utility ===
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Cardinality |A|
|
|
278
|
+
*/
|
|
279
|
+
get size(): number {
|
|
280
|
+
return this._elements.size;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Convert to native Set (shallow)
|
|
285
|
+
*/
|
|
286
|
+
toSet(): Set<T | RecursiveSet<T>> {
|
|
287
|
+
return new Set(this._elements);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Iterate over elements
|
|
292
|
+
*/
|
|
293
|
+
*[Symbol.iterator](): Iterator<T | RecursiveSet<T>> {
|
|
294
|
+
yield* this._elements;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Pretty print with mathematical notation
|
|
299
|
+
*/
|
|
300
|
+
toString(): string {
|
|
301
|
+
if (this.isEmpty()) {
|
|
302
|
+
return "∅";
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const elements = Array.from(this._elements).map(el => {
|
|
306
|
+
if (el instanceof RecursiveSet) {
|
|
307
|
+
return el.toString();
|
|
308
|
+
}
|
|
309
|
+
return String(el);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
return `{${elements.join(", ")}}`;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* For console.log
|
|
317
|
+
*/
|
|
318
|
+
[Symbol.for('nodejs.util.inspect.custom')](): string {
|
|
319
|
+
return this.toString();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// === Helper Functions ===
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Create empty set (Null Set axiom)
|
|
327
|
+
*/
|
|
328
|
+
export function emptySet<T = any>(): RecursiveSet<T> {
|
|
329
|
+
return new RecursiveSet<T>();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Create singleton set
|
|
334
|
+
*/
|
|
335
|
+
export function singleton<T>(element: T): RecursiveSet<T> {
|
|
336
|
+
return new RecursiveSet<T>(element);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Create set from iterable
|
|
341
|
+
*/
|
|
342
|
+
export function fromIterable<T>(iterable: Iterable<T>): RecursiveSet<T> {
|
|
343
|
+
return new RecursiveSet<T>(...iterable);
|
|
344
|
+
}
|
package/test.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { RecursiveSet } from './src/index.js';
|
|
2
|
+
|
|
3
|
+
console.log('=== RecursiveSet Test Suite ===\n');
|
|
4
|
+
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Test 1: Basic FSM State Sets
|
|
7
|
+
// ============================================================================
|
|
8
|
+
console.log('--- Test 1: Basic State Sets ---');
|
|
9
|
+
const q0 = "q0";
|
|
10
|
+
const q1 = "q1";
|
|
11
|
+
const q2 = "q2";
|
|
12
|
+
const q3 = "q3";
|
|
13
|
+
|
|
14
|
+
const eqClass1 = new RecursiveSet(q0, q1);
|
|
15
|
+
const eqClass2 = new RecursiveSet(q2, q3);
|
|
16
|
+
console.log(`Equivalence class 1: ${eqClass1}`);
|
|
17
|
+
console.log(`Equivalence class 2: ${eqClass2}`);
|
|
18
|
+
console.log();
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Test 2: Sets of Sets (Equivalence Classes)
|
|
22
|
+
// ============================================================================
|
|
23
|
+
console.log('--- Test 2: Sets of Sets ---');
|
|
24
|
+
const eqClasses = new RecursiveSet(eqClass1, eqClass2);
|
|
25
|
+
console.log(`Equivalence classes: ${eqClasses}`);
|
|
26
|
+
console.log();
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Test 3: Mutability - Adding Elements
|
|
30
|
+
// ============================================================================
|
|
31
|
+
console.log('--- Test 3: Mutability ---');
|
|
32
|
+
const eqClass3 = new RecursiveSet("q4");
|
|
33
|
+
eqClasses.add(eqClass3);
|
|
34
|
+
console.log(`After adding {q4}: ${eqClasses}`);
|
|
35
|
+
console.log();
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Test 4: Extensionality (Structural Equality)
|
|
39
|
+
// ============================================================================
|
|
40
|
+
console.log('--- Test 4: Extensionality Axiom ---');
|
|
41
|
+
const eqClass1_copy = new RecursiveSet("q0", "q1");
|
|
42
|
+
console.log(`eqClass1: ${eqClass1}`);
|
|
43
|
+
console.log(`eqClass1_copy: ${eqClass1_copy}`);
|
|
44
|
+
console.log(`Are they equal? ${eqClass1.equals(eqClass1_copy)}`);
|
|
45
|
+
console.log();
|
|
46
|
+
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Test 5: Empty Set Handling
|
|
49
|
+
// ============================================================================
|
|
50
|
+
console.log('--- Test 5: Empty Set ---');
|
|
51
|
+
const emptyClass = new RecursiveSet();
|
|
52
|
+
const eqClassesWithEmpty = new RecursiveSet(eqClass1, emptyClass);
|
|
53
|
+
console.log(`With empty class: ${eqClassesWithEmpty}`);
|
|
54
|
+
console.log();
|
|
55
|
+
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Test 6: Deep Nesting
|
|
58
|
+
// ============================================================================
|
|
59
|
+
console.log('--- Test 6: Deep Nesting ---');
|
|
60
|
+
const nested = new RecursiveSet(eqClasses, eqClassesWithEmpty);
|
|
61
|
+
console.log(`Nested structure: ${nested}`);
|
|
62
|
+
console.log();
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// Test 7: Set Operations
|
|
66
|
+
// ============================================================================
|
|
67
|
+
console.log('--- Test 7: Set Operations ---');
|
|
68
|
+
const difference = eqClasses.difference(new RecursiveSet(eqClass2));
|
|
69
|
+
console.log(`eqClasses - {eqClass2} = ${difference}`);
|
|
70
|
+
|
|
71
|
+
const union = eqClass1.union(eqClass2);
|
|
72
|
+
console.log(`eqClass1 ∪ eqClass2 = ${union}`);
|
|
73
|
+
|
|
74
|
+
const intersection = new RecursiveSet(q0, q1, q2).intersection(new RecursiveSet(q1, q2, q3));
|
|
75
|
+
console.log(`{q0, q1, q2} ∩ {q1, q2, q3} = ${intersection}`);
|
|
76
|
+
console.log();
|
|
77
|
+
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// Test 8: Power Set
|
|
80
|
+
// ============================================================================
|
|
81
|
+
console.log('--- Test 8: Power Set ---');
|
|
82
|
+
const small = new RecursiveSet(1, 2);
|
|
83
|
+
const power = small.powerset();
|
|
84
|
+
console.log(`𝒫({1, 2}) = ${power}`);
|
|
85
|
+
console.log();
|
|
86
|
+
|
|
87
|
+
// ============================================================================
|
|
88
|
+
// Test 9: Foundation Axiom (Cycle Detection)
|
|
89
|
+
// ============================================================================
|
|
90
|
+
console.log('--- Test 9: Foundation Axiom ---');
|
|
91
|
+
try {
|
|
92
|
+
const circular = new RecursiveSet(1, 2);
|
|
93
|
+
circular.add(circular);
|
|
94
|
+
console.log('❌ ERROR: Cycle should have been detected!');
|
|
95
|
+
} catch (e: any) {
|
|
96
|
+
console.log(`✓ Cycle detected: ${e.message}`);
|
|
97
|
+
}
|
|
98
|
+
console.log();
|
|
99
|
+
|
|
100
|
+
// ============================================================================
|
|
101
|
+
// Test 10: Subset Relations
|
|
102
|
+
// ============================================================================
|
|
103
|
+
console.log('--- Test 10: Subset Relations ---');
|
|
104
|
+
const setA = new RecursiveSet(1, 2, 3);
|
|
105
|
+
const setB = new RecursiveSet(1, 2);
|
|
106
|
+
console.log(`A = ${setA}`);
|
|
107
|
+
console.log(`B = ${setB}`);
|
|
108
|
+
console.log(`B ⊆ A? ${setB.isSubset(setA)}`);
|
|
109
|
+
console.log(`A ⊆ B? ${setA.isSubset(setB)}`);
|
|
110
|
+
console.log();
|
|
111
|
+
|
|
112
|
+
// ============================================================================
|
|
113
|
+
// Summary
|
|
114
|
+
// ============================================================================
|
|
115
|
+
console.log('=== All Tests Completed Successfully ✓ ===');
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022", /* Modern JavaScript */
|
|
4
|
+
"module": "ES2022", /* ESM modules */
|
|
5
|
+
"lib": ["ES2022"], /* Available APIs */
|
|
6
|
+
"outDir": "./dist", /* Compiled JS output directory */
|
|
7
|
+
"rootDir": "./src", /* TypeScript source files */
|
|
8
|
+
"declaration": true, /* Generate .d.ts type files */
|
|
9
|
+
"declarationMap": true, /* Source maps for types */
|
|
10
|
+
"sourceMap": true, /* Source maps for debugging */
|
|
11
|
+
"strict": true, /* Enable all strict checks */
|
|
12
|
+
"esModuleInterop": true, /* Better module compatibility */
|
|
13
|
+
"skipLibCheck": true, /* Faster compilation */
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"moduleResolution": "node" /* Node.js module resolution */
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"], /* Files to compile */
|
|
18
|
+
"exclude": ["node_modules", "dist"] /* Files to ignore */
|
|
19
|
+
}
|