Skip to content

Commit

Permalink
Merge pull request #373 from jialunzhang-psu/main
Browse files Browse the repository at this point in the history
add `from_hex`, `to_hex` for BigInt
  • Loading branch information
jialunzhang-psu committed May 8, 2024
2 parents 787d815 + 27af45d commit 6874cd7
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 0 deletions.
77 changes: 77 additions & 0 deletions bigint/bigint.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,83 @@ pub fn from_string(input : String) -> BigInt {
{ limbs: b, sign, len: b_len }
}

/// Converts a hex string to a BigInt.
pub fn from_hex(input : String) -> BigInt {
// WARN: this implementation assumes that `radix` is a power of 2.
let len = input.length()
if len == 0 {
abort("empty string")
}
let sign : Sign = if input[0] == '-' { Negative } else { Positive }
let mut b_len = (len * 4 + 1 + radix_bit_len) / radix_bit_len + 1 // TODO: 4 is hardcoded.
let b = Array::make(b_len, 0)
for i = match sign {
Negative => 1
Positive => 0
}
i < input.length()
i = i + 1 {
let x = input[i].to_int()
let y = if x >= 48 && x <= 57 {
// ASCII value of '0'
x - 48
} else if x >= 65 && x <= 70 {
// ASCII value of 'A'
x - 55
} else if x >= 97 && x <= 102 {
// ASCII value of 'a'
x - 87
} else {
abort("invalid character")
}
let mut carry = y
for j = 0; j < b_len; j = j + 1 {
carry += b[j] * 16
b[j] = carry % radix.to_int()
carry /= radix.to_int()
}
}
while b[b_len - 1] == 0 && b_len > 1 {
b_len -= 1
}
{ limbs: b, sign, len: b_len }
}

/// Converts a BigInt to a hex string.
pub fn to_hex(self : BigInt) -> String {
// WARN: this implementation assumes that `radix_bit_len` is a multiple of 4.
let mut result = ""
for i = self.len - 1; i >= 0; i = i - 1 { // TODO: reverse iteration would be a bit faster.
// split the limb into 4-bit chunks
let mut x = self.limbs[i]
let mut tmp = ""
while x > 0 {
let y = x % 16
x /= 16
tmp = if y < 10 {
Char::from_int(y + 48).to_string()
} else {
Char::from_int(y + 55).to_string()
} + tmp
}
if i != self.len - 1 && tmp.length() < 4 {
let pad = 4 - tmp.length()
// pad with zeros if not the last limb(the front of the BigInt)
for j = 0; j < pad; j = j + 1 {
tmp = "0" + tmp
}
}
result = result + tmp
}
if result == "" {
"0"
} else if self.sign == Negative {
"-" + result
} else {
result
}
}

fn deep_clone(self : BigInt) -> BigInt {
let new_limbs = Array::make(self.len, 0)
for i = 0; i < self.len; i = i + 1 {
Expand Down
3 changes: 3 additions & 0 deletions bigint/bigint.mbti
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package moonbitlang/core/bigint

// Values
fn from_hex(String) -> BigInt

fn from_int(Int) -> BigInt

fn from_int64(Int64) -> BigInt
Expand All @@ -24,6 +26,7 @@ impl BigInt {
op_mul(Self, Self) -> Self
op_neg(Self) -> Self
op_sub(Self, Self) -> Self
to_hex(Self) -> String
to_string(Self) -> String
}

Expand Down
129 changes: 129 additions & 0 deletions bigint/bigint_test.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -426,3 +426,132 @@ test "compare" {
@assertion.assert_true(a.compare(b) < 0)?
@assertion.assert_true(b.compare(a) > 0)?
}

test "from_hex" {
// Check zero
let a = from_hex("0")
check_len(a)?
inspect(a.to_string(), content="0")?

// Test positive number
let a = from_hex("1")
check_len(a)?
inspect(a.to_string(), content="1")?

// Test negative number
let a = from_hex("-F")
check_len(a)?
inspect(a.to_string(), content="-15")?
let a = from_hex("-a")
check_len(a)?
inspect(a.to_string(), content="-10")?

// Test large positive number
let a = from_hex("112210F47DE98115")
check_len(a)?
inspect(a.to_string(), content="1234567890123456789")?

// Test very large positive number
let a = from_hex(
"123456789012345678123456789012345678123456789012345678123456789012345678",
)
check_len(a)?
inspect(
a.to_string(),
content="35365207917649046390549507392234216535182073572857507984542859337680634154115797374584",
)?
let a = from_hex(
"11E3444DC1F35F057AD2CBC2791737468140A426FAC3CBA7AF8C92A8F34E",
)
check_len(a)?
inspect(
a.to_string(),
content="123456789012345678123456789012345678123456789012345678123456789012345678",
)?
let a = from_hex(
"805146F2F58580962A0A2E6134BC75E25AD97AE3D09CD34BA4F629AB8911F3F5CB8573A62EDD16B0D46775A415F545A75518DA3439914D9CAA26449067D85E704E8FCF9B29182485B41F952616BACDFDDF52B413B524D0EB743E8264A9C6AE32D12C3D20C5B81189060F4AC5D216713D503A69644EA8E4EA220A720C41F6B3D18BED65B4238318E6B0A41D8540D756865EC92DF40E8D365A230F17DF1D0A440BC6A557CD46D00B10D74C0E75500B2ADB3A0336223F9285B78CD04F485E417E1DB562B9EFCF79433209E6D6E2F43A484E471DE4F1F5AE38E08E7DAEB644C2C0A22697DD6D29BE0B40FF50DB575FEF02FA5525953C7C198B4A3600BA8CE1F917852A4B957151189F09DCDFCB79963E7D850127858A97855B94870ACCBE8203E73FE79791EE6EA1B1282A0CEAC54D6F6B7CD6C7B8D8092E949FF0A84",
)
check_len(a)?
inspect(
a.to_string(),
content="12345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812",
)?

// Test very large negative number
let a = from_hex(
"-123456789012345678123456789012345678123456789012345678123456789012345678",
)
check_len(a)?
inspect(
a.to_string(),
content="-35365207917649046390549507392234216535182073572857507984542859337680634154115797374584",
)?
}

test "to_hex" {
// Check zero
let a = from_hex("00")
check_len(a)?
inspect(a.to_hex(), content="0")?

// Test negative number
let a = from_hex("-F")
check_len(a)?
inspect(a.to_hex(), content="-F")?

// Test positive number
let a = from_hex("F")
check_len(a)?
inspect(a.to_hex(), content="F")?

// Test positive number with leading zero
let a = from_hex("10")
check_len(a)?
inspect(a.to_hex(), content="10")?

// Test large positive number
let a = from_hex("01234567890123456789")
check_len(a)?
inspect(a.to_hex(), content="1234567890123456789")?

// Check padding
let a = from_hex("100000")
check_len(a)?
inspect(a.to_string(), content="1048576")?
inspect(a.to_hex(), content="100000")?

// Test very large positive number
let a = from_string(
"123456789012345678123456789012345678123456789012345678123456789012345678",
)
check_len(a)?
inspect(
a.to_hex(),
content="11E3444DC1F35F057AD2CBC2791737468140A426FAC3CBA7AF8C92A8F34E",
)?
let a = from_string(
"35365207917649046390549507392234216535182073572857507984542859337680634154115797374584",
)
check_len(a)?
inspect(
a.to_hex(),
content="123456789012345678123456789012345678123456789012345678123456789012345678",
)?
let str = "12345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812345678901234567812"
let a = from_string(str)
check_len(a)?
inspect(
a.to_hex(),
content="805146F2F58580962A0A2E6134BC75E25AD97AE3D09CD34BA4F629AB8911F3F5CB8573A62EDD16B0D46775A415F545A75518DA3439914D9CAA26449067D85E704E8FCF9B29182485B41F952616BACDFDDF52B413B524D0EB743E8264A9C6AE32D12C3D20C5B81189060F4AC5D216713D503A69644EA8E4EA220A720C41F6B3D18BED65B4238318E6B0A41D8540D756865EC92DF40E8D365A230F17DF1D0A440BC6A557CD46D00B10D74C0E75500B2ADB3A0336223F9285B78CD04F485E417E1DB562B9EFCF79433209E6D6E2F43A484E471DE4F1F5AE38E08E7DAEB644C2C0A22697DD6D29BE0B40FF50DB575FEF02FA5525953C7C198B4A3600BA8CE1F917852A4B957151189F09DCDFCB79963E7D850127858A97855B94870ACCBE8203E73FE79791EE6EA1B1282A0CEAC54D6F6B7CD6C7B8D8092E949FF0A84",
)?

// Test very large negative number
let a = from_string(
"-123456789012345678123456789012345678123456789012345678123456789012345678",
)
check_len(a)?
inspect(
a.to_hex(),
content="-11E3444DC1F35F057AD2CBC2791737468140A426FAC3CBA7AF8C92A8F34E",
)?
}
1 change: 1 addition & 0 deletions bigint/moon.pkg.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"import": [
"moonbitlang/core/builtin",
"moonbitlang/core/char",
"moonbitlang/core/coverage"
],
"test_import": ["moonbitlang/core/assertion"]
Expand Down

0 comments on commit 6874cd7

Please sign in to comment.