Skip to content

Commit

Permalink
Merge pull request #20 from twiby/feat/set_to_mul
Browse files Browse the repository at this point in the history
biguint: add `set_to_mul` API to keep existing allocation when mul
  • Loading branch information
twiby authored Sep 11, 2024
2 parents c34cfe8 + e9caaf5 commit 6defe28
Show file tree
Hide file tree
Showing 15 changed files with 407 additions and 72 deletions.
6 changes: 6 additions & 0 deletions src/bigfloat/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ impl<T: Digit> BigFloat<T> {
Self::from(BigUint::<T>::new(val))
}

#[inline]
pub(crate) fn with_capacity(mut self, capcity: usize) -> Self {
self.int.uint.set_capacity(capcity);
self
}

/// Remove zero-digits at the beginning
fn simplify(&mut self) {
let nb_zeros: usize = self
Expand Down
51 changes: 39 additions & 12 deletions src/bigfloat/ops/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,25 @@ use std::ops::MulAssign;

use crate::traits::Digit;

impl<T: Digit> BigFloat<T> {
#[inline]
pub(crate) fn _set_to_mul(
&mut self,
a_sign: bool,
a_scale: isize,
a: &[T],
b_sign: bool,
b_scale: isize,
b: &[T],
) {
self.int._set_to_mul(a_sign, a, b_sign, b);
self.scale = a_scale + b_scale;
}
}

impl<T: Digit> MulAssign<T> for BigFloat<T> {
fn mul_assign(&mut self, other: T) {
*self *= BigFloat::<T>::new(other);
self.int *= other;
}
}
impl<T: Digit> MulAssign<&T> for BigFloat<T> {
Expand All @@ -28,36 +44,47 @@ impl<T: Digit> MulAssign<BigFloat<T>> for BigFloat<T> {
impl<T: Digit> Mul<T> for &BigFloat<T> {
type Output = BigFloat<T>;
fn mul(self, other: T) -> BigFloat<T> {
self * BigFloat::<T>::new(other)
let mut ret = self.clone();
ret *= other;
ret
}
}
impl<T: Digit> Mul<&T> for &BigFloat<T> {
type Output = BigFloat<T>;
fn mul(self, other: &T) -> BigFloat<T> {
self * *other
let mut ret = self.clone();
ret *= other;
ret
}
}
impl<T: Digit> Mul<T> for BigFloat<T> {
type Output = BigFloat<T>;
fn mul(self, other: T) -> BigFloat<T> {
&self * other
fn mul(mut self, other: T) -> BigFloat<T> {
self *= other;
self
}
}
impl<T: Digit> Mul<&T> for BigFloat<T> {
type Output = BigFloat<T>;
fn mul(self, other: &T) -> BigFloat<T> {
&self * other
fn mul(mut self, other: &T) -> BigFloat<T> {
self *= other;
self
}
}

impl<T: Digit> Mul<&BigFloat<T>> for &BigFloat<T> {
type Output = BigFloat<T>;
fn mul(self, other: &BigFloat<T>) -> BigFloat<T> {
let int = (&self.int * &other.int).into();
let mut ret = BigFloat {
int,
scale: self.scale + other.scale,
};
let mut ret = BigFloat::default()
.with_capacity((self.int.uint.val.len() + other.int.uint.val.len()) * T::NB_BITS);
ret._set_to_mul(
self.int.sign,
self.scale,
&self.int.uint.val,
other.int.sign,
other.scale,
&other.int.uint.val,
);
ret.simplify();
ret
}
Expand Down
9 changes: 9 additions & 0 deletions src/bigfloat/ops/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,15 @@ fn shift_coherence<T: Digit>() {
assert_eq!(&n1 << T::NB_BITS + 5, &n2 << T::NB_BITS + 5);
}

#[test_with(u32, u64)]
fn digit_mul<T: Digit>() {
let a = BigFloat::from(vec![T::ONE << 1, T::ONE << 1]);

let c = a * T::MAX;
assert_eq!(c.scale, 0);
assert_eq!(c.int.uint.val, vec![T::MAX - T::ONE, T::MAX, T::ONE]);
}

#[test_with(u32, u64)]
fn mul<T: Digit>() {
let a = BigFloat::from(vec![T::ONE, T::ONE]);
Expand Down
7 changes: 7 additions & 0 deletions src/bigint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ impl<T: Digit> BigInt<T> {
sign: val.is_positive(),
}
}

#[inline]
pub(crate) fn with_capacity(mut self, capacity: usize) -> Self {
self.uint.set_capacity(capacity);
self
}

pub fn from_unsigned(val: T) -> BigInt<T> {
BigInt::<T> {
uint: BigUint::<T>::new(val),
Expand Down
46 changes: 38 additions & 8 deletions src/bigint/ops/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,34 @@ use core::ops::{Mul, MulAssign};
use crate::traits::Digit;
use crate::{BigInt, BigUint};

impl<T: Digit> BigInt<T> {
/// Use this integer to store the multiplication of `a` and `b`
///
/// This is handy when one wants to keep allocations around to store
/// the result of a multiplication
///
/// ```
/// use twibint::BigInt;
///
/// let mut n = -BigInt::from(vec![u64::MAX; 4]);
/// let a = BigInt::from(-2i64);
/// let b = BigInt::from(-4i64);
///
/// n.set_to_mul(&a, &b);
/// assert_eq!(n, BigInt::from(8));
/// ```
#[inline]
pub fn set_to_mul(&mut self, a: &BigInt<T>, b: &BigInt<T>) {
self._set_to_mul(a.sign, &a.uint.val, b.sign, &b.uint.val);
}

#[inline]
pub(crate) fn _set_to_mul(&mut self, a_sign: bool, a: &[T], b_sign: bool, b: &[T]) {
self.uint._set_to_mul(a, b);
self.sign = a_sign == b_sign;
}
}

impl<T: Digit> MulAssign<T> for BigInt<T> {
fn mul_assign(&mut self, other: T) {
self.uint *= other;
Expand Down Expand Up @@ -42,23 +70,25 @@ impl<T: Digit> Mul<&T> for &BigInt<T> {
}
impl<T: Digit> Mul<T> for BigInt<T> {
type Output = BigInt<T>;
fn mul(self, other: T) -> BigInt<T> {
&self * other
fn mul(mut self, other: T) -> BigInt<T> {
self *= other;
self
}
}
impl<T: Digit> Mul<&T> for BigInt<T> {
type Output = BigInt<T>;
fn mul(self, other: &T) -> BigInt<T> {
&self * *other
fn mul(mut self, other: &T) -> BigInt<T> {
self *= other;
self
}
}
impl<T: Digit> Mul<&BigInt<T>> for &BigInt<T> {
type Output = BigInt<T>;
fn mul(self, other: &BigInt<T>) -> BigInt<T> {
BigInt::<T> {
uint: &self.uint * &other.uint,
sign: self.sign == other.sign,
}
let mut ret = BigInt::<T>::default()
.with_capacity((self.uint.val.len() + other.uint.val.len()) * T::NB_BITS);
ret._set_to_mul(self.sign, &self.uint.val, other.sign, &other.uint.val);
ret
}
}
impl<T: Digit> Mul<BigInt<T>> for BigInt<T> {
Expand Down
29 changes: 29 additions & 0 deletions src/bigint/ops/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,35 @@ fn mul_full_test<T: Digit>() {
);
}

#[test_with(u32, u64)]
fn mul_full_test_with_buffer<T: Digit>() {
let mut buff = BigInt::<T>::default();

buff.set_to_mul(
&BigInt::<T>::from("4294967295"),
&BigInt::<T>::from("4294967295"),
);
assert_eq!(buff, BigInt::<T>::from("18446744065119617025"));

buff.set_to_mul(
&BigInt::<T>::from("-4294967295"),
&BigInt::<T>::from("-4294967295"),
);
assert_eq!(buff, BigInt::<T>::from("18446744065119617025"));

buff.set_to_mul(
&BigInt::<T>::from("-4294967295"),
&BigInt::<T>::from("4294967295"),
);
assert_eq!(buff, BigInt::<T>::from("-18446744065119617025"));

buff.set_to_mul(
&BigInt::<T>::from("4294967295"),
&BigInt::<T>::from("-4294967295"),
);
assert_eq!(buff, BigInt::<T>::from("-18446744065119617025"));
}

#[test_with(u32, u64)]
fn div_rem_1<T: Digit>() {
let two = T::ONE + T::ONE;
Expand Down
9 changes: 4 additions & 5 deletions src/biguint/froms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,10 @@ impl<T: Digit> From<BigUint<T>> for String {
}

impl<T: Digit> From<Vec<T>> for BigUint<T> {
fn from(v: Vec<T>) -> BigUint<T> {
let v = match v.len() > 0 {
true => v,
false => vec![T::ZERO],
};
fn from(mut v: Vec<T>) -> BigUint<T> {
if v.len() == 0 {
v.push(T::ZERO);
}
let mut ret = BigUint::<T> { val: v };
ret.remove_leading_zeros();
ret
Expand Down
42 changes: 42 additions & 0 deletions src/biguint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,48 @@ impl<T: Digit> BigUint<T> {
BigUint { val: vec![val] }
}

/// Allocates for at least `capacity` bits, total.
///
/// ```
/// use twibint::BigUint;
/// let n = BigUint::<u64>::default().with_capacity(100);
/// assert!(n.capacity() >= 100);
/// ```
#[inline]
pub fn with_capacity(mut self, capacity: usize) -> BigUint<T> {
self.set_capacity(capacity);
self
}

/// Allocates for at least `capacity` bits, total.
///
/// ```
/// use twibint::BigUint;
/// let mut n = BigUint::<u64>::default();
/// n.set_capacity(100);
/// assert!(n.capacity() >= 100);
/// ```
#[inline]
pub fn set_capacity(&mut self, capacity: usize) {
if capacity > 0 {
let target_length = (capacity - 1) / T::NB_BITS + 1;
let reserve = target_length.max(self.val.len()) - self.val.len();
self.val.reserve(reserve);
}
}

/// Returns the number of bits this integer can store without reallocating
///
/// ```
/// use twibint::BigUint;
/// let n = BigUint::<u64>::default().with_capacity(100);
/// assert!(n.capacity() >= 100);
/// ```
#[inline]
pub fn capacity(&self) -> usize {
self.val.capacity() * T::NB_BITS
}

/// Returns the minimal number of bits necessary for a binary representation.
#[inline]
pub fn nb_bits(&self) -> usize {
Expand Down
4 changes: 2 additions & 2 deletions src/biguint/ops/implem_choices/add/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use crate::traits::{Digit, DoubleDigit};

#[cfg(feature = "unsafe")]
use super::u32_ptrs_aligned;
use super::u32_ptrs_aligned_2;
#[cfg(feature = "unsafe")]
use crate::traits::ToPtr;

Expand All @@ -17,7 +17,7 @@ pub(crate) fn add_assign<T: Digit>(rhs: &mut [T], lhs: &[T]) -> bool {
// Specifically for u32 digits, we accelerate by reinterpreting arrays as u64
#[cfg(feature = "unsafe")]
if let (Some(rhs_cast), Some(lhs_cast)) = (rhs.to_mut_ptr::<u32>(), lhs.to_ptr::<u32>()) {
if u32_ptrs_aligned(rhs_cast, lhs_cast) {
if u32_ptrs_aligned_2(rhs_cast, lhs_cast) {
// Case pointers correctly aligned: pretend they are u64
let size = lhs.len() / 2;
let carry: bool =
Expand Down
15 changes: 14 additions & 1 deletion src/biguint/ops/implem_choices/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,24 @@ mod sub;

pub(crate) use add::add_assign;
pub(crate) use mul::mul;
pub(crate) use mul::mul_assign_digit;
pub(crate) use rsub::rsub_assign;
pub(crate) use sub::sub_assign;

#[cfg(feature = "unsafe")]
pub(crate) fn u32_ptrs_aligned(a: *const u32, b: *const u32) -> bool {
pub(crate) fn u32_ptr_aligned(a: *mut u32) -> bool {
a.align_offset(std::mem::align_of::<u64>()) == 0
}

#[cfg(feature = "unsafe")]
pub(crate) fn u32_ptrs_aligned_2(a: *const u32, b: *const u32) -> bool {
a.align_offset(std::mem::align_of::<u64>()) == 0
&& b.align_offset(std::mem::align_of::<u64>()) == 0
}

#[cfg(feature = "unsafe")]
pub(crate) fn u32_ptrs_aligned_3(a: *mut u32, b: *const u32, c: *const u32) -> bool {
a.align_offset(std::mem::align_of::<u64>()) == 0
&& b.align_offset(std::mem::align_of::<u64>()) == 0
&& c.align_offset(std::mem::align_of::<u64>()) == 0
}
Loading

0 comments on commit 6defe28

Please sign in to comment.