@itwin/core-quantity 4.0.0-dev.44 → 4.0.0-dev.48

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 (79) hide show
  1. package/lib/cjs/Constants.d.ts +27 -27
  2. package/lib/cjs/Constants.js +52 -52
  3. package/lib/cjs/Constants.js.map +1 -1
  4. package/lib/cjs/Exception.d.ts +26 -26
  5. package/lib/cjs/Exception.js +38 -38
  6. package/lib/cjs/Exception.js.map +1 -1
  7. package/lib/cjs/Formatter/Format.d.ts +91 -91
  8. package/lib/cjs/Formatter/Format.js +328 -328
  9. package/lib/cjs/Formatter/Format.js.map +1 -1
  10. package/lib/cjs/Formatter/FormatEnums.d.ts +133 -133
  11. package/lib/cjs/Formatter/FormatEnums.js +318 -318
  12. package/lib/cjs/Formatter/FormatEnums.js.map +1 -1
  13. package/lib/cjs/Formatter/Formatter.d.ts +44 -44
  14. package/lib/cjs/Formatter/Formatter.js +371 -371
  15. package/lib/cjs/Formatter/Formatter.js.map +1 -1
  16. package/lib/cjs/Formatter/FormatterSpec.d.ts +39 -39
  17. package/lib/cjs/Formatter/FormatterSpec.js +101 -101
  18. package/lib/cjs/Formatter/FormatterSpec.js.map +1 -1
  19. package/lib/cjs/Formatter/Interfaces.d.ts +62 -62
  20. package/lib/cjs/Formatter/Interfaces.js +17 -17
  21. package/lib/cjs/Formatter/Interfaces.js.map +1 -1
  22. package/lib/cjs/Interfaces.d.ts +86 -86
  23. package/lib/cjs/Interfaces.js +9 -9
  24. package/lib/cjs/Interfaces.js.map +1 -1
  25. package/lib/cjs/Parser.d.ts +93 -93
  26. package/lib/cjs/Parser.js +592 -592
  27. package/lib/cjs/Parser.js.map +1 -1
  28. package/lib/cjs/ParserSpec.d.ts +34 -34
  29. package/lib/cjs/ParserSpec.js +47 -47
  30. package/lib/cjs/ParserSpec.js.map +1 -1
  31. package/lib/cjs/Quantity.d.ts +27 -27
  32. package/lib/cjs/Quantity.js +46 -46
  33. package/lib/cjs/Quantity.js.map +1 -1
  34. package/lib/cjs/Unit.d.ts +25 -25
  35. package/lib/cjs/Unit.js +44 -44
  36. package/lib/cjs/Unit.js.map +1 -1
  37. package/lib/cjs/core-quantity.d.ts +19 -19
  38. package/lib/cjs/core-quantity.js +35 -35
  39. package/lib/cjs/core-quantity.js.map +1 -1
  40. package/lib/esm/Constants.d.ts +27 -27
  41. package/lib/esm/Constants.js +48 -48
  42. package/lib/esm/Constants.js.map +1 -1
  43. package/lib/esm/Exception.d.ts +26 -26
  44. package/lib/esm/Exception.js +34 -34
  45. package/lib/esm/Exception.js.map +1 -1
  46. package/lib/esm/Formatter/Format.d.ts +91 -91
  47. package/lib/esm/Formatter/Format.js +323 -323
  48. package/lib/esm/Formatter/Format.js.map +1 -1
  49. package/lib/esm/Formatter/FormatEnums.d.ts +133 -133
  50. package/lib/esm/Formatter/FormatEnums.js +302 -302
  51. package/lib/esm/Formatter/FormatEnums.js.map +1 -1
  52. package/lib/esm/Formatter/Formatter.d.ts +44 -44
  53. package/lib/esm/Formatter/Formatter.js +367 -367
  54. package/lib/esm/Formatter/Formatter.js.map +1 -1
  55. package/lib/esm/Formatter/FormatterSpec.d.ts +39 -39
  56. package/lib/esm/Formatter/FormatterSpec.js +97 -97
  57. package/lib/esm/Formatter/FormatterSpec.js.map +1 -1
  58. package/lib/esm/Formatter/Interfaces.d.ts +62 -62
  59. package/lib/esm/Formatter/Interfaces.js +13 -13
  60. package/lib/esm/Formatter/Interfaces.js.map +1 -1
  61. package/lib/esm/Interfaces.d.ts +86 -86
  62. package/lib/esm/Interfaces.js +8 -8
  63. package/lib/esm/Interfaces.js.map +1 -1
  64. package/lib/esm/Parser.d.ts +93 -93
  65. package/lib/esm/Parser.js +588 -588
  66. package/lib/esm/Parser.js.map +1 -1
  67. package/lib/esm/ParserSpec.d.ts +34 -34
  68. package/lib/esm/ParserSpec.js +43 -43
  69. package/lib/esm/ParserSpec.js.map +1 -1
  70. package/lib/esm/Quantity.d.ts +27 -27
  71. package/lib/esm/Quantity.js +42 -42
  72. package/lib/esm/Quantity.js.map +1 -1
  73. package/lib/esm/Unit.d.ts +25 -25
  74. package/lib/esm/Unit.js +39 -39
  75. package/lib/esm/Unit.js.map +1 -1
  76. package/lib/esm/core-quantity.d.ts +19 -19
  77. package/lib/esm/core-quantity.js +23 -23
  78. package/lib/esm/core-quantity.js.map +1 -1
  79. package/package.json +5 -5
@@ -1,372 +1,372 @@
1
- "use strict";
2
- /*---------------------------------------------------------------------------------------------
3
- * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
4
- * See LICENSE.md in the project root for license terms and full copyright notice.
5
- *--------------------------------------------------------------------------------------------*/
6
- /** @packageDocumentation
7
- * @module Quantity
8
- */
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.Formatter = void 0;
11
- const Constants_1 = require("../Constants");
12
- const Exception_1 = require("../Exception");
13
- const FormatEnums_1 = require("./FormatEnums");
14
- /** rounding additive
15
- * @internal
16
- */
17
- const FPV_ROUNDFACTOR = 0.50000000001;
18
- /** A private helper class used to format fraction part of value into a numerator and denominator.
19
- * @internal
20
- */
21
- class FractionalNumeric {
22
- constructor(value, precision, reduce) {
23
- this._integral = 0;
24
- this._numerator = 0;
25
- this._denominator = 1;
26
- this._greatestCommonFactor = 1;
27
- this._textParts = [];
28
- this.calculate(value, precision);
29
- this.formTextParts(reduce);
30
- }
31
- calculate(value, denominator) {
32
- const positiveValue = Math.abs(value);
33
- this._denominator = denominator;
34
- this._integral = Math.floor(positiveValue);
35
- const fractionPart = positiveValue - this._integral;
36
- this._numerator = Math.floor(fractionPart * this._denominator + FPV_ROUNDFACTOR);
37
- if (0 !== denominator && (this._numerator / this._denominator) === 1) {
38
- this._numerator = 0;
39
- this._integral += 1;
40
- }
41
- else {
42
- this._greatestCommonFactor = this.getGreatestCommonFactor(this._numerator, this._denominator);
43
- }
44
- }
45
- /** Determine the GCD given two values. This value can be used to reduce a fraction.
46
- * See algorithm description http://en.wikipedia.org/wiki/Euclidean_algorithm
47
- */
48
- getGreatestCommonFactor(numerator, denominator) {
49
- let r;
50
- while (denominator !== 0) {
51
- r = numerator % denominator;
52
- numerator = denominator;
53
- denominator = r;
54
- }
55
- return (numerator < 0) ? -numerator : numerator;
56
- }
57
- get greatestCommonFactor() { return this._greatestCommonFactor; }
58
- get hasFractionPart() { return this._textParts.length > 0; }
59
- get isZero() { return 0 === this._numerator; }
60
- getIntegralString() {
61
- if (this._textParts.length > 0)
62
- return this._textParts[0];
63
- return "";
64
- }
65
- getNumeratorString() {
66
- if (this._textParts.length >= 3)
67
- return this._textParts[1];
68
- return "";
69
- }
70
- getDenominatorString() {
71
- if (this._textParts.length >= 3)
72
- return this._textParts[2];
73
- return "";
74
- }
75
- formTextParts(reduce) {
76
- let numerator = this._numerator;
77
- let denominator = this._denominator;
78
- if (reduce && this._greatestCommonFactor > 1) {
79
- numerator /= this.greatestCommonFactor;
80
- denominator /= this.greatestCommonFactor;
81
- }
82
- this._textParts.push(this._integral.toFixed(0));
83
- if (numerator > 0) {
84
- this._textParts.push(numerator.toFixed(0));
85
- this._textParts.push(denominator.toFixed(0));
86
- }
87
- }
88
- }
89
- /** A helper class that contains methods used to format quantity values based on a format that are defined via the Format class.
90
- * @beta
91
- */
92
- class Formatter {
93
- static isNegligible(value) { return (Math.abs(value) < Formatter.FPV_MINTHRESHOLD); }
94
- /** Return floating point value rounded by specific rounding factor.
95
- * @param value Value to be rounded.
96
- * @param roundTo Rounding factor.
97
- */
98
- static roundDouble(value, roundTo) {
99
- if (Formatter.isNegligible(roundTo))
100
- return value;
101
- roundTo = Math.abs(roundTo);
102
- let rnd = FPV_ROUNDFACTOR + (value / roundTo);
103
- const iVal = Math.floor(rnd);
104
- rnd = iVal * roundTo;
105
- return (value < 0.0) ? -rnd : rnd;
106
- }
107
- /** Generate a formatted text string integer value insert 1000 separators if appropriate.
108
- * @param wholePart Integer value to be formatted.
109
- */
110
- static integerPartToText(wholePart, spec) {
111
- // build invariant string represent wholePart
112
- let formattedValue = wholePart.toFixed(0);
113
- if ((formattedValue.length > 3) && (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.Use1000Separator) && (spec.format.thousandSeparator.length > 0))) {
114
- let numSeparators = Math.floor(formattedValue.length / 3);
115
- let groupLength = formattedValue.length % 3;
116
- if (groupLength === 0) {
117
- numSeparators = numSeparators - 1;
118
- groupLength = groupLength + 3;
119
- }
120
- let outString = formattedValue.substr(0, groupLength);
121
- for (let i = 1; i <= numSeparators; i += 1) {
122
- outString = outString + spec.format.thousandSeparator + formattedValue.substr(groupLength, 3);
123
- groupLength = groupLength + 3;
124
- }
125
- formattedValue = outString;
126
- }
127
- return formattedValue;
128
- }
129
- /** Trim trailing "0" from the text that represent the fractional part of a floating point value.
130
- * @param strVal The value string.
131
- */
132
- static trimTrailingZeroes(strVal) {
133
- let lastNonZeroCharIndex = -1;
134
- for (let i = strVal.length - 1; i >= 0; i--) {
135
- if (strVal.charCodeAt(i) !== Constants_1.QuantityConstants.CHAR_DIGIT_ZERO) {
136
- lastNonZeroCharIndex = i;
137
- break;
138
- }
139
- }
140
- if (lastNonZeroCharIndex >= 0)
141
- return strVal.substr(0, lastNonZeroCharIndex + 1);
142
- return "";
143
- }
144
- /** Format a quantity value into a composite format such as ft-in or deg-min-sec.
145
- * @param compositeValue The value for this part of the composite
146
- * @param isLastPart If false the composite value should be a whole value, if true then the value should be formatted as a floating point value.
147
- * @param label Label for this part of the composite. This will be either the default unit label or a custom label specified the format specification.
148
- */
149
- static formatCompositePart(compositeValue, isLastPart, label, spec) {
150
- let componentText = "";
151
- if (!isLastPart) {
152
- componentText = Formatter.integerPartToText(compositeValue, spec);
153
- }
154
- else {
155
- componentText = Formatter.formatMagnitude(compositeValue, spec);
156
- }
157
- if (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.ShowUnitLabel)) {
158
- componentText = componentText + spec.format.uomSeparator + label;
159
- }
160
- else {
161
- if (!isLastPart)
162
- componentText = `${componentText}:`;
163
- }
164
- return componentText;
165
- }
166
- /** Format a quantity value into a composite format such as ft-in or deg-min-sec.
167
- * @param magnitude quantity value
168
- * @param fromUnit quantity unit
169
- */
170
- static formatComposite(magnitude, spec) {
171
- const compositeStrings = [];
172
- // Caller will deal with appending +||-||() value sign as specified by formatting options so just format positive value
173
- let posMagnitude = Math.abs(magnitude);
174
- // eslint-disable-next-line @typescript-eslint/prefer-for-of
175
- for (let i = 0; i < spec.unitConversions.length; i++) {
176
- const currentLabel = spec.unitConversions[i].label;
177
- const unitConversion = spec.unitConversions[i].conversion;
178
- if (i > 0 && unitConversion.factor < 1.0)
179
- throw new Exception_1.QuantityError(Exception_1.QuantityStatus.InvalidCompositeFormat, `The Format ${spec.format.name} has a invalid unit specification..`);
180
- if (i > 0 && unitConversion.offset !== 0)
181
- throw new Exception_1.QuantityError(Exception_1.QuantityStatus.InvalidCompositeFormat, `The Format ${spec.format.name} has a invalid unit specification..`);
182
- let unitValue = (posMagnitude * unitConversion.factor) + unitConversion.offset + Formatter.FPV_MINTHRESHOLD; // offset should only ever be defined for major unit
183
- if (0 === i) {
184
- const precisionScale = Math.pow(10, 8); // use a fixed round off precision of 8 to avoid loss of precision in actual magnitude
185
- unitValue = Math.floor(unitValue * precisionScale + FPV_ROUNDFACTOR) / precisionScale;
186
- if ((Math.abs(unitValue) < 0.0001) && spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.ZeroEmpty))
187
- return "";
188
- }
189
- if (i < spec.format.units.length - 1) {
190
- const wholePart = Math.floor(unitValue);
191
- const componentText = Formatter.formatCompositePart(wholePart, false, currentLabel, spec);
192
- posMagnitude = unitValue - wholePart;
193
- compositeStrings.push(componentText);
194
- }
195
- else {
196
- const componentText = Formatter.formatCompositePart(unitValue, true, currentLabel, spec);
197
- compositeStrings.push(componentText);
198
- }
199
- }
200
- return compositeStrings.join(spec.format.spacer ? spec.format.spacer : "");
201
- }
202
- /** Format a quantity value into a single text string. Imitate how formatting done by server method NumericFormatSpec::FormatDouble.
203
- * @param magnitude quantity value
204
- */
205
- static formatMagnitude(magnitude, spec) {
206
- let posMagnitude = Math.abs(magnitude);
207
- if ((Math.abs(posMagnitude) < 0.0001) && spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.ZeroEmpty))
208
- return "";
209
- if (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.ApplyRounding))
210
- posMagnitude = Math.abs(Formatter.roundDouble(magnitude, spec.format.roundFactor));
211
- const isSci = ((posMagnitude > 1.0e12) || spec.format.type === FormatEnums_1.FormatType.Scientific);
212
- const isDecimal = (isSci || spec.format.type === FormatEnums_1.FormatType.Decimal);
213
- const isFractional = (!isDecimal && spec.format.type === FormatEnums_1.FormatType.Fractional);
214
- /* const usesStops = spec.format.type === FormatType.Station; */
215
- const isPrecisionZero = spec.format.precision === FormatEnums_1.DecimalPrecision.Zero;
216
- const isKeepSingleZero = spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.KeepSingleZero);
217
- const precisionScale = Math.pow(10.0, spec.format.precision);
218
- const isKeepTrailingZeroes = spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.TrailZeroes);
219
- let expInt = 0.0;
220
- if (isSci && (posMagnitude !== 0.0)) {
221
- let exp = Math.log10(posMagnitude);
222
- let negativeExp = false;
223
- if (exp < 0.0) {
224
- exp = -exp;
225
- negativeExp = true;
226
- }
227
- expInt = Math.floor(exp);
228
- if (spec.format.type === FormatEnums_1.FormatType.Scientific) {
229
- if (spec.format.scientificType === FormatEnums_1.ScientificType.ZeroNormalized && posMagnitude > 1.0)
230
- expInt += 1.0;
231
- else if (spec.format.scientificType === FormatEnums_1.ScientificType.Normalized && posMagnitude < 1.0)
232
- expInt += 1.0;
233
- if (negativeExp)
234
- expInt = -expInt;
235
- }
236
- const factor = Math.pow(10.0, -expInt);
237
- posMagnitude *= factor;
238
- }
239
- let formattedValue = "";
240
- if (isDecimal) {
241
- const actualVal = isPrecisionZero ? posMagnitude + FPV_ROUNDFACTOR : posMagnitude + Formatter.FPV_MINTHRESHOLD;
242
- let wholePart = Math.floor(actualVal);
243
- let fractionPart = actualVal - wholePart;
244
- if (!isPrecisionZero) {
245
- fractionPart = Math.abs(fractionPart) * precisionScale + FPV_ROUNDFACTOR;
246
- if (fractionPart >= precisionScale) {
247
- wholePart += 1;
248
- fractionPart -= precisionScale;
249
- }
250
- }
251
- formattedValue = Formatter.integerPartToText(wholePart, spec);
252
- if (isPrecisionZero) {
253
- if (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.KeepDecimalPoint) && !isKeepSingleZero)
254
- formattedValue = formattedValue + spec.format.decimalSeparator;
255
- else if (isKeepSingleZero)
256
- formattedValue = `${formattedValue + spec.format.decimalSeparator}0`;
257
- }
258
- else {
259
- fractionPart = Math.floor(fractionPart) / precisionScale;
260
- let fractionString = fractionPart.toFixed(spec.format.precision);
261
- // remove leading "0."
262
- fractionString = fractionString.substr(2).padEnd(spec.format.precision, "0");
263
- if (!isKeepTrailingZeroes)
264
- fractionString = Formatter.trimTrailingZeroes(fractionString);
265
- if (fractionString.length > 0)
266
- formattedValue = formattedValue + spec.format.decimalSeparator + fractionString;
267
- else {
268
- if (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.KeepDecimalPoint))
269
- formattedValue = formattedValue + spec.format.decimalSeparator;
270
- }
271
- }
272
- if (isSci) {
273
- const expString = `e${expInt.toFixed(0)}`;
274
- formattedValue = formattedValue + expString;
275
- }
276
- }
277
- else if (isFractional) {
278
- const fn = new FractionalNumeric(posMagnitude, spec.format.precision, true);
279
- formattedValue = fn.getIntegralString();
280
- if (!fn.isZero && fn.hasFractionPart) {
281
- const wholeFractionSeparator = spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.FractionDash) ? "-" : " ";
282
- const fractionString = `${fn.getNumeratorString()}/${fn.getDenominatorString()}`;
283
- formattedValue = formattedValue + wholeFractionSeparator + fractionString;
284
- }
285
- }
286
- else /* if (usesStops)*/ {
287
- // we assume that stopping value is always positive
288
- posMagnitude = Math.floor(posMagnitude * precisionScale + FPV_ROUNDFACTOR) / precisionScale;
289
- const denominator = (Math.pow(10, spec.format.stationOffsetSize));
290
- const tVal = Math.floor(posMagnitude); // this is the integer part only
291
- const hiPart = Math.floor(tVal / denominator);
292
- const lowPart = tVal - hiPart * denominator;
293
- const fract = posMagnitude - tVal;
294
- const fractionPart = Math.floor(fract * precisionScale + FPV_ROUNDFACTOR);
295
- const stationString = hiPart.toFixed(0) + spec.format.stationSeparator + lowPart.toFixed(0).padStart(spec.format.stationOffsetSize, "0");
296
- let fractionString = "";
297
- if (fractionPart > 0) {
298
- fractionString = (fractionPart / precisionScale).toFixed(spec.format.precision);
299
- // remove leading "0."
300
- fractionString = fractionString.substr(2).padEnd(spec.format.precision, "0");
301
- if (!isKeepTrailingZeroes)
302
- fractionString = Formatter.trimTrailingZeroes(fractionString);
303
- formattedValue = stationString + spec.format.decimalSeparator + fractionString;
304
- }
305
- else {
306
- if (isKeepTrailingZeroes)
307
- fractionString = spec.format.decimalSeparator + "".padEnd(spec.format.precision, "0");
308
- else if (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.KeepDecimalPoint))
309
- fractionString = spec.format.decimalSeparator;
310
- formattedValue = stationString + fractionString;
311
- }
312
- }
313
- return formattedValue;
314
- }
315
- /** Format a quantity value into a single text string based on the current format specification of this class.
316
- * @param magnitude defines the value to spec.format.
317
- * @param spec A FormatterSpec object the defines specification for the magnitude and unit conversions for the formatter.
318
- */
319
- static formatQuantity(magnitude, spec) {
320
- const valueIsNegative = magnitude < 0.0;
321
- let prefix = "";
322
- let suffix = "";
323
- let formattedValue = "";
324
- switch (spec.format.showSignOption) {
325
- case FormatEnums_1.ShowSignOption.NegativeParentheses:
326
- if (valueIsNegative) {
327
- prefix = "(";
328
- suffix = ")";
329
- }
330
- break;
331
- case FormatEnums_1.ShowSignOption.OnlyNegative:
332
- if (valueIsNegative)
333
- prefix = "-";
334
- break;
335
- case FormatEnums_1.ShowSignOption.SignAlways:
336
- if (valueIsNegative)
337
- prefix = "-";
338
- else
339
- prefix = "+";
340
- break;
341
- case FormatEnums_1.ShowSignOption.NoSign:
342
- default:
343
- break;
344
- }
345
- let formattedMagnitude = "";
346
- if (spec.format.hasUnits) {
347
- formattedMagnitude = Formatter.formatComposite(magnitude, spec);
348
- }
349
- else {
350
- // unitless quantity
351
- formattedMagnitude = Formatter.formatMagnitude(magnitude, spec);
352
- if (formattedMagnitude.length > 0 && spec.unitConversions.length > 0 && spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.ShowUnitLabel)) {
353
- if (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.PrependUnitLabel))
354
- formattedMagnitude = spec.unitConversions[0].label + spec.format.uomSeparator + formattedMagnitude;
355
- else
356
- formattedMagnitude = formattedMagnitude + spec.format.uomSeparator + spec.unitConversions[0].label;
357
- }
358
- }
359
- // add Sign prefix and suffix as necessary
360
- if ((prefix.length > 0 || suffix.length > 0) && formattedMagnitude.length > 0)
361
- formattedValue = prefix + formattedMagnitude + suffix;
362
- else
363
- formattedValue = formattedMagnitude;
364
- if (spec.format.minWidth && spec.format.minWidth < formattedValue.length)
365
- formattedValue.padStart(spec.format.minWidth, " ");
366
- return formattedValue;
367
- }
368
- }
369
- exports.Formatter = Formatter;
370
- // eslint-disable-next-line @typescript-eslint/naming-convention
371
- Formatter.FPV_MINTHRESHOLD = 1.0e-14;
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
4
+ * See LICENSE.md in the project root for license terms and full copyright notice.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ /** @packageDocumentation
7
+ * @module Quantity
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.Formatter = void 0;
11
+ const Constants_1 = require("../Constants");
12
+ const Exception_1 = require("../Exception");
13
+ const FormatEnums_1 = require("./FormatEnums");
14
+ /** rounding additive
15
+ * @internal
16
+ */
17
+ const FPV_ROUNDFACTOR = 0.50000000001;
18
+ /** A private helper class used to format fraction part of value into a numerator and denominator.
19
+ * @internal
20
+ */
21
+ class FractionalNumeric {
22
+ constructor(value, precision, reduce) {
23
+ this._integral = 0;
24
+ this._numerator = 0;
25
+ this._denominator = 1;
26
+ this._greatestCommonFactor = 1;
27
+ this._textParts = [];
28
+ this.calculate(value, precision);
29
+ this.formTextParts(reduce);
30
+ }
31
+ calculate(value, denominator) {
32
+ const positiveValue = Math.abs(value);
33
+ this._denominator = denominator;
34
+ this._integral = Math.floor(positiveValue);
35
+ const fractionPart = positiveValue - this._integral;
36
+ this._numerator = Math.floor(fractionPart * this._denominator + FPV_ROUNDFACTOR);
37
+ if (0 !== denominator && (this._numerator / this._denominator) === 1) {
38
+ this._numerator = 0;
39
+ this._integral += 1;
40
+ }
41
+ else {
42
+ this._greatestCommonFactor = this.getGreatestCommonFactor(this._numerator, this._denominator);
43
+ }
44
+ }
45
+ /** Determine the GCD given two values. This value can be used to reduce a fraction.
46
+ * See algorithm description http://en.wikipedia.org/wiki/Euclidean_algorithm
47
+ */
48
+ getGreatestCommonFactor(numerator, denominator) {
49
+ let r;
50
+ while (denominator !== 0) {
51
+ r = numerator % denominator;
52
+ numerator = denominator;
53
+ denominator = r;
54
+ }
55
+ return (numerator < 0) ? -numerator : numerator;
56
+ }
57
+ get greatestCommonFactor() { return this._greatestCommonFactor; }
58
+ get hasFractionPart() { return this._textParts.length > 0; }
59
+ get isZero() { return 0 === this._numerator; }
60
+ getIntegralString() {
61
+ if (this._textParts.length > 0)
62
+ return this._textParts[0];
63
+ return "";
64
+ }
65
+ getNumeratorString() {
66
+ if (this._textParts.length >= 3)
67
+ return this._textParts[1];
68
+ return "";
69
+ }
70
+ getDenominatorString() {
71
+ if (this._textParts.length >= 3)
72
+ return this._textParts[2];
73
+ return "";
74
+ }
75
+ formTextParts(reduce) {
76
+ let numerator = this._numerator;
77
+ let denominator = this._denominator;
78
+ if (reduce && this._greatestCommonFactor > 1) {
79
+ numerator /= this.greatestCommonFactor;
80
+ denominator /= this.greatestCommonFactor;
81
+ }
82
+ this._textParts.push(this._integral.toFixed(0));
83
+ if (numerator > 0) {
84
+ this._textParts.push(numerator.toFixed(0));
85
+ this._textParts.push(denominator.toFixed(0));
86
+ }
87
+ }
88
+ }
89
+ /** A helper class that contains methods used to format quantity values based on a format that are defined via the Format class.
90
+ * @beta
91
+ */
92
+ class Formatter {
93
+ static isNegligible(value) { return (Math.abs(value) < Formatter.FPV_MINTHRESHOLD); }
94
+ /** Return floating point value rounded by specific rounding factor.
95
+ * @param value Value to be rounded.
96
+ * @param roundTo Rounding factor.
97
+ */
98
+ static roundDouble(value, roundTo) {
99
+ if (Formatter.isNegligible(roundTo))
100
+ return value;
101
+ roundTo = Math.abs(roundTo);
102
+ let rnd = FPV_ROUNDFACTOR + (value / roundTo);
103
+ const iVal = Math.floor(rnd);
104
+ rnd = iVal * roundTo;
105
+ return (value < 0.0) ? -rnd : rnd;
106
+ }
107
+ /** Generate a formatted text string integer value insert 1000 separators if appropriate.
108
+ * @param wholePart Integer value to be formatted.
109
+ */
110
+ static integerPartToText(wholePart, spec) {
111
+ // build invariant string represent wholePart
112
+ let formattedValue = wholePart.toFixed(0);
113
+ if ((formattedValue.length > 3) && (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.Use1000Separator) && (spec.format.thousandSeparator.length > 0))) {
114
+ let numSeparators = Math.floor(formattedValue.length / 3);
115
+ let groupLength = formattedValue.length % 3;
116
+ if (groupLength === 0) {
117
+ numSeparators = numSeparators - 1;
118
+ groupLength = groupLength + 3;
119
+ }
120
+ let outString = formattedValue.substr(0, groupLength);
121
+ for (let i = 1; i <= numSeparators; i += 1) {
122
+ outString = outString + spec.format.thousandSeparator + formattedValue.substr(groupLength, 3);
123
+ groupLength = groupLength + 3;
124
+ }
125
+ formattedValue = outString;
126
+ }
127
+ return formattedValue;
128
+ }
129
+ /** Trim trailing "0" from the text that represent the fractional part of a floating point value.
130
+ * @param strVal The value string.
131
+ */
132
+ static trimTrailingZeroes(strVal) {
133
+ let lastNonZeroCharIndex = -1;
134
+ for (let i = strVal.length - 1; i >= 0; i--) {
135
+ if (strVal.charCodeAt(i) !== Constants_1.QuantityConstants.CHAR_DIGIT_ZERO) {
136
+ lastNonZeroCharIndex = i;
137
+ break;
138
+ }
139
+ }
140
+ if (lastNonZeroCharIndex >= 0)
141
+ return strVal.substr(0, lastNonZeroCharIndex + 1);
142
+ return "";
143
+ }
144
+ /** Format a quantity value into a composite format such as ft-in or deg-min-sec.
145
+ * @param compositeValue The value for this part of the composite
146
+ * @param isLastPart If false the composite value should be a whole value, if true then the value should be formatted as a floating point value.
147
+ * @param label Label for this part of the composite. This will be either the default unit label or a custom label specified the format specification.
148
+ */
149
+ static formatCompositePart(compositeValue, isLastPart, label, spec) {
150
+ let componentText = "";
151
+ if (!isLastPart) {
152
+ componentText = Formatter.integerPartToText(compositeValue, spec);
153
+ }
154
+ else {
155
+ componentText = Formatter.formatMagnitude(compositeValue, spec);
156
+ }
157
+ if (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.ShowUnitLabel)) {
158
+ componentText = componentText + spec.format.uomSeparator + label;
159
+ }
160
+ else {
161
+ if (!isLastPart)
162
+ componentText = `${componentText}:`;
163
+ }
164
+ return componentText;
165
+ }
166
+ /** Format a quantity value into a composite format such as ft-in or deg-min-sec.
167
+ * @param magnitude quantity value
168
+ * @param fromUnit quantity unit
169
+ */
170
+ static formatComposite(magnitude, spec) {
171
+ const compositeStrings = [];
172
+ // Caller will deal with appending +||-||() value sign as specified by formatting options so just format positive value
173
+ let posMagnitude = Math.abs(magnitude);
174
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
175
+ for (let i = 0; i < spec.unitConversions.length; i++) {
176
+ const currentLabel = spec.unitConversions[i].label;
177
+ const unitConversion = spec.unitConversions[i].conversion;
178
+ if (i > 0 && unitConversion.factor < 1.0)
179
+ throw new Exception_1.QuantityError(Exception_1.QuantityStatus.InvalidCompositeFormat, `The Format ${spec.format.name} has a invalid unit specification..`);
180
+ if (i > 0 && unitConversion.offset !== 0)
181
+ throw new Exception_1.QuantityError(Exception_1.QuantityStatus.InvalidCompositeFormat, `The Format ${spec.format.name} has a invalid unit specification..`);
182
+ let unitValue = (posMagnitude * unitConversion.factor) + unitConversion.offset + Formatter.FPV_MINTHRESHOLD; // offset should only ever be defined for major unit
183
+ if (0 === i) {
184
+ const precisionScale = Math.pow(10, 8); // use a fixed round off precision of 8 to avoid loss of precision in actual magnitude
185
+ unitValue = Math.floor(unitValue * precisionScale + FPV_ROUNDFACTOR) / precisionScale;
186
+ if ((Math.abs(unitValue) < 0.0001) && spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.ZeroEmpty))
187
+ return "";
188
+ }
189
+ if (i < spec.format.units.length - 1) {
190
+ const wholePart = Math.floor(unitValue);
191
+ const componentText = Formatter.formatCompositePart(wholePart, false, currentLabel, spec);
192
+ posMagnitude = unitValue - wholePart;
193
+ compositeStrings.push(componentText);
194
+ }
195
+ else {
196
+ const componentText = Formatter.formatCompositePart(unitValue, true, currentLabel, spec);
197
+ compositeStrings.push(componentText);
198
+ }
199
+ }
200
+ return compositeStrings.join(spec.format.spacer ? spec.format.spacer : "");
201
+ }
202
+ /** Format a quantity value into a single text string. Imitate how formatting done by server method NumericFormatSpec::FormatDouble.
203
+ * @param magnitude quantity value
204
+ */
205
+ static formatMagnitude(magnitude, spec) {
206
+ let posMagnitude = Math.abs(magnitude);
207
+ if ((Math.abs(posMagnitude) < 0.0001) && spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.ZeroEmpty))
208
+ return "";
209
+ if (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.ApplyRounding))
210
+ posMagnitude = Math.abs(Formatter.roundDouble(magnitude, spec.format.roundFactor));
211
+ const isSci = ((posMagnitude > 1.0e12) || spec.format.type === FormatEnums_1.FormatType.Scientific);
212
+ const isDecimal = (isSci || spec.format.type === FormatEnums_1.FormatType.Decimal);
213
+ const isFractional = (!isDecimal && spec.format.type === FormatEnums_1.FormatType.Fractional);
214
+ /* const usesStops = spec.format.type === FormatType.Station; */
215
+ const isPrecisionZero = spec.format.precision === FormatEnums_1.DecimalPrecision.Zero;
216
+ const isKeepSingleZero = spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.KeepSingleZero);
217
+ const precisionScale = Math.pow(10.0, spec.format.precision);
218
+ const isKeepTrailingZeroes = spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.TrailZeroes);
219
+ let expInt = 0.0;
220
+ if (isSci && (posMagnitude !== 0.0)) {
221
+ let exp = Math.log10(posMagnitude);
222
+ let negativeExp = false;
223
+ if (exp < 0.0) {
224
+ exp = -exp;
225
+ negativeExp = true;
226
+ }
227
+ expInt = Math.floor(exp);
228
+ if (spec.format.type === FormatEnums_1.FormatType.Scientific) {
229
+ if (spec.format.scientificType === FormatEnums_1.ScientificType.ZeroNormalized && posMagnitude > 1.0)
230
+ expInt += 1.0;
231
+ else if (spec.format.scientificType === FormatEnums_1.ScientificType.Normalized && posMagnitude < 1.0)
232
+ expInt += 1.0;
233
+ if (negativeExp)
234
+ expInt = -expInt;
235
+ }
236
+ const factor = Math.pow(10.0, -expInt);
237
+ posMagnitude *= factor;
238
+ }
239
+ let formattedValue = "";
240
+ if (isDecimal) {
241
+ const actualVal = isPrecisionZero ? posMagnitude + FPV_ROUNDFACTOR : posMagnitude + Formatter.FPV_MINTHRESHOLD;
242
+ let wholePart = Math.floor(actualVal);
243
+ let fractionPart = actualVal - wholePart;
244
+ if (!isPrecisionZero) {
245
+ fractionPart = Math.abs(fractionPart) * precisionScale + FPV_ROUNDFACTOR;
246
+ if (fractionPart >= precisionScale) {
247
+ wholePart += 1;
248
+ fractionPart -= precisionScale;
249
+ }
250
+ }
251
+ formattedValue = Formatter.integerPartToText(wholePart, spec);
252
+ if (isPrecisionZero) {
253
+ if (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.KeepDecimalPoint) && !isKeepSingleZero)
254
+ formattedValue = formattedValue + spec.format.decimalSeparator;
255
+ else if (isKeepSingleZero)
256
+ formattedValue = `${formattedValue + spec.format.decimalSeparator}0`;
257
+ }
258
+ else {
259
+ fractionPart = Math.floor(fractionPart) / precisionScale;
260
+ let fractionString = fractionPart.toFixed(spec.format.precision);
261
+ // remove leading "0."
262
+ fractionString = fractionString.substr(2).padEnd(spec.format.precision, "0");
263
+ if (!isKeepTrailingZeroes)
264
+ fractionString = Formatter.trimTrailingZeroes(fractionString);
265
+ if (fractionString.length > 0)
266
+ formattedValue = formattedValue + spec.format.decimalSeparator + fractionString;
267
+ else {
268
+ if (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.KeepDecimalPoint))
269
+ formattedValue = formattedValue + spec.format.decimalSeparator + (isKeepSingleZero ? "0" : "");
270
+ }
271
+ }
272
+ if (isSci) {
273
+ const expString = `e${expInt.toFixed(0)}`;
274
+ formattedValue = formattedValue + expString;
275
+ }
276
+ }
277
+ else if (isFractional) {
278
+ const fn = new FractionalNumeric(posMagnitude, spec.format.precision, true);
279
+ formattedValue = fn.getIntegralString();
280
+ if (!fn.isZero && fn.hasFractionPart) {
281
+ const wholeFractionSeparator = spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.FractionDash) ? "-" : " ";
282
+ const fractionString = `${fn.getNumeratorString()}/${fn.getDenominatorString()}`;
283
+ formattedValue = formattedValue + wholeFractionSeparator + fractionString;
284
+ }
285
+ }
286
+ else /* if (usesStops)*/ {
287
+ // we assume that stopping value is always positive
288
+ posMagnitude = Math.floor(posMagnitude * precisionScale + FPV_ROUNDFACTOR) / precisionScale;
289
+ const denominator = (Math.pow(10, spec.format.stationOffsetSize));
290
+ const tVal = Math.floor(posMagnitude); // this is the integer part only
291
+ const hiPart = Math.floor(tVal / denominator);
292
+ const lowPart = tVal - hiPart * denominator;
293
+ const fract = posMagnitude - tVal;
294
+ const fractionPart = Math.floor(fract * precisionScale + FPV_ROUNDFACTOR);
295
+ const stationString = hiPart.toFixed(0) + spec.format.stationSeparator + lowPart.toFixed(0).padStart(spec.format.stationOffsetSize, "0");
296
+ let fractionString = "";
297
+ if (fractionPart > 0) {
298
+ fractionString = (fractionPart / precisionScale).toFixed(spec.format.precision);
299
+ // remove leading "0."
300
+ fractionString = fractionString.substr(2).padEnd(spec.format.precision, "0");
301
+ if (!isKeepTrailingZeroes)
302
+ fractionString = Formatter.trimTrailingZeroes(fractionString);
303
+ formattedValue = stationString + spec.format.decimalSeparator + fractionString;
304
+ }
305
+ else {
306
+ if (isKeepTrailingZeroes)
307
+ fractionString = spec.format.decimalSeparator + "".padEnd(spec.format.precision, "0");
308
+ else if (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.KeepDecimalPoint))
309
+ fractionString = spec.format.decimalSeparator;
310
+ formattedValue = stationString + fractionString;
311
+ }
312
+ }
313
+ return formattedValue;
314
+ }
315
+ /** Format a quantity value into a single text string based on the current format specification of this class.
316
+ * @param magnitude defines the value to spec.format.
317
+ * @param spec A FormatterSpec object the defines specification for the magnitude and unit conversions for the formatter.
318
+ */
319
+ static formatQuantity(magnitude, spec) {
320
+ const valueIsNegative = magnitude < 0.0;
321
+ let prefix = "";
322
+ let suffix = "";
323
+ let formattedValue = "";
324
+ switch (spec.format.showSignOption) {
325
+ case FormatEnums_1.ShowSignOption.NegativeParentheses:
326
+ if (valueIsNegative) {
327
+ prefix = "(";
328
+ suffix = ")";
329
+ }
330
+ break;
331
+ case FormatEnums_1.ShowSignOption.OnlyNegative:
332
+ if (valueIsNegative)
333
+ prefix = "-";
334
+ break;
335
+ case FormatEnums_1.ShowSignOption.SignAlways:
336
+ if (valueIsNegative)
337
+ prefix = "-";
338
+ else
339
+ prefix = "+";
340
+ break;
341
+ case FormatEnums_1.ShowSignOption.NoSign:
342
+ default:
343
+ break;
344
+ }
345
+ let formattedMagnitude = "";
346
+ if (spec.format.hasUnits) {
347
+ formattedMagnitude = Formatter.formatComposite(magnitude, spec);
348
+ }
349
+ else {
350
+ // unitless quantity
351
+ formattedMagnitude = Formatter.formatMagnitude(magnitude, spec);
352
+ if (formattedMagnitude.length > 0 && spec.unitConversions.length > 0 && spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.ShowUnitLabel)) {
353
+ if (spec.format.hasFormatTraitSet(FormatEnums_1.FormatTraits.PrependUnitLabel))
354
+ formattedMagnitude = spec.unitConversions[0].label + spec.format.uomSeparator + formattedMagnitude;
355
+ else
356
+ formattedMagnitude = formattedMagnitude + spec.format.uomSeparator + spec.unitConversions[0].label;
357
+ }
358
+ }
359
+ // add Sign prefix and suffix as necessary
360
+ if ((prefix.length > 0 || suffix.length > 0) && formattedMagnitude.length > 0)
361
+ formattedValue = prefix + formattedMagnitude + suffix;
362
+ else
363
+ formattedValue = formattedMagnitude;
364
+ if (spec.format.minWidth && spec.format.minWidth < formattedValue.length)
365
+ formattedValue.padStart(spec.format.minWidth, " ");
366
+ return formattedValue;
367
+ }
368
+ }
369
+ exports.Formatter = Formatter;
370
+ // eslint-disable-next-line @typescript-eslint/naming-convention
371
+ Formatter.FPV_MINTHRESHOLD = 1.0e-14;
372
372
  //# sourceMappingURL=Formatter.js.map