Home | History | Annotate | Line # | Download | only in generic
gcdext_lehmer.c revision 1.1.1.2
      1 /* mpn_gcdext -- Extended Greatest Common Divisor.
      2 
      3 Copyright 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2012 Free
      4 Software Foundation, 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 the GNU Lesser General Public License as published by
     10 the Free Software Foundation; either version 3 of the License, or (at your
     11 option) any later version.
     12 
     13 The GNU MP Library is distributed in the hope that it will be useful, but
     14 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     15 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
     16 License for more details.
     17 
     18 You should have received a copy of the GNU Lesser General Public License
     19 along with the GNU MP Library.  If not, see http://www.gnu.org/licenses/.  */
     20 
     21 #include "gmp.h"
     22 #include "gmp-impl.h"
     23 #include "longlong.h"
     24 
     25 /* Here, d is the index of the cofactor to update. FIXME: Could use qn
     26    = 0 for the common case q = 1. */
     27 void
     28 mpn_gcdext_hook (void *p, mp_srcptr gp, mp_size_t gn,
     29 		 mp_srcptr qp, mp_size_t qn, int d)
     30 {
     31   struct gcdext_ctx *ctx = (struct gcdext_ctx *) p;
     32   mp_size_t un = ctx->un;
     33 
     34   if (gp)
     35     {
     36       mp_srcptr up;
     37 
     38       ASSERT (gn > 0);
     39       ASSERT (gp[gn-1] > 0);
     40 
     41       MPN_COPY (ctx->gp, gp, gn);
     42       ctx->gn = gn;
     43 
     44       if (d < 0)
     45 	{
     46 	  int c;
     47 
     48 	  /* Must return the smallest cofactor, +u1 or -u0 */
     49 	  MPN_CMP (c, ctx->u0, ctx->u1, un);
     50 	  ASSERT (c != 0 || (un == 1 && ctx->u0[0] == 1 && ctx->u1[0] == 1));
     51 
     52 	  d = c < 0;
     53 	}
     54 
     55       up = d ? ctx->u0 : ctx->u1;
     56 
     57       MPN_NORMALIZE (up, un);
     58       MPN_COPY (ctx->up, up, un);
     59 
     60       *ctx->usize = d ? -un : un;
     61     }
     62   else
     63     {
     64       mp_limb_t cy;
     65       mp_ptr u0 = ctx->u0;
     66       mp_ptr u1 = ctx->u1;
     67 
     68       ASSERT (d >= 0);
     69 
     70       if (d)
     71 	MP_PTR_SWAP (u0, u1);
     72 
     73       qn -= (qp[qn-1] == 0);
     74 
     75       /* Update u0 += q  * u1 */
     76       if (qn == 1)
     77 	{
     78 	  mp_limb_t q = qp[0];
     79 
     80 	  if (q == 1)
     81 	    /* A common case. */
     82 	    cy = mpn_add_n (u0, u0, u1, un);
     83 	  else
     84 	    cy = mpn_addmul_1 (u0, u1, un, q);
     85 	}
     86       else
     87 	{
     88 	  mp_size_t u1n;
     89 	  mp_ptr tp;
     90 
     91 	  u1n = un;
     92 	  MPN_NORMALIZE (u1, u1n);
     93 
     94 	  if (u1n == 0)
     95 	    return;
     96 
     97 	  /* Should always have u1n == un here, and u1 >= u0. The
     98 	     reason is that we alternate adding u0 to u1 and u1 to u0
     99 	     (corresponding to subtractions a - b and b - a), and we
    100 	     can get a large quotient only just after a switch, which
    101 	     means that we'll add (a multiple of) the larger u to the
    102 	     smaller. */
    103 
    104 	  tp = ctx->tp;
    105 
    106 	  if (qn > u1n)
    107 	    mpn_mul (tp, qp, qn, u1, u1n);
    108 	  else
    109 	    mpn_mul (tp, u1, u1n, qp, qn);
    110 
    111 	  u1n += qn;
    112 	  u1n -= tp[u1n-1] == 0;
    113 
    114 	  if (u1n >= un)
    115 	    {
    116 	      cy = mpn_add (u0, tp, u1n, u0, un);
    117 	      un = u1n;
    118 	    }
    119 	  else
    120 	    /* Note: Unlikely case, maybe never happens? */
    121 	    cy = mpn_add (u0, u0, un, tp, u1n);
    122 
    123 	}
    124       u0[un] = cy;
    125       ctx->un = un + (cy > 0);
    126     }
    127 }
    128 
    129 /* Temporary storage: 3*(n+1) for u. If hgcd2 succeeds, we need n for
    130    the matrix-vector multiplication adjusting a, b. If hgcd fails, we
    131    need at most n for the quotient and n+1 for the u update (reusing
    132    the extra u). In all, 4n + 3. */
    133 
    134 mp_size_t
    135 mpn_gcdext_lehmer_n (mp_ptr gp, mp_ptr up, mp_size_t *usize,
    136 		     mp_ptr ap, mp_ptr bp, mp_size_t n,
    137 		     mp_ptr tp)
    138 {
    139   mp_size_t ualloc = n + 1;
    140 
    141   /* Keeps track of the second row of the reduction matrix
    142    *
    143    *   M = (v0, v1 ; u0, u1)
    144    *
    145    * which correspond to the first column of the inverse
    146    *
    147    *   M^{-1} = (u1, -v1; -u0, v0)
    148    *
    149    * This implies that
    150    *
    151    *   a =  u1 A (mod B)
    152    *   b = -u0 A (mod B)
    153    *
    154    * where A, B denotes the input values.
    155    */
    156 
    157   struct gcdext_ctx ctx;
    158   mp_size_t un;
    159   mp_ptr u0;
    160   mp_ptr u1;
    161   mp_ptr u2;
    162 
    163   MPN_ZERO (tp, 3*ualloc);
    164   u0 = tp; tp += ualloc;
    165   u1 = tp; tp += ualloc;
    166   u2 = tp; tp += ualloc;
    167 
    168   u1[0] = 1; un = 1;
    169 
    170   ctx.gp = gp;
    171   ctx.up = up;
    172   ctx.usize = usize;
    173 
    174   /* FIXME: Handle n == 2 differently, after the loop? */
    175   while (n >= 2)
    176     {
    177       struct hgcd_matrix1 M;
    178       mp_limb_t ah, al, bh, bl;
    179       mp_limb_t mask;
    180 
    181       mask = ap[n-1] | bp[n-1];
    182       ASSERT (mask > 0);
    183 
    184       if (mask & GMP_NUMB_HIGHBIT)
    185 	{
    186 	  ah = ap[n-1]; al = ap[n-2];
    187 	  bh = bp[n-1]; bl = bp[n-2];
    188 	}
    189       else if (n == 2)
    190 	{
    191 	  /* We use the full inputs without truncation, so we can
    192 	     safely shift left. */
    193 	  int shift;
    194 
    195 	  count_leading_zeros (shift, mask);
    196 	  ah = MPN_EXTRACT_NUMB (shift, ap[1], ap[0]);
    197 	  al = ap[0] << shift;
    198 	  bh = MPN_EXTRACT_NUMB (shift, bp[1], bp[0]);
    199 	  bl = bp[0] << shift;
    200 	}
    201       else
    202 	{
    203 	  int shift;
    204 
    205 	  count_leading_zeros (shift, mask);
    206 	  ah = MPN_EXTRACT_NUMB (shift, ap[n-1], ap[n-2]);
    207 	  al = MPN_EXTRACT_NUMB (shift, ap[n-2], ap[n-3]);
    208 	  bh = MPN_EXTRACT_NUMB (shift, bp[n-1], bp[n-2]);
    209 	  bl = MPN_EXTRACT_NUMB (shift, bp[n-2], bp[n-3]);
    210 	}
    211 
    212       /* Try an mpn_nhgcd2 step */
    213       if (mpn_hgcd2 (ah, al, bh, bl, &M))
    214 	{
    215 	  n = mpn_matrix22_mul1_inverse_vector (&M, tp, ap, bp, n);
    216 	  MP_PTR_SWAP (ap, tp);
    217 	  un = mpn_hgcd_mul_matrix1_vector(&M, u2, u0, u1, un);
    218 	  MP_PTR_SWAP (u0, u2);
    219 	}
    220       else
    221 	{
    222 	  /* mpn_hgcd2 has failed. Then either one of a or b is very
    223 	     small, or the difference is very small. Perform one
    224 	     subtraction followed by one division. */
    225 	  ctx.u0 = u0;
    226 	  ctx.u1 = u1;
    227 	  ctx.tp = u2;
    228 	  ctx.un = un;
    229 
    230 	  /* Temporary storage n for the quotient and ualloc for the
    231 	     new cofactor. */
    232 	  n = mpn_gcd_subdiv_step (ap, bp, n, 0, mpn_gcdext_hook, &ctx, tp);
    233 	  if (n == 0)
    234 	    return ctx.gn;
    235 
    236 	  un = ctx.un;
    237 	}
    238     }
    239   ASSERT_ALWAYS (ap[0] > 0);
    240   ASSERT_ALWAYS (bp[0] > 0);
    241 
    242   if (ap[0] == bp[0])
    243     {
    244       int c;
    245 
    246       /* Which cofactor to return now? Candidates are +u1 and -u0,
    247 	 depending on which of a and b was most recently reduced,
    248 	 which we don't keep track of. So compare and get the smallest
    249 	 one. */
    250 
    251       gp[0] = ap[0];
    252 
    253       MPN_CMP (c, u0, u1, un);
    254       ASSERT (c != 0 || (un == 1 && u0[0] == 1 && u1[0] == 1));
    255       if (c < 0)
    256 	{
    257 	  MPN_NORMALIZE (u0, un);
    258 	  MPN_COPY (up, u0, un);
    259 	  *usize = -un;
    260 	}
    261       else
    262 	{
    263 	  MPN_NORMALIZE_NOT_ZERO (u1, un);
    264 	  MPN_COPY (up, u1, un);
    265 	  *usize = un;
    266 	}
    267       return 1;
    268     }
    269   else
    270     {
    271       mp_limb_t uh, vh;
    272       mp_limb_signed_t u;
    273       mp_limb_signed_t v;
    274       int negate;
    275 
    276       gp[0] = mpn_gcdext_1 (&u, &v, ap[0], bp[0]);
    277 
    278       /* Set up = u u1 - v u0. Keep track of size, un grows by one or
    279 	 two limbs. */
    280 
    281       if (u == 0)
    282 	{
    283 	  ASSERT (v == 1);
    284 	  MPN_NORMALIZE (u0, un);
    285 	  MPN_COPY (up, u0, un);
    286 	  *usize = -un;
    287 	  return 1;
    288 	}
    289       else if (v == 0)
    290 	{
    291 	  ASSERT (u == 1);
    292 	  MPN_NORMALIZE (u1, un);
    293 	  MPN_COPY (up, u1, un);
    294 	  *usize = un;
    295 	  return 1;
    296 	}
    297       else if (u > 0)
    298 	{
    299 	  negate = 0;
    300 	  ASSERT (v < 0);
    301 	  v = -v;
    302 	}
    303       else
    304 	{
    305 	  negate = 1;
    306 	  ASSERT (v > 0);
    307 	  u = -u;
    308 	}
    309 
    310       uh = mpn_mul_1 (up, u1, un, u);
    311       vh = mpn_addmul_1 (up, u0, un, v);
    312 
    313       if ( (uh | vh) > 0)
    314 	{
    315 	  uh += vh;
    316 	  up[un++] = uh;
    317 	  if (uh < vh)
    318 	    up[un++] = 1;
    319 	}
    320 
    321       MPN_NORMALIZE_NOT_ZERO (up, un);
    322 
    323       *usize = negate ? -un : un;
    324       return 1;
    325     }
    326 }
    327