// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. module bits fn C._umul128(x u64, y u64, result_hi &u64) u64 fn C._addcarry_u64(carry_in u8, a u64, b u64, out &u64) u8 fn C._udiv128(hi u64, lo u64, y u64, rem &u64) u64 // mul_64 returns the 128-bit product of x and y: (hi, lo) = x * y // with the product bits' upper half returned in hi and the lower // half returned in lo. // // This function's execution time does not depend on the inputs. @[inline] pub fn mul_64(x u64, y u64) (u64, u64) { mut hi := y mut lo := x $if msvc { lo = C._umul128(x, y, &hi) } $else { asm amd64 { mulq rdx ; +a (lo) +d (hi) ; ; cc } } return hi, lo } // mul_add_64 returns the 128-bit result of x * y + z: (hi, lo) = x * y + z // with the result bits' upper half returned in hi and the lower // half returned in lo. @[inline] pub fn mul_add_64(x u64, y u64, z u64) (u64, u64) { mut hi := y mut lo := x $if msvc { lo = C._umul128(x, y, &hi) carry := C._addcarry_u64(0, lo, z, &lo) hi += carry } $else { asm amd64 { mulq rdx addq rax, z adcq rdx, 0 ; +a (lo) +d (hi) ; r (z) ; cc } } return hi, lo } // 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). @[inline] pub fn div_64(hi u64, lo u64, y1 u64) (u64, u64) { mut y := y1 if y == 0 { panic(overflow_error) } if y <= hi { panic(overflow_error) } mut quo := lo mut rem := hi $if msvc { quo = C._udiv128(hi, lo, y, &rem) } $else { asm amd64 { div y ; +a (quo) +d (rem) ; r (y) ; cc } } return quo, rem }