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.
- package/dist/pimath.js +188 -159
- package/dist/pimath.js.map +1 -1
- package/package.json +4 -2
- package/src/algebra/equation.ts +556 -0
- package/src/algebra/equationSolver.ts +539 -0
- package/src/algebra/factor.ts +339 -0
- package/src/algebra/index.ts +11 -0
- package/src/algebra/linearSystem.ts +388 -0
- package/src/algebra/logicalset.ts +256 -0
- package/src/algebra/matrix.ts +474 -0
- package/src/algebra/monom.ts +1015 -0
- package/src/algebra/operations.ts +24 -0
- package/src/algebra/polyFactor.ts +668 -0
- package/src/algebra/polynom.ts +1394 -0
- package/src/analyze/solution.ts +115 -0
- package/src/analyze/tableOfSigns.ts +30 -0
- package/src/coefficients/fraction.ts +678 -0
- package/src/coefficients/index.ts +4 -0
- package/src/coefficients/nthRoot.ts +149 -0
- package/src/coefficients/root.ts +299 -0
- package/src/geometry/circle.ts +386 -0
- package/src/geometry/geomMath.ts +70 -0
- package/src/geometry/index.ts +10 -0
- package/src/geometry/line.ts +677 -0
- package/src/geometry/line3.ts +206 -0
- package/src/geometry/plane3.ts +170 -0
- package/src/geometry/point.ts +66 -0
- package/src/geometry/sphere3.ts +214 -0
- package/src/geometry/triangle.ts +354 -0
- package/src/geometry/vector.ts +341 -0
- package/src/helpers.ts +35 -0
- package/src/index.ts +60 -0
- package/src/numeric.ts +199 -0
- package/src/pimath.interface.ts +160 -0
- package/src/randomization/algebra/rndEquation.ts +41 -0
- package/src/randomization/algebra/rndMonom.ts +39 -0
- package/src/randomization/algebra/rndPolynom.ts +86 -0
- package/src/randomization/coefficient/rndFraction.ts +38 -0
- package/src/randomization/geometry/rndCircle.ts +27 -0
- package/src/randomization/geometry/rndLine.ts +37 -0
- package/src/randomization/geometry/rndLine3.ts +27 -0
- package/src/randomization/geometry/rndVector.ts +63 -0
- package/src/randomization/random.ts +91 -0
- package/src/randomization/rndHelpers.ts +102 -0
- package/src/randomization/rndTypes.ts +63 -0
- package/types/algebra/equationSolver.d.ts +3 -0
- package/types/algebra/equationSolver.d.ts.map +1 -1
- package/types/algebra/polyFactor.d.ts +5 -0
- package/types/algebra/polyFactor.d.ts.map +1 -1
- package/types/analyze/solution.d.ts +21 -0
- package/types/analyze/solution.d.ts.map +1 -0
- package/types/analyze/tableOfSigns.d.ts +9 -0
- package/types/analyze/tableOfSigns.d.ts.map +1 -0
- package/types/coefficients/root.d.ts +38 -0
- package/types/coefficients/root.d.ts.map +1 -0
- package/types/geometry/point.d.ts +1 -1
- package/types/geometry/point.d.ts.map +1 -1
- package/types/helpers.d.ts +1 -0
- package/types/helpers.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/numeric.d.ts +2 -0
- package/types/numeric.d.ts.map +1 -1
- package/types/pimath.interface.d.ts +26 -26
- package/types/pimath.interface.d.ts.map +1 -1
|
@@ -0,0 +1,1394 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polynom module contains everything necessary to handle polynoms.*
|
|
3
|
+
*/
|
|
4
|
+
import {ShutingYard, ShutingyardType, type Token} from "piexpression"
|
|
5
|
+
import type {
|
|
6
|
+
IAlgebra,
|
|
7
|
+
IAnalyse,
|
|
8
|
+
IExpression,
|
|
9
|
+
InputAlgebra,
|
|
10
|
+
InputValue,
|
|
11
|
+
IPiMathObject,
|
|
12
|
+
ISolution,
|
|
13
|
+
literalType,
|
|
14
|
+
TABLE_OF_SIGNS,
|
|
15
|
+
TABLE_OF_SIGNS_VALUES
|
|
16
|
+
} from "../pimath.interface"
|
|
17
|
+
import {Fraction} from "../coefficients"
|
|
18
|
+
import {Numeric} from '../numeric'
|
|
19
|
+
import {EquationSolver} from './equationSolver'
|
|
20
|
+
import {Monom} from './monom'
|
|
21
|
+
import {replace_in_array} from "../helpers"
|
|
22
|
+
import {operation_pow} from "./operations"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
export type PolynomParsingType = InputValue<Polynom> | Monom
|
|
26
|
+
|
|
27
|
+
export interface IEuclidean {
|
|
28
|
+
quotient: Polynom,
|
|
29
|
+
reminder: Polynom
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Polynom class can handle polynoms, reorder, resolve, ...
|
|
34
|
+
* ```
|
|
35
|
+
* let P = new Polynom('3x-4')
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export class Polynom implements IPiMathObject<Polynom>,
|
|
39
|
+
IExpression<Polynom>,
|
|
40
|
+
IAnalyse<Polynom>,
|
|
41
|
+
IAlgebra<Polynom> {
|
|
42
|
+
|
|
43
|
+
#defaultVariable = 'x'
|
|
44
|
+
#factors: Polynom[]
|
|
45
|
+
#monoms: Monom[]
|
|
46
|
+
#roots: ISolution[]
|
|
47
|
+
#rootsCache = false
|
|
48
|
+
|
|
49
|
+
constructor(value: InputValue<Fraction>)
|
|
50
|
+
constructor(value: string)
|
|
51
|
+
constructor(value: Monom)
|
|
52
|
+
constructor(value: Polynom)
|
|
53
|
+
constructor(...values: InputValue<Fraction>[])
|
|
54
|
+
constructor(...values: InputAlgebra<Polynom>[])
|
|
55
|
+
constructor(polynomString?: InputAlgebra<Polynom>, ...values: InputAlgebra<Fraction>[]) {
|
|
56
|
+
this.#monoms = []
|
|
57
|
+
this.#factors = []
|
|
58
|
+
this.#roots = []
|
|
59
|
+
|
|
60
|
+
if (polynomString !== undefined) {
|
|
61
|
+
this.parse(polynomString, ...values)
|
|
62
|
+
}
|
|
63
|
+
return this
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Parse a string to a polynom.
|
|
68
|
+
* @param inputStr
|
|
69
|
+
* @param values
|
|
70
|
+
*/
|
|
71
|
+
public parse = (inputStr: PolynomParsingType, ...values: InputAlgebra<Monom>[]): this => {
|
|
72
|
+
// Reset the main variables.
|
|
73
|
+
this.#monoms = []
|
|
74
|
+
this.#factors = []
|
|
75
|
+
|
|
76
|
+
if (typeof inputStr === 'string') {
|
|
77
|
+
return this.#parseString(inputStr, ...values)
|
|
78
|
+
} else if (
|
|
79
|
+
(typeof inputStr === 'number' || inputStr instanceof Fraction || inputStr instanceof Monom)
|
|
80
|
+
&& (values.length === 0)
|
|
81
|
+
) {
|
|
82
|
+
this.#monoms.push(new Monom(inputStr as Monom))
|
|
83
|
+
} else if (inputStr instanceof Monom && values.length > 0) {
|
|
84
|
+
this.#monoms.push(new Monom(inputStr))
|
|
85
|
+
values.forEach(m => {
|
|
86
|
+
this.#monoms.push(new Monom(m as Monom))
|
|
87
|
+
})
|
|
88
|
+
} else if (inputStr instanceof Polynom) {
|
|
89
|
+
for (const m of inputStr.monoms) {
|
|
90
|
+
this.#monoms.push(m.clone())
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
return this
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Clone the polynom
|
|
100
|
+
*/
|
|
101
|
+
public clone = (): Polynom => {
|
|
102
|
+
const P = new Polynom()
|
|
103
|
+
const M: Monom[] = []
|
|
104
|
+
|
|
105
|
+
for (const m of this.#monoms) {
|
|
106
|
+
M.push(m.clone())
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
P.monoms = M
|
|
111
|
+
|
|
112
|
+
return P
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
public get tex(): string {
|
|
116
|
+
return this.#genDisplay('tex')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public get display(): string {
|
|
120
|
+
return this.#genDisplay()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public add = (...values: InputAlgebra<Polynom>[]): Polynom => {
|
|
124
|
+
|
|
125
|
+
for (const value of values) {
|
|
126
|
+
if (value instanceof Polynom) {
|
|
127
|
+
this.#monoms = this.#monoms.concat(value.monoms)
|
|
128
|
+
} else if (value instanceof Monom) {
|
|
129
|
+
this.#monoms.push(value.clone())
|
|
130
|
+
} else if (typeof value === "number" && Number.isSafeInteger(value)) {
|
|
131
|
+
this.#monoms.push(new Monom(value.toString()))
|
|
132
|
+
} else {
|
|
133
|
+
this.#monoms.push(new Monom(value))
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
return this.reduce()
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public commonMonom = (): Monom => {
|
|
142
|
+
const M = new Monom().one()
|
|
143
|
+
const numerator: number = this.gcdNumerator()
|
|
144
|
+
const denominator: number = this.gcdDenominator()
|
|
145
|
+
const degree = this.degree()
|
|
146
|
+
|
|
147
|
+
M.coefficient = new Fraction(numerator, denominator)
|
|
148
|
+
for (const L of this.variables) {
|
|
149
|
+
// Initialize the setLetter with the max degree
|
|
150
|
+
M.setLetter(L, degree)
|
|
151
|
+
for (const m of this.#monoms) {
|
|
152
|
+
M.setLetter(L, Fraction.min(m.degree(L), M.degree(L)))
|
|
153
|
+
if (M.degree(L).isZero()) {
|
|
154
|
+
break
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return M
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public degree = (letter?: string): Fraction => {
|
|
163
|
+
let d: Fraction = new Fraction().zero()
|
|
164
|
+
for (const m of this.#monoms) {
|
|
165
|
+
d = Fraction.max(m.degree(letter).value, d)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return d
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public derivative = (letter?: string): Polynom => {
|
|
172
|
+
const dP = new Polynom()
|
|
173
|
+
|
|
174
|
+
for (const m of this.#monoms) {
|
|
175
|
+
dP.add(m.derivative(letter))
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return dP.reduce()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
public divide = (value: InputAlgebra<Polynom>): Polynom => {
|
|
182
|
+
|
|
183
|
+
if (value instanceof Fraction) {
|
|
184
|
+
return this.#divideByFraction(value)
|
|
185
|
+
} else if (typeof value === 'number' && Number.isSafeInteger(value)) {
|
|
186
|
+
return this.#divideByInteger(value)
|
|
187
|
+
} else if (value instanceof Monom) {
|
|
188
|
+
return this.divide(new Polynom(value))
|
|
189
|
+
} else if (value instanceof Polynom) {
|
|
190
|
+
if (value.monoms.length === 1 && value.variables.length === 0) {
|
|
191
|
+
return this.#divideByFraction(value.monoms[0].coefficient)
|
|
192
|
+
} else {
|
|
193
|
+
const {quotient, reminder} = this.euclidean(value)
|
|
194
|
+
if (reminder.isZero()) {
|
|
195
|
+
this.#monoms = quotient.monoms
|
|
196
|
+
return this
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} else if (typeof value === 'string') {
|
|
200
|
+
return this.divide(new Polynom(value))
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
throw new Error(`Cannot divide by ${value as unknown as string}`)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
public empty = (): this => {
|
|
207
|
+
this.#monoms = []
|
|
208
|
+
return this
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Divide the current polynom by another polynom.
|
|
213
|
+
* @param P
|
|
214
|
+
* returns {quotient: Polynom, reminder: Polynom}
|
|
215
|
+
*/
|
|
216
|
+
public euclidean = (P: Polynom): IEuclidean => {
|
|
217
|
+
const letter: string = P.variables[0]
|
|
218
|
+
const quotient: Polynom = new Polynom().zero()
|
|
219
|
+
const reminder: Polynom = this.clone().reorder(letter)
|
|
220
|
+
|
|
221
|
+
// There is no variable - means it's a number
|
|
222
|
+
if (P.variables.length === 0) {
|
|
223
|
+
const q = this.clone().divide(P)
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
quotient: q.reduce(),
|
|
227
|
+
reminder: new Polynom().zero()
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Get at least a letter
|
|
232
|
+
const maxMP: Monom = P.monomByDegree(undefined, letter)
|
|
233
|
+
const degreeP: Fraction = P.degree(letter)
|
|
234
|
+
|
|
235
|
+
let newM: Monom
|
|
236
|
+
|
|
237
|
+
// Make the Euclidean division of the two polynoms.
|
|
238
|
+
let MaxIteration = this.degree(letter).value * 2
|
|
239
|
+
while (reminder.degree(letter).isGeq(degreeP) && MaxIteration > 0) {
|
|
240
|
+
MaxIteration--
|
|
241
|
+
|
|
242
|
+
// Get the greatest monom divided by the max monom of the divider
|
|
243
|
+
newM = reminder.monomByDegree(undefined, letter).clone().divide(maxMP)
|
|
244
|
+
|
|
245
|
+
if (newM.isZero()) {
|
|
246
|
+
continue
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Get the new quotient and reminder.
|
|
250
|
+
quotient.add(newM)
|
|
251
|
+
reminder.subtract(P.clone().multiply(newM)).reduce()
|
|
252
|
+
|
|
253
|
+
// Check if the reminder is zero.
|
|
254
|
+
if (newM.degree(letter).isZero()) {
|
|
255
|
+
break
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
quotient.reduce()
|
|
260
|
+
reminder.reduce()
|
|
261
|
+
return {quotient, reminder}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
public evaluate = (values: literalType<Fraction | number> | InputValue<Fraction>, asNumeric?: boolean): Fraction | number => {
|
|
265
|
+
// Return the numeric value, without using Fraction
|
|
266
|
+
if (asNumeric) {
|
|
267
|
+
return this.#evaluateAsNumeric(values)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Build the evaluated fraction
|
|
271
|
+
const r = new Fraction().zero()
|
|
272
|
+
this.#monoms.forEach(monom => {
|
|
273
|
+
//console.log('Evaluate polynom: ', monom.display, values, monom.evaluate(values).display);
|
|
274
|
+
r.add(monom.evaluate(values, asNumeric))
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
return r
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// -------------------------------------
|
|
281
|
+
/**
|
|
282
|
+
* Factorize a polynom and store the best results in factors.
|
|
283
|
+
* @param letter
|
|
284
|
+
*/
|
|
285
|
+
|
|
286
|
+
// REFACTOR: duplicate code with equationSolver.
|
|
287
|
+
public factorize = (letter?: string): Polynom[] => {
|
|
288
|
+
let factors: Polynom[] = []
|
|
289
|
+
let P = this.clone().reorder()
|
|
290
|
+
|
|
291
|
+
// Extract the common monom
|
|
292
|
+
// 2x^3+6x^2 => 2x^2
|
|
293
|
+
const M = P.commonMonom()
|
|
294
|
+
// If the polynom starts with a negative monom, factorize it.
|
|
295
|
+
if (P.monomByDegree().coefficient.isStrictlyNegative() && M.coefficient.isStrictlyPositive() && !M.isOne()) {
|
|
296
|
+
M.opposite()
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
if (!M.isOne()) {
|
|
301
|
+
const tempPolynom: Polynom = new Polynom(M)
|
|
302
|
+
factors = [tempPolynom.clone()]
|
|
303
|
+
P = P.euclidean(tempPolynom).quotient
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Main loop
|
|
307
|
+
let securityLoop = P.degree().clone().multiply(2).value,
|
|
308
|
+
maxDegree = 1
|
|
309
|
+
while (securityLoop >= 0) {
|
|
310
|
+
securityLoop--
|
|
311
|
+
if (P.monoms.length < 2) {
|
|
312
|
+
// The polynom has only one monom => 7x^2
|
|
313
|
+
// No need to continue.
|
|
314
|
+
if (!P.isOne()) {
|
|
315
|
+
factors.push(P.clone())
|
|
316
|
+
P.one()
|
|
317
|
+
}
|
|
318
|
+
break
|
|
319
|
+
} else if (P.degree(letter).isOne()) {
|
|
320
|
+
// The polynom is a first degree polynom => 3x-5
|
|
321
|
+
// No need to continue
|
|
322
|
+
factors.push(P.clone())
|
|
323
|
+
P.one()
|
|
324
|
+
break
|
|
325
|
+
} else {
|
|
326
|
+
// Create the list of all "potential" polynom dividers.
|
|
327
|
+
let allDividers: Polynom[] = this.#getAllPotentialFactors(P, maxDegree, letter ?? 'x')
|
|
328
|
+
maxDegree = P.degree(letter).value
|
|
329
|
+
|
|
330
|
+
// Actually: 100ms
|
|
331
|
+
while (allDividers.length > 0) {
|
|
332
|
+
const div = allDividers[0]
|
|
333
|
+
|
|
334
|
+
if (!P.isDividableBy(div))
|
|
335
|
+
// Not dividable. Remove it from the list
|
|
336
|
+
{
|
|
337
|
+
allDividers.shift()
|
|
338
|
+
} else {
|
|
339
|
+
// It's dividable - so make the division
|
|
340
|
+
const result = P.euclidean(div)
|
|
341
|
+
|
|
342
|
+
// Add the factor
|
|
343
|
+
factors.push(div)
|
|
344
|
+
|
|
345
|
+
// As it's dividable, get the quotient.
|
|
346
|
+
P = result.quotient.clone()
|
|
347
|
+
|
|
348
|
+
// filter all dividers that are no more suitable.
|
|
349
|
+
allDividers = allDividers.filter(x => {
|
|
350
|
+
const pX = P.monoms[0],
|
|
351
|
+
pC = P.monoms[P.monoms.length - 1],
|
|
352
|
+
dX = x.monoms[0],
|
|
353
|
+
dC = x.monoms[x.monoms.length - 1]
|
|
354
|
+
|
|
355
|
+
// Check last item (degree zero)
|
|
356
|
+
if (!pC.isDivisible(dC)) {
|
|
357
|
+
return false
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Check the first item (degree max)
|
|
361
|
+
return pX.isDivisible(dX)
|
|
362
|
+
})
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Maybe there is still something in the Polynom (not everything was possible to factorize)
|
|
369
|
+
if (!P.isOne()) {
|
|
370
|
+
factors.push(P.clone())
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
// Save the factors
|
|
375
|
+
this.#factors = factors
|
|
376
|
+
|
|
377
|
+
return this.#factors
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
public fromCoefficients(...values: InputValue<Fraction>[]) {
|
|
381
|
+
|
|
382
|
+
this.#monoms = []
|
|
383
|
+
const letter = this.#defaultVariable ?? 'x'
|
|
384
|
+
values.reverse().forEach((coeff, index) => {
|
|
385
|
+
const monom = new Monom()
|
|
386
|
+
monom.coefficient = new Fraction(coeff)
|
|
387
|
+
monom.setLetter(letter, index)
|
|
388
|
+
|
|
389
|
+
this.#monoms.push(monom)
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
return this.reorder()
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
public gcdDenominator = (): number => {
|
|
396
|
+
return Numeric.gcd(...this.getDenominators())
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
public gcdNumerator = (): number => {
|
|
400
|
+
return Numeric.gcd(...this.getNumerators())
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
public getCoefficients(): Fraction[] {
|
|
404
|
+
// Assume there is only one letter.
|
|
405
|
+
const orderedPolynom = this.clone().reorder()
|
|
406
|
+
|
|
407
|
+
const length = this.degree().value + 1
|
|
408
|
+
const coeffs = new Array(length).fill(new Fraction(0)) as unknown as Fraction[]
|
|
409
|
+
|
|
410
|
+
orderedPolynom.monoms.forEach(monom => {
|
|
411
|
+
const index = length - monom.degree().value - 1
|
|
412
|
+
coeffs[index] = monom.coefficient.clone()
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
// return orderedPolynom.monoms.map(x=>x.coefficient)
|
|
416
|
+
return coeffs
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Next functions are used for for commonMonom, which is used in the factorize method.
|
|
420
|
+
public getDenominators = (): number[] => {
|
|
421
|
+
const denominators: number[] = []
|
|
422
|
+
for (const m of this.#monoms) {
|
|
423
|
+
denominators.push(m.coefficient.denominator)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return denominators
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
public getNumerators = (): number[] => {
|
|
430
|
+
const numerators: number[] = []
|
|
431
|
+
for (const m of this.#monoms) {
|
|
432
|
+
numerators.push(m.coefficient.numerator)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return numerators
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
public getZeroes = (): ISolution[] => {
|
|
439
|
+
if (this.degree().isZero()) {
|
|
440
|
+
return []
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
this.roots = new EquationSolver(this.clone()).solve()
|
|
444
|
+
return this.roots
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
public hasVariable(letter: string): boolean {
|
|
448
|
+
return this.variables.includes(letter)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
public integrate = (a: InputValue<Fraction>, b: InputValue<Fraction>, letter = 'x'): Fraction => {
|
|
452
|
+
const primitive = this.primitive(letter)
|
|
453
|
+
|
|
454
|
+
const valuesA: literalType<Fraction> = {},
|
|
455
|
+
valuesB: literalType<Fraction> = {}
|
|
456
|
+
|
|
457
|
+
valuesA[letter] = new Fraction(a)
|
|
458
|
+
valuesB[letter] = new Fraction(b)
|
|
459
|
+
|
|
460
|
+
return (primitive.evaluate(valuesB) as Fraction).subtract(primitive.evaluate(valuesA))
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
public inverse(): Polynom | undefined {
|
|
464
|
+
return undefined
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
public isDeveloped = (polynomString: string): boolean => {
|
|
468
|
+
let P: Polynom
|
|
469
|
+
|
|
470
|
+
// Start by removing the parenthesis after a "power"
|
|
471
|
+
const pString = polynomString.replaceAll(/\^\(([-0-9/]+)\)/g, '$1')
|
|
472
|
+
|
|
473
|
+
// There is at least one parenthesis - it is not developed.
|
|
474
|
+
if (pString.includes('(') || pString.includes(')')) {
|
|
475
|
+
return false
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
// Try to build the polynom
|
|
480
|
+
try {
|
|
481
|
+
// Build the polynom
|
|
482
|
+
P = new Polynom(polynomString)
|
|
483
|
+
} catch {
|
|
484
|
+
return false
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Both polynom aren't the same (once developed and reduced => they cannot be equivalent)
|
|
488
|
+
if (!this.isEqual(P)) {
|
|
489
|
+
return false
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
// Check that everything is completely developed. Actually, there are no parentheses... so it is fully developed
|
|
494
|
+
return true
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
public isDividableBy = (div: Polynom): boolean => {
|
|
498
|
+
// Quick evaluation.
|
|
499
|
+
if (div.degree().isOne()) {
|
|
500
|
+
const zero = div.getZeroes()[0]
|
|
501
|
+
|
|
502
|
+
if (zero.exact instanceof Fraction) {
|
|
503
|
+
return (this.evaluate(zero.exact) as Fraction).isZero()
|
|
504
|
+
} else {
|
|
505
|
+
return false
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
} else {
|
|
509
|
+
const {reminder} = this.euclidean(div)
|
|
510
|
+
return reminder.isZero()
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
public isEqual = (P: Polynom): boolean => {
|
|
515
|
+
return this.#compare(P, '=')
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
public get isMultiVariable(): boolean {
|
|
519
|
+
// Determine if a monom has more than one variable.
|
|
520
|
+
return this.#monoms.some(m => m.variables.length > 1)
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
public isOne(): boolean {
|
|
524
|
+
return this.#monoms.length === 1 && this.#monoms[0].coefficient.isOne() && this.degree().isZero()
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
public isOppositeAt = (P: Polynom): boolean => {
|
|
528
|
+
return this.#compare(P.clone().opposite(), '=')
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
public isReduced = (polynomString: string): boolean => {
|
|
532
|
+
// The polynom must be developed to be reduced.
|
|
533
|
+
if (!this.isDeveloped(polynomString)) {
|
|
534
|
+
return false
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
const P = new Polynom(polynomString)
|
|
539
|
+
if (P.monoms.length > this.monoms.length) {
|
|
540
|
+
return false
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
// TODO: Not sure the reduced system checking is working properly !
|
|
545
|
+
for (const m of P.monoms) {
|
|
546
|
+
if (!m.coefficient.isReduced()) {
|
|
547
|
+
return false
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
return false
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
public isSameAs = (P: Polynom): boolean => {
|
|
556
|
+
return this.#compare(P, 'same')
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
public isZero(): boolean {
|
|
560
|
+
return (this.#monoms.length === 1 && this.#monoms[0].coefficient.isZero()) || this.#monoms.length === 0
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
public lcmDenominator = (): number => {
|
|
564
|
+
return Numeric.lcm(...this.getDenominators())
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
public lcmNumerator = (): number => {
|
|
568
|
+
return Numeric.lcm(...this.getNumerators())
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
public get length() {
|
|
572
|
+
return this.#monoms.length
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
public letters = (): string[] => {
|
|
576
|
+
let S = new Set<string>()
|
|
577
|
+
|
|
578
|
+
for (const m of this.#monoms) {
|
|
579
|
+
S = new Set([...S, ...m.variables])
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
return [...S]
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
public limitToInfinity = (letter?: string): Fraction => {
|
|
587
|
+
const M = this.monomByDegree(undefined, letter),
|
|
588
|
+
sign = M.coefficient.sign(),
|
|
589
|
+
degree = M.degree(letter)
|
|
590
|
+
|
|
591
|
+
if (degree.isStrictlyPositive()) {
|
|
592
|
+
return sign === 1 ? (new Fraction()).infinite() : (new Fraction()).infinite().opposite()
|
|
593
|
+
} else if (degree.isZero()) {
|
|
594
|
+
return M.coefficient
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
// Any other cases
|
|
599
|
+
return (new Fraction()).zero()
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
public limitToNegativeInfinity = (letter?: string): Fraction => {
|
|
603
|
+
const M = this.monomByDegree(undefined, letter),
|
|
604
|
+
sign = M.coefficient.sign(),
|
|
605
|
+
degree = M.degree(letter)
|
|
606
|
+
|
|
607
|
+
if (degree.isStrictlyPositive()) {
|
|
608
|
+
return sign === -1 ? (new Fraction()).infinite() : (new Fraction()).infinite().opposite()
|
|
609
|
+
} else if (degree.isZero()) {
|
|
610
|
+
return M.coefficient
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
// Any other cases
|
|
615
|
+
return (new Fraction()).zero()
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
public monomByDegree = (degree?: Fraction | number, letter?: string): Monom => {
|
|
619
|
+
if (degree === undefined)
|
|
620
|
+
// return the highest degree monom.
|
|
621
|
+
{
|
|
622
|
+
return this.monomByDegree(this.degree(letter), letter)
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
// Reduce the polynom.
|
|
627
|
+
const M = this.clone().reduce()
|
|
628
|
+
for (const m of M.#monoms) {
|
|
629
|
+
if (m.degree(letter).isEqual(degree)) {
|
|
630
|
+
return m.clone()
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
// Nothing was found - return the null monom.
|
|
636
|
+
return new Monom().zero()
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Used in LinearSystem.tex
|
|
640
|
+
public monomByLetter = (letter: string): Monom => {
|
|
641
|
+
const M = this.clone().reduce()
|
|
642
|
+
for (const m of M.#monoms) {
|
|
643
|
+
if (m.hasVariable(letter)) {
|
|
644
|
+
return m.clone()
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
return new Monom().zero()
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// ------------------------------------------
|
|
653
|
+
public get monoms() {
|
|
654
|
+
return this.#monoms
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
public set monoms(M: Monom[]) {
|
|
658
|
+
this.#monoms = M
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
public monomsByDegree = (degree?: number | Fraction, letter?: string): Monom[] => {
|
|
662
|
+
if (degree === undefined)
|
|
663
|
+
// return the highest degree monom.
|
|
664
|
+
{
|
|
665
|
+
return this.monomsByDegree(this.degree(letter))
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Reduce the polynom.
|
|
669
|
+
const Ms: Monom[] = []
|
|
670
|
+
|
|
671
|
+
const M = this.clone().reduce()
|
|
672
|
+
for (const m of M.#monoms) {
|
|
673
|
+
if (m.degree(letter).isEqual(degree)) {
|
|
674
|
+
Ms.push(m.clone())
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
return Ms
|
|
680
|
+
// Nothing was found - return
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
public multiply = (value: InputAlgebra<Polynom>): Polynom => {
|
|
684
|
+
|
|
685
|
+
if (value instanceof Polynom) {
|
|
686
|
+
return this.#multiplyByPolynom(value)
|
|
687
|
+
} else if (value instanceof Fraction) {
|
|
688
|
+
return this.#multiplyByFraction(value)
|
|
689
|
+
} else if (value instanceof Monom) {
|
|
690
|
+
return this.#multiplyByMonom(value)
|
|
691
|
+
} else if (Number.isSafeInteger(value) && typeof value === 'number') {
|
|
692
|
+
return this.#multiplyByInteger(value)
|
|
693
|
+
} else if (typeof value === 'string') {
|
|
694
|
+
try {
|
|
695
|
+
const k = new Fraction(value)
|
|
696
|
+
return this.#multiplyByFraction(k)
|
|
697
|
+
} catch {
|
|
698
|
+
throw new Error('Cannot multiply by this value.')
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
// Something went wrong...
|
|
706
|
+
throw new Error('Cannot multiply by this value.')
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
public get numberOfVars(): number {
|
|
710
|
+
return this.variables.length
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
public one = (): this => {
|
|
714
|
+
this.#monoms = []
|
|
715
|
+
this.#monoms.push(new Monom().one())
|
|
716
|
+
return this
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// ------------------------------------------
|
|
720
|
+
public opposite = (): this => {
|
|
721
|
+
this.#monoms = this.#monoms.map(m => m.opposite())
|
|
722
|
+
return this
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
public get plotFunction(): string {
|
|
726
|
+
return this.#genDisplay('tex', false, false, true)
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
public pow = (nb: number): Polynom => {
|
|
730
|
+
return operation_pow(this as Polynom, nb).reduce()
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
public primitive = (letter?: string): Polynom => {
|
|
734
|
+
const dP = new Polynom()
|
|
735
|
+
|
|
736
|
+
for (const m of this.#monoms) {
|
|
737
|
+
dP.add(m.primitive(letter))
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
return dP
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
public reduce = (): Polynom => {
|
|
744
|
+
// Reduce the polynom
|
|
745
|
+
|
|
746
|
+
// Group the monoms by similarity
|
|
747
|
+
let i = 0
|
|
748
|
+
while (i < this.#monoms.length) {
|
|
749
|
+
for (let j = i + 1; j < this.#monoms.length; j++) {
|
|
750
|
+
if (this.#monoms[i].isSameAs(this.#monoms[j])) {
|
|
751
|
+
this.#monoms[i].add(this.#monoms[j])
|
|
752
|
+
this.#monoms.splice(j, 1)
|
|
753
|
+
if (this.#monoms[i].isZero()) {
|
|
754
|
+
this.#monoms[i] = new Monom().zero()
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
j--
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
i++
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Remove all null monoms
|
|
766
|
+
this.#monoms = this.#monoms.filter((m) => {
|
|
767
|
+
return !m.coefficient.isZero()
|
|
768
|
+
})
|
|
769
|
+
|
|
770
|
+
// Reduce all monoms coefficient.
|
|
771
|
+
for (const m of this.#monoms) {
|
|
772
|
+
m.coefficient.reduce()
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
if (this.length === 0) {
|
|
777
|
+
return new Polynom().zero()
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
return this.reorder()
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
public reorder = (letter = 'x', revert = false): this => {
|
|
785
|
+
const otherLetters = this.variables.filter(x => x !== letter)
|
|
786
|
+
this.#monoms.sort(function (a, b) {
|
|
787
|
+
const da = a.degree(letter).value,
|
|
788
|
+
db = b.degree(letter).value
|
|
789
|
+
|
|
790
|
+
// Values are different
|
|
791
|
+
if (da !== db) {
|
|
792
|
+
return revert ? da - db : db - da
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// if values are equals, check other letters - it must be reverted in that case !
|
|
796
|
+
if (otherLetters.length > 0) {
|
|
797
|
+
for (const L of otherLetters) {
|
|
798
|
+
const da = a.degree(L).value,
|
|
799
|
+
db = b.degree(L).value
|
|
800
|
+
|
|
801
|
+
// Values are different
|
|
802
|
+
if (da !== db) {
|
|
803
|
+
return revert ? da - db : db - da
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
return 0
|
|
810
|
+
})
|
|
811
|
+
|
|
812
|
+
return this
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Replace a variable (letter) by a polynom.
|
|
817
|
+
* @param letter
|
|
818
|
+
* @param P
|
|
819
|
+
*/
|
|
820
|
+
public replaceBy = (letter: string, P: Polynom): this => {
|
|
821
|
+
let pow: Fraction
|
|
822
|
+
const resultPolynom: Polynom = new Polynom().zero()
|
|
823
|
+
|
|
824
|
+
for (const m of this.monoms) {
|
|
825
|
+
if (!m.hasVariable(letter) || m.literal[letter].isZero()) {
|
|
826
|
+
resultPolynom.add(m.clone())
|
|
827
|
+
} else {
|
|
828
|
+
// We have found a variable to replace.
|
|
829
|
+
|
|
830
|
+
// Get the power.
|
|
831
|
+
pow = m.literal[letter].clone()
|
|
832
|
+
|
|
833
|
+
// Remove the variable from the monom
|
|
834
|
+
m.removeVariable(letter)
|
|
835
|
+
|
|
836
|
+
// Add the new monom to the result polynom
|
|
837
|
+
resultPolynom.add(P.clone().pow(Math.abs(pow.numerator)).multiply(m))
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
// Reduce the monoms
|
|
843
|
+
this.#monoms = resultPolynom.reduce().monoms
|
|
844
|
+
return this
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// ------------------------------------------
|
|
848
|
+
|
|
849
|
+
public root(): Polynom {
|
|
850
|
+
throw new Error('Cannot take the root from a polynom')
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
get roots(): ISolution[] {
|
|
854
|
+
return this.#rootsCache ? this.#roots : this.getZeroes()
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
set roots(value: ISolution[]) {
|
|
858
|
+
this.#rootsCache = true
|
|
859
|
+
this.#roots = value
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
public setVariable(value: string): this {
|
|
863
|
+
this.#defaultVariable = value
|
|
864
|
+
|
|
865
|
+
return this
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
public sqrt(): Polynom {
|
|
869
|
+
throw new Error('Cannot take the square root from a polynom')
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
public subtract = (...values: InputAlgebra<Polynom>[]): Polynom => {
|
|
873
|
+
for (const value of values) {
|
|
874
|
+
if (value instanceof Polynom) {
|
|
875
|
+
this.add(value.clone().opposite())
|
|
876
|
+
} else if (value instanceof Monom) {
|
|
877
|
+
this.#monoms.push(value.clone().opposite())
|
|
878
|
+
} else {
|
|
879
|
+
this.#monoms.push(new Monom(value).opposite())
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
return this.reduce()
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
public tableOfSigns(): TABLE_OF_SIGNS {
|
|
888
|
+
// returns ['+-', 'd|t|z', '+-']...
|
|
889
|
+
|
|
890
|
+
// global roots from eventually Polyfactor. Allows to add "extra column".
|
|
891
|
+
const roots: ISolution[] = this.roots
|
|
892
|
+
|
|
893
|
+
// Build the table os sign length and default values
|
|
894
|
+
// The signs looks like: ['+', 't', '+', 't', '+', 't', '+']
|
|
895
|
+
let signs: TABLE_OF_SIGNS_VALUES[] = new Array(2 * roots.length + 1)
|
|
896
|
+
.fill('')
|
|
897
|
+
.map((_x, index) => {
|
|
898
|
+
return index % 2 === 0 ? '' : 'z'
|
|
899
|
+
})
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
if (signs.length === 1) {
|
|
903
|
+
// The polynom is a constant or has not roots
|
|
904
|
+
const [a] = this.getCoefficients().map(x => x.value)
|
|
905
|
+
signs = replace_in_array(signs, '', a > 0 ? '+' : '-')
|
|
906
|
+
} else if (this.degree().isOne()) {
|
|
907
|
+
// First degree: ax+b
|
|
908
|
+
const [a] = this.getCoefficients().map(x => x.value)
|
|
909
|
+
|
|
910
|
+
// Get the index of the zero.
|
|
911
|
+
signs[0] = a > 0 ? '-' : '+'
|
|
912
|
+
signs[1] = 'z'
|
|
913
|
+
signs[2] = a > 0 ? '+' : '-'
|
|
914
|
+
} else {
|
|
915
|
+
const testingRoots = [
|
|
916
|
+
roots[0].value - 1,
|
|
917
|
+
...roots.map((_root, index) => {
|
|
918
|
+
return index === roots.length - 1 ?
|
|
919
|
+
roots[index].value + 1 :
|
|
920
|
+
(roots[index].value + roots[index + 1].value) / 2
|
|
921
|
+
})
|
|
922
|
+
]
|
|
923
|
+
|
|
924
|
+
testingRoots.forEach((test, index) => {
|
|
925
|
+
const sign = this.evaluate({x: test}, true) as number
|
|
926
|
+
signs[index * 2] = sign > 0 ? '+' : '-'
|
|
927
|
+
})
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
return {roots, signs}
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
public get value(): number | undefined {
|
|
934
|
+
if (this.degree().isZero()) {
|
|
935
|
+
return this.monoms[0]?.coefficient.value ?? 0
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
return undefined
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
public get variables(): string[] {
|
|
942
|
+
let V: string[] = []
|
|
943
|
+
|
|
944
|
+
for (const m of this.#monoms) {
|
|
945
|
+
V = V.concat(m.variables)
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
// Remove duplicates.
|
|
950
|
+
V = [...new Set(V)]
|
|
951
|
+
V.sort()
|
|
952
|
+
return V
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* Set the polynom to zero.
|
|
957
|
+
* @returns {this}
|
|
958
|
+
*/
|
|
959
|
+
public zero = (): this => {
|
|
960
|
+
this.#monoms = []
|
|
961
|
+
this.#monoms.push(new Monom().zero())
|
|
962
|
+
return this
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
public get zeroes(): ISolution[] {
|
|
966
|
+
return this.getZeroes()
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
#compare = (P: Polynom, sign?: string): boolean => {
|
|
970
|
+
sign ??= '='
|
|
971
|
+
|
|
972
|
+
// Create clone version to reduce them without altering the original polynoms.
|
|
973
|
+
const cP1 = this.clone().reduce().reorder()
|
|
974
|
+
const cP2 = P.clone().reduce().reorder()
|
|
975
|
+
|
|
976
|
+
switch (sign) {
|
|
977
|
+
case '=':
|
|
978
|
+
// They must have the isSame length and the isSame degree
|
|
979
|
+
if (cP1.length !== cP2.length || !cP1.degree().isEqual(cP2.degree())) {
|
|
980
|
+
return false
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// Check if the coefficients are the isSame.
|
|
984
|
+
return cP1.monoms
|
|
985
|
+
.every((m1, index) => m1.isEqual(cP2.monoms[index]))
|
|
986
|
+
|
|
987
|
+
case 'same':
|
|
988
|
+
// They must have the same length and the same degree
|
|
989
|
+
if (cP1.length !== cP2.length || !cP1.degree().isEqual(cP2.degree())) {
|
|
990
|
+
return false
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
return cP1.monoms
|
|
994
|
+
.every((m1, index) => m1.isSameAs(cP2.monoms[index]))
|
|
995
|
+
|
|
996
|
+
default:
|
|
997
|
+
return false
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
|
|
1002
|
+
#divideByFraction = (F: Fraction): this => {
|
|
1003
|
+
for (const m of this.#monoms) {
|
|
1004
|
+
m.coefficient.divide(F)
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
return this
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
#divideByInteger = (nb: number): this => {
|
|
1011
|
+
const nbF = new Fraction(nb)
|
|
1012
|
+
for (const m of this.#monoms) {
|
|
1013
|
+
m.coefficient.divide(nbF)
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
return this
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
#evaluateAsNumeric = (values: literalType<number | Fraction> | InputValue<Fraction>): number => {
|
|
1020
|
+
let r = 0
|
|
1021
|
+
this.#monoms.forEach(monom => {
|
|
1022
|
+
r += monom.evaluate(values, true) as number
|
|
1023
|
+
})
|
|
1024
|
+
|
|
1025
|
+
return r
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
#factorize2ndDegree = (letter: string): Polynom[] => {
|
|
1029
|
+
let P1: Polynom, P2: Polynom,
|
|
1030
|
+
a, b, c, delta, x1, x2, factor
|
|
1031
|
+
|
|
1032
|
+
// One variable only
|
|
1033
|
+
if (this.numberOfVars === 1) {
|
|
1034
|
+
a = this.monomByDegree(2, letter).coefficient
|
|
1035
|
+
b = this.monomByDegree(1, letter).coefficient
|
|
1036
|
+
c = this.monomByDegree(0, letter).coefficient
|
|
1037
|
+
delta = b.clone().pow(2).subtract(a.clone().multiply(c).multiply(4))
|
|
1038
|
+
|
|
1039
|
+
if (delta.isZero()) {
|
|
1040
|
+
x1 = b.clone().opposite().divide(a.clone().multiply(2))
|
|
1041
|
+
P1 = new Polynom(letter).subtract(x1.display).multiply(x1.denominator)
|
|
1042
|
+
P2 = new Polynom(letter).subtract(x1.display).multiply(x1.denominator)
|
|
1043
|
+
factor = a.divide(x1.denominator).divide(x1.denominator)
|
|
1044
|
+
|
|
1045
|
+
if (!factor.isOne()) {
|
|
1046
|
+
return [new Polynom(factor.display), P1, P2]
|
|
1047
|
+
} else {
|
|
1048
|
+
return [P1, P2]
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
} else if (delta.isPositive() && delta.isSquare()) {
|
|
1052
|
+
x1 = b.clone().opposite()
|
|
1053
|
+
.add(delta.clone().sqrt())
|
|
1054
|
+
.divide(a.clone().multiply(2))
|
|
1055
|
+
x2 = b.clone().opposite()
|
|
1056
|
+
.subtract(delta.clone().sqrt())
|
|
1057
|
+
.divide(a.clone().multiply(2))
|
|
1058
|
+
|
|
1059
|
+
// (2x+5)(3x-2)
|
|
1060
|
+
// 6x^2+11x-10
|
|
1061
|
+
// a = 6, b = 11, c = -10
|
|
1062
|
+
// delta = 121-4*6*(-10) = 361= 19^2
|
|
1063
|
+
// x1 = (-11 + 19) / 12 = 8/12 = 2/3
|
|
1064
|
+
// x2 = (-11 - 19) / 12 = -30/12 = -5/2
|
|
1065
|
+
factor = a.divide(x1.denominator).divide(x2.denominator)
|
|
1066
|
+
if (factor.isOne()) {
|
|
1067
|
+
return [
|
|
1068
|
+
new Polynom(letter).subtract(x1.display).multiply(x1.denominator),
|
|
1069
|
+
new Polynom(letter).subtract(x2.display).multiply(x2.denominator),
|
|
1070
|
+
]
|
|
1071
|
+
} else {
|
|
1072
|
+
return [
|
|
1073
|
+
new Polynom(factor.display),
|
|
1074
|
+
new Polynom(letter).subtract(x1.display).multiply(x1.denominator),
|
|
1075
|
+
new Polynom(letter).subtract(x2.display).multiply(x2.denominator),
|
|
1076
|
+
]
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
|
|
1080
|
+
} else
|
|
1081
|
+
// No solution possible - return the complete value.
|
|
1082
|
+
{
|
|
1083
|
+
return [this.clone()]
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
} else {
|
|
1087
|
+
// If multiple variables, only handle perfect squares...
|
|
1088
|
+
a = this.monomByDegree(2, letter)
|
|
1089
|
+
b = this.monomByDegree(1, letter)
|
|
1090
|
+
c = this.monomByDegree(0, letter)
|
|
1091
|
+
|
|
1092
|
+
if (a.isLiteralSquare() && c.isLiteralSquare())
|
|
1093
|
+
// Check the middle item is same as...
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
{
|
|
1097
|
+
if (b.clone().pow(2).isSameAs(a.clone().multiply(c))) {
|
|
1098
|
+
// Determine if the coefficient values matches.
|
|
1099
|
+
|
|
1100
|
+
// Search 4 values (r, s, t, u) that matches:
|
|
1101
|
+
// (r X + s Y)(t X + u Y) = rt X^2 + (ru + st) XY + su Y^2
|
|
1102
|
+
|
|
1103
|
+
const xPolynom = new Polynom('x', a.coefficient, b.coefficient, c.coefficient)
|
|
1104
|
+
const xFactors = xPolynom.#factorize2ndDegree('x')
|
|
1105
|
+
|
|
1106
|
+
const factors = []
|
|
1107
|
+
let xyzPolynom: Polynom
|
|
1108
|
+
|
|
1109
|
+
if (xFactors.length >= 2) {
|
|
1110
|
+
for (const p of xFactors) {
|
|
1111
|
+
if (p.degree().isZero()) {
|
|
1112
|
+
factors.push(p.clone())
|
|
1113
|
+
} else {
|
|
1114
|
+
xyzPolynom = p.clone()
|
|
1115
|
+
xyzPolynom.monoms[0].literal = a.literalSqrt
|
|
1116
|
+
xyzPolynom.monoms[1].literal = c.literalSqrt
|
|
1117
|
+
factors.push(xyzPolynom.clone())
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
return factors
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
|
|
1128
|
+
return [this.clone()]
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
#genDisplay = (output?: string, forceSign?: boolean, wrapParentheses?: boolean, withAllMultiplicationSign?: boolean): string => {
|
|
1133
|
+
let P = ''
|
|
1134
|
+
|
|
1135
|
+
for (const k of this.#monoms) {
|
|
1136
|
+
if (k.coefficient.value === 0) {
|
|
1137
|
+
continue
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
|
|
1141
|
+
// The monom to be displayed
|
|
1142
|
+
let m
|
|
1143
|
+
if (withAllMultiplicationSign) {
|
|
1144
|
+
m = k.plotFunction
|
|
1145
|
+
} else {
|
|
1146
|
+
m = (output === 'tex') ? k.tex : k.display
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
P += `${(k.coefficient.sign() === 1 && (P !== '' || forceSign === true)) ? '+' : ''}${m}`
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
if (wrapParentheses === true && this.length > 1) {
|
|
1153
|
+
if (output === 'tex') {
|
|
1154
|
+
P = `\\left( ${P} \\right)`
|
|
1155
|
+
} else {
|
|
1156
|
+
P = `(${P})`
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
|
|
1161
|
+
if (P === '') {
|
|
1162
|
+
P = '0'
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
return P
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
#getAllPotentialFactors = (P: Polynom, maxDegree: number, letter: string): Polynom[] => {
|
|
1169
|
+
const m1 = P.monoms[0].dividers,
|
|
1170
|
+
m2 = P.monoms[P.monoms.length - 1].dividers
|
|
1171
|
+
|
|
1172
|
+
const allDividers: Polynom[] = []
|
|
1173
|
+
m1.forEach(m1d => {
|
|
1174
|
+
// Get only polynom that has a degree less than a specific value
|
|
1175
|
+
if (m1d.degree(letter).isLeq(maxDegree)) {
|
|
1176
|
+
m2.forEach(m2d => {
|
|
1177
|
+
if (m1d.degree(letter).isNotEqual(m2d.degree(letter))) {
|
|
1178
|
+
allDividers.push(new Polynom(m1d, m2d))
|
|
1179
|
+
allDividers.push(new Polynom(m1d, m2d.clone().opposite()))
|
|
1180
|
+
}
|
|
1181
|
+
})
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
|
|
1185
|
+
})
|
|
1186
|
+
|
|
1187
|
+
return allDividers
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
#multiplyByFraction = (F: Fraction): Polynom => {
|
|
1191
|
+
for (const m of this.#monoms) {
|
|
1192
|
+
m.coefficient.multiply(F)
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
|
|
1196
|
+
return this.reduce()
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
#multiplyByInteger = (nb: number): Polynom => {
|
|
1200
|
+
return this.#multiplyByFraction(new Fraction(nb))
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
#multiplyByMonom = (M: Monom): Polynom => {
|
|
1204
|
+
for (const m of this.#monoms) {
|
|
1205
|
+
m.multiply(M)
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
return this.reduce()
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
#multiplyByPolynom = (P: Polynom): Polynom => {
|
|
1212
|
+
const M: Monom[] = []
|
|
1213
|
+
for (const m1 of this.#monoms) {
|
|
1214
|
+
for (const m2 of P.monoms) {
|
|
1215
|
+
M.push(Monom.xMultiply(m1, m2))
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
this.#monoms = M
|
|
1221
|
+
return this.reduce()
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
#parseString(inputStr: string, ...values: unknown[]): this {
|
|
1225
|
+
if (values.length === 0) {
|
|
1226
|
+
// Parse the polynom using the shutting yard algorithm
|
|
1227
|
+
if (inputStr !== '' && !isNaN(Number(inputStr))) {
|
|
1228
|
+
this.empty()
|
|
1229
|
+
// It's a simple number.
|
|
1230
|
+
const m = new Monom(Number(inputStr))
|
|
1231
|
+
// m.coefficient = new Fraction(inputStr);
|
|
1232
|
+
// m.literalStr = '';
|
|
1233
|
+
this.add(m)
|
|
1234
|
+
return this
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
// Parse the string.
|
|
1238
|
+
return this.#shutingYardToReducedPolynom(inputStr)
|
|
1239
|
+
} else if (/^[a-z]+/.test(inputStr)) {
|
|
1240
|
+
// We assume the inputStr contains only letters.
|
|
1241
|
+
this.empty()
|
|
1242
|
+
|
|
1243
|
+
const fractions = values.map(x => new Fraction(x as InputValue<Fraction>))
|
|
1244
|
+
|
|
1245
|
+
// Multiple setLetter version
|
|
1246
|
+
if (inputStr.length > 1) {
|
|
1247
|
+
const letters = inputStr.split('')
|
|
1248
|
+
|
|
1249
|
+
if (letters.length < values.length - 2) {
|
|
1250
|
+
throw new Error('Too many factors for too few variables !')
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
let i = 0
|
|
1254
|
+
|
|
1255
|
+
for (const F of fractions) {
|
|
1256
|
+
const m = new Monom()
|
|
1257
|
+
m.coefficient = F.clone()
|
|
1258
|
+
m.literalStr = letters[i] || ''
|
|
1259
|
+
this.add(m)
|
|
1260
|
+
i++
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
// Single setLetter version
|
|
1264
|
+
else {
|
|
1265
|
+
let n = fractions.length - 1
|
|
1266
|
+
for (const F of fractions) {
|
|
1267
|
+
const m = new Monom()
|
|
1268
|
+
m.coefficient = F.clone()
|
|
1269
|
+
m.literalStr = `${inputStr}^${n}`
|
|
1270
|
+
this.add(m)
|
|
1271
|
+
n--
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
return this
|
|
1275
|
+
} else {
|
|
1276
|
+
return this.zero()
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/**
|
|
1282
|
+
* Main parse using a shutting yard class
|
|
1283
|
+
* @param inputStr
|
|
1284
|
+
*/
|
|
1285
|
+
#shutingYardToReducedPolynom = (inputStr: string): this => {
|
|
1286
|
+
// Get the RPN array of the current expression
|
|
1287
|
+
const SY: ShutingYard = new ShutingYard().parse(inputStr)
|
|
1288
|
+
const rpn: { token: string, tokenType: ShutingyardType }[] = SY.rpn
|
|
1289
|
+
|
|
1290
|
+
// New version for reducing shuting yard.
|
|
1291
|
+
this.zero()
|
|
1292
|
+
|
|
1293
|
+
const stack: Polynom[] = []
|
|
1294
|
+
|
|
1295
|
+
// Loop through the each element of the RPN
|
|
1296
|
+
for (const element of rpn) {
|
|
1297
|
+
this.#shutingYard_addToken(stack, element)
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
|
|
1301
|
+
if (stack.length === 1) {
|
|
1302
|
+
this.add(stack[0])
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
|
|
1306
|
+
return this.reorder()
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
#shutingYard_addToken = (stack: Polynom[], element: Token): void => {
|
|
1310
|
+
switch (element.tokenType) {
|
|
1311
|
+
case ShutingyardType.COEFFICIENT:
|
|
1312
|
+
stack.push(new Polynom(element.token))
|
|
1313
|
+
break
|
|
1314
|
+
|
|
1315
|
+
case ShutingyardType.VARIABLE:
|
|
1316
|
+
stack.push(new Polynom().add(new Monom(element.token)))
|
|
1317
|
+
break
|
|
1318
|
+
|
|
1319
|
+
case ShutingyardType.CONSTANT:
|
|
1320
|
+
// TODO: add constant support to Polynom parsing.
|
|
1321
|
+
console.log('Actually, not supported - will be added later !')
|
|
1322
|
+
break
|
|
1323
|
+
|
|
1324
|
+
case ShutingyardType.OPERATION:
|
|
1325
|
+
if (stack.length >= 2) {
|
|
1326
|
+
const b = stack.pop(),
|
|
1327
|
+
a = stack.pop()
|
|
1328
|
+
|
|
1329
|
+
// Check if the polynoms are not undefined.
|
|
1330
|
+
if (a === undefined || b === undefined) {
|
|
1331
|
+
break
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
if (element.token === '+') {
|
|
1335
|
+
stack.push(a.add(b))
|
|
1336
|
+
} else if (element.token === '-') {
|
|
1337
|
+
stack.push(a.subtract(b))
|
|
1338
|
+
} else if (element.token === '*') {
|
|
1339
|
+
stack.push(a.multiply(b))
|
|
1340
|
+
} else if (element.token === '/') {
|
|
1341
|
+
if (b.degree().isStrictlyPositive()) {
|
|
1342
|
+
console.log('divide by a polynom -> should create a rational polynom !')
|
|
1343
|
+
} else {
|
|
1344
|
+
// a.divide(b.monoms[0].coefficient)
|
|
1345
|
+
stack.push(a.divide(b.monoms[0].coefficient))
|
|
1346
|
+
}
|
|
1347
|
+
} else if (element.token === '^') {
|
|
1348
|
+
if (b.degree().isStrictlyPositive()) {
|
|
1349
|
+
throw new Error('Cannot elevate a polynom with another polynom !')
|
|
1350
|
+
} else if (b.monoms[0].coefficient.isRelative())
|
|
1351
|
+
// Integer power
|
|
1352
|
+
{
|
|
1353
|
+
stack.push(a.pow(b.monoms[0].coefficient.value))
|
|
1354
|
+
} else {
|
|
1355
|
+
// Only allow power if the previous polynom is only a monom, without coefficient.
|
|
1356
|
+
if (a.monoms.length === 1 && a.monoms[0].coefficient.isOne()) {
|
|
1357
|
+
for (const letter in a.monoms[0].literal) {
|
|
1358
|
+
a.monoms[0].literal[letter].multiply(b.monoms[0].coefficient)
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
stack.push(a)
|
|
1362
|
+
} else {
|
|
1363
|
+
console.error('Cannot have power with fraction')
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
} else if (element.token === '-') {
|
|
1369
|
+
const a = stack.pop()
|
|
1370
|
+
if (a) {
|
|
1371
|
+
stack.push(a.opposite())
|
|
1372
|
+
}
|
|
1373
|
+
} else {
|
|
1374
|
+
throw new Error("Error parsing the polynom")
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
|
|
1378
|
+
break
|
|
1379
|
+
|
|
1380
|
+
case ShutingyardType.MONOM:
|
|
1381
|
+
// Should never appear.
|
|
1382
|
+
console.error('The monom token should not appear here')
|
|
1383
|
+
break
|
|
1384
|
+
|
|
1385
|
+
case ShutingyardType.FUNCTION:
|
|
1386
|
+
// Should never appear.
|
|
1387
|
+
console.error('The function token should not appear here - might be introduced later.')
|
|
1388
|
+
break
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
}
|