1 /++ 2 Complex numbers 3 4 Authors: Ilya Yaroshenko 5 +/ 6 module mir.complex; 7 8 import mir.math.common: optmath; 9 10 private alias CommonType(A, B) = typeof(A.init + B.init); 11 12 @optmath: 13 14 /++ 15 Generic complex number type 16 +/ 17 struct Complex(T) 18 if (is(T == float) || is(T == double) || is(T == real)) 19 { 20 import mir.internal.utility: isComplex; 21 import std.traits: isNumeric; 22 23 @optmath: 24 25 /++ 26 Real part. Default value is zero. 27 +/ 28 T re = 0; 29 /++ 30 Imaginary part. Default value is zero. 31 +/ 32 T im = 0; 33 34 /// 35 ref Complex opAssign(R)(Complex!R rhs) 36 if (!is(R == T)) 37 { 38 this.re = rhs.re; 39 this.im = rhs.im; 40 return this; 41 } 42 43 /// 44 ref Complex opAssign(F)(const F rhs) 45 if (isNumeric!F) 46 { 47 this.re = rhs; 48 this.im = 0; 49 return this; 50 } 51 52 /// 53 ref Complex opOpAssign(string op : "+", R)(Complex!R rhs) return 54 { 55 re += rhs.re; 56 im += rhs.im; 57 return this; 58 } 59 60 /// 61 ref Complex opOpAssign(string op : "-", R)(Complex!R rhs) return 62 { 63 re -= rhs.re; 64 im -= rhs.im; 65 return this; 66 } 67 68 /// 69 ref Complex opOpAssign(string op, R)(Complex!R rhs) return 70 if (op == "*" || op == "/") 71 { 72 return this = this.opBinary!op(rhs); 73 } 74 75 /// 76 ref Complex opOpAssign(string op : "+", R)(const R rhs) return 77 if (isNumeric!R) 78 { 79 re += rhs; 80 return this; 81 } 82 83 /// 84 ref Complex opOpAssign(string op : "-", R)(const R rhs) return 85 if (isNumeric!R) 86 { 87 re -= rhs; 88 return this; 89 } 90 91 /// 92 ref Complex opOpAssign(string op : "*", R)(const R rhs) return 93 if (isNumeric!R) 94 { 95 re *= rhs; 96 return this; 97 } 98 99 /// 100 ref Complex opOpAssign(string op : "/", R)(const R rhs) return 101 if (isNumeric!R) 102 { 103 re /= rhs; 104 return this; 105 } 106 107 const: 108 109 /// 110 bool opEquals(const Complex rhs) 111 { 112 return re == rhs.re && im == rhs.im; 113 } 114 115 /// 116 size_t toHash() 117 { 118 T[2] val = [re, im]; 119 return hashOf(val) ; 120 } 121 122 scope: 123 124 /// 125 bool opEquals(R)(Complex!R rhs) 126 if (!is(R == T)) 127 { 128 return re == rhs.re && im == rhs.im; 129 } 130 131 /// 132 bool opEquals(F)(const F rhs) 133 if (isNumeric!F) 134 { 135 return re == rhs && im == 0; 136 } 137 138 /// 139 Complex opUnary(string op : "+")() 140 { 141 return this; 142 } 143 144 /// 145 Complex opUnary(string op : "-")() 146 { 147 return typeof(return)(-re, -im); 148 } 149 150 /// 151 Complex!(CommonType!(T, R)) opBinary(string op : "+", R)(Complex!R rhs) 152 { 153 return typeof(return)(re + rhs.re, im + rhs.im); 154 } 155 156 /// 157 Complex!(CommonType!(T, R)) opBinary(string op : "-", R)(Complex!R rhs) 158 { 159 return typeof(return)(re - rhs.re, im - rhs.im); 160 } 161 162 /// 163 Complex!(CommonType!(T, R)) opBinary(string op : "*", R)(Complex!R rhs) 164 { 165 return typeof(return)(re * rhs.re - im * rhs.im, re * rhs.im + im * rhs.re); 166 } 167 168 /// 169 Complex!(CommonType!(T, R)) opBinary(string op : "/", R)(Complex!R rhs) 170 { 171 // TODO: use more precise algorithm 172 auto norm = rhs.re * rhs.re + rhs.im * rhs.im; 173 return typeof(return)( 174 (re * rhs.re + im * rhs.im) / norm, 175 (im * rhs.re - re * rhs.im) / norm, 176 ); 177 } 178 179 /// 180 Complex!(CommonType!(T, R)) opBinary(string op : "+", R)(const R rhs) 181 if (isNumeric!R) 182 { 183 return typeof(return)(re + rhs, im); 184 } 185 186 /// 187 Complex!(CommonType!(T, R)) opBinary(string op : "-", R)(const R rhs) 188 if (isNumeric!R) 189 { 190 return typeof(return)(re - rhs, im); 191 } 192 193 /// 194 Complex!(CommonType!(T, R)) opBinary(string op : "*", R)(const R rhs) 195 if (isNumeric!R) 196 { 197 return typeof(return)(re * rhs, im * rhs); 198 } 199 200 /// 201 Complex!(CommonType!(T, R)) opBinary(string op : "/", R)(const R rhs) 202 if (isNumeric!R) 203 { 204 return typeof(return)(re / rhs, im / rhs); 205 } 206 207 208 /// 209 Complex!(CommonType!(T, R)) opBinaryRight(string op : "+", R)(const R rhs) 210 if (isNumeric!R) 211 { 212 return typeof(return)(rhs + re, im); 213 } 214 215 /// 216 Complex!(CommonType!(T, R)) opBinaryRight(string op : "-", R)(const R rhs) 217 if (isNumeric!R) 218 { 219 return typeof(return)(rhs - re, -im); 220 } 221 222 /// 223 Complex!(CommonType!(T, R)) opBinaryRight(string op : "*", R)(const R rhs) 224 if (isNumeric!R) 225 { 226 return typeof(return)(rhs * re, rhs * im); 227 } 228 229 /// 230 Complex!(CommonType!(T, R)) opBinaryRight(string op : "/", R)(const R rhs) 231 if (isNumeric!R) 232 { 233 // TODO: use more precise algorithm 234 auto norm = this.re * this.re + this.im * this.im; 235 return typeof(return)( 236 rhs * (this.re / norm), 237 -rhs * (this.im / norm), 238 ); 239 } 240 241 /// 242 R opCast(R)() 243 if (isNumeric!R || isComplex!R) 244 { 245 static if (isNumeric!R) 246 return cast(R) re; 247 else 248 return R(re, im); 249 } 250 } 251 252 /// ditto 253 Complex!T complex(T)(const T re, const T im) 254 if (is(T == float) || is(T == double) || is(T == real)) 255 { 256 return typeof(return)(re, im); 257 } 258 259 private alias _cdouble_ = Complex!double; 260 private alias _cfloat_ = Complex!float; 261 private alias _creal_ = Complex!real; 262 263 /// 264 unittest 265 { 266 auto a = complex(1.0, 3); 267 auto b = a; 268 b.re += 3; 269 a = b; 270 assert(a == b); 271 272 a = Complex!float(5, 6); 273 assert(a == Complex!real(5, 6)); 274 275 a += b; 276 a -= b; 277 a *= b; 278 a /= b; 279 280 a = a + b; 281 a = a - b; 282 a = a * b; 283 a = a / b; 284 285 a += 2; 286 a -= 2; 287 a *= 2; 288 a /= 2; 289 290 a = a + 2; 291 a = a - 2; 292 a = a * 2; 293 a = a / 2; 294 295 a = 2 + a; 296 a = 2 - a; 297 a = 2 * a; 298 a = 2 / a; 299 300 a = -a; 301 a = +a; 302 303 assert(a != 4.0); 304 a = 4; 305 assert(a == 4); 306 assert(cast(int)a == 4); 307 assert(cast(Complex!float)a == 4); 308 309 import std.complex : StdComplex = Complex; 310 assert(cast(StdComplex!double)a == StdComplex!double(4, 0)); 311 }