fable 3.0.132 → 3.0.134
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/debug/Harness.js +27 -36
- package/dist/fable.compatible.js +336 -165
- package/dist/fable.compatible.min.js +2 -2
- package/dist/fable.compatible.min.js.map +1 -1
- package/dist/fable.js +277 -106
- package/dist/fable.min.js +2 -2
- package/dist/fable.min.js.map +1 -1
- package/example_applications/data/Fruit-Data.json +694 -0
- package/example_applications/data/Fruit-Manyfest.json +56 -0
- package/example_applications/mathematical_playground/AppData.json +8 -0
- package/example_applications/mathematical_playground/Equations.json +12 -0
- package/example_applications/mathematical_playground/Math-Solver-Harness.js +89 -0
- package/package.json +3 -3
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-FunctionMap.json +80 -20
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Postfix.js +218 -200
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-SolvePostfixedExpression.js +11 -1
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-TokenMap.json +1 -0
- package/source/services/Fable-Service-ExpressionParser.js +22 -3
- package/source/services/Fable-Service-Math.js +570 -19
- package/test/ExpressionParser_tests.js +20 -1
- package/test/Math_test.js +54 -3
- package/test/data/chocodata.json +248 -0
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Fable-Service-Math.js
|
|
3
|
+
* @description This file contains the implementation of the FableServiceMath class, which provides simple functions for performing arbitrary precision math operations.
|
|
4
|
+
* @module FableServiceMath
|
|
5
|
+
* @extends libFableServiceBase
|
|
6
|
+
*/
|
|
1
7
|
const libFableServiceBase = require('fable-serviceproviderbase');
|
|
2
8
|
|
|
3
9
|
/**
|
|
@@ -11,26 +17,35 @@ class FableServiceMath extends libFableServiceBase
|
|
|
11
17
|
{
|
|
12
18
|
constructor(pFable, pOptions, pServiceHash)
|
|
13
19
|
{
|
|
14
|
-
|
|
20
|
+
super(pFable, pOptions, pServiceHash);
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
}
|
|
22
|
+
this.serviceType = 'Math';
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
this.pi = '3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679';
|
|
25
|
+
}
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
/*
|
|
28
|
+
Pass-through Rounding Method Constants
|
|
29
|
+
|
|
30
|
+
Property Value BigDecimal Equiv Description
|
|
31
|
+
---------- ----- ---------------- -----------
|
|
32
|
+
roundDown 0 ROUND_DOWN Rounds towards zero. (_I.e. truncate, no rounding._)
|
|
33
|
+
roundHalfUp 1 ROUND_HALF_UP Rounds towards nearest neighbour. (_If equidistant, rounds away from zero._)
|
|
34
|
+
roundHalfEven 2 ROUND_HALF_EVEN Rounds towards nearest neighbour. (_If equidistant, rounds towards even neighbour._)
|
|
35
|
+
roundUp 3 ROUND_UP Rounds positively away from zero. (_Always round up._)
|
|
36
|
+
*/
|
|
29
37
|
get roundDown() { return this.fable.Utility.bigNumber.roundDown; }
|
|
30
38
|
get roundHalfUp() { return this.fable.Utility.bigNumber.roundHalfUp; }
|
|
31
39
|
get roundHalfEven() { return this.fable.Utility.bigNumber.roundHalfEven; }
|
|
32
40
|
get roundUp() { return this.fable.Utility.bigNumber.roundUp; }
|
|
33
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Parses a precise number value.
|
|
44
|
+
*
|
|
45
|
+
* @param {number} pValue - The value to parse.
|
|
46
|
+
* @param {any} pNonNumberValue - The value to use if parsing fails.
|
|
47
|
+
* @returns {string} - The parsed number as a string.
|
|
48
|
+
*/
|
|
34
49
|
parsePrecise(pValue, pNonNumberValue)
|
|
35
50
|
{
|
|
36
51
|
let tmpNumber;
|
|
@@ -39,15 +54,32 @@ class FableServiceMath extends libFableServiceBase
|
|
|
39
54
|
{
|
|
40
55
|
tmpNumber = new this.fable.Utility.bigNumber(pValue);
|
|
41
56
|
}
|
|
42
|
-
catch(pError)
|
|
57
|
+
catch (pError)
|
|
43
58
|
{
|
|
44
|
-
this.log.warn(`Error parsing number (type ${typeof(pValue)}): ${pError}`);
|
|
45
|
-
tmpNumber = (typeof(pNonNumberValue) === 'undefined') ? "0.0" : pNonNumberValue;
|
|
59
|
+
this.log.warn(`Error parsing number (type ${typeof (pValue)}): ${pError}`);
|
|
60
|
+
tmpNumber = (typeof (pNonNumberValue) === 'undefined') ? "0.0" : pNonNumberValue;
|
|
46
61
|
}
|
|
47
62
|
|
|
48
63
|
return tmpNumber.toString();
|
|
49
64
|
}
|
|
50
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Assigns the given value. For equals operations in the solver.
|
|
68
|
+
* @param {*} pValue - The value to be assigned.
|
|
69
|
+
* @returns {*} The assigned value.
|
|
70
|
+
*/
|
|
71
|
+
assignValue(pValue)
|
|
72
|
+
{
|
|
73
|
+
return pValue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Calculates the precise percentage of a given value compared to another value.
|
|
78
|
+
*
|
|
79
|
+
* @param {number} pIs - The value to calculate the percentage of.
|
|
80
|
+
* @param {number} pOf - The value to calculate the percentage against.
|
|
81
|
+
* @returns {string} The precise percentage as a string.
|
|
82
|
+
*/
|
|
51
83
|
percentagePrecise(pIs, pOf)
|
|
52
84
|
{
|
|
53
85
|
let tmpLeftValue = isNaN(pIs) ? 0 : pIs;
|
|
@@ -64,29 +96,51 @@ class FableServiceMath extends libFableServiceBase
|
|
|
64
96
|
return tmpResult.toString();
|
|
65
97
|
}
|
|
66
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Rounds a value to a specified number of decimal places using a specified rounding method.
|
|
101
|
+
*
|
|
102
|
+
* @param {number} pValue - The value to be rounded.
|
|
103
|
+
* @param {number} pDecimals - The number of decimal places to round to.
|
|
104
|
+
* @param {function} [pRoundingMethod] - The rounding method to use. Defaults to `this.roundHalfUp`.
|
|
105
|
+
* @returns {string} - The rounded value as a string.
|
|
106
|
+
*/
|
|
67
107
|
roundPrecise(pValue, pDecimals, pRoundingMethod)
|
|
68
108
|
{
|
|
69
109
|
let tmpValue = isNaN(pValue) ? 0 : pValue;
|
|
70
110
|
let tmpDecimals = isNaN(pDecimals) ? 0 : pDecimals;
|
|
71
|
-
let tmpRoundingMethod = (typeof(pRoundingMethod) === 'undefined') ? this.roundHalfUp : pRoundingMethod;
|
|
111
|
+
let tmpRoundingMethod = (typeof (pRoundingMethod) === 'undefined') ? this.roundHalfUp : pRoundingMethod;
|
|
72
112
|
|
|
73
113
|
let tmpArbitraryValue = new this.fable.Utility.bigNumber(tmpValue);
|
|
74
114
|
let tmpResult = tmpArbitraryValue.round(tmpDecimals, tmpRoundingMethod);
|
|
75
115
|
return tmpResult.toString();
|
|
76
116
|
}
|
|
77
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Returns a string representation of a number with a specified number of decimals.
|
|
120
|
+
*
|
|
121
|
+
* @param {number} pValue - The number to be formatted.
|
|
122
|
+
* @param {number} pDecimals - The number of decimals to include in the formatted string.
|
|
123
|
+
* @param {string} [pRoundingMethod] - The rounding method to use. Defaults to 'roundHalfUp'.
|
|
124
|
+
* @returns {string} - The formatted number as a string.
|
|
125
|
+
*/
|
|
78
126
|
toFixedPrecise(pValue, pDecimals, pRoundingMethod)
|
|
79
127
|
{
|
|
80
128
|
let tmpValue = isNaN(pValue) ? 0 : pValue;
|
|
81
129
|
let tmpDecimals = isNaN(pDecimals) ? 0 : pDecimals;
|
|
82
|
-
let tmpRoundingMethod = (typeof(pRoundingMethod) === 'undefined') ? this.roundHalfUp : pRoundingMethod;
|
|
130
|
+
let tmpRoundingMethod = (typeof (pRoundingMethod) === 'undefined') ? this.roundHalfUp : pRoundingMethod;
|
|
83
131
|
|
|
84
132
|
let tmpArbitraryValue = new this.fable.Utility.bigNumber(tmpValue);
|
|
85
133
|
let tmpResult = tmpArbitraryValue.toFixed(tmpDecimals, tmpRoundingMethod);
|
|
86
|
-
|
|
134
|
+
|
|
87
135
|
return tmpResult.toString();
|
|
88
136
|
}
|
|
89
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Adds two values precisely.
|
|
140
|
+
* @param {number} pLeftValue - The left value to be added.
|
|
141
|
+
* @param {number} pRightValue - The right value to be added.
|
|
142
|
+
* @returns {string} - The result of adding the two values as a string.
|
|
143
|
+
*/
|
|
90
144
|
addPrecise(pLeftValue, pRightValue)
|
|
91
145
|
{
|
|
92
146
|
let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
|
|
@@ -97,6 +151,13 @@ class FableServiceMath extends libFableServiceBase
|
|
|
97
151
|
return tmpResult.toString();
|
|
98
152
|
}
|
|
99
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Subtracts two values precisely.
|
|
156
|
+
*
|
|
157
|
+
* @param {number} pLeftValue - The left value to subtract.
|
|
158
|
+
* @param {number} pRightValue - The right value to subtract.
|
|
159
|
+
* @returns {string} The result of the subtraction as a string.
|
|
160
|
+
*/
|
|
100
161
|
subtractPrecise(pLeftValue, pRightValue)
|
|
101
162
|
{
|
|
102
163
|
let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
|
|
@@ -107,7 +168,13 @@ class FableServiceMath extends libFableServiceBase
|
|
|
107
168
|
return tmpResult.toString();
|
|
108
169
|
}
|
|
109
170
|
|
|
110
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Calculates the precise power of two numbers.
|
|
173
|
+
*
|
|
174
|
+
* @param {number} pLeftValue - The base value.
|
|
175
|
+
* @param {number} pRightValue - The exponent value.
|
|
176
|
+
* @returns {string} The result of raising the base value to the exponent value.
|
|
177
|
+
*/
|
|
111
178
|
powerPrecise(pLeftValue, pRightValue)
|
|
112
179
|
{
|
|
113
180
|
let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
|
|
@@ -118,6 +185,13 @@ class FableServiceMath extends libFableServiceBase
|
|
|
118
185
|
return tmpResult.toString();
|
|
119
186
|
}
|
|
120
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Multiplies two values precisely.
|
|
190
|
+
*
|
|
191
|
+
* @param {number} pLeftValue - The left value to multiply.
|
|
192
|
+
* @param {number} pRightValue - The right value to multiply.
|
|
193
|
+
* @returns {string} The result of the multiplication as a string.
|
|
194
|
+
*/
|
|
121
195
|
multiplyPrecise(pLeftValue, pRightValue)
|
|
122
196
|
{
|
|
123
197
|
let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
|
|
@@ -128,6 +202,13 @@ class FableServiceMath extends libFableServiceBase
|
|
|
128
202
|
return tmpResult.toString();
|
|
129
203
|
}
|
|
130
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Divides two values precisely.
|
|
207
|
+
*
|
|
208
|
+
* @param {number} pLeftValue - The left value to be divided.
|
|
209
|
+
* @param {number} pRightValue - The right value to divide by.
|
|
210
|
+
* @returns {string} The result of the division as a string.
|
|
211
|
+
*/
|
|
131
212
|
dividePrecise(pLeftValue, pRightValue)
|
|
132
213
|
{
|
|
133
214
|
let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
|
|
@@ -138,6 +219,13 @@ class FableServiceMath extends libFableServiceBase
|
|
|
138
219
|
return tmpResult.toString();
|
|
139
220
|
}
|
|
140
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Calculates the modulus of two values with precision.
|
|
224
|
+
*
|
|
225
|
+
* @param {number} pLeftValue - The left value.
|
|
226
|
+
* @param {number} pRightValue - The right value.
|
|
227
|
+
* @returns {string} The result of the modulus operation as a string.
|
|
228
|
+
*/
|
|
141
229
|
modPrecise(pLeftValue, pRightValue)
|
|
142
230
|
{
|
|
143
231
|
let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
|
|
@@ -148,6 +236,12 @@ class FableServiceMath extends libFableServiceBase
|
|
|
148
236
|
return tmpResult.toString();
|
|
149
237
|
}
|
|
150
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Calculates the square root of a number with precise decimal places.
|
|
241
|
+
*
|
|
242
|
+
* @param {number} pValue - The number to calculate the square root of.
|
|
243
|
+
* @returns {string} The square root of the input number as a string.
|
|
244
|
+
*/
|
|
151
245
|
sqrtPrecise(pValue)
|
|
152
246
|
{
|
|
153
247
|
let tmpValue = isNaN(pValue) ? 0 : pValue;
|
|
@@ -157,6 +251,12 @@ class FableServiceMath extends libFableServiceBase
|
|
|
157
251
|
return tmpResult.toString();
|
|
158
252
|
}
|
|
159
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Calculates the absolute value of a number precisely.
|
|
256
|
+
*
|
|
257
|
+
* @param {number} pValue - The number to calculate the absolute value of.
|
|
258
|
+
* @returns {string} The absolute value of the input number as a string.
|
|
259
|
+
*/
|
|
160
260
|
absPrecise(pValue)
|
|
161
261
|
{
|
|
162
262
|
let tmpValue = isNaN(pValue) ? 0 : pValue;
|
|
@@ -166,6 +266,13 @@ class FableServiceMath extends libFableServiceBase
|
|
|
166
266
|
return tmpResult.toString();
|
|
167
267
|
}
|
|
168
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Compares two values precisely.
|
|
271
|
+
*
|
|
272
|
+
* @param {number} pLeftValue - The left value to compare.
|
|
273
|
+
* @param {number} pRightValue - The right value to compare.
|
|
274
|
+
* @returns {number} - Returns the result of the comparison.
|
|
275
|
+
*/
|
|
169
276
|
comparePrecise(pLeftValue, pRightValue)
|
|
170
277
|
{
|
|
171
278
|
let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
|
|
@@ -175,6 +282,13 @@ class FableServiceMath extends libFableServiceBase
|
|
|
175
282
|
return tmpLeftArbitraryValue.cmp(tmpRightValue);
|
|
176
283
|
}
|
|
177
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Determines if the left value is greater than the right value precisely.
|
|
287
|
+
*
|
|
288
|
+
* @param {number} pLeftValue - The left value to compare.
|
|
289
|
+
* @param {number} pRightValue - The right value to compare.
|
|
290
|
+
* @returns {boolean} - Returns true if the left value is greater than the right value, otherwise returns false.
|
|
291
|
+
*/
|
|
178
292
|
gtPrecise(pLeftValue, pRightValue)
|
|
179
293
|
{
|
|
180
294
|
let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
|
|
@@ -184,6 +298,14 @@ class FableServiceMath extends libFableServiceBase
|
|
|
184
298
|
return tmpLeftArbitraryValue.gt(tmpRightValue);
|
|
185
299
|
}
|
|
186
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Checks if the left value is greater than or equal to the right value.
|
|
303
|
+
* If either value is not a number, it is treated as 0.
|
|
304
|
+
*
|
|
305
|
+
* @param {number} pLeftValue - The left value to compare.
|
|
306
|
+
* @param {number} pRightValue - The right value to compare.
|
|
307
|
+
* @returns {boolean} - True if the left value is greater than or equal to the right value, false otherwise.
|
|
308
|
+
*/
|
|
187
309
|
gtePrecise(pLeftValue, pRightValue)
|
|
188
310
|
{
|
|
189
311
|
let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
|
|
@@ -193,6 +315,13 @@ class FableServiceMath extends libFableServiceBase
|
|
|
193
315
|
return tmpLeftArbitraryValue.gte(tmpRightValue);
|
|
194
316
|
}
|
|
195
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Determines if the left value is less than the right value precisely.
|
|
320
|
+
*
|
|
321
|
+
* @param {number} pLeftValue - The left value to compare.
|
|
322
|
+
* @param {number} pRightValue - The right value to compare.
|
|
323
|
+
* @returns {boolean} - Returns true if the left value is less than the right value, otherwise returns false.
|
|
324
|
+
*/
|
|
196
325
|
ltPrecise(pLeftValue, pRightValue)
|
|
197
326
|
{
|
|
198
327
|
let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
|
|
@@ -202,6 +331,13 @@ class FableServiceMath extends libFableServiceBase
|
|
|
202
331
|
return tmpLeftArbitraryValue.lt(tmpRightValue);
|
|
203
332
|
}
|
|
204
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Determines if the left value is less than or equal to the right value.
|
|
336
|
+
*
|
|
337
|
+
* @param {number} pLeftValue - The left value to compare.
|
|
338
|
+
* @param {number} pRightValue - The right value to compare.
|
|
339
|
+
* @returns {boolean} - Returns true if the left value is less than or equal to the right value, otherwise returns false.
|
|
340
|
+
*/
|
|
205
341
|
ltePrecise(pLeftValue, pRightValue)
|
|
206
342
|
{
|
|
207
343
|
let tmpLeftValue = isNaN(pLeftValue) ? 0 : pLeftValue;
|
|
@@ -211,6 +347,12 @@ class FableServiceMath extends libFableServiceBase
|
|
|
211
347
|
return tmpLeftArbitraryValue.lt(tmpRightValue);
|
|
212
348
|
}
|
|
213
349
|
|
|
350
|
+
/**
|
|
351
|
+
* Converts degrees to radians with arbitrary precision.
|
|
352
|
+
*
|
|
353
|
+
* @param {number} pDegrees - The degrees to convert to radians.
|
|
354
|
+
* @returns {string} - The converted radians as a string.
|
|
355
|
+
*/
|
|
214
356
|
radPrecise(pDegrees)
|
|
215
357
|
{
|
|
216
358
|
let tmpDegrees = isNaN(pDegrees) ? 0 : pDegrees;
|
|
@@ -221,23 +363,432 @@ class FableServiceMath extends libFableServiceBase
|
|
|
221
363
|
return tmpResult.toString();
|
|
222
364
|
}
|
|
223
365
|
|
|
366
|
+
/**
|
|
367
|
+
* Calculates the value of pi with the specified precision.
|
|
368
|
+
* If no precision is provided, returns 100 digits after the decimal.
|
|
369
|
+
*
|
|
370
|
+
* @param {number} [pPrecision] - The precision to use for calculating pi.
|
|
371
|
+
* @returns {number} - The calculated value of pi.
|
|
372
|
+
*/
|
|
373
|
+
piPrecise(pPrecision)
|
|
374
|
+
{
|
|
375
|
+
if (typeof (pPrecision) === 'undefined')
|
|
376
|
+
{
|
|
377
|
+
return this.pi;
|
|
378
|
+
}
|
|
379
|
+
else
|
|
380
|
+
{
|
|
381
|
+
return this.roundPrecise(this.pi, pPrecision);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Calculates the sine of the given angle in radians.
|
|
387
|
+
*
|
|
388
|
+
* @param {number} pRadians - The angle in radians.
|
|
389
|
+
* @returns {number} The sine of the angle.
|
|
390
|
+
*/
|
|
224
391
|
sin(pRadians)
|
|
225
392
|
{
|
|
226
393
|
let tmpRadians = isNaN(pRadians) ? 0 : pRadians;
|
|
227
394
|
return Math.sin(tmpRadians);
|
|
228
395
|
}
|
|
229
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Calculates the cosine of the given angle in radians.
|
|
399
|
+
*
|
|
400
|
+
* @param {number} pRadians - The angle in radians.
|
|
401
|
+
* @returns {number} The cosine of the angle.
|
|
402
|
+
*/
|
|
230
403
|
cos(pRadians)
|
|
231
404
|
{
|
|
232
405
|
let tmpRadians = isNaN(pRadians) ? 0 : pRadians;
|
|
233
406
|
return Math.cos(tmpRadians);
|
|
234
407
|
}
|
|
235
408
|
|
|
409
|
+
/**
|
|
410
|
+
* Calculates the tangent of an angle in radians.
|
|
411
|
+
*
|
|
412
|
+
* @param {number} pRadians - The angle in radians.
|
|
413
|
+
* @returns {number} The tangent of the angle.
|
|
414
|
+
*/
|
|
236
415
|
tan(pRadians)
|
|
237
416
|
{
|
|
238
417
|
let tmpRadians = isNaN(pRadians) ? 0 : pRadians;
|
|
239
418
|
return Math.tan(tmpRadians);
|
|
240
419
|
}
|
|
420
|
+
|
|
421
|
+
/* * * * * * * * * * * * * * * *
|
|
422
|
+
* Set functions
|
|
423
|
+
* These are meant to work fine with arrays and more complex set descriptions returned by Manyfest.
|
|
424
|
+
* Manyfest sometimes returns values as arrays and sometimes as a map of addresses with values depending
|
|
425
|
+
* on what was requested.
|
|
426
|
+
*
|
|
427
|
+
* The following functions will likely be broken into their own service.
|
|
428
|
+
*/
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Counts the number of elements in a set.
|
|
432
|
+
*
|
|
433
|
+
* @param {Array|Object|any} pValueSet - The set to count the elements of.
|
|
434
|
+
* @returns {number} The number of elements in the set.
|
|
435
|
+
*/
|
|
436
|
+
countSetElements(pValueSet)
|
|
437
|
+
{
|
|
438
|
+
if (Array.isArray(pValueSet))
|
|
439
|
+
{
|
|
440
|
+
return pValueSet.length;
|
|
441
|
+
}
|
|
442
|
+
else if (typeof (pValueSet) === 'object')
|
|
443
|
+
{
|
|
444
|
+
return Object.keys(pValueSet).length;
|
|
445
|
+
}
|
|
446
|
+
else if (pValueSet)
|
|
447
|
+
{
|
|
448
|
+
// This is controversial. Discuss with colleagues!
|
|
449
|
+
return 1;
|
|
450
|
+
}
|
|
451
|
+
return 0;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Sorts the elements in the given value set in ascending order using the precise parsing and comparison.
|
|
456
|
+
*
|
|
457
|
+
* @param {Array|Object} pValueSet - The value set to be sorted.
|
|
458
|
+
* @returns {Array} - The sorted value set.
|
|
459
|
+
*/
|
|
460
|
+
sortSetPrecise(pValueSet)
|
|
461
|
+
{
|
|
462
|
+
let tmpSortedSet = [];
|
|
463
|
+
if (Array.isArray(pValueSet))
|
|
464
|
+
{
|
|
465
|
+
for (let i = 0; i < pValueSet.length; i++)
|
|
466
|
+
{
|
|
467
|
+
tmpSortedSet.push(this.parsePrecise(pValueSet[i], NaN));
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
else if (typeof (pValueSet) === 'object')
|
|
471
|
+
{
|
|
472
|
+
let tmpKeys = Object.keys(pValueSet);
|
|
473
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
474
|
+
{
|
|
475
|
+
tmpSortedSet.push(this.parsePrecise(pValueSet[tmpKeys[i]], NaN));
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
tmpSortedSet.sort((pLeft, pRight) => { return this.comparePrecise(pLeft, pRight); });
|
|
480
|
+
|
|
481
|
+
return tmpSortedSet;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Bucketizes a set of values based on a specified bucket size.
|
|
486
|
+
*
|
|
487
|
+
* @param {Array|Object} pValueSet - The set of values to be bucketized.
|
|
488
|
+
* @param {number} pBucketSize - The size of each bucket. Optional - If NaN, the values will be bucketized by their value.
|
|
489
|
+
* @returns {Object} - The bucketized set of values.
|
|
490
|
+
*/
|
|
491
|
+
bucketSetPrecise(pValueSet, pBucketSize)
|
|
492
|
+
{
|
|
493
|
+
let tmpBucketedSet = {};
|
|
494
|
+
let tmpBucketSize = this.parsePrecise(pBucketSize, NaN);
|
|
495
|
+
|
|
496
|
+
if (Array.isArray(pValueSet))
|
|
497
|
+
{
|
|
498
|
+
for (let i = 0; i < pValueSet.length; i++)
|
|
499
|
+
{
|
|
500
|
+
let tmpValue = this.parsePrecise(pValueSet[i], NaN);
|
|
501
|
+
let tmpBucket = tmpValue.toString();
|
|
502
|
+
if (!isNaN(tmpBucketSize))
|
|
503
|
+
{
|
|
504
|
+
tmpBucket = this.dividePrecise(pValueSet[i], tmpBucketSize);
|
|
505
|
+
}
|
|
506
|
+
if (!(tmpBucket in tmpBucketedSet))
|
|
507
|
+
{
|
|
508
|
+
tmpBucketedSet[tmpBucket] = 0;
|
|
509
|
+
}
|
|
510
|
+
tmpBucketedSet[tmpBucket] = tmpBucketedSet[tmpBucket] + 1;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
else if (typeof (pValueSet) === 'object')
|
|
514
|
+
{
|
|
515
|
+
let tmpKeys = Object.keys(pValueSet);
|
|
516
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
517
|
+
{
|
|
518
|
+
let tmpValue = this.parsePrecise(pValueSet[tmpKeys[i]], NaN);
|
|
519
|
+
let tmpBucket = tmpValue.toString();
|
|
520
|
+
if (!isNaN(tmpBucketSize))
|
|
521
|
+
{
|
|
522
|
+
tmpBucket = this.dividePrecise(pValueSet[i], tmpBucketSize);
|
|
523
|
+
}
|
|
524
|
+
if (!(tmpBucket in tmpBucketedSet))
|
|
525
|
+
{
|
|
526
|
+
tmpBucketedSet[tmpBucket] = 0;
|
|
527
|
+
}
|
|
528
|
+
tmpBucketedSet[tmpBucket] = tmpBucketedSet[tmpBucket] + 1;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return tmpBucketedSet;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Calculates the histogram using precise bucket set for the given pValueSet.
|
|
537
|
+
*
|
|
538
|
+
* @param {Array<number>} pValueSet - The array of p-values.
|
|
539
|
+
* @returns {Array<number>} The histogram of the p-values.
|
|
540
|
+
*/
|
|
541
|
+
histogramPrecise(pValueSet)
|
|
542
|
+
{
|
|
543
|
+
return this.bucketSetPrecise(pValueSet);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Sorts the histogram object in ascending order based on the frequencies of the buckets.
|
|
548
|
+
*
|
|
549
|
+
* @param {Object} pHistogram - The histogram object to be sorted.
|
|
550
|
+
* @returns {Object} - The sorted histogram object.
|
|
551
|
+
*/
|
|
552
|
+
sortHistogramPrecise(pHistogram)
|
|
553
|
+
{
|
|
554
|
+
let tmpSortedHistogram = {};
|
|
555
|
+
let tmpKeys = Object.keys(pHistogram);
|
|
556
|
+
|
|
557
|
+
tmpKeys.sort((pLeft, pRight) => { return pHistogram[pLeft] - pHistogram[pRight]; });
|
|
558
|
+
|
|
559
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
560
|
+
{
|
|
561
|
+
tmpSortedHistogram[tmpKeys[i]] = pHistogram[tmpKeys[i]];
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return tmpSortedHistogram;
|
|
565
|
+
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Finds the maximum value from a set of precise values.
|
|
570
|
+
*
|
|
571
|
+
* @param {Array|Object} pValueSet - The set of values to find the maximum from.
|
|
572
|
+
* @returns {number} - The maximum value from the set.
|
|
573
|
+
*/
|
|
574
|
+
maxPrecise(pValueSet)
|
|
575
|
+
{
|
|
576
|
+
let tmpMaxValue = NaN;
|
|
577
|
+
if (Array.isArray(pValueSet))
|
|
578
|
+
{
|
|
579
|
+
for (let i = 0; i < pValueSet.length; i++)
|
|
580
|
+
{
|
|
581
|
+
if (!tmpMaxValue)
|
|
582
|
+
{
|
|
583
|
+
tmpMaxValue = this.parsePrecise(pValueSet[i], NaN);
|
|
584
|
+
}
|
|
585
|
+
else
|
|
586
|
+
{
|
|
587
|
+
let tmpComparisonValue = this.parsePrecise(pValueSet[i], NaN);
|
|
588
|
+
if (this.gtPrecise(tmpComparisonValue, tmpMaxValue))
|
|
589
|
+
{
|
|
590
|
+
tmpMaxValue = tmpComparisonValue;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
else if (typeof (pValueSet) === 'object')
|
|
596
|
+
{
|
|
597
|
+
let tmpKeys = Object.keys(pValueSet);
|
|
598
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
599
|
+
{
|
|
600
|
+
if (!tmpMaxValue)
|
|
601
|
+
{
|
|
602
|
+
tmpMaxValue = this.parsePrecise(pValueSet[tmpKeys[i]], NaN);
|
|
603
|
+
}
|
|
604
|
+
else
|
|
605
|
+
{
|
|
606
|
+
let tmpComparisonValue = this.parsePrecise(pValueSet[tmpKeys[i]], NaN);
|
|
607
|
+
if (this.gtPrecise(tmpComparisonValue, tmpMaxValue))
|
|
608
|
+
{
|
|
609
|
+
tmpMaxValue = tmpComparisonValue;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return tmpMaxValue;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Finds the minimum value from a set of values.
|
|
619
|
+
*
|
|
620
|
+
* @param {Array|Object} pValueSet - The set of values to find the minimum from.
|
|
621
|
+
* @returns {number} The minimum value from the set.
|
|
622
|
+
*/
|
|
623
|
+
minPrecise(pValueSet)
|
|
624
|
+
{
|
|
625
|
+
let tmpMinValue = NaN;
|
|
626
|
+
if (Array.isArray(pValueSet))
|
|
627
|
+
{
|
|
628
|
+
for (let i = 0; i < pValueSet.length; i++)
|
|
629
|
+
{
|
|
630
|
+
if (!tmpMinValue)
|
|
631
|
+
{
|
|
632
|
+
tmpMinValue = this.parsePrecise(pValueSet[i], NaN);
|
|
633
|
+
}
|
|
634
|
+
else
|
|
635
|
+
{
|
|
636
|
+
let tmpComparisonValue = this.parsePrecise(pValueSet[i], NaN);
|
|
637
|
+
if (!isNaN(tmpComparisonValue) && this.ltPrecise(tmpComparisonValue, tmpMinValue))
|
|
638
|
+
{
|
|
639
|
+
tmpMinValue = tmpComparisonValue;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
else if (typeof (pValueSet) === 'object')
|
|
645
|
+
{
|
|
646
|
+
let tmpKeys = Object.keys(pValueSet);
|
|
647
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
648
|
+
{
|
|
649
|
+
if (!tmpMinValue)
|
|
650
|
+
{
|
|
651
|
+
tmpMinValue = this.parsePrecise(pValueSet[tmpKeys[i]], NaN);
|
|
652
|
+
}
|
|
653
|
+
else
|
|
654
|
+
{
|
|
655
|
+
let tmpComparisonValue = this.parsePrecise(pValueSet[tmpKeys[i]], NaN);
|
|
656
|
+
if (!isNaN(tmpComparisonValue) && this.ltPrecise(tmpComparisonValue, tmpMinValue))
|
|
657
|
+
{
|
|
658
|
+
tmpMinValue = tmpComparisonValue;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return tmpMinValue;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Calculates the precise sum of values in the given value set.
|
|
668
|
+
*
|
|
669
|
+
* @param {Array|Object} pValueSet - The value set to calculate the sum from.
|
|
670
|
+
* @returns {string} The precise sum value as a string.
|
|
671
|
+
*/
|
|
672
|
+
sumPrecise(pValueSet)
|
|
673
|
+
{
|
|
674
|
+
let tmpSumValue = "0.0";
|
|
675
|
+
if (Array.isArray(pValueSet))
|
|
676
|
+
{
|
|
677
|
+
for (let i = 0; i < pValueSet.length; i++)
|
|
678
|
+
{
|
|
679
|
+
let tmpComparisonValue = this.parsePrecise(pValueSet[i], NaN);
|
|
680
|
+
if (!isNaN(tmpComparisonValue))
|
|
681
|
+
{
|
|
682
|
+
tmpSumValue = this.addPrecise(tmpSumValue, tmpComparisonValue);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
else if (typeof (pValueSet) === 'object')
|
|
687
|
+
{
|
|
688
|
+
let tmpKeys = Object.keys(pValueSet);
|
|
689
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
690
|
+
{
|
|
691
|
+
let tmpComparisonValue = this.parsePrecise(pValueSet[tmpKeys[i]], NaN);
|
|
692
|
+
if (!isNaN(tmpComparisonValue))
|
|
693
|
+
{
|
|
694
|
+
tmpSumValue = this.addPrecise(tmpSumValue, tmpComparisonValue);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
return tmpSumValue;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Calculates the precise mean of a given value set.
|
|
703
|
+
*
|
|
704
|
+
* @param {Array<number>} pValueSet - The array of values to calculate the mean.
|
|
705
|
+
* @returns {string} The precise mean value as a string.
|
|
706
|
+
*/
|
|
707
|
+
meanPrecise(pValueSet)
|
|
708
|
+
{
|
|
709
|
+
let tmpSumValue = this.sumPrecise(pValueSet);
|
|
710
|
+
let tmpCount = this.countSetElements(pValueSet);
|
|
711
|
+
if (tmpCount == 0)
|
|
712
|
+
{
|
|
713
|
+
return '0.0';
|
|
714
|
+
}
|
|
715
|
+
return this.dividePrecise(tmpSumValue, tmpCount);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Calculates the average of an array of values precisely.
|
|
720
|
+
*
|
|
721
|
+
* @param {Array<number>} pValueSet - The array of values to calculate the average of.
|
|
722
|
+
* @returns {number} The precise average of the values.
|
|
723
|
+
*/
|
|
724
|
+
averagePrecise(pValueSet)
|
|
725
|
+
{
|
|
726
|
+
return this.meanPrecise(pValueSet);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Calculates the precise median value of a given value set.
|
|
731
|
+
*
|
|
732
|
+
* @param {Array<number>} pValueSet - The array of values to calculate the median from.
|
|
733
|
+
* @returns {number|string} - The median value of the given value set. If the value set is empty, returns '0.0'.
|
|
734
|
+
*/
|
|
735
|
+
medianPrecise(pValueSet)
|
|
736
|
+
{
|
|
737
|
+
let tmpCount = this.countSetElements(pValueSet);
|
|
738
|
+
// If there are no elements, return 0 ... should this be NaN?
|
|
739
|
+
if (tmpCount == 0)
|
|
740
|
+
{
|
|
741
|
+
return '0.0';
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
let tmpSortedValueSet = this.sortSetPrecise(pValueSet);
|
|
745
|
+
let tmpMiddleElement = Math.floor(tmpCount / 2);
|
|
746
|
+
|
|
747
|
+
// If the count is odd, return the middle element
|
|
748
|
+
if (tmpCount % 2 == 1)
|
|
749
|
+
{
|
|
750
|
+
return tmpSortedValueSet[tmpMiddleElement];
|
|
751
|
+
}
|
|
752
|
+
// If the count is even, return the average of the two middle elements
|
|
753
|
+
else
|
|
754
|
+
{
|
|
755
|
+
let tmpLeftMiddleValue = tmpSortedValueSet[tmpMiddleElement - 1];
|
|
756
|
+
let tmpRightMiddleValue = tmpSortedValueSet[tmpMiddleElement];
|
|
757
|
+
return this.dividePrecise(this.addPrecise(tmpLeftMiddleValue, tmpRightMiddleValue), 2);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Calculates the mode (most frequently occurring value) of a given value set using precise mode calculation.
|
|
763
|
+
*
|
|
764
|
+
* @param {Array} pValueSet - The array of values to calculate the mode from.
|
|
765
|
+
* @returns {Array} - An array containing the mode value(s) from the given value set.
|
|
766
|
+
*/
|
|
767
|
+
modePrecise(pValueSet)
|
|
768
|
+
{
|
|
769
|
+
let tmpHistogram = this.bucketSetPrecise(pValueSet);
|
|
770
|
+
let tmpMaxCount = 0;
|
|
771
|
+
|
|
772
|
+
// Philosophical question about whether the values should be returned sorted.
|
|
773
|
+
let tmpHistogramValueSet = Object.keys(tmpHistogram);
|
|
774
|
+
|
|
775
|
+
let tmpModeValueSet = [];
|
|
776
|
+
|
|
777
|
+
for (let i = 0; i < tmpHistogramValueSet.length; i++)
|
|
778
|
+
{
|
|
779
|
+
if (tmpHistogram[tmpHistogramValueSet[i]] > tmpMaxCount)
|
|
780
|
+
{
|
|
781
|
+
tmpMaxCount = tmpHistogram[tmpHistogramValueSet[i]];
|
|
782
|
+
tmpModeValueSet = [tmpHistogramValueSet[i]];
|
|
783
|
+
}
|
|
784
|
+
else if (tmpHistogram[tmpHistogramValueSet[i]] == tmpMaxCount)
|
|
785
|
+
{
|
|
786
|
+
tmpModeValueSet.push(tmpHistogramValueSet[i]);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
return tmpModeValueSet;
|
|
791
|
+
}
|
|
241
792
|
}
|
|
242
793
|
|
|
243
794
|
module.exports = FableServiceMath;
|