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