Blame mpn/generic/gcdext_1.c

Packit 5c3484
/* mpn_gcdext -- Extended Greatest Common Divisor.
Packit 5c3484
Packit 5c3484
Copyright 1996, 1998, 2000-2005, 2008, 2009 Free Software Foundation, Inc.
Packit 5c3484
Packit 5c3484
This file is part of the GNU MP Library.
Packit 5c3484
Packit 5c3484
The GNU MP Library is free software; you can redistribute it and/or modify
Packit 5c3484
it under the terms of either:
Packit 5c3484
Packit 5c3484
  * the GNU Lesser General Public License as published by the Free
Packit 5c3484
    Software Foundation; either version 3 of the License, or (at your
Packit 5c3484
    option) any later version.
Packit 5c3484
Packit 5c3484
or
Packit 5c3484
Packit 5c3484
  * the GNU General Public License as published by the Free Software
Packit 5c3484
    Foundation; either version 2 of the License, or (at your option) any
Packit 5c3484
    later version.
Packit 5c3484
Packit 5c3484
or both in parallel, as here.
Packit 5c3484
Packit 5c3484
The GNU MP Library is distributed in the hope that it will be useful, but
Packit 5c3484
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 5c3484
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
Packit 5c3484
for more details.
Packit 5c3484
Packit 5c3484
You should have received copies of the GNU General Public License and the
Packit 5c3484
GNU Lesser General Public License along with the GNU MP Library.  If not,
Packit 5c3484
see https://www.gnu.org/licenses/.  */
Packit 5c3484
Packit 5c3484
#include "gmp.h"
Packit 5c3484
#include "gmp-impl.h"
Packit 5c3484
#include "longlong.h"
Packit 5c3484
Packit 5c3484
#ifndef GCDEXT_1_USE_BINARY
Packit 5c3484
#define GCDEXT_1_USE_BINARY 0
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#ifndef GCDEXT_1_BINARY_METHOD
Packit 5c3484
#define GCDEXT_1_BINARY_METHOD 2
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#ifndef USE_ZEROTAB
Packit 5c3484
#define USE_ZEROTAB 1
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if GCDEXT_1_USE_BINARY
Packit 5c3484
Packit 5c3484
#if USE_ZEROTAB
Packit 5c3484
static unsigned char zerotab[0x40] = {
Packit 5c3484
  6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
Packit 5c3484
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
Packit 5c3484
  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
Packit 5c3484
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
Packit 5c3484
};
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
mp_limb_t
Packit 5c3484
mpn_gcdext_1 (mp_limb_signed_t *sp, mp_limb_signed_t *tp,
Packit 5c3484
	      mp_limb_t u, mp_limb_t v)
Packit 5c3484
{
Packit 5c3484
  /* Maintain
Packit 5c3484
Packit 5c3484
     U = t1 u + t0 v
Packit 5c3484
     V = s1 u + s0 v
Packit 5c3484
Packit 5c3484
     where U, V are the inputs (without any shared power of two),
Packit 5c3484
     and the matrix has determinant ± 2^{shift}.
Packit 5c3484
  */
Packit 5c3484
  mp_limb_t s0 = 1;
Packit 5c3484
  mp_limb_t t0 = 0;
Packit 5c3484
  mp_limb_t s1 = 0;
Packit 5c3484
  mp_limb_t t1 = 1;
Packit 5c3484
  mp_limb_t ug;
Packit 5c3484
  mp_limb_t vg;
Packit 5c3484
  mp_limb_t ugh;
Packit 5c3484
  mp_limb_t vgh;
Packit 5c3484
  unsigned zero_bits;
Packit 5c3484
  unsigned shift;
Packit 5c3484
  unsigned i;
Packit 5c3484
#if GCDEXT_1_BINARY_METHOD == 2
Packit 5c3484
  mp_limb_t det_sign;
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
  ASSERT (u > 0);
Packit 5c3484
  ASSERT (v > 0);
Packit 5c3484
Packit 5c3484
  count_trailing_zeros (zero_bits, u | v);
Packit 5c3484
  u >>= zero_bits;
Packit 5c3484
  v >>= zero_bits;
Packit 5c3484
Packit 5c3484
  if ((u & 1) == 0)
Packit 5c3484
    {
Packit 5c3484
      count_trailing_zeros (shift, u);
Packit 5c3484
      u >>= shift;
Packit 5c3484
      t1 <<= shift;
Packit 5c3484
    }
Packit 5c3484
  else if ((v & 1) == 0)
Packit 5c3484
    {
Packit 5c3484
      count_trailing_zeros (shift, v);
Packit 5c3484
      v >>= shift;
Packit 5c3484
      s0 <<= shift;
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    shift = 0;
Packit 5c3484
Packit 5c3484
#if GCDEXT_1_BINARY_METHOD == 1
Packit 5c3484
  while (u != v)
Packit 5c3484
    {
Packit 5c3484
      unsigned count;
Packit 5c3484
      if (u > v)
Packit 5c3484
	{
Packit 5c3484
	  u -= v;
Packit 5c3484
#if USE_ZEROTAB
Packit 5c3484
	  count = zerotab [u & 0x3f];
Packit 5c3484
	  u >>= count;
Packit 5c3484
	  if (UNLIKELY (count == 6))
Packit 5c3484
	    {
Packit 5c3484
	      unsigned c;
Packit 5c3484
	      do
Packit 5c3484
		{
Packit 5c3484
		  c = zerotab[u & 0x3f];
Packit 5c3484
		  u >>= c;
Packit 5c3484
		  count += c;
Packit 5c3484
		}
Packit 5c3484
	      while (c == 6);
Packit 5c3484
	    }
Packit 5c3484
#else
Packit 5c3484
	  count_trailing_zeros (count, u);
Packit 5c3484
	  u >>= count;
Packit 5c3484
#endif
Packit 5c3484
	  t0 += t1; t1 <<= count;
Packit 5c3484
	  s0 += s1; s1 <<= count;
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  v -= u;
Packit 5c3484
#if USE_ZEROTAB
Packit 5c3484
	  count = zerotab [v & 0x3f];
Packit 5c3484
	  v >>= count;
Packit 5c3484
	  if (UNLIKELY (count == 6))
Packit 5c3484
	    {
Packit 5c3484
	      unsigned c;
Packit 5c3484
	      do
Packit 5c3484
		{
Packit 5c3484
		  c = zerotab[v & 0x3f];
Packit 5c3484
		  v >>= c;
Packit 5c3484
		  count += c;
Packit 5c3484
		}
Packit 5c3484
	      while (c == 6);
Packit 5c3484
	    }
Packit 5c3484
#else
Packit 5c3484
	  count_trailing_zeros (count, v);
Packit 5c3484
	  v >>= count;
Packit 5c3484
#endif
Packit 5c3484
	  t1 += t0; t0 <<= count;
Packit 5c3484
	  s1 += s0; s0 <<= count;
Packit 5c3484
	}
Packit 5c3484
      shift += count;
Packit 5c3484
    }
Packit 5c3484
#else
Packit 5c3484
# if GCDEXT_1_BINARY_METHOD == 2
Packit 5c3484
  u >>= 1;
Packit 5c3484
  v >>= 1;
Packit 5c3484
Packit 5c3484
  det_sign = 0;
Packit 5c3484
Packit 5c3484
  while (u != v)
Packit 5c3484
    {
Packit 5c3484
      unsigned count;
Packit 5c3484
      mp_limb_t d =  u - v;
Packit 5c3484
      mp_limb_t vgtu = LIMB_HIGHBIT_TO_MASK (d);
Packit 5c3484
      mp_limb_t sx;
Packit 5c3484
      mp_limb_t tx;
Packit 5c3484
Packit 5c3484
      /* When v <= u (vgtu == 0), the updates are:
Packit 5c3484
Packit 5c3484
	   (u; v)   <-- ( (u - v) >> count; v)    (det = +(1<
Packit 5c3484
	   (t1, t0) <-- (t1 << count, t0 + t1)
Packit 5c3484
Packit 5c3484
	 and when v > 0, the updates are
Packit 5c3484
Packit 5c3484
	   (u; v)   <-- ( (v - u) >> count; u)    (det = -(1<
Packit 5c3484
	   (t1, t0) <-- (t0 << count, t0 + t1)
Packit 5c3484
Packit 5c3484
	 and similarly for s1, s0
Packit 5c3484
      */
Packit 5c3484
Packit 5c3484
      /* v <-- min (u, v) */
Packit 5c3484
      v += (vgtu & d);
Packit 5c3484
Packit 5c3484
      /* u <-- |u - v| */
Packit 5c3484
      u = (d ^ vgtu) - vgtu;
Packit 5c3484
Packit 5c3484
      /* Number of trailing zeros is the same no matter if we look at
Packit 5c3484
       * d or u, but using d gives more parallelism. */
Packit 5c3484
#if USE_ZEROTAB
Packit 5c3484
      count = zerotab[d & 0x3f];
Packit 5c3484
      if (UNLIKELY (count == 6))
Packit 5c3484
	{
Packit 5c3484
	  unsigned c = 6;
Packit 5c3484
	  do
Packit 5c3484
	    {
Packit 5c3484
	      d >>= c;
Packit 5c3484
	      c = zerotab[d & 0x3f];
Packit 5c3484
	      count += c;
Packit 5c3484
	    }
Packit 5c3484
	  while (c == 6);
Packit 5c3484
	}
Packit 5c3484
#else
Packit 5c3484
      count_trailing_zeros (count, d);
Packit 5c3484
#endif
Packit 5c3484
      det_sign ^= vgtu;
Packit 5c3484
Packit 5c3484
      tx = vgtu & (t0 - t1);
Packit 5c3484
      sx = vgtu & (s0 - s1);
Packit 5c3484
      t0 += t1;
Packit 5c3484
      s0 += s1;
Packit 5c3484
      t1 += tx;
Packit 5c3484
      s1 += sx;
Packit 5c3484
Packit 5c3484
      count++;
Packit 5c3484
      u >>= count;
Packit 5c3484
      t1 <<= count;
Packit 5c3484
      s1 <<= count;
Packit 5c3484
      shift += count;
Packit 5c3484
    }
Packit 5c3484
  u = (u << 1) + 1;
Packit 5c3484
# else /* GCDEXT_1_BINARY_METHOD == 2 */
Packit 5c3484
#  error Unknown GCDEXT_1_BINARY_METHOD
Packit 5c3484
# endif
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
  /* Now u = v = g = gcd (u,v). Compute U/g and V/g */
Packit 5c3484
  ug = t0 + t1;
Packit 5c3484
  vg = s0 + s1;
Packit 5c3484
Packit 5c3484
  ugh = ug/2 + (ug & 1);
Packit 5c3484
  vgh = vg/2 + (vg & 1);
Packit 5c3484
Packit 5c3484
  /* Now ±2^{shift} g = s0 U - t0 V. Get rid of the power of two, using
Packit 5c3484
     s0 U - t0 V = (s0 + V/g) U - (t0 + U/g) V. */
Packit 5c3484
  for (i = 0; i < shift; i++)
Packit 5c3484
    {
Packit 5c3484
      mp_limb_t mask = - ( (s0 | t0) & 1);
Packit 5c3484
Packit 5c3484
      s0 /= 2;
Packit 5c3484
      t0 /= 2;
Packit 5c3484
      s0 += mask & vgh;
Packit 5c3484
      t0 += mask & ugh;
Packit 5c3484
    }
Packit 5c3484
  /* FIXME: Try simplifying this condition. */
Packit 5c3484
  if ( (s0 > 1 && 2*s0 >= vg) || (t0 > 1 && 2*t0 >= ug) )
Packit 5c3484
    {
Packit 5c3484
      s0 -= vg;
Packit 5c3484
      t0 -= ug;
Packit 5c3484
    }
Packit 5c3484
#if GCDEXT_1_BINARY_METHOD == 2
Packit 5c3484
  /* Conditional negation. */
Packit 5c3484
  s0 = (s0 ^ det_sign) - det_sign;
Packit 5c3484
  t0 = (t0 ^ det_sign) - det_sign;
Packit 5c3484
#endif
Packit 5c3484
  *sp = s0;
Packit 5c3484
  *tp = -t0;
Packit 5c3484
Packit 5c3484
  return u << zero_bits;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
#else /* !GCDEXT_1_USE_BINARY */
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* FIXME: Takes two single-word limbs. It could be extended to a
Packit 5c3484
 * function that accepts a bignum for the first input, and only
Packit 5c3484
 * returns the first co-factor. */
Packit 5c3484
Packit 5c3484
mp_limb_t
Packit 5c3484
mpn_gcdext_1 (mp_limb_signed_t *up, mp_limb_signed_t *vp,
Packit 5c3484
	      mp_limb_t a, mp_limb_t b)
Packit 5c3484
{
Packit 5c3484
  /* Maintain
Packit 5c3484
Packit 5c3484
     a =  u0 A + v0 B
Packit 5c3484
     b =  u1 A + v1 B
Packit 5c3484
Packit 5c3484
     where A, B are the original inputs.
Packit 5c3484
  */
Packit 5c3484
  mp_limb_signed_t u0 = 1;
Packit 5c3484
  mp_limb_signed_t v0 = 0;
Packit 5c3484
  mp_limb_signed_t u1 = 0;
Packit 5c3484
  mp_limb_signed_t v1 = 1;
Packit 5c3484
Packit 5c3484
  ASSERT (a > 0);
Packit 5c3484
  ASSERT (b > 0);
Packit 5c3484
Packit 5c3484
  if (a < b)
Packit 5c3484
    goto divide_by_b;
Packit 5c3484
Packit 5c3484
  for (;;)
Packit 5c3484
    {
Packit 5c3484
      mp_limb_t q;
Packit 5c3484
Packit 5c3484
      q = a / b;
Packit 5c3484
      a -= q * b;
Packit 5c3484
Packit 5c3484
      if (a == 0)
Packit 5c3484
	{
Packit 5c3484
	  *up = u1;
Packit 5c3484
	  *vp = v1;
Packit 5c3484
	  return b;
Packit 5c3484
	}
Packit 5c3484
      u0 -= q * u1;
Packit 5c3484
      v0 -= q * v1;
Packit 5c3484
Packit 5c3484
    divide_by_b:
Packit 5c3484
      q = b / a;
Packit 5c3484
      b -= q * a;
Packit 5c3484
Packit 5c3484
      if (b == 0)
Packit 5c3484
	{
Packit 5c3484
	  *up = u0;
Packit 5c3484
	  *vp = v0;
Packit 5c3484
	  return a;
Packit 5c3484
	}
Packit 5c3484
      u1 -= q * u0;
Packit 5c3484
      v1 -= q * v0;
Packit 5c3484
    }
Packit 5c3484
}
Packit 5c3484
#endif /* !GCDEXT_1_USE_BINARY */