Skip to content

Commit

Permalink
Remove big_digit::* from the public API
Browse files Browse the repository at this point in the history
The *idea* of `big_digit` and its type aliases is that we may someday
use something other than `u32` in the representation, perhaps even
different sizes for different targets.  That's still a possibility, but
I think it's not really feasible to expose this variation in the public
API.  Calling `BigUint::from_slice([1, 2, 3])` is only meaningful if you
know what that size is, and users can't really alternate this kind of
thing based on a type definition.  So for now, we just commit to `u32`
units in the public API, no matter what we may do internally.

This removal is a breaking change, part of the 0.2 semver bump.  If I'm
wrong and somebody can show a compelling use case for `big_digit`, we
can always add things back into the public API later.
  • Loading branch information
cuviper committed Apr 28, 2018
1 parent 7424bbe commit a849284
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 112 deletions.
47 changes: 3 additions & 44 deletions src/algorithms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,49 +12,7 @@ use bigint::BigInt;
use bigint::Sign;
use bigint::Sign::{Minus, NoSign, Plus};

#[allow(non_snake_case)]
pub mod big_digit {
/// A `BigDigit` is a `BigUint`'s composing element.
pub type BigDigit = u32;

/// A `DoubleBigDigit` is the internal type used to do the computations. Its
/// size is the double of the size of `BigDigit`.
pub type DoubleBigDigit = u64;

/// A `SignedDoubleBigDigit` is the signed version of `DoubleBigDigit`.
pub type SignedDoubleBigDigit = i64;

pub const ZERO_BIG_DIGIT: BigDigit = 0;

// `DoubleBigDigit` size dependent
pub const BITS: usize = 32;

pub const BASE: DoubleBigDigit = 1 << BITS;
const LO_MASK: DoubleBigDigit = (-1i32 as DoubleBigDigit) >> BITS;

#[inline]
fn get_hi(n: DoubleBigDigit) -> BigDigit {
(n >> BITS) as BigDigit
}
#[inline]
fn get_lo(n: DoubleBigDigit) -> BigDigit {
(n & LO_MASK) as BigDigit
}

/// Split one `DoubleBigDigit` into two `BigDigit`s.
#[inline]
pub fn from_doublebigdigit(n: DoubleBigDigit) -> (BigDigit, BigDigit) {
(get_hi(n), get_lo(n))
}

/// Join two `BigDigit`s into one `DoubleBigDigit`
#[inline]
pub fn to_doublebigdigit(hi: BigDigit, lo: BigDigit) -> DoubleBigDigit {
(lo as DoubleBigDigit) | ((hi as DoubleBigDigit) << BITS)
}
}

use big_digit::{BigDigit, DoubleBigDigit, SignedDoubleBigDigit};
use big_digit::{self, BigDigit, DoubleBigDigit, SignedDoubleBigDigit};

// Generic functions for add/subtract/multiply with carry/borrow:

Expand Down Expand Up @@ -647,7 +605,8 @@ pub fn cmp_slice(a: &[BigDigit], b: &[BigDigit]) -> Ordering {

#[cfg(test)]
mod algorithm_tests {
use {BigDigit, BigUint, BigInt};
use big_digit::BigDigit;
use {BigUint, BigInt};
use Sign::Plus;
use traits::Num;

Expand Down
8 changes: 4 additions & 4 deletions src/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use traits::{ToPrimitive, FromPrimitive, Num, CheckedAdd, CheckedSub,
use self::Sign::{Minus, NoSign, Plus};

use super::ParseBigIntError;
use super::big_digit::{self, BigDigit, DoubleBigDigit};
use big_digit::{self, BigDigit, DoubleBigDigit};
use biguint;
use biguint::to_str_radix_reversed;
use biguint::{BigUint, IntDigits};
Expand Down Expand Up @@ -1868,7 +1868,7 @@ impl BigInt {
///
/// The digits are in little-endian base 2<sup>32</sup>.
#[inline]
pub fn new(sign: Sign, digits: Vec<BigDigit>) -> BigInt {
pub fn new(sign: Sign, digits: Vec<u32>) -> BigInt {
BigInt::from_biguint(sign, BigUint::new(digits))
}

Expand All @@ -1891,13 +1891,13 @@ impl BigInt {

/// Creates and initializes a `BigInt`.
#[inline]
pub fn from_slice(sign: Sign, slice: &[BigDigit]) -> BigInt {
pub fn from_slice(sign: Sign, slice: &[u32]) -> BigInt {
BigInt::from_biguint(sign, BigUint::from_slice(slice))
}

/// Reinitializes a `BigInt`.
#[inline]
pub fn assign_from_slice(&mut self, sign: Sign, slice: &[BigDigit]) {
pub fn assign_from_slice(&mut self, sign: Sign, slice: &[u32]) {
if sign == NoSign {
self.data.assign_from_slice(&[]);
self.sign = NoSign;
Expand Down
13 changes: 5 additions & 8 deletions src/biguint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ use integer::Integer;
use traits::{ToPrimitive, FromPrimitive, Float, Num, Unsigned, CheckedAdd, CheckedSub, CheckedMul,
CheckedDiv, Zero, One};

use big_digit::{self, BigDigit, DoubleBigDigit};

#[path = "algorithms.rs"]
mod algorithms;
#[path = "monty.rs"]
mod monty;
pub use self::algorithms::big_digit;
pub use self::big_digit::{BigDigit, DoubleBigDigit, ZERO_BIG_DIGIT};

use self::algorithms::{mac_with_carry, mul3, scalar_mul, div_rem, div_rem_digit};
use self::algorithms::{__add2, __sub2rev, add2, sub2, sub2rev};
Expand All @@ -39,9 +39,6 @@ use UsizePromotion;
use ParseBigIntError;

/// A big unsigned integer type.
///
/// A `BigUint`-typed value `BigUint { data: vec!(a, b, c) }` represents a number
/// `(a + b * big_digit::BASE + c * big_digit::BASE^2)`.
#[derive(Clone, Debug, Hash)]
pub struct BigUint {
data: Vec<BigDigit>,
Expand Down Expand Up @@ -1379,23 +1376,23 @@ impl BigUint {
///
/// The digits are in little-endian base 2<sup>32</sup>.
#[inline]
pub fn new(digits: Vec<BigDigit>) -> BigUint {
pub fn new(digits: Vec<u32>) -> BigUint {
BigUint { data: digits }.normalized()
}

/// Creates and initializes a `BigUint`.
///
/// The digits are in little-endian base 2<sup>32</sup>.
#[inline]
pub fn from_slice(slice: &[BigDigit]) -> BigUint {
pub fn from_slice(slice: &[u32]) -> BigUint {
BigUint::new(slice.to_vec())
}

/// Assign a value to a `BigUint`.
///
/// The digits are in little-endian base 2<sup>32</sup>.
#[inline]
pub fn assign_from_slice(&mut self, slice: &[BigDigit]) {
pub fn assign_from_slice(&mut self, slice: &[u32]) {
self.data.resize(slice.len(), 0);
self.data.clone_from_slice(slice);
self.normalize();
Expand Down
40 changes: 38 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,46 @@ mod bigint;

pub use biguint::BigUint;
pub use biguint::ToBigUint;
pub use biguint::big_digit;
pub use biguint::big_digit::{BigDigit, DoubleBigDigit, ZERO_BIG_DIGIT};

pub use bigint::Sign;
pub use bigint::BigInt;
pub use bigint::ToBigInt;
pub use bigint::RandBigInt;

mod big_digit {
/// A `BigDigit` is a `BigUint`'s composing element.
pub type BigDigit = u32;

/// A `DoubleBigDigit` is the internal type used to do the computations. Its
/// size is the double of the size of `BigDigit`.
pub type DoubleBigDigit = u64;

/// A `SignedDoubleBigDigit` is the signed version of `DoubleBigDigit`.
pub type SignedDoubleBigDigit = i64;

// `DoubleBigDigit` size dependent
pub const BITS: usize = 32;

const LO_MASK: DoubleBigDigit = (-1i32 as DoubleBigDigit) >> BITS;

#[inline]
fn get_hi(n: DoubleBigDigit) -> BigDigit {
(n >> BITS) as BigDigit
}
#[inline]
fn get_lo(n: DoubleBigDigit) -> BigDigit {
(n & LO_MASK) as BigDigit
}

/// Split one `DoubleBigDigit` into two `BigDigit`s.
#[inline]
pub fn from_doublebigdigit(n: DoubleBigDigit) -> (BigDigit, BigDigit) {
(get_hi(n), get_lo(n))
}

/// Join two `BigDigit`s into one `DoubleBigDigit`
#[inline]
pub fn to_doublebigdigit(hi: BigDigit, lo: BigDigit) -> DoubleBigDigit {
(lo as DoubleBigDigit) | ((hi as DoubleBigDigit) << BITS)
}
}
22 changes: 11 additions & 11 deletions tests/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ extern crate num_integer;
extern crate num_traits;
extern crate rand;

use num_bigint::{BigDigit, BigUint, big_digit};
use num_bigint::BigUint;
use num_bigint::{BigInt, ToBigInt};
use num_bigint::Sign::{Minus, NoSign, Plus};

Expand Down Expand Up @@ -163,7 +163,7 @@ fn test_from_signed_bytes_be() {

#[test]
fn test_cmp() {
let vs: [&[BigDigit]; 4] = [&[2 as BigDigit], &[1, 1], &[2, 1], &[1, 1, 1]];
let vs: [&[u32]; 4] = [&[2 as u32], &[1, 1], &[2, 1], &[1, 1, 1]];
let mut nums = Vec::new();
for s in vs.iter().rev() {
nums.push(BigInt::from_slice(Minus, *s));
Expand Down Expand Up @@ -244,7 +244,7 @@ fn test_convert_i64() {
None);

assert_eq!(BigInt::from_biguint(Minus,
BigUint::new(vec![1, 0, 0, 1 << (big_digit::BITS - 1)]))
BigUint::new(vec![1, 0, 0, 1 << 31]))
.to_i64(),
None);

Expand Down Expand Up @@ -458,11 +458,11 @@ fn test_convert_from_uint() {
}
}

check!(u8, BigInt::from_slice(Plus, &[u8::MAX as BigDigit]));
check!(u16, BigInt::from_slice(Plus, &[u16::MAX as BigDigit]));
check!(u32, BigInt::from_slice(Plus, &[u32::MAX as BigDigit]));
check!(u8, BigInt::from_slice(Plus, &[u8::MAX as u32]));
check!(u16, BigInt::from_slice(Plus, &[u16::MAX as u32]));
check!(u32, BigInt::from_slice(Plus, &[u32::MAX]));
check!(u64,
BigInt::from_slice(Plus, &[u32::MAX as BigDigit, u32::MAX as BigDigit]));
BigInt::from_slice(Plus, &[u32::MAX, u32::MAX]));
check!(usize, BigInt::from(usize::MAX as u64));
}

Expand All @@ -482,16 +482,16 @@ fn test_convert_from_int() {

check!(i8,
BigInt::from_slice(Minus, &[1 << 7]),
BigInt::from_slice(Plus, &[i8::MAX as BigDigit]));
BigInt::from_slice(Plus, &[i8::MAX as u32]));
check!(i16,
BigInt::from_slice(Minus, &[1 << 15]),
BigInt::from_slice(Plus, &[i16::MAX as BigDigit]));
BigInt::from_slice(Plus, &[i16::MAX as u32]));
check!(i32,
BigInt::from_slice(Minus, &[1 << 31]),
BigInt::from_slice(Plus, &[i32::MAX as BigDigit]));
BigInt::from_slice(Plus, &[i32::MAX as u32]));
check!(i64,
BigInt::from_slice(Minus, &[0, 1 << 31]),
BigInt::from_slice(Plus, &[u32::MAX as BigDigit, i32::MAX as BigDigit]));
BigInt::from_slice(Plus, &[u32::MAX, i32::MAX as u32]));
check!(isize,
BigInt::from(isize::MIN as i64),
BigInt::from(isize::MAX as i64));
Expand Down
6 changes: 3 additions & 3 deletions tests/bigint_bitwise.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
extern crate num_bigint;
extern crate num_traits;

use num_bigint::{BigDigit, BigInt, Sign, ToBigInt};
use num_bigint::{BigInt, Sign, ToBigInt};
use num_traits::ToPrimitive;
use std::{i32, i64, u32};

enum ValueVec {
N,
P(&'static [BigDigit]),
M(&'static [BigDigit]),
P(&'static [u32]),
M(&'static [u32]),
}

use ValueVec::*;
Expand Down
6 changes: 3 additions & 3 deletions tests/bigint_scalar.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
extern crate num_bigint;
extern crate num_traits;

use num_bigint::{BigDigit, BigInt};
use num_bigint::BigInt;
use num_bigint::Sign::Plus;
use num_traits::{Zero, Signed, ToPrimitive};

Expand Down Expand Up @@ -88,7 +88,7 @@ fn test_scalar_mul() {

#[test]
fn test_scalar_div_rem() {
fn check_sub(a: &BigInt, b: BigDigit, ans_q: &BigInt, ans_r: &BigInt) {
fn check_sub(a: &BigInt, b: u32, ans_q: &BigInt, ans_r: &BigInt) {
let (q, r) = (a / b, a % b);
if !r.is_zero() {
assert_eq!(r.sign(), a.sign());
Expand All @@ -109,7 +109,7 @@ fn test_scalar_div_rem() {
}
}

fn check(a: &BigInt, b: BigDigit, q: &BigInt, r: &BigInt) {
fn check(a: &BigInt, b: u32, q: &BigInt, r: &BigInt) {
check_sub(a, b, q, r);
check_sub(&a.neg(), b, &q.neg(), &r.neg());
}
Expand Down
52 changes: 29 additions & 23 deletions tests/biguint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extern crate num_traits;
extern crate rand;

use num_integer::Integer;
use num_bigint::{BigDigit, BigUint, ToBigUint, big_digit};
use num_bigint::{BigUint, ToBigUint};
use num_bigint::{BigInt, ToBigInt};
use num_bigint::Sign::Plus;

Expand Down Expand Up @@ -146,19 +146,25 @@ fn test_hash() {
assert!(hash(&d) != hash(&e));
}

const BIT_TESTS: &'static [(&'static [BigDigit],
&'static [BigDigit],
&'static [BigDigit],
&'static [BigDigit],
&'static [BigDigit])] = &[// LEFT RIGHT AND OR XOR
(&[], &[], &[], &[], &[]),
(&[1, 0, 1], &[1, 1], &[1], &[1, 1, 1], &[0, 1, 1]),
(&[1, 0, 1], &[0, 1, 1], &[0, 0, 1], &[1, 1, 1], &[1, 1]),
(&[268, 482, 17],
&[964, 54],
&[260, 34],
&[972, 502, 17],
&[712, 468, 17])];
// LEFT, RIGHT, AND, OR, XOR
const BIT_TESTS: &'static [(
&'static [u32],
&'static [u32],
&'static [u32],
&'static [u32],
&'static [u32],
)] = &[
(&[], &[], &[], &[], &[]),
(&[1, 0, 1], &[1, 1], &[1], &[1, 1, 1], &[0, 1, 1]),
(&[1, 0, 1], &[0, 1, 1], &[0, 0, 1], &[1, 1, 1], &[1, 1]),
(
&[268, 482, 17],
&[964, 54],
&[260, 34],
&[972, 502, 17],
&[712, 468, 17],
),
];

#[test]
fn test_bitand() {
Expand Down Expand Up @@ -473,9 +479,9 @@ fn test_convert_i64() {
check(i64::MAX.to_biguint().unwrap(), i64::MAX);

check(BigUint::new(vec![]), 0);
check(BigUint::new(vec![1]), 1 << (0 * big_digit::BITS));
check(BigUint::new(vec![N1]), (1 << (1 * big_digit::BITS)) - 1);
check(BigUint::new(vec![0, 1]), 1 << (1 * big_digit::BITS));
check(BigUint::new(vec![1]), 1);
check(BigUint::new(vec![N1]), (1 << 32) - 1);
check(BigUint::new(vec![0, 1]), 1 << 32);
check(BigUint::new(vec![N1, N1 >> 1]), i64::MAX);

assert_eq!(i64::MIN.to_biguint(), None);
Expand All @@ -499,9 +505,9 @@ fn test_convert_u64() {
check(u64::MAX.to_biguint().unwrap(), u64::MAX);

check(BigUint::new(vec![]), 0);
check(BigUint::new(vec![1]), 1 << (0 * big_digit::BITS));
check(BigUint::new(vec![N1]), (1 << (1 * big_digit::BITS)) - 1);
check(BigUint::new(vec![0, 1]), 1 << (1 * big_digit::BITS));
check(BigUint::new(vec![1]), 1);
check(BigUint::new(vec![N1]), (1 << 32) - 1);
check(BigUint::new(vec![0, 1]), 1 << 32);
check(BigUint::new(vec![N1, N1]), u64::MAX);

assert_eq!(BigUint::new(vec![0, 0, 1]).to_u64(), None);
Expand Down Expand Up @@ -667,8 +673,8 @@ fn test_convert_from_uint() {
}
}

check!(u8, BigUint::from_slice(&[u8::MAX as BigDigit]));
check!(u16, BigUint::from_slice(&[u16::MAX as BigDigit]));
check!(u8, BigUint::from_slice(&[u8::MAX as u32]));
check!(u16, BigUint::from_slice(&[u16::MAX as u32]));
check!(u32, BigUint::from_slice(&[u32::MAX]));
check!(u64, BigUint::from_slice(&[u32::MAX, u32::MAX]));
check!(usize, BigUint::from(usize::MAX as u64));
Expand Down Expand Up @@ -915,7 +921,7 @@ fn test_is_even() {
}

fn to_str_pairs() -> Vec<(BigUint, Vec<(u32, String)>)> {
let bits = big_digit::BITS;
let bits = 32;
vec![(Zero::zero(),
vec![(2, "0".to_string()), (3, "0".to_string())]),
(BigUint::from_slice(&[0xff]),
Expand Down
Loading

4 comments on commit a849284

@cmpute
Copy link

@cmpute cmpute commented on a849284 Feb 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cuviper Is it possible to only expose the BigDigit type without changing the API? The use case in my situation is that I want to implement a general implementation of Montgomery multiplication (for various number theoretic functions). If the related function montgomery can be exposed (which requires BigDigit to be exposed), I can reduce a lot of redundant code!

I agree that users should not depends on the size of BigDigit when creating new big integers, however it doesn't hurt to expose the BigDigit type and some low level algorithms for other algorithms that work on big integers (just like mpn functions in GMP)

@cuviper
Copy link
Member Author

@cuviper cuviper commented on a849284 Feb 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cmpute BigDigit is a type alias, and I'm pretty firm that I don't want to expose that as it flips between u32 or u64. We could possibly expose an opaque wrapper type. In the particular instance of the montgomery function, it might make sense to publicize MontyReducer and its new constructor, as well as a montgomery method on that type.

@cmpute
Copy link

@cmpute cmpute commented on a849284 Feb 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the reply! That would be great if MontyReducer could be exposed. But how will that work if BigDigit is not exposed? The montgomery method depends on the modular inverse k, which has to be the size of a BigDigit

@cmpute
Copy link

@cmpute cmpute commented on a849284 Feb 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My motivation for this is actually implementing this trait in my num-modular trait to support various operations in Montgomery form

Please sign in to comment.