1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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) -> i8 {
assert!(n.is_odd(), "jacobi symbol is not defined for even moduli");
assert!(n > 0, "jacobi symbol is not negative moduli");
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 0;
}
while num.is_even() {
acc *= two_over(den);
num /= 2;
}
if num == 1 {
return acc;
}
if num.gcd(&den) > 1 {
return 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!(1, jacobi(-1, 5));
assert_eq!(1, jacobi(-1, 13));
assert_eq!(-1, jacobi(-1, 3));
assert_eq!(-1, jacobi(-1, 7));
}
#[test]
fn two_over_p() {
assert_eq!(-1, jacobi(2, 3));
assert_eq!(-1, jacobi(2, 5));
assert_eq!(1, jacobi(2, 7));
assert_eq!(1, jacobi(2, 17));
}
#[test]
fn three_over_p() {
assert_eq!(0, jacobi(3, 3));
assert_eq!(-1, jacobi(3, 5));
assert_eq!(-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!(-1, jacobi(2, 45));
assert_eq!(0, jacobi(3, 45));
assert_eq!(-1, jacobi(7, 45));
assert_eq!(1, jacobi(2, 15));
assert_eq!(-1, jacobi(1001, 9907));
}
#[test]
#[should_panic]
fn even_moduli_asserts() {
jacobi(2, 4);
}
#[test]
#[should_panic]
fn negative_moduli_asserts() {
jacobi(2, -3);
}
}