Blame stdlib/divmod_1.c

Packit 6c4009
/* mpn_divmod_1(quot_ptr, dividend_ptr, dividend_size, divisor_limb) --
Packit 6c4009
   Divide (DIVIDEND_PTR,,DIVIDEND_SIZE) by DIVISOR_LIMB.
Packit 6c4009
   Write DIVIDEND_SIZE limbs of quotient at QUOT_PTR.
Packit 6c4009
   Return the single-limb remainder.
Packit 6c4009
   There are no constraints on the value of the divisor.
Packit 6c4009
Packit 6c4009
   QUOT_PTR and DIVIDEND_PTR might point to the same limb.
Packit 6c4009
Packit 6c4009
Copyright (C) 1991-2018 Free Software Foundation, Inc.
Packit 6c4009
Packit 6c4009
This file is part of the GNU MP Library.
Packit 6c4009
Packit 6c4009
The GNU MP Library is free software; you can redistribute it and/or modify
Packit 6c4009
it under the terms of the GNU Lesser General Public License as published by
Packit 6c4009
the Free Software Foundation; either version 2.1 of the License, or (at your
Packit 6c4009
option) any later version.
Packit 6c4009
Packit 6c4009
The GNU MP Library is distributed in the hope that it will be useful, but
Packit 6c4009
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 6c4009
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit 6c4009
License for more details.
Packit 6c4009
Packit 6c4009
You should have received a copy of the GNU Lesser General Public License
Packit 6c4009
along with the GNU MP Library; see the file COPYING.LIB.  If not, see
Packit 6c4009
<http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <gmp.h>
Packit 6c4009
#include "gmp-impl.h"
Packit 6c4009
#include "longlong.h"
Packit 6c4009
Packit 6c4009
#ifndef UMUL_TIME
Packit 6c4009
#define UMUL_TIME 1
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifndef UDIV_TIME
Packit 6c4009
#define UDIV_TIME UMUL_TIME
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* FIXME: We should be using invert_limb (or invert_normalized_limb)
Packit 6c4009
   here (not udiv_qrnnd).  */
Packit 6c4009
Packit 6c4009
mp_limb_t
Packit 6c4009
mpn_divmod_1 (mp_ptr quot_ptr,
Packit 6c4009
	      mp_srcptr dividend_ptr, mp_size_t dividend_size,
Packit 6c4009
	      mp_limb_t divisor_limb)
Packit 6c4009
{
Packit 6c4009
  mp_size_t i;
Packit 6c4009
  mp_limb_t n1, n0, r;
Packit 6c4009
  mp_limb_t dummy __attribute__ ((unused));
Packit 6c4009
Packit 6c4009
  /* ??? Should this be handled at all?  Rely on callers?  */
Packit 6c4009
  if (dividend_size == 0)
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  /* If multiplication is much faster than division, and the
Packit 6c4009
     dividend is large, pre-invert the divisor, and use
Packit 6c4009
     only multiplications in the inner loop.  */
Packit 6c4009
Packit 6c4009
  /* This test should be read:
Packit 6c4009
       Does it ever help to use udiv_qrnnd_preinv?
Packit 6c4009
	 && Does what we save compensate for the inversion overhead?  */
Packit 6c4009
  if (UDIV_TIME > (2 * UMUL_TIME + 6)
Packit 6c4009
      && (UDIV_TIME - (2 * UMUL_TIME + 6)) * dividend_size > UDIV_TIME)
Packit 6c4009
    {
Packit 6c4009
      int normalization_steps;
Packit 6c4009
Packit 6c4009
      count_leading_zeros (normalization_steps, divisor_limb);
Packit 6c4009
      if (normalization_steps != 0)
Packit 6c4009
	{
Packit 6c4009
	  mp_limb_t divisor_limb_inverted;
Packit 6c4009
Packit 6c4009
	  divisor_limb <<= normalization_steps;
Packit 6c4009
Packit 6c4009
	  /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB.  The
Packit 6c4009
	     result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
Packit 6c4009
	     most significant bit (with weight 2**N) implicit.  */
Packit 6c4009
Packit 6c4009
	  /* Special case for DIVISOR_LIMB == 100...000.  */
Packit 6c4009
	  if (divisor_limb << 1 == 0)
Packit 6c4009
	    divisor_limb_inverted = ~(mp_limb_t) 0;
Packit 6c4009
	  else
Packit 6c4009
	    udiv_qrnnd (divisor_limb_inverted, dummy,
Packit 6c4009
			-divisor_limb, 0, divisor_limb);
Packit 6c4009
Packit 6c4009
	  n1 = dividend_ptr[dividend_size - 1];
Packit 6c4009
	  r = n1 >> (BITS_PER_MP_LIMB - normalization_steps);
Packit 6c4009
Packit 6c4009
	  /* Possible optimization:
Packit 6c4009
	     if (r == 0
Packit 6c4009
	     && divisor_limb > ((n1 << normalization_steps)
Packit 6c4009
			     | (dividend_ptr[dividend_size - 2] >> ...)))
Packit 6c4009
	     ...one division less... */
Packit 6c4009
Packit 6c4009
	  for (i = dividend_size - 2; i >= 0; i--)
Packit 6c4009
	    {
Packit 6c4009
	      n0 = dividend_ptr[i];
Packit 6c4009
	      udiv_qrnnd_preinv (quot_ptr[i + 1], r, r,
Packit 6c4009
				 ((n1 << normalization_steps)
Packit 6c4009
				  | (n0 >> (BITS_PER_MP_LIMB - normalization_steps))),
Packit 6c4009
				 divisor_limb, divisor_limb_inverted);
Packit 6c4009
	      n1 = n0;
Packit 6c4009
	    }
Packit 6c4009
	  udiv_qrnnd_preinv (quot_ptr[0], r, r,
Packit 6c4009
			     n1 << normalization_steps,
Packit 6c4009
			     divisor_limb, divisor_limb_inverted);
Packit 6c4009
	  return r >> normalization_steps;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  mp_limb_t divisor_limb_inverted;
Packit 6c4009
Packit 6c4009
	  /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB.  The
Packit 6c4009
	     result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
Packit 6c4009
	     most significant bit (with weight 2**N) implicit.  */
Packit 6c4009
Packit 6c4009
	  /* Special case for DIVISOR_LIMB == 100...000.  */
Packit 6c4009
	  if (divisor_limb << 1 == 0)
Packit 6c4009
	    divisor_limb_inverted = ~(mp_limb_t) 0;
Packit 6c4009
	  else
Packit 6c4009
	    udiv_qrnnd (divisor_limb_inverted, dummy,
Packit 6c4009
			-divisor_limb, 0, divisor_limb);
Packit 6c4009
Packit 6c4009
	  i = dividend_size - 1;
Packit 6c4009
	  r = dividend_ptr[i];
Packit 6c4009
Packit 6c4009
	  if (r >= divisor_limb)
Packit 6c4009
	    r = 0;
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      quot_ptr[i] = 0;
Packit 6c4009
	      i--;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  for (; i >= 0; i--)
Packit 6c4009
	    {
Packit 6c4009
	      n0 = dividend_ptr[i];
Packit 6c4009
	      udiv_qrnnd_preinv (quot_ptr[i], r, r,
Packit 6c4009
				 n0, divisor_limb, divisor_limb_inverted);
Packit 6c4009
	    }
Packit 6c4009
	  return r;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      if (UDIV_NEEDS_NORMALIZATION)
Packit 6c4009
	{
Packit 6c4009
	  int normalization_steps;
Packit 6c4009
Packit 6c4009
	  count_leading_zeros (normalization_steps, divisor_limb);
Packit 6c4009
	  if (normalization_steps != 0)
Packit 6c4009
	    {
Packit 6c4009
	      divisor_limb <<= normalization_steps;
Packit 6c4009
Packit 6c4009
	      n1 = dividend_ptr[dividend_size - 1];
Packit 6c4009
	      r = n1 >> (BITS_PER_MP_LIMB - normalization_steps);
Packit 6c4009
Packit 6c4009
	      /* Possible optimization:
Packit 6c4009
		 if (r == 0
Packit 6c4009
		 && divisor_limb > ((n1 << normalization_steps)
Packit 6c4009
				 | (dividend_ptr[dividend_size - 2] >> ...)))
Packit 6c4009
		 ...one division less... */
Packit 6c4009
Packit 6c4009
	      for (i = dividend_size - 2; i >= 0; i--)
Packit 6c4009
		{
Packit 6c4009
		  n0 = dividend_ptr[i];
Packit 6c4009
		  udiv_qrnnd (quot_ptr[i + 1], r, r,
Packit 6c4009
			      ((n1 << normalization_steps)
Packit 6c4009
			       | (n0 >> (BITS_PER_MP_LIMB - normalization_steps))),
Packit 6c4009
			      divisor_limb);
Packit 6c4009
		  n1 = n0;
Packit 6c4009
		}
Packit 6c4009
	      udiv_qrnnd (quot_ptr[0], r, r,
Packit 6c4009
			  n1 << normalization_steps,
Packit 6c4009
			  divisor_limb);
Packit 6c4009
	      return r >> normalization_steps;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
      /* No normalization needed, either because udiv_qrnnd doesn't require
Packit 6c4009
	 it, or because DIVISOR_LIMB is already normalized.  */
Packit 6c4009
Packit 6c4009
      i = dividend_size - 1;
Packit 6c4009
      r = dividend_ptr[i];
Packit 6c4009
Packit 6c4009
      if (r >= divisor_limb)
Packit 6c4009
	r = 0;
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  quot_ptr[i] = 0;
Packit 6c4009
	  i--;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      for (; i >= 0; i--)
Packit 6c4009
	{
Packit 6c4009
	  n0 = dividend_ptr[i];
Packit 6c4009
	  udiv_qrnnd (quot_ptr[i], r, r, n0, divisor_limb);
Packit 6c4009
	}
Packit 6c4009
      return r;
Packit 6c4009
    }
Packit 6c4009
}