pimath 0.2.13 → 0.2.15
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 +856 -1882
- package/dist/pimath.js.map +1 -1
- package/package.json +2 -2
- package/src/algebra/equation.ts +562 -558
- package/src/algebra/equationSolver.ts +489 -489
- package/src/algebra/factor.ts +396 -392
- package/src/algebra/linearSystem.ts +446 -442
- package/src/algebra/matrix.ts +515 -511
- package/src/algebra/monom.ts +932 -928
- package/src/algebra/polyFactor.ts +724 -720
- package/src/algebra/polynom.ts +1233 -1229
- package/src/coefficients/fraction.ts +907 -748
- package/src/coefficients/root.ts +500 -359
- package/src/geometry/TupleN.ts +129 -129
- package/src/geometry/circle.ts +455 -455
- package/src/geometry/line.ts +658 -654
- package/src/geometry/point.ts +105 -105
- package/src/geometry/sphere3.ts +2 -2
- package/src/geometry/triangle.ts +488 -488
- package/src/geometry/vector.ts +230 -226
- package/src/numeric.ts +136 -51
- package/src/pimath.interface.ts +2 -0
- package/types/algebra/equation.d.ts +1 -0
- package/types/algebra/equation.d.ts.map +1 -1
- package/types/algebra/equationSolver.d.ts.map +1 -1
- package/types/algebra/factor.d.ts +1 -0
- package/types/algebra/factor.d.ts.map +1 -1
- package/types/algebra/linearSystem.d.ts +1 -0
- package/types/algebra/linearSystem.d.ts.map +1 -1
- package/types/algebra/matrix.d.ts +6 -5
- package/types/algebra/matrix.d.ts.map +1 -1
- package/types/algebra/monom.d.ts +1 -0
- package/types/algebra/monom.d.ts.map +1 -1
- package/types/algebra/polyFactor.d.ts +1 -0
- package/types/algebra/polyFactor.d.ts.map +1 -1
- package/types/algebra/polynom.d.ts +1 -0
- package/types/algebra/polynom.d.ts.map +1 -1
- package/types/coefficients/fraction.d.ts +62 -53
- package/types/coefficients/fraction.d.ts.map +1 -1
- package/types/coefficients/root.d.ts +3 -0
- package/types/coefficients/root.d.ts.map +1 -1
- package/types/geometry/TupleN.d.ts +1 -1
- package/types/geometry/TupleN.d.ts.map +1 -1
- package/types/geometry/circle.d.ts.map +1 -1
- package/types/geometry/line.d.ts +1 -0
- package/types/geometry/line.d.ts.map +1 -1
- package/types/geometry/point.d.ts.map +1 -1
- package/types/geometry/sphere3.d.ts +2 -2
- package/types/geometry/sphere3.d.ts.map +1 -1
- package/types/geometry/triangle.d.ts.map +1 -1
- package/types/geometry/vector.d.ts +1 -0
- package/types/geometry/vector.d.ts.map +1 -1
- package/types/index.d.ts +3 -3
- package/types/numeric.d.ts +4 -4
- package/types/numeric.d.ts.map +1 -1
- package/types/pimath.interface.d.ts +1 -0
- package/types/pimath.interface.d.ts.map +1 -1
- package/types/primes.data.d.ts +2 -0
- package/types/primes.data.d.ts.map +1 -0
|
@@ -1,748 +1,907 @@
|
|
|
1
|
-
import type {compareSign, IExpression, InputValue, IPiMathObject} from "../pimath.interface"
|
|
2
|
-
import {Numeric} from "../numeric"
|
|
3
|
-
import {InvalidArgumentError, MathError} from "../errors"
|
|
4
|
-
|
|
5
|
-
export enum FRAC_TYPE {
|
|
6
|
-
frac = 'frac',
|
|
7
|
-
dfrac = 'dfrac',
|
|
8
|
-
tfrac = 'tfrac'
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* The fraction class make possible to handle
|
|
13
|
-
* \\(\frac{a}{b}\\) or \\[\frac{a}{b}\\] values.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
export class Fraction implements IPiMathObject<Fraction>, IExpression<Fraction> {
|
|
17
|
-
#denominator = 1
|
|
18
|
-
#digits = 3
|
|
19
|
-
#exact = true
|
|
20
|
-
#numerator = 1
|
|
21
|
-
#type: FRAC_TYPE = FRAC_TYPE.frac
|
|
22
|
-
#withSign = false
|
|
23
|
-
|
|
24
|
-
constructor()
|
|
25
|
-
constructor(value: InputValue<Fraction>)
|
|
26
|
-
constructor(numerator: number, denominator: number)
|
|
27
|
-
constructor(value?: InputValue<Fraction>, denominator?: number) {
|
|
28
|
-
|
|
29
|
-
if (value !== undefined) {
|
|
30
|
-
this.parse(value, denominator)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return this
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ------------------------------------------
|
|
37
|
-
/**
|
|
38
|
-
* Parse the value to get the numerator and denominator
|
|
39
|
-
* @param value : number or string to parse to get the fraction
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// A null value means a zero fraction.
|
|
44
|
-
if (value === "") {
|
|
45
|
-
this.#numerator = 0
|
|
46
|
-
this.#denominator = 1
|
|
47
|
-
return this
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if(typeof value === "number" && isNaN(value)) {
|
|
51
|
-
return this.invalid()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if(typeof value === "number" && !isFinite(value)) {
|
|
55
|
-
this.infinite()
|
|
56
|
-
if(value < 0) {
|
|
57
|
-
this.opposite()
|
|
58
|
-
}
|
|
59
|
-
return this
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (typeof value === "string") {
|
|
63
|
-
return this.fromString(value)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (typeof value === "number" && denominator === undefined) {
|
|
67
|
-
return this.fromNumber(value)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (typeof value === "number" && typeof denominator === "number") {
|
|
71
|
-
return this.fromNumbers(value, denominator)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (value instanceof Fraction) {
|
|
75
|
-
return this.copy(value)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return this
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const F = new Fraction()
|
|
83
|
-
F.numerator = this.#numerator
|
|
84
|
-
F.denominator = this.#denominator
|
|
85
|
-
F.exact = this.exact
|
|
86
|
-
F.#type = this.#type
|
|
87
|
-
F.#digits = this.#digits
|
|
88
|
-
F.#withSign = this.#withSign
|
|
89
|
-
return F
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
return
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
this
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
case "
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
this.#
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
this
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
this
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
return this.
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
this
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
return
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
if (
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
//
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
this.
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
this.#
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
1
|
+
import type {compareSign, IExpression, InputValue, IPiMathObject} from "../pimath.interface"
|
|
2
|
+
import {Numeric} from "../numeric"
|
|
3
|
+
import {InvalidArgumentError, MathError} from "../errors"
|
|
4
|
+
|
|
5
|
+
export enum FRAC_TYPE {
|
|
6
|
+
frac = 'frac',
|
|
7
|
+
dfrac = 'dfrac',
|
|
8
|
+
tfrac = 'tfrac'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The fraction class make possible to handle
|
|
13
|
+
* \\(\frac{a}{b}\\) or \\[\frac{a}{b}\\] values.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export class Fraction implements IPiMathObject<Fraction>, IExpression<Fraction> {
|
|
17
|
+
#denominator = 1
|
|
18
|
+
#digits = 3
|
|
19
|
+
#exact = true
|
|
20
|
+
#numerator = 1
|
|
21
|
+
#type: FRAC_TYPE = FRAC_TYPE.frac
|
|
22
|
+
#withSign = false
|
|
23
|
+
|
|
24
|
+
constructor()
|
|
25
|
+
constructor(value: InputValue<Fraction>)
|
|
26
|
+
constructor(numerator: number, denominator: number)
|
|
27
|
+
constructor(value?: InputValue<Fraction>, denominator?: number) {
|
|
28
|
+
|
|
29
|
+
if (value !== undefined) {
|
|
30
|
+
this.parse(value, denominator)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return this
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ------------------------------------------
|
|
37
|
+
/**
|
|
38
|
+
* Parse the value to get the numerator and denominator
|
|
39
|
+
* @param value : number or string to parse to get the fraction
|
|
40
|
+
*/
|
|
41
|
+
parse(value: InputValue<Fraction>, denominator?: number): this {
|
|
42
|
+
|
|
43
|
+
// A null value means a zero fraction.
|
|
44
|
+
if (value === "") {
|
|
45
|
+
this.#numerator = 0
|
|
46
|
+
this.#denominator = 1
|
|
47
|
+
return this
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (typeof value === "number" && isNaN(value)) {
|
|
51
|
+
return this.invalid()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (typeof value === "number" && !isFinite(value)) {
|
|
55
|
+
this.infinite()
|
|
56
|
+
if (value < 0) {
|
|
57
|
+
this.opposite()
|
|
58
|
+
}
|
|
59
|
+
return this
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof value === "string") {
|
|
63
|
+
return this.fromString(value)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (typeof value === "number" && denominator === undefined) {
|
|
67
|
+
return this.fromNumber(value)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (typeof value === "number" && typeof denominator === "number") {
|
|
71
|
+
return this.fromNumbers(value, denominator)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (value instanceof Fraction) {
|
|
75
|
+
return this.copy(value)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return this
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
clone(): Fraction {
|
|
82
|
+
const F = new Fraction()
|
|
83
|
+
F.numerator = this.#numerator
|
|
84
|
+
F.denominator = this.#denominator
|
|
85
|
+
F.exact = this.exact
|
|
86
|
+
F.#type = this.#type
|
|
87
|
+
F.#digits = this.#digits
|
|
88
|
+
F.#withSign = this.#withSign
|
|
89
|
+
return F
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Copy the mathematical value of another fraction into this one.
|
|
94
|
+
*
|
|
95
|
+
* NOTE: unlike {@link clone}, `copy` intentionally transfers only the
|
|
96
|
+
* mathematical state (`#numerator`, `#denominator`, `#exact`) and NOT the
|
|
97
|
+
* display state (`#type`, `#digits`, `#withSign`). This divergence is by
|
|
98
|
+
* design: `parse()` routes a `Fraction` input to `copy()`, so the receiving
|
|
99
|
+
* fraction keeps its own formatting configuration.
|
|
100
|
+
*/
|
|
101
|
+
copy(value: Fraction): this {
|
|
102
|
+
this.#numerator = value.numerator
|
|
103
|
+
this.#denominator = value.denominator
|
|
104
|
+
this.#exact = value.exact
|
|
105
|
+
|
|
106
|
+
return this
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
get tex(): string {
|
|
110
|
+
if (this.isInfinity()) {
|
|
111
|
+
return `${this.sign() === 1 ? '+' : '-'}\\infty`
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const plus = this.#withSign && this.isPositive() ? '+' : ''
|
|
115
|
+
|
|
116
|
+
if (this.exact) {
|
|
117
|
+
if (this.#denominator === 1) {
|
|
118
|
+
return `${plus}${this.#numerator}`
|
|
119
|
+
} else if (this.#numerator < 0) {
|
|
120
|
+
return `-\\${this.#type}{ ${-this.#numerator} }{ ${this.#denominator} }`
|
|
121
|
+
} else {
|
|
122
|
+
return `${plus}\\${this.#type}{ ${this.#numerator} }{ ${this.#denominator} }`
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
return plus + this.value.toFixed(this.#digits)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
get display(): string {
|
|
130
|
+
if (this.isInfinity()) {
|
|
131
|
+
return `${this.sign() === 1 ? '+' : '-'}oo`
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const plus = this.#withSign && this.isPositive() ? '+' : ''
|
|
135
|
+
|
|
136
|
+
if (this.exact) {
|
|
137
|
+
if (this.#denominator === 1) {
|
|
138
|
+
return `${plus}${this.#numerator}`
|
|
139
|
+
} else {
|
|
140
|
+
return `${plus}${this.#numerator}/${this.#denominator}`
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
return plus + this.value.toFixed(this.#digits)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static areEquals(...F: Fraction[]): boolean {
|
|
148
|
+
return F.every(f => f.isEqual(F[0]))
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
static average(...fractions: (InputValue<Fraction>)[]): Fraction {
|
|
152
|
+
const M = new Fraction().zero()
|
|
153
|
+
|
|
154
|
+
for (const f of fractions) {
|
|
155
|
+
M.add(f)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
M.divide(fractions.length)
|
|
159
|
+
|
|
160
|
+
return M
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static isFraction(value: InputValue<Fraction>) {
|
|
164
|
+
if (value instanceof Fraction ||
|
|
165
|
+
(typeof value === "number" && !isNaN(value))
|
|
166
|
+
) {
|
|
167
|
+
return true
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (typeof value === "string") {
|
|
171
|
+
const parts = value.split('/')
|
|
172
|
+
|
|
173
|
+
return parts.length <= 2 && parts.every(p => !isNaN(+p))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return false
|
|
177
|
+
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
static max(...fractions: InputValue<Fraction>[]): Fraction {
|
|
181
|
+
let M = new Fraction(fractions[0])
|
|
182
|
+
|
|
183
|
+
for (const m of fractions) {
|
|
184
|
+
const compare = new Fraction(m)
|
|
185
|
+
if (compare.isGreater(M)) {
|
|
186
|
+
M = compare.clone()
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return M
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
static min(...fractions: (InputValue<Fraction>)[]): Fraction {
|
|
194
|
+
let M = new Fraction(fractions[0])
|
|
195
|
+
|
|
196
|
+
for (const m of fractions) {
|
|
197
|
+
const compare = new Fraction(m)
|
|
198
|
+
if (compare.isLesser(M)) {
|
|
199
|
+
M = compare.clone()
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return M
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
static sort(fractions: (InputValue<Fraction>)[], reverse?: boolean): Fraction[] {
|
|
207
|
+
const fractionsObject: Fraction[] = fractions.map(f => f instanceof Fraction ? f : new Fraction(f))
|
|
208
|
+
|
|
209
|
+
const sorted = fractionsObject.sort((a, b) => a.value - b.value)
|
|
210
|
+
|
|
211
|
+
if (reverse) {
|
|
212
|
+
sorted.reverse()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return sorted
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
static toSameDenominateur(...fractions: InputValue<Fraction>[]): Fraction[] {
|
|
219
|
+
const F = fractions.map(x => new Fraction(x))
|
|
220
|
+
const lcm = Numeric.lcm(...F.map(x => x.denominator))
|
|
221
|
+
|
|
222
|
+
F.forEach(x => x.amplify(lcm / x.denominator))
|
|
223
|
+
|
|
224
|
+
return F
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
static unique(fractions: (InputValue<Fraction>)[]): Fraction[] {
|
|
228
|
+
const unique: Record<string, boolean> = {}
|
|
229
|
+
const distinct: Fraction[] = []
|
|
230
|
+
|
|
231
|
+
fractions.forEach(x => {
|
|
232
|
+
if (!(x instanceof Fraction)) {
|
|
233
|
+
x = new Fraction(x)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (!unique[x.clone().reduce().tex]) {
|
|
237
|
+
distinct.push(x.clone())
|
|
238
|
+
unique[x.clone().reduce().tex] = true
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
return distinct
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
static xMultiply(...values: (InputValue<Fraction>)[]): Fraction {
|
|
246
|
+
const R = new Fraction()
|
|
247
|
+
|
|
248
|
+
values.forEach(f => R.multiply(f, false))
|
|
249
|
+
|
|
250
|
+
return R
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
abs(): this {
|
|
254
|
+
this.#numerator = Math.abs(this.#numerator)
|
|
255
|
+
this.#denominator = Math.abs(this.#denominator)
|
|
256
|
+
return this
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
add(F: InputValue<Fraction>): this {
|
|
260
|
+
if (F instanceof Fraction) {
|
|
261
|
+
const N: number = this.#numerator
|
|
262
|
+
const D: number = this.#denominator
|
|
263
|
+
|
|
264
|
+
this.#numerator = N * F.denominator + F.numerator * D
|
|
265
|
+
this.#denominator = D * F.denominator
|
|
266
|
+
this.exact = this.exact && F.exact
|
|
267
|
+
} else {
|
|
268
|
+
return this.add(new Fraction(F))
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return this.reduce()
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
amplify(k: number): this {
|
|
275
|
+
if (!Number.isSafeInteger(k)) {
|
|
276
|
+
throw new InvalidArgumentError("The amplification factor must be a safe integer.")
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (k === 0) {
|
|
280
|
+
throw new InvalidArgumentError("The amplification factor cannot be zero.")
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
this.#numerator *= k
|
|
284
|
+
this.#denominator *= k
|
|
285
|
+
|
|
286
|
+
return this
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Compare the current coefficient with another coefficient
|
|
291
|
+
* @param F (Coefficient) The coefficient to _compare
|
|
292
|
+
* @param sign (string| default is =): authorized values: =, <, <=, >, >= with some variations.
|
|
293
|
+
*/
|
|
294
|
+
compare(F: InputValue<Fraction>, sign?: compareSign): boolean {
|
|
295
|
+
sign ??= '='
|
|
296
|
+
|
|
297
|
+
let compareFraction: Fraction
|
|
298
|
+
if (F instanceof Fraction) {
|
|
299
|
+
compareFraction = F.clone()
|
|
300
|
+
} else {
|
|
301
|
+
compareFraction = new Fraction(F)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
switch (sign) {
|
|
305
|
+
case '>':
|
|
306
|
+
return this.value > compareFraction.value
|
|
307
|
+
case ">=":
|
|
308
|
+
case "=>":
|
|
309
|
+
case "geq":
|
|
310
|
+
return this.value >= compareFraction.value
|
|
311
|
+
case "<":
|
|
312
|
+
return this.value < compareFraction.value
|
|
313
|
+
case "<=":
|
|
314
|
+
case "=<":
|
|
315
|
+
case "leq":
|
|
316
|
+
return this.value <= compareFraction.value
|
|
317
|
+
case "=":
|
|
318
|
+
return this.#numerator * compareFraction.denominator ===
|
|
319
|
+
compareFraction.numerator * this.#denominator
|
|
320
|
+
case "<>":
|
|
321
|
+
return this.#numerator * compareFraction.denominator !==
|
|
322
|
+
compareFraction.numerator * this.#denominator
|
|
323
|
+
default:
|
|
324
|
+
return false
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
get denominator(): number {
|
|
329
|
+
return this.#denominator
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
set denominator(value: number) {
|
|
333
|
+
this.#denominator = value
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
get asDfrac(): this {
|
|
337
|
+
this.#type = FRAC_TYPE.dfrac
|
|
338
|
+
return this
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
digits(value: number): this {
|
|
342
|
+
this.#digits = value
|
|
343
|
+
return this
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
divide(F: Fraction | number): this {
|
|
347
|
+
const Q = new Fraction(F)
|
|
348
|
+
|
|
349
|
+
if (Q.numerator === 0) {
|
|
350
|
+
// Division by zero yields infinity; preserve the dividend's sign
|
|
351
|
+
// so that e.g. (-3) / 0 evaluates to -Infinity, not +Infinity.
|
|
352
|
+
const s = this.sign()
|
|
353
|
+
this.infinite()
|
|
354
|
+
return s === -1 ? this.opposite() : this
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const N: number = this.#numerator
|
|
358
|
+
const D: number = this.#denominator
|
|
359
|
+
|
|
360
|
+
this.#numerator = N * Q.denominator
|
|
361
|
+
this.#denominator = D * Q.numerator
|
|
362
|
+
|
|
363
|
+
this.exact = this.exact && Q.exact
|
|
364
|
+
return this.reduce()
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
get exact(): boolean {
|
|
368
|
+
return this.#exact
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
set exact(value: boolean) {
|
|
372
|
+
this.#exact = value
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
get asFrac(): this {
|
|
376
|
+
this.#type = FRAC_TYPE.frac
|
|
377
|
+
return this
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
fromNumber(value: number): this {
|
|
381
|
+
if (Number.isSafeInteger(value)) {
|
|
382
|
+
// The given value is an integer
|
|
383
|
+
this.#numerator = value
|
|
384
|
+
this.#denominator = 1
|
|
385
|
+
this.#exact = true
|
|
386
|
+
return this
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Exponential notation (e.g. 1e-7, 1e21) is not supported: toString()
|
|
390
|
+
// would break the "integer numerator / integer denominator" invariant.
|
|
391
|
+
// Fail explicitly rather than silently producing a corrupted fraction.
|
|
392
|
+
if (value.toString().includes('e') || value.toString().includes('E')) {
|
|
393
|
+
throw new InvalidArgumentError(`Exponential notation is not supported: ${value}`)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// The given value is a float number
|
|
397
|
+
// Get the number of decimals after the float sign
|
|
398
|
+
const [, decimal] = (value.toString()).split('.')
|
|
399
|
+
const p: number = decimal ? decimal.length : 0
|
|
400
|
+
const power = Math.pow(10, p)
|
|
401
|
+
|
|
402
|
+
this.#numerator = value * power
|
|
403
|
+
this.#denominator = power
|
|
404
|
+
|
|
405
|
+
this.#numerator = Numeric.numberCorrection(this.#numerator)
|
|
406
|
+
this.#denominator = Numeric.numberCorrection(this.#denominator)
|
|
407
|
+
|
|
408
|
+
this.reduce()
|
|
409
|
+
|
|
410
|
+
// assume it's not exact if the decimal part is more than 10 decimals
|
|
411
|
+
this.#exact = p < 10
|
|
412
|
+
|
|
413
|
+
return this
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
fromNumbers(numerator: number, denominator: number): this {
|
|
417
|
+
if (denominator === 0) {
|
|
418
|
+
// Single, centralized handling of a zero denominator:
|
|
419
|
+
// x/0 → signed infinity, and 0/0 → invalid (NaN).
|
|
420
|
+
if (numerator === 0) {
|
|
421
|
+
return this.invalid()
|
|
422
|
+
}
|
|
423
|
+
return numerator < 0 ? this.infinite().opposite() : this.infinite()
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (Number.isSafeInteger(numerator) && Number.isSafeInteger(denominator)) {
|
|
427
|
+
// The given value is an integer
|
|
428
|
+
this.#numerator = numerator
|
|
429
|
+
this.#denominator = denominator
|
|
430
|
+
this.#exact = true
|
|
431
|
+
return this
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return this.fromNumber(numerator / denominator)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
fromPeriodic(value: string | number, length: number): this {
|
|
438
|
+
const [, decimal] = (value.toString()).split(/[.,]/)
|
|
439
|
+
const p: number = decimal ? decimal.length : 0
|
|
440
|
+
|
|
441
|
+
if (!Number.isSafeInteger(length) || length <= 0) {
|
|
442
|
+
throw new InvalidArgumentError("The periodic length must be a positive integer.")
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (length > p) {
|
|
446
|
+
throw new InvalidArgumentError(`The periodic length (${length}) cannot exceed the number of decimal digits (${p}).`)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const power = Math.pow(10, p)
|
|
450
|
+
const shift = Math.pow(10, p - length)
|
|
451
|
+
|
|
452
|
+
// Handle the sign separately: Math.floor rounds toward -Infinity,
|
|
453
|
+
// which would corrupt the truncation for negative values.
|
|
454
|
+
const numericValue = +value
|
|
455
|
+
const sign = numericValue < 0 ? -1 : 1
|
|
456
|
+
const absValue = Math.abs(numericValue)
|
|
457
|
+
|
|
458
|
+
this.#numerator = sign * Math.round(absValue * power - Math.floor(absValue * shift))
|
|
459
|
+
this.#denominator = power - shift
|
|
460
|
+
this.#exact = true
|
|
461
|
+
|
|
462
|
+
return this.reduce()
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
fromString(value: string): this {
|
|
466
|
+
// Periodic decimal notation, e.g. "2.31(25)" = 2.31252525...
|
|
467
|
+
const periodicMatch = /^([+-]?)(\d*)\.(\d*)\((\d+)\)$/.exec(value)
|
|
468
|
+
if (periodicMatch) {
|
|
469
|
+
const [, sign, intPart, nonPeriodic, period] = periodicMatch
|
|
470
|
+
const absDecimal = `${intPart || '0'}.${nonPeriodic}${period}`
|
|
471
|
+
|
|
472
|
+
this.fromPeriodic(absDecimal, period.length)
|
|
473
|
+
if (sign === '-') {
|
|
474
|
+
this.opposite()
|
|
475
|
+
}
|
|
476
|
+
return this.reduce()
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// A string using parentheses that did not match the notation is malformed.
|
|
480
|
+
if (value.includes('(') || value.includes(')')) {
|
|
481
|
+
return this.invalid()
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Split the string value in two parts: Numerator/Denominator
|
|
485
|
+
const S = value.split('/').map(Number)
|
|
486
|
+
|
|
487
|
+
this.#exact = true
|
|
488
|
+
|
|
489
|
+
// Only one divide sign allowed
|
|
490
|
+
if (S.length > 2) {
|
|
491
|
+
this.#numerator = NaN
|
|
492
|
+
return this
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Each parts must be a number
|
|
496
|
+
if (S.some(x => isNaN(x))) {
|
|
497
|
+
this.#numerator = NaN
|
|
498
|
+
return this
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (S.length === 1) {
|
|
502
|
+
// No divide sign - it's a number
|
|
503
|
+
return this.fromNumber(+value)
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// One divide sign: delegate to fromNumbers so the zero-denominator case
|
|
507
|
+
// is handled at a single place (see fromNumbers).
|
|
508
|
+
return this.fromNumbers(S[0], S[1])
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
infinite(): this {
|
|
512
|
+
this.#numerator = Infinity
|
|
513
|
+
this.#denominator = 1
|
|
514
|
+
this.exact = true
|
|
515
|
+
return this
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
invalid(): this {
|
|
519
|
+
this.#numerator = NaN
|
|
520
|
+
this.#denominator = 1
|
|
521
|
+
this.exact = true
|
|
522
|
+
return this
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
inverse(): this {
|
|
526
|
+
const sign = this.sign()
|
|
527
|
+
const n = Math.abs(this.#numerator)
|
|
528
|
+
this.#numerator = Math.abs(this.#denominator) * sign
|
|
529
|
+
this.#denominator = n
|
|
530
|
+
|
|
531
|
+
return this
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
isEqual(than: Fraction | number): boolean {
|
|
535
|
+
return this.compare(than, '=')
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
isEven(): boolean {
|
|
539
|
+
return this.isRelative() && this.value % 2 === 0
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
isFinite(): boolean {
|
|
543
|
+
return !this.isInfinity() && !this.isNaN()
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
isGeq(than: Fraction | number): boolean {
|
|
547
|
+
return this.compare(than, '>=')
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
isGreater(than: Fraction | number): boolean {
|
|
551
|
+
return this.compare(than, '>')
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
isInfinity(): boolean {
|
|
555
|
+
return Math.abs(this.#numerator) === Infinity
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
isLeq(than: Fraction | number): boolean {
|
|
559
|
+
return this.compare(than, '<=')
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
isLesser(than: Fraction | number): boolean {
|
|
563
|
+
return this.compare(than, '<')
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
isNaN(): boolean {
|
|
567
|
+
return isNaN(this.#numerator)
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
isNatural(): boolean {
|
|
571
|
+
return this.isRelative() && this.isPositive()
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
isNegative(): boolean {
|
|
575
|
+
return this.sign() === -1
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
isNotEqual(than: Fraction | number): boolean {
|
|
579
|
+
return this.compare(than, '<>')
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
isNotZero(): boolean {
|
|
583
|
+
return this.#numerator !== 0
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
isOdd(): boolean {
|
|
587
|
+
return this.isRelative() && Math.abs(this.value % 2) === 1
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
isOne(): boolean {
|
|
591
|
+
return this.#numerator === 1 && this.#denominator === 1
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
isPeriodic(): boolean {
|
|
595
|
+
if (!this.exact || !this.isFinite()) {
|
|
596
|
+
return false
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
let d = Math.abs(this.clone().reduce().denominator)
|
|
600
|
+
while (d % 2 === 0) {
|
|
601
|
+
d /= 2
|
|
602
|
+
}
|
|
603
|
+
while (d % 5 === 0) {
|
|
604
|
+
d /= 5
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return d !== 1
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
isPositive(): boolean {
|
|
611
|
+
return this.sign() === 1
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* True when the fraction is an exact rational number: a finite, exact
|
|
616
|
+
* numerator/denominator. Integers are included (4 = 4/1 is rational).
|
|
617
|
+
* Approximate (non-exact) fractions and ±Infinity / NaN are not rational.
|
|
618
|
+
*/
|
|
619
|
+
isRational(): boolean {
|
|
620
|
+
return this.exact && this.isFinite()
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* True when the fraction is exact and NOT an integer, i.e. it has a non-zero
|
|
625
|
+
* fractional part (e.g. 3/2). Use {@link isRelative} to test for an integer.
|
|
626
|
+
* (This was the former behaviour of {@link isRational}.)
|
|
627
|
+
*/
|
|
628
|
+
hasFractionalPart(): boolean {
|
|
629
|
+
return this.exact && !this.isRelative()
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
isReduced(): boolean {
|
|
633
|
+
return Math.abs(Numeric.gcd(this.#numerator, this.#denominator)) === 1
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* True when the fraction represents a relative integer, i.e. its reduced
|
|
638
|
+
* denominator equals 1 (e.g. 6/3 = 2). See {@link hasFractionalPart} for the
|
|
639
|
+
* exact non-integer case.
|
|
640
|
+
*/
|
|
641
|
+
isRelative(): boolean {
|
|
642
|
+
return this.exact && this.clone().reduce().denominator === 1
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
isSquare(): boolean {
|
|
646
|
+
const f = this.clone().reduce()
|
|
647
|
+
|
|
648
|
+
// A negative value can never be a perfect square.
|
|
649
|
+
if (f.isNegative()) {
|
|
650
|
+
return false
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const n = Math.abs(f.numerator)
|
|
654
|
+
const d = Math.abs(f.denominator)
|
|
655
|
+
const rootN = Math.round(Math.sqrt(n))
|
|
656
|
+
const rootD = Math.round(Math.sqrt(d))
|
|
657
|
+
|
|
658
|
+
return rootN * rootN === n && rootD * rootD === d
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
isStrictlyNegative(): boolean {
|
|
662
|
+
return this.value < 0
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
isStrictlyPositive(): boolean {
|
|
666
|
+
return this.value > 0
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
isUnit(): boolean {
|
|
670
|
+
return Math.abs(this.#numerator) === 1 && this.#denominator === 1
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// Mathematical operations specific to fractions
|
|
674
|
+
isZero(): boolean {
|
|
675
|
+
return this.#numerator === 0
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
multiply(F: InputValue<Fraction>, reduce = true): this {
|
|
679
|
+
// Parse the value.
|
|
680
|
+
// If it's a fraction, return a clone of it
|
|
681
|
+
// If it's an integer, return the fraction F/1
|
|
682
|
+
const Q = new Fraction(F)
|
|
683
|
+
|
|
684
|
+
this.#numerator = this.#numerator * Q.numerator
|
|
685
|
+
this.#denominator = this.#denominator * Q.denominator
|
|
686
|
+
|
|
687
|
+
this.exact = this.exact && Q.exact
|
|
688
|
+
|
|
689
|
+
return reduce ? this.reduce() : this
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// ------------------------------------------
|
|
693
|
+
get numerator(): number {
|
|
694
|
+
return this.#numerator
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
set numerator(value: number) {
|
|
698
|
+
this.#numerator = value
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
one(): this {
|
|
702
|
+
return this.fromNumber(1)
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
opposite(): this {
|
|
706
|
+
this.#numerator = -this.#numerator
|
|
707
|
+
return this
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
pow(p: number | Fraction): this {
|
|
711
|
+
if (p instanceof Fraction) {
|
|
712
|
+
return this.pow(p.value)
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
this.reduce()
|
|
716
|
+
if (p < 0) {
|
|
717
|
+
this.inverse()
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
const absP = Math.abs(p)
|
|
721
|
+
|
|
722
|
+
if (Number.isInteger(absP)) {
|
|
723
|
+
// Integer exponent: the result is always an exact rational.
|
|
724
|
+
// Round to absorb floating-point noise on large powers.
|
|
725
|
+
this.#numerator = Math.round(Math.pow(this.#numerator, absP))
|
|
726
|
+
this.#denominator = Math.round(Math.pow(this.#denominator, absP))
|
|
727
|
+
return this
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Fractional exponent (e.g. 0.5): exact only when both parts are
|
|
731
|
+
// perfect powers. Round the candidate, then verify by raising it back
|
|
732
|
+
// through the inverse exponent — no arbitrary epsilon involved.
|
|
733
|
+
const powNum = Math.round(Math.pow(this.#numerator, absP))
|
|
734
|
+
const powDen = Math.round(Math.pow(this.#denominator, absP))
|
|
735
|
+
|
|
736
|
+
if (Math.round(Math.pow(powNum, 1 / absP)) === this.#numerator &&
|
|
737
|
+
Math.round(Math.pow(powDen, 1 / absP)) === this.#denominator) {
|
|
738
|
+
// Exact result — e.g. (9/4)^0.5 = 3/2
|
|
739
|
+
this.#numerator = powNum
|
|
740
|
+
this.#denominator = powDen
|
|
741
|
+
} else {
|
|
742
|
+
// Non-exact result — e.g. (2/1)^0.5 = 1.414...
|
|
743
|
+
this.#numerator = Math.pow(this.#numerator, absP) / Math.pow(this.#denominator, absP)
|
|
744
|
+
this.#denominator = 1
|
|
745
|
+
this.exact = false
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
return this
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
reduce(): this {
|
|
752
|
+
const g = Numeric.gcd(this.#numerator, this.#denominator)
|
|
753
|
+
this.#numerator = this.#numerator / g
|
|
754
|
+
this.#denominator = this.#denominator / g
|
|
755
|
+
|
|
756
|
+
if (this.#denominator < 0) {
|
|
757
|
+
this.#denominator = -this.#denominator
|
|
758
|
+
this.#numerator = -this.#numerator
|
|
759
|
+
}
|
|
760
|
+
return this
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
root(p: number): this {
|
|
764
|
+
|
|
765
|
+
// Check if they are perfect roots..
|
|
766
|
+
if (p === 0) {
|
|
767
|
+
return this
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// if p is not a safe integer, throw error
|
|
771
|
+
if (!Number.isSafeInteger(p)) {
|
|
772
|
+
throw new InvalidArgumentError("The root must be an integer.")
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Validate before any mutation so a rejected root leaves the receiver
|
|
776
|
+
// intact (atomicity). inverse() preserves the sign, so testing
|
|
777
|
+
// isNegative() here is equivalent to testing it after the inversion below.
|
|
778
|
+
if (this.isNegative() && p % 2 === 0) {
|
|
779
|
+
throw new MathError("The root of a negative number must be odd.")
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// If negative exponent, inverse the fraction
|
|
783
|
+
if (p < 0) {
|
|
784
|
+
this.inverse()
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// get the sign of the fraction and make it positive
|
|
788
|
+
const sign = this.sign()
|
|
789
|
+
this.abs()
|
|
790
|
+
|
|
791
|
+
// Reduce the fraction
|
|
792
|
+
this.reduce()
|
|
793
|
+
|
|
794
|
+
// The root degree magnitude (the sign of p was already handled above).
|
|
795
|
+
const q = Math.abs(p)
|
|
796
|
+
|
|
797
|
+
// Round the candidate root, then verify by raising it back to the
|
|
798
|
+
// integer power q. Since q is an integer, that check is exact — this
|
|
799
|
+
// catches perfect roots (e.g. ∛27 = 3) that a plain floor would miss.
|
|
800
|
+
const rootNum = Math.round(Math.pow(this.#numerator, 1 / q))
|
|
801
|
+
const rootDen = Math.round(Math.pow(this.#denominator, 1 / q))
|
|
802
|
+
|
|
803
|
+
if (Math.pow(rootNum, q) === this.#numerator && Math.pow(rootDen, q) === this.#denominator) {
|
|
804
|
+
// Perfect root
|
|
805
|
+
this.#numerator = rootNum
|
|
806
|
+
this.#denominator = rootDen
|
|
807
|
+
} else {
|
|
808
|
+
// The fraction is not a perfect root - make it approximative
|
|
809
|
+
this.#numerator = Math.pow(this.#numerator, 1 / q) / Math.pow(this.#denominator, 1 / q)
|
|
810
|
+
this.#denominator = 1
|
|
811
|
+
this.exact = false
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// Restore the sign
|
|
815
|
+
this.multiply(sign)
|
|
816
|
+
|
|
817
|
+
return this
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Sign of the fraction, as +1 or -1.
|
|
822
|
+
*
|
|
823
|
+
* NOTE (assumed): zero is classified as positive (`sign()` returns 1).
|
|
824
|
+
* Consequently `isPositive()` and `isNatural()` are true for zero, while
|
|
825
|
+
* `isNegative()` is false.
|
|
826
|
+
*/
|
|
827
|
+
sign(): 1 | -1 {
|
|
828
|
+
return (this.#numerator * this.#denominator >= 0) ? 1 : -1
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
sqrt(): this {
|
|
832
|
+
return this.root(2)
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
subtract(F: Fraction | number): this {
|
|
836
|
+
if (F instanceof Fraction) {
|
|
837
|
+
return this.add(F.clone().opposite())
|
|
838
|
+
} else {
|
|
839
|
+
return this.add(-F)
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
get asTfrac(): this {
|
|
844
|
+
this.#type = FRAC_TYPE.tfrac
|
|
845
|
+
return this
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
toRepeatingDecimal(): {
|
|
849
|
+
sign: 1 | -1
|
|
850
|
+
integerPart: string
|
|
851
|
+
nonPeriodic: string
|
|
852
|
+
periodic: string
|
|
853
|
+
periodLength: number
|
|
854
|
+
} {
|
|
855
|
+
if (!this.exact || !this.isFinite()) {
|
|
856
|
+
throw new InvalidArgumentError("Cannot decompose a non-exact or infinite fraction.")
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
const f = this.clone().reduce()
|
|
860
|
+
const sign = f.sign()
|
|
861
|
+
const n = Math.abs(f.numerator)
|
|
862
|
+
const d = Math.abs(f.denominator)
|
|
863
|
+
|
|
864
|
+
const integerPart = Math.floor(n / d).toString()
|
|
865
|
+
let remainder = n % d
|
|
866
|
+
|
|
867
|
+
const digits: number[] = []
|
|
868
|
+
const seen = new Map<number, number>() // remainder -> index in digits
|
|
869
|
+
let periodStart = -1
|
|
870
|
+
|
|
871
|
+
while (remainder !== 0) {
|
|
872
|
+
const seenIndex = seen.get(remainder)
|
|
873
|
+
if (seenIndex !== undefined) {
|
|
874
|
+
periodStart = seenIndex
|
|
875
|
+
break
|
|
876
|
+
}
|
|
877
|
+
seen.set(remainder, digits.length)
|
|
878
|
+
remainder *= 10
|
|
879
|
+
digits.push(Math.floor(remainder / d))
|
|
880
|
+
remainder %= d
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
const nonPeriodic = (periodStart === -1 ? digits : digits.slice(0, periodStart)).join('')
|
|
884
|
+
const periodic = periodStart === -1 ? '' : digits.slice(periodStart).join('')
|
|
885
|
+
|
|
886
|
+
return {sign, integerPart, nonPeriodic, periodic, periodLength: periodic.length}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
get value(): number {
|
|
890
|
+
const result = this.#numerator / this.#denominator
|
|
891
|
+
return result === 0 ? 0 : result
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
get withSign(): this {
|
|
895
|
+
this.#withSign = true
|
|
896
|
+
return this
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
get withoutSign(): this {
|
|
900
|
+
this.#withSign = false
|
|
901
|
+
return this
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
zero(): this {
|
|
905
|
+
return this.fromNumber(0)
|
|
906
|
+
}
|
|
907
|
+
}
|