/Utilities/Math/Complex.cs
C# | 772 lines | 496 code | 86 blank | 190 comment | 11 complexity | aa8bafbcfffb7ea3c00cffd7e17a57b9 MD5 | raw file
1using System; 2using System.Diagnostics; 3using System.IO; 4using System.Runtime.InteropServices; 5using Delta.Utilities.Helpers; 6using NUnit.Framework; 7 8namespace Delta.Utilities.Math 9{ 10 /// <summary> 11 /// Complex number with real and imaginary parts as floats. Rarely used, 12 /// but can be useful for complex mathematical problems. 13 /// </summary> 14 [StructLayout(LayoutKind.Explicit)] 15 [DebuggerDisplay("Complex(Real={Real}, Imaginary={Imaginary})")] 16 public struct Complex : ISaveLoadBinary, IEquatable<Complex> 17 { 18 #region Constants 19 /// <summary> 20 /// Represents the size in bytes of each Complex (2 * 4 = 8 bytes). 21 /// </summary> 22 public const int DataSize = 2 * 4; 23 24 /// <summary> 25 /// Returns a Complex with all values filled to zero 26 /// </summary> 27 public static readonly Complex Zero = 28 new Complex(0, 0); 29 #endregion 30 31 #region FromSqrt (Static) 32 /// <summary> 33 /// From sqrt 34 /// </summary> 35 public static Complex FromSqrt(float setR) 36 { 37 return new Complex( 38 setR >= 0.0f 39 ? MathHelper.Sqrt(setR) 40 : 0.0f, 41 setR < 0.0f 42 ? MathHelper.Sqrt(-setR) 43 : 0.0f); 44 } 45 #endregion 46 47 #region Real (Public) 48 /// <summary> 49 /// Real 50 /// </summary> 51 [FieldOffset(0)] 52 public float Real; 53 #endregion 54 55 #region Imaginary (Public) 56 /// <summary> 57 /// Imaginary 58 /// </summary> 59 [FieldOffset(4)] 60 public float Imaginary; 61 #endregion 62 63 #region LengthSquared (Public) 64 /// <summary> 65 /// Length squared 66 /// </summary> 67 public float LengthSquared 68 { 69 get 70 { 71 return Real * Real + Imaginary * Imaginary; 72 } 73 } 74 #endregion 75 76 #region Length (Public) 77 /// <summary> 78 /// Length 79 /// </summary> 80 public float Length 81 { 82 get 83 { 84 return MathHelper.Sqrt(Real * Real + Imaginary * Imaginary); 85 } 86 } 87 #endregion 88 89 #region Argument (Public) 90 /// <summary> 91 /// Argument 92 /// </summary> 93 public float Argument 94 { 95 get 96 { 97 return MathHelper.Atan(Imaginary, Real); 98 } 99 } 100 #endregion 101 102 #region Constructors 103 /// <summary> 104 /// Create complex 105 /// </summary> 106 public Complex(float setR, float setI) 107 { 108 Real = setR; 109 Imaginary = setI; 110 } 111 112 /// <summary> 113 /// Create complex from phasor values. 114 /// </summary> 115 public Complex(float setModulus, float setArgument, bool phasorDummy) 116 { 117 Real = setModulus * MathHelper.Cos(setArgument); 118 Imaginary = setModulus * MathHelper.Sin(setArgument); 119 } 120 #endregion 121 122 #region IEquatable<Complex> Members 123 /// <summary> 124 /// Equals 125 /// </summary> 126 /// <param name="other">Other</param> 127 /// <returns>Value indicating the equality of two vectors</returns> 128 public bool Equals(Complex other) 129 { 130 return Real == other.Real && 131 Imaginary == other.Imaginary; 132 } 133 #endregion 134 135 #region ISaveLoadBinary Members 136 /// <summary> 137 /// Load real and imaginary part of this complex number from a stream. 138 /// </summary> 139 public void Load(BinaryReader reader) 140 { 141 Real = reader.ReadSingle(); 142 Imaginary = reader.ReadSingle(); 143 } 144 145 /// <summary> 146 /// Save real and imaginary part of this complex number to a stream. 147 /// </summary> 148 public void Save(BinaryWriter writer) 149 { 150 writer.Write(Real); 151 writer.Write(Imaginary); 152 } 153 #endregion 154 155 #region op_UnaryNegation (Operator) 156 /// <summary> 157 /// Op unary negation 158 /// </summary> 159 public static Complex operator -(Complex value1) 160 { 161 return new Complex(-value1.Real, -value1.Imaginary); 162 } 163 #endregion 164 165 #region op_UnaryPlus (Operator) 166 /// <summary> 167 /// Op unary plus 168 /// </summary> 169 public static Complex operator +(Complex value1) 170 { 171 return new Complex(+value1.Real, +value1.Imaginary); 172 } 173 #endregion 174 175 #region op_Addition (Operator) 176 /// <summary> 177 /// Op addition 178 /// </summary> 179 public static Complex operator +(Complex value1, Complex value2) 180 { 181 return new Complex(value1.Real + value2.Real, 182 value1.Imaginary + value2.Imaginary); 183 } 184 #endregion 185 186 #region op_Subtraction (Operator) 187 /// <summary> 188 /// Op subtraction 189 /// </summary> 190 public static Complex operator -(Complex value1, Complex value2) 191 { 192 return new Complex(value1.Real - value2.Real, 193 value1.Imaginary - value2.Imaginary); 194 } 195 #endregion 196 197 #region op_Multiply (Operator) 198 /// <summary> 199 /// Op multiply 200 /// </summary> 201 public static Complex operator *(Complex value1, Complex value2) 202 { 203 return 204 new Complex(value1.Real * value2.Real - value1.Imaginary * value2.Imaginary, 205 value1.Real * value2.Imaginary + value2.Real * value1.Imaginary); 206 } 207 208 /// <summary> 209 /// Op multiply 210 /// </summary> 211 public static Complex operator *(Complex value1, float scalar) 212 { 213 return new Complex(value1.Real * scalar, value1.Imaginary * scalar); 214 } 215 216 /// <summary> 217 /// Op multiply 218 /// </summary> 219 public static Complex operator *(float scalar, Complex value1) 220 { 221 return new Complex(value1.Real * scalar, value1.Imaginary * scalar); 222 } 223 #endregion 224 225 #region op_Division (Operator) 226 /// <summary> 227 /// Op division 228 /// </summary> 229 public static Complex operator /(Complex value1, Complex value2) 230 { 231 float invertedSquared = 1.0f / value2.LengthSquared; 232 return new Complex( 233 (value1.Real * value2.Real + value1.Imaginary * value2.Imaginary) * 234 invertedSquared, 235 (value1.Imaginary * value2.Real - value1.Real * value2.Imaginary) * 236 invertedSquared); 237 } 238 239 /// <summary> 240 /// Op division 241 /// </summary> 242 public static Complex operator /(Complex value1, float scalar) 243 { 244 float Is = 1.0f / scalar; 245 return new Complex(value1.Real * Is, value1.Imaginary * Is); 246 } 247 #endregion 248 249 #region op_Equality (Operator) 250 /// <summary> 251 /// Check for equality 252 /// </summary> 253 /// <param name="value1">Value 1</param> 254 /// <param name="value2">Value 2</param> 255 /// <returns>True if the values are equal, false otherwise</returns> 256 public static bool operator ==(Complex value1, Complex value2) 257 { 258 return value1.Real == value2.Real && 259 value1.Imaginary == value2.Imaginary; 260 } 261 #endregion 262 263 #region op_Inequality (Operator) 264 /// <summary> 265 /// Check for inequality 266 /// </summary> 267 /// <param name="value1">Value 1</param> 268 /// <param name="value2">Value 2</param> 269 /// <returns>Bool</returns> 270 public static bool operator !=(Complex value1, Complex value2) 271 { 272 return value1.Real != value2.Real || 273 value1.Imaginary != value2.Imaginary; 274 } 275 #endregion 276 277 #region Min (Public) 278 /// <summary> 279 /// Minimum 280 /// </summary> 281 public float Min() 282 { 283 return MathHelper.Min(Real, Imaginary); 284 } 285 286 /// <summary> 287 /// Minimum 288 /// </summary> 289 public void Min(Complex other) 290 { 291 Real = MathHelper.Min(Real, other.Real); 292 Imaginary = MathHelper.Min(Imaginary, other.Imaginary); 293 } 294 #endregion 295 296 #region Max (Public) 297 /// <summary> 298 /// Maximum 299 /// </summary> 300 public float Max() 301 { 302 return MathHelper.Max(Real, Imaginary); 303 } 304 305 /// <summary> 306 /// Maximum 307 /// </summary> 308 public void Max(Complex other) 309 { 310 Real = MathHelper.Max(Real, other.Real); 311 Imaginary = MathHelper.Max(Imaginary, other.Imaginary); 312 } 313 #endregion 314 315 #region Sum (Public) 316 /// <summary> 317 /// Sum 318 /// </summary> 319 public float Sum() 320 { 321 return Real + Imaginary; 322 } 323 #endregion 324 325 #region Product (Public) 326 /// <summary> 327 /// Product 328 /// </summary> 329 public float Product() 330 { 331 return Real * Imaginary; 332 } 333 #endregion 334 335 #region Conjugate (Public) 336 /// <summary> 337 /// Conjugate 338 /// </summary> 339 public Complex Conjugate() 340 { 341 return new Complex(Real, -Imaginary); 342 } 343 #endregion 344 345 #region Sqrt (Public) 346 /// <summary> 347 /// Sqrt 348 /// </summary> 349 public Complex Sqrt() 350 { 351 return new Complex(MathHelper.Pow(LengthSquared, 0.25f), 352 0.5f * Argument, true); 353 } 354 #endregion 355 356 #region ToString (Public) 357 /// <summary> 358 /// To string 359 /// </summary> 360 public override string ToString() 361 { 362 return Real.ToString() + " + i * " + Imaginary.ToString(); 363 } 364 #endregion 365 366 #region GetHashCode (Public) 367 /// <summary> 368 /// Get hash code 369 /// </summary> 370 public override int GetHashCode() 371 { 372 return Real.GetHashCode() ^ (Imaginary.GetHashCode() * 7); 373 } 374 #endregion 375 376 #region Equals (Public) 377 /// <summary> 378 /// Equals 379 /// </summary> 380 public override bool Equals(object obj) 381 { 382 if (obj is Complex) 383 { 384 return Equals((Complex)obj); 385 } 386 return base.Equals(obj); 387 } 388 #endregion 389 390 /// <summary> 391 /// Tests 392 /// </summary> 393 internal class ComplexTests 394 { 395 #region TestCreate 396 /// <summary> 397 /// Test Constructors 398 /// </summary> 399 [Test] 400 public void TestCreate() 401 { 402 // Create complex 403 Complex complex = new Complex(1.0f, 5.0f); 404 405 // Make sure the values are correct 406 Assert.NearlyEqual(complex.Real, 1.0f); 407 Assert.NearlyEqual(complex.Imaginary, 5.0f); 408 } 409 #endregion 410 411 #region Equality 412 /// <summary> 413 /// Equality 414 /// </summary> 415 [Test] 416 public void Equality() 417 { 418 Complex c1 = new Complex(1.0f, 2.0f); 419 Complex c2 = new Complex(1.0f, 2.0f); 420 Complex c3 = new Complex(1.5f, 2.0f); 421 422 Assert.True(c1 == c2); 423 Assert.False(c1 == c3); 424 } 425 #endregion 426 427 #region Inequality 428 /// <summary> 429 /// Inequality 430 /// </summary> 431 [Test] 432 public void Inequality() 433 { 434 Complex c1 = new Complex(1.0f, 2.0f); 435 Complex c2 = new Complex(1.0f, 2.0f); 436 Complex c3 = new Complex(1.5f, 2.0f); 437 438 Assert.False(c1 != c2); 439 Assert.True(c1 != c3); 440 } 441 #endregion 442 443 #region ScalarDivison 444 /// <summary> 445 /// Scalar divison 446 /// </summary> 447 [Test] 448 public void ScalarDivison() 449 { 450 Complex c1 = new Complex(6.0f, 20.0f); 451 float scalar = 2.0f; 452 Complex result = c1 / scalar; 453 454 Assert.NearlyEqual(result.Real, 3.0f); 455 Assert.NearlyEqual(result.Imaginary, 10.0f); 456 } 457 #endregion 458 459 #region ComplexDivision 460 /// <summary> 461 /// Complex division 462 /// </summary> 463 [Test] 464 public void ComplexDivision() 465 { 466 float r1 = 12.0f; 467 float i1 = 20.0f; 468 float r2 = 2.0f; 469 float i2 = 4.0f; 470 471 Complex c1 = new Complex(r1, i1); 472 Complex c2 = new Complex(r2, i2); 473 Complex result = c1 / c2; 474 475 // Source: http://de.wikipedia.org/wiki/Komplexe_Zahl#Division 476 float rResult = ((r1 * r2) + (i1 * i2)) / ((r2 * r2) + (i2 * i2)); 477 float iResult = ((i1 * r2) - (r1 * i2)) / ((r2 * r2) + (i2 * i2)); 478 479 Assert.NearlyEqual(result.Real, rResult); 480 Assert.NearlyEqual(result.Imaginary, iResult); 481 } 482 #endregion 483 484 #region ScalarMultiply 485 /// <summary> 486 /// Scalar multiplyScalar 487 /// </summary> 488 [Test] 489 public void ScalarMultiply() 490 { 491 Complex c1 = new Complex(6.0f, 20.0f); 492 float scalar = 2.0f; 493 Complex result = c1 * scalar; 494 // Test reverse too 495 Complex result2 = scalar * c1; 496 497 Assert.NearlyEqual(result.Real, 12.0f); 498 Assert.NearlyEqual(result.Imaginary, 40.0f); 499 500 Assert.NearlyEqual(result2.Real, 12.0f); 501 Assert.NearlyEqual(result2.Imaginary, 40.0f); 502 } 503 #endregion 504 505 #region ComplexMultiply 506 /// <summary> 507 /// Complex multiply 508 /// </summary> 509 [Test] 510 public void ComplexMultiply() 511 { 512 float r1 = 1.0f; 513 float i1 = 2.0f; 514 float r2 = 4.0f; 515 float i2 = 8.0f; 516 517 Complex c1 = new Complex(r1, i1); 518 Complex c2 = new Complex(r2, i2); 519 Complex result = c1 * c2; 520 521 // Source: http://de.wikipedia.org/wiki/Komplexe_Zahl#Multiplikation 522 float rResult = (r1 * r2) - (i1 * i2); 523 float iResult = (r1 * i2) + (i1 * r2); 524 525 Assert.NearlyEqual(result.Real, rResult); 526 Assert.NearlyEqual(result.Imaginary, iResult); 527 } 528 #endregion 529 530 #region ComplexSubtract 531 /// <summary> 532 /// Complex subtract 533 /// </summary> 534 [Test] 535 public void ComplexSubtract() 536 { 537 float r1 = 10.0f; 538 float i1 = 20.0f; 539 float r2 = 5.0f; 540 float i2 = 7.0f; 541 542 Complex c1 = new Complex(r1, i1); 543 Complex c2 = new Complex(r2, i2); 544 Complex result = c1 - c2; 545 546 // Source: http://de.wikipedia.org/wiki/Komplexe_Zahl#Subtraktion 547 float rResult = r1 - r2; 548 float iResult = i1 - i2; 549 550 Assert.NearlyEqual(result.Real, rResult); 551 Assert.NearlyEqual(result.Imaginary, iResult); 552 } 553 #endregion 554 555 #region ComplexAddition 556 /// <summary> 557 /// Complex addition 558 /// </summary> 559 [Test] 560 public void ComplexAddition() 561 { 562 float r1 = 10.0f; 563 float i1 = 20.0f; 564 float r2 = 5.0f; 565 float i2 = 7.0f; 566 567 Complex c1 = new Complex(r1, i1); 568 Complex c2 = new Complex(r2, i2); 569 Complex result = c1 + c2; 570 571 // Source: http://de.wikipedia.org/wiki/Komplexe_Zahl#Addition 572 float rResult = r1 + r2; 573 float iResult = i1 + i2; 574 575 Assert.NearlyEqual(result.Real, rResult); 576 Assert.NearlyEqual(result.Imaginary, iResult); 577 } 578 #endregion 579 580 #region DataSize 581 /// <summary> 582 /// Data size 583 /// </summary> 584 [Test] 585 public void DataSize() 586 { 587 int calculatedDatasize = sizeof(float) + sizeof(float); 588 Assert.Equal(calculatedDatasize, Complex.DataSize); 589 } 590 #endregion 591 592 #region ComplexMin 593 /// <summary> 594 /// Complex minimum 595 /// </summary> 596 [Test] 597 public void ComplexMin() 598 { 599 float r1 = 1.0f; 600 float i1 = 5.0f; 601 float r2 = 2.0f; 602 float i2 = 3.0f; 603 604 Complex c1 = new Complex(r1, i1); 605 Complex c2 = new Complex(r2, i2); 606 c1.Min(c2); 607 608 Assert.NearlyEqual(c1.Real, r1); 609 Assert.NearlyEqual(c1.Imaginary, i2); 610 } 611 #endregion 612 613 #region ComplexMax 614 /// <summary> 615 /// Complex maximum 616 /// </summary> 617 [Test] 618 public void ComplexMax() 619 { 620 float r1 = 1.0f; 621 float i1 = 5.0f; 622 float r2 = 2.0f; 623 float i2 = 3.0f; 624 625 Complex c1 = new Complex(r1, i1); 626 Complex c2 = new Complex(r2, i2); 627 c1.Max(c2); 628 629 Assert.NearlyEqual(c1.Real, r2); 630 Assert.NearlyEqual(c1.Imaginary, i1); 631 } 632 #endregion 633 634 #region Sum 635 /// <summary> 636 /// Complex sum 637 /// </summary> 638 [Test] 639 public void Sum() 640 { 641 float r1 = 1.0f; 642 float i1 = 5.0f; 643 Complex c1 = new Complex(r1, i1); 644 645 Assert.NearlyEqual(c1.Sum(), r1 + i1); 646 } 647 #endregion 648 649 #region Product 650 /// <summary> 651 /// Product 652 /// </summary> 653 [Test] 654 public void Product() 655 { 656 float r1 = 2.0f; 657 float i1 = 5.0f; 658 Complex c1 = new Complex(r1, i1); 659 660 Assert.NearlyEqual(c1.Product(), r1 * i1); 661 } 662 #endregion 663 664 #region LengthSquared 665 /// <summary> 666 /// Length squared 667 /// </summary> 668 [Test] 669 public void LengthSquared() 670 { 671 float r1 = 2.0f; 672 float i1 = 5.0f; 673 Complex c1 = new Complex(r1, i1); 674 675 Assert.NearlyEqual(c1.LengthSquared, (r1 * r1) + (i1 * i1)); 676 } 677 #endregion 678 679 #region Length 680 /// <summary> 681 /// Length 682 /// </summary> 683 [Test] 684 public void Length() 685 { 686 float r1 = 2.0f; 687 float i1 = 5.0f; 688 Complex c1 = new Complex(r1, i1); 689 690 Assert.NearlyEqual(c1.Length, 691 MathHelper.Sqrt((r1 * r1) + (i1 * i1))); 692 } 693 #endregion 694 695 #region FromSqrt 696 /// <summary> 697 /// From sqrt 698 /// </summary> 699 [Test] 700 public void FromSqrt() 701 { 702 float square = 4.0f; 703 Complex c1 = Complex.FromSqrt(square); 704 Complex c2 = Complex.FromSqrt(-square); 705 706 Assert.Equal(c1, new Complex(MathHelper.Sqrt(square), 0.0f)); 707 Assert.Equal(c2, new Complex(0.0f, MathHelper.Sqrt(square))); 708 } 709 #endregion 710 711 #region Argument 712 /// <summary> 713 /// Argument 714 /// </summary> 715 [Test] 716 public void Argument() 717 { 718 float r1 = 2.0f; 719 float i1 = 5.0f; 720 float result = (new Complex(r1, i1)).Argument; 721 722 Assert.NearlyEqual(result, MathHelper.Atan(i1, r1)); 723 } 724 #endregion 725 726 #region Conjugate 727 /// <summary> 728 /// Conjugate 729 /// </summary> 730 [Test] 731 public void Conjugate() 732 { 733 float r1 = 2.0f; 734 float i1 = 5.0f; 735 Complex c1 = new Complex(r1, i1).Conjugate(); 736 737 Assert.NearlyEqual(c1.Real, r1); 738 Assert.NearlyEqual(c1.Imaginary, -i1); 739 } 740 #endregion 741 742 #region Sqrt 743 /// <summary> 744 /// Sqrt 745 /// </summary> 746 [Test] 747 public void Sqrt() 748 { 749 float r1 = 2.0f; 750 float i1 = 5.0f; 751 Complex c1 = new Complex(r1, i1).Sqrt(); 752 float modulus = (float)System.Math.Pow((r1 * r1) + (i1 * i1), 0.25f); 753 float argument = 0.5f * MathHelper.Atan(i1, r1); 754 755 Assert.NearlyEqual(c1.Real, modulus * MathHelper.Cos(argument)); 756 Assert.NearlyEqual(c1.Imaginary, modulus * MathHelper.Sin(argument)); 757 } 758 #endregion 759 760 #region ToString 761 /// <summary> 762 /// To string 763 /// </summary> 764 [Test] 765 public new void ToString() 766 { 767 Assert.Equal("5 + i * 2", new Complex(5.0f, 2.0f).ToString()); 768 } 769 #endregion 770 } 771 } 772}