pimath 0.2.14 → 0.2.15

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 (59) hide show
  1. package/dist/pimath.js +850 -1879
  2. package/dist/pimath.js.map +1 -1
  3. package/package.json +1 -1
  4. package/src/algebra/equation.ts +562 -558
  5. package/src/algebra/equationSolver.ts +489 -489
  6. package/src/algebra/factor.ts +396 -392
  7. package/src/algebra/linearSystem.ts +446 -442
  8. package/src/algebra/matrix.ts +515 -511
  9. package/src/algebra/monom.ts +932 -928
  10. package/src/algebra/polyFactor.ts +724 -720
  11. package/src/algebra/polynom.ts +1233 -1229
  12. package/src/coefficients/fraction.ts +907 -748
  13. package/src/coefficients/root.ts +500 -359
  14. package/src/geometry/TupleN.ts +129 -129
  15. package/src/geometry/circle.ts +455 -455
  16. package/src/geometry/line.ts +658 -654
  17. package/src/geometry/point.ts +105 -105
  18. package/src/geometry/sphere3.ts +2 -2
  19. package/src/geometry/triangle.ts +488 -488
  20. package/src/geometry/vector.ts +230 -226
  21. package/src/numeric.ts +136 -51
  22. package/src/pimath.interface.ts +2 -0
  23. package/types/algebra/equation.d.ts +1 -0
  24. package/types/algebra/equation.d.ts.map +1 -1
  25. package/types/algebra/equationSolver.d.ts.map +1 -1
  26. package/types/algebra/factor.d.ts +1 -0
  27. package/types/algebra/factor.d.ts.map +1 -1
  28. package/types/algebra/linearSystem.d.ts +1 -0
  29. package/types/algebra/linearSystem.d.ts.map +1 -1
  30. package/types/algebra/matrix.d.ts +6 -5
  31. package/types/algebra/matrix.d.ts.map +1 -1
  32. package/types/algebra/monom.d.ts +1 -0
  33. package/types/algebra/monom.d.ts.map +1 -1
  34. package/types/algebra/polyFactor.d.ts +1 -0
  35. package/types/algebra/polyFactor.d.ts.map +1 -1
  36. package/types/algebra/polynom.d.ts +1 -0
  37. package/types/algebra/polynom.d.ts.map +1 -1
  38. package/types/coefficients/fraction.d.ts +62 -53
  39. package/types/coefficients/fraction.d.ts.map +1 -1
  40. package/types/coefficients/root.d.ts +3 -0
  41. package/types/coefficients/root.d.ts.map +1 -1
  42. package/types/geometry/TupleN.d.ts +1 -1
  43. package/types/geometry/TupleN.d.ts.map +1 -1
  44. package/types/geometry/circle.d.ts.map +1 -1
  45. package/types/geometry/line.d.ts +1 -0
  46. package/types/geometry/line.d.ts.map +1 -1
  47. package/types/geometry/point.d.ts.map +1 -1
  48. package/types/geometry/sphere3.d.ts +2 -2
  49. package/types/geometry/sphere3.d.ts.map +1 -1
  50. package/types/geometry/triangle.d.ts.map +1 -1
  51. package/types/geometry/vector.d.ts +1 -0
  52. package/types/geometry/vector.d.ts.map +1 -1
  53. package/types/index.d.ts +3 -3
  54. package/types/numeric.d.ts +4 -4
  55. package/types/numeric.d.ts.map +1 -1
  56. package/types/pimath.interface.d.ts +1 -0
  57. package/types/pimath.interface.d.ts.map +1 -1
  58. package/types/primes.data.d.ts +2 -0
  59. package/types/primes.data.d.ts.map +1 -0
@@ -1,442 +1,446 @@
1
- import type {IAlgebra, IEquation, InputValue, IPiMathObject, 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
- import {Numeric} from "../numeric"
7
- import {Solution} from "../analyze"
8
- import {DimensionError, IndexError, MathError, NotImplementedError} from "../errors"
9
-
10
- export class LinearSystem implements IPiMathObject<LinearSystem>,
11
- IEquation<LinearSystem>,
12
- IAlgebra<LinearSystem> {
13
-
14
- #equations: Equation[]
15
-
16
- // Determine the letters in the linear asSystem, usually ['x', 'y']
17
- #variables: string[]
18
-
19
- constructor(...values: (string | Equation)[]) {
20
- this.#equations = []
21
- this.#variables = []
22
-
23
- if (values.length > 0) {
24
- this.parse(...values)
25
- }
26
-
27
- return this
28
- }
29
-
30
- public parse = (...equations: (string | Equation)[]): this => {
31
- // make the original equations
32
- this.#equations = equations.map(value => new Equation(value))
33
-
34
- // get the letters.
35
- this.#findLetters()
36
- return this
37
- }
38
-
39
- public clone = (): LinearSystem => {
40
- return new LinearSystem()
41
- .parse(...this.#equations.map(equ => equ.clone()))
42
- }
43
-
44
- public get tex(): string {
45
- // Build the array of values.
46
- // Reorder
47
- // This clone the asSystem :!!!
48
- //TODO: Avoid cloning this linear asSystem
49
- const LS = this.clone().reorder()
50
-
51
- return this.buildTex(LS.equations)
52
- }
53
-
54
- get display() {
55
- // TODO : LinearSystem - display: implement the display of the linear asSystem
56
- return this.tex + 'as display'
57
- }
58
-
59
- public static fromMatrix(
60
- matrix: InputValue<Fraction>[][],
61
- letters = 'xyz'): LinearSystem {
62
- // Check that each row has the same number of columns
63
- const cols = matrix[0].length
64
- if (matrix.some(row => row.length !== cols)) {
65
- throw new DimensionError("All rows must have the same number of columns")
66
- }
67
-
68
- // Determine the default letters. The number of letters are cols-1
69
- const vars = letters.split('')
70
- .splice(0, cols - 1)
71
-
72
- // Create a new LinearSystem
73
- return new LinearSystem(
74
- ...matrix.map(row => {
75
- const P = new Polynom(vars.join(''), ...row)
76
- return new Equation(P, 0)
77
- })
78
- )
79
-
80
- }
81
-
82
- public static solutionAsDisplay(value: Solution[]): string {
83
- return `(${value.map(x=>x.display).join(";")})`
84
- }
85
- public static solutionAsTex(value: Solution[]): string {
86
- return `\\left( ${value.map(x=>x.tex).join(" ; ")} \\right)`
87
- }
88
- public add(value: InputValue<LinearSystem | Equation | Polynom>, index?: number): this {
89
- if (value instanceof LinearSystem) {
90
- const length = value.equations.length
91
- if (length !== this.#equations.length) {
92
- throw new DimensionError("The number of equations must be the same")
93
- }
94
-
95
- for (let i = 0; i < length; i++) {
96
- this.#equations[i].add(value.equations[i])
97
- }
98
- } else {
99
- if (index === undefined || index < 0 || index >= this.#equations.length) {
100
- throw new IndexError("Index out of range")
101
- }
102
- const equ = new Equation(value)
103
- this.#equations[index].add(equ)
104
- }
105
-
106
- return this
107
- }
108
-
109
- public buildTex = (equations: Equation[], operators?: (string[])[]): string => {
110
- let equStr: string[]
111
- let m: Monom
112
- let letters: string[] = []
113
- const equArray: string[] = []
114
-
115
- // Get the letters from the linear asSystem
116
- for (const equ of equations) {
117
- letters = letters.concat(equ.letters())
118
- }
119
-
120
- letters = [...new Set(letters)]
121
- letters.sort()
122
-
123
- for (let i = 0; i < equations.length; i++) {
124
- const equ = equations[i]
125
-
126
- equStr = []
127
- for (const L of letters) {
128
- m = equ.left.monomByLetter(L)
129
-
130
- if (equStr.length === 0) {
131
- equStr.push(m.isZero() ? '' : m.tex)
132
- } else {
133
- equStr.push(m.isZero() ? '' : ((m.coefficient.sign() === 1) ? '+' : '') + m.tex)
134
- }
135
- }
136
-
137
- // Add the equal sign
138
- equStr.push('=')
139
-
140
- // Add the right hand part of the equation (should be only a number, because it has been reordered)
141
- equStr.push(equ.right.tex)
142
-
143
- // Add the operations if existing
144
- if (operators?.[i] !== undefined) {
145
- // add extra space at the end of the equation
146
- equStr[equStr.length - 1] = equStr[equStr.length - 1] + ' \\phantom{\\quad}'
147
- for (const o of operators[i]) {
148
- equStr.push(`\\ \\cdot\\ ${o.startsWith('-') ? "\\left(" + o + "\\right)" : o}`)
149
- }
150
- }
151
-
152
- // Add to the list.
153
- equArray.push(equStr.join('&'))
154
- }
155
-
156
- let operatorsColumns = 0
157
- if (operators !== undefined && operators.length > 0) {
158
- operatorsColumns = operators[0].length
159
- }
160
-
161
- return `\\left\\{\\begin{array}{${"r".repeat(letters.length)}cl ${"|l".repeat(operatorsColumns)}}${equArray.join('\\\\ ')}\\end{array}\\right.`
162
- }
163
-
164
- public degree(letter?: string): Fraction {
165
- return Fraction.max(...this.#equations.map(equ => equ.degree(letter)))
166
- }
167
-
168
- // ------------------------------------------
169
- public get equations(): Equation[] {
170
- return this.#equations
171
- }
172
-
173
- public set equations(value) {
174
- this.#equations = value
175
-
176
- // update the variables.
177
- this.#findLetters()
178
- }
179
-
180
- public evaluate(values: InputValue<Fraction> | literalType<number | Fraction>, asNumeric?: boolean): number | Fraction {
181
- throw new NotImplementedError("Method not implemented.")
182
- }
183
-
184
- public hasVariable(letter: string): boolean {
185
- return this.#variables.includes(letter)
186
- }
187
-
188
- public isEqual(value: LinearSystem): boolean {
189
- return this.equations.every((equ, index) => equ.isEqual(value.equations[index]))
190
- }
191
-
192
- public get isSolvable(): boolean {
193
- const V = this.variables
194
-
195
- // TODO: in some case, it is possible to resolve systems if there isn't the isSame number of vars and equations
196
- if (V.length !== this.#equations.length) {
197
- return false
198
- }
199
-
200
- //TODO: Must check if two equations isn't a linear combination of the others ?
201
-
202
- return true
203
- }
204
-
205
- public get matrix(): [Fraction[][], Fraction[]] {
206
- //TODO: use Matrix class
207
- return this.#makeMatrix()
208
- }
209
-
210
- public mergeEquations(equation1: { id: number, factor: InputValue<Fraction> }, equation2: {
211
- id: number,
212
- factor: number
213
- }): Equation {
214
- // Set and clone the equations.
215
- const eq1multiplied = this.equations[equation1.id].clone().multiply(equation1.factor)
216
- const eq2multiplied = this.equations[equation2.id].clone().multiply(equation2.factor)
217
-
218
- // Add both equations together.
219
- return eq1multiplied.add(eq2multiplied)
220
- }
221
-
222
- public multiply(value: InputValue<Fraction> | InputValue<Fraction>[], index?: number): this {
223
- // Multiply the asSystem by a number
224
- // the value can be an array of numbers
225
- // the value can be a number and the index of the equation to multiply
226
- if (Array.isArray(value)) {
227
- if (value.length !== this.#equations.length) {
228
- throw new DimensionError("The number of values must be the same as the number of equations")
229
- }
230
-
231
- for (let i = 0; i < value.length; i++) {
232
- this.#equations[i].multiply(value[i])
233
- }
234
- return this
235
- }
236
-
237
- if (index === undefined || index < 0 || index >= this.#equations.length) {
238
- throw new IndexError("Index out of range")
239
- }
240
-
241
- this.#equations[index].multiply(value)
242
-
243
- return this
244
- }
245
-
246
- public reduce(): this {
247
- // reduce all equations at once.
248
- this.equations.forEach(equ => equ.reduce())
249
- return this
250
- }
251
-
252
- // ------------------------------------------
253
- public reorder = (): this => {
254
- for (const E of this.#equations) {
255
- E.reorder()
256
- }
257
-
258
- return this
259
- }
260
-
261
- public solve(): Solution[] {
262
- const [matrix, vector] = this.matrix
263
- // Solve the matrix
264
-
265
- // Make the augmented matrix (matrix + vector)
266
- const augmentedMatrix: Fraction[][] = matrix.map((row, index) => [...row, vector[index]])
267
-
268
- // Reduce the matrix
269
- for (let i = 0; i < matrix.length; i++) {
270
- // Find the pivot (the first non-zero element in the row)
271
- let pivot = augmentedMatrix[i][i].clone()
272
- if (pivot.isZero()) {
273
- // throw new Error('Divide by zero !')
274
- // Search a line below that would add it.
275
- const row_to_add = augmentedMatrix
276
- .find((row, index) => {
277
- return index > i && !row[i].isZero()
278
- })
279
-
280
- if (row_to_add) {
281
- augmentedMatrix[i].forEach((value, index) => value.add(row_to_add[index]))
282
- pivot = augmentedMatrix[i][i].clone()
283
- } else {
284
- throw new MathError('Unsolvable...')
285
- }
286
-
287
-
288
- }
289
-
290
- // Normalize the row: divide all elements by the pivot
291
- // the pivot is now 1
292
- augmentedMatrix[i] = augmentedMatrix[i].map(x => x.divide(pivot))
293
-
294
- // reduce the other rows using the pivot.
295
- for (let j = 0; j < matrix.length; j++) {
296
- if (j === i) {
297
- continue
298
- }
299
-
300
- const factor = augmentedMatrix[j][i].clone().opposite()
301
- for (let k = 0; k < augmentedMatrix[j].length; k++) {
302
- augmentedMatrix[j][k].add(augmentedMatrix[i][k].clone().multiply(factor))
303
- }
304
-
305
- // Check if the asSystem is undetermined (no solution or infinite solutions)
306
- // the j line must not be all zeros
307
- // the last element must be zero => the asSystem is undetermined
308
- // the last element must not be zero => the asSystem is impossible
309
- if (augmentedMatrix[j].slice(0, augmentedMatrix[j].length - 1).every(x => x.isZero())) {
310
- if (augmentedMatrix[j][augmentedMatrix[j].length - 1].isZero()) {
311
- // Infinite solution
312
- return this.#mapMatrixSolutions()
313
- } else {
314
- // No solution
315
- return []
316
- }
317
- }
318
- }
319
- }
320
-
321
- return this.#mapMatrixSolutions(augmentedMatrix)
322
-
323
- }
324
-
325
- solve_compute_factors(letter: string):
326
- [{ id: number, factor: number }, { id: number, factor: number }][] {
327
- // when solving, every monoms with a variable is on the left !
328
- // and every coefficient are relative numbers.
329
- const result: [{ id: number, factor: number }, { id: number, factor: number }][] = []
330
- const coefficients = this.equations.map(equ => equ.left.monomByLetter(letter).coefficient.value)
331
-
332
- // search for a factor
333
- coefficients.forEach((reference, index) => {
334
- for (let i = index + 1; i < coefficients.length; i++) {
335
- const lcm = Numeric.lcm(reference, coefficients[i])
336
-
337
- const sign = reference < 0 ? -1 : 1
338
- result.push([
339
- {
340
- id: index, factor: sign * lcm / reference
341
- }, {
342
- id: i, factor: -sign * lcm / coefficients[i]
343
- }])
344
- }
345
- })
346
-
347
- // Sort the value: prefer the smallest absolute values (1/-1, 2/-2, ...)
348
- return result.sort((a, b) => {
349
- return (Math.abs(a[0].factor) + Math.abs(a[1].factor)) - (Math.abs(b[0].factor) + Math.abs(b[1].factor))
350
- })
351
- }
352
-
353
- public subtract(value: InputValue<LinearSystem | Equation | Polynom>, index?: number): this {
354
- if (value instanceof LinearSystem) {
355
- const length = value.equations.length
356
- if (length !== this.#equations.length) {
357
- throw new DimensionError("The number of equations must be the same")
358
- }
359
-
360
- for (let i = 0; i < length; i++) {
361
- this.#equations[i].subtract(value.equations[i])
362
- }
363
- } else {
364
- if (index === undefined || index < 0 || index >= this.#equations.length) {
365
- throw new IndexError("Index out of range")
366
- }
367
- const equ = new Equation(value)
368
- this.#equations[index].subtract(equ)
369
- }
370
-
371
- return this
372
- }
373
-
374
- public get variables(): string[] {
375
- return this.#variables
376
- }
377
-
378
- public set variables(value: string | string[]) {
379
- const vars = (typeof value === "string") ? value.split('') : [...value]
380
- vars.sort()
381
- this.#variables = vars
382
- }
383
-
384
- #findLetters = (): this => {
385
- this.#variables = this.#equations.reduce((acc: string[], equ) => {
386
- return [...new Set([...acc, ...equ.variables])]
387
- }, [])
388
-
389
- this.#variables.sort()
390
- return this
391
- }
392
-
393
- #makeMatrix = (): [Fraction[][], Fraction[]] => {
394
- // Make the matrix
395
- const matrix: Fraction[][] = []
396
- const vector: Fraction[] = []
397
-
398
- for (const E of this.#equations) {
399
- const row: Fraction[] = []
400
-
401
- const equ = E.clone().reorder()
402
- for (const L of this.variables) {
403
- const m = equ.left.monomByLetter(L)
404
- row.push(m.coefficient)
405
- }
406
-
407
- // Add the "no letter part"
408
- vector.push(equ.right.monoms[0].coefficient)
409
-
410
- // Add to the matrix
411
- matrix.push(row)
412
- }
413
-
414
- return [matrix, vector]
415
- }
416
-
417
- #mapMatrixSolutions(matrix?: Fraction[][]): Solution[] {
418
- const result: Solution[] = []
419
-
420
- if(!matrix){
421
- // infinite solutions
422
- this.variables.forEach(letter=>{
423
- const sol = Solution.fromFraction(Infinity)
424
- sol.variable = letter
425
- result.push(sol)
426
- })
427
-
428
- return result
429
- }
430
-
431
- matrix.forEach((x, index) => {
432
-
433
- const sol = Solution.fromFraction(x[x.length - 1])
434
- sol.variable = this.variables[index]
435
-
436
- result.push(sol)
437
- })
438
-
439
- return result
440
- }
441
-
442
- }
1
+ import type {IAlgebra, IEquation, InputValue, IPiMathObject, 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
+ import {Numeric} from "../numeric"
7
+ import {Solution} from "../analyze"
8
+ import {DimensionError, IndexError, MathError, NotImplementedError} from "../errors"
9
+
10
+ export class LinearSystem implements IPiMathObject<LinearSystem>,
11
+ IEquation<LinearSystem>,
12
+ IAlgebra<LinearSystem> {
13
+
14
+ #equations: Equation[]
15
+
16
+ // Determine the letters in the linear asSystem, usually ['x', 'y']
17
+ #variables: string[]
18
+
19
+ constructor(...values: (string | Equation)[]) {
20
+ this.#equations = []
21
+ this.#variables = []
22
+
23
+ if (values.length > 0) {
24
+ this.parse(...values)
25
+ }
26
+
27
+ return this
28
+ }
29
+
30
+ parse = (...equations: (string | Equation)[]): this => {
31
+ // make the original equations
32
+ this.#equations = equations.map(value => new Equation(value))
33
+
34
+ // get the letters.
35
+ this.#findLetters()
36
+ return this
37
+ }
38
+
39
+ copy(): this {
40
+ throw new NotImplementedError("copy() is not yet implemented for LinearSystem")
41
+ }
42
+
43
+ clone = (): LinearSystem => {
44
+ return new LinearSystem()
45
+ .parse(...this.#equations.map(equ => equ.clone()))
46
+ }
47
+
48
+ get tex(): string {
49
+ // Build the array of values.
50
+ // Reorder
51
+ // This clone the asSystem :!!!
52
+ //TODO: Avoid cloning this linear asSystem
53
+ const LS = this.clone().reorder()
54
+
55
+ return this.buildTex(LS.equations)
56
+ }
57
+
58
+ get display() {
59
+ // TODO : LinearSystem - display: implement the display of the linear asSystem
60
+ return this.tex + 'as display'
61
+ }
62
+
63
+ static fromMatrix(
64
+ matrix: InputValue<Fraction>[][],
65
+ letters = 'xyz'): LinearSystem {
66
+ // Check that each row has the same number of columns
67
+ const cols = matrix[0].length
68
+ if (matrix.some(row => row.length !== cols)) {
69
+ throw new DimensionError("All rows must have the same number of columns")
70
+ }
71
+
72
+ // Determine the default letters. The number of letters are cols-1
73
+ const vars = letters.split('')
74
+ .splice(0, cols - 1)
75
+
76
+ // Create a new LinearSystem
77
+ return new LinearSystem(
78
+ ...matrix.map(row => {
79
+ const P = new Polynom(vars.join(''), ...row)
80
+ return new Equation(P, 0)
81
+ })
82
+ )
83
+
84
+ }
85
+
86
+ static solutionAsDisplay(value: Solution[]): string {
87
+ return `(${value.map(x=>x.display).join(";")})`
88
+ }
89
+ static solutionAsTex(value: Solution[]): string {
90
+ return `\\left( ${value.map(x=>x.tex).join(" ; ")} \\right)`
91
+ }
92
+ add(value: InputValue<LinearSystem | Equation | Polynom>, index?: number): this {
93
+ if (value instanceof LinearSystem) {
94
+ const length = value.equations.length
95
+ if (length !== this.#equations.length) {
96
+ throw new DimensionError("The number of equations must be the same")
97
+ }
98
+
99
+ for (let i = 0; i < length; i++) {
100
+ this.#equations[i].add(value.equations[i])
101
+ }
102
+ } else {
103
+ if (index === undefined || index < 0 || index >= this.#equations.length) {
104
+ throw new IndexError("Index out of range")
105
+ }
106
+ const equ = new Equation(value)
107
+ this.#equations[index].add(equ)
108
+ }
109
+
110
+ return this
111
+ }
112
+
113
+ buildTex = (equations: Equation[], operators?: (string[])[]): string => {
114
+ let equStr: string[]
115
+ let m: Monom
116
+ let letters: string[] = []
117
+ const equArray: string[] = []
118
+
119
+ // Get the letters from the linear asSystem
120
+ for (const equ of equations) {
121
+ letters = letters.concat(equ.letters())
122
+ }
123
+
124
+ letters = [...new Set(letters)]
125
+ letters.sort()
126
+
127
+ for (let i = 0; i < equations.length; i++) {
128
+ const equ = equations[i]
129
+
130
+ equStr = []
131
+ for (const L of letters) {
132
+ m = equ.left.monomByLetter(L)
133
+
134
+ if (equStr.length === 0) {
135
+ equStr.push(m.isZero() ? '' : m.tex)
136
+ } else {
137
+ equStr.push(m.isZero() ? '' : ((m.coefficient.sign() === 1) ? '+' : '') + m.tex)
138
+ }
139
+ }
140
+
141
+ // Add the equal sign
142
+ equStr.push('=')
143
+
144
+ // Add the right hand part of the equation (should be only a number, because it has been reordered)
145
+ equStr.push(equ.right.tex)
146
+
147
+ // Add the operations if existing
148
+ if (operators?.[i] !== undefined) {
149
+ // add extra space at the end of the equation
150
+ equStr[equStr.length - 1] = equStr[equStr.length - 1] + ' \\phantom{\\quad}'
151
+ for (const o of operators[i]) {
152
+ equStr.push(`\\ \\cdot\\ ${o.startsWith('-') ? "\\left(" + o + "\\right)" : o}`)
153
+ }
154
+ }
155
+
156
+ // Add to the list.
157
+ equArray.push(equStr.join('&'))
158
+ }
159
+
160
+ let operatorsColumns = 0
161
+ if (operators !== undefined && operators.length > 0) {
162
+ operatorsColumns = operators[0].length
163
+ }
164
+
165
+ return `\\left\\{\\begin{array}{${"r".repeat(letters.length)}cl ${"|l".repeat(operatorsColumns)}}${equArray.join('\\\\ ')}\\end{array}\\right.`
166
+ }
167
+
168
+ degree(letter?: string): Fraction {
169
+ return Fraction.max(...this.#equations.map(equ => equ.degree(letter)))
170
+ }
171
+
172
+ // ------------------------------------------
173
+ get equations(): Equation[] {
174
+ return this.#equations
175
+ }
176
+
177
+ set equations(value) {
178
+ this.#equations = value
179
+
180
+ // update the variables.
181
+ this.#findLetters()
182
+ }
183
+
184
+ evaluate(values: InputValue<Fraction> | literalType<number | Fraction>, asNumeric?: boolean): number | Fraction {
185
+ throw new NotImplementedError("Method not implemented.")
186
+ }
187
+
188
+ hasVariable(letter: string): boolean {
189
+ return this.#variables.includes(letter)
190
+ }
191
+
192
+ isEqual(value: LinearSystem): boolean {
193
+ return this.equations.every((equ, index) => equ.isEqual(value.equations[index]))
194
+ }
195
+
196
+ get isSolvable(): boolean {
197
+ const V = this.variables
198
+
199
+ // TODO: in some case, it is possible to resolve systems if there isn't the isSame number of vars and equations
200
+ if (V.length !== this.#equations.length) {
201
+ return false
202
+ }
203
+
204
+ //TODO: Must check if two equations isn't a linear combination of the others ?
205
+
206
+ return true
207
+ }
208
+
209
+ get matrix(): [Fraction[][], Fraction[]] {
210
+ //TODO: use Matrix class
211
+ return this.#makeMatrix()
212
+ }
213
+
214
+ mergeEquations(equation1: { id: number, factor: InputValue<Fraction> }, equation2: {
215
+ id: number,
216
+ factor: number
217
+ }): Equation {
218
+ // Set and clone the equations.
219
+ const eq1multiplied = this.equations[equation1.id].clone().multiply(equation1.factor)
220
+ const eq2multiplied = this.equations[equation2.id].clone().multiply(equation2.factor)
221
+
222
+ // Add both equations together.
223
+ return eq1multiplied.add(eq2multiplied)
224
+ }
225
+
226
+ multiply(value: InputValue<Fraction> | InputValue<Fraction>[], index?: number): this {
227
+ // Multiply the asSystem by a number
228
+ // the value can be an array of numbers
229
+ // the value can be a number and the index of the equation to multiply
230
+ if (Array.isArray(value)) {
231
+ if (value.length !== this.#equations.length) {
232
+ throw new DimensionError("The number of values must be the same as the number of equations")
233
+ }
234
+
235
+ for (let i = 0; i < value.length; i++) {
236
+ this.#equations[i].multiply(value[i])
237
+ }
238
+ return this
239
+ }
240
+
241
+ if (index === undefined || index < 0 || index >= this.#equations.length) {
242
+ throw new IndexError("Index out of range")
243
+ }
244
+
245
+ this.#equations[index].multiply(value)
246
+
247
+ return this
248
+ }
249
+
250
+ reduce(): this {
251
+ // reduce all equations at once.
252
+ this.equations.forEach(equ => equ.reduce())
253
+ return this
254
+ }
255
+
256
+ // ------------------------------------------
257
+ reorder = (): this => {
258
+ for (const E of this.#equations) {
259
+ E.reorder()
260
+ }
261
+
262
+ return this
263
+ }
264
+
265
+ solve(): Solution[] {
266
+ const [matrix, vector] = this.matrix
267
+ // Solve the matrix
268
+
269
+ // Make the augmented matrix (matrix + vector)
270
+ const augmentedMatrix: Fraction[][] = matrix.map((row, index) => [...row, vector[index]])
271
+
272
+ // Reduce the matrix
273
+ for (let i = 0; i < matrix.length; i++) {
274
+ // Find the pivot (the first non-zero element in the row)
275
+ let pivot = augmentedMatrix[i][i].clone()
276
+ if (pivot.isZero()) {
277
+ // throw new Error('Divide by zero !')
278
+ // Search a line below that would add it.
279
+ const row_to_add = augmentedMatrix
280
+ .find((row, index) => {
281
+ return index > i && !row[i].isZero()
282
+ })
283
+
284
+ if (row_to_add) {
285
+ augmentedMatrix[i].forEach((value, index) => value.add(row_to_add[index]))
286
+ pivot = augmentedMatrix[i][i].clone()
287
+ } else {
288
+ throw new MathError('Unsolvable...')
289
+ }
290
+
291
+
292
+ }
293
+
294
+ // Normalize the row: divide all elements by the pivot
295
+ // the pivot is now 1
296
+ augmentedMatrix[i] = augmentedMatrix[i].map(x => x.divide(pivot))
297
+
298
+ // reduce the other rows using the pivot.
299
+ for (let j = 0; j < matrix.length; j++) {
300
+ if (j === i) {
301
+ continue
302
+ }
303
+
304
+ const factor = augmentedMatrix[j][i].clone().opposite()
305
+ for (let k = 0; k < augmentedMatrix[j].length; k++) {
306
+ augmentedMatrix[j][k].add(augmentedMatrix[i][k].clone().multiply(factor))
307
+ }
308
+
309
+ // Check if the asSystem is undetermined (no solution or infinite solutions)
310
+ // the j line must not be all zeros
311
+ // the last element must be zero => the asSystem is undetermined
312
+ // the last element must not be zero => the asSystem is impossible
313
+ if (augmentedMatrix[j].slice(0, augmentedMatrix[j].length - 1).every(x => x.isZero())) {
314
+ if (augmentedMatrix[j][augmentedMatrix[j].length - 1].isZero()) {
315
+ // Infinite solution
316
+ return this.#mapMatrixSolutions()
317
+ } else {
318
+ // No solution
319
+ return []
320
+ }
321
+ }
322
+ }
323
+ }
324
+
325
+ return this.#mapMatrixSolutions(augmentedMatrix)
326
+
327
+ }
328
+
329
+ solve_compute_factors(letter: string):
330
+ [{ id: number, factor: number }, { id: number, factor: number }][] {
331
+ // when solving, every monoms with a variable is on the left !
332
+ // and every coefficient are relative numbers.
333
+ const result: [{ id: number, factor: number }, { id: number, factor: number }][] = []
334
+ const coefficients = this.equations.map(equ => equ.left.monomByLetter(letter).coefficient.value)
335
+
336
+ // search for a factor
337
+ coefficients.forEach((reference, index) => {
338
+ for (let i = index + 1; i < coefficients.length; i++) {
339
+ const lcm = Numeric.lcm(reference, coefficients[i])
340
+
341
+ const sign = reference < 0 ? -1 : 1
342
+ result.push([
343
+ {
344
+ id: index, factor: sign * lcm / reference
345
+ }, {
346
+ id: i, factor: -sign * lcm / coefficients[i]
347
+ }])
348
+ }
349
+ })
350
+
351
+ // Sort the value: prefer the smallest absolute values (1/-1, 2/-2, ...)
352
+ return result.sort((a, b) => {
353
+ return (Math.abs(a[0].factor) + Math.abs(a[1].factor)) - (Math.abs(b[0].factor) + Math.abs(b[1].factor))
354
+ })
355
+ }
356
+
357
+ subtract(value: InputValue<LinearSystem | Equation | Polynom>, index?: number): this {
358
+ if (value instanceof LinearSystem) {
359
+ const length = value.equations.length
360
+ if (length !== this.#equations.length) {
361
+ throw new DimensionError("The number of equations must be the same")
362
+ }
363
+
364
+ for (let i = 0; i < length; i++) {
365
+ this.#equations[i].subtract(value.equations[i])
366
+ }
367
+ } else {
368
+ if (index === undefined || index < 0 || index >= this.#equations.length) {
369
+ throw new IndexError("Index out of range")
370
+ }
371
+ const equ = new Equation(value)
372
+ this.#equations[index].subtract(equ)
373
+ }
374
+
375
+ return this
376
+ }
377
+
378
+ get variables(): string[] {
379
+ return this.#variables
380
+ }
381
+
382
+ set variables(value: string | string[]) {
383
+ const vars = (typeof value === "string") ? value.split('') : [...value]
384
+ vars.sort()
385
+ this.#variables = vars
386
+ }
387
+
388
+ #findLetters = (): this => {
389
+ this.#variables = this.#equations.reduce((acc: string[], equ) => {
390
+ return [...new Set([...acc, ...equ.variables])]
391
+ }, [])
392
+
393
+ this.#variables.sort()
394
+ return this
395
+ }
396
+
397
+ #makeMatrix = (): [Fraction[][], Fraction[]] => {
398
+ // Make the matrix
399
+ const matrix: Fraction[][] = []
400
+ const vector: Fraction[] = []
401
+
402
+ for (const E of this.#equations) {
403
+ const row: Fraction[] = []
404
+
405
+ const equ = E.clone().reorder()
406
+ for (const L of this.variables) {
407
+ const m = equ.left.monomByLetter(L)
408
+ row.push(m.coefficient)
409
+ }
410
+
411
+ // Add the "no letter part"
412
+ vector.push(equ.right.monoms[0].coefficient)
413
+
414
+ // Add to the matrix
415
+ matrix.push(row)
416
+ }
417
+
418
+ return [matrix, vector]
419
+ }
420
+
421
+ #mapMatrixSolutions(matrix?: Fraction[][]): Solution[] {
422
+ const result: Solution[] = []
423
+
424
+ if(!matrix){
425
+ // infinite solutions
426
+ this.variables.forEach(letter=>{
427
+ const sol = Solution.fromFraction(Infinity)
428
+ sol.variable = letter
429
+ result.push(sol)
430
+ })
431
+
432
+ return result
433
+ }
434
+
435
+ matrix.forEach((x, index) => {
436
+
437
+ const sol = Solution.fromFraction(x[x.length - 1])
438
+ sol.variable = this.variables[index]
439
+
440
+ result.push(sol)
441
+ })
442
+
443
+ return result
444
+ }
445
+
446
+ }