Blame src/rootofunity.c

Packit Service 2e9770
/* mpc_rootofunity -- primitive root of unity.
Packit Service 2e9770
Packit Service 2e9770
Copyright (C) 2012, 2016 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 <stdio.h> /* for MPC_ASSERT */
Packit Service 2e9770
#include "mpc-impl.h"
Packit Service 2e9770
Packit Service 2e9770
static unsigned long
Packit Service 2e9770
gcd (unsigned long a, unsigned long b)
Packit Service 2e9770
{
Packit Service 2e9770
   if (b == 0)
Packit Service 2e9770
      return a;
Packit Service 2e9770
   else return gcd (b, a % b);
Packit Service 2e9770
}
Packit Service 2e9770
Packit Service 2e9770
/* put in rop the value of exp(2*i*pi*k/n) rounded according to rnd */
Packit Service 2e9770
int
Packit Service 2e9770
mpc_rootofunity (mpc_ptr rop, unsigned long n, unsigned long k, mpc_rnd_t rnd)
Packit Service 2e9770
{
Packit Service 2e9770
   unsigned long g;
Packit Service 2e9770
   mpq_t kn;
Packit Service 2e9770
   mpfr_t t, s, c;
Packit Service 2e9770
   mpfr_prec_t prec;
Packit Service 2e9770
   int inex_re, inex_im;
Packit Service 2e9770
   mpfr_rnd_t rnd_re, rnd_im;
Packit Service 2e9770
Packit Service 2e9770
   if (n == 0) {
Packit Service 2e9770
      /* Compute exp (0 + i*inf). */
Packit Service 2e9770
      mpfr_set_nan (mpc_realref (rop));
Packit Service 2e9770
      mpfr_set_nan (mpc_imagref (rop));
Packit Service 2e9770
      return MPC_INEX (0, 0);
Packit Service 2e9770
   }
Packit Service 2e9770
Packit Service 2e9770
   /* Eliminate common denominator. */
Packit Service 2e9770
   k %= n;
Packit Service 2e9770
   g = gcd (k, n);
Packit Service 2e9770
   k /= g;
Packit Service 2e9770
   n /= g;
Packit Service 2e9770
Packit Service 2e9770
   /* Now 0 <= k < n and gcd(k,n)=1. */
Packit Service 2e9770
Packit Service 2e9770
   /* We assume that only n=1, 2, 3, 4, 6 and 12 may yield exact results
Packit Service 2e9770
      and treat them separately; n=8 is also treated here for efficiency
Packit Service 2e9770
      reasons. */
Packit Service 2e9770
   if (n == 1)
Packit Service 2e9770
     {
Packit Service 2e9770
       /* necessarily k=0 thus we want exp(0)=1 */
Packit Service 2e9770
       MPC_ASSERT (k == 0);
Packit Service 2e9770
       return mpc_set_ui_ui (rop, 1, 0, rnd);
Packit Service 2e9770
     }
Packit Service 2e9770
   else if (n == 2)
Packit Service 2e9770
     {
Packit Service 2e9770
       /* since gcd(k,n)=1, necessarily k=1, thus we want exp(i*pi)=-1 */
Packit Service 2e9770
       MPC_ASSERT (k == 1);
Packit Service 2e9770
       return mpc_set_si_si (rop, -1, 0, rnd);
Packit Service 2e9770
     }
Packit Service 2e9770
   else if (n == 4)
Packit Service 2e9770
     {
Packit Service 2e9770
       /* since gcd(k,n)=1, necessarily k=1 or k=3, thus we want
Packit Service 2e9770
          exp(2*i*pi/4)=i or exp(2*i*pi*3/4)=-i */
Packit Service 2e9770
       MPC_ASSERT (k == 1 || k == 3);
Packit Service 2e9770
       if (k == 1)
Packit Service 2e9770
         return mpc_set_ui_ui (rop, 0, 1, rnd);
Packit Service 2e9770
       else
Packit Service 2e9770
         return mpc_set_si_si (rop, 0, -1, rnd);
Packit Service 2e9770
     }
Packit Service 2e9770
   else if (n == 3 || n == 6)
Packit Service 2e9770
     {
Packit Service 2e9770
       MPC_ASSERT ((n == 3 && (k == 1 || k == 2)) ||
Packit Service 2e9770
                   (n == 6 && (k == 1 || k == 5)));
Packit Service 2e9770
       /* for n=3, necessarily k=1 or k=2: -1/2+/-1/2*sqrt(3)*i;
Packit Service 2e9770
          for n=6, necessarily k=1 or k=5: 1/2+/-1/2*sqrt(3)*i */
Packit Service 2e9770
       inex_re = mpfr_set_si (mpc_realref (rop), (n == 3 ? -1 : 1),
Packit Service 2e9770
                              MPC_RND_RE (rnd));
Packit Service 2e9770
       /* inverse the rounding mode for -sqrt(3)/2 for zeta_3^2 and zeta_6^5 */
Packit Service 2e9770
       rnd_im = MPC_RND_IM (rnd);
Packit Service 2e9770
       if (k != 1)
Packit Service 2e9770
         rnd_im = INV_RND (rnd_im);
Packit Service 2e9770
       inex_im = mpfr_sqrt_ui (mpc_imagref (rop), 3, rnd_im);
Packit Service 2e9770
       mpc_div_2ui (rop, rop, 1, MPC_RNDNN);
Packit Service 2e9770
       if (k != 1)
Packit Service 2e9770
         {
Packit Service 2e9770
           mpfr_neg (mpc_imagref (rop), mpc_imagref (rop), MPFR_RNDN);
Packit Service 2e9770
           inex_im = -inex_im;
Packit Service 2e9770
         }
Packit Service 2e9770
       return MPC_INEX (inex_re, inex_im);
Packit Service 2e9770
     }
Packit Service 2e9770
   else if (n == 12)
Packit Service 2e9770
     {
Packit Service 2e9770
       /* necessarily k=1, 5, 7, 11:
Packit Service 2e9770
          k=1: 1/2*sqrt(3) + 1/2*I
Packit Service 2e9770
          k=5: -1/2*sqrt(3) + 1/2*I
Packit Service 2e9770
          k=7: -1/2*sqrt(3) - 1/2*I
Packit Service 2e9770
          k=11: 1/2*sqrt(3) - 1/2*I */
Packit Service 2e9770
       MPC_ASSERT (k == 1 || k == 5 || k == 7 || k == 11);
Packit Service 2e9770
       /* inverse the rounding mode for -sqrt(3)/2 for zeta_12^5 and zeta_12^7 */
Packit Service 2e9770
       rnd_re = MPC_RND_RE (rnd);
Packit Service 2e9770
       if (k == 5 || k == 7)
Packit Service 2e9770
         rnd_re = INV_RND (rnd_re);
Packit Service 2e9770
       inex_re = mpfr_sqrt_ui (mpc_realref (rop), 3, rnd_re);
Packit Service 2e9770
       inex_im = mpfr_set_si (mpc_imagref (rop), k < 6 ? 1 : -1,
Packit Service 2e9770
                              MPC_RND_IM (rnd));
Packit Service 2e9770
       mpc_div_2ui (rop, rop, 1, MPC_RNDNN);
Packit Service 2e9770
       if (k == 5 || k == 7)
Packit Service 2e9770
         {
Packit Service 2e9770
           mpfr_neg (mpc_realref (rop), mpc_realref (rop), MPFR_RNDN);
Packit Service 2e9770
           inex_re = -inex_re;
Packit Service 2e9770
         }
Packit Service 2e9770
       return MPC_INEX (inex_re, inex_im);
Packit Service 2e9770
     }
Packit Service 2e9770
   else if (n == 8)
Packit Service 2e9770
     {
Packit Service 2e9770
       /* k=1, 3, 5 or 7:
Packit Service 2e9770
          k=1: (1/2*I + 1/2)*sqrt(2)
Packit Service 2e9770
          k=3: (1/2*I - 1/2)*sqrt(2)
Packit Service 2e9770
          k=5: -(1/2*I + 1/2)*sqrt(2)
Packit Service 2e9770
          k=7: -(1/2*I - 1/2)*sqrt(2) */
Packit Service 2e9770
       MPC_ASSERT (k == 1 || k == 3 || k == 5 || k == 7);
Packit Service 2e9770
       rnd_re = MPC_RND_RE (rnd);
Packit Service 2e9770
       if (k == 3 || k == 5)
Packit Service 2e9770
         rnd_re = INV_RND (rnd_re);
Packit Service 2e9770
       rnd_im = MPC_RND_IM (rnd);
Packit Service 2e9770
       if (k > 4)
Packit Service 2e9770
         rnd_im = INV_RND (rnd_im);
Packit Service 2e9770
       inex_re = mpfr_sqrt_ui (mpc_realref (rop), 2, rnd_re);
Packit Service 2e9770
       inex_im = mpfr_sqrt_ui (mpc_imagref (rop), 2, rnd_im);
Packit Service 2e9770
       mpc_div_2ui (rop, rop, 1, MPC_RNDNN);
Packit Service 2e9770
       if (k == 3 || k == 5)
Packit Service 2e9770
         {
Packit Service 2e9770
           mpfr_neg (mpc_realref (rop), mpc_realref (rop), MPFR_RNDN);
Packit Service 2e9770
           inex_re = -inex_re;
Packit Service 2e9770
         }
Packit Service 2e9770
       if (k > 4)
Packit Service 2e9770
         {
Packit Service 2e9770
           mpfr_neg (mpc_imagref (rop), mpc_imagref (rop), MPFR_RNDN);
Packit Service 2e9770
           inex_im = -inex_im;
Packit Service 2e9770
         }
Packit Service 2e9770
       return MPC_INEX (inex_re, inex_im);
Packit Service 2e9770
     }
Packit Service 2e9770
Packit Service 2e9770
   prec = MPC_MAX_PREC(rop);
Packit Service 2e9770
Packit Service 2e9770
   /* For the error analysis justifying the following algorithm,
Packit Service 2e9770
      see algorithms.tex. */
Packit Service 2e9770
   mpfr_init2 (t, 67);
Packit Service 2e9770
   mpfr_init2 (s, 67);
Packit Service 2e9770
   mpfr_init2 (c, 67);
Packit Service 2e9770
   mpq_init (kn);
Packit Service 2e9770
   mpq_set_ui (kn, k, n);
Packit Service 2e9770
   mpq_mul_2exp (kn, kn, 1); /* kn=2*k/n < 2 */
Packit Service 2e9770
Packit Service 2e9770
   do {
Packit Service 2e9770
      prec += mpc_ceil_log2 (prec) + 5; /* prec >= 6 */
Packit Service 2e9770
Packit Service 2e9770
      mpfr_set_prec (t, prec);
Packit Service 2e9770
      mpfr_set_prec (s, prec);
Packit Service 2e9770
      mpfr_set_prec (c, prec);
Packit Service 2e9770
Packit Service 2e9770
      mpfr_const_pi (t, MPFR_RNDN);
Packit Service 2e9770
      mpfr_mul_q (t, t, kn, MPFR_RNDN);
Packit Service 2e9770
      mpfr_sin_cos (s, c, t, MPFR_RNDN);
Packit Service 2e9770
   }
Packit Service 2e9770
   while (   !mpfr_can_round (c, prec - (4 - mpfr_get_exp (c)),
Packit Service 2e9770
                 MPFR_RNDN, MPFR_RNDZ,
Packit Service 2e9770
                 MPC_PREC_RE(rop) + (MPC_RND_RE(rnd) == MPFR_RNDN))
Packit Service 2e9770
          || !mpfr_can_round (s, prec - (4 - mpfr_get_exp (s)),
Packit Service 2e9770
                 MPFR_RNDN, MPFR_RNDZ,
Packit Service 2e9770
                 MPC_PREC_IM(rop) + (MPC_RND_IM(rnd) == MPFR_RNDN)));
Packit Service 2e9770
Packit Service 2e9770
   inex_re = mpfr_set (mpc_realref(rop), c, MPC_RND_RE(rnd));
Packit Service 2e9770
   inex_im = mpfr_set (mpc_imagref(rop), s, MPC_RND_IM(rnd));
Packit Service 2e9770
Packit Service 2e9770
   mpfr_clear (t);
Packit Service 2e9770
   mpfr_clear (s);
Packit Service 2e9770
   mpfr_clear (c);
Packit Service 2e9770
   mpq_clear (kn);
Packit Service 2e9770
Packit Service 2e9770
   return MPC_INEX(inex_re, inex_im);
Packit Service 2e9770
}