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,539 @@
1
+ import type {InputValue, ISolution} from "../pimath.interface"
2
+ import type {Polynom} from "./polynom"
3
+ import {Fraction} from "../coefficients"
4
+ import {Numeric} from "../numeric"
5
+ import type {Equation} from "./equation"
6
+
7
+ export class EquationSolver {
8
+ #bissectionCompexityCounter: number
9
+ #bissectionDeltaX: number
10
+ readonly #leftPolynom: Polynom
11
+ readonly #variable: string
12
+
13
+ constructor(left: Polynom | Equation, right?: Polynom, variable = "x") {
14
+ this.#variable = variable
15
+ this.#bissectionDeltaX = 1e-4
16
+ this.#bissectionCompexityCounter = 0
17
+
18
+ if (Object.hasOwn(left, 'moveLeft')) {
19
+ const equ = left as Equation
20
+ this.#leftPolynom = equ.left.clone().subtract(equ.right)
21
+ } else {
22
+ this.#leftPolynom = (left as Polynom).clone().subtract(right ?? 0)
23
+ }
24
+ }
25
+
26
+ get bissectionCompexityCounter(){
27
+ return this.#bissectionCompexityCounter
28
+ }
29
+
30
+ get bissectionDeltaX() {
31
+ return this.#bissectionDeltaX
32
+ }
33
+
34
+ set bissectionDeltaX(value: number) {
35
+ this.#bissectionDeltaX = value
36
+ }
37
+
38
+ public solve(): ISolution[] {
39
+ const degree = this.#leftPolynom.degree().value
40
+
41
+ if (degree === 0) {
42
+ return []
43
+ }
44
+
45
+ if (degree === 1) {
46
+ return this.#solveLinear()
47
+ }
48
+
49
+ if (degree === 2) {
50
+ return this.#solveQuadratic()
51
+ }
52
+
53
+ // Try to solve by factorization -> exact solutions.
54
+ const {solutions, polynom} = this.#solveByFactorization()
55
+
56
+ // The remaining polynom is of degree zero. No more solutions available.
57
+ if (polynom.degree().isZero()) {
58
+ return solutions
59
+ }
60
+
61
+ // The remaining polyno is of degree one or two, but cannot be solved by factorization (!).
62
+ if (polynom.degree().value <= 2) {
63
+ return solutions.concat(
64
+ new EquationSolver(polynom.clone()).solve()
65
+ )
66
+ }
67
+
68
+ // Use approximative solutions, using bissection algorithm.
69
+ // TODO: doit gérer le fait que si on a trouvé des solutions, on peut réduire avant de faire la bissection
70
+ this.#bissectionCompexityCounter = 0
71
+ return solutions.concat(
72
+ this.#solveByBissection(polynom)
73
+ ).sort((a, b) => a.value - b.value)
74
+ }
75
+
76
+ public solveAsCardan(): ISolution[] {
77
+ if (this.#leftPolynom.degree().value !== 3) {
78
+ throw new Error("The equation is not cubic.")
79
+ }
80
+ return this.#solveCubic_CardanFormula()
81
+ }
82
+
83
+ #makeApproximativeSolution(value: number, output?: { tex: string, display: string }): ISolution {
84
+ return {
85
+ variable: this.#variable,
86
+ exact: false,
87
+ value: +value.toFixed(10),
88
+ tex: output?.tex ?? '',
89
+ display: output?.display ?? ''
90
+ }
91
+ }
92
+
93
+ #makeSolution(value: InputValue<Fraction>): ISolution {
94
+ if (value instanceof Fraction && value.isApproximative()) {
95
+ return this.#makeApproximativeSolution(value.value)
96
+ }
97
+
98
+ const fraction = new Fraction(value)
99
+ return {
100
+ variable: this.#variable,
101
+ exact: fraction,
102
+ value: fraction.value,
103
+ tex: fraction.tex,
104
+ display: fraction.display
105
+ }
106
+ }
107
+
108
+ // Solve using bissection algorithm (approximative solution)
109
+ #solveByBissection(polynom: Polynom): ISolution[] {
110
+ const solutions: ISolution[] = []
111
+ const degree = polynom.degree().value
112
+
113
+ // Calculate the Cauchy Bounds.
114
+ const [a, ...values] = polynom.getCoefficients()
115
+ const B = 2 + Math.max(...values.map(x => x.value / a.value))
116
+
117
+ // Cut the [-B;B] interval in *n* parts : n = 100
118
+
119
+ // Calculate the f(x) value at each points
120
+ const evaluatedPoints = this.#solveByBissection_evaluatePoints(polynom, B, 100)
121
+
122
+ // Check if there is a least n opposite couples
123
+ const couples = this.#solveByBissection_getCouples(evaluatedPoints, degree)
124
+
125
+ // All solutions fund !
126
+ couples.forEach(couple => {
127
+ const [a, b] = couple
128
+
129
+ if (a === b) {
130
+ // Exact solution
131
+ solutions.push(this.#makeSolution(a))
132
+ } else {
133
+ const bissection = this.#solveByBissection_algorithm(polynom, a, b)
134
+
135
+ if (bissection !== null) {
136
+ solutions.push(this.#makeApproximativeSolution(bissection))
137
+ }
138
+ }
139
+ })
140
+
141
+ console.log('COMPLEXITY: ', this.#bissectionCompexityCounter)
142
+ return solutions
143
+ }
144
+
145
+ #solveByBissection_algorithm(polynom: Polynom, a: number, b: number): number | null {
146
+ let fa = polynom.evaluate(a, true) as number
147
+ let fb = polynom.evaluate(b, true) as number
148
+
149
+ if (fa * fb > 0) {
150
+ console.log("Pas de racine dans l'intervalle donné")
151
+ return null
152
+ }
153
+
154
+ let mid: number
155
+ while ((b - a) / 2 > this.#bissectionDeltaX) {
156
+ this.#bissectionCompexityCounter++
157
+
158
+ mid = (a + b) / 2
159
+ const fmid = polynom.evaluate(mid, true) as number
160
+
161
+ if (fmid === 0) {
162
+ return mid // racine exacte trouvée
163
+ } else if (fa * fmid < 0) {
164
+ b = mid
165
+ fb = fmid
166
+ } else {
167
+ a = mid
168
+ fa = fmid
169
+ }
170
+ }
171
+ return (a + b) / 2 // retourner la racine approximative
172
+ }
173
+
174
+ #solveByBissection_evaluatePoints(polynom: Polynom, bounds: number, slice: number): { x: number, fx: number }[] {
175
+
176
+ const evaluatedPoints: { x: number, fx: number }[] = []
177
+
178
+ const dx = 2 * bounds / slice
179
+
180
+ for (let searchValue = -bounds; searchValue <= bounds; searchValue += dx) {
181
+
182
+ const x = Numeric.numberCorrection(searchValue)
183
+ evaluatedPoints.push(
184
+ {
185
+ x,
186
+ fx: polynom.evaluate(x, true) as number
187
+ }
188
+ )
189
+ }
190
+
191
+ return evaluatedPoints
192
+ }
193
+
194
+ #solveByBissection_getCouples(evaluatedPoints: { x: number, fx: number }[], degree: number): [number, number][] {
195
+ const couples: [number, number][] = []
196
+
197
+ for (let index = 1; index < evaluatedPoints.length; index++) {
198
+
199
+ const value = evaluatedPoints[index]
200
+ const previous = evaluatedPoints[index - 1]
201
+
202
+ if (value.fx === 0) {
203
+ // exact value
204
+ couples.push([value.x, value.x])
205
+ } else if (value.fx * previous.fx < 0) {
206
+ // both evaluated expression are of opposite sign.
207
+ couples.push([previous.x, value.x])
208
+ }
209
+
210
+ if (couples.length === degree) {
211
+ // All couples are found.
212
+ return couples
213
+ }
214
+ }
215
+
216
+ return couples
217
+ }
218
+
219
+ #solveByFactorization(): { solutions: ISolution[], polynom: Polynom } {
220
+ // Move everything to the left.
221
+
222
+ // Get the polynom on the left (on the right, it's zero)
223
+ const left = this.#leftPolynom.clone()
224
+
225
+ // The solutions of the equation
226
+ const solutions: ISolution[] = []
227
+
228
+ // multiply by the lcm of the denominators
229
+ // to get rid of the fractions
230
+ const lcm = left.lcmDenominator()
231
+ if (lcm !== 1) {
232
+ left.multiply(lcm)
233
+ }
234
+
235
+ // alternative method : if there is no monom of degree zero.
236
+ // - get the monom with the smallest degree.
237
+ // - if degree>0, divide by x^{degree}
238
+ const a = left.monomByDegree().coefficient
239
+ const b = left.monomByDegree(0).coefficient
240
+ if (b.isZero()) {
241
+ solutions.push(this.#makeSolution(0))
242
+
243
+ const m = left.monoms.reduce((min, curr) => curr.degree().value < min.degree().value ? curr : min)
244
+ const k = m.coefficient
245
+ m.clone().divide(k) // make the monom unit
246
+ left.divide(m)
247
+ }
248
+
249
+ // get all dividers of a and b
250
+ const dividersA = Numeric.dividers(a.value)
251
+ const dividersB = Numeric.dividers(b.value)
252
+
253
+ // gel all possible solutions
254
+ const testingSolutions: Fraction[] = []
255
+ for (const da of dividersA) {
256
+ for (const db of dividersB) {
257
+ const f = new Fraction(db, da)
258
+ if (!testingSolutions.find(s => s.value === f.value)) {
259
+ testingSolutions.push(f.clone())
260
+ testingSolutions.push(f.opposite().clone())
261
+ }
262
+
263
+ }
264
+ }
265
+
266
+ // Each value in testingSolutions are "unique" -> juste test them to see if it evaluates to zero.
267
+ testingSolutions.forEach(f => {
268
+ if ((left.evaluate(f) as Fraction).isZero()) {
269
+ solutions.push(this.#makeSolution(f))
270
+ }
271
+ })
272
+
273
+ // divide the left polynom by the solutions (as polynom)
274
+ // to get the reduced polynom
275
+ for (const s of solutions) {
276
+ // all solutions are exact solutions.
277
+ // skip the zero solutions if it exists.
278
+ if ((s.exact as Fraction).isZero()) {
279
+ continue
280
+ }
281
+
282
+ // // if the solution is exact and is zero, it's already divided: skip it !
283
+ // if (s.exact !== false && (s.exact as Fraction).isZero()) {
284
+ // continue
285
+ // }
286
+
287
+ const p = left.clone().fromCoefficients(
288
+ (s.exact as Fraction).denominator,
289
+ -(s.exact as Fraction).numerator
290
+ )
291
+
292
+ // const p = this.#equation.clone().parse('x', (s.exact as Fraction).denominator, -(s.exact as Fraction).numerator)
293
+
294
+ while (left.isDividableBy(p)) {
295
+ left.divide(p)
296
+ }
297
+ }
298
+
299
+ // if the reduced polynom is of degree 0, we have found all the solutions
300
+ // if the reduced polynom is of degree greater than 3, we can't solve it
301
+ if (left.degree().isZero() || left.degree().value > 3) {
302
+ // Tri des réponses
303
+ solutions.sort((a, b) => a.value - b.value)
304
+ return {solutions, polynom: left}
305
+ }
306
+
307
+ // if the reduced polynom is of degree 1 or 2, we can solve it
308
+ const zeroPolynom = left.clone().zero()
309
+
310
+ const solver = new EquationSolver(left, zeroPolynom, this.#variable)
311
+ return {
312
+ solutions: solutions
313
+ .concat(solver.solve())
314
+ .sort((a, b) => a.value - b.value),
315
+ polynom: zeroPolynom
316
+ }
317
+ }
318
+
319
+ #solveCubic_CardanFormula(): ISolution[] {
320
+ // get the coefficients of the equation
321
+ const left = this.#leftPolynom
322
+
323
+ // left is a polynom ax^3+bx^2+cx+d => the solution is x = (-b±√(b^2-4ac))/2a
324
+ const a = left.monomByDegree(3).coefficient
325
+ const b = left.monomByDegree(2).coefficient
326
+ const c = left.monomByDegree(1).coefficient
327
+ const d = left.monomByDegree(0).coefficient
328
+
329
+ // normalize the coefficient by dividing by a
330
+ const an = b.clone().divide(a)
331
+ const bn = c.clone().divide(a)
332
+ const cn = d.clone().divide(a)
333
+
334
+ // Depressed cubic equation
335
+ // x^3+px+q=0
336
+ const p = bn.clone().subtract(an.clone().pow(2).divide(3))
337
+ const q = cn.clone()
338
+ .subtract(an.clone().multiply(bn).divide(3))
339
+ .add(an.clone().pow(3).multiply(2).divide(27))
340
+
341
+
342
+ // Cardan method
343
+ // X^2 + qX - p^3/27 = 0
344
+ // X^2 -SX + P = 0
345
+ // S = u^3 + v^3 = -q
346
+ // P = u^3v^3 = -p^3/27
347
+ // u^3 and v^3 are the roots of the equation
348
+ const S = q.clone().opposite()
349
+ const P = p.clone().opposite().pow(3).divide(27)
350
+
351
+ // Discriminant : delta = -(S^2 - 4P)
352
+ // delta < 0 : 1 real solution
353
+ // delta = 0 : 2 real solutions
354
+ // delta > 0 : 3 real solutions
355
+ const delta = S.clone().pow(2).subtract(P.clone().multiply(4)).opposite()
356
+ // console.log('an=', an.display, 'bn=', bn.display, 'cn=', cn.display)
357
+ // console.log('p=', p.display, 'q=', q.display)
358
+ // console.log('S=', S.display, 'P=', P.display)
359
+ // console.log('delta=', delta.display)
360
+
361
+ // if delta is negative, there is one real solution
362
+ if (delta.isNegative()) {
363
+ const u = q.clone().opposite().add(delta.clone().opposite().sqrt()).divide(2).root(3)
364
+ const v = q.clone().opposite().subtract(delta.clone().opposite().sqrt()).divide(2).root(3)
365
+
366
+ const x = u.clone().add(v).subtract(an.clone().divide(3))
367
+
368
+ return [this.#makeSolution(x)]
369
+ }
370
+
371
+ // if delta is zero, there are two real solutions
372
+ if (delta.isZero()) {
373
+ const u = q.clone().opposite().divide(2).root(3)
374
+
375
+ const x1 = u.clone().opposite().subtract(an.clone().divide(3))
376
+ const x2 = u.clone().multiply(2).subtract(an.clone().divide(3))
377
+
378
+ // There is only one unique solution
379
+ if (x1.isEqual(x2)) {
380
+ return [this.#makeSolution(x1)]
381
+ }
382
+
383
+ return [
384
+ this.#makeSolution(x2),
385
+ this.#makeSolution(x1)
386
+ ].sort((a, b) => a.value - b.value)
387
+ }
388
+
389
+ // if delta is positive, there are three real solutions
390
+ if (delta.isPositive()) {
391
+ const x: number[] = []
392
+ const pv = p.value,
393
+ qv = q.value,
394
+ anv = an.value
395
+
396
+ for (let i = 0; i < 3; i++) {
397
+ x.push(2 * Math.sqrt(-pv / 3) * Math.cos(Math.acos(3 * qv / (2 * pv) * Math.sqrt(-3 / pv)) / 3 + 2 * Math.PI * i / 3) - anv / 3)
398
+ }
399
+
400
+ return x
401
+ .map(v => this.#makeApproximativeSolution(v))
402
+ .sort((a, b) => a.value - b.value)
403
+
404
+ }
405
+
406
+ return []
407
+ }
408
+
409
+ #solveLinear(): ISolution[] {
410
+ // The equation is linear.
411
+ const [a, b] = this.#leftPolynom.getCoefficients()
412
+
413
+ // left is a polynom ax+b => the solution is x = -b/a
414
+ const f = b.opposite().divide(a)
415
+
416
+ return [
417
+ this.#makeSolution(f)
418
+ ]
419
+ }
420
+
421
+ #solveQuadratic(): ISolution[] {
422
+
423
+ // The equation is quadratic.
424
+ // We can solve it by isolating the variable.
425
+
426
+ // The monom with greatest degree must be positive.
427
+ const left = this.#leftPolynom
428
+ if (left.monomByDegree().coefficient.isNegative()) {
429
+ left.opposite()
430
+ }
431
+
432
+ // left is a polynom ax^2+bx+c => the solution is x = (-b±√(b^2-4ac))/2a
433
+ const [a, b, c] = left.getCoefficients()
434
+
435
+ // delta2 = b^2-4ac
436
+ const delta2 = b.clone().pow(2).subtract(a.clone().multiply(c).multiply(4))
437
+
438
+ // if delta2 is negative, there is no solution
439
+ if (delta2.isNegative()) {
440
+ return []
441
+ }
442
+
443
+ // if delta2 is zero, there is one solution
444
+ // if delta2 is positive, there are two solutions
445
+ // if delta2 is a square, it will be an exact solution.
446
+
447
+ if (delta2.isSquare()) {
448
+ // delta is a fraction.
449
+ // the solutions are (-b±√(b^2-4ac))/2a
450
+ const delta = delta2.sqrt()
451
+ const f1 = b.clone().opposite().subtract(delta).divide(a.clone().multiply(2))
452
+ const f2 = b.clone().opposite().add(delta).divide(a.clone().multiply(2))
453
+
454
+ // Delta is zero, there is only one solution
455
+ if (delta.isZero()) {
456
+ return [this.#makeSolution(f1)]
457
+ }
458
+
459
+ // delta is positive, there are two solutions
460
+ return [
461
+ this.#makeSolution(f1),
462
+ this.#makeSolution(f2)
463
+ ].sort((a, b) => a.value - b.value)
464
+ }
465
+
466
+ // delta is not a square, there are one or two approximative solutions.
467
+ // We will use the approximate value of the square root.
468
+ // const delta = delta2.value ** 0.5
469
+ // const f1 = (-b.value + delta) / (2 * a.value)
470
+ // const f2 = (-b.value - delta) / (2 * a.value)
471
+
472
+ return this.#solveQuadratic_Output(a, b, delta2)
473
+ }
474
+
475
+ #solveQuadratic_Output(a: Fraction, b: Fraction, delta: Fraction): ISolution[] {
476
+ // -b +/- sqrt(delta) / 2a
477
+ // reduce the sqrt - extract pow.
478
+
479
+ // Get the greatest square factor
480
+ const deltaFactor: number = Numeric
481
+ .dividers(delta.value)
482
+ .filter(x => Math.sqrt(x) % 1 === 0)
483
+ .map(x => Math.sqrt(x)).pop() ?? 1
484
+
485
+ // Get the GCD of a, b, and the greatest delta factor.
486
+ const gcd = Numeric.gcd(2 * a.value, b.value, deltaFactor) * (a.isNegative() ? -1 : 1)
487
+
488
+ // Calculate the various values and transforming
489
+ const b2 = b.clone().divide(gcd).opposite()
490
+ const a2 = a.clone().divide(gcd).multiply(2)
491
+ const deltaGcd = Math.abs(deltaFactor / gcd)
492
+ const deltaTex = `${deltaFactor === 1 ? '' : deltaGcd + ' '}\\sqrt{ ${delta.clone().divide(deltaFactor ** 2).tex} }`
493
+ const deltaDisplay = `${deltaFactor === 1 ? '' : deltaGcd}sqrt(${delta.clone().divide(deltaFactor ** 2).display})`
494
+ // const deltaK1 = deltaFactor === 1 ? '-' : `-${deltaGcd} `
495
+ // const deltaK2 = deltaFactor === 1 ? '+' : `+${deltaGcd} `
496
+
497
+ function texOutput(a: string, b: string, sign: string, delta: string) {
498
+ // (B+D)/A
499
+ const B = b === '0' ? '' : b
500
+ const S = (sign === '-' || B !== '') ? ` ${sign} ` : ''
501
+
502
+ if (a === "1") {
503
+ return `${B}${S}${delta}`
504
+ }
505
+ return `\\frac{ ${S}${S}${delta} }{ ${a} }`
506
+ }
507
+
508
+ function displayOutput(a: string, b: string, sign: string, delta: string) {
509
+ // (B+D)/A
510
+ const B = b === '0' ? '' : b
511
+ const S = (sign === '-' || B !== '') ? sign : ''
512
+
513
+
514
+ if (a === "1") {
515
+ return `${B}${S}${delta}`
516
+ }
517
+ return `(${B}${S}${delta})/${a}`
518
+ }
519
+
520
+ const d = delta.value ** 0.5
521
+ const f1 = (-b.value - d) / (2 * a.value)
522
+ const f2 = (-b.value + d) / (2 * a.value)
523
+
524
+ return [
525
+ this.#makeApproximativeSolution(f1,
526
+ {
527
+ tex: texOutput(a2.tex, b2.tex, '-', deltaTex),
528
+ display: displayOutput(a2.display, b2.display, '-', deltaDisplay),
529
+ }
530
+ ),
531
+ this.#makeApproximativeSolution(f2,
532
+ {
533
+ tex: texOutput(a2.tex, b2.tex, '+', deltaTex),
534
+ display: displayOutput(a2.display, b2.display, '+', deltaDisplay),
535
+ }
536
+ )
537
+ ].sort((a, b) => a.value - b.value)
538
+ }
539
+ }