From 906a51952c0351fdd5e1dd97f98298f54cc34eed Mon Sep 17 00:00:00 2001 From: Syed Ghufran Hassan Date: Thu, 14 May 2026 15:20:46 +0500 Subject: [PATCH] (feat) Add Euler's totient function with sieve implementation and tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implements compute_totient() using prime sieve approach - Includes comprehensive test suite covering edge cases, properties, and correctness - Returns vector of φ(i) values for i = 1..n --- src/number_theory/compute_totient.rs | 108 ++++++++++++++++++++++----- 1 file changed, 88 insertions(+), 20 deletions(-) diff --git a/src/number_theory/compute_totient.rs b/src/number_theory/compute_totient.rs index 88af0649fcd..61893f93e2e 100644 --- a/src/number_theory/compute_totient.rs +++ b/src/number_theory/compute_totient.rs @@ -8,31 +8,23 @@ use std::vec; -pub fn compute_totient(n: i32) -> vec::Vec { - let mut phi: Vec = Vec::new(); - - // initialize phi[i] = i - for i in 0..=n { - phi.push(i); +pub fn compute_totient(n: usize) -> Vec { + if n == 0 { + return vec![]; } - - // Compute other Phi values + + let mut phi: Vec = (0..=n).collect(); + for p in 2..=n { - // If phi[p] is not computed already, - // then number p is prime - if phi[(p) as usize] == p { - // Phi of a prime number p is - // always equal to p-1. - phi[(p) as usize] = p - 1; - - // Update phi values of all - // multiples of p - for i in ((2 * p)..=n).step_by(p as usize) { - phi[(i) as usize] = (phi[i as usize] / p) * (p - 1); + if phi[p] == p { + phi[p] = p - 1; + let step = p; + for i in ((2 * p)..=n).step_by(step) { + phi[i] = (phi[i] / p) * (p - 1); } } } - + phi[1..].to_vec() } @@ -57,4 +49,80 @@ mod tests { fn test_3() { assert_eq!(compute_totient(4), vec![1, 1, 2, 2]); } + #[test] + fn test_edge_cases() { + assert_eq!(compute_totient(0), vec![]); + assert_eq!(compute_totient(1), vec![1]); + assert_eq!(compute_totient(2), vec![1, 1]); + } + + #[test] + fn test_prime_numbers() { + // For prime p, φ(p) = p-1 + assert_eq!(compute_totient(13), vec![1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 2, 12]); + assert_eq!(compute_totient(17).last(), Some(&16)); + } + + #[test] + fn test_powers_of_two() { + // For 2^k, φ(2^k) = 2^(k-1) + assert_eq!(compute_totient(8), vec![1, 1, 2, 2, 4, 2, 6, 4]); + // Verify φ(8) = 4 + assert_eq!(compute_totient(8)[7], 4); + } + + #[test] + fn test_small_values() { + let result = compute_totient(10); + // Known values: φ(1)=1, φ(2)=1, φ(3)=2, φ(4)=2, φ(5)=4, + // φ(6)=2, φ(7)=6, φ(8)=4, φ(9)=6, φ(10)=4 + assert_eq!(result, vec![1, 1, 2, 2, 4, 2, 6, 4, 6, 4]); + } + + #[test] + fn test_property_multiplicative() { + // φ(mn) = φ(m) * φ(n) when m,n are coprime + let result = compute_totient(15); // 15 = 3 * 5 + // φ(3)=2, φ(5)=4, so φ(15)=8 + assert_eq!(result[14], 8); + } + + #[test] + fn test_larger_n() { + let result = compute_totient(20); + assert_eq!(result.len(), 20); + // Spot check known values + assert_eq!(result[0], 1); // φ(1) + assert_eq!(result[6], 6); // φ(7) + assert_eq!(result[8], 6); // φ(9) + assert_eq!(result[11], 4); // φ(12) + assert_eq!(result[19], 8); // φ(20) + } + + #[test] + fn test_consistency() { + // Test that φ(n) ≤ n-1 for n > 1 + for n in 2..=50 { + let result = compute_totient(n); + assert!(result[n-1] <= n-1, "φ({}) = {} exceeds {}", n, result[n-1], n-1); + } + } + + #[test] + fn test_sieve_correctness() { + // Compare with direct computation for small n + fn direct_phi(n: usize) -> usize { + (1..=n).filter(|&x| gcd(x, n) == 1).count() + } + + fn gcd(a: usize, b: usize) -> usize { + if b == 0 { a } else { gcd(b, a % b) } + } + + for n in 1..=20 { + let sieve_result = compute_totient(n); + let direct_result: Vec = (1..=n).map(direct_phi).collect(); + assert_eq!(sieve_result, direct_result, "Failed for n={}", n); + } + } }