From 5eefb715986994b7f9a9648a192f17266bb15968 Mon Sep 17 00:00:00 2001 From: Jackson Walters Date: Tue, 11 Feb 2025 16:16:51 -0500 Subject: [PATCH 1/2] failing test for 2*p^k --- src/test.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/test.rs b/src/test.rs index c0b0d4a..4ef6fcd 100644 --- a/src/test.rs +++ b/src/test.rs @@ -50,7 +50,30 @@ mod tests { #[test] fn test_polymul_ntt_prime_power_modulus() { - let modulus: i64 = (17 as i64).pow(4); // modulus p^k or 2*p^k + let modulus: i64 = (17 as i64).pow(4); // modulus p^k + let root: i64 = 3; // Primitive root of unity + let n: usize = 8; // Length of the NTT (must be a power of 2) + let omega = omega(root, modulus, n); // n-th root of unity + + // Input polynomials (padded to length `n`) + let mut a = vec![1, 2, 3, 4]; + let mut b = vec![4, 5, 6, 7]; + a.resize(n, 0); + b.resize(n, 0); + + // Perform the standard polynomial multiplication + let c_std = polymul(&a, &b, n as i64, modulus); + + // Perform the NTT-based polynomial multiplication + let c_fast = polymul_ntt(&a, &b, n, modulus, omega); + + // Ensure both methods produce the same result + assert_eq!(c_std, c_fast, "The results of polymul and polymul_ntt do not match"); + } + + #[test] + fn test_polymul_ntt_twice_prime_power_modulus() { + let modulus: i64 = 2*(17 as i64).pow(4); // modulus 2*p^k let root: i64 = 3; // Primitive root of unity let n: usize = 8; // Length of the NTT (must be a power of 2) let omega = omega(root, modulus, n); // n-th root of unity From 611a0bf7cdf13113109a698090a0cf244fae4634 Mon Sep 17 00:00:00 2001 From: Jackson Walters Date: Tue, 11 Feb 2025 18:05:47 -0500 Subject: [PATCH 2/2] remove 2*p^k test note that gcd(n,2*p^k) = 2 since n is a power of 2, so n is not invertible. since the divide-and-conquer method requires n be a power of 2, it is currently unclear how to get the NTT working in this case despite the multiplicative group being cyclic. --- src/lib.rs | 10 ++++++++++ src/test.rs | 24 +----------------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 38f5b39..e1bc79f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,14 @@ use reikna::totient::totient; +fn gcd(mut a: i64, mut b: i64) -> i64 { + while b != 0 { + let temp = b; + b = a % b; + a = temp; + } + a.abs() +} + // Modular arithmetic functions using i64 fn mod_add(a: i64, b: i64, p: i64) -> i64 { (a + b) % p @@ -24,6 +33,7 @@ pub fn mod_exp(mut base: i64, mut exp: i64, p: i64) -> i64 { //compute the modular inverse of a modulo p using Fermat's little theorem, p not necessarily prime fn mod_inv(a: i64, p: i64) -> i64 { + assert!(gcd(a, p) == 1, "{} and {} are not coprime", a, p); mod_exp(a, totient(p as u64) as i64 - 1, p) // order of mult. group is Euler's totient function } diff --git a/src/test.rs b/src/test.rs index 4ef6fcd..3553a79 100644 --- a/src/test.rs +++ b/src/test.rs @@ -70,27 +70,5 @@ mod tests { // Ensure both methods produce the same result assert_eq!(c_std, c_fast, "The results of polymul and polymul_ntt do not match"); } - - #[test] - fn test_polymul_ntt_twice_prime_power_modulus() { - let modulus: i64 = 2*(17 as i64).pow(4); // modulus 2*p^k - let root: i64 = 3; // Primitive root of unity - let n: usize = 8; // Length of the NTT (must be a power of 2) - let omega = omega(root, modulus, n); // n-th root of unity - - // Input polynomials (padded to length `n`) - let mut a = vec![1, 2, 3, 4]; - let mut b = vec![4, 5, 6, 7]; - a.resize(n, 0); - b.resize(n, 0); - - // Perform the standard polynomial multiplication - let c_std = polymul(&a, &b, n as i64, modulus); - - // Perform the NTT-based polynomial multiplication - let c_fast = polymul_ntt(&a, &b, n, modulus, omega); - - // Ensure both methods produce the same result - assert_eq!(c_std, c_fast, "The results of polymul and polymul_ntt do not match"); - } + }