use rug::Integer;
pub fn totient(n: &Integer) -> Integer {
if n.is_zero() {
return Integer::from(0);
}
if n == &1 {
return Integer::from(1);
}
let mut n_abs = n.clone().abs();
let mut result = n_abs.clone();
let original = n_abs.clone();
if n_abs.is_even() {
while n_abs.is_even() {
n_abs /= 2;
}
let two = Integer::from(2);
let quot: Integer = (&result / &two).into();
result -= quot;
}
let mut i = Integer::from(3);
loop {
let i_sq: Integer = (&i * &i).into();
if i_sq > original {
break;
}
if n_abs.is_divisible(&i) {
while n_abs.is_divisible(&i) {
n_abs /= &i;
}
let quot: Integer = (&result / &i).into();
result -= quot;
}
i += 2;
}
if n_abs > 1 {
let quot: Integer = (&result / &n_abs).into();
result -= quot;
}
result
}
pub fn totient_from_factors(n: &Integer) -> Option<Integer> {
if n.is_zero() {
return Some(Integer::from(0));
}
if n == &1 {
return Some(Integer::from(1));
}
let n_abs = n.clone().abs();
let factors = prime_factors(&n_abs)?;
let mut result = n_abs;
for prime in factors {
let prime_minus_1 = &prime - Integer::from(1);
let temp: Integer = (&result * &prime_minus_1).into();
result = (&temp / &prime).into();
}
Some(result)
}
fn prime_factors(n: &Integer) -> Option<Vec<Integer>> {
if n.is_zero() {
return None;
}
if n == &1 {
return Some(Vec::new());
}
let mut n = n.clone();
let mut factors = Vec::new();
while n.is_even() {
if factors.is_empty() || factors.last() != Some(&Integer::from(2)) {
factors.push(Integer::from(2));
}
n /= 2;
}
let mut i = Integer::from(3);
loop {
let i_sq: Integer = (&i * &i).into();
if i_sq > n {
break;
}
while n.is_divisible(&i) {
if factors.last() != Some(&i) {
factors.push(i.clone());
}
n /= &i;
}
i += 2;
}
if n > 1 {
factors.push(n);
}
Some(factors)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_totient() {
assert_eq!(totient(&Integer::from(1)), 1);
assert_eq!(totient(&Integer::from(2)), 1);
assert_eq!(totient(&Integer::from(3)), 2);
assert_eq!(totient(&Integer::from(5)), 4);
assert_eq!(totient(&Integer::from(7)), 6);
assert_eq!(totient(&Integer::from(11)), 10);
assert_eq!(totient(&Integer::from(4)), 2); assert_eq!(totient(&Integer::from(8)), 4); assert_eq!(totient(&Integer::from(9)), 6); assert_eq!(totient(&Integer::from(27)), 18);
assert_eq!(totient(&Integer::from(10)), 4); assert_eq!(totient(&Integer::from(14)), 6); assert_eq!(totient(&Integer::from(15)), 8);
assert_eq!(totient(&Integer::from(30)), 8);
}
#[test]
fn test_totient_negative() {
assert_eq!(totient(&Integer::from(-7)), 6);
assert_eq!(totient(&Integer::from(-10)), 4);
}
#[test]
fn test_prime_factors() {
assert_eq!(prime_factors(&Integer::from(1)), Some(vec![]));
assert_eq!(
prime_factors(&Integer::from(2)),
Some(vec![Integer::from(2)])
);
assert_eq!(
prime_factors(&Integer::from(12)),
Some(vec![Integer::from(2), Integer::from(3)])
);
assert_eq!(
prime_factors(&Integer::from(30)),
Some(vec![Integer::from(2), Integer::from(3), Integer::from(5)])
);
assert_eq!(prime_factors(&Integer::from(0)), None);
}
}