pimath 0.0.25 → 0.0.29
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/dev/index.html +10 -7
- package/dev/pi.js +251 -98
- package/dev/pi.js.map +1 -1
- package/dist/pi.js +1 -1
- package/dist/pi.js.map +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/algebra.Equation.html +9 -9
- package/docs/classes/algebra.LinearSystem.html +1 -1
- package/docs/classes/algebra.Logicalset.html +2 -2
- package/docs/classes/algebra.Monom.html +45 -44
- package/docs/classes/algebra.Polynom.html +9 -9
- package/docs/classes/algebra.Rational.html +2 -2
- package/docs/classes/coefficients.Fraction.html +6 -6
- package/docs/classes/coefficients.Nthroot.html +1 -1
- package/docs/classes/geometry.Circle.html +1 -1
- package/docs/classes/geometry.Line.html +2 -2
- package/docs/classes/geometry.Point.html +1 -1
- package/docs/classes/geometry.Triangle.html +5 -5
- package/docs/classes/geometry.Vector.html +1 -1
- package/docs/classes/numeric.Numeric.html +5 -5
- package/docs/classes/shutingyard.Shutingyard.html +5 -5
- package/docs/interfaces/geometry.remarquableLines.html +1 -1
- package/docs/modules/algebra.html +1 -1
- package/docs/modules/random.Random.html +1 -1
- package/docs/modules/random.html +1 -1
- package/esm/main.js +2 -0
- package/esm/main.js.map +1 -1
- package/esm/maths/algebra/logicalset.js +1 -1
- package/esm/maths/algebra/logicalset.js.map +1 -1
- package/esm/maths/algebra/monom.d.ts +2 -1
- package/esm/maths/algebra/monom.js +7 -1
- package/esm/maths/algebra/monom.js.map +1 -1
- package/esm/maths/algebra/polynom.js +1 -1
- package/esm/maths/algebra/polynom.js.map +1 -1
- package/esm/maths/algebra/rational.d.ts +1 -1
- package/esm/maths/algebra/rational.js +2 -2
- package/esm/maths/algebra/rational.js.map +1 -1
- package/esm/maths/coefficients/fraction.js.map +1 -1
- package/esm/maths/geometry/line.d.ts +1 -0
- package/esm/maths/geometry/line.js +3 -0
- package/esm/maths/geometry/line.js.map +1 -1
- package/esm/maths/geometry/vector.js +7 -2
- package/esm/maths/geometry/vector.js.map +1 -1
- package/esm/maths/numexp.d.ts +16 -0
- package/esm/maths/numexp.js +119 -0
- package/esm/maths/numexp.js.map +1 -0
- package/esm/maths/shutingyard.d.ts +21 -4
- package/esm/maths/shutingyard.js +76 -76
- package/esm/maths/shutingyard.js.map +1 -1
- package/package.json +1 -1
- package/src/main.ts +2 -0
- package/src/maths/algebra/logicalset.ts +2 -2
- package/src/maths/algebra/monom.ts +35 -22
- package/src/maths/algebra/polynom.ts +1 -1
- package/src/maths/algebra/rational.ts +1 -1
- package/src/maths/geometry/line.ts +3 -0
- package/src/maths/geometry/vector.ts +10 -2
- package/src/maths/numexp.ts +138 -0
- package/src/maths/shutingyard.ts +94 -97
- package/tests/algebra/monom.test.ts +1 -1
- package/tests/algebra/polynom.test.ts +10 -1
- package/tests/numexp.test.ts +14 -0
- package/tests/shutingyard.test.ts +3 -3
- package/tsconfig.json +0 -1
- package/esm/docs.d.ts +0 -6
- package/esm/docs.js +0 -7
- package/esm/docs.js.map +0 -1
- package/esm/maths/random/random.d.ts +0 -13
- package/esm/maths/random/random.js +0 -27
- package/esm/maths/random/random.js.map +0 -1
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
/***
|
|
2
2
|
* Monom class
|
|
3
|
-
* Defined as coefficient * literal
|
|
4
|
-
* Examples: 3x^2, 3/5x^2, ...
|
|
5
3
|
*/
|
|
6
4
|
import {Fraction} from "../coefficients";
|
|
7
5
|
import {Numeric} from "../numeric";
|
|
@@ -16,8 +14,10 @@ export class Monom {
|
|
|
16
14
|
private _literal: literalType;
|
|
17
15
|
|
|
18
16
|
/**
|
|
19
|
-
* Create
|
|
20
|
-
*
|
|
17
|
+
* Create a Monom
|
|
18
|
+
* Defined as \\(k \\cdot x^{n}\\), where \\( k,n \in \\mathbb{Q}\\).
|
|
19
|
+
* Examples: \\(3x^2\\) or \\(3/5x^2\\)
|
|
20
|
+
* @param value (optional) string The value that should be parse. Can be a Monom, a Fraction, a string or a number. If nothing is provided, it will return the trivial monom (0).
|
|
21
21
|
*/
|
|
22
22
|
constructor(value?: unknown) {
|
|
23
23
|
this.zero();
|
|
@@ -34,29 +34,35 @@ export class Monom {
|
|
|
34
34
|
// Getter and setter
|
|
35
35
|
// ------------------------------------------
|
|
36
36
|
/**
|
|
37
|
-
* Get the coefficient
|
|
37
|
+
* Get the coefficient \\(k\\) of the Monom \\(k\\cdot x^{n}\\)
|
|
38
|
+
* @returns {Fraction}
|
|
38
39
|
*/
|
|
39
40
|
get coefficient(): Fraction {
|
|
40
41
|
return this._coefficient;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
|
-
* Set the coefficient value of the monom
|
|
45
|
-
* @param F
|
|
45
|
+
* Set the coefficient \\(k\\) value of the monom
|
|
46
|
+
* @param {Fraction | number | string} F
|
|
46
47
|
*/
|
|
47
|
-
set coefficient(F: Fraction) {
|
|
48
|
-
this._coefficient = F;
|
|
48
|
+
set coefficient(F: Fraction | number | string) {
|
|
49
|
+
this._coefficient = new Fraction(F);
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
/**
|
|
52
|
-
* Get the literal part
|
|
53
|
+
* Get the literal part of \\(x^{n_1}y^{n_2}\\) as dictionary \\[\\begin{array}{ll}x&=n_1\\\\y&=n_2\\end{array}\\]
|
|
54
|
+
* @returns {literalType}
|
|
53
55
|
*/
|
|
54
56
|
get literal(): literalType {
|
|
55
57
|
return this._literal;
|
|
56
58
|
}
|
|
57
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Get the literal square roots of the Monom.
|
|
62
|
+
* TODO: remove this getter ? Is it used and is it correct ?
|
|
63
|
+
* @returns {literalType}
|
|
64
|
+
*/
|
|
58
65
|
get literalSqrt(): literalType {
|
|
59
|
-
|
|
60
66
|
if (this.isLiteralSquare()) {
|
|
61
67
|
let L: literalType = {}
|
|
62
68
|
for (let key in this._literal) {
|
|
@@ -69,8 +75,8 @@ export class Monom {
|
|
|
69
75
|
}
|
|
70
76
|
|
|
71
77
|
/**
|
|
72
|
-
* Set the literal part of the monom
|
|
73
|
-
* @param L
|
|
78
|
+
* Set the literal part of the monom. Must be a dictionary {x: Fraction, y: Fraction, ...}
|
|
79
|
+
* @param {literalType} L
|
|
74
80
|
*/
|
|
75
81
|
set literal(L: literalType) {
|
|
76
82
|
this._literal = L;
|
|
@@ -230,11 +236,18 @@ export class Monom {
|
|
|
230
236
|
* Display the monom, forcing the '+' sign to appear
|
|
231
237
|
*/
|
|
232
238
|
get displayWithSign(): string {
|
|
233
|
-
// TODO: Rename or remove this getter ?
|
|
234
239
|
let d: String = this.display;
|
|
235
240
|
return (d[0] !== '-' ? '+' : '') + d;
|
|
236
241
|
}
|
|
237
242
|
|
|
243
|
+
get texWithSign(): string {
|
|
244
|
+
if (this.coefficient.isStrictlyPositive()) {
|
|
245
|
+
return '+' + this.tex
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return this.tex
|
|
249
|
+
}
|
|
250
|
+
|
|
238
251
|
/**
|
|
239
252
|
* Get the tex output of the monom
|
|
240
253
|
*/
|
|
@@ -281,15 +294,15 @@ export class Monom {
|
|
|
281
294
|
*/
|
|
282
295
|
parse = (inputStr: unknown): Monom => {
|
|
283
296
|
|
|
284
|
-
if(typeof inputStr === 'string') {
|
|
297
|
+
if (typeof inputStr === 'string') {
|
|
285
298
|
this._shutingYardToReducedMonom(inputStr)
|
|
286
|
-
}else if(typeof inputStr ==='number') {
|
|
299
|
+
} else if (typeof inputStr === 'number') {
|
|
287
300
|
this._coefficient = new Fraction(inputStr)
|
|
288
301
|
this._literal = {}
|
|
289
|
-
}else if(inputStr instanceof Fraction) {
|
|
302
|
+
} else if (inputStr instanceof Fraction) {
|
|
290
303
|
this._coefficient = inputStr.clone()
|
|
291
304
|
this._literal = {}
|
|
292
|
-
}else if(inputStr instanceof Monom){
|
|
305
|
+
} else if (inputStr instanceof Monom) {
|
|
293
306
|
this._coefficient = inputStr._coefficient.clone()
|
|
294
307
|
this._literal = this.copyLiterals(inputStr.literal)
|
|
295
308
|
}
|
|
@@ -384,7 +397,7 @@ export class Monom {
|
|
|
384
397
|
};
|
|
385
398
|
|
|
386
399
|
copyLiterals = (literal: literalType): literalType => {
|
|
387
|
-
let L:literalType = {}
|
|
400
|
+
let L: literalType = {}
|
|
388
401
|
|
|
389
402
|
for (let k in literal) {
|
|
390
403
|
L[k] = literal[k].clone()
|
|
@@ -392,7 +405,7 @@ export class Monom {
|
|
|
392
405
|
return L
|
|
393
406
|
}
|
|
394
407
|
|
|
395
|
-
makeSame = (M: Monom):Monom => {
|
|
408
|
+
makeSame = (M: Monom): Monom => {
|
|
396
409
|
// Copy the literal parts.
|
|
397
410
|
for (let k in M._literal) {
|
|
398
411
|
this.setLetter(k, M._literal[k].clone());
|
|
@@ -451,7 +464,7 @@ export class Monom {
|
|
|
451
464
|
add = (...M: Monom[]): Monom => {
|
|
452
465
|
for (let m of M) {
|
|
453
466
|
if (this.isSameAs(m)) {
|
|
454
|
-
if(this.isZero()){
|
|
467
|
+
if (this.isZero()) {
|
|
455
468
|
this.makeSame(m)
|
|
456
469
|
}
|
|
457
470
|
this._coefficient.add(m.coefficient);
|
|
@@ -469,7 +482,7 @@ export class Monom {
|
|
|
469
482
|
subtract = (...M: Monom[]): Monom => {
|
|
470
483
|
for (let m of M) {
|
|
471
484
|
if (this.isSameAs(m)) {
|
|
472
|
-
if(this.isZero()){
|
|
485
|
+
if (this.isZero()) {
|
|
473
486
|
this.makeSame(m)
|
|
474
487
|
}
|
|
475
488
|
this._coefficient.add(m.clone().coefficient.opposed());
|
|
@@ -461,7 +461,7 @@ export class Polynom {
|
|
|
461
461
|
|
|
462
462
|
// Make the euclidian division of the two polynoms.
|
|
463
463
|
let MaxIteration = this.degree(letter).clone().multiply(2);
|
|
464
|
-
while (reminder.degree(letter)
|
|
464
|
+
while (reminder.degree(letter).geq(degreeP) && MaxIteration.isPositive()) {
|
|
465
465
|
MaxIteration.subtract(1)
|
|
466
466
|
|
|
467
467
|
// Get the greatest monom divided by the max monom of the divider
|
|
@@ -312,6 +312,9 @@ export class Line {
|
|
|
312
312
|
isSameAs = (line: Line): Boolean => {
|
|
313
313
|
return this.slope.isEqual(line.slope) && this.height.isEqual(line.height);
|
|
314
314
|
}
|
|
315
|
+
isVertical = (): Boolean => {
|
|
316
|
+
return this.slope.isInfinity()
|
|
317
|
+
}
|
|
315
318
|
simplify = (): Line => {
|
|
316
319
|
let lcm = Numeric.lcm(this._a.denominator, this._b.denominator, this._c.denominator),
|
|
317
320
|
gcd = Numeric.gcd(this._a.numerator, this._b.numerator, this._c.numerator);
|
|
@@ -81,12 +81,20 @@ export class Vector {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// Fractions or a number are give
|
|
84
|
-
if (values[0]
|
|
84
|
+
if (values[0] instanceof Fraction || !isNaN(values[0])) {
|
|
85
85
|
this._x = new Fraction(values[0])
|
|
86
86
|
}
|
|
87
|
-
if (values[1]
|
|
87
|
+
if (values[1] instanceof Fraction || !isNaN(values[1])) {
|
|
88
88
|
this._y = new Fraction(values[1])
|
|
89
89
|
}
|
|
90
|
+
|
|
91
|
+
if(
|
|
92
|
+
(typeof values[0] === 'object' && !isNaN(values[0].x) && !isNaN(values[0].x)) &&
|
|
93
|
+
(typeof values[1] === 'object' && !isNaN(values[1].x) && !isNaN(values[1].x))
|
|
94
|
+
){
|
|
95
|
+
this._x = new Fraction(+values[1].x-values[0].x)
|
|
96
|
+
this._y = new Fraction(+values[1].y-values[0].y)
|
|
97
|
+
}
|
|
90
98
|
}
|
|
91
99
|
|
|
92
100
|
return this;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import {Shutingyard, ShutingyardMode, ShutingyardType, tokenConstant} from "./shutingyard";
|
|
2
|
+
import {Fraction} from "./coefficients";
|
|
3
|
+
|
|
4
|
+
export class NumExp {
|
|
5
|
+
private _rpn: { token: string, tokenType: string }[]
|
|
6
|
+
private _expression: string
|
|
7
|
+
|
|
8
|
+
constructor(value: string) {
|
|
9
|
+
this._expression = value
|
|
10
|
+
this._rpn = new Shutingyard(ShutingyardMode.NUMERIC).parse(value).rpn
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get rpn(): { token: string; tokenType: string }[] {
|
|
14
|
+
return this._rpn;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get expression(): string {
|
|
18
|
+
return this._expression;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private _extractDecimalPart(value: number): string {
|
|
22
|
+
let decimal = value.toString()
|
|
23
|
+
|
|
24
|
+
if (!decimal.includes('.')) {
|
|
25
|
+
return ''
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
decimal = decimal.split('.')[1]
|
|
29
|
+
|
|
30
|
+
return decimal.substring(0, decimal.length - 2)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private _numberCorrection(value: number): number {
|
|
34
|
+
// Must modify the number if it's like:
|
|
35
|
+
// a: 3.0000000000000003
|
|
36
|
+
// b: 3.9999999999999994
|
|
37
|
+
// remove the last character
|
|
38
|
+
// check if around n last characters are either 0 or 9
|
|
39
|
+
// if it is, 'round' the number.
|
|
40
|
+
|
|
41
|
+
const epsilon = 0.00000000000001,
|
|
42
|
+
number_of_digits = 6
|
|
43
|
+
|
|
44
|
+
let decimal = this._extractDecimalPart(value)
|
|
45
|
+
if(decimal===''){return value}
|
|
46
|
+
|
|
47
|
+
const n9 = decimal.match(/9+$/g)
|
|
48
|
+
const n0 = decimal.match(/0+$/g)
|
|
49
|
+
|
|
50
|
+
if (n9 && n9[0].length >= number_of_digits) {
|
|
51
|
+
// New tested values.
|
|
52
|
+
let mod = this._extractDecimalPart(value + epsilon),
|
|
53
|
+
mod0 = mod.match(/0+$/g)
|
|
54
|
+
|
|
55
|
+
if(mod0 && mod0[0].length>= number_of_digits){
|
|
56
|
+
// The value can be changed. Remove all zeros!
|
|
57
|
+
return +((value+epsilon).toString().split(mod0[0])[0])
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (n0 && n0[0].length >= number_of_digits) {
|
|
62
|
+
// New tested values.
|
|
63
|
+
let mod = this._extractDecimalPart(value - epsilon),
|
|
64
|
+
mod9 = mod.match(/9+$/g)
|
|
65
|
+
|
|
66
|
+
if(mod9 && mod9[0].length>= number_of_digits){
|
|
67
|
+
// The value can be changed. Remove all nines!
|
|
68
|
+
return +(value.toString().split(n0[0])[0])
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return value
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private _addToStack(stack:number[], value: number): void {
|
|
76
|
+
stack.push(this._numberCorrection(value))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
evaluate(values: { [Key: string]: number }): number {
|
|
80
|
+
let stack: number[] = []
|
|
81
|
+
for (const element of this._rpn) {
|
|
82
|
+
if (element.tokenType === ShutingyardType.COEFFICIENT) {
|
|
83
|
+
// May be a numeric value or a Fraction.
|
|
84
|
+
if (!isNaN(+element.token)) {
|
|
85
|
+
this._addToStack(stack, +element.token)
|
|
86
|
+
} else {
|
|
87
|
+
this._addToStack(stack, new Fraction(element.token).value)
|
|
88
|
+
}
|
|
89
|
+
} else if (element.tokenType === ShutingyardType.VARIABLE) {
|
|
90
|
+
if (values[element.token] !== undefined) {
|
|
91
|
+
this._addToStack(stack, +values[element.token])
|
|
92
|
+
}
|
|
93
|
+
} else if (element.tokenType === ShutingyardType.CONSTANT) {
|
|
94
|
+
this._addToStack(stack, tokenConstant[element.token])
|
|
95
|
+
} else if (element.tokenType === ShutingyardType.OPERATION) {
|
|
96
|
+
if (element.token === '*') {
|
|
97
|
+
const b = +stack.pop(),
|
|
98
|
+
a = +stack.pop()
|
|
99
|
+
this._addToStack(stack, a * b)
|
|
100
|
+
} else if (element.token === '/') {
|
|
101
|
+
const b = +stack.pop(),
|
|
102
|
+
a = +stack.pop()
|
|
103
|
+
this._addToStack(stack, a / b)
|
|
104
|
+
} else if (element.token === '+') {
|
|
105
|
+
const b = +stack.pop(),
|
|
106
|
+
a = +stack.pop()
|
|
107
|
+
this._addToStack(stack, a + b)
|
|
108
|
+
} else if (element.token === '-') {
|
|
109
|
+
const b = +stack.pop(),
|
|
110
|
+
a = +stack.pop()
|
|
111
|
+
this._addToStack(stack, a - b)
|
|
112
|
+
} else if (element.token === '^') {
|
|
113
|
+
const b = +stack.pop(),
|
|
114
|
+
a = +stack.pop()
|
|
115
|
+
this._addToStack(stack, Math.pow(a, b))
|
|
116
|
+
}
|
|
117
|
+
} else if (element.tokenType === ShutingyardType.FUNCTION) {
|
|
118
|
+
const a = +stack.pop()
|
|
119
|
+
if (element.token === 'sin') {
|
|
120
|
+
this._addToStack(stack, Math.sin(a))
|
|
121
|
+
} else if (element.token === 'cos') {
|
|
122
|
+
this._addToStack(stack, Math.cos(a))
|
|
123
|
+
} else if (element.token === 'tan') {
|
|
124
|
+
this._addToStack(stack, Math.tan(a))
|
|
125
|
+
} else if(element.token === 'sqrt') {
|
|
126
|
+
this._addToStack(stack, Math.sqrt(a))
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (stack.length === 1) {
|
|
132
|
+
return stack[0]
|
|
133
|
+
} else {
|
|
134
|
+
console.error('There was a problem parsing', this._expression, '. The RPN array is', this._rpn)
|
|
135
|
+
return 0
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
package/src/maths/shutingyard.ts
CHANGED
|
@@ -1,20 +1,42 @@
|
|
|
1
1
|
import {loadHighlighter} from "typedoc/dist/lib/utils/highlighter";
|
|
2
|
+
import exp = require("constants");
|
|
2
3
|
|
|
3
4
|
type tokenType = {
|
|
4
5
|
[key: string]: {
|
|
5
6
|
precedence: number,
|
|
6
|
-
associative: string
|
|
7
|
+
associative: string,
|
|
8
|
+
type: string
|
|
7
9
|
}
|
|
8
10
|
}
|
|
9
11
|
|
|
12
|
+
export const tokenConstant:{[Key:string]:number} = {
|
|
13
|
+
pi: Math.PI,
|
|
14
|
+
e: Math.exp(1)
|
|
15
|
+
}
|
|
16
|
+
export enum ShutingyardType {
|
|
17
|
+
VARIABLE='variable',
|
|
18
|
+
COEFFICIENT='coefficient',
|
|
19
|
+
OPERATION = 'operation',
|
|
20
|
+
CONSTANT = 'constant',
|
|
21
|
+
FUNCTION = 'function',
|
|
22
|
+
MONOM = 'monom'
|
|
23
|
+
}
|
|
24
|
+
export enum ShutingyardMode {
|
|
25
|
+
POLYNOM= 'polynom',
|
|
26
|
+
SET = 'set',
|
|
27
|
+
NUMERIC = 'numeric'
|
|
28
|
+
}
|
|
29
|
+
|
|
10
30
|
export class Shutingyard {
|
|
11
31
|
private _rpn: { token: string, tokenType: string }[] = [];
|
|
12
|
-
readonly _mode:
|
|
32
|
+
readonly _mode: ShutingyardMode;
|
|
13
33
|
private _tokenConfig: tokenType;
|
|
34
|
+
private _tokenConstant: {[Key:string]: number}
|
|
14
35
|
private _uniformize: boolean;
|
|
36
|
+
private _tokenKeys: string[]
|
|
15
37
|
|
|
16
|
-
constructor(mode?:
|
|
17
|
-
this._mode = typeof mode === 'undefined' ?
|
|
38
|
+
constructor(mode?: ShutingyardMode ) {
|
|
39
|
+
this._mode = typeof mode === 'undefined' ? ShutingyardMode.POLYNOM : mode;
|
|
18
40
|
this.tokenConfigInitialization()
|
|
19
41
|
}
|
|
20
42
|
|
|
@@ -23,41 +45,57 @@ export class Shutingyard {
|
|
|
23
45
|
* Defined operations: + - * / ^ sin cos tan
|
|
24
46
|
* @param token
|
|
25
47
|
*/
|
|
26
|
-
isOperation(token: string): boolean {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
48
|
+
// isOperation(token: string): boolean {
|
|
49
|
+
// if (token[0].match(/[+\-*/^]/g)) {
|
|
50
|
+
// return true;
|
|
51
|
+
// }
|
|
52
|
+
// //
|
|
53
|
+
// // if (token.match(/^sin|cos|tan/g)) {
|
|
54
|
+
// // return true;
|
|
55
|
+
// // }
|
|
56
|
+
//
|
|
57
|
+
// return false;
|
|
58
|
+
// }
|
|
37
59
|
|
|
38
60
|
tokenConfigInitialization(): tokenType {
|
|
39
|
-
if (this._mode ===
|
|
61
|
+
if (this._mode === ShutingyardMode.SET) {
|
|
40
62
|
this._tokenConfig = {
|
|
41
|
-
'&': {precedence: 3, associative: 'left'},
|
|
42
|
-
'|': {precedence: 3, associative: 'left'},
|
|
43
|
-
'!': {precedence: 4, associative: 'right'},
|
|
44
|
-
'-': {precedence: 2, associative: 'left'}
|
|
63
|
+
'&': {precedence: 3, associative: 'left', type: ShutingyardType.OPERATION},
|
|
64
|
+
'|': {precedence: 3, associative: 'left', type: ShutingyardType.OPERATION},
|
|
65
|
+
'!': {precedence: 4, associative: 'right', type: ShutingyardType.OPERATION},
|
|
66
|
+
'-': {precedence: 2, associative: 'left', type: ShutingyardType.OPERATION}
|
|
45
67
|
}
|
|
46
68
|
this._uniformize = false;
|
|
69
|
+
}else if (this._mode === ShutingyardMode.NUMERIC){
|
|
70
|
+
this._tokenConfig = {
|
|
71
|
+
'^': {precedence: 4, associative: 'right', type: ShutingyardType.OPERATION},
|
|
72
|
+
'*': {precedence: 3, associative: 'left', type: ShutingyardType.OPERATION},
|
|
73
|
+
'/': {precedence: 3, associative: 'left', type: ShutingyardType.OPERATION},
|
|
74
|
+
'+': {precedence: 2, associative: 'left', type: ShutingyardType.OPERATION},
|
|
75
|
+
'-': {precedence: 2, associative: 'left', type: ShutingyardType.OPERATION},
|
|
76
|
+
'%': {precedence: 3, associative: 'right', type: ShutingyardType.OPERATION},
|
|
77
|
+
'sin': {precedence: 4, associative: 'right', type: ShutingyardType.FUNCTION},
|
|
78
|
+
'cos': {precedence: 4, associative: 'right', type: ShutingyardType.FUNCTION},
|
|
79
|
+
'tan': {precedence: 4, associative: 'right', type: ShutingyardType.FUNCTION},
|
|
80
|
+
'sqrt': {precedence: 4, associative: 'right', type: ShutingyardType.FUNCTION},
|
|
81
|
+
}
|
|
82
|
+
this._uniformize = false
|
|
47
83
|
} else {
|
|
48
84
|
this._tokenConfig = {
|
|
49
|
-
'^': {precedence: 4, associative: 'right'},
|
|
50
|
-
'*': {precedence: 3, associative: 'left'},
|
|
51
|
-
'/': {precedence: 3, associative: 'left'},
|
|
52
|
-
'+': {precedence: 2, associative: 'left'},
|
|
53
|
-
'-': {precedence: 2, associative: 'left'},
|
|
54
|
-
'%': {precedence: 3, associative: 'right'},
|
|
55
|
-
'sin': {precedence: 4, associative: 'right'},
|
|
56
|
-
'cos': {precedence: 4, associative: 'right'},
|
|
57
|
-
'
|
|
85
|
+
'^': {precedence: 4, associative: 'right', type: ShutingyardType.OPERATION},
|
|
86
|
+
'*': {precedence: 3, associative: 'left', type: ShutingyardType.OPERATION},
|
|
87
|
+
'/': {precedence: 3, associative: 'left', type: ShutingyardType.OPERATION},
|
|
88
|
+
'+': {precedence: 2, associative: 'left', type: ShutingyardType.OPERATION},
|
|
89
|
+
'-': {precedence: 2, associative: 'left', type: ShutingyardType.OPERATION},
|
|
90
|
+
'%': {precedence: 3, associative: 'right', type: ShutingyardType.OPERATION},
|
|
91
|
+
'sin': {precedence: 4, associative: 'right', type: ShutingyardType.FUNCTION},
|
|
92
|
+
'cos': {precedence: 4, associative: 'right', type: ShutingyardType.FUNCTION},
|
|
93
|
+
'tan': {precedence: 4, associative: 'right', type: ShutingyardType.FUNCTION},
|
|
58
94
|
}
|
|
59
95
|
this._uniformize = true
|
|
60
96
|
}
|
|
97
|
+
|
|
98
|
+
this._tokenKeys = Object.keys(this._tokenConfig).sort((a,b)=>b.length-a.length)
|
|
61
99
|
return this._tokenConfig
|
|
62
100
|
}
|
|
63
101
|
|
|
@@ -66,7 +104,7 @@ export class Shutingyard {
|
|
|
66
104
|
* @param expr (string) Expression to analyse
|
|
67
105
|
* @param start (number) CUrrent position in the expr string.
|
|
68
106
|
*/
|
|
69
|
-
|
|
107
|
+
NextToken(expr: string, start: number): [string, number, string] {
|
|
70
108
|
let token: string, tokenType: string;
|
|
71
109
|
token = '';
|
|
72
110
|
tokenType = '';
|
|
@@ -86,12 +124,23 @@ export class Shutingyard {
|
|
|
86
124
|
tokenType = 'function-argument';
|
|
87
125
|
} else{
|
|
88
126
|
// Order token keys by token characters length (descending)
|
|
89
|
-
|
|
127
|
+
// TODO: this is done each time ! SHould be done once !
|
|
128
|
+
// const keys = Object.keys(this._tokenConfig).sort((a,b)=>b.length-a.length)
|
|
129
|
+
|
|
130
|
+
// Extract operation and function tokens
|
|
131
|
+
for(let key of this._tokenKeys){
|
|
132
|
+
if(expr.substring(start, start+key.length) === key){
|
|
133
|
+
token += key;
|
|
134
|
+
tokenType = this._tokenConfig[key].type
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
}
|
|
90
138
|
|
|
91
|
-
|
|
92
|
-
|
|
139
|
+
// Extract constant
|
|
140
|
+
for(let key in tokenConstant){
|
|
141
|
+
if(expr.substring(start, start+key.length) === key){
|
|
93
142
|
token += key;
|
|
94
|
-
tokenType =
|
|
143
|
+
tokenType = ShutingyardType.CONSTANT
|
|
95
144
|
break
|
|
96
145
|
}
|
|
97
146
|
}
|
|
@@ -99,77 +148,24 @@ export class Shutingyard {
|
|
|
99
148
|
if(token===''){
|
|
100
149
|
// No function found ! Might be a coefficient !
|
|
101
150
|
if( expr[start].match(/[0-9]/) ) {
|
|
102
|
-
|
|
103
|
-
|
|
151
|
+
if(this._mode === ShutingyardMode.POLYNOM) {
|
|
152
|
+
token = expr.substring(start).match(/^([0-9.,/]+)/)[0]
|
|
153
|
+
}else{
|
|
154
|
+
token = expr.substring(start).match(/^([0-9.,]+)/)[0]
|
|
155
|
+
}
|
|
156
|
+
tokenType = ShutingyardType.COEFFICIENT
|
|
104
157
|
}else if (expr[start].match(/[a-zA-Z]/)) {
|
|
105
|
-
token = expr.
|
|
106
|
-
tokenType =
|
|
158
|
+
token = expr.substring(start).match(/^([a-zA-Z])/)[0]
|
|
159
|
+
tokenType = ShutingyardType.VARIABLE
|
|
107
160
|
}else{
|
|
108
161
|
console.log('Unidentified token', expr[start], expr, start)
|
|
109
162
|
token = expr[start]
|
|
110
|
-
tokenType =
|
|
163
|
+
tokenType = ShutingyardType.MONOM
|
|
111
164
|
}
|
|
112
165
|
|
|
113
166
|
}
|
|
114
167
|
}
|
|
115
168
|
|
|
116
|
-
|
|
117
|
-
// console.log(token, tokenType)
|
|
118
|
-
return [token, start + token.length, tokenType];
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
NextToken(expr: string, start: number): [string, number, string] {
|
|
122
|
-
let tokenMatch: string[], token: string, tokenType: string;
|
|
123
|
-
|
|
124
|
-
this.NextToken2(expr, start)
|
|
125
|
-
// Detect a fraction monoms or return empty array
|
|
126
|
-
tokenMatch = (expr.substr(start).match(/^[0-9/a-zA-Z^]+/g)) || [];
|
|
127
|
-
|
|
128
|
-
if (expr.substr(start, start + 3).match(/^(sin|cos|tan)/g)) {
|
|
129
|
-
token = expr.substr(start, 3)
|
|
130
|
-
tokenType = 'function'
|
|
131
|
-
} else if (tokenMatch.length > 0) {
|
|
132
|
-
token = tokenMatch[0];
|
|
133
|
-
tokenType = 'monom';
|
|
134
|
-
}
|
|
135
|
-
// It's an operation !
|
|
136
|
-
else if (expr[start].match(/[+\-*/^]/g)) {
|
|
137
|
-
token = expr[start];
|
|
138
|
-
tokenType = 'operation';
|
|
139
|
-
} else if (expr[start].match(/[&|!]/g)) {
|
|
140
|
-
token = expr[start];
|
|
141
|
-
tokenType = 'operation';
|
|
142
|
-
}
|
|
143
|
-
// It's an opening parenthese
|
|
144
|
-
else if (expr[start] === '(') {
|
|
145
|
-
token = '(';
|
|
146
|
-
tokenType = '(';
|
|
147
|
-
}
|
|
148
|
-
// It's a closing parenthese
|
|
149
|
-
else if (expr[start] === ')') {
|
|
150
|
-
token = ')';
|
|
151
|
-
tokenType = ')';
|
|
152
|
-
}
|
|
153
|
-
// It's an argument separator for a function
|
|
154
|
-
else if (expr[start] === ',') {
|
|
155
|
-
token = ',';
|
|
156
|
-
tokenType = 'function-argument';
|
|
157
|
-
}
|
|
158
|
-
// It's a monom.
|
|
159
|
-
else {
|
|
160
|
-
// TODO: Actually, negative exposant aren't supported.
|
|
161
|
-
// token = (expr.substr(start).match(/^[\da-z\^]+/g)[0])||'';
|
|
162
|
-
token = tokenMatch[0];
|
|
163
|
-
tokenType = 'monom';
|
|
164
|
-
|
|
165
|
-
if (token === '') {
|
|
166
|
-
token = expr[start];
|
|
167
|
-
tokenType = 'monom';
|
|
168
|
-
console.log('SHUTING YARD - NEXT TOKEN: error at ', start);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// console.log(token, start + token.length, tokenType);
|
|
173
169
|
return [token, start + token.length, tokenType];
|
|
174
170
|
}
|
|
175
171
|
|
|
@@ -241,12 +237,13 @@ export class Shutingyard {
|
|
|
241
237
|
}
|
|
242
238
|
|
|
243
239
|
// Get the next token and the corresponding new (ending) position
|
|
244
|
-
[token, tokenPos, tokenType] = this.
|
|
240
|
+
[token, tokenPos, tokenType] = this.NextToken(expr, tokenPos);
|
|
245
241
|
|
|
246
242
|
switch (tokenType) {
|
|
247
243
|
case 'monom':
|
|
248
244
|
case 'coefficient':
|
|
249
245
|
case 'variable':
|
|
246
|
+
case 'constant':
|
|
250
247
|
outQueue.push({
|
|
251
248
|
token,
|
|
252
249
|
tokenType
|
|
@@ -46,8 +46,17 @@ describe('Polynom tests', () => {
|
|
|
46
46
|
}
|
|
47
47
|
})
|
|
48
48
|
|
|
49
|
-
console.log(P.tex)
|
|
50
49
|
expect(P.length).to.be.equal(3)
|
|
51
50
|
expect(P.degree().value).to.be.equal(6)
|
|
52
51
|
});
|
|
52
|
+
|
|
53
|
+
it('should calculate correctly the quotient and reminder', function () {
|
|
54
|
+
let P = new Polynom('(x-3)(x^2+5x-4)+12'),
|
|
55
|
+
D = new Polynom('x-3')
|
|
56
|
+
|
|
57
|
+
let euclidian = P.euclidian(D);
|
|
58
|
+
|
|
59
|
+
expect(euclidian.quotient.tex).to.be.equal('x^{2}+5x-4')
|
|
60
|
+
expect(euclidian.reminder.tex).to.be.equal('12')
|
|
61
|
+
});
|
|
53
62
|
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {NumExp} from "../esm/maths/numexp";
|
|
3
|
+
|
|
4
|
+
describe('Numerical expression', () => { // the tests container
|
|
5
|
+
it('RPN for numerical expression', () => {
|
|
6
|
+
const RPN = new NumExp('3*x+5').rpn
|
|
7
|
+
expect(RPN.map(x => x.token)).to.have.all.members(['3', 'x', '*', '5', '+'])
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('Evaluation simple mathematical functions', () => {
|
|
11
|
+
const expr = new NumExp('sqrt(x)')
|
|
12
|
+
expect(expr.evaluate({x: 9})).to.be.equal(3)
|
|
13
|
+
})
|
|
14
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {expect} from 'chai';
|
|
2
|
-
import {Shutingyard} from "../src/maths/shutingyard";
|
|
2
|
+
import {Shutingyard, ShutingyardMode} from "../src/maths/shutingyard";
|
|
3
3
|
|
|
4
4
|
describe('Shuting yard', () => { // the tests container
|
|
5
5
|
it('RPN for polynom', () => {
|
|
@@ -28,8 +28,8 @@ describe('Shuting yard', () => { // the tests container
|
|
|
28
28
|
})
|
|
29
29
|
|
|
30
30
|
it('Custom RPN', () => {
|
|
31
|
-
const SY1: Shutingyard = new Shutingyard(
|
|
32
|
-
const SY2: Shutingyard = new Shutingyard(
|
|
31
|
+
const SY1: Shutingyard = new Shutingyard(ShutingyardMode.SET).parse('(A|B)&C');
|
|
32
|
+
const SY2: Shutingyard = new Shutingyard(ShutingyardMode.SET).parse('(A-B)&!C');
|
|
33
33
|
expect(SY1.rpn.map(x=>x.token)).to.have.all.members(['A', 'B', '|', 'C', '&'])
|
|
34
34
|
expect(SY2.rpn.map(x=>x.token)).to.have.all.members(['A', 'B', '-', 'C', '!', '&'])
|
|
35
35
|
})
|