pub fn sqrt_factor(n: usize) -> usize {
let twos = n.trailing_zeros();
let base = n >> twos;
match base {
1 => 1 << (twos / 2),
3 => {
if twos == 0 {
1
} else {
if twos.is_multiple_of(2) {
3 << ((twos - 1) / 2)
} else {
2 << (twos / 2)
}
}
}
9 => {
if twos == 1 {
3
} else {
if twos.is_multiple_of(2) {
3 << (twos / 2)
} else {
4 << (twos / 2)
}
}
}
_ => panic!("n is not in the form 2^k * {{1,3,9}}"),
}
}
pub const fn lcm(a: usize, b: usize) -> usize {
a * (b / gcd(a, b))
}
pub const fn gcd(mut a: usize, mut b: usize) -> usize {
while b != 0 {
(a, b) = (b, a % b);
}
a
}
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use super::{gcd, lcm, sqrt_factor};
fn get_largest_divisor_up_to_sqrt(x: usize) -> usize {
if x == 0 {
return 0;
}
let mut result = 1;
#[allow(clippy::cast_sign_loss)]
let isqrt_x = (x as f64).sqrt() as usize;
for i in 1..=isqrt_x {
if x.is_multiple_of(i) {
result = i;
}
}
result
}
#[test]
fn test_gcd() {
assert_eq!(gcd(4, 6), 2);
assert_eq!(gcd(0, 4), 4);
assert_eq!(gcd(4, 0), 4);
assert_eq!(gcd(1, 1), 1);
assert_eq!(gcd(64, 16), 16);
assert_eq!(gcd(81, 9), 9);
assert_eq!(gcd(0, 0), 0);
}
#[test]
fn test_lcm() {
assert_eq!(lcm(5, 6), 30);
assert_eq!(lcm(3, 7), 21);
assert_eq!(lcm(0, 10), 0);
}
#[test]
fn test_sqrt_factor() {
assert_eq!(sqrt_factor(1), 1); assert_eq!(sqrt_factor(4), 2); assert_eq!(sqrt_factor(16), 4); assert_eq!(sqrt_factor(32), 4); assert_eq!(sqrt_factor(64), 8); assert_eq!(sqrt_factor(256), 16);
assert_eq!(sqrt_factor(3), 1); assert_eq!(sqrt_factor(12), 3); assert_eq!(sqrt_factor(48), 6); assert_eq!(sqrt_factor(192), 12); assert_eq!(sqrt_factor(768), 24);
assert_eq!(sqrt_factor(9), 3); assert_eq!(sqrt_factor(36), 6); assert_eq!(sqrt_factor(144), 12); assert_eq!(sqrt_factor(576), 24); assert_eq!(sqrt_factor(2304), 48); }
proptest! {
#[test]
fn proptest_sqrt_factor(k in 0usize..30, base in prop_oneof![Just(1), Just(3), Just(9)])
{
let n = (1 << k) * base;
let expected = get_largest_divisor_up_to_sqrt(n);
prop_assert_eq!(sqrt_factor(n), expected);
}
}
}