mixradix 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js ADDED
@@ -0,0 +1,284 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { parseArgs } from "node:util";
3
+ import { MixedRadix } from "./core/index.js";
4
+ import { MixedRadixError } from "./errors/index.js";
5
+ import { UsageError } from "./usage-error.js";
6
+ const processIo = {
7
+ stdout: (text) => process.stdout.write(text),
8
+ stderr: (text) => process.stderr.write(text),
9
+ };
10
+ const DESCRIPTION = "Add, subtract and convert integers of any size in mixed radix bases, where every digit position has its own radix.";
11
+ const NOTATION_HELP = `bases:
12
+ One radix per digit position, most significant first, like a number.
13
+ 10, 16 plain decimal, hexadecimal
14
+ 24:60:60 hours:minutes:seconds, with days above that have no limit
15
+ 24:60/wrap a 24-hour clock that wraps around
16
+ 2:3:4/repeat the leftmost radix repeats: ...2:2:3:4
17
+
18
+ After '/', the overflow says what is left of the leftmost radix:
19
+ repeat (default for one radix), extend (default for several: one more
20
+ digit with no limit) or wrap (fixed width, values wrap around).
21
+
22
+ numbers:
23
+ ff, 1011 one character per digit, in bases like 16 or 2
24
+ 1:23:59 decimal digits separated by ':', in every base
25
+ 45 in a base like 24:60:60, a single digit: 45 seconds`;
26
+ const EXAMPLES = `examples:
27
+ mixradix add 23:59:59 1 -b 24:60:60 1:0:0:0
28
+ mixradix add 1:30 2:45 0:50 -b 60 5:5
29
+ mixradix sub 0:15 0:30 -b 24:60/wrap 23:45
30
+ mixradix convert 100000 -t 24:60:60 1:3:46:40
31
+ mixradix convert ff -b 16 -t 2 11111111
32
+ mixradix info 24:60:60`;
33
+ const TOP_USAGE = "usage: mixradix [-h] [-V] COMMAND ...";
34
+ const TOP_HELP = `${TOP_USAGE}
35
+
36
+ ${DESCRIPTION}
37
+
38
+ commands:
39
+ add Add numbers
40
+ sub (subtract) Subtract every later number from the first
41
+ convert Write a number in another base
42
+ info Describe a base: overflow, place values and range
43
+
44
+ options:
45
+ -h, --help show this help message and exit
46
+ -V, --version show program's version number and exit
47
+
48
+ ${NOTATION_HELP}
49
+
50
+ ${EXAMPLES}
51
+ `;
52
+ const HELP_OPTION = { help: { type: "boolean", short: "h" } };
53
+ const JSON_OPTION = { json: { type: "boolean" } };
54
+ const BASE_OPTIONS = {
55
+ base: { type: "string", short: "b", default: "10" },
56
+ to: { type: "string", short: "t" },
57
+ };
58
+ const calculate = (name, description) => ({
59
+ usage: `usage: mixradix ${name} [-h] [-b BASE] [-t TO] [--json] NUMBER [NUMBER ...]`,
60
+ description,
61
+ options: `options:
62
+ -h, --help show this help message and exit
63
+ -b, --base BASE base the numbers are written in (default: 10)
64
+ -t, --to TO base to write the result in (default: --base)
65
+ --json print the result as one line of JSON`,
66
+ config: { ...HELP_OPTION, ...BASE_OPTIONS, ...JSON_OPTION },
67
+ });
68
+ const COMMANDS = {
69
+ add: calculate("add", "Add numbers."),
70
+ sub: calculate("sub", "Subtract every later number from the first."),
71
+ convert: {
72
+ usage: "usage: mixradix convert [-h] [-b BASE] -t TO [--json] NUMBER",
73
+ description: "Write a number in another base.",
74
+ options: `options:
75
+ -h, --help show this help message and exit
76
+ -b, --base BASE base the number is written in (default: 10)
77
+ -t, --to TO base to write the number in
78
+ --json print the result as one line of JSON`,
79
+ config: { ...HELP_OPTION, ...BASE_OPTIONS, ...JSON_OPTION },
80
+ },
81
+ info: {
82
+ usage: "usage: mixradix info [-h] [--json] BASE",
83
+ description: "Describe a base: overflow, place values and range.",
84
+ options: `options:
85
+ -h, --help show this help message and exit
86
+ --json print the result as one line of JSON`,
87
+ config: { ...HELP_OPTION, ...JSON_OPTION },
88
+ },
89
+ };
90
+ const ALIASES = {
91
+ add: "add",
92
+ sub: "sub",
93
+ subtract: "sub",
94
+ convert: "convert",
95
+ info: "info",
96
+ };
97
+ // Short options. Any other argument starting with '-' and one more character,
98
+ // like -5, -1:30 or -ff, is a negative number.
99
+ const SHORT_OPTIONS = new Set(["b", "t", "h", "V", "-"]);
100
+ /**
101
+ * Stops the option parser from reading negative numbers as options. A leading
102
+ * space makes an argument positional, and number parsing ignores it. After
103
+ * `--`, every argument is positional.
104
+ */
105
+ function protectNegativeNumbers(argv) {
106
+ const result = [];
107
+ let restPositional = false;
108
+ for (const arg of argv) {
109
+ if (restPositional) {
110
+ result.push(arg.startsWith("-") ? ` ${arg}` : arg);
111
+ }
112
+ else if (arg === "--") {
113
+ restPositional = true;
114
+ }
115
+ else if (arg.length > 1 && arg[0] === "-" && !SHORT_OPTIONS.has(arg[1])) {
116
+ result.push(` ${arg}`);
117
+ }
118
+ else {
119
+ result.push(arg);
120
+ }
121
+ }
122
+ return result;
123
+ }
124
+ /** Runs the CLI with `argv` (without the node and script paths) and returns the exit code. */
125
+ export function main(argv = process.argv.slice(2), io = processIo) {
126
+ try {
127
+ return run(protectNegativeNumbers(argv), io);
128
+ }
129
+ catch (e) {
130
+ if (e instanceof UsageError) {
131
+ io.stderr(`${e.usage}\n${e.prog}: error: ${e.message}\n`);
132
+ return 2;
133
+ }
134
+ if (e instanceof MixedRadixError) {
135
+ io.stderr(`error: ${e.message}\n`);
136
+ return 1;
137
+ }
138
+ throw e;
139
+ }
140
+ }
141
+ function run(args, io) {
142
+ const [first, ...rest] = args;
143
+ if (first === "-h" || first === "--help") {
144
+ io.stdout(TOP_HELP);
145
+ return 0;
146
+ }
147
+ if (first === "-V" || first === "--version") {
148
+ io.stdout(`mixradix ${version()}\n`);
149
+ return 0;
150
+ }
151
+ if (first === undefined) {
152
+ throw new UsageError(TOP_USAGE, "mixradix", "the following arguments are required: COMMAND");
153
+ }
154
+ const name = ALIASES[first];
155
+ if (name === undefined) {
156
+ throw new UsageError(TOP_USAGE, "mixradix", `argument COMMAND: invalid choice: '${first.trim()}' (choose from add, sub, subtract, convert, info)`);
157
+ }
158
+ const command = COMMANDS[name];
159
+ const prog = `mixradix ${first}`;
160
+ const usageError = (message) => new UsageError(command.usage, prog, message);
161
+ let parsed;
162
+ try {
163
+ parsed = parseArgs({
164
+ args: rest,
165
+ options: command.config,
166
+ allowPositionals: true,
167
+ strict: true,
168
+ });
169
+ }
170
+ catch (e) {
171
+ throw usageError(e instanceof Error ? e.message : String(e));
172
+ }
173
+ const values = parsed.values;
174
+ const positionals = parsed.positionals.map((p) => p.trim());
175
+ if (values.help) {
176
+ io.stdout(`${command.usage}\n\n${command.description}\n\n${command.options}\n\n${NOTATION_HELP}\n`);
177
+ return 0;
178
+ }
179
+ const json = values.json ?? false;
180
+ if (name === "info") {
181
+ const [baseText, ...extra] = positionals;
182
+ if (baseText === undefined)
183
+ throw usageError("the following arguments are required: BASE");
184
+ if (extra.length > 0)
185
+ throw usageError(`unrecognized arguments: ${extra.join(" ")}`);
186
+ const base = new MixedRadix(baseText);
187
+ io.stdout(`${json ? infoJson(base) : infoText(base)}\n`);
188
+ return 0;
189
+ }
190
+ const base = new MixedRadix((values.base ?? "10").trim());
191
+ const to = values.to === undefined ? undefined : new MixedRadix(values.to.trim());
192
+ let result;
193
+ if (name === "convert") {
194
+ const [numberText, ...extra] = positionals;
195
+ if (to === undefined)
196
+ throw usageError("the following arguments are required: -t/--to");
197
+ if (numberText === undefined)
198
+ throw usageError("the following arguments are required: NUMBER");
199
+ if (extra.length > 0)
200
+ throw usageError(`unrecognized arguments: ${extra.join(" ")}`);
201
+ result = base.parse(numberText);
202
+ }
203
+ else {
204
+ if (positionals.length === 0)
205
+ throw usageError("the following arguments are required: NUMBER");
206
+ if (positionals.length < 2)
207
+ throw usageError("at least two numbers are required");
208
+ const [head, ...tail] = positionals.map((p) => base.parse(p));
209
+ result = tail.reduce((acc, n) => (name === "sub" ? acc.sub(n) : acc.add(n)), head);
210
+ }
211
+ if (to !== undefined)
212
+ result = result.to(to);
213
+ io.stdout(`${json ? numberJson(result) : result.toString()}\n`);
214
+ return 0;
215
+ }
216
+ function version() {
217
+ const path = new URL("../package.json", import.meta.url);
218
+ return JSON.parse(readFileSync(path, "utf8")).version;
219
+ }
220
+ /** Compact JSON where bigints are written as plain JSON numbers of any size. */
221
+ function toJson(value) {
222
+ if (typeof value === "bigint")
223
+ return value.toString();
224
+ if (Array.isArray(value))
225
+ return `[${value.map(toJson).join(",")}]`;
226
+ if (value !== null && typeof value === "object") {
227
+ const entries = Object.entries(value).map(([k, v]) => `${JSON.stringify(k)}:${toJson(v)}`);
228
+ return `{${entries.join(",")}}`;
229
+ }
230
+ return JSON.stringify(value);
231
+ }
232
+ function numberJson(number) {
233
+ return toJson({
234
+ result: number.toString(),
235
+ value: number.value,
236
+ digits: [...number.digits],
237
+ negative: number.isNegative,
238
+ base: number.base.toString(),
239
+ });
240
+ }
241
+ /** Place values shown by `info`, most significant first. */
242
+ function placeValues(base) {
243
+ const count = base.radices.length;
244
+ const shown = { extend: count + 1, wrap: count, repeat: Math.max(count + 1, 4) }[base.overflow];
245
+ return Array.from({ length: shown }, (_, i) => base.placeValue(shown - 1 - i));
246
+ }
247
+ function infoJson(base) {
248
+ return toJson({
249
+ base: base.toString(),
250
+ radices: [...base.radices],
251
+ overflow: base.overflow,
252
+ max_digits: base.maxDigits,
253
+ place_values: placeValues(base),
254
+ modulus: base.modulus,
255
+ });
256
+ }
257
+ function infoText(base) {
258
+ let places = placeValues(base).join(", ");
259
+ const modulus = base.modulus;
260
+ let overflow;
261
+ let digits;
262
+ if (base.overflow === "extend") {
263
+ overflow = "extend (the leftmost digit has no limit)";
264
+ digits = `at most ${base.maxDigits}`;
265
+ }
266
+ else if (modulus !== null) {
267
+ overflow = `wrap (values wrap around modulo ${modulus})`;
268
+ digits = `exactly ${base.maxDigits}`;
269
+ }
270
+ else {
271
+ overflow = `repeat (positions further left use radix ${base.radices[0]})`;
272
+ digits = "any number";
273
+ places = `..., ${places}`;
274
+ }
275
+ const lines = [
276
+ ["base", base.toString()],
277
+ ["overflow", overflow],
278
+ ["digits", digits],
279
+ ["place values", places],
280
+ ];
281
+ if (modulus !== null)
282
+ lines.push(["range", `0 to ${modulus - 1n}`]);
283
+ return lines.map(([label, value]) => `${label}:`.padEnd(14) + value).join("\n");
284
+ }
@@ -0,0 +1,18 @@
1
+ /** What is left of the leftmost radix: see the README. */
2
+ export type Overflow = "extend" | "wrap" | "repeat";
3
+ export declare const SEPARATOR = ":";
4
+ export declare const OVERFLOW_SEPARATOR = "/";
5
+ export declare const DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz";
6
+ export declare const MAX_COMPACT_RADIX: bigint;
7
+ export declare const BASE_SYNTAX = "expected radices of 2 or more separated by ':', like 16 or 24:60:60";
8
+ export declare const DIGIT_SYNTAX = "expected decimal digits separated by ':'";
9
+ export declare const isDecimal: (text: string) => boolean;
10
+ export declare const defaultOverflow: (radixCount: number) => Overflow;
11
+ export declare const listLabel: (values: readonly unknown[]) => string;
12
+ export declare function checkOverflow(base: string, overflow: string): Overflow;
13
+ /** A safe integer or bigint as a bigint, or undefined for anything else. */
14
+ export declare function asInteger(value: unknown): bigint | undefined;
15
+ export declare function toInteger(value: number | bigint): bigint;
16
+ export declare const abs: (value: bigint) => bigint;
17
+ /** Remainder that is never negative, for wrapping values into [0, modulus). */
18
+ export declare const mod: (value: bigint, modulus: bigint) => bigint;
@@ -0,0 +1,37 @@
1
+ /** Notation and integer helpers shared by MixedRadix and MixedRadixNumber. */
2
+ import { InvalidBaseError } from "../errors/index.js";
3
+ const OVERFLOWS = ["extend", "wrap", "repeat"];
4
+ export const SEPARATOR = ":";
5
+ export const OVERFLOW_SEPARATOR = "/";
6
+ export const DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz";
7
+ export const MAX_COMPACT_RADIX = BigInt(DIGIT_CHARS.length);
8
+ export const BASE_SYNTAX = "expected radices of 2 or more separated by ':', like 16 or 24:60:60";
9
+ export const DIGIT_SYNTAX = "expected decimal digits separated by ':'";
10
+ export const isDecimal = (text) => /^[0-9]+$/.test(text);
11
+ export const defaultOverflow = (radixCount) => radixCount === 1 ? "repeat" : "extend";
12
+ export const listLabel = (values) => `[${values.map(String).join(", ")}]`;
13
+ export function checkOverflow(base, overflow) {
14
+ const known = OVERFLOWS.find((o) => o === overflow);
15
+ if (known === undefined) {
16
+ throw new InvalidBaseError(base, `unknown overflow '${overflow}' (expected extend, wrap or repeat)`);
17
+ }
18
+ return known;
19
+ }
20
+ /** A safe integer or bigint as a bigint, or undefined for anything else. */
21
+ export function asInteger(value) {
22
+ if (typeof value === "bigint")
23
+ return value;
24
+ if (typeof value === "number" && Number.isSafeInteger(value))
25
+ return BigInt(value);
26
+ return undefined;
27
+ }
28
+ export function toInteger(value) {
29
+ const integer = asInteger(value);
30
+ if (integer === undefined) {
31
+ throw new TypeError(`expected a safe integer or a bigint, got ${String(value)}`);
32
+ }
33
+ return integer;
34
+ }
35
+ export const abs = (value) => (value < 0n ? -value : value);
36
+ /** Remainder that is never negative, for wrapping values into [0, modulus). */
37
+ export const mod = (value, modulus) => ((value % modulus) + modulus) % modulus;
@@ -0,0 +1,3 @@
1
+ export type { Overflow } from "./common.js";
2
+ export { type BaseLike, type BaseOptions, MixedRadix } from "./mixed-radix.js";
3
+ export { MixedRadixNumber, type NumberLike } from "./mixed-radix-number.js";
@@ -0,0 +1,2 @@
1
+ export { MixedRadix } from "./mixed-radix.js";
2
+ export { MixedRadixNumber } from "./mixed-radix-number.js";
@@ -0,0 +1,34 @@
1
+ import { type BaseLike, MixedRadix } from "./mixed-radix.js";
2
+ /** An operand for arithmetic: a number, an integer, or text in the left operand's base. */
3
+ export type NumberLike = MixedRadixNumber | string | number | bigint;
4
+ /**
5
+ * A signed integer of any size, written in a MixedRadix.
6
+ *
7
+ * Arithmetic is on values, so operands may be in different bases, integers,
8
+ * or text in this number's base. Results use the left operand's base.
9
+ *
10
+ * ```ts
11
+ * new MixedRadixNumber("23:59:59", "24:60:60").add(1).toString(); // "1:0:0:0"
12
+ * ```
13
+ */
14
+ export declare class MixedRadixNumber {
15
+ readonly value: bigint;
16
+ readonly base: MixedRadix;
17
+ constructor(number: string | number | bigint, base?: BaseLike);
18
+ get isNegative(): boolean;
19
+ /** Digit values of the absolute value, most significant first. */
20
+ get digits(): readonly bigint[];
21
+ /** The same value written in another base. */
22
+ to(base: BaseLike): MixedRadixNumber;
23
+ add(other: NumberLike): MixedRadixNumber;
24
+ sub(other: NumberLike): MixedRadixNumber;
25
+ neg(): MixedRadixNumber;
26
+ abs(): MixedRadixNumber;
27
+ /** -1, 0 or 1 as this value is below, equal to or above `other`'s. */
28
+ compare(other: MixedRadixNumber | number | bigint): -1 | 0 | 1;
29
+ /** True when the values are equal, whatever the bases. */
30
+ equals(other: MixedRadixNumber | number | bigint): boolean;
31
+ toBigInt(): bigint;
32
+ toString(): string;
33
+ toJSON(): string;
34
+ }
@@ -0,0 +1,111 @@
1
+ import { abs, DIGIT_CHARS, mod, SEPARATOR, toInteger } from "./common.js";
2
+ import { MixedRadix } from "./mixed-radix.js";
3
+ /**
4
+ * A signed integer of any size, written in a MixedRadix.
5
+ *
6
+ * Arithmetic is on values, so operands may be in different bases, integers,
7
+ * or text in this number's base. Results use the left operand's base.
8
+ *
9
+ * ```ts
10
+ * new MixedRadixNumber("23:59:59", "24:60:60").add(1).toString(); // "1:0:0:0"
11
+ * ```
12
+ */
13
+ export class MixedRadixNumber {
14
+ value;
15
+ base;
16
+ constructor(number, base = 10) {
17
+ const mixedRadix = MixedRadix.of(base);
18
+ const built = typeof number === "string" ? mixedRadix.parse(number) : mixedRadix.fromInt(number);
19
+ this.value = built.value;
20
+ this.base = mixedRadix;
21
+ Object.freeze(this);
22
+ }
23
+ /** @internal Makes a number from a value, wrapping it for `wrap` bases. */
24
+ static create(value, base) {
25
+ const modulus = base.modulus;
26
+ const number = Object.create(MixedRadixNumber.prototype);
27
+ Object.assign(number, { value: modulus === null ? value : mod(value, modulus), base });
28
+ return Object.freeze(number);
29
+ }
30
+ get isNegative() {
31
+ return this.value < 0n;
32
+ }
33
+ /** Digit values of the absolute value, most significant first. */
34
+ get digits() {
35
+ let remaining = abs(this.value);
36
+ const digits = []; // least significant first while building
37
+ if (this.base.overflow === "wrap") {
38
+ for (let i = this.base.radices.length - 1; i >= 0; i--) {
39
+ const radix = this.base.radices[i];
40
+ digits.push(remaining % radix);
41
+ remaining /= radix;
42
+ }
43
+ }
44
+ else {
45
+ for (let position = 0;; position++) {
46
+ const radix = this.base.radixAt(position);
47
+ if (radix === null) {
48
+ digits.push(remaining);
49
+ break;
50
+ }
51
+ digits.push(remaining % radix);
52
+ remaining /= radix;
53
+ if (remaining === 0n)
54
+ break;
55
+ }
56
+ }
57
+ return Object.freeze(digits.reverse());
58
+ }
59
+ /** The same value written in another base. */
60
+ to(base) {
61
+ return MixedRadixNumber.create(this.value, MixedRadix.of(base));
62
+ }
63
+ // -- Arithmetic --------------------------------------------------------------
64
+ add(other) {
65
+ return MixedRadixNumber.create(this.value + operandValue(this, other), this.base);
66
+ }
67
+ sub(other) {
68
+ return MixedRadixNumber.create(this.value - operandValue(this, other), this.base);
69
+ }
70
+ neg() {
71
+ return MixedRadixNumber.create(-this.value, this.base);
72
+ }
73
+ abs() {
74
+ return MixedRadixNumber.create(abs(this.value), this.base);
75
+ }
76
+ // -- Comparison (by value) -----------------------------------------------------
77
+ /** -1, 0 or 1 as this value is below, equal to or above `other`'s. */
78
+ compare(other) {
79
+ const value = other instanceof MixedRadixNumber ? other.value : toInteger(other);
80
+ return this.value < value ? -1 : this.value > value ? 1 : 0;
81
+ }
82
+ /** True when the values are equal, whatever the bases. */
83
+ equals(other) {
84
+ return this.compare(other) === 0;
85
+ }
86
+ // -- Conversion ------------------------------------------------------------------
87
+ toBigInt() {
88
+ return this.value;
89
+ }
90
+ toString() {
91
+ const sign = this.value < 0n ? "-" : "";
92
+ const digits = this.digits;
93
+ if (this.base.isUniform)
94
+ return sign + digits.map((d) => DIGIT_CHARS[Number(d)]).join("");
95
+ return sign + digits.join(SEPARATOR);
96
+ }
97
+ toJSON() {
98
+ return this.toString();
99
+ }
100
+ [Symbol.for("nodejs.util.inspect.custom")]() {
101
+ return `MixedRadixNumber('${this}', base='${this.base}')`;
102
+ }
103
+ }
104
+ /** The value of an arithmetic operand; text is parsed in the left operand's base. */
105
+ function operandValue(left, other) {
106
+ if (other instanceof MixedRadixNumber)
107
+ return other.value;
108
+ if (typeof other === "string")
109
+ return left.base.parse(other).value;
110
+ return toInteger(other);
111
+ }
@@ -0,0 +1,60 @@
1
+ import { type Overflow } from "./common.js";
2
+ import { MixedRadixNumber } from "./mixed-radix-number.js";
3
+ /** Anything that can be turned into a base: `"24:60:60"`, `16`, `[24, 60, 60]`. */
4
+ export type BaseLike = MixedRadix | string | number | bigint | readonly (number | bigint)[];
5
+ export interface BaseOptions {
6
+ overflow?: Overflow;
7
+ }
8
+ /**
9
+ * A base with one radix per digit position, most significant first.
10
+ *
11
+ * ```ts
12
+ * const time = new MixedRadix("24:60:60");
13
+ * time.parse("1:23:59").value; // 5039n
14
+ * new MixedRadix(16).fromInt(255).toString(); // "ff"
15
+ * ```
16
+ */
17
+ export declare class MixedRadix {
18
+ #private;
19
+ /** The written radices, most significant first (normalized). */
20
+ readonly radices: readonly bigint[];
21
+ readonly overflow: Overflow;
22
+ constructor(base: string | number | bigint | readonly (number | bigint)[], options?: BaseOptions);
23
+ /** Returns `base` itself if it is a MixedRadix, otherwise builds one. */
24
+ static of(base: BaseLike): MixedRadix;
25
+ /**
26
+ * True for one radix of at most 36 that never has an unlimited digit, so
27
+ * numbers can be written one character per digit (`ff`, `1011`).
28
+ */
29
+ get isUniform(): boolean;
30
+ /** How many digits a number can have, or null for no limit. */
31
+ get maxDigits(): number | null;
32
+ /** The product of all radices for `wrap`, otherwise null. */
33
+ get modulus(): bigint | null;
34
+ /**
35
+ * Radix of a digit position (0 = least significant).
36
+ *
37
+ * Returns null for the unlimited leading digit of `extend`, and throws a
38
+ * RangeError for a position the base doesn't have.
39
+ */
40
+ radixAt(position: number): bigint | null;
41
+ /** What a 1 at `position` is worth: the product of the radices below it. */
42
+ placeValue(position: number): bigint;
43
+ /** Parses `"1:23:59"`, `"-ff"`, `"45"` and so on in this base. */
44
+ parse(text: string): MixedRadixNumber;
45
+ /** The integer `value` in this base. */
46
+ fromInt(value: number | bigint): MixedRadixNumber;
47
+ /**
48
+ * A number from digit values, most significant first.
49
+ *
50
+ * ```ts
51
+ * new MixedRadix("24:60:60").fromDigits([1, 23, 59]).toString(); // "1:23:59"
52
+ * ```
53
+ */
54
+ fromDigits(digits: readonly (number | bigint)[], options?: {
55
+ negative?: boolean;
56
+ }): MixedRadixNumber;
57
+ equals(other: MixedRadix): boolean;
58
+ toString(): string;
59
+ toJSON(): string;
60
+ }