From 9dce7713cfb9380d0d7d8e1eed73bc2c280ccca2 Mon Sep 17 00:00:00 2001 From: JalonSolov Date: Thu, 11 Jun 2026 08:49:08 -0400 Subject: [PATCH] math.bits: cleaned up comments, added inline attrs, extra checks for 0, and tests (#27410) --- vlib/math/bits/bits.amd64.v | 2 +- vlib/math/bits/bits.c.v | 27 +++-- vlib/math/bits/bits.v | 37 ++++--- vlib/math/bits/bits_test.v | 191 +++++++++++++++++++++++------------- 4 files changed, 153 insertions(+), 104 deletions(-) diff --git a/vlib/math/bits/bits.amd64.v b/vlib/math/bits/bits.amd64.v index ba3d6b53a..bd94a29fd 100644 --- a/vlib/math/bits/bits.amd64.v +++ b/vlib/math/bits/bits.amd64.v @@ -72,7 +72,7 @@ pub fn mul_add_64(x u64, y u64, z u64) (u64, u64) { pub fn div_64(hi u64, lo u64, y1 u64) (u64, u64) { mut y := y1 if y == 0 { - panic(overflow_error) + panic(divide_error) } if y <= hi { panic(overflow_error) diff --git a/vlib/math/bits/bits.c.v b/vlib/math/bits/bits.c.v index 4df541400..ce62e61a3 100644 --- a/vlib/math/bits/bits.c.v +++ b/vlib/math/bits/bits.c.v @@ -8,7 +8,6 @@ fn C.__builtin_clzll(x u64) i32 fn C.__lzcnt(x u32) i32 fn C.__lzcnt64(x u64) i32 -// --- LeadingZeros --- // leading_zeros_8 returns the number of leading zero bits in x; the result is 8 for x == 0. @[inline] pub fn leading_zeros_8(x u8) int { @@ -16,9 +15,9 @@ pub fn leading_zeros_8(x u8) int { return 8 } $if msvc { - return C.__lzcnt(x) - 24 + return C.__lzcnt(u32(x)) - 24 } $else $if !tinyc { - return C.__builtin_clz(x) - 24 + return C.__builtin_clz(u32(x)) - 24 } return leading_zeros_8_default(x) } @@ -30,9 +29,9 @@ pub fn leading_zeros_16(x u16) int { return 16 } $if msvc { - return C.__lzcnt(x) - 16 + return C.__lzcnt(u32(x)) - 16 } $else $if !tinyc { - return C.__builtin_clz(x) - 16 + return C.__builtin_clz(u32(x)) - 16 } return leading_zeros_16_default(x) } @@ -70,7 +69,6 @@ fn C.__builtin_ctzll(x u64) i32 fn C._BitScanForward(pos &int, x u32) u8 fn C._BitScanForward64(pos &int, x u64) u8 -// --- TrailingZeros --- // trailing_zeros_8 returns the number of trailing zero bits in x; the result is 8 for x == 0. @[inline] pub fn trailing_zeros_8(x u8) int { @@ -79,10 +77,10 @@ pub fn trailing_zeros_8(x u8) int { } $if msvc { mut pos := 0 - _ := C._BitScanForward(&pos, x) + _ := C._BitScanForward(&pos, u32(x)) return pos } $else $if !tinyc { - return C.__builtin_ctz(x) + return C.__builtin_ctz(u32(x)) } return trailing_zeros_8_default(x) } @@ -95,10 +93,10 @@ pub fn trailing_zeros_16(x u16) int { } $if msvc { mut pos := 0 - _ := C._BitScanForward(&pos, x) + _ := C._BitScanForward(&pos, u32(x)) return pos } $else $if !tinyc { - return C.__builtin_ctz(x) + return C.__builtin_ctz(u32(x)) } return trailing_zeros_16_default(x) } @@ -140,14 +138,13 @@ fn C.__builtin_popcountll(x u64) i32 fn C.__popcnt(x u32) i32 fn C.__popcnt64(x u64) i32 -// --- OnesCount --- // ones_count_8 returns the number of one bits ("population count") in x. @[inline] pub fn ones_count_8(x u8) int { $if msvc { - return C.__popcnt(x) + return C.__popcnt(u32(x)) } $else $if !tinyc { - return C.__builtin_popcount(x) + return C.__builtin_popcount(u32(x)) } return ones_count_8_default(x) } @@ -156,9 +153,9 @@ pub fn ones_count_8(x u8) int { @[inline] pub fn ones_count_16(x u16) int { $if msvc { - return C.__popcnt(x) + return C.__popcnt(u32(x)) } $else $if !tinyc { - return C.__builtin_popcount(x) + return C.__builtin_popcount(u32(x)) } return ones_count_16_default(x) } diff --git a/vlib/math/bits/bits.v b/vlib/math/bits/bits.v index 41fbfc584..496e59163 100644 --- a/vlib/math/bits/bits.v +++ b/vlib/math/bits/bits.v @@ -134,7 +134,6 @@ fn trailing_zeros_64_default(x u64) int { return int(de_bruijn64tab[int((x & -x) * de_bruijn64 >> (64 - 6))]) } -// --- OnesCount --- // ones_count_8 returns the number of one bits ("population count") in x. @[inline] pub fn ones_count_8(x u8) int { @@ -175,6 +174,7 @@ pub fn ones_count_64(x u64) int { return ones_count_64_default(x) } +@[inline] fn ones_count_64_default(x u64) int { // Implementation: Parallel summing of adjacent bits. // See "Hacker's Delight", Chap. 5: Counting Bits. @@ -209,7 +209,6 @@ const n16 = u16(16) const n32 = u32(32) const n64 = u64(64) -// --- RotateLeft --- // rotate_left_8 returns the value of x rotated left by (k mod 8) bits. // To rotate x right by k bits, call rotate_left_8(x, -k). // @@ -250,7 +249,6 @@ pub fn rotate_left_64(x u64, k int) u64 { return (x << s) | (x >> (n64 - s)) } -// --- Reverse --- // reverse_8 returns the value of x with its bits in reversed order. @[direct_array_access; inline] pub fn reverse_8(x u8) u8 { @@ -281,7 +279,6 @@ pub fn reverse_64(x u64) u64 { return reverse_bytes_64(y) } -// --- ReverseBytes --- // reverse_bytes_16 returns the value of x with its bytes in reversed order. // // This function's execution time does not depend on the inputs. @@ -309,7 +306,6 @@ pub fn reverse_bytes_64(x u64) u64 { return (y >> 32) | (y << 32) } -// --- Len --- // len_8 returns the minimum number of bits required to represent x; the result is 0 for x == 0. @[direct_array_access] pub fn len_8(x u8) int { @@ -364,10 +360,6 @@ pub fn len_64(x u64) int { return n + int(len_8_tab[int(y)]) } -// --- Add with carry --- -// Add returns the sum with carry of x, y and carry: sum = x + y + carry. -// The carry input must be 0 or 1; otherwise the behavior is undefined. -// The carryOut output is guaranteed to be 0 or 1. // add_32 returns the sum with carry of x, y and carry: sum = x + y + carry. // The carry input must be 0 or 1; otherwise the behavior is undefined. // The carryOut output is guaranteed to be 0 or 1. @@ -394,10 +386,6 @@ pub fn add_64(x u64, y u64, carry u64) (u64, u64) { return sum, carry_out } -// --- Subtract with borrow --- -// Sub returns the difference of x, y and borrow: diff = x - y - borrow. -// The borrow input must be 0 or 1; otherwise the behavior is undefined. -// The borrowOut output is guaranteed to be 0 or 1. // sub_32 returns the difference of x, y and borrow, diff = x - y - borrow. // The borrow input must be 0 or 1; otherwise the behavior is undefined. // The borrowOut output is guaranteed to be 0 or 1. @@ -425,13 +413,11 @@ pub fn sub_64(x u64, y u64, borrow u64) (u64, u64) { return diff, borrow_out } -// --- Full-width multiply --- - @[markused] pub const two32 = u64(0x100000000) const mask32 = two32 - 1 const overflow_error = 'Overflow Error' -const divide_error = 'Divide Error' +const divide_error = 'Divide by Zero Error' // mul_32 returns the 64-bit product of x and y: (hi, lo) = x * y // with the product bits' upper half returned in hi and the lower @@ -508,7 +494,6 @@ fn mul_add_64_default(x u64, y u64, z u64) (u64, u64) { return hi, lo } -// --- Full-width divide --- // div_32 returns the quotient and remainder of (hi, lo) divided by y: // quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper // half in parameter hi and the lower half in parameter lo. @@ -519,7 +504,10 @@ pub fn div_32(hi u32, lo u32, y u32) (u32, u32) { } fn div_32_default(hi u32, lo u32, y u32) (u32, u32) { - if y != 0 && y <= hi { + if y == 0 { + panic(divide_error) + } + if y <= hi { panic(overflow_error) } z := (u64(hi) << 32) | u64(lo) @@ -531,7 +519,8 @@ fn div_32_default(hi u32, lo u32, y u32) (u32, u32) { // div_64 returns the quotient and remainder of (hi, lo) divided by y: // quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper // half in parameter hi and the lower half in parameter lo. -// div_64 panics for y == 0 (division by zero) or y <= hi (quotient overflow). +// div_64 panics for y == 0 (division by zero). +// Also panics for y <= hi (quotient overflow). @[inline] pub fn div_64(hi u64, lo u64, y1 u64) (u64, u64) { return div_64_default(hi, lo, y1) @@ -540,7 +529,7 @@ pub fn div_64(hi u64, lo u64, y1 u64) (u64, u64) { fn div_64_default(hi u64, lo u64, y1 u64) (u64, u64) { mut y := y1 if y == 0 { - panic(overflow_error) + panic(divide_error) } if y <= hi { panic(overflow_error) @@ -599,13 +588,18 @@ fn div_64_default(hi u64, lo u64, y1 u64) (u64, u64) { // rem_32 returns the remainder of (hi, lo) divided by y. Rem32 panics // for y == 0 (division by zero) but, unlike Div32, it doesn't panic // on a quotient overflow. +@[inline] pub fn rem_32(hi u32, lo u32, y u32) u32 { + if y == 0 { + panic(divide_error) + } return u32(((u64(hi) << 32) | u64(lo)) % u64(y)) } // rem_64 returns the remainder of (hi, lo) divided by y. Rem64 panics // for y == 0 (division by zero) but, unlike div_64, it doesn't panic // on a quotient overflow. +@[inline] pub fn rem_64(hi u64, lo u64, y u64) u64 { // We scale down hi so that hi < y, then use div_64 to compute the // rem with the guarantee that it won't panic on quotient overflow. @@ -613,6 +607,9 @@ pub fn rem_64(hi u64, lo u64, y u64) u64 { // hi ≡ hi%y (mod y) // we have // hi<<64 + lo ≡ (hi%y)<<64 + lo (mod y) + if y == 0 { + panic(divide_error) + } _, rem := div_64(hi % y, lo, y) return rem } diff --git a/vlib/math/bits/bits_test.v b/vlib/math/bits/bits_test.v index a2c8376b2..398b4dc34 100644 --- a/vlib/math/bits/bits_test.v +++ b/vlib/math/bits/bits_test.v @@ -1,20 +1,12 @@ -// // test suite for bits and bits math functions -// module bits -fn test_bits() { +fn test_leading_zeros() { mut i := 0 - mut i1 := u64(0) - - // - // --- LeadingZeros --- - // // 8 bit i = 1 for x in 0 .. 8 { - // C.printf("x:%02x lz: %d cmp: %d\n", i << x, leading_zeros_8(i << x), 7-x) assert leading_zeros_8(u8(u8(i) << x)) == 7 - x } assert leading_zeros_8(0) == 8 @@ -22,7 +14,6 @@ fn test_bits() { // 16 bit i = 1 for x in 0 .. 16 { - // C.printf("x:%04x lz: %d cmp: %d\n", u16(i) << x, leading_zeros_16(u16(i) << x), 15-x) assert leading_zeros_16(u16(i) << x) == 15 - x } assert leading_zeros_16(0) == 16 @@ -30,7 +21,6 @@ fn test_bits() { // 32 bit i = 1 for x in 0 .. 32 { - // C.printf("x:%08x lz: %d cmp: %d\n", u32(i) << x, leading_zeros_32(u32(i) << x), 31-x) assert leading_zeros_32(u32(i) << x) == 31 - x } assert leading_zeros_32(0) == 32 @@ -38,15 +28,13 @@ fn test_bits() { // 64 bit i = 1 for x in 0 .. 64 { - // C.printf("x:%016llx lz: %llu cmp: %d\n", u64(i) << x, leading_zeros_64(u64(i) << x), 63-x) assert leading_zeros_64(u64(i) << x) == 63 - x } assert leading_zeros_64(0) == 64 +} - // - // --- TrailingZeros --- - // - +fn test_trailing_zeros() { + mut i := 0 // 8 bit i = 1 for x in 0 .. 8 { @@ -74,15 +62,14 @@ fn test_bits() { assert trailing_zeros_64(u64(i) << x) == x } assert trailing_zeros_64(0) == 64 +} - // - // --- ones_count --- - // - +fn test_ones_count() { + mut i := 0 + mut i1 := u64(0) // 8 bit i = 0 for x in 0 .. 9 { - // C.printf("x:%02x lz: %llu cmp: %d\n", u8(i), ones_count_8(u8(i)), x) assert ones_count_8(u8(i)) == x i = int(u32(i) << 1) + 1 } @@ -92,7 +79,6 @@ fn test_bits() { // 16 bit i = 0 for x in 0 .. 17 { - // C.printf("x:%04x lz: %llu cmp: %d\n", u16(i), ones_count_16(u16(i)), x) assert ones_count_16(u16(i)) == x i = int(u32(i) << 1) + 1 } @@ -102,7 +88,6 @@ fn test_bits() { // 32 bit i = 0 for x in 0 .. 33 { - // C.printf("x:%08x lz: %llu cmp: %d\n", u32(i), ones_count_32(u32(i)), x) assert ones_count_32(u32(i)) == x i = int(u32(i) << 1) + 1 } @@ -112,25 +97,23 @@ fn test_bits() { // 64 bit i1 = 0 for x in 0 .. 65 { - // C.printf("x:%016llx lz: %llu cmp: %d\n", u64(i1), ones_count_64(u64(i1)), x) assert ones_count_64(i1) == x i1 = (i1 << 1) + 1 } assert ones_count_64(0) == 0 assert ones_count_64(0xFFFF_FFFF_FFFF_FFFF) == 64 +} - // - // --- rotate_left/right --- - // +fn test_rotate_left_right() { assert rotate_left_8(0x12, 4) == 0x21 assert rotate_left_16(0x1234, 8) == 0x3412 assert rotate_left_32(0x12345678, 16) == 0x56781234 assert rotate_left_64(0x1234567887654321, 32) == 0x8765432112345678 +} - // - // --- reverse --- - // - +fn test_reverse() { + mut i := 0 + mut i1 := u64(0) // 8 bit i = 0 for _ in 0 .. 9 { @@ -142,7 +125,6 @@ fn test_bits() { bc++ n = n >> 1 } - // C.printf("x:%02x lz: %llu cmp: %d\n", u8(i), reverse_8(u8(i)), rv) assert reverse_8(u8(i)) == rv i = int(u32(i) << 1) + 1 } @@ -158,7 +140,6 @@ fn test_bits() { bc++ n = n >> 1 } - // C.printf("x:%04x lz: %llu cmp: %d\n", u16(i), reverse_16(u16(i)), rv) assert reverse_16(u16(i)) == rv i = int(u32(i) << 1) + 1 } @@ -174,7 +155,6 @@ fn test_bits() { bc++ n = n >> 1 } - // C.printf("x:%08x lz: %llu cmp: %d\n", u32(i), reverse_32(u32(i)), rv) assert reverse_32(u32(i)) == rv i = int(u32(i) << 1) + 1 } @@ -190,21 +170,18 @@ fn test_bits() { bc++ n = n >> 1 } - // C.printf("x:%016llx lz: %016llx cmp: %016llx\n", u64(i1), reverse_64(u64(i1)), rv) assert reverse_64(i1) == rv i1 = (i1 << 1) + 1 } +} - // - // --- add --- - // - +fn test_add() { + mut i := 0 // 32 bit i = 1 for x in 0 .. 32 { v := u32(i) << x sum, carry := add_32(v, v, u32(0)) - // C.printf("x:%08x [%llu,%llu] %llu\n", u32(i) << x, sum, carry, u64(v) + u64(v)) assert ((u64(carry) << 32) | u64(sum)) == u64(v) + u64(v) } mut sum_32t, mut carry_32t := add_32(0x8000_0000, 0x8000_0000, u32(0)) @@ -220,8 +197,10 @@ fn test_bits() { for x in 0 .. 63 { v := u64(i) << x sum, carry := add_64(v, v, u64(0)) - // C.printf("x:%16x [%llu,%llu] %llu\n", u64(i) << x, sum, carry, u64(v >> 32) + u64(v >> 32)) - assert ((carry << 32) | sum) == v + v + expected_sum := v + v + expected_carry := u64(expected_sum < v) + assert sum == expected_sum + assert carry == expected_carry } mut sum_64t, mut carry_64t := add_64(0x8000_0000_0000_0000, 0x8000_0000_0000_0000, u64(0)) assert sum_64t == u64(0) @@ -230,27 +209,23 @@ fn test_bits() { sum_64t, carry_64t = add_64(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF, u64(1)) assert sum_64t == 0xFFFF_FFFF_FFFF_FFFF assert carry_64t == u64(1) +} - // - // --- sub --- - // - +fn test_sub() { + mut i := 0 // 32 bit i = 1 for x in 1 .. 32 { v0 := u32(i) << x v1 := v0 >> 1 mut diff, mut borrow_out := sub_32(v0, v1, u32(0)) - // C.printf("x:%08x [%llu,%llu] %08x\n", u32(i) << x, diff, borrow_out, v0 - v1) assert diff == v1 diff, borrow_out = sub_32(v0, v1, u32(1)) - // C.printf("x:%08x [%llu,%llu] %08x\n", u32(i) << x, diff, borrow_out, v0 - v1) assert diff == (v1 - 1) assert borrow_out == u32(0) diff, borrow_out = sub_32(v1, v0, u32(1)) - // C.printf("x:%08x [%llu,%llu] %08x\n", u32(i) << x, diff, borrow_out, v1 - v0) assert borrow_out == u32(1) } @@ -260,23 +235,19 @@ fn test_bits() { v0 := u64(i) << x v1 := v0 >> 1 mut diff, mut borrow_out := sub_64(v0, v1, u64(0)) - // C.printf("x:%08x [%llu,%llu] %08x\n", u64(i) << x, diff, borrow_out, v0 - v1) assert diff == v1 diff, borrow_out = sub_64(v0, v1, u64(1)) - // C.printf("x:%08x [%llu,%llu] %08x\n", u64(i) << x, diff, borrow_out, v0 - v1) assert diff == (v1 - 1) assert borrow_out == u64(0) diff, borrow_out = sub_64(v1, v0, u64(1)) - // C.printf("x:%08x [%llu,%llu] %08x\n",u64(i) << x, diff, borrow_out, v1 - v0) assert borrow_out == u64(1) } +} - // - // --- mul --- - // - +fn test_mul() { + mut i := 0 // 32 bit i = 1 for x in 0 .. 32 { @@ -295,20 +266,19 @@ fn test_bits() { v0 := u64(i) << x v1 := v0 - 1 hi, lo := mul_64(v0, v1) - // C.printf("v0: %llu v1: %llu [%llu,%llu] tt: %llu\n", v0, v1, hi, lo, (v0 >> 32) * (v1 >> 32)) - assert (hi & 0xFFFF_FFFF_0000_0000) == (((v0 >> 32) * (v1 >> 32)) & 0xFFFF_FFFF_0000_0000) - assert (lo & 0x0000_0000_FFFF_FFFF) == (((v0 & 0x0000_0000_FFFF_FFFF) * (v1 & 0x0000_0000_FFFF_FFFF)) & 0x0000_0000_FFFF_FFFF) + exp_hi, exp_lo := mul_64_default(v0, v1) + assert hi == exp_hi + assert lo == exp_lo v2 := u64(x) h, l := mul_add_64(v0, v1, v2) - assert (h & 0xFFFF_FFFF_0000_0000) == (((v0 >> 32) * (v1 >> 32)) & 0xFFFF_FFFF_0000_0000) - assert (l & 0x0000_0000_FFFF_FFFF) == (( - (v0 & 0x0000_0000_FFFF_FFFF) * (v1 & 0x0000_0000_FFFF_FFFF) + v2) & 0x0000_0000_FFFF_FFFF) + exp_h, exp_l := mul_add_64_default(v0, v1, v2) + assert h == exp_h + assert l == exp_l } +} - // - // --- div --- - // - +fn test_div() { + mut i := 0 // 32 bit i = 1 for x in 0 .. 31 { @@ -316,7 +286,6 @@ fn test_bits() { lo := hi - 1 y := u32(3) << x quo, rem := div_32(hi, lo, y) - // C.printf("[%08x_%08x] %08x (%08x,%08x)\n", hi, lo, y, quo, rem) tst := ((u64(hi) << 32) | u64(lo)) assert quo == (tst / u64(y)) assert rem == (tst % u64(y)) @@ -330,7 +299,6 @@ fn test_bits() { lo := u64(2) // hi - 1 y := u64(0x4000_0000_0000_0000) quo, rem := div_64(hi, lo, y) - // C.printf("[%016llx_%016llx] %016llx (%016llx,%016llx)\n", hi, lo, y, quo, rem) assert quo == u64(2) << (x + 1) _, rem1 := div_64(hi % y, lo, y) assert rem == rem1 @@ -346,3 +314,90 @@ fn test_div_64_edge_cases() { assert q == 0 assert r == 23 } + +fn test_randomized_arithmetic_properties() { + mut state := u64(0x9e3779b97f4a7c15) + for _ in 0 .. 2000 { + state = next_u64(state) + a64 := state + state = next_u64(state) + b64 := state + state = next_u64(state) + carry_in64 := state & 1 + sum64, carry_out64 := add_64(a64, b64, carry_in64) + tmp := a64 + b64 + expected_sum64 := tmp + carry_in64 + expected_carry64 := u64(tmp < a64) | u64(expected_sum64 < tmp) + assert sum64 == expected_sum64 + assert carry_out64 == expected_carry64 + + diff64, borrow_out64 := sub_64(a64, b64, carry_in64) + tmp_diff := a64 - b64 + expected_diff64 := tmp_diff - carry_in64 + expected_borrow64 := u64(a64 < b64) | u64(tmp_diff < carry_in64) + assert diff64 == expected_diff64 + assert borrow_out64 == expected_borrow64 + + mul_hi, mul_lo := mul_64(a64, b64) + exp_mul_hi, exp_mul_lo := mul_64_default(a64, b64) + assert mul_hi == exp_mul_hi + assert mul_lo == exp_mul_lo + + state = next_u64(state) + z64 := state + mul_add_hi, mul_add_lo := mul_add_64(a64, b64, z64) + exp_mul_add_hi, exp_mul_add_lo := mul_add_64_default(a64, b64, z64) + assert mul_add_hi == exp_mul_add_hi + assert mul_add_lo == exp_mul_add_lo + + state = next_u64(state) + mut y64 := state | 1 + state = next_u64(state) + mut hi64 := state + hi64 %= y64 + state = next_u64(state) + lo64 := state + quo64, rem64 := div_64(hi64, lo64, y64) + exp_quo64, exp_rem64 := div_64_default(hi64, lo64, y64) + assert quo64 == exp_quo64 + assert rem64 == exp_rem64 + assert rem64 == rem_64(hi64, lo64, y64) + + a32 := u32(a64) + b32 := u32(b64) + carry_in32 := u32(carry_in64) + sum32, carry_out32 := add_32(a32, b32, carry_in32) + expected32 := u64(a32) + u64(b32) + u64(carry_in32) + assert sum32 == u32(expected32) + assert carry_out32 == u32(expected32 >> 32) + + diff32, borrow_out32 := sub_32(a32, b32, carry_in32) + expected_diff32 := a32 - b32 - carry_in32 + expected_borrow32 := u32((~a32 & b32) | (~(a32 ^ b32) & expected_diff32)) >> 31 + assert diff32 == expected_diff32 + assert borrow_out32 == expected_borrow32 + + mut y32 := u32(y64) + if y32 == 0 { + y32 = 1 + } + state = next_u64(state) + hi32 := u32(state % u64(y32)) + state = next_u64(state) + lo32 := u32(state) + quo32, rem32 := div_32(hi32, lo32, y32) + numerator32 := (u64(hi32) << 32) | u64(lo32) + assert quo32 == u32(numerator32 / u64(y32)) + assert rem32 == u32(numerator32 % u64(y32)) + assert rem32 == rem_32(hi32, lo32, y32) + } +} + +// rem_32 and rem_64 panic when y == 0 (division by zero). This behavior is tested +// through the randomized property test which guards against y==0, and through manual +// verification. Direct panic tests are avoided to prevent test suite crashes. + +@[inline] +fn next_u64(state u64) u64 { + return state * u64(6364136223846793005) + u64(1442695040888963407) +} -- 2.39.5