Coverage Report

Created: 2025-03-18 06:55

/src/gmp/mpn/gcdext.c
Line
Count
Source (jump to first uncovered line)
1
/* mpn_gcdext -- Extended Greatest Common Divisor.
2
3
Copyright 1996, 1998, 2000-2005, 2008, 2009, 2012 Free Software Foundation,
4
Inc.
5
6
This file is part of the GNU MP Library.
7
8
The GNU MP Library is free software; you can redistribute it and/or modify
9
it under the terms of either:
10
11
  * the GNU Lesser General Public License as published by the Free
12
    Software Foundation; either version 3 of the License, or (at your
13
    option) any later version.
14
15
or
16
17
  * the GNU General Public License as published by the Free Software
18
    Foundation; either version 2 of the License, or (at your option) any
19
    later version.
20
21
or both in parallel, as here.
22
23
The GNU MP Library is distributed in the hope that it will be useful, but
24
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
26
for more details.
27
28
You should have received copies of the GNU General Public License and the
29
GNU Lesser General Public License along with the GNU MP Library.  If not,
30
see https://www.gnu.org/licenses/.  */
31
32
#include "gmp-impl.h"
33
#include "longlong.h"
34
35
/* Computes (r;b) = (a; b) M. Result is of size n + M->n +/- 1, and
36
   the size is returned (if inputs are non-normalized, result may be
37
   non-normalized too). Temporary space needed is M->n + n.
38
 */
39
static size_t
40
hgcd_mul_matrix_vector (struct hgcd_matrix *M,
41
      mp_ptr rp, mp_srcptr ap, mp_ptr bp, mp_size_t n, mp_ptr tp)
42
0
{
43
0
  mp_limb_t ah, bh;
44
45
  /* Compute (r,b) <-- (u00 a + u10 b, u01 a + u11 b) as
46
47
     t  = u00 * a
48
     r  = u10 * b
49
     r += t;
50
51
     t  = u11 * b
52
     b  = u01 * a
53
     b += t;
54
  */
55
56
0
  if (M->n >= n)
57
0
    {
58
0
      mpn_mul (tp, M->p[0][0], M->n, ap, n);
59
0
      mpn_mul (rp, M->p[1][0], M->n, bp, n);
60
0
    }
61
0
  else
62
0
    {
63
0
      mpn_mul (tp, ap, n, M->p[0][0], M->n);
64
0
      mpn_mul (rp, bp, n, M->p[1][0], M->n);
65
0
    }
66
67
0
  ah = mpn_add_n (rp, rp, tp, n + M->n);
68
69
0
  if (M->n >= n)
70
0
    {
71
0
      mpn_mul (tp, M->p[1][1], M->n, bp, n);
72
0
      mpn_mul (bp, M->p[0][1], M->n, ap, n);
73
0
    }
74
0
  else
75
0
    {
76
0
      mpn_mul (tp, bp, n, M->p[1][1], M->n);
77
0
      mpn_mul (bp, ap, n, M->p[0][1], M->n);
78
0
    }
79
0
  bh = mpn_add_n (bp, bp, tp, n + M->n);
80
81
0
  n += M->n;
82
0
  if ( (ah | bh) > 0)
83
0
    {
84
0
      rp[n] = ah;
85
0
      bp[n] = bh;
86
0
      n++;
87
0
    }
88
0
  else
89
0
    {
90
      /* Normalize */
91
0
      while ( (rp[n-1] | bp[n-1]) == 0)
92
0
  n--;
93
0
    }
94
95
0
  return n;
96
0
}
97
98
#define COMPUTE_V_ITCH(n) (2*(n))
99
100
/* Computes |v| = |(g - u a)| / b, where u may be positive or
101
   negative, and v is of the opposite sign. max(a, b) is of size n, u and
102
   v at most size n, and v must have space for n+1 limbs. */
103
static mp_size_t
104
compute_v (mp_ptr vp,
105
     mp_srcptr ap, mp_srcptr bp, mp_size_t n,
106
     mp_srcptr gp, mp_size_t gn,
107
     mp_srcptr up, mp_size_t usize,
108
     mp_ptr tp)
109
0
{
110
0
  mp_size_t size;
111
0
  mp_size_t an;
112
0
  mp_size_t bn;
113
0
  mp_size_t vn;
114
115
0
  ASSERT (n > 0);
116
0
  ASSERT (gn > 0);
117
0
  ASSERT (usize != 0);
118
119
0
  size = ABS (usize);
120
0
  ASSERT (size <= n);
121
0
  ASSERT (up[size-1] > 0);
122
123
0
  an = n;
124
0
  MPN_NORMALIZE (ap, an);
125
0
  ASSERT (gn <= an);
126
127
0
  if (an >= size)
128
0
    mpn_mul (tp, ap, an, up, size);
129
0
  else
130
0
    mpn_mul (tp, up, size, ap, an);
131
132
0
  size += an;
133
134
0
  if (usize > 0)
135
0
    {
136
      /* |v| = -v = (u a - g) / b */
137
138
0
      ASSERT_NOCARRY (mpn_sub (tp, tp, size, gp, gn));
139
0
      MPN_NORMALIZE (tp, size);
140
0
      if (size == 0)
141
0
  return 0;
142
0
    }
143
0
  else
144
0
    { /* |v| = v = (g - u a) / b = (g + |u| a) / b. Since g <= a,
145
   (g + |u| a) always fits in (|usize| + an) limbs. */
146
147
0
      ASSERT_NOCARRY (mpn_add (tp, tp, size, gp, gn));
148
0
      size -= (tp[size - 1] == 0);
149
0
    }
150
151
  /* Now divide t / b. There must be no remainder */
152
0
  bn = n;
153
0
  MPN_NORMALIZE (bp, bn);
154
0
  ASSERT (size >= bn);
155
156
0
  vn = size + 1 - bn;
157
0
  ASSERT (vn <= n + 1);
158
159
0
  mpn_divexact (vp, tp, size, bp, bn);
160
0
  vn -= (vp[vn-1] == 0);
161
162
0
  return vn;
163
0
}
164
165
/* Temporary storage:
166
167
   Initial division: Quotient of at most an - n + 1 <= an limbs.
168
169
   Storage for u0 and u1: 2(n+1).
170
171
   Storage for hgcd matrix M, with input ceil(n/2): 5 * ceil(n/4)
172
173
   Storage for hgcd, input (n + 1)/2: 9 n/4 plus some.
174
175
   When hgcd succeeds: 1 + floor(3n/2) for adjusting a and b, and 2(n+1) for the cofactors.
176
177
   When hgcd fails: 2n + 1 for mpn_gcdext_subdiv_step, which is less.
178
179
   For the lehmer call after the loop, Let T denote
180
   GCDEXT_DC_THRESHOLD. For the gcdext_lehmer call, we need T each for
181
   u, a and b, and 4T+3 scratch space. Next, for compute_v, we need T
182
   for u, T+1 for v and 2T scratch space. In all, 7T + 3 is
183
   sufficient for both operations.
184
185
*/
186
187
/* Optimal choice of p seems difficult. In each iteration the division
188
 * of work between hgcd and the updates of u0 and u1 depends on the
189
 * current size of the u. It may be desirable to use a different
190
 * choice of p in each iteration. Also the input size seems to matter;
191
 * choosing p = n / 3 in the first iteration seems to improve
192
 * performance slightly for input size just above the threshold, but
193
 * degrade performance for larger inputs. */
194
0
#define CHOOSE_P_1(n) ((n) / 2)
195
0
#define CHOOSE_P_2(n) ((n) / 3)
196
197
mp_size_t
198
mpn_gcdext (mp_ptr gp, mp_ptr up, mp_size_t *usizep,
199
      mp_ptr ap, mp_size_t an, mp_ptr bp, mp_size_t n)
200
0
{
201
0
  mp_size_t talloc;
202
0
  mp_size_t scratch;
203
0
  mp_size_t matrix_scratch;
204
0
  mp_size_t ualloc = n + 1;
205
206
0
  struct gcdext_ctx ctx;
207
0
  mp_size_t un;
208
0
  mp_ptr u0;
209
0
  mp_ptr u1;
210
211
0
  mp_ptr tp;
212
213
0
  TMP_DECL;
214
215
0
  ASSERT (an >= n);
216
0
  ASSERT (n > 0);
217
0
  ASSERT (bp[n-1] > 0);
218
219
0
  TMP_MARK;
220
221
  /* FIXME: Check for small sizes first, before setting up temporary
222
     storage etc. */
223
0
  talloc = MPN_GCDEXT_LEHMER_N_ITCH(n);
224
225
  /* For initial division */
226
0
  scratch = an - n + 1;
227
0
  if (scratch > talloc)
228
0
    talloc = scratch;
229
230
0
  if (ABOVE_THRESHOLD (n, GCDEXT_DC_THRESHOLD))
231
0
    {
232
      /* For hgcd loop. */
233
0
      mp_size_t hgcd_scratch;
234
0
      mp_size_t update_scratch;
235
0
      mp_size_t p1 = CHOOSE_P_1 (n);
236
0
      mp_size_t p2 = CHOOSE_P_2 (n);
237
0
      mp_size_t min_p = MIN(p1, p2);
238
0
      mp_size_t max_p = MAX(p1, p2);
239
0
      matrix_scratch = MPN_HGCD_MATRIX_INIT_ITCH (n - min_p);
240
0
      hgcd_scratch = mpn_hgcd_itch (n - min_p);
241
0
      update_scratch = max_p + n - 1;
242
243
0
      scratch = matrix_scratch + MAX(hgcd_scratch, update_scratch);
244
0
      if (scratch > talloc)
245
0
  talloc = scratch;
246
247
      /* Final mpn_gcdext_lehmer_n call. Need space for u and for
248
   copies of a and b. */
249
0
      scratch = MPN_GCDEXT_LEHMER_N_ITCH (GCDEXT_DC_THRESHOLD)
250
0
  + 3*GCDEXT_DC_THRESHOLD;
251
252
0
      if (scratch > talloc)
253
0
  talloc = scratch;
254
255
      /* Cofactors u0 and u1 */
256
0
      talloc += 2*(n+1);
257
0
    }
258
259
0
  tp = TMP_ALLOC_LIMBS(talloc);
260
261
0
  if (an > n)
262
0
    {
263
0
      mpn_tdiv_qr (tp, ap, 0, ap, an, bp, n);
264
265
0
      if (mpn_zero_p (ap, n))
266
0
  {
267
0
    MPN_COPY (gp, bp, n);
268
0
    *usizep = 0;
269
0
    TMP_FREE;
270
0
    return n;
271
0
  }
272
0
    }
273
274
0
  if (BELOW_THRESHOLD (n, GCDEXT_DC_THRESHOLD))
275
0
    {
276
0
      mp_size_t gn = mpn_gcdext_lehmer_n(gp, up, usizep, ap, bp, n, tp);
277
278
0
      TMP_FREE;
279
0
      return gn;
280
0
    }
281
282
0
  MPN_ZERO (tp, 2*ualloc);
283
0
  u0 = tp; tp += ualloc;
284
0
  u1 = tp; tp += ualloc;
285
286
0
  ctx.gp = gp;
287
0
  ctx.up = up;
288
0
  ctx.usize = usizep;
289
290
0
  {
291
    /* For the first hgcd call, there are no u updates, and it makes
292
       some sense to use a different choice for p. */
293
294
    /* FIXME: We could trim use of temporary storage, since u0 and u1
295
       are not used yet. For the hgcd call, we could swap in the u0
296
       and u1 pointers for the relevant matrix elements. */
297
298
0
    struct hgcd_matrix M;
299
0
    mp_size_t p = CHOOSE_P_1 (n);
300
0
    mp_size_t nn;
301
302
0
    mpn_hgcd_matrix_init (&M, n - p, tp);
303
0
    nn = mpn_hgcd (ap + p, bp + p, n - p, &M, tp + matrix_scratch);
304
0
    if (nn > 0)
305
0
      {
306
0
  ASSERT (M.n <= (n - p - 1)/2);
307
0
  ASSERT (M.n + p <= (p + n - 1) / 2);
308
309
  /* Temporary storage 2 (p + M->n) <= p + n - 1 */
310
0
  n = mpn_hgcd_matrix_adjust (&M, p + nn, ap, bp, p, tp + matrix_scratch);
311
312
0
  MPN_COPY (u0, M.p[1][0], M.n);
313
0
  MPN_COPY (u1, M.p[1][1], M.n);
314
0
  un = M.n;
315
0
  while ( (u0[un-1] | u1[un-1] ) == 0)
316
0
    un--;
317
0
      }
318
0
    else
319
0
      {
320
  /* mpn_hgcd has failed. Then either one of a or b is very
321
     small, or the difference is very small. Perform one
322
     subtraction followed by one division. */
323
0
  u1[0] = 1;
324
325
0
  ctx.u0 = u0;
326
0
  ctx.u1 = u1;
327
0
  ctx.tp = tp + n; /* ualloc */
328
0
  ctx.un = 1;
329
330
  /* Temporary storage n */
331
0
  n = mpn_gcd_subdiv_step (ap, bp, n, 0, mpn_gcdext_hook, &ctx, tp);
332
0
  if (n == 0)
333
0
    {
334
0
      TMP_FREE;
335
0
      return ctx.gn;
336
0
    }
337
338
0
  un = ctx.un;
339
0
  ASSERT (un < ualloc);
340
0
      }
341
0
  }
342
343
0
  while (ABOVE_THRESHOLD (n, GCDEXT_DC_THRESHOLD))
344
0
    {
345
0
      struct hgcd_matrix M;
346
0
      mp_size_t p = CHOOSE_P_2 (n);
347
0
      mp_size_t nn;
348
349
0
      mpn_hgcd_matrix_init (&M, n - p, tp);
350
0
      nn = mpn_hgcd (ap + p, bp + p, n - p, &M, tp + matrix_scratch);
351
0
      if (nn > 0)
352
0
  {
353
0
    mp_ptr t0;
354
355
0
    t0 = tp + matrix_scratch;
356
0
    ASSERT (M.n <= (n - p - 1)/2);
357
0
    ASSERT (M.n + p <= (p + n - 1) / 2);
358
359
    /* Temporary storage 2 (p + M->n) <= p + n - 1 */
360
0
    n = mpn_hgcd_matrix_adjust (&M, p + nn, ap, bp, p, t0);
361
362
    /* By the same analysis as for mpn_hgcd_matrix_mul */
363
0
    ASSERT (M.n + un <= ualloc);
364
365
    /* FIXME: This copying could be avoided by some swapping of
366
     * pointers. May need more temporary storage, though. */
367
0
    MPN_COPY (t0, u0, un);
368
369
    /* Temporary storage ualloc */
370
0
    un = hgcd_mul_matrix_vector (&M, u0, t0, u1, un, t0 + un);
371
372
0
    ASSERT (un < ualloc);
373
0
    ASSERT ( (u0[un-1] | u1[un-1]) > 0);
374
0
  }
375
0
      else
376
0
  {
377
    /* mpn_hgcd has failed. Then either one of a or b is very
378
       small, or the difference is very small. Perform one
379
       subtraction followed by one division. */
380
0
    ctx.u0 = u0;
381
0
    ctx.u1 = u1;
382
0
    ctx.tp = tp + n; /* ualloc */
383
0
    ctx.un = un;
384
385
    /* Temporary storage n */
386
0
    n = mpn_gcd_subdiv_step (ap, bp, n, 0, mpn_gcdext_hook, &ctx, tp);
387
0
    if (n == 0)
388
0
      {
389
0
        TMP_FREE;
390
0
        return ctx.gn;
391
0
      }
392
393
0
    un = ctx.un;
394
0
    ASSERT (un < ualloc);
395
0
  }
396
0
    }
397
  /* We have A = ... a + ... b
398
       B =  u0 a +  u1 b
399
400
       a = u1  A + ... B
401
       b = -u0 A + ... B
402
403
     with bounds
404
405
       |u0|, |u1| <= B / min(a, b)
406
407
     We always have u1 > 0, and u0 == 0 is possible only if u1 == 1,
408
     in which case the only reduction done so far is a = A - k B for
409
     some k.
410
411
     Compute g = u a + v b = (u u1 - v u0) A + (...) B
412
     Here, u, v are bounded by
413
414
       |u| <= b,
415
       |v| <= a
416
  */
417
418
0
  ASSERT ( (ap[n-1] | bp[n-1]) > 0);
419
420
0
  if (UNLIKELY (mpn_cmp (ap, bp, n) == 0))
421
0
    {
422
      /* Must return the smallest cofactor, +u1 or -u0 */
423
0
      int c;
424
425
0
      MPN_COPY (gp, ap, n);
426
427
0
      MPN_CMP (c, u0, u1, un);
428
      /* c == 0 can happen only when A = (2k+1) G, B = 2 G. And in
429
   this case we choose the cofactor + 1, corresponding to G = A
430
   - k B, rather than -1, corresponding to G = - A + (k+1) B. */
431
0
      ASSERT (c != 0 || (un == 1 && u0[0] == 1 && u1[0] == 1));
432
0
      if (c < 0)
433
0
  {
434
0
    MPN_NORMALIZE (u0, un);
435
0
    MPN_COPY (up, u0, un);
436
0
    *usizep = -un;
437
0
  }
438
0
      else
439
0
  {
440
0
    MPN_NORMALIZE_NOT_ZERO (u1, un);
441
0
    MPN_COPY (up, u1, un);
442
0
    *usizep = un;
443
0
  }
444
445
0
      TMP_FREE;
446
0
      return n;
447
0
    }
448
0
  else if (UNLIKELY (u0[0] == 0) && un == 1)
449
0
    {
450
0
      mp_size_t gn;
451
0
      ASSERT (u1[0] == 1);
452
453
      /* g = u a + v b = (u u1 - v u0) A + (...) B = u A + (...) B */
454
0
      gn = mpn_gcdext_lehmer_n (gp, up, usizep, ap, bp, n, tp);
455
456
0
      TMP_FREE;
457
0
      return gn;
458
0
    }
459
0
  else
460
0
    {
461
0
      mp_size_t u0n;
462
0
      mp_size_t u1n;
463
0
      mp_size_t lehmer_un;
464
0
      mp_size_t lehmer_vn;
465
0
      mp_size_t gn;
466
467
0
      mp_ptr lehmer_up;
468
0
      mp_ptr lehmer_vp;
469
0
      int negate;
470
471
0
      lehmer_up = tp; tp += n;
472
473
      /* Call mpn_gcdext_lehmer_n with copies of a and b. */
474
0
      MPN_COPY (tp, ap, n);
475
0
      MPN_COPY (tp + n, bp, n);
476
0
      gn = mpn_gcdext_lehmer_n (gp, lehmer_up, &lehmer_un, tp, tp + n, n, tp + 2*n);
477
478
0
      u0n = un;
479
0
      MPN_NORMALIZE (u0, u0n);
480
0
      ASSERT (u0n > 0);
481
482
0
      if (lehmer_un == 0)
483
0
  {
484
    /* u == 0  ==>  v = g / b == 1  ==> g = - u0 A + (...) B */
485
0
    MPN_COPY (up, u0, u0n);
486
0
    *usizep = -u0n;
487
488
0
    TMP_FREE;
489
0
    return gn;
490
0
  }
491
492
0
      lehmer_vp = tp;
493
      /* Compute v = (g - u a) / b */
494
0
      lehmer_vn = compute_v (lehmer_vp,
495
0
           ap, bp, n, gp, gn, lehmer_up, lehmer_un, tp + n + 1);
496
497
0
      if (lehmer_un > 0)
498
0
  negate = 0;
499
0
      else
500
0
  {
501
0
    lehmer_un = -lehmer_un;
502
0
    negate = 1;
503
0
  }
504
505
0
      u1n = un;
506
0
      MPN_NORMALIZE (u1, u1n);
507
0
      ASSERT (u1n > 0);
508
509
0
      ASSERT (lehmer_un + u1n <= ualloc);
510
0
      ASSERT (lehmer_vn + u0n <= ualloc);
511
512
      /* We may still have v == 0 */
513
514
      /* Compute u u0 */
515
0
      if (lehmer_un <= u1n)
516
  /* Should be the common case */
517
0
  mpn_mul (up, u1, u1n, lehmer_up, lehmer_un);
518
0
      else
519
0
  mpn_mul (up, lehmer_up, lehmer_un, u1, u1n);
520
521
0
      un = u1n + lehmer_un;
522
0
      un -= (up[un - 1] == 0);
523
524
0
      if (lehmer_vn > 0)
525
0
  {
526
0
    mp_limb_t cy;
527
528
    /* Overwrites old u1 value */
529
0
    if (lehmer_vn <= u0n)
530
      /* Should be the common case */
531
0
      mpn_mul (u1, u0, u0n, lehmer_vp, lehmer_vn);
532
0
    else
533
0
      mpn_mul (u1, lehmer_vp, lehmer_vn, u0, u0n);
534
535
0
    u1n = u0n + lehmer_vn;
536
0
    u1n -= (u1[u1n - 1] == 0);
537
538
0
    if (u1n <= un)
539
0
      {
540
0
        cy = mpn_add (up, up, un, u1, u1n);
541
0
      }
542
0
    else
543
0
      {
544
0
        cy = mpn_add (up, u1, u1n, up, un);
545
0
        un = u1n;
546
0
      }
547
0
    up[un] = cy;
548
0
    un += (cy != 0);
549
550
0
    ASSERT (un < ualloc);
551
0
  }
552
0
      *usizep = negate ? -un : un;
553
554
0
      TMP_FREE;
555
0
      return gn;
556
0
    }
557
0
}