const std = @import("../../std.zig");
const testing = std.testing;
const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
pub fn exp(z: anytype) @TypeOf(z) {
const T = @TypeOf(z.re);
return switch (T) {
f32 => exp32(z),
f64 => exp64(z),
else => @compileError("exp not implemented for " ++ @typeName(z)),
};
}
fn exp32(z: Complex(f32)) Complex(f32) {
const exp_overflow = 0x42b17218;
const cexp_overflow = 0x43400074;
const x = z.re;
const y = z.im;
const hy = @bitCast(u32, y) & 0x7fffffff;
if (hy == 0) {
return Complex(f32).init(@exp(x), y);
}
const hx = @bitCast(u32, x);
if ((hx & 0x7fffffff) == 0) {
return Complex(f32).init(@cos(y), @sin(y));
}
if (hy >= 0x7f800000) {
if ((hx & 0x7fffffff) != 0x7f800000) {
return Complex(f32).init(y - y, y - y);
}
else if (hx & 0x80000000 != 0) {
return Complex(f32).init(0, 0);
}
else {
return Complex(f32).init(x, y - y);
}
}
if (hx >= exp_overflow and hx <= cexp_overflow) {
return ldexp_cexp(z, 0);
}
else {
const exp_x = @exp(x);
return Complex(f32).init(exp_x * @cos(y), exp_x * @sin(y));
}
}
fn exp64(z: Complex(f64)) Complex(f64) {
const exp_overflow = 0x40862e42;
const cexp_overflow = 0x4096b8e4;
const x = z.re;
const y = z.im;
const fy = @bitCast(u64, y);
const hy = @intCast(u32, (fy >> 32) & 0x7fffffff);
const ly = @truncate(u32, fy);
if (hy | ly == 0) {
return Complex(f64).init(@exp(x), y);
}
const fx = @bitCast(u64, x);
const hx = @intCast(u32, fx >> 32);
const lx = @truncate(u32, fx);
if ((hx & 0x7fffffff) | lx == 0) {
return Complex(f64).init(@cos(y), @sin(y));
}
if (hy >= 0x7ff00000) {
if (lx != 0 or (hx & 0x7fffffff) != 0x7ff00000) {
return Complex(f64).init(y - y, y - y);
}
else if (hx & 0x80000000 != 0) {
return Complex(f64).init(0, 0);
}
else {
return Complex(f64).init(x, y - y);
}
}
if (hx >= exp_overflow and hx <= cexp_overflow) {
return ldexp_cexp(z, 0);
}
else {
const exp_x = @exp(x);
return Complex(f64).init(exp_x * @cos(y), exp_x * @sin(y));
}
}
test "complex.cexp32" {
const tolerance_f32 = @sqrt(math.floatEps(f32));
{
const a = Complex(f32).init(5, 3);
const c = exp(a);
try testing.expectApproxEqRel(@as(f32, -1.46927917e+02), c.re, tolerance_f32);
try testing.expectApproxEqRel(@as(f32, 2.0944065e+01), c.im, tolerance_f32);
}
{
const a = Complex(f32).init(88.8, 0x1p-149);
const c = exp(a);
try testing.expectApproxEqAbs(math.inf(f32), c.re, tolerance_f32);
try testing.expectApproxEqAbs(@as(f32, 5.15088629e-07), c.im, tolerance_f32);
}
}
test "complex.cexp64" {
const tolerance_f64 = @sqrt(math.floatEps(f64));
{
const a = Complex(f64).init(5, 3);
const c = exp(a);
try testing.expectApproxEqRel(@as(f64, -1.469279139083189e+02), c.re, tolerance_f64);
try testing.expectApproxEqRel(@as(f64, 2.094406620874596e+01), c.im, tolerance_f64);
}
{
const a = Complex(f64).init(709.8, 0x1p-1074);
const c = exp(a);
try testing.expectApproxEqAbs(math.inf(f64), c.re, tolerance_f64);
try testing.expectApproxEqAbs(@as(f64, 9.036659362159884e-16), c.im, tolerance_f64);
}
}