const std = @import("std"); const isNan = std.math.isNan; const isInf = std.math.isInf; const copysign = std.math.copysign; pub fn Complex(comptime T: type) type { return extern struct { real: T, imag: T, }; } /// Implementation based on Annex G of C17 Standard (N2176) pub inline fn mulc3(comptime T: type, a_in: T, b_in: T, c_in: T, d_in: T) Complex(T) { var a = a_in; var b = b_in; var c = c_in; var d = d_in; const ac = a * c; const bd = b * d; const ad = a * d; const bc = b * c; const zero: T = 0.0; const one: T = 1.0; var z = Complex(T){ .real = ac - bd, .imag = ad + bc, }; if (isNan(z.real) and isNan(z.imag)) { var recalc: bool = false; if (isInf(a) or isInf(b)) { // (a + ib) is infinite // "Box" the infinity (+/-inf goes to +/-1, all finite values go to 0) a = copysign(if (isInf(a)) one else zero, a); b = copysign(if (isInf(b)) one else zero, b); // Replace NaNs in the other factor with (signed) 0 if (isNan(c)) c = copysign(zero, c); if (isNan(d)) d = copysign(zero, d); recalc = true; } if (isInf(c) or isInf(d)) { // (c + id) is infinite // "Box" the infinity (+/-inf goes to +/-1, all finite values go to 0) c = copysign(if (isInf(c)) one else zero, c); d = copysign(if (isInf(d)) one else zero, d); // Replace NaNs in the other factor with (signed) 0 if (isNan(a)) a = copysign(zero, a); if (isNan(b)) b = copysign(zero, b); recalc = true; } if (!recalc and (isInf(ac) or isInf(bd) or isInf(ad) or isInf(bc))) { // Recover infinities from overflow by changing NaNs to 0 if (isNan(a)) a = copysign(zero, a); if (isNan(b)) b = copysign(zero, b); if (isNan(c)) c = copysign(zero, c); if (isNan(d)) d = copysign(zero, d); recalc = true; } if (recalc) { return .{ .real = std.math.inf(T) * (a * c - b * d), .imag = std.math.inf(T) * (a * d + b * c), }; } } return z; }