| 1 | const std = @import("../std.zig"); |
| 2 | const testing = std.testing; |
| 3 | const math = std.math; |
| 4 | |
| 5 | pub const abs = @import("complex/abs.zig").abs; |
| 6 | pub const acosh = @import("complex/acosh.zig").acosh; |
| 7 | pub const acos = @import("complex/acos.zig").acos; |
| 8 | pub const arg = @import("complex/arg.zig").arg; |
| 9 | pub const asinh = @import("complex/asinh.zig").asinh; |
| 10 | pub const asin = @import("complex/asin.zig").asin; |
| 11 | pub const atanh = @import("complex/atanh.zig").atanh; |
| 12 | pub const atan = @import("complex/atan.zig").atan; |
| 13 | pub const conj = @import("complex/conj.zig").conj; |
| 14 | pub const cosh = @import("complex/cosh.zig").cosh; |
| 15 | pub const cos = @import("complex/cos.zig").cos; |
| 16 | pub const exp = @import("complex/exp.zig").exp; |
| 17 | pub const log = @import("complex/log.zig").log; |
| 18 | pub const pow = @import("complex/pow.zig").pow; |
| 19 | pub const proj = @import("complex/proj.zig").proj; |
| 20 | pub const sinh = @import("complex/sinh.zig").sinh; |
| 21 | pub const sin = @import("complex/sin.zig").sin; |
| 22 | pub const sqrt = @import("complex/sqrt.zig").sqrt; |
| 23 | pub const tanh = @import("complex/tanh.zig").tanh; |
| 24 | pub const tan = @import("complex/tan.zig").tan; |
| 25 | |
| 26 | /// A complex number consisting of a real an imaginary part. T must be a floating-point value. |
| 27 | pub fn Complex(comptime T: type) type { |
| 28 | return struct { |
| 29 | const Self = @This(); |
| 30 | |
| 31 | /// Real part. |
| 32 | re: T, |
| 33 | |
| 34 | /// Imaginary part. |
| 35 | im: T, |
| 36 | |
| 37 | /// Create a new Complex number from the given real and imaginary parts. |
| 38 | pub fn init(re: T, im: T) Self { |
| 39 | return Self{ |
| 40 | .re = re, |
| 41 | .im = im, |
| 42 | }; |
| 43 | } |
| 44 | |
| 45 | /// Returns the sum of two complex numbers. |
| 46 | pub fn add(self: Self, other: Self) Self { |
| 47 | return Self{ |
| 48 | .re = self.re + other.re, |
| 49 | .im = self.im + other.im, |
| 50 | }; |
| 51 | } |
| 52 | |
| 53 | /// Returns the subtraction of two complex numbers. |
| 54 | pub fn sub(self: Self, other: Self) Self { |
| 55 | return Self{ |
| 56 | .re = self.re - other.re, |
| 57 | .im = self.im - other.im, |
| 58 | }; |
| 59 | } |
| 60 | |
| 61 | /// Returns the product of two complex numbers. |
| 62 | pub fn mul(self: Self, other: Self) Self { |
| 63 | return Self{ |
| 64 | .re = self.re * other.re - self.im * other.im, |
| 65 | .im = self.im * other.re + self.re * other.im, |
| 66 | }; |
| 67 | } |
| 68 | |
| 69 | /// Returns the quotient of two complex numbers. |
| 70 | pub fn div(self: Self, other: Self) Self { |
| 71 | const re_num = self.re * other.re + self.im * other.im; |
| 72 | const im_num = self.im * other.re - self.re * other.im; |
| 73 | const den = other.re * other.re + other.im * other.im; |
| 74 | |
| 75 | return Self{ |
| 76 | .re = re_num / den, |
| 77 | .im = im_num / den, |
| 78 | }; |
| 79 | } |
| 80 | |
| 81 | /// Returns the complex conjugate of a number. |
| 82 | pub fn conjugate(self: Self) Self { |
| 83 | return Self{ |
| 84 | .re = self.re, |
| 85 | .im = -self.im, |
| 86 | }; |
| 87 | } |
| 88 | |
| 89 | /// Returns the negation of a complex number. |
| 90 | pub fn neg(self: Self) Self { |
| 91 | return Self{ |
| 92 | .re = -self.re, |
| 93 | .im = -self.im, |
| 94 | }; |
| 95 | } |
| 96 | |
| 97 | /// Returns the product of complex number and i=sqrt(-1) |
| 98 | pub fn mulbyi(self: Self) Self { |
| 99 | return Self{ |
| 100 | .re = -self.im, |
| 101 | .im = self.re, |
| 102 | }; |
| 103 | } |
| 104 | |
| 105 | /// Returns the reciprocal of a complex number. |
| 106 | pub fn reciprocal(self: Self) Self { |
| 107 | const m = self.re * self.re + self.im * self.im; |
| 108 | return Self{ |
| 109 | .re = self.re / m, |
| 110 | .im = -self.im / m, |
| 111 | }; |
| 112 | } |
| 113 | |
| 114 | /// Returns the magnitude of a complex number. |
| 115 | pub fn magnitude(self: Self) T { |
| 116 | return @sqrt(self.re * self.re + self.im * self.im); |
| 117 | } |
| 118 | |
| 119 | pub fn squaredMagnitude(self: Self) T { |
| 120 | return self.re * self.re + self.im * self.im; |
| 121 | } |
| 122 | }; |
| 123 | } |
| 124 | |
| 125 | const epsilon = 0.0001; |
| 126 | |
| 127 | test "add" { |
| 128 | const a = Complex(f32).init(5, 3); |
| 129 | const b = Complex(f32).init(2, 7); |
| 130 | const c = a.add(b); |
| 131 | |
| 132 | try testing.expect(c.re == 7 and c.im == 10); |
| 133 | } |
| 134 | |
| 135 | test "sub" { |
| 136 | const a = Complex(f32).init(5, 3); |
| 137 | const b = Complex(f32).init(2, 7); |
| 138 | const c = a.sub(b); |
| 139 | |
| 140 | try testing.expect(c.re == 3 and c.im == -4); |
| 141 | } |
| 142 | |
| 143 | test "mul" { |
| 144 | const a = Complex(f32).init(5, 3); |
| 145 | const b = Complex(f32).init(2, 7); |
| 146 | const c = a.mul(b); |
| 147 | |
| 148 | try testing.expect(c.re == -11 and c.im == 41); |
| 149 | } |
| 150 | |
| 151 | test "div" { |
| 152 | const a = Complex(f32).init(5, 3); |
| 153 | const b = Complex(f32).init(2, 7); |
| 154 | const c = a.div(b); |
| 155 | |
| 156 | try testing.expect(math.approxEqAbs(f32, c.re, @as(f32, 31) / 53, epsilon) and |
| 157 | math.approxEqAbs(f32, c.im, @as(f32, -29) / 53, epsilon)); |
| 158 | } |
| 159 | |
| 160 | test "conjugate" { |
| 161 | const a = Complex(f32).init(5, 3); |
| 162 | const c = a.conjugate(); |
| 163 | |
| 164 | try testing.expect(c.re == 5 and c.im == -3); |
| 165 | } |
| 166 | |
| 167 | test "neg" { |
| 168 | const a = Complex(f32).init(5, 3); |
| 169 | const c = a.neg(); |
| 170 | |
| 171 | try testing.expect(c.re == -5 and c.im == -3); |
| 172 | } |
| 173 | |
| 174 | test "mulbyi" { |
| 175 | const a = Complex(f32).init(5, 3); |
| 176 | const c = a.mulbyi(); |
| 177 | |
| 178 | try testing.expect(c.re == -3 and c.im == 5); |
| 179 | } |
| 180 | |
| 181 | test "reciprocal" { |
| 182 | const a = Complex(f32).init(5, 3); |
| 183 | const c = a.reciprocal(); |
| 184 | |
| 185 | try testing.expect(math.approxEqAbs(f32, c.re, @as(f32, 5) / 34, epsilon) and |
| 186 | math.approxEqAbs(f32, c.im, @as(f32, -3) / 34, epsilon)); |
| 187 | } |
| 188 | |
| 189 | test "magnitude" { |
| 190 | const a = Complex(f32).init(5, 3); |
| 191 | const c = a.magnitude(); |
| 192 | |
| 193 | try testing.expect(math.approxEqAbs(f32, c, 5.83095, epsilon)); |
| 194 | } |
| 195 | |
| 196 | test "squaredMagnitude" { |
| 197 | const a = Complex(f32).init(5, 3); |
| 198 | const c = a.squaredMagnitude(); |
| 199 | |
| 200 | try testing.expect(math.approxEqAbs(f32, c, math.pow(f32, a.magnitude(), 2), epsilon)); |
| 201 | } |
| 202 | |
| 203 | test { |
| 204 | _ = @import("complex/abs.zig"); |
| 205 | _ = @import("complex/acosh.zig"); |
| 206 | _ = @import("complex/acos.zig"); |
| 207 | _ = @import("complex/arg.zig"); |
| 208 | _ = @import("complex/asinh.zig"); |
| 209 | _ = @import("complex/asin.zig"); |
| 210 | _ = @import("complex/atanh.zig"); |
| 211 | _ = @import("complex/atan.zig"); |
| 212 | _ = @import("complex/conj.zig"); |
| 213 | _ = @import("complex/cosh.zig"); |
| 214 | _ = @import("complex/cos.zig"); |
| 215 | _ = @import("complex/exp.zig"); |
| 216 | _ = @import("complex/log.zig"); |
| 217 | _ = @import("complex/pow.zig"); |
| 218 | _ = @import("complex/proj.zig"); |
| 219 | _ = @import("complex/sinh.zig"); |
| 220 | _ = @import("complex/sin.zig"); |
| 221 | _ = @import("complex/sqrt.zig"); |
| 222 | _ = @import("complex/tanh.zig"); |
| 223 | _ = @import("complex/tan.zig"); |
| 224 | } |