Blame mpn/generic/hgcd_appr.c

Packit 5c3484
/* hgcd_appr.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 2011, 2012 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
/* Identical to mpn_hgcd_itch. FIXME: Do we really need to add
Packit 5c3484
   HGCD_THRESHOLD at the end? */
Packit 5c3484
mp_size_t
Packit 5c3484
mpn_hgcd_appr_itch (mp_size_t n)
Packit 5c3484
{
Packit 5c3484
  if (BELOW_THRESHOLD (n, HGCD_APPR_THRESHOLD))
Packit 5c3484
    return n;
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      unsigned k;
Packit 5c3484
      int count;
Packit 5c3484
      mp_size_t nscaled;
Packit 5c3484
Packit 5c3484
      /* Get the recursion depth. */
Packit 5c3484
      nscaled = (n - 1) / (HGCD_APPR_THRESHOLD - 1);
Packit 5c3484
      count_leading_zeros (count, nscaled);
Packit 5c3484
      k = GMP_LIMB_BITS - count;
Packit 5c3484
Packit 5c3484
      return 20 * ((n+3) / 4) + 22 * k + HGCD_THRESHOLD;
Packit 5c3484
    }
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
/* Destroys inputs. */
Packit 5c3484
int
Packit 5c3484
mpn_hgcd_appr (mp_ptr ap, mp_ptr bp, mp_size_t n,
Packit 5c3484
	       struct hgcd_matrix *M, mp_ptr tp)
Packit 5c3484
{
Packit 5c3484
  mp_size_t s;
Packit 5c3484
  int success = 0;
Packit 5c3484
Packit 5c3484
  ASSERT (n > 0);
Packit 5c3484
Packit 5c3484
  ASSERT ((ap[n-1] | bp[n-1]) != 0);
Packit 5c3484
Packit 5c3484
  if (n <= 2)
Packit 5c3484
    /* Implies s = n. A fairly uninteresting case but exercised by the
Packit 5c3484
       random inputs of the testsuite. */
Packit 5c3484
    return 0;
Packit 5c3484
Packit 5c3484
  ASSERT ((n+1)/2 - 1 < M->alloc);
Packit 5c3484
Packit 5c3484
  /* We aim for reduction of to GMP_NUMB_BITS * s bits. But each time
Packit 5c3484
     we discard some of the least significant limbs, we must keep one
Packit 5c3484
     additional bit to account for the truncation error. We maintain
Packit 5c3484
     the GMP_NUMB_BITS * s - extra_bits as the current target size. */
Packit 5c3484
Packit 5c3484
  s = n/2 + 1;
Packit 5c3484
  if (BELOW_THRESHOLD (n, HGCD_APPR_THRESHOLD))
Packit 5c3484
    {
Packit 5c3484
      unsigned extra_bits = 0;
Packit 5c3484
Packit 5c3484
      while (n > 2)
Packit 5c3484
	{
Packit 5c3484
	  mp_size_t nn;
Packit 5c3484
Packit 5c3484
	  ASSERT (n > s);
Packit 5c3484
	  ASSERT (n <= 2*s);
Packit 5c3484
Packit 5c3484
	  nn = mpn_hgcd_step (n, ap, bp, s, M, tp);
Packit 5c3484
	  if (!nn)
Packit 5c3484
	    break;
Packit 5c3484
Packit 5c3484
	  n = nn;
Packit 5c3484
	  success = 1;
Packit 5c3484
Packit 5c3484
	  /* We can truncate and discard the lower p bits whenever nbits <=
Packit 5c3484
	     2*sbits - p. To account for the truncation error, we must
Packit 5c3484
	     adjust
Packit 5c3484
Packit 5c3484
	     sbits <-- sbits + 1 - p,
Packit 5c3484
Packit 5c3484
	     rather than just sbits <-- sbits - p. This adjustment makes
Packit 5c3484
	     the produced matrix slightly smaller than it could be. */
Packit 5c3484
Packit 5c3484
	  if (GMP_NUMB_BITS * (n + 1) + 2 * extra_bits <= 2*GMP_NUMB_BITS * s)
Packit 5c3484
	    {
Packit 5c3484
	      mp_size_t p = (GMP_NUMB_BITS * (2*s - n) - 2*extra_bits) / GMP_NUMB_BITS;
Packit 5c3484
Packit 5c3484
	      if (extra_bits == 0)
Packit 5c3484
		{
Packit 5c3484
		  /* We cross a limb boundary and bump s. We can't do that
Packit 5c3484
		     if the result is that it makes makes min(U, V)
Packit 5c3484
		     smaller than 2^{GMP_NUMB_BITS} s. */
Packit 5c3484
		  if (s + 1 == n
Packit 5c3484
		      || mpn_zero_p (ap + s + 1, n - s - 1)
Packit 5c3484
		      || mpn_zero_p (bp + s + 1, n - s - 1))
Packit 5c3484
		    continue;
Packit 5c3484
Packit 5c3484
		  extra_bits = GMP_NUMB_BITS - 1;
Packit 5c3484
		  s++;
Packit 5c3484
		}
Packit 5c3484
	      else
Packit 5c3484
		{
Packit 5c3484
		  extra_bits--;
Packit 5c3484
		}
Packit 5c3484
Packit 5c3484
	      /* Drop the p least significant limbs */
Packit 5c3484
	      ap += p; bp += p; n -= p; s -= p;
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      ASSERT (s > 0);
Packit 5c3484
Packit 5c3484
      if (extra_bits > 0)
Packit 5c3484
	{
Packit 5c3484
	  /* We can get here only of we have dropped at least one of the least
Packit 5c3484
	     significant bits, so we can decrement ap and bp. We can then shift
Packit 5c3484
	     left extra bits using mpn_rshift. */
Packit 5c3484
	  /* NOTE: In the unlikely case that n is large, it would be preferable
Packit 5c3484
	     to do an initial subdiv step to reduce the size before shifting,
Packit 5c3484
	     but that would mean duplicating mpn_gcd_subdiv_step with a bit
Packit 5c3484
	     count rather than a limb count. */
Packit 5c3484
	  ap--; bp--;
Packit 5c3484
	  ap[0] = mpn_rshift (ap+1, ap+1, n, GMP_NUMB_BITS - extra_bits);
Packit 5c3484
	  bp[0] = mpn_rshift (bp+1, bp+1, n, GMP_NUMB_BITS - extra_bits);
Packit 5c3484
	  n += (ap[n] | bp[n]) > 0;
Packit 5c3484
Packit 5c3484
	  ASSERT (success);
Packit 5c3484
Packit 5c3484
	  while (n > 2)
Packit 5c3484
	    {
Packit 5c3484
	      mp_size_t nn;
Packit 5c3484
Packit 5c3484
	      ASSERT (n > s);
Packit 5c3484
	      ASSERT (n <= 2*s);
Packit 5c3484
Packit 5c3484
	      nn = mpn_hgcd_step (n, ap, bp, s, M, tp);
Packit 5c3484
Packit 5c3484
	      if (!nn)
Packit 5c3484
		return 1;
Packit 5c3484
Packit 5c3484
	      n = nn;
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      if (n == 2)
Packit 5c3484
	{
Packit 5c3484
	  struct hgcd_matrix1 M1;
Packit 5c3484
	  ASSERT (s == 1);
Packit 5c3484
Packit 5c3484
	  if (mpn_hgcd2 (ap[1], ap[0], bp[1], bp[0], &M1))
Packit 5c3484
	    {
Packit 5c3484
	      /* Multiply M <- M * M1 */
Packit 5c3484
	      mpn_hgcd_matrix_mul_1 (M, &M1, tp);
Packit 5c3484
	      success = 1;
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
      return success;
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      mp_size_t n2 = (3*n)/4 + 1;
Packit 5c3484
      mp_size_t p = n/2;
Packit 5c3484
      mp_size_t nn;
Packit 5c3484
Packit 5c3484
      nn = mpn_hgcd_reduce (M, ap, bp, n, p, tp);
Packit 5c3484
      if (nn)
Packit 5c3484
	{
Packit 5c3484
	  n = nn;
Packit 5c3484
	  /* FIXME: Discard some of the low limbs immediately? */
Packit 5c3484
	  success = 1;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      while (n > n2)
Packit 5c3484
	{
Packit 5c3484
	  mp_size_t nn;
Packit 5c3484
Packit 5c3484
	  /* Needs n + 1 storage */
Packit 5c3484
	  nn = mpn_hgcd_step (n, ap, bp, s, M, tp);
Packit 5c3484
	  if (!nn)
Packit 5c3484
	    return success;
Packit 5c3484
Packit 5c3484
	  n = nn;
Packit 5c3484
	  success = 1;
Packit 5c3484
	}
Packit 5c3484
      if (n > s + 2)
Packit 5c3484
	{
Packit 5c3484
	  struct hgcd_matrix M1;
Packit 5c3484
	  mp_size_t scratch;
Packit 5c3484
Packit 5c3484
	  p = 2*s - n + 1;
Packit 5c3484
	  scratch = MPN_HGCD_MATRIX_INIT_ITCH (n-p);
Packit 5c3484
Packit 5c3484
	  mpn_hgcd_matrix_init(&M1, n - p, tp);
Packit 5c3484
	  if (mpn_hgcd_appr (ap + p, bp + p, n - p, &M1, tp + scratch))
Packit 5c3484
	    {
Packit 5c3484
	      /* We always have max(M) > 2^{-(GMP_NUMB_BITS + 1)} max(M1) */
Packit 5c3484
	      ASSERT (M->n + 2 >= M1.n);
Packit 5c3484
Packit 5c3484
	      /* Furthermore, assume M ends with a quotient (1, q; 0, 1),
Packit 5c3484
		 then either q or q + 1 is a correct quotient, and M1 will
Packit 5c3484
		 start with either (1, 0; 1, 1) or (2, 1; 1, 1). This
Packit 5c3484
		 rules out the case that the size of M * M1 is much
Packit 5c3484
		 smaller than the expected M->n + M1->n. */
Packit 5c3484
Packit 5c3484
	      ASSERT (M->n + M1.n < M->alloc);
Packit 5c3484
Packit 5c3484
	      /* We need a bound for of M->n + M1.n. Let n be the original
Packit 5c3484
		 input size. Then
Packit 5c3484
Packit 5c3484
		 ceil(n/2) - 1 >= size of product >= M.n + M1.n - 2
Packit 5c3484
Packit 5c3484
		 and it follows that
Packit 5c3484
Packit 5c3484
		 M.n + M1.n <= ceil(n/2) + 1
Packit 5c3484
Packit 5c3484
		 Then 3*(M.n + M1.n) + 5 <= 3 * ceil(n/2) + 8 is the
Packit 5c3484
		 amount of needed scratch space. */
Packit 5c3484
	      mpn_hgcd_matrix_mul (M, &M1, tp + scratch);
Packit 5c3484
	      return 1;
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      for(;;)
Packit 5c3484
	{
Packit 5c3484
	  mp_size_t nn;
Packit 5c3484
Packit 5c3484
	  ASSERT (n > s);
Packit 5c3484
	  ASSERT (n <= 2*s);
Packit 5c3484
Packit 5c3484
	  nn = mpn_hgcd_step (n, ap, bp, s, M, tp);
Packit 5c3484
Packit 5c3484
	  if (!nn)
Packit 5c3484
	    return success;
Packit 5c3484
Packit 5c3484
	  n = nn;
Packit 5c3484
	  success = 1;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
}