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 }