README

#BigInt Package Documentation

This package provides arbitrary-precision integer arithmetic through the BigInt type. BigInt allows you to work with integers of unlimited size, making it perfect for cryptographic operations, mathematical computations, and any scenario where standard integer types are insufficient.

#Creating BigInt Values

There are several ways to create BigInt values:

///|
test "creating bigint values" {
// From integer literals with 'N' suffix
let big1 = 12345678901234567890N
inspect(big1, content="12345678901234567890")

// From regular integers
let big2 = @bigint.BigInt::from_int(42)
inspect(big2, content="42")

// From Int64 values
let big3 = @bigint.BigInt::from_int64(9223372036854775807L)
inspect(big3, content="9223372036854775807")

// From strings
let big4 = @bigint.BigInt::from_string("123456789012345678901234567890")
inspect(big4, content="123456789012345678901234567890")

// From hexadecimal strings
let big5 = @bigint.BigInt::from_string("1a2b3c4d5e6f", radix=16)
inspect(big5, content="28772997619311")
}

#Basic Arithmetic Operations

BigInt supports all standard arithmetic operations:

///|
test "arithmetic operations" {
let a = 123456789012345678901234567890N
let b = 987654321098765432109876543210N

// Addition
let sum = a + b
inspect(sum, content="1111111110111111111011111111100")

// Subtraction
let diff = b - a
inspect(diff, content="864197532086419753208641975320")

// Multiplication
let product = @bigint.BigInt::from_int(123) * @bigint.BigInt::from_int(456)
inspect(product, content="56088")

// Division
let quotient = @bigint.BigInt::from_int(1000) / @bigint.BigInt::from_int(7)
inspect(quotient, content="142")

// Modulo
let remainder = @bigint.BigInt::from_int(1000) % @bigint.BigInt::from_int(7)
inspect(remainder, content="6")

// Negation
let neg = -a
inspect(neg, content="-123456789012345678901234567890")
}

#Comparison Operations

Compare BigInt values with each other and with regular integers:

///|
test "comparisons" {
let big = 12345N
let small = 123N

// BigInt to BigInt comparison
inspect(big > small, content="true")
inspect(big == small, content="false")
inspect(small < big, content="true")

// BigInt to Int comparison
inspect(big.equal_int(12345), content="true")
inspect(big.compare_int(12345), content="0")
inspect(big.compare_int(1000), content="1") // greater than
inspect(small.compare_int(200), content="-1") // less than

// BigInt to Int64 comparison
let big64 = @bigint.BigInt::from_int64(9223372036854775807L)
inspect(big64.equal_int64(9223372036854775807L), content="true")
}

#Bitwise Operations

BigInt supports bitwise operations for bit manipulation:

///|
test "bitwise operations" {
let a = 0b11110000N // 240 in decimal
let b = 0b10101010N // 170 in decimal

// Bitwise AND
let and_result = a & b
inspect(and_result, content="160") // 0b10100000

// Bitwise OR
let or_result = a | b
inspect(or_result, content="250") // 0b11111010

// Bitwise XOR
let xor_result = a ^ b
inspect(xor_result, content="90") // 0b01011010

// Bit length
let big_num = 255N
inspect(big_num.bit_length(), content="8")

// Count trailing zeros
let with_zeros = 1000N // Has trailing zeros in binary
let ctz = with_zeros.ctz()
inspect(ctz >= 0, content="true")
}

#Power and Modular Arithmetic

BigInt provides efficient power and modular exponentiation:

///|
test "power operations" {
// Basic power
let base = 2N
let exponent = 10N
let power = base.pow(exponent)
inspect(power, content="1024")

// Modular exponentiation (useful for cryptography)
let base2 = 3N
let exp2 = 5N
let modulus = 7N
let mod_power = base2.pow(exp2, modulus~)
inspect(mod_power, content="5") // (3^5) % 7 = 243 % 7 = 5

// Large modular exponentiation (optimized for speed)
let large_base = 123N
let large_exp = 20N
let large_mod = 1000007N
let result = large_base.pow(large_exp, modulus=large_mod)
inspect(result, content="378446") // (123^20) % 1000007
}

#String and Hexadecimal Conversion

Convert BigInt to and from various string representations:

///|
test "string conversions" {
let big = 255N

// Decimal string
let decimal = big.to_string()
inspect(decimal, content="255")

// Hexadecimal (lowercase)
let hex = big.to_string(radix=16)
inspect(hex, content="ff")

// Parse from hex (radix=16)
let from_radix16 = @bigint.BigInt::from_string("deadbeef", radix=16)
inspect(from_radix16, content="3735928559")

// Round-trip conversion
let original = 98765432109876543210N
let as_string = original.to_string()
let parsed_back = @bigint.BigInt::from_string(as_string)
inspect(original == parsed_back, content="true")
}

#Byte Array Conversion

Convert BigInt to and from byte arrays:

///|
test "byte conversions" {
let big = 0x123456789abcdefN

// Convert to bytes
let bytes = big.to_octets()
inspect(bytes.length() > 0, content="true")

// Convert from bytes (positive number)
let from_bytes = @bigint.BigInt::from_octets(bytes)
inspect(from_bytes == big, content="true")

// Convert with specific length
let fixed_length = @bigint.BigInt::from_int(255).to_octets(length=4)
inspect(fixed_length.length(), content="4")

// Negative numbers
// let negative = -big
// let neg_bytes = negative.to_octets()
// to_octets does not accept negative numbers
// let neg_from_bytes = @bigint.BigInt::from_octets(neg_bytes, signum=-1)
// inspect(neg_from_bytes == negative, content="true")
}

#Type Conversions

Convert BigInt to standard integer types:

///|
test "type conversions" {
let big = 12345N

// To Int (truncates if too large)
let as_int = big.to_int()
inspect(as_int, content="12345")

// To Int64
let as_int64 = big.to_int64()
inspect(as_int64, content="12345")

// To UInt
let as_uint = big.to_uint()
inspect(as_uint, content="12345")

// To smaller types
let small = 255N
let as_int16 = small.to_int16()
inspect(as_int16, content="255")
let as_uint16 = small.to_uint16()
inspect(as_uint16, content="255")
}

#JSON Serialization

BigInt values can be serialized to and from JSON:

///|
test "json serialization" {
let big = 12345678901234567890N

// Convert to JSON (as string to preserve precision)
let json = big.to_json()
@debug.debug_inspect(json, content="String(\"12345678901234567890\")")

// Large numbers that exceed JavaScript's safe integer range
let very_big = @bigint.BigInt::from_string("123456789012345678901234567890")
let big_json = very_big.to_json()
@debug.debug_inspect(
big_json,
content="String(\"123456789012345678901234567890\")",
)
}

#Utility Functions

Check properties of BigInt values:

///|
test "utility functions" {
let zero = 0N
let positive = 42N
let negative = -42N

// Check if zero
inspect(zero.is_zero(), content="true")
inspect(positive.is_zero(), content="false")

// Sign testing through comparison
inspect(positive > zero, content="true")
inspect(negative < zero, content="true")
inspect(zero == zero, content="true")
}

#Use Cases and Applications

BigInt is particularly useful for:

  1. Cryptography: RSA encryption, digital signatures, and key generation
  2. Mathematical computations: Factorial calculations, Fibonacci sequences, prime number testing
  3. Financial calculations: High-precision monetary computations
  4. Scientific computing: Large integer calculations in physics and chemistry
  5. Data processing: Handling large numeric IDs and checksums

#Performance Considerations

  • BigInt operations are slower than regular integer operations due to arbitrary precision
  • Addition and subtraction are generally fast
  • Multiplication and division become slower with larger numbers
  • Modular exponentiation is optimized for cryptographic use cases
  • String conversions can be expensive for very large numbers

#Best Practices

  1. Use regular integers when possible: Only use BigInt when you need arbitrary precision
  2. Cache string representations: If you need to display the same BigInt multiple times
  3. Use modular arithmetic: For cryptographic applications, always use modular exponentiation
  4. Be careful with conversions: Converting very large BigInt to regular integers will truncate
  5. Consider memory usage: Very large BigInt values consume more memory

#
BigInt

type BigInt

A big integer represented as an array of Int.
impl Add for BigInt
impl BitAnd for BigInt
impl BitOr for BigInt
impl BitXOr for BigInt
impl Compare for BigInt
impl Default for BigInt
impl Div for BigInt
impl Eq for BigInt
impl Hash for BigInt
impl Mod for BigInt
impl Mul for BigInt
impl Neg for BigInt
impl Shl for BigInt
impl Show for BigInt
impl Shr for BigInt
impl Sub for BigInt
impl ToJson for BigInt

#
BigInt::add

fn BigInt::add(self : BigInt, other : BigInt) -> BigInt

#
BigInt::bit_length

fn BigInt::bit_length(self : BigInt) -> Int

Returns the number of bits required to represent a BigInt value in two's complement format, excluding the sign bit.

Parameters:

  • self : The BigInt value whose bit length is to be calculated.

Example:

test {
let pos = 16N // 10000
inspect(pos.bit_length(), content="5")
let neg = -16N //
inspect(neg.bit_length(), content="4")
let zero = 0N
inspect(zero.bit_length(), content="0")
}

#
BigInt::compare

fn BigInt::compare(self : BigInt, other : BigInt) -> Int

#
BigInt::compare_int

fn BigInt::compare_int(self : BigInt, other : Int) -> Int

Compares a BigInt with an Int and returns their relative order.

Parameters:

  • self : The BigInt value to compare.
  • other : The Int value to compare against.

Returns an integer indicating the relative order:

  • A negative value if self is less than other
  • Zero if self equals other
  • A positive value if self is greater than other

Example:

test {
let big = 42N
inspect(big.compare_int(24), content="1") // 42 > 24
inspect(big.compare_int(42), content="0") // 42 = 42
inspect(big.compare_int(100), content="-1") // 42 < 100
}

#
BigInt::compare_int64

fn BigInt::compare_int64(self : BigInt, other : Int64) -> Int

Compares a BigInt with an Int64 and returns their relative order.

Parameters:

  • self : The BigInt value to compare.
  • other : The Int64 value to compare against.

Returns an integer indicating the relative order:

  • A negative value if self is less than other
  • Zero if self equals other
  • A positive value if self is greater than other

Example:

test {
let big = 42N
inspect(big.compare_int64(24L), content="1") // 42 > 24
inspect(big.compare_int64(42L), content="0") // 42 = 42
inspect(big.compare_int64(100L), content="-1") // 42 < 100
}

#
BigInt::compare_uint

fn BigInt::compare_uint(self : BigInt, other : UInt) -> Int

Compares a BigInt with a UInt and returns their relative order.

#
BigInt::compare_uint64

fn BigInt::compare_uint64(self : BigInt, other : UInt64) -> Int

Compares a BigInt with a UInt64 and returns their relative order.

#
BigInt::ctz

fn BigInt::ctz(self : BigInt) -> Int

Returns the number of trailing zero bits in the binary representation of the absolute value of this BigInt.

Example:
test {
inspect(8N.ctz(), content="3") // 0b1000
inspect(12N.ctz(), content="2") // 0b1100
inspect(0N.ctz(), content="0")
}

#
BigInt::div

fn BigInt::div(self : BigInt, other : BigInt) -> BigInt

#
BigInt::equal

fn BigInt::equal(self : BigInt, other : BigInt) -> Bool

#
BigInt::equal_int

fn BigInt::equal_int(self : BigInt, other : Int) -> Bool

Tests equality between a BigInt and an Int value.

This function performs a safe comparison by first checking if the BigInt can be converted to an Int without overflow, then comparing the converted value with the Int parameter. If the BigInt is too large or too small to fit in an Int, the function returns false.

Parameters:

  • self : The BigInt value to compare.
  • other : The Int value to compare against.

Returns true if the BigInt and Int represent the same numerical value, false otherwise.

Example:

test {
let big = 42N
inspect(big.equal_int(42), content="true")
inspect(big.equal_int(41), content="false")
let large = 9223372036854775808N // Beyond Int64 range
inspect(large.equal_int(42), content="false")
}

#
BigInt::equal_int64

fn BigInt::equal_int64(self : BigInt, other : Int64) -> Bool

Tests whether a BigInt value is equal to an Int64 value.

This function performs an efficient equality check by first verifying if the BigInt can be represented as an Int64 without overflow. If it can be converted, it then compares the converted value with the given Int64.

Parameters:

  • self : The BigInt value to compare.
  • other : The Int64 value to compare against.

Returns true if the BigInt and Int64 represent the same numerical value, false otherwise.

Example:

test {
let big = @bigint.BigInt::from_int64(9223372036854775807L) // Int64 max value
inspect(big.equal_int64(9223372036854775807L), content="true")
inspect(big.equal_int64(42L), content="false")
let overflow = @bigint.BigInt::from_string("9223372036854775808") // Beyond Int64 range
inspect(overflow.equal_int64(9223372036854775807L), content="false")
}

#
BigInt::equal_uint

fn BigInt::equal_uint(self : BigInt, other : UInt) -> Bool

Tests whether a BigInt value is equal to a UInt value.

This is a convenience helper mirroring equal_int/equal_int64 and avoids forcing callers to construct a temporary BigInt manually.

#
BigInt::equal_uint64

fn BigInt::equal_uint64(self : BigInt, other : UInt64) -> Bool

Tests whether a BigInt value is equal to a UInt64 value.

This is a convenience helper mirroring equal_int/equal_int64 and avoids forcing callers to construct a temporary BigInt manually.

#
BigInt::from_hex

#deprecated("Use `from_string(radix=16)` instead")
fn BigInt::from_hex(input : String) -> BigInt

Deprecated: use from_string(radix=16) instead.

#
BigInt::from_int

fn BigInt::from_int(n : Int) -> BigInt

Converts a 32-bit signed integer to a BigInt.

Parameters:

  • value : The 32-bit signed integer (Int) to be converted.

Returns a BigInt equivalent to the input integer.

Example:

test {
let big = @bigint.BigInt::from_int(42)
inspect(big, content="42")
let neg = @bigint.BigInt::from_int(-42)
inspect(neg, content="-42")
}

#
BigInt::from_int64

fn BigInt::from_int64(n : Int64) -> BigInt

Converts a signed 64-bit integer to a BigInt.

Parameters:

  • number : A 64-bit signed integer (Int64) to be converted.

Returns a BigInt value that represents the same numerical value as the input.

Example:

test {
let big = @bigint.BigInt::from_int64(9223372036854775807L) // max value of Int64
inspect(big, content="9223372036854775807")
let neg = @bigint.BigInt::from_int64(-9223372036854775808L) // min value of Int64
inspect(neg, content="-9223372036854775808")
}

#
BigInt::from_octets

fn BigInt::from_octets(input : BytesView, signum? : Int) -> BigInt

Converts a big-endian byte sequence to a BigInt value with an optional sign. Interprets the input bytes as a big-endian representation of an unsigned integer, and applies the specified sign to create the final BigInt value.

Parameters:

  • bytes : A sequence of bytes representing the magnitude of the number in big-endian order. The sequence must not be empty unless sign is 0.
  • sign : An integer specifying the sign of the resulting number (default: 1). A value of 1 creates a positive number, -1 creates a negative number, and 0 returns zero regardless of the input bytes.

Returns a BigInt value representing the number encoded in the byte sequence with the specified sign.

Throws a panic if the input byte sequence is empty and the sign is not 0.

Example:

test {
let bytes = b"\x01\x02\x03" // Represents 0x010203
let positive = @bigint.BigInt::from_octets(bytes)
let negative = @bigint.BigInt::from_octets(bytes, signum=-1)
inspect(positive, content="66051")
inspect(negative, content="-66051")
}

#
BigInt::from_string

fn BigInt::from_string(input : String, radix? : Int) -> BigInt

Converts a string representation in the specified radix to a BigInt value.

Panics if the input is malformed. Use @string.parse_bigint to handle errors.

#
BigInt::from_uint

fn BigInt::from_uint(n : UInt) -> BigInt

Converts an unsigned 32-bit integer to a BigInt.

Parameters:

  • value : The unsigned 32-bit integer to be converted.

Returns a BigInt representing the same numerical value as the input.

Example:

test {
let n = 42U
inspect(@bigint.BigInt::from_uint(n), content="42")
}

#
BigInt::from_uint64

fn BigInt::from_uint64(n : UInt64) -> BigInt

Converts an unsigned 64-bit integer to a BigInt.

Parameters:

  • value : The unsigned 64-bit integer (UInt64) to be converted.

Returns a new BigInt with the same value as the input. The resulting BigInt will always have a positive sign since the input is an unsigned integer.

Example:

test {
let n = @bigint.BigInt::from_uint64(12345678901234567890UL)
inspect(n, content="12345678901234567890")
let zero = @bigint.BigInt::from_uint64(0UL)
inspect(zero, content="0")
}

#
BigInt::hash

fn BigInt::hash(self : BigInt) -> Int

#
BigInt::is_zero

fn BigInt::is_zero(self : BigInt) -> Bool

Checks whether a BigInt value is equal to zero.

Parameters:

  • self : The BigInt value to be checked.

Returns true if the BigInt is zero, false otherwise.

Example:

test {
inspect(0N.is_zero(), content="true")
inspect(42N.is_zero(), content="false")
inspect((-1N).is_zero(), content="false")
}

#
BigInt::land

fn BigInt::land(self : BigInt, other : BigInt) -> BigInt

#
BigInt::lor

fn BigInt::lor(self : BigInt, other : BigInt) -> BigInt

#
BigInt::lxor

fn BigInt::lxor(self : BigInt, other : BigInt) -> BigInt

#
BigInt::mod

fn BigInt::mod(self : BigInt, other : BigInt) -> BigInt

#
BigInt::mul

fn BigInt::mul(self : BigInt, other : BigInt) -> BigInt

#
BigInt::neg

fn BigInt::neg(self : BigInt) -> BigInt

#
BigInt::pow

fn BigInt::pow(self : BigInt, exp : BigInt, modulus? : BigInt) -> BigInt

Computes the result of raising a BigInt to the power of another BigInt, with an optional modulus.

When a modulus is provided, computes the modular exponentiation using the square-and-multiply algorithm. This is particularly useful in cryptographic applications where direct exponentiation would result in numbers too large to handle efficiently.

Parameters:

  • self : The base number to be raised to a power.
  • exp : The exponent (must be non-negative).
  • modulus : Optional modulus for modular exponentiation (must be positive if provided).

Returns the result of the exponentiation, or the result modulo modulus if a modulus is provided.

Throws:

  • Aborts if the exponent is negative.
  • Aborts if the provided modulus is zero or negative.

Example:

test {
let base = @bigint.BigInt::from_string("3")
let exp = @bigint.BigInt::from_string("4")
inspect(base.pow(exp), content="81")
inspect(base.pow(exp, modulus=@bigint.BigInt::from_string("10")), content="1")
}

#
BigInt::shl

fn BigInt::shl(self : BigInt, n : Int) -> BigInt

#
BigInt::shr

fn BigInt::shr(self : BigInt, n : Int) -> BigInt

#
BigInt::sub

fn BigInt::sub(self : BigInt, other : BigInt) -> BigInt

#
BigInt::to_hex

#deprecated("Use `to_string(radix=16)` instead")
fn BigInt::to_hex(self : BigInt, uppercase? : Bool) -> String

Deprecated: use to_string(radix=16) instead.

#
BigInt::to_int

fn BigInt::to_int(self : BigInt) -> Int

Converts a BigInt to a 32-bit signed integer (Int).

Parameters:

  • self : The BigInt value to be converted.

Returns a 32-bit signed integer representing the lower 32 bits of the input BigInt.

Example:

test {
let big = 2147483648N // 2^31
inspect(big.to_int(), content="-2147483648") // Overflow to Int.min_value
}

#
BigInt::to_int16

fn BigInt::to_int16(self : BigInt) -> Int16

Converts a BigInt value to a signed 16-bit integer (Int16).

Parameters:

  • self : The BigInt value to be converted.

Returns a 16-bit signed integer representing the lower 16 bits of the input BigInt.

Example:

test {
let n = 42N
inspect(n.to_int16(), content="42")
let neg = -1N
inspect(neg.to_int16(), content="-1")
let big = 32768N // 2^15
inspect(big.to_int16(), content="-32768") // Overflow to Int16.min_value
}

#
BigInt::to_int64

fn BigInt::to_int64(self : BigInt) -> Int64

Converts a BigInt to a signed 64-bit integer (Int64).

Parameters:

  • value : The BigInt value to be converted.

Returns a 64-bit signed integer (Int64) representing the lower 64 bits of the input BigInt.

Example:

test {
let big = 9223372036854775807N // max value of Int64
inspect(big.to_int64(), content="9223372036854775807")
let bigger = big + 1
inspect(bigger.to_int64(), content="-9223372036854775808") // Overflow to Int64.min_value
}

#
BigInt::to_json

fn BigInt::to_json(self : BigInt) -> Json

#
BigInt::to_octets

fn BigInt::to_octets(self : BigInt, length? : Int) -> Bytes

Converts a non-negative arbitrary-precision integer to a big-endian byte sequence. The output can be padded with leading zeros to meet a specified length requirement, but only if the actual length is less than the requested length.

Parameters:

  • self : The arbitrary-precision integer to convert. Must be non-negative.
  • length : Optional minimum length of the output byte sequence. If provided, must be positive. Defaults to 1 if not specified.

Returns a byte sequence representing the number in big-endian order, possibly padded with leading zeros to reach the specified length.

Throws a panic if:

  • The input number is negative
  • The specified length is negative or zero

Example:

test {
let n = @bigint.BigInt::from_string("abcdef", radix=16)
inspect(n.to_octets(length=4), content="b\"\\x00\\xab\\xcd\\xef\"")
let m = @bigint.BigInt::from_string("0")
inspect(m.to_octets(), content="b\"\\x00\"")
}

#
BigInt::to_string

fn BigInt::to_string(self : BigInt, radix? : Int) -> String

Converts a BigInt value to a string representation in the specified radix.

Parameters:

  • self : The BigInt value to convert to a string.
  • radix : The base to use for formatting (2 to 36). Defaults to 10.

Returns a string containing the representation of the number in the given radix, with a leading minus sign for negative numbers. Digits above 9 use lowercase letters (a to z).

Example:

test {
let n = 12345678901234567890N
inspect(n.to_string(), content="12345678901234567890")
inspect(n.to_string(radix=16), content="ab54a98ceb1f0ad2")
let neg = -42N
inspect(neg.to_string(), content="-42")
let zero = 0N
inspect(zero.to_string(), content="0")
}

#
BigInt::to_uint

fn BigInt::to_uint(self : BigInt) -> UInt

Converts a BigInt to an unsigned 32-bit integer (UInt).

Parameters:

  • self : The BigInt value to be converted.

Returns a UInt value representing the lower 32 bits of the input BigInt.

Example:

test {
let n = 42N
inspect(n.to_uint(), content="42")
let neg = -1N
inspect(neg.to_uint(), content="4294967295") // 2^32 - 1
}

#
BigInt::to_uint16

fn BigInt::to_uint16(self : BigInt) -> UInt16

Converts a BigInt value to an unsigned 16-bit integer (UInt16).

Parameters:

  • self : The BigInt value to be converted.

Returns a UInt16 value representing the lower 16 bits of the input BigInt.

Example:

test {
let n = 42N
inspect(n.to_uint16(), content="42")
let neg = -1N
inspect(neg.to_uint16(), content="65535") // 2^16 - 1
}

#
BigInt::to_uint64

fn BigInt::to_uint64(self : BigInt) -> UInt64

Converts a BigInt to an unsigned 64-bit integer (UInt64).

Parameters:

  • self : The BigInt value to be converted.

Returns a UInt64 value representing the lower 64 bits of the input BigInt.

Example:

test {
let n = 12345678901234567890N
inspect(n.to_uint64(), content="12345678901234567890")
let neg = -1N
inspect(neg.to_uint64(), content="18446744073709551615") // 2^64 - 1
}