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 }