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