Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 33 additions & 15 deletions bigint/bigint_nonjs.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,17 @@ pub impl Shr for BigInt with shr(self : BigInt, n : Int) -> BigInt {
if r == 0 {
let new_limbs = FixedArray::make(new_len, 0U)
new_limbs.unsafe_blit(0, self.limbs, lz, new_len)
{ limbs: new_limbs, sign: self.sign, len: new_len }
let result = { limbs: new_limbs, sign: self.sign, len: new_len }
// For negative numbers with floor division semantics, check if any
// dropped low limbs were non-zero and round toward negative infinity
if self.sign == Negative {
for i in 0..<lz {
if self.limbs[i] != 0 {
return result - 1
}
}
}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
result
} else {
let new_limbs = FixedArray::make(new_len, 0U)
let a = self.limbs
Expand All @@ -802,11 +812,21 @@ pub impl Shr for BigInt with shr(self : BigInt, n : Int) -> BigInt {
if new_len > 1 && new_limbs[new_len - 1] == 0 {
new_len -= 1
}
if self.sign == Negative && (carry & (1UL << r)) != carry {
{ limbs: new_limbs, sign: self.sign, len: new_len } - 1
} else {
{ limbs: new_limbs, sign: self.sign, len: new_len }
if self.sign == Negative {
let mut has_remainder = carry != 0UL
if !has_remainder {
for i in 0..<lz {
if self.limbs[i] != 0 {
has_remainder = true
break
}
}
}
if has_remainder {
return { limbs: new_limbs, sign: self.sign, len: new_len } - 1
}
}
{ limbs: new_limbs, sign: self.sign, len: new_len }
}
}

Expand Down Expand Up @@ -1370,8 +1390,8 @@ pub fn BigInt::pow(self : BigInt, exp : BigInt, modulus? : BigInt) -> BigInt {
}
Some(modulus) => {
guard !(modulus.is_zero() || modulus.sign == Negative)
let mut result = 1N
let mut base = self
let mut result = 1N % modulus
let mut base = (self % modulus + modulus) % modulus
let mut exp = exp
while exp > 0 {
if exp % 2 == 1 {
Expand Down Expand Up @@ -1908,15 +1928,13 @@ pub fn BigInt::bit_length(self : BigInt) -> Int {
let mut bit_length = (self.len - 1) * radix_bit_len +
(radix_bit_len - self.limbs[self.len - 1].clz())
if self.sign == Negative {
// check if this number is a power of two
let mut pow2 = self.limbs[0].popcnt() == 1
for i in 1..<self.len {
if !pow2 {
break
}
pow2 = self.limbs[i] == 0
// Check if the magnitude is a power of two (exactly one bit set
// across all limbs). For example, -2^32 has limbs [0, 1].
let mut total_bits = 0
for i in 0..<self.len {
total_bits += self.limbs[i].popcnt()
}
if pow2 {
if total_bits == 1 {
bit_length -= 1
}
}
Expand Down
39 changes: 39 additions & 0 deletions bigint/bigint_test.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,10 @@ test "shr" {
@bigint.BigInt::from_int64(0b1111_1111L) >> 44,
@bigint.BigInt::from_int64(0),
)
// dropped limbs bug: -(2^33+1) >> 33 should be -2
assert_eq(-8589934593N >> 33, -2N)
// carry condition bug: -65537 >> 16 should be -2
assert_eq(-65537N >> 16, -2N)
}

///|
Expand Down Expand Up @@ -1271,3 +1275,38 @@ test "BigInt bit_length negative multi-limb" {
test "BigInt bit_length zero" {
inspect(@bigint.BigInt::from_int(0).bit_length(), content="0")
}

///|
test "BigInt bit_length negative multi-limb power of two" {
// -2^32 has limbs [0, 1] - bit_length should be 32, not 33
let neg = @bigint.BigInt::from_string("-4294967296")
inspect(neg.bit_length(), content="32")
// -2^64 has limbs [0, 0, 1] - bit_length should be 64
let neg2 = @bigint.BigInt::from_string("-18446744073709551616")
inspect(neg2.bit_length(), content="64")
}

///|
test "BigInt right shift negative exact multiple of 32" {
// (-4294967297) >> 32 should be -2 (floor division), not -1
// -4294967297 = -(2^32 + 1), so >>32 should give -2
let neg = @bigint.BigInt::from_string("-4294967297")
inspect((neg >> 32).to_string(), content="-2")
// -4294967296 = -(2^32), so >>32 should give -1
let neg2 = @bigint.BigInt::from_string("-4294967296")
inspect((neg2 >> 32).to_string(), content="-1")
}

///|
test "BigInt modular exponentiation edge cases" {
// 2^0 mod 1 should be 0
let base = @bigint.BigInt::from_int(2)
let zero = @bigint.BigInt::from_int(0)
let one = @bigint.BigInt::from_int(1)
inspect(base.pow(zero, modulus=one).to_string(), content="0")
// 3^4 mod 10 should be 1
let three = @bigint.BigInt::from_int(3)
let four = @bigint.BigInt::from_int(4)
let ten = @bigint.BigInt::from_int(10)
inspect(three.pow(four, modulus=ten).to_string(), content="1")
}
Loading