crypto_bigint/uint/
mod_symbol.rs1use crate::{JacobiSymbol, Odd, Uint};
4
5impl<const LIMBS: usize> Uint<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 if LIMBS > RHS_LIMBS {
17 let a = self.rem(rhs.as_nz_ref());
19 return a.jacobi_symbol(rhs);
20 }
21
22 let (gcd, jacobi_neg) = rhs.classic_bingcd_(&self.resize());
23
24 let jacobi = (jacobi_neg as i8 * -2 + 1) as i64;
27 JacobiSymbol::from_i8(Uint::eq(gcd.as_ref(), &Uint::ONE).select_i64(0, jacobi) as i8)
28 }
29
30 #[must_use]
37 #[allow(clippy::cast_possible_truncation)]
38 pub const fn jacobi_symbol_vartime<const RHS_LIMBS: usize>(
39 &self,
40 rhs: &Odd<Uint<RHS_LIMBS>>,
41 ) -> JacobiSymbol {
42 if LIMBS > RHS_LIMBS {
43 let a = self.rem_vartime(rhs.as_nz_ref());
45 return a.jacobi_symbol_vartime(rhs);
46 } else if RHS_LIMBS > LIMBS {
47 if self.is_zero_vartime() {
48 return if rhs.as_ref().cmp_vartime(&Uint::ONE).is_eq() {
49 JacobiSymbol::One
50 } else {
51 JacobiSymbol::Zero
52 };
53 } else {
54 let mut jacobi_neg = 0;
56
57 let tail = self.trailing_zeros_vartime();
58 if tail & 1 == 1 {
59 let b_lo = rhs.as_ref().limbs[0].0;
62 jacobi_neg ^= ((b_lo >> 1) ^ (b_lo >> 2)) & 1;
63 }
64
65 let b = self
67 .shr_vartime(tail)
68 .to_odd()
69 .expect_copied("ensured non-zero");
70 let a_b_mod_4 = b.as_ref().limbs[0].0 & rhs.as_ref().limbs[0].0 & 3;
71 jacobi_neg ^= a_b_mod_4 & (a_b_mod_4 >> 1) & 1;
72
73 let a = rhs.as_ref().rem_vartime(b.as_nz_ref());
75 let jacobi_symbol = a.jacobi_symbol_vartime(&b);
76 return if jacobi_neg == 1 {
77 jacobi_symbol.neg()
78 } else {
79 jacobi_symbol
80 };
81 }
82 }
83
84 let (gcd, jacobi_neg) = rhs.classic_bingcd_vartime_(&self.resize());
85 JacobiSymbol::from_i8(if gcd.as_ref().cmp_vartime(&Uint::ONE).is_eq() {
86 jacobi_neg as i8 * -2 + 1
87 } else {
88 0
89 })
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use crate::{JacobiSymbol, U256};
96
97 #[test]
98 fn jacobi_quad_residue() {
99 let f = U256::from(59u32 * 67);
102 let g = U256::from(61u32 * 71).to_odd().unwrap();
103 let res = f.jacobi_symbol(&g);
104 let res_small = f.resize::<1>().jacobi_symbol(&g);
105 let res_large = f.jacobi_symbol(&g.resize::<1>());
106 let res_vartime = f.jacobi_symbol_vartime(&g);
107 let res_vartime_small = f.resize::<1>().jacobi_symbol_vartime(&g);
108 let res_vartime_large = f.jacobi_symbol_vartime(&g.resize::<1>());
109 assert_eq!(res, JacobiSymbol::One);
110 assert_eq!(res, res_small);
111 assert_eq!(res, res_large);
112 assert_eq!(res, res_vartime);
113 assert_eq!(res, res_vartime_small);
114 assert_eq!(res, res_vartime_large);
115 }
116
117 #[test]
118 fn jacobi_non_quad_residue() {
119 let f = U256::from(59u32 * 67 + 2);
122 let g = U256::from(61u32 * 71).to_odd().unwrap();
123 let res = f.jacobi_symbol(&g);
124 let res_small = f.resize::<1>().jacobi_symbol(&g);
125 let res_large = f.jacobi_symbol(&g.resize::<1>());
126 let res_vartime = f.jacobi_symbol_vartime(&g);
127 let res_vartime_small = f.resize::<1>().jacobi_symbol_vartime(&g);
128 let res_vartime_large = f.jacobi_symbol_vartime(&g.resize::<1>());
129 assert_eq!(res, JacobiSymbol::MinusOne);
130 assert_eq!(res, res_small);
131 assert_eq!(res, res_large);
132 assert_eq!(res, res_vartime);
133 assert_eq!(res, res_vartime_small);
134 assert_eq!(res, res_vartime_large);
135 }
136
137 #[test]
138 fn jacobi_non_coprime() {
139 let f = U256::from(4391633u32);
140 let g = U256::from(2022161u32).to_odd().unwrap();
141 let res = f.jacobi_symbol(&g);
142 let res_small = f.resize::<1>().jacobi_symbol(&g);
143 let res_large = f.jacobi_symbol(&g.resize::<1>());
144 let res_vartime = f.jacobi_symbol_vartime(&g);
145 let res_vartime_small = f.resize::<1>().jacobi_symbol_vartime(&g);
146 let res_vartime_large = f.jacobi_symbol_vartime(&g.resize::<1>());
147 assert_eq!(res, JacobiSymbol::Zero);
148 assert_eq!(res, res_small);
149 assert_eq!(res, res_large);
150 assert_eq!(res, res_vartime);
151 assert_eq!(res, res_vartime_small);
152 assert_eq!(res, res_vartime_large);
153 }
154
155 #[test]
156 fn jacobi_zero() {
157 let f = U256::ZERO;
158 let g = U256::ONE.to_odd().unwrap();
159 let res = f.jacobi_symbol(&g);
160 let res_small = f.resize::<1>().jacobi_symbol(&g);
161 let res_large = f.jacobi_symbol(&g.resize::<1>());
162 let res_vartime = f.jacobi_symbol_vartime(&g);
163 let res_vartime_small = f.resize::<1>().jacobi_symbol_vartime(&g);
164 let res_vartime_large = f.jacobi_symbol_vartime(&g.resize::<1>());
165 assert_eq!(res, JacobiSymbol::One);
166 assert_eq!(res, res_small);
167 assert_eq!(res, res_large);
168 assert_eq!(res, res_vartime);
169 assert_eq!(res, res_vartime_small);
170 assert_eq!(res, res_vartime_large);
171 }
172
173 #[test]
174 fn jacobi_one() {
175 let f = U256::ONE;
176 let g = U256::ONE.to_odd().unwrap();
177 let res = f.jacobi_symbol(&g);
178 let res_small = f.resize::<1>().jacobi_symbol(&g);
179 let res_large = f.jacobi_symbol(&g.resize::<1>());
180 let res_vartime = f.jacobi_symbol_vartime(&g);
181 let res_vartime_small = f.resize::<1>().jacobi_symbol_vartime(&g);
182 let res_vartime_large = f.jacobi_symbol_vartime(&g.resize::<1>());
183 assert_eq!(res, JacobiSymbol::One);
184 assert_eq!(res, res_small);
185 assert_eq!(res, res_large);
186 assert_eq!(res, res_vartime);
187 assert_eq!(res, res_vartime_small);
188 assert_eq!(res, res_vartime_large);
189 }
190
191 #[test]
192 fn jacobi_edge() {
194 use crate::{Odd, U256};
195
196 assert_eq!(
197 U256::from_be_hex("0000000000000002108DEAFCB180F023912BEED0186CEEAD593A8507B7DA4E9B")
198 .jacobi_symbol(&Odd::<U256>::from_be_hex(
199 "0000000000000002108DEAFCB180F023912BEED0186CEEED593A8507B7DA4E9B",
200 )),
201 JacobiSymbol::MinusOne
202 );
203 }
204}