Pollard's rho algorithm for computing discrete logs.
[u/mdw/catacomb] / mp-gcd.c
CommitLineData
d3409d5e 1/* -*-c-*-
2 *
c9110b35 3 * $Id: mp-gcd.c,v 1.4 2000/06/17 11:34:46 mdw Exp $
d3409d5e 4 *
5 * Extended GCD calculation
6 *
7 * (c) 1999 Straylight/Edgeware
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Catacomb.
13 *
14 * Catacomb is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
18 *
19 * Catacomb is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
25 * License along with Catacomb; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27 * MA 02111-1307, USA.
28 */
29
30/*----- Revision history --------------------------------------------------*
31 *
32 * $Log: mp-gcd.c,v $
c9110b35 33 * Revision 1.4 2000/06/17 11:34:46 mdw
34 * More hacking for the signs of the outputs.
35 *
ef5f4810 36 * Revision 1.3 1999/12/10 23:18:39 mdw
37 * Change interface for suggested destinations.
38 *
da83a561 39 * Revision 1.2 1999/11/22 20:49:56 mdw
40 * Fix bug which failed to favour `x' when `y' wasn't wanted and the two
41 * arguments needed swapping.
42 *
d3409d5e 43 * Revision 1.1 1999/11/17 18:02:16 mdw
44 * New multiprecision integer arithmetic suite.
45 *
46 */
47
48/*----- Header files ------------------------------------------------------*/
49
50#include "mp.h"
51
52/*----- Main code ---------------------------------------------------------*/
53
54/* --- @mp_gcd@ --- *
55 *
56 * Arguments: @mp **gcd, **xx, **yy@ = where to write the results
57 * @mp *a, *b@ = sources (must be nonzero)
58 *
59 * Returns: ---
60 *
61 * Use: Calculates @gcd(a, b)@, and two numbers @x@ and @y@ such that
62 * @ax + by = gcd(a, b)@. This is useful for computing modular
ef5f4810 63 * inverses.
d3409d5e 64 */
65
66void mp_gcd(mp **gcd, mp **xx, mp **yy, mp *a, mp *b)
67{
68 mp *X = MP_ONE, *Y = MP_ZERO;
69 mp *x = MP_ZERO, *y = MP_ONE;
70 mp *u, *v;
71 size_t shift = 0;
ef5f4810 72 unsigned f = 0;
73
74 enum {
75 f_swap = 1u,
76 f_aneg = 2u,
77 f_bneg = 4u,
78 f_ext = 8u
79 };
80
81 /* --- Sort out some initial flags --- */
d3409d5e 82
ef5f4810 83 if (xx || yy)
84 f |= f_ext;
d3409d5e 85
ef5f4810 86 if (a->f & MP_NEG)
87 f |= f_aneg;
88 if (b->f & MP_NEG)
89 f |= f_bneg;
90
91 /* --- Ensure that @a@ is larger than @b@ --- *
92 *
93 * Use absolute values here!
94 */
95
96 if (MPX_UCMP(a->v, a->vl, <, b->v, b->vl)) {
d3409d5e 97 { mp *t = a; a = b; b = t; }
ef5f4810 98 f |= f_swap;
99 }
100
101 /* --- Check for zeroness --- */
102
103 if (MP_CMP(b, ==, MP_ZERO)) {
104
105 /* --- Store %$|a|$% as the GCD --- */
106
107 if (gcd) {
108 if (*gcd) MP_DROP(*gcd);
109 a = MP_COPY(a);
110 if (a->f & MP_NEG) {
111 MP_SPLIT(a);
112 a->f &= ~MP_NEG;
113 f |= f_aneg;
114 }
115 *gcd = a;
116 }
117
118 /* --- Store %$1$% and %$0$% in the appropriate bins --- */
119
120 if (f & f_ext) {
121 if (f & f_swap) {
122 mp **t = xx; xx = yy; yy = t;
123 }
124 if (xx) {
125 if (*xx) MP_DROP(*xx);
126 if (MP_CMP(a, ==, MP_ZERO))
127 *xx = MP_ZERO;
128 else if (f & f_aneg)
129 *xx = MP_MONE;
130 else
131 *xx = MP_ONE;
132 }
133 if (yy) {
134 if (*yy) MP_DROP(*yy);
135 *yy = MP_ZERO;
136 }
137 }
138 return;
d3409d5e 139 }
140
141 /* --- Take a reference to the arguments --- */
142
143 a = MP_COPY(a);
144 b = MP_COPY(b);
145
146 /* --- Make sure @a@ and @b@ are not both even --- */
147
ef5f4810 148 MP_SPLIT(a); a->f &= ~MP_NEG;
149 MP_SPLIT(b); b->f &= ~MP_NEG;
150
d3409d5e 151 if (((a->v[0] | b->v[0]) & 1) == 0) {
152 mpscan asc, bsc;
153
154 /* --- Break off my copies --- */
155
d3409d5e 156 MP_SCAN(&asc, a);
157 MP_SCAN(&bsc, b);
158
159 /* --- Start scanning --- */
160
161 for (;;) {
162 if (!MP_STEP(&asc) || !MP_STEP(&bsc))
163 assert(((void)"zero argument passed to mp_gcd", 0));
164 if (MP_BIT(&asc) || MP_BIT(&bsc))
165 break;
166 shift++;
167 }
168
169 /* --- Shift @a@ and @b@ down --- */
170
171 a = mp_lsr(a, a, shift);
172 b = mp_lsr(b, b, shift);
173 }
174
175 /* --- Set up @u@ and @v@ --- */
176
177 u = MP_COPY(a);
178 v = MP_COPY(b);
179
180 /* --- Start the main loop --- */
181
182 for (;;) {
183
184 /* --- While @u@ is even --- */
185
186 {
187 mpscan sc, xsc, ysc;
188 size_t n = 0, nn = 0;
189
190 MP_SCAN(&sc, u);
191 MP_SCAN(&xsc, X); MP_SCAN(&ysc, Y);
192 for (;;) {
193 MP_STEP(&sc);
194 MP_STEP(&xsc); MP_STEP(&ysc);
195 if (MP_BIT(&sc))
196 break;
ef5f4810 197 if ((f & f_ext) && (MP_BIT(&xsc) | MP_BIT(&ysc))) {
d3409d5e 198 if (n) {
199 X = mp_lsr(X, X, n);
200 Y = mp_lsr(Y, Y, n);
201 n = 0;
202 }
203 X = mp_add(X, X, b);
204 Y = mp_sub(Y, Y, a);
205 MP_SCAN(&xsc, X);
206 MP_SCAN(&ysc, Y);
207 MP_STEP(&xsc); MP_STEP(&ysc);
208 }
209 n++; nn++;
210 }
211
212 if (nn) {
213 u = mp_lsr(u, u, nn);
ef5f4810 214 if ((f & f_ext) && n) {
d3409d5e 215 X = mp_lsr(X, X, n);
216 Y = mp_lsr(Y, Y, n);
217 }
218 }
219 }
220
221 /* --- While @v@ is even --- */
222
223 {
224 mpscan sc, xsc, ysc;
225 size_t n = 0, nn = 0;
226
227 MP_SCAN(&sc, v);
228 MP_SCAN(&xsc, x); MP_SCAN(&ysc, y);
229 for (;;) {
230 MP_STEP(&sc);
231 MP_STEP(&xsc); MP_STEP(&ysc);
232 if (MP_BIT(&sc))
233 break;
ef5f4810 234 if ((f & f_ext) && (MP_BIT(&xsc) | MP_BIT(&ysc))) {
d3409d5e 235 if (n) {
236 x = mp_lsr(x, x, n);
237 y = mp_lsr(y, y, n);
238 n = 0;
239 }
240 x = mp_add(x, x, b);
241 y = mp_sub(y, y, a);
242 MP_SCAN(&xsc, x);
243 MP_SCAN(&ysc, y);
244 MP_STEP(&xsc); MP_STEP(&ysc);
245 }
246 n++; nn++;
247 }
248
249 if (nn) {
250 v = mp_lsr(v, v, nn);
ef5f4810 251 if ((f & f_ext) && n) {
d3409d5e 252 x = mp_lsr(x, x, n);
253 y = mp_lsr(y, y, n);
254 }
255 }
256 }
257
258 /* --- End-of-loop fiddling --- */
259
260 if (MP_CMP(u, >=, v)) {
261 u = mp_sub(u, u, v);
ef5f4810 262 if (f & f_ext) {
d3409d5e 263 X = mp_sub(X, X, x);
264 Y = mp_sub(Y, Y, y);
265 }
266 } else {
267 v = mp_sub(v, v, u);
ef5f4810 268 if (f & f_ext) {
d3409d5e 269 x = mp_sub(x, x, X);
270 y = mp_sub(y, y, Y);
271 }
272 }
273
274 if (MP_CMP(u, ==, MP_ZERO))
275 break;
276 }
277
278 /* --- Write the results out --- */
279
ef5f4810 280 if (!gcd)
d3409d5e 281 MP_DROP(v);
ef5f4810 282 else {
283 if (*gcd) MP_DROP(*gcd);
284 *gcd = mp_lsl(v, v, shift);
285 }
d3409d5e 286
287 /* --- Perform a little normalization --- *
288 *
289 * Ensure that the coefficient returned is positive, if there is only one.
ef5f4810 290 * If there are two, favour @y@. Of course, if the original arguments were
291 * negative then I'll need to twiddle their signs as well.
d3409d5e 292 */
293
ef5f4810 294 if (f & f_ext) {
295
296 /* --- If @a@ and @b@ got swapped, swap the coefficients back --- */
297
298 if (f & f_swap) {
d3409d5e 299 mp *t = x; x = y; y = t;
da83a561 300 t = a; a = b; b = t;
d3409d5e 301 }
ef5f4810 302
303 /* --- Sort out the signs --- *
304 *
305 * Note that %$ax + by = a(x - b) + b(y + a)$%.
c9110b35 306 *
307 * This is currently bodgy. It needs sorting out at some time.
ef5f4810 308 */
309
d3409d5e 310 if (yy) {
311 if (y->f & MP_NEG) {
c9110b35 312 do {
313 y = mp_add(y, y, a);
314 x = mp_sub(x, x, b);
315 } while (y->f & MP_NEG);
316 } else {
317 while (MP_CMP(y, >=, a)) {
318 y = mp_sub(y, y, a);
319 x = mp_add(x, x, b);
320 }
d3409d5e 321 }
c9110b35 322 } else {
323 if (x->f & MP_NEG) {
324 do
325 x = mp_add(x, x, b);
326 while (x->f & MP_NEG);
327 } else {
328 while (MP_CMP(x, >=, b))
329 x = mp_sub(x, x, b);
330 }
331 }
d3409d5e 332
ef5f4810 333 /* --- Twiddle the signs --- */
334
335 if (f & f_aneg)
336 x->f ^= MP_NEG;
337 if (f & f_bneg)
338 y->f ^= MP_NEG;
339
340 /* --- Store the results --- */
341
342 if (!xx)
343 MP_DROP(x);
344 else {
345 if (*xx) MP_DROP(*xx);
346 *xx = x;
347 }
348
349 if (!yy)
350 MP_DROP(y);
351 else {
352 if (*yy) MP_DROP(*yy);
353 *yy = y;
354 }
d3409d5e 355 }
356
357 MP_DROP(u);
358 MP_DROP(X); MP_DROP(Y);
359 MP_DROP(a); MP_DROP(b);
360}
361
362/*----- Test rig ----------------------------------------------------------*/
363
364#ifdef TEST_RIG
365
366static int gcd(dstr *v)
367{
368 int ok = 1;
369 mp *a = *(mp **)v[0].buf;
370 mp *b = *(mp **)v[1].buf;
371 mp *g = *(mp **)v[2].buf;
372 mp *x = *(mp **)v[3].buf;
373 mp *y = *(mp **)v[4].buf;
374
ef5f4810 375 mp *gg = MP_NEW, *xx = MP_NEW, *yy = MP_NEW;
d3409d5e 376 mp_gcd(&gg, &xx, &yy, a, b);
377 if (MP_CMP(x, !=, xx)) {
378 fputs("\n*** mp_gcd(x) failed", stderr);
379 fputs("\na = ", stderr); mp_writefile(a, stderr, 10);
380 fputs("\nb = ", stderr); mp_writefile(b, stderr, 10);
381 fputs("\nexpect = ", stderr); mp_writefile(x, stderr, 10);
382 fputs("\nresult = ", stderr); mp_writefile(xx, stderr, 10);
383 fputc('\n', stderr);
384 ok = 0;
385 }
386 if (MP_CMP(y, !=, yy)) {
387 fputs("\n*** mp_gcd(y) failed", stderr);
388 fputs("\na = ", stderr); mp_writefile(a, stderr, 10);
389 fputs("\nb = ", stderr); mp_writefile(b, stderr, 10);
390 fputs("\nexpect = ", stderr); mp_writefile(y, stderr, 10);
391 fputs("\nresult = ", stderr); mp_writefile(yy, stderr, 10);
392 fputc('\n', stderr);
393 ok = 0;
394 }
395
396 if (!ok) {
397 mp *ax = mp_mul(MP_NEW, a, xx);
398 mp *by = mp_mul(MP_NEW, b, yy);
399 ax = mp_add(ax, ax, by);
400 if (MP_CMP(ax, ==, gg))
401 fputs("\n*** (Alternative result found.)\n", stderr);
402 MP_DROP(ax);
403 MP_DROP(by);
404 }
405
406 if (MP_CMP(g, !=, gg)) {
407 fputs("\n*** mp_gcd(gcd) failed", stderr);
408 fputs("\na = ", stderr); mp_writefile(a, stderr, 10);
409 fputs("\nb = ", stderr); mp_writefile(b, stderr, 10);
410 fputs("\nexpect = ", stderr); mp_writefile(g, stderr, 10);
411 fputs("\nresult = ", stderr); mp_writefile(gg, stderr, 10);
412 fputc('\n', stderr);
413 ok = 0;
414 }
415 MP_DROP(a); MP_DROP(b); MP_DROP(g); MP_DROP(x); MP_DROP(y);
416 MP_DROP(gg); MP_DROP(xx); MP_DROP(yy);
ef5f4810 417 assert(mparena_count(MPARENA_GLOBAL) == 0);
d3409d5e 418 return (ok);
419}
420
421static test_chunk tests[] = {
422 { "gcd", gcd, { &type_mp, &type_mp, &type_mp, &type_mp, &type_mp, 0 } },
423 { 0, 0, { 0 } }
424};
425
426int main(int argc, char *argv[])
427{
428 sub_init();
429 test_run(argc, argv, tests, SRCDIR "/tests/mp");
430 return (0);
431}
432
433#endif
434
435/*----- That's all, folks -------------------------------------------------*/