PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Math/Complex.cs

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