//****************************************************************************************************** // ComplexNumber.cs - Gbtc // // Copyright © 2010, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/eclipse-1.0.php // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 01/25/2008 - J. Ritchie Carroll // Initial version of source generated. // 08/3/2009 - Josh L. Patterson // Updated comments. // 09/14/2009 - Stephen C. Wills // Added new header and license agreement. // 10/8/2012 - Danyelle Gilliam // Modified Header // //****************************************************************************************************** #region [ Contributor License Agreements ] /**************************************************************************\ Copyright © 2009 - J. Ritchie Carroll All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \**************************************************************************/ #endregion using GSF.Units; using System; using System.Text; namespace GSF { /// /// Represents a complex number. /// public struct ComplexNumber : IEquatable { #region [ Members ] // Fields private double? m_real; // Real value of complex number private double? m_imaginary; // Imaginary value of complex number // Polar value fields will only cache values until both exist in order // to create equivalent rectangular values private double? m_angle; // Temporary angle value of complex number private double? m_magnitude; // Temporary magnitude value of complex number #endregion #region [ Constructors ] /// /// Creates a from the given rectangular values. /// /// The real component of the . /// The imaginary component of the . public ComplexNumber(double real, double imaginary) : this() { m_real = real; m_imaginary = imaginary; } /// /// Creates a from the given polar values. /// /// The angle component, in radians, of the . /// The magnitude (or absolute value) component of the . public ComplexNumber(Angle angle, double magnitude) : this() { m_angle = angle; m_magnitude = magnitude; CalculateRectangularFromPolar(); } /// /// Creates a from the given . /// /// to be copied. public ComplexNumber(ComplexNumber z) : this() { // Make sure state of source complex number is replicated extactly m_real = z.m_real; m_imaginary = z.m_imaginary; m_angle = z.m_angle; m_magnitude = z.m_magnitude; } #endregion #region [ Properties ] /// /// Gets or sets the real component of this . /// public double Real { get { return m_real.GetValueOrDefault(); } set { m_real = value; } } /// /// Gets or sets the imaginary component of this . /// public double Imaginary { get { return m_imaginary.GetValueOrDefault(); } set { m_imaginary = value; } } /// /// Gets or sets the magnitude (a.k.a. the modulus or absolute value) of this . /// public double Magnitude { get { if (AllAssigned) { // Complex number is internally represented in rectangluar coordinates, so we return calculated magnitude double real = m_real.Value; double imaginary = m_imaginary.Value; return Math.Sqrt(real * real + imaginary * imaginary); } else if (m_magnitude.HasValue) { // Return any assigned value if magnitude can't be calculated return m_magnitude.Value; } else return double.NaN; } set { if (!m_real.HasValue || !m_imaginary.HasValue) { // Complex number is internally represented in rectangluar coordinates but these values have yet to be // fully assigned so we cache magnitude so we can calculate the real and imaginary components once we // also receive the angle value m_magnitude = value; // If all composite polar values have been received, we can calculate real and imaginary values CalculateRectangularFromPolar(); } else { // Rectangular values have already been assigned, user is simply requesting to change complex number // by updating its absolute value so we calculate a new complex number based on the updated polar // coordinates and then update the real and imaginary components ComplexNumber updatedValue = new ComplexNumber(Angle, value); m_real = updatedValue.m_real; m_imaginary = updatedValue.m_imaginary; } } } /// /// Gets or sets the (a.k.a. the argument) in radians of this . /// public Angle Angle { get { if (AllAssigned) { // Complex number is internally represented in rectangluar coordinates, so we return calculated angle return Math.Atan2(m_imaginary.Value, m_real.Value); } else if (m_angle.HasValue) { // Return any assigned value if angle can't be calculated return m_angle.Value; } else return double.NaN; } set { if (!m_real.HasValue || !m_imaginary.HasValue) { // Complex number is internally represented in rectangluar coordinates but these values have yet to be // fully assigned so we cache angle so we can calculate the real and imaginary components once we also // receive the magnitude value m_angle = value; // If all composite polar values have been received, we can calculate real and imaginary values CalculateRectangularFromPolar(); } else { // Rectangular values have already been assigned, user is simply requesting to change complex number // by updating its angle value so we calculate a new complex number based on the updated polar // coordinates and then update the real and imaginary components ComplexNumber updatedValue = new ComplexNumber(value, Magnitude); m_real = updatedValue.m_real; m_imaginary = updatedValue.m_imaginary; } } } /// /// Gets the complex conjugate of this . /// public ComplexNumber Conjugate { get { return new ComplexNumber(Real, -Imaginary); } } /// /// Gets a boolean value indicating if each composite value of the (i.e., real and imaginary) has been assigned a value. /// /// True, if all composite values have been assigned a value; otherwise, false. public bool AllAssigned { get { return m_real.HasValue && m_imaginary.HasValue; } } /// /// Gets a boolean value indicating if each composite value of the (i.e., real and imaginary) has not been assigned a value. /// /// True, if none of the composite values have been assigned a value; otherwise, false. public bool NoneAssigned { get { return !m_real.HasValue && !m_imaginary.HasValue; } } #endregion #region [ Methods ] /// /// Returns a value indicating whether this instance is equal to a specified object. /// /// An object to compare, or null. /// /// True if obj is an instance of ComplexNumber and equals the value of this instance; /// otherwise, False. /// public override bool Equals(object obj) { if (obj is ComplexNumber) return Equals((ComplexNumber)obj); return false; } /// /// Returns a value indicating whether this instance is equal to a specified Int24 value. /// /// A to compare to this instance. /// /// True if has the same value as this instance; otherwise, False. /// public bool Equals(ComplexNumber obj) { return (this == obj); } /// /// Returns the hash code for this instance. /// /// /// A 32-bit signed integer hash code. /// public override int GetHashCode() { return (Real.GetHashCode() ^ Imaginary.GetHashCode()); } /// /// Converts the numeric value of this instance to its equivalent string representation. /// /// /// The string representation of the value of this instance. /// public override string ToString() { StringBuilder image = new StringBuilder(); image.Append(Real); if (Imaginary != 0.0D) { image.Append(Imaginary > 0.0D ? " + " : " - "); image.Append(Math.Abs(Imaginary)); image.Append('i'); } return image.ToString(); } // Calculate real and imaginary components from angle and magnitude private void CalculateRectangularFromPolar() { if (m_angle.HasValue && m_magnitude.HasValue) { // All values assigned, calculate a new rectangular based complex number from its polar composite values double angle = m_angle.Value; double magnitude = m_magnitude.Value; m_real = magnitude * Math.Cos(angle); m_imaginary = magnitude * Math.Sin(angle); // Once rectangular values are available, polar values are no longer needed m_angle = null; m_magnitude = null; } } #endregion #region [ Operators ] /// /// Implicitly converts a to a . /// /// Operand. /// ComplexNumber representing the result of the operation. public static implicit operator ComplexNumber(double value) { return new ComplexNumber(value, 0.0D); } /// /// Compares the two values for equality. /// /// Left hand operand. /// Right hand operand. /// Boolean representing the result of the addition operation. public static bool operator ==(ComplexNumber value1, ComplexNumber value2) { return (value1.Real == value2.Real && value1.Imaginary == value2.Imaginary); } /// /// Compares the two values for inequality. /// /// Left hand operand. /// Right hand operand. /// Boolean representing the result of the inequality operation. public static bool operator !=(ComplexNumber value1, ComplexNumber value2) { return !(value1 == value2); } /// /// Returns the negated value. /// /// Left hand operand. /// ComplexNumber representing the result of the unary negation operation. public static ComplexNumber operator -(ComplexNumber z) { return new ComplexNumber(-z.Real, -z.Imaginary); } /// /// Returns computed sum of values. /// /// Left hand operand. /// Right hand operand. /// ComplexNumber representing the result of the addition operation. public static ComplexNumber operator +(ComplexNumber value1, ComplexNumber value2) { return new ComplexNumber(value1.Real + value2.Real, value1.Imaginary + value2.Imaginary); } /// /// Returns computed difference of values. /// /// Left hand operand. /// Right hand operand. /// ComplexNumber representing the result of the subtraction operation. public static ComplexNumber operator -(ComplexNumber value1, ComplexNumber value2) { return new ComplexNumber(value1.Real - value2.Real, value1.Imaginary - value2.Imaginary); } /// /// Returns computed product of values. /// /// Left hand operand. /// Right hand operand. /// ComplexNumber representing the result of the multiplication operation. public static ComplexNumber operator *(ComplexNumber value1, ComplexNumber value2) { double real = value1.Real * value2.Real - value1.Imaginary * value2.Imaginary; double imaginary = value1.Imaginary * value2.Real + value1.Real * value2.Imaginary; return new ComplexNumber(real, imaginary); } /// /// Returns computed division of values. /// /// Left hand operand. /// Right hand operand. /// ComplexNumber representing the result of the division operation. public static ComplexNumber operator /(ComplexNumber value1, ComplexNumber value2) { double divisor = Math.Pow(value2.Real, 2) + Math.Pow(value2.Imaginary, 2); double real = (value1.Real * value2.Real + value1.Imaginary * value2.Imaginary) / divisor; double imaginary = (value1.Imaginary * value2.Real - value1.Real * value2.Imaginary) / divisor; return new ComplexNumber(real, imaginary); } /// ///Returns specified raised to the specified power. /// ///Complex number to be raised to power . ///Power to raise . /// ComplexNumber representing the result of the operation. public static ComplexNumber Pow(ComplexNumber z, double y) { return new ComplexNumber(z.Angle * y, Math.Pow(z.Magnitude, y)); } #endregion } }