Skip to content

Commit ece3fbc

Browse files
committed
feat: add radix for BigInt::to/from_string and deprecated BigInt::to/from_hex
1 parent 2197fd2 commit ece3fbc

File tree

9 files changed

+538
-275
lines changed

9 files changed

+538
-275
lines changed

bigint/README.mbt.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ test "creating bigint values" {
2626
inspect(big4, content="123456789012345678901234567890")
2727
2828
// From hexadecimal strings
29-
let big5 = @bigint.BigInt::from_hex("1a2b3c4d5e6f")
29+
let big5 = @bigint.BigInt::from_string("1a2b3c4d5e6f", radix=16)
3030
inspect(big5, content="28772997619311")
3131
}
3232
```
@@ -170,16 +170,12 @@ test "string conversions" {
170170
inspect(decimal, content="255")
171171
172172
// Hexadecimal (lowercase)
173-
let hex_lower = big.to_hex()
174-
inspect(hex_lower, content="FF")
173+
let hex = big.to_string(radix=16)
174+
inspect(hex, content="ff")
175175
176-
// Hexadecimal (uppercase)
177-
let hex_upper = big.to_hex(uppercase=true)
178-
inspect(hex_upper, content="FF")
179-
180-
// Parse from hex
181-
let from_hex = @bigint.BigInt::from_hex("deadbeef")
182-
inspect(from_hex, content="3735928559")
176+
// Parse from hex (radix=16)
177+
let from_radix16 = @bigint.BigInt::from_string("deadbeef", radix=16)
178+
inspect(from_radix16, content="3735928559")
183179
184180
// Round-trip conversion
185181
let original = 98765432109876543210N

bigint/bigint_js.mbt

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,16 @@ type BigInt
1919
let zero = 0N
2020

2121
///|
22-
pub fn BigInt::from_string(str : String) -> BigInt {
22+
pub fn BigInt::from_string(str : String, radix? : Int = 10) -> BigInt {
2323
if str.length() == 0 {
2424
abort("empty string")
2525
}
26+
if radix < 2 || radix > 36 {
27+
abort("radix must be between 2 and 36")
28+
}
29+
if radix != 10 {
30+
return BigInt::from_string_radix(str, radix)
31+
}
2632
BigInt::js_from_string(str)
2733
}
2834

@@ -31,27 +37,76 @@ extern "js" fn BigInt::js_from_string(str : String) -> BigInt =
3137
#|(x) => BigInt(x)
3238

3339
///|
34-
pub impl Show for BigInt with output(self, logger) {
35-
logger.write_string(self.to_string())
40+
fn digit_from_char(x : Int) -> Int {
41+
match x {
42+
'0'..='9' => x - '0'
43+
'A'..='Z' => x + (10 - 'A')
44+
'a'..='z' => x + (10 - 'a')
45+
_ => -1
46+
}
3647
}
3748

3849
///|
39-
pub extern "js" fn BigInt::to_string(self : BigInt) -> String =
40-
#|(x) => String(x)
50+
fn pow2_shift(radix : Int) -> Int? {
51+
if radix >= 2 && (radix & (radix - 1)) == 0 {
52+
Some(radix.ctz())
53+
} else {
54+
None
55+
}
56+
}
4157

4258
///|
43-
pub extern "js" fn BigInt::from_hex(str : String) -> BigInt =
44-
#|(x) => x.startsWith('-') ? -BigInt(`0x${x.slice(1)}`) : BigInt(`0x${x}`)
59+
fn BigInt::from_string_radix(str : String, radix : Int) -> BigInt {
60+
let len = str.length()
61+
let sign = if str.unsafe_get(0) == '-' { -1 } else { 1 }
62+
let start = if sign == -1 { 1 } else { 0 }
63+
if start == len {
64+
abort("invalid character")
65+
}
66+
let mut acc = 0N
67+
match pow2_shift(radix) {
68+
Some(shift) =>
69+
for i in start..<len {
70+
let digit = digit_from_char(str.unsafe_get(i).to_int())
71+
if digit < 0 || digit >= radix {
72+
abort("invalid character")
73+
}
74+
acc = (acc << shift) | BigInt::from_int(digit)
75+
}
76+
None => {
77+
let base = BigInt::from_int(radix)
78+
for i in start..<len {
79+
let digit = digit_from_char(str.unsafe_get(i).to_int())
80+
if digit < 0 || digit >= radix {
81+
abort("invalid character")
82+
}
83+
acc = acc * base + BigInt::from_int(digit)
84+
}
85+
}
86+
}
87+
if sign == -1 {
88+
-acc
89+
} else {
90+
acc
91+
}
92+
}
4593

4694
///|
47-
pub extern "js" fn BigInt::to_hex(
48-
self : BigInt,
49-
uppercase? : Bool = true,
50-
) -> String =
51-
#|(x, uppercase) => {
52-
#| const r = x.toString(16);
53-
#| return uppercase ? r.toUpperCase() : r;
54-
#|}
95+
pub impl Show for BigInt with output(self, logger) {
96+
logger.write_string(self.to_string())
97+
}
98+
99+
///|
100+
pub fn BigInt::to_string(self : BigInt, radix? : Int = 10) -> String {
101+
if radix < 2 || radix > 36 {
102+
abort("radix must be between 2 and 36")
103+
}
104+
BigInt::js_to_string_radix(self, radix)
105+
}
106+
107+
///|
108+
extern "js" fn BigInt::js_to_string_radix(self : BigInt, radix : Int) -> String =
109+
#|(x, radix) => x.toString(radix)
55110

56111
///|
57112
extern "js" fn hex2(b : Byte) -> String =

0 commit comments

Comments
 (0)