use crate::BitCount;
#[allow(dead_code)]
pub(crate) trait UnsignedUtilities: Sized {
fn uaddc(r: &mut Self, a: Self, b: Self, carry: &mut u8);
fn usubb(r: &mut Self, a: Self, b: Self, borrow: &mut u8);
fn uadd_overflow(r: &mut Self, a: Self, b: Self) -> bool;
fn usub_overflow(r: &mut Self, a: Self, b: Self) -> bool;
fn umul_overflow(r: &mut Self, a: Self, b: Self) -> bool;
fn bit_length(value: Self) -> u8;
fn clz(value: Self) -> u8;
fn has_double_exact(value: Self) -> bool;
fn div_ceil_(x: Self, y: Self) -> Self;
fn ilog2_(v: Self) -> u8;
fn ilog_(v: Self, base: Self) -> u32;
fn ilog10_(v: Self) -> u32;
fn isqrt_(self) -> Self;
}
macro_rules! impl_unsigned_utilities {
($($t:ty),*) => {
$(
impl UnsignedUtilities for $t {
fn uaddc(r: &mut Self, a: Self, b: Self, carry: &mut u8) {
let temp = a.wrapping_add(*carry as $t);
*r = b.wrapping_add(temp);
*carry = if *r < b || temp < a { 1 } else { 0 };
}
fn usubb(r: &mut Self, a: Self, b: Self, borrow: &mut u8) {
let temp = a.wrapping_sub(b);
*r = temp.wrapping_sub(*borrow as $t);
*borrow = if *r > temp || temp > a { 1 } else { 0 };
}
fn uadd_overflow(r: &mut Self, a: Self, b: Self) -> bool {
*r = a.wrapping_add(b);
return *r < a;
}
fn usub_overflow(r: &mut Self, a: Self, b: Self) -> bool {
*r = a.wrapping_sub(b);
return a < b;
}
fn umul_overflow(r: &mut Self, a: Self, b: Self) -> bool {
*r = a.wrapping_mul(b);
return if a != 0 { true } else { false } && *r / a != b;
}
fn bit_length(number: Self) -> u8 {
if number == 0 {
1
} else {
<$t>::BITS as u8 - number.leading_zeros() as u8
}
}
fn ilog2_(v: Self) -> u8 {
if v <= 0 {
panic!("ilog2_(): value must be positive: {}", v)
}
Self::bit_length(v) - 1
}
fn ilog_(v: Self, base: Self) -> u32 {
if v < 1 || base < 2 {
panic!("ilog_(): value ({}) must be positive and base ({}) >= 2", v, base);
}
let mut ret = 0;
let mut cur = v;
while cur >= base {
cur /= base;
ret += 1;
}
ret
}
fn ilog10_(v: Self) -> u32 {
Self::ilog_(v, 10)
}
fn clz(v: Self) -> u8 {
let width = Self::BITS as u8;
width - Self::bit_length(v)
}
fn has_double_exact(value: Self) -> bool {
const DBL_MAX_INT: u64 = 0x20000000000000; if Self::BITS >= u64::BITS {
value <= DBL_MAX_INT as Self
} else {
value as u64 <= DBL_MAX_INT
}
}
fn div_ceil_(x: Self, y: Self) -> Self {
if x == 0 {
0
} else {
1 + (x - 1) / y
}
}
fn isqrt_(self) -> Self {
let x = self;
let mut l = 0;
let mut r = x;
let mut result = 0;
while l <= r {
let m = l + (r - l) / 2;
let (square, overflow) = m.overflowing_mul(m);
if !overflow && square <= x {
result = m;
l = m + 1; } else {
r = m - 1;
}
}
result
}
}
)*
}
}
impl_unsigned_utilities!(u8, u16, u32, u64, u128, usize);
pub(crate) const fn div_ceil_usize(x: usize, y: usize) -> usize {
if x == 0 {
0
} else {
1 + (x - 1) / y
}
}
#[allow(dead_code)]
pub(crate) const fn div_ceil_bitcount(x: BitCount, y: BitCount) -> BitCount {
if x == 0 {
0
} else {
1 + (x - 1) / y
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_uaddc() {
let a = 255u8;
let b = 1u8;
let mut carry = 0u8;
let mut result = 0;
u8::uaddc(&mut result, a, b, &mut carry);
assert_eq!(result, 0);
assert_eq!(carry, 1);
let a = 100u32;
let b = 200u32;
let mut carry = 0u8;
let mut result = 0;
u32::uaddc(&mut result, a, b, &mut carry);
assert_eq!(result, 300);
assert_eq!(carry, 0);
}
#[test]
fn test_usubb() {
let a = 0u8;
let b = 1u8;
let mut borrow = 0u8;
let mut result = 0;
u8::usubb(&mut result, a, b, &mut borrow);
assert_eq!(result, 255);
assert_eq!(borrow, 1);
let a = 300u32;
let b = 200u32;
let mut borrow = 0u8;
let mut result = 0;
u32::usubb(&mut result, a, b, &mut borrow);
assert_eq!(result, 100);
assert_eq!(borrow, 0);
}
#[test]
fn test_uadd_overflow() {
let a = u32::MAX;
let b = 1u32;
let mut result = 0;
let overflow = u32::uadd_overflow(&mut result, a, b);
assert_eq!(result, 0);
assert!(overflow);
assert_eq!(a.wrapping_add(b), result);
let a = 10u32;
let b = 20u32;
let mut result = 0;
let overflow = u32::uadd_overflow(&mut result, a, b);
assert_eq!(result, 30);
assert!(!overflow);
assert_eq!(a.wrapping_add(b), result);
}
#[test]
fn test_usub_overflow() {
let a = 10u32;
let b = 20u32;
let mut result = 0;
let overflow = u32::usub_overflow(&mut result, a, b);
assert_eq!(result, u32::MAX - 9);
assert!(overflow);
assert_eq!(a.wrapping_sub(b), result);
let a = 30u32;
let b = 10u32;
let mut result = 0;
let overflow = u32::usub_overflow(&mut result, a, b);
assert_eq!(result, 20);
assert!(!overflow);
assert_eq!(a.wrapping_sub(b), result);
}
#[test]
fn test_umul_overflow() {
let a = u32::MAX / 2 + 1;
let b = 2u32;
let mut result = 0;
let overflow = u32::umul_overflow(&mut result, a, b);
assert_eq!(result, 0);
assert!(overflow);
assert_eq!(a.wrapping_mul(b), result);
let a = 0u32;
let b = 123u32;
let mut result = 0;
let overflow = u32::umul_overflow(&mut result, a, b);
assert_eq!(result, 0);
assert!(!overflow);
assert_eq!(a.wrapping_mul(b), result);
}
}
#[cfg(test)]
mod test_other {
use super::*;
#[test]
fn test_bit_length() {
assert_eq!(u32::bit_length(0), 1);
assert_eq!(u32::bit_length(8), 4);
assert_eq!(u32::bit_length(255), 8);
}
#[test]
fn test_clz() {
assert_eq!(u32::clz(1), 31);
assert_eq!(u32::clz(8), 28);
}
#[test]
fn test_has_double_exact() {
assert!(u64::has_double_exact(0x20000000000000 - 1));
assert!(u64::has_double_exact(0x20000000000000));
assert!(!u64::has_double_exact(0x20000000000000 + 1));
}
#[test]
fn test_div_ceil() {
assert_eq!(u32::div_ceil_(5, 2), 3);
assert_eq!(u32::div_ceil_(10, 5), 2);
assert_eq!(u32::div_ceil_(11, 5), 3);
}
enum BinOp {
Add,
Sub,
Mul,
}
fn test_binop_overflow_(op: BinOp) {
use crate::util::test::{
get_seedable_rng, get_uniform_die, Distribution,
};
use crate::{Arbi, DDigit};
let (mut rng, _) = get_seedable_rng();
let die = get_uniform_die(DDigit::MIN, DDigit::MAX);
for _ in 0..i16::MAX {
let (a_in, b_in) = (die.sample(&mut rng), die.sample(&mut rng));
let (a, b) = (Arbi::from(a_in), Arbi::from(b_in));
let mut e: DDigit = 0;
match op {
BinOp::Add => {
let overflow = DDigit::uadd_overflow(&mut e, a_in, b_in);
if !overflow {
assert_eq!(a + b, e);
}
}
BinOp::Sub => {
let overflow = DDigit::usub_overflow(&mut e, a_in, b_in);
if !overflow {
assert_eq!(a - b, e);
}
}
BinOp::Mul => {
let overflow = DDigit::umul_overflow(&mut e, a_in, b_in);
if !overflow {
assert_eq!(a * b, e);
}
}
}
}
}
#[test]
fn test_binop_overflow() {
test_binop_overflow_(BinOp::Add);
test_binop_overflow_(BinOp::Sub);
test_binop_overflow_(BinOp::Mul);
}
}