crypto_bigint/int/
mod_symbol.rs1use crate::{Int, JacobiSymbol, Odd, Uint, word};
4
5impl<const LIMBS: usize> Int<LIMBS> {
6 #[must_use]
11 #[allow(clippy::cast_possible_truncation)]
12 pub const fn jacobi_symbol<const RHS_LIMBS: usize>(
13 &self,
14 rhs: &Odd<Uint<RHS_LIMBS>>,
15 ) -> JacobiSymbol {
16 let (abs, sign) = self.abs_sign();
17 let jacobi = abs.jacobi_symbol(rhs) as i64;
18 let swap = sign.and(word::choice_from_eq(rhs.as_ref().limbs[0].0 & 3, 3));
20 JacobiSymbol::from_i8(swap.select_i64(jacobi, -jacobi) as i8)
21 }
22
23 #[must_use]
30 pub const fn jacobi_symbol_vartime<const RHS_LIMBS: usize>(
31 &self,
32 rhs: &Odd<Uint<RHS_LIMBS>>,
33 ) -> JacobiSymbol {
34 let (abs, sign) = self.abs_sign();
35 let jacobi = abs.jacobi_symbol_vartime(rhs);
36 JacobiSymbol::from_i8(if sign.to_bool() && rhs.as_ref().limbs[0].0 & 3 == 3 {
37 -(jacobi as i8)
38 } else {
39 jacobi as i8
40 })
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use crate::{I64, I256, JacobiSymbol, U64, U256};
47
48 #[test]
49 fn jacobi_quad_residue() {
50 let f = I256::from(59i32 * 67);
53 let g = U256::from(61u32 * 71).to_odd().unwrap();
54 let res = f.jacobi_symbol(&g);
55 let res_vartime = f.jacobi_symbol_vartime(&g);
56 assert_eq!(res, JacobiSymbol::One);
57 assert_eq!(res, res_vartime);
58 }
59
60 #[test]
61 fn jacobi_non_quad_residue() {
62 let f = I256::from(59i32 * 67 + 2);
65 let g = U256::from(61u32 * 71).to_odd().unwrap();
66 let res = f.jacobi_symbol(&g);
67 let res_vartime = f.jacobi_symbol_vartime(&g);
68 assert_eq!(res, JacobiSymbol::MinusOne);
69 assert_eq!(res, res_vartime);
70 }
71
72 #[test]
73 fn jacobi_non_coprime() {
74 let f = I256::from(4391633i32);
75 let g = U256::from(2022161u32).to_odd().unwrap();
76 let res = f.jacobi_symbol(&g);
77 let res_vartime = f.jacobi_symbol_vartime(&g);
78 assert_eq!(res, JacobiSymbol::Zero);
79 assert_eq!(res, res_vartime);
80 }
81
82 #[test]
83 fn jacobi_zero() {
84 assert_eq!(
85 I256::ZERO.jacobi_symbol(&U256::ONE.to_odd().unwrap()),
86 JacobiSymbol::One
87 );
88 assert_eq!(
89 I256::ZERO.jacobi_symbol_vartime(&U256::ONE.to_odd().unwrap()),
90 JacobiSymbol::One
91 );
92 assert_eq!(
93 I64::ZERO.jacobi_symbol_vartime(&U256::ONE.to_odd().unwrap()),
94 JacobiSymbol::One
95 );
96 assert_eq!(
97 I256::ZERO.jacobi_symbol_vartime(&U64::ONE.to_odd().unwrap()),
98 JacobiSymbol::One
99 );
100 }
101
102 #[test]
103 fn jacobi_neg_one() {
104 let f = I256::MINUS_ONE;
105 assert_eq!(
106 f.jacobi_symbol(&U256::ONE.to_odd().unwrap()),
107 JacobiSymbol::One
108 );
109 assert_eq!(
110 f.jacobi_symbol_vartime(&U256::ONE.to_odd().unwrap()),
111 JacobiSymbol::One
112 );
113 assert_eq!(
114 I64::MINUS_ONE.jacobi_symbol_vartime(&U256::ONE.to_odd().unwrap()),
115 JacobiSymbol::One
116 );
117 assert_eq!(
118 f.jacobi_symbol(&U256::from(3u8).to_odd().unwrap()),
119 JacobiSymbol::MinusOne
120 );
121 assert_eq!(
122 f.jacobi_symbol_vartime(&U256::from(3u8).to_odd().unwrap()),
123 JacobiSymbol::MinusOne
124 );
125 assert_eq!(
126 I64::MINUS_ONE.jacobi_symbol_vartime(&U256::from(3u8).to_odd().unwrap()),
127 JacobiSymbol::MinusOne
128 );
129 }
130
131 #[test]
132 fn jacobi_int() {
133 let f = I256::from(59i32 * 67);
136 let g = U256::from(61u32 * 71).to_odd().unwrap();
137 let res = f.jacobi_symbol(&g);
138 let res_vartime = f.jacobi_symbol_vartime(&g);
139 assert_eq!(res, JacobiSymbol::One);
140 assert_eq!(res, res_vartime);
141
142 let res = f.checked_neg().unwrap().jacobi_symbol(&g);
143 let res_vartime = f.checked_neg().unwrap().jacobi_symbol_vartime(&g);
144 assert_eq!(res, JacobiSymbol::MinusOne);
145 assert_eq!(res, res_vartime);
146 }
147}