1 // Written in the D programming language.
2 /*
3 NYSL Version 0.9982
4 
5 A. This software is "Everyone'sWare". It means:
6   Anybody who has this software can use it as if he/she is
7   the author.
8 
9   A-1. Freeware. No fee is required.
10   A-2. You can freely redistribute this software.
11   A-3. You can freely modify this software. And the source
12       may be used in any software with no limitation.
13   A-4. When you release a modified version to public, you
14       must publish it with your name.
15 
16 B. The author is not responsible for any kind of damages or loss
17   while using or misusing this software, which is distributed
18   "AS IS". No warranty of any kind is expressed or implied.
19   You use AT YOUR OWN RISK.
20 
21 C. Copyrighted to Kazuki KOMATSU
22 
23 D. Above three clauses are applied both to source and binary
24   form of this software.
25 */
26 module carbon.complex;
27 
28 import std.traits;
29 import std.format;
30 
31 
32 /**
33 
34 */
35 template std_complex_t(F)
36 {
37   static if(is(F == float))
38     alias std_complex_t = cfloat;
39   else static if(is(F == double))
40     alias std_complex_t = cdouble;
41   else static if(is(F == real))
42     alias std_complex_t = creal;
43 }
44 
45 
46 /**
47 
48 */
49 template complexTypeTemplate(C)
50 {
51   static if(is(C : creal))
52     alias complexTypeTemplate = std_complex_t;
53   else static if(is(C : CPX!F, alias CPX, F))
54     alias complexTypeTemplate = CPX;
55   else
56     static assert(0);
57 }
58 
59 ///
60 unittest
61 {
62     import std.complex;
63 
64     static assert(__traits(isSame, complexTypeTemplate!(Complex!float), Complex));
65     static assert(__traits(isSame, complexTypeTemplate!cfloat, std_complex_t));
66 }
67 
68 
69 /**
70 
71 */
72 enum bool isComplex(T) = !isIntegral!T && !isFloatingPoint!T && is(typeof((T t){
73     auto r = t.re;
74     typeof(r) i = t.im;
75 }));
76 
77 
78 /// ditto
79 enum bool isComplex(T, F) = isComplex!T && is(typeof(T.init.re) == F) && is(typeof(T.init.im) == F);
80 
81 
82 ///
83 unittest
84 {
85     import std.complex;
86     static assert(isComplex!(Complex!float));
87     static assert(isComplex!cfloat);
88     static assert(!isComplex!float);
89 
90     static assert(isComplex!(Complex!float, float));
91     static assert(isComplex!(cfloat, float));
92     static assert(!isComplex!(float, float));
93 
94     static assert(!isComplex!(Complex!double, float));
95     static assert(!isComplex!(cdouble, float));
96     static assert(!isComplex!(double, float));
97 }
98 
99 
100 /**
101 
102 */
103 Cpx!R cpx(alias Cpx = complex_t, R)(R re)
104 if(!isComplex!R)
105 {
106   static if(__traits(isSame, Cpx, std_complex_t))
107     return cast(typeof(return))re;
108   else
109     return typeof(return)(re, 0);
110 }
111 
112 
113 /// ditto
114 Cpx!(CommonType!(R, I)) cpx(alias Cpx = complex_t, R, I)(R re, I im)
115 if(!isComplex!R && !isComplex!I)
116 {
117   static if(__traits(isSame, Cpx, std_complex_t))
118     return cast(typeof(return))(re + im*1.0Li);
119   else
120     return typeof(return)(re, im);
121 }
122 
123 
124 /// ditto
125 Cpx!(typeof(C.init.re)) cpx(alias Cpx = complex_t, C)(C c)
126 if(isComplex!C)
127 {
128   static if(__traits(isSame, Cpx, std_complex_t))
129     return cast(typeof(return))(c.re + c.im*1.0i);
130   else
131     return typeof(return)(c.re, c.im);
132 }
133 
134 ///
135 unittest
136 {
137     complex_t!float c1 = cpx(1.0f);
138     assert(c1.re == 1);
139     assert(c1.im == 0);
140 
141     complex_t!float c2 = cpx(1.0f, 1.0f);
142     assert(c2.re == 1);
143     assert(c2.im == 1);
144 
145     complex_t!float c3 = cpx(1.0f + 1.0fi);
146     assert(c2.re == 1);
147     assert(c2.im == 1);
148 
149     cfloat c4 = cpx!std_complex_t(1.0f);
150     assert(c4.re == 1);
151     assert(c4.im == 0);
152 
153     cfloat c5 = cpx!std_complex_t(1.0f, 1.0f);
154     assert(c5.re == 1);
155     assert(c5.im == 1);
156 
157     cfloat c6 = cpx!std_complex_t(1.0f + 1.0fi);
158     assert(c6.re == 1);
159     assert(c6.im == 1);
160 }
161 
162 
163 align(1)
164 struct complex_t(T)
165 {
166     T re;
167     T im;
168 
169 
170   pure nothrow @safe @nogc
171   {
172     this(R)(R r)
173     if(!isComplex!R)
174     {
175         re = r;
176         im = 0;
177     }
178 
179 
180     this(R, I)(R r, I i)
181     {
182         re = r;
183         im = i;
184     }
185 
186 
187     this(C)(C c)
188     if(isComplex!C)
189     {
190         this.re = c.re;
191         this.im = c.im;
192     }
193 
194 
195     ref complex_t opAssign(C)(C z)
196     if(isComplex!C && isAssignable!(T, typeof(C.init.re)))
197     {
198         re = z.re;
199         im = z.im;
200         return this;
201     }
202 
203 
204     ref complex_t opAssign(R : T)(R r)
205     {
206         re = r;
207         im = 0;
208         return this;
209     }
210 
211 
212     bool opEquals(C)(C z) const
213     if(isComplex!C && is(typeof((C z){ assert(complex_t.init.re == z.re); })))
214     {
215         return re == z.re && im == z.im;
216     }
217 
218 
219     bool opEquals(R)(R r) const
220     if(!isComplex!R && is(typeof((R r){ assert(complex_t.init.re == r); })))
221     {
222         return re == r && im == 0;
223     }
224 
225 
226     complex_t opUnary(string op : "+")() const
227     {
228         return this;
229     }
230 
231 
232     complex_t opUnary(string op : "-")() const
233     {
234         return complex_t(-re, -im);
235     }
236 
237 
238     complex_t!(typeof(mixin(`complex_t.re ` ~ op ~ ` C.init.re`))) opBinary(string op, C)(C z) const
239     if((op == "+" || op == "-") && isComplex!C)
240     {
241         return mixin(`typeof(return)(re ` ~ op ~ ` z.re, im ` ~ op ~ ` z.im)`);
242     }
243 
244 
245     complex_t!(typeof(mixin(`complex_t.re ` ~ op ~ ` C.init.re`))) opBinaryRight(string op, C)(C z) const
246     if((op == "+" || op == "-") && isComplex!C)
247     {
248         return mixin(`typeof(return)(z.re ` ~ op ~ ` re, z.im ` ~ op ~ ` im)`);
249     }
250 
251 
252     complex_t!(typeof(mixin(`complex_t.re ` ~ op ~ ` C.init.re`))) opBinary(string op, C)(C z) const
253     if((op == "*") && isComplex!C)
254     {
255         return typeof(return)(re*z.re - im*z.im, im*z.re + re*z.im);
256     }
257 
258 
259     complex_t!(typeof(mixin(`complex_t.re ` ~ op ~ ` C.init.re`))) opBinaryRight(string op, C)(C z) const
260     if((op == "*") && isComplex!C)
261     {
262         return typeof(return)(z.re*re - z.im*im, z.im*re + z.re*im);
263     }
264 
265 
266     complex_t!(typeof(mixin(`complex_t.re ` ~ op ~ ` C.init.re`))) opBinary(string op, C)(C z) const
267     if((op == "/") && isComplex!C)
268     {
269         immutable zr2 = z.re^^2 + z.im^^2;
270         immutable newRe = (re * z.re + im * z.im) / zr2;
271         immutable newIm = (im * z.re - re * z.im) / zr2;
272         return typeof(return)(newRe, newIm);
273     }
274 
275 
276     complex_t!(typeof(mixin(`complex_t.re ` ~ op ~ ` C.init.re`))) opBinaryRight(string op, C)(C z) const
277     if((op == "/") && isComplex!C)
278     {
279         immutable zr2 = re^^2 + im^^2;
280         immutable newRe = (z.re * re + z.im * im) / zr2;
281         immutable newIm = (z.im * re - z.re * im) / zr2;
282         return typeof(return)(newRe, newIm);
283     }
284 
285 
286     complex_t!(typeof(mixin(`complex_t.re ` ~ op ~ ` C.init.re`))) opBinary(string op, C)(C z) const
287     if((op == "^^") && isComplex!C)
288     {
289         alias R = typeof(typeof(return).init.re);
290 
291         import std.complex;
292         auto res = Complex!R(re, im) ^^ Complex!R(z.re, z.im);
293         return typeof(return)(res.re, res.im);
294     }
295 
296 
297     complex_t!(typeof(mixin(`complex_t.re ` ~ op ~ ` C.init.re`))) opBinaryRight(string op, C)(C z) const
298     if((op == "^^") && isComplex!C)
299     {
300         alias R = typeof(typeof(return).init.re);
301 
302         import std.complex;
303         auto res = Complex!R(z.re, z.im) ^^ Complex!R(re, im);
304         return typeof(return)(res.re, res.im);
305     }
306 
307 
308     complex_t!(typeof(mixin(`complex_t.re ` ~ op ~ ` R.init`))) opBinary(string op, R)(R r) const
309     if((op == "+" || op == "-") && !isComplex!R)
310     {
311         return mixin(`typeof(return)(re ` ~ op ~ ` r, im)`);
312     }
313 
314 
315     complex_t!(typeof(mixin(`complex_t.re ` ~ op ~ ` R.init`))) opBinary(string op, R)(R r) const
316     if((op == "*" || op == "/") && !isComplex!R)
317     {
318         return mixin(`typeof(return)(re ` ~ op ~ ` r, im ` ~ op ~ ` r)`);
319     }
320 
321 
322     complex_t!(typeof(mixin(`R.init ` ~ op ~ ` complex_t.re`))) opBinaryRight(string op, R)(R r) const
323     if((op == "+" || op == "-" || op == "*") && !isComplex!R)
324     {
325       static if(op == "-")
326         return typeof(return)(r - re, -im);
327       else return mixin(`this ` ~ op ~ ` r`);
328     }
329 
330 
331     complex_t!(typeof(mixin(`R.init ` ~ op ~ ` complex_t.re`))) opBinaryRight(string op, R)(R r) const
332     if((op == "/") && !isComplex!R)
333     {
334         immutable zr2 = re^^2 + im^^2;
335         immutable newRe = r * re / zr2;
336         immutable newIm = -r * im / zr2;
337         return typeof(return)(newRe, newIm);
338     }
339 
340 
341     complex_t!(typeof(mixin(`complex_t.re ` ~ op ~ ` R.init`))) opBinary(string op, R)(R r) const
342     if((op == "^^") && !isComplex!R && isFloatingPoint!R)
343     {
344         import std.math;
345         immutable newR = this.abs() ^^ r;
346         immutable newA = this.arg() * r;
347         return typeof(return)(newR * cos(newA), newR * sin(newA));
348     }
349 
350 
351     complex_t!(typeof(mixin(`complex_t.re ` ~ op ~ ` R.init`))) opBinary(string op, R)(R r) const
352     if((op == "^^") && !isComplex!R && isIntegral!R)
353     {
354         switch(r)
355         {
356           case 0:   // 0
357             return complex_t.one;
358 
359           case 1:   // 0
360             return this;
361 
362           case 2:   // 1
363             return this * this;
364 
365           case 3:   // 2
366             return this * this * this;
367 
368           case 4:   // 2
369             immutable pow2 = this * this;
370             return pow2 * pow2;
371 
372           case 5:   // 2
373             immutable pow2 = this * this;
374             return pow2 * pow2 * this;
375 
376           case 6:   // 3
377             immutable pow2 = this * this;
378             immutable pow4 = pow2 * pow2;
379             return pow2 * pow4;
380 
381           case 7:   // 4
382             immutable pow2 = this * this;
383             immutable pow4 = pow2 * pow2;
384             return pow2 * pow4 * this;
385 
386           case 8:   // 3
387             immutable pow2 = this * this;
388             immutable pow4 = pow2 * pow2;
389             return pow4 * pow4;
390 
391           case 9:   // 4
392             immutable pow2 = this * this;
393             immutable pow4 = pow2 * pow2;
394             return pow4 * pow4 * this;
395 
396           case 10:   // 4
397             immutable pow2 = this * this;
398             immutable pow4 = pow2 * pow2;
399             return pow4 * pow4 * pow2;
400 
401           case 12:   // 4
402             immutable pow2 = this * this;
403             immutable pow4 = pow2 * pow2;
404             return pow4 * pow4 * pow4;
405 
406           case 16:  // 4
407             immutable pow2 = this * this;
408             immutable pow4 = pow2 * pow2;
409             immutable pow8 = pow4 * pow4;
410             return pow8 * pow8;
411 
412           default:
413             //return this ^^ cast(typeof(typeof(return).init.re))r;
414             complex_t cpx = this;
415             complex_t ret = 1;
416             while(r){
417                 if(r & 1) ret *= cpx;
418                 cpx *= cpx;
419                 r >>= 1;
420             }
421 
422             return ret;
423         }
424     }
425 
426 
427     complex_t!(typeof(mixin(`R.init ` ~ op ~ ` complex_t.re`))) opBinaryRight(string op, R)(R r) const
428     if((op == "^^") && !isComplex!R)
429     {
430         alias Re = typeof(typeof(return).init.re);
431 
432         import std.complex;
433         auto res = (r ^^ Complex!Re(re, im));
434         return typeof(return)(res.re, res.im);
435     }
436 
437 
438     ref complex_t opOpAssign(string op, X)(X x)
439     {
440         this = mixin(`this ` ~ op ~ ` x`);
441         return this;
442     }
443 
444 
445     T abs() const @property
446     {
447         import std.math : hypot;
448         return hypot(re, im);
449     }
450 
451 
452     T sqAbs() const @property
453     {
454         return re*re + im*im;
455     }
456 
457 
458     T arg() const @property
459     {
460         import std.math : atan2;
461         return atan2(im, re);
462     }
463 
464 
465     complex_t conj() const @property
466     {
467         return complex_t(re, -im);
468     }
469 
470 
471     complex_t sqrt() const @property
472     {
473         auto ret = this ^^ 0.5;
474         return typeof(return)(ret.re, ret.im);
475     }
476 
477 
478     static
479     complex_t fromPhase(T y)
480     {
481         import std.math : cos, sin;
482         return complex_t(cos(y), sin(y));
483     }
484   } // pure nothrow @safe @nogc
485 
486 
487     void toString(Writer, Char)(scope Writer w, FormatSpec!Char formatSpec) const
488     {
489         import std.complex;
490         Complex!T(re, im).toString(w, formatSpec);
491     }
492 
493 
494     string toString() const @property
495     {
496         import std.complex;
497         return Complex!T(re, im).toString();
498     }
499 
500 
501     static immutable complex_t zero = complex_t(0, 0);
502     static immutable complex_t one = complex_t(1, 0);
503 }
504 
505 
506 // from std.complex.d
507 @safe pure nothrow unittest
508 {
509     static import std.math;
510     assert (cpx(1.0).abs == 1.0);
511     assert (cpx(0.0, 1.0).abs == 1.0);
512     assert (cpx(1.0L, -2.0L).abs == std.math.sqrt(5.0L));
513 }
514 
515 
516 // from std.complex.d
517 @safe pure nothrow unittest
518 {
519     import std.math;
520     assert (cpx(0.0).sqAbs == 0.0);
521     assert (cpx(1.0).sqAbs == 1.0);
522     assert (cpx(0.0, 1.0).sqAbs == 1.0);
523     assert (approxEqual(cpx(1.0L, -2.0L).sqAbs, 5.0L));
524     assert (approxEqual(cpx(-3.0L, 1.0L).sqAbs, 10.0L));
525     assert (approxEqual(cpx(1.0f,-1.0f).sqAbs, 2.0f));
526 }
527 
528 
529 // from std.complex.d
530 @safe pure nothrow unittest
531 {
532     import std.math;
533     assert (cpx(1.0).arg == 0.0);
534     assert (cpx(0.0L, 1.0L).arg == PI_2);
535     assert (cpx(1.0L, 1.0L).arg == PI_4);
536 }
537 
538 
539 // from std.complex.d
540 @safe pure nothrow unittest
541 {
542     assert (cpx(1.0).conj == cpx(1.0));
543     assert (cpx(1.0, 2.0).conj == cpx(1.0, -2.0));
544 }
545 
546 
547 @safe pure nothrow unittest
548 {
549     complex_t!float c1;
550     c1 = 1;
551     assert(c1 == 1);
552     c1 = 1i;
553     assert(c1 == 1i);
554     c1 = 1+1i;
555     assert(c1 == 1+1i);
556     c1 = 10+10i;
557     assert(c1.re == 10);
558     assert(c1.im == 10);
559 }
560 
561 
562 // from std.complex.d
563 @safe pure nothrow
564 unittest
565 {
566     import std.math;
567     import std.complex;
568 
569     enum EPS = double.epsilon;
570     auto c1 = cpx(1.0 + 1.0i);
571 
572     // Check unary operations.
573     auto c2 = complex_t!double(0.5, 2.0);
574 
575     assert (c2 == +c2);
576 
577     assert ((-c2).re == -(c2.re));
578     assert ((-c2).im == -(c2.im));
579     assert (c2 == -(-c2));
580 
581     // Check complex-complex operations.
582     auto cpc = c1 + c2;
583     assert (cpc.re == c1.re + c2.re);
584     assert (cpc.im == c1.im + c2.im);
585 
586     auto cmc = c1 - c2;
587     assert (cmc.re == c1.re - c2.re);
588     assert (cmc.im == c1.im - c2.im);
589 
590     auto ctc = c1 * c2;
591     assert (approxEqual(ctc.abs, c1.abs*c2.abs, EPS));
592     assert (approxEqual(ctc.arg, c1.arg+c2.arg, EPS));
593 
594     auto cdc = c1 / c2;
595     assert (approxEqual(cdc.abs, c1.abs/c2.abs, EPS));
596     assert (approxEqual(cdc.arg, c1.arg-c2.arg, EPS));
597 
598     auto cec = c1^^c2;
599     assert (approxEqual(cec.re, 0.11524131979943839881, EPS));
600     assert (approxEqual(cec.im, 0.21870790452746026696, EPS));
601 
602     // Check complex-real operations.
603     double a = 123.456;
604 
605     auto cpr = c1 + a;
606     assert (cpr.re == c1.re + a);
607     assert (cpr.im == c1.im);
608 
609     auto cmr = c1 - a;
610     assert (cmr.re == c1.re - a);
611     assert (cmr.im == c1.im);
612 
613     auto ctr = c1 * a;
614     assert (ctr.re == c1.re*a);
615     assert (ctr.im == c1.im*a);
616 
617     auto cdr = c1 / a;
618     assert (approxEqual(cdr.abs, c1.abs/a, EPS));
619     assert (approxEqual(cdr.arg, c1.arg, EPS));
620 
621     auto cer = c1^^3.0;
622     assert (approxEqual(cer.abs, c1.abs^^3, EPS));
623     assert (approxEqual(cer.arg, c1.arg*3, EPS));
624 
625     auto rpc = a + c1;
626     assert (rpc == cpr);
627 
628     auto rmc = a - c1;
629     assert (rmc.re == a-c1.re);
630     assert (rmc.im == -c1.im);
631 
632     auto rtc = a * c1;
633     assert (rtc == ctr);
634 
635     auto rdc = a / c1;
636     assert (approxEqual(rdc.abs, a/c1.abs, EPS));
637     assert (approxEqual(rdc.arg, -c1.arg, EPS));
638 
639     rdc = a / c2;
640     assert (approxEqual(rdc.abs, a/c2.abs, EPS));
641     assert (approxEqual(rdc.arg, -c2.arg, EPS));
642 
643     auto rec1a = 1.0 ^^ c1;
644     assert(rec1a.re == 1.0);
645     assert(rec1a.im == 0.0);
646 
647     auto rec2a = 1.0 ^^ c2;
648     assert(rec2a.re == 1.0);
649     assert(rec2a.im == 0.0);
650 
651     auto rec1b = (-1.0) ^^ c1;
652     assert(approxEqual(rec1b.abs, std.math.exp(-PI * c1.im), EPS));
653     auto arg1b = rec1b.arg;
654     /* The argument _should_ be PI, but floating-point rounding error
655      * means that in fact the imaginary part is very slightly negative.
656      */
657     assert(approxEqual(arg1b, PI, EPS) || approxEqual(arg1b, -PI, EPS));
658 
659     auto rec2b = (-1.0) ^^ c2;
660     assert(approxEqual(rec2b.abs, std.math.exp(-2 * PI), EPS));
661     assert(approxEqual(rec2b.arg, PI_2, EPS));
662 
663     auto rec3a = 0.79 ^^ complex(6.8, 5.7);
664     auto rec3b = complex(0.79, 0.0) ^^ complex(6.8, 5.7);
665     assert(approxEqual(rec3a.re, rec3b.re, EPS));
666     assert(approxEqual(rec3a.im, rec3b.im, EPS));
667 
668     auto rec4a = (-0.79) ^^ complex(6.8, 5.7);
669     auto rec4b = complex(-0.79, 0.0) ^^ complex(6.8, 5.7);
670     assert(approxEqual(rec4a.re, rec4b.re, EPS));
671     assert(approxEqual(rec4a.im, rec4b.im, EPS));
672 
673     auto rer = a ^^ complex(2.0, 0.0);
674     auto rcheck = a ^^ 2.0;
675     static assert(is(typeof(rcheck) == double));
676     assert(feqrel(rer.re, rcheck) == double.mant_dig);
677     assert(isIdentical(rer.re, rcheck));
678     assert(rer.im == 0.0);
679 
680     auto rer2 = (-a) ^^ complex(2.0, 0.0);
681     rcheck = (-a) ^^ 2.0;
682     assert(feqrel(rer2.re, rcheck) == double.mant_dig);
683     assert(isIdentical(rer2.re, rcheck));
684     assert(approxEqual(rer2.im, 0.0, EPS));
685 
686     auto rer3 = (-a) ^^ complex(-2.0, 0.0);
687     rcheck = (-a) ^^ (-2.0);
688     assert(feqrel(rer3.re, rcheck) == double.mant_dig);
689     assert(isIdentical(rer3.re, rcheck));
690     assert(approxEqual(rer3.im, 0.0, EPS));
691 
692     auto rer4 = a ^^ complex(-2.0, 0.0);
693     rcheck = a ^^ (-2.0);
694     assert(feqrel(rer4.re, rcheck) == double.mant_dig);
695     assert(isIdentical(rer4.re, rcheck));
696     assert(rer4.im == 0.0);
697 
698     // Check Complex-int operations.
699     foreach (i; 0..20)
700     {
701         auto cei = c1^^i;
702         assert (approxEqual(cei.abs, c1.abs^^i, EPS));
703         // Use cos() here to deal with arguments that go outside
704         // the (-pi,pi] interval (only an issue for i>3).
705         assert (approxEqual(std.math.cos(cei.arg), std.math.cos(c1.arg*i), EPS));
706     }
707 
708     // Check operations between different complex types.
709     auto cf = complex_t!float(1.0, 1.0);
710     auto cr = complex_t!real(1.0, 1.0);
711     auto c1pcf = c1 + cf;
712     auto c1pcr = c1 + cr;
713     static assert (is(typeof(c1pcf) == complex_t!double));
714     static assert (is(typeof(c1pcr) == complex_t!real));
715     assert (c1pcf.re == c1pcr.re);
716     assert (c1pcf.im == c1pcr.im);
717 
718     auto c1c = c1;
719     auto c2c = c2;
720 
721     c1c /= c1;
722     assert(approxEqual(c1c.re, 1.0, EPS));
723     assert(approxEqual(c1c.im, 0.0, EPS));
724 
725     c1c = c1;
726     c1c /= c2;
727     assert(approxEqual(c1c.re, 0.588235, EPS));
728     assert(approxEqual(c1c.im, -0.352941, EPS));
729 
730     c2c /= c1;
731     assert(approxEqual(c2c.re, 1.25, EPS));
732     assert(approxEqual(c2c.im, 0.75, EPS));
733 
734     c2c = c2;
735     c2c /= c2;
736     assert(approxEqual(c2c.re, 1.0, EPS));
737     assert(approxEqual(c2c.im, 0.0, EPS));
738 }
739 
740 // from std.complex.d
741 @safe pure nothrow unittest
742 {
743     // Initialization
744     complex_t!double a = 1;
745     assert (a.re == 1 && a.im == 0);
746     complex_t!double b = 1.0;
747     assert (b.re == 1.0 && b.im == 0);
748     complex_t!double c = complex_t!real(1.0, 2);
749     assert (c.re == 1.0 && c.im == 2);
750 }
751 
752 // from std.complex.d
753 @safe pure nothrow unittest
754 {
755     // Assignments and comparisons
756     complex_t!double z;
757 
758     z = 1;
759     assert (z == 1);
760     assert (z.re == 1.0  &&  z.im == 0.0);
761 
762     z = 2.0;
763     assert (z == 2.0);
764     assert (z.re == 2.0  &&  z.im == 0.0);
765 
766     z = 1.0L;
767     assert (z == 1.0L);
768     assert (z.re == 1.0  &&  z.im == 0.0);
769 
770     auto w = complex_t!real(1.0, 1.0);
771     z = w;
772     assert (z == w);
773     assert (z.re == 1.0  &&  z.im == 1.0);
774 
775     auto c = complex_t!float(2.0, 2.0);
776     z = c;
777     assert (z == c);
778     assert (z.re == 2.0  &&  z.im == 2.0);
779 }