Blame src/sqrt.c

Packit Service 2e9770
/* mpc_sqrt -- Take the square root of a complex number.
Packit Service 2e9770
Packit Service 2e9770
Copyright (C) 2002, 2008, 2009, 2010, 2011, 2012 INRIA
Packit Service 2e9770
Packit Service 2e9770
This file is part of GNU MPC.
Packit Service 2e9770
Packit Service 2e9770
GNU MPC is free software; you can redistribute it and/or modify it under
Packit Service 2e9770
the terms of the GNU Lesser General Public License as published by the
Packit Service 2e9770
Free Software Foundation; either version 3 of the License, or (at your
Packit Service 2e9770
option) any later version.
Packit Service 2e9770
Packit Service 2e9770
GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
Packit Service 2e9770
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
Packit Service 2e9770
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
Packit Service 2e9770
more details.
Packit Service 2e9770
Packit Service 2e9770
You should have received a copy of the GNU Lesser General Public License
Packit Service 2e9770
along with this program. If not, see http://www.gnu.org/licenses/ .
Packit Service 2e9770
*/
Packit Service 2e9770
Packit Service 2e9770
#include "mpc-impl.h"
Packit Service 2e9770
Packit Service 2e9770
#if MPFR_VERSION_MAJOR < 3
Packit Service 2e9770
#define mpfr_min_prec(x) \
Packit Service 2e9770
   ( ((prec + BITS_PER_MP_LIMB - 1) / BITS_PER_MP_LIMB) * BITS_PER_MP_LIMB \
Packit Service 2e9770
     - mpn_scan1 (x->_mpfr_d, 0))
Packit Service 2e9770
#endif
Packit Service 2e9770
Packit Service 2e9770
Packit Service 2e9770
int
Packit Service 2e9770
mpc_sqrt (mpc_ptr a, mpc_srcptr b, mpc_rnd_t rnd)
Packit Service 2e9770
{
Packit Service 2e9770
  int ok_w, ok_t = 0;
Packit Service 2e9770
  mpfr_t    w, t;
Packit Service 2e9770
  mpfr_rnd_t  rnd_w, rnd_t;
Packit Service 2e9770
  mpfr_prec_t prec_w, prec_t;
Packit Service 2e9770
  /* the rounding mode and the precision required for w and t, which can */
Packit Service 2e9770
  /* be either the real or the imaginary part of a */
Packit Service 2e9770
  mpfr_prec_t prec;
Packit Service 2e9770
  int inex_w, inex_t = 1, inex_re, inex_im, loops = 0;
Packit Service 2e9770
  const int re_cmp = mpfr_cmp_ui (mpc_realref (b), 0),
Packit Service 2e9770
            im_cmp = mpfr_cmp_ui (mpc_imagref (b), 0);
Packit Service 2e9770
     /* comparison of the real/imaginary part of b with 0 */
Packit Service 2e9770
  int repr_w, repr_t = 0 /* to avoid gcc warning */ ;
Packit Service 2e9770
     /* flag indicating whether the computed value is already representable
Packit Service 2e9770
        at the target precision */
Packit Service 2e9770
  const int im_sgn = mpfr_signbit (mpc_imagref (b)) == 0 ? 0 : -1;
Packit Service 2e9770
     /* we need to know the sign of Im(b) when it is +/-0 */
Packit Service 2e9770
  const mpfr_rnd_t r = im_sgn ? MPFR_RNDD : MPFR_RNDU;
Packit Service 2e9770
     /* rounding mode used when computing t */
Packit Service 2e9770
Packit Service 2e9770
  /* special values */
Packit Service 2e9770
  if (!mpc_fin_p (b)) {
Packit Service 2e9770
   /* sqrt(x +i*Inf) = +Inf +I*Inf, even if x = NaN */
Packit Service 2e9770
   /* sqrt(x -i*Inf) = +Inf -I*Inf, even if x = NaN */
Packit Service 2e9770
   if (mpfr_inf_p (mpc_imagref (b)))
Packit Service 2e9770
      {
Packit Service 2e9770
         mpfr_set_inf (mpc_realref (a), +1);
Packit Service 2e9770
         mpfr_set_inf (mpc_imagref (a), im_sgn);
Packit Service 2e9770
         return MPC_INEX (0, 0);
Packit Service 2e9770
      }
Packit Service 2e9770
Packit Service 2e9770
   if (mpfr_inf_p (mpc_realref (b)))
Packit Service 2e9770
      {
Packit Service 2e9770
         if (mpfr_signbit (mpc_realref (b)))
Packit Service 2e9770
         {
Packit Service 2e9770
            if (mpfr_number_p (mpc_imagref (b)))
Packit Service 2e9770
               {
Packit Service 2e9770
               /* sqrt(-Inf +i*y) = +0 +i*Inf, when y positive */
Packit Service 2e9770
               /* sqrt(-Inf +i*y) = +0 -i*Inf, when y positive */
Packit Service 2e9770
               mpfr_set_ui (mpc_realref (a), 0, MPFR_RNDN);
Packit Service 2e9770
               mpfr_set_inf (mpc_imagref (a), im_sgn);
Packit Service 2e9770
               return MPC_INEX (0, 0);
Packit Service 2e9770
               }
Packit Service 2e9770
            else
Packit Service 2e9770
               {
Packit Service 2e9770
               /* sqrt(-Inf +i*NaN) = NaN +/-i*Inf */
Packit Service 2e9770
               mpfr_set_nan (mpc_realref (a));
Packit Service 2e9770
               mpfr_set_inf (mpc_imagref (a), im_sgn);
Packit Service 2e9770
               return MPC_INEX (0, 0);
Packit Service 2e9770
               }
Packit Service 2e9770
         }
Packit Service 2e9770
         else
Packit Service 2e9770
         {
Packit Service 2e9770
            if (mpfr_number_p (mpc_imagref (b)))
Packit Service 2e9770
               {
Packit Service 2e9770
               /* sqrt(+Inf +i*y) = +Inf +i*0, when y positive */
Packit Service 2e9770
               /* sqrt(+Inf +i*y) = +Inf -i*0, when y positive */
Packit Service 2e9770
               mpfr_set_inf (mpc_realref (a), +1);
Packit Service 2e9770
               mpfr_set_ui (mpc_imagref (a), 0, MPFR_RNDN);
Packit Service 2e9770
               if (im_sgn)
Packit Service 2e9770
                  mpc_conj (a, a, MPC_RNDNN);
Packit Service 2e9770
               return MPC_INEX (0, 0);
Packit Service 2e9770
               }
Packit Service 2e9770
            else
Packit Service 2e9770
               {
Packit Service 2e9770
               /* sqrt(+Inf -i*Inf) = +Inf -i*Inf */
Packit Service 2e9770
               /* sqrt(+Inf +i*Inf) = +Inf +i*Inf */
Packit Service 2e9770
               /* sqrt(+Inf +i*NaN) = +Inf +i*NaN */
Packit Service 2e9770
               return mpc_set (a, b, rnd);
Packit Service 2e9770
               }
Packit Service 2e9770
         }
Packit Service 2e9770
      }
Packit Service 2e9770
Packit Service 2e9770
   /* sqrt(x +i*NaN) = NaN +i*NaN, if x is not infinite */
Packit Service 2e9770
   /* sqrt(NaN +i*y) = NaN +i*NaN, if y is not infinite */
Packit Service 2e9770
   if (mpfr_nan_p (mpc_realref (b)) || mpfr_nan_p (mpc_imagref (b)))
Packit Service 2e9770
      {
Packit Service 2e9770
         mpfr_set_nan (mpc_realref (a));
Packit Service 2e9770
         mpfr_set_nan (mpc_imagref (a));
Packit Service 2e9770
         return MPC_INEX (0, 0);
Packit Service 2e9770
      }
Packit Service 2e9770
  }
Packit Service 2e9770
Packit Service 2e9770
  /* purely real */
Packit Service 2e9770
  if (im_cmp == 0)
Packit Service 2e9770
    {
Packit Service 2e9770
      if (re_cmp == 0)
Packit Service 2e9770
        {
Packit Service 2e9770
          mpc_set_ui_ui (a, 0, 0, MPC_RNDNN);
Packit Service 2e9770
          if (im_sgn)
Packit Service 2e9770
            mpc_conj (a, a, MPC_RNDNN);
Packit Service 2e9770
          return MPC_INEX (0, 0);
Packit Service 2e9770
        }
Packit Service 2e9770
      else if (re_cmp > 0)
Packit Service 2e9770
        {
Packit Service 2e9770
          inex_w = mpfr_sqrt (mpc_realref (a), mpc_realref (b), MPC_RND_RE (rnd));
Packit Service 2e9770
          mpfr_set_ui (mpc_imagref (a), 0, MPFR_RNDN);
Packit Service 2e9770
          if (im_sgn)
Packit Service 2e9770
            mpc_conj (a, a, MPC_RNDNN);
Packit Service 2e9770
          return MPC_INEX (inex_w, 0);
Packit Service 2e9770
        }
Packit Service 2e9770
      else
Packit Service 2e9770
        {
Packit Service 2e9770
          mpfr_init2 (w, MPC_PREC_RE (b));
Packit Service 2e9770
          mpfr_neg (w, mpc_realref (b), MPFR_RNDN);
Packit Service 2e9770
          if (im_sgn)
Packit Service 2e9770
            {
Packit Service 2e9770
              inex_w = -mpfr_sqrt (mpc_imagref (a), w, INV_RND (MPC_RND_IM (rnd)));
Packit Service 2e9770
              mpfr_neg (mpc_imagref (a), mpc_imagref (a), MPFR_RNDN);
Packit Service 2e9770
            }
Packit Service 2e9770
          else
Packit Service 2e9770
            inex_w = mpfr_sqrt (mpc_imagref (a), w, MPC_RND_IM (rnd));
Packit Service 2e9770
Packit Service 2e9770
          mpfr_set_ui (mpc_realref (a), 0, MPFR_RNDN);
Packit Service 2e9770
          mpfr_clear (w);
Packit Service 2e9770
          return MPC_INEX (0, inex_w);
Packit Service 2e9770
        }
Packit Service 2e9770
    }
Packit Service 2e9770
Packit Service 2e9770
  /* purely imaginary */
Packit Service 2e9770
  if (re_cmp == 0)
Packit Service 2e9770
    {
Packit Service 2e9770
      mpfr_t y;
Packit Service 2e9770
Packit Service 2e9770
      y[0] = mpc_imagref (b)[0];
Packit Service 2e9770
      /* If y/2 underflows, so does sqrt(y/2) */
Packit Service 2e9770
      mpfr_div_2ui (y, y, 1, MPFR_RNDN);
Packit Service 2e9770
      if (im_cmp > 0)
Packit Service 2e9770
        {
Packit Service 2e9770
          inex_w = mpfr_sqrt (mpc_realref (a), y, MPC_RND_RE (rnd));
Packit Service 2e9770
          inex_t = mpfr_sqrt (mpc_imagref (a), y, MPC_RND_IM (rnd));
Packit Service 2e9770
        }
Packit Service 2e9770
      else
Packit Service 2e9770
        {
Packit Service 2e9770
          mpfr_neg (y, y, MPFR_RNDN);
Packit Service 2e9770
          inex_w = mpfr_sqrt (mpc_realref (a), y, MPC_RND_RE (rnd));
Packit Service 2e9770
          inex_t = -mpfr_sqrt (mpc_imagref (a), y, INV_RND (MPC_RND_IM (rnd)));
Packit Service 2e9770
          mpfr_neg (mpc_imagref (a), mpc_imagref (a), MPFR_RNDN);
Packit Service 2e9770
        }
Packit Service 2e9770
      return MPC_INEX (inex_w, inex_t);
Packit Service 2e9770
    }
Packit Service 2e9770
Packit Service 2e9770
  prec = MPC_MAX_PREC(a);
Packit Service 2e9770
Packit Service 2e9770
  mpfr_init (w);
Packit Service 2e9770
  mpfr_init (t);
Packit Service 2e9770
Packit Service 2e9770
   if (re_cmp > 0) {
Packit Service 2e9770
      rnd_w = MPC_RND_RE (rnd);
Packit Service 2e9770
      prec_w = MPC_PREC_RE (a);
Packit Service 2e9770
      rnd_t = MPC_RND_IM(rnd);
Packit Service 2e9770
      if (rnd_t == MPFR_RNDZ)
Packit Service 2e9770
         /* force MPFR_RNDD or MPFR_RNDUP, using sign(t) = sign(y) */
Packit Service 2e9770
         rnd_t = (im_cmp > 0 ? MPFR_RNDD : MPFR_RNDU);
Packit Service 2e9770
      prec_t = MPC_PREC_IM (a);
Packit Service 2e9770
   }
Packit Service 2e9770
   else {
Packit Service 2e9770
      prec_w = MPC_PREC_IM (a);
Packit Service 2e9770
      prec_t = MPC_PREC_RE (a);
Packit Service 2e9770
      if (im_cmp > 0) {
Packit Service 2e9770
         rnd_w = MPC_RND_IM(rnd);
Packit Service 2e9770
         rnd_t = MPC_RND_RE(rnd);
Packit Service 2e9770
         if (rnd_t == MPFR_RNDZ)
Packit Service 2e9770
            rnd_t = MPFR_RNDD;
Packit Service 2e9770
      }
Packit Service 2e9770
      else {
Packit Service 2e9770
         rnd_w = INV_RND(MPC_RND_IM (rnd));
Packit Service 2e9770
         rnd_t = INV_RND(MPC_RND_RE (rnd));
Packit Service 2e9770
         if (rnd_t == MPFR_RNDZ)
Packit Service 2e9770
            rnd_t = MPFR_RNDU;
Packit Service 2e9770
      }
Packit Service 2e9770
   }
Packit Service 2e9770
Packit Service 2e9770
  do
Packit Service 2e9770
    {
Packit Service 2e9770
      loops ++;
Packit Service 2e9770
      prec += (loops <= 2) ? mpc_ceil_log2 (prec) + 4 : prec / 2;
Packit Service 2e9770
      mpfr_set_prec (w, prec);
Packit Service 2e9770
      mpfr_set_prec (t, prec);
Packit Service 2e9770
      /* let b = x + iy */
Packit Service 2e9770
      /* w = sqrt ((|x| + sqrt (x^2 + y^2)) / 2), rounded down */
Packit Service 2e9770
      /* total error bounded by 3 ulps */
Packit Service 2e9770
      inex_w = mpc_abs (w, b, MPFR_RNDD);
Packit Service 2e9770
      if (re_cmp < 0)
Packit Service 2e9770
        inex_w |= mpfr_sub (w, w, mpc_realref (b), MPFR_RNDD);
Packit Service 2e9770
      else
Packit Service 2e9770
        inex_w |= mpfr_add (w, w, mpc_realref (b), MPFR_RNDD);
Packit Service 2e9770
      inex_w |= mpfr_div_2ui (w, w, 1, MPFR_RNDD);
Packit Service 2e9770
      inex_w |= mpfr_sqrt (w, w, MPFR_RNDD);
Packit Service 2e9770
Packit Service 2e9770
      repr_w = mpfr_min_prec (w) <= prec_w;
Packit Service 2e9770
      if (!repr_w)
Packit Service 2e9770
         /* use the usual trick for obtaining the ternary value */
Packit Service 2e9770
         ok_w = mpfr_can_round (w, prec - 2, MPFR_RNDD, MPFR_RNDU,
Packit Service 2e9770
                                prec_w + (rnd_w == MPFR_RNDN));
Packit Service 2e9770
      else {
Packit Service 2e9770
            /* w is representable in the target precision and thus cannot be
Packit Service 2e9770
               rounded up */
Packit Service 2e9770
         if (rnd_w == MPFR_RNDN)
Packit Service 2e9770
            /* If w can be rounded to nearest, then actually no rounding
Packit Service 2e9770
               occurs, and the ternary value is known from inex_w. */
Packit Service 2e9770
            ok_w = mpfr_can_round (w, prec - 2, MPFR_RNDD, MPFR_RNDN, prec_w);
Packit Service 2e9770
         else
Packit Service 2e9770
            /* If w can be rounded down, then any direct rounding and the
Packit Service 2e9770
               ternary flag can be determined from inex_w. */
Packit Service 2e9770
            ok_w = mpfr_can_round (w, prec - 2, MPFR_RNDD, MPFR_RNDD, prec_w);
Packit Service 2e9770
      }
Packit Service 2e9770
Packit Service 2e9770
      if (!inex_w || ok_w) {
Packit Service 2e9770
         /* t = y / 2w, rounded away */
Packit Service 2e9770
         /* total error bounded by 7 ulps */
Packit Service 2e9770
         inex_t = mpfr_div (t, mpc_imagref (b), w, r);
Packit Service 2e9770
         if (!inex_t && inex_w)
Packit Service 2e9770
            /* The division was exact, but w was not. */
Packit Service 2e9770
            inex_t = im_sgn ? -1 : 1;
Packit Service 2e9770
         inex_t |= mpfr_div_2ui (t, t, 1, r);
Packit Service 2e9770
         repr_t = mpfr_min_prec (t) <= prec_t;
Packit Service 2e9770
         if (!repr_t)
Packit Service 2e9770
             /* As for w; since t was rounded away, we check whether rounding to 0
Packit Service 2e9770
                is possible. */
Packit Service 2e9770
            ok_t = mpfr_can_round (t, prec - 3, r, MPFR_RNDZ,
Packit Service 2e9770
                                   prec_t + (rnd_t == MPFR_RNDN));
Packit Service 2e9770
         else {
Packit Service 2e9770
            if (rnd_t == MPFR_RNDN)
Packit Service 2e9770
               ok_t = mpfr_can_round (t, prec - 3, r, MPFR_RNDN, prec_t);
Packit Service 2e9770
            else
Packit Service 2e9770
               ok_t = mpfr_can_round (t, prec - 3, r, r, prec_t);
Packit Service 2e9770
         }
Packit Service 2e9770
      }
Packit Service 2e9770
    }
Packit Service 2e9770
    while ((inex_w && !ok_w) || (inex_t && !ok_t));
Packit Service 2e9770
Packit Service 2e9770
   if (re_cmp > 0) {
Packit Service 2e9770
         inex_re = mpfr_set (mpc_realref (a), w, MPC_RND_RE(rnd));
Packit Service 2e9770
         inex_im = mpfr_set (mpc_imagref (a), t, MPC_RND_IM(rnd));
Packit Service 2e9770
   }
Packit Service 2e9770
   else if (im_cmp > 0) {
Packit Service 2e9770
      inex_re = mpfr_set (mpc_realref(a), t, MPC_RND_RE(rnd));
Packit Service 2e9770
      inex_im = mpfr_set (mpc_imagref(a), w, MPC_RND_IM(rnd));
Packit Service 2e9770
   }
Packit Service 2e9770
   else {
Packit Service 2e9770
      inex_re = mpfr_neg (mpc_realref (a), t, MPC_RND_RE(rnd));
Packit Service 2e9770
      inex_im = mpfr_neg (mpc_imagref (a), w, MPC_RND_IM(rnd));
Packit Service 2e9770
   }
Packit Service 2e9770
Packit Service 2e9770
   if (repr_w && inex_w) {
Packit Service 2e9770
      if (rnd_w == MPFR_RNDN) {
Packit Service 2e9770
         /* w has not been rounded with mpfr_set/mpfr_neg, determine ternary
Packit Service 2e9770
            value from inex_w instead */
Packit Service 2e9770
         if (re_cmp > 0)
Packit Service 2e9770
            inex_re = inex_w;
Packit Service 2e9770
         else if (im_cmp > 0)
Packit Service 2e9770
            inex_im = inex_w;
Packit Service 2e9770
         else
Packit Service 2e9770
            inex_im = -inex_w;
Packit Service 2e9770
      }
Packit Service 2e9770
      else {
Packit Service 2e9770
         /* determine ternary value, but also potentially add 1 ulp; can only
Packit Service 2e9770
            be done now when we are in the target precision */
Packit Service 2e9770
         if (re_cmp > 0) {
Packit Service 2e9770
            if (rnd_w == MPFR_RNDU) {
Packit Service 2e9770
               MPFR_ADD_ONE_ULP (mpc_realref (a));
Packit Service 2e9770
               inex_re = +1;
Packit Service 2e9770
            }
Packit Service 2e9770
            else
Packit Service 2e9770
               inex_re = -1;
Packit Service 2e9770
         }
Packit Service 2e9770
         else if (im_cmp > 0) {
Packit Service 2e9770
            if (rnd_w == MPFR_RNDU) {
Packit Service 2e9770
               MPFR_ADD_ONE_ULP (mpc_imagref (a));
Packit Service 2e9770
               inex_im = +1;
Packit Service 2e9770
            }
Packit Service 2e9770
            else
Packit Service 2e9770
               inex_im = -1;
Packit Service 2e9770
         }
Packit Service 2e9770
         else {
Packit Service 2e9770
            if (rnd_w == MPFR_RNDU) {
Packit Service 2e9770
               MPFR_ADD_ONE_ULP (mpc_imagref (a));
Packit Service 2e9770
               inex_im = -1;
Packit Service 2e9770
            }
Packit Service 2e9770
            else
Packit Service 2e9770
               inex_im = +1;
Packit Service 2e9770
         }
Packit Service 2e9770
      }
Packit Service 2e9770
   }
Packit Service 2e9770
   if (repr_t && inex_t) {
Packit Service 2e9770
      if (rnd_t == MPFR_RNDN) {
Packit Service 2e9770
         if (re_cmp > 0)
Packit Service 2e9770
            inex_im = inex_t;
Packit Service 2e9770
         else if (im_cmp > 0)
Packit Service 2e9770
            inex_re = inex_t;
Packit Service 2e9770
         else
Packit Service 2e9770
            inex_re = -inex_t;
Packit Service 2e9770
      }
Packit Service 2e9770
      else {
Packit Service 2e9770
         if (re_cmp > 0) {
Packit Service 2e9770
            if (rnd_t == r)
Packit Service 2e9770
               inex_im = inex_t;
Packit Service 2e9770
            else {
Packit Service 2e9770
               inex_im = -inex_t;
Packit Service 2e9770
               /* im_cmp > 0 implies that Im(b) > 0, thus im_sgn = 0
Packit Service 2e9770
                  and r = MPFR_RNDU.
Packit Service 2e9770
                  im_cmp < 0 implies that Im(b) < 0, thus im_sgn = -1
Packit Service 2e9770
                  and r = MPFR_RNDD. */
Packit Service 2e9770
               MPFR_SUB_ONE_ULP (mpc_imagref (a));
Packit Service 2e9770
            }
Packit Service 2e9770
         }
Packit Service 2e9770
         else if (im_cmp > 0) {
Packit Service 2e9770
            if (rnd_t == r)
Packit Service 2e9770
               inex_re = inex_t;
Packit Service 2e9770
            else {
Packit Service 2e9770
               inex_re = -inex_t;
Packit Service 2e9770
               /* im_cmp > 0 implies r = MPFR_RNDU (see above) */
Packit Service 2e9770
               MPFR_SUB_ONE_ULP (mpc_realref (a));
Packit Service 2e9770
            }
Packit Service 2e9770
         }
Packit Service 2e9770
         else { /* im_cmp < 0 */
Packit Service 2e9770
            if (rnd_t == r)
Packit Service 2e9770
               inex_re = -inex_t;
Packit Service 2e9770
            else {
Packit Service 2e9770
               inex_re = inex_t;
Packit Service 2e9770
               /* im_cmp < 0 implies r = MPFR_RNDD (see above) */
Packit Service 2e9770
               MPFR_SUB_ONE_ULP (mpc_realref (a));
Packit Service 2e9770
            }
Packit Service 2e9770
         }
Packit Service 2e9770
      }
Packit Service 2e9770
   }
Packit Service 2e9770
Packit Service 2e9770
  mpfr_clear (w);
Packit Service 2e9770
  mpfr_clear (t);
Packit Service 2e9770
Packit Service 2e9770
  return MPC_INEX (inex_re, inex_im);
Packit Service 2e9770
}