Blame mpn/generic/gcd_subdiv_step.c

Packit 5c3484
/* gcd_subdiv_step.c.
Packit 5c3484
Packit 5c3484
   THE FUNCTIONS IN THIS FILE ARE INTERNAL WITH MUTABLE INTERFACES.  IT IS ONLY
Packit 5c3484
   SAFE TO REACH THEM THROUGH DOCUMENTED INTERFACES.  IN FACT, IT IS ALMOST
Packit 5c3484
   GUARANTEED THAT THEY'LL CHANGE OR DISAPPEAR IN A FUTURE GNU MP RELEASE.
Packit 5c3484
Packit 5c3484
Copyright 2003-2005, 2008, 2010, 2011 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 <stdlib.h>		/* for NULL */
Packit 5c3484
Packit 5c3484
#include "gmp.h"
Packit 5c3484
#include "gmp-impl.h"
Packit 5c3484
#include "longlong.h"
Packit 5c3484
Packit 5c3484
/* Used when mpn_hgcd or mpn_hgcd2 has failed. Then either one of a or
Packit 5c3484
   b is small, or the difference is small. Perform one subtraction
Packit 5c3484
   followed by one division. The normal case is to compute the reduced
Packit 5c3484
   a and b, and return the new size.
Packit 5c3484
Packit 5c3484
   If s == 0 (used for gcd and gcdext), returns zero if the gcd is
Packit 5c3484
   found.
Packit 5c3484
Packit 5c3484
   If s > 0, don't reduce to size <= s, and return zero if no
Packit 5c3484
   reduction is possible (if either a, b or |a-b| is of size <= s). */
Packit 5c3484
Packit 5c3484
/* The hook function is called as
Packit 5c3484
Packit 5c3484
     hook(ctx, gp, gn, qp, qn, d)
Packit 5c3484
Packit 5c3484
   in the following cases:
Packit 5c3484
Packit 5c3484
   + If A = B at the start, G is the gcd, Q is NULL, d = -1.
Packit 5c3484
Packit 5c3484
   + If one input is zero at the start, G is the gcd, Q is NULL,
Packit 5c3484
     d = 0 if A = G and d = 1 if B = G.
Packit 5c3484
Packit 5c3484
   Otherwise, if d = 0 we have just subtracted a multiple of A from B,
Packit 5c3484
   and if d = 1 we have subtracted a multiple of B from A.
Packit 5c3484
Packit 5c3484
   + If A = B after subtraction, G is the gcd, Q is NULL.
Packit 5c3484
Packit 5c3484
   + If we get a zero remainder after division, G is the gcd, Q is the
Packit 5c3484
     quotient.
Packit 5c3484
Packit 5c3484
   + Otherwise, G is NULL, Q is the quotient (often 1).
Packit 5c3484
Packit 5c3484
 */
Packit 5c3484
Packit 5c3484
mp_size_t
Packit 5c3484
mpn_gcd_subdiv_step (mp_ptr ap, mp_ptr bp, mp_size_t n, mp_size_t s,
Packit 5c3484
		     gcd_subdiv_step_hook *hook, void *ctx,
Packit 5c3484
		     mp_ptr tp)
Packit 5c3484
{
Packit 5c3484
  static const mp_limb_t one = CNST_LIMB(1);
Packit 5c3484
  mp_size_t an, bn, qn;
Packit 5c3484
Packit 5c3484
  int swapped;
Packit 5c3484
Packit 5c3484
  ASSERT (n > 0);
Packit 5c3484
  ASSERT (ap[n-1] > 0 || bp[n-1] > 0);
Packit 5c3484
Packit 5c3484
  an = bn = n;
Packit 5c3484
  MPN_NORMALIZE (ap, an);
Packit 5c3484
  MPN_NORMALIZE (bp, bn);
Packit 5c3484
Packit 5c3484
  swapped = 0;
Packit 5c3484
Packit 5c3484
  /* Arrange so that a < b, subtract b -= a, and maintain
Packit 5c3484
     normalization. */
Packit 5c3484
  if (an == bn)
Packit 5c3484
    {
Packit 5c3484
      int c;
Packit 5c3484
      MPN_CMP (c, ap, bp, an);
Packit 5c3484
      if (UNLIKELY (c == 0))
Packit 5c3484
	{
Packit 5c3484
	  /* For gcdext, return the smallest of the two cofactors, so
Packit 5c3484
	     pass d = -1. */
Packit 5c3484
	  if (s == 0)
Packit 5c3484
	    hook (ctx, ap, an, NULL, 0, -1);
Packit 5c3484
	  return 0;
Packit 5c3484
	}
Packit 5c3484
      else if (c > 0)
Packit 5c3484
	{
Packit 5c3484
	  MP_PTR_SWAP (ap, bp);
Packit 5c3484
	  swapped ^= 1;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      if (an > bn)
Packit 5c3484
	{
Packit 5c3484
	  MPN_PTR_SWAP (ap, an, bp, bn);
Packit 5c3484
	  swapped ^= 1;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
  if (an <= s)
Packit 5c3484
    {
Packit 5c3484
      if (s == 0)
Packit 5c3484
	hook (ctx, bp, bn, NULL, 0, swapped ^ 1);
Packit 5c3484
      return 0;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  ASSERT_NOCARRY (mpn_sub (bp, bp, bn, ap, an));
Packit 5c3484
  MPN_NORMALIZE (bp, bn);
Packit 5c3484
  ASSERT (bn > 0);
Packit 5c3484
Packit 5c3484
  if (bn <= s)
Packit 5c3484
    {
Packit 5c3484
      /* Undo subtraction. */
Packit 5c3484
      mp_limb_t cy = mpn_add (bp, ap, an, bp, bn);
Packit 5c3484
      if (cy > 0)
Packit 5c3484
	bp[an] = cy;
Packit 5c3484
      return 0;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  /* Arrange so that a < b */
Packit 5c3484
  if (an == bn)
Packit 5c3484
    {
Packit 5c3484
      int c;
Packit 5c3484
      MPN_CMP (c, ap, bp, an);
Packit 5c3484
      if (UNLIKELY (c == 0))
Packit 5c3484
	{
Packit 5c3484
	  if (s > 0)
Packit 5c3484
	    /* Just record subtraction and return */
Packit 5c3484
	    hook (ctx, NULL, 0, &one, 1, swapped);
Packit 5c3484
	  else
Packit 5c3484
	    /* Found gcd. */
Packit 5c3484
	    hook (ctx, bp, bn, NULL, 0, swapped);
Packit 5c3484
	  return 0;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      hook (ctx, NULL, 0, &one, 1, swapped);
Packit 5c3484
Packit 5c3484
      if (c > 0)
Packit 5c3484
	{
Packit 5c3484
	  MP_PTR_SWAP (ap, bp);
Packit 5c3484
	  swapped ^= 1;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      hook (ctx, NULL, 0, &one, 1, swapped);
Packit 5c3484
Packit 5c3484
      if (an > bn)
Packit 5c3484
	{
Packit 5c3484
	  MPN_PTR_SWAP (ap, an, bp, bn);
Packit 5c3484
	  swapped ^= 1;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  mpn_tdiv_qr (tp, bp, 0, bp, bn, ap, an);
Packit 5c3484
  qn = bn - an + 1;
Packit 5c3484
  bn = an;
Packit 5c3484
  MPN_NORMALIZE (bp, bn);
Packit 5c3484
Packit 5c3484
  if (UNLIKELY (bn <= s))
Packit 5c3484
    {
Packit 5c3484
      if (s == 0)
Packit 5c3484
	{
Packit 5c3484
	  hook (ctx, ap, an, tp, qn, swapped);
Packit 5c3484
	  return 0;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      /* Quotient is one too large, so decrement it and add back A. */
Packit 5c3484
      if (bn > 0)
Packit 5c3484
	{
Packit 5c3484
	  mp_limb_t cy = mpn_add (bp, ap, an, bp, bn);
Packit 5c3484
	  if (cy)
Packit 5c3484
	    bp[an++] = cy;
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	MPN_COPY (bp, ap, an);
Packit 5c3484
Packit 5c3484
      MPN_DECR_U (tp, qn, 1);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  hook (ctx, NULL, 0, tp, qn, swapped);
Packit 5c3484
  return an;
Packit 5c3484
}