pimath 0.2.13 → 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 +856 -1882
  2. package/dist/pimath.js.map +1 -1
  3. package/package.json +2 -2
  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,720 +1,724 @@
1
- import type {
2
- FACTOR_TABLE_OF_SIGNS,
3
- IAlgebra,
4
- IExpression,
5
- InputAlgebra,
6
- InputValue,
7
- IPiMathObject,
8
- literalType,
9
- POLYFACTOR_TABLE_OF_SIGNS,
10
- TABLE_OF_SIGNS_VALUES
11
- } from "../pimath.interface"
12
- import {Fraction} from "../coefficients"
13
- import {MathError, NotImplementedError, ParseError} from "../errors"
14
- import {Factor, FACTOR_DISPLAY} from "./factor"
15
- import {Polynom} from "./polynom"
16
- import type {Solution} from "../analyze"
17
- import {splitIfOutsideParentheses, stripParenthesis} from "../helpers"
18
-
19
-
20
- // PolyFactor is a class that represents a polynomial in factored form.
21
- export class PolyFactor implements IPiMathObject<PolyFactor>,
22
- IExpression<PolyFactor>,
23
- IAlgebra<PolyFactor> {
24
-
25
- #displayMode: FACTOR_DISPLAY = FACTOR_DISPLAY.POWER
26
- #factors: Factor[] = []
27
-
28
- constructor(...values: (InputAlgebra<Polynom> | Factor | PolyFactor)[]) {
29
- this.parse(...values)
30
- return this
31
- }
32
-
33
- public parse(...values: (InputAlgebra<Polynom> | Factor | PolyFactor)[]): this {
34
- // Init the factors list
35
- this.#factors = []
36
-
37
- // Nothing to create
38
- if (values.length === 0) {
39
- return this
40
- }
41
-
42
- values.forEach(value => {
43
- if(typeof value==="string"){
44
- return this.fromString(value)
45
- }else if (value instanceof PolyFactor) {
46
- this.#factors.push(...value.factors.map(f => f.clone()))
47
- } else {
48
- this.#factors.push(new Factor(value))
49
- }
50
- })
51
-
52
- return this
53
- }
54
-
55
- public clone(): PolyFactor {
56
- return new PolyFactor(...this.#factors.map(f => f.clone()))
57
- }
58
-
59
- public get tex(): string {
60
- const {num, den} = this.#extractNumeratorAndDenominator()
61
-
62
- if (den.length === 0) {
63
- if (num.length === 1) {
64
- return num[0].asSingle.tex
65
- }
66
-
67
- return num.map(f => f.tex).join("")
68
- }
69
-
70
- // There is a numerator and a denominator
71
- const numTeX = num.length === 1 ? num[0].asSingle.tex : num.map(f => f.tex).join("")
72
- const denTeX = den.length === 1 ? den[0].asSingle.tex : den.map(f => f.tex).join("")
73
-
74
- return `\\frac{ ${numTeX} }{ ${denTeX} }`
75
-
76
- }
77
-
78
- public get display(): string {
79
- const {num, den} = this.#extractNumeratorAndDenominator()
80
-
81
- if (den.length === 0) {
82
- if (num.length === 1) {
83
- return num[0].asSingle.display
84
- }
85
-
86
- return num.map((f, index) =>
87
- index === 0 && f.polynom.monoms.length === 1 ? f.asSingle.display : f.display
88
- ).join("")
89
- }
90
-
91
- // There is a numerator and a denominator
92
- const numTeX = num.length === 1 ? num[0].asSingle.display : num.map(f => f.display).join("")
93
- const denTeX = den.length === 1 ? den[0].asSingle.display : den.map(f => f.display).join("")
94
-
95
- return `(${numTeX})/(${denTeX})`
96
-
97
- }
98
-
99
-
100
-
101
-
102
- static #gcdWith(PF1: PolyFactor, PF2: PolyFactor): PolyFactor {
103
- // Get all factors of the two polynomials
104
- // Find the common factors
105
- const factors1 = keyFactors(PF1)
106
-
107
- const factors2 = keyFactors(PF2)
108
-
109
- const common = Object.keys(factors1).filter(k => Object.hasOwn(factors2, k))
110
-
111
- // Find the minimum power of the common factors
112
- const factors = common.map(k => {
113
- const power = factors1[k].reduce((acc, f) => acc.add(f.power), new Fraction('0'))
114
- const power2 = factors2[k].reduce((acc, f) => acc.add(f.power), new Fraction('0'))
115
- return new Factor(k, Fraction.min(power, power2))
116
- })
117
-
118
- return new PolyFactor(...factors)
119
- }
120
-
121
- static #lcmWith(PF1: PolyFactor, PF2: PolyFactor): PolyFactor {
122
-
123
- const factors1 = keyFactors(PF1)
124
-
125
- const factors2 = keyFactors(PF2)
126
-
127
- const common = [...new Set([...Object.keys(factors1), ...Object.keys(factors2)])]
128
-
129
- // Find the maximum power of the common factors
130
- const factors = common.map(k => {
131
- const power =
132
- Object.hasOwn(factors1, k) ?
133
- factors1[k].reduce((acc, f) => acc.add(f.power), new Fraction('0')) :
134
- new Fraction(0)
135
- const power2 = Object.hasOwn(factors2, k) ?
136
- factors2[k].reduce((acc, f) => acc.add(f.power), new Fraction('0')) :
137
- new Fraction(0)
138
- return new Factor(k, Fraction.max(power, power2))
139
- })
140
-
141
- return new PolyFactor(...factors)
142
- }
143
-
144
- public static gcd(...values: PolyFactor[]): PolyFactor {
145
- if (values.length === 0) {
146
- return new PolyFactor().one()
147
- }
148
- if (values.length === 1) {
149
- return values[0]
150
- }
151
- if (values.length === 2) {
152
- return PolyFactor.#gcdWith(values[0], values[1])
153
- }
154
-
155
- // values is not undefined,
156
- let PF = values[0]
157
- values.shift()
158
- values.forEach(value => PF = PolyFactor.#gcdWith(PF, value))
159
-
160
- return PF
161
- }
162
-
163
- public static lcm(...values: PolyFactor[]): PolyFactor {
164
- if (values.length === 0) {
165
- return new PolyFactor().one()
166
- }
167
- if (values.length === 1) {
168
- return values[0]
169
- }
170
-
171
- let PF = values[0]
172
- values.shift()
173
- values.forEach(value => PF = PolyFactor.#lcmWith(PF, value))
174
- return PF
175
- }
176
-
177
- public add(...values: PolyFactor[]): this {
178
- // Adding two (or more) polyfactors:
179
- // if both are numerators only, use the common polynom
180
- // if there are denominators, multiply every polyfactors to match the lcm polyfactor.
181
-
182
- const numerators: PolyFactor[] = [this.numerator, ...values.map(x => x.numerator)]
183
- const denominators: PolyFactor[] = [this.denominator, ...values.map(x => x.denominator)]
184
-
185
- let denominator: PolyFactor | undefined
186
- if (denominators.some(d => d.factors.length > 0)) {
187
- // At least one of the denominators is not empty.
188
- const lcm = PolyFactor.lcm(...denominators)
189
-
190
- // Multiply each numerators by the complementary.
191
- numerators.forEach((n, index) => {
192
- n.multiply(lcm.clone().divide(denominators[index]))
193
- })
194
-
195
- // Set the common denominator to the lcm PolyFctor
196
- denominator = lcm
197
- }
198
-
199
- // Now, every polyfactor should have a common denominator. Just add the numerators.
200
- const gcd = PolyFactor.gcd(...numerators)
201
- const remainingPolynom = new Polynom(0)
202
- .add(...numerators.map(pf => {
203
- return pf
204
- .divide(gcd).reduce()
205
- .develop() // should be a polyfactor with one factor, with a power of 1
206
- .factors[0]
207
- .polynom
208
- })
209
- ).reduce()
210
-
211
- this.#factors = [
212
- ...gcd.factors,
213
- new Factor(remainingPolynom)
214
- ]
215
-
216
- if (denominator) {
217
- this.divide(denominator)
218
- }
219
-
220
- // Remove all factors with a power of zero
221
- this.#factors = this.#factors.filter(x => !x.power.isZero())
222
-
223
- return this
224
- }
225
-
226
- get asPower(): this {
227
- this.#displayMode = FACTOR_DISPLAY.POWER
228
- return this
229
- }
230
-
231
- get asRoot(): this {
232
- this.#displayMode = FACTOR_DISPLAY.ROOT
233
- return this
234
- }
235
-
236
- public degree(letter?: string): Fraction {
237
- return this.#factors.reduce((acc, f) => acc.add(f.degree(letter)), new Fraction('0'))
238
- }
239
-
240
- get denominator(): PolyFactor {
241
- return new PolyFactor(...this.#factors
242
- .filter(f => f.power.isNegative())
243
- .map(f => f.clone().inverse())
244
- )
245
- }
246
-
247
- public derivative(): this {
248
- // (fgh)' = f'gh+fg'h+fgh'
249
- // dPF = [f'gh, fg'h, fgh']
250
- const dPF: PolyFactor[] = []
251
-
252
- const length = this.#factors.length
253
-
254
- for (let i = 0; i < length; i++) {
255
- // unchanged factors
256
- const factors = this.#factors.slice()
257
- // derivative factor
258
- const derivativeFactor = factors.splice(i, 1)[0].derivative()
259
- // Add the product of factors
260
- dPF.push(
261
- new PolyFactor(...factors, ...derivativeFactor))
262
- }
263
-
264
- // Reduce the polyFactors
265
- dPF.forEach(pf => pf.reduce())
266
-
267
- const first = dPF.shift()
268
- if (first !== undefined) {
269
- this.#factors = first.factors
270
- }
271
-
272
- // Add each factors together.
273
- return this.add(...dPF)
274
- }
275
-
276
- public develop(): PolyFactor {
277
- // Develop each factor and multiply them
278
- const N = new Polynom('1')
279
- const D = new Polynom('1')
280
-
281
- this.numerator.factors.forEach(f => {
282
- N.multiply(f.develop())
283
- })
284
- this.denominator.factors.forEach(f => {
285
- D.multiply(f.develop())
286
- })
287
-
288
- return new PolyFactor().fromPolynom(N, D)
289
- }
290
-
291
- public divide(value: PolyFactor): this {
292
- this.#factors = this.#factors.concat(value.clone().factors.map(f => f.inverse()))
293
- return this
294
- }
295
-
296
- public evaluate(values: InputValue<Fraction> | literalType<number | Fraction>, asNumeric?: boolean): number | Fraction {
297
- if (asNumeric) {
298
- return this.#factors
299
- .reduce((acc, f) => acc * (f.evaluate(values, asNumeric) as number), 1)
300
- }
301
-
302
- return this.#factors
303
- .reduce((acc, f) => acc.multiply(f.evaluate(values)), new Fraction('1'))
304
- }
305
-
306
- public factorize(letter?: string): PolyFactor {
307
- // Go through each factors.
308
- // If it can be factorized, remove the factor (set its power to zero) and insert the new factors instead.
309
- const newFactors: Factor[] = []
310
-
311
- this.#factors.forEach(factor => {
312
- const factors = factor.polynom.factorize(letter)
313
- if (factors.length > 1) {
314
- const pow = factor.power.clone()
315
- newFactors.push(...factors.map(x => new Factor(x, pow)))
316
- } else {
317
- newFactors.push(factor.clone())
318
- }
319
- })
320
-
321
- const result = new PolyFactor(...newFactors)
322
- const numerator = result.numerator.reduce()
323
- const denominator = result.denominator.reduce()
324
-
325
- return numerator.divide(denominator)
326
- }
327
-
328
- public get factors(): Factor[] {
329
- return this.#factors
330
- }
331
-
332
- public set factors(value: Factor[]) {
333
- this.#factors = value
334
- }
335
-
336
- public fromPolynom(numerator: InputAlgebra<Polynom>, denominator?: InputAlgebra<Polynom>): this {
337
- // fromPolynom loads the numerator and denominator as is, without factorizing !
338
- // TODO: fromPolynom should parse the string if it's factorized: 3x(...)^3(..)^2(12x)
339
- this.#factors = [new Factor(new Polynom(numerator))]
340
-
341
- if (denominator) {
342
- const polynom = new Polynom(denominator)
343
-
344
- if (polynom.isOne()) return this
345
-
346
- if (polynom.isZero()) throw new MathError("Cannot divide by zero")
347
-
348
- this.#factors.push(new Factor(polynom, -1))
349
- }
350
- return this
351
- }
352
-
353
- public fromString(value: string): this {
354
- this.#factors = []
355
-
356
- // Two cases :
357
- // - either the string is given as a factorized string
358
- // - or the string has something that prevents this => use fromPolynom
359
-
360
- const [num, ...den] = splitIfOutsideParentheses(value, '/')
361
- if(num==='') throw new ParseError('Parsing a PolyFactor from a string requires a numerator')
362
- if(den.length>1) throw new ParseError('Parsing a PolyFactor from a string only allows max one signe "/"')
363
-
364
- if(den.length===0) {
365
- this.#factors = Factor.factorsFromString(stripParenthesis(num), true)
366
- }else{
367
- this.#factors = [
368
- ...Factor.factorsFromString(stripParenthesis(num), true),
369
- ...Factor.factorsFromString(stripParenthesis(den[0]), false),
370
- ]
371
- }
372
-
373
- return this
374
- }
375
-
376
- /**
377
- * Get the roots of the PolyFactor.
378
- */
379
- public getRoots(): Solution[] {
380
- return []
381
- }
382
-
383
- public getZeroes(): Solution[] {
384
- // Calculate the list of roots (ordered, unique)
385
- const roots: Solution[] = ([] as Solution[])
386
- .concat(...this.#factors.map(x => x.polynom.getZeroes()))
387
- // .concat(...tos.map(x => x.roots))
388
-
389
- // Sort the values.
390
- roots.sort((a, b) => a.value - b.value)
391
- // Remove duplicates.
392
- return roots
393
- .filter((value, index, self) =>
394
- index === self.findIndex((t) =>
395
- t.value === value.value
396
- )
397
- )
398
- }
399
-
400
- public hasVariable(letter: string): boolean {
401
- return this.#factors.some(f => f.hasVariable(letter))
402
- }
403
-
404
- public inverse(): this {
405
- this.#factors = this.#factors.map(f => f.inverse())
406
- return this
407
- }
408
-
409
- public isEqual(value: PolyFactor): boolean {
410
- const gcd = PolyFactor.gcd(this, value)
411
-
412
- const PF1 = this.clone().divide(gcd).reduce()
413
- const PF2 = value.clone().divide(gcd).reduce()
414
-
415
- return PF1.isOne() && PF2.isOne()
416
- }
417
-
418
- public isOne(): boolean {
419
- return this.#factors.every(f => f.isOne())
420
- }
421
-
422
- public isZero(): boolean {
423
- return this.#factors.every(f => f.isZero())
424
- }
425
-
426
- public multiply(...values: (InputAlgebra<Polynom> | Factor |PolyFactor)[]): this {
427
- values.forEach(value => {
428
- const PF = new PolyFactor( value )
429
- this.#factors = this.#factors.concat(PF.factors)
430
- })
431
-
432
- return this
433
- }
434
-
435
- get numerator(): PolyFactor {
436
- return new PolyFactor(...this.#factors.filter(f => f.power.isPositive()))
437
- }
438
-
439
- public one(): this {
440
- this.#factors = [new Factor('1', '1')]
441
- return this
442
- }
443
-
444
- public opposite(): this {
445
- // Add the -1 factor or remove if it exists
446
- const index = this.#factors.findIndex(f => f.display === '(-1)')
447
-
448
- if (index >= 0) {
449
- this.#factors.splice(index, 1)
450
- } else {
451
- this.#factors.push(new Factor('-1', '1'))
452
- }
453
-
454
- return this
455
- }
456
-
457
- public pow(value: number | Fraction): this {
458
- this.#factors = this.#factors.map(f => f.pow(value))
459
- return this
460
- }
461
-
462
- public primitive(): PolyFactor {
463
- throw new NotImplementedError("Method not implemented.")
464
- }
465
-
466
- public reduce(): this {
467
- // Regroup the factors by their base
468
- const factors = keyFactors(this)
469
-
470
- // Multiply the factors of the same base
471
- this.#factors = Object.values(factors)
472
- .map(f => {
473
- const base = f[0].polynom
474
- const power = f.reduce((acc, f) => acc.add(f.power), new Fraction('0'))
475
-
476
- return new Factor(base, power.reduce())
477
- })
478
- .filter(f => !f.power.isZero())
479
-
480
- // Everything is reduced - merge all monomial factors together.
481
- const numfactor = new Factor(1)
482
- const denfactor = new Factor(1)
483
- const monomials = this.#factors.filter(x=>x.polynom.monoms.length===1 && x.power.isRelative())
484
- if(monomials.length>0){
485
- monomials.forEach(m=> {
486
- if(m.power.isPositive()){
487
- numfactor.multiply(new Factor(m.polynom.clone().pow(m.power.value)))
488
- }else{
489
- denfactor.multiply(new Factor(m.polynom.clone().pow(-m.power.value)))
490
- }
491
- })
492
-
493
- this.#factors = this.#factors.filter(x=>!(x.polynom.monoms.length===1 && x.power.isRelative()))
494
- if (!denfactor.polynom.isOne()){this.#factors.push(denfactor.inverse())}
495
- if (!numfactor.polynom.isOne()){this.#factors.push(numfactor)}
496
- }
497
-
498
- return this
499
- }
500
-
501
- public root(value: number): this {
502
- this.#factors = this.#factors.map(f => f.root(value))
503
- return this
504
- }
505
-
506
- /**
507
- * Reoarder the factors using :
508
- * 1. number of monoms
509
- * 2. degree of polynom
510
- * 3. power of polyfactor
511
- */
512
- public sort(letter?: string): this {
513
- // Sort each polynom
514
- this.#factors.forEach(f=>f.polynom.reorder(letter))
515
-
516
- this.#factors.sort((a, b) => {
517
- // If the compare powers are opposite, the negative power goes to the end.
518
- const aPower = a.power.value
519
- const bPower = b.power.value
520
- if (aPower * bPower < 0) {
521
- return -aPower
522
- }
523
-
524
- // Monom length
525
- const aLength = a.polynom.monoms.length
526
- const bLength = b.polynom.monoms.length
527
- if (aLength !== bLength) {
528
- return aLength - bLength
529
- }
530
-
531
- // The monom length are the same, check the polynom degree.
532
- const aDegree = a.polynom.degree(letter).value
533
- const bDegree = b.polynom.degree(letter).value
534
- if (aDegree !== bDegree) {
535
- return aDegree - bDegree
536
- }
537
-
538
- // The power of the PolyFactor
539
- if (aPower !== bPower) {
540
- return aPower - bPower
541
- }
542
-
543
- // It the monoms length are two AND of degree one, sort by the zero.
544
- if(a.polynom.degree(letter).isOne() && b.polynom.degree(letter).isOne()){
545
- const aSol = a.polynom.monoms[1].coefficient.clone().opposite().divide(a.polynom.monoms[0].coefficient).value
546
- const bSol = b.polynom.monoms[1].coefficient.clone().opposite().divide(b.polynom.monoms[0].coefficient).value
547
-
548
- return aSol - bSol
549
- }
550
-
551
- return a.degree().isLeq(b.degree()) ? -1 : 1
552
- })
553
-
554
- return this
555
- }
556
-
557
- public sqrt(): this {
558
- this.#factors = this.#factors.map(f => f.sqrt())
559
- return this
560
- }
561
-
562
- public subtract(...values: PolyFactor[]): this {
563
- return this.add(...values.map(f => f.opposite()))
564
- }
565
-
566
- public tableOfSigns(): POLYFACTOR_TABLE_OF_SIGNS {
567
- // Calculate the table of signs for each factor
568
- const roots = this.getZeroes()
569
- const roots_key = roots.map(x => x.value)
570
-
571
- // Get the table of signs of every factors
572
-
573
-
574
- // These signs are NOT aligned
575
- const factors: FACTOR_TABLE_OF_SIGNS[] = this.factorize().factors
576
- .map(factor => {
577
- return {factor: new Factor(factor), ...factor.tableOfSigns()}
578
- })
579
-
580
-
581
- // Build the table of signs with extra roots for each factors.
582
- factors.forEach(item => {
583
- // Create the new signs row
584
- const empty_signs: TABLE_OF_SIGNS_VALUES[] = new Array(2 * roots.length + 1).fill('') as unknown as TABLE_OF_SIGNS_VALUES[]
585
-
586
- let sign = item.signs.shift() as unknown as TABLE_OF_SIGNS_VALUES
587
- let root = item.roots.shift()
588
-
589
- const aligned_signs = empty_signs.map((_sign, index) => {
590
- if (index % 2 === 0) {
591
- return sign
592
- }
593
-
594
- // The sign for this indexed root is a t(ab)
595
- if (root?.value !== roots_key[(index - 1) / 2]) {
596
- return 't'
597
- }
598
-
599
- // The indexed root is the new root
600
- const rootSign = item.signs.shift() as unknown as TABLE_OF_SIGNS_VALUES
601
-
602
- // Make the new signs.
603
- sign = item.signs.shift() as unknown as TABLE_OF_SIGNS_VALUES
604
- root = item.roots.shift()
605
- return rootSign
606
- })
607
-
608
- // Set the roots for this item to all roots
609
- item.roots = roots
610
- item.signs = aligned_signs
611
- })
612
-
613
- // Build the table of signs with extra roots : reault line
614
- const signs: TABLE_OF_SIGNS_VALUES[] = factors
615
- .map((item) => [...item.signs])
616
- .reduce<TABLE_OF_SIGNS_VALUES[]>((a, b) => {
617
- if (a.length === 0) {
618
- return b
619
- }
620
-
621
- // a and b are "not aligned"
622
- b.forEach((value, index) => {
623
- // Case of a zero, invalid or tab value
624
- // tab < zero < defence
625
- switch (value) {
626
- case "d":
627
- a[index] = "d"
628
- break
629
- case "z":
630
- a[index] = a[index] === "d" ? "d" : "z"
631
- break
632
- case "h":
633
- a[index] = "h"
634
- break
635
- case "-":
636
- a[index] = a[index] === "h" ? "h" : a[index] === "-" ? "+" : "-"
637
- break
638
- }
639
- })
640
-
641
- return a
642
- }, [])
643
-
644
- return {signs, roots, factors}
645
- }
646
-
647
- public get variables(): string[] {
648
- return this.#factors
649
- .reduce((acc: string[], f: Factor) => acc.concat(f.variables), [])
650
- }
651
-
652
- public zero(): this {
653
- this.#factors = [new Factor('0', '1')]
654
- return this
655
- }
656
-
657
- #extractNumeratorAndDenominator() {
658
- let num: Factor[],
659
- den: Factor[] = []
660
-
661
- if (this.#displayMode === FACTOR_DISPLAY.ROOT) {
662
- // the power are positive integers
663
- num = this.numerator.factors
664
- den = this.denominator.factors
665
- } else {
666
- num = this.#factors
667
- }
668
-
669
- // There is no factor
670
- if (num.length === 0) {
671
- num = [new Factor('1')]
672
- }
673
- return {num, den}
674
- }
675
-
676
- }
677
-
678
- function keyFactors(value: PolyFactor): Record<string, Factor[]> {
679
- const k_numerator = new Fraction().one()
680
- const k_denominator = new Fraction().one()
681
-
682
- const kF = value.factors
683
- .reduce((acc: Record<string, Factor[]>, f) => {
684
- // It's only a value (without roots
685
- // TODO: implement reduce root numeric.
686
- if (f.polynom.degree().isZero() && f.power.isRelative()) {
687
- const value = f.polynom.monoms[0].coefficient.pow(f.power.clone().abs())
688
-
689
- if (f.power.isPositive()) {
690
- k_numerator.multiply(value)
691
- } else {
692
- k_denominator.multiply(value)
693
- }
694
-
695
- return acc
696
- }
697
-
698
- // It's a polynom
699
- const base = f.polynom.display
700
- if (Object.hasOwn(acc, base)) {
701
- acc[base].push(f)
702
- } else {
703
- acc[base] = [f]
704
- }
705
-
706
- return acc
707
- }, {})
708
-
709
-
710
- const {numerator, denominator} = k_numerator.divide(k_denominator).reduce()
711
- if(numerator!==1){
712
- kF[numerator.toString()] = [new Factor(numerator, 1)]
713
- }
714
- if(denominator!==1){
715
- kF[denominator.toString()] = [new Factor(denominator, -1)]
716
- }
717
-
718
- return kF
719
- }
720
-
1
+ import type {
2
+ FACTOR_TABLE_OF_SIGNS,
3
+ IAlgebra,
4
+ IExpression,
5
+ InputAlgebra,
6
+ InputValue,
7
+ IPiMathObject,
8
+ literalType,
9
+ POLYFACTOR_TABLE_OF_SIGNS,
10
+ TABLE_OF_SIGNS_VALUES
11
+ } from "../pimath.interface"
12
+ import {Fraction} from "../coefficients"
13
+ import {MathError, NotImplementedError, ParseError} from "../errors"
14
+ import {Factor, FACTOR_DISPLAY} from "./factor"
15
+ import {Polynom} from "./polynom"
16
+ import type {Solution} from "../analyze"
17
+ import {splitIfOutsideParentheses, stripParenthesis} from "../helpers"
18
+
19
+
20
+ // PolyFactor is a class that represents a polynomial in factored form.
21
+ export class PolyFactor implements IPiMathObject<PolyFactor>,
22
+ IExpression<PolyFactor>,
23
+ IAlgebra<PolyFactor> {
24
+
25
+ #displayMode: FACTOR_DISPLAY = FACTOR_DISPLAY.POWER
26
+ #factors: Factor[] = []
27
+
28
+ constructor(...values: (InputAlgebra<Polynom> | Factor | PolyFactor)[]) {
29
+ this.parse(...values)
30
+ return this
31
+ }
32
+
33
+ parse(...values: (InputAlgebra<Polynom> | Factor | PolyFactor)[]): this {
34
+ // Init the factors list
35
+ this.#factors = []
36
+
37
+ // Nothing to create
38
+ if (values.length === 0) {
39
+ return this
40
+ }
41
+
42
+ values.forEach(value => {
43
+ if(typeof value==="string"){
44
+ return this.fromString(value)
45
+ }else if (value instanceof PolyFactor) {
46
+ this.#factors.push(...value.factors.map(f => f.clone()))
47
+ } else {
48
+ this.#factors.push(new Factor(value))
49
+ }
50
+ })
51
+
52
+ return this
53
+ }
54
+
55
+ copy(): this {
56
+ throw new NotImplementedError("copy() is not yet implemented for PolyFactor")
57
+ }
58
+
59
+ clone(): PolyFactor {
60
+ return new PolyFactor(...this.#factors.map(f => f.clone()))
61
+ }
62
+
63
+ get tex(): string {
64
+ const {num, den} = this.#extractNumeratorAndDenominator()
65
+
66
+ if (den.length === 0) {
67
+ if (num.length === 1) {
68
+ return num[0].asSingle.tex
69
+ }
70
+
71
+ return num.map(f => f.tex).join("")
72
+ }
73
+
74
+ // There is a numerator and a denominator
75
+ const numTeX = num.length === 1 ? num[0].asSingle.tex : num.map(f => f.tex).join("")
76
+ const denTeX = den.length === 1 ? den[0].asSingle.tex : den.map(f => f.tex).join("")
77
+
78
+ return `\\frac{ ${numTeX} }{ ${denTeX} }`
79
+
80
+ }
81
+
82
+ get display(): string {
83
+ const {num, den} = this.#extractNumeratorAndDenominator()
84
+
85
+ if (den.length === 0) {
86
+ if (num.length === 1) {
87
+ return num[0].asSingle.display
88
+ }
89
+
90
+ return num.map((f, index) =>
91
+ index === 0 && f.polynom.monoms.length === 1 ? f.asSingle.display : f.display
92
+ ).join("")
93
+ }
94
+
95
+ // There is a numerator and a denominator
96
+ const numTeX = num.length === 1 ? num[0].asSingle.display : num.map(f => f.display).join("")
97
+ const denTeX = den.length === 1 ? den[0].asSingle.display : den.map(f => f.display).join("")
98
+
99
+ return `(${numTeX})/(${denTeX})`
100
+
101
+ }
102
+
103
+
104
+
105
+
106
+ static #gcdWith(PF1: PolyFactor, PF2: PolyFactor): PolyFactor {
107
+ // Get all factors of the two polynomials
108
+ // Find the common factors
109
+ const factors1 = keyFactors(PF1)
110
+
111
+ const factors2 = keyFactors(PF2)
112
+
113
+ const common = Object.keys(factors1).filter(k => Object.hasOwn(factors2, k))
114
+
115
+ // Find the minimum power of the common factors
116
+ const factors = common.map(k => {
117
+ const power = factors1[k].reduce((acc, f) => acc.add(f.power), new Fraction('0'))
118
+ const power2 = factors2[k].reduce((acc, f) => acc.add(f.power), new Fraction('0'))
119
+ return new Factor(k, Fraction.min(power, power2))
120
+ })
121
+
122
+ return new PolyFactor(...factors)
123
+ }
124
+
125
+ static #lcmWith(PF1: PolyFactor, PF2: PolyFactor): PolyFactor {
126
+
127
+ const factors1 = keyFactors(PF1)
128
+
129
+ const factors2 = keyFactors(PF2)
130
+
131
+ const common = [...new Set([...Object.keys(factors1), ...Object.keys(factors2)])]
132
+
133
+ // Find the maximum power of the common factors
134
+ const factors = common.map(k => {
135
+ const power =
136
+ Object.hasOwn(factors1, k) ?
137
+ factors1[k].reduce((acc, f) => acc.add(f.power), new Fraction('0')) :
138
+ new Fraction(0)
139
+ const power2 = Object.hasOwn(factors2, k) ?
140
+ factors2[k].reduce((acc, f) => acc.add(f.power), new Fraction('0')) :
141
+ new Fraction(0)
142
+ return new Factor(k, Fraction.max(power, power2))
143
+ })
144
+
145
+ return new PolyFactor(...factors)
146
+ }
147
+
148
+ static gcd(...values: PolyFactor[]): PolyFactor {
149
+ if (values.length === 0) {
150
+ return new PolyFactor().one()
151
+ }
152
+ if (values.length === 1) {
153
+ return values[0]
154
+ }
155
+ if (values.length === 2) {
156
+ return PolyFactor.#gcdWith(values[0], values[1])
157
+ }
158
+
159
+ // values is not undefined,
160
+ let PF = values[0]
161
+ values.shift()
162
+ values.forEach(value => PF = PolyFactor.#gcdWith(PF, value))
163
+
164
+ return PF
165
+ }
166
+
167
+ static lcm(...values: PolyFactor[]): PolyFactor {
168
+ if (values.length === 0) {
169
+ return new PolyFactor().one()
170
+ }
171
+ if (values.length === 1) {
172
+ return values[0]
173
+ }
174
+
175
+ let PF = values[0]
176
+ values.shift()
177
+ values.forEach(value => PF = PolyFactor.#lcmWith(PF, value))
178
+ return PF
179
+ }
180
+
181
+ add(...values: PolyFactor[]): this {
182
+ // Adding two (or more) polyfactors:
183
+ // if both are numerators only, use the common polynom
184
+ // if there are denominators, multiply every polyfactors to match the lcm polyfactor.
185
+
186
+ const numerators: PolyFactor[] = [this.numerator, ...values.map(x => x.numerator)]
187
+ const denominators: PolyFactor[] = [this.denominator, ...values.map(x => x.denominator)]
188
+
189
+ let denominator: PolyFactor | undefined
190
+ if (denominators.some(d => d.factors.length > 0)) {
191
+ // At least one of the denominators is not empty.
192
+ const lcm = PolyFactor.lcm(...denominators)
193
+
194
+ // Multiply each numerators by the complementary.
195
+ numerators.forEach((n, index) => {
196
+ n.multiply(lcm.clone().divide(denominators[index]))
197
+ })
198
+
199
+ // Set the common denominator to the lcm PolyFctor
200
+ denominator = lcm
201
+ }
202
+
203
+ // Now, every polyfactor should have a common denominator. Just add the numerators.
204
+ const gcd = PolyFactor.gcd(...numerators)
205
+ const remainingPolynom = new Polynom(0)
206
+ .add(...numerators.map(pf => {
207
+ return pf
208
+ .divide(gcd).reduce()
209
+ .develop() // should be a polyfactor with one factor, with a power of 1
210
+ .factors[0]
211
+ .polynom
212
+ })
213
+ ).reduce()
214
+
215
+ this.#factors = [
216
+ ...gcd.factors,
217
+ new Factor(remainingPolynom)
218
+ ]
219
+
220
+ if (denominator) {
221
+ this.divide(denominator)
222
+ }
223
+
224
+ // Remove all factors with a power of zero
225
+ this.#factors = this.#factors.filter(x => !x.power.isZero())
226
+
227
+ return this
228
+ }
229
+
230
+ get asPower(): this {
231
+ this.#displayMode = FACTOR_DISPLAY.POWER
232
+ return this
233
+ }
234
+
235
+ get asRoot(): this {
236
+ this.#displayMode = FACTOR_DISPLAY.ROOT
237
+ return this
238
+ }
239
+
240
+ degree(letter?: string): Fraction {
241
+ return this.#factors.reduce((acc, f) => acc.add(f.degree(letter)), new Fraction('0'))
242
+ }
243
+
244
+ get denominator(): PolyFactor {
245
+ return new PolyFactor(...this.#factors
246
+ .filter(f => f.power.isNegative())
247
+ .map(f => f.clone().inverse())
248
+ )
249
+ }
250
+
251
+ derivative(): this {
252
+ // (fgh)' = f'gh+fg'h+fgh'
253
+ // dPF = [f'gh, fg'h, fgh']
254
+ const dPF: PolyFactor[] = []
255
+
256
+ const length = this.#factors.length
257
+
258
+ for (let i = 0; i < length; i++) {
259
+ // unchanged factors
260
+ const factors = this.#factors.slice()
261
+ // derivative factor
262
+ const derivativeFactor = factors.splice(i, 1)[0].derivative()
263
+ // Add the product of factors
264
+ dPF.push(
265
+ new PolyFactor(...factors, ...derivativeFactor))
266
+ }
267
+
268
+ // Reduce the polyFactors
269
+ dPF.forEach(pf => pf.reduce())
270
+
271
+ const first = dPF.shift()
272
+ if (first !== undefined) {
273
+ this.#factors = first.factors
274
+ }
275
+
276
+ // Add each factors together.
277
+ return this.add(...dPF)
278
+ }
279
+
280
+ develop(): PolyFactor {
281
+ // Develop each factor and multiply them
282
+ const N = new Polynom('1')
283
+ const D = new Polynom('1')
284
+
285
+ this.numerator.factors.forEach(f => {
286
+ N.multiply(f.develop())
287
+ })
288
+ this.denominator.factors.forEach(f => {
289
+ D.multiply(f.develop())
290
+ })
291
+
292
+ return new PolyFactor().fromPolynom(N, D)
293
+ }
294
+
295
+ divide(value: PolyFactor): this {
296
+ this.#factors = this.#factors.concat(value.clone().factors.map(f => f.inverse()))
297
+ return this
298
+ }
299
+
300
+ evaluate(values: InputValue<Fraction> | literalType<number | Fraction>, asNumeric?: boolean): number | Fraction {
301
+ if (asNumeric) {
302
+ return this.#factors
303
+ .reduce((acc, f) => acc * (f.evaluate(values, asNumeric) as number), 1)
304
+ }
305
+
306
+ return this.#factors
307
+ .reduce((acc, f) => acc.multiply(f.evaluate(values)), new Fraction('1'))
308
+ }
309
+
310
+ factorize(letter?: string): PolyFactor {
311
+ // Go through each factors.
312
+ // If it can be factorized, remove the factor (set its power to zero) and insert the new factors instead.
313
+ const newFactors: Factor[] = []
314
+
315
+ this.#factors.forEach(factor => {
316
+ const factors = factor.polynom.factorize(letter)
317
+ if (factors.length > 1) {
318
+ const pow = factor.power.clone()
319
+ newFactors.push(...factors.map(x => new Factor(x, pow)))
320
+ } else {
321
+ newFactors.push(factor.clone())
322
+ }
323
+ })
324
+
325
+ const result = new PolyFactor(...newFactors)
326
+ const numerator = result.numerator.reduce()
327
+ const denominator = result.denominator.reduce()
328
+
329
+ return numerator.divide(denominator)
330
+ }
331
+
332
+ get factors(): Factor[] {
333
+ return this.#factors
334
+ }
335
+
336
+ set factors(value: Factor[]) {
337
+ this.#factors = value
338
+ }
339
+
340
+ fromPolynom(numerator: InputAlgebra<Polynom>, denominator?: InputAlgebra<Polynom>): this {
341
+ // fromPolynom loads the numerator and denominator as is, without factorizing !
342
+ // TODO: fromPolynom should parse the string if it's factorized: 3x(...)^3(..)^2(12x)
343
+ this.#factors = [new Factor(new Polynom(numerator))]
344
+
345
+ if (denominator) {
346
+ const polynom = new Polynom(denominator)
347
+
348
+ if (polynom.isOne()) return this
349
+
350
+ if (polynom.isZero()) throw new MathError("Cannot divide by zero")
351
+
352
+ this.#factors.push(new Factor(polynom, -1))
353
+ }
354
+ return this
355
+ }
356
+
357
+ fromString(value: string): this {
358
+ this.#factors = []
359
+
360
+ // Two cases :
361
+ // - either the string is given as a factorized string
362
+ // - or the string has something that prevents this => use fromPolynom
363
+
364
+ const [num, ...den] = splitIfOutsideParentheses(value, '/')
365
+ if(num==='') throw new ParseError('Parsing a PolyFactor from a string requires a numerator')
366
+ if(den.length>1) throw new ParseError('Parsing a PolyFactor from a string only allows max one signe "/"')
367
+
368
+ if(den.length===0) {
369
+ this.#factors = Factor.factorsFromString(stripParenthesis(num), true)
370
+ }else{
371
+ this.#factors = [
372
+ ...Factor.factorsFromString(stripParenthesis(num), true),
373
+ ...Factor.factorsFromString(stripParenthesis(den[0]), false),
374
+ ]
375
+ }
376
+
377
+ return this
378
+ }
379
+
380
+ /**
381
+ * Get the roots of the PolyFactor.
382
+ */
383
+ getRoots(): Solution[] {
384
+ return []
385
+ }
386
+
387
+ getZeroes(): Solution[] {
388
+ // Calculate the list of roots (ordered, unique)
389
+ const roots: Solution[] = ([] as Solution[])
390
+ .concat(...this.#factors.map(x => x.polynom.getZeroes()))
391
+ // .concat(...tos.map(x => x.roots))
392
+
393
+ // Sort the values.
394
+ roots.sort((a, b) => a.value - b.value)
395
+ // Remove duplicates.
396
+ return roots
397
+ .filter((value, index, self) =>
398
+ index === self.findIndex((t) =>
399
+ t.value === value.value
400
+ )
401
+ )
402
+ }
403
+
404
+ hasVariable(letter: string): boolean {
405
+ return this.#factors.some(f => f.hasVariable(letter))
406
+ }
407
+
408
+ inverse(): this {
409
+ this.#factors = this.#factors.map(f => f.inverse())
410
+ return this
411
+ }
412
+
413
+ isEqual(value: PolyFactor): boolean {
414
+ const gcd = PolyFactor.gcd(this, value)
415
+
416
+ const PF1 = this.clone().divide(gcd).reduce()
417
+ const PF2 = value.clone().divide(gcd).reduce()
418
+
419
+ return PF1.isOne() && PF2.isOne()
420
+ }
421
+
422
+ isOne(): boolean {
423
+ return this.#factors.every(f => f.isOne())
424
+ }
425
+
426
+ isZero(): boolean {
427
+ return this.#factors.every(f => f.isZero())
428
+ }
429
+
430
+ multiply(...values: (InputAlgebra<Polynom> | Factor |PolyFactor)[]): this {
431
+ values.forEach(value => {
432
+ const PF = new PolyFactor( value )
433
+ this.#factors = this.#factors.concat(PF.factors)
434
+ })
435
+
436
+ return this
437
+ }
438
+
439
+ get numerator(): PolyFactor {
440
+ return new PolyFactor(...this.#factors.filter(f => f.power.isPositive()))
441
+ }
442
+
443
+ one(): this {
444
+ this.#factors = [new Factor('1', '1')]
445
+ return this
446
+ }
447
+
448
+ opposite(): this {
449
+ // Add the -1 factor or remove if it exists
450
+ const index = this.#factors.findIndex(f => f.display === '(-1)')
451
+
452
+ if (index >= 0) {
453
+ this.#factors.splice(index, 1)
454
+ } else {
455
+ this.#factors.push(new Factor('-1', '1'))
456
+ }
457
+
458
+ return this
459
+ }
460
+
461
+ pow(value: number | Fraction): this {
462
+ this.#factors = this.#factors.map(f => f.pow(value))
463
+ return this
464
+ }
465
+
466
+ primitive(): PolyFactor {
467
+ throw new NotImplementedError("Method not implemented.")
468
+ }
469
+
470
+ reduce(): this {
471
+ // Regroup the factors by their base
472
+ const factors = keyFactors(this)
473
+
474
+ // Multiply the factors of the same base
475
+ this.#factors = Object.values(factors)
476
+ .map(f => {
477
+ const base = f[0].polynom
478
+ const power = f.reduce((acc, f) => acc.add(f.power), new Fraction('0'))
479
+
480
+ return new Factor(base, power.reduce())
481
+ })
482
+ .filter(f => !f.power.isZero())
483
+
484
+ // Everything is reduced - merge all monomial factors together.
485
+ const numfactor = new Factor(1)
486
+ const denfactor = new Factor(1)
487
+ const monomials = this.#factors.filter(x=>x.polynom.monoms.length===1 && x.power.isRelative())
488
+ if(monomials.length>0){
489
+ monomials.forEach(m=> {
490
+ if(m.power.isPositive()){
491
+ numfactor.multiply(new Factor(m.polynom.clone().pow(m.power.value)))
492
+ }else{
493
+ denfactor.multiply(new Factor(m.polynom.clone().pow(-m.power.value)))
494
+ }
495
+ })
496
+
497
+ this.#factors = this.#factors.filter(x=>!(x.polynom.monoms.length===1 && x.power.isRelative()))
498
+ if (!denfactor.polynom.isOne()){this.#factors.push(denfactor.inverse())}
499
+ if (!numfactor.polynom.isOne()){this.#factors.push(numfactor)}
500
+ }
501
+
502
+ return this
503
+ }
504
+
505
+ root(value: number): this {
506
+ this.#factors = this.#factors.map(f => f.root(value))
507
+ return this
508
+ }
509
+
510
+ /**
511
+ * Reoarder the factors using :
512
+ * 1. number of monoms
513
+ * 2. degree of polynom
514
+ * 3. power of polyfactor
515
+ */
516
+ sort(letter?: string): this {
517
+ // Sort each polynom
518
+ this.#factors.forEach(f=>f.polynom.reorder(letter))
519
+
520
+ this.#factors.sort((a, b) => {
521
+ // If the compare powers are opposite, the negative power goes to the end.
522
+ const aPower = a.power.value
523
+ const bPower = b.power.value
524
+ if (aPower * bPower < 0) {
525
+ return -aPower
526
+ }
527
+
528
+ // Monom length
529
+ const aLength = a.polynom.monoms.length
530
+ const bLength = b.polynom.monoms.length
531
+ if (aLength !== bLength) {
532
+ return aLength - bLength
533
+ }
534
+
535
+ // The monom length are the same, check the polynom degree.
536
+ const aDegree = a.polynom.degree(letter).value
537
+ const bDegree = b.polynom.degree(letter).value
538
+ if (aDegree !== bDegree) {
539
+ return aDegree - bDegree
540
+ }
541
+
542
+ // The power of the PolyFactor
543
+ if (aPower !== bPower) {
544
+ return aPower - bPower
545
+ }
546
+
547
+ // It the monoms length are two AND of degree one, sort by the zero.
548
+ if(a.polynom.degree(letter).isOne() && b.polynom.degree(letter).isOne()){
549
+ const aSol = a.polynom.monoms[1].coefficient.clone().opposite().divide(a.polynom.monoms[0].coefficient).value
550
+ const bSol = b.polynom.monoms[1].coefficient.clone().opposite().divide(b.polynom.monoms[0].coefficient).value
551
+
552
+ return aSol - bSol
553
+ }
554
+
555
+ return a.degree().isLeq(b.degree()) ? -1 : 1
556
+ })
557
+
558
+ return this
559
+ }
560
+
561
+ sqrt(): this {
562
+ this.#factors = this.#factors.map(f => f.sqrt())
563
+ return this
564
+ }
565
+
566
+ subtract(...values: PolyFactor[]): this {
567
+ return this.add(...values.map(f => f.opposite()))
568
+ }
569
+
570
+ tableOfSigns(): POLYFACTOR_TABLE_OF_SIGNS {
571
+ // Calculate the table of signs for each factor
572
+ const roots = this.getZeroes()
573
+ const roots_key = roots.map(x => x.value)
574
+
575
+ // Get the table of signs of every factors
576
+
577
+
578
+ // These signs are NOT aligned
579
+ const factors: FACTOR_TABLE_OF_SIGNS[] = this.factorize().factors
580
+ .map(factor => {
581
+ return {factor: new Factor(factor), ...factor.tableOfSigns()}
582
+ })
583
+
584
+
585
+ // Build the table of signs with extra roots for each factors.
586
+ factors.forEach(item => {
587
+ // Create the new signs row
588
+ const empty_signs: TABLE_OF_SIGNS_VALUES[] = new Array(2 * roots.length + 1).fill('') as unknown as TABLE_OF_SIGNS_VALUES[]
589
+
590
+ let sign = item.signs.shift() as unknown as TABLE_OF_SIGNS_VALUES
591
+ let root = item.roots.shift()
592
+
593
+ const aligned_signs = empty_signs.map((_sign, index) => {
594
+ if (index % 2 === 0) {
595
+ return sign
596
+ }
597
+
598
+ // The sign for this indexed root is a t(ab)
599
+ if (root?.value !== roots_key[(index - 1) / 2]) {
600
+ return 't'
601
+ }
602
+
603
+ // The indexed root is the new root
604
+ const rootSign = item.signs.shift() as unknown as TABLE_OF_SIGNS_VALUES
605
+
606
+ // Make the new signs.
607
+ sign = item.signs.shift() as unknown as TABLE_OF_SIGNS_VALUES
608
+ root = item.roots.shift()
609
+ return rootSign
610
+ })
611
+
612
+ // Set the roots for this item to all roots
613
+ item.roots = roots
614
+ item.signs = aligned_signs
615
+ })
616
+
617
+ // Build the table of signs with extra roots : reault line
618
+ const signs: TABLE_OF_SIGNS_VALUES[] = factors
619
+ .map((item) => [...item.signs])
620
+ .reduce<TABLE_OF_SIGNS_VALUES[]>((a, b) => {
621
+ if (a.length === 0) {
622
+ return b
623
+ }
624
+
625
+ // a and b are "not aligned"
626
+ b.forEach((value, index) => {
627
+ // Case of a zero, invalid or tab value
628
+ // tab < zero < defence
629
+ switch (value) {
630
+ case "d":
631
+ a[index] = "d"
632
+ break
633
+ case "z":
634
+ a[index] = a[index] === "d" ? "d" : "z"
635
+ break
636
+ case "h":
637
+ a[index] = "h"
638
+ break
639
+ case "-":
640
+ a[index] = a[index] === "h" ? "h" : a[index] === "-" ? "+" : "-"
641
+ break
642
+ }
643
+ })
644
+
645
+ return a
646
+ }, [])
647
+
648
+ return {signs, roots, factors}
649
+ }
650
+
651
+ get variables(): string[] {
652
+ return this.#factors
653
+ .reduce((acc: string[], f: Factor) => acc.concat(f.variables), [])
654
+ }
655
+
656
+ zero(): this {
657
+ this.#factors = [new Factor('0', '1')]
658
+ return this
659
+ }
660
+
661
+ #extractNumeratorAndDenominator() {
662
+ let num: Factor[],
663
+ den: Factor[] = []
664
+
665
+ if (this.#displayMode === FACTOR_DISPLAY.ROOT) {
666
+ // the power are positive integers
667
+ num = this.numerator.factors
668
+ den = this.denominator.factors
669
+ } else {
670
+ num = this.#factors
671
+ }
672
+
673
+ // There is no factor
674
+ if (num.length === 0) {
675
+ num = [new Factor('1')]
676
+ }
677
+ return {num, den}
678
+ }
679
+
680
+ }
681
+
682
+ function keyFactors(value: PolyFactor): Record<string, Factor[]> {
683
+ const k_numerator = new Fraction().one()
684
+ const k_denominator = new Fraction().one()
685
+
686
+ const kF = value.factors
687
+ .reduce((acc: Record<string, Factor[]>, f) => {
688
+ // It's only a value (without roots
689
+ // TODO: implement reduce root numeric.
690
+ if (f.polynom.degree().isZero() && f.power.isRelative()) {
691
+ const value = f.polynom.monoms[0].coefficient.pow(f.power.clone().abs())
692
+
693
+ if (f.power.isPositive()) {
694
+ k_numerator.multiply(value)
695
+ } else {
696
+ k_denominator.multiply(value)
697
+ }
698
+
699
+ return acc
700
+ }
701
+
702
+ // It's a polynom
703
+ const base = f.polynom.display
704
+ if (Object.hasOwn(acc, base)) {
705
+ acc[base].push(f)
706
+ } else {
707
+ acc[base] = [f]
708
+ }
709
+
710
+ return acc
711
+ }, {})
712
+
713
+
714
+ const {numerator, denominator} = k_numerator.divide(k_denominator).reduce()
715
+ if(numerator!==1){
716
+ kF[numerator.toString()] = [new Factor(numerator, 1)]
717
+ }
718
+ if(denominator!==1){
719
+ kF[denominator.toString()] = [new Factor(denominator, -1)]
720
+ }
721
+
722
+ return kF
723
+ }
724
+