master
1// Ported from musl, which is licensed under the MIT license:
2// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
3//
4// https://git.musl-libc.org/cgit/musl/tree/src/complex/catanf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/complex/catan.c
6
7const std = @import("../../std.zig");
8const testing = std.testing;
9const math = std.math;
10const cmath = math.complex;
11const Complex = cmath.Complex;
12
13/// Returns the arc-tangent of z.
14pub fn atan(z: anytype) Complex(@TypeOf(z.re, z.im)) {
15 const T = @TypeOf(z.re, z.im);
16 return switch (T) {
17 f32 => atan32(z),
18 f64 => atan64(z),
19 else => @compileError("atan not implemented for " ++ @typeName(z)),
20 };
21}
22
23fn redupif32(x: f32) f32 {
24 const DP1 = 3.140625;
25 const DP2 = 9.67502593994140625e-4;
26 const DP3 = 1.509957990978376432e-7;
27
28 var t = x / math.pi;
29 if (t >= 0.0) {
30 t += 0.5;
31 } else {
32 t -= 0.5;
33 }
34
35 const u: f32 = @trunc(t);
36 return ((x - u * DP1) - u * DP2) - u * DP3;
37}
38
39fn atan32(z: Complex(f32)) Complex(f32) {
40 const x = z.re;
41 const y = z.im;
42
43 const x2 = x * x;
44 var a = 1.0 - x2 - (y * y);
45
46 var t = 0.5 * math.atan2(2.0 * x, a);
47 const w = redupif32(t);
48
49 t = y - 1.0;
50 a = x2 + t * t;
51
52 t = y + 1.0;
53 a = (x2 + (t * t)) / a;
54 return Complex(f32).init(w, 0.25 * @log(a));
55}
56
57fn redupif64(x: f64) f64 {
58 const DP1 = 3.14159265160560607910;
59 const DP2 = 1.98418714791870343106e-9;
60 const DP3 = 1.14423774522196636802e-17;
61
62 var t = x / math.pi;
63 if (t >= 0.0) {
64 t += 0.5;
65 } else {
66 t -= 0.5;
67 }
68
69 const u: f64 = @trunc(t);
70 return ((x - u * DP1) - u * DP2) - u * DP3;
71}
72
73fn atan64(z: Complex(f64)) Complex(f64) {
74 const x = z.re;
75 const y = z.im;
76
77 const x2 = x * x;
78 var a = 1.0 - x2 - (y * y);
79
80 var t = 0.5 * math.atan2(2.0 * x, a);
81 const w = redupif64(t);
82
83 t = y - 1.0;
84 a = x2 + t * t;
85
86 t = y + 1.0;
87 a = (x2 + (t * t)) / a;
88 return Complex(f64).init(w, 0.25 * @log(a));
89}
90
91test atan32 {
92 const epsilon = math.floatEps(f32);
93 const a = Complex(f32).init(5, 3);
94 const c = atan(a);
95
96 try testing.expectApproxEqAbs(1.423679, c.re, epsilon);
97 try testing.expectApproxEqAbs(0.086569, c.im, epsilon);
98}
99
100test atan64 {
101 const epsilon = math.floatEps(f64);
102 const a = Complex(f64).init(5, 3);
103 const c = atan(a);
104
105 try testing.expectApproxEqAbs(1.4236790442393028, c.re, epsilon);
106 try testing.expectApproxEqAbs(0.08656905917945844, c.im, epsilon);
107}