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.
- package/dist/pimath.js +188 -159
- package/dist/pimath.js.map +1 -1
- package/package.json +4 -2
- package/src/algebra/equation.ts +556 -0
- package/src/algebra/equationSolver.ts +539 -0
- package/src/algebra/factor.ts +339 -0
- package/src/algebra/index.ts +11 -0
- package/src/algebra/linearSystem.ts +388 -0
- package/src/algebra/logicalset.ts +256 -0
- package/src/algebra/matrix.ts +474 -0
- package/src/algebra/monom.ts +1015 -0
- package/src/algebra/operations.ts +24 -0
- package/src/algebra/polyFactor.ts +668 -0
- package/src/algebra/polynom.ts +1394 -0
- package/src/analyze/solution.ts +115 -0
- package/src/analyze/tableOfSigns.ts +30 -0
- package/src/coefficients/fraction.ts +678 -0
- package/src/coefficients/index.ts +4 -0
- package/src/coefficients/nthRoot.ts +149 -0
- package/src/coefficients/root.ts +299 -0
- package/src/geometry/circle.ts +386 -0
- package/src/geometry/geomMath.ts +70 -0
- package/src/geometry/index.ts +10 -0
- package/src/geometry/line.ts +677 -0
- package/src/geometry/line3.ts +206 -0
- package/src/geometry/plane3.ts +170 -0
- package/src/geometry/point.ts +66 -0
- package/src/geometry/sphere3.ts +214 -0
- package/src/geometry/triangle.ts +354 -0
- package/src/geometry/vector.ts +341 -0
- package/src/helpers.ts +35 -0
- package/src/index.ts +60 -0
- package/src/numeric.ts +199 -0
- package/src/pimath.interface.ts +160 -0
- package/src/randomization/algebra/rndEquation.ts +41 -0
- package/src/randomization/algebra/rndMonom.ts +39 -0
- package/src/randomization/algebra/rndPolynom.ts +86 -0
- package/src/randomization/coefficient/rndFraction.ts +38 -0
- package/src/randomization/geometry/rndCircle.ts +27 -0
- package/src/randomization/geometry/rndLine.ts +37 -0
- package/src/randomization/geometry/rndLine3.ts +27 -0
- package/src/randomization/geometry/rndVector.ts +63 -0
- package/src/randomization/random.ts +91 -0
- package/src/randomization/rndHelpers.ts +102 -0
- package/src/randomization/rndTypes.ts +63 -0
- package/types/algebra/equationSolver.d.ts +3 -0
- package/types/algebra/equationSolver.d.ts.map +1 -1
- package/types/algebra/polyFactor.d.ts +5 -0
- package/types/algebra/polyFactor.d.ts.map +1 -1
- package/types/analyze/solution.d.ts +21 -0
- package/types/analyze/solution.d.ts.map +1 -0
- package/types/analyze/tableOfSigns.d.ts +9 -0
- package/types/analyze/tableOfSigns.d.ts.map +1 -0
- package/types/coefficients/root.d.ts +38 -0
- package/types/coefficients/root.d.ts.map +1 -0
- package/types/geometry/point.d.ts +1 -1
- package/types/geometry/point.d.ts.map +1 -1
- package/types/helpers.d.ts +1 -0
- package/types/helpers.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/numeric.d.ts +2 -0
- package/types/numeric.d.ts.map +1 -1
- package/types/pimath.interface.d.ts +26 -26
- 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
|
+
}
|