extern crate num;
use num::Integer;
use num::traits::ToPrimitive;
fn two_over(n: usize) -> (i8) {
if n % 8 == 1 || n % 8 == 7 { 1 } else { -1 }
}
fn reciprocity(num: usize, den: usize) -> (i8) {
if num % 4 == 3 && den % 4 == 3 { -1 } else { 1 }
}
pub fn jacobi(a: isize, n: isize) -> Option<i8> {
if n.is_even() || n <= 0 {
return None;
}
let mut acc = 1;
let mut num = a.mod_floor(&n).to_usize().unwrap();
let mut den = n as usize;
loop {
num = num % den;
if num == 0 {
return Some(0);
}
while num.is_even() {
acc *= two_over(den);
num /= 2;
}
if num == 1 {
return Some(acc);
}
if num.gcd(&den) > 1 {
return Some(0);
}
acc *= reciprocity(num, den);
let tmp = num;
num = den;
den = tmp;
}
}
#[cfg(test)]
mod tests {
use jacobi;
#[test]
fn minus_one_over_p() {
assert_eq!(Some(1), jacobi(-1, 5));
assert_eq!(Some(1), jacobi(-1, 13));
assert_eq!(Some(-1), jacobi(-1, 3));
assert_eq!(Some(-1), jacobi(-1, 7));
}
#[test]
fn two_over_p() {
assert_eq!(Some(-1), jacobi(2, 3));
assert_eq!(Some(-1), jacobi(2, 5));
assert_eq!(Some(1), jacobi(2, 7));
assert_eq!(Some(1), jacobi(2, 17)); }
#[test]
fn three_over_p() {
assert_eq!(Some(0), jacobi(3, 3));
assert_eq!(Some(-1), jacobi(3, 5));
assert_eq!(Some(-1), jacobi(3, 7));
}
#[test]
fn periodicity() {
assert_eq!(jacobi(3,5), jacobi(-2,5));
assert_eq!(jacobi(-1,5), jacobi(4,5));
assert_eq!(jacobi(11,7), jacobi(4,7));
assert_eq!(jacobi(-3,7), jacobi(4,7));
assert_eq!(jacobi(10,7), jacobi(3,7));
}
#[test]
fn jacobi_simple() { assert_eq!(Some(-1), jacobi(2, 45)); assert_eq!(Some(0), jacobi(3, 45)); assert_eq!(Some(-1), jacobi(7, 45)); assert_eq!(Some(1), jacobi(2, 15)); assert_eq!(Some(-1), jacobi(1001, 9907)); }
#[test]
fn even_moduli_fails() {
assert_eq!(None, jacobi(2, 4));
}
#[test]
fn negative_moduli_fails() {
assert_eq!(None, jacobi(2, -3));
}
}