Blame stdlib/divrem.c

Packit Service 82fcde
/* mpn_divrem -- Divide natural numbers, producing both remainder and
Packit Service 82fcde
   quotient.
Packit Service 82fcde
Packit Service 82fcde
Copyright (C) 1993-2018 Free Software Foundation, Inc.
Packit Service 82fcde
Packit Service 82fcde
This file is part of the GNU MP Library.
Packit Service 82fcde
Packit Service 82fcde
The GNU MP Library is free software; you can redistribute it and/or modify
Packit Service 82fcde
it under the terms of the GNU Lesser General Public License as published by
Packit Service 82fcde
the Free Software Foundation; either version 2.1 of the License, or (at your
Packit Service 82fcde
option) any later version.
Packit Service 82fcde
Packit Service 82fcde
The GNU MP Library is distributed in the hope that it will be useful, but
Packit Service 82fcde
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit Service 82fcde
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit Service 82fcde
License for more details.
Packit Service 82fcde
Packit Service 82fcde
You should have received a copy of the GNU Lesser General Public License
Packit Service 82fcde
along with the GNU MP Library; see the file COPYING.LIB.  If not, see
Packit Service 82fcde
<http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <gmp.h>
Packit Service 82fcde
#include "gmp-impl.h"
Packit Service 82fcde
#include "longlong.h"
Packit Service 82fcde
Packit Service 82fcde
/* Divide num (NP/NSIZE) by den (DP/DSIZE) and write
Packit Service 82fcde
   the NSIZE-DSIZE least significant quotient limbs at QP
Packit Service 82fcde
   and the DSIZE long remainder at NP.  If QEXTRA_LIMBS is
Packit Service 82fcde
   non-zero, generate that many fraction bits and append them after the
Packit Service 82fcde
   other quotient limbs.
Packit Service 82fcde
   Return the most significant limb of the quotient, this is always 0 or 1.
Packit Service 82fcde
Packit Service 82fcde
   Preconditions:
Packit Service 82fcde
   0. NSIZE >= DSIZE.
Packit Service 82fcde
   1. The most significant bit of the divisor must be set.
Packit Service 82fcde
   2. QP must either not overlap with the input operands at all, or
Packit Service 82fcde
      QP + DSIZE >= NP must hold true.  (This means that it's
Packit Service 82fcde
      possible to put the quotient in the high part of NUM, right after the
Packit Service 82fcde
      remainder in NUM.
Packit Service 82fcde
   3. NSIZE >= DSIZE, even if QEXTRA_LIMBS is non-zero.  */
Packit Service 82fcde
Packit Service 82fcde
mp_limb_t
Packit Service 82fcde
mpn_divrem (mp_ptr qp, mp_size_t qextra_limbs,
Packit Service 82fcde
	    mp_ptr np, mp_size_t nsize,
Packit Service 82fcde
	    mp_srcptr dp, mp_size_t dsize)
Packit Service 82fcde
{
Packit Service 82fcde
  mp_limb_t most_significant_q_limb = 0;
Packit Service 82fcde
Packit Service 82fcde
  switch (dsize)
Packit Service 82fcde
    {
Packit Service 82fcde
    case 0:
Packit Service 82fcde
      /* We are asked to divide by zero, so go ahead and do it!  (To make
Packit Service 82fcde
	 the compiler not remove this statement, return the value.)  */
Packit Service 82fcde
      return 1 / dsize;
Packit Service 82fcde
Packit Service 82fcde
    case 1:
Packit Service 82fcde
      {
Packit Service 82fcde
	mp_size_t i;
Packit Service 82fcde
	mp_limb_t n1;
Packit Service 82fcde
	mp_limb_t d;
Packit Service 82fcde
Packit Service 82fcde
	d = dp[0];
Packit Service 82fcde
	n1 = np[nsize - 1];
Packit Service 82fcde
Packit Service 82fcde
	if (n1 >= d)
Packit Service 82fcde
	  {
Packit Service 82fcde
	    n1 -= d;
Packit Service 82fcde
	    most_significant_q_limb = 1;
Packit Service 82fcde
	  }
Packit Service 82fcde
Packit Service 82fcde
	qp += qextra_limbs;
Packit Service 82fcde
	for (i = nsize - 2; i >= 0; i--)
Packit Service 82fcde
	  udiv_qrnnd (qp[i], n1, n1, np[i], d);
Packit Service 82fcde
	qp -= qextra_limbs;
Packit Service 82fcde
Packit Service 82fcde
	for (i = qextra_limbs - 1; i >= 0; i--)
Packit Service 82fcde
	  udiv_qrnnd (qp[i], n1, n1, 0, d);
Packit Service 82fcde
Packit Service 82fcde
	np[0] = n1;
Packit Service 82fcde
      }
Packit Service 82fcde
      break;
Packit Service 82fcde
Packit Service 82fcde
    case 2:
Packit Service 82fcde
      {
Packit Service 82fcde
	mp_size_t i;
Packit Service 82fcde
	mp_limb_t n1, n0, n2;
Packit Service 82fcde
	mp_limb_t d1, d0;
Packit Service 82fcde
Packit Service 82fcde
	np += nsize - 2;
Packit Service 82fcde
	d1 = dp[1];
Packit Service 82fcde
	d0 = dp[0];
Packit Service 82fcde
	n1 = np[1];
Packit Service 82fcde
	n0 = np[0];
Packit Service 82fcde
Packit Service 82fcde
	if (n1 >= d1 && (n1 > d1 || n0 >= d0))
Packit Service 82fcde
	  {
Packit Service 82fcde
	    sub_ddmmss (n1, n0, n1, n0, d1, d0);
Packit Service 82fcde
	    most_significant_q_limb = 1;
Packit Service 82fcde
	  }
Packit Service 82fcde
Packit Service 82fcde
	for (i = qextra_limbs + nsize - 2 - 1; i >= 0; i--)
Packit Service 82fcde
	  {
Packit Service 82fcde
	    mp_limb_t q;
Packit Service 82fcde
	    mp_limb_t r;
Packit Service 82fcde
Packit Service 82fcde
	    if (i >= qextra_limbs)
Packit Service 82fcde
	      np--;
Packit Service 82fcde
	    else
Packit Service 82fcde
	      np[0] = 0;
Packit Service 82fcde
Packit Service 82fcde
	    if (n1 == d1)
Packit Service 82fcde
	      {
Packit Service 82fcde
		/* Q should be either 111..111 or 111..110.  Need special
Packit Service 82fcde
		   treatment of this rare case as normal division would
Packit Service 82fcde
		   give overflow.  */
Packit Service 82fcde
		q = ~(mp_limb_t) 0;
Packit Service 82fcde
Packit Service 82fcde
		r = n0 + d1;
Packit Service 82fcde
		if (r < d1)	/* Carry in the addition? */
Packit Service 82fcde
		  {
Packit Service 82fcde
		    add_ssaaaa (n1, n0, r - d0, np[0], 0, d0);
Packit Service 82fcde
		    qp[i] = q;
Packit Service 82fcde
		    continue;
Packit Service 82fcde
		  }
Packit Service 82fcde
		n1 = d0 - (d0 != 0);
Packit Service 82fcde
		n0 = -d0;
Packit Service 82fcde
	      }
Packit Service 82fcde
	    else
Packit Service 82fcde
	      {
Packit Service 82fcde
		udiv_qrnnd (q, r, n1, n0, d1);
Packit Service 82fcde
		umul_ppmm (n1, n0, d0, q);
Packit Service 82fcde
	      }
Packit Service 82fcde
Packit Service 82fcde
	    n2 = np[0];
Packit Service 82fcde
	  q_test:
Packit Service 82fcde
	    if (n1 > r || (n1 == r && n0 > n2))
Packit Service 82fcde
	      {
Packit Service 82fcde
		/* The estimated Q was too large.  */
Packit Service 82fcde
		q--;
Packit Service 82fcde
Packit Service 82fcde
		sub_ddmmss (n1, n0, n1, n0, 0, d0);
Packit Service 82fcde
		r += d1;
Packit Service 82fcde
		if (r >= d1)	/* If not carry, test Q again.  */
Packit Service 82fcde
		  goto q_test;
Packit Service 82fcde
	      }
Packit Service 82fcde
Packit Service 82fcde
	    qp[i] = q;
Packit Service 82fcde
	    sub_ddmmss (n1, n0, r, n2, n1, n0);
Packit Service 82fcde
	  }
Packit Service 82fcde
	np[1] = n1;
Packit Service 82fcde
	np[0] = n0;
Packit Service 82fcde
      }
Packit Service 82fcde
      break;
Packit Service 82fcde
Packit Service 82fcde
    default:
Packit Service 82fcde
      {
Packit Service 82fcde
	mp_size_t i;
Packit Service 82fcde
	mp_limb_t dX, d1, n0;
Packit Service 82fcde
Packit Service 82fcde
	np += nsize - dsize;
Packit Service 82fcde
	dX = dp[dsize - 1];
Packit Service 82fcde
	d1 = dp[dsize - 2];
Packit Service 82fcde
	n0 = np[dsize - 1];
Packit Service 82fcde
Packit Service 82fcde
	if (n0 >= dX)
Packit Service 82fcde
	  {
Packit Service 82fcde
	    if (n0 > dX || mpn_cmp (np, dp, dsize - 1) >= 0)
Packit Service 82fcde
	      {
Packit Service 82fcde
		mpn_sub_n (np, np, dp, dsize);
Packit Service 82fcde
		n0 = np[dsize - 1];
Packit Service 82fcde
		most_significant_q_limb = 1;
Packit Service 82fcde
	      }
Packit Service 82fcde
	  }
Packit Service 82fcde
Packit Service 82fcde
	for (i = qextra_limbs + nsize - dsize - 1; i >= 0; i--)
Packit Service 82fcde
	  {
Packit Service 82fcde
	    mp_limb_t q;
Packit Service 82fcde
	    mp_limb_t n1, n2;
Packit Service 82fcde
	    mp_limb_t cy_limb;
Packit Service 82fcde
Packit Service 82fcde
	    if (i >= qextra_limbs)
Packit Service 82fcde
	      {
Packit Service 82fcde
		np--;
Packit Service 82fcde
		n2 = np[dsize];
Packit Service 82fcde
	      }
Packit Service 82fcde
	    else
Packit Service 82fcde
	      {
Packit Service 82fcde
		n2 = np[dsize - 1];
Packit Service 82fcde
		MPN_COPY_DECR (np + 1, np, dsize);
Packit Service 82fcde
		np[0] = 0;
Packit Service 82fcde
	      }
Packit Service 82fcde
Packit Service 82fcde
	    if (n0 == dX)
Packit Service 82fcde
	      /* This might over-estimate q, but it's probably not worth
Packit Service 82fcde
		 the extra code here to find out.  */
Packit Service 82fcde
	      q = ~(mp_limb_t) 0;
Packit Service 82fcde
	    else
Packit Service 82fcde
	      {
Packit Service 82fcde
		mp_limb_t r;
Packit Service 82fcde
Packit Service 82fcde
		udiv_qrnnd (q, r, n0, np[dsize - 1], dX);
Packit Service 82fcde
		umul_ppmm (n1, n0, d1, q);
Packit Service 82fcde
Packit Service 82fcde
		while (n1 > r || (n1 == r && n0 > np[dsize - 2]))
Packit Service 82fcde
		  {
Packit Service 82fcde
		    q--;
Packit Service 82fcde
		    r += dX;
Packit Service 82fcde
		    if (r < dX)	/* I.e. "carry in previous addition?"  */
Packit Service 82fcde
		      break;
Packit Service 82fcde
		    n1 -= n0 < d1;
Packit Service 82fcde
		    n0 -= d1;
Packit Service 82fcde
		  }
Packit Service 82fcde
	      }
Packit Service 82fcde
Packit Service 82fcde
	    /* Possible optimization: We already have (q * n0) and (1 * n1)
Packit Service 82fcde
	       after the calculation of q.  Taking advantage of that, we
Packit Service 82fcde
	       could make this loop make two iterations less.  */
Packit Service 82fcde
Packit Service 82fcde
	    cy_limb = mpn_submul_1 (np, dp, dsize, q);
Packit Service 82fcde
Packit Service 82fcde
	    if (n2 != cy_limb)
Packit Service 82fcde
	      {
Packit Service 82fcde
		mpn_add_n (np, np, dp, dsize);
Packit Service 82fcde
		q--;
Packit Service 82fcde
	      }
Packit Service 82fcde
Packit Service 82fcde
	    qp[i] = q;
Packit Service 82fcde
	    n0 = np[dsize - 1];
Packit Service 82fcde
	  }
Packit Service 82fcde
      }
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  return most_significant_q_limb;
Packit Service 82fcde
}