Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement sqrt for BigInt and BigUint #51

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions benches/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,11 @@ fn modpow_even(b: &mut Bencher) {

b.iter(|| base.modpow(&e, &m));
}

#[bench]
fn sqrt(b: &mut Bencher) {
let mut rng = get_rng();
let n = rng.gen_biguint(2048);

b.iter(|| n.sqrt());
}
12 changes: 12 additions & 0 deletions src/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2538,6 +2538,18 @@ impl BigInt {
};
BigInt::from_biguint(sign, mag)
}

/// Finds square root of `self`.
///
/// The result is the greatest integer less than or equal to the
/// square root of `self`.
///
/// Panics if `self` is a negative number.
pub fn sqrt(&self) -> Self {
assert!(!self.is_negative(), "number is negative");

BigInt::from_biguint(self.sign, self.data.sqrt())
}
}

impl_sum_iter_type!(BigInt);
Expand Down
44 changes: 44 additions & 0 deletions src/biguint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1749,6 +1749,50 @@ impl BigUint {
}
acc
}

/// Finds square root of `self`.
///
/// The result is the greatest integer less than or equal to the
/// square root of `self`.
pub fn sqrt(&self) -> Self {
let one = BigUint::one();
let two = BigUint::from(2 as u8);

// Trivial cases
if self.is_zero() {
return BigUint::zero();
}

if self.is_one() {
return one;
}

// Newton's method to compute the square root of an integer.
//
// Reference:
// Brent & Zimmermann, Modern Computer Arithmetic, v0.5.9, Algorithm 1.13
//
// Set initial guess to something >= floor(sqrt(self)), but as low
// as possible to speed up convergence.
let bit_len = self.len() * big_digit::BITS;
let guess = one.shl(bit_len/2 + 1);

let mut u = guess;
let mut s: BigUint;

loop {
s = u;
let q = self.div_floor(&s);
let t: BigUint = &s + &q;
Copy link
Member

Choose a reason for hiding this comment

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

We don't need q after this, so adding &s + q by value will let it reuse that memory allocation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, fixed.


// Compute the candidate value for next iteration
u = t.div_floor(&two);
Copy link
Member

Choose a reason for hiding this comment

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

Any reason you're using div_floor instead of the / operator? With the operator you can also use integer literals, like t / 2u32. But even more, t >> 1 is probably faster.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No reason whatsoever, it just slipped my mind it seems. I replaced the usage of div_floor with the / and >> operators as appropriate, and also replaced the lsh call with <<. Looks much cleaner now.


if u >= s { break; }
}

s
}
}

/// Returns the number of least-significant bits that are zero,
Expand Down
21 changes: 21 additions & 0 deletions tests/biguint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,27 @@ fn test_lcm() {
check(99, 17, 1683);
}

#[test]
fn test_sqrt() {
fn check(n: usize, expected: usize) {
let big_n: BigUint = FromPrimitive::from_usize(n).unwrap();
let big_expected: BigUint = FromPrimitive::from_usize(expected).unwrap();

assert_eq!(big_n.sqrt(), big_expected);
}

check(0, 0);
check(1, 1);
check(99, 9);
check(100, 10);
check(102, 10);
check(120, 10);

let big_n: BigUint = FromStr::from_str("123_456_789").unwrap();
let expected : BigUint = FromStr::from_str("11_111").unwrap();
assert_eq!(big_n.sqrt(), expected);
}

#[test]
fn test_is_even() {
let one: BigUint = FromStr::from_str("1").unwrap();
Expand Down