Blob Blame History Raw
/* mpc_mul -- Multiply two complex numbers

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

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 mul_infinite (mpc_ptr a, mpc_srcptr u, mpc_srcptr v);
static int mul_pure_imaginary (mpc_ptr a, mpc_srcptr u, mpfr_srcptr y,
                               mpc_rnd_t rnd, int overlap);

/* return inex such that MPC_INEX_RE(inex) = -1, 0, 1
                         MPC_INEX_IM(inex) = -1, 0, 1
   depending on the signs of the real/imaginary parts of the result
*/
int
mpc_mul (mpc_ptr a, mpc_srcptr b, mpc_srcptr c, mpc_rnd_t rnd)
{
  int brs, bis, crs, cis;

  /* Conforming to ISO C99 standard (G.5.1 multiplicative operators),
     infinities have special treatment if both parts are NaN when computed
     naively. */
  if (mpc_inf_p (b))
    return mul_infinite (a, b, c);
  if (mpc_inf_p (c))
    return mul_infinite (a, c, b);

  /* NaN contamination of both part in result */
  if (mpfr_nan_p (MPC_RE (b)) || mpfr_nan_p (MPC_IM (b))
      || mpfr_nan_p (MPC_RE (c)) || mpfr_nan_p (MPC_IM (c)))
    {
      mpfr_set_nan (MPC_RE (a));
      mpfr_set_nan (MPC_IM (a));
      return MPC_INEX (0, 0);
    }

  /* save operands' sign */
  brs = MPFR_SIGNBIT (MPC_RE (b));
  bis = MPFR_SIGNBIT (MPC_IM (b));
  crs = MPFR_SIGNBIT (MPC_RE (c));
  cis = MPFR_SIGNBIT (MPC_IM (c));

  /* first check for real multiplication */
  if (mpfr_zero_p (MPC_IM (b))) /* b * (x+i*y) = b*x + i *(b*y) */
    {
      int inex;
      inex = mpc_mul_fr (a, c, MPC_RE (b), rnd);

      /* Sign of zeros is wrong in some cases. This correction doesn't change
         inexact flag. */
      if (mpfr_zero_p (MPC_RE (a)))
        mpfr_setsign (MPC_RE (a), MPC_RE (a), MPC_RND_RE(rnd) == GMP_RNDD
                      || (brs != crs && bis == cis), GMP_RNDN); /* exact */
      if (mpfr_zero_p (MPC_IM (a)))
        mpfr_setsign (MPC_IM (a), MPC_IM (a), MPC_RND_IM (rnd) == GMP_RNDD
                      || (brs != cis && bis != crs), GMP_RNDN); /* exact */

      return inex;
    }
  if (mpfr_zero_p (MPC_IM (c)))
    {
      int inex;
      inex = mpc_mul_fr (a, b, MPC_RE (c), rnd);

      /* Sign of zeros is wrong in some cases. This correction doesn't change
         inexact flag. */
      if (mpfr_zero_p (MPC_RE (a)))
        mpfr_setsign (MPC_RE (a), MPC_RE (a), MPC_RND_RE(rnd) == GMP_RNDD
                      || (brs != crs && bis == cis), GMP_RNDN);
      if (mpfr_zero_p (MPC_IM (a)))
        mpfr_setsign (MPC_IM (a), MPC_IM (a), MPC_RND_IM (rnd) == GMP_RNDD
                      || (brs != cis && bis != crs), GMP_RNDN);

      return inex;
    }

  /* check for purely complex multiplication */
  if (mpfr_zero_p (MPC_RE (b))) /* i*b * (x+i*y) = -b*y + i*(b*x) */
    {
      int inex;
      inex = mul_pure_imaginary (a, c, MPC_IM (b), rnd, (a == b || a == c));

      /* Sign of zeros is wrong in some cases (note that Re(a) cannot be a
         zero) */
      if (mpfr_zero_p (MPC_IM (a)))
        mpfr_setsign (MPC_IM (a), MPC_IM (a), MPC_RND_IM (rnd) == GMP_RNDD
                      || (brs != cis && bis != crs), GMP_RNDN);

      return inex;
    }
  if (mpfr_zero_p (MPC_RE (c)))
    /* note that a cannot be a zero */
    return mul_pure_imaginary (a, b, MPC_IM (c), rnd, (a == b || a == c));

  /* Check if b==c and call mpc_sqr in this case, to make sure            */
  /* mpc_mul(a,b,b) behaves exactly like mpc_sqr(a,b) concerning          */
  /* internal overflows etc.                                              */
  if (mpc_cmp (b, c) == 0)
     return mpc_sqr (a, b, rnd);

  /* If the real and imaginary part of one argument have a very different */
  /* exponent, it is not reasonable to use Karatsuba multiplication.      */
  if (   SAFE_ABS (mpfr_exp_t,
                   mpfr_get_exp (MPC_RE (b)) - mpfr_get_exp (MPC_IM (b)))
         > (mpfr_exp_t) MPC_MAX_PREC (b) / 2
      || SAFE_ABS (mpfr_exp_t,
                   mpfr_get_exp (MPC_RE (c)) - mpfr_get_exp (MPC_IM (c)))
         > (mpfr_exp_t) MPC_MAX_PREC (c) / 2)
    return mpc_mul_naive (a, b, c, rnd);
  else
    return ((MPC_MAX_PREC(a)
             <= (mpfr_prec_t) MUL_KARATSUBA_THRESHOLD * BITS_PER_MP_LIMB)
            ? mpc_mul_naive : mpc_mul_karatsuba) (a, b, c, rnd);
}

/* compute a=u*v when u has an infinite part */
static int
mul_infinite (mpc_ptr a, mpc_srcptr u, mpc_srcptr v)
{
  /* let u = ur+i*ui and v =vr+i*vi */
  int x, y;

  /* save operands' sign */
  int urs = mpfr_signbit (MPC_RE (u)) ? -1 : 1;
  int uis = mpfr_signbit (MPC_IM (u)) ? -1 : 1;
  int vrs = mpfr_signbit (MPC_RE (v)) ? -1 : 1;
  int vis = mpfr_signbit (MPC_IM (v)) ? -1 : 1;

  /* compute the sign of
     x = urs * ur * vrs * vr - uis * ui * vis * vi
     y = urs * ur * vis * vi + uis * ui * vrs * vr
     +1 if positive, -1 if negative, 0 if zero or NaN */
  if (mpfr_nan_p (MPC_RE (u)) || mpfr_nan_p (MPC_IM (u))
      || mpfr_nan_p (MPC_RE (v)) || mpfr_nan_p (MPC_IM (v)))
    {
      x = 0;
      y = 0;
    }
  else if (mpfr_inf_p (MPC_RE (u)))
    {
      /* u = (+/-inf) +i*v */
      x = mpfr_zero_p (MPC_RE (v))
        || (mpfr_inf_p (MPC_IM (u)) && mpfr_zero_p (MPC_IM (v)))
        || (mpfr_zero_p (MPC_IM (u)) && mpfr_inf_p (MPC_IM (v)))
        || ((mpfr_inf_p (MPC_IM (u)) || mpfr_inf_p (MPC_IM (v)))
            && urs*vrs == uis*vis) ?
        0 : urs * vrs;
      y = mpfr_zero_p (MPC_IM (v))
        || (mpfr_inf_p (MPC_IM (u)) && mpfr_zero_p (MPC_RE (v)))
        || (mpfr_zero_p (MPC_IM (u)) && mpfr_inf_p (MPC_RE (v)))
        || ((mpfr_inf_p (MPC_IM (u)) || mpfr_inf_p (MPC_IM (u)))
            && urs*vis == -uis*vrs) ?
        0 : urs * vis;
    }
  else
    {
      /* u = u +i*(+/-inf) where |u| < inf */
      x = mpfr_zero_p (MPC_IM (v))
        || (mpfr_zero_p (MPC_RE (u)) && mpfr_inf_p (MPC_RE (v)))
        || (mpfr_inf_p (MPC_RE (v)) && urs*vrs == uis*vis) ?
        0 : -uis * vis;
      y = mpfr_zero_p (MPC_RE (v))
        || (mpfr_zero_p (MPC_RE (u)) && mpfr_inf_p (MPC_IM (v)))
        || (mpfr_inf_p (MPC_IM (v)) && urs*vis == -uis*vrs) ?
        0 : uis * vrs;
    }

  /* Naive result is NaN+i*NaN. We will try to obtain an infinite using
     algorithm given in Annex G of ISO C99 standard */
  if (x == 0 && y ==0)
    {
      int ur = mpfr_zero_p (MPC_RE (u)) || mpfr_nan_p (MPC_RE (u)) ?
        0 : 1;
      int ui = mpfr_zero_p (MPC_IM (u)) || mpfr_nan_p (MPC_IM (u)) ?
        0 : 1;
      int vr = mpfr_zero_p (MPC_RE (v)) || mpfr_nan_p (MPC_RE (v)) ?
        0 : 1;
      int vi = mpfr_zero_p (MPC_IM (v)) || mpfr_nan_p (MPC_IM (v)) ?
        0 : 1;
      if (mpc_inf_p (u))
        {
          ur = mpfr_inf_p (MPC_RE (u)) ? 1 : 0;
          ui = mpfr_inf_p (MPC_IM (u)) ? 1 : 0;
        }
      if (mpc_inf_p (v))
        {
          vr = mpfr_inf_p (MPC_RE (v)) ? 1 : 0;
          vi = mpfr_inf_p (MPC_IM (v)) ? 1 : 0;
        }

      x = urs * ur * vrs * vr - uis * ui * vis * vi;
      y = urs * ur * vis * vi + uis * ui * vrs * vr;
    }

  if (x == 0)
    mpfr_set_nan (MPC_RE (a));
  else
    mpfr_set_inf (MPC_RE (a), x);

  if (y == 0)
    mpfr_set_nan (MPC_IM (a));
  else
    mpfr_set_inf (MPC_IM (a), y);

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

static int
mul_pure_imaginary (mpc_ptr a, mpc_srcptr u, mpfr_srcptr y, mpc_rnd_t rnd,
                    int overlap)
{
  int inex_re, inex_im;
  mpc_t result;

  if (overlap)
    mpc_init3 (result, MPC_PREC_RE (a), MPC_PREC_IM (a));
  else
    result [0] = a [0];

  inex_re = -mpfr_mul (MPC_RE(result), MPC_IM(u), y, INV_RND(MPC_RND_RE(rnd)));
  mpfr_neg (MPC_RE (result), MPC_RE (result), GMP_RNDN);
  inex_im = mpfr_mul (MPC_IM(result), MPC_RE(u), y, MPC_RND_IM(rnd));
  mpc_set (a, result, MPC_RNDNN);

  if (overlap)
    mpc_clear (result);

  return MPC_INEX(inex_re, inex_im);
}

int
mpc_mul_naive (mpc_ptr a, mpc_srcptr b, mpc_srcptr c, mpc_rnd_t rnd)
{
   /* We assume that b and c are different, which is checked in mpc_mul. */
  int overlap, inex_re, inex_im;
  mpfr_t u, v, t;
  mpfr_prec_t prec;

  overlap = (a == b) || (a == c);

  prec = MPC_MAX_PREC(b) + MPC_MAX_PREC(c);

  mpfr_init2 (u, prec);
  mpfr_init2 (v, prec);

  /* Re(a) = Re(b)*Re(c) - Im(b)*Im(c) */
  /* FIXME: this code suffers undue overflows: u or v can overflow while u-v
     or u+v is representable */
  mpfr_mul (u, MPC_RE(b), MPC_RE(c), GMP_RNDN); /* exact */
  mpfr_mul (v, MPC_IM(b), MPC_IM(c), GMP_RNDN); /* exact */

  if (overlap)
    {
      mpfr_init2 (t, MPC_PREC_RE(a));
      inex_re = mpfr_sub (t, u, v, MPC_RND_RE(rnd));
    }
  else
    inex_re = mpfr_sub (MPC_RE(a), u, v, MPC_RND_RE(rnd));

  /* Im(a) = Re(b)*Im(c) + Im(b)*Re(c) */
  mpfr_mul (u, MPC_RE(b), MPC_IM(c), GMP_RNDN); /* exact */
  mpfr_mul (v, MPC_IM(b), MPC_RE(c), GMP_RNDN); /* exact */
  inex_im = mpfr_add (MPC_IM(a), u, v, MPC_RND_IM(rnd));

  mpfr_clear (u);
  mpfr_clear (v);

  if (overlap)
    {
      mpfr_set (MPC_RE(a), t, GMP_RNDN); /* exact */
      mpfr_clear (t);
    }

  return MPC_INEX(inex_re, inex_im);
}


/* Karatsuba multiplication, with 3 real multiplies */
int
mpc_mul_karatsuba (mpc_ptr rop, mpc_srcptr op1, mpc_srcptr op2, mpc_rnd_t rnd)
{
  mpfr_srcptr a, b, c, d;
  int mul_i, ok, inexact, mul_a, mul_c, inex_re, inex_im, sign_x, sign_u;
  mpfr_t u, v, w, x;
  mpfr_prec_t prec, prec_re, prec_u, prec_v, prec_w;
  mpfr_rnd_t rnd_re, rnd_u;
  int overlap;
     /* true if rop == op1 or rop == op2 */
  mpc_t result;
     /* overlap is quite difficult to handle, because we have to tentatively
        round the variable u in the end to either the real or the imaginary
        part of rop (it is not possible to tell now whether the real or
        imaginary part is used). If this fails, we have to start again and
        need the correct values of op1 and op2.
        So we just create a new variable for the result in this case. */
  int loop;
  const int MAX_MUL_LOOP = 1;

  overlap = (rop == op1) || (rop == op2);
  if (overlap)
     mpc_init3 (result, MPC_PREC_RE (rop), MPC_PREC_IM (rop));
  else
     result [0] = rop [0];

  a = MPC_RE(op1);
  b = MPC_IM(op1);
  c = MPC_RE(op2);
  d = MPC_IM(op2);

  /* (a + i*b) * (c + i*d) = [ac - bd] + i*[ad + bc] */

  mul_i = 0; /* number of multiplications by i */
  mul_a = 1; /* implicit factor for a */
  mul_c = 1; /* implicit factor for c */

  if (mpfr_cmp_abs (a, b) < 0)
    {
      MPFR_SWAP (a, b);
      mul_i ++;
      mul_a = -1; /* consider i * (a+i*b) = -b + i*a */
    }

  if (mpfr_cmp_abs (c, d) < 0)
    {
      MPFR_SWAP (c, d);
      mul_i ++;
      mul_c = -1; /* consider -d + i*c instead of c + i*d */
    }

  /* find the precision and rounding mode for the new real part */
  if (mul_i % 2)
    {
      prec_re = MPC_PREC_IM(rop);
      rnd_re = MPC_RND_IM(rnd);
    }
  else /* mul_i = 0 or 2 */
    {
      prec_re = MPC_PREC_RE(rop);
      rnd_re = MPC_RND_RE(rnd);
    }

  if (mul_i)
    rnd_re = INV_RND(rnd_re);

  /* now |a| >= |b| and |c| >= |d| */
  prec = MPC_MAX_PREC(rop);

  mpfr_init2 (u, 2);
  mpfr_init2 (v, prec_v = mpfr_get_prec (a) + mpfr_get_prec (d));
  mpfr_init2 (w, prec_w = mpfr_get_prec (b) + mpfr_get_prec (c));
  mpfr_init2 (x, 2);

  mpfr_mul (v, a, d, GMP_RNDN); /* exact */
  if (mul_a == -1)
    mpfr_neg (v, v, GMP_RNDN);

  mpfr_mul (w, b, c, GMP_RNDN); /* exact */
  if (mul_c == -1)
    mpfr_neg (w, w, GMP_RNDN);

  /* compute sign(v-w) */
  sign_x = mpfr_cmp_abs (v, w);
  if (sign_x > 0)
    sign_x = 2 * mpfr_sgn (v) - mpfr_sgn (w);
  else if (sign_x == 0)
    sign_x = mpfr_sgn (v) - mpfr_sgn (w);
  else
    sign_x = mpfr_sgn (v) - 2 * mpfr_sgn (w);

   sign_u = mul_a * mpfr_sgn (a) * mul_c * mpfr_sgn (c);

   if (sign_x * sign_u < 0)
    {  /* swap inputs */
      MPFR_SWAP (a, c);
      MPFR_SWAP (b, d);
      mpfr_swap (v, w);
      { int tmp; tmp = mul_a; mul_a = mul_c; mul_c = tmp; }
      sign_x = - sign_x;
    }

   /* now sign_x * sign_u >= 0 */
   loop = 0;
   do
     {
        loop++;
         /* the following should give failures with prob. <= 1/prec */
         prec += mpc_ceil_log2 (prec) + 3;

         mpfr_set_prec (u, prec_u = prec);
         mpfr_set_prec (x, prec);

         /* first compute away(b +/- a) and store it in u */
         inexact = (mul_a == -1 ?
                    ROUND_AWAY (mpfr_sub (u, b, a, MPFR_RNDA), u) :
                    ROUND_AWAY (mpfr_add (u, b, a, MPFR_RNDA), u));

         /* then compute away(+/-c - d) and store it in x */
         inexact |= (mul_c == -1 ?
                     ROUND_AWAY (mpfr_add (x, c, d, MPFR_RNDA), x) :
                     ROUND_AWAY (mpfr_sub (x, c, d, MPFR_RNDA), x));
         if (mul_c == -1)
           mpfr_neg (x, x, GMP_RNDN);

         if (inexact == 0)
            mpfr_prec_round (u, prec_u = 2 * prec, GMP_RNDN);

         /* compute away(u*x) and store it in u */
         inexact |= ROUND_AWAY (mpfr_mul (u, u, x, MPFR_RNDA), u);
            /* (a+b)*(c-d) */

	 /* if all computations are exact up to here, it may be that
	    the real part is exact, thus we need if possible to
	    compute v - w exactly */
	 if (inexact == 0)
	   {
	     mpfr_prec_t prec_x;
             if (mpfr_zero_p(v))
               prec_x = prec_w;
             else if (mpfr_zero_p(w))
               prec_x = prec_v;
             else
                 prec_x = SAFE_ABS (mpfr_exp_t, mpfr_get_exp (v) - mpfr_get_exp (w))
                          + MPC_MAX (prec_v, prec_w) + 1;
                 /* +1 is necessary for a potential carry */
	     /* ensure we do not use a too large precision */
	     if (prec_x > prec_u)
               prec_x = prec_u;
	     if (prec_x > prec)
	       mpfr_prec_round (x, prec_x, GMP_RNDN);
	   }

         rnd_u = (sign_u > 0) ? GMP_RNDU : GMP_RNDD;
         inexact |= mpfr_sub (x, v, w, rnd_u); /* ad - bc */

         /* in case u=0, ensure that rnd_u rounds x away from zero */
         if (mpfr_sgn (u) == 0)
           rnd_u = (mpfr_sgn (x) > 0) ? GMP_RNDU : GMP_RNDD;
         inexact |= mpfr_add (u, u, x, rnd_u); /* ac - bd */

         ok = inexact == 0 ||
           mpfr_can_round (u, prec_u - 3, rnd_u, GMP_RNDZ,
                           prec_re + (rnd_re == GMP_RNDN));
         /* this ensures both we can round correctly and determine the correct
            inexact flag (for rounding to nearest) */
     }
   while (!ok && loop <= MAX_MUL_LOOP);
      /* after MAX_MUL_LOOP rounds, use mpc_naive instead */

   if (ok) {
      /* if inexact is zero, then u is exactly ac-bd, otherwise fix the sign
         of the inexact flag for u, which was rounded away from ac-bd */
      if (inexact != 0)
      inexact = mpfr_sgn (u);

      if (mul_i == 0)
      {
         inex_re = mpfr_set (MPC_RE(result), u, MPC_RND_RE(rnd));
         if (inex_re == 0)
            {
            inex_re = inexact; /* u is rounded away from 0 */
            inex_im = mpfr_add (MPC_IM(result), v, w, MPC_RND_IM(rnd));
            }
         else
            inex_im = mpfr_add (MPC_IM(result), v, w, MPC_RND_IM(rnd));
      }
      else if (mul_i == 1) /* (x+i*y)/i = y - i*x */
      {
         inex_im = mpfr_neg (MPC_IM(result), u, MPC_RND_IM(rnd));
         if (inex_im == 0)
            {
            inex_im = -inexact; /* u is rounded away from 0 */
            inex_re = mpfr_add (MPC_RE(result), v, w, MPC_RND_RE(rnd));
            }
         else
            inex_re = mpfr_add (MPC_RE(result), v, w, MPC_RND_RE(rnd));
      }
      else /* mul_i = 2, z/i^2 = -z */
      {
         inex_re = mpfr_neg (MPC_RE(result), u, MPC_RND_RE(rnd));
         if (inex_re == 0)
            {
            inex_re = -inexact; /* u is rounded away from 0 */
            inex_im = -mpfr_add (MPC_IM(result), v, w,
                                 INV_RND(MPC_RND_IM(rnd)));
            mpfr_neg (MPC_IM(result), MPC_IM(result), MPC_RND_IM(rnd));
            }
         else
            {
            inex_im = -mpfr_add (MPC_IM(result), v, w,
                                 INV_RND(MPC_RND_IM(rnd)));
            mpfr_neg (MPC_IM(result), MPC_IM(result), MPC_RND_IM(rnd));
            }
      }

      mpc_set (rop, result, MPC_RNDNN);
   }

   mpfr_clear (u);
   mpfr_clear (v);
   mpfr_clear (w);
   mpfr_clear (x);
   if (overlap)
      mpc_clear (result);

   if (ok)
      return MPC_INEX(inex_re, inex_im);
   else
      return mpc_mul_naive (rop, op1, op2, rnd);
}