-
Notifications
You must be signed in to change notification settings - Fork 251
/
Copy pathatan.c
128 lines (111 loc) · 3.7 KB
/
atan.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/*
Copyright (C) 2017 Fredrik Johansson
This file is part of FLINT.
FLINT is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version. See <https://www.gnu.org/licenses/>.
*/
#include "arb.h"
void
arb_atan(arb_t res, const arb_t x, slong prec)
{
if (mag_is_zero(arb_radref(x)))
{
arb_atan_arf(res, arb_midref(x), prec);
}
else if (arf_is_nan(arb_midref(x)))
{
arb_indeterminate(res);
}
else if (mag_is_inf(arb_radref(x)) || arf_is_zero(arb_midref(x)))
{
mag_atan(arb_radref(res), arb_radref(x));
arf_zero(arb_midref(res));
}
else if (arf_is_special(arb_midref(x))) /* at +/- inf */
{
arb_atan_arf(res, arb_midref(x), prec);
}
else /* both mid(x), rad(x) non-special */
{
slong acc;
mag_t t, u;
/* Estimate accuracy (first guess, only good near 0). */
acc = _fmpz_sub_small(ARF_EXPREF(arb_midref(x)),
MAG_EXPREF(arb_radref(x)));
if (acc < -10) /* Only compute a rough bound. */
{
arb_get_mag(arb_radref(res), x);
mag_atan(arb_radref(res), arb_radref(res));
arf_zero(arb_midref(res));
return;
}
mag_init(t);
mag_init(u);
arb_get_mag_lower(t, x);
if (mag_is_zero(t)) /* Interval includes zero. */
{
/* atan(rad(x) - |mid(x)|) */
arf_get_mag_lower(t, arb_midref(x));
mag_sub(t, arb_radref(x), t);
mag_atan(t, t);
/* atan(|mid(x)| + rad(x)) */
arf_get_mag(u, arb_midref(x));
mag_add(u, arb_radref(x), u);
mag_atan(u, u);
if (arf_sgn(arb_midref(x)) > 0)
arb_set_interval_neg_pos_mag(res, t, u, FLINT_MIN(prec, MAG_BITS));
else
arb_set_interval_neg_pos_mag(res, u, t, FLINT_MIN(prec, MAG_BITS));
}
else
{
/* Adjust estimate of accuracy for large x. */
if (fmpz_sgn(MAG_EXPREF(t)) > 0)
{
/* acc = 2 * texp - radexp */
acc = _fmpz_sub_small(MAG_EXPREF(t), MAG_EXPREF(arb_radref(x)));
if (acc < prec && !COEFF_IS_MPZ(MAG_EXP(t)))
acc += MAG_EXP(t);
}
/* Clamp acc and adjust precision. */
acc = FLINT_MAX(acc, 0);
acc = FLINT_MIN(acc, prec);
prec = FLINT_MIN(prec, acc + MAG_BITS);
prec = FLINT_MAX(prec, 2);
/* Compute using endpoints */
if (acc < 20)
{
arb_get_mag(u, x);
mag_atan_lower(t, t);
mag_atan(u, u);
if (arf_sgn(arb_midref(x)) > 0)
{
arb_set_interval_mag(res, t, u, prec);
}
else
{
arb_set_interval_mag(res, t, u, prec);
arb_neg(res, res);
}
}
else
{
mag_mul_lower(t, t, t);
mag_one(u);
mag_add_lower(t, t, u);
mag_div(t, arb_radref(x), t);
if (mag_cmp_2exp_si(t, 0) > 0)
{
mag_const_pi(u);
mag_min(t, t, u);
}
arb_atan_arf(res, arb_midref(x), prec);
mag_add(arb_radref(res), arb_radref(res), t);
}
}
mag_clear(t);
mag_clear(u);
}
}