pimath 0.1.39 → 0.1.40

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.
Files changed (65) hide show
  1. package/dist/pimath.js +188 -159
  2. package/dist/pimath.js.map +1 -1
  3. package/package.json +4 -2
  4. package/src/algebra/equation.ts +556 -0
  5. package/src/algebra/equationSolver.ts +539 -0
  6. package/src/algebra/factor.ts +339 -0
  7. package/src/algebra/index.ts +11 -0
  8. package/src/algebra/linearSystem.ts +388 -0
  9. package/src/algebra/logicalset.ts +256 -0
  10. package/src/algebra/matrix.ts +474 -0
  11. package/src/algebra/monom.ts +1015 -0
  12. package/src/algebra/operations.ts +24 -0
  13. package/src/algebra/polyFactor.ts +668 -0
  14. package/src/algebra/polynom.ts +1394 -0
  15. package/src/analyze/solution.ts +115 -0
  16. package/src/analyze/tableOfSigns.ts +30 -0
  17. package/src/coefficients/fraction.ts +678 -0
  18. package/src/coefficients/index.ts +4 -0
  19. package/src/coefficients/nthRoot.ts +149 -0
  20. package/src/coefficients/root.ts +299 -0
  21. package/src/geometry/circle.ts +386 -0
  22. package/src/geometry/geomMath.ts +70 -0
  23. package/src/geometry/index.ts +10 -0
  24. package/src/geometry/line.ts +677 -0
  25. package/src/geometry/line3.ts +206 -0
  26. package/src/geometry/plane3.ts +170 -0
  27. package/src/geometry/point.ts +66 -0
  28. package/src/geometry/sphere3.ts +214 -0
  29. package/src/geometry/triangle.ts +354 -0
  30. package/src/geometry/vector.ts +341 -0
  31. package/src/helpers.ts +35 -0
  32. package/src/index.ts +60 -0
  33. package/src/numeric.ts +199 -0
  34. package/src/pimath.interface.ts +160 -0
  35. package/src/randomization/algebra/rndEquation.ts +41 -0
  36. package/src/randomization/algebra/rndMonom.ts +39 -0
  37. package/src/randomization/algebra/rndPolynom.ts +86 -0
  38. package/src/randomization/coefficient/rndFraction.ts +38 -0
  39. package/src/randomization/geometry/rndCircle.ts +27 -0
  40. package/src/randomization/geometry/rndLine.ts +37 -0
  41. package/src/randomization/geometry/rndLine3.ts +27 -0
  42. package/src/randomization/geometry/rndVector.ts +63 -0
  43. package/src/randomization/random.ts +91 -0
  44. package/src/randomization/rndHelpers.ts +102 -0
  45. package/src/randomization/rndTypes.ts +63 -0
  46. package/types/algebra/equationSolver.d.ts +3 -0
  47. package/types/algebra/equationSolver.d.ts.map +1 -1
  48. package/types/algebra/polyFactor.d.ts +5 -0
  49. package/types/algebra/polyFactor.d.ts.map +1 -1
  50. package/types/analyze/solution.d.ts +21 -0
  51. package/types/analyze/solution.d.ts.map +1 -0
  52. package/types/analyze/tableOfSigns.d.ts +9 -0
  53. package/types/analyze/tableOfSigns.d.ts.map +1 -0
  54. package/types/coefficients/root.d.ts +38 -0
  55. package/types/coefficients/root.d.ts.map +1 -0
  56. package/types/geometry/point.d.ts +1 -1
  57. package/types/geometry/point.d.ts.map +1 -1
  58. package/types/helpers.d.ts +1 -0
  59. package/types/helpers.d.ts.map +1 -1
  60. package/types/index.d.ts +1 -0
  61. package/types/index.d.ts.map +1 -1
  62. package/types/numeric.d.ts +2 -0
  63. package/types/numeric.d.ts.map +1 -1
  64. package/types/pimath.interface.d.ts +26 -26
  65. package/types/pimath.interface.d.ts.map +1 -1
@@ -0,0 +1,388 @@
1
+ import type {IAlgebra, IEquation, InputValue, IPiMathObject, ISolution, literalType} from "../pimath.interface"
2
+ import {Fraction} from "../coefficients"
3
+ import {Equation} from "./equation"
4
+ import {Monom} from "./monom"
5
+ import {Polynom} from "./polynom"
6
+
7
+ export class LinearSystem implements IPiMathObject<LinearSystem>,
8
+ IEquation<LinearSystem>,
9
+ IAlgebra<LinearSystem> {
10
+
11
+ #equations: Equation[]
12
+
13
+ // Determine the letters in the linear system, usually ['x', 'y']
14
+ #variables: string[]
15
+
16
+ constructor(...values: (string | Equation)[]) {
17
+ this.#equations = []
18
+ this.#variables = []
19
+
20
+ if (values.length > 0) {
21
+ this.parse(...values)
22
+ }
23
+
24
+ return this
25
+ }
26
+
27
+ public parse = (...equations: (string | Equation)[]): this => {
28
+ // make the original equations
29
+ this.#equations = equations.map(value => new Equation(value))
30
+
31
+ // get the letters.
32
+ this.#findLetters()
33
+ return this
34
+ }
35
+
36
+ public clone = (): LinearSystem => {
37
+ return new LinearSystem()
38
+ .parse(...this.#equations.map(equ => equ.clone()))
39
+ }
40
+
41
+ public static fromMatrix(
42
+ matrix: InputValue<Fraction>[][],
43
+ letters = 'xyz'): LinearSystem {
44
+ // Check that each row has the same number of columns
45
+ const cols = matrix[0].length
46
+ if (matrix.some(row => row.length !== cols)) {
47
+ throw new Error("All rows must have the same number of columns")
48
+ }
49
+
50
+ // Determine the default letters. The number of letters are cols-1
51
+ const vars = letters.split('')
52
+ .splice(0, cols - 1)
53
+
54
+ // Create a new LinearSystem
55
+ return new LinearSystem(
56
+ ...matrix.map(row => {
57
+ const P = new Polynom(vars.join(''), ...row)
58
+ return new Equation(P, 0)
59
+ })
60
+ )
61
+
62
+ }
63
+
64
+ public add(value: InputValue<LinearSystem | Equation | Polynom>, index?: number): this {
65
+ if (value instanceof LinearSystem) {
66
+ const length = value.equations.length
67
+ if (length !== this.#equations.length) {
68
+ throw new Error("The number of equations must be the same")
69
+ }
70
+
71
+ for (let i = 0; i < length; i++) {
72
+ this.#equations[i].add(value.equations[i])
73
+ }
74
+ } else {
75
+ if (index === undefined || index < 0 || index >= this.#equations.length) {
76
+ throw new Error("Index out of range")
77
+ }
78
+ const equ = new Equation(value)
79
+ this.#equations[index].add(equ)
80
+ }
81
+
82
+ return this
83
+ }
84
+
85
+ public buildTex = (equations: Equation[], operators?: (string[])[]): string => {
86
+ let equStr: string[]
87
+ let m: Monom
88
+ let letters: string[] = []
89
+ const equArray: string[] = []
90
+
91
+ // Get the letters from the linear system
92
+ for (const equ of equations) {
93
+ letters = letters.concat(equ.letters())
94
+ }
95
+
96
+ letters = [...new Set(letters)]
97
+ letters.sort()
98
+
99
+ for (let i = 0; i < equations.length; i++) {
100
+ const equ = equations[i]
101
+
102
+ equStr = []
103
+ for (const L of letters) {
104
+ m = equ.left.monomByLetter(L)
105
+
106
+ if (equStr.length === 0) {
107
+ equStr.push(m.isZero() ? '' : m.tex)
108
+ } else {
109
+ equStr.push(m.isZero() ? '' : ((m.coefficient.sign() === 1) ? '+' : '') + m.tex)
110
+ }
111
+ }
112
+
113
+ // Add the equal sign
114
+ equStr.push('=')
115
+
116
+ // Add the right hand part of the equation (should be only a number, because it has been reordered)
117
+ equStr.push(equ.right.tex)
118
+
119
+ // Add the operations if existing
120
+ if (operators?.[i] !== undefined) {
121
+ // add extra space at the end of the equation
122
+ equStr[equStr.length - 1] = equStr[equStr.length - 1] + ' \\phantom{\\quad}'
123
+ for (const o of operators[i]) {
124
+ equStr.push(`\\ \\cdot\\ ${o.startsWith('-') ? "\\left(" + o + "\\right)" : o}`)
125
+ }
126
+ }
127
+
128
+ // Add to the list.
129
+ equArray.push(equStr.join('&'))
130
+ }
131
+
132
+ let operatorsColumns = 0
133
+ if (operators !== undefined && operators.length > 0) {
134
+ operatorsColumns = operators[0].length
135
+ }
136
+
137
+ return `\\left\\{\\begin{array}{${"r".repeat(letters.length)}cl ${"|l".repeat(operatorsColumns)}}${equArray.join('\\\\ ')}\\end{array}\\right.`
138
+ }
139
+
140
+ public degree(letter?: string): Fraction {
141
+ return Fraction.max(...this.#equations.map(equ => equ.degree(letter)))
142
+ }
143
+
144
+ get display() {
145
+ // TODO : LinearSystem - display: implement the display of the linear system
146
+ return this.tex + 'as display'
147
+ }
148
+
149
+ // ------------------------------------------
150
+ public get equations(): Equation[] {
151
+ return this.#equations
152
+ }
153
+
154
+ public set equations(value) {
155
+ this.#equations = value
156
+ }
157
+
158
+ public evaluate(values: InputValue<Fraction> | literalType<number | Fraction>, asNumeric?: boolean): number | Fraction {
159
+ throw new Error("Method not implemented.")
160
+ }
161
+
162
+ public hasVariable(letter: string): boolean {
163
+ return this.#variables.includes(letter)
164
+ }
165
+
166
+ public isEqual(value: LinearSystem): boolean {
167
+ return this.equations.every((equ, index) => equ.isEqual(value.equations[index]))
168
+ }
169
+
170
+ public get isSolvable(): boolean {
171
+ const V = this.variables
172
+
173
+ // TODO: in some case, it is possible to resolve systems if there isn't the isSame number of vars and equations
174
+ if (V.length !== this.#equations.length) {
175
+ return false
176
+ }
177
+
178
+ //TODO: Must check if two equations isn't a linear combination of the others ?
179
+
180
+ return true
181
+ }
182
+
183
+ public get matrix(): [Fraction[][], Fraction[]] {
184
+ //TODO: use Matrix class
185
+ return this.#makeMatrix()
186
+ }
187
+
188
+ public mergeEquations = (eq1: Equation, eq2: Equation, factor1: Fraction, factor2: Fraction): Equation => {
189
+ // Set and clone the equations.
190
+
191
+ const eq1multiplied = eq1.clone().multiply(new Fraction(factor1)),
192
+ eq2multiplied = eq2.clone().multiply(new Fraction(factor2))
193
+
194
+ // Add both equations together.
195
+ eq1multiplied.left.add(eq2multiplied.left)
196
+ eq1multiplied.right.add(eq2multiplied.right)
197
+
198
+ return eq1multiplied
199
+ }
200
+
201
+ public multiply(value: InputValue<Fraction> | InputValue<Fraction>[], index?: number): this {
202
+ // Multiply the system by a number
203
+ // the value can be an array of numbers
204
+ // the value can be a number and the index of the equation to multiply
205
+ if (Array.isArray(value)) {
206
+ if (value.length !== this.#equations.length) {
207
+ throw new Error("The number of values must be the same as the number of equations")
208
+ }
209
+
210
+ for (let i = 0; i < value.length; i++) {
211
+ this.#equations[i].multiply(value[i])
212
+ }
213
+ return this
214
+ }
215
+
216
+ if (index === undefined || index < 0 || index >= this.#equations.length) {
217
+ throw new Error("Index out of range")
218
+ }
219
+
220
+ this.#equations[index].multiply(value)
221
+
222
+ return this
223
+ }
224
+
225
+ public reduce(): LinearSystem {
226
+ throw new Error("Method not implemented.")
227
+ }
228
+
229
+ // ------------------------------------------
230
+ public reorder = (): this => {
231
+ for (const E of this.#equations) {
232
+ E.reorder()
233
+ }
234
+
235
+ return this
236
+ }
237
+
238
+ solve(): ISolution[] {
239
+ return []
240
+ }
241
+
242
+ public solveMatrix = (): Fraction[] => {
243
+ const [matrix, vector] = this.matrix
244
+ // Solve the matrix
245
+
246
+ // console.log(matrix.map(row=>row.map(x=>x.display)))
247
+ // console.log(vector.map(x=>x.display))
248
+
249
+ // Make the augmented matrix (matrix + vector)
250
+ const augmentedMatrix: Fraction[][] = matrix.map((row, index) => [...row, vector[index]])
251
+
252
+ // Reduce the matrix
253
+ for (let i = 0; i < matrix.length; i++) {
254
+ // Find the pivot (the first non-zero element in the row)
255
+ let pivot = augmentedMatrix[i][i].clone()
256
+ if (pivot.isZero()) {
257
+ // throw new Error('Divide by zero !')
258
+ // Search a line below that would add it.
259
+ const row_to_add = augmentedMatrix
260
+ .find((row, index) => {
261
+ return index > i && !row[i].isZero()
262
+ })
263
+
264
+ if (row_to_add) {
265
+ augmentedMatrix[i].forEach((value, index) => value.add(row_to_add[index]))
266
+ pivot = augmentedMatrix[i][i].clone()
267
+ } else {
268
+ throw new Error('Unsolvable...')
269
+ }
270
+
271
+
272
+ }
273
+
274
+ // Normalize the row: divide all elements by the pivot
275
+ // the pivot is now 1
276
+ augmentedMatrix[i] = augmentedMatrix[i].map(x => x.divide(pivot))
277
+
278
+ // reduce the other rows using the pivot.
279
+ for (let j = 0; j < matrix.length; j++) {
280
+ if (j === i) {
281
+ continue
282
+ }
283
+
284
+ const factor = augmentedMatrix[j][i].clone().opposite()
285
+ for (let k = 0; k < augmentedMatrix[j].length; k++) {
286
+ augmentedMatrix[j][k].add(augmentedMatrix[i][k].clone().multiply(factor))
287
+ }
288
+
289
+ // Check if the system is undetermined (no solution or infinite solutions)
290
+ // the j line must not be all zeros
291
+ // the last element must be zero => the system is undetermined
292
+ // the last element must not be zero => the system is impossible
293
+ if (augmentedMatrix[j].slice(0, augmentedMatrix[j].length - 1).every(x => x.isZero())) {
294
+ if (augmentedMatrix[j][augmentedMatrix[j].length - 1].isZero()) {
295
+ return [new Fraction().infinite()]
296
+ } else {
297
+ return []
298
+ }
299
+ }
300
+ }
301
+ }
302
+
303
+ return augmentedMatrix.map(x => x[x.length - 1])
304
+ }
305
+
306
+ public subtract(value: InputValue<LinearSystem | Equation | Polynom>, index?: number): this {
307
+ if (value instanceof LinearSystem) {
308
+ const length = value.equations.length
309
+ if (length !== this.#equations.length) {
310
+ throw new Error("The number of equations must be the same")
311
+ }
312
+
313
+ for (let i = 0; i < length; i++) {
314
+ this.#equations[i].subtract(value.equations[i])
315
+ }
316
+ } else {
317
+ if (index === undefined || index < 0 || index >= this.#equations.length) {
318
+ throw new Error("Index out of range")
319
+ }
320
+ const equ = new Equation(value)
321
+ this.#equations[index].subtract(equ)
322
+ }
323
+
324
+ return this
325
+ }
326
+
327
+ public get tex(): string {
328
+ // Build the array of values.
329
+ // Reorder
330
+ // This clone the system :!!!
331
+ //TODO: Avoid cloning this linear system
332
+ const LS = this.clone().reorder()
333
+
334
+ return this.buildTex(LS.equations)
335
+ }
336
+
337
+ public get variables(): string[] {
338
+ return this.#variables
339
+ }
340
+
341
+ public set variables(value: string | string[]) {
342
+ const vars = (typeof value === "string") ? value.split('') : [...value]
343
+ vars.sort()
344
+ this.#variables = vars
345
+ }
346
+
347
+ #findLetters = (): this => {
348
+ this.#variables = this.#equations.reduce((acc: string[], equ) => {
349
+ return [...new Set([...acc, ...equ.variables])]
350
+ }, [])
351
+ //
352
+ // // Find all letters used.
353
+ // let variables = new Set<string>()
354
+ //
355
+ // for (const equ of this.#equations) {
356
+ // variables = new Set([...variables, ...equ.variables])
357
+ // }
358
+ //
359
+ // this.#variables = [...variables]
360
+ this.#variables.sort()
361
+ return this
362
+ }
363
+
364
+ #makeMatrix = (): [Fraction[][], Fraction[]] => {
365
+ // Make the matrix
366
+ const matrix: Fraction[][] = []
367
+ const vector: Fraction[] = []
368
+
369
+ for (const E of this.#equations) {
370
+ const row: Fraction[] = []
371
+
372
+ const equ = E.clone().reorder()
373
+ for (const L of this.variables) {
374
+ const m = equ.left.monomByLetter(L)
375
+ row.push(m.coefficient)
376
+ }
377
+
378
+ // Add the "no letter part"
379
+ vector.push(equ.right.monoms[0].coefficient)
380
+
381
+ // Add to the matrix
382
+ matrix.push(row)
383
+ }
384
+
385
+ return [matrix, vector]
386
+ }
387
+
388
+ }
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Polynom module contains everything necessary to handle polynoms.
3
+ * @module Logicalset
4
+ */
5
+ import {ShutingYard, ShutingyardMode} from "piexpression"
6
+
7
+ /**
8
+ * Polynom class can handle polynoms, reorder, resolve, ...
9
+ */
10
+ export class LogicalSet {
11
+ #rpn: { token: string, tokenType: string }[]
12
+
13
+ /**
14
+ *
15
+ * @param {string} value (optional) Default polynom to parse on class creation
16
+ */
17
+ constructor(value?: string) {
18
+ this.#rpn = []
19
+
20
+ if (value !== undefined) {
21
+ this.parse(value)
22
+ }
23
+
24
+ return this
25
+ }
26
+
27
+ parse = (value: string): this => {
28
+ // Parse the updated value to the shutingyard algorithm
29
+ this.#rpn = new ShutingYard(ShutingyardMode.SET)
30
+ .parse(value)
31
+ .rpn
32
+
33
+ return this
34
+ }
35
+
36
+ evaluate(values: Record<string, boolean>): boolean {
37
+ // Add missing key(s) and set them as false by default.
38
+ this.variables.forEach(key => {
39
+ if (!Object.hasOwn(values, key)) {
40
+ values[key] = false
41
+ }
42
+ })
43
+
44
+ const stack: boolean[] = []
45
+ for (const token of this.#rpn) {
46
+ console.log(token)
47
+ if (token.tokenType === 'variable') {
48
+ stack.push(values[token.token])
49
+ } else if (token.tokenType === 'operation') {
50
+ if (token.token === '!') {
51
+ // need only one item from stack
52
+ if (stack.length >= 1) {
53
+ const a = stack.pop()
54
+ stack.push(!a)
55
+ } else {
56
+ return false
57
+ }
58
+ } else {
59
+ // All other operations needs two items from stack
60
+ const a = stack.pop()
61
+ const b = stack.pop()
62
+ if (a !== undefined && b !== undefined) {
63
+ switch (token.token) {
64
+ case "&":
65
+ stack.push(a && b)
66
+ break
67
+ case "|":
68
+ stack.push(a || b)
69
+ break
70
+ case "-":
71
+ return false
72
+ }
73
+
74
+ } else {
75
+ return false
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ return stack.length === 1 && stack[0]
82
+ }
83
+
84
+ get rpn(): { token: string, tokenType: string }[] {
85
+ return this.#rpn
86
+ }
87
+
88
+ get tex(): string {
89
+ const varStack: { token: string, tokenType: string }[] = []
90
+
91
+ for (const token of this.#rpn) {
92
+ if (token.tokenType === 'variable') {
93
+ varStack.push(token)
94
+ } else {
95
+ switch (token.token) {
96
+ case '&':
97
+ if (varStack.length >= 2) {
98
+ const second = varStack.pop(),
99
+ first = varStack.pop()
100
+
101
+ if (second && first) {
102
+ if (first.tokenType === 'mix') {
103
+ first.token = `( ${first.token} )`
104
+ }
105
+ if (second.tokenType === 'mix') {
106
+ second.token = `( ${second.token} )`
107
+ }
108
+ varStack.push({token: `${first.token} \\cap ${second.token}`, tokenType: 'mix'})
109
+ }
110
+ }
111
+ break
112
+ case '|':
113
+ if (varStack.length >= 2) {
114
+ const second = varStack.pop(),
115
+ first = varStack.pop()
116
+
117
+ if (second && first) {
118
+ if (first.tokenType === 'mix') {
119
+ first.token = `( ${first.token} )`
120
+ }
121
+ if (second.tokenType === 'mix') {
122
+ second.token = `( ${second.token} )`
123
+ }
124
+ varStack.push({token: `${first.token} \\cup ${second.token}`, tokenType: 'mix'})
125
+ }
126
+ }
127
+ break
128
+ case '-':
129
+ if (varStack.length >= 2) {
130
+ const second = varStack.pop(),
131
+ first = varStack.pop()
132
+
133
+ if (second && first) {
134
+ if (first.tokenType === 'mix') {
135
+ first.token = `( ${first.token} )`
136
+ }
137
+ if (second.tokenType === 'mix') {
138
+ second.token = `( ${second.token} )`
139
+ }
140
+ varStack.push({token: `${first.token} \\setminus ${second.token}`, tokenType: 'mix'})
141
+ }
142
+ }
143
+ break
144
+ case '!':
145
+ if (varStack.length >= 1) {
146
+ const first = varStack.pop()
147
+
148
+ if (first) {
149
+ varStack.push({token: `\\overline{ ${first.token} }`, tokenType: 'variable'})
150
+ }
151
+ }
152
+ break
153
+ }
154
+ }
155
+ }
156
+
157
+ return varStack[0].token
158
+ }
159
+
160
+ get variables(): string[] {
161
+ return this.#rpn
162
+ .filter(value => value.tokenType === 'variable')
163
+ .map(value => value.token)
164
+ }
165
+
166
+ vennAB(): string[] {
167
+ return this.#evaluateAsVenn({
168
+ A: ['A', 'AB'],
169
+ B: ['B', 'AB']
170
+ },
171
+ ['A', 'B', 'AB', 'E']
172
+ )
173
+ }
174
+
175
+ vennABC(): string[] {
176
+ return this.#evaluateAsVenn({
177
+ A: ['A', 'AB', 'AC', 'ABC'],
178
+ B: ['B', 'AB', 'BC', 'ABC'],
179
+ C: ['C', 'AC', 'BC', 'ABC']
180
+ },
181
+ ['A', 'B', 'C', 'AB', 'AC', 'BC', 'ABC', 'E']
182
+ )
183
+ }
184
+
185
+ #evaluateAsVenn(tokenSets: Record<string, string[] | undefined>, reference?: string[]): string[] {
186
+ const varStack: (Set<string>)[] = []
187
+
188
+ let referenceSet: Set<string>
189
+ if (reference === undefined) {
190
+ referenceSet = new Set()
191
+ for (const key in tokenSets) {
192
+ referenceSet = new Set([
193
+ ...referenceSet,
194
+ ...(tokenSets[key] ?? [])
195
+ ])
196
+ }
197
+ } else {
198
+ referenceSet = new Set(reference)
199
+ }
200
+
201
+ for (const token of this.#rpn) {
202
+ if (token.tokenType === 'variable') {
203
+ // The variable has no token - assume it's empty.
204
+ if (tokenSets[token.token] === undefined) {
205
+ varStack.push(new Set())
206
+ } else {
207
+ varStack.push(new Set(tokenSets[token.token]))
208
+ }
209
+
210
+ } else {
211
+ switch (token.token) {
212
+ case '&':
213
+ if (varStack.length >= 2) {
214
+ const second = varStack.pop(),
215
+ first = varStack.pop()
216
+
217
+ if (first && second) {
218
+ varStack.push(new Set([...first].filter(x => second.has(x))))
219
+ }
220
+ }
221
+ break
222
+ case '|':
223
+ if (varStack.length >= 2) {
224
+ const second = varStack.pop(),
225
+ first = varStack.pop()
226
+ if (first && second) {
227
+ varStack.push(new Set([...first, ...second]))
228
+ }
229
+ }
230
+ break
231
+ case '-':
232
+ if (varStack.length >= 2) {
233
+ const second = varStack.pop(),
234
+ first = varStack.pop()
235
+
236
+ if (first && second) {
237
+ varStack.push(new Set([...first].filter(x => !second.has(x))))
238
+ }
239
+ }
240
+ break
241
+ case '!':
242
+ if (varStack.length >= 1) {
243
+ const first = varStack.pop()
244
+
245
+ if (first) {
246
+ varStack.push(new Set([...referenceSet].filter(x => !first.has(x))))
247
+ }
248
+ }
249
+ break
250
+ }
251
+ }
252
+ }
253
+
254
+ return [...varStack[0]].sort()
255
+ }
256
+ }