pimath 0.1.39 → 0.1.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/pimath.js +188 -159
  2. package/dist/pimath.js.map +1 -1
  3. package/package.json +4 -2
  4. package/src/algebra/equation.ts +556 -0
  5. package/src/algebra/equationSolver.ts +539 -0
  6. package/src/algebra/factor.ts +339 -0
  7. package/src/algebra/index.ts +11 -0
  8. package/src/algebra/linearSystem.ts +388 -0
  9. package/src/algebra/logicalset.ts +256 -0
  10. package/src/algebra/matrix.ts +474 -0
  11. package/src/algebra/monom.ts +1015 -0
  12. package/src/algebra/operations.ts +24 -0
  13. package/src/algebra/polyFactor.ts +668 -0
  14. package/src/algebra/polynom.ts +1394 -0
  15. package/src/analyze/solution.ts +115 -0
  16. package/src/analyze/tableOfSigns.ts +30 -0
  17. package/src/coefficients/fraction.ts +678 -0
  18. package/src/coefficients/index.ts +4 -0
  19. package/src/coefficients/nthRoot.ts +149 -0
  20. package/src/coefficients/root.ts +299 -0
  21. package/src/geometry/circle.ts +386 -0
  22. package/src/geometry/geomMath.ts +70 -0
  23. package/src/geometry/index.ts +10 -0
  24. package/src/geometry/line.ts +677 -0
  25. package/src/geometry/line3.ts +206 -0
  26. package/src/geometry/plane3.ts +170 -0
  27. package/src/geometry/point.ts +66 -0
  28. package/src/geometry/sphere3.ts +214 -0
  29. package/src/geometry/triangle.ts +354 -0
  30. package/src/geometry/vector.ts +341 -0
  31. package/src/helpers.ts +35 -0
  32. package/src/index.ts +60 -0
  33. package/src/numeric.ts +199 -0
  34. package/src/pimath.interface.ts +160 -0
  35. package/src/randomization/algebra/rndEquation.ts +41 -0
  36. package/src/randomization/algebra/rndMonom.ts +39 -0
  37. package/src/randomization/algebra/rndPolynom.ts +86 -0
  38. package/src/randomization/coefficient/rndFraction.ts +38 -0
  39. package/src/randomization/geometry/rndCircle.ts +27 -0
  40. package/src/randomization/geometry/rndLine.ts +37 -0
  41. package/src/randomization/geometry/rndLine3.ts +27 -0
  42. package/src/randomization/geometry/rndVector.ts +63 -0
  43. package/src/randomization/random.ts +91 -0
  44. package/src/randomization/rndHelpers.ts +102 -0
  45. package/src/randomization/rndTypes.ts +63 -0
  46. package/types/algebra/equationSolver.d.ts +3 -0
  47. package/types/algebra/equationSolver.d.ts.map +1 -1
  48. package/types/algebra/polyFactor.d.ts +5 -0
  49. package/types/algebra/polyFactor.d.ts.map +1 -1
  50. package/types/analyze/solution.d.ts +21 -0
  51. package/types/analyze/solution.d.ts.map +1 -0
  52. package/types/analyze/tableOfSigns.d.ts +9 -0
  53. package/types/analyze/tableOfSigns.d.ts.map +1 -0
  54. package/types/coefficients/root.d.ts +38 -0
  55. package/types/coefficients/root.d.ts.map +1 -0
  56. package/types/geometry/point.d.ts +1 -1
  57. package/types/geometry/point.d.ts.map +1 -1
  58. package/types/helpers.d.ts +1 -0
  59. package/types/helpers.d.ts.map +1 -1
  60. package/types/index.d.ts +1 -0
  61. package/types/index.d.ts.map +1 -1
  62. package/types/numeric.d.ts +2 -0
  63. package/types/numeric.d.ts.map +1 -1
  64. package/types/pimath.interface.d.ts +26 -26
  65. package/types/pimath.interface.d.ts.map +1 -1
@@ -0,0 +1,1015 @@
1
+ /***
2
+ * Monom class
3
+ */
4
+ import type {
5
+ IAlgebra,
6
+ IAnalyse,
7
+ IExpression,
8
+ InputAlgebra,
9
+ InputValue,
10
+ IPiMathObject,
11
+ literalType
12
+ } from "../pimath.interface"
13
+ import {Fraction} from "../coefficients/fraction"
14
+ import {NthRoot} from "../coefficients/nthRoot"
15
+ import {Numeric} from "../numeric"
16
+
17
+ import {ShutingYard, ShutingyardType, type Token} from "piexpression"
18
+
19
+ export class Monom implements IPiMathObject<Monom>, IExpression<Monom>, IAnalyse<Monom>, IAlgebra<Monom> {
20
+ #coefficient: Fraction
21
+ #literal: literalType<Fraction>
22
+
23
+ constructor(value?: InputValue<Fraction>)
24
+ constructor(value?: Monom)
25
+ constructor(value?: InputAlgebra<Fraction>) {
26
+ this.#coefficient = new Fraction().zero()
27
+ this.#literal = {}
28
+
29
+ if (value !== undefined) {
30
+ // A string is given - try to parse the value.
31
+ this.parse(value)
32
+ }
33
+
34
+ return this
35
+ }
36
+
37
+ // -----------------------------------------
38
+ /**
39
+ * Parse a string to a monom. The string may include fraction.
40
+ * @param inputStr
41
+ */
42
+ public parse(inputStr: InputAlgebra<Monom>): this {
43
+ // Initialize the monom
44
+ this.#coefficient = new Fraction()
45
+ this.#literal = {}
46
+
47
+ if (typeof inputStr === 'string') {
48
+ if(!isNaN(Number(inputStr))){
49
+ this.#coefficient = new Fraction(Number(inputStr))
50
+ }else {
51
+ this.#shutingYardToReducedMonom(inputStr)
52
+ }
53
+ } else if (typeof inputStr === 'number') {
54
+ this.#coefficient = new Fraction(inputStr)
55
+ } else if (inputStr instanceof Fraction) {
56
+ this.#coefficient = inputStr.clone()
57
+ } else if (inputStr instanceof Monom) {
58
+ this.#coefficient = inputStr.#coefficient.clone()
59
+
60
+ // Copy the literal parts
61
+ this.#cloneLiteral(inputStr)
62
+ }
63
+
64
+ return this
65
+ }
66
+
67
+ /**
68
+ * Clone the current Monom.
69
+ */
70
+ public clone = (): Monom => {
71
+ const F: Monom = new Monom()
72
+
73
+ F.coefficient = this.#coefficient.clone()
74
+
75
+ // Copy the literal parts.
76
+ for (const k in this.#literal) {
77
+ F.setLetter(k, this.#literal[k].clone())
78
+ }
79
+ return F
80
+ }
81
+
82
+ /**
83
+ * Get the tex output of the monom
84
+ */
85
+ public get tex(): string {
86
+ // TODO: display with square root !
87
+ // TODO: Refactor to make it more readable
88
+ let L = ''
89
+ const letters = Object.keys(this.#literal).sort()
90
+
91
+ for (const letter of letters) {
92
+ if (this.#literal[letter].isNotZero()) {
93
+ L += letter
94
+ if (this.#literal[letter].isNotEqual(1)) {
95
+ L += `^{ ${this.#literal[letter].tfrac.tex } }`
96
+ }
97
+ }
98
+ }
99
+
100
+ if (L === '') {
101
+ // No setLetter - means it's only a number !
102
+ if (this.#coefficient.value != 0) {
103
+ return this.#coefficient.frac.tex
104
+ } else {
105
+ return '0'
106
+ }
107
+ } else {
108
+ if (this.#coefficient.value === 1) {
109
+ return L
110
+ } else if (this.#coefficient.value === -1) {
111
+ return `-${L}`
112
+ } else if (this.#coefficient.value === 0) {
113
+ return '0'
114
+ } else {
115
+ return `${this.#coefficient.frac.tex}${L}`
116
+ }
117
+ }
118
+ }
119
+
120
+ // Display getter
121
+ /**
122
+ * This display getter is to be used in the polynom display getter
123
+ */
124
+ public get display(): string {
125
+ let L = ''
126
+ const letters = Object.keys(this.#literal).sort()
127
+ for (const letter of letters) {
128
+ if (this.#literal[letter].isNotZero()) {
129
+ L += letter
130
+ if (this.#literal[letter].isNotEqual(1)) {
131
+ L += `^(${this.#literal[letter].display})`
132
+ }
133
+ }
134
+ }
135
+
136
+ if (L === '') {
137
+ // No setLetter - means it's only a number !
138
+ if (this.#coefficient.value != 0) {
139
+ return this.#coefficient.display
140
+ } else {
141
+ return ''
142
+ }
143
+ } else {
144
+ if (this.#coefficient.value === 1) {
145
+ return L
146
+ } else if (this.#coefficient.value === -1) {
147
+ return `-${L}`
148
+ } else if (this.#coefficient.value === 0) {
149
+ return '0'
150
+ } else {
151
+ return `${this.#coefficient.display}${L}`
152
+ }
153
+ }
154
+ }
155
+
156
+ public static gcd = (...monoms: Monom[]): Monom => {
157
+ // All the monoms must be with natural powers...
158
+ for (const m of monoms) {
159
+ if (m.containsRationalPower()) {
160
+ return new Monom().zero()
161
+ }
162
+ }
163
+
164
+ const M = new Monom(),
165
+ n: number = Numeric.gcd(...monoms.map(value => value.coefficient.numerator)),
166
+ d: number = Numeric.lcm(...monoms.map(value => value.coefficient.denominator))
167
+
168
+ // Get the coefficient.
169
+ M.coefficient = new Fraction(n, d).reduce()
170
+
171
+ // Set the literal parts - go through each monoms literal parts and get only the lowest degree of each letters.
172
+ for (const m of monoms) {
173
+ // Remove the inexistant letters from the resulting monom
174
+ for (const letter in M.literal) {
175
+ if (!(letter in m.literal)) {
176
+ M.literal[letter].zero()
177
+ }
178
+ }
179
+ for (const letter in m.literal) {
180
+ if (!M.hasVariable(letter) && m.literal[letter].isStrictlyPositive()) {
181
+ M.literal[letter] = m.literal[letter].clone()
182
+ } else {
183
+ M.literal[letter] = new Fraction(Math.min(m.literal[letter].value, M.literal[letter].value))
184
+ }
185
+ }
186
+ }
187
+
188
+ return M
189
+ }
190
+
191
+ /**
192
+ * Multiply two monoms and return a NEW monom.
193
+ * @param monoms
194
+ */
195
+ public static xMultiply = (...monoms: Monom[]): Monom => {
196
+ const M = new Monom().one()
197
+
198
+ for (const m of monoms) {
199
+ M.multiply(m)
200
+ }
201
+
202
+ return M
203
+ }
204
+
205
+ /**
206
+ * Add all similar monoms. If they aren't similar, they are simply skipped.
207
+ * @param M (Monom[]) The monoms to add.
208
+ */
209
+ public add = (...M: InputAlgebra<Fraction>[]): this => {
210
+ for (const m of M) {
211
+ // If the value given is not a monom, create it.
212
+ const mAsMonom = (!(m instanceof Monom)) ? new Monom(m) : m
213
+
214
+ if (this.isSameAs(mAsMonom)) {
215
+ if (this.isZero()) {
216
+ this.#cloneLiteral(mAsMonom)
217
+ }
218
+
219
+ this.#coefficient.add(mAsMonom.coefficient)
220
+ } else {
221
+ console.log('Add monom: ' + this.display + ' is not similar with ', mAsMonom.display)
222
+ }
223
+ }
224
+ return this
225
+ }
226
+
227
+ /**
228
+ * Get the coefficient \\(k\\) of the Monom \\(k\\cdot x^{n}\\)
229
+ * @returns {Fraction}
230
+ */
231
+ public get coefficient(): Fraction {
232
+ return this.#coefficient
233
+ }
234
+
235
+ /**
236
+ * Set the coefficient \\(k\\) value of the monom
237
+ * @param {Fraction | number | string} F
238
+ */
239
+ public set coefficient(F: InputValue<Fraction>) {
240
+ this.#coefficient = new Fraction(F)
241
+ }
242
+
243
+ public containsRationalPower = (): boolean => {
244
+ return Object.values(this.#literal).some((value) => value.isRational())
245
+ }
246
+
247
+ /**
248
+ * Get the degree of a monom. If no setLetter is given, the result will be the global degree.
249
+ * @param letter (string) Letter to get to degree (power)
250
+ */
251
+ public degree = (letter?: string): Fraction => {
252
+ if (this.variables.length === 0) {
253
+ return new Fraction().zero()
254
+ }
255
+ if (letter === undefined) {
256
+ // Not setLetter given -> we get the global monom degree (sum of all the letters).
257
+ return Object.values(this.#literal).reduce((t, n) => t.clone().add(n))
258
+ } else {
259
+ // A setLetter is given -> get the corresponding power.
260
+ return !this.hasVariable(letter) ? new Fraction().zero() : this.#literal[letter].clone()
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Derivative the monom
266
+ * @param letter
267
+ */
268
+ public derivative = (letter?: string): Monom => {
269
+ // No setLetter given - assume it's the setLetter 'x'
270
+ if (letter === undefined) {
271
+ letter = 'x'
272
+ }
273
+
274
+ if (this.hasVariable(letter)) {
275
+ const d = this.#literal[letter].clone(),
276
+ dM = this.clone()
277
+
278
+ // Subtract one to the degree.
279
+ dM.#literal[letter].subtract(1)
280
+
281
+ // Multiply the coefficient by the previous degree
282
+ dM.#coefficient.multiply(new Fraction(d.clone()))
283
+ return dM
284
+ } else {
285
+ return new Monom().zero()
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Divide the current monoms by multiple monoms
291
+ * @param M (Monom[])
292
+ */
293
+ public divide = (...M: InputAlgebra<Fraction>[]): this => {
294
+ // Depending on the given value, choose the current item
295
+ for (const m of M) {
296
+ // If the value given is not a monom, create it.
297
+ const mAsMonom = (!(m instanceof Monom)) ? new Monom(m) : m
298
+
299
+ // Divide the coefficient
300
+ this.#coefficient.divide(mAsMonom.coefficient)
301
+
302
+ // Subtract the power values
303
+ for (const letter in mAsMonom.literal) {
304
+
305
+ this.#literal[letter] = this.hasVariable(letter) ?
306
+ this.#literal[letter].subtract(mAsMonom.literal[letter]) :
307
+ mAsMonom.literal[letter].clone().opposite()
308
+
309
+
310
+ // If the power of a particular setLetter is zero, delete it from the literal part..
311
+ if (this.#literal[letter].isZero()) {
312
+ this.removeVariable(letter)
313
+ }
314
+ }
315
+ }
316
+ return this
317
+ }
318
+
319
+ public get dividers(): Monom[] {
320
+ // Decompose only if the coefficient is a natural number
321
+ if (!this.coefficient.isRelative()) {
322
+ return [this.clone()]
323
+ }
324
+
325
+ // Decompose only if the power values are natural numbers.
326
+ if (this.containsRationalPower()) {
327
+ return [this.clone()]
328
+ }
329
+
330
+ // Security : do not do this if greater than 10000
331
+ if (this.coefficient.numerator > 1000000) {
332
+ return [this.clone()]
333
+ }
334
+
335
+ const dividers = Numeric.dividers(Math.abs(this.coefficient.numerator))
336
+
337
+ // Decompose the literals parts.
338
+ let literals: literalType<Fraction>[] = []
339
+ for (const L in this.literal) {
340
+ // L is the letter.
341
+ literals = this._getLiteralDividers(literals, L)
342
+ }
343
+
344
+ const monomDividers: Monom[] = []
345
+ if (literals.length > 0 && dividers.length > 0) {
346
+ for (const N of dividers) {
347
+ for (const L of literals) {
348
+ const M = new Monom()
349
+ M.coefficient = new Fraction(N)
350
+ M.literal = L
351
+ monomDividers.push(M)
352
+ }
353
+ }
354
+ } else if (dividers.length === 0) {
355
+ for (const L of literals) {
356
+ const M = new Monom()
357
+ M.coefficient = new Fraction().one()
358
+ M.literal = L
359
+ monomDividers.push(M)
360
+ }
361
+ } else {
362
+ for (const N of dividers) {
363
+ const M = new Monom()
364
+ M.coefficient = new Fraction(N)
365
+ monomDividers.push(M)
366
+ }
367
+ }
368
+
369
+ return monomDividers.length === 0 ? [new Monom().one()] : monomDividers
370
+ }
371
+
372
+ /**
373
+ * Evaluate a monom. Each setLetter must be assigned to a Fraction.
374
+ * @param values Dictionary of <setLetter: Fraction>
375
+ * @param asNumeric
376
+ */
377
+ public evaluate = (values: literalType<number | Fraction> | InputValue<Fraction>, asNumeric?: boolean): Fraction | number => {
378
+ // If as numeric return the numeric value
379
+ if (asNumeric === true) {
380
+ // Convert all values to numeric
381
+ // If the value is a Fraction, convert it to a number
382
+ if (values instanceof Fraction) {
383
+ return this.#evaluateAsNumeric(values.value)
384
+ }
385
+
386
+ // If the value is a NthRoot, return undefined
387
+ if (values instanceof NthRoot) {
388
+ return new Fraction().invalid()
389
+ }
390
+
391
+ // If the value is a number, return the numeric value
392
+ if (typeof values === 'number') {
393
+ return this.#evaluateAsNumeric(values)
394
+ }
395
+
396
+ // If the value is an object, return the numeric value
397
+ if (typeof values === 'object') {
398
+ // Convert {[key:string]:Fraction} to {[key:string]:number}
399
+ const tmpValues: literalType<number> = {}
400
+ for (const L in values) {
401
+ tmpValues[L] = new Fraction(values[L]).value
402
+ }
403
+
404
+ return this.#evaluateAsNumeric(tmpValues)
405
+ }
406
+ }
407
+
408
+ // The answer must be a Fraction
409
+ const r = this.coefficient.clone()
410
+
411
+ if (typeof values === 'number' || values instanceof Fraction) {
412
+ const tmpValues: literalType<Fraction> = {}
413
+ tmpValues[this.variables[0]] = new Fraction(values)
414
+ return this.evaluate(tmpValues)
415
+ }
416
+
417
+ if (values instanceof NthRoot) {
418
+ return new Fraction().invalid()
419
+ }
420
+
421
+ if (typeof values === 'object') {
422
+ if (this.variables.length === 0) {
423
+ return this.coefficient
424
+ }
425
+
426
+ for (const L in this.#literal) {
427
+ const value = new Fraction(values[L])
428
+
429
+ r.multiply(value.pow(this.#literal[L]))
430
+ }
431
+ }
432
+
433
+ return r
434
+ }
435
+
436
+ // -------------------------------------
437
+ /**
438
+ * Determine if a monom contains a setLetter in it's literal part
439
+ * @param letter
440
+ */
441
+ public hasVariable = (letter?: string): boolean => {
442
+ // The letter was not found
443
+ return Object.hasOwn(this.#literal, letter ?? 'x')
444
+ }
445
+
446
+ public integrate(a: InputValue<Fraction>, b: InputValue<Fraction>, letter?: string ): Fraction {
447
+ const primitive = this.primitive(letter)
448
+
449
+ return (primitive.evaluate(b) as Fraction)
450
+ .subtract(primitive.evaluate(a) as Fraction)
451
+ }
452
+
453
+ public inverse = (): this => {
454
+ this.#coefficient.opposite()
455
+ for (const letter in this.#literal) {
456
+ this.#literal[letter].opposite()
457
+ }
458
+ return this
459
+ }
460
+
461
+ public isDivisible = (div: Monom): boolean => {
462
+ // For all variables (letters), the current monom must have a degree higher than the divider
463
+ if (div.degree().isStrictlyPositive()) {
464
+ for (const letter of div.variables) {
465
+ if (!this.degree(letter).isGeq(div.degree(letter))) {
466
+ return false
467
+ }
468
+ }
469
+ }
470
+
471
+ // If the coefficient is rational, we suppose we don't need to check the division by the coefficient.
472
+ if (this.coefficient.isRational() || div.coefficient.isRational()) {
473
+ return true
474
+ }
475
+
476
+ return this.coefficient.clone().divide(div.coefficient).isRelative()
477
+ }
478
+
479
+ /**
480
+ * Determine if two monoms are equals
481
+ * @param M
482
+ */
483
+ public isEqual = (M: Monom): boolean => {
484
+ return this.isSameAs(M) && this.#coefficient.isEqual(M.coefficient)
485
+ }
486
+
487
+ public isLiteralSquare = (): boolean => {
488
+ for (const letter in this.literal) {
489
+ // A literal square must have a natural power
490
+ if (this.literal[letter].isRational()) {
491
+ return false
492
+ }
493
+
494
+ // The natural power must be be even
495
+ if (this.literal[letter].isEven()) {
496
+ return false
497
+ }
498
+ }
499
+
500
+ return true
501
+ }
502
+
503
+ /**
504
+ * Determine if the monom is one
505
+ */
506
+ public isOne = (): boolean => {
507
+ return this.#coefficient.value === 1 && this.variables.length === 0
508
+ }
509
+
510
+ /**
511
+ * Determine if two monoms are similar
512
+ * @param M
513
+ */
514
+ public isSameAs = (M: Monom): boolean => {
515
+ // Get the list of all variables from both monoms.
516
+ const M1: string[] = this.variables
517
+ const M2: string[] = M.variables
518
+
519
+ // Get the list of all variables from both monoms.
520
+ const K: string[] = M1.concat(M2.filter((item) => !M1.includes(item)))
521
+
522
+ // If one of the monom is zero, it is the same than the other.
523
+ if (this.isZero() || M.isZero()) {
524
+ return true
525
+ }
526
+
527
+ // Both monoms has no literal part.
528
+ if (M1.length === 0 && M2.length === 0) {
529
+ return true
530
+ }
531
+
532
+ // Both monoms must have the same variables
533
+ if (M1.length !== M2.length) {
534
+ return false
535
+ }
536
+
537
+ // To _compare, both must be different from zero.
538
+ if (!this.isZero() && !M.isZero()) {
539
+ for (const key of K) {
540
+ // The variable is not available in one of the monom
541
+ if (!this.hasVariable(key) || !M.hasVariable(key)) {
542
+ return false
543
+ }
544
+
545
+ // The variable does not have the same power in each monoms.
546
+ if (!this.#literal[key].isEqual(M.literal[key])) {
547
+ return false
548
+ }
549
+ }
550
+ }
551
+
552
+ // All are positive check - the monoms are the sames.
553
+ return true
554
+ }
555
+
556
+ public isSquare = (): boolean => {
557
+ if (!this.coefficient.isSquare()) {
558
+ return false
559
+ }
560
+ return this.isLiteralSquare()
561
+ }
562
+
563
+ /**
564
+ * Determine if the monom is null
565
+ */
566
+ public isZero = (): boolean => {
567
+ return this.#coefficient.value === 0
568
+ }
569
+
570
+ /**
571
+ * Get the literal part of \\(x^{n_1}y^{n_2}\\) as dictionary \\[\\begin{array}{ll}x&=n_1\\\\y&=n_2\\end{array}\\]
572
+ * @returns {literalType}
573
+ */
574
+ public get literal(): literalType<Fraction> {
575
+ return this.#literal
576
+ }
577
+
578
+ /**
579
+ * Set the literal part of the monom. Must be a dictionary {x: Fraction, y: Fraction, ...}
580
+ * @param {literalType<Fraction>} L
581
+ */
582
+ public set literal(L: literalType<Fraction>) {
583
+ this.#literal = L
584
+ }
585
+
586
+ /**
587
+ * Get the literal square roots of the Monom.
588
+ * @returns {literalType<Fraction>}
589
+ */
590
+ public get literalSqrt(): literalType<Fraction> {
591
+ // TODO: used in Polynom._factorize2ndDegree : remove it from here ?
592
+ if (this.isLiteralSquare()) {
593
+ const L: literalType<Fraction> = {}
594
+ for (const key in this.#literal) {
595
+ L[key] = this.#literal[key].clone().sqrt()
596
+ }
597
+ return L
598
+ } else {
599
+ return this.#literal
600
+ }
601
+ }
602
+
603
+ /**
604
+ * Set the literal part of the monom from a string
605
+ * @param inputStr String like x^2y^3
606
+ */
607
+ public set literalStr(inputStr: string) {
608
+ // TODO : parse using ShutingYard tree !
609
+
610
+ // Match all x^n
611
+ for (const v of [...inputStr.matchAll(/([a-z])\^([+-]?[0-9]+)/g)]) {
612
+ // Create the default letter entry if necessary.
613
+ if (!(v[1] in this.#literal)) {
614
+ this.#literal[v[1]] = new Fraction().zero()
615
+ }
616
+
617
+ // Add the new value.
618
+ // TODO: actually, it adds only numeric value
619
+ this.#literal[v[1]].add(+v[2])
620
+ }
621
+
622
+ // Match all x
623
+ for (const v of [...inputStr.matchAll(/([a-z](?!\^))/g)]) {
624
+ // Match all single letters
625
+ if (!(v[1] in this.#literal)) {
626
+ this.#literal[v[1]] = new Fraction().zero()
627
+ }
628
+
629
+ // Add one to the value.
630
+ this.#literal[v[1]].add(1)
631
+ }
632
+ }
633
+
634
+ /**
635
+ * Multiple multiple monoms to the current monom
636
+ * @param M (Monom[]) The monoms to multiply to.
637
+ */
638
+ public multiply = (...M: InputAlgebra<Fraction>[]): this => {
639
+ for (const m of M) {
640
+ // If the value given is not a monom, create it.
641
+ const mAsMonom = (!(m instanceof Monom)) ? new Monom(m) : m
642
+
643
+ // Multiply the coefficient.
644
+ this.#coefficient.multiply(mAsMonom.coefficient)
645
+
646
+ // Multiply the literal parts.
647
+ for (const letter in mAsMonom.literal) {
648
+ if (!this.hasVariable(letter)) {
649
+ this.#literal[letter] = mAsMonom.literal[letter].clone()
650
+ } else {
651
+ this.#literal[letter].add(mAsMonom.literal[letter])
652
+ }
653
+ }
654
+ }
655
+ return this
656
+ }
657
+
658
+ /**
659
+ * Create a one value monom
660
+ */
661
+ public one = (): this => {
662
+ this.#coefficient = new Fraction().one()
663
+ this.#literal = {}
664
+ return this
665
+ }
666
+
667
+ /**
668
+ * Get the opposite
669
+ * Returns a monom.
670
+ */
671
+ public opposite = (): this => {
672
+ this.#coefficient.opposite()
673
+ return this
674
+ }
675
+
676
+ public get plotFunction(): string {
677
+ let L = ''
678
+ const letters = Object.keys(this.#literal).sort()
679
+
680
+ for (const letter of letters) {
681
+ if (this.#literal[letter].isNotZero()) {
682
+ L += (L === '' ? "" : "*") + letter
683
+ if (this.#literal[letter].isNotEqual(1)) {
684
+ L += `^(${this.#literal[letter].display})`
685
+ }
686
+ }
687
+ }
688
+
689
+ // No literal part
690
+ if (L === '') {
691
+ // No setLetter - means it's only a number !
692
+ if (this.#coefficient.value != 0) {
693
+ return this.#coefficient.display
694
+ } else {
695
+ return ''
696
+ }
697
+ } else {
698
+ if (this.#coefficient.value === 1) {
699
+ return L
700
+ } else if (this.#coefficient.value === -1) {
701
+ return `-${L}`
702
+ } else if (this.#coefficient.value === 0) {
703
+ return '0'
704
+ } else {
705
+ return `${this.#coefficient.display}*${L}`
706
+ }
707
+ }
708
+ }
709
+
710
+ /**
711
+ * Get the pow of a monom.
712
+ * @param nb (number) : Mathematical pow
713
+ */
714
+ public pow = (nb: number | Fraction): this => {
715
+ this.#coefficient.pow(nb)
716
+ for (const letter in this.#literal) {
717
+ this.#literal[letter].multiply(nb)
718
+ }
719
+ return this
720
+ }
721
+
722
+ public primitive = (letter?: string): Monom => {
723
+ // TODO: derivative including the ln value => implies creating different monom system ?
724
+ if (letter === undefined) {
725
+ letter = 'x'
726
+ }
727
+
728
+ // Zero monom
729
+ const M = this.clone()
730
+ let degree: Fraction
731
+
732
+ if (M.hasVariable(letter)) {
733
+ degree = M.degree(letter).clone().add(1)
734
+ M.coefficient = M.coefficient.clone().divide(degree)
735
+ M.setLetter(letter, degree)
736
+ } else {
737
+ // There is no letter.
738
+
739
+ // The coefficient might be zero (=> x) or a number a (=> ax)
740
+ if (M.coefficient.isZero()) {
741
+ M.coefficient = new Fraction().one()
742
+ }
743
+ M.setLetter(letter, 1)
744
+ }
745
+
746
+ return M
747
+ }
748
+
749
+ public reduce = (): this => {
750
+ // Reduce the coefficient
751
+ this.coefficient.reduce()
752
+
753
+ // Reduce the literal parts (removing null powers)
754
+ for (const letter in this.#literal) {
755
+ if (this.#literal[letter].isZero()) {
756
+ this.removeVariable(letter)
757
+ }
758
+ }
759
+ return this
760
+ }
761
+
762
+ public removeVariable(letter: string) {
763
+ /* eslint-disable */
764
+ delete this.#literal[letter]
765
+ /* eslint-enable */
766
+ }
767
+
768
+ /**
769
+ * Get the nth-root of the monom
770
+ */
771
+ public root = (): this => {
772
+ throw new Error('Method not implemented.')
773
+ }
774
+
775
+ /**
776
+ * Set the power of a particular setLetter
777
+ * @param letter (string) Letter to change
778
+ * @param pow (number) Power of the setLetter (must be positive integer.
779
+ */
780
+ public setLetter = (letter: string, pow: InputValue<Fraction>): this => {
781
+ if (!(pow instanceof Fraction)) {
782
+ return this.setLetter(letter, new Fraction(pow))
783
+ }
784
+
785
+ // Set the power of the letter to zero => remove it
786
+ if (this.hasVariable(letter) && pow.isZero()) {
787
+ this.removeVariable(letter)
788
+ }
789
+
790
+
791
+ this.#literal[letter] = pow.clone()
792
+
793
+ return this
794
+ }
795
+
796
+ /**
797
+ * Return the square root of a monom
798
+ */
799
+ public sqrt = (): this => {
800
+ if (this.isSquare()) {
801
+ this.#coefficient.sqrt()
802
+ for (const letter in this.#literal) {
803
+ this.#literal[letter].clone().divide(2)
804
+ }
805
+ }
806
+
807
+ return this
808
+ }
809
+
810
+ /**
811
+ * Subtract multiple monoms
812
+ * @param M (Monom[]) The monoms to subtract
813
+ */
814
+ public subtract = (...M: InputAlgebra<Fraction>[]): this => {
815
+ for (const m of M) {
816
+ // If the value given is not a monom, create it.
817
+ const mAsMonom = (!(m instanceof Monom)) ? new Monom(m) : m
818
+
819
+ if (this.isSameAs(mAsMonom)) {
820
+ if (this.isZero()) {
821
+ this.#cloneLiteral(mAsMonom)
822
+ }
823
+
824
+ this.#coefficient.add(mAsMonom.clone().coefficient.opposite())
825
+ } else {
826
+ console.log('Subtract: Is not similar: ', mAsMonom.display)
827
+ }
828
+ }
829
+ return this
830
+ }
831
+
832
+ // Getter helpers.
833
+ /**
834
+ * Get the variables letters
835
+ */
836
+ public get variables(): string[] {
837
+ // const M = this.clone().clean()
838
+
839
+ const L: string[] = []
840
+ Object.entries(this.literal).forEach(
841
+ ([key, value]) => {
842
+ if (!value.isZero()) {
843
+ L.push(key)
844
+ }
845
+ })
846
+ L.sort()
847
+ return L
848
+ // return Object.keys(M.literal)
849
+ }
850
+
851
+ /**
852
+ * Create a zero value monom
853
+ */
854
+ public zero = (): this => {
855
+ this.#coefficient = new Fraction().zero()
856
+ this.#literal = {}
857
+ return this
858
+ }
859
+
860
+ #cloneLiteral(inputStr: Monom) {
861
+ for (const k in inputStr.literal) {
862
+ this.#literal[k] = inputStr.literal[k].clone()
863
+ }
864
+ }
865
+
866
+ #evaluateAsNumeric = (values: literalType<number | Fraction> | InputValue<Fraction>): number => {
867
+ let r = this.coefficient.value
868
+
869
+ if (typeof values === "number") {
870
+ const tmpValues: literalType<number> = {}
871
+ const key = this.variables[0]
872
+ tmpValues[key] = values
873
+
874
+ return this.#evaluateAsNumeric(tmpValues)
875
+ }
876
+
877
+ if (values instanceof Fraction) {
878
+ const tmpValues: literalType<number> = {}
879
+ tmpValues[this.variables[0]] = new Fraction(values).value
880
+ return this.#evaluateAsNumeric(tmpValues)
881
+ }
882
+
883
+ if (values instanceof NthRoot) {
884
+ return NaN
885
+ }
886
+
887
+ if (typeof values === 'object') {
888
+ if (this.variables.length === 0) {
889
+ return this.coefficient.value
890
+ }
891
+
892
+ for (const L in this.#literal) {
893
+ const v = values[L]
894
+
895
+ if (v instanceof Fraction) {
896
+ r *= v.value ** (this.#literal[L].value)
897
+ } else {
898
+ r *= v ** (this.#literal[L].value)
899
+ }
900
+ }
901
+ }
902
+
903
+ return r
904
+ }
905
+
906
+ #shutingYardToReducedMonom = (inputStr: string): this => {
907
+ // Get the RPN array of the current expression
908
+ const SY: ShutingYard = new ShutingYard().parse(inputStr)
909
+ const rpn: { token: string, tokenType: ShutingyardType }[] = SY.rpn
910
+
911
+ const stack: Monom[] = []
912
+
913
+ if (rpn.length === 0) {
914
+ this.zero()
915
+ return this
916
+ } else if (rpn.length === 1) {
917
+ const element = rpn[0]
918
+
919
+ this.one()
920
+ if (element.tokenType === ShutingyardType.COEFFICIENT) {
921
+ this.coefficient = new Fraction(element.token)
922
+ } else if (element.tokenType === ShutingyardType.VARIABLE) {
923
+ this.setLetter(element.token, 1)
924
+ }
925
+ return this
926
+ } else {
927
+ // Reset the monom
928
+ for (const element of rpn) {
929
+ this.#shutingYard_AddToken(stack, element)
930
+ }
931
+ }
932
+
933
+ this.one()
934
+ this.multiply(stack[0])
935
+ return this
936
+ }
937
+
938
+ #shutingYard_AddToken = (stack: Monom[], element: Token): void => {
939
+ let q1: Monom, q2: Monom, m: Monom, letter: string, pow: Fraction
940
+
941
+ if (element.tokenType === ShutingyardType.COEFFICIENT) {
942
+ stack.push(new Monom(new Fraction(element.token)))
943
+
944
+ } else if (element.tokenType === ShutingyardType.VARIABLE) {
945
+ const M = new Monom().one()
946
+ M.setLetter(element.token, 1)
947
+ stack.push(M.clone())
948
+
949
+ } else if (element.tokenType === ShutingyardType.OPERATION) {
950
+ switch (element.token) {
951
+ case '-':
952
+ // this should only happen for negative powers or for negative coefficient.
953
+ q2 = (stack.pop()) ?? new Monom().zero()
954
+ q1 = (stack.pop()) ?? new Monom().zero()
955
+
956
+ stack.push(q1.subtract(q2))
957
+
958
+ break
959
+ case '*':
960
+ // Get the last element in the stack
961
+ q2 = (stack.pop()) ?? new Monom().one()
962
+ q1 = (stack.pop()) ?? new Monom().one()
963
+
964
+ stack.push(q1.multiply(q2))
965
+ break
966
+ case '/':
967
+ // Get the last element in the stack
968
+ q2 = (stack.pop()) ?? new Monom().one()
969
+ q1 = (stack.pop()) ?? new Monom().one()
970
+
971
+ stack.push(q1.divide(q2))
972
+ break
973
+ case '^': {
974
+ // get the two last elements in the stack
975
+ const poppedCoefficient = stack.pop()?.coefficient
976
+ pow = poppedCoefficient ?? new Fraction().one()
977
+ m = stack.pop() ?? new Monom().one()
978
+
979
+ letter = m.variables[0]
980
+
981
+ if (letter) {
982
+ m.setLetter(letter, pow)
983
+ }
984
+
985
+ stack.push(m)
986
+ // this.multiply(m.clone())
987
+ break
988
+ }
989
+ }
990
+ }
991
+ }
992
+
993
+ private _getLiteralDividers(arr: literalType<Fraction>[], letter: string): literalType<Fraction>[] {
994
+ const tmpList: Record<string, Fraction>[] = []
995
+
996
+ // Be default, this.literal[letter] should be a rational number.
997
+ for (let d = 0; d <= this.literal[letter].value; d++) {
998
+ if (arr.length === 0) {
999
+ const litt: literalType<Fraction> = {}
1000
+ litt[letter] = new Fraction(d)
1001
+ tmpList.push(litt)
1002
+ } else {
1003
+ for (const item of arr) {
1004
+ const litt: literalType<Fraction> = {}
1005
+ for (const currentLetter in item) {
1006
+ litt[currentLetter] = item[currentLetter]
1007
+ }
1008
+ litt[letter] = new Fraction(d)
1009
+ tmpList.push(litt)
1010
+ }
1011
+ }
1012
+ }
1013
+ return tmpList
1014
+ }
1015
+ }