Blob Blame History Raw
/* mpc_div -- Divide two complex numbers.

Copyright (C) INRIA, 2002, 2003, 2004, 2005, 2008, 2009, 2010

This file is part of the MPC Library.

The MPC Library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or (at your
option) any later version.

The MPC Library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
License for more details.

You should have received a copy of the GNU Lesser General Public License
along with the MPC Library; see the file COPYING.LIB.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA. */

#include "mpc-impl.h"

static int
mpc_div_zero (mpc_ptr a, mpc_srcptr z, mpc_srcptr w, mpc_rnd_t rnd)
{
   /* Assumes w==0, implementation according to C99 G.5.1.8 */
   int sign = MPFR_SIGNBIT (MPC_RE (w));
   mpfr_t infty;
   mpfr_set_inf (infty, sign);
   mpfr_mul (MPC_RE (a), infty, MPC_RE (z), MPC_RND_RE (rnd));
   mpfr_mul (MPC_IM (a), infty, MPC_IM (z), MPC_RND_IM (rnd));
   return MPC_INEX (0, 0); /* exact */
}

static int
mpc_div_inf_fin (mpc_ptr rop, mpc_srcptr z, mpc_srcptr w)
{
   /* Assumes w finite and non-zero and z infinite; implementation
      according to C99 G.5.1.8                                     */
   int a, b, x, y;

   a = (mpfr_inf_p (MPC_RE (z)) ? MPFR_SIGNBIT (MPC_RE (z)) : 0);
   b = (mpfr_inf_p (MPC_IM (z)) ? MPFR_SIGNBIT (MPC_IM (z)) : 0);

   /* x = MPC_MPFR_SIGN (a * MPC_RE (w) + b * MPC_IM (w)) */
   /* y = MPC_MPFR_SIGN (b * MPC_RE (w) - a * MPC_IM (w)) */
   if (a == 0 || b == 0) {
      x = a * MPC_MPFR_SIGN (MPC_RE (w)) + b * MPC_MPFR_SIGN (MPC_IM (w));
      y = b * MPC_MPFR_SIGN (MPC_RE (w)) - a * MPC_MPFR_SIGN (MPC_IM (w));
   }
   else {
      /* Both parts of z are infinite; x could be determined by sign
         considerations and comparisons. Since operations with non-finite
         numbers are not considered time-critical, we let mpfr do the work. */
      mpfr_t sign;
      mpfr_init2 (sign, 2);
         /* This is enough to determine the sign of sums and differences. */

      if (a == 1)
         if (b == 1) {
            mpfr_add (sign, MPC_RE (w), MPC_IM (w), GMP_RNDN);
            x = MPC_MPFR_SIGN (sign);
            mpfr_sub (sign, MPC_RE (w), MPC_IM (w), GMP_RNDN);
            y = MPC_MPFR_SIGN (sign);
         }
         else { /* b == -1 */
            mpfr_sub (sign, MPC_RE (w), MPC_IM (w), GMP_RNDN);
            x = MPC_MPFR_SIGN (sign);
            mpfr_add (sign, MPC_RE (w), MPC_IM (w), GMP_RNDN);
            y = -MPC_MPFR_SIGN (sign);
         }
      else /* a == -1 */
         if (b == 1) {
            mpfr_sub (sign, MPC_IM (w), MPC_RE (w), GMP_RNDN);
            x = MPC_MPFR_SIGN (sign);
            mpfr_add (sign, MPC_RE (w), MPC_IM (w), GMP_RNDN);
            y = MPC_MPFR_SIGN (sign);
         }
         else { /* b == -1 */
            mpfr_add (sign, MPC_RE (w), MPC_IM (w), GMP_RNDN);
            x = -MPC_MPFR_SIGN (sign);
            mpfr_sub (sign, MPC_IM (w), MPC_RE (w), GMP_RNDN);
            y = MPC_MPFR_SIGN (sign);
         }
      mpfr_clear (sign);
   }

   if (x == 0)
      mpfr_set_nan (MPC_RE (rop));
   else
      mpfr_set_inf (MPC_RE (rop), x);
   if (y == 0)
      mpfr_set_nan (MPC_IM (rop));
   else
      mpfr_set_inf (MPC_IM (rop), y);

   return MPC_INEX (0, 0); /* exact */
}


static int
mpc_div_fin_inf (mpc_ptr rop, mpc_srcptr z, mpc_srcptr w)
{
   /* Assumes z finite and w infinite; implementation according to
      C99 G.5.1.8                                                  */
   mpfr_t c, d, a, b, x, y, zero;

   mpfr_init2 (c, 2); /* needed to hold a signed zero, +1 or -1 */
   mpfr_init2 (d, 2);
   mpfr_init2 (x, 2);
   mpfr_init2 (y, 2);
   mpfr_init2 (zero, 2);
   mpfr_set_ui (zero, 0ul, GMP_RNDN);
   mpfr_init2 (a, mpfr_get_prec (MPC_RE (z)));
   mpfr_init2 (b, mpfr_get_prec (MPC_IM (z)));

   mpfr_set_ui (c, (mpfr_inf_p (MPC_RE (w)) ? 1 : 0), GMP_RNDN);
   MPFR_COPYSIGN (c, c, MPC_RE (w), GMP_RNDN);
   mpfr_set_ui (d, (mpfr_inf_p (MPC_IM (w)) ? 1 : 0), GMP_RNDN);
   MPFR_COPYSIGN (d, d, MPC_IM (w), GMP_RNDN);

   mpfr_mul (a, MPC_RE (z), c, GMP_RNDN); /* exact */
   mpfr_mul (b, MPC_IM (z), d, GMP_RNDN);
   mpfr_add (x, a, b, GMP_RNDN);

   mpfr_mul (b, MPC_IM (z), c, GMP_RNDN);
   mpfr_mul (a, MPC_RE (z), d, GMP_RNDN);
   mpfr_sub (y, b, a, GMP_RNDN);

   MPFR_COPYSIGN (MPC_RE (rop), zero, x, GMP_RNDN);
   MPFR_COPYSIGN (MPC_IM (rop), zero, y, GMP_RNDN);

   mpfr_clear (c);
   mpfr_clear (d);
   mpfr_clear (x);
   mpfr_clear (y);
   mpfr_clear (zero);
   mpfr_clear (a);
   mpfr_clear (b);

   return MPC_INEX (0, 0); /* exact */
}


int
mpc_div (mpc_ptr a, mpc_srcptr b, mpc_srcptr c, mpc_rnd_t rnd)
{
   int ok_re = 0, ok_im = 0;
   mpc_t res, c_conj;
   mpfr_t q;
   mpfr_prec_t prec;
   int inexact_prod, inexact_norm, inexact_re, inexact_im, loops = 0;

   /* save signs of operands in case there are overlaps */
   int brs = MPFR_SIGNBIT (MPC_RE (b));
   int bis = MPFR_SIGNBIT (MPC_IM (b));
   int crs = MPFR_SIGNBIT (MPC_RE (c));
   int cis = MPFR_SIGNBIT (MPC_IM (c));

   /* According to the C standard G.3, there are three types of numbers:   */
   /* finite (both parts are usual real numbers; contains 0), infinite     */
   /* (at least one part is a real infinity) and all others; the latter    */
   /* are numbers containing a nan, but no infinity, and could reasonably  */
   /* be called nan.                                                       */
   /* By G.5.1.4, infinite/finite=infinite; finite/infinite=0;             */
   /* all other divisions that are not finite/finite return nan+i*nan.     */
   /* Division by 0 could be handled by the following case of division by  */
   /* a real; we handle it separately instead.                             */
   if (mpc_zero_p (c))
      return mpc_div_zero (a, b, c, rnd);
   else {
      if (mpc_inf_p (b) && mpc_fin_p (c))
         return mpc_div_inf_fin (a, b, c);
      else if (mpc_fin_p (b) && mpc_inf_p (c))
         return mpc_div_fin_inf (a, b, c);
      else if (!mpc_fin_p (b) || !mpc_fin_p (c)) {
         mpfr_set_nan (MPC_RE (a));
         mpfr_set_nan (MPC_IM (a));
         return MPC_INEX (0, 0);
      }
   }

   /* check for real divisor */
   if (mpfr_zero_p(MPC_IM(c))) /* (re_b+i*im_b)/c = re_b/c + i * (im_b/c) */
   {
      /* warning: a may overlap with b,c so treat the imaginary part first */
      inexact_im = mpfr_div (MPC_IM(a), MPC_IM(b), MPC_RE(c), MPC_RND_IM(rnd));
      inexact_re = mpfr_div (MPC_RE(a), MPC_RE(b), MPC_RE(c), MPC_RND_RE(rnd));

      /* correct signs of zeroes if necessary, which does not affect the
         inexact flags                                                    */
      if (mpfr_zero_p (MPC_RE (a)))
         mpfr_setsign (MPC_RE (a), MPC_RE (a), (brs != crs && bis != cis),
            GMP_RNDN); /* exact */
      if (mpfr_zero_p (MPC_IM (a)))
         mpfr_setsign (MPC_IM (a), MPC_IM (a), (bis != crs && brs == cis),
            GMP_RNDN);

      return MPC_INEX(inexact_re, inexact_im);
   }

   /* check for purely imaginary divisor */
   if (mpfr_zero_p(MPC_RE(c)))
   {
      /* (re_b+i*im_b)/(i*c) = im_b/c - i * (re_b/c) */
      int overlap = (a == b) || (a == c);
      int imag_b = mpfr_zero_p (MPC_RE (b));
      mpfr_t cloc;
      mpc_t tmpa;
      mpc_ptr dest = (overlap) ? tmpa : a;

      if (overlap)
         mpc_init3 (tmpa, MPC_PREC_RE (a), MPC_PREC_IM (a));

      cloc[0] = MPC_IM(c)[0]; /* copies mpfr struct IM(c) into cloc */
      inexact_re = mpfr_div (MPC_RE(dest), MPC_IM(b), cloc, MPC_RND_RE(rnd));
      mpfr_neg (cloc, cloc, GMP_RNDN);
      /* changes the sign only in cloc, not in c; no need to correct later */
      inexact_im = mpfr_div (MPC_IM(dest), MPC_RE(b), cloc, MPC_RND_IM(rnd));

      if (overlap)
        {
          /* Note: we could use mpc_swap here, but this might cause problems
             if a and tmpa have been allocated using different methods, since
             it will swap the significands of a and tmpa. See http://
         lists.gforge.inria.fr/pipermail/mpc-discuss/2009-August/000504.html */
          mpc_set (a, tmpa, MPC_RNDNN); /* exact */
          mpc_clear (tmpa);
        }

      /* correct signs of zeroes if necessary, which does not affect the
         inexact flags                                                    */
      if (mpfr_zero_p (MPC_RE (a)))
         mpfr_setsign (MPC_RE (a), MPC_RE (a), (brs != crs && bis != cis),
            GMP_RNDN); /* exact */
      if (imag_b)
         mpfr_setsign (MPC_IM (a), MPC_IM (a), (bis != crs && brs == cis),
            GMP_RNDN);

      return MPC_INEX(inexact_re, inexact_im);
   }

   prec = MPC_MAX_PREC(a);

   mpc_init2 (res, 2);
   mpfr_init (q);

   /* create the conjugate of c in c_conj without allocating new memory */
   MPC_RE (c_conj)[0] = MPC_RE (c)[0];
   MPC_IM (c_conj)[0] = MPC_IM (c)[0];
   MPFR_CHANGE_SIGN (MPC_IM (c_conj));

   do
   {
      loops ++;
      prec += loops <= 2 ? mpc_ceil_log2 (prec) + 5 : prec / 2;

      mpc_set_prec (res, prec);
      mpfr_set_prec (q, prec);

      /* first compute norm(c)^2 */
      inexact_norm = mpc_norm (q, c, GMP_RNDD);

      /* now compute b*conjugate(c) */
      /* We need rounding away from zero for both the real and the imagin-  */
      /* ary part; then the final result is also rounded away from zero.    */
      /* The error is less than 1 ulp. Since this is not implemented in     */
      /* mpc, we round towards zero and add 1 ulp to the absolute values    */
      /* if they are not exact. */
      inexact_prod = mpc_mul (res, b, c_conj, MPC_RNDZZ);
      inexact_re = MPC_INEX_RE (inexact_prod);
      inexact_im = MPC_INEX_IM (inexact_prod);
      if (inexact_re != 0)
         MPFR_ADD_ONE_ULP (MPC_RE (res));
      if (inexact_im != 0)
         MPFR_ADD_ONE_ULP (MPC_IM (res));

      /* divide the product by the norm */
      if (inexact_norm == 0 && (inexact_re == 0 || inexact_im == 0))
      {
         /* The division has good chances to be exact in at least one part.   */
         /* Since this can cause problems when not rounding to the nearest,   */
         /* we use the division code of mpfr, which handles the situation.    */
         if (MPFR_SIGN (MPC_RE (res)) > 0)
         {
            inexact_re |= mpfr_div (MPC_RE (res), MPC_RE (res), q, GMP_RNDU);
            ok_re = mpfr_inf_p (MPC_RE (res)) || mpfr_zero_p (MPC_RE (res)) ||
              mpfr_can_round (MPC_RE (res), prec - 4, GMP_RNDU,
                              MPC_RND_RE(rnd), MPC_PREC_RE(a));
         }
         else
         {
            inexact_re |= mpfr_div (MPC_RE (res), MPC_RE (res), q, GMP_RNDD);
            ok_re = mpfr_inf_p (MPC_RE (res)) || mpfr_zero_p (MPC_RE (res)) ||
              mpfr_can_round (MPC_RE (res), prec - 4, GMP_RNDD,
                              MPC_RND_RE(rnd), MPC_PREC_RE(a));
         }

         if (ok_re || !inexact_re) /* compute imaginary part */
         {
            if (MPFR_SIGN (MPC_IM (res)) > 0)
            {
               inexact_im |= mpfr_div (MPC_IM (res), MPC_IM (res), q, GMP_RNDU);
               ok_im = mpfr_can_round (MPC_IM (res), prec - 4, GMP_RNDU,
                                       MPC_RND_IM(rnd), MPC_PREC_IM(a));
            }
            else
            {
               inexact_im |= mpfr_div (MPC_IM (res), MPC_IM (res), q, GMP_RNDD);
               ok_im = mpfr_can_round (MPC_IM (res), prec - 4, GMP_RNDD,
                                       MPC_RND_IM(rnd), MPC_PREC_IM(a));
            }
         }
      }
      else
      {
         /* The division is inexact, so for efficiency reasons we invert q */
         /* only once and multiply by the inverse. */
         /* We do not decide about the sign of the difference. */
         if (mpfr_ui_div (q, 1, q, GMP_RNDU) || inexact_norm)
           {
             /* if 1/q is inexact, the approximations of the real and
                imaginary part below will be inexact, unless RE(res)
                or IM(res) is zero */
             inexact_re = inexact_re || !mpfr_zero_p (MPC_RE (res));
             inexact_im = inexact_im || !mpfr_zero_p (MPC_IM (res));
           }
         if (MPFR_SIGN (MPC_RE (res)) > 0)
         {
           inexact_re = mpfr_mul (MPC_RE (res), MPC_RE (res), q, GMP_RNDU)
             || inexact_re;
           ok_re = mpfr_inf_p (MPC_RE (res)) || mpfr_zero_p (MPC_RE (res)) ||
             mpfr_can_round (MPC_RE (res), prec - 4, GMP_RNDU,
                             MPC_RND_RE(rnd), MPC_PREC_RE(a));
         }
         else
         {
           inexact_re = mpfr_mul (MPC_RE (res), MPC_RE (res), q, GMP_RNDD)
             || inexact_re;
           ok_re = mpfr_inf_p (MPC_RE (res)) || mpfr_zero_p (MPC_RE (res)) ||
             mpfr_can_round (MPC_RE (res), prec - 4, GMP_RNDD,
                             MPC_RND_RE(rnd), MPC_PREC_RE(a));
         }

         if (ok_re) /* compute imaginary part */
         {
            if (MPFR_SIGN (MPC_IM (res)) > 0)
            {
              inexact_im = mpfr_mul (MPC_IM (res), MPC_IM (res), q, GMP_RNDU)
                || inexact_im;
              ok_im = mpfr_can_round (MPC_IM (res), prec - 4, GMP_RNDU,
                                      MPC_RND_IM(rnd), MPC_PREC_IM(a));
            }
            else
            {
              inexact_im = mpfr_mul (MPC_IM (res), MPC_IM (res), q, GMP_RNDD)
                || inexact_im;
              ok_im = mpfr_can_round (MPC_IM (res), prec - 4, GMP_RNDD,
                                      MPC_RND_IM(rnd), MPC_PREC_IM(a));
            }
         }
      }

      /* check for overflow or underflow on the imaginary part */
      if (ok_im == 0 &&
          (mpfr_inf_p (MPC_IM (res)) || mpfr_zero_p (MPC_IM (res))))
        ok_im = 1;
   }
   while ((!ok_re && inexact_re) || (!ok_im && inexact_im));

   mpc_set (a, res, rnd);

   /* fix inexact flags in case of overflow/underflow */
   if (mpfr_inf_p (MPC_RE (res)))
     inexact_re = mpfr_sgn (MPC_RE (res));
   else if (mpfr_zero_p (MPC_RE (res)))
     inexact_re = -mpfr_sgn (MPC_RE (res));
   if (mpfr_inf_p (MPC_IM (res)))
     inexact_im = mpfr_sgn (MPC_IM (res));
   else if (mpfr_zero_p (MPC_IM (res)))
     inexact_im = -mpfr_sgn (MPC_IM (res));

   mpc_clear (res);
   mpfr_clear (q);

   return (MPC_INEX (inexact_re, inexact_im));
      /* Only exactness vs. inexactness is tested, not the sign. */
}