From 13d4c9a8c1d724d6757b48856dfa977547bb0398 Mon Sep 17 00:00:00 2001 From: Charles Hubain Date: Sun, 7 Jul 2024 15:42:53 +0900 Subject: [PATCH] Implemented division and remainder --- src/auto.rs | 51 ++++- src/dynamic.rs | 296 ++++++++++++++++++++++++++++- src/fixed.rs | 401 +++++++++++++++++++++++++++++++++++++++- src/lib.rs | 26 ++- src/tests/arithmetic.rs | 28 ++- src/tests/binary.rs | 10 +- src/tests/mod.rs | 58 +++++- 7 files changed, 831 insertions(+), 39 deletions(-) diff --git a/src/auto.rs b/src/auto.rs index c0927a0..1a8e4f5 100644 --- a/src/auto.rs +++ b/src/auto.rs @@ -12,8 +12,8 @@ use std::cmp::Ordering; use std::fmt::{Binary, Display, LowerHex, Octal, UpperHex}; use std::ops::{ - Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Mul, MulAssign, - Not, Range, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, + Mul, MulAssign, Not, Range, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, }; use crate::dynamic::BVD; @@ -26,11 +26,11 @@ use crate::{BitVector, ConvertionError, Endianness}; // Choose a fixed BVF type which should match the size of BVD inside the enum #[cfg(target_pointer_width = "16")] -type BVP = BV32; +pub(crate) type BVP = BV32; #[cfg(target_pointer_width = "32")] -type BVP = BV64; +pub(crate) type BVP = BV64; #[cfg(target_pointer_width = "64")] -type BVP = BV128; +pub(crate) type BVP = BV128; // ------------------------------------------------------------------------------------------------ // Bit Vector automatic allocation implementation @@ -332,6 +332,33 @@ impl BitVector for BV { BV::Dynamic(b) => b.is_zero(), } } + + fn div_rem(&self, divisor: &B) -> (Self, Self) + where + Self: for<'a> TryFrom<&'a B, Error: std::fmt::Debug>, + { + assert!(!divisor.is_zero(), "Division by zero"); + let mut quotient = BV::zeros(self.len()); + let mut rem = self.clone(); + if divisor.significant_bits() > self.significant_bits() { + return (quotient, rem); + } + + let shift = self.significant_bits() - divisor.significant_bits(); + let mut divisor: BV = divisor.try_into().expect("should never fail"); + divisor.resize(self.len(), Bit::Zero); + divisor <<= shift; + + for i in (0..shift + 1).rev() { + if rem >= divisor { + rem -= &divisor; + quotient.set(i, Bit::One); + } + divisor >>= 1u32; + } + + (quotient, rem) + } } // ------------------------------------------------------------------------------------------------ @@ -683,7 +710,10 @@ macro_rules! impl_op_assign { } } - impl $trait<&BVF> for BV { + impl $trait<&BVF> for BV + where + u64: StaticCast, + { fn $method(&mut self, bvf: &BVF) { match self { BV::Fixed(b) => b.$method(bvf), @@ -692,7 +722,10 @@ macro_rules! impl_op_assign { } } - impl $trait> for BV { + impl $trait> for BV + where + u64: StaticCast, + { fn $method(&mut self, bvf: BVF) { match self { BV::Fixed(b) => b.$method(bvf), @@ -727,6 +760,8 @@ impl_op_assign!(BitXorAssign, bitxor_assign); impl_op_assign!(AddAssign, add_assign); impl_op_assign!(SubAssign, sub_assign); impl_op_assign!(MulAssign, mul_assign); +impl_op_assign!(DivAssign, div_assign); +impl_op_assign!(RemAssign, rem_assign); macro_rules! impl_op { ($trait:ident, $method:ident, $assign_trait:ident, $assign_method:ident) => { @@ -761,3 +796,5 @@ impl_op!(BitXor, bitxor, BitXorAssign, bitxor_assign); impl_op!(Add, add, AddAssign, add_assign); impl_op!(Sub, sub, SubAssign, sub_assign); impl_op!(Mul, mul, MulAssign, mul_assign); +impl_op!(Div, div, DivAssign, div_assign); +impl_op!(Rem, rem, RemAssign, rem_assign); diff --git a/src/dynamic.rs b/src/dynamic.rs index 91b5d02..2b951a6 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -10,8 +10,8 @@ use std::io::{Read, Write}; use std::iter::repeat; use std::mem::size_of; use std::ops::{ - Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Mul, MulAssign, - Not, Range, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, + Mul, MulAssign, Not, Range, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, }; use crate::auto::BV; @@ -513,6 +513,33 @@ impl BitVector for BVD { .iter() .all(|&x| x == 0) } + + fn div_rem(&self, divisor: &B) -> (Self, Self) + where + Self: for<'a> TryFrom<&'a B, Error: std::fmt::Debug>, + { + assert!(!divisor.is_zero(), "Division by zero"); + let mut quotient = BVD::zeros(self.length); + let mut rem = self.clone(); + if divisor.significant_bits() > self.significant_bits() { + return (quotient, rem); + } + + let shift = self.significant_bits() - divisor.significant_bits(); + let mut divisor: BVD = divisor.try_into().expect("should never fail"); + divisor.resize(self.length, Bit::Zero); + divisor <<= shift; + + for i in (0..shift + 1).rev() { + if rem >= divisor { + rem -= &divisor; + quotient.set(i, Bit::One); + } + divisor >>= 1u32; + } + + (quotient, rem) + } } // ------------------------------------------------------------------------------------------------ @@ -752,6 +779,15 @@ impl Ord for BVD { // BVD - Conversion traits // ------------------------------------------------------------------------------------------------ +impl From<&BVD> for BVD { + fn from(bvd: &BVD) -> Self { + BVD { + length: bvd.len(), + data: bvd.data.clone(), + } + } +} + macro_rules! impl_from_ints {($($st:ty),+) => { $( impl From<$st> for BVD { @@ -1345,3 +1381,259 @@ impl MulAssign for BVD { *self = Mul::mul(&*self, &rhs); } } + +// ------------------------------------------------------------------------------------------------ +// BVF - Division +// ------------------------------------------------------------------------------------------------ + +impl Div<&BVD> for &BVD { + type Output = BVD; + + fn div(self, rhs: &BVD) -> Self::Output { + self.div_rem(rhs).0 + } +} + +impl Div for &BVD { + type Output = BVD; + fn div(self, rhs: BVD) -> BVD { + self.div_rem(&rhs).0 + } +} + +impl Div<&BVD> for BVD { + type Output = BVD; + fn div(self, rhs: &BVD) -> BVD { + self.div_rem(rhs).0 + } +} + +impl Div for BVD { + type Output = BVD; + fn div(self, rhs: BVD) -> BVD { + self.div_rem(&rhs).0 + } +} + +impl Div<&BVF> for &BVD { + type Output = BVD; + + fn div(self, rhs: &BVF) -> BVD { + self.div_rem(rhs).0 + } +} + +impl Div> for &BVD { + type Output = BVD; + fn div(self, rhs: BVF) -> BVD { + self.div_rem(&rhs).0 + } +} + +impl Div<&BVF> for BVD { + type Output = BVD; + fn div(self, rhs: &BVF) -> BVD { + self.div_rem(rhs).0 + } +} + +impl Div> for BVD { + type Output = BVD; + fn div(self, rhs: BVF) -> BVD { + self.div_rem(&rhs).0 + } +} + +impl Div<&BV> for &BVD { + type Output = BVD; + fn div(self, rhs: &BV) -> BVD { + match rhs { + BV::Fixed(bvf) => self.div_rem(bvf).0, + BV::Dynamic(bvd) => self.div_rem(bvd).0, + } + } +} + +impl Div for &BVD { + type Output = BVD; + fn div(self, rhs: BV) -> BVD { + self.div(&rhs) + } +} + +impl Div<&BV> for BVD { + type Output = BVD; + fn div(self, rhs: &BV) -> BVD { + (&self).div(rhs) + } +} + +impl Div for BVD { + type Output = BVD; + fn div(self, rhs: BV) -> BVD { + (&self).div(&rhs) + } +} + +impl DivAssign<&BVD> for BVD { + fn div_assign(&mut self, rhs: &BVD) { + *self = Div::div(&*self, rhs); + } +} + +impl DivAssign for BVD { + fn div_assign(&mut self, rhs: BVD) { + *self = Div::div(&*self, &rhs); + } +} + +impl DivAssign<&BVF> for BVD { + fn div_assign(&mut self, rhs: &BVF) { + *self = Div::div(&*self, rhs); + } +} + +impl DivAssign> for BVD { + fn div_assign(&mut self, rhs: BVF) { + *self = Div::div(&*self, &rhs); + } +} + +impl DivAssign<&BV> for BVD { + fn div_assign(&mut self, rhs: &BV) { + *self = Div::div(&*self, rhs); + } +} + +impl DivAssign for BVD { + fn div_assign(&mut self, rhs: BV) { + *self = Div::div(&*self, &rhs); + } +} + +// ------------------------------------------------------------------------------------------------ +// BVF - Division +// ------------------------------------------------------------------------------------------------ + +impl Rem<&BVD> for &BVD { + type Output = BVD; + fn rem(self, rhs: &BVD) -> Self::Output { + self.div_rem(rhs).1 + } +} + +impl Rem for &BVD { + type Output = BVD; + fn rem(self, rhs: BVD) -> BVD { + self.div_rem(&rhs).1 + } +} + +impl Rem<&BVD> for BVD { + type Output = BVD; + fn rem(self, rhs: &BVD) -> BVD { + self.div_rem(rhs).1 + } +} + +impl Rem for BVD { + type Output = BVD; + fn rem(self, rhs: BVD) -> BVD { + self.div_rem(&rhs).1 + } +} + +impl Rem<&BVF> for &BVD { + type Output = BVD; + fn rem(self, rhs: &BVF) -> BVD { + self.div_rem(rhs).1 + } +} + +impl Rem> for &BVD { + type Output = BVD; + fn rem(self, rhs: BVF) -> BVD { + self.div_rem(&rhs).1 + } +} + +impl Rem<&BVF> for BVD { + type Output = BVD; + fn rem(self, rhs: &BVF) -> BVD { + self.div_rem(rhs).1 + } +} + +impl Rem> for BVD { + type Output = BVD; + fn rem(self, rhs: BVF) -> BVD { + self.div_rem(&rhs).1 + } +} + +impl Rem<&BV> for &BVD { + type Output = BVD; + fn rem(self, rhs: &BV) -> BVD { + match rhs { + BV::Fixed(bvf) => self.div_rem(bvf).1, + BV::Dynamic(bvd) => self.div_rem(bvd).1, + } + } +} + +impl Rem for &BVD { + type Output = BVD; + fn rem(self, rhs: BV) -> BVD { + self.rem(&rhs) + } +} + +impl Rem<&BV> for BVD { + type Output = BVD; + fn rem(self, rhs: &BV) -> BVD { + (&self).rem(rhs) + } +} + +impl Rem for BVD { + type Output = BVD; + fn rem(self, rhs: BV) -> BVD { + (&self).rem(&rhs) + } +} + +impl RemAssign<&BVD> for BVD { + fn rem_assign(&mut self, rhs: &BVD) { + *self = Rem::rem(&*self, rhs); + } +} + +impl RemAssign for BVD { + fn rem_assign(&mut self, rhs: BVD) { + *self = Rem::rem(&*self, &rhs); + } +} + +impl RemAssign<&BVF> for BVD { + fn rem_assign(&mut self, rhs: &BVF) { + *self = Rem::rem(&*self, rhs); + } +} + +impl RemAssign> for BVD { + fn rem_assign(&mut self, rhs: BVF) { + *self = Rem::rem(&*self, &rhs); + } +} + +impl RemAssign<&BV> for BVD { + fn rem_assign(&mut self, rhs: &BV) { + *self = Rem::rem(&*self, rhs); + } +} + +impl RemAssign for BVD { + fn rem_assign(&mut self, rhs: BV) { + *self = Rem::rem(&*self, &rhs); + } +} diff --git a/src/fixed.rs b/src/fixed.rs index 31a0da2..b882a59 100644 --- a/src/fixed.rs +++ b/src/fixed.rs @@ -3,8 +3,8 @@ use std::fmt; use std::iter::repeat; use std::mem::size_of; use std::ops::{ - Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Mul, MulAssign, - Not, Range, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, + Mul, MulAssign, Not, Range, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, }; use crate::auto::BV; @@ -502,6 +502,33 @@ where fn is_zero(&self) -> bool { self.data.iter().all(|&v| v == I::ZERO) } + + fn div_rem(&self, divisor: &B) -> (Self, Self) + where + Self: for<'a> TryFrom<&'a B, Error: fmt::Debug>, + { + assert!(!divisor.is_zero(), "Division by zero"); + let mut rem = *self; + let mut quotient = BVF::::zeros(self.length); + if divisor.significant_bits() > self.significant_bits() { + return (quotient, rem); + } + + let shift = self.significant_bits() - divisor.significant_bits(); + let mut divisor: BVF = divisor.try_into().expect("divisor should fit in Self"); + divisor.resize(self.length, Bit::Zero); + divisor <<= shift; + + for i in (0..shift + 1).rev() { + if rem >= divisor { + rem -= &divisor; + quotient.set(i, Bit::One); + } + divisor >>= 1u32; + } + + (quotient, rem) + } } // ------------------------------------------------------------------------------------------------ @@ -1456,3 +1483,373 @@ where *self = Mul::mul(&*self, &rhs); } } + +// ------------------------------------------------------------------------------------------------ +// BVF - Division +// ------------------------------------------------------------------------------------------------ + +impl Div<&BVF> for &BVF +where + I2: StaticCast, +{ + type Output = BVF; + fn div(self, rhs: &BVF) -> Self::Output { + self.div_rem(rhs).0 + } +} + +impl Div> for &BVF +where + I2: StaticCast, +{ + type Output = BVF; + fn div(self, rhs: BVF) -> Self::Output { + self.div_rem(&rhs).0 + } +} + +impl Div<&BVF> for BVF +where + I2: StaticCast, +{ + type Output = BVF; + fn div(self, rhs: &BVF) -> Self::Output { + self.div_rem(rhs).0 + } +} + +impl Div> for BVF +where + I2: StaticCast, +{ + type Output = BVF; + fn div(self, rhs: BVF) -> Self::Output { + self.div_rem(&rhs).0 + } +} + +impl Div<&BVD> for &BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn div(self, rhs: &BVD) -> Self::Output { + self.div_rem::(rhs).0 + } +} + +impl Div for &BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn div(self, rhs: BVD) -> Self::Output { + self.div_rem::(&rhs).0 + } +} + +impl Div<&BVD> for BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn div(self, rhs: &BVD) -> Self::Output { + self.div_rem::(rhs).0 + } +} + +impl Div for BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn div(self, rhs: BVD) -> Self::Output { + self.div_rem::(&rhs).0 + } +} + +impl Div<&BV> for &BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn div(self, rhs: &BV) -> Self::Output { + match rhs { + BV::Fixed(bvf) => self.div_rem::(bvf).0, + BV::Dynamic(bvd) => self.div_rem::(bvd).0, + } + } +} + +impl Div<&BV> for BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn div(self, rhs: &BV) -> Self::Output { + (&self).div(rhs) + } +} + +impl Div for &BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn div(self, rhs: BV) -> Self::Output { + self.div(&rhs) + } +} + +impl Div for BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn div(self, rhs: BV) -> Self::Output { + (&self).div(&rhs) + } +} + +impl DivAssign<&BVF> + for BVF +where + I1: StaticCast, + I2: StaticCast, +{ + fn div_assign(&mut self, rhs: &BVF) { + *self = Div::div(&*self, rhs); + } +} + +impl DivAssign> + for BVF +where + I1: StaticCast, + I2: StaticCast, +{ + fn div_assign(&mut self, rhs: BVF) { + *self = Div::div(&*self, &rhs); + } +} + +impl DivAssign<&BVD> for BVF +where + u64: StaticCast, +{ + fn div_assign(&mut self, rhs: &BVD) { + *self = Div::div(&*self, rhs); + } +} + +impl DivAssign for BVF +where + u64: StaticCast, +{ + fn div_assign(&mut self, rhs: BVD) { + *self = Div::div(&*self, &rhs); + } +} + +impl DivAssign<&BV> for BVF +where + u64: StaticCast, +{ + fn div_assign(&mut self, rhs: &BV) { + *self = Div::div(&*self, rhs); + } +} + +impl DivAssign for BVF +where + u64: StaticCast, +{ + fn div_assign(&mut self, rhs: BV) { + *self = Div::div(&*self, &rhs); + } +} + +// ------------------------------------------------------------------------------------------------ +// BVF - Remainder +// ------------------------------------------------------------------------------------------------ + +impl Rem<&BVF> for &BVF +where + I2: StaticCast, +{ + type Output = BVF; + fn rem(self, rhs: &BVF) -> Self::Output { + self.div_rem(rhs).1 + } +} + +impl Rem> for &BVF +where + I2: StaticCast, +{ + type Output = BVF; + fn rem(self, rhs: BVF) -> Self::Output { + self.div_rem(&rhs).1 + } +} + +impl Rem<&BVF> for BVF +where + I2: StaticCast, +{ + type Output = BVF; + fn rem(self, rhs: &BVF) -> Self::Output { + self.div_rem(rhs).1 + } +} + +impl Rem> for BVF +where + I2: StaticCast, +{ + type Output = BVF; + fn rem(self, rhs: BVF) -> Self::Output { + self.div_rem(&rhs).1 + } +} + +impl Rem<&BVD> for &BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn rem(self, rhs: &BVD) -> Self::Output { + self.div_rem::(rhs).1 + } +} + +impl Rem for &BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn rem(self, rhs: BVD) -> Self::Output { + self.div_rem::(&rhs).1 + } +} + +impl Rem<&BVD> for BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn rem(self, rhs: &BVD) -> Self::Output { + self.div_rem::(rhs).1 + } +} + +impl Rem for BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn rem(self, rhs: BVD) -> Self::Output { + self.div_rem::(&rhs).1 + } +} + +impl Rem<&BV> for &BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn rem(self, rhs: &BV) -> Self::Output { + match rhs { + BV::Fixed(bvf) => self.div_rem::(bvf).1, + BV::Dynamic(bvd) => self.div_rem::(bvd).1, + } + } +} + +impl Rem<&BV> for BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn rem(self, rhs: &BV) -> Self::Output { + (&self).rem(rhs) + } +} + +impl Rem for &BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn rem(self, rhs: BV) -> Self::Output { + self.rem(&rhs) + } +} + +impl Rem for BVF +where + u64: StaticCast, +{ + type Output = BVF; + fn rem(self, rhs: BV) -> Self::Output { + (&self).rem(&rhs) + } +} + +impl RemAssign<&BVF> + for BVF +where + I1: StaticCast, + I2: StaticCast, +{ + fn rem_assign(&mut self, rhs: &BVF) { + *self = Rem::rem(&*self, rhs); + } +} + +impl RemAssign> + for BVF +where + I1: StaticCast, + I2: StaticCast, +{ + fn rem_assign(&mut self, rhs: BVF) { + *self = Rem::rem(&*self, &rhs); + } +} + +impl RemAssign<&BVD> for BVF +where + u64: StaticCast, +{ + fn rem_assign(&mut self, rhs: &BVD) { + *self = Rem::rem(&*self, rhs); + } +} + +impl RemAssign for BVF +where + u64: StaticCast, +{ + fn rem_assign(&mut self, rhs: BVD) { + *self = Rem::rem(&*self, &rhs); + } +} + +impl RemAssign<&BV> for BVF +where + u64: StaticCast, +{ + fn rem_assign(&mut self, rhs: &BV) { + *self = Rem::rem(&*self, rhs); + } +} + +impl RemAssign for BVF +where + u64: StaticCast, +{ + fn rem_assign(&mut self, rhs: BV) { + *self = Rem::rem(&*self, &rhs); + } +} diff --git a/src/lib.rs b/src/lib.rs index 8ec08dc..20beddc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,6 +104,17 @@ pub trait BitVector: /// Will panic if there is not enough capacity and it is a fixed variant. fn ones(length: usize) -> Self; + /// Return the cpacity of the bit vector in bits. + fn capacity(&self) -> usize; + + /// Return the length of the bit vector in bits. + fn len(&self) -> usize; + + /// Return wether the bit vector is empty or not. + fn is_empty(&self) -> bool { + self.len() == 0 + } + /// Construct a bit vector from a binary string made of `'0'` and `'1'`. /// Return `None` if the string is invalid or exceed the maximum capacity. fn from_binary>(string: S) -> Result; @@ -202,16 +213,11 @@ pub trait BitVector: /// By convention, the empty bit vector is considered to be zero. fn is_zero(&self) -> bool; - /// Return the cpacity of the bit vector in bits. - fn capacity(&self) -> usize; - - /// Return the length of the bit vector in bits. - fn len(&self) -> usize; - - /// Return wether the bit vector is empty or not. - fn is_empty(&self) -> bool { - self.len() == 0 - } + /// Divide by another bit vector and return the quotient and the remainder. + /// Will panic if the divisor is zero. + fn div_rem(&self, divisor: &B) -> (Self, Self) + where + Self: for<'a> TryFrom<&'a B, Error: std::fmt::Debug>; fn iter(&self) -> BitIterator<'_, Self>; } diff --git a/src/tests/arithmetic.rs b/src/tests/arithmetic.rs index 6b0e63c..50e716b 100644 --- a/src/tests/arithmetic.rs +++ b/src/tests/arithmetic.rs @@ -4,16 +4,16 @@ mod add { use crate::tests::*; use crate::utils::StaticCast; - op_test_section!(Add::add, AddAssign::add_assign); + op_test_section!(Add::add, AddAssign::add_assign, { op_test_block }); } -mod mul { - use std::ops::{Mul, MulAssign}; +mod div { + use std::ops::{Div, DivAssign}; use crate::tests::*; use crate::utils::StaticCast; - op_test_section!(Mul::mul, MulAssign::mul_assign); + op_test_section!(Div::div, DivAssign::div_assign, { op_test_block2 }); } mod sub { @@ -22,5 +22,23 @@ mod sub { use crate::tests::*; use crate::utils::StaticCast; - op_test_section!(Sub::sub, SubAssign::sub_assign); + op_test_section!(Sub::sub, SubAssign::sub_assign, { op_test_block }); +} + +mod mul { + use std::ops::{Mul, MulAssign}; + + use crate::tests::*; + use crate::utils::StaticCast; + + op_test_section!(Mul::mul, MulAssign::mul_assign, { op_test_block }); +} + +mod rem { + use std::ops::{Rem, RemAssign}; + + use crate::tests::*; + use crate::utils::StaticCast; + + op_test_section!(Rem::rem, RemAssign::rem_assign, { op_test_block2 }); } diff --git a/src/tests/binary.rs b/src/tests/binary.rs index ba6cfd0..60e72f0 100644 --- a/src/tests/binary.rs +++ b/src/tests/binary.rs @@ -4,7 +4,9 @@ mod and { use crate::tests::*; use crate::utils::StaticCast; - op_test_section!(BitAnd::bitand, BitAndAssign::bitand_assign); + op_test_section!(BitAnd::bitand, BitAndAssign::bitand_assign, { + op_test_block + }); } mod not { @@ -49,7 +51,7 @@ mod or { use crate::tests::*; use crate::utils::StaticCast; - op_test_section!(BitOr::bitor, BitOrAssign::bitor_assign); + op_test_section!(BitOr::bitor, BitOrAssign::bitor_assign, { op_test_block }); } mod shift { @@ -147,5 +149,7 @@ mod xor { use crate::tests::*; use crate::utils::StaticCast; - op_test_section!(BitXor::bitxor, BitXorAssign::bitxor_assign); + op_test_section!(BitXor::bitxor, BitXorAssign::bitxor_assign, { + op_test_block + }); } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 30aff02..5119fb5 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -138,8 +138,33 @@ macro_rules! op_test_block { }; } +// Variant more suited for division and modulo but also test lhs and rhs of different sizes. +macro_rules! op_test_block2 { + ($lhs:ty, $rhs:ty, $op:path, $op_assign:path, $size:ident) => { + let modulo = BigInt::from(1u8) << $size; + let (bv1, bi1) = random_test_bv::<$lhs>($size); + let (mut bv2, mut bi2) = random_test_bv::<$rhs>(usize::min(1, $size)); + while bv2.is_zero() { + (bv2, bi2) = random_test_bv::<$rhs>(usize::min(1, $size)); + } + let reference = $op(&bi1, &bi2) % &modulo; + // Normal op + assert_eq!($op(&bv1, &bv2), reference); + assert_eq!($op(bv1.clone(), &bv2), reference); + assert_eq!($op(&bv1, bv2.clone()), reference); + assert_eq!($op(bv1.clone(), bv2.clone()), reference); + // Assign op + let mut bv3 = bv1.clone(); + $op_assign(&mut bv3, &bv2); + assert_eq!(bv3, reference); + bv3 = bv1.clone(); + $op_assign(&mut bv3, bv2); + assert_eq!(bv3, reference); + }; +} + macro_rules! op_test_section { - ($op:path, $op_assign:path) => { + ($op:path, $op_assign:path, {$($block:path),+}) => { fn bvf_bvf_inner< I1: Integer + StaticCast, I2: Integer + StaticCast, @@ -147,7 +172,9 @@ macro_rules! op_test_section { const N2: usize, >() { for size in 1..usize::min(BVF::::capacity(), BVF::::capacity()) { - op_test_block!(BVF, BVF, $op, $op_assign, size); + $( + $block!(BVF, BVF, $op, $op_assign, size); + )+ } } @@ -161,8 +188,10 @@ macro_rules! op_test_section { u64: StaticCast, { for size in 1..BVF::::capacity() { - op_test_block!(BVF, BVD, $op, $op_assign, size); - op_test_block!(BVD, BVF, $op, $op_assign, size); + $( + $block!(BVF, BVD, $op, $op_assign, size); + $block!(BVD, BVF, $op, $op_assign, size); + )+ } } @@ -176,8 +205,10 @@ macro_rules! op_test_section { u64: StaticCast, { for size in 1..BVF::::capacity() { - op_test_block!(BVF, BV, $op, $op_assign, size); - op_test_block!(BV, BVF, $op, $op_assign, size); + $( + $block!(BVF, BV, $op, $op_assign, size); + $block!(BV, BVF, $op, $op_assign, size); + )+ } } @@ -189,22 +220,28 @@ macro_rules! op_test_section { #[test] fn bvd_bvd() { for size in 1..512 { - op_test_block!(BVD, BVD, $op, $op_assign, size); + $( + $block!(BVD, BVD, $op, $op_assign, size); + )+ } } #[test] fn bvd_bv() { for size in 1..512 { - op_test_block!(BVD, BV, $op, $op_assign, size); - op_test_block!(BV, BVD, $op, $op_assign, size); + $( + $block!(BVD, BV, $op, $op_assign, size); + $block!(BV, BVD, $op, $op_assign, size); + )+ } } #[test] fn bv_bv() { for size in 1..512 { - op_test_block!(BV, BV, $op, $op_assign, size); + $( + $block!(BV, BV, $op, $op_assign, size); + )+ } } }; @@ -214,4 +251,5 @@ pub(crate) use bvf_bvf_inner_unroll; pub(crate) use bvf_inner_unroll; pub(crate) use bvf_inner_unroll_cap; pub(crate) use op_test_block; +pub(crate) use op_test_block2; pub(crate) use op_test_section;