@obinexusmk2/hypernum 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +355 -256
- package/dist/index.cjs +4 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -7
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +2 -2
- package/dist/index.umd.js.map +1 -1
- package/dist/types/{config → src/config}/config-loader.d.ts +0 -0
- package/dist/types/src/config/config-loader.d.ts.map +1 -0
- package/dist/types/{config → src/config}/config-parser.d.ts +0 -0
- package/dist/types/src/config/config-parser.d.ts.map +1 -0
- package/dist/types/{config → src/config}/config-resolver.d.ts +0 -0
- package/dist/types/src/config/config-resolver.d.ts.map +1 -0
- package/dist/types/{config → src/config}/config-source.d.ts +0 -0
- package/dist/types/src/config/config-source.d.ts.map +1 -0
- package/dist/types/{config → src/config}/index.d.ts +0 -0
- package/dist/types/src/config/index.d.ts.map +1 -0
- package/dist/types/{core → src/core}/common.d.ts +0 -0
- package/dist/types/src/core/common.d.ts.map +1 -0
- package/dist/types/{core → src/core}/config.d.ts +0 -0
- package/dist/types/src/core/config.d.ts.map +1 -0
- package/dist/types/{core → src/core}/constants.d.ts +0 -0
- package/dist/types/src/core/constants.d.ts.map +1 -0
- package/dist/types/{core → src/core}/errors.d.ts +0 -0
- package/dist/types/src/core/errors.d.ts.map +1 -0
- package/dist/types/{core → src/core}/hypernum.d.ts +0 -0
- package/dist/types/src/core/hypernum.d.ts.map +1 -0
- package/dist/types/{core → src/core}/index.d.ts +0 -0
- package/dist/types/src/core/index.d.ts.map +1 -0
- package/dist/types/{index.d.ts → src/index.d.ts} +1 -1
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/arithmetic.d.ts +0 -0
- package/dist/types/src/operations/arithmetic.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/bitwise.d.ts +0 -0
- package/dist/types/src/operations/bitwise.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/comparison.d.ts +0 -0
- package/dist/types/src/operations/comparison.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/conversion.d.ts +0 -0
- package/dist/types/src/operations/conversion.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/factorial.d.ts +0 -0
- package/dist/types/src/operations/factorial.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/index.d.ts +0 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/{operations → src/operations}/power.d.ts +0 -0
- package/dist/types/src/operations/power.d.ts.map +1 -0
- package/dist/types/{storage → src/storage}/Heap.d.ts +0 -0
- package/dist/types/src/storage/Heap.d.ts.map +1 -0
- package/dist/types/{storage → src/storage}/index.d.ts +0 -0
- package/dist/types/src/storage/index.d.ts.map +1 -0
- package/dist/types/{structures → src/structures}/ackermann.d.ts +0 -0
- package/dist/types/src/structures/ackermann.d.ts.map +1 -0
- package/dist/types/{structures → src/structures}/big-array.d.ts +0 -0
- package/dist/types/src/structures/big-array.d.ts.map +1 -0
- package/dist/types/{structures → src/structures}/index.d.ts +0 -0
- package/dist/types/src/structures/index.d.ts.map +1 -0
- package/dist/types/{structures → src/structures}/number-tree.d.ts +0 -0
- package/dist/types/src/structures/number-tree.d.ts.map +1 -0
- package/dist/types/{structures → src/structures}/power-tower.d.ts +0 -0
- package/dist/types/src/structures/power-tower.d.ts.map +1 -0
- package/dist/types/{utils → src/utils}/formatting.d.ts +0 -0
- package/dist/types/src/utils/formatting.d.ts.map +1 -0
- package/dist/types/{utils → src/utils}/index.d.ts +0 -0
- package/dist/types/src/utils/index.d.ts.map +1 -0
- package/dist/types/{utils → src/utils}/parser.d.ts +0 -0
- package/dist/types/src/utils/parser.d.ts.map +1 -0
- package/dist/types/{utils → src/utils}/precision.d.ts +0 -0
- package/dist/types/src/utils/precision.d.ts.map +1 -0
- package/dist/types/{utils → src/utils}/validation.d.ts +0 -0
- package/dist/types/src/utils/validation.d.ts.map +1 -0
- package/package.json +169 -164
- package/rollup.config.js +163 -161
- package/src/cli/hypernum.js +272 -0
- package/src/config/config-loader.ts +0 -0
- package/src/config/config-parser.ts +160 -160
- package/src/config/config-resolver.ts +0 -0
- package/src/config/config-source.ts +0 -0
- package/src/config/index.ts +0 -0
- package/src/core/common.ts +184 -184
- package/src/core/config.ts +392 -392
- package/src/core/constants.ts +101 -101
- package/src/core/errors.ts +202 -202
- package/src/core/hypernum.ts +240 -240
- package/src/core/index.ts +4 -4
- package/src/index.ts +179 -182
- package/src/operations/arithmetic.ts +332 -332
- package/src/operations/bitwise.ts +366 -366
- package/src/operations/comparison.ts +271 -271
- package/src/operations/conversion.ts +399 -399
- package/src/operations/factorial.ts +278 -278
- package/src/operations/index.ts +4 -4
- package/src/operations/power.ts +315 -315
- package/src/storage/Heap.ts +237 -237
- package/src/storage/index.ts +0 -0
- package/src/structures/ackermann.ts +232 -232
- package/src/structures/big-array.ts +305 -305
- package/src/structures/index.ts +3 -3
- package/src/structures/number-tree.ts +403 -403
- package/src/structures/power-tower.ts +277 -277
- package/src/types/common.d.ts +356 -356
- package/src/types/core.d.ts +160 -160
- package/src/types/index.d.ts +1 -1
- package/src/utils/formatting.ts +245 -245
- package/src/utils/index.ts +4 -4
- package/src/utils/parser.ts +244 -244
- package/src/utils/precision.ts +216 -216
- package/src/utils/validation.ts +182 -182
- package/tsconfig.json +83 -83
- package/dist/types/config/config-loader.d.ts.map +0 -1
- package/dist/types/config/config-parser.d.ts.map +0 -1
- package/dist/types/config/config-resolver.d.ts.map +0 -1
- package/dist/types/config/config-source.d.ts.map +0 -1
- package/dist/types/config/index.d.ts.map +0 -1
- package/dist/types/core/common.d.ts.map +0 -1
- package/dist/types/core/config.d.ts.map +0 -1
- package/dist/types/core/constants.d.ts.map +0 -1
- package/dist/types/core/errors.d.ts.map +0 -1
- package/dist/types/core/hypernum.d.ts.map +0 -1
- package/dist/types/core/index.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/operations/arithmetic.d.ts.map +0 -1
- package/dist/types/operations/bitwise.d.ts.map +0 -1
- package/dist/types/operations/comparison.d.ts.map +0 -1
- package/dist/types/operations/conversion.d.ts.map +0 -1
- package/dist/types/operations/factorial.d.ts.map +0 -1
- package/dist/types/operations/index.d.ts.map +0 -1
- package/dist/types/operations/power.d.ts.map +0 -1
- package/dist/types/storage/Heap.d.ts.map +0 -1
- package/dist/types/storage/index.d.ts.map +0 -1
- package/dist/types/structures/ackermann.d.ts.map +0 -1
- package/dist/types/structures/big-array.d.ts.map +0 -1
- package/dist/types/structures/index.d.ts.map +0 -1
- package/dist/types/structures/number-tree.d.ts.map +0 -1
- package/dist/types/structures/power-tower.d.ts.map +0 -1
- package/dist/types/utils/formatting.d.ts.map +0 -1
- package/dist/types/utils/index.d.ts.map +0 -1
- package/dist/types/utils/parser.d.ts.map +0 -1
- package/dist/types/utils/precision.d.ts.map +0 -1
- package/dist/types/utils/validation.d.ts.map +0 -1
package/src/utils/parser.ts
CHANGED
|
@@ -1,245 +1,245 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parser utilities for Hypernum library
|
|
3
|
-
* Provides functions for parsing various number formats and notations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { ValidationError } from './validation';
|
|
7
|
-
import { RoundingMode } from './precision';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Supported number format types
|
|
11
|
-
*/
|
|
12
|
-
export enum NumberFormat {
|
|
13
|
-
STANDARD = 'STANDARD', // Regular decimal notation
|
|
14
|
-
SCIENTIFIC = 'SCIENTIFIC', // Scientific notation (e.g., 1.23e4)
|
|
15
|
-
ENGINEERING = 'ENGINEERING', // Engineering notation (e.g., 1.23e6)
|
|
16
|
-
COMPACT = 'COMPACT', // Compact notation with suffixes (e.g., 1.23M)
|
|
17
|
-
FRACTION = 'FRACTION', // Fractional notation (e.g., 1/3)
|
|
18
|
-
PERCENTAGE = 'PERCENTAGE', // Percentage notation (e.g., 12.3%)
|
|
19
|
-
HEXADECIMAL = 'HEXADECIMAL', // Hex notation (e.g., 0xFF)
|
|
20
|
-
BINARY = 'BINARY', // Binary notation (e.g., 0b1010)
|
|
21
|
-
OCTAL = 'OCTAL' // Octal notation (e.g., 0o777)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Options for parsing numbers
|
|
26
|
-
*/
|
|
27
|
-
export interface ParseOptions {
|
|
28
|
-
format?: NumberFormat;
|
|
29
|
-
base?: number;
|
|
30
|
-
allowFractions?: boolean;
|
|
31
|
-
rounding?: RoundingMode;
|
|
32
|
-
precision?: number;
|
|
33
|
-
strict?: boolean;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const DEFAULT_PARSE_OPTIONS: Required<ParseOptions> = {
|
|
37
|
-
format: NumberFormat.STANDARD,
|
|
38
|
-
base: 10,
|
|
39
|
-
allowFractions: false,
|
|
40
|
-
rounding: RoundingMode.HALF_EVEN,
|
|
41
|
-
precision: 0,
|
|
42
|
-
strict: true
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Parse any supported number format to BigInt
|
|
47
|
-
*/
|
|
48
|
-
export const parseNumber = (input: string, options: ParseOptions = {}): bigint => {
|
|
49
|
-
const opts: Required<ParseOptions> = { ...DEFAULT_PARSE_OPTIONS, ...options };
|
|
50
|
-
|
|
51
|
-
// Remove whitespace if not in strict mode
|
|
52
|
-
const str = opts.strict ? input : input.trim();
|
|
53
|
-
|
|
54
|
-
if (str === '') {
|
|
55
|
-
throw new ValidationError('Empty string cannot be parsed as a number');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
switch (opts.format) {
|
|
60
|
-
case NumberFormat.SCIENTIFIC:
|
|
61
|
-
case NumberFormat.ENGINEERING:
|
|
62
|
-
return parseScientificNotation(str);
|
|
63
|
-
case NumberFormat.COMPACT:
|
|
64
|
-
return parseCompactNotation(str);
|
|
65
|
-
case NumberFormat.FRACTION:
|
|
66
|
-
return parseFraction(str, opts.rounding, opts.precision);
|
|
67
|
-
case NumberFormat.PERCENTAGE:
|
|
68
|
-
return parsePercentage(str);
|
|
69
|
-
case NumberFormat.HEXADECIMAL:
|
|
70
|
-
return parseBaseNotation(str, 16);
|
|
71
|
-
case NumberFormat.BINARY:
|
|
72
|
-
return parseBaseNotation(str, 2);
|
|
73
|
-
case NumberFormat.OCTAL:
|
|
74
|
-
return parseBaseNotation(str, 8);
|
|
75
|
-
default:
|
|
76
|
-
return parseStandardNotation(str);
|
|
77
|
-
}
|
|
78
|
-
} catch (error) {
|
|
79
|
-
if (error instanceof ValidationError) {
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
throw new ValidationError(`Failed to parse number: ${input}`);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Parse standard decimal notation
|
|
88
|
-
*/
|
|
89
|
-
const parseStandardNotation = (str: string): bigint => {
|
|
90
|
-
// Remove commas and validate format
|
|
91
|
-
const cleanStr = str.replace(/,/g, '');
|
|
92
|
-
if (!/^-?\d+$/.test(cleanStr)) {
|
|
93
|
-
throw new ValidationError('Invalid standard notation format');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return BigInt(cleanStr);
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Parse scientific notation (e.g., 1.23e4)
|
|
101
|
-
*/
|
|
102
|
-
const parseScientificNotation = (str: string): bigint => {
|
|
103
|
-
const match = str.match(/^(-?\d*\.?\d+)e([+-]?\d+)$/i);
|
|
104
|
-
if (!match) {
|
|
105
|
-
throw new ValidationError('Invalid scientific notation format');
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const [, coefficient, exponent] = match;
|
|
109
|
-
if (!coefficient) {
|
|
110
|
-
throw new ValidationError('Invalid scientific notation format');
|
|
111
|
-
}
|
|
112
|
-
const [intPart, fracPart = ''] = coefficient.split('.');
|
|
113
|
-
const normalizedCoef = intPart + fracPart;
|
|
114
|
-
const adjustedExp = parseInt(exponent || '0') - fracPart.length;
|
|
115
|
-
|
|
116
|
-
if (adjustedExp >= 0) {
|
|
117
|
-
return BigInt(normalizedCoef) * (BigInt(10) ** BigInt(adjustedExp));
|
|
118
|
-
} else {
|
|
119
|
-
return BigInt(normalizedCoef) / (BigInt(10) ** BigInt(-adjustedExp));
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Parse compact notation with suffixes (e.g., 1.23M)
|
|
125
|
-
*/
|
|
126
|
-
const parseCompactNotation = (str: string): bigint => {
|
|
127
|
-
const match = str.match(/^(-?\d*\.?\d+)\s*([KMBTQ])$/i);
|
|
128
|
-
if (!match) {
|
|
129
|
-
throw new ValidationError('Invalid compact notation format');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const [, number, suffix] = match;
|
|
133
|
-
const value = parseFloat(number || '0');
|
|
134
|
-
|
|
135
|
-
const multipliers = {
|
|
136
|
-
K: 1000n,
|
|
137
|
-
M: 1000000n,
|
|
138
|
-
B: 1000000000n,
|
|
139
|
-
T: 1000000000000n,
|
|
140
|
-
Q: 1000000000000000n
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
if (!suffix) {
|
|
144
|
-
throw new ValidationError('Invalid compact notation format');
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const multiplier = multipliers[suffix.toUpperCase() as keyof typeof multipliers];
|
|
148
|
-
return BigInt(Math.round(value * Number(multiplier)));
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Parse fraction notation (e.g., 1/3)
|
|
153
|
-
*/
|
|
154
|
-
const parseFraction = (
|
|
155
|
-
str: string,
|
|
156
|
-
rounding: RoundingMode = RoundingMode.HALF_EVEN,
|
|
157
|
-
precision: number = 0
|
|
158
|
-
): bigint => {
|
|
159
|
-
const [numerator, denominator] = str.split('/');
|
|
160
|
-
if (!numerator || !denominator) {
|
|
161
|
-
throw new ValidationError('Invalid fraction format');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const num = BigInt(numerator);
|
|
165
|
-
const den = BigInt(denominator);
|
|
166
|
-
|
|
167
|
-
if (den === 0n) {
|
|
168
|
-
throw new ValidationError('Division by zero in fraction');
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const scale = BigInt(10) ** BigInt(precision);
|
|
172
|
-
const scaledResult = (num * scale) / den;
|
|
173
|
-
|
|
174
|
-
// Apply rounding if needed
|
|
175
|
-
if (rounding === RoundingMode.FLOOR) {
|
|
176
|
-
return scaledResult;
|
|
177
|
-
} else if (rounding === RoundingMode.CEIL) {
|
|
178
|
-
return scaledResult + (num * scale % den === 0n ? 0n : 1n);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return scaledResult;
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Parse percentage notation (e.g., 12.3%)
|
|
186
|
-
*/
|
|
187
|
-
const parsePercentage = (str: string): bigint => {
|
|
188
|
-
const match = str.match(/^(-?\d*\.?\d+)%$/);
|
|
189
|
-
if (!match) {
|
|
190
|
-
throw new ValidationError('Invalid percentage format');
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const value = parseFloat(match[1] || '0');
|
|
194
|
-
return BigInt(Math.round(value * 100));
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Parse number in different bases (binary, octal, hex)
|
|
199
|
-
*/
|
|
200
|
-
const parseBaseNotation = (str: string, base: number): bigint => {
|
|
201
|
-
let cleanStr = str.toLowerCase();
|
|
202
|
-
|
|
203
|
-
// Remove prefix if present
|
|
204
|
-
const prefixes: Record<number, string> = {
|
|
205
|
-
2: '0b',
|
|
206
|
-
8: '0o',
|
|
207
|
-
16: '0x'
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
const prefix = prefixes[base];
|
|
211
|
-
if (prefix && cleanStr.startsWith(prefix)) {
|
|
212
|
-
cleanStr = cleanStr.slice(2);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Validate characters based on base
|
|
216
|
-
const validChars = {
|
|
217
|
-
2: /^[01]+$/,
|
|
218
|
-
8: /^[0-7]+$/,
|
|
219
|
-
16: /^[0-9a-f]+$/
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
if (!validChars[base as keyof typeof validChars].test(cleanStr)) {
|
|
223
|
-
throw new ValidationError(`Invalid ${base}-base number format`);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return BigInt(`0${prefixes[base]}${cleanStr}`);
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Detect number format from string
|
|
231
|
-
*/
|
|
232
|
-
export const detectNumberFormat = (str: string): NumberFormat => {
|
|
233
|
-
const cleanStr = str.trim();
|
|
234
|
-
|
|
235
|
-
if (/^-?\d+$/.test(cleanStr)) return NumberFormat.STANDARD;
|
|
236
|
-
if (/^-?\d*\.?\d+e[+-]?\d+$/i.test(cleanStr)) return NumberFormat.SCIENTIFIC;
|
|
237
|
-
if (/^-?\d*\.?\d+[KMBTQ]$/i.test(cleanStr)) return NumberFormat.COMPACT;
|
|
238
|
-
if (/^-?\d+\/\d+$/.test(cleanStr)) return NumberFormat.FRACTION;
|
|
239
|
-
if (/^-?\d*\.?\d+%$/.test(cleanStr)) return NumberFormat.PERCENTAGE;
|
|
240
|
-
if (/^(0x|0X)[0-9a-fA-F]+$/.test(cleanStr)) return NumberFormat.HEXADECIMAL;
|
|
241
|
-
if (/^(0b|0B)[01]+$/.test(cleanStr)) return NumberFormat.BINARY;
|
|
242
|
-
if (/^(0o|0O)[0-7]+$/.test(cleanStr)) return NumberFormat.OCTAL;
|
|
243
|
-
|
|
244
|
-
throw new ValidationError('Unable to detect number format');
|
|
1
|
+
/**
|
|
2
|
+
* Parser utilities for Hypernum library
|
|
3
|
+
* Provides functions for parsing various number formats and notations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ValidationError } from './validation';
|
|
7
|
+
import { RoundingMode } from './precision';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Supported number format types
|
|
11
|
+
*/
|
|
12
|
+
export enum NumberFormat {
|
|
13
|
+
STANDARD = 'STANDARD', // Regular decimal notation
|
|
14
|
+
SCIENTIFIC = 'SCIENTIFIC', // Scientific notation (e.g., 1.23e4)
|
|
15
|
+
ENGINEERING = 'ENGINEERING', // Engineering notation (e.g., 1.23e6)
|
|
16
|
+
COMPACT = 'COMPACT', // Compact notation with suffixes (e.g., 1.23M)
|
|
17
|
+
FRACTION = 'FRACTION', // Fractional notation (e.g., 1/3)
|
|
18
|
+
PERCENTAGE = 'PERCENTAGE', // Percentage notation (e.g., 12.3%)
|
|
19
|
+
HEXADECIMAL = 'HEXADECIMAL', // Hex notation (e.g., 0xFF)
|
|
20
|
+
BINARY = 'BINARY', // Binary notation (e.g., 0b1010)
|
|
21
|
+
OCTAL = 'OCTAL' // Octal notation (e.g., 0o777)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Options for parsing numbers
|
|
26
|
+
*/
|
|
27
|
+
export interface ParseOptions {
|
|
28
|
+
format?: NumberFormat;
|
|
29
|
+
base?: number;
|
|
30
|
+
allowFractions?: boolean;
|
|
31
|
+
rounding?: RoundingMode;
|
|
32
|
+
precision?: number;
|
|
33
|
+
strict?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const DEFAULT_PARSE_OPTIONS: Required<ParseOptions> = {
|
|
37
|
+
format: NumberFormat.STANDARD,
|
|
38
|
+
base: 10,
|
|
39
|
+
allowFractions: false,
|
|
40
|
+
rounding: RoundingMode.HALF_EVEN,
|
|
41
|
+
precision: 0,
|
|
42
|
+
strict: true
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Parse any supported number format to BigInt
|
|
47
|
+
*/
|
|
48
|
+
export const parseNumber = (input: string, options: ParseOptions = {}): bigint => {
|
|
49
|
+
const opts: Required<ParseOptions> = { ...DEFAULT_PARSE_OPTIONS, ...options };
|
|
50
|
+
|
|
51
|
+
// Remove whitespace if not in strict mode
|
|
52
|
+
const str = opts.strict ? input : input.trim();
|
|
53
|
+
|
|
54
|
+
if (str === '') {
|
|
55
|
+
throw new ValidationError('Empty string cannot be parsed as a number');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
switch (opts.format) {
|
|
60
|
+
case NumberFormat.SCIENTIFIC:
|
|
61
|
+
case NumberFormat.ENGINEERING:
|
|
62
|
+
return parseScientificNotation(str);
|
|
63
|
+
case NumberFormat.COMPACT:
|
|
64
|
+
return parseCompactNotation(str);
|
|
65
|
+
case NumberFormat.FRACTION:
|
|
66
|
+
return parseFraction(str, opts.rounding, opts.precision);
|
|
67
|
+
case NumberFormat.PERCENTAGE:
|
|
68
|
+
return parsePercentage(str);
|
|
69
|
+
case NumberFormat.HEXADECIMAL:
|
|
70
|
+
return parseBaseNotation(str, 16);
|
|
71
|
+
case NumberFormat.BINARY:
|
|
72
|
+
return parseBaseNotation(str, 2);
|
|
73
|
+
case NumberFormat.OCTAL:
|
|
74
|
+
return parseBaseNotation(str, 8);
|
|
75
|
+
default:
|
|
76
|
+
return parseStandardNotation(str);
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error instanceof ValidationError) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
throw new ValidationError(`Failed to parse number: ${input}`);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Parse standard decimal notation
|
|
88
|
+
*/
|
|
89
|
+
const parseStandardNotation = (str: string): bigint => {
|
|
90
|
+
// Remove commas and validate format
|
|
91
|
+
const cleanStr = str.replace(/,/g, '');
|
|
92
|
+
if (!/^-?\d+$/.test(cleanStr)) {
|
|
93
|
+
throw new ValidationError('Invalid standard notation format');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return BigInt(cleanStr);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Parse scientific notation (e.g., 1.23e4)
|
|
101
|
+
*/
|
|
102
|
+
const parseScientificNotation = (str: string): bigint => {
|
|
103
|
+
const match = str.match(/^(-?\d*\.?\d+)e([+-]?\d+)$/i);
|
|
104
|
+
if (!match) {
|
|
105
|
+
throw new ValidationError('Invalid scientific notation format');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const [, coefficient, exponent] = match;
|
|
109
|
+
if (!coefficient) {
|
|
110
|
+
throw new ValidationError('Invalid scientific notation format');
|
|
111
|
+
}
|
|
112
|
+
const [intPart, fracPart = ''] = coefficient.split('.');
|
|
113
|
+
const normalizedCoef = intPart + fracPart;
|
|
114
|
+
const adjustedExp = parseInt(exponent || '0') - fracPart.length;
|
|
115
|
+
|
|
116
|
+
if (adjustedExp >= 0) {
|
|
117
|
+
return BigInt(normalizedCoef) * (BigInt(10) ** BigInt(adjustedExp));
|
|
118
|
+
} else {
|
|
119
|
+
return BigInt(normalizedCoef) / (BigInt(10) ** BigInt(-adjustedExp));
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Parse compact notation with suffixes (e.g., 1.23M)
|
|
125
|
+
*/
|
|
126
|
+
const parseCompactNotation = (str: string): bigint => {
|
|
127
|
+
const match = str.match(/^(-?\d*\.?\d+)\s*([KMBTQ])$/i);
|
|
128
|
+
if (!match) {
|
|
129
|
+
throw new ValidationError('Invalid compact notation format');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const [, number, suffix] = match;
|
|
133
|
+
const value = parseFloat(number || '0');
|
|
134
|
+
|
|
135
|
+
const multipliers = {
|
|
136
|
+
K: 1000n,
|
|
137
|
+
M: 1000000n,
|
|
138
|
+
B: 1000000000n,
|
|
139
|
+
T: 1000000000000n,
|
|
140
|
+
Q: 1000000000000000n
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
if (!suffix) {
|
|
144
|
+
throw new ValidationError('Invalid compact notation format');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const multiplier = multipliers[suffix.toUpperCase() as keyof typeof multipliers];
|
|
148
|
+
return BigInt(Math.round(value * Number(multiplier)));
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Parse fraction notation (e.g., 1/3)
|
|
153
|
+
*/
|
|
154
|
+
const parseFraction = (
|
|
155
|
+
str: string,
|
|
156
|
+
rounding: RoundingMode = RoundingMode.HALF_EVEN,
|
|
157
|
+
precision: number = 0
|
|
158
|
+
): bigint => {
|
|
159
|
+
const [numerator, denominator] = str.split('/');
|
|
160
|
+
if (!numerator || !denominator) {
|
|
161
|
+
throw new ValidationError('Invalid fraction format');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const num = BigInt(numerator);
|
|
165
|
+
const den = BigInt(denominator);
|
|
166
|
+
|
|
167
|
+
if (den === 0n) {
|
|
168
|
+
throw new ValidationError('Division by zero in fraction');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const scale = BigInt(10) ** BigInt(precision);
|
|
172
|
+
const scaledResult = (num * scale) / den;
|
|
173
|
+
|
|
174
|
+
// Apply rounding if needed
|
|
175
|
+
if (rounding === RoundingMode.FLOOR) {
|
|
176
|
+
return scaledResult;
|
|
177
|
+
} else if (rounding === RoundingMode.CEIL) {
|
|
178
|
+
return scaledResult + (num * scale % den === 0n ? 0n : 1n);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return scaledResult;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Parse percentage notation (e.g., 12.3%)
|
|
186
|
+
*/
|
|
187
|
+
const parsePercentage = (str: string): bigint => {
|
|
188
|
+
const match = str.match(/^(-?\d*\.?\d+)%$/);
|
|
189
|
+
if (!match) {
|
|
190
|
+
throw new ValidationError('Invalid percentage format');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const value = parseFloat(match[1] || '0');
|
|
194
|
+
return BigInt(Math.round(value * 100));
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Parse number in different bases (binary, octal, hex)
|
|
199
|
+
*/
|
|
200
|
+
const parseBaseNotation = (str: string, base: number): bigint => {
|
|
201
|
+
let cleanStr = str.toLowerCase();
|
|
202
|
+
|
|
203
|
+
// Remove prefix if present
|
|
204
|
+
const prefixes: Record<number, string> = {
|
|
205
|
+
2: '0b',
|
|
206
|
+
8: '0o',
|
|
207
|
+
16: '0x'
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const prefix = prefixes[base];
|
|
211
|
+
if (prefix && cleanStr.startsWith(prefix)) {
|
|
212
|
+
cleanStr = cleanStr.slice(2);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Validate characters based on base
|
|
216
|
+
const validChars = {
|
|
217
|
+
2: /^[01]+$/,
|
|
218
|
+
8: /^[0-7]+$/,
|
|
219
|
+
16: /^[0-9a-f]+$/
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
if (!validChars[base as keyof typeof validChars].test(cleanStr)) {
|
|
223
|
+
throw new ValidationError(`Invalid ${base}-base number format`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return BigInt(`0${prefixes[base]}${cleanStr}`);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Detect number format from string
|
|
231
|
+
*/
|
|
232
|
+
export const detectNumberFormat = (str: string): NumberFormat => {
|
|
233
|
+
const cleanStr = str.trim();
|
|
234
|
+
|
|
235
|
+
if (/^-?\d+$/.test(cleanStr)) return NumberFormat.STANDARD;
|
|
236
|
+
if (/^-?\d*\.?\d+e[+-]?\d+$/i.test(cleanStr)) return NumberFormat.SCIENTIFIC;
|
|
237
|
+
if (/^-?\d*\.?\d+[KMBTQ]$/i.test(cleanStr)) return NumberFormat.COMPACT;
|
|
238
|
+
if (/^-?\d+\/\d+$/.test(cleanStr)) return NumberFormat.FRACTION;
|
|
239
|
+
if (/^-?\d*\.?\d+%$/.test(cleanStr)) return NumberFormat.PERCENTAGE;
|
|
240
|
+
if (/^(0x|0X)[0-9a-fA-F]+$/.test(cleanStr)) return NumberFormat.HEXADECIMAL;
|
|
241
|
+
if (/^(0b|0B)[01]+$/.test(cleanStr)) return NumberFormat.BINARY;
|
|
242
|
+
if (/^(0o|0O)[0-7]+$/.test(cleanStr)) return NumberFormat.OCTAL;
|
|
243
|
+
|
|
244
|
+
throw new ValidationError('Unable to detect number format');
|
|
245
245
|
};
|