diff --git a/bench/Cargo.toml b/bench/Cargo.toml index b33434e..84ed56c 100644 --- a/bench/Cargo.toml +++ b/bench/Cargo.toml @@ -12,6 +12,7 @@ num-traits = "0.2" uint = "0.9" rand = { version = "0.8", features = ["std", "std_rng"] } paste = "1.0" +bnum-old = { package = "bnum", version = "0.12.0", features = ["rand"]} [[bench]] name = "float" diff --git a/bench/benches/float.rs b/bench/benches/float.rs index 7071626..fee0889 100644 --- a/bench/benches/float.rs +++ b/bench/benches/float.rs @@ -1,10 +1,12 @@ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use rand::prelude::*; +use bnum::cast::CastFrom; mod unzip; const SAMPLE_SIZE: usize = 10000; +use bnum::{BIntD8, BUintD8}; use bnum::Float; type F256 = Float<32, 236>; @@ -14,6 +16,7 @@ type F32 = Float<4, 23>; type U256 = bnum::BUintD8<32>; type U1024 = bnum::BUintD8<128>; +type I1024 = bnum::BIntD8<128>; fn bench_fibs(c: &mut Criterion) { @@ -49,21 +52,27 @@ fn bench_fibs(c: &mut Criterion) { fn bench_add(c: &mut Criterion) { let mut group = c.benchmark_group("round"); let mut rng = rand::rngs::StdRng::seed_from_u64(0); + const N: usize = 18; let big_inputs = (0..SAMPLE_SIZE) - .map(|_| rng.gen::<(U1024, u32)>()); - // .map(|(a, b)| ( - // (F64::from_bits((a >> 1)), F64::from_bits((b >> 1))) - // )); - let big_inputs: Vec<_> = big_inputs.collect(); + .map(|_| rng.gen::<(BUintD8<{N*8}>, BUintD8<{N*8}>)>()) + .map(|(a, b)| ( + ((a, b), (unsafe { core::mem::transmute::<_, bnum_old::BUint>(a) }, unsafe { core::mem::transmute::<_, bnum_old::BUint>(b) })) + )); + let (inputs1, inputs2) = unzip::unzip2(big_inputs); // group.bench_with_input(BenchmarkId::new("Recursive", "new"), &big_inputs, |b, inputs| b.iter(|| { // for a in inputs.iter().cloned() { // let _ = black_box(a).floor(); // } // })); - group.bench_with_input(BenchmarkId::new("Iterative", "d64"), &big_inputs, |b, inputs| b.iter(|| { + group.bench_with_input(BenchmarkId::new("Iterative", "d8"), &inputs1, |b, inputs| b.iter(|| { + inputs.iter().cloned().for_each(|(a, b)| { + let _ = black_box(a & b); + }) + })); + group.bench_with_input(BenchmarkId::new("Iterative", "d64"), &inputs2, |b, inputs| b.iter(|| { inputs.iter().cloned().for_each(|(a, b)| { - let _ = black_box((a).trailing_ones()); + let _ = black_box(a & b); }) })); group.finish(); diff --git a/src/bint/overflowing.rs b/src/bint/overflowing.rs index f73240a..f9ea90e 100644 --- a/src/bint/overflowing.rs +++ b/src/bint/overflowing.rs @@ -11,6 +11,7 @@ impl BIntD8 { #[must_use = doc::must_use_op!()] #[inline] pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { + // TODO: can use u128 let mut out = Self::ZERO; let mut carry = false; @@ -47,6 +48,7 @@ impl BIntD8 { #[must_use = doc::must_use_op!()] #[inline] pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { + // TODO: can use u128 let mut out = Self::ZERO; let mut borrow = false; @@ -198,6 +200,8 @@ impl BIntD8 { #[must_use = doc::must_use_op!()] #[inline] pub const fn overflowing_neg(mut self) -> (Self, bool) { + // TODO: can use u128 + // this is faster than self.not().overflowing_add(Self::ONE); let mut i = 0; while i < N - 1 { let (s, o) = (!self.bits.digits[i]).overflowing_add(1); // TODO: use overflowing add on signed integer digit instead @@ -212,7 +216,7 @@ impl BIntD8 { } i += 1; } - let (s, o) = (!self.bits.digits[i] as digit::SignedDigit).overflowing_add(1); + let (s, o) = (self.bits.digits[i] as digit::SignedDigit).overflowing_neg(); self.bits.digits[i] = s as Digit; (self, o) } diff --git a/src/buint/bigint_helpers.rs b/src/buint/bigint_helpers.rs index 1525ada..0d2bd12 100644 --- a/src/buint/bigint_helpers.rs +++ b/src/buint/bigint_helpers.rs @@ -10,6 +10,7 @@ impl BUintD8 { #[must_use = doc::must_use_op!()] #[inline] pub const fn widening_mul(self, rhs: Self) -> (Self, Self) { + // TODO: can use u128 let mut low = Self::ZERO; let mut high = Self::ZERO; let mut carry: Digit; diff --git a/src/buint/cast.rs b/src/buint/cast.rs index 6f9c77c..f0df6de 100644 --- a/src/buint/cast.rs +++ b/src/buint/cast.rs @@ -30,6 +30,15 @@ macro_rules! buint_as_int { #[must_use = doc::must_use_op!()] #[inline] fn cast_from(from: BUintD8) -> Self { + const BYTES: usize = <$int>::BITS as usize / 8; + // let low_digits: [u8; BYTES] = unsafe { *(from.digits.as_ptr() as *const _) }; + let mut bytes = [0u8; BYTES]; + let mut i = 0; + while i < BYTES && i < N { + bytes[i] = from.digits[i]; + i += 1; + } + return Self::from_le_bytes(bytes); let mut out = 0; let mut i = 0; while i << crate::digit::BIT_SHIFT < <$int>::BITS as usize && i < N { @@ -225,62 +234,3 @@ mod tests { crate::int::cast::tests!(utest); } - -macro_rules! buint_as_different_digit_bigint { - (BUintD8: ident, BIntD8: ident, Digit: ident; $(($OtherBUint: ident, $OtherDigit: ident)), *) => { - $( - impl crate::cast::CastFrom<$OtherBUint> for BUintD8 { - #[must_use = doc::must_use_op!()] - #[inline] - fn cast_from(from: $OtherBUint) -> Self { - let mut out = Self::ZERO; - if Digit::BITS < $OtherDigit::BITS { - const DIVIDE_COUNT: usize = ($OtherDigit::BITS / Digit::BITS) as usize; - let stop_index: usize = if <$OtherBUint>::BITS > >::BITS { - N - } else { - M * DIVIDE_COUNT - }; - let mut i = 0; - while i < stop_index { - let wider_digit = from.digits[i / DIVIDE_COUNT]; - let mini_shift = i % DIVIDE_COUNT; - let digit = (wider_digit >> (mini_shift << digit::BIT_SHIFT)) as Digit; - out.digits[i] = digit; - i += 1; - } - } else { - const DIVIDE_COUNT: usize = (Digit::BITS / $OtherDigit::BITS) as usize; - let stop_index: usize = if <$OtherBUint>::BITS > >::BITS { - N * DIVIDE_COUNT - } else { - M - }; - let mut current_digit: Digit = 0; - let mut i = 0; - while i < stop_index { - let mini_shift = i % DIVIDE_COUNT; - current_digit |= (from.digits[i] as Digit) << (mini_shift << digit::$OtherDigit::BIT_SHIFT); - if mini_shift == DIVIDE_COUNT - 1 || i == stop_index - 1 { - out.digits[i / DIVIDE_COUNT] = current_digit; - current_digit = 0; - } - i += 1; - } - } - out - } - } - - impl crate::cast::CastFrom<$OtherBUint> for BIntD8 { - #[must_use = doc::must_use_op!()] - #[inline] - fn cast_from(from: $OtherBUint) -> Self { - Self::from_bits(BUintD8::cast_from(from)) - } - } - )* - } -} - -pub(crate) use buint_as_different_digit_bigint; diff --git a/src/buint/checked.rs b/src/buint/checked.rs index 29d3147..b0656c1 100644 --- a/src/buint/checked.rs +++ b/src/buint/checked.rs @@ -36,6 +36,7 @@ impl BUintD8 { } pub(crate) const fn div_rem_digit(self, rhs: Digit) -> (Self, Digit) { + // TODO: can use u128 let mut out = Self::ZERO; let mut rem: Digit = 0; let mut i = N; diff --git a/src/buint/const_trait_fillers.rs b/src/buint/const_trait_fillers.rs index 4655276..498f485 100644 --- a/src/buint/const_trait_fillers.rs +++ b/src/buint/const_trait_fillers.rs @@ -8,17 +8,32 @@ use core::cmp::Ordering; impl BUintD8 { #[inline] pub const fn bitand(self, rhs: Self) -> Self { + // TODO: can use u128 let mut out = Self::ZERO; let mut i = 0; while i < N { out.digits[i] = self.digits[i] & rhs.digits[i]; i += 1; } + return out; + while i < N { + let u128_a = super::u128_from_digits(&self.digits, i); + let u128_b = super::u128_from_digits(&rhs.digits, i); + let u128_out = u128_a & u128_b; + let out_bytes = u128_out.to_le_bytes(); + let mut j = 0; + while j < 16 { + out.digits[i + j] = out_bytes[j]; + j += 1; + } + i += 16; + } out } #[inline] pub const fn bitor(self, rhs: Self) -> Self { + // TODO: can use u128 let mut out = Self::ZERO; let mut i = 0; while i < N { @@ -30,6 +45,7 @@ impl BUintD8 { #[inline] pub const fn bitxor(self, rhs: Self) -> Self { + // TODO: can use u128 let mut out = Self::ZERO; let mut i = 0; while i < N { @@ -41,6 +57,7 @@ impl BUintD8 { #[inline] pub const fn not(self) -> Self { + // TODO: can use u128 let mut out = Self::ZERO; let mut i = 0; while i < N { @@ -52,6 +69,7 @@ impl BUintD8 { #[inline] pub const fn eq(&self, other: &Self) -> bool { + // TODO: can use u128 let mut i = 0; while i < N { if self.digits[i] != other.digits[i] { @@ -69,6 +87,7 @@ impl BUintD8 { #[inline] pub const fn cmp(&self, other: &Self) -> Ordering { + // TODO: can use u128 let mut i = N; while i > 0 { i -= 1; diff --git a/src/buint/consts.rs b/src/buint/consts.rs index 31138ab..64aac8d 100644 --- a/src/buint/consts.rs +++ b/src/buint/consts.rs @@ -31,4 +31,7 @@ impl BUintD8 { pub const ZERO: Self = Self::MIN; pos_const!(ONE 1, TWO 2, THREE 3, FOUR 4, FIVE 5, SIX 6, SEVEN 7, EIGHT 8, NINE 9, TEN 10); + + pub(crate) const U128_DIGITS: usize = Self::BITS as usize / 128; + pub(crate) const U128_DIGIT_REMAINDER: usize = Self::BITS as usize % 128; } diff --git a/src/buint/div.rs b/src/buint/div.rs index 5221b02..55ab9a5 100644 --- a/src/buint/div.rs +++ b/src/buint/div.rs @@ -4,6 +4,7 @@ use crate::{digit, Digit}; impl BUintD8 { pub(crate) const fn basecase_div_rem(self, mut v: Self, n: usize) -> (Self, Self) { + // TODO: can use u128 // The Art of Computer Programming Volume 2 by Donald Knuth, Section 4.3.1, Algorithm D let mut q = Self::ZERO; diff --git a/src/buint/fmt.rs b/src/buint/fmt.rs index dd5594e..5dee46d 100644 --- a/src/buint/fmt.rs +++ b/src/buint/fmt.rs @@ -1,42 +1,35 @@ use super::BUintD8; -use crate::{digit, Digit}; -use alloc::string::String; -use core::fmt::Write; use core::fmt::{Binary, Debug, Display, Formatter, LowerExp, LowerHex, Octal, UpperExp, UpperHex}; -macro_rules! fmt_method { - ($format: expr, $format_pad: expr, $pad: expr, $prefix: expr) => { - #[inline] - fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { - let mut format_string = String::new(); - for digit in self.digits.iter().rev() { - if format_string.is_empty() { - if digit != &0 { - write!(format_string, $format, digit)?; - } - } else { - write!(format_string, $format_pad, digit, $pad)?; - } - } - f.pad_integral( - true, - $prefix, - if format_string.is_empty() { - "0" - } else { - &format_string - }, - ) - } - }; -} impl Binary for BUintD8 { - /*#[inline] + #[inline] fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { f.pad_integral(true, "0b", &self.to_str_radix(2)) - }*/ - fmt_method!("{:b}", "{:01$b}", digit::BITS as usize, "0b"); + } +} + +impl LowerHex for BUintD8 { + #[inline] + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { + f.pad_integral(true, "0x", &self.to_str_radix(16)) + } +} + +impl UpperHex for BUintD8 { + #[inline] + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { + let mut s = self.to_str_radix(16); + s.make_ascii_uppercase(); + f.pad_integral(true, "0x", &s) + } +} + +impl Octal for BUintD8 { + #[inline] + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { + f.pad_integral(true, "0o", &self.to_str_radix(8)) + } } impl Debug for BUintD8 { @@ -53,49 +46,37 @@ impl Display for BUintD8 { } } -macro_rules! exp_fmt { - ($e: expr) => { - #[inline] - fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { - let decimal_str = self.to_str_radix(10); - let buf = if decimal_str == "0" { - format!("{}{}0", 0, $e) +impl BUintD8 { + #[inline] + fn exp_fmt(&self, f: &mut Formatter, e: &str) -> core::fmt::Result { + let decimal_str = self.to_str_radix(10); + let buf = if decimal_str == "0" { + format!("{}{}0", 0, e) + } else { + let exp = decimal_str.len() - 1; + let decimal_str = decimal_str.trim_end_matches('0'); + if decimal_str.len() == 1 { + format!("{}{}{}", &decimal_str[0..1], e, exp) } else { - let exp = decimal_str.len() - 1; - let decimal_str = decimal_str.trim_end_matches('0'); - if decimal_str.len() == 1 { - format!("{}{}{}", &decimal_str[0..1], $e, exp) - } else { - format!("{}.{}{}{}", &decimal_str[0..1], &decimal_str[1..], $e, exp) - } - }; - f.pad_integral(true, "", &buf) - } - }; + format!("{}.{}{}{}", &decimal_str[0..1], &decimal_str[1..], e, exp) + } + }; + f.pad_integral(true, "", &buf) + } } impl LowerExp for BUintD8 { - exp_fmt!("e"); -} - -impl LowerHex for BUintD8 { - fmt_method!("{:x}", "{:01$x}", digit::HEX_PADDING, "0x"); -} - -impl Octal for BUintD8 { #[inline] - fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { - let string = self.to_str_radix(8); - f.pad_integral(true, "0o", &string) + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + self.exp_fmt(f, "e") } } impl UpperExp for BUintD8 { - exp_fmt!("E"); -} - -impl UpperHex for BUintD8 { - fmt_method!("{:X}", "{:01$X}", digit::HEX_PADDING, "0x"); + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + self.exp_fmt(f, "E") + } } #[cfg(test)] diff --git a/src/buint/mod.rs b/src/buint/mod.rs index 667fcdc..fe023b4 100644 --- a/src/buint/mod.rs +++ b/src/buint/mod.rs @@ -47,6 +47,12 @@ pub struct BUintD8 { #[cfg(feature = "zeroize")] impl zeroize::DefaultIsZeroes for BUintD8 {} +pub const fn u128_from_digits(digits: &[Digit], start_index: usize) -> u128 { + let i = start_index; + let bytes = [digits[i], digits[i + 1], digits[i + 2], digits[i + 3], digits[i + 4], digits[i + 5], digits[i + 6], digits[i + 7], digits[i + 8], digits[i + 9], digits[i + 10], digits[i + 11], digits[i + 12], digits[i + 13], digits[i + 14], digits[i + 15]]; + u128::from_le_bytes(bytes) // NOTE: need to change if using ne byte representation +} + impl BUintD8 { #[doc = doc::count_ones!(U 1024)] #[must_use = doc::must_use_op!()] @@ -65,19 +71,14 @@ impl BUintD8 { #[must_use = doc::must_use_op!()] #[inline] pub const fn count_zeros(self) -> ExpType { - let mut zeros = 0; - let mut i = 0; - while i < N { - zeros += self.digits[i].count_zeros() as ExpType; - i += 1; - } - zeros + Self::BITS - self.count_ones() } #[doc = doc::leading_zeros!(U 1024)] #[must_use = doc::must_use_op!()] #[inline] pub const fn leading_zeros(self) -> ExpType { + // don't need to use larger digits, the compiler optimises this code well let mut zeros = 0; let mut i = N; while i > 0 { @@ -95,6 +96,7 @@ impl BUintD8 { #[must_use = doc::must_use_op!()] #[inline] pub const fn trailing_zeros(self) -> ExpType { + // TODO: can use u128. slower than leading_zeros using naive method let mut zeros = 0; let mut i = 0; while i < N { @@ -112,6 +114,7 @@ impl BUintD8 { #[must_use = doc::must_use_op!()] #[inline] pub const fn leading_ones(self) -> ExpType { + // don't need to use larger digits, the compiler optimises this code well let mut ones = 0; let mut i = N; while i > 0 { @@ -129,6 +132,7 @@ impl BUintD8 { #[must_use = doc::must_use_op!()] #[inline] pub const fn trailing_ones(self) -> ExpType { + // TODO: can use u128. slower than leading_ones using naive method let mut ones = 0; let mut i = 0; while i < N { @@ -151,6 +155,7 @@ impl BUintD8 { #[inline] const unsafe fn rotate_digits_left(self, n: usize) -> Self { + // TODO: can use u128 let mut out = Self::ZERO; let mut i = n; while i < N { @@ -169,6 +174,7 @@ impl BUintD8 { #[inline] const unsafe fn unchecked_rotate_left(self, rhs: ExpType) -> Self { + // TODO: can use u128 let digit_shift = (rhs >> digit::BIT_SHIFT) as usize; let bit_shift = rhs & digit::BITS_MINUS_1; @@ -214,6 +220,7 @@ impl BUintD8 { #[must_use = doc::must_use_op!()] #[inline] pub const fn swap_bytes(self) -> Self { + // TODO: can use u128 let mut uint = Self::ZERO; let mut i = 0; while i < N { @@ -227,6 +234,7 @@ impl BUintD8 { #[must_use = doc::must_use_op!()] #[inline] pub const fn reverse_bits(self) -> Self { + // TODO: can use u128 let mut uint = Self::ZERO; let mut i = 0; while i < N { @@ -273,6 +281,7 @@ impl BUintD8 { #[must_use] #[inline] pub const fn is_power_of_two(self) -> bool { + // TODO: can use u128 let mut i = 0; let mut ones = 0; while i < N { @@ -385,6 +394,7 @@ impl BUintD8 { impl BUintD8 { #[inline] pub(crate) const unsafe fn unchecked_shl_internal(self, rhs: ExpType) -> Self { + // TODO: can use u128 let mut out = BUintD8::ZERO; let digit_shift = (rhs >> digit::BIT_SHIFT) as usize; let bit_shift = rhs & digit::BITS_MINUS_1; @@ -419,6 +429,7 @@ impl BUintD8 { self, rhs: ExpType, ) -> Self { + // TODO: can use u128 let mut out = if NEG { BUintD8::MAX } else { BUintD8::ZERO }; let digit_shift = (rhs >> digit::BIT_SHIFT) as usize; let bit_shift = rhs & digit::BITS_MINUS_1; @@ -479,7 +490,7 @@ impl BUintD8 { let digit = &mut self.digits[index as usize >> digit::BIT_SHIFT]; let shift = index & digit::BITS_MINUS_1; if value { - *digit |= (1 << shift); + *digit |= 1 << shift; } else { *digit &= !(1 << shift); } @@ -546,9 +557,10 @@ impl BUintD8 { #[must_use] #[inline] pub const fn is_zero(&self) -> bool { + // TODO: can use u128 let mut i = 0; while i < N { - if (&self.digits)[i] != 0 { + if self.digits[i] != 0 { return false; } i += 1; @@ -560,6 +572,7 @@ impl BUintD8 { #[must_use] #[inline] pub const fn is_one(&self) -> bool { + // TODO: can use u128 if N == 0 || self.digits[0] != 1 { return false; } @@ -575,6 +588,7 @@ impl BUintD8 { #[inline] pub(crate) const fn last_digit_index(&self) -> usize { + // TODO: can use u128 let mut index = 0; let mut i = 1; while i < N { @@ -633,6 +647,7 @@ impl<'a, const N: usize> Sum<&'a Self> for BUintD8 { #[cfg(any(test, feature = "quickcheck"))] impl quickcheck::Arbitrary for BUintD8 { fn arbitrary(g: &mut quickcheck::Gen) -> Self { + // TODO: can use u128 let mut out = Self::ZERO; for digit in out.digits.iter_mut() { *digit = ::arbitrary(g); diff --git a/src/buint/mul.rs b/src/buint/mul.rs index 8bed84f..cc03098 100644 --- a/src/buint/mul.rs +++ b/src/buint/mul.rs @@ -4,6 +4,7 @@ use crate::{digit, Digit}; impl BUintD8 { #[inline] pub(super) const fn long_mul(self, rhs: Self) -> (Self, bool) { + // TODO: can use u128 let mut overflow = false; let mut out = Self::ZERO; let mut carry: Digit; diff --git a/src/buint/ops.rs b/src/buint/ops.rs index f615d94..f35423b 100644 --- a/src/buint/ops.rs +++ b/src/buint/ops.rs @@ -11,6 +11,7 @@ impl Add for BUintD8 { #[inline] fn add(self, rhs: Digit) -> Self { + // TODO: can use u128 let mut out = self; let result = digit::carrying_add(out.digits[0], rhs, false); out.digits[0] = result.0; diff --git a/src/buint/overflowing.rs b/src/buint/overflowing.rs index 30bd286..02431f5 100644 --- a/src/buint/overflowing.rs +++ b/src/buint/overflowing.rs @@ -9,6 +9,7 @@ impl BUintD8 { #[must_use = doc::must_use_op!()] #[inline] pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { + // TODO: can use u128 let mut out = Self::ZERO; let mut carry = false; let mut i = 0; @@ -33,6 +34,7 @@ impl BUintD8 { #[must_use = doc::must_use_op!()] #[inline] pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { + // TODO: can use u128 let mut out = Self::ZERO; let mut borrow = false; let mut i = 0; diff --git a/src/buint/radix.rs b/src/buint/radix.rs index 797aefa..b788094 100644 --- a/src/buint/radix.rs +++ b/src/buint/radix.rs @@ -213,6 +213,7 @@ impl BUintD8 { radix: u32, leading_sign: bool, ) -> Result { + // TODO: can use u128 if leading_sign && buf.len() == 1 { return Err(ParseIntError { kind: IntErrorKind::InvalidDigit, @@ -480,6 +481,7 @@ impl BUintD8 { /// assert_eq!(n.to_radix_le(250), digits); /// ``` pub fn to_radix_le(&self, radix: u32) -> Vec { + // TODO: can use u128 if self.is_zero() { vec![0] } else if radix.is_power_of_two() { @@ -504,6 +506,7 @@ impl BUintD8 { } fn to_bitwise_digits_le(self, bits: u8) -> Vec { + // TODO: can use u128 let last_digit_index = self.last_digit_index(); let mask: Digit = (1 << bits) - 1; let digits_per_big_digit = digit::BITS_U8 / bits; @@ -526,6 +529,7 @@ impl BUintD8 { } fn to_inexact_bitwise_digits_le(self, bits: u8) -> Vec { + // TODO: can use u128 let mask: Digit = (1 << bits) - 1; let digits = div_ceil(self.bits(), bits as ExpType); let mut out = Vec::with_capacity(digits as usize);