Blame src/pow_ui.c

Packit Service 2e9770
/* mpc_pow_ui -- Raise a complex number to an integer power.
Packit Service 2e9770
Packit Service 2e9770
Copyright (C) 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 <limits.h> /* for CHAR_BIT */
Packit Service 2e9770
#include "mpc-impl.h"
Packit Service 2e9770
Packit Service 2e9770
static int
Packit Service 2e9770
mpc_pow_usi_naive (mpc_ptr z, mpc_srcptr x, unsigned long y, int sign,
Packit Service 2e9770
   mpc_rnd_t rnd)
Packit Service 2e9770
{
Packit Service 2e9770
   int inex;
Packit Service 2e9770
   mpc_t t;
Packit Service 2e9770
Packit Service 2e9770
   mpc_init3 (t, sizeof (unsigned long) * CHAR_BIT, MPFR_PREC_MIN);
Packit Service 2e9770
   if (sign > 0)
Packit Service 2e9770
      mpc_set_ui (t, y, MPC_RNDNN); /* exact */
Packit Service 2e9770
   else
Packit Service 2e9770
      mpc_set_si (t, - (signed long) y, MPC_RNDNN);
Packit Service 2e9770
   inex = mpc_pow (z, x, t, rnd);
Packit Service 2e9770
   mpc_clear (t);
Packit Service 2e9770
Packit Service 2e9770
   return inex;
Packit Service 2e9770
}
Packit Service 2e9770
Packit Service 2e9770
Packit Service 2e9770
int
Packit Service 2e9770
mpc_pow_usi (mpc_ptr z, mpc_srcptr x, unsigned long y, int sign,
Packit Service 2e9770
   mpc_rnd_t rnd)
Packit Service 2e9770
   /* computes z = x^(sign*y) */
Packit Service 2e9770
{
Packit Service 2e9770
   int inex;
Packit Service 2e9770
   mpc_t t, x3;
Packit Service 2e9770
   mpfr_prec_t p, l, l0;
Packit Service 2e9770
   long unsigned int u;
Packit Service 2e9770
   int has3; /* non-zero if y has '11' in its binary representation */
Packit Service 2e9770
   int loop, done;
Packit Service 2e9770
Packit Service 2e9770
   /* let mpc_pow deal with special values */
Packit Service 2e9770
   if (!mpc_fin_p (x) || mpfr_zero_p (mpc_realref (x)) || mpfr_zero_p (mpc_imagref(x))
Packit Service 2e9770
       || y == 0)
Packit Service 2e9770
      return mpc_pow_usi_naive (z, x, y, sign, rnd);
Packit Service 2e9770
   /* easy special cases */
Packit Service 2e9770
   else if (y == 1) {
Packit Service 2e9770
      if (sign > 0)
Packit Service 2e9770
         return mpc_set (z, x, rnd);
Packit Service 2e9770
      else
Packit Service 2e9770
         return mpc_ui_div (z, 1ul, x, rnd);
Packit Service 2e9770
   }
Packit Service 2e9770
   else if (y == 2 && sign > 0)
Packit Service 2e9770
      return mpc_sqr (z, x, rnd);
Packit Service 2e9770
   /* let mpc_pow treat potential over- and underflows */
Packit Service 2e9770
   else {
Packit Service 2e9770
      mpfr_exp_t exp_r = mpfr_get_exp (mpc_realref (x)),
Packit Service 2e9770
                 exp_i = mpfr_get_exp (mpc_imagref (x));
Packit Service 2e9770
      if (   MPC_MAX (exp_r, exp_i) > mpfr_get_emax () / (mpfr_exp_t) y
Packit Service 2e9770
             /* heuristic for overflow */
Packit Service 2e9770
          || MPC_MAX (-exp_r, -exp_i) > (-mpfr_get_emin ()) / (mpfr_exp_t) y
Packit Service 2e9770
             /* heuristic for underflow */
Packit Service 2e9770
         )
Packit Service 2e9770
         return mpc_pow_usi_naive (z, x, y, sign, rnd);
Packit Service 2e9770
   }
Packit Service 2e9770
Packit Service 2e9770
   has3 = (y & (y >> 1)) != 0;
Packit Service 2e9770
   for (l = 0, u = y; u > 3; l ++, u >>= 1);
Packit Service 2e9770
   /* l>0 is the number of bits of y, minus 2, thus y has bits:
Packit Service 2e9770
      y_{l+1} y_l y_{l-1} ... y_1 y_0 */
Packit Service 2e9770
   l0 = l + 2;
Packit Service 2e9770
   p = MPC_MAX_PREC(z) + l0 + 32; /* l0 ensures that y*2^{-p} <= 1 below */
Packit Service 2e9770
   mpc_init2 (t, p);
Packit Service 2e9770
   if (has3)
Packit Service 2e9770
      mpc_init2 (x3, p);
Packit Service 2e9770
Packit Service 2e9770
   loop = 0;
Packit Service 2e9770
   done = 0;
Packit Service 2e9770
   while (!done) {
Packit Service 2e9770
      loop++;
Packit Service 2e9770
Packit Service 2e9770
      mpc_sqr (t, x, MPC_RNDNN);
Packit Service 2e9770
      if (has3) {
Packit Service 2e9770
         mpc_mul (x3, t, x, MPC_RNDNN);
Packit Service 2e9770
         if ((y >> l) & 1) /* y starts with 11... */
Packit Service 2e9770
            mpc_set (t, x3, MPC_RNDNN);
Packit Service 2e9770
      }
Packit Service 2e9770
      while (l-- > 0) {
Packit Service 2e9770
         mpc_sqr (t, t, MPC_RNDNN);
Packit Service 2e9770
         if ((y >> l) & 1) {
Packit Service 2e9770
            if ((l > 0) && ((y >> (l-1)) & 1)) /* implies has3 <> 0 */ {
Packit Service 2e9770
               l--;
Packit Service 2e9770
               mpc_sqr (t, t, MPC_RNDNN);
Packit Service 2e9770
               mpc_mul (t, t, x3, MPC_RNDNN);
Packit Service 2e9770
            }
Packit Service 2e9770
            else
Packit Service 2e9770
               mpc_mul (t, t, x, MPC_RNDNN);
Packit Service 2e9770
         }
Packit Service 2e9770
      }
Packit Service 2e9770
      if (sign < 0)
Packit Service 2e9770
         mpc_ui_div (t, 1ul, t, MPC_RNDNN);
Packit Service 2e9770
Packit Service 2e9770
      if (mpfr_zero_p (mpc_realref(t)) || mpfr_zero_p (mpc_imagref(t))) {
Packit Service 2e9770
         inex = mpc_pow_usi_naive (z, x, y, sign, rnd);
Packit Service 2e9770
            /* since mpfr_get_exp() is not defined for zero */
Packit Service 2e9770
         done = 1;
Packit Service 2e9770
      }
Packit Service 2e9770
      else {
Packit Service 2e9770
         /* see error bound in algorithms.tex; we use y<2^l0 instead of y-1
Packit Service 2e9770
            also when sign>0                                                */
Packit Service 2e9770
         mpfr_exp_t diff;
Packit Service 2e9770
         mpfr_prec_t er, ei;
Packit Service 2e9770
Packit Service 2e9770
         diff = mpfr_get_exp (mpc_realref(t)) - mpfr_get_exp (mpc_imagref(t));
Packit Service 2e9770
         /* the factor on the real part is 2+2^(-diff+2) <= 4 for diff >= 1
Packit Service 2e9770
            and < 2^(-diff+3) for diff <= 0 */
Packit Service 2e9770
         er = (diff >= 1) ? l0 + 3 : l0 + (-diff) + 3;
Packit Service 2e9770
         /* the factor on the imaginary part is 2+2^(diff+2) <= 4 for diff <= -1
Packit Service 2e9770
            and < 2^(diff+3) for diff >= 0 */
Packit Service 2e9770
         ei = (diff <= -1) ? l0 + 3 : l0 + diff + 3;
Packit Service 2e9770
         if (mpfr_can_round (mpc_realref(t), p - er, MPFR_RNDN, MPFR_RNDZ,
Packit Service 2e9770
                              MPC_PREC_RE(z) + (MPC_RND_RE(rnd) == MPFR_RNDN))
Packit Service 2e9770
               && mpfr_can_round (mpc_imagref(t), p - ei, MPFR_RNDN, MPFR_RNDZ,
Packit Service 2e9770
                              MPC_PREC_IM(z) + (MPC_RND_IM(rnd) == MPFR_RNDN))) {
Packit Service 2e9770
            inex = mpc_set (z, t, rnd);
Packit Service 2e9770
            done = 1;
Packit Service 2e9770
         }
Packit Service 2e9770
         else if (loop == 1 && SAFE_ABS(mpfr_prec_t, diff) < MPC_MAX_PREC(z)) {
Packit Service 2e9770
            /* common case, make a second trial at higher precision */
Packit Service 2e9770
            p += MPC_MAX_PREC(x);
Packit Service 2e9770
            mpc_set_prec (t, p);
Packit Service 2e9770
            if (has3)
Packit Service 2e9770
               mpc_set_prec (x3, p);
Packit Service 2e9770
            l = l0 - 2;
Packit Service 2e9770
         }
Packit Service 2e9770
         else {
Packit Service 2e9770
            /* stop the loop and use mpc_pow */
Packit Service 2e9770
            inex = mpc_pow_usi_naive (z, x, y, sign, rnd);
Packit Service 2e9770
            done = 1;
Packit Service 2e9770
         }
Packit Service 2e9770
      }
Packit Service 2e9770
   }
Packit Service 2e9770
Packit Service 2e9770
   mpc_clear (t);
Packit Service 2e9770
   if (has3)
Packit Service 2e9770
      mpc_clear (x3);
Packit Service 2e9770
Packit Service 2e9770
   return inex;
Packit Service 2e9770
}
Packit Service 2e9770
Packit Service 2e9770
Packit Service 2e9770
int
Packit Service 2e9770
mpc_pow_ui (mpc_ptr z, mpc_srcptr x, unsigned long y, mpc_rnd_t rnd)
Packit Service 2e9770
{
Packit Service 2e9770
  return mpc_pow_usi (z, x, y, 1, rnd);
Packit Service 2e9770
}