예제 #1
0
void
mpf_ui_sub (mpf_ptr r, unsigned long int u, mpf_srcptr v)
{
#if 1
  __mpf_struct uu;
  mp_limb_t ul;

  if (u == 0)
    {
      mpf_neg (r, v);
      return;
    }

  ul = u;
  uu._mp_size = 1;
  uu._mp_d = &ul;
  uu._mp_exp = 1;
  mpf_sub (r, &uu, v);

#else
  mp_srcptr up, vp;
  mp_ptr rp, tp;
  mp_size_t usize, vsize, rsize;
  mp_size_t prec;
  mp_exp_t uexp;
  mp_size_t ediff;
  int negate;
  mp_limb_t ulimb;
  TMP_DECL;

  vsize = v->_mp_size;

  /* Handle special cases that don't work in generic code below.  */
  if (u == 0)
    {
      mpf_neg (r, v);
      return;
    }
  if (vsize == 0)
    {
      mpf_set_ui (r, u);
      return;
    }

  /* If signs of U and V are different, perform addition.  */
  if (vsize < 0)
    {
      __mpf_struct v_negated;
      v_negated._mp_size = -vsize;
      v_negated._mp_exp = v->_mp_exp;
      v_negated._mp_d = v->_mp_d;
      mpf_add_ui (r, &v_negated, u);
      return;
    }

  /* Signs are now known to be the same.  */
  ASSERT (vsize > 0);
  ulimb = u;
  /* Make U be the operand with the largest exponent.  */
  negate = 1 < v->_mp_exp;
  prec = r->_mp_prec + negate;
  rp = r->_mp_d;
  if (negate)
    {
      usize = vsize;
      vsize = 1;
      up = v->_mp_d;
      vp = &ulimb;
      uexp = v->_mp_exp;
      ediff = uexp - 1;

      /* If U extends beyond PREC, ignore the part that does.  */
      if (usize > prec)
	{
	  up += usize - prec;
	  usize = prec;
	}
      ASSERT (ediff > 0);
    }
  else
    {
      vp = v->_mp_d;
      ediff = 1 - v->_mp_exp;
  /* Ignore leading limbs in U and V that are equal.  Doing
     this helps increase the precision of the result.  */
      if (ediff == 0 && ulimb == vp[vsize - 1])
	{
	  usize = 0;
	  vsize--;
	  uexp = 0;
	  /* Note that V might now have leading zero limbs.
	     In that case we have to adjust uexp.  */
	  for (;;)
	    {
	      if (vsize == 0) {
		rsize = 0;
		uexp = 0;
		goto done;
	      }
	      if ( vp[vsize - 1] != 0)
		break;
	      vsize--, uexp--;
	    }
	}
      else
	{
	  usize = 1;
	  uexp = 1;
	  up = &ulimb;
	}
      ASSERT (usize <= prec);
    }

  if (ediff >= prec)
    {
      /* V completely cancelled.  */
      if (rp != up)
	MPN_COPY (rp, up, usize);
      rsize = usize;
    }
  else
    {
  /* If V extends beyond PREC, ignore the part that does.
     Note that this can make vsize neither zero nor negative.  */
  if (vsize + ediff > prec)
    {
      vp += vsize + ediff - prec;
      vsize = prec - ediff;
    }

      /* Locate the least significant non-zero limb in (the needed
	 parts of) U and V, to simplify the code below.  */
      ASSERT (vsize > 0);
      for (;;)
	{
	  if (vp[0] != 0)
	    break;
	  vp++, vsize--;
	  if (vsize == 0)
	    {
	      MPN_COPY (rp, up, usize);
	      rsize = usize;
	      goto done;
	    }
	}
      for (;;)
	{
	  if (usize == 0)
	    {
	      MPN_COPY (rp, vp, vsize);
	      rsize = vsize;
	      negate ^= 1;
	      goto done;
	    }
	  if (up[0] != 0)
	    break;
	  up++, usize--;
	}

      ASSERT (usize > 0 && vsize > 0);
      TMP_MARK;

      tp = TMP_ALLOC_LIMBS (prec);

      /* uuuu     |  uuuu     |  uuuu     |  uuuu     |  uuuu    */
      /* vvvvvvv  |  vv       |    vvvvv  |    v      |       vv */

      if (usize > ediff)
	{
	  /* U and V partially overlaps.  */
	  if (ediff == 0)
	    {
	      ASSERT (usize == 1 && vsize >= 1 && ulimb == *up); /* usize is 1>ediff, vsize >= 1 */
	      if (1 < vsize)
		{
		  /* u        */
		  /* vvvvvvv  */
		  rsize = vsize;
		  vsize -= 1;
		  /* mpn_cmp (up, vp + vsize - usize, usize) > 0 */
		  if (ulimb > vp[vsize])
		    {
		      tp[vsize] = ulimb - vp[vsize] - 1;
		      ASSERT_CARRY (mpn_neg (tp, vp, vsize));
		    }
		  else
		    {
		      /* vvvvvvv  */  /* Swap U and V. */
		      /* u        */
		      MPN_COPY (tp, vp, vsize);
		      tp[vsize] = vp[vsize] - ulimb;
		      negate = 1;
		    }
		}
	      else /* vsize == usize == 1 */
		{
		  /* u     */
		  /* v     */
		  rsize = 1;
		  negate = ulimb < vp[0];
		  tp[0] = negate ? vp[0] - ulimb: ulimb - vp[0];
		}
	    }
	  else
	    {
	      ASSERT (vsize + ediff <= usize);
	      ASSERT (vsize == 1 && usize >= 2 && ulimb == *vp);
		{
		  /* uuuu     */
		  /*   v      */
		  mp_size_t size;
		  size = usize - ediff - 1;
		  MPN_COPY (tp, up, size);
		  ASSERT_NOCARRY (mpn_sub_1 (tp + size, up + size, usize - size, ulimb));
		  rsize = usize;
		}
		/* Other cases are not possible */
		/* uuuu     */
		/*   vvvvv  */
	    }
	}
      else
	{
	  /* uuuu     */
	  /*      vv  */
	  mp_size_t size, i;
	  ASSERT_CARRY (mpn_neg (tp, vp, vsize));
	  rsize = vsize + ediff;
	  size = rsize - usize;
	  for (i = vsize; i < size; i++)
	    tp[i] = GMP_NUMB_MAX;
	  ASSERT_NOCARRY (mpn_sub_1 (tp + size, up, usize, CNST_LIMB (1)));
	}

      /* Full normalize.  Optimize later.  */
      while (rsize != 0 && tp[rsize - 1] == 0)
	{
	  rsize--;
	  uexp--;
	}
      MPN_COPY (rp, tp, rsize);
      TMP_FREE;
    }

 done:
  r->_mp_size = negate ? -rsize : rsize;
  r->_mp_exp = uexp;
#endif
}
예제 #2
0
mp_limb_t mpn_divrem_euclidean_qr_2(mp_ptr qp, mp_ptr xp, mp_size_t xn, mp_srcptr dp)
{
   mp_size_t qn;
   mp_limb_t qf, t[2], t1[2], q, h, l, d1, d2, i;
   int c1, c3, c4;

   ASSERT(xn >= 2);
   ASSERT_MPN(dp, 2);
   ASSERT_MPN(xp, xn);
   ASSERT(dp[1] != 0);

   qn = xn - 1;

   /* ASSERT(!MPN_OVERLAP_P(qp, qn, xp, xn)); */ /* FIXME: correct this overlap requirement */
   ASSERT((dp[1]>>(GMP_NUMB_BITS - 1)) != 0);

   h = 0;
   d1 = dp[1];
   d2 = dp[0];
   
   invert_limb(i, d1);
   
   l = xp[xn - 1];
   qn = xn - 2;
   t[0] = xp[qn];

   if (l < d1)
   { 
      h = t[1] = l;
      l = t[0] = xp[qn];
      qf = 0;
   }
   else
   {
      qf = 1;
      t[1] = l - d1;
      t1[1] = 0;
      t1[0] = d2;
   
      if (mpn_sub_n(t, t, t1, 2))
      {
         qf--;
         mpn_add_n(t, t, dp, 2);
      }
   
      h = t[1];
      l = t[0];
   }

   for (qn = xn - 3; qn >= 0; qn--)
   {
      t[0] = xp[qn];
    
      if (h < d1)
      {
         udiv_qrnnd_preinv(q, t[1], h, l, d1, i);
         umul_ppmm(t1[1], t1[0], q, d2);
         if (mpn_sub_n(t, t, t1, 2))
         {
            q--;
            if (mpn_add_n(t, t, dp, 2) == 0)
            {
               q--;
               
               ASSERT_CARRY(mpn_add_n(t, t, dp, 2));
            }
         }
      }
      else
      {
         ASSERT(h == d1);
         q = -1;
         t[1] = l;
         c3 = mpn_add_n(t, t, dp, 2);
         c1 = mpn_sub_1(t + 1, t + 1, 1, d2);
         c4 = c3 - c1;
       
         if (l >= d1)
         {
            ASSERT(c3 != 0);
            ASSERT(c4 == 0);
         } /* our guess is B + 1, so q = B - 1 is correct */
         else
         {
            ASSERT(c4 <= 0); /* our guess is B so q = B - 1 or B - 2 */
            if (c4 != 0)
            {
               q--;
               mpn_add_n(t, t, dp, 2);
            }
         }       
      }
    
      h = t[1];
      l = t[0];
      qp[qn] = q;
   }

   xp[1] = t[1];
   xp[0] = t[0];

   return qf;
}