]> www.ginac.de Git - cln.git/blob - src/numtheory/cl_nt_sqrtmodp.cc
098c4065e0803c1146fe63d473dd43f4b6076ffe
[cln.git] / src / numtheory / cl_nt_sqrtmodp.cc
1 // sqrt_mod_p().
2
3 // General includes.
4 #include "cl_sysdep.h"
5
6 // Specification.
7 #include "cln/numtheory.h"
8
9
10 // Implementation.
11
12 #include "cl_I.h"
13 #include "cln/abort.h"
14
15 #undef floor
16 #include <cmath>
17 #define floor cln_floor
18
19 namespace cln {
20
21 // Algorithm 1 (for very small p only):
22 // Try different values.
23 // Assume p is prime and a nonzero square in Z/pZ.
24 static uint32 search_sqrt (uint32 p, uint32 a)
25 {
26         var uint32 x = 1;
27         var uint32 x2 = 1;
28         loop {
29                 // 0 < x <= p/2, x2 = x^2 mod p.
30                 if (x2 == a)
31                         return x;
32                 x2 += x; x++; x2 += x;
33                 if (x2 >= p)
34                         x2 -= p;
35         }
36 }
37
38 // Algorithm 2 (for p > 2 only):
39 // Cantor-Zassenhaus.
40 // [Beth et al.: Computer Algebra, 1988, Kapitel 5.3.3.]
41 // [Cohen, A Course in Computational Algebraic Number Theory,
42 //  Section 3.4.4., Algorithm 3.4.6.]
43 // Input: R = Z/pZ with p>2, and a (nonzero square in R).
44 static const sqrt_mod_p_t cantor_zassenhaus_sqrt (const cl_modint_ring& R, const cl_MI& a);
45         // Compute in the polynomial ring R[X]/(X^2-a).
46         struct pol2 {
47                 // A polynomial c0+c1*X mod (X^2-a)
48                 cl_MI c0;
49                 cl_MI c1;
50                 // Constructor.
51                 pol2 (const cl_MI& _c0, const cl_MI& _c1) : c0 (_c0), c1 (_c1) {}
52         };
53         struct pol2ring {
54                 const cl_modint_ring& R;
55                 const cl_MI& a;
56                 const pol2 zero ()
57                 {
58                         return pol2(R->zero(),R->zero());
59                 }
60                 const pol2 one ()
61                 {
62                         return pol2(R->one(),R->zero());
63                 }
64                 const pol2 plus (const pol2& u, const pol2& v)
65                 {
66                         return pol2(u.c0+v.c0, u.c1+v.c1);
67                 }
68                 const pol2 minus (const pol2& u, const pol2& v)
69                 {
70                         return pol2(u.c0-v.c0, u.c1-v.c1);
71                 }
72                 const pol2 mul (const pol2& u, const pol2& v)
73                 {
74                         return pol2(u.c0*v.c0+u.c1*v.c1*a, u.c0*v.c1+u.c1*v.c0);
75                 }
76                 const pol2 square (const pol2& u)
77                 {
78                         return pol2(cln::square(u.c0) + cln::square(u.c1)*a, (u.c0*u.c1)<<1);
79                 }
80                 const pol2 expt_pos (const pol2& x, const cl_I& y)
81                 {
82                         // Right-Left Binary, [Cohen, Algorithm 1.2.1.]
83                         var pol2 a = x;
84                         var cl_I b = y;
85                         while (!oddp(b)) { a = square(a); b = b = b >> 1; } // a^b = x^y
86                         var pol2 c = a;
87                         until (eq(b,1)) {
88                                 b = b >> 1;
89                                 a = square(a);
90                                 // a^b*c = x^y
91                                 if (oddp(b))
92                                         c = mul(a,c);
93                         }
94                         return c;
95                 }
96                 const pol2 random ()
97                 {
98                         return pol2(R->random(),R->random());
99                 }
100                 // Computes the degree of gcd(u(X),X^2-a) and, if it is 1,
101                 // also the zero if this polynomial of degree 1.
102                 struct gcd_result {
103                         cl_composite_condition* condition;
104                         int gcd_degree;
105                         cl_MI solution;
106                         // Constructors.
107                         gcd_result (cl_composite_condition* c) : condition (c) {}
108                         gcd_result (int deg) : condition (NULL), gcd_degree (deg) {}
109                         gcd_result (int deg, const cl_MI& sol) : condition (NULL), gcd_degree (deg), solution (sol) {}
110                 };
111                 const gcd_result gcd (const pol2& u)
112                 {
113                         if (zerop(u.c1))
114                                 // constant polynomial u(X)
115                                 if (zerop(u.c0))
116                                         return gcd_result(2);
117                                 else
118                                         return gcd_result(0);
119                         // u(X) = c0 + c1*X has zero -c0/c1.
120                         var cl_MI_x c1inv = R->recip(u.c1);
121                         if (c1inv.condition)
122                                 return c1inv.condition;
123                         var cl_MI z = -u.c0*c1inv;
124                         if (cln::square(z) == a)
125                                 return gcd_result(1,z);
126                         else
127                                 return gcd_result(0);
128                 }
129                 // Constructor.
130                 pol2ring (const cl_modint_ring& _R, const cl_MI& _a) : R (_R), a (_a) {}
131         };
132 static const sqrt_mod_p_t cantor_zassenhaus_sqrt (const cl_modint_ring& R, const cl_MI& a)
133 {
134         var pol2ring PR = pol2ring(R,a);
135         var cl_I& p = R->modulus;
136         // Assuming p is a prime, then R[X]/(X^2-a) is the direct product of
137         // two rings R[X]/(X-sqrt(a)), each being isomorphic to R. Thus taking
138         // a (p-1)/2-th power in this ring will return one of (0,+1,-1) in
139         // each ring, with independent probabilities (1/p, (p-1)/2p, (p-1)/2p).
140         // For any polynomial u(X), setting v(X) := u(X)^((p-1)/2) yields
141         // gcd(u(X),X^2-a) * gcd(v(X)-1,X^2-a) * gcd(v(X)+1,X^2-a) = X^2-a.
142         // If p is not prime, all of these gcd's are likely to be 1.
143         var cl_I e = (p-1) >> 1;
144         loop {
145                 // Choose a random polynomial u(X) in the ring.
146                 var pol2 u = PR.random();
147                 // Compute v(X) = u(X)^((p-1)/2).
148                 var pol2 v = PR.expt_pos(u,e);
149                 // Compute the three gcds.
150                 var pol2ring::gcd_result g1 = PR.gcd(PR.minus(v,PR.one()));
151                 if (g1.condition)
152                         return g1.condition;
153                 if (g1.gcd_degree == 1)
154                         return sqrt_mod_p_t(2,g1.solution,-g1.solution);
155                 if (g1.gcd_degree == 2)
156                         continue;
157                 var pol2ring::gcd_result g2 = PR.gcd(PR.plus(v,PR.one()));
158                 if (g2.condition)
159                         return g2.condition;
160                 if (g2.gcd_degree == 1)
161                         return sqrt_mod_p_t(2,g2.solution,-g2.solution);
162                 if (g2.gcd_degree == 2)
163                         continue;
164                 var pol2ring::gcd_result g3 = PR.gcd(u);
165                 if (g3.condition)
166                         return g3.condition;
167                 if (g3.gcd_degree == 1)
168                         return sqrt_mod_p_t(2,g3.solution,-g3.solution);
169                 if (g1.gcd_degree + g2.gcd_degree + g3.gcd_degree < 2)
170                         // If the sum of the degrees of the gcd is != 2,
171                         // p cannot be prime.
172                         return new cl_composite_condition(p);
173         }
174 }
175
176 #if defined(__GNUC__) && defined(__s390__) && (__GNUC__ == 2)  // Workaround GCC-bug (see below)
177                 struct cl_sylow2gen_property : public cl_property {
178                         SUBCLASS_cl_property();
179                 public:
180                         cl_I h_rep;
181                         // Constructor.
182                         cl_sylow2gen_property (const cl_symbol& k, const cl_MI& h) : cl_property (k), h_rep (h.rep) {}
183                 };
184 #endif
185
186 // Algorithm 3 (for p > 2 only):
187 // Tonelli-Shanks.
188 // [Cohen, A Course in Computational Algebraic Number Theory,
189 //  Section 1.5.1., Algorithm 1.5.1.]
190 static const sqrt_mod_p_t tonelli_shanks_sqrt (const cl_modint_ring& R, const cl_MI& a)
191 {
192         // Idea:
193         // Write p-1 = 2^e*m, m odd. G = (Z/pZ)^* (cyclic of order p-1) has
194         // subgroups G_0 < G_1 < ... < G_e, G_j of order 2^j. (G_e is called
195         // the "2-Sylow subgroup" of G.) More precisely
196         //          G_j = { x in (Z/pZ)^* : x^(2^j) = 1 },
197         //        G/G_j = { x^(2^j) : x in (Z/pZ)^* }.
198         // We compute the square root of a first in G/G_e, then lift it to
199         // G/G_(e-1), etc., up to G/G_0.
200         // Start with b = a^((m+1)/2), then (a^-1*b^2)^(2^e) = 1, i.e.
201         // a = b^2 in G/G_e.
202         // Lifting from G/G_j to G/G_(j-1) is easy: Assume a = b^2 in G/G_j.
203         // If a = b^2 in G/G_(j-1), then nothing needs to be done. Else
204         // a^-1*b^2 is in G_j \ G_(j-1). If j=e, a^-1*b^2 is a non-square
205         // mod p, hence a is a non-square as well, contradiction. If j<e,
206         // take h in G_(j+1) \ G_j, so that h^2 in G_j \ G_(j-1), and
207         // a^-1*b^2*h^2 is in G_(j-1). So multiply b with h.
208         var cl_I& p = R->modulus;
209         var uintL e = ord2(p-1);
210         var cl_I m = (p-1) >> e;
211         // p-1 = 2^e*m, m odd.
212         // We will have the invariant c = a^-1*b^2 in G/G_j.
213         var uintL j = e;
214         // Initialize b = a^((m+1)/2), c = a^m, but avoid to divide by a.
215         var cl_MI c = R->expt_pos(a,(m-1)>>1);
216         var cl_MI b = R->mul(a,c);
217         c = R->mul(b,c);
218         // Find h in G_e \ G_(e-1): h = h'^m, where h' is any non-square.
219         var cl_MI h;
220         if (e==1)
221                 h = - R->one();
222         else {
223                 // Since this computation is a bit costly, we cache its result
224                 // on the ring's property list.
225                 static const cl_symbol key = (cl_symbol)(cl_string)"generator of 2-Sylow subgroup of (Z/pZ)^*";
226 #if !(defined(__GNUC__) && defined(__s390__) && (__GNUC__ == 2))  // Workaround GCC-bug (see above)
227                 struct cl_sylow2gen_property : public cl_property {
228                         SUBCLASS_cl_property();
229                 public:
230                         cl_I h_rep;
231                         // Constructor.
232                         cl_sylow2gen_property (const cl_symbol& k, const cl_MI& h) : cl_property (k), h_rep (h.rep) {}
233                 };
234 #endif
235                 var cl_sylow2gen_property* prop = (cl_sylow2gen_property*) R->get_property(key);
236                 if (prop)
237                         h = cl_MI(R,prop->h_rep);
238                 else {
239                         do { h = R->random(); }
240                            until (jacobi(R->retract(h),p) == -1);
241                         h = R->expt_pos(h,m);
242                         R->add_property(new cl_sylow2gen_property(key,h));
243                 }
244         }
245         do {
246                 // Now c = a^-1*b^2 in G_j, h in G_j \ G_(j-1).
247                 // Determine the smallest i such that c in G_i.
248                 var uintL i = 0;
249                 var cl_MI ci = c; // c_i = c^(2^i)
250                 for ( ; i < j; i++, ci = R->square(ci))
251                         if (ci == R->one())
252                                 break;
253                 if (i==j)
254                         // Some problem: if j=e, a non-square, if j<e, the
255                         // previous iteration didn't do its job correctly.
256                         // Indicates that p is not prime.
257                         return new cl_composite_condition(p);
258                 // OK, i < j.
259                 for (var uintL count = j-i-1; count > 0; count--)
260                         h = R->square(h);
261                 // Now h in G_(i+1) \ G_i.
262                 b = R->mul(b,h);
263                 h = R->square(h);
264                 c = R->mul(c,h);
265                 // Now c = a^-1*b^2 in G_(i-1), h in G_i \ G_(i-1).
266                 j = i;
267         } while (j > 0);
268         if (R->square(b) != a)
269                 // Problem again.
270                 return new cl_composite_condition(p);
271         return sqrt_mod_p_t(2,b,-b);
272 }
273
274 // Break-Even-Points (on a i486 with 33 MHz):
275 // Algorithm 1 fastest for p < 1500..2000
276 // Algorithm 3 generally fastest for p > 2000.
277 // But the running time of algorithm 3 is proportional to e^2.
278 // For large e, algorithm 2 becomes faster.
279 // l=50 bits: for e >= 40
280 // l=100 bits: for e >= 55
281 // l=200 bits: for e >= 80
282 // l=400 bits: for e >= 130
283 // in general something like  e > l/(log(l)/(2*log(2))-1).
284
285 const sqrt_mod_p_t sqrt_mod_p (const cl_modint_ring& R, const cl_MI& a)
286 {
287         if (!(a.ring() == R)) cl_abort();
288         var cl_I& p = R->modulus;
289         var cl_I aa = R->retract(a);
290         switch (jacobi(aa,p)) {
291                 case -1: // no solution
292                         return sqrt_mod_p_t(0);
293                 case 0: // gcd(aa,p) > 1
294                         if (zerop(a))
295                                 // one solution
296                                 return sqrt_mod_p_t(1,a);
297                         else
298                                 // found factor of p
299                                 return new cl_composite_condition(p,gcd(aa,p));
300                 case 1: // two solutions
301                         break;
302         }
303         if (p < 2000) {
304                 // Algorithm 1.
305                 var cl_I x1 = search_sqrt(cl_I_to_UL(p),cl_I_to_UL(aa));
306                 var cl_I x2 = p-x1;
307                 if (x1==x2) // can only happen when p = 2
308                         return sqrt_mod_p_t(1,R->canonhom(x1));
309                 else
310                         return sqrt_mod_p_t(2,R->canonhom(x1),R->canonhom(x2));
311         }
312         var uintL l = integer_length(p);
313         var uintL e = ord2(p-1);
314         //if (e > 30 && e > l/(log((double)l)*0.72-1))
315         if (e > 30 && e > l/(::log((double)l)*0.92-2.41))
316                 // Algorithm 2.
317                 return cantor_zassenhaus_sqrt(R,a);
318         else
319                 // Algorithm 3.
320                 return tonelli_shanks_sqrt(R,a);
321 }
322
323 }  // namespace cln