@oliasoft-open-source/units 5.5.2-beta-2 → 5.6.0-beta-4
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/{units2.cjs → chunks/conversion.cjs} +1711 -1998
- package/dist/chunks/conversion.cjs.map +1 -0
- package/dist/{units2.js → chunks/conversion.js} +1710 -1973
- package/dist/chunks/conversion.js.map +1 -0
- package/dist/{interfaces2.d.ts → chunks/types.d.cts} +2 -2
- package/dist/chunks/types.d.cts.map +1 -0
- package/dist/{interfaces2.d.cts → chunks/types.d.ts} +2 -2
- package/dist/chunks/types.d.ts.map +1 -0
- package/dist/chunks/units.cjs +55 -0
- package/dist/chunks/units.cjs.map +1 -0
- package/dist/chunks/units.d.cts +47 -0
- package/dist/chunks/units.d.cts.map +1 -0
- package/dist/chunks/units.d.ts +47 -0
- package/dist/chunks/units.d.ts.map +1 -0
- package/dist/chunks/units.js +38 -0
- package/dist/chunks/units.js.map +1 -0
- package/dist/index.cjs +91 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -20
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +25 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -3
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.cts +2 -2
- package/dist/interfaces.d.ts +2 -2
- package/dist/numbers/numbers.cjs +5 -5
- package/dist/numbers/numbers.d.cts.map +1 -1
- package/dist/numbers/numbers.d.ts.map +1 -1
- package/dist/numbers/numbers.js +1 -1
- package/dist/units.cjs +27 -26
- package/dist/units.d.cts +2 -34
- package/dist/units.d.ts +2 -34
- package/dist/units.js +2 -1
- package/package.json +6 -6
- package/dist/interfaces2.d.cts.map +0 -1
- package/dist/interfaces2.d.ts.map +0 -1
- package/dist/units.d.cts.map +0 -1
- package/dist/units.d.ts.map +0 -1
- package/dist/units2.cjs.map +0 -1
- package/dist/units2.js.map +0 -1
|
@@ -23,1165 +23,281 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
//#endregion
|
|
24
24
|
let fraction_js = require("fraction.js");
|
|
25
25
|
fraction_js = __toESM(fraction_js, 1);
|
|
26
|
-
//#region src/
|
|
27
|
-
const
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
//#region src/numbers/statistics/confidence-interval.ts
|
|
27
|
+
const { abs, exp, sqrt } = Math;
|
|
28
|
+
/** Applies the one-dimensional iterative Newton method. */
|
|
29
|
+
function newton(functionValue, derivative, initialValue, tolerance = 1e-8, maxIterations = 30) {
|
|
30
|
+
let currentValue = initialValue;
|
|
31
|
+
let iteration = 0;
|
|
32
|
+
let error = 1;
|
|
33
|
+
while (error > tolerance && iteration < maxIterations) {
|
|
34
|
+
const nextValue = currentValue - functionValue(currentValue) / derivative(currentValue);
|
|
35
|
+
error = abs(functionValue(nextValue) - functionValue(currentValue));
|
|
36
|
+
if (error < tolerance) return [
|
|
37
|
+
nextValue,
|
|
38
|
+
true,
|
|
39
|
+
iteration
|
|
40
|
+
];
|
|
41
|
+
currentValue = nextValue;
|
|
42
|
+
iteration += 1;
|
|
39
43
|
}
|
|
40
|
-
return
|
|
44
|
+
return [
|
|
45
|
+
initialValue,
|
|
46
|
+
false,
|
|
47
|
+
iteration
|
|
48
|
+
];
|
|
41
49
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* isValidNum('1 1/2') -> true
|
|
55
|
-
* toNum('foobar|m') -> false
|
|
56
|
-
*/
|
|
57
|
-
const isValidNum = (value) => {
|
|
58
|
-
const parsedValue = parseValue(value);
|
|
59
|
-
if (isEmptyString(parsedValue) || Number.isNaN(parsedValue) || parsedValue === Infinity || parsedValue === -Infinity) return true;
|
|
60
|
-
else if (!(isNull(parsedValue) || isUndefined(parsedValue) || isTrailingPeriodSeparator(parsedValue) || isTrailingCommaSeparator(parsedValue) || isArray(parsedValue) || isObject(parsedValue))) {
|
|
61
|
-
const cleanedValue = cleanNumStr(String(parsedValue));
|
|
62
|
-
if (cleanedValue.includes("|")) return false;
|
|
63
|
-
const number = isFraction(cleanedValue) ? numFraction(cleanedValue) : isNumeric(cleanedValue) ? parseFloat(cleanedValue) : cleanedValue;
|
|
64
|
-
if (number === Infinity || number === -Infinity) return true;
|
|
65
|
-
if (!isNumeric(number)) return false;
|
|
66
|
-
if (!Number.isNaN(number)) return true;
|
|
67
|
-
}
|
|
68
|
-
return false;
|
|
69
|
-
};
|
|
70
|
-
/**
|
|
71
|
-
* Checks whether a value is a valid number, in string representation with scientific notation (e.g. '1e3')
|
|
72
|
-
*
|
|
73
|
-
* Note - it's not possible to check whether *number* types are stored in scientific notation (all numbers are stored
|
|
74
|
-
* the same way internally in floating-point formats, so there is no difference between 1000 and 1e3 internally).
|
|
75
|
-
* Whether number types get displayed in scientific notation or not in the console is a browser-specific implementation
|
|
76
|
-
* detail (display formatting) and not something we can check/rely on, so this function is only intended for checking
|
|
77
|
-
* user-input values in string format. See https://stackoverflow.com/a/66005705/942635.
|
|
78
|
-
*
|
|
79
|
-
* @param value - value to be checked
|
|
80
|
-
* @returns whether the value is a valid number in scientific notation
|
|
81
|
-
*
|
|
82
|
-
* @example
|
|
83
|
-
* isValidNum('1e3') -> true
|
|
84
|
-
* toNum(1000) -> false
|
|
85
|
-
* toNum(1e3) -> false (we cannot check scientific notation of number types)
|
|
86
|
-
*/
|
|
87
|
-
const isScientificStringNum = (value) => {
|
|
88
|
-
if (typeof value === "string") return isValidNum(value) && value.toLowerCase().includes("e");
|
|
89
|
-
return false;
|
|
90
|
-
};
|
|
91
|
-
/**
|
|
92
|
-
* Converts a numeric value to number type (when possible).
|
|
93
|
-
* - need to know if it's possible first? Call isValidNum()
|
|
94
|
-
* - accepts number types (1.234), stringified numbers ('1.234'), fractions ('1/2'), and unit numbers ('1.234|m')
|
|
95
|
-
* - returns the converted number if possible, otherwise returns the input value or default value when provided
|
|
96
|
-
*
|
|
97
|
-
* @param value - value to be converted to number type
|
|
98
|
-
* @param [fallback] - optional fallback value (returned when not possible to convert)
|
|
99
|
-
* @param [minimum] - optional minimum value
|
|
100
|
-
* @returns valid number after conversion, or fallback, or returns the original input
|
|
101
|
-
*
|
|
102
|
-
* @example
|
|
103
|
-
* toNum('1.2345) -> 1.2345
|
|
104
|
-
* toNum('1.2345|m') -> 1.2345
|
|
105
|
-
*/
|
|
106
|
-
const toNum = (value, fallback, minimum) => {
|
|
107
|
-
const fallbackResult = fallback ?? value;
|
|
108
|
-
const parsedValue = parseValue(value);
|
|
109
|
-
if (!isValidNum(parsedValue)) return fallbackResult;
|
|
110
|
-
else {
|
|
111
|
-
const cleanedValue = cleanNumStr(String(parsedValue));
|
|
112
|
-
const number = isFraction(cleanedValue) ? numFraction(cleanedValue) : isNumeric(cleanedValue) ? parseFloat(cleanedValue) : cleanedValue;
|
|
113
|
-
if (number === Infinity || number === -Infinity) return number;
|
|
114
|
-
if (Number.isNaN(number) || !isNumeric(number)) return fallbackResult;
|
|
115
|
-
else if (minimum && number < minimum) return minimum;
|
|
116
|
-
return number;
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
/**
|
|
120
|
-
* Convert a number to a string safely, better than String(value)
|
|
121
|
-
* String(0.0000002) returns '2e-7' which is unwanted if we need to preserve formatting
|
|
122
|
-
*
|
|
123
|
-
* @param value
|
|
124
|
-
* @returns number or string output value
|
|
125
|
-
*/
|
|
126
|
-
const toString = (value) => {
|
|
127
|
-
if (isValidNum(value)) {
|
|
128
|
-
if (typeof value === "string") return value;
|
|
129
|
-
if (typeof value === "number") {
|
|
130
|
-
if (Number.isNaN(value) || !Number.isFinite(value)) return String(value);
|
|
131
|
-
return formatDecimalDisplayNumber(value, { noThousandsSeparator: true });
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return value;
|
|
135
|
-
};
|
|
136
|
-
//#endregion
|
|
137
|
-
//#region src/parse/parse-number.ts
|
|
138
|
-
const countTrailingZeros = (value, decimalPartOnly = false) => {
|
|
139
|
-
const condition = decimalPartOnly ? /0+((?=[|eE])|$)/ : /(0+|0+\.0+)((?=[|eE])|$)/;
|
|
140
|
-
if (typeof value === "string" && (decimalPartOnly ? value.includes(".") || value.includes(",") : true)) return value?.match(condition)?.[0]?.replaceAll(/[.,]/g, "")?.length ?? 0;
|
|
141
|
-
return 0;
|
|
142
|
-
};
|
|
143
|
-
const hasTrailingZeros = (value) => {
|
|
144
|
-
return countTrailingZeros(value) > 0;
|
|
145
|
-
};
|
|
146
|
-
/**
|
|
147
|
-
* Internal function to parse the value, unit, and type from a generic numeric input
|
|
148
|
-
*
|
|
149
|
-
* Accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
150
|
-
*
|
|
151
|
-
* @param value
|
|
152
|
-
* @returns object with number, unit, and type
|
|
153
|
-
*/
|
|
154
|
-
const parseNumber = (value, preserveTrailingZeros = false) => {
|
|
155
|
-
const isString = typeof value === "string";
|
|
156
|
-
const hasUnit = isString && isValueWithUnit(value);
|
|
157
|
-
const unit = hasUnit ? getUnit(value) : null;
|
|
158
|
-
const cleaned = cleanNumStr(hasUnit ? getValue(value) : value);
|
|
159
|
-
return {
|
|
160
|
-
number: preserveTrailingZeros && hasTrailingZeros(value) ? cleaned : toNum(cleaned),
|
|
161
|
-
unit,
|
|
162
|
-
isString
|
|
50
|
+
/** Approximates the Gaussian error function. */
|
|
51
|
+
function erf(value) {
|
|
52
|
+
const t = 1 / (1 + .5 * abs(value));
|
|
53
|
+
const answer = 1 - t * exp(-(value ** 2) - 1.26551223 + t * (1.00002368 + t * (.37409196 + t * (.09678418 + t * (-.18628806 + t * (.27886807 + t * (-1.13520398 + t * (1.48851587 + t * (-.82215223 + t * .17087277)))))))));
|
|
54
|
+
return value >= 0 ? answer : -answer;
|
|
55
|
+
}
|
|
56
|
+
/** Converts a confidence interval to a number of standard deviations. */
|
|
57
|
+
function get_k_from_conf_int(confidenceInterval) {
|
|
58
|
+
const functionValue = (value) => {
|
|
59
|
+
return confidenceInterval - erf(value / sqrt(2));
|
|
163
60
|
};
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
* Convert a number to a string safely, better than String(value)
|
|
167
|
-
* String(0.0000002) returns '2e-7' which is unwanted if we need to preserve formatting
|
|
168
|
-
*
|
|
169
|
-
* @param value
|
|
170
|
-
* @param [isScientific] whether to preserve scientific notation
|
|
171
|
-
* @returns number or string output value
|
|
172
|
-
*/
|
|
173
|
-
const safeStringifyNumber = (value, isScientific) => {
|
|
174
|
-
return isScientific ? String(value) : toString(value);
|
|
175
|
-
};
|
|
176
|
-
/**
|
|
177
|
-
* Internal function to unParse a value, unit, and type back to an output value
|
|
178
|
-
*
|
|
179
|
-
* Accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
180
|
-
*
|
|
181
|
-
* @param args
|
|
182
|
-
* @param args.value
|
|
183
|
-
* @param args.unit
|
|
184
|
-
* @param args.isString
|
|
185
|
-
* @param args.isScientific
|
|
186
|
-
* @returns number or string output value
|
|
187
|
-
*/
|
|
188
|
-
const unParseNumber = ({ value, unit, isString, isScientific }) => {
|
|
189
|
-
const convertedValue = typeof value === "number" && isString ? safeStringifyNumber(value, isScientific) : value;
|
|
190
|
-
if (unit) return withUnit(convertedValue, unit);
|
|
191
|
-
return convertedValue;
|
|
192
|
-
};
|
|
193
|
-
//#endregion
|
|
194
|
-
//#region src/comparison/comparison.ts
|
|
195
|
-
const DEFAULT_MAX_RELATIVE_DIFF = Number.EPSILON;
|
|
196
|
-
const convertNumbers = (firstValue, secondValue) => {
|
|
197
|
-
const { number: firstNumber, unit: firstUnit } = parseNumber(firstValue);
|
|
198
|
-
const { number: secondNumber, unit: secondUnit } = parseNumber(secondValue);
|
|
199
|
-
return {
|
|
200
|
-
firstNumber,
|
|
201
|
-
secondNumber: firstUnit && secondUnit && firstUnit !== secondUnit ? convertAndGetValue(secondNumber, firstUnit, secondUnit) : secondNumber
|
|
61
|
+
const derivative = (value) => {
|
|
62
|
+
return -sqrt(2 / Math.PI) * exp(-.5 * value ** 2);
|
|
202
63
|
};
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return null;
|
|
213
|
-
};
|
|
214
|
-
/**
|
|
215
|
-
* Determines whether two numbers are close in value with a tolerance
|
|
216
|
-
* (mitigates excess JavaScript floating point precision quirks)
|
|
217
|
-
*/
|
|
218
|
-
const isCloseTo = (firstValue, secondValue, options = { relativeDiff: DEFAULT_MAX_RELATIVE_DIFF }) => {
|
|
219
|
-
const { relativeDiff, absoluteDiff } = options;
|
|
220
|
-
const toleranceNumber = getToleranceNumber(relativeDiff ?? absoluteDiff);
|
|
221
|
-
if (firstValue === null || secondValue === null) return false;
|
|
222
|
-
const hasUnitFirstValue = isValueWithUnit(firstValue);
|
|
223
|
-
const hasUnitSecondValue = isValueWithUnit(secondValue);
|
|
224
|
-
if (hasUnitFirstValue && !hasUnitSecondValue || !hasUnitFirstValue && hasUnitSecondValue) throw new Error(`Parameters must either both have units or both not have units. Received "${firstValue}" and "${secondValue}"`);
|
|
225
|
-
if (toleranceNumber === null) {
|
|
226
|
-
console.warn("Tolerance number is not defined!");
|
|
227
|
-
return firstValue === secondValue;
|
|
228
|
-
}
|
|
229
|
-
if (toleranceNumber <= 0 || toleranceNumber < Number.EPSILON) throw Error("Unpredictable results - toleranceNumber should be bigger than zero or less then EPSILON");
|
|
230
|
-
const { firstNumber, secondNumber } = convertNumbers(firstValue, secondValue);
|
|
231
|
-
if ((firstNumber === Infinity || firstNumber === "Infinity") && (secondNumber === Infinity || secondNumber === "Infinity") || (firstNumber === -Infinity || firstNumber === "-Infinity") && (secondNumber === -Infinity || secondNumber === "-Infinity")) return true;
|
|
232
|
-
if (typeof firstNumber === "number" && typeof secondNumber === "number") {
|
|
233
|
-
if (firstNumber === secondNumber) return true;
|
|
234
|
-
if (absoluteDiff || firstNumber === 0 || secondNumber === 0) {
|
|
235
|
-
const diff = Math.abs(firstNumber - secondNumber);
|
|
236
|
-
return isCloseTo(diff, toleranceNumber, { relativeDiff: "1%" }) || diff < toleranceNumber;
|
|
237
|
-
} else return 2 * Math.abs((firstNumber - secondNumber) / (firstNumber + secondNumber)) < toleranceNumber;
|
|
238
|
-
}
|
|
239
|
-
return false;
|
|
240
|
-
};
|
|
241
|
-
/**
|
|
242
|
-
* Determines whether two numbers are close enough to be equal
|
|
243
|
-
* or checks the firstValue is greater than the secondValue
|
|
244
|
-
*/
|
|
245
|
-
const isCloseToOrGreaterThan = (firstValue, secondValue, options = { relativeDiff: DEFAULT_MAX_RELATIVE_DIFF }) => {
|
|
246
|
-
if (firstValue === null || secondValue === null) return false;
|
|
247
|
-
const { firstNumber, secondNumber } = convertNumbers(firstValue, secondValue);
|
|
248
|
-
if (typeof firstNumber === "number" && typeof secondNumber === "number") return isCloseTo(firstNumber, secondNumber, options) || firstNumber > secondNumber;
|
|
249
|
-
return false;
|
|
250
|
-
};
|
|
251
|
-
/**
|
|
252
|
-
* Determines whether two numbers are close enough to be equal
|
|
253
|
-
* or checks the firstValue is less than the secondValue
|
|
254
|
-
*/
|
|
255
|
-
const isCloseToOrLessThan = (firstValue, secondValue, options = { relativeDiff: DEFAULT_MAX_RELATIVE_DIFF }) => {
|
|
256
|
-
if (firstValue === null || secondValue === null) return false;
|
|
257
|
-
const { firstNumber, secondNumber } = convertNumbers(firstValue, secondValue);
|
|
258
|
-
if (typeof firstNumber === "number" && typeof secondNumber === "number") return isCloseTo(firstNumber, secondNumber, options) || firstNumber < secondNumber;
|
|
259
|
-
return false;
|
|
260
|
-
};
|
|
261
|
-
/**
|
|
262
|
-
* Determines whether two objects or arrays are deeply close equal (all nested child numbers)
|
|
263
|
-
*/
|
|
264
|
-
const isDeepCloseTo = (a, b, options = { relativeDiff: DEFAULT_MAX_RELATIVE_DIFF }) => {
|
|
265
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
266
|
-
if (a.length !== b.length) return false;
|
|
267
|
-
return a.every((a, i) => isDeepCloseTo(a, b[i], options));
|
|
268
|
-
}
|
|
269
|
-
if (typeof a === "object" && a !== null && typeof b === "object" && b !== null) {
|
|
270
|
-
const aKeys = Object.keys(a);
|
|
271
|
-
const bKeys = Object.keys(b);
|
|
272
|
-
if (aKeys.length !== bKeys.length) return false;
|
|
273
|
-
return aKeys.every((key) => isDeepCloseTo(a[key], b[key], options));
|
|
274
|
-
}
|
|
275
|
-
if (Number.isNaN(a) && Number.isNaN(b) || a === "" && b === "") return true;
|
|
276
|
-
if (typeof a === "number" && typeof b === "number" || isValueWithUnit(a) && isValueWithUnit(b) || isValidNum(a) && isValidNum(b)) return isCloseTo(a, b, options);
|
|
277
|
-
return true;
|
|
278
|
-
};
|
|
64
|
+
const [result] = newton(functionValue, derivative, 1);
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
/** Converts a number of standard deviations to a confidence interval. */
|
|
68
|
+
function get_conf_int_from_k(standardDeviations) {
|
|
69
|
+
return erf(standardDeviations / sqrt(2));
|
|
70
|
+
}
|
|
279
71
|
//#endregion
|
|
280
|
-
//#region src/
|
|
281
|
-
const DEFAULT_SIGNIFICANT_DIGITS = 4;
|
|
72
|
+
//#region src/units/constants.ts
|
|
282
73
|
/**
|
|
283
|
-
*
|
|
74
|
+
* Units labels
|
|
284
75
|
*
|
|
285
|
-
* @
|
|
286
|
-
* @
|
|
287
|
-
* @param [n]
|
|
288
|
-
* @returns rounded number
|
|
76
|
+
* @readonly
|
|
77
|
+
* @enum {Object}
|
|
289
78
|
*/
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
79
|
+
const LABELS = Object.freeze({
|
|
80
|
+
in: "in",
|
|
81
|
+
mm: "mm",
|
|
82
|
+
cm: "cm",
|
|
83
|
+
m: "m",
|
|
84
|
+
microM: "μm",
|
|
85
|
+
km: "km",
|
|
86
|
+
ft: "ft",
|
|
87
|
+
usft: "usft",
|
|
88
|
+
in2: "in²",
|
|
89
|
+
cm2: "cm²",
|
|
90
|
+
m2: "m²",
|
|
91
|
+
kg: "kg",
|
|
92
|
+
tonnes: "t",
|
|
93
|
+
mt: "mt",
|
|
94
|
+
kip: "kip",
|
|
95
|
+
bbl: "bbl",
|
|
96
|
+
m3: "m³",
|
|
97
|
+
Mm3: "Mm³",
|
|
98
|
+
MMSCF: "MMSCF",
|
|
99
|
+
lbm: "lbm",
|
|
100
|
+
"kg/mol": "kg/mol",
|
|
101
|
+
"lbf/mol": "lbf/mol",
|
|
102
|
+
sg: "sg",
|
|
103
|
+
ppg: "ppg",
|
|
104
|
+
"kg/m3": "kg/m³",
|
|
105
|
+
"lbm/ft3": "lbm/ft³",
|
|
106
|
+
s: "s",
|
|
107
|
+
min: "min",
|
|
108
|
+
h: "h",
|
|
109
|
+
d: "d",
|
|
110
|
+
month: "month",
|
|
111
|
+
year: "year",
|
|
112
|
+
"bbl/ft": "bbl/ft",
|
|
113
|
+
lpm: "L/min",
|
|
114
|
+
lps: "L/s",
|
|
115
|
+
bpm: "bbl/min",
|
|
116
|
+
"m3/min": "m³/min",
|
|
117
|
+
"m3/s": "m³/s",
|
|
118
|
+
MMSCFD: "MMSCFD",
|
|
119
|
+
bar: "Bar",
|
|
120
|
+
Pa: "Pa",
|
|
121
|
+
kPa: "kPa",
|
|
122
|
+
MPa: "MPa",
|
|
123
|
+
GPa: "GPa",
|
|
124
|
+
kPsi: "Psi",
|
|
125
|
+
ksi: "ksi",
|
|
126
|
+
"lbf/100ft2": "lbf/100ft²",
|
|
127
|
+
"1/Pa": "Pa⁻¹",
|
|
128
|
+
"1/kPa": "kPa⁻¹",
|
|
129
|
+
"1/MPa": "MPa⁻¹",
|
|
130
|
+
"1/GPa": "GPa⁻¹",
|
|
131
|
+
"1/psi": "psi⁻¹",
|
|
132
|
+
"kPa/m": "kPa/m",
|
|
133
|
+
"1/bar": "bar⁻¹",
|
|
134
|
+
klbf: "klbf",
|
|
135
|
+
"psi/ft": "Psi/ft",
|
|
136
|
+
"bar/100m": "bar/100m",
|
|
137
|
+
"psi/100ft": "psi/100ft",
|
|
138
|
+
"kPa/100m": "kPa/100m",
|
|
139
|
+
C: "°C",
|
|
140
|
+
F: "°F",
|
|
141
|
+
K: "K",
|
|
142
|
+
"C/100m": "°C/100m",
|
|
143
|
+
"C/m": "°C/m",
|
|
144
|
+
"Pa/C": "Pa/°C",
|
|
145
|
+
"Bar/C": "Bar/°C",
|
|
146
|
+
"psi/F": "psi/°F",
|
|
147
|
+
"psi/C": "psi/°C",
|
|
148
|
+
"F/100ft": "°F/100ft",
|
|
149
|
+
"F/ft": "°F/ft",
|
|
150
|
+
"K/100m": "K/100m",
|
|
151
|
+
"K/m": "K/m",
|
|
152
|
+
"lbf/ft": "lbf/ft",
|
|
153
|
+
N: "N",
|
|
154
|
+
kN: "kN",
|
|
155
|
+
"N/m": "N/m",
|
|
156
|
+
"daN/m": "daN/m",
|
|
157
|
+
lbf: "lbf",
|
|
158
|
+
kgf: "kgf",
|
|
159
|
+
rad: "rad",
|
|
160
|
+
"BTU/lbm": "BTU/lbm",
|
|
161
|
+
ppf: "ppf",
|
|
162
|
+
"kg/m": "kg/m",
|
|
163
|
+
"E-06/degC": "10⁻⁶/°C",
|
|
164
|
+
"E-06/degF": "10⁻⁶/°F",
|
|
165
|
+
"1/K": "K⁻¹",
|
|
166
|
+
km2: "km²",
|
|
167
|
+
ft2: "ft²",
|
|
168
|
+
mm2: "mm²",
|
|
169
|
+
mile2: "mile²",
|
|
170
|
+
ft3: "ft³",
|
|
171
|
+
"g/cm3": "g/cm³",
|
|
172
|
+
Sm3: "Sm³",
|
|
173
|
+
"ft3/s": "ft³/s",
|
|
174
|
+
"ft3/d": "ft³/d",
|
|
175
|
+
"m3/d": "m³/d",
|
|
176
|
+
"1/m3/d": "1/m³/d",
|
|
177
|
+
"s/m3": "s/m³",
|
|
178
|
+
"1/MMSCFD": "1/MMSCFD",
|
|
179
|
+
"bbl/d": "bbl/d",
|
|
180
|
+
tonneForce: "tonne-force",
|
|
181
|
+
USGal: "US gal",
|
|
182
|
+
"g/mol": "g/mol",
|
|
183
|
+
Nm: "N⋅m",
|
|
184
|
+
kNm: "kN⋅m",
|
|
185
|
+
ftlbf: "ft⋅lbf",
|
|
186
|
+
"J/(kg*degC)": "J/(kg⋅°C)",
|
|
187
|
+
"J/(kg*degK)": "J/(kg⋅K)",
|
|
188
|
+
"J/(s*m*degK)": "J/(s⋅m⋅K)",
|
|
189
|
+
"BTU/(lbm*degF)": "BTU/(lbm⋅°F)",
|
|
190
|
+
"BTU/(h*ft*degF)": "BTU/(h⋅ft⋅°F)",
|
|
191
|
+
l: "L",
|
|
192
|
+
"l/m": "L/m",
|
|
193
|
+
"kJ/kg": "kJ/kg",
|
|
194
|
+
"J/kg": "J/kg",
|
|
195
|
+
deg: "°",
|
|
196
|
+
"W/(mK)": "W/(m⋅K)",
|
|
197
|
+
psi: "psi",
|
|
198
|
+
"deg/100ft": "°/100ft",
|
|
199
|
+
"deg/30m": "°/30m",
|
|
200
|
+
"deg/10m": "°/10m",
|
|
201
|
+
"%": "%",
|
|
202
|
+
Hz: "Hz",
|
|
203
|
+
"1/s": "1/s",
|
|
204
|
+
rpm: "rpm",
|
|
205
|
+
"Pa/m": "Pa/m",
|
|
206
|
+
"bar/m": "bar/m",
|
|
207
|
+
gpm: "gpm",
|
|
208
|
+
"kg/s": "kg/s",
|
|
209
|
+
"lbm/s": "lbm/s",
|
|
210
|
+
"tonnes/h": "tonnes/h",
|
|
211
|
+
"tons/h": "tons/h",
|
|
212
|
+
"deg/m": "°/m",
|
|
213
|
+
"deg/ft": "°/ft",
|
|
214
|
+
"rad/m": "rad/m",
|
|
215
|
+
"rad/ft": "rad/ft",
|
|
216
|
+
"dyn/cm": "dyn/cm",
|
|
217
|
+
"mN/m": "mN/m",
|
|
218
|
+
"m/s": "m/s",
|
|
219
|
+
"ft/s": "ft/s",
|
|
220
|
+
"m/min": "m/min",
|
|
221
|
+
"ft/min": "ft/min",
|
|
222
|
+
"m/h": "m/h",
|
|
223
|
+
"ft/h": "ft/h",
|
|
224
|
+
mph: "mph",
|
|
225
|
+
"km/h": "km/h",
|
|
226
|
+
"m/s2": "m/s²",
|
|
227
|
+
Gs: "Gs",
|
|
228
|
+
nT: "nT",
|
|
229
|
+
g: "g",
|
|
230
|
+
"ft/s2": "ft/s²",
|
|
231
|
+
"Pa*s": "Pa⋅s",
|
|
232
|
+
P: "P",
|
|
233
|
+
"mPa*s": "mPa⋅s",
|
|
234
|
+
cP: "cP",
|
|
235
|
+
W: "W",
|
|
236
|
+
hhp: "hhp",
|
|
237
|
+
hp: "hp",
|
|
238
|
+
kW: "kW",
|
|
239
|
+
MW: "MW",
|
|
240
|
+
"BTU/h": "BTU/h",
|
|
241
|
+
"W/m2": "W/m²",
|
|
242
|
+
"hhp/in2": "hhp/in²",
|
|
243
|
+
"hhp/ft2": "hhp/ft²",
|
|
244
|
+
"Mm3/d": "Mm³/d",
|
|
245
|
+
"STB/d": "STB/d",
|
|
246
|
+
"Sm3/d": "Sm³/d",
|
|
247
|
+
"Sm3/min": "Sm³/min",
|
|
248
|
+
"MSm3/d": "MSm³/d",
|
|
249
|
+
"SCF/STB": "SCF / STB",
|
|
250
|
+
"Sm3/Sm3": "Sm³ / Sm³",
|
|
251
|
+
"SCF/d": "SCF/d",
|
|
252
|
+
STB: "STB",
|
|
253
|
+
SCF: "SCF",
|
|
254
|
+
MSm3: "MSm³",
|
|
255
|
+
Gsg: "sg",
|
|
256
|
+
Gppg: "ppg",
|
|
257
|
+
"Gkg/m3": "kg/m³",
|
|
258
|
+
"Glbm/ft3": "lbm/ft³",
|
|
259
|
+
"lb/ft3": "lb/ft³",
|
|
260
|
+
"°N": "°N",
|
|
261
|
+
"°S": "°S",
|
|
262
|
+
"°W": "°W",
|
|
263
|
+
"°E": "°E",
|
|
264
|
+
fr: " ",
|
|
265
|
+
mD: "mD",
|
|
266
|
+
CI: "CI",
|
|
267
|
+
Sigma: "σ",
|
|
268
|
+
"Sm3/d/bar": "Sm³/d/bar",
|
|
269
|
+
"STB/d/psi": "STB/d/psi",
|
|
270
|
+
"m3/s/bar": "m³/s/bar",
|
|
271
|
+
"lb/ft": "lb/ft",
|
|
272
|
+
"E-09/bar": "10⁻⁹/bar",
|
|
273
|
+
"E-10/psi": "10⁻¹⁰/psi",
|
|
274
|
+
"E-14/pa": "10⁻¹⁴/pa",
|
|
275
|
+
"m3/m": " m³/m",
|
|
276
|
+
"cm3/m": "cm³/m",
|
|
277
|
+
"mm3/m": "mm³/m",
|
|
278
|
+
"ft3/ft": "ft³/ft",
|
|
279
|
+
"in3/ft": "in³/ft",
|
|
280
|
+
lk: "lk",
|
|
281
|
+
ftCla: "ftCla",
|
|
282
|
+
lkCla: "lkCla",
|
|
283
|
+
ftSe: "ftSe",
|
|
284
|
+
ydSe: "ydSe",
|
|
285
|
+
chSe: "chSe",
|
|
286
|
+
"chSe(T)": "chSe(T)",
|
|
287
|
+
ftGC: "ftGC",
|
|
288
|
+
ydInd: "ydInd",
|
|
289
|
+
"d/stand": "d/stand",
|
|
290
|
+
"h/stand": "h/stand",
|
|
291
|
+
"min/stand": "min/stand",
|
|
292
|
+
"s/stand": "s/stand",
|
|
293
|
+
"m3/t": "m3/t",
|
|
294
|
+
"L/100kg": "L/100kg"
|
|
295
|
+
});
|
|
318
296
|
/**
|
|
319
|
-
*
|
|
297
|
+
* Alternative units grouped by quantity
|
|
320
298
|
*
|
|
321
|
-
* @
|
|
322
|
-
* @
|
|
323
|
-
* @param [n] number of significant digits
|
|
324
|
-
* @returns rounded number
|
|
325
|
-
*/
|
|
326
|
-
const roundNumberToPrecision = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
327
|
-
return Number(value.toPrecision(n));
|
|
328
|
-
};
|
|
329
|
-
/**
|
|
330
|
-
* Rounds a number to N significant digits, preserving trailing decimal zeros
|
|
331
|
-
*
|
|
332
|
-
* @private (see roundByMagnitudeToFixed() for the public interface)
|
|
333
|
-
* @param value
|
|
334
|
-
* @param [n] number of significant digits
|
|
335
|
-
* @returns rounded number as string
|
|
336
|
-
*/
|
|
337
|
-
const roundNumberToFixedPrecision = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
338
|
-
const [integerPart, decimalPart] = toNum(value).toPrecision(n).split(".");
|
|
339
|
-
if (decimalPart) {
|
|
340
|
-
const decimalDigits = n - integerPart.length;
|
|
341
|
-
return `${integerPart}.${decimalPart.padEnd(decimalDigits, "0")}`;
|
|
342
|
-
} else return integerPart;
|
|
343
|
-
};
|
|
344
|
-
/**
|
|
345
|
-
* Rounds a number to N significant digits, excluding the integer part (only rounds decimal part)
|
|
346
|
-
*
|
|
347
|
-
* @private (see roundToPrecision() for the public interface)
|
|
348
|
-
* @param value
|
|
349
|
-
* @param [n] number of significant digits
|
|
350
|
-
* @returns rounded number
|
|
351
|
-
*/
|
|
352
|
-
const roundNumberToDecimalPrecision = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
353
|
-
if (Math.abs(value) > 1) {
|
|
354
|
-
const [integerPart, decimalPart] = formatDecimal(value, "").split(".");
|
|
355
|
-
if (!decimalPart) return value;
|
|
356
|
-
else {
|
|
357
|
-
const roundedDecimalPart = Number(`0.${decimalPart}`).toPrecision(n).slice(1);
|
|
358
|
-
return Number(integerPart + roundedDecimalPart);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
return roundNumberToPrecision(value, n);
|
|
362
|
-
};
|
|
363
|
-
/**
|
|
364
|
-
* Rounds a numeric value to N significant digits.
|
|
365
|
-
*
|
|
366
|
-
* - accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
367
|
-
* - returns same type as input
|
|
368
|
-
* - similar to Number.toPrecision() but safer
|
|
369
|
-
* - does *not* append trailing zeros (unlike `Number(4).toPrecision(4)` -> '4.000')
|
|
370
|
-
*
|
|
371
|
-
* @param value - the value to round
|
|
372
|
-
* @param [n] - the number of significant digits
|
|
373
|
-
* @returns rounded value, or input value when unable to round
|
|
374
|
-
*
|
|
375
|
-
* @example
|
|
376
|
-
* roundToPrecision(0.0000456789) -> 0.00004568
|
|
377
|
-
*/
|
|
378
|
-
const roundToPrecision = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
379
|
-
if (typeof value === "number") return roundNumberToPrecision(value, n);
|
|
380
|
-
if (value === null || Number.isNaN(value) || value === Infinity || value === -Infinity || value === void 0) return value;
|
|
381
|
-
const { number, unit, isString } = parseNumber(value);
|
|
382
|
-
return unParseNumber({
|
|
383
|
-
value: !isNumeric(number) || typeof number === "string" && number.endsWith(".") ? value : roundNumberToPrecision(number, n),
|
|
384
|
-
unit,
|
|
385
|
-
isString,
|
|
386
|
-
isScientific: isScientificStringNum(value)
|
|
387
|
-
});
|
|
388
|
-
};
|
|
389
|
-
/**
|
|
390
|
-
* Rounds a numeric value to N significant digits (only the decimal part)
|
|
391
|
-
*
|
|
392
|
-
* - accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
393
|
-
* - returns same type as input
|
|
394
|
-
* - unlike roundToPrecision(), acts only on the decimal part, not the integer part too
|
|
395
|
-
*
|
|
396
|
-
* @param value - the value to round
|
|
397
|
-
* @param [n]- the number of significant digits
|
|
398
|
-
* @returns rounded value, or input value when unable to round
|
|
399
|
-
*
|
|
400
|
-
* @example
|
|
401
|
-
* roundToPrecision(1234.0000456789) -> 1234.00004568
|
|
402
|
-
*/
|
|
403
|
-
const roundToDecimalPrecision = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
404
|
-
if (typeof value === "number") return roundNumberToDecimalPrecision(value, n);
|
|
405
|
-
if (value === null || Number.isNaN(value) || value === Infinity || value === -Infinity || value === void 0) return value;
|
|
406
|
-
const { number, unit, isString } = parseNumber(value);
|
|
407
|
-
return unParseNumber({
|
|
408
|
-
value: !isNumeric(number) || typeof number === "string" && number.endsWith(".") ? value : roundNumberToDecimalPrecision(number, n),
|
|
409
|
-
unit,
|
|
410
|
-
isString,
|
|
411
|
-
isScientific: isScientificStringNum(value)
|
|
412
|
-
});
|
|
413
|
-
};
|
|
414
|
-
/**
|
|
415
|
-
* Rounds a number to an appropriate number of digits, based on its size.
|
|
416
|
-
*
|
|
417
|
-
* @private (see roundByMagnitude() for the public interface)
|
|
418
|
-
* @param value
|
|
419
|
-
* @param [n] the number of significant digits
|
|
420
|
-
* @param [toFixed] to fixed digits (i.e. with trailing zeros)
|
|
421
|
-
* @returns rounded number
|
|
422
|
-
*/
|
|
423
|
-
const roundNumberByMagnitude = (value, n = DEFAULT_SIGNIFICANT_DIGITS, toFixed = false) => {
|
|
424
|
-
const noDecimalsAbove = 10 ** n;
|
|
425
|
-
const result = noDecimalsAbove && value > noDecimalsAbove ? roundNumber(value, 0) : toFixed ? roundNumberToFixedPrecision(value, n) : roundNumberToPrecision(value, n);
|
|
426
|
-
return toFixed ? String(result) : result;
|
|
427
|
-
};
|
|
428
|
-
/**
|
|
429
|
-
* Rounds a numeric value to an appropriate number of digits, based on its size. It rounds to N significant digits,
|
|
430
|
-
* but never rounds the integer part.
|
|
431
|
-
*
|
|
432
|
-
* - accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
433
|
-
* - returns same type as input
|
|
434
|
-
*
|
|
435
|
-
* @param value - the value to round
|
|
436
|
-
* @param [n] - the number of significant digits
|
|
437
|
-
* @returns rounded value, or input value when unable to round
|
|
438
|
-
*
|
|
439
|
-
* @example
|
|
440
|
-
* roundByMagnitude(0.000123456789) -> 0.0001235
|
|
441
|
-
* roundByMagnitude(1.123456789) -> 1.123
|
|
442
|
-
* roundByMagnitude(19999.123456789) -> 19999
|
|
443
|
-
*/
|
|
444
|
-
const roundByMagnitude = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
445
|
-
if (typeof value === "number") return roundNumberByMagnitude(value, n);
|
|
446
|
-
if (value === null || Number.isNaN(value) || value === Infinity || value === -Infinity || value === void 0) return value;
|
|
447
|
-
const { number, unit, isString } = parseNumber(value);
|
|
448
|
-
return unParseNumber({
|
|
449
|
-
value: !isNumeric(number) || typeof number === "string" && number.endsWith(".") ? value : roundNumberByMagnitude(number, n),
|
|
450
|
-
unit,
|
|
451
|
-
isString,
|
|
452
|
-
isScientific: isScientificStringNum(value)
|
|
453
|
-
});
|
|
454
|
-
};
|
|
455
|
-
/**
|
|
456
|
-
* Rounds a numeric value to an appropriate number of digits, based on its size. It rounds to N significant digits,
|
|
457
|
-
* but never rounds the integer part. Similar to roundByMagnitude, but adds trailing zeros.
|
|
458
|
-
*
|
|
459
|
-
* - accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
460
|
-
* - returns string type (or original value and type when not possible to convert)
|
|
461
|
-
*
|
|
462
|
-
* @param value - the value to round
|
|
463
|
-
* @param [n] - the number of significant digits
|
|
464
|
-
* @returns rounded value as a string, or input value when unable to round
|
|
465
|
-
*
|
|
466
|
-
* @example
|
|
467
|
-
* roundByMagnitudeToFixed(0.000120016789) -> 0.0001200
|
|
468
|
-
*/
|
|
469
|
-
const roundByMagnitudeToFixed = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
470
|
-
const toFixed = true;
|
|
471
|
-
if (value === null || Number.isNaN(value) || value === Infinity || value === -Infinity || value === void 0) return value;
|
|
472
|
-
if (typeof value === "number") return roundNumberByMagnitude(value, n, toFixed);
|
|
473
|
-
const { number, unit } = parseNumber(value, true);
|
|
474
|
-
return unParseNumber({
|
|
475
|
-
value: !isNumeric(number) || typeof number === "string" && number.endsWith(".") ? value : roundNumberByMagnitude(number, n, toFixed),
|
|
476
|
-
unit,
|
|
477
|
-
isString: true,
|
|
478
|
-
isScientific: isScientificStringNum(value)
|
|
479
|
-
});
|
|
480
|
-
};
|
|
481
|
-
/**
|
|
482
|
-
* Rounds a number to an appropriate number of digits, based on its size in relation to a range.
|
|
483
|
-
*
|
|
484
|
-
* @private (see roundByMagnitude() for the public interface)
|
|
485
|
-
* @param value
|
|
486
|
-
* @param min
|
|
487
|
-
* @param max
|
|
488
|
-
* @param [n] the minimum number of significant digits
|
|
489
|
-
* @returns rounded number
|
|
490
|
-
*/
|
|
491
|
-
const roundNumberByRange = (value, min, max, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
492
|
-
if (isCloseToOrLessThan(max, min)) return value;
|
|
493
|
-
const range = max - min;
|
|
494
|
-
return roundNumber(value, Math.max(n, 0 - Math.floor(Math.log10(range))));
|
|
495
|
-
};
|
|
496
|
-
/**
|
|
497
|
-
* Rounds a numeric value to an appropriate number of digits, based on its size within a range of values.
|
|
498
|
-
*
|
|
499
|
-
* - accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
500
|
-
* - returns same type as input
|
|
501
|
-
*
|
|
502
|
-
* @param value - the value to round
|
|
503
|
-
* @param min - the min value in the range
|
|
504
|
-
* @param max - the max value in the range
|
|
505
|
-
* @param [n] - the minimum number of significant digits
|
|
506
|
-
* @returns rounded value, or input value when unable to round
|
|
507
|
-
*
|
|
508
|
-
*/
|
|
509
|
-
const roundByRange = (value, min, max, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
510
|
-
if (typeof value === "number") return roundNumberByRange(value, min, max, n);
|
|
511
|
-
if (value === null || Number.isNaN(value) || value === Infinity || value === -Infinity || value === void 0) return value;
|
|
512
|
-
const { number, unit, isString } = parseNumber(value);
|
|
513
|
-
return unParseNumber({
|
|
514
|
-
value: !isNumeric(number) || typeof number === "string" && number.endsWith(".") ? value : roundNumberByRange(number, min, max, n),
|
|
515
|
-
unit,
|
|
516
|
-
isString,
|
|
517
|
-
isScientific: isScientificStringNum(value)
|
|
518
|
-
});
|
|
519
|
-
};
|
|
520
|
-
/**
|
|
521
|
-
* Rounds a numeric value to N fixed decimal digits.
|
|
522
|
-
*
|
|
523
|
-
* - accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
524
|
-
* - returns same type as input
|
|
525
|
-
*
|
|
526
|
-
* @param value - the value to round
|
|
527
|
-
* @param [n] - the number of fixed decimal digits
|
|
528
|
-
* @returns rounded value, or input value when unable to round
|
|
529
|
-
*
|
|
530
|
-
*/
|
|
531
|
-
const roundToFixed = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
532
|
-
if (value === null || Number.isNaN(value) || value === Infinity || value === -Infinity || value === void 0) return value;
|
|
533
|
-
if (typeof value === "number") return value.toFixed(n);
|
|
534
|
-
const { number, unit, isString } = parseNumber(value);
|
|
535
|
-
return unParseNumber({
|
|
536
|
-
value: !isNumeric(number) || typeof number === "string" && number.endsWith(".") ? value : number.toFixed(n),
|
|
537
|
-
unit,
|
|
538
|
-
isString,
|
|
539
|
-
isScientific: isScientificStringNum(value)
|
|
540
|
-
});
|
|
541
|
-
};
|
|
542
|
-
//#endregion
|
|
543
|
-
//#region src/numbers/display-number.ts
|
|
544
|
-
const DEFAULT_AUTO_SCIENTIFIC_BELOW = 1e-4;
|
|
545
|
-
const DEFAULT_AUTO_SCIENTIFIC_ABOVE = 1e7;
|
|
546
|
-
const superscriptSymbols = {
|
|
547
|
-
"0": "⁰",
|
|
548
|
-
"1": "¹",
|
|
549
|
-
"2": "²",
|
|
550
|
-
"3": "³",
|
|
551
|
-
"4": "⁴",
|
|
552
|
-
"5": "⁵",
|
|
553
|
-
"6": "⁶",
|
|
554
|
-
"7": "⁷",
|
|
555
|
-
"8": "⁸",
|
|
556
|
-
"9": "⁹",
|
|
557
|
-
"+": "⁺",
|
|
558
|
-
"-": "⁻"
|
|
559
|
-
};
|
|
560
|
-
const appendTrailingZeros = (value, numberOfZeros) => {
|
|
561
|
-
const zeros = "0".repeat(numberOfZeros);
|
|
562
|
-
return numberOfZeros > 0 && value !== "0" ? !value.includes(".") ? `${value}.${zeros}` : `${value}${zeros}` : value;
|
|
563
|
-
};
|
|
564
|
-
const formatDecimal = (value, thousandSeparator, preserveTrailingZeros = false) => {
|
|
565
|
-
const convertedValue = convertNumberToLocale(toNum(value), "en-US").replaceAll(",", thousandSeparator);
|
|
566
|
-
return preserveTrailingZeros ? appendTrailingZeros(convertedValue, countTrailingZeros(value, true)) : convertedValue;
|
|
567
|
-
};
|
|
568
|
-
const formatDecimalDisplayNumber = (value, options) => {
|
|
569
|
-
const { nonBreakingSpace } = options ?? {};
|
|
570
|
-
if (value === "") return value;
|
|
571
|
-
if (value === null || value === void 0) return "";
|
|
572
|
-
if (!isValidNum(value)) return trim(value.toString());
|
|
573
|
-
return formatDecimal(value, options?.noThousandsSeparator ? "" : nonBreakingSpace ? " " : " ", options?.preserveTrailingZeros);
|
|
574
|
-
};
|
|
575
|
-
const formatScientificDisplayNumber = (value, options) => {
|
|
576
|
-
const { roundScientificCoefficient, eNotation } = options ?? {};
|
|
577
|
-
if (Number.isNaN(value)) return "Invalid";
|
|
578
|
-
if (value === null || value === void 0) return "";
|
|
579
|
-
if (!isValidNum(value) || value === "") return trim(value.toString());
|
|
580
|
-
const sanitizedValue = toNum(value);
|
|
581
|
-
if (!Number.isFinite(sanitizedValue)) return trim(value.toString());
|
|
582
|
-
const power = eNotation ? "e" : "·10";
|
|
583
|
-
const [coefficient, exponent] = sanitizedValue.toExponential().split("e");
|
|
584
|
-
const roundedCoefficient = typeof roundScientificCoefficient === "number" ? round(coefficient, roundScientificCoefficient) : coefficient;
|
|
585
|
-
const noExponent = exponent === "+0" || exponent === "-0";
|
|
586
|
-
const formattedExponent = [...exponent.replaceAll("+", "")].map((c) => eNotation ? c : superscriptSymbols[c]).join("");
|
|
587
|
-
return noExponent ? roundedCoefficient : `${roundedCoefficient}${power}${formattedExponent}`;
|
|
588
|
-
};
|
|
589
|
-
const formatDisplayNumber = (value, options) => {
|
|
590
|
-
const abs = Math.abs(toNum(value));
|
|
591
|
-
return (options?.scientific === "auto" && options?.autoScientificBelow && options?.autoScientificAbove ? abs < options?.autoScientificBelow || abs > options?.autoScientificAbove : options?.scientific) ? formatScientificDisplayNumber(value, options) : formatDecimalDisplayNumber(value, options);
|
|
592
|
-
};
|
|
593
|
-
/**
|
|
594
|
-
* Displays a number with human-friendly formatting (use for non-editable display labels, text)
|
|
595
|
-
*
|
|
596
|
-
* Accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
597
|
-
*
|
|
598
|
-
* @example
|
|
599
|
-
* //returns '1 234.56'
|
|
600
|
-
* displayNumber(1234.56)
|
|
601
|
-
*
|
|
602
|
-
* By default, adds thousands separators. Can be configured to display in scientific notation, and with formatted units.
|
|
603
|
-
*
|
|
604
|
-
* @param value
|
|
605
|
-
* @param options
|
|
606
|
-
* @returns formatted display number
|
|
607
|
-
*/
|
|
608
|
-
const displayNumber = (value, options) => {
|
|
609
|
-
const optionsWithDefaults = {
|
|
610
|
-
scientific: options?.scientific ?? "auto",
|
|
611
|
-
eNotation: options?.eNotation ?? false,
|
|
612
|
-
autoScientificBelow: options?.autoScientificBelow ?? DEFAULT_AUTO_SCIENTIFIC_BELOW,
|
|
613
|
-
autoScientificAbove: options?.autoScientificAbove ?? DEFAULT_AUTO_SCIENTIFIC_ABOVE,
|
|
614
|
-
withUnit: options?.withUnit ?? false,
|
|
615
|
-
nonBreakingSpace: options?.nonBreakingSpace ?? false,
|
|
616
|
-
roundScientificCoefficient: options?.roundScientificCoefficient
|
|
617
|
-
};
|
|
618
|
-
const { withUnit } = optionsWithDefaults;
|
|
619
|
-
if (value === null || value === void 0) return "";
|
|
620
|
-
const { number, unit } = parseNumber(value);
|
|
621
|
-
const formattedNumber = formatDisplayNumber(number, optionsWithDefaults);
|
|
622
|
-
const formattedUnit = unit ? LABELS?.[unit] : "";
|
|
623
|
-
return withUnit && unit ? formattedNumber === "" ? formattedUnit : `${formattedNumber} ${formattedUnit}` : formattedNumber;
|
|
624
|
-
};
|
|
625
|
-
/**
|
|
626
|
-
* Displays a number with human-friendly formatting (use for non-editable display labels, text)
|
|
627
|
-
*
|
|
628
|
-
* Accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
629
|
-
*
|
|
630
|
-
* @example
|
|
631
|
-
* //returns '1 234.5600'
|
|
632
|
-
* displayNumberToFixed('1234.5600')
|
|
633
|
-
*
|
|
634
|
-
* By default, adds thousands separators. Can be configured to display in scientific notation, and with formatted units.
|
|
635
|
-
*
|
|
636
|
-
* @param value
|
|
637
|
-
* @param options
|
|
638
|
-
* @returns formatted display number
|
|
639
|
-
*/
|
|
640
|
-
const displayNumberToFixed = (value, options) => {
|
|
641
|
-
const optionsWithDefaults = {
|
|
642
|
-
scientific: options?.scientific ?? "auto",
|
|
643
|
-
eNotation: options?.eNotation ?? false,
|
|
644
|
-
autoScientificBelow: options?.autoScientificBelow ?? DEFAULT_AUTO_SCIENTIFIC_BELOW,
|
|
645
|
-
autoScientificAbove: options?.autoScientificAbove ?? DEFAULT_AUTO_SCIENTIFIC_ABOVE,
|
|
646
|
-
withUnit: options?.withUnit ?? false,
|
|
647
|
-
nonBreakingSpace: options?.nonBreakingSpace ?? false,
|
|
648
|
-
roundScientificCoefficient: options?.roundScientificCoefficient
|
|
649
|
-
};
|
|
650
|
-
const { withUnit } = optionsWithDefaults;
|
|
651
|
-
if (value === null || value === void 0) return "";
|
|
652
|
-
const { number, unit } = parseNumber(value, true);
|
|
653
|
-
const formattedNumber = formatDisplayNumber(number, {
|
|
654
|
-
...optionsWithDefaults,
|
|
655
|
-
preserveTrailingZeros: true
|
|
656
|
-
});
|
|
657
|
-
const formattedUnit = unit ? LABELS?.[unit] : "";
|
|
658
|
-
return withUnit && unit ? formattedNumber === "" ? formattedUnit : `${formattedNumber} ${formattedUnit}` : formattedNumber;
|
|
659
|
-
};
|
|
660
|
-
//#endregion
|
|
661
|
-
//#region src/utils.ts
|
|
662
|
-
const isNull = (str) => str === null;
|
|
663
|
-
const isUndefined = (str) => str === void 0;
|
|
664
|
-
const isArray = (str) => str && str.constructor === Array;
|
|
665
|
-
const isObject = (str) => str && str.constructor === Object;
|
|
666
|
-
const isEmptyString = (str) => str === "";
|
|
667
|
-
const isTrailingPeriodSeparator = (str) => str && str[str.length - 1] === ".";
|
|
668
|
-
const isTrailingCommaSeparator = (str) => str && str[str.length - 1] === ",";
|
|
669
|
-
const isPercentage = (value) => typeof value === "string" && /^\d+(\.\d+)?%$/.test(value);
|
|
670
|
-
/**
|
|
671
|
-
* Checks if input is a string that starts with '|', e.g. '|m' or '|in'
|
|
672
|
-
*
|
|
673
|
-
* @param val - string or number for checking
|
|
674
|
-
* @returns true if input starts with '|', e.g. '|m' or '|in'
|
|
675
|
-
*/
|
|
676
|
-
function isEmptyValueWithUnit(val) {
|
|
677
|
-
return typeof val === "string" && val.length > 0 && val.startsWith("|");
|
|
678
|
-
}
|
|
679
|
-
const trimWhiteSpace = (value) => value.trim().replace(/\s+/g, " ");
|
|
680
|
-
/**
|
|
681
|
-
* Check if provided argument is number
|
|
682
|
-
* @param v - value for checking
|
|
683
|
-
* @returns true if valid number
|
|
684
|
-
*/
|
|
685
|
-
/**
|
|
686
|
-
* Checks if a value can be parsed as a valid fraction (uses fraction.js)
|
|
687
|
-
*
|
|
688
|
-
* @param value - The value to check.
|
|
689
|
-
* @returns True if the value is a valid fraction, false otherwise.
|
|
690
|
-
*/
|
|
691
|
-
const isFraction = (value) => {
|
|
692
|
-
if (typeof value !== "string") return false;
|
|
693
|
-
if (!value.includes("/")) return false;
|
|
694
|
-
try {
|
|
695
|
-
new fraction_js.default(trimWhiteSpace(value));
|
|
696
|
-
return true;
|
|
697
|
-
} catch (error) {
|
|
698
|
-
return error.message === "Division by Zero";
|
|
699
|
-
}
|
|
700
|
-
};
|
|
701
|
-
function isNumeric(v) {
|
|
702
|
-
if (v === void 0 || v === null || Array.isArray(v) || typeof v === "object" || Number.isNaN(v) || v === "NaN" || v === Infinity || v === -Infinity || v === "Infinity" || v === "-Infinity") return false;
|
|
703
|
-
if (isFraction(v)) return true;
|
|
704
|
-
return typeof v === "string" ? !isNaN(parseFloat(v)) && (EXP_NOTATION_RE.test(v) || isFinite(v)) : isFinite(v);
|
|
705
|
-
}
|
|
706
|
-
/**
|
|
707
|
-
* Check array values if all are numbers
|
|
708
|
-
* @param arr - array of items
|
|
709
|
-
* @returns true if all array items are numbers
|
|
710
|
-
*/
|
|
711
|
-
function allNumbers(arr) {
|
|
712
|
-
return !arr.some((val) => typeof val !== "number");
|
|
713
|
-
}
|
|
714
|
-
/**
|
|
715
|
-
* Outputs a human-friendly formatted number for UI display (with thousands separators)
|
|
716
|
-
*
|
|
717
|
-
* @deprecated use displayNumber instead
|
|
718
|
-
* @param number - anything for converting to pretty format
|
|
719
|
-
* @returns string with formatted display number
|
|
720
|
-
*/
|
|
721
|
-
const formatNumber = (number) => displayNumber(number);
|
|
722
|
-
/**
|
|
723
|
-
* Counts all occurence of set character
|
|
724
|
-
* From http://stackoverflow.com/questions/4009756/how-to-count-string-occurrence-in-string
|
|
725
|
-
* @param chr - character that will be counted in string
|
|
726
|
-
* @param str - string in which the character will be counted
|
|
727
|
-
* @returns number of character occurencies
|
|
728
|
-
*/
|
|
729
|
-
function charCount(chr, str) {
|
|
730
|
-
const single_char = (chr + "")[0];
|
|
731
|
-
let total = 0;
|
|
732
|
-
let last_location = str.indexOf(single_char, 0) + 1;
|
|
733
|
-
while (last_location > 0) {
|
|
734
|
-
last_location = str.indexOf(single_char, last_location) + 1;
|
|
735
|
-
total += 1;
|
|
736
|
-
}
|
|
737
|
-
return total;
|
|
738
|
-
}
|
|
739
|
-
/**
|
|
740
|
-
* Calculates the number of digits to be rounded off, typically for values less than 1
|
|
741
|
-
* E.g. when trying to format 1e-9 by roundNumber(), the output value will only show '0'
|
|
742
|
-
* if just rounded off with 4 digits from roundNumber(val)
|
|
743
|
-
* Then it is more useful to calculate the number of digits to be rounded off, and pass this in,
|
|
744
|
-
* i.e. roundNumber(val, getNumberOfDigitsToShow(val))
|
|
745
|
-
* which will return 0.0000000001.
|
|
746
|
-
* Probably a lot of room for improvement here...
|
|
747
|
-
*
|
|
748
|
-
* @deprecated Use roundByMagnitude or other units package rounding functions instead
|
|
749
|
-
* @param num - number for caculating digits
|
|
750
|
-
* @param maxNumDigits - optional limiter of digits number, by default 20
|
|
751
|
-
* @returns number of digits that will be shown for defined number
|
|
752
|
-
*/
|
|
753
|
-
function getNumberOfDigitsToShow(num, maxNumDigits = 20) {
|
|
754
|
-
const defaultDigits = Math.min(4, maxNumDigits);
|
|
755
|
-
let digits = defaultDigits;
|
|
756
|
-
if (typeof num !== "number") return defaultDigits;
|
|
757
|
-
const numStr = String(num);
|
|
758
|
-
if (/-?[0-9.,]*[Ee]-?[0-9]+/.test(numStr)) {
|
|
759
|
-
while (Math.abs(num) * 10 ** digits < 1 && digits < maxNumDigits) digits++;
|
|
760
|
-
return Math.min(digits + (defaultDigits - 1), maxNumDigits);
|
|
761
|
-
}
|
|
762
|
-
if (num > 1 || num < -1) return defaultDigits;
|
|
763
|
-
for (let i = 2; i < numStr.length; i++) if (numStr[i] !== "0") return Math.min(maxNumDigits, digits + i - 2);
|
|
764
|
-
return digits;
|
|
765
|
-
}
|
|
766
|
-
/**
|
|
767
|
-
* Convert set fraction to decimal value
|
|
768
|
-
* @param str - fraction to be converted
|
|
769
|
-
* @returns either number in decimal format, Infinity if fraction is divided by 0 or NaN in other cases
|
|
770
|
-
*/
|
|
771
|
-
function fraction(str) {
|
|
772
|
-
if (str instanceof Array || str === null || str === void 0) return NaN;
|
|
773
|
-
if (typeof str === "string") str = trimWhiteSpace(str);
|
|
774
|
-
if (str === "") return NaN;
|
|
775
|
-
let result = NaN;
|
|
776
|
-
let infinite = false;
|
|
777
|
-
try {
|
|
778
|
-
const fractionObject = new fraction_js.default(str);
|
|
779
|
-
if (fractionObject !== void 0) result = fractionObject.valueOf();
|
|
780
|
-
} catch (e) {
|
|
781
|
-
if (e instanceof Error && e.message === "Division by Zero") infinite = true;
|
|
782
|
-
}
|
|
783
|
-
return infinite ? Infinity : result;
|
|
784
|
-
}
|
|
785
|
-
/**
|
|
786
|
-
* Converts decimal number to fractional format
|
|
787
|
-
* @param str - value to be converted
|
|
788
|
-
* @returns string with fractional format of set value
|
|
789
|
-
*/
|
|
790
|
-
function asFraction(str) {
|
|
791
|
-
if (typeof str === "string") str = trimWhiteSpace(str);
|
|
792
|
-
if (str === "") str = "0";
|
|
793
|
-
return new fraction_js.default(str).toFraction(true);
|
|
794
|
-
}
|
|
795
|
-
/**
|
|
796
|
-
* Convert fraction string to number (return input value if conversion fails)
|
|
797
|
-
* For historical reasons, numFraction returns the string value
|
|
798
|
-
* unmodified if it is not able to convert to a number. This is
|
|
799
|
-
* useful where user inputs are filtered through calls to
|
|
800
|
-
* numFraction. For "detecting" when numFraction fails, check
|
|
801
|
-
* if the return value is a string or a number. If it is a string
|
|
802
|
-
* it means number conversion failed.
|
|
803
|
-
*
|
|
804
|
-
* @param str - fraction to be converted
|
|
805
|
-
* @returns string or number with decimal format of fraction
|
|
806
|
-
*/
|
|
807
|
-
function numFraction(str) {
|
|
808
|
-
if (str instanceof Array || str === null || str === void 0 || str === "" || str === Infinity || str === -Infinity || str === "Infinity" || str === "-Infinity" || Number.isNaN(str) || str === "NaN") return str;
|
|
809
|
-
if (typeof str === "string") str = trimWhiteSpace(str);
|
|
810
|
-
let result = str;
|
|
811
|
-
try {
|
|
812
|
-
const fractionObject = new fraction_js.default(str);
|
|
813
|
-
if (fractionObject !== void 0) result = fractionObject.valueOf();
|
|
814
|
-
} catch (error) {
|
|
815
|
-
if (error.message === "Division by Zero") return str.charAt(0) === "-" ? -Infinity : Infinity;
|
|
816
|
-
console.warn("Error in numFraction() method: ", str);
|
|
817
|
-
}
|
|
818
|
-
return result.valueOf();
|
|
819
|
-
}
|
|
820
|
-
/**
|
|
821
|
-
* Basic trimming of string values to remove leading and trailing spaces, tabs, newlines
|
|
822
|
-
* @param value
|
|
823
|
-
* @returns trimmed string
|
|
824
|
-
*/
|
|
825
|
-
const trim = (value) => value.trim().replace(/[\t\r\n]/g, "");
|
|
826
|
-
/**
|
|
827
|
-
* Cleaning up and fixing provided number to correct numerical format
|
|
828
|
-
* removing redundant '.' dots, ',' commas, spaces
|
|
829
|
-
* @param str - string for cleaning up
|
|
830
|
-
* @returns cleaned/formatted string
|
|
831
|
-
*/
|
|
832
|
-
function cleanNumStr(str) {
|
|
833
|
-
let cleanString = trim(str + "");
|
|
834
|
-
const slashCount = charCount("/", cleanString);
|
|
835
|
-
const spaceCount = charCount(" ", cleanString) + charCount("\xA0", cleanString);
|
|
836
|
-
let dotcount = charCount(".", cleanString);
|
|
837
|
-
let commacount = charCount(",", cleanString);
|
|
838
|
-
if (slashCount === 0 && spaceCount > 0) cleanString = cleanString.replace(/\s/g, "");
|
|
839
|
-
if (commacount > 1) cleanString = cleanString.replace(/,/g, "");
|
|
840
|
-
if (dotcount > 1) cleanString = cleanString.replace(/\./g, "");
|
|
841
|
-
commacount = charCount(",", cleanString);
|
|
842
|
-
dotcount = charCount(".", cleanString);
|
|
843
|
-
if (dotcount === 1 && commacount === 1) {
|
|
844
|
-
if (cleanString.indexOf(",") > cleanString.indexOf(".")) {
|
|
845
|
-
cleanString = cleanString.replace(".", "");
|
|
846
|
-
cleanString = cleanString.replace(",", ".");
|
|
847
|
-
} else cleanString = cleanString.replace(",", "");
|
|
848
|
-
if (cleanString.indexOf(".") === 0) cleanString = 0 + cleanString;
|
|
849
|
-
return cleanString;
|
|
850
|
-
}
|
|
851
|
-
if (!dotcount && commacount) cleanString = cleanString.replace(",", ".");
|
|
852
|
-
if (cleanString.indexOf(".") === 0) cleanString = 0 + cleanString;
|
|
853
|
-
return cleanString;
|
|
854
|
-
}
|
|
855
|
-
const stripLeadingZeros = (value) => {
|
|
856
|
-
const isMinus = value?.[0] === "-";
|
|
857
|
-
const cleanedValue = value.replace(/^-/gm, "").replace(/^(?:0+(?=[1-9])|0+(?=0))/gm, "");
|
|
858
|
-
return isMinus ? `-${cleanedValue}` : cleanedValue;
|
|
859
|
-
};
|
|
860
|
-
/**
|
|
861
|
-
* Cleaning and fixing numerical string but returns it as number
|
|
862
|
-
* @param str - string for cleaning up
|
|
863
|
-
* @returns number after cleanup and fixing
|
|
864
|
-
*/
|
|
865
|
-
function cleanNum(str) {
|
|
866
|
-
if (typeof str === "number") return str;
|
|
867
|
-
return parseFloat(cleanNumStr(str));
|
|
868
|
-
}
|
|
869
|
-
/**
|
|
870
|
-
* Check if value is non-numerical
|
|
871
|
-
*
|
|
872
|
-
* @param value
|
|
873
|
-
* @return boolean
|
|
874
|
-
*/
|
|
875
|
-
const isNonNumerical = (value) => !isNumeric(value);
|
|
876
|
-
const { abs, exp, sqrt } = Math;
|
|
877
|
-
/**
|
|
878
|
-
* The 1D iterative Newton's method
|
|
879
|
-
*
|
|
880
|
-
* @param f
|
|
881
|
-
* @param df
|
|
882
|
-
* @param x0
|
|
883
|
-
* @param tol
|
|
884
|
-
* @param max_iter
|
|
885
|
-
*/
|
|
886
|
-
function newton(f, df, x0, tol = 1e-8, max_iter = 30) {
|
|
887
|
-
let X = x0;
|
|
888
|
-
let iter = 0;
|
|
889
|
-
let err = 1;
|
|
890
|
-
while (err > tol && iter < max_iter) {
|
|
891
|
-
const X_new = X - f(X) / df(X);
|
|
892
|
-
err = abs(f(X_new) - f(X));
|
|
893
|
-
if (err < tol) return [
|
|
894
|
-
X_new,
|
|
895
|
-
true,
|
|
896
|
-
iter
|
|
897
|
-
];
|
|
898
|
-
else X = X_new;
|
|
899
|
-
iter++;
|
|
900
|
-
}
|
|
901
|
-
return [
|
|
902
|
-
x0,
|
|
903
|
-
false,
|
|
904
|
-
iter
|
|
905
|
-
];
|
|
906
|
-
}
|
|
907
|
-
/**
|
|
908
|
-
* Approximating the error function erf
|
|
909
|
-
*
|
|
910
|
-
* @param z
|
|
911
|
-
*/
|
|
912
|
-
function erf(z) {
|
|
913
|
-
const t = 1 / (1 + .5 * abs(z));
|
|
914
|
-
const ans = 1 - t * exp(-(z ** 2) - 1.26551223 + t * (1.00002368 + t * (.37409196 + t * (.09678418 + t * (-.18628806 + t * (.27886807 + t * (-1.13520398 + t * (1.48851587 + t * (-.82215223 + t * .17087277)))))))));
|
|
915
|
-
return z >= 0 ? ans : -ans;
|
|
916
|
-
}
|
|
917
|
-
/**
|
|
918
|
-
* Convert from confidence interval to number of standard deviations
|
|
919
|
-
*
|
|
920
|
-
* @param a
|
|
921
|
-
*/
|
|
922
|
-
function get_k_from_conf_int(a) {
|
|
923
|
-
const f = (k) => a - erf(k / sqrt(2));
|
|
924
|
-
const df = (k) => -sqrt(2 / Math.PI) * exp(-.5 * k ** 2);
|
|
925
|
-
const [t0] = newton(f, df, 1);
|
|
926
|
-
return t0;
|
|
927
|
-
}
|
|
928
|
-
/**
|
|
929
|
-
* Convert from number of standard deviations to confidence interval
|
|
930
|
-
*
|
|
931
|
-
* @param k
|
|
932
|
-
*/
|
|
933
|
-
function get_conf_int_from_k(k) {
|
|
934
|
-
return erf(k / sqrt(2));
|
|
935
|
-
}
|
|
936
|
-
const normalizeExponent = (input) => {
|
|
937
|
-
return input.replace(/^([+-]?(?:\d+\.?\d*|\.\d+))[ ]*[eE][ ]*([+-]?[ ]*\d+)/, (_m, mantissa, exp) => {
|
|
938
|
-
let m = mantissa.endsWith(".") ? mantissa.slice(0, -1) : mantissa;
|
|
939
|
-
if (m.startsWith(".")) m = "0" + m;
|
|
940
|
-
const e = exp.replace(/[ ]+/g, "");
|
|
941
|
-
return `${m}e${e}`;
|
|
942
|
-
});
|
|
943
|
-
};
|
|
944
|
-
function normalizeScientific(val) {
|
|
945
|
-
if (typeof val !== "string") return val;
|
|
946
|
-
let s = val.replace(/\u00A0|\u2007|\u202F/g, " ").trim();
|
|
947
|
-
if (isValueWithUnit(s)) {
|
|
948
|
-
const [val, unit] = split(s);
|
|
949
|
-
return `${normalizeExponent(val.trim())}|${unit.trim()}`;
|
|
950
|
-
}
|
|
951
|
-
const m = s.match(/^([+-]?(?:\d+(?:\.\d*)?|\.\d+)\s*[eE]\s*[+-]?\s*\d+)\s+(.+)$/);
|
|
952
|
-
if (m) return `${normalizeExponent(m[1])}|${m[2].trim()}`;
|
|
953
|
-
return normalizeExponent(s);
|
|
954
|
-
}
|
|
955
|
-
//#endregion
|
|
956
|
-
//#region src/constants.ts
|
|
957
|
-
/**
|
|
958
|
-
* Units labels
|
|
959
|
-
*
|
|
960
|
-
* @readonly
|
|
961
|
-
* @enum {Object}
|
|
962
|
-
*/
|
|
963
|
-
const LABELS = Object.freeze({
|
|
964
|
-
in: "in",
|
|
965
|
-
mm: "mm",
|
|
966
|
-
cm: "cm",
|
|
967
|
-
m: "m",
|
|
968
|
-
microM: "μm",
|
|
969
|
-
km: "km",
|
|
970
|
-
ft: "ft",
|
|
971
|
-
usft: "usft",
|
|
972
|
-
in2: "in²",
|
|
973
|
-
cm2: "cm²",
|
|
974
|
-
m2: "m²",
|
|
975
|
-
kg: "kg",
|
|
976
|
-
tonnes: "t",
|
|
977
|
-
mt: "mt",
|
|
978
|
-
kip: "kip",
|
|
979
|
-
bbl: "bbl",
|
|
980
|
-
m3: "m³",
|
|
981
|
-
Mm3: "Mm³",
|
|
982
|
-
MMSCF: "MMSCF",
|
|
983
|
-
lbm: "lbm",
|
|
984
|
-
"kg/mol": "kg/mol",
|
|
985
|
-
"lbf/mol": "lbf/mol",
|
|
986
|
-
sg: "sg",
|
|
987
|
-
ppg: "ppg",
|
|
988
|
-
"kg/m3": "kg/m³",
|
|
989
|
-
"lbm/ft3": "lbm/ft³",
|
|
990
|
-
s: "s",
|
|
991
|
-
min: "min",
|
|
992
|
-
h: "h",
|
|
993
|
-
d: "d",
|
|
994
|
-
month: "month",
|
|
995
|
-
year: "year",
|
|
996
|
-
"bbl/ft": "bbl/ft",
|
|
997
|
-
lpm: "L/min",
|
|
998
|
-
lps: "L/s",
|
|
999
|
-
bpm: "bbl/min",
|
|
1000
|
-
"m3/min": "m³/min",
|
|
1001
|
-
"m3/s": "m³/s",
|
|
1002
|
-
MMSCFD: "MMSCFD",
|
|
1003
|
-
bar: "Bar",
|
|
1004
|
-
Pa: "Pa",
|
|
1005
|
-
kPa: "kPa",
|
|
1006
|
-
MPa: "MPa",
|
|
1007
|
-
GPa: "GPa",
|
|
1008
|
-
kPsi: "Psi",
|
|
1009
|
-
ksi: "ksi",
|
|
1010
|
-
"lbf/100ft2": "lbf/100ft²",
|
|
1011
|
-
"1/Pa": "Pa⁻¹",
|
|
1012
|
-
"1/kPa": "kPa⁻¹",
|
|
1013
|
-
"1/MPa": "MPa⁻¹",
|
|
1014
|
-
"1/GPa": "GPa⁻¹",
|
|
1015
|
-
"1/psi": "psi⁻¹",
|
|
1016
|
-
"kPa/m": "kPa/m",
|
|
1017
|
-
"1/bar": "bar⁻¹",
|
|
1018
|
-
klbf: "klbf",
|
|
1019
|
-
"psi/ft": "Psi/ft",
|
|
1020
|
-
"bar/100m": "bar/100m",
|
|
1021
|
-
"psi/100ft": "psi/100ft",
|
|
1022
|
-
"kPa/100m": "kPa/100m",
|
|
1023
|
-
C: "°C",
|
|
1024
|
-
F: "°F",
|
|
1025
|
-
K: "K",
|
|
1026
|
-
"C/100m": "°C/100m",
|
|
1027
|
-
"C/m": "°C/m",
|
|
1028
|
-
"Pa/C": "Pa/°C",
|
|
1029
|
-
"Bar/C": "Bar/°C",
|
|
1030
|
-
"psi/F": "psi/°F",
|
|
1031
|
-
"psi/C": "psi/°C",
|
|
1032
|
-
"F/100ft": "°F/100ft",
|
|
1033
|
-
"F/ft": "°F/ft",
|
|
1034
|
-
"K/100m": "K/100m",
|
|
1035
|
-
"K/m": "K/m",
|
|
1036
|
-
"lbf/ft": "lbf/ft",
|
|
1037
|
-
N: "N",
|
|
1038
|
-
kN: "kN",
|
|
1039
|
-
"N/m": "N/m",
|
|
1040
|
-
"daN/m": "daN/m",
|
|
1041
|
-
lbf: "lbf",
|
|
1042
|
-
kgf: "kgf",
|
|
1043
|
-
rad: "rad",
|
|
1044
|
-
"BTU/lbm": "BTU/lbm",
|
|
1045
|
-
ppf: "ppf",
|
|
1046
|
-
"kg/m": "kg/m",
|
|
1047
|
-
"E-06/degC": "10⁻⁶/°C",
|
|
1048
|
-
"E-06/degF": "10⁻⁶/°F",
|
|
1049
|
-
"1/K": "K⁻¹",
|
|
1050
|
-
km2: "km²",
|
|
1051
|
-
ft2: "ft²",
|
|
1052
|
-
mm2: "mm²",
|
|
1053
|
-
mile2: "mile²",
|
|
1054
|
-
ft3: "ft³",
|
|
1055
|
-
"g/cm3": "g/cm³",
|
|
1056
|
-
Sm3: "Sm³",
|
|
1057
|
-
"ft3/s": "ft³/s",
|
|
1058
|
-
"ft3/d": "ft³/d",
|
|
1059
|
-
"m3/d": "m³/d",
|
|
1060
|
-
"1/m3/d": "1/m³/d",
|
|
1061
|
-
"s/m3": "s/m³",
|
|
1062
|
-
"1/MMSCFD": "1/MMSCFD",
|
|
1063
|
-
"bbl/d": "bbl/d",
|
|
1064
|
-
tonneForce: "tonne-force",
|
|
1065
|
-
USGal: "US gal",
|
|
1066
|
-
"g/mol": "g/mol",
|
|
1067
|
-
Nm: "N⋅m",
|
|
1068
|
-
kNm: "kN⋅m",
|
|
1069
|
-
ftlbf: "ft⋅lbf",
|
|
1070
|
-
"J/(kg*degC)": "J/(kg⋅°C)",
|
|
1071
|
-
"J/(kg*degK)": "J/(kg⋅K)",
|
|
1072
|
-
"J/(s*m*degK)": "J/(s⋅m⋅K)",
|
|
1073
|
-
"BTU/(lbm*degF)": "BTU/(lbm⋅°F)",
|
|
1074
|
-
"BTU/(h*ft*degF)": "BTU/(h⋅ft⋅°F)",
|
|
1075
|
-
l: "L",
|
|
1076
|
-
"l/m": "L/m",
|
|
1077
|
-
"kJ/kg": "kJ/kg",
|
|
1078
|
-
"J/kg": "J/kg",
|
|
1079
|
-
deg: "°",
|
|
1080
|
-
"W/(mK)": "W/(m⋅K)",
|
|
1081
|
-
psi: "psi",
|
|
1082
|
-
"deg/100ft": "°/100ft",
|
|
1083
|
-
"deg/30m": "°/30m",
|
|
1084
|
-
"deg/10m": "°/10m",
|
|
1085
|
-
"%": "%",
|
|
1086
|
-
Hz: "Hz",
|
|
1087
|
-
"1/s": "1/s",
|
|
1088
|
-
rpm: "rpm",
|
|
1089
|
-
"Pa/m": "Pa/m",
|
|
1090
|
-
"bar/m": "bar/m",
|
|
1091
|
-
gpm: "gpm",
|
|
1092
|
-
"kg/s": "kg/s",
|
|
1093
|
-
"lbm/s": "lbm/s",
|
|
1094
|
-
"tonnes/h": "tonnes/h",
|
|
1095
|
-
"tons/h": "tons/h",
|
|
1096
|
-
"deg/m": "°/m",
|
|
1097
|
-
"deg/ft": "°/ft",
|
|
1098
|
-
"rad/m": "rad/m",
|
|
1099
|
-
"rad/ft": "rad/ft",
|
|
1100
|
-
"dyn/cm": "dyn/cm",
|
|
1101
|
-
"mN/m": "mN/m",
|
|
1102
|
-
"m/s": "m/s",
|
|
1103
|
-
"ft/s": "ft/s",
|
|
1104
|
-
"m/min": "m/min",
|
|
1105
|
-
"ft/min": "ft/min",
|
|
1106
|
-
"m/h": "m/h",
|
|
1107
|
-
"ft/h": "ft/h",
|
|
1108
|
-
mph: "mph",
|
|
1109
|
-
"km/h": "km/h",
|
|
1110
|
-
"m/s2": "m/s²",
|
|
1111
|
-
Gs: "Gs",
|
|
1112
|
-
nT: "nT",
|
|
1113
|
-
g: "g",
|
|
1114
|
-
"ft/s2": "ft/s²",
|
|
1115
|
-
"Pa*s": "Pa⋅s",
|
|
1116
|
-
P: "P",
|
|
1117
|
-
"mPa*s": "mPa⋅s",
|
|
1118
|
-
cP: "cP",
|
|
1119
|
-
W: "W",
|
|
1120
|
-
hhp: "hhp",
|
|
1121
|
-
hp: "hp",
|
|
1122
|
-
kW: "kW",
|
|
1123
|
-
MW: "MW",
|
|
1124
|
-
"BTU/h": "BTU/h",
|
|
1125
|
-
"W/m2": "W/m²",
|
|
1126
|
-
"hhp/in2": "hhp/in²",
|
|
1127
|
-
"hhp/ft2": "hhp/ft²",
|
|
1128
|
-
"Mm3/d": "Mm³/d",
|
|
1129
|
-
"STB/d": "STB/d",
|
|
1130
|
-
"Sm3/d": "Sm³/d",
|
|
1131
|
-
"Sm3/min": "Sm³/min",
|
|
1132
|
-
"MSm3/d": "MSm³/d",
|
|
1133
|
-
"SCF/STB": "SCF / STB",
|
|
1134
|
-
"Sm3/Sm3": "Sm³ / Sm³",
|
|
1135
|
-
"SCF/d": "SCF/d",
|
|
1136
|
-
STB: "STB",
|
|
1137
|
-
SCF: "SCF",
|
|
1138
|
-
MSm3: "MSm³",
|
|
1139
|
-
Gsg: "sg",
|
|
1140
|
-
Gppg: "ppg",
|
|
1141
|
-
"Gkg/m3": "kg/m³",
|
|
1142
|
-
"Glbm/ft3": "lbm/ft³",
|
|
1143
|
-
"lb/ft3": "lb/ft³",
|
|
1144
|
-
"°N": "°N",
|
|
1145
|
-
"°S": "°S",
|
|
1146
|
-
"°W": "°W",
|
|
1147
|
-
"°E": "°E",
|
|
1148
|
-
fr: " ",
|
|
1149
|
-
mD: "mD",
|
|
1150
|
-
CI: "CI",
|
|
1151
|
-
Sigma: "σ",
|
|
1152
|
-
"Sm3/d/bar": "Sm³/d/bar",
|
|
1153
|
-
"STB/d/psi": "STB/d/psi",
|
|
1154
|
-
"m3/s/bar": "m³/s/bar",
|
|
1155
|
-
"lb/ft": "lb/ft",
|
|
1156
|
-
"E-09/bar": "10⁻⁹/bar",
|
|
1157
|
-
"E-10/psi": "10⁻¹⁰/psi",
|
|
1158
|
-
"E-14/pa": "10⁻¹⁴/pa",
|
|
1159
|
-
"m3/m": " m³/m",
|
|
1160
|
-
"cm3/m": "cm³/m",
|
|
1161
|
-
"mm3/m": "mm³/m",
|
|
1162
|
-
"ft3/ft": "ft³/ft",
|
|
1163
|
-
"in3/ft": "in³/ft",
|
|
1164
|
-
lk: "lk",
|
|
1165
|
-
ftCla: "ftCla",
|
|
1166
|
-
lkCla: "lkCla",
|
|
1167
|
-
ftSe: "ftSe",
|
|
1168
|
-
ydSe: "ydSe",
|
|
1169
|
-
chSe: "chSe",
|
|
1170
|
-
"chSe(T)": "chSe(T)",
|
|
1171
|
-
ftGC: "ftGC",
|
|
1172
|
-
ydInd: "ydInd",
|
|
1173
|
-
"d/stand": "d/stand",
|
|
1174
|
-
"h/stand": "h/stand",
|
|
1175
|
-
"min/stand": "min/stand",
|
|
1176
|
-
"s/stand": "s/stand",
|
|
1177
|
-
"m3/t": "m3/t",
|
|
1178
|
-
"L/100kg": "L/100kg"
|
|
1179
|
-
});
|
|
1180
|
-
/**
|
|
1181
|
-
* Alternative units grouped by quantity
|
|
1182
|
-
*
|
|
1183
|
-
* @readonly
|
|
1184
|
-
* @enum {Object}
|
|
299
|
+
* @readonly
|
|
300
|
+
* @enum {Object}
|
|
1185
301
|
*/
|
|
1186
302
|
const ALT_UNITS = Object.freeze({
|
|
1187
303
|
acceleration: ["ft/s2", "m/s2"],
|
|
@@ -1618,13 +734,6 @@ const ALT_UNITS = Object.freeze({
|
|
|
1618
734
|
"ft3/ft",
|
|
1619
735
|
"in3/ft"
|
|
1620
736
|
],
|
|
1621
|
-
wellheadGrowthMovement: [
|
|
1622
|
-
"mm",
|
|
1623
|
-
"cm",
|
|
1624
|
-
"m",
|
|
1625
|
-
"in",
|
|
1626
|
-
"ft"
|
|
1627
|
-
],
|
|
1628
737
|
shearStress: ["Pa", "lbf/100ft2"],
|
|
1629
738
|
sensorAccelerometer: ["g", "m/s2"],
|
|
1630
739
|
sensorMagnetometer: ["nT", "Gs"],
|
|
@@ -1707,7 +816,6 @@ const UNIT_FROM_KEY = Object.freeze({
|
|
|
1707
816
|
entalphy: "J/kg",
|
|
1708
817
|
shearStress: "Pa",
|
|
1709
818
|
volumeGradient: "m3/m",
|
|
1710
|
-
wellheadGrowthMovement: "m",
|
|
1711
819
|
sensorAccelerometer: "g",
|
|
1712
820
|
sensorMagnetometer: "Gs"
|
|
1713
821
|
});
|
|
@@ -2197,495 +1305,1381 @@ const KNOWN_CONVERSIONS = Object.freeze({
|
|
|
2197
1305
|
"nT|Gs": (val) => val / 1e5
|
|
2198
1306
|
});
|
|
2199
1307
|
/**
|
|
2200
|
-
* Deprecated units
|
|
1308
|
+
* Deprecated units
|
|
1309
|
+
*
|
|
1310
|
+
* @readonly
|
|
1311
|
+
* @enum {Object}
|
|
1312
|
+
*/
|
|
1313
|
+
const DEPRECATED_UNITS = Object.freeze({
|
|
1314
|
+
"N-m": "Nm",
|
|
1315
|
+
"ft-lbf": "ftlbf",
|
|
1316
|
+
"BTU/hr": "BTU/h",
|
|
1317
|
+
"BTU/(htf*degF)": "BTU/(h*ft*degF)",
|
|
1318
|
+
"BTU/(hft*degF)": "BTU/(h*ft*degF)"
|
|
1319
|
+
});
|
|
1320
|
+
/**
|
|
1321
|
+
* This list is mapping from legal alternative unit names to our selected unit name
|
|
1322
|
+
*
|
|
1323
|
+
* @readonly
|
|
1324
|
+
* @enum {Object}
|
|
1325
|
+
*/
|
|
1326
|
+
const UNIT_ALIASES = Object.freeze({
|
|
1327
|
+
"lbs/ft": "lb/ft",
|
|
1328
|
+
"lbm/ft": "lb/ft",
|
|
1329
|
+
ftUS: "usft"
|
|
1330
|
+
});
|
|
1331
|
+
/**
|
|
1332
|
+
* Intermediate conversions
|
|
1333
|
+
*
|
|
1334
|
+
* @readonly
|
|
1335
|
+
* @enum {Object}
|
|
1336
|
+
*/
|
|
1337
|
+
const INTERMEDIATE_CONVERSIONS = Object.freeze({
|
|
1338
|
+
mm: "m",
|
|
1339
|
+
cm: "m",
|
|
1340
|
+
km: "m",
|
|
1341
|
+
ft: "m",
|
|
1342
|
+
in: "m",
|
|
1343
|
+
microM: "m",
|
|
1344
|
+
lbf: "kg",
|
|
1345
|
+
t: "kg",
|
|
1346
|
+
tonnes: "kg",
|
|
1347
|
+
mt: "kg",
|
|
1348
|
+
kip: "kg",
|
|
1349
|
+
kW: "W",
|
|
1350
|
+
MW: "W",
|
|
1351
|
+
hp: "W",
|
|
1352
|
+
hhp: "hp",
|
|
1353
|
+
mm2: "m2",
|
|
1354
|
+
cm2: "m2",
|
|
1355
|
+
km2: "m2",
|
|
1356
|
+
in2: "m2",
|
|
1357
|
+
ft2: "m2",
|
|
1358
|
+
mile2: "m2",
|
|
1359
|
+
bbl: "m3",
|
|
1360
|
+
ft3: "m3",
|
|
1361
|
+
Mm3: "m3",
|
|
1362
|
+
l: "m3",
|
|
1363
|
+
USGal: "m3",
|
|
1364
|
+
Sm3: "m3",
|
|
1365
|
+
STB: "m3",
|
|
1366
|
+
MMSCF: "m3",
|
|
1367
|
+
MSm3: "m3",
|
|
1368
|
+
SCF: "m3",
|
|
1369
|
+
kN: "N",
|
|
1370
|
+
kgf: "N",
|
|
1371
|
+
tonneForce: "N",
|
|
1372
|
+
klbf: "lbf",
|
|
1373
|
+
psi: "Pa",
|
|
1374
|
+
bar: "Pa",
|
|
1375
|
+
kPa: "Pa",
|
|
1376
|
+
MPa: "Pa",
|
|
1377
|
+
ksi: "psi",
|
|
1378
|
+
"lbf/100ft2": "psi",
|
|
1379
|
+
"psi/100ft": "Pa/m",
|
|
1380
|
+
"psi/ft": "Pa/m",
|
|
1381
|
+
"bar/100m": "Pa/m",
|
|
1382
|
+
"bar/m": "Pa/m",
|
|
1383
|
+
"Pa/m": "kPa/m",
|
|
1384
|
+
sg: "kg/m3",
|
|
1385
|
+
"g/cm3": "kg/m3",
|
|
1386
|
+
"lbm/ft3": "kg/m3",
|
|
1387
|
+
"lb/ft3": "kg/m3",
|
|
1388
|
+
ppg: "kg/m3",
|
|
1389
|
+
"kPa/m": "sg",
|
|
1390
|
+
Gsg: "Gkg/m3",
|
|
1391
|
+
Gppg: "Gkg/m3",
|
|
1392
|
+
"Glbm/ft3": "Gkg/m3",
|
|
1393
|
+
"1/psi": "1/Pa",
|
|
1394
|
+
"1/bar": "1/Pa",
|
|
1395
|
+
"1/kPa": "1/Pa",
|
|
1396
|
+
"1/MPa": "1/Pa",
|
|
1397
|
+
"1/GPa": "1/Pa",
|
|
1398
|
+
"ft/s": "m/s",
|
|
1399
|
+
"ft/min": "m/s",
|
|
1400
|
+
"ft/h": "m/s",
|
|
1401
|
+
"m/min": "m/s",
|
|
1402
|
+
"m/h": "m/s",
|
|
1403
|
+
mph: "m/s",
|
|
1404
|
+
"ft3/s": "m3/s",
|
|
1405
|
+
lpm: "m3/s",
|
|
1406
|
+
lps: "lpm",
|
|
1407
|
+
bpm: "m3/s",
|
|
1408
|
+
"m3/d": "m3/s",
|
|
1409
|
+
"bbl/d": "m3/s",
|
|
1410
|
+
"ft3/d": "m3/s",
|
|
1411
|
+
"STB/d": "m3/s",
|
|
1412
|
+
"Sm3/d": "m3/s",
|
|
1413
|
+
"Sm3/min": "m3/s",
|
|
1414
|
+
gpm: "m3/s",
|
|
1415
|
+
MMSCFD: "m3/s",
|
|
1416
|
+
"Mm3/d": "m3/d",
|
|
1417
|
+
"m3/min": "m3/s",
|
|
1418
|
+
"MSm3/d": "m3/s",
|
|
1419
|
+
"lbf/ft": "N/m",
|
|
1420
|
+
min: "s",
|
|
1421
|
+
h: "s",
|
|
1422
|
+
d: "s",
|
|
1423
|
+
month: "d",
|
|
1424
|
+
year: "d",
|
|
1425
|
+
"J/(kg*degK)": "J/(kg*degC)",
|
|
1426
|
+
"BTU/(lbm*degF)": "J/(kg*degC)",
|
|
1427
|
+
"BTU/(Kg*K)": "J/(kg*degC)",
|
|
1428
|
+
"BTU/(h*ft*degF)": "J/(s*m*degK)",
|
|
1429
|
+
"W/(m*degK)": "J/(s*m*degK)",
|
|
1430
|
+
"W/(mK)": "J/(s*m*degK)",
|
|
1431
|
+
K: "C",
|
|
1432
|
+
"C/m": "C/100m",
|
|
1433
|
+
"F/ft": "C/100m",
|
|
1434
|
+
"K/m": "C/100m",
|
|
1435
|
+
"F/100ft": "C/100m",
|
|
1436
|
+
"K/100m": "C/100m",
|
|
1437
|
+
"Bar/C": "Pa/C",
|
|
1438
|
+
"s/m3": "1/m3/d",
|
|
1439
|
+
"1/MMSCFD": "1/m3/d",
|
|
1440
|
+
P: "Pa*s",
|
|
1441
|
+
"mPa*s": "Pa*s",
|
|
1442
|
+
cP: "Pa*s",
|
|
1443
|
+
"hhp/in2": "W/m2",
|
|
1444
|
+
"hhp/ft2": "W/m2",
|
|
1445
|
+
"Sm3/d/bar": "m3/s/bar",
|
|
1446
|
+
"STB/d/psi": "m3/s/bar",
|
|
1447
|
+
"deg/100ft": "deg/m",
|
|
1448
|
+
"deg/30m": "deg/m",
|
|
1449
|
+
"deg/10m": "deg/m",
|
|
1450
|
+
"deg/ft": "deg/m",
|
|
1451
|
+
"rad/ft": "deg/m",
|
|
1452
|
+
"lbf/mol": "kg/mol",
|
|
1453
|
+
"g/mol": "kg/mol",
|
|
1454
|
+
"1/K": "E-06/degC",
|
|
1455
|
+
"E-09/bar": "E-10/psi",
|
|
1456
|
+
"E-14/pa": "E-10/psi",
|
|
1457
|
+
"1/Pa": "E-14/pa",
|
|
1458
|
+
kNm: "Nm",
|
|
1459
|
+
ftlbf: "Nm",
|
|
1460
|
+
lk: "m",
|
|
1461
|
+
ftCla: "m",
|
|
1462
|
+
lkCla: "m",
|
|
1463
|
+
ftSe: "m",
|
|
1464
|
+
ydSe: "m",
|
|
1465
|
+
chSe: "m",
|
|
1466
|
+
"chSe(T)": "m",
|
|
1467
|
+
ftGC: "m",
|
|
1468
|
+
ydInd: "m",
|
|
1469
|
+
"BTU/lbm": "J/kg"
|
|
1470
|
+
});
|
|
1471
|
+
/**
|
|
1472
|
+
* List of all known units in application
|
|
1473
|
+
*
|
|
1474
|
+
* @readonly
|
|
1475
|
+
* @enum {string[]}
|
|
1476
|
+
*/
|
|
1477
|
+
const KNOWN_UNITS = Object.freeze(Array.from(new Set(Object.values(ALT_UNITS).flat())));
|
|
1478
|
+
const SPECIAL_NUMBERS_STRING = [
|
|
1479
|
+
NaN,
|
|
1480
|
+
-Infinity,
|
|
1481
|
+
Infinity
|
|
1482
|
+
].map((number) => number.toString());
|
|
1483
|
+
/**
|
|
1484
|
+
* Description for the different quantites
|
|
1485
|
+
* @readonly
|
|
1486
|
+
*/
|
|
1487
|
+
const QUANTITIES_DESCRIPTION = {
|
|
1488
|
+
density: "Density",
|
|
1489
|
+
length: "Length",
|
|
1490
|
+
duration: "Duration",
|
|
1491
|
+
temperature: "Temperature",
|
|
1492
|
+
tempgrad: "Temperature Gradient",
|
|
1493
|
+
volume: "Volume",
|
|
1494
|
+
weight: "Weight",
|
|
1495
|
+
angles: "Angles",
|
|
1496
|
+
depth: "Depth",
|
|
1497
|
+
distance: "Distances",
|
|
1498
|
+
height: "Height",
|
|
1499
|
+
diameters: "Diameters",
|
|
1500
|
+
doglegSeverity: "Dogleg severity",
|
|
1501
|
+
fluidCompressibility: "Fluid Compressibility",
|
|
1502
|
+
force: "Force",
|
|
1503
|
+
gasVolume: "Gas volume",
|
|
1504
|
+
oilVolume: "Oil volume",
|
|
1505
|
+
moleWeight: "Mole weight",
|
|
1506
|
+
linearCapacity: "Linear Capacity",
|
|
1507
|
+
stress: "Stress",
|
|
1508
|
+
thermalConductivity: "Thermal conductivity",
|
|
1509
|
+
specificHeatCapacity: "Specific heat capacity",
|
|
1510
|
+
thermalExpansionCoefficient: "Thermal expansion coefficient",
|
|
1511
|
+
youngsModulus: "Youngs Modulus",
|
|
1512
|
+
torque: "Torque",
|
|
1513
|
+
areaOther: "Area - Other",
|
|
1514
|
+
areaTubular: "Area - Tubular",
|
|
1515
|
+
pumpRate: "Pump Rate",
|
|
1516
|
+
pressure: "Pressure",
|
|
1517
|
+
blowoutFlowRate: "Flowrate (blowout)",
|
|
1518
|
+
percentage: "Percentage",
|
|
1519
|
+
frequency: "Frequency",
|
|
1520
|
+
torqueGradient: "Torque gradient",
|
|
1521
|
+
pressureGradient: "Pressure gradient",
|
|
1522
|
+
flowrate: "Volumetric flow rate",
|
|
1523
|
+
massFlowRate: "Mass flow rate",
|
|
1524
|
+
angleGradient: "Angle gradient",
|
|
1525
|
+
weightGradient: "Weight gradient",
|
|
1526
|
+
forceGradient: "Force gradient",
|
|
1527
|
+
interfacialTension: "Interfacial tension",
|
|
1528
|
+
acceleration: "Acceleration",
|
|
1529
|
+
viscosity: "Viscosity",
|
|
1530
|
+
power: "Power",
|
|
1531
|
+
intensity: "Power intensity",
|
|
1532
|
+
gasliftFlowRate: "Flowrate (Gas lift)",
|
|
1533
|
+
productionFlowRate: "Flowrate (production)",
|
|
1534
|
+
productionFlowRateOil: "Flowrate for Oil (production)",
|
|
1535
|
+
productionFlowRateGas: "Flowrate for Gas (production)",
|
|
1536
|
+
injectionFlowRate: "Flowrate (injection)",
|
|
1537
|
+
blowoutOilFlowRate: "Flowrate for Oil (blowout)",
|
|
1538
|
+
blowoutGasFlowRate: "Flowrate for Gas (blowout)",
|
|
1539
|
+
gor: "Gas Oil Ratio",
|
|
1540
|
+
rotationalSpeed: "Rotational Speed",
|
|
1541
|
+
densityGas: "Density for gas",
|
|
1542
|
+
inflowProductivityIndex: "Inflow productivity index",
|
|
1543
|
+
latitude: "Latitude",
|
|
1544
|
+
longitude: "Longitude",
|
|
1545
|
+
permeability: "Permeability",
|
|
1546
|
+
sdstats: "Standard deviation",
|
|
1547
|
+
roughness: "Material Roughness",
|
|
1548
|
+
wltubulars: "Tubular weight",
|
|
1549
|
+
speed: "Velocity",
|
|
1550
|
+
inverseStandSpeed: "Inverse Stand velocity",
|
|
1551
|
+
rop: "Rate of Penetration (ROP)",
|
|
1552
|
+
densityOil: "Density for oil",
|
|
1553
|
+
densityOilGas: "Density for oil/gas",
|
|
1554
|
+
kickToleranceVolume: "Volume for kick tolerance",
|
|
1555
|
+
densitySolid: "Density for solid",
|
|
1556
|
+
massPerLength: "Mass Per Length",
|
|
1557
|
+
durationShort: "Duration (TempSim short)",
|
|
1558
|
+
durationLong: "Duration (TempSim long)",
|
|
1559
|
+
wearFactor: "Wear factor",
|
|
1560
|
+
turbulentSkin: "Turbulent skin",
|
|
1561
|
+
pressurePerTemperature: "Pressure per temperature",
|
|
1562
|
+
location: "Location coordinates length",
|
|
1563
|
+
mixingRequirements: "Ratio between water and cement",
|
|
1564
|
+
Entalphy: "Entalphy",
|
|
1565
|
+
shearStress: "Shear Stress, force parallel to surface",
|
|
1566
|
+
volumeGradient: "Rate of Volume change per unit",
|
|
1567
|
+
deg: "Degree, a unit of angular measurement",
|
|
1568
|
+
dls: "Dogleg Severity, measure of wellbore curvature changes per unit length",
|
|
1569
|
+
wgrad: "Weight Gradient, change in weight per unit length",
|
|
1570
|
+
entalphy: "Entalphy, total heat content of a system",
|
|
1571
|
+
pressurechange: "Change in pressure over time or between two points",
|
|
1572
|
+
rpm: "Rotations per minute, a measure of the frequency of rotation, specifying the number of full rotations completed in one minute around a fixed axis",
|
|
1573
|
+
sensorAccelerometer: "Strength of gravitational acceleration",
|
|
1574
|
+
sensorMagnetometer: "Strength of magnetic forces at a position"
|
|
1575
|
+
};
|
|
1576
|
+
/**
|
|
1577
|
+
* Description for the different units
|
|
1578
|
+
* @readonly
|
|
1579
|
+
*/
|
|
1580
|
+
const UNITS_DESCRIPTION = {
|
|
1581
|
+
in: "Inches",
|
|
1582
|
+
mm: "Milimeters",
|
|
1583
|
+
cm: "Centimeters",
|
|
1584
|
+
m: "Meters",
|
|
1585
|
+
km: "Kilometers",
|
|
1586
|
+
ft: "Feets",
|
|
1587
|
+
usft: "US Feets",
|
|
1588
|
+
in2: "Square inches",
|
|
1589
|
+
cm2: "Square centimeters",
|
|
1590
|
+
m2: "Square meters",
|
|
1591
|
+
kg: "Kilograms",
|
|
1592
|
+
tonnes: "Tonnes",
|
|
1593
|
+
mt: "Metric tonnes",
|
|
1594
|
+
kip: "Kip",
|
|
1595
|
+
bbl: "Barrels",
|
|
1596
|
+
Mm3: "Mega cubic meters",
|
|
1597
|
+
MMSCF: "Million Standard Cubic Feet",
|
|
1598
|
+
lbm: "Pound mass",
|
|
1599
|
+
"kg/mol": "Kilograms per mole",
|
|
1600
|
+
"lbf/mol": "Pounds per mole",
|
|
1601
|
+
sg: "Specific gravity",
|
|
1602
|
+
ppg: "Pounds per gallon",
|
|
1603
|
+
"kg/m3": "Kilogram per cubic meters",
|
|
1604
|
+
"lbm/ft3": "Pounds per cubic foot",
|
|
1605
|
+
s: "Seconds",
|
|
1606
|
+
min: "Minutes",
|
|
1607
|
+
h: "Hours",
|
|
1608
|
+
d: "Days",
|
|
1609
|
+
month: "Months",
|
|
1610
|
+
year: "Years",
|
|
1611
|
+
"bbl/ft": "Barrels per foot",
|
|
1612
|
+
lpm: "Litres per minute",
|
|
1613
|
+
bpm: "Barrels per minute",
|
|
1614
|
+
"m3/min": "Cubic meters per minute",
|
|
1615
|
+
"m3/s": "Cubic meters per second",
|
|
1616
|
+
MMSCFD: "Million Standard Cubic Feet per day",
|
|
1617
|
+
"1/MMSCFD": "Inverse Million Standard Cubic Feet per day",
|
|
1618
|
+
bar: "Bar",
|
|
1619
|
+
Pa: "Pascals",
|
|
1620
|
+
kPa: "Kilopascals",
|
|
1621
|
+
MPa: "Megapascals",
|
|
1622
|
+
kPsi: "Kilo pounds per square inch",
|
|
1623
|
+
"kPa/m": "Kilopascals per meter",
|
|
1624
|
+
"psi/ft": "Psi per foot",
|
|
1625
|
+
"bar/100m": "Bars per 100m",
|
|
1626
|
+
"psi/100ft": "Psi per 100ft",
|
|
1627
|
+
"kPa/100m": "Kilopascals per 100m",
|
|
1628
|
+
C: "Degrees Celsius",
|
|
1629
|
+
F: "Degrees Fahrenheit",
|
|
1630
|
+
K: "Kelvins",
|
|
1631
|
+
"C/100m": "Degrees Celsius per 100m",
|
|
1632
|
+
"F/100ft": "Degrees Fahrenheit per 100m",
|
|
1633
|
+
"K/100m": "Kelvins per 100m",
|
|
1634
|
+
"lbf/ft": "Pound force / foot",
|
|
1635
|
+
"Pa/C": "Pascal per celsius",
|
|
1636
|
+
"Bar/C": "Bar per celsius",
|
|
1637
|
+
"psi/F": "Psi per fahrenheit",
|
|
1638
|
+
"psi/C": "Psi per celsius",
|
|
1639
|
+
N: "Newtons",
|
|
1640
|
+
kN: "Kilo Newtons",
|
|
1641
|
+
"N/m": "Newtons per meter",
|
|
1642
|
+
"daN/m": "Decanewtons per meter",
|
|
1643
|
+
lbf: "Pound force",
|
|
1644
|
+
kgf: "Kilogram force",
|
|
1645
|
+
rad: "Radians",
|
|
1646
|
+
"BTU/lbm": "British Thermal Units per pound",
|
|
1647
|
+
ppf: "Pound per foot",
|
|
1648
|
+
"kg/m": "Kilograms per meter",
|
|
1649
|
+
"E-06/degC": "Micro per degree Celsius",
|
|
1650
|
+
"E-06/degF": "Micro per degree Fahrenheit",
|
|
1651
|
+
km2: "Square kilometers",
|
|
1652
|
+
ft2: "Square feet",
|
|
1653
|
+
mm2: "Square millimeters",
|
|
1654
|
+
mile2: "Square miles",
|
|
1655
|
+
ft3: "Cubic feet",
|
|
1656
|
+
"g/cm3": "Grams per cubic centimeter",
|
|
1657
|
+
Sm3: "Standard cubic meter",
|
|
1658
|
+
"ft3/s": "Cubic feet per second",
|
|
1659
|
+
"ft3/d": "Cubic feet per day",
|
|
1660
|
+
"m3/d": "Cubic meter per day",
|
|
1661
|
+
"1/m3/d": "Inverse Cubic meter per day",
|
|
1662
|
+
"s/m3": "Seconds per cubic meters",
|
|
1663
|
+
"bbl/d": "Barrels per day",
|
|
1664
|
+
tonneForce: "Tonne force",
|
|
1665
|
+
USGal: "US gallon",
|
|
1666
|
+
"g/mol": "Grams per mol",
|
|
1667
|
+
Nm: "Newton meter",
|
|
1668
|
+
kNm: "Kilo Newton meter",
|
|
1669
|
+
ftlbf: "Foot pound",
|
|
1670
|
+
"J/(kg*degC)": "Joules per kilogram degree Celsius",
|
|
1671
|
+
"J/(kg*degK)": "Joules per kilogram degree Kelwin",
|
|
1672
|
+
"BTU/(lbm*degF)": "British Thermal Unit per pound Fahrenheit",
|
|
1673
|
+
"BTU/(h*ft*degF)": "British Thermal Units per hour feet degree Fahrenheit",
|
|
1674
|
+
l: "Litres",
|
|
1675
|
+
"l/m": "Litres per meter",
|
|
1676
|
+
"kJ/kg": "Kilo joules per kilogram",
|
|
1677
|
+
"J/kg": "Joules per kilogram",
|
|
1678
|
+
deg: "Degrees",
|
|
1679
|
+
"W/(mK)": "Watts per milli Kelvin",
|
|
1680
|
+
psi: "Pounds per square inch",
|
|
1681
|
+
"1/bar": "1/bar",
|
|
1682
|
+
"1/psi": "1/psi",
|
|
1683
|
+
"deg/100ft": "Degrees per 100ft",
|
|
1684
|
+
"deg/10m": "Degrees per 10m",
|
|
1685
|
+
"deg/30m": "Degrees per 30m",
|
|
1686
|
+
"%": "Percent",
|
|
1687
|
+
Hz: "Hertz",
|
|
1688
|
+
"1/s": "Inverse second",
|
|
1689
|
+
rpm: "Revolutions per minute",
|
|
1690
|
+
"Pa/m": "Pascal per meter",
|
|
1691
|
+
"bar/m": "Bar per meter",
|
|
1692
|
+
gpm: "Gallons per minute",
|
|
1693
|
+
"kg/s": "Kilograms per second",
|
|
1694
|
+
"lbm/s": "Pound mass per second",
|
|
1695
|
+
"tonnes/h": "Tonnes per hour",
|
|
1696
|
+
"tons/h": "Tons per hour",
|
|
1697
|
+
"deg/m": "Degrees per meter",
|
|
1698
|
+
"deg/ft": "Degrees per foot",
|
|
1699
|
+
"rad/m": "Radians per meter",
|
|
1700
|
+
"rad/ft": "Radians per foot",
|
|
1701
|
+
"dyn/cm": "Dyn per centimeter",
|
|
1702
|
+
"mN/m": "Millinewtons per meter",
|
|
1703
|
+
"1/kPa": "1/kPa",
|
|
1704
|
+
m3: "Cubic meters",
|
|
1705
|
+
"m/s": "Meters per second",
|
|
1706
|
+
"ft/s": "Feet per second",
|
|
1707
|
+
"m/min": "Meters per minute",
|
|
1708
|
+
"ft/min": "Feet per min",
|
|
1709
|
+
"m/h": "Meters per hour",
|
|
1710
|
+
"ft/h": "Feet per hour",
|
|
1711
|
+
mph: "Miles per hour",
|
|
1712
|
+
"km/h": "Kilometers per hour",
|
|
1713
|
+
"m/s2": "Meters per second squared",
|
|
1714
|
+
"ft/s2": "Feet per second squared",
|
|
1715
|
+
"Pa*s": "Pascal seconds",
|
|
1716
|
+
P: "Poise (dyne second per square centimeter)",
|
|
1717
|
+
"mPa*s": "Millipascal seconds",
|
|
1718
|
+
cP: "Centi Poise",
|
|
1719
|
+
W: "Watts",
|
|
1720
|
+
hhp: "Hydraulic horsepower",
|
|
1721
|
+
hp: "Horsepower",
|
|
1722
|
+
kW: "Kilowatts",
|
|
1723
|
+
MW: "Megawatts",
|
|
1724
|
+
"BTU/h": "British Thermal Units per hour",
|
|
1725
|
+
"hhp/in2": "Hydraulic horsepower per square inch",
|
|
1726
|
+
"hhp/ft2": "Hydraulic horsepower per square feet",
|
|
1727
|
+
"Mm3/d": "Mega cubic meters per day",
|
|
1728
|
+
"STB/d": "Stock Tank Barrel per day",
|
|
1729
|
+
"Sm3/d": "Standard cubic meters per day",
|
|
1730
|
+
"Sm3/min": "Standard cubic meters per minute",
|
|
1731
|
+
"MSm3/d": "Mega standard cubic meters per day",
|
|
1732
|
+
"SCF/STB": "Standard Cubic Feet per Stock Tank Barrel",
|
|
1733
|
+
"Sm3/Sm3": "Standard cubic meters per Standard cubic meter",
|
|
1734
|
+
"SCF/d": "Standard cubic feet per day",
|
|
1735
|
+
STB: "Stock Tank Barrel",
|
|
1736
|
+
SCF: "Standard Cubic Feet",
|
|
1737
|
+
Gsg: "Gas - specific gravity",
|
|
1738
|
+
Gppg: "Gas - pounds per gallon",
|
|
1739
|
+
"Gkg/m3": "Gas - kilogram per cubic meters",
|
|
1740
|
+
"Glbm/ft3": "Gas - pounds per cubic foot",
|
|
1741
|
+
MSm3: "Mega standard cubic meters",
|
|
1742
|
+
"m3/s/bar": "Cubic per second per bar",
|
|
1743
|
+
"Sm3/d/bar": "Standard cubic per day per bar",
|
|
1744
|
+
"STB/d/psi": "Standard barrels per day per psi",
|
|
1745
|
+
klbf: "kilopound force",
|
|
1746
|
+
"1/Pa": "1/Pascal",
|
|
1747
|
+
"1/MPa": "1/MPa",
|
|
1748
|
+
ksi: "Kilopound per square inch",
|
|
1749
|
+
"lbf/100ft2": "Pounds per 100 square foot",
|
|
1750
|
+
"lb/ft3": "lb/ft3",
|
|
1751
|
+
"°N": "°N (latitude)",
|
|
1752
|
+
"°S": "°S (latitude)",
|
|
1753
|
+
"°W": "°W (longitude)",
|
|
1754
|
+
"°E": "°E (longitude)",
|
|
1755
|
+
fr: "Fraction",
|
|
1756
|
+
mD: "Millidarcy",
|
|
1757
|
+
Sigma: "Sigma",
|
|
1758
|
+
CI: "Confidence Interval",
|
|
1759
|
+
"J/(s*m*degK)": "Joules per second meter Kelvin",
|
|
1760
|
+
"C/m": "Degrees Celsius per meter",
|
|
1761
|
+
"F/ft": "Degrees Fahrenheit per meter",
|
|
1762
|
+
"K/m": "Kelvin per meter",
|
|
1763
|
+
microM: "Micro meter",
|
|
1764
|
+
"W/m2": "Watt per square meter",
|
|
1765
|
+
"1/K": "Inverse Kelvin",
|
|
1766
|
+
"lb/ft": "Pound Per Feet",
|
|
1767
|
+
"E-09/bar": "Nano per bar",
|
|
1768
|
+
"E-10/psi": "10⁻¹⁰ per psi",
|
|
1769
|
+
"E-14/pa": "10⁻¹⁴ per pascal",
|
|
1770
|
+
lk: "Link",
|
|
1771
|
+
ftCla: "Clark`s foot",
|
|
1772
|
+
lkCla: "Clark`s link",
|
|
1773
|
+
ftSe: "British foot (Sears 1922)",
|
|
1774
|
+
ydSe: "British yard (Sears 1922)",
|
|
1775
|
+
chSe: "British chain (Sears 1922)",
|
|
1776
|
+
"chSe(T)": "British chain (Sears 1922 Truncated)",
|
|
1777
|
+
ftGC: "Gold Coast foot",
|
|
1778
|
+
ydInd: "Indian yard",
|
|
1779
|
+
"d/stand": "Day per stand",
|
|
1780
|
+
"h/stand": "Hour per stand",
|
|
1781
|
+
"min/stand": "Minute per stand",
|
|
1782
|
+
"s/stand": "Second per stand",
|
|
1783
|
+
"m3/t": "Cubic meter per ton",
|
|
1784
|
+
"L/100kg": "Liter per 100kg",
|
|
1785
|
+
GPa: "Gigapascals, unit of pressure",
|
|
1786
|
+
"cm3/m": "Cubic cm per meter",
|
|
1787
|
+
"in3/ft": "Cubic inches per foot",
|
|
1788
|
+
"ft3/ft": "Cubic feet per foot",
|
|
1789
|
+
"m3/m": "Cubic meters per meter",
|
|
1790
|
+
"mm3/m": "Cubic millimeters per meter",
|
|
1791
|
+
"1/GPa": "Inverse gigapascal",
|
|
1792
|
+
lps: "Liters per second",
|
|
1793
|
+
nT: "nanoTesla",
|
|
1794
|
+
Gs: "Gauss",
|
|
1795
|
+
g: "g force"
|
|
1796
|
+
};
|
|
1797
|
+
//#endregion
|
|
1798
|
+
//#region src/units/unit-catalog.ts
|
|
1799
|
+
/** Returns the units configured for a quantity key. */
|
|
1800
|
+
function showAltUnitsList(quantityKey) {
|
|
1801
|
+
return ALT_UNITS[quantityKey];
|
|
1802
|
+
}
|
|
1803
|
+
/** Returns the units configured for a quantity. */
|
|
1804
|
+
function getUnitsForQuantity(quantity) {
|
|
1805
|
+
return showAltUnitsList(quantity);
|
|
1806
|
+
}
|
|
1807
|
+
/** Returns all configured quantity keys. */
|
|
1808
|
+
function getQuantities() {
|
|
1809
|
+
return Object.keys(ALT_UNITS);
|
|
1810
|
+
}
|
|
1811
|
+
/** Returns the display label for a unit key. */
|
|
1812
|
+
function label(unitKey) {
|
|
1813
|
+
return LABELS[unitKey];
|
|
1814
|
+
}
|
|
1815
|
+
/** Returns the base unit configured for a quantity. */
|
|
1816
|
+
function unitFromKey(quantity) {
|
|
1817
|
+
return UNIT_FROM_KEY[quantity];
|
|
1818
|
+
}
|
|
1819
|
+
/** Alias for `unitFromKey`. */
|
|
1820
|
+
function unitFromQuantity(quantity) {
|
|
1821
|
+
return unitFromKey(quantity);
|
|
1822
|
+
}
|
|
1823
|
+
/** Returns the alternative units and their display labels for a quantity. */
|
|
1824
|
+
function getAltUnitsListByQuantity(quantity) {
|
|
1825
|
+
const quantityUnitList = showAltUnitsList(quantity);
|
|
1826
|
+
return quantityUnitList ? quantityUnitList.map((unit) => ({
|
|
1827
|
+
unit,
|
|
1828
|
+
label: label(unit)
|
|
1829
|
+
})) : void 0;
|
|
1830
|
+
}
|
|
1831
|
+
//#endregion
|
|
1832
|
+
//#region src/numbers/parsing/number-input.ts
|
|
1833
|
+
/** Basic trimming of numeric input strings. */
|
|
1834
|
+
const trim = (value) => {
|
|
1835
|
+
return value.trim().replace(/[\t\r\n]/g, "");
|
|
1836
|
+
};
|
|
1837
|
+
/** Counts occurrences of a character in a string. */
|
|
1838
|
+
function charCount(character, value) {
|
|
1839
|
+
const singleCharacter = String(character)[0];
|
|
1840
|
+
let total = 0;
|
|
1841
|
+
let lastLocation = value.indexOf(singleCharacter, 0) + 1;
|
|
1842
|
+
while (lastLocation > 0) {
|
|
1843
|
+
lastLocation = value.indexOf(singleCharacter, lastLocation) + 1;
|
|
1844
|
+
total += 1;
|
|
1845
|
+
}
|
|
1846
|
+
return total;
|
|
1847
|
+
}
|
|
1848
|
+
/** Normalizes supported decimal and thousands separators in a numeric input string. */
|
|
1849
|
+
function cleanNumStr(value) {
|
|
1850
|
+
let cleanString = trim(String(value));
|
|
1851
|
+
const slashCount = charCount("/", cleanString);
|
|
1852
|
+
const spaceCount = charCount(" ", cleanString) + charCount("\xA0", cleanString);
|
|
1853
|
+
let dotCount = charCount(".", cleanString);
|
|
1854
|
+
let commaCount = charCount(",", cleanString);
|
|
1855
|
+
if (slashCount === 0 && spaceCount > 0) cleanString = cleanString.replace(/\s/g, "");
|
|
1856
|
+
if (commaCount > 1) cleanString = cleanString.replace(/,/g, "");
|
|
1857
|
+
if (dotCount > 1) cleanString = cleanString.replace(/\./g, "");
|
|
1858
|
+
commaCount = charCount(",", cleanString);
|
|
1859
|
+
dotCount = charCount(".", cleanString);
|
|
1860
|
+
if (dotCount === 1 && commaCount === 1) {
|
|
1861
|
+
if (cleanString.indexOf(",") > cleanString.indexOf(".")) {
|
|
1862
|
+
cleanString = cleanString.replace(".", "");
|
|
1863
|
+
cleanString = cleanString.replace(",", ".");
|
|
1864
|
+
} else cleanString = cleanString.replace(",", "");
|
|
1865
|
+
if (cleanString.indexOf(".") === 0) cleanString = `0${cleanString}`;
|
|
1866
|
+
return cleanString;
|
|
1867
|
+
}
|
|
1868
|
+
if (!dotCount && commaCount) cleanString = cleanString.replace(",", ".");
|
|
1869
|
+
if (cleanString.indexOf(".") === 0) cleanString = `0${cleanString}`;
|
|
1870
|
+
return cleanString;
|
|
1871
|
+
}
|
|
1872
|
+
/** Removes redundant leading zeros while preserving the sign and fractional part. */
|
|
1873
|
+
const stripLeadingZeros = (value) => {
|
|
1874
|
+
const isMinus = value?.[0] === "-";
|
|
1875
|
+
const cleanedValue = value.replace(/^-/gm, "").replace(/^(?:0+(?=[1-9])|0+(?=0))/gm, "");
|
|
1876
|
+
return isMinus ? `-${cleanedValue}` : cleanedValue;
|
|
1877
|
+
};
|
|
1878
|
+
/** Normalizes a numeric input string and parses it as a number. */
|
|
1879
|
+
function cleanNum(value) {
|
|
1880
|
+
if (typeof value === "number") return value;
|
|
1881
|
+
return parseFloat(cleanNumStr(value));
|
|
1882
|
+
}
|
|
1883
|
+
const isNull = (value) => {
|
|
1884
|
+
return value === null;
|
|
1885
|
+
};
|
|
1886
|
+
const isUndefined = (value) => {
|
|
1887
|
+
return value === void 0;
|
|
1888
|
+
};
|
|
1889
|
+
const isArray = (value) => {
|
|
1890
|
+
return Boolean(value) && value.constructor === Array;
|
|
1891
|
+
};
|
|
1892
|
+
const isObject = (value) => {
|
|
1893
|
+
return Boolean(value) && value.constructor === Object;
|
|
1894
|
+
};
|
|
1895
|
+
const isEmptyString = (value) => {
|
|
1896
|
+
return value === "";
|
|
1897
|
+
};
|
|
1898
|
+
const isTrailingPeriodSeparator = (value) => {
|
|
1899
|
+
return Boolean(value) && String(value)[String(value).length - 1] === ".";
|
|
1900
|
+
};
|
|
1901
|
+
const isTrailingCommaSeparator = (value) => {
|
|
1902
|
+
return Boolean(value) && String(value)[String(value).length - 1] === ",";
|
|
1903
|
+
};
|
|
1904
|
+
//#endregion
|
|
1905
|
+
//#region src/units/unit-string.ts
|
|
1906
|
+
const SEPARATOR = "|";
|
|
1907
|
+
const UNIT_RE = /^(-?[0-9., /]*?(?:e[-+]?[0-9]+)?)([^0-9-., /].*)?$/;
|
|
1908
|
+
/** Checks whether a unit string has an empty numeric part, for example `|m`. */
|
|
1909
|
+
function isEmptyValueWithUnit(value) {
|
|
1910
|
+
return typeof value === "string" && value.length > 0 && value.startsWith("|");
|
|
1911
|
+
}
|
|
1912
|
+
/** Splits a supported unit string into its numeric and unit parts. */
|
|
1913
|
+
function split(valueWithUnit) {
|
|
1914
|
+
let match;
|
|
1915
|
+
let normalizedValue = valueWithUnit !== void 0 && valueWithUnit !== null ? String(valueWithUnit) : "";
|
|
1916
|
+
if (charCount("|", normalizedValue) > 1) {
|
|
1917
|
+
match = normalizedValue.split("|");
|
|
1918
|
+
normalizedValue = match.slice(0, -1).join("") + "|" + match.slice(-1);
|
|
1919
|
+
}
|
|
1920
|
+
if (normalizedValue.indexOf("|") >= 0) match = normalizedValue.split("|");
|
|
1921
|
+
else if (SPECIAL_NUMBERS_STRING.includes(normalizedValue)) match = [normalizedValue, ""];
|
|
1922
|
+
else {
|
|
1923
|
+
match = cleanNumStr(normalizedValue).match(UNIT_RE);
|
|
1924
|
+
if (match) match = match.slice(1);
|
|
1925
|
+
}
|
|
1926
|
+
if (!match) match = ["0", ""];
|
|
1927
|
+
if (match[1] == null) match[1] = "";
|
|
1928
|
+
return [match[0], match[1]];
|
|
1929
|
+
}
|
|
1930
|
+
/** Returns the numeric part of a unit string. */
|
|
1931
|
+
function getValue(valueWithUnit) {
|
|
1932
|
+
return split(valueWithUnit)[0];
|
|
1933
|
+
}
|
|
1934
|
+
/** Returns the unit part of a unit string. */
|
|
1935
|
+
function getUnit(valueWithUnit) {
|
|
1936
|
+
return split(valueWithUnit)[1];
|
|
1937
|
+
}
|
|
1938
|
+
/** Checks whether a value contains exactly one known unit suffix. */
|
|
1939
|
+
function isValueWithUnit(value) {
|
|
1940
|
+
if (!value) return false;
|
|
1941
|
+
const parts = String(value).split("|");
|
|
1942
|
+
return parts.length === 2 && KNOWN_UNITS.includes(parts[1]);
|
|
1943
|
+
}
|
|
1944
|
+
/** Joins a value and unit using the package unit separator. */
|
|
1945
|
+
function withUnit(value, unit, defaultValue = "") {
|
|
1946
|
+
if (value === null || value === "" || value === void 0) value = defaultValue;
|
|
1947
|
+
if (unit === null) return String(value);
|
|
1948
|
+
let [normalizedValue, normalizedUnit] = String(value).includes("|") ? split(String(value)) : [value, unit];
|
|
1949
|
+
if (!normalizedUnit) normalizedUnit = unit;
|
|
1950
|
+
return [normalizedValue, normalizedUnit].join("|");
|
|
1951
|
+
}
|
|
1952
|
+
/** Replaces a unit key in a unit string with its display label. */
|
|
1953
|
+
function withPrettyUnitLabel(valueWithUnits) {
|
|
1954
|
+
const [value, unit] = split(valueWithUnits);
|
|
1955
|
+
return `${value} ${LABELS[unit] ?? ""}`;
|
|
1956
|
+
}
|
|
1957
|
+
//#endregion
|
|
1958
|
+
//#region src/numbers/fractions/fractions.ts
|
|
1959
|
+
const normalizeFractionWhitespace = (value) => {
|
|
1960
|
+
return value.trim().replace(/\s+/g, " ");
|
|
1961
|
+
};
|
|
1962
|
+
/** Checks whether a string can be parsed as a fraction by fraction.js. */
|
|
1963
|
+
const isFraction = (value) => {
|
|
1964
|
+
if (typeof value !== "string" || !value.includes("/")) return false;
|
|
1965
|
+
try {
|
|
1966
|
+
new fraction_js.default(normalizeFractionWhitespace(value));
|
|
1967
|
+
return true;
|
|
1968
|
+
} catch (error) {
|
|
1969
|
+
return error.message === "Division by Zero";
|
|
1970
|
+
}
|
|
1971
|
+
};
|
|
1972
|
+
/** Converts a fraction to its decimal value, or returns `NaN` when it cannot be parsed. */
|
|
1973
|
+
function fraction(value) {
|
|
1974
|
+
if (value instanceof Array || value === null || value === void 0) return NaN;
|
|
1975
|
+
if (typeof value === "string") value = normalizeFractionWhitespace(value);
|
|
1976
|
+
if (value === "") return NaN;
|
|
1977
|
+
let result = NaN;
|
|
1978
|
+
let infinite = false;
|
|
1979
|
+
try {
|
|
1980
|
+
const fractionObject = new fraction_js.default(value);
|
|
1981
|
+
if (fractionObject !== void 0) result = fractionObject.valueOf();
|
|
1982
|
+
} catch (error) {
|
|
1983
|
+
if (error instanceof Error && error.message === "Division by Zero") infinite = true;
|
|
1984
|
+
}
|
|
1985
|
+
return infinite ? Infinity : result;
|
|
1986
|
+
}
|
|
1987
|
+
/** Converts a decimal value to a fractional string. */
|
|
1988
|
+
function asFraction(value) {
|
|
1989
|
+
if (typeof value === "string") value = normalizeFractionWhitespace(value);
|
|
1990
|
+
if (value === "") value = "0";
|
|
1991
|
+
return new fraction_js.default(value).toFraction(true);
|
|
1992
|
+
}
|
|
1993
|
+
/**
|
|
1994
|
+
* Converts a fraction string to a number.
|
|
1995
|
+
*
|
|
1996
|
+
* For compatibility, invalid inputs are returned unchanged and division by zero returns signed infinity.
|
|
1997
|
+
*/
|
|
1998
|
+
function numFraction(value) {
|
|
1999
|
+
if (value instanceof Array || value === null || value === void 0 || value === "" || value === Infinity || value === -Infinity || value === "Infinity" || value === "-Infinity" || Number.isNaN(value) || value === "NaN") return value;
|
|
2000
|
+
if (typeof value === "string") value = normalizeFractionWhitespace(value);
|
|
2001
|
+
let result = value;
|
|
2002
|
+
try {
|
|
2003
|
+
const fractionObject = new fraction_js.default(value);
|
|
2004
|
+
if (fractionObject !== void 0) result = fractionObject.valueOf();
|
|
2005
|
+
} catch (error) {
|
|
2006
|
+
if (error.message === "Division by Zero") return value.charAt(0) === "-" ? -Infinity : Infinity;
|
|
2007
|
+
console.warn("Error in numFraction() method: ", value);
|
|
2008
|
+
}
|
|
2009
|
+
return result.valueOf();
|
|
2010
|
+
}
|
|
2011
|
+
//#endregion
|
|
2012
|
+
//#region src/numbers/scientific-notation/scientific-notation.ts
|
|
2013
|
+
const EXP_NOTATION_RE = /^[-+]?[0-9]*\.?[0-9]+(?:\/[0-9]*\.?[0-9]+)?(?:[eE][-+]?[0-9]+)?$/;
|
|
2014
|
+
/** Normalizes whitespace and mantissa syntax in a scientific-notation string. */
|
|
2015
|
+
const normalizeExponent = (input) => {
|
|
2016
|
+
return input.replace(/^([+-]?(?:\d+\.?\d*|\.\d+))[ ]*[eE][ ]*([+-]?[ ]*\d+)/, (_match, mantissa, exponent) => {
|
|
2017
|
+
let normalizedMantissa = mantissa.endsWith(".") ? mantissa.slice(0, -1) : mantissa;
|
|
2018
|
+
if (normalizedMantissa.startsWith(".")) normalizedMantissa = `0${normalizedMantissa}`;
|
|
2019
|
+
const normalizedExponent = exponent.replace(/[ ]+/g, "");
|
|
2020
|
+
return `${normalizedMantissa}e${normalizedExponent}`;
|
|
2021
|
+
});
|
|
2022
|
+
};
|
|
2023
|
+
/** Normalizes scientific notation while preserving an optional unit suffix. */
|
|
2024
|
+
function normalizeScientific(value) {
|
|
2025
|
+
if (typeof value !== "string") return value;
|
|
2026
|
+
const normalizedSpaces = value.replace(/\u00A0|\u2007|\u202F/g, " ").trim();
|
|
2027
|
+
if (isValueWithUnit(normalizedSpaces)) {
|
|
2028
|
+
const [number, unit] = split(normalizedSpaces);
|
|
2029
|
+
return `${normalizeExponent(number.trim())}|${unit.trim()}`;
|
|
2030
|
+
}
|
|
2031
|
+
const match = normalizedSpaces.match(/^([+-]?(?:\d+(?:\.\d*)?|\.\d+)\s*[eE]\s*[+-]?\s*\d+)\s+(.+)$/);
|
|
2032
|
+
if (match) return `${normalizeExponent(match[1])}|${match[2].trim()}`;
|
|
2033
|
+
return normalizeExponent(normalizedSpaces);
|
|
2034
|
+
}
|
|
2035
|
+
//#endregion
|
|
2036
|
+
//#region src/numbers/predicates.ts
|
|
2037
|
+
/** Checks whether a value is supported as a finite numeric or fraction input. */
|
|
2038
|
+
function isNumeric(value) {
|
|
2039
|
+
if (value === void 0 || value === null || Array.isArray(value) || typeof value === "object" || Number.isNaN(value) || value === "NaN" || value === Infinity || value === -Infinity || value === "Infinity" || value === "-Infinity") return false;
|
|
2040
|
+
if (isFraction(value)) return true;
|
|
2041
|
+
return typeof value === "string" ? !isNaN(parseFloat(value)) && (EXP_NOTATION_RE.test(value) || isFinite(value)) : isFinite(value);
|
|
2042
|
+
}
|
|
2043
|
+
const isNonNumerical = (value) => {
|
|
2044
|
+
return !isNumeric(value);
|
|
2045
|
+
};
|
|
2046
|
+
const isPercentage = (value) => {
|
|
2047
|
+
return typeof value === "string" && /^\d+(\.\d+)?%$/.test(value);
|
|
2048
|
+
};
|
|
2049
|
+
function allNumbers(values) {
|
|
2050
|
+
return !values.some((value) => typeof value !== "number");
|
|
2051
|
+
}
|
|
2052
|
+
//#endregion
|
|
2053
|
+
//#region src/numbers/parsing/parse-number.ts
|
|
2054
|
+
const countTrailingZeros = (value, decimalPartOnly = false) => {
|
|
2055
|
+
const condition = decimalPartOnly ? /0+((?=[|eE])|$)/ : /(0+|0+\.0+)((?=[|eE])|$)/;
|
|
2056
|
+
if (typeof value === "string" && (decimalPartOnly ? value.includes(".") || value.includes(",") : true)) return value?.match(condition)?.[0]?.replaceAll(/[.,]/g, "")?.length ?? 0;
|
|
2057
|
+
return 0;
|
|
2058
|
+
};
|
|
2059
|
+
const hasTrailingZeros = (value) => {
|
|
2060
|
+
return countTrailingZeros(value) > 0;
|
|
2061
|
+
};
|
|
2062
|
+
/**
|
|
2063
|
+
* Internal function to parse the value, unit, and type from a generic numeric input
|
|
2064
|
+
*
|
|
2065
|
+
* Accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
2066
|
+
*
|
|
2067
|
+
* @param value
|
|
2068
|
+
* @returns object with number, unit, and type
|
|
2069
|
+
*/
|
|
2070
|
+
const parseNumber = (value, preserveTrailingZeros = false) => {
|
|
2071
|
+
const isString = typeof value === "string";
|
|
2072
|
+
const hasUnit = isString && isValueWithUnit(value);
|
|
2073
|
+
const unit = hasUnit ? getUnit(value) : null;
|
|
2074
|
+
const cleaned = cleanNumStr(hasUnit ? getValue(value) : value);
|
|
2075
|
+
return {
|
|
2076
|
+
number: preserveTrailingZeros && hasTrailingZeros(value) ? cleaned : toNum(cleaned),
|
|
2077
|
+
unit,
|
|
2078
|
+
isString
|
|
2079
|
+
};
|
|
2080
|
+
};
|
|
2081
|
+
/**
|
|
2082
|
+
* Convert a number to a string safely, better than String(value)
|
|
2083
|
+
* String(0.0000002) returns '2e-7' which is unwanted if we need to preserve formatting
|
|
2084
|
+
*
|
|
2085
|
+
* @param value
|
|
2086
|
+
* @param [isScientific] whether to preserve scientific notation
|
|
2087
|
+
* @returns number or string output value
|
|
2088
|
+
*/
|
|
2089
|
+
const safeStringifyNumber = (value, isScientific) => {
|
|
2090
|
+
return isScientific ? String(value) : toString(value);
|
|
2091
|
+
};
|
|
2092
|
+
/**
|
|
2093
|
+
* Internal function to unParse a value, unit, and type back to an output value
|
|
2094
|
+
*
|
|
2095
|
+
* Accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
2096
|
+
*
|
|
2097
|
+
* @param args
|
|
2098
|
+
* @param args.value
|
|
2099
|
+
* @param args.unit
|
|
2100
|
+
* @param args.isString
|
|
2101
|
+
* @param args.isScientific
|
|
2102
|
+
* @returns number or string output value
|
|
2103
|
+
*/
|
|
2104
|
+
const unParseNumber = ({ value, unit, isString, isScientific }) => {
|
|
2105
|
+
const convertedValue = typeof value === "number" && isString ? safeStringifyNumber(value, isScientific) : value;
|
|
2106
|
+
if (unit) return withUnit(convertedValue, unit);
|
|
2107
|
+
return convertedValue;
|
|
2108
|
+
};
|
|
2109
|
+
//#endregion
|
|
2110
|
+
//#region src/numbers/comparison/comparison.ts
|
|
2111
|
+
const DEFAULT_MAX_RELATIVE_DIFF = Number.EPSILON;
|
|
2112
|
+
const convertNumbers = (firstValue, secondValue) => {
|
|
2113
|
+
const { number: firstNumber, unit: firstUnit } = parseNumber(firstValue);
|
|
2114
|
+
const { number: secondNumber, unit: secondUnit } = parseNumber(secondValue);
|
|
2115
|
+
return {
|
|
2116
|
+
firstNumber,
|
|
2117
|
+
secondNumber: firstUnit && secondUnit && firstUnit !== secondUnit ? convertAndGetValue(secondNumber, firstUnit, secondUnit) : secondNumber
|
|
2118
|
+
};
|
|
2119
|
+
};
|
|
2120
|
+
const getToleranceNumber = (relativeDiff) => {
|
|
2121
|
+
if (relativeDiff !== null && relativeDiff !== void 0) {
|
|
2122
|
+
if (isNumeric(relativeDiff) && typeof relativeDiff === "number") return relativeDiff;
|
|
2123
|
+
if (isPercentage(relativeDiff)) {
|
|
2124
|
+
const percentageValue = toNum(relativeDiff?.toString().replace("%", ""));
|
|
2125
|
+
if (isNumeric(percentageValue)) return percentageValue / 100;
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
return null;
|
|
2129
|
+
};
|
|
2130
|
+
/**
|
|
2131
|
+
* Determines whether two numbers are close in value with a tolerance
|
|
2132
|
+
* (mitigates excess JavaScript floating point precision quirks)
|
|
2133
|
+
*/
|
|
2134
|
+
const isCloseTo = (firstValue, secondValue, options = { relativeDiff: DEFAULT_MAX_RELATIVE_DIFF }) => {
|
|
2135
|
+
const { relativeDiff, absoluteDiff } = options;
|
|
2136
|
+
const toleranceNumber = getToleranceNumber(relativeDiff ?? absoluteDiff);
|
|
2137
|
+
if (firstValue === null || secondValue === null) return false;
|
|
2138
|
+
const hasUnitFirstValue = isValueWithUnit(firstValue);
|
|
2139
|
+
const hasUnitSecondValue = isValueWithUnit(secondValue);
|
|
2140
|
+
if (hasUnitFirstValue && !hasUnitSecondValue || !hasUnitFirstValue && hasUnitSecondValue) throw new Error(`Parameters must either both have units or both not have units. Received "${firstValue}" and "${secondValue}"`);
|
|
2141
|
+
if (toleranceNumber === null) {
|
|
2142
|
+
console.warn("Tolerance number is not defined!");
|
|
2143
|
+
return firstValue === secondValue;
|
|
2144
|
+
}
|
|
2145
|
+
if (toleranceNumber <= 0 || toleranceNumber < Number.EPSILON) throw Error("Unpredictable results - toleranceNumber should be bigger than zero or less then EPSILON");
|
|
2146
|
+
const { firstNumber, secondNumber } = convertNumbers(firstValue, secondValue);
|
|
2147
|
+
if ((firstNumber === Infinity || firstNumber === "Infinity") && (secondNumber === Infinity || secondNumber === "Infinity") || (firstNumber === -Infinity || firstNumber === "-Infinity") && (secondNumber === -Infinity || secondNumber === "-Infinity")) return true;
|
|
2148
|
+
if (typeof firstNumber === "number" && typeof secondNumber === "number") {
|
|
2149
|
+
if (firstNumber === secondNumber) return true;
|
|
2150
|
+
if (absoluteDiff || firstNumber === 0 || secondNumber === 0) {
|
|
2151
|
+
const diff = Math.abs(firstNumber - secondNumber);
|
|
2152
|
+
return isCloseTo(diff, toleranceNumber, { relativeDiff: "1%" }) || diff < toleranceNumber;
|
|
2153
|
+
} else return 2 * Math.abs((firstNumber - secondNumber) / (firstNumber + secondNumber)) < toleranceNumber;
|
|
2154
|
+
}
|
|
2155
|
+
return false;
|
|
2156
|
+
};
|
|
2157
|
+
/**
|
|
2158
|
+
* Determines whether two numbers are close enough to be equal
|
|
2159
|
+
* or checks the firstValue is greater than the secondValue
|
|
2160
|
+
*/
|
|
2161
|
+
const isCloseToOrGreaterThan = (firstValue, secondValue, options = { relativeDiff: DEFAULT_MAX_RELATIVE_DIFF }) => {
|
|
2162
|
+
if (firstValue === null || secondValue === null) return false;
|
|
2163
|
+
const { firstNumber, secondNumber } = convertNumbers(firstValue, secondValue);
|
|
2164
|
+
if (typeof firstNumber === "number" && typeof secondNumber === "number") return isCloseTo(firstNumber, secondNumber, options) || firstNumber > secondNumber;
|
|
2165
|
+
return false;
|
|
2166
|
+
};
|
|
2167
|
+
/**
|
|
2168
|
+
* Determines whether two numbers are close enough to be equal
|
|
2169
|
+
* or checks the firstValue is less than the secondValue
|
|
2170
|
+
*/
|
|
2171
|
+
const isCloseToOrLessThan = (firstValue, secondValue, options = { relativeDiff: DEFAULT_MAX_RELATIVE_DIFF }) => {
|
|
2172
|
+
if (firstValue === null || secondValue === null) return false;
|
|
2173
|
+
const { firstNumber, secondNumber } = convertNumbers(firstValue, secondValue);
|
|
2174
|
+
if (typeof firstNumber === "number" && typeof secondNumber === "number") return isCloseTo(firstNumber, secondNumber, options) || firstNumber < secondNumber;
|
|
2175
|
+
return false;
|
|
2176
|
+
};
|
|
2177
|
+
/**
|
|
2178
|
+
* Determines whether two objects or arrays are deeply close equal (all nested child numbers)
|
|
2179
|
+
*/
|
|
2180
|
+
const isDeepCloseTo = (a, b, options = { relativeDiff: DEFAULT_MAX_RELATIVE_DIFF }) => {
|
|
2181
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
2182
|
+
if (a.length !== b.length) return false;
|
|
2183
|
+
return a.every((a, i) => isDeepCloseTo(a, b[i], options));
|
|
2184
|
+
}
|
|
2185
|
+
if (typeof a === "object" && a !== null && typeof b === "object" && b !== null) {
|
|
2186
|
+
const aKeys = Object.keys(a);
|
|
2187
|
+
const bKeys = Object.keys(b);
|
|
2188
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
2189
|
+
return aKeys.every((key) => isDeepCloseTo(a[key], b[key], options));
|
|
2190
|
+
}
|
|
2191
|
+
if (Number.isNaN(a) && Number.isNaN(b) || a === "" && b === "") return true;
|
|
2192
|
+
if (typeof a === "number" && typeof b === "number" || isValueWithUnit(a) && isValueWithUnit(b) || isValidNum(a) && isValidNum(b)) return isCloseTo(a, b, options);
|
|
2193
|
+
return true;
|
|
2194
|
+
};
|
|
2195
|
+
//#endregion
|
|
2196
|
+
//#region src/numbers/rounding/rounding.ts
|
|
2197
|
+
const DEFAULT_SIGNIFICANT_DIGITS = 4;
|
|
2198
|
+
/**
|
|
2199
|
+
* Rounds a number to N decimal places.
|
|
2200
|
+
*
|
|
2201
|
+
* @private (see round() for the public interface)
|
|
2202
|
+
* @param value
|
|
2203
|
+
* @param [n]
|
|
2204
|
+
* @returns rounded number
|
|
2205
|
+
*/
|
|
2206
|
+
const roundNumber = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
2207
|
+
const factor = 10 ** n;
|
|
2208
|
+
return Math.round(value * factor) / factor;
|
|
2209
|
+
};
|
|
2210
|
+
/**
|
|
2211
|
+
* Rounds a numeric value to N decimal places.
|
|
2212
|
+
*
|
|
2213
|
+
* - accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
2214
|
+
* - returns same type as input
|
|
2215
|
+
*
|
|
2216
|
+
* @param value - the value to round
|
|
2217
|
+
* @param [n] - the number of decimal places to round to
|
|
2218
|
+
* @returns rounded value, or input value when unable to round
|
|
2219
|
+
*
|
|
2220
|
+
* @example
|
|
2221
|
+
* round(3.14159265) -> 3.1416
|
|
2222
|
+
*/
|
|
2223
|
+
const round = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
2224
|
+
if (typeof value === "number") return roundNumber(value, n);
|
|
2225
|
+
if (value === null || Number.isNaN(value) || value === Infinity || value === -Infinity || value === void 0) return value;
|
|
2226
|
+
const { number, unit, isString } = parseNumber(value);
|
|
2227
|
+
return unParseNumber({
|
|
2228
|
+
value: !isNumeric(number) || typeof number === "string" && number.endsWith(".") ? value : roundNumber(number, n),
|
|
2229
|
+
unit,
|
|
2230
|
+
isString,
|
|
2231
|
+
isScientific: isScientificStringNum(value)
|
|
2232
|
+
});
|
|
2233
|
+
};
|
|
2234
|
+
/**
|
|
2235
|
+
* Rounds a number to N significant digits.
|
|
2236
|
+
*
|
|
2237
|
+
* @private (see roundToPrecision() for the public interface)
|
|
2238
|
+
* @param value
|
|
2239
|
+
* @param [n] number of significant digits
|
|
2240
|
+
* @returns rounded number
|
|
2241
|
+
*/
|
|
2242
|
+
const roundNumberToPrecision = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
2243
|
+
return Number(value.toPrecision(n));
|
|
2244
|
+
};
|
|
2245
|
+
/**
|
|
2246
|
+
* Rounds a number to N significant digits, preserving trailing decimal zeros
|
|
2247
|
+
*
|
|
2248
|
+
* @private (see roundByMagnitudeToFixed() for the public interface)
|
|
2249
|
+
* @param value
|
|
2250
|
+
* @param [n] number of significant digits
|
|
2251
|
+
* @returns rounded number as string
|
|
2252
|
+
*/
|
|
2253
|
+
const roundNumberToFixedPrecision = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
2254
|
+
const [integerPart, decimalPart] = toNum(value).toPrecision(n).split(".");
|
|
2255
|
+
if (decimalPart) {
|
|
2256
|
+
const decimalDigits = n - integerPart.length;
|
|
2257
|
+
return `${integerPart}.${decimalPart.padEnd(decimalDigits, "0")}`;
|
|
2258
|
+
} else return integerPart;
|
|
2259
|
+
};
|
|
2260
|
+
/**
|
|
2261
|
+
* Rounds a number to N significant digits, excluding the integer part (only rounds decimal part)
|
|
2262
|
+
*
|
|
2263
|
+
* @private (see roundToPrecision() for the public interface)
|
|
2264
|
+
* @param value
|
|
2265
|
+
* @param [n] number of significant digits
|
|
2266
|
+
* @returns rounded number
|
|
2267
|
+
*/
|
|
2268
|
+
const roundNumberToDecimalPrecision = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
2269
|
+
if (Math.abs(value) > 1) {
|
|
2270
|
+
const [integerPart, decimalPart] = formatDecimal(value, "").split(".");
|
|
2271
|
+
if (!decimalPart) return value;
|
|
2272
|
+
else {
|
|
2273
|
+
const roundedDecimalPart = Number(`0.${decimalPart}`).toPrecision(n).slice(1);
|
|
2274
|
+
return Number(integerPart + roundedDecimalPart);
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
return roundNumberToPrecision(value, n);
|
|
2278
|
+
};
|
|
2279
|
+
/**
|
|
2280
|
+
* Rounds a numeric value to N significant digits.
|
|
2201
2281
|
*
|
|
2202
|
-
*
|
|
2203
|
-
*
|
|
2282
|
+
* - accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
2283
|
+
* - returns same type as input
|
|
2284
|
+
* - similar to Number.toPrecision() but safer
|
|
2285
|
+
* - does *not* append trailing zeros (unlike `Number(4).toPrecision(4)` -> '4.000')
|
|
2286
|
+
*
|
|
2287
|
+
* @param value - the value to round
|
|
2288
|
+
* @param [n] - the number of significant digits
|
|
2289
|
+
* @returns rounded value, or input value when unable to round
|
|
2290
|
+
*
|
|
2291
|
+
* @example
|
|
2292
|
+
* roundToPrecision(0.0000456789) -> 0.00004568
|
|
2204
2293
|
*/
|
|
2205
|
-
const
|
|
2206
|
-
"
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2294
|
+
const roundToPrecision = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
2295
|
+
if (typeof value === "number") return roundNumberToPrecision(value, n);
|
|
2296
|
+
if (value === null || Number.isNaN(value) || value === Infinity || value === -Infinity || value === void 0) return value;
|
|
2297
|
+
const { number, unit, isString } = parseNumber(value);
|
|
2298
|
+
return unParseNumber({
|
|
2299
|
+
value: !isNumeric(number) || typeof number === "string" && number.endsWith(".") ? value : roundNumberToPrecision(number, n),
|
|
2300
|
+
unit,
|
|
2301
|
+
isString,
|
|
2302
|
+
isScientific: isScientificStringNum(value)
|
|
2303
|
+
});
|
|
2304
|
+
};
|
|
2212
2305
|
/**
|
|
2213
|
-
*
|
|
2306
|
+
* Rounds a numeric value to N significant digits (only the decimal part)
|
|
2214
2307
|
*
|
|
2215
|
-
*
|
|
2216
|
-
*
|
|
2308
|
+
* - accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
2309
|
+
* - returns same type as input
|
|
2310
|
+
* - unlike roundToPrecision(), acts only on the decimal part, not the integer part too
|
|
2311
|
+
*
|
|
2312
|
+
* @param value - the value to round
|
|
2313
|
+
* @param [n]- the number of significant digits
|
|
2314
|
+
* @returns rounded value, or input value when unable to round
|
|
2315
|
+
*
|
|
2316
|
+
* @example
|
|
2317
|
+
* roundToPrecision(1234.0000456789) -> 1234.00004568
|
|
2217
2318
|
*/
|
|
2218
|
-
const
|
|
2219
|
-
"
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2319
|
+
const roundToDecimalPrecision = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
2320
|
+
if (typeof value === "number") return roundNumberToDecimalPrecision(value, n);
|
|
2321
|
+
if (value === null || Number.isNaN(value) || value === Infinity || value === -Infinity || value === void 0) return value;
|
|
2322
|
+
const { number, unit, isString } = parseNumber(value);
|
|
2323
|
+
return unParseNumber({
|
|
2324
|
+
value: !isNumeric(number) || typeof number === "string" && number.endsWith(".") ? value : roundNumberToDecimalPrecision(number, n),
|
|
2325
|
+
unit,
|
|
2326
|
+
isString,
|
|
2327
|
+
isScientific: isScientificStringNum(value)
|
|
2328
|
+
});
|
|
2329
|
+
};
|
|
2223
2330
|
/**
|
|
2224
|
-
*
|
|
2331
|
+
* Rounds a number to an appropriate number of digits, based on its size.
|
|
2225
2332
|
*
|
|
2226
|
-
* @
|
|
2227
|
-
* @
|
|
2333
|
+
* @private (see roundByMagnitude() for the public interface)
|
|
2334
|
+
* @param value
|
|
2335
|
+
* @param [n] the number of significant digits
|
|
2336
|
+
* @param [toFixed] to fixed digits (i.e. with trailing zeros)
|
|
2337
|
+
* @returns rounded number
|
|
2228
2338
|
*/
|
|
2229
|
-
const
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
"
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
"1/kPa": "1/Pa",
|
|
2288
|
-
"1/MPa": "1/Pa",
|
|
2289
|
-
"1/GPa": "1/Pa",
|
|
2290
|
-
"ft/s": "m/s",
|
|
2291
|
-
"ft/min": "m/s",
|
|
2292
|
-
"ft/h": "m/s",
|
|
2293
|
-
"m/min": "m/s",
|
|
2294
|
-
"m/h": "m/s",
|
|
2295
|
-
mph: "m/s",
|
|
2296
|
-
"ft3/s": "m3/s",
|
|
2297
|
-
lpm: "m3/s",
|
|
2298
|
-
lps: "lpm",
|
|
2299
|
-
bpm: "m3/s",
|
|
2300
|
-
"m3/d": "m3/s",
|
|
2301
|
-
"bbl/d": "m3/s",
|
|
2302
|
-
"ft3/d": "m3/s",
|
|
2303
|
-
"STB/d": "m3/s",
|
|
2304
|
-
"Sm3/d": "m3/s",
|
|
2305
|
-
"Sm3/min": "m3/s",
|
|
2306
|
-
gpm: "m3/s",
|
|
2307
|
-
MMSCFD: "m3/s",
|
|
2308
|
-
"Mm3/d": "m3/d",
|
|
2309
|
-
"m3/min": "m3/s",
|
|
2310
|
-
"MSm3/d": "m3/s",
|
|
2311
|
-
"lbf/ft": "N/m",
|
|
2312
|
-
min: "s",
|
|
2313
|
-
h: "s",
|
|
2314
|
-
d: "s",
|
|
2315
|
-
month: "d",
|
|
2316
|
-
year: "d",
|
|
2317
|
-
"J/(kg*degK)": "J/(kg*degC)",
|
|
2318
|
-
"BTU/(lbm*degF)": "J/(kg*degC)",
|
|
2319
|
-
"BTU/(Kg*K)": "J/(kg*degC)",
|
|
2320
|
-
"BTU/(h*ft*degF)": "J/(s*m*degK)",
|
|
2321
|
-
"W/(m*degK)": "J/(s*m*degK)",
|
|
2322
|
-
"W/(mK)": "J/(s*m*degK)",
|
|
2323
|
-
K: "C",
|
|
2324
|
-
"C/m": "C/100m",
|
|
2325
|
-
"F/ft": "C/100m",
|
|
2326
|
-
"K/m": "C/100m",
|
|
2327
|
-
"F/100ft": "C/100m",
|
|
2328
|
-
"K/100m": "C/100m",
|
|
2329
|
-
"Bar/C": "Pa/C",
|
|
2330
|
-
"s/m3": "1/m3/d",
|
|
2331
|
-
"1/MMSCFD": "1/m3/d",
|
|
2332
|
-
P: "Pa*s",
|
|
2333
|
-
"mPa*s": "Pa*s",
|
|
2334
|
-
cP: "Pa*s",
|
|
2335
|
-
"hhp/in2": "W/m2",
|
|
2336
|
-
"hhp/ft2": "W/m2",
|
|
2337
|
-
"Sm3/d/bar": "m3/s/bar",
|
|
2338
|
-
"STB/d/psi": "m3/s/bar",
|
|
2339
|
-
"deg/100ft": "deg/m",
|
|
2340
|
-
"deg/30m": "deg/m",
|
|
2341
|
-
"deg/10m": "deg/m",
|
|
2342
|
-
"deg/ft": "deg/m",
|
|
2343
|
-
"rad/ft": "deg/m",
|
|
2344
|
-
"lbf/mol": "kg/mol",
|
|
2345
|
-
"g/mol": "kg/mol",
|
|
2346
|
-
"1/K": "E-06/degC",
|
|
2347
|
-
"E-09/bar": "E-10/psi",
|
|
2348
|
-
"E-14/pa": "E-10/psi",
|
|
2349
|
-
"1/Pa": "E-14/pa",
|
|
2350
|
-
kNm: "Nm",
|
|
2351
|
-
ftlbf: "Nm",
|
|
2352
|
-
lk: "m",
|
|
2353
|
-
ftCla: "m",
|
|
2354
|
-
lkCla: "m",
|
|
2355
|
-
ftSe: "m",
|
|
2356
|
-
ydSe: "m",
|
|
2357
|
-
chSe: "m",
|
|
2358
|
-
"chSe(T)": "m",
|
|
2359
|
-
ftGC: "m",
|
|
2360
|
-
ydInd: "m",
|
|
2361
|
-
"BTU/lbm": "J/kg"
|
|
2362
|
-
});
|
|
2339
|
+
const roundNumberByMagnitude = (value, n = DEFAULT_SIGNIFICANT_DIGITS, toFixed = false) => {
|
|
2340
|
+
const noDecimalsAbove = 10 ** n;
|
|
2341
|
+
const result = noDecimalsAbove && value > noDecimalsAbove ? roundNumber(value, 0) : toFixed ? roundNumberToFixedPrecision(value, n) : roundNumberToPrecision(value, n);
|
|
2342
|
+
return toFixed ? String(result) : result;
|
|
2343
|
+
};
|
|
2344
|
+
/**
|
|
2345
|
+
* Rounds a numeric value to an appropriate number of digits, based on its size. It rounds to N significant digits,
|
|
2346
|
+
* but never rounds the integer part.
|
|
2347
|
+
*
|
|
2348
|
+
* - accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
2349
|
+
* - returns same type as input
|
|
2350
|
+
*
|
|
2351
|
+
* @param value - the value to round
|
|
2352
|
+
* @param [n] - the number of significant digits
|
|
2353
|
+
* @returns rounded value, or input value when unable to round
|
|
2354
|
+
*
|
|
2355
|
+
* @example
|
|
2356
|
+
* roundByMagnitude(0.000123456789) -> 0.0001235
|
|
2357
|
+
* roundByMagnitude(1.123456789) -> 1.123
|
|
2358
|
+
* roundByMagnitude(19999.123456789) -> 19999
|
|
2359
|
+
*/
|
|
2360
|
+
const roundByMagnitude = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
2361
|
+
if (typeof value === "number") return roundNumberByMagnitude(value, n);
|
|
2362
|
+
if (value === null || Number.isNaN(value) || value === Infinity || value === -Infinity || value === void 0) return value;
|
|
2363
|
+
const { number, unit, isString } = parseNumber(value);
|
|
2364
|
+
return unParseNumber({
|
|
2365
|
+
value: !isNumeric(number) || typeof number === "string" && number.endsWith(".") ? value : roundNumberByMagnitude(number, n),
|
|
2366
|
+
unit,
|
|
2367
|
+
isString,
|
|
2368
|
+
isScientific: isScientificStringNum(value)
|
|
2369
|
+
});
|
|
2370
|
+
};
|
|
2371
|
+
/**
|
|
2372
|
+
* Rounds a numeric value to an appropriate number of digits, based on its size. It rounds to N significant digits,
|
|
2373
|
+
* but never rounds the integer part. Similar to roundByMagnitude, but adds trailing zeros.
|
|
2374
|
+
*
|
|
2375
|
+
* - accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
2376
|
+
* - returns string type (or original value and type when not possible to convert)
|
|
2377
|
+
*
|
|
2378
|
+
* @param value - the value to round
|
|
2379
|
+
* @param [n] - the number of significant digits
|
|
2380
|
+
* @returns rounded value as a string, or input value when unable to round
|
|
2381
|
+
*
|
|
2382
|
+
* @example
|
|
2383
|
+
* roundByMagnitudeToFixed(0.000120016789) -> 0.0001200
|
|
2384
|
+
*/
|
|
2385
|
+
const roundByMagnitudeToFixed = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
2386
|
+
const toFixed = true;
|
|
2387
|
+
if (value === null || Number.isNaN(value) || value === Infinity || value === -Infinity || value === void 0) return value;
|
|
2388
|
+
if (typeof value === "number") return roundNumberByMagnitude(value, n, toFixed);
|
|
2389
|
+
const { number, unit } = parseNumber(value, true);
|
|
2390
|
+
return unParseNumber({
|
|
2391
|
+
value: !isNumeric(number) || typeof number === "string" && number.endsWith(".") ? value : roundNumberByMagnitude(number, n, toFixed),
|
|
2392
|
+
unit,
|
|
2393
|
+
isString: true,
|
|
2394
|
+
isScientific: isScientificStringNum(value)
|
|
2395
|
+
});
|
|
2396
|
+
};
|
|
2363
2397
|
/**
|
|
2364
|
-
*
|
|
2398
|
+
* Rounds a number to an appropriate number of digits, based on its size in relation to a range.
|
|
2365
2399
|
*
|
|
2366
|
-
* @
|
|
2367
|
-
* @
|
|
2400
|
+
* @private (see roundByMagnitude() for the public interface)
|
|
2401
|
+
* @param value
|
|
2402
|
+
* @param min
|
|
2403
|
+
* @param max
|
|
2404
|
+
* @param [n] the minimum number of significant digits
|
|
2405
|
+
* @returns rounded number
|
|
2368
2406
|
*/
|
|
2369
|
-
const
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
-
|
|
2373
|
-
|
|
2374
|
-
].map((number) => number.toString());
|
|
2407
|
+
const roundNumberByRange = (value, min, max, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
2408
|
+
if (isCloseToOrLessThan(max, min)) return value;
|
|
2409
|
+
const range = max - min;
|
|
2410
|
+
return roundNumber(value, Math.max(n, 0 - Math.floor(Math.log10(range))));
|
|
2411
|
+
};
|
|
2375
2412
|
/**
|
|
2376
|
-
*
|
|
2377
|
-
*
|
|
2413
|
+
* Rounds a numeric value to an appropriate number of digits, based on its size within a range of values.
|
|
2414
|
+
*
|
|
2415
|
+
* - accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
2416
|
+
* - returns same type as input
|
|
2417
|
+
*
|
|
2418
|
+
* @param value - the value to round
|
|
2419
|
+
* @param min - the min value in the range
|
|
2420
|
+
* @param max - the max value in the range
|
|
2421
|
+
* @param [n] - the minimum number of significant digits
|
|
2422
|
+
* @returns rounded value, or input value when unable to round
|
|
2423
|
+
*
|
|
2378
2424
|
*/
|
|
2379
|
-
const
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2425
|
+
const roundByRange = (value, min, max, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
2426
|
+
if (typeof value === "number") return roundNumberByRange(value, min, max, n);
|
|
2427
|
+
if (value === null || Number.isNaN(value) || value === Infinity || value === -Infinity || value === void 0) return value;
|
|
2428
|
+
const { number, unit, isString } = parseNumber(value);
|
|
2429
|
+
return unParseNumber({
|
|
2430
|
+
value: !isNumeric(number) || typeof number === "string" && number.endsWith(".") ? value : roundNumberByRange(number, min, max, n),
|
|
2431
|
+
unit,
|
|
2432
|
+
isString,
|
|
2433
|
+
isScientific: isScientificStringNum(value)
|
|
2434
|
+
});
|
|
2435
|
+
};
|
|
2436
|
+
/**
|
|
2437
|
+
* Rounds a numeric value to N fixed decimal digits.
|
|
2438
|
+
*
|
|
2439
|
+
* - accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
2440
|
+
* - returns same type as input
|
|
2441
|
+
*
|
|
2442
|
+
* @param value - the value to round
|
|
2443
|
+
* @param [n] - the number of fixed decimal digits
|
|
2444
|
+
* @returns rounded value, or input value when unable to round
|
|
2445
|
+
*
|
|
2446
|
+
*/
|
|
2447
|
+
const roundToFixed = (value, n = DEFAULT_SIGNIFICANT_DIGITS) => {
|
|
2448
|
+
if (value === null || Number.isNaN(value) || value === Infinity || value === -Infinity || value === void 0) return value;
|
|
2449
|
+
if (typeof value === "number") return value.toFixed(n);
|
|
2450
|
+
const { number, unit, isString } = parseNumber(value);
|
|
2451
|
+
return unParseNumber({
|
|
2452
|
+
value: !isNumeric(number) || typeof number === "string" && number.endsWith(".") ? value : number.toFixed(n),
|
|
2453
|
+
unit,
|
|
2454
|
+
isString,
|
|
2455
|
+
isScientific: isScientificStringNum(value)
|
|
2456
|
+
});
|
|
2457
|
+
};
|
|
2458
|
+
//#endregion
|
|
2459
|
+
//#region src/numbers/format/display-number.ts
|
|
2460
|
+
const DEFAULT_AUTO_SCIENTIFIC_BELOW = 1e-4;
|
|
2461
|
+
const DEFAULT_AUTO_SCIENTIFIC_ABOVE = 1e7;
|
|
2462
|
+
const NUMBER_FORMAT_OPTIONS = { maximumFractionDigits: 20 };
|
|
2463
|
+
const formatEnglishNumber = (value) => {
|
|
2464
|
+
let numberFormat;
|
|
2465
|
+
try {
|
|
2466
|
+
numberFormat = new Intl.NumberFormat("en-US", NUMBER_FORMAT_OPTIONS);
|
|
2467
|
+
} catch {
|
|
2468
|
+
numberFormat = new Intl.NumberFormat(void 0, NUMBER_FORMAT_OPTIONS);
|
|
2469
|
+
}
|
|
2470
|
+
return numberFormat.format(value);
|
|
2471
|
+
};
|
|
2472
|
+
const superscriptSymbols = {
|
|
2473
|
+
"0": "⁰",
|
|
2474
|
+
"1": "¹",
|
|
2475
|
+
"2": "²",
|
|
2476
|
+
"3": "³",
|
|
2477
|
+
"4": "⁴",
|
|
2478
|
+
"5": "⁵",
|
|
2479
|
+
"6": "⁶",
|
|
2480
|
+
"7": "⁷",
|
|
2481
|
+
"8": "⁸",
|
|
2482
|
+
"9": "⁹",
|
|
2483
|
+
"+": "⁺",
|
|
2484
|
+
"-": "⁻"
|
|
2485
|
+
};
|
|
2486
|
+
const appendTrailingZeros = (value, numberOfZeros) => {
|
|
2487
|
+
const zeros = "0".repeat(numberOfZeros);
|
|
2488
|
+
return numberOfZeros > 0 && value !== "0" ? !value.includes(".") ? `${value}.${zeros}` : `${value}${zeros}` : value;
|
|
2489
|
+
};
|
|
2490
|
+
const formatDecimal = (value, thousandSeparator, preserveTrailingZeros = false) => {
|
|
2491
|
+
const convertedValue = formatEnglishNumber(toNum(value)).replaceAll(",", thousandSeparator);
|
|
2492
|
+
return preserveTrailingZeros ? appendTrailingZeros(convertedValue, countTrailingZeros(value, true)) : convertedValue;
|
|
2493
|
+
};
|
|
2494
|
+
const formatDecimalDisplayNumber = (value, options) => {
|
|
2495
|
+
const { nonBreakingSpace } = options ?? {};
|
|
2496
|
+
if (value === "") return value;
|
|
2497
|
+
if (value === null || value === void 0) return "";
|
|
2498
|
+
if (!isValidNum(value)) return trim(value.toString());
|
|
2499
|
+
return formatDecimal(value, options?.noThousandsSeparator ? "" : nonBreakingSpace ? " " : " ", options?.preserveTrailingZeros);
|
|
2500
|
+
};
|
|
2501
|
+
const formatScientificDisplayNumber = (value, options) => {
|
|
2502
|
+
const { roundScientificCoefficient, eNotation } = options ?? {};
|
|
2503
|
+
if (Number.isNaN(value)) return "Invalid";
|
|
2504
|
+
if (value === null || value === void 0) return "";
|
|
2505
|
+
if (!isValidNum(value) || value === "") return trim(value.toString());
|
|
2506
|
+
const sanitizedValue = toNum(value);
|
|
2507
|
+
if (!Number.isFinite(sanitizedValue)) return trim(value.toString());
|
|
2508
|
+
const power = eNotation ? "e" : "·10";
|
|
2509
|
+
const [coefficient, exponent] = sanitizedValue.toExponential().split("e");
|
|
2510
|
+
const roundedCoefficient = typeof roundScientificCoefficient === "number" ? round(coefficient, roundScientificCoefficient) : coefficient;
|
|
2511
|
+
const noExponent = exponent === "+0" || exponent === "-0";
|
|
2512
|
+
const formattedExponent = [...exponent.replaceAll("+", "")].map((c) => eNotation ? c : superscriptSymbols[c]).join("");
|
|
2513
|
+
return noExponent ? roundedCoefficient : `${roundedCoefficient}${power}${formattedExponent}`;
|
|
2514
|
+
};
|
|
2515
|
+
const formatDisplayNumber = (value, options) => {
|
|
2516
|
+
const abs = Math.abs(toNum(value));
|
|
2517
|
+
return (options?.scientific === "auto" && options?.autoScientificBelow && options?.autoScientificAbove ? abs < options?.autoScientificBelow || abs > options?.autoScientificAbove : options?.scientific) ? formatScientificDisplayNumber(value, options) : formatDecimalDisplayNumber(value, options);
|
|
2518
|
+
};
|
|
2519
|
+
/**
|
|
2520
|
+
* Displays a number with human-friendly formatting (use for non-editable display labels, text)
|
|
2521
|
+
*
|
|
2522
|
+
* Accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
2523
|
+
*
|
|
2524
|
+
* @example
|
|
2525
|
+
* //returns '1 234.56'
|
|
2526
|
+
* displayNumber(1234.56)
|
|
2527
|
+
*
|
|
2528
|
+
* By default, adds thousands separators. Can be configured to display in scientific notation, and with formatted units.
|
|
2529
|
+
*
|
|
2530
|
+
* @param value
|
|
2531
|
+
* @param options
|
|
2532
|
+
* @returns formatted display number
|
|
2533
|
+
*/
|
|
2534
|
+
const displayNumber = (value, options) => {
|
|
2535
|
+
const optionsWithDefaults = {
|
|
2536
|
+
scientific: options?.scientific ?? "auto",
|
|
2537
|
+
eNotation: options?.eNotation ?? false,
|
|
2538
|
+
autoScientificBelow: options?.autoScientificBelow ?? DEFAULT_AUTO_SCIENTIFIC_BELOW,
|
|
2539
|
+
autoScientificAbove: options?.autoScientificAbove ?? DEFAULT_AUTO_SCIENTIFIC_ABOVE,
|
|
2540
|
+
withUnit: options?.withUnit ?? false,
|
|
2541
|
+
nonBreakingSpace: options?.nonBreakingSpace ?? false,
|
|
2542
|
+
roundScientificCoefficient: options?.roundScientificCoefficient
|
|
2543
|
+
};
|
|
2544
|
+
const { withUnit } = optionsWithDefaults;
|
|
2545
|
+
if (value === null || value === void 0) return "";
|
|
2546
|
+
const { number, unit } = parseNumber(value);
|
|
2547
|
+
const formattedNumber = formatDisplayNumber(number, optionsWithDefaults);
|
|
2548
|
+
const formattedUnit = unit ? LABELS?.[unit] : "";
|
|
2549
|
+
return withUnit && unit ? formattedNumber === "" ? formattedUnit : `${formattedNumber} ${formattedUnit}` : formattedNumber;
|
|
2468
2550
|
};
|
|
2469
2551
|
/**
|
|
2470
|
-
*
|
|
2471
|
-
*
|
|
2552
|
+
* Displays a number with human-friendly formatting (use for non-editable display labels, text)
|
|
2553
|
+
*
|
|
2554
|
+
* Accepts number types (1.234), stringified numbers ('1.234'), and unit numbers ('1.234|m')
|
|
2555
|
+
*
|
|
2556
|
+
* @example
|
|
2557
|
+
* //returns '1 234.5600'
|
|
2558
|
+
* displayNumberToFixed('1234.5600')
|
|
2559
|
+
*
|
|
2560
|
+
* By default, adds thousands separators. Can be configured to display in scientific notation, and with formatted units.
|
|
2561
|
+
*
|
|
2562
|
+
* @param value
|
|
2563
|
+
* @param options
|
|
2564
|
+
* @returns formatted display number
|
|
2472
2565
|
*/
|
|
2473
|
-
const
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
"
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
"deg/m": "Degrees per meter",
|
|
2591
|
-
"deg/ft": "Degrees per foot",
|
|
2592
|
-
"rad/m": "Radians per meter",
|
|
2593
|
-
"rad/ft": "Radians per foot",
|
|
2594
|
-
"dyn/cm": "Dyn per centimeter",
|
|
2595
|
-
"mN/m": "Millinewtons per meter",
|
|
2596
|
-
"1/kPa": "1/kPa",
|
|
2597
|
-
m3: "Cubic meters",
|
|
2598
|
-
"m/s": "Meters per second",
|
|
2599
|
-
"ft/s": "Feet per second",
|
|
2600
|
-
"m/min": "Meters per minute",
|
|
2601
|
-
"ft/min": "Feet per min",
|
|
2602
|
-
"m/h": "Meters per hour",
|
|
2603
|
-
"ft/h": "Feet per hour",
|
|
2604
|
-
mph: "Miles per hour",
|
|
2605
|
-
"km/h": "Kilometers per hour",
|
|
2606
|
-
"m/s2": "Meters per second squared",
|
|
2607
|
-
"ft/s2": "Feet per second squared",
|
|
2608
|
-
"Pa*s": "Pascal seconds",
|
|
2609
|
-
P: "Poise (dyne second per square centimeter)",
|
|
2610
|
-
"mPa*s": "Millipascal seconds",
|
|
2611
|
-
cP: "Centi Poise",
|
|
2612
|
-
W: "Watts",
|
|
2613
|
-
hhp: "Hydraulic horsepower",
|
|
2614
|
-
hp: "Horsepower",
|
|
2615
|
-
kW: "Kilowatts",
|
|
2616
|
-
MW: "Megawatts",
|
|
2617
|
-
"BTU/h": "British Thermal Units per hour",
|
|
2618
|
-
"hhp/in2": "Hydraulic horsepower per square inch",
|
|
2619
|
-
"hhp/ft2": "Hydraulic horsepower per square feet",
|
|
2620
|
-
"Mm3/d": "Mega cubic meters per day",
|
|
2621
|
-
"STB/d": "Stock Tank Barrel per day",
|
|
2622
|
-
"Sm3/d": "Standard cubic meters per day",
|
|
2623
|
-
"Sm3/min": "Standard cubic meters per minute",
|
|
2624
|
-
"MSm3/d": "Mega standard cubic meters per day",
|
|
2625
|
-
"SCF/STB": "Standard Cubic Feet per Stock Tank Barrel",
|
|
2626
|
-
"Sm3/Sm3": "Standard cubic meters per Standard cubic meter",
|
|
2627
|
-
"SCF/d": "Standard cubic feet per day",
|
|
2628
|
-
STB: "Stock Tank Barrel",
|
|
2629
|
-
SCF: "Standard Cubic Feet",
|
|
2630
|
-
Gsg: "Gas - specific gravity",
|
|
2631
|
-
Gppg: "Gas - pounds per gallon",
|
|
2632
|
-
"Gkg/m3": "Gas - kilogram per cubic meters",
|
|
2633
|
-
"Glbm/ft3": "Gas - pounds per cubic foot",
|
|
2634
|
-
MSm3: "Mega standard cubic meters",
|
|
2635
|
-
"m3/s/bar": "Cubic per second per bar",
|
|
2636
|
-
"Sm3/d/bar": "Standard cubic per day per bar",
|
|
2637
|
-
"STB/d/psi": "Standard barrels per day per psi",
|
|
2638
|
-
klbf: "kilopound force",
|
|
2639
|
-
"1/Pa": "1/Pascal",
|
|
2640
|
-
"1/MPa": "1/MPa",
|
|
2641
|
-
ksi: "Kilopound per square inch",
|
|
2642
|
-
"lbf/100ft2": "Pounds per 100 square foot",
|
|
2643
|
-
"lb/ft3": "lb/ft3",
|
|
2644
|
-
"°N": "°N (latitude)",
|
|
2645
|
-
"°S": "°S (latitude)",
|
|
2646
|
-
"°W": "°W (longitude)",
|
|
2647
|
-
"°E": "°E (longitude)",
|
|
2648
|
-
fr: "Fraction",
|
|
2649
|
-
mD: "Millidarcy",
|
|
2650
|
-
Sigma: "Sigma",
|
|
2651
|
-
CI: "Confidence Interval",
|
|
2652
|
-
"J/(s*m*degK)": "Joules per second meter Kelvin",
|
|
2653
|
-
"C/m": "Degrees Celsius per meter",
|
|
2654
|
-
"F/ft": "Degrees Fahrenheit per meter",
|
|
2655
|
-
"K/m": "Kelvin per meter",
|
|
2656
|
-
microM: "Micro meter",
|
|
2657
|
-
"W/m2": "Watt per square meter",
|
|
2658
|
-
"1/K": "Inverse Kelvin",
|
|
2659
|
-
"lb/ft": "Pound Per Feet",
|
|
2660
|
-
"E-09/bar": "Nano per bar",
|
|
2661
|
-
"E-10/psi": "10⁻¹⁰ per psi",
|
|
2662
|
-
"E-14/pa": "10⁻¹⁴ per pascal",
|
|
2663
|
-
lk: "Link",
|
|
2664
|
-
ftCla: "Clark`s foot",
|
|
2665
|
-
lkCla: "Clark`s link",
|
|
2666
|
-
ftSe: "British foot (Sears 1922)",
|
|
2667
|
-
ydSe: "British yard (Sears 1922)",
|
|
2668
|
-
chSe: "British chain (Sears 1922)",
|
|
2669
|
-
"chSe(T)": "British chain (Sears 1922 Truncated)",
|
|
2670
|
-
ftGC: "Gold Coast foot",
|
|
2671
|
-
ydInd: "Indian yard",
|
|
2672
|
-
"d/stand": "Day per stand",
|
|
2673
|
-
"h/stand": "Hour per stand",
|
|
2674
|
-
"min/stand": "Minute per stand",
|
|
2675
|
-
"s/stand": "Second per stand",
|
|
2676
|
-
"m3/t": "Cubic meter per ton",
|
|
2677
|
-
"L/100kg": "Liter per 100kg",
|
|
2678
|
-
GPa: "Gigapascals, unit of pressure",
|
|
2679
|
-
"cm3/m": "Cubic cm per meter",
|
|
2680
|
-
"in3/ft": "Cubic inches per foot",
|
|
2681
|
-
"ft3/ft": "Cubic feet per foot",
|
|
2682
|
-
"m3/m": "Cubic meters per meter",
|
|
2683
|
-
"mm3/m": "Cubic millimeters per meter",
|
|
2684
|
-
"1/GPa": "Inverse gigapascal",
|
|
2685
|
-
lps: "Liters per second",
|
|
2686
|
-
nT: "nanoTesla",
|
|
2687
|
-
Gs: "Gauss",
|
|
2688
|
-
g: "g force"
|
|
2566
|
+
const displayNumberToFixed = (value, options) => {
|
|
2567
|
+
const optionsWithDefaults = {
|
|
2568
|
+
scientific: options?.scientific ?? "auto",
|
|
2569
|
+
eNotation: options?.eNotation ?? false,
|
|
2570
|
+
autoScientificBelow: options?.autoScientificBelow ?? DEFAULT_AUTO_SCIENTIFIC_BELOW,
|
|
2571
|
+
autoScientificAbove: options?.autoScientificAbove ?? DEFAULT_AUTO_SCIENTIFIC_ABOVE,
|
|
2572
|
+
withUnit: options?.withUnit ?? false,
|
|
2573
|
+
nonBreakingSpace: options?.nonBreakingSpace ?? false,
|
|
2574
|
+
roundScientificCoefficient: options?.roundScientificCoefficient
|
|
2575
|
+
};
|
|
2576
|
+
const { withUnit } = optionsWithDefaults;
|
|
2577
|
+
if (value === null || value === void 0) return "";
|
|
2578
|
+
const { number, unit } = parseNumber(value, true);
|
|
2579
|
+
const formattedNumber = formatDisplayNumber(number, {
|
|
2580
|
+
...optionsWithDefaults,
|
|
2581
|
+
preserveTrailingZeros: true
|
|
2582
|
+
});
|
|
2583
|
+
const formattedUnit = unit ? LABELS?.[unit] : "";
|
|
2584
|
+
return withUnit && unit ? formattedNumber === "" ? formattedUnit : `${formattedNumber} ${formattedUnit}` : formattedNumber;
|
|
2585
|
+
};
|
|
2586
|
+
/** @deprecated Use `displayNumber` instead. */
|
|
2587
|
+
const formatNumber = (number) => {
|
|
2588
|
+
return displayNumber(number);
|
|
2589
|
+
};
|
|
2590
|
+
//#endregion
|
|
2591
|
+
//#region src/numbers/numbers.ts
|
|
2592
|
+
const parseValue = (value) => {
|
|
2593
|
+
return typeof value === "string" && isValueWithUnit(value) ? getValue(value) : value;
|
|
2594
|
+
};
|
|
2595
|
+
/**
|
|
2596
|
+
* Checks whether a value can be converted to number type by the toNum() function
|
|
2597
|
+
*
|
|
2598
|
+
* @param value - value to be checked
|
|
2599
|
+
* @returns whether number can be converted by toNum() function
|
|
2600
|
+
*
|
|
2601
|
+
* @example
|
|
2602
|
+
* isValidNum('1 1/2') -> true
|
|
2603
|
+
* toNum('foobar|m') -> false
|
|
2604
|
+
*/
|
|
2605
|
+
const isValidNum = (value) => {
|
|
2606
|
+
const parsedValue = parseValue(value);
|
|
2607
|
+
if (isEmptyString(parsedValue) || Number.isNaN(parsedValue) || parsedValue === Infinity || parsedValue === -Infinity) return true;
|
|
2608
|
+
else if (!(isNull(parsedValue) || isUndefined(parsedValue) || isTrailingPeriodSeparator(parsedValue) || isTrailingCommaSeparator(parsedValue) || isArray(parsedValue) || isObject(parsedValue))) {
|
|
2609
|
+
const cleanedValue = cleanNumStr(String(parsedValue));
|
|
2610
|
+
if (cleanedValue.includes("|")) return false;
|
|
2611
|
+
const number = isFraction(cleanedValue) ? numFraction(cleanedValue) : isNumeric(cleanedValue) ? parseFloat(cleanedValue) : cleanedValue;
|
|
2612
|
+
if (number === Infinity || number === -Infinity) return true;
|
|
2613
|
+
if (!isNumeric(number)) return false;
|
|
2614
|
+
if (!Number.isNaN(number)) return true;
|
|
2615
|
+
}
|
|
2616
|
+
return false;
|
|
2617
|
+
};
|
|
2618
|
+
/**
|
|
2619
|
+
* Checks whether a value is a valid number, in string representation with scientific notation (e.g. '1e3')
|
|
2620
|
+
*
|
|
2621
|
+
* Note - it's not possible to check whether *number* types are stored in scientific notation (all numbers are stored
|
|
2622
|
+
* the same way internally in floating-point formats, so there is no difference between 1000 and 1e3 internally).
|
|
2623
|
+
* Whether number types get displayed in scientific notation or not in the console is a browser-specific implementation
|
|
2624
|
+
* detail (display formatting) and not something we can check/rely on, so this function is only intended for checking
|
|
2625
|
+
* user-input values in string format. See https://stackoverflow.com/a/66005705/942635.
|
|
2626
|
+
*
|
|
2627
|
+
* @param value - value to be checked
|
|
2628
|
+
* @returns whether the value is a valid number in scientific notation
|
|
2629
|
+
*
|
|
2630
|
+
* @example
|
|
2631
|
+
* isValidNum('1e3') -> true
|
|
2632
|
+
* toNum(1000) -> false
|
|
2633
|
+
* toNum(1e3) -> false (we cannot check scientific notation of number types)
|
|
2634
|
+
*/
|
|
2635
|
+
const isScientificStringNum = (value) => {
|
|
2636
|
+
if (typeof value === "string") return isValidNum(value) && value.toLowerCase().includes("e");
|
|
2637
|
+
return false;
|
|
2638
|
+
};
|
|
2639
|
+
/**
|
|
2640
|
+
* Converts a numeric value to number type (when possible).
|
|
2641
|
+
* - need to know if it's possible first? Call isValidNum()
|
|
2642
|
+
* - accepts number types (1.234), stringified numbers ('1.234'), fractions ('1/2'), and unit numbers ('1.234|m')
|
|
2643
|
+
* - returns the converted number if possible, otherwise returns the input value or default value when provided
|
|
2644
|
+
*
|
|
2645
|
+
* @param value - value to be converted to number type
|
|
2646
|
+
* @param [fallback] - optional fallback value (returned when not possible to convert)
|
|
2647
|
+
* @param [minimum] - optional minimum value
|
|
2648
|
+
* @returns valid number after conversion, or fallback, or returns the original input
|
|
2649
|
+
*
|
|
2650
|
+
* @example
|
|
2651
|
+
* toNum('1.2345) -> 1.2345
|
|
2652
|
+
* toNum('1.2345|m') -> 1.2345
|
|
2653
|
+
*/
|
|
2654
|
+
const toNum = (value, fallback, minimum) => {
|
|
2655
|
+
const fallbackResult = fallback ?? value;
|
|
2656
|
+
const parsedValue = parseValue(value);
|
|
2657
|
+
if (!isValidNum(parsedValue)) return fallbackResult;
|
|
2658
|
+
else {
|
|
2659
|
+
const cleanedValue = cleanNumStr(String(parsedValue));
|
|
2660
|
+
const number = isFraction(cleanedValue) ? numFraction(cleanedValue) : isNumeric(cleanedValue) ? parseFloat(cleanedValue) : cleanedValue;
|
|
2661
|
+
if (number === Infinity || number === -Infinity) return number;
|
|
2662
|
+
if (Number.isNaN(number) || !isNumeric(number)) return fallbackResult;
|
|
2663
|
+
else if (minimum && number < minimum) return minimum;
|
|
2664
|
+
return number;
|
|
2665
|
+
}
|
|
2666
|
+
};
|
|
2667
|
+
/**
|
|
2668
|
+
* Convert a number to a string safely, better than String(value)
|
|
2669
|
+
* String(0.0000002) returns '2e-7' which is unwanted if we need to preserve formatting
|
|
2670
|
+
*
|
|
2671
|
+
* @param value
|
|
2672
|
+
* @returns number or string output value
|
|
2673
|
+
*/
|
|
2674
|
+
const toString = (value) => {
|
|
2675
|
+
if (isValidNum(value)) {
|
|
2676
|
+
if (typeof value === "string") return value;
|
|
2677
|
+
if (typeof value === "number") {
|
|
2678
|
+
if (Number.isNaN(value) || !Number.isFinite(value)) return String(value);
|
|
2679
|
+
return formatDecimalDisplayNumber(value, { noThousandsSeparator: true });
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
return value;
|
|
2689
2683
|
};
|
|
2690
2684
|
//#endregion
|
|
2691
2685
|
//#region src/validate/ajv-validators.ts
|
|
@@ -2759,99 +2753,40 @@ const transformErrors = (errors) => {
|
|
|
2759
2753
|
return errors?.map(({ message }) => message);
|
|
2760
2754
|
};
|
|
2761
2755
|
//#endregion
|
|
2762
|
-
//#region src/units.ts
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
const
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
function showAltUnitsList(quantityKey) {
|
|
2773
|
-
return ALT_UNITS[quantityKey];
|
|
2774
|
-
}
|
|
2775
|
-
/**
|
|
2776
|
-
* Get list of units for a given quantity
|
|
2777
|
-
*
|
|
2778
|
-
* @param quantity
|
|
2779
|
-
* @return [] | undefined
|
|
2780
|
-
*/
|
|
2781
|
-
function getUnitsForQuantity(quantity) {
|
|
2782
|
-
return showAltUnitsList(quantity);
|
|
2783
|
-
}
|
|
2784
|
-
/**
|
|
2785
|
-
* Get list of all defined quantities
|
|
2786
|
-
* @returns []
|
|
2787
|
-
*/
|
|
2788
|
-
function getQuantities() {
|
|
2789
|
-
return Object.keys(ALT_UNITS);
|
|
2790
|
-
}
|
|
2791
|
-
/**
|
|
2792
|
-
* Get unit label
|
|
2793
|
-
*
|
|
2794
|
-
* @param unitKey
|
|
2795
|
-
* @return string|undefined
|
|
2796
|
-
*/
|
|
2797
|
-
function label(unitKey) {
|
|
2798
|
-
return LABELS[unitKey];
|
|
2799
|
-
}
|
|
2800
|
-
/**
|
|
2801
|
-
* Get unit by quantity
|
|
2802
|
-
*
|
|
2803
|
-
* @param quantity
|
|
2804
|
-
* @return string|undefined
|
|
2805
|
-
*/
|
|
2806
|
-
function unitFromKey(quantity) {
|
|
2807
|
-
return UNIT_FROM_KEY[quantity];
|
|
2808
|
-
}
|
|
2809
|
-
/**
|
|
2810
|
-
* Get unit by quantity
|
|
2811
|
-
*
|
|
2812
|
-
* @param quantity
|
|
2813
|
-
* @return string|undefined
|
|
2814
|
-
*/
|
|
2815
|
-
function unitFromQuantity(quantity) {
|
|
2816
|
-
return unitFromKey(quantity);
|
|
2756
|
+
//#region src/units/validation.ts
|
|
2757
|
+
/** Normalizes repeated decimal dots and decimal commas. */
|
|
2758
|
+
function checkAndCleanDecimalComma(value) {
|
|
2759
|
+
const repeatedDots = /\.{2,}/;
|
|
2760
|
+
const comma = /,/;
|
|
2761
|
+
if (typeof value === "string") while (repeatedDots.test(value) || comma.test(value)) {
|
|
2762
|
+
value = value.replace(repeatedDots, ".");
|
|
2763
|
+
value = value.replace(comma, ".");
|
|
2764
|
+
}
|
|
2765
|
+
return value;
|
|
2817
2766
|
}
|
|
2818
|
-
/**
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
*
|
|
2822
|
-
|
|
2823
|
-
*/
|
|
2824
|
-
function getAltUnitsListByQuantity(quantity) {
|
|
2825
|
-
const quantityUnitList = showAltUnitsList(quantity);
|
|
2826
|
-
return quantityUnitList ? quantityUnitList.map((unit) => ({
|
|
2827
|
-
unit,
|
|
2828
|
-
label: label(unit)
|
|
2829
|
-
})) : void 0;
|
|
2767
|
+
/** Cleans raw numeric input while retaining the unit from the previous value. */
|
|
2768
|
+
function validateAndClean(previousValue, nextText) {
|
|
2769
|
+
const unit = split(previousValue)[1];
|
|
2770
|
+
const cleanedValue = nextText.replace(/[^0-9.,-Ee]/g, "").replace(/^([^(e|E)]*[eE])|[eE]/g, "$1").replace(/^[E|e]*|[E|e]*$/g, "").replace(/,/g, ".").replace(/^([^.]*\.)|\./g, "$1").replace(/\.(?=e)/g, "").replace(/-|[eE]-/g, (match, offset) => offset === 0 || match.toLowerCase() === "e-" ? match : "");
|
|
2771
|
+
return !Number.isFinite(+cleanedValue) ? previousValue : `${cleanedValue}${unit ? "|" : ""}${unit}`;
|
|
2830
2772
|
}
|
|
2831
|
-
/**
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
}
|
|
2845
|
-
return val;
|
|
2773
|
+
/** Validates a numeric or unit-encoded value against the package number schema. */
|
|
2774
|
+
function validateNumber(value) {
|
|
2775
|
+
let normalizedValue = value;
|
|
2776
|
+
if (typeof value === "string" && isValueWithUnit(value)) normalizedValue = getValue(value);
|
|
2777
|
+
normalizedValue = checkAndCleanDecimalComma(normalizedValue);
|
|
2778
|
+
if (isNumeric(normalizedValue)) return {
|
|
2779
|
+
valid: numberSchemaValidator(toNum(normalizedValue)),
|
|
2780
|
+
errors: transformErrors(numberSchemaValidator.errors)
|
|
2781
|
+
};
|
|
2782
|
+
return {
|
|
2783
|
+
valid: numberSchemaValidator(normalizedValue),
|
|
2784
|
+
errors: transformErrors(numberSchemaValidator.errors)
|
|
2785
|
+
};
|
|
2846
2786
|
}
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
* @param value clean value without unit
|
|
2851
|
-
* @param fromUnit unit from which we are trying to convert
|
|
2852
|
-
* @param toUnit unit to which we are trying to convert
|
|
2853
|
-
* @return number|Error
|
|
2854
|
-
*/
|
|
2787
|
+
//#endregion
|
|
2788
|
+
//#region src/units/conversion.ts
|
|
2789
|
+
/** Converts a numeric value between compatible units. */
|
|
2855
2790
|
function to(value, fromUnit, toUnit) {
|
|
2856
2791
|
value = checkAndCleanDecimalComma(value);
|
|
2857
2792
|
value = normalizeScientific(value);
|
|
@@ -2864,8 +2799,8 @@ function to(value, fromUnit, toUnit) {
|
|
|
2864
2799
|
if (value === Infinity || value === "Infinity") return Infinity;
|
|
2865
2800
|
if (value === -Infinity || value === "-Infinity") return -Infinity;
|
|
2866
2801
|
if (isNonNumerical(value) && value !== "") return NaN;
|
|
2867
|
-
const
|
|
2868
|
-
if (
|
|
2802
|
+
const conversion = KNOWN_CONVERSIONS[`${fromUnit}|${toUnit}`];
|
|
2803
|
+
if (conversion) return conversion(toNum(value));
|
|
2869
2804
|
if (DEPRECATED_UNITS[fromUnit]) {
|
|
2870
2805
|
console.warn(`Unit '${fromUnit}' is deprecated - use '${DEPRECATED_UNITS[fromUnit]}' instead.`);
|
|
2871
2806
|
return to(value, DEPRECATED_UNITS[fromUnit], toUnit);
|
|
@@ -2876,108 +2811,40 @@ function to(value, fromUnit, toUnit) {
|
|
|
2876
2811
|
}
|
|
2877
2812
|
if (UNIT_ALIASES[toUnit]) return to(value, fromUnit, UNIT_ALIASES[toUnit]);
|
|
2878
2813
|
if (UNIT_ALIASES[fromUnit]) return to(value, UNIT_ALIASES[fromUnit], toUnit);
|
|
2879
|
-
const
|
|
2880
|
-
if (
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
if (toUnit && int_to && int_to !== toUnit) return to(to(value, fromUnit, int_to), int_to, toUnit);
|
|
2884
|
-
}
|
|
2814
|
+
const intermediateFromUnit = INTERMEDIATE_CONVERSIONS[fromUnit];
|
|
2815
|
+
if (intermediateFromUnit) return to(to(value, fromUnit, intermediateFromUnit), intermediateFromUnit, toUnit);
|
|
2816
|
+
const intermediateToUnit = INTERMEDIATE_CONVERSIONS[toUnit];
|
|
2817
|
+
if (toUnit && intermediateToUnit && intermediateToUnit !== toUnit) return to(to(value, fromUnit, intermediateToUnit), intermediateToUnit, toUnit);
|
|
2885
2818
|
console.error("no conversions found", value, fromUnit, "->", toUnit);
|
|
2886
|
-
throw new Error(
|
|
2887
|
-
}
|
|
2888
|
-
/**
|
|
2889
|
-
* Split string into value and unit
|
|
2890
|
-
*
|
|
2891
|
-
* @param numWithUnit
|
|
2892
|
-
* @return string[] where first element it's actual value and second unit
|
|
2893
|
-
*/
|
|
2894
|
-
function split(numWithUnit) {
|
|
2895
|
-
let m;
|
|
2896
|
-
let vu = numWithUnit !== void 0 && numWithUnit !== null ? String(numWithUnit) : "";
|
|
2897
|
-
if (charCount("|", vu) > 1) {
|
|
2898
|
-
m = vu.split("|");
|
|
2899
|
-
vu = m.slice(0, -1).join("") + "|" + m.slice(-1);
|
|
2900
|
-
}
|
|
2901
|
-
if (vu.indexOf("|") >= 0) m = vu.split("|");
|
|
2902
|
-
else if (SPECIAL_NUMBERS_STRING.includes(vu)) m = [vu, ""];
|
|
2903
|
-
else {
|
|
2904
|
-
m = cleanNumStr(vu).match(UNIT_RE);
|
|
2905
|
-
if (m) m = m.slice(1);
|
|
2906
|
-
}
|
|
2907
|
-
if (!m) m = ["0", ""];
|
|
2908
|
-
if (m[1] == null) m[1] = "";
|
|
2909
|
-
return [m[0], m[1]];
|
|
2910
|
-
}
|
|
2911
|
-
/**
|
|
2912
|
-
* Get value of the number with unit string ("1|m") will return "1"
|
|
2913
|
-
* @param {sting} numWithUnit
|
|
2914
|
-
* @returns {string}
|
|
2915
|
-
*/
|
|
2916
|
-
function getValue(numWithUnit) {
|
|
2917
|
-
return split(numWithUnit)[0];
|
|
2918
|
-
}
|
|
2919
|
-
/**
|
|
2920
|
-
* Get unit of the number with unit string ("1|m") will return "m"
|
|
2921
|
-
* @param {sting} numWithUnit
|
|
2922
|
-
* @returns {string}
|
|
2923
|
-
*/
|
|
2924
|
-
function getUnit(numWithUnit) {
|
|
2925
|
-
return split(numWithUnit)[1];
|
|
2819
|
+
throw new Error(`No conversions found: ${value} ${fromUnit} -> ${toUnit}`);
|
|
2926
2820
|
}
|
|
2927
|
-
/**
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
if (
|
|
2937
|
-
|
|
2938
|
-
if (!
|
|
2939
|
-
else throw new Error("unum: invalid number: " + numWithUnit);
|
|
2940
|
-
if (m[0] == null) m[0] = "0";
|
|
2941
|
-
if (m[1]) fromUnit = m[1];
|
|
2942
|
-
if (!fromUnit && toUnit !== fromUnit) throw new Error(`unum: unable to figure out unit: ${numWithUnit} fromUnit ${fromUnit}`);
|
|
2821
|
+
/** Converts a value with an optional unit suffix to the requested unit. */
|
|
2822
|
+
function unum(valueWithUnit, toUnit, fromUnit) {
|
|
2823
|
+
if (valueWithUnit == null || valueWithUnit === "") return 0;
|
|
2824
|
+
if (typeof valueWithUnit === "string" && valueWithUnit.startsWith("NaN") || typeof valueWithUnit === "number" && isNaN(valueWithUnit)) return NaN;
|
|
2825
|
+
const parts = split(cleanNumStr(normalizeScientific(valueWithUnit)).replaceAll("+", ""));
|
|
2826
|
+
if (!parts) {
|
|
2827
|
+
if (toUnit && fromUnit) return unum(valueWithUnit + fromUnit, toUnit);
|
|
2828
|
+
throw new Error(`unum: invalid number: ${valueWithUnit}`);
|
|
2829
|
+
}
|
|
2830
|
+
if (parts[0] == null) parts[0] = "0";
|
|
2831
|
+
if (parts[1]) fromUnit = parts[1];
|
|
2832
|
+
if (!fromUnit && toUnit !== fromUnit) throw new Error(`unum: unable to figure out unit: ${valueWithUnit} fromUnit ${fromUnit}`);
|
|
2943
2833
|
if (toUnit === fromUnit) {
|
|
2944
|
-
const
|
|
2945
|
-
if (
|
|
2946
|
-
if (
|
|
2947
|
-
if (typeof
|
|
2948
|
-
if (!isNumeric(
|
|
2949
|
-
return cleanNum(
|
|
2950
|
-
}
|
|
2951
|
-
return to(
|
|
2952
|
-
}
|
|
2953
|
-
/**
|
|
2954
|
-
* Takes user input and returns true if it was input with unit and false in every other case
|
|
2955
|
-
*
|
|
2956
|
-
* @param {String|Number} value
|
|
2957
|
-
* @returns {Boolean}
|
|
2958
|
-
*/
|
|
2959
|
-
function isValueWithUnit(value) {
|
|
2960
|
-
if (!value) return false;
|
|
2961
|
-
const splittedValue = String(value).split("|");
|
|
2962
|
-
return splittedValue.length === 2 && KNOWN_UNITS.includes(splittedValue[1]);
|
|
2834
|
+
const value = parts[0] ? toNum(parts[0]) : 0;
|
|
2835
|
+
if (value === Infinity || value === "Infinity") return Infinity;
|
|
2836
|
+
if (value === -Infinity || value === "-Infinity") return -Infinity;
|
|
2837
|
+
if (typeof value === "string" && EXP_NOTATION_RE.test(value)) return parseFloat(value);
|
|
2838
|
+
if (!isNumeric(value) && value !== Infinity && value !== -Infinity) throw new Error(`unum: invalid number: ${value}, ${typeof value}`);
|
|
2839
|
+
return cleanNum(value);
|
|
2840
|
+
}
|
|
2841
|
+
return to(parts[0], fromUnit, toUnit);
|
|
2963
2842
|
}
|
|
2964
|
-
/**
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
* @param numWithUnit value (with optional unit string)
|
|
2968
|
-
* @param toUnit unit to which we are trying to convert
|
|
2969
|
-
* @param fromUnit unit from which we are trying to convert
|
|
2970
|
-
*/
|
|
2971
|
-
function convertAndGetValue(numWithUnit, toUnit, fromUnit) {
|
|
2972
|
-
return unum(numWithUnit, toUnit, fromUnit);
|
|
2843
|
+
/** Alias for `unum`. */
|
|
2844
|
+
function convertAndGetValue(value, toUnit, fromUnit) {
|
|
2845
|
+
return unum(value, toUnit, fromUnit);
|
|
2973
2846
|
}
|
|
2974
|
-
/**
|
|
2975
|
-
* Convert value to another unit (preserves types and empty values)
|
|
2976
|
-
*
|
|
2977
|
-
* @param value value (with optional unit string)
|
|
2978
|
-
* @param toUnit unit to which we are trying to convert
|
|
2979
|
-
* @param fromUnit unit from which we are trying to convert
|
|
2980
|
-
*/
|
|
2847
|
+
/** Converts a value while preserving string types and empty values. */
|
|
2981
2848
|
function convertAndGetValueStrict(value, toUnit, fromUnit) {
|
|
2982
2849
|
if (value === "" || value === null) return value;
|
|
2983
2850
|
if (typeof value === "string" && isValueWithUnit(value) && getValue(value) === "") return "";
|
|
@@ -2985,174 +2852,44 @@ function convertAndGetValueStrict(value, toUnit, fromUnit) {
|
|
|
2985
2852
|
const result = convertAndGetValue(value, toUnit, fromUnit);
|
|
2986
2853
|
return isString ? toString(result) : result;
|
|
2987
2854
|
}
|
|
2988
|
-
/**
|
|
2989
|
-
* Convert value to the base unit given by the quantity
|
|
2990
|
-
*
|
|
2991
|
-
* @param value
|
|
2992
|
-
* @param quantity
|
|
2993
|
-
* @return number
|
|
2994
|
-
*/
|
|
2855
|
+
/** Converts a value to the base unit configured for a quantity. */
|
|
2995
2856
|
function toBase(value, quantity) {
|
|
2996
|
-
const
|
|
2997
|
-
return unum(value,
|
|
2998
|
-
}
|
|
2999
|
-
/**
|
|
3000
|
-
* Convert table of values to another unit
|
|
3001
|
-
*
|
|
3002
|
-
* @param toUnitRow array of units to which we are trying to convert for example: ['ft', 'ppg']
|
|
3003
|
-
* @param table array which represent table for example:
|
|
3004
|
-
* [['m', 'sg'],
|
|
3005
|
-
[100, 1],
|
|
3006
|
-
[1000, 1.25],
|
|
3007
|
-
[2000, 1.5],
|
|
3008
|
-
[3000, 1.1]]
|
|
3009
|
-
* @param defaultUnitRow
|
|
3010
|
-
* @param removeFinalUnitsRow if true first table row where we define units will be removed
|
|
3011
|
-
*/
|
|
3012
|
-
function convertTable(toUnitRow, table, defaultUnitRow, removeFinalUnitsRow = false) {
|
|
3013
|
-
if (!table || !table.length || !table[0].length) return table;
|
|
3014
|
-
if (!toUnitRow) toUnitRow = table[0];
|
|
3015
|
-
if (!defaultUnitRow) defaultUnitRow = toUnitRow;
|
|
3016
|
-
const firstCell = table[0][0];
|
|
3017
|
-
const splittedFirstCell = firstCell && split(`${firstCell}`);
|
|
3018
|
-
const tableHasUnits = !(splittedFirstCell && splittedFirstCell[0] && isNaN(splittedFirstCell[1])) && isNaN(table[0][0]);
|
|
3019
|
-
let ix = tableHasUnits ? 1 : 0;
|
|
3020
|
-
const fromunitrow = tableHasUnits ? table[0] : defaultUnitRow;
|
|
3021
|
-
const newTable = [toUnitRow];
|
|
3022
|
-
for (; ix < table.length; ix++) {
|
|
3023
|
-
const cols = Array(toUnitRow.length);
|
|
3024
|
-
const coli = table[ix];
|
|
3025
|
-
for (let uix = 0; uix < cols.length; uix++) cols[uix] = toUnitRow[uix] && coli[uix] ? unum(coli[uix], toUnitRow[uix], fromunitrow[uix]) : coli[uix];
|
|
3026
|
-
newTable.push(cols);
|
|
3027
|
-
}
|
|
3028
|
-
if (removeFinalUnitsRow) newTable.shift();
|
|
3029
|
-
return newTable;
|
|
3030
|
-
}
|
|
3031
|
-
/**
|
|
3032
|
-
* Rounds a unit string to N decimal places and appends formatted unit label
|
|
3033
|
-
*
|
|
3034
|
-
* @param value
|
|
3035
|
-
* @param n number of decimal digits
|
|
3036
|
-
* @return rounded value with formatted units
|
|
3037
|
-
*/
|
|
3038
|
-
function roundNumberWithLabel(value, n = 2) {
|
|
3039
|
-
return displayNumber(round(value, n), { withUnit: true });
|
|
3040
|
-
}
|
|
3041
|
-
/**
|
|
3042
|
-
* Get value with unit splitted by | separator
|
|
3043
|
-
*
|
|
3044
|
-
* @param value
|
|
3045
|
-
* @param unit
|
|
3046
|
-
* @param defaultVal
|
|
3047
|
-
* @return string
|
|
3048
|
-
*/
|
|
3049
|
-
function withUnit(value, unit, defaultVal = "") {
|
|
3050
|
-
if (value === null || value === "" || value === void 0) value = defaultVal;
|
|
3051
|
-
if (unit === null) return String(value);
|
|
3052
|
-
let [v, u] = String(value).includes("|") ? split(String(value)) : [value, unit];
|
|
3053
|
-
if (!u) u = unit;
|
|
3054
|
-
return [v, u].join("|");
|
|
2857
|
+
const baseUnit = unitFromKey(quantity);
|
|
2858
|
+
return unum(value, baseUnit, split((value || "").toString())[1] || baseUnit);
|
|
3055
2859
|
}
|
|
3056
|
-
/**
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
*
|
|
3060
|
-
* @param numWithUnit value with unit
|
|
3061
|
-
* @param toUnit unit to which we are trying to convert
|
|
3062
|
-
* @param fromUnit unit from which we are trying to convert
|
|
3063
|
-
* @return {string} - "number converted | unit"
|
|
3064
|
-
*/
|
|
3065
|
-
function unumWithUnit(numWithUnit, toUnit, fromUnit) {
|
|
3066
|
-
return withUnit(unum(numWithUnit, toUnit, fromUnit), toUnit);
|
|
2860
|
+
/** Converts a value and appends the requested unit. */
|
|
2861
|
+
function unumWithUnit(value, toUnit, fromUnit) {
|
|
2862
|
+
return withUnit(unum(value, toUnit, fromUnit), toUnit);
|
|
3067
2863
|
}
|
|
3068
|
-
/**
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
* @param {string} toUnit - unit to which we are trying to convert
|
|
3074
|
-
* @param {number} digits - optional number of digits to round the number
|
|
3075
|
-
* @returns {string} - converted number including the unit (number|unit)
|
|
3076
|
-
*/
|
|
3077
|
-
function convertSamePrecision(numWithUnit, toUnit, digits) {
|
|
3078
|
-
const validNumWithUnit = String(numWithUnit);
|
|
3079
|
-
const m = split(validNumWithUnit);
|
|
3080
|
-
const convertedNumber = !m[1] || m[1] == toUnit ? Number(m[0]) : to(m[0], String(m[1]), toUnit);
|
|
2864
|
+
/** Converts a unit value while retaining approximately the input precision. */
|
|
2865
|
+
function convertSamePrecision(valueWithUnit, toUnit, digits) {
|
|
2866
|
+
const validValueWithUnit = String(valueWithUnit);
|
|
2867
|
+
const parts = split(validValueWithUnit);
|
|
2868
|
+
const convertedNumber = !parts[1] || parts[1] == toUnit ? Number(parts[0]) : to(parts[0], String(parts[1]), toUnit);
|
|
3081
2869
|
let prettyNumber = "0";
|
|
3082
2870
|
let targetDigits = digits;
|
|
3083
2871
|
if (convertedNumber !== 0) {
|
|
3084
2872
|
if (!targetDigits) {
|
|
3085
|
-
if (
|
|
3086
|
-
const
|
|
3087
|
-
|
|
3088
|
-
else targetDigits = 3;
|
|
2873
|
+
if (parts[1] === toUnit) return validValueWithUnit;
|
|
2874
|
+
const digitParts = String(parts[0]).match(/^[-+.,0]*(\d*)[.,]?(\d*)/);
|
|
2875
|
+
targetDigits = digitParts ? Math.max(3, digitParts[1].length + digitParts[2].length) : 3;
|
|
3089
2876
|
}
|
|
3090
|
-
const
|
|
3091
|
-
const
|
|
3092
|
-
if (
|
|
3093
|
-
else if (
|
|
2877
|
+
const limit = Math.pow(10, --targetDigits);
|
|
2878
|
+
const absoluteNumber = Math.abs(convertedNumber);
|
|
2879
|
+
if (absoluteNumber >= 1e5 * limit || absoluteNumber * 1e4 * (1 + limit) < limit) prettyNumber = convertedNumber.toExponential(targetDigits);
|
|
2880
|
+
else if (absoluteNumber >= limit) prettyNumber = convertedNumber.toFixed();
|
|
3094
2881
|
else {
|
|
3095
|
-
const
|
|
3096
|
-
prettyNumber = convertedNumber.toFixed(targetDigits -
|
|
3097
|
-
let
|
|
3098
|
-
if (prettyNumber[--
|
|
3099
|
-
while (prettyNumber[--
|
|
3100
|
-
prettyNumber = prettyNumber.slice(0,
|
|
2882
|
+
const magnitude = Math.floor(Math.log10(absoluteNumber));
|
|
2883
|
+
prettyNumber = convertedNumber.toFixed(targetDigits - magnitude);
|
|
2884
|
+
let index = prettyNumber.length;
|
|
2885
|
+
if (prettyNumber[--index] == "0") {
|
|
2886
|
+
while (prettyNumber[--index] == "0");
|
|
2887
|
+
prettyNumber = prettyNumber.slice(0, index + (prettyNumber[index] == "." ? 0 : 1));
|
|
3101
2888
|
}
|
|
3102
2889
|
}
|
|
3103
2890
|
}
|
|
3104
2891
|
return withUnit(prettyNumber, toUnit);
|
|
3105
2892
|
}
|
|
3106
|
-
/**
|
|
3107
|
-
* Get list of values, with same precision as the given value, in all the units of the given quantity
|
|
3108
|
-
*
|
|
3109
|
-
* @param {string} value
|
|
3110
|
-
* @param {string} quantity
|
|
3111
|
-
* @return {[][string, string, string]}
|
|
3112
|
-
*/
|
|
3113
|
-
function altUnitsList(value, quantity) {
|
|
3114
|
-
let v = value;
|
|
3115
|
-
if (!getUnit(value)) v = withUnit(value, unitFromQuantity(quantity) ?? "");
|
|
3116
|
-
return (ALT_UNITS[quantity] ?? []).map((unit) => [...split(convertSamePrecision(v, unit)), label(unit)]);
|
|
3117
|
-
}
|
|
3118
|
-
/**
|
|
3119
|
-
* Validates and cleans raw text numeric user input, typically from UnitInput
|
|
3120
|
-
* The previous value is for optionally determining the pre-existing unit
|
|
3121
|
-
* The next text is a raw input string
|
|
3122
|
-
* The return value is reformatted from the next text (removing invalid patterns)
|
|
3123
|
-
*
|
|
3124
|
-
* @param {String} previousValue with optional unit e.g. `25|m`
|
|
3125
|
-
* @param {String} nextText raw text for next value e.g. `26` (no unit)
|
|
3126
|
-
* @returns {String} e.g. `26|m`
|
|
3127
|
-
*/
|
|
3128
|
-
function validateAndClean(previousValue, nextText) {
|
|
3129
|
-
const unit = split(previousValue)[1];
|
|
3130
|
-
const cleanedValue = nextText.replace(/[^0-9.,-Ee]/g, "").replace(/^([^(e|E)]*[eE])|[eE]/g, "$1").replace(/^[E|e]*|[E|e]*$/g, "").replace(/,/g, ".").replace(/^([^.]*\.)|\./g, "$1").replace(/\.(?=e)/g, "").replace(/-|[eE]-/g, (match, offset) => offset === 0 || match.toLowerCase() === "e-" ? match : "");
|
|
3131
|
-
return !Number.isFinite(+cleanedValue) ? previousValue : `${cleanedValue}${unit ? "|" : ""}${unit}`;
|
|
3132
|
-
}
|
|
3133
|
-
/**
|
|
3134
|
-
* Takes string included value with units and return display it in pretty format.
|
|
3135
|
-
*
|
|
3136
|
-
* @param {String} valueWithUnits
|
|
3137
|
-
* @returns {String}
|
|
3138
|
-
*/
|
|
3139
|
-
function withPrettyUnitLabel(valueWithUnits) {
|
|
3140
|
-
const [val, unit] = split(valueWithUnits);
|
|
3141
|
-
return `${val} ${LABELS[unit] ?? ""}`;
|
|
3142
|
-
}
|
|
3143
|
-
function validateNumber(value) {
|
|
3144
|
-
let val = value;
|
|
3145
|
-
if (typeof value === "string" && isValueWithUnit(value)) val = getValue(value);
|
|
3146
|
-
val = checkAndCleanDecimalComma(val);
|
|
3147
|
-
if (isNumeric(val)) return {
|
|
3148
|
-
valid: numberSchemaValidator(toNum(val)),
|
|
3149
|
-
errors: transformErrors(numberSchemaValidator.errors)
|
|
3150
|
-
};
|
|
3151
|
-
return {
|
|
3152
|
-
valid: numberSchemaValidator(val),
|
|
3153
|
-
errors: transformErrors(numberSchemaValidator.errors)
|
|
3154
|
-
};
|
|
3155
|
-
}
|
|
3156
2893
|
//#endregion
|
|
3157
2894
|
Object.defineProperty(exports, "ALT_UNITS", {
|
|
3158
2895
|
enumerable: true,
|
|
@@ -3232,12 +2969,6 @@ Object.defineProperty(exports, "allNumbers", {
|
|
|
3232
2969
|
return allNumbers;
|
|
3233
2970
|
}
|
|
3234
2971
|
});
|
|
3235
|
-
Object.defineProperty(exports, "altUnitsList", {
|
|
3236
|
-
enumerable: true,
|
|
3237
|
-
get: function() {
|
|
3238
|
-
return altUnitsList;
|
|
3239
|
-
}
|
|
3240
|
-
});
|
|
3241
2972
|
Object.defineProperty(exports, "asFraction", {
|
|
3242
2973
|
enumerable: true,
|
|
3243
2974
|
get: function() {
|
|
@@ -3286,12 +3017,6 @@ Object.defineProperty(exports, "convertSamePrecision", {
|
|
|
3286
3017
|
return convertSamePrecision;
|
|
3287
3018
|
}
|
|
3288
3019
|
});
|
|
3289
|
-
Object.defineProperty(exports, "convertTable", {
|
|
3290
|
-
enumerable: true,
|
|
3291
|
-
get: function() {
|
|
3292
|
-
return convertTable;
|
|
3293
|
-
}
|
|
3294
|
-
});
|
|
3295
3020
|
Object.defineProperty(exports, "displayNumber", {
|
|
3296
3021
|
enumerable: true,
|
|
3297
3022
|
get: function() {
|
|
@@ -3322,12 +3047,6 @@ Object.defineProperty(exports, "getAltUnitsListByQuantity", {
|
|
|
3322
3047
|
return getAltUnitsListByQuantity;
|
|
3323
3048
|
}
|
|
3324
3049
|
});
|
|
3325
|
-
Object.defineProperty(exports, "getNumberOfDigitsToShow", {
|
|
3326
|
-
enumerable: true,
|
|
3327
|
-
get: function() {
|
|
3328
|
-
return getNumberOfDigitsToShow;
|
|
3329
|
-
}
|
|
3330
|
-
});
|
|
3331
3050
|
Object.defineProperty(exports, "getQuantities", {
|
|
3332
3051
|
enumerable: true,
|
|
3333
3052
|
get: function() {
|
|
@@ -3454,12 +3173,6 @@ Object.defineProperty(exports, "roundByRange", {
|
|
|
3454
3173
|
return roundByRange;
|
|
3455
3174
|
}
|
|
3456
3175
|
});
|
|
3457
|
-
Object.defineProperty(exports, "roundNumberWithLabel", {
|
|
3458
|
-
enumerable: true,
|
|
3459
|
-
get: function() {
|
|
3460
|
-
return roundNumberWithLabel;
|
|
3461
|
-
}
|
|
3462
|
-
});
|
|
3463
3176
|
Object.defineProperty(exports, "roundToDecimalPrecision", {
|
|
3464
3177
|
enumerable: true,
|
|
3465
3178
|
get: function() {
|
|
@@ -3569,4 +3282,4 @@ Object.defineProperty(exports, "withUnit", {
|
|
|
3569
3282
|
}
|
|
3570
3283
|
});
|
|
3571
3284
|
|
|
3572
|
-
//# sourceMappingURL=
|
|
3285
|
+
//# sourceMappingURL=conversion.cjs.map
|