xref: /netbsd-src/external/lgpl3/mpc/dist/src/tan.c (revision 367b82799ab709709d3c3b541df56a2a14644d3e)
18fa80f29Smrg /* mpc_tan -- tangent of a complex number.
28fa80f29Smrg 
3*367b8279Smrg Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2015, 2020, 2022 INRIA
48fa80f29Smrg 
58fa80f29Smrg This file is part of GNU MPC.
68fa80f29Smrg 
78fa80f29Smrg GNU MPC is free software; you can redistribute it and/or modify it under
88fa80f29Smrg the terms of the GNU Lesser General Public License as published by the
98fa80f29Smrg Free Software Foundation; either version 3 of the License, or (at your
108fa80f29Smrg option) any later version.
118fa80f29Smrg 
128fa80f29Smrg GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
138fa80f29Smrg WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
148fa80f29Smrg FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
158fa80f29Smrg more details.
168fa80f29Smrg 
178fa80f29Smrg You should have received a copy of the GNU Lesser General Public License
188fa80f29Smrg along with this program. If not, see http://www.gnu.org/licenses/ .
198fa80f29Smrg */
208fa80f29Smrg 
218fa80f29Smrg #include <stdio.h>    /* for MPC_ASSERT */
228fa80f29Smrg #include <limits.h>
238fa80f29Smrg #include "mpc-impl.h"
248fa80f29Smrg 
2590a8ff21Smrg /* special case where the imaginary part of tan(op) rounds to -1 or 1:
2690a8ff21Smrg    return 1 if |Im(tan(op))| > 1, and -1 if |Im(tan(op))| < 1, return 0
2790a8ff21Smrg    if we can't decide.
2890a8ff21Smrg    The imaginary part is sinh(2*y)/(cos(2*x) + cosh(2*y)) where op = (x,y).
2990a8ff21Smrg */
3090a8ff21Smrg static int
tan_im_cmp_one(mpc_srcptr op)3190a8ff21Smrg tan_im_cmp_one (mpc_srcptr op)
3290a8ff21Smrg {
3390a8ff21Smrg   mpfr_t x, c;
3490a8ff21Smrg   int ret = 0;
3590a8ff21Smrg   mpfr_exp_t expc;
3690a8ff21Smrg 
3790a8ff21Smrg   mpfr_init2 (x, mpfr_get_prec (mpc_realref (op)));
3890a8ff21Smrg   mpfr_mul_2exp (x, mpc_realref (op), 1, MPFR_RNDN);
3990a8ff21Smrg   mpfr_init2 (c, 32);
4090a8ff21Smrg   mpfr_cos (c, x, MPFR_RNDN);
4190a8ff21Smrg   /* if cos(2x) >= 0, then |sinh(2y)/(cos(2x)+cosh(2y))| < 1 */
4290a8ff21Smrg   if (mpfr_sgn (c) >= 0)
4390a8ff21Smrg     ret = -1; /* |Im(tan(op))| < 1 */
4490a8ff21Smrg   else
4590a8ff21Smrg     {
4690a8ff21Smrg       /* now cos(2x) < 0: |cosh(2y) - sinh(2y)| = exp(-2|y|) */
4790a8ff21Smrg       expc = mpfr_get_exp (c);
4890a8ff21Smrg       mpfr_abs (c, mpc_imagref (op), MPFR_RNDN);
4990a8ff21Smrg       mpfr_mul_si (c, c, -2, MPFR_RNDN);
5090a8ff21Smrg       mpfr_exp (c, c, MPFR_RNDN);
5190a8ff21Smrg       if (mpfr_zero_p (c) || mpfr_get_exp (c) < expc)
5290a8ff21Smrg         ret = 1; /* |Im(tan(op))| > 1 */
5390a8ff21Smrg     }
5490a8ff21Smrg   mpfr_clear (c);
5590a8ff21Smrg   mpfr_clear (x);
5690a8ff21Smrg   return ret;
5790a8ff21Smrg }
5890a8ff21Smrg 
5990a8ff21Smrg /* special case where the real part of tan(op) underflows to 0:
6090a8ff21Smrg    return 1 if 0 < Re(tan(op)) < 2^(emin-2),
6190a8ff21Smrg    -1 if -2^(emin-2) < Re(tan(op))| < 0, and 0 if we can't decide.
6290a8ff21Smrg    The real part is sin(2*x)/(cos(2*x) + cosh(2*y)) where op = (x,y),
6390a8ff21Smrg    thus has the sign of sin(2*x).
6490a8ff21Smrg */
6590a8ff21Smrg static int
tan_re_cmp_zero(mpc_srcptr op,mpfr_exp_t emin)6690a8ff21Smrg tan_re_cmp_zero (mpc_srcptr op, mpfr_exp_t emin)
6790a8ff21Smrg {
6890a8ff21Smrg   mpfr_t x, s, c;
6990a8ff21Smrg   int ret = 0;
7090a8ff21Smrg 
7190a8ff21Smrg   mpfr_init2 (x, mpfr_get_prec (mpc_realref (op)));
7290a8ff21Smrg   mpfr_mul_2exp (x, mpc_realref (op), 1, MPFR_RNDN);
7390a8ff21Smrg   mpfr_init2 (s, 32);
7490a8ff21Smrg   mpfr_init2 (c, 32);
7590a8ff21Smrg   mpfr_sin (s, x, MPFR_RNDA);
7690a8ff21Smrg   mpfr_mul_2exp (x, mpc_imagref (op), 1, MPFR_RNDN);
7790a8ff21Smrg   mpfr_cosh (c, x, MPFR_RNDZ);
7890a8ff21Smrg   mpfr_sub_ui (c, c, 1, MPFR_RNDZ);
7990a8ff21Smrg   mpfr_div (s, s, c, MPFR_RNDA);
8090a8ff21Smrg   if (mpfr_zero_p (s) || mpfr_get_exp (s) <= emin - 2)
8190a8ff21Smrg     ret = mpfr_sgn (s);
8290a8ff21Smrg   mpfr_clear (s);
8390a8ff21Smrg   mpfr_clear (c);
8490a8ff21Smrg   mpfr_clear (x);
8590a8ff21Smrg   return ret;
8690a8ff21Smrg }
8790a8ff21Smrg 
888fa80f29Smrg int
mpc_tan(mpc_ptr rop,mpc_srcptr op,mpc_rnd_t rnd)898fa80f29Smrg mpc_tan (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd)
908fa80f29Smrg {
918fa80f29Smrg   mpc_t x, y;
928fa80f29Smrg   mpfr_prec_t prec;
938fa80f29Smrg   mpfr_exp_t err;
9490a8ff21Smrg   int ok;
9590a8ff21Smrg   int inex, inex_re, inex_im;
9690a8ff21Smrg   mpfr_exp_t saved_emin, saved_emax;
978fa80f29Smrg 
988fa80f29Smrg   /* special values */
998fa80f29Smrg   if (!mpc_fin_p (op))
1008fa80f29Smrg     {
1018fa80f29Smrg       if (mpfr_nan_p (mpc_realref (op)))
1028fa80f29Smrg         {
1038fa80f29Smrg           if (mpfr_inf_p (mpc_imagref (op)))
1048fa80f29Smrg             /* tan(NaN -i*Inf) = +/-0 -i */
1058fa80f29Smrg             /* tan(NaN +i*Inf) = +/-0 +i */
1068fa80f29Smrg             {
1078fa80f29Smrg               /* exact unless 1 is not in exponent range */
1088fa80f29Smrg               inex = mpc_set_si_si (rop, 0,
1098fa80f29Smrg                                     (MPFR_SIGN (mpc_imagref (op)) < 0) ? -1 : +1,
1108fa80f29Smrg                                     rnd);
1118fa80f29Smrg             }
1128fa80f29Smrg           else
1138fa80f29Smrg             /* tan(NaN +i*y) = NaN +i*NaN, when y is finite */
1148fa80f29Smrg             /* tan(NaN +i*NaN) = NaN +i*NaN */
1158fa80f29Smrg             {
1168fa80f29Smrg               mpfr_set_nan (mpc_realref (rop));
1178fa80f29Smrg               mpfr_set_nan (mpc_imagref (rop));
1188fa80f29Smrg               inex = MPC_INEX (0, 0); /* always exact */
1198fa80f29Smrg             }
1208fa80f29Smrg         }
1218fa80f29Smrg       else if (mpfr_nan_p (mpc_imagref (op)))
1228fa80f29Smrg         {
1238fa80f29Smrg           if (mpfr_cmp_ui (mpc_realref (op), 0) == 0)
1248fa80f29Smrg             /* tan(-0 +i*NaN) = -0 +i*NaN */
1258fa80f29Smrg             /* tan(+0 +i*NaN) = +0 +i*NaN */
1268fa80f29Smrg             {
1278fa80f29Smrg               mpc_set (rop, op, rnd);
1288fa80f29Smrg               inex = MPC_INEX (0, 0); /* always exact */
1298fa80f29Smrg             }
1308fa80f29Smrg           else
1318fa80f29Smrg             /* tan(x +i*NaN) = NaN +i*NaN, when x != 0 */
1328fa80f29Smrg             {
1338fa80f29Smrg               mpfr_set_nan (mpc_realref (rop));
1348fa80f29Smrg               mpfr_set_nan (mpc_imagref (rop));
1358fa80f29Smrg               inex = MPC_INEX (0, 0); /* always exact */
1368fa80f29Smrg             }
1378fa80f29Smrg         }
1388fa80f29Smrg       else if (mpfr_inf_p (mpc_realref (op)))
1398fa80f29Smrg         {
1408fa80f29Smrg           if (mpfr_inf_p (mpc_imagref (op)))
1418fa80f29Smrg             /* tan(-Inf -i*Inf) = -/+0 -i */
1428fa80f29Smrg             /* tan(-Inf +i*Inf) = -/+0 +i */
1438fa80f29Smrg             /* tan(+Inf -i*Inf) = +/-0 -i */
1448fa80f29Smrg             /* tan(+Inf +i*Inf) = +/-0 +i */
1458fa80f29Smrg             {
1468fa80f29Smrg               const int sign_re = mpfr_signbit (mpc_realref (op));
1478fa80f29Smrg 
1488fa80f29Smrg               mpfr_set_ui (mpc_realref (rop), 0, MPC_RND_RE (rnd));
14939f28e1eSmrg               mpfr_setsign (mpc_realref (rop), mpc_realref (rop), sign_re, MPFR_RNDN);
1508fa80f29Smrg 
1518fa80f29Smrg               /* exact, unless 1 is not in exponent range */
1528fa80f29Smrg               inex_im = mpfr_set_si (mpc_imagref (rop),
1538fa80f29Smrg                                      mpfr_signbit (mpc_imagref (op)) ? -1 : +1,
1548fa80f29Smrg                                      MPC_RND_IM (rnd));
1558fa80f29Smrg               inex = MPC_INEX (0, inex_im);
1568fa80f29Smrg             }
1578fa80f29Smrg           else
1588fa80f29Smrg             /* tan(-Inf +i*y) = tan(+Inf +i*y) = NaN +i*NaN, when y is
1598fa80f29Smrg                finite */
1608fa80f29Smrg             {
1618fa80f29Smrg               mpfr_set_nan (mpc_realref (rop));
1628fa80f29Smrg               mpfr_set_nan (mpc_imagref (rop));
1638fa80f29Smrg               inex = MPC_INEX (0, 0); /* always exact */
1648fa80f29Smrg             }
1658fa80f29Smrg         }
1668fa80f29Smrg       else
1678fa80f29Smrg         /* tan(x -i*Inf) = +0*sin(x)*cos(x) -i, when x is finite */
1688fa80f29Smrg         /* tan(x +i*Inf) = +0*sin(x)*cos(x) +i, when x is finite */
1698fa80f29Smrg         {
1708fa80f29Smrg           mpfr_t c;
1718fa80f29Smrg           mpfr_t s;
1728fa80f29Smrg 
1738fa80f29Smrg           mpfr_init (c);
1748fa80f29Smrg           mpfr_init (s);
1758fa80f29Smrg 
17639f28e1eSmrg           mpfr_sin_cos (s, c, mpc_realref (op), MPFR_RNDN);
1778fa80f29Smrg           mpfr_set_ui (mpc_realref (rop), 0, MPC_RND_RE (rnd));
1788fa80f29Smrg           mpfr_setsign (mpc_realref (rop), mpc_realref (rop),
17939f28e1eSmrg                         mpfr_signbit (c) != mpfr_signbit (s), MPFR_RNDN);
1808fa80f29Smrg           /* exact, unless 1 is not in exponent range */
1818fa80f29Smrg           inex_im = mpfr_set_si (mpc_imagref (rop),
1828fa80f29Smrg                                  (mpfr_signbit (mpc_imagref (op)) ? -1 : +1),
1838fa80f29Smrg                                  MPC_RND_IM (rnd));
1848fa80f29Smrg           inex = MPC_INEX (0, inex_im);
1858fa80f29Smrg 
1868fa80f29Smrg           mpfr_clear (s);
1878fa80f29Smrg           mpfr_clear (c);
1888fa80f29Smrg         }
1898fa80f29Smrg 
1908fa80f29Smrg       return inex;
1918fa80f29Smrg     }
1928fa80f29Smrg 
1938fa80f29Smrg   if (mpfr_zero_p (mpc_realref (op)))
1948fa80f29Smrg     /* tan(-0 -i*y) = -0 +i*tanh(y), when y is finite. */
1958fa80f29Smrg     /* tan(+0 +i*y) = +0 +i*tanh(y), when y is finite. */
1968fa80f29Smrg     {
1978fa80f29Smrg       mpfr_set (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd));
1988fa80f29Smrg       inex_im = mpfr_tanh (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd));
1998fa80f29Smrg 
2008fa80f29Smrg       return MPC_INEX (0, inex_im);
2018fa80f29Smrg     }
2028fa80f29Smrg 
2038fa80f29Smrg   if (mpfr_zero_p (mpc_imagref (op)))
2048fa80f29Smrg     /* tan(x -i*0) = tan(x) -i*0, when x is finite. */
2058fa80f29Smrg     /* tan(x +i*0) = tan(x) +i*0, when x is finite. */
2068fa80f29Smrg     {
2078fa80f29Smrg       inex_re = mpfr_tan (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd));
2088fa80f29Smrg       mpfr_set (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd));
2098fa80f29Smrg 
2108fa80f29Smrg       return MPC_INEX (inex_re, 0);
2118fa80f29Smrg     }
2128fa80f29Smrg 
21390a8ff21Smrg   saved_emin = mpfr_get_emin ();
21490a8ff21Smrg   saved_emax = mpfr_get_emax ();
21590a8ff21Smrg   mpfr_set_emin (mpfr_get_emin_min ());
21690a8ff21Smrg   mpfr_set_emax (mpfr_get_emax_max ());
21790a8ff21Smrg 
2188fa80f29Smrg   /* ordinary (non-zero) numbers */
2198fa80f29Smrg 
2208fa80f29Smrg   /* tan(op) = sin(op) / cos(op).
2218fa80f29Smrg 
2228fa80f29Smrg      We use the following algorithm with rounding away from 0 for all
2238fa80f29Smrg      operations, and working precision w:
2248fa80f29Smrg 
2258fa80f29Smrg      (1) x = A(sin(op))
2268fa80f29Smrg      (2) y = A(cos(op))
2278fa80f29Smrg      (3) z = A(x/y)
2288fa80f29Smrg 
2298fa80f29Smrg      the error on Im(z) is at most 81 ulp,
2308fa80f29Smrg      the error on Re(z) is at most
2318fa80f29Smrg      7 ulp if k < 2,
2328fa80f29Smrg      8 ulp if k = 2,
2338fa80f29Smrg      else 5+k ulp, where
2348fa80f29Smrg      k = Exp(Re(x))+Exp(Re(y))-2min{Exp(Re(y)), Exp(Im(y))}-Exp(Re(x/y))
2358fa80f29Smrg      see proof in algorithms.tex.
2368fa80f29Smrg   */
2378fa80f29Smrg 
2388fa80f29Smrg   prec = MPC_MAX_PREC(rop);
2398fa80f29Smrg 
2408fa80f29Smrg   mpc_init2 (x, 2);
2418fa80f29Smrg   mpc_init2 (y, 2);
2428fa80f29Smrg 
2438fa80f29Smrg   err = 7;
2448fa80f29Smrg 
2458fa80f29Smrg   do
2468fa80f29Smrg     {
2478fa80f29Smrg       mpfr_exp_t k, exr, eyr, eyi, ezr;
2488fa80f29Smrg 
2498fa80f29Smrg       ok = 0;
2508fa80f29Smrg 
2518fa80f29Smrg       /* FIXME: prevent addition overflow */
2528fa80f29Smrg       prec += mpc_ceil_log2 (prec) + err;
2538fa80f29Smrg       mpc_set_prec (x, prec);
2548fa80f29Smrg       mpc_set_prec (y, prec);
2558fa80f29Smrg 
256*367b8279Smrg       mpc_sin_cos (x, y, op, MPC_RNDAA, MPC_RNDAA);
2578fa80f29Smrg 
2588fa80f29Smrg       if (   mpfr_inf_p (mpc_realref (x)) || mpfr_inf_p (mpc_imagref (x))
2598fa80f29Smrg           || mpfr_inf_p (mpc_realref (y)) || mpfr_inf_p (mpc_imagref (y))) {
2608fa80f29Smrg          /* If the real or imaginary part of x is infinite, it means that
2618fa80f29Smrg             Im(op) was large, in which case the result is
2628fa80f29Smrg             sign(tan(Re(op)))*0 + sign(Im(op))*I,
2638fa80f29Smrg             where sign(tan(Re(op))) = sign(Re(x))*sign(Re(y)). */
26439f28e1eSmrg           mpfr_set_ui (mpc_realref (rop), 0, MPFR_RNDN);
2658fa80f29Smrg           if (mpfr_sgn (mpc_realref (x)) * mpfr_sgn (mpc_realref (y)) < 0)
2668fa80f29Smrg             {
26739f28e1eSmrg               mpfr_neg (mpc_realref (rop), mpc_realref (rop), MPFR_RNDN);
2688fa80f29Smrg               inex_re = 1;
2698fa80f29Smrg             }
2708fa80f29Smrg           else
2718fa80f29Smrg             inex_re = -1; /* +0 is rounded down */
2728fa80f29Smrg           if (mpfr_sgn (mpc_imagref (op)) > 0)
2738fa80f29Smrg             {
27439f28e1eSmrg               mpfr_set_ui (mpc_imagref (rop), 1, MPFR_RNDN);
2758fa80f29Smrg               inex_im = 1;
2768fa80f29Smrg             }
2778fa80f29Smrg           else
2788fa80f29Smrg             {
27939f28e1eSmrg               mpfr_set_si (mpc_imagref (rop), -1, MPFR_RNDN);
2808fa80f29Smrg               inex_im = -1;
2818fa80f29Smrg             }
28239f28e1eSmrg           /* if rounding is toward zero, fix the imaginary part */
28339f28e1eSmrg           if (MPC_IS_LIKE_RNDZ(MPC_RND_IM(rnd), MPFR_SIGNBIT(mpc_imagref (rop))))
28439f28e1eSmrg             {
28539f28e1eSmrg               mpfr_nexttoward (mpc_imagref (rop), mpc_realref (rop));
28639f28e1eSmrg               inex_im = -inex_im;
28739f28e1eSmrg             }
28839f28e1eSmrg           if (mpfr_zero_p (mpc_realref (rop)))
28939f28e1eSmrg             inex_re = mpc_fix_zero (mpc_realref (rop), MPC_RND_RE(rnd));
29039f28e1eSmrg           if (mpfr_zero_p (mpc_imagref (rop)))
29139f28e1eSmrg             inex_im = mpc_fix_zero (mpc_imagref (rop), MPC_RND_IM(rnd));
2928fa80f29Smrg           inex = MPC_INEX(inex_re, inex_im);
2938fa80f29Smrg           goto end;
2948fa80f29Smrg         }
2958fa80f29Smrg 
2968fa80f29Smrg       exr = mpfr_get_exp (mpc_realref (x));
2978fa80f29Smrg       eyr = mpfr_get_exp (mpc_realref (y));
2988fa80f29Smrg       eyi = mpfr_get_exp (mpc_imagref (y));
2998fa80f29Smrg 
3008fa80f29Smrg       /* some parts of the quotient may be exact */
3018fa80f29Smrg       inex = mpc_div (x, x, y, MPC_RNDZZ);
3028fa80f29Smrg       /* OP is no pure real nor pure imaginary, so in theory the real and
3038fa80f29Smrg          imaginary parts of its tangent cannot be null. However due to
30439f28e1eSmrg          rounding errors this might happen. Consider for example
3058fa80f29Smrg          tan(1+14*I) = 1.26e-10 + 1.00*I. For small precision sin(op) and
3068fa80f29Smrg          cos(op) differ only by a factor I, thus after mpc_div x = I and
3078fa80f29Smrg          its real part is zero. */
30890a8ff21Smrg       if (mpfr_zero_p (mpc_realref (x)))
3098fa80f29Smrg         {
31090a8ff21Smrg           /* since we use an extended exponent range, if real(x) is zero,
31190a8ff21Smrg              this means the real part underflows, and we assume we can round */
31290a8ff21Smrg           ok = tan_re_cmp_zero (op, saved_emin);
31390a8ff21Smrg           if (ok > 0)
31490a8ff21Smrg             MPFR_ADD_ONE_ULP (mpc_realref (x));
31590a8ff21Smrg           else
31690a8ff21Smrg             MPFR_SUB_ONE_ULP (mpc_realref (x));
3178fa80f29Smrg         }
31890a8ff21Smrg       else
31990a8ff21Smrg         {
3208fa80f29Smrg           if (MPC_INEX_RE (inex))
3218fa80f29Smrg             MPFR_ADD_ONE_ULP (mpc_realref (x));
3228fa80f29Smrg           MPC_ASSERT (mpfr_zero_p (mpc_realref (x)) == 0);
3238fa80f29Smrg           ezr = mpfr_get_exp (mpc_realref (x));
3248fa80f29Smrg 
3258fa80f29Smrg           /* FIXME: compute
3268fa80f29Smrg              k = Exp(Re(x))+Exp(Re(y))-2min{Exp(Re(y)), Exp(Im(y))}-Exp(Re(x/y))
3278fa80f29Smrg              avoiding overflow */
3288fa80f29Smrg           k = exr - ezr + MPC_MAX(-eyr, eyr - 2 * eyi);
3298fa80f29Smrg           err = k < 2 ? 7 : (k == 2 ? 8 : (5 + k));
3308fa80f29Smrg 
3318fa80f29Smrg           /* Can the real part be rounded? */
3328fa80f29Smrg           ok = (!mpfr_number_p (mpc_realref (x)))
33339f28e1eSmrg             || mpfr_can_round (mpc_realref(x), prec - err, MPFR_RNDN, MPFR_RNDZ,
33439f28e1eSmrg                                MPC_PREC_RE(rop) + (MPC_RND_RE(rnd) == MPFR_RNDN));
33590a8ff21Smrg         }
3368fa80f29Smrg 
3378fa80f29Smrg       if (ok)
3388fa80f29Smrg         {
33990a8ff21Smrg           if (MPC_INEX_IM (inex))
34090a8ff21Smrg             MPFR_ADD_ONE_ULP (mpc_imagref (x));
34190a8ff21Smrg 
3428fa80f29Smrg           /* Can the imaginary part be rounded? */
3438fa80f29Smrg           ok = (!mpfr_number_p (mpc_imagref (x)))
34439f28e1eSmrg             || mpfr_can_round (mpc_imagref(x), prec - 6, MPFR_RNDN, MPFR_RNDZ,
34539f28e1eSmrg                             MPC_PREC_IM(rop) + (MPC_RND_IM(rnd) == MPFR_RNDN));
34690a8ff21Smrg 
34790a8ff21Smrg           /* Special case when Im(x) = +/- 1:
34890a8ff21Smrg              tan z = [sin(2x)+i*sinh(2y)] / [cos(2x) + cosh(2y)]
34990a8ff21Smrg              (formula 4.3.57 of Abramowitz and Stegun) thus for y large
35090a8ff21Smrg              in absolute value the imaginary part is near -1 or +1.
35190a8ff21Smrg              More precisely cos(2x) + cosh(2y) = cosh(2y) + t with |t| <= 1,
35290a8ff21Smrg              thus since cosh(2y) >= exp|2y|/2, then the imaginary part is:
35390a8ff21Smrg              tanh(2y) * 1/(1+u) where u = |cos(2x)/cosh(2y)| <= 2/exp|2y|
35490a8ff21Smrg              thus |im(z) - tanh(2y)| <= 2/exp|2y| * tanh(2y).
35590a8ff21Smrg              Since |tanh(2y)| = (1-exp(-4|y|))/(1+exp(-4|y|)),
35690a8ff21Smrg              we have 1-|tanh(2y)| < 2*exp(-4|y|).
35790a8ff21Smrg              Thus |im(z)-1| < 2/exp|2y| + 2/exp|4y| < 4/exp|2y| < 4/2^|2y|.
35890a8ff21Smrg              If 2^EXP(y) >= p+2, then im(z) rounds to -1 or 1. */
35990a8ff21Smrg           if (ok == 0 && (mpfr_cmp_ui (mpc_imagref(x), 1) == 0 ||
36090a8ff21Smrg                           mpfr_cmp_si (mpc_imagref(x), -1) == 0) &&
36190a8ff21Smrg               mpfr_get_exp (mpc_imagref(op)) >= 0 &&
36290a8ff21Smrg               ((size_t) mpfr_get_exp (mpc_imagref(op)) >= 8 * sizeof (mpfr_prec_t) ||
36390a8ff21Smrg                ((mpfr_prec_t) 1) << mpfr_get_exp (mpc_imagref(op)) >= mpfr_get_prec (mpc_imagref (rop)) + 2))
36490a8ff21Smrg             {
36590a8ff21Smrg               /* subtract one ulp, so that we get the correct inexact flag */
36690a8ff21Smrg               ok = tan_im_cmp_one (op);
36790a8ff21Smrg               if (ok < 0)
36890a8ff21Smrg                 MPFR_SUB_ONE_ULP (mpc_imagref(x));
36990a8ff21Smrg               else if (ok > 0)
37090a8ff21Smrg                 MPFR_ADD_ONE_ULP (mpc_imagref(x));
3718fa80f29Smrg             }
3728fa80f29Smrg         }
37390a8ff21Smrg 
37490a8ff21Smrg       if (ok == 0)
37590a8ff21Smrg         prec += prec / 2;
37690a8ff21Smrg     }
3778fa80f29Smrg   while (ok == 0);
3788fa80f29Smrg 
3798fa80f29Smrg   inex = mpc_set (rop, x, rnd);
3808fa80f29Smrg 
3818fa80f29Smrg  end:
3828fa80f29Smrg   mpc_clear (x);
3838fa80f29Smrg   mpc_clear (y);
3848fa80f29Smrg 
38590a8ff21Smrg   /* restore the exponent range, and check the range of results */
38690a8ff21Smrg   mpfr_set_emin (saved_emin);
38790a8ff21Smrg   mpfr_set_emax (saved_emax);
38890a8ff21Smrg   inex_re = mpfr_check_range (mpc_realref (rop), MPC_INEX_RE(inex),
38990a8ff21Smrg                               MPC_RND_RE (rnd));
39090a8ff21Smrg   inex_im = mpfr_check_range (mpc_imagref (rop), MPC_INEX_IM(inex),
39190a8ff21Smrg                               MPC_RND_IM (rnd));
39290a8ff21Smrg 
39390a8ff21Smrg   return MPC_INEX(inex_re, inex_im);
3948fa80f29Smrg }
395