pimath 0.2.3 → 0.2.4

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.
@@ -69,13 +69,14 @@ export class Polynom implements IPiMathObject<Polynom>,
69
69
  * @param inputStr
70
70
  * @param values
71
71
  */
72
- public parse = (inputStr: PolynomParsingType, ...values: InputAlgebra<Monom>[]): this => {
72
+ public parse(inputStr: PolynomParsingType, ...values: InputAlgebra<Monom>[]): this {
73
73
  // Reset the main variables.
74
74
  this.#monoms = []
75
75
  this.#factors = []
76
+ this.#invalidateCache()
76
77
 
77
78
  if (typeof inputStr === 'string') {
78
- return this.#parseString(inputStr, ...values)
79
+ return this.#parseString(inputStr, ...values as InputValue<Fraction>[])
79
80
  } else if (
80
81
  (typeof inputStr === 'number' || inputStr instanceof Fraction || inputStr instanceof Monom)
81
82
  && (values.length === 0)
@@ -99,15 +100,15 @@ export class Polynom implements IPiMathObject<Polynom>,
99
100
  /**
100
101
  * Clone the polynom
101
102
  */
102
- public clone = (): Polynom => {
103
+ public clone(): Polynom {
103
104
  const P = new Polynom()
104
105
  const M: Monom[] = []
106
+ P.#defaultVariable = this.#defaultVariable
105
107
 
106
108
  for (const m of this.#monoms) {
107
109
  M.push(m.clone())
108
110
  }
109
111
 
110
-
111
112
  P.monoms = M
112
113
 
113
114
  return P
@@ -131,11 +132,11 @@ export class Polynom implements IPiMathObject<Polynom>,
131
132
  return result
132
133
  }
133
134
 
134
- public add = (...values: InputAlgebra<Polynom>[]): Polynom => {
135
+ public add(...values: InputAlgebra<Polynom>[]): this {
135
136
 
136
137
  for (const value of values) {
137
138
  if (value instanceof Polynom) {
138
- this.#monoms = this.#monoms.concat(value.monoms)
139
+ this.#monoms = this.#monoms.concat(value.monoms.map(m=>m.clone()))
139
140
  } else if (value instanceof Monom) {
140
141
  this.#monoms.push(value.clone())
141
142
  } else if (typeof value === "number" && Number.isSafeInteger(value)) {
@@ -149,16 +150,15 @@ export class Polynom implements IPiMathObject<Polynom>,
149
150
  return this.reduce()
150
151
  }
151
152
 
152
- public commonMonom = (): Monom => {
153
+ public commonMonom(): Monom {
153
154
  const M = new Monom().one()
154
155
  const numerator: number = this.gcdNumerator()
155
156
  const denominator: number = this.gcdDenominator()
156
- const degree = this.degree()
157
157
 
158
158
  M.coefficient = new Fraction(numerator, denominator)
159
159
  for (const L of this.variables) {
160
160
  // Initialize the setLetter with the max degree
161
- M.setLetter(L, degree)
161
+ M.setLetter(L, this.degree(L))
162
162
  for (const m of this.#monoms) {
163
163
  M.setLetter(L, Fraction.min(m.degree(L), M.degree(L)))
164
164
  if (M.degree(L).isZero()) {
@@ -170,7 +170,7 @@ export class Polynom implements IPiMathObject<Polynom>,
170
170
  return M
171
171
  }
172
172
 
173
- public degree = (letter?: string): Fraction => {
173
+ public degree(letter?: string): Fraction {
174
174
  let d: Fraction = new Fraction().zero()
175
175
  for (const m of this.#monoms) {
176
176
  d = Fraction.max(m.degree(letter).value, d)
@@ -179,7 +179,7 @@ export class Polynom implements IPiMathObject<Polynom>,
179
179
  return d
180
180
  }
181
181
 
182
- public derivative = (letter?: string): Polynom => {
182
+ public derivative(letter?: string): Polynom {
183
183
  const dP = new Polynom()
184
184
 
185
185
  for (const m of this.#monoms) {
@@ -189,7 +189,7 @@ export class Polynom implements IPiMathObject<Polynom>,
189
189
  return dP.reduce()
190
190
  }
191
191
 
192
- public divide = (value: InputAlgebra<Polynom>): Polynom => {
192
+ public divide(value: InputAlgebra<Polynom>): this {
193
193
 
194
194
  if (value instanceof Fraction) {
195
195
  return this.#divideByFraction(value)
@@ -204,6 +204,7 @@ export class Polynom implements IPiMathObject<Polynom>,
204
204
  const {quotient, reminder} = this.euclidean(value)
205
205
  if (reminder.isZero()) {
206
206
  this.#monoms = quotient.monoms
207
+ this.#invalidateCache()
207
208
  return this
208
209
  }
209
210
  }
@@ -214,8 +215,9 @@ export class Polynom implements IPiMathObject<Polynom>,
214
215
  throw new Error(`Cannot divide by ${value as unknown as string}`)
215
216
  }
216
217
 
217
- public empty = (): this => {
218
+ public empty(): this {
218
219
  this.#monoms = []
220
+ this.#invalidateCache()
219
221
  return this
220
222
  }
221
223
 
@@ -224,8 +226,8 @@ export class Polynom implements IPiMathObject<Polynom>,
224
226
  * @param P
225
227
  * returns {quotient: Polynom, reminder: Polynom}
226
228
  */
227
- public euclidean = (P: Polynom): IEuclidean => {
228
- const letter: string = P.variables[0]
229
+ public euclidean(P: Polynom): IEuclidean {
230
+ const letter: string | undefined = P.variables[0]
229
231
  const quotient: Polynom = new Polynom().zero()
230
232
  const reminder: Polynom = this.clone().reorder(letter)
231
233
 
@@ -239,6 +241,10 @@ export class Polynom implements IPiMathObject<Polynom>,
239
241
  }
240
242
  }
241
243
 
244
+ if (!this.degree(letter).isNatural() || !P.degree(letter).isNatural()) {
245
+ throw new Error('Euclidean division requires integer degrees')
246
+ }
247
+
242
248
  // Get at least a letter
243
249
  const maxMP: Monom = P.monomByDegree(undefined, letter)
244
250
  const degreeP: Fraction = P.degree(letter)
@@ -246,7 +252,8 @@ export class Polynom implements IPiMathObject<Polynom>,
246
252
  let newM: Monom
247
253
 
248
254
  // Make the Euclidean division of the two polynoms.
249
- let MaxIteration = this.degree(letter).value * 2
255
+ let MaxIteration = this.degree(letter).value - degreeP.value + 1
256
+
250
257
  while (reminder.degree(letter).isGeq(degreeP) && MaxIteration > 0) {
251
258
  MaxIteration--
252
259
 
@@ -272,7 +279,7 @@ export class Polynom implements IPiMathObject<Polynom>,
272
279
  return {quotient, reminder}
273
280
  }
274
281
 
275
- public evaluate = (values: literalType<Fraction | number> | InputValue<Fraction>, asNumeric?: boolean): Fraction | number => {
282
+ public evaluate(values: literalType<Fraction | number> | InputValue<Fraction>, asNumeric?: boolean): Fraction | number {
276
283
  // Return the numeric value, without using Fraction
277
284
  if (asNumeric) {
278
285
  return this.#evaluateAsNumeric(values)
@@ -281,7 +288,6 @@ export class Polynom implements IPiMathObject<Polynom>,
281
288
  // Build the evaluated fraction
282
289
  const r = new Fraction().zero()
283
290
  this.#monoms.forEach(monom => {
284
- //console.log('Evaluate polynom: ', monom.display, values, monom.evaluate(values).display);
285
291
  r.add(monom.evaluate(values, asNumeric))
286
292
  })
287
293
 
@@ -291,10 +297,10 @@ export class Polynom implements IPiMathObject<Polynom>,
291
297
  // -------------------------------------
292
298
  /**
293
299
  * Factorize a polynom and store the best results in factors.
294
- * @param letter
300
+ * @param _letter
295
301
  * TODO: Handle other letter than 'x'.
296
302
  */
297
- public factorize(letter?: string): Polynom[] {
303
+ public factorize(_letter?: string): Polynom[] {
298
304
  this.#factors = []
299
305
 
300
306
  let P = this.clone().reorder()
@@ -314,7 +320,13 @@ export class Polynom implements IPiMathObject<Polynom>,
314
320
  const solutions = new EquationSolver(P).solve()
315
321
 
316
322
  if (solutions.length === 0) {
317
- this.#factors = [this.clone()]
323
+ if (this.#factors.length === 0) {
324
+ // No common monom was extracted: the whole polynomial is irreducible
325
+ this.#factors = [this.clone()]
326
+ } else {
327
+ // Common monom was extracted: P is the irreducible remaining part
328
+ this.#factors.push(P)
329
+ }
318
330
  return this.#factors
319
331
  }
320
332
 
@@ -352,7 +364,7 @@ export class Polynom implements IPiMathObject<Polynom>,
352
364
  return this.#factors
353
365
  }
354
366
 
355
- public fromCoefficients(...values: InputValue<Fraction>[]) {
367
+ public fromCoefficients(...values: InputValue<Fraction>[]): this {
356
368
 
357
369
  this.#monoms = []
358
370
  const letter = this.#defaultVariable ?? 'x'
@@ -364,35 +376,39 @@ export class Polynom implements IPiMathObject<Polynom>,
364
376
  this.#monoms.push(monom)
365
377
  })
366
378
 
379
+ this.#invalidateCache()
367
380
  return this.reorder()
368
381
  }
369
382
 
370
- public gcdDenominator = (): number => {
383
+ public gcdDenominator(): number {
371
384
  return Numeric.gcd(...this.getDenominators())
372
385
  }
373
386
 
374
- public gcdNumerator = (): number => {
387
+ public gcdNumerator(): number {
375
388
  return Numeric.gcd(...this.getNumerators())
376
389
  }
377
390
 
378
391
  public getCoefficients(): Fraction[] {
392
+ if (!this.degree().isNatural()) {
393
+ throw new Error('getCoefficients() requires a polynomial with integer degrees')
394
+ }
395
+
379
396
  // Assume there is only one letter.
380
397
  const orderedPolynom = this.clone().reorder()
381
398
 
382
399
  const length = this.degree().value + 1
383
- const coeffs = new Array(length).fill(new Fraction(0)) as unknown as Fraction[]
400
+ const coeffs = Array.from({length}, ()=>new Fraction(0))
384
401
 
385
402
  orderedPolynom.monoms.forEach(monom => {
386
403
  const index = length - monom.degree().value - 1
387
404
  coeffs[index] = monom.coefficient.clone()
388
405
  })
389
406
 
390
- // return orderedPolynom.monoms.map(x=>x.coefficient)
391
407
  return coeffs
392
408
  }
393
409
 
394
410
  // Next functions are used for for commonMonom, which is used in the factorize method.
395
- public getDenominators = (): number[] => {
411
+ public getDenominators(): number[] {
396
412
  const denominators: number[] = []
397
413
  for (const m of this.#monoms) {
398
414
  denominators.push(m.coefficient.denominator)
@@ -401,7 +417,7 @@ export class Polynom implements IPiMathObject<Polynom>,
401
417
  return denominators
402
418
  }
403
419
 
404
- public getNumerators = (): number[] => {
420
+ public getNumerators(): number[] {
405
421
  const numerators: number[] = []
406
422
  for (const m of this.#monoms) {
407
423
  numerators.push(m.coefficient.numerator)
@@ -410,7 +426,7 @@ export class Polynom implements IPiMathObject<Polynom>,
410
426
  return numerators
411
427
  }
412
428
 
413
- public getZeroes = (): Solution[] => {
429
+ public getZeroes(): Solution[] {
414
430
  if (this.degree().isZero()) {
415
431
  return []
416
432
  }
@@ -423,7 +439,7 @@ export class Polynom implements IPiMathObject<Polynom>,
423
439
  return this.variables.includes(letter)
424
440
  }
425
441
 
426
- public integrate = (a: InputValue<Fraction>, b: InputValue<Fraction>, letter = 'x'): Fraction => {
442
+ public integrate(a: InputValue<Fraction>, b: InputValue<Fraction>, letter = 'x'): Fraction {
427
443
  const primitive = this.primitive(letter)
428
444
 
429
445
  const valuesA: literalType<Fraction> = {},
@@ -439,7 +455,7 @@ export class Polynom implements IPiMathObject<Polynom>,
439
455
  return undefined
440
456
  }
441
457
 
442
- public isDeveloped = (polynomString: string): boolean => {
458
+ public isDeveloped(polynomString: string): boolean {
443
459
  let P: Polynom
444
460
 
445
461
  // Start by removing the parenthesis after a "power"
@@ -469,7 +485,7 @@ export class Polynom implements IPiMathObject<Polynom>,
469
485
  return true
470
486
  }
471
487
 
472
- public isDividableBy = (div: Polynom): boolean => {
488
+ public isDividableBy(div: Polynom): boolean {
473
489
  // Quick evaluation.
474
490
  if (div.degree().isOne()) {
475
491
  const zero = div.getZeroes()[0]
@@ -486,7 +502,7 @@ export class Polynom implements IPiMathObject<Polynom>,
486
502
  }
487
503
  }
488
504
 
489
- public isEqual = (P: Polynom): boolean => {
505
+ public isEqual(P: Polynom): boolean {
490
506
  return this.#compare(P, '=')
491
507
  }
492
508
 
@@ -499,11 +515,11 @@ export class Polynom implements IPiMathObject<Polynom>,
499
515
  return this.#monoms.length === 1 && this.#monoms[0].coefficient.isOne() && this.degree().isZero()
500
516
  }
501
517
 
502
- public isOppositeAt = (P: Polynom): boolean => {
518
+ public isOppositeAt(P: Polynom): boolean {
503
519
  return this.#compare(P.clone().opposite(), '=')
504
520
  }
505
521
 
506
- public isSameAs = (P: Polynom): boolean => {
522
+ public isSameAs(P: Polynom): boolean {
507
523
  return this.#compare(P, 'same')
508
524
  }
509
525
 
@@ -511,11 +527,11 @@ export class Polynom implements IPiMathObject<Polynom>,
511
527
  return (this.#monoms.length === 1 && this.#monoms[0].coefficient.isZero()) || this.#monoms.length === 0
512
528
  }
513
529
 
514
- public lcmDenominator = (): number => {
530
+ public lcmDenominator(): number {
515
531
  return Numeric.lcm(...this.getDenominators())
516
532
  }
517
533
 
518
- public lcmNumerator = (): number => {
534
+ public lcmNumerator(): number {
519
535
  return Numeric.lcm(...this.getNumerators())
520
536
  }
521
537
 
@@ -523,57 +539,49 @@ export class Polynom implements IPiMathObject<Polynom>,
523
539
  return this.#monoms.length
524
540
  }
525
541
 
526
- public letters = (): string[] => {
527
- let S = new Set<string>()
542
+ public limitTo(value: InputValue<Fraction>, letter?: string): Fraction {
543
+ const f = new Fraction(value)
528
544
 
529
- for (const m of this.#monoms) {
530
- S = new Set([...S, ...m.variables])
545
+ // Finite value: evaluate directly
546
+ if (f.isFinite()) {
547
+ const l = letter ?? this.variables[0] ?? 'x'
548
+ return this.evaluate({[l]: f}) as Fraction
531
549
  }
532
550
 
551
+ const M = this.monomByDegree(undefined, letter)
552
+ const sign = M.coefficient.sign()
553
+ const degree = M.degree(letter)
533
554
 
534
- return [...S]
535
- }
536
-
537
- public limitToInfinity = (letter?: string): Fraction => {
538
- const M = this.monomByDegree(undefined, letter),
539
- sign = M.coefficient.sign(),
540
- degree = M.degree(letter)
541
-
542
- if (degree.isStrictlyPositive()) {
543
- return sign === 1 ? (new Fraction()).infinite() : (new Fraction()).infinite().opposite()
544
- } else if (degree.isZero()) {
545
- return M.coefficient
555
+ // Constant polynomial
556
+ if (degree.isZero()) {
557
+ return M.coefficient.clone()
546
558
  }
547
559
 
548
-
549
- // Any other cases
550
- return (new Fraction()).zero()
551
- }
552
-
553
- public limitToNegativeInfinity = (letter?: string): Fraction => {
554
- const M = this.monomByDegree(undefined, letter),
555
- sign = M.coefficient.sign(),
556
- degree = M.degree(letter)
557
-
558
- if (degree.isStrictlyPositive()) {
559
- return sign === -1 ? (new Fraction()).infinite() : (new Fraction()).infinite().opposite()
560
- } else if (degree.isZero()) {
561
- return M.coefficient
560
+ if (!degree.isStrictlyPositive()) {
561
+ return new Fraction().zero()
562
562
  }
563
563
 
564
+ // Limit at +∞: determined by the leading coefficient sign
565
+ if (f.isPositive()) {
566
+ return sign === 1
567
+ ? new Fraction().infinite()
568
+ : new Fraction().infinite().opposite()
569
+ }
564
570
 
565
- // Any other cases
566
- return (new Fraction()).zero()
571
+ // Limit at -∞: accounts for the parity of the degree
572
+ const degreeIsEven = degree.value % 2 === 0
573
+ const limitSign = degreeIsEven ? sign : -sign
574
+ return limitSign === 1
575
+ ? new Fraction().infinite()
576
+ : new Fraction().infinite().opposite()
567
577
  }
568
578
 
569
- public monomByDegree = (degree?: Fraction | number, letter?: string): Monom => {
570
- if (degree === undefined)
571
- // return the highest degree monom.
572
- {
579
+ public monomByDegree(degree?: Fraction | number, letter?: string): Monom {
580
+ // return the highest degree monom.
581
+ if (degree === undefined) {
573
582
  return this.monomByDegree(this.degree(letter), letter)
574
583
  }
575
584
 
576
-
577
585
  // Reduce the polynom.
578
586
  const M = this.clone().reduce()
579
587
  for (const m of M.#monoms) {
@@ -582,13 +590,12 @@ export class Polynom implements IPiMathObject<Polynom>,
582
590
  }
583
591
  }
584
592
 
585
-
586
593
  // Nothing was found - return the null monom.
587
594
  return new Monom().zero()
588
595
  }
589
596
 
590
597
  // Used in LinearSystem.tex
591
- public monomByLetter = (letter: string): Monom => {
598
+ public monomByLetter(letter: string): Monom {
592
599
  const M = this.clone().reduce()
593
600
  for (const m of M.#monoms) {
594
601
  if (m.hasVariable(letter)) {
@@ -596,7 +603,6 @@ export class Polynom implements IPiMathObject<Polynom>,
596
603
  }
597
604
  }
598
605
 
599
-
600
606
  return new Monom().zero()
601
607
  }
602
608
 
@@ -607,13 +613,13 @@ export class Polynom implements IPiMathObject<Polynom>,
607
613
 
608
614
  public set monoms(M: Monom[]) {
609
615
  this.#monoms = M
616
+ this.#invalidateCache()
610
617
  }
611
618
 
612
- public monomsByDegree = (degree?: number | Fraction, letter?: string): Monom[] => {
613
- if (degree === undefined)
614
- // return the highest degree monom.
615
- {
616
- return this.monomsByDegree(this.degree(letter))
619
+ public monomsByDegree(degree?: number | Fraction, letter?: string): Monom[] {
620
+ // return the highest degree monom.
621
+ if (degree === undefined) {
622
+ return this.monomsByDegree(this.degree(letter), letter)
617
623
  }
618
624
 
619
625
  // Reduce the polynom.
@@ -626,12 +632,10 @@ export class Polynom implements IPiMathObject<Polynom>,
626
632
  }
627
633
  }
628
634
 
629
-
630
635
  return Ms
631
- // Nothing was found - return
632
636
  }
633
637
 
634
- public multiply = (value: InputAlgebra<Polynom>): Polynom => {
638
+ public multiply(value: InputAlgebra<Polynom>): this {
635
639
 
636
640
  if (value instanceof Polynom) {
637
641
  return this.#multiplyByPolynom(value)
@@ -666,15 +670,17 @@ export class Polynom implements IPiMathObject<Polynom>,
666
670
  return this.variables.length
667
671
  }
668
672
 
669
- public one = (): this => {
673
+ public one(): this {
670
674
  this.#monoms = []
671
675
  this.#monoms.push(new Monom().one())
676
+ this.#invalidateCache()
672
677
  return this
673
678
  }
674
679
 
675
680
  // ------------------------------------------
676
- public opposite = (): this => {
681
+ public opposite(): this {
677
682
  this.#monoms = this.#monoms.map(m => m.opposite())
683
+ this.#invalidateCache()
678
684
  return this
679
685
  }
680
686
 
@@ -682,11 +688,11 @@ export class Polynom implements IPiMathObject<Polynom>,
682
688
  return this.#genDisplay('tex', false, false, true)
683
689
  }
684
690
 
685
- public pow = (nb: number): Polynom => {
691
+ public pow(nb: number): Polynom {
686
692
  return operation_pow(this as Polynom, nb).reduce()
687
693
  }
688
694
 
689
- public primitive = (letter?: string): Polynom => {
695
+ public primitive(letter?: string): Polynom {
690
696
  const dP = new Polynom()
691
697
 
692
698
  for (const m of this.#monoms) {
@@ -696,7 +702,7 @@ export class Polynom implements IPiMathObject<Polynom>,
696
702
  return dP
697
703
  }
698
704
 
699
- public reduce = (): Polynom => {
705
+ public reduce(): this {
700
706
  // Reduce the polynom
701
707
 
702
708
  // Group the monoms by similarity
@@ -714,7 +720,6 @@ export class Polynom implements IPiMathObject<Polynom>,
714
720
  }
715
721
  }
716
722
 
717
-
718
723
  i++
719
724
  }
720
725
 
@@ -728,16 +733,15 @@ export class Polynom implements IPiMathObject<Polynom>,
728
733
  m.coefficient.reduce()
729
734
  }
730
735
 
731
-
732
736
  if (this.length === 0) {
733
- return new Polynom().zero()
737
+ return this.zero()
734
738
  }
735
739
 
736
-
740
+ this.#invalidateCache()
737
741
  return this.reorder()
738
742
  }
739
743
 
740
- public reorder = (letter = 'x', revert = false): this => {
744
+ public reorder(letter = 'x', revert = false): this {
741
745
  const otherLetters = this.variables.filter(x => x !== letter)
742
746
  this.#monoms.sort(function (a, b) {
743
747
  const da = a.degree(letter).value,
@@ -773,7 +777,7 @@ export class Polynom implements IPiMathObject<Polynom>,
773
777
  * @param letter
774
778
  * @param P
775
779
  */
776
- public replaceBy = (letter: string, P: Polynom): this => {
780
+ public replaceBy(letter: string, P: Polynom): this {
777
781
  let pow: Fraction
778
782
  const resultPolynom: Polynom = new Polynom().zero()
779
783
 
@@ -790,13 +794,17 @@ export class Polynom implements IPiMathObject<Polynom>,
790
794
  m.removeVariable(letter)
791
795
 
792
796
  // Add the new monom to the result polynom
793
- resultPolynom.add(P.clone().pow(Math.abs(pow.numerator)).multiply(m))
797
+ if (pow.isStrictlyNegative()) {
798
+ throw new Error(`Cannot replace variable "${letter}" with negative power in a Polynom`)
799
+ }
800
+ resultPolynom.add(P.clone().pow(pow.numerator).multiply(m))
794
801
  }
795
802
  }
796
803
 
797
804
 
798
805
  // Reduce the monoms
799
806
  this.#monoms = resultPolynom.reduce().monoms
807
+ this.#invalidateCache()
800
808
  return this
801
809
  }
802
810
 
@@ -825,7 +833,7 @@ export class Polynom implements IPiMathObject<Polynom>,
825
833
  throw new Error('Cannot take the square root from a polynom')
826
834
  }
827
835
 
828
- public subtract = (...values: InputAlgebra<Polynom>[]): Polynom => {
836
+ public subtract(...values: InputAlgebra<Polynom>[]): this {
829
837
  for (const value of values) {
830
838
  if (value instanceof Polynom) {
831
839
  this.add(value.clone().opposite())
@@ -878,7 +886,7 @@ export class Polynom implements IPiMathObject<Polynom>,
878
886
  ]
879
887
 
880
888
  testingRoots.forEach((test, index) => {
881
- const sign = this.evaluate({x: test}, true) as number
889
+ const sign = this.evaluate({[this.variables[0] ?? 'x']: test}, true) as number
882
890
  signs[index * 2] = sign > 0 ? '+' : '-'
883
891
  })
884
892
  }
@@ -895,26 +903,17 @@ export class Polynom implements IPiMathObject<Polynom>,
895
903
  }
896
904
 
897
905
  public get variables(): string[] {
898
- let V: string[] = []
899
-
900
- for (const m of this.#monoms) {
901
- V = V.concat(m.variables)
902
- }
903
-
904
-
905
- // Remove duplicates.
906
- V = [...new Set(V)]
907
- V.sort()
908
- return V
906
+ return [...new Set(this.#monoms.flatMap(m => m.variables))].sort()
909
907
  }
910
908
 
911
909
  /**
912
910
  * Set the polynom to zero.
913
911
  * @returns {this}
914
912
  */
915
- public zero = (): this => {
913
+ public zero(): this {
916
914
  this.#monoms = []
917
915
  this.#monoms.push(new Monom().zero())
916
+ this.#invalidateCache()
918
917
  return this
919
918
  }
920
919
 
@@ -922,7 +921,7 @@ export class Polynom implements IPiMathObject<Polynom>,
922
921
  return this.getZeroes()
923
922
  }
924
923
 
925
- #compare = (P: Polynom, sign?: string): boolean => {
924
+ #compare(P: Polynom, sign?: string): boolean {
926
925
  sign ??= '='
927
926
 
928
927
  // Create clone version to reduce them without altering the original polynoms.
@@ -954,25 +953,26 @@ export class Polynom implements IPiMathObject<Polynom>,
954
953
  }
955
954
  }
956
955
 
957
-
958
- #divideByFraction = (F: Fraction): this => {
956
+ #divideByFraction(F: Fraction): this {
959
957
  for (const m of this.#monoms) {
960
958
  m.coefficient.divide(F)
961
959
  }
962
960
 
961
+ this.#invalidateCache()
963
962
  return this
964
963
  }
965
964
 
966
- #divideByInteger = (nb: number): this => {
965
+ #divideByInteger(nb: number): this {
967
966
  const nbF = new Fraction(nb)
968
967
  for (const m of this.#monoms) {
969
968
  m.coefficient.divide(nbF)
970
969
  }
971
970
 
971
+ this.#invalidateCache()
972
972
  return this
973
973
  }
974
974
 
975
- #evaluateAsNumeric = (values: literalType<number | Fraction> | InputValue<Fraction>): number => {
975
+ #evaluateAsNumeric(values: literalType<number | Fraction> | InputValue<Fraction>): number {
976
976
  let r = 0
977
977
  this.#monoms.forEach(monom => {
978
978
  r += monom.evaluate(values, true) as number
@@ -981,7 +981,7 @@ export class Polynom implements IPiMathObject<Polynom>,
981
981
  return r
982
982
  }
983
983
 
984
- #genDisplay = (output?: string, forceSign?: boolean, wrapParentheses?: boolean, withAllMultiplicationSign?: boolean): string => {
984
+ #genDisplay(output?: string, forceSign?: boolean, wrapParentheses?: boolean, withAllMultiplicationSign?: boolean): string {
985
985
  let P = ''
986
986
 
987
987
  for (const k of this.#monoms) {
@@ -989,7 +989,6 @@ export class Polynom implements IPiMathObject<Polynom>,
989
989
  continue
990
990
  }
991
991
 
992
-
993
992
  // The monom to be displayed
994
993
  let m
995
994
  if (withAllMultiplicationSign) {
@@ -1017,42 +1016,24 @@ export class Polynom implements IPiMathObject<Polynom>,
1017
1016
  return P
1018
1017
  }
1019
1018
 
1020
- #getAllPotentialFactors = (P: Polynom, maxDegree: number, letter: string): Polynom[] => {
1021
- const m1 = P.monoms[0].dividers,
1022
- m2 = P.monoms[P.monoms.length - 1].dividers
1023
-
1024
- const allDividers: Polynom[] = []
1025
- m1.forEach(m1d => {
1026
- // Get only polynom that has a degree less than a specific value
1027
- if (m1d.degree(letter).isLeq(maxDegree)) {
1028
- m2.forEach(m2d => {
1029
- if (m1d.degree(letter).isNotEqual(m2d.degree(letter))) {
1030
- allDividers.push(new Polynom(m1d, m2d))
1031
- allDividers.push(new Polynom(m1d, m2d.clone().opposite()))
1032
- }
1033
- })
1034
- }
1035
-
1036
-
1037
- })
1038
-
1039
- return allDividers
1019
+ #invalidateCache(): void {
1020
+ this.#rootsCache = false
1021
+ this.#factors = []
1040
1022
  }
1041
1023
 
1042
- #multiplyByFraction = (F: Fraction): Polynom => {
1024
+ #multiplyByFraction(F: Fraction): this {
1043
1025
  for (const m of this.#monoms) {
1044
1026
  m.coefficient.multiply(F)
1045
1027
  }
1046
1028
 
1047
-
1048
1029
  return this.reduce()
1049
1030
  }
1050
1031
 
1051
- #multiplyByInteger = (nb: number): Polynom => {
1032
+ #multiplyByInteger(nb: number): this {
1052
1033
  return this.#multiplyByFraction(new Fraction(nb))
1053
1034
  }
1054
1035
 
1055
- #multiplyByMonom = (M: Monom): Polynom => {
1036
+ #multiplyByMonom(M: Monom): this {
1056
1037
  for (const m of this.#monoms) {
1057
1038
  m.multiply(M)
1058
1039
  }
@@ -1060,7 +1041,7 @@ export class Polynom implements IPiMathObject<Polynom>,
1060
1041
  return this.reduce()
1061
1042
  }
1062
1043
 
1063
- #multiplyByPolynom = (P: Polynom): Polynom => {
1044
+ #multiplyByPolynom(P: Polynom): this {
1064
1045
  const M: Monom[] = []
1065
1046
  for (const m1 of this.#monoms) {
1066
1047
  for (const m2 of P.monoms) {
@@ -1068,12 +1049,11 @@ export class Polynom implements IPiMathObject<Polynom>,
1068
1049
  }
1069
1050
  }
1070
1051
 
1071
-
1072
1052
  this.#monoms = M
1073
1053
  return this.reduce()
1074
1054
  }
1075
1055
 
1076
- #parseString(inputStr: string, ...values: unknown[]): this {
1056
+ #parseString(inputStr: string, ...values: InputValue<Fraction>[]): this {
1077
1057
  if (values.length === 0) {
1078
1058
  // Parse the polynom using the shutting yard algorithm
1079
1059
  if (inputStr !== '' && !isNaN(Number(inputStr))) {
@@ -1088,18 +1068,18 @@ export class Polynom implements IPiMathObject<Polynom>,
1088
1068
 
1089
1069
  // Parse the string.
1090
1070
  return this.#shutingYardToReducedPolynom(inputStr)
1091
- } else if (/^[a-z]+/.test(inputStr)) {
1071
+ } else if (/^[a-z]+$/.test(inputStr)) {
1092
1072
  // We assume the inputStr contains only letters.
1093
1073
  this.empty()
1094
1074
 
1095
- const fractions = values.map(x => new Fraction(x as InputValue<Fraction>))
1075
+ const fractions = values.map(x => new Fraction(x))
1096
1076
 
1097
1077
  // Multiple setLetter version
1098
1078
  if (inputStr.length > 1) {
1099
1079
  const letters = inputStr.split('')
1100
1080
 
1101
- if (letters.length < values.length - 2) {
1102
- throw new Error('Too many factors for too few variables !')
1081
+ if (fractions.length > letters.length + 1) {
1082
+ throw new Error(`Too many values: ${letters.length} letters but ${fractions.length} values provided`)
1103
1083
  }
1104
1084
 
1105
1085
  let i = 0
@@ -1107,7 +1087,7 @@ export class Polynom implements IPiMathObject<Polynom>,
1107
1087
  for (const F of fractions) {
1108
1088
  const m = new Monom()
1109
1089
  m.coefficient = F.clone()
1110
- m.literalStr = letters[i] || ''
1090
+ m.literalStr = letters[i] ?? ''
1111
1091
  this.add(m)
1112
1092
  i++
1113
1093
  }
@@ -1130,35 +1110,7 @@ export class Polynom implements IPiMathObject<Polynom>,
1130
1110
 
1131
1111
  }
1132
1112
 
1133
- /**
1134
- * Main parse using a shutting yard class
1135
- * @param inputStr
1136
- */
1137
- #shutingYardToReducedPolynom = (inputStr: string): this => {
1138
- // Get the RPN array of the current expression
1139
- const SY: ShutingYard = new ShutingYard().parse(inputStr)
1140
- const rpn: { token: string, tokenType: ShutingyardType }[] = SY.rpn
1141
-
1142
- // New version for reducing shuting yard.
1143
- this.zero()
1144
-
1145
- const stack: Polynom[] = []
1146
-
1147
- // Loop through the each element of the RPN
1148
- for (const element of rpn) {
1149
- this.#shutingYard_addToken(stack, element)
1150
- }
1151
-
1152
-
1153
- if (stack.length === 1) {
1154
- this.add(stack[0])
1155
- }
1156
-
1157
-
1158
- return this.reorder()
1159
- }
1160
-
1161
- #shutingYard_addToken = (stack: Polynom[], element: Token): void => {
1113
+ #shutingYardAddToken(stack: Polynom[], element: Token): void {
1162
1114
  switch (element.tokenType) {
1163
1115
  case ShutingyardType.COEFFICIENT:
1164
1116
  stack.push(new Polynom(element.token))
@@ -1169,9 +1121,7 @@ export class Polynom implements IPiMathObject<Polynom>,
1169
1121
  break
1170
1122
 
1171
1123
  case ShutingyardType.CONSTANT:
1172
- // TODO: add constant support to Polynom parsing.
1173
- console.log('Actually, not supported - will be added later !')
1174
- break
1124
+ throw new Error('Unsupported CONSTANT token in Polynom parser')
1175
1125
 
1176
1126
  case ShutingyardType.OPERATION:
1177
1127
  if (stack.length >= 2) {
@@ -1180,7 +1130,7 @@ export class Polynom implements IPiMathObject<Polynom>,
1180
1130
 
1181
1131
  // Check if the polynoms are not undefined.
1182
1132
  if (a === undefined || b === undefined) {
1183
- break
1133
+ throw new Error('Unexpected undefined operand in Polynom parser stack')
1184
1134
  }
1185
1135
 
1186
1136
  if (element.token === '+') {
@@ -1191,7 +1141,7 @@ export class Polynom implements IPiMathObject<Polynom>,
1191
1141
  stack.push(a.multiply(b))
1192
1142
  } else if (element.token === '/') {
1193
1143
  if (b.degree().isStrictlyPositive()) {
1194
- console.log('divide by a polynom -> should create a rational polynom !')
1144
+ throw new Error('Cannot divide a Polynom by another Polynom of degree > 0')
1195
1145
  } else {
1196
1146
  // a.divide(b.monoms[0].coefficient)
1197
1147
  stack.push(a.divide(b.monoms[0].coefficient))
@@ -1212,7 +1162,7 @@ export class Polynom implements IPiMathObject<Polynom>,
1212
1162
 
1213
1163
  stack.push(a)
1214
1164
  } else {
1215
- console.error('Cannot have power with fraction')
1165
+ throw new Error('Cannot raise a multi-monom Polynom to a fractional power')
1216
1166
  }
1217
1167
  }
1218
1168
  }
@@ -1231,16 +1181,42 @@ export class Polynom implements IPiMathObject<Polynom>,
1231
1181
 
1232
1182
  case ShutingyardType.MONOM:
1233
1183
  // Should never appear.
1234
- console.error('The monom token should not appear here')
1235
- break
1184
+ throw new Error('Unexpected MONOM token in polynom parser')
1236
1185
 
1237
1186
  case ShutingyardType.FUNCTION:
1238
1187
  // Should never appear.
1239
- console.error('The function token should not appear here - might be introduced later.')
1240
- break
1188
+ throw new Error(`Unsupported function token "${element.token}" in polynom parser`)
1189
+ }
1190
+
1191
+
1192
+ }
1193
+
1194
+ /**
1195
+ * Main parse using a shutting yard class
1196
+ * @param inputStr
1197
+ */
1198
+ #shutingYardToReducedPolynom(inputStr: string): this {
1199
+ // Get the RPN array of the current expression
1200
+ const SY: ShutingYard = new ShutingYard().parse(inputStr)
1201
+ const rpn: { token: string, tokenType: ShutingyardType }[] = SY.rpn
1202
+
1203
+ // New version for reducing shuting yard.
1204
+ this.zero()
1205
+
1206
+ const stack: Polynom[] = []
1207
+
1208
+ // Loop through the each element of the RPN
1209
+ for (const element of rpn) {
1210
+ this.#shutingYardAddToken(stack, element)
1241
1211
  }
1242
1212
 
1243
1213
 
1214
+ if (stack.length === 1) {
1215
+ this.add(stack[0])
1216
+ }
1217
+
1218
+
1219
+ return this.reorder()
1244
1220
  }
1245
1221
 
1246
1222