1 /// Author: Aziz Köksal
2 /// License: GPL3
3 /// $(Maturity low)
4 module dil.Complex;
5 
6 import dil.Float;
7 import common;
8 
9 alias Float = dil.Float.Float;
10 
11 /// A class for working with imaginary numbers.
12 class Complex
13 {
14   Float re; /// The real part.
15   Float im; /// The imaginary part.
16   /// The length or magnitude of the vector.
17   alias mag = re;
18   /// The angle of the vector.
19   alias phi = im;
20 
21   /// Constructs an initialized Float.
22   this()
23   {
24     re = new Float();
25     im = new Float();
26   }
27 
28   /// Constructs from a Complex.
29   this(Complex c)
30   {
31     re = c.re.dup();
32     im = c.im.dup();
33   }
34 
35   /// Constructs from two Floats.
36   this(Float r, Float i=null)
37   {
38     re = r;
39     im = i ? i : new Float();
40   }
41 
42   /// Constructs from two longs.
43   this(long r, long i=0)
44   {
45     re = new Float(r);
46     im = new Float(i);
47   }
48 
49   /// Constructs from two ulongs.
50   this(ulong r, ulong i=0)
51   {
52     re = new Float(r);
53     im = new Float(i);
54   }
55 
56   /// Constructs from two longs.
57   this(double r, double i=0)
58   {
59     re = new Float(r);
60     im = new Float(i);
61   }
62 
63   /// Constructs from two strings.
64   this(cstring r, cstring i)
65   {
66     re = new Float(r);
67     im = new Float(i);
68   }
69 
70   /// Constructs from a string.
71   /// Params:
72   ///   x = Can be "a", "aj", "-ai", "a + bj", "a - bi" etc.
73   this(cstring x)
74   {
75     if (!x.length)
76       this();
77     else
78     {
79       if (x[$-1] != '\0')
80         x = x ~ '\0'; // Terminate with zero
81       this(x.ptr);
82     }
83   }
84 
85   /// Constructs a Complex from a zero-terminated string.
86   this(cchar* x)
87   {
88     set(x);
89   }
90 
91   /// Parses the string and sets the real and imaginary parts. Returns itself.
92   Complex set(cchar* x)
93   {
94     x = x && *x ? x : "0";
95 
96     cstring r_str, i_str;
97     bool i_neg;
98     auto p = x;
99     while (*p == ' ')
100       p++;
101     x = p; // Let x point to the beginning of the number.
102     if (*p == '-' || *p == '+')
103       p++; // Skip for look-behind expression below (p[-1]).
104     while (*p != 0 && *p != ' ' &&
105            (*p != '-' && *p != '+' || p[-1] == 'e'))
106       p++;
107     if (p[-1] == 'i' || p[-1] == 'j')
108       i_str = x[0..p-x]; // Only an imaginary component.
109     else
110     {
111       r_str = x[0..p-x]; // Real component.
112       while (*p == ' ') // Skip whitespace.
113         p++;
114       i_neg = *p == '-';
115       if (i_neg || *p == '+') // ±bi
116       {
117         while (*++p == ' '){} // Skip whitespace.
118         x = p; // Update beginning of the imaginary component.
119         while (*p != 0 && *p != 'i' && *p != 'j')
120           p++;
121         if (*p != 0)
122           i_str = x[0..p-x];
123       }
124     }
125 
126     re = new Float(r_str);
127     im = new Float(i_str);
128     if (i_neg)
129       im.neg();
130     return this;
131   }
132 
133   /// For convenient construction of Complex numbers.
134   static Complex opCall(Params...)(Params P)
135   {
136     return new Complex(P);
137   }
138 
139   /// Clears this number and deallocates its data.
140   void clear()
141   {
142     re.clear();
143     im.clear();
144   }
145 
146   /// Returns a deep copy of this number.
147   Complex dup()
148   {
149     return new Complex(this);
150   }
151 
152   /// Calculates z += x. Returns itself.
153   Complex opAddAssign(Complex x)
154   {
155     re += x.re;
156     im += x.im;
157     return this;
158   }
159 
160   /// Calculates z+x. Returns a new number.
161   Complex opAdd(Complex x)
162   {
163     return new Complex() += x;
164   }
165 
166   /// ditto
167   Complex opAdd(uint x)
168   {
169     auto z = new Complex();
170     z.re += x;
171     return z;
172   }
173 
174 //   /// Calculates x-z.
175 //   Complex opAdd_r(T)(T x)
176 //   {
177 //     static if (is(T == Complex))
178 //       return x.dup() + this;
179 //     else
180 //       return new Complex(x) + this;
181 //   }
182 
183   /// Calculates z -= x. Returns itself.
184   Complex opSubAssign(Complex x)
185   {
186     re -= x.re;
187     im -= x.im;
188     return this;
189   }
190 
191   /// ditto
192   Complex opSubAssign(uint x)
193   {
194     re -= x;
195     return this;
196   }
197 
198   /// Calculates z-x. Returns a new number.
199   Complex opSub(T)(T x)
200   {
201     static if (is(T == Complex) || is(T == uint))
202       return dup() -= x;
203     else
204       return dup() -= new Complex(x);
205   }
206 
207 //   /// Calculates x-z.
208 //   Complex opSub_r(T)(T x)
209 //   {
210 //     static if (is(T == Complex))
211 //       return x.dup() - this;
212 //     else
213 //       return new Complex(x) - this;
214 //   }
215 
216   /// Calculates z /= x. Returns itself.
217   Complex opDivAssign(T:Complex)(T x)
218   { // Special handling.
219     if (x.im == 0)
220       (re /= x.re),
221       (im /= x.re);
222     else
223     {
224       // auto n = x.re / x.im;
225       // auto d = x.re * n + x.im;
226       // auto r_ = re.dup();
227       // re *= n; re += im; re /= d;
228       // im *= n; im -= r_; im /= d;
229 
230       auto rx = x.re, ix = x.im;
231       // d = x.re² + x.im²
232       auto d = rx.dup().square() += ix.dup().square();
233       auto r_ = re.dup();
234       re *= rx; re += im * ix; re /= d;
235       im *= rx; im -= r_ * ix; im /= d;
236     }
237     return this;
238   }
239 
240   /// ditto
241   Complex opDivAssign(T)(T x)
242   {
243     static if (is(T == Float) || is(T == uint))
244       alias x z;
245     else
246       auto z = new Float(x);
247     re /= z;
248     im /= z;
249     return this;
250   }
251 
252   /// Calculates z/x.  Returns a new number.
253   Complex opDiv(T)(T x)
254   {
255     static if (is(T == Complex) || is(T == uint))
256       alias x z;
257     else
258       auto z = new Complex(x);
259     return dup() /= z;
260   }
261 
262   // Cannot do the following because it conflicts with opDiv.
263   // Complex opDiv_r(T)(T x)
264   // { return new Complex(x) /= this; }
265 
266   /// Calculates x/z. Returns a new number.
267   Complex opDiv_r(uint x)
268   {
269     return new Complex(cast(ulong)x) /= this;
270   }
271 
272   /// ditto
273   Complex opDiv_r(double x)
274   {
275     return new Complex(x) /= this;
276   }
277 
278   /// ditto
279   Complex opDiv_r(cstring x)
280   {
281     return new Complex(x) /= this;
282   }
283 
284   /// ditto
285   Complex opDiv_r(cchar* x)
286   {
287     return new Complex(x) /= this;
288   }
289 
290   /// Calculates z %= w. Returns itself.
291   Complex opModAssign(Complex w)
292   { // modulo(z, w) = frac(z/w) * w
293     // TODO: optimize for certain cases.
294     (this /= w).fraction() *= w;
295     return this;
296   }
297 
298   /// Calculates z %= f. Returns itself.
299   Complex opModAssign(Float f)
300   {
301     this %= new Complex(f);
302     return this;
303   }
304 
305   /// Calculates z % w. Returns a new number.
306   Complex opMod(Complex w)
307   {
308     return dup() %= w;
309   }
310 
311   /// Calculates z % f. Returns a new number.
312   Complex opMod(Float f)
313   {
314     return dup() %= f;
315   }
316 
317   /// Calculates z *= x. Returns itself.
318   Complex opMulAssign(T:Complex)(T x)
319   { // Special handling.
320     if (x.im == 0)
321       (re *= x.re),
322       (im *= x.re);
323     else
324     {
325       auto r_ = re.dup();
326       re *= x.re; re -= im*x.im;
327       im *= x.re; im += r_*=x.im;
328     }
329     return this;
330   }
331 
332   /// ditto
333   Complex opMulAssign(T)(T x)
334   {
335     static if (is(T == Float) || is(T == uint))
336       alias x z;
337     else
338       auto z = new Float(x);
339     re *= z;
340     im *= z;
341     return this;
342   }
343 
344   /// Calculates z*x. Returns a new number.
345   Complex opMul(T)(T x)
346   {
347     static if (is(T == Complex) || is(T == uint))
348       alias x z;
349     else
350       auto z = new Complex(x);
351     return dup() *= z;
352   }
353 
354 //   /// Calculates x*z.
355 //   Complex opMul_r(T)(T x)
356 //   {
357 //     static if (is(T == Complex))
358 //       return x.dup() *= this;
359 //     else
360 //       return new Complex(x) *= this;
361 //   }
362 
363   /// Compares z to x.
364   override bool opEquals(Object x)
365   {
366     if (auto f = cast(Float)x)
367       return opEquals(f);
368     if (auto c = cast(Complex)x)
369       return opEquals(c);
370     return true;
371   }
372 
373   /// Compares z to x.
374   bool opEquals(Complex x)
375   {
376     return re.equals(x.re) && im.equals(x.im);
377   }
378 
379   static string opEqualsMacro(string s)
380   {
381     return "bool opEquals(" ~ s ~ " x) { return opEquals(new Complex(x)); }";
382   }
383 
384   mixin(opEqualsMacro("long"));
385   mixin(opEqualsMacro("Float"));
386 
387   alias equals = opEquals;
388 
389   /// Returns a negated copy of this number.
390   Complex opNeg()
391   {
392     auto n = dup();
393     n.re.neg();
394     n.im.neg();
395     return n;
396   }
397 
398   /// Negates this number. Returns itself.
399   Complex neg()
400   {
401     re.neg();
402     im.neg();
403     return this;
404   }
405 
406   /// Converts this number to polar representation. Returns itself.
407   Complex polar()
408   {
409     auto phi_ = im.dup.atan2(re);
410     re.hypot(im); // r = √(re² + im²)
411     phi = phi_;   // φ = arctan(im/re)
412     return this;
413   }
414 
415   /// Converts this number to cartesian representation. Returns itself.
416   Complex cart()
417   { // Looks weird but saves temporary variables.
418     auto mag_ = mag.dup();
419     mag *= phi.dup().cos(); // re = r*cos(φ)
420     phi.sin() *= mag_;      // im = r*sin(φ)
421     return this;
422   }
423 
424   /// Calculates frac(z). Returns itself.
425   Complex fraction()
426   {
427     re.fraction();
428     im.fraction();
429     return this;
430   }
431 
432   /// Calculates √z. Returns itself.
433   Complex sqrt()
434   { // √z = √(r.e^iφ) = √(r).e^(iφ/2)
435     polar();
436     mag.sqrt();
437     phi /= 2;
438     return cart();
439   }
440 
441   /// Calculates z^w. Returns itself.
442   Complex pow(T:Complex)(T w)
443   { // z^w = e^(w*ln(z))
444     ln() *= w; // z = ln(z); z *= w
445     return exp(); // e^z
446   }
447 
448   /// Calculates z^x. Returns itself.
449   Complex pow(T)(T x)
450   { // z^x = (r.e^iφ)^x = r^x.e^(xφi)
451     polar();
452     mag.pow(x);
453     phi *= x;
454     return cart();
455   }
456 
457   /// Calculates e^z. Returns itself.
458   Complex exp()
459   { // e^z = e^(a+bi) = e^a * e^bi = e^a (cos(b) + i.sin(b))
460     re.exp(); // r = e^Re(z)  φ = Im(z)
461     return cart();
462   }
463 
464   /// Calculates ln(z). Returns itself.
465   Complex ln()
466   { // ln(z) = ln(r.e^iφ) = ln(r) + ln(e^iφ) = ln(r) + iφ
467     polar();
468     mag.ln();
469     return this;
470   }
471 
472   /// Calculates log$(SUB a+bi)(w) = ln(w)/ln(a+bi). Returns a new number.
473   Complex logz(Complex w)
474   {
475     return w.dup().ln() /= dup().ln();
476   }
477 
478   /// Conjugates this number: conj(z) = Re(z) - Im(z). Returns itself.
479   Complex conjugate()
480   {
481     im = -im;
482     return this;
483   }
484 
485   /// Returns a conjugated copy of this number.
486   Complex conjugated()
487   {
488     return dup().conjugate();
489   }
490 
491   /// Inverses this number: z = z^-1. Returns itself.
492   Complex inverse()
493   { // re/(a²+b²) - im/(a²+b²)
494     auto d = re.dup().square() += im.dup().square();
495     re /= d;
496     im /= d.neg();
497     return this;
498   }
499 
500   /// Returns an inversed copy of this number.
501   Complex inversed()
502   {
503     return dup().inverse();
504   }
505 
506   /// Returns the polar angle: φ = arctan(b/a).
507   Float arg()
508   {
509     return im.dup().atan2(re);
510   }
511 
512   /// Returns the absolute value: |z| = √(re² + im²).
513   Float abs()
514   {
515     return hypot(re, im);
516   }
517 
518   /// Returns this number as a string.
519   override string toString()
520   {
521     return toString(30).idup;
522   }
523 
524   /// Returns this number as a string.
525   char[] toString(uint precision)
526   {
527     auto im_sign = im.isNeg() ? "" : "+";
528     return re.toString(precision) ~ im_sign ~ im.toString(precision) ~ "i";
529   }
530 }
531 
532 void testComplex()
533 {
534   return; // Remove when Complex/Float is fixed.
535   scope msg = new UnittestMsg("Testing class Complex.");
536 
537   alias F = Float;
538   alias C = Complex;
539 
540   assert(C(F(3)) == F(3));
541   assert(F(99) == C(F(99)));
542   assert(-C(F(10), F(9)) == C(F(-10), F(-9)));
543   assert(C(5., 20.) / 5. == C(1., 4.));
544   assert(1. / C(5., 20.) == C(5., 20.).inverse());
545   assert(C(3L, 2L) / C(4L,-6L) == C(0., 0.5));
546   assert(C(3L, 4L).abs() == F(5));
547   assert(C(3L, 4L).conjugate() == C(3L, -4L));
548 //   assert(C(3L, 4L).pow(2) == C(-7L, 24L));
549 //   assert(C(3L, 4L).sqrt() == C(2L, 1L));
550   assert(C("3+4j") == C(3L, 4L));
551   assert(C("-4e+2j") == C(0L, -400L));
552 }