Skip to content

Commit

Permalink
Use u128 sqrt from the num crate
Browse files Browse the repository at this point in the history
- Remove the local unoptimized square root implementation for unsigned 128 bit integers and import `num` crate instead
- Update to the latest `stderrlog` version
- Update README with up-to-date time estiamtes
  • Loading branch information
Fairglow committed Feb 11, 2024
1 parent 8de372f commit fcf5e5a
Show file tree
Hide file tree
Showing 4 changed files with 9 additions and 44 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ harness = false
[dependencies]
clap = "4.4"
log = "0.4"
stderrlog = "0.5"
stderrlog = "0.6"
genawaiter = "0.99"
rayon = "1.8"
num = "0.4.1"

[dev-dependencies]
criterion = "0.5"
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ On an old system (i7-6700):
- full test suite completes in about 7 minutes

On a modern system (i7-12700):
- 32-bit, random number in about 7 us and worst case in 66.5 us
- 64-bit, random number in about 348 ms ([58 .. 765] ms) and worst case in 4.44 s
- 32-bit, random number in about 6.5 us and worst case in 68 us
- 64-bit, random number in about 140 ms ([3 .. 340] ms) and worst case in 4.6 s
- full test suite completes in less than 3 minutes

The above numbers are taken from the included benchmark test, which you can run with the command: `cargo bench`. Note that it will take a few minutes to run the full suite, in which time you need to close all other applications and leave it unattended, to give the benchmark the most processing power possible.
21 changes: 4 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::convert::From;
use std::fmt;
use genawaiter::stack::let_gen_using;
use candidates::prime_wheel_30;
use num::integer::Roots;

#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct IntFactor {
Expand Down Expand Up @@ -97,7 +98,7 @@ impl From<u128> for PrimeFactors {
let mut pf = PrimeFactors::new();
if n < 2 { return pf; }
// A factor of n must have a value less than or equal to sqrt(n)
let mut maxf = u128_sqrt(n) + 1;
let mut maxf = n.sqrt() + 1;
let_gen_using!(mpgen, prime_wheel_30);
let mut x = n;
for f in mpgen.into_iter() {
Expand All @@ -111,7 +112,7 @@ impl From<u128> for PrimeFactors {
}
if c > 0 {
// A factor of x must have a value less than or equal to sqrt(x)
maxf = u128_sqrt(x) + 1;
maxf = x.sqrt() + 1;
pf.add(f, c);
}
if x == 1 {
Expand Down Expand Up @@ -164,20 +165,6 @@ impl<'a> IntoIterator for &'a PrimeFactors {
}
}

/// Unsigned 128-bit integer square root calculation.
/// Based on example implementation in C at:
/// https://en.wikipedia.org/wiki/Integer_square_root
pub fn u128_sqrt(s: u128) -> u128 {
let mut g = s >> 1; // Initial guess
if g == 0 { return s; } // sanity check
let mut u = (g + s / g) >> 1; // update
while u < g { // this also checks for cycle
g = u;
u = (g + s / g) >> 1;
}
g
}

/// Test if the value is a prime number, or not
pub fn u128_is_prime(n: u128) -> bool {
if n < 2 { return false; }
Expand All @@ -189,7 +176,7 @@ pub fn u128_is_prime(n: u128) -> bool {
}
}
// A factor of n must have a value less than or equal to sqrt(n)
let maxf = u128_sqrt(n) + 1;
let maxf = n.sqrt() + 1;
let_gen_using!(mpgen, prime_wheel_30);
for f in mpgen.into_iter() {
if f >= maxf {
Expand Down
25 changes: 1 addition & 24 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ use primefactor::{
PrimeFactors,
u128_gcd,
u128_is_prime,
u128_lcm,
u128_sqrt};
u128_lcm};

#[test]
fn test_early_prime_wheel_numbers() {
Expand Down Expand Up @@ -60,28 +59,6 @@ fn test_prime_wheel_quality() {
assert!(percent > 25.0);
}

#[test]
fn test_int_sqrt_pow_of_2() {
let mut rnd = rand::thread_rng();
for _ in 1..1000 {
let n = rnd.gen_range(1..u128_sqrt(u128::MAX));
let sqrt = u128_sqrt(n.pow(2));
assert_eq!(sqrt, n);
}
}

#[test]
fn test_int_sqrt_floor() {
let mut rnd = rand::thread_rng();
for _ in 1..1000 {
// Largest integer in a f64 is 2^53-1 (52 bits mantissa)
let n = rnd.gen_range(1..u64::pow(2, 53) as u128);
let expt = f64::sqrt(n as f64) as u128;
let sqrt = u128_sqrt(n);
assert_eq!(sqrt, expt);
}
}

#[test]
fn test_is_prime() {
for num in 2..=1000 {
Expand Down

0 comments on commit fcf5e5a

Please sign in to comment.