Skip to content

Commit 1f25906

Browse files
committed
Implement Roots for BigInt and BigUint
This commit implements num-integer::Roots trait for BigInt and BigUint types, and also adds sqrt, cbrt, nth_root as inherent methods to allow access to them without importing Roots trait. For each type tests were added as submodules in the roots test module. Signed-off-by: Manca Bizjak <[email protected]>
1 parent 86e019b commit 1f25906

File tree

5 files changed

+212
-5
lines changed

5 files changed

+212
-5
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ name = "shootout-pidigits"
3131
[dependencies]
3232

3333
[dependencies.num-integer]
34-
version = "0.1.38"
34+
version = "0.1.39"
3535
default-features = false
3636

3737
[dependencies.num-traits]

benches/bigint.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
extern crate test;
55
extern crate num_bigint;
66
extern crate num_traits;
7+
extern crate num_integer;
78
extern crate rand;
89

910
use std::mem::replace;
1011
use test::Bencher;
1112
use num_bigint::{BigInt, BigUint, RandBigInt};
1213
use num_traits::{Zero, One, FromPrimitive, Num};
13-
use rand::{SeedableRng, StdRng};
14+
use rand::{SeedableRng, StdRng, Rng};
1415

1516
fn get_rng() -> StdRng {
1617
let mut seed = [0; 32];
@@ -342,3 +343,32 @@ fn modpow_even(b: &mut Bencher) {
342343

343344
b.iter(|| base.modpow(&e, &m));
344345
}
346+
347+
#[bench]
348+
fn roots_sqrt(b: &mut Bencher) {
349+
let mut rng = get_rng();
350+
let x = rng.gen_biguint(2048);
351+
352+
b.iter(|| x.sqrt());
353+
}
354+
355+
#[bench]
356+
fn roots_cbrt(b: &mut Bencher) {
357+
let mut rng = get_rng();
358+
let x = rng.gen_biguint(2048);
359+
360+
b.iter(|| x.cbrt());
361+
}
362+
363+
#[bench]
364+
fn roots_nth(b: &mut Bencher) {
365+
let mut rng = get_rng();
366+
let x = rng.gen_biguint(2048);
367+
// Although n is u32, here we limit it to the set of u8 values since it
368+
// hugely impacts the performance of nth_root due to exponentiation to
369+
// the power of n-1. Using very large values for n is also not very realistic,
370+
// and any n > x's bit size produces 1 as a result anyway.
371+
let n: u8 = rng.gen();
372+
373+
b.iter(|| { x.nth_root(n as u32) });
374+
}

src/bigint.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::iter::{Product, Sum};
1616
#[cfg(feature = "serde")]
1717
use serde;
1818

19-
use integer::Integer;
19+
use integer::{Integer, Roots};
2020
use traits::{ToPrimitive, FromPrimitive, Num, CheckedAdd, CheckedSub,
2121
CheckedMul, CheckedDiv, Signed, Zero, One};
2222

@@ -1802,6 +1802,15 @@ impl Integer for BigInt {
18021802
}
18031803
}
18041804

1805+
impl Roots for BigInt {
1806+
fn nth_root(&self, n: u32) -> Self {
1807+
assert!(!(self.is_negative() && n.is_even()),
1808+
"n-th root is undefined for number (n={})", n);
1809+
1810+
BigInt::from_biguint(self.sign, self.data.nth_root(n))
1811+
}
1812+
}
1813+
18051814
impl ToPrimitive for BigInt {
18061815
#[inline]
18071816
fn to_i64(&self) -> Option<i64> {
@@ -2538,6 +2547,25 @@ impl BigInt {
25382547
};
25392548
BigInt::from_biguint(sign, mag)
25402549
}
2550+
2551+
/// Returns the truncated principal square root of `self` --
2552+
/// see [Roots::sqrt](Roots::sqrt).
2553+
// struct.BigInt.html#trait.Roots
2554+
pub fn sqrt(&self) -> Self {
2555+
Roots::sqrt(self)
2556+
}
2557+
2558+
/// Returns the truncated principal cube root of `self` --
2559+
/// see [Roots::cbrt](Roots::cbrt).
2560+
pub fn cbrt(&self) -> Self {
2561+
Roots::cbrt(self)
2562+
}
2563+
2564+
/// Returns the truncated principal `n`th root of `self` --
2565+
/// See [Roots::nth_root](Roots::nth_root).
2566+
pub fn nth_root(&self, n: u32) -> Self {
2567+
Roots::nth_root(self, n)
2568+
}
25412569
}
25422570

25432571
impl_sum_iter_type!(BigInt);

src/biguint.rs

+67-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ use std::ascii::AsciiExt;
1717
#[cfg(feature = "serde")]
1818
use serde;
1919

20-
use integer::Integer;
20+
use integer::{Integer, Roots};
2121
use traits::{ToPrimitive, FromPrimitive, Float, Num, Unsigned, CheckedAdd, CheckedSub, CheckedMul,
22-
CheckedDiv, Zero, One};
22+
CheckedDiv, Zero, One, pow};
2323

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

@@ -1026,6 +1026,52 @@ impl Integer for BigUint {
10261026
}
10271027
}
10281028

1029+
impl Roots for BigUint {
1030+
fn nth_root(&self, n: u32) -> Self {
1031+
assert!(n > 0, "n must be at least 1");
1032+
1033+
let one = BigUint::one();
1034+
1035+
// Trivial cases
1036+
if self.is_zero() {
1037+
return BigUint::zero();
1038+
}
1039+
1040+
if self.is_one() {
1041+
return one;
1042+
}
1043+
1044+
let n = n as usize;
1045+
let n_min_1 = (n as usize) - 1;
1046+
1047+
// Newton's method to compute the nth root of an integer.
1048+
//
1049+
// Reference:
1050+
// Brent & Zimmermann, Modern Computer Arithmetic, v0.5.9, Algorithm 1.14
1051+
//
1052+
// Set initial guess to something definitely >= floor(nth_root of self)
1053+
// but as low as possible to speed up convergence.
1054+
let bit_len = self.len() * big_digit::BITS;
1055+
let guess = one << (bit_len/n + 1);
1056+
1057+
let mut u = guess;
1058+
let mut s: BigUint;
1059+
1060+
loop {
1061+
s = u;
1062+
let q = self / pow(s.clone(), n_min_1);
1063+
let t: BigUint = n_min_1 * &s + q;
1064+
1065+
// Compute the candidate value for next iteration
1066+
u = t / n;
1067+
1068+
if u >= s { break; }
1069+
}
1070+
1071+
s
1072+
}
1073+
}
1074+
10291075
fn high_bits_to_u64(v: &BigUint) -> u64 {
10301076
match v.data.len() {
10311077
0 => 0,
@@ -1749,6 +1795,25 @@ impl BigUint {
17491795
}
17501796
acc
17511797
}
1798+
1799+
/// Returns the truncated principal square root of `self` --
1800+
/// see [Roots::sqrt](Roots::sqrt).
1801+
// struct.BigInt.html#trait.Roots
1802+
pub fn sqrt(&self) -> Self {
1803+
Roots::sqrt(self)
1804+
}
1805+
1806+
/// Returns the truncated principal cube root of `self` --
1807+
/// see [Roots::cbrt](Roots::cbrt).
1808+
pub fn cbrt(&self) -> Self {
1809+
Roots::cbrt(self)
1810+
}
1811+
1812+
/// Returns the truncated principal `n`th root of `self` --
1813+
/// See [Roots::nth_root](Roots::nth_root).
1814+
pub fn nth_root(&self, n: u32) -> Self {
1815+
Roots::nth_root(self, n)
1816+
}
17521817
}
17531818

17541819
/// Returns the number of least-significant bits that are zero,

tests/roots.rs

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
extern crate num_bigint;
2+
extern crate num_integer;
3+
extern crate num_traits;
4+
5+
mod biguint {
6+
use num_bigint::BigUint;
7+
use num_traits::FromPrimitive;
8+
use std::str::FromStr;
9+
10+
fn check(x: i32, n: u32, expected: i32) {
11+
let big_x: BigUint = FromPrimitive::from_i32(x).unwrap();
12+
let big_expected: BigUint = FromPrimitive::from_i32(expected).unwrap();
13+
14+
assert_eq!(big_x.nth_root(n), big_expected);
15+
}
16+
17+
#[test]
18+
fn test_sqrt() {
19+
check(99, 2, 9);
20+
check(100, 2, 10);
21+
check(120, 2, 10);
22+
}
23+
24+
#[test]
25+
fn test_cbrt() {
26+
check(8, 3, 2);
27+
check(26, 3, 2);
28+
}
29+
30+
#[test]
31+
fn test_nth_root() {
32+
check(0, 1, 0);
33+
check(10, 1, 10);
34+
check(100, 4, 3);
35+
}
36+
37+
#[test]
38+
#[should_panic]
39+
fn test_nth_root_n_is_zero() {
40+
check(4, 0, 0);
41+
}
42+
43+
#[test]
44+
fn test_nth_root_big() {
45+
let x: BigUint = FromStr::from_str("123_456_789").unwrap();
46+
let expected : BigUint = FromPrimitive::from_i32(6).unwrap();
47+
48+
assert_eq!(x.nth_root(10), expected);
49+
}
50+
}
51+
52+
mod bigint {
53+
use num_bigint::BigInt;
54+
use num_traits::FromPrimitive;
55+
56+
fn check(x: i32, n: u32, expected: i32) {
57+
let big_x: BigInt = FromPrimitive::from_i32(x).unwrap();
58+
let big_expected: BigInt = FromPrimitive::from_i32(expected).unwrap();
59+
60+
assert_eq!(big_x.nth_root(n), big_expected);
61+
}
62+
63+
#[test]
64+
fn test_nth_root() {
65+
check(-100, 3, -4);
66+
}
67+
68+
#[test]
69+
#[should_panic]
70+
fn test_nth_root_x_neg_n_even() {
71+
check(-100, 4, 0);
72+
}
73+
74+
#[test]
75+
#[should_panic]
76+
fn test_sqrt_x_neg() {
77+
check(-4, 2, -2);
78+
}
79+
80+
#[test]
81+
fn test_cbrt() {
82+
check(-8, 3, -2);
83+
}
84+
}

0 commit comments

Comments
 (0)