PageRenderTime 147ms CodeModel.GetById 60ms app.highlight 41ms RepoModel.GetById 39ms app.codeStats 1ms

/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}