536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
422 lines
15 KiB
C#
422 lines
15 KiB
C#
// ==++==
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// ==--==
|
|
/*=========================================================================
|
|
**
|
|
** Class: Complex
|
|
**
|
|
**
|
|
** Purpose:
|
|
** This feature is intended to create Complex Number as a type
|
|
** that can be a part of the .NET framework (base class libraries).
|
|
** A complex number z is a number of the form z = x + yi, where x and y
|
|
** are real numbers, and i is the imaginary unit, with the property i2= -1.
|
|
**
|
|
**
|
|
===========================================================================*/
|
|
|
|
using System;
|
|
using System.Globalization;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Diagnostics.Contracts;
|
|
|
|
namespace System.Numerics {
|
|
|
|
#if !SILVERLIGHT
|
|
[Serializable]
|
|
#endif // !SILVERLIGHT
|
|
public struct Complex : IEquatable<Complex>, IFormattable {
|
|
|
|
// --------------SECTION: Private Data members ----------- //
|
|
|
|
private Double m_real;
|
|
private Double m_imaginary;
|
|
|
|
// ---------------SECTION: Necessary Constants ----------- //
|
|
|
|
private const Double LOG_10_INV = 0.43429448190325;
|
|
|
|
|
|
// --------------SECTION: Public Properties -------------- //
|
|
|
|
public Double Real {
|
|
get {
|
|
return m_real;
|
|
}
|
|
}
|
|
|
|
public Double Imaginary {
|
|
get {
|
|
return m_imaginary;
|
|
}
|
|
}
|
|
|
|
public Double Magnitude {
|
|
get {
|
|
return Complex.Abs(this);
|
|
}
|
|
}
|
|
|
|
public Double Phase {
|
|
get {
|
|
return Math.Atan2(m_imaginary, m_real);
|
|
}
|
|
}
|
|
|
|
// --------------SECTION: Attributes -------------- //
|
|
|
|
public static readonly Complex Zero = new Complex(0.0, 0.0);
|
|
public static readonly Complex One = new Complex(1.0, 0.0);
|
|
public static readonly Complex ImaginaryOne = new Complex(0.0, 1.0);
|
|
|
|
// --------------SECTION: Constructors and factory methods -------------- //
|
|
|
|
public Complex(Double real, Double imaginary) /* Constructor to create a complex number with rectangular co-ordinates */
|
|
{
|
|
this.m_real = real;
|
|
this.m_imaginary = imaginary;
|
|
}
|
|
|
|
public static Complex FromPolarCoordinates(Double magnitude, Double phase) /* Factory method to take polar inputs and create a Complex object */
|
|
{
|
|
return new Complex((magnitude * Math.Cos(phase)), (magnitude * Math.Sin(phase)));
|
|
}
|
|
|
|
public static Complex Negate(Complex value) {
|
|
return -value;
|
|
}
|
|
|
|
public static Complex Add(Complex left, Complex right) {
|
|
return left + right;
|
|
}
|
|
|
|
public static Complex Subtract(Complex left, Complex right) {
|
|
return left - right;
|
|
}
|
|
|
|
public static Complex Multiply(Complex left, Complex right) {
|
|
return left * right;
|
|
}
|
|
|
|
public static Complex Divide(Complex dividend, Complex divisor) {
|
|
return dividend / divisor;
|
|
}
|
|
|
|
// --------------SECTION: Arithmetic Operator(unary) Overloading -------------- //
|
|
public static Complex operator -(Complex value) /* Unary negation of a complex number */
|
|
{
|
|
|
|
return (new Complex((-value.m_real), (-value.m_imaginary)));
|
|
}
|
|
|
|
// --------------SECTION: Arithmetic Operator(binary) Overloading -------------- //
|
|
public static Complex operator +(Complex left, Complex right) {
|
|
return (new Complex((left.m_real + right.m_real), (left.m_imaginary + right.m_imaginary)));
|
|
|
|
}
|
|
|
|
public static Complex operator -(Complex left, Complex right) {
|
|
return (new Complex((left.m_real - right.m_real), (left.m_imaginary - right.m_imaginary)));
|
|
}
|
|
|
|
public static Complex operator *(Complex left, Complex right) {
|
|
// Multiplication: (a + bi)(c + di) = (ac -bd) + (bc + ad)i
|
|
Double result_Realpart = (left.m_real * right.m_real) - (left.m_imaginary * right.m_imaginary);
|
|
Double result_Imaginarypart = (left.m_imaginary * right.m_real) + (left.m_real * right.m_imaginary);
|
|
return (new Complex(result_Realpart, result_Imaginarypart));
|
|
}
|
|
|
|
public static Complex operator /(Complex left, Complex right) {
|
|
// Division : Smith's formula.
|
|
double a = left.m_real;
|
|
double b = left.m_imaginary;
|
|
double c = right.m_real;
|
|
double d = right.m_imaginary;
|
|
|
|
if (Math.Abs(d) < Math.Abs(c)) {
|
|
double doc = d / c;
|
|
return new Complex((a + b * doc) / (c + d * doc), (b - a * doc) / (c + d * doc));
|
|
} else {
|
|
double cod = c / d;
|
|
return new Complex((b + a * cod) / (d + c * cod), (-a + b * cod) / (d + c * cod));
|
|
}
|
|
}
|
|
|
|
|
|
// --------------SECTION: Other arithmetic operations -------------- //
|
|
|
|
public static Double Abs(Complex value) {
|
|
|
|
if(Double.IsInfinity(value.m_real) || Double.IsInfinity(value.m_imaginary)) {
|
|
return double.PositiveInfinity;
|
|
}
|
|
|
|
// |value| == sqrt(a^2 + b^2)
|
|
// sqrt(a^2 + b^2) == a/a * sqrt(a^2 + b^2) = a * sqrt(a^2/a^2 + b^2/a^2)
|
|
// Using the above we can factor out the square of the larger component to dodge overflow.
|
|
|
|
|
|
double c = Math.Abs(value.m_real);
|
|
double d = Math.Abs(value.m_imaginary);
|
|
|
|
if (c > d) {
|
|
double r = d / c;
|
|
return c * Math.Sqrt(1.0 + r * r);
|
|
} else if (d == 0.0) {
|
|
return c; // c is either 0.0 or NaN
|
|
} else {
|
|
double r = c / d;
|
|
return d * Math.Sqrt(1.0 + r * r);
|
|
}
|
|
}
|
|
public static Complex Conjugate(Complex value) {
|
|
// Conjugate of a Complex number: the conjugate of x+i*y is x-i*y
|
|
|
|
return (new Complex(value.m_real, (-value.m_imaginary)));
|
|
|
|
}
|
|
public static Complex Reciprocal(Complex value) {
|
|
// Reciprocal of a Complex number : the reciprocal of x+i*y is 1/(x+i*y)
|
|
if ((value.m_real == 0) && (value.m_imaginary == 0)) {
|
|
return Complex.Zero;
|
|
}
|
|
|
|
return Complex.One / value;
|
|
}
|
|
|
|
// --------------SECTION: Comparison Operator(binary) Overloading -------------- //
|
|
|
|
public static bool operator ==(Complex left, Complex right) {
|
|
return ((left.m_real == right.m_real) && (left.m_imaginary == right.m_imaginary));
|
|
|
|
|
|
}
|
|
public static bool operator !=(Complex left, Complex right) {
|
|
return ((left.m_real != right.m_real) || (left.m_imaginary != right.m_imaginary));
|
|
|
|
}
|
|
|
|
// --------------SECTION: Comparison operations (methods implementing IEquatable<ComplexNumber>,IComparable<ComplexNumber>) -------------- //
|
|
|
|
public override bool Equals(object obj) {
|
|
if (!(obj is Complex)) return false;
|
|
return this == ((Complex)obj);
|
|
}
|
|
public bool Equals(Complex value) {
|
|
return ((this.m_real.Equals(value.m_real)) && (this.m_imaginary.Equals(value.m_imaginary)));
|
|
|
|
}
|
|
|
|
// --------------SECTION: Type-casting basic numeric data-types to ComplexNumber -------------- //
|
|
|
|
public static implicit operator Complex(Int16 value) {
|
|
return (new Complex(value, 0.0));
|
|
}
|
|
public static implicit operator Complex(Int32 value) {
|
|
return (new Complex(value, 0.0));
|
|
}
|
|
public static implicit operator Complex(Int64 value) {
|
|
return (new Complex(value, 0.0));
|
|
}
|
|
[CLSCompliant(false)]
|
|
public static implicit operator Complex(UInt16 value) {
|
|
return (new Complex(value, 0.0));
|
|
}
|
|
[CLSCompliant(false)]
|
|
public static implicit operator Complex(UInt32 value) {
|
|
return (new Complex(value, 0.0));
|
|
}
|
|
[CLSCompliant(false)]
|
|
public static implicit operator Complex(UInt64 value) {
|
|
return (new Complex(value, 0.0));
|
|
}
|
|
[CLSCompliant(false)]
|
|
public static implicit operator Complex(SByte value) {
|
|
return (new Complex(value, 0.0));
|
|
}
|
|
public static implicit operator Complex(Byte value) {
|
|
return (new Complex(value, 0.0));
|
|
}
|
|
public static implicit operator Complex(Single value) {
|
|
return (new Complex(value, 0.0));
|
|
}
|
|
public static implicit operator Complex(Double value) {
|
|
return (new Complex(value, 0.0));
|
|
}
|
|
public static explicit operator Complex(BigInteger value) {
|
|
return (new Complex((Double)value, 0.0));
|
|
}
|
|
public static explicit operator Complex(Decimal value) {
|
|
return (new Complex((Double)value, 0.0));
|
|
}
|
|
|
|
|
|
// --------------SECTION: Formattig/Parsing options -------------- //
|
|
|
|
public override String ToString() {
|
|
return (String.Format(CultureInfo.CurrentCulture, "({0}, {1})", this.m_real, this.m_imaginary));
|
|
}
|
|
|
|
public String ToString(String format) {
|
|
return (String.Format(CultureInfo.CurrentCulture, "({0}, {1})", this.m_real.ToString(format, CultureInfo.CurrentCulture), this.m_imaginary.ToString(format, CultureInfo.CurrentCulture)));
|
|
}
|
|
|
|
public String ToString(IFormatProvider provider) {
|
|
return (String.Format(provider, "({0}, {1})", this.m_real, this.m_imaginary));
|
|
}
|
|
|
|
public String ToString(String format, IFormatProvider provider) {
|
|
return (String.Format(provider, "({0}, {1})", this.m_real.ToString(format, provider), this.m_imaginary.ToString(format, provider)));
|
|
}
|
|
|
|
|
|
public override Int32 GetHashCode() {
|
|
Int32 n1 = 99999997;
|
|
Int32 hash_real = this.m_real.GetHashCode() % n1;
|
|
Int32 hash_imaginary = this.m_imaginary.GetHashCode();
|
|
Int32 final_hashcode = hash_real ^ hash_imaginary;
|
|
return (final_hashcode);
|
|
}
|
|
|
|
|
|
|
|
// --------------SECTION: Trigonometric operations (methods implementing ITrigonometric) -------------- //
|
|
|
|
public static Complex Sin(Complex value) {
|
|
double a = value.m_real;
|
|
double b = value.m_imaginary;
|
|
return new Complex(Math.Sin(a) * Math.Cosh(b), Math.Cos(a) * Math.Sinh(b));
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sinh", Justification = "Microsoft: Existing Name")]
|
|
public static Complex Sinh(Complex value) /* Hyperbolic sin */
|
|
{
|
|
double a = value.m_real;
|
|
double b = value.m_imaginary;
|
|
return new Complex(Math.Sinh(a) * Math.Cos(b), Math.Cosh(a) * Math.Sin(b));
|
|
|
|
}
|
|
public static Complex Asin(Complex value) /* Arcsin */
|
|
{
|
|
if ((value.m_imaginary == 0 && value.m_real < 0) || value.m_imaginary > 0)
|
|
{
|
|
return -Asin(-value);
|
|
}
|
|
return (-ImaginaryOne) * Log(ImaginaryOne * value + Sqrt(One - value * value));
|
|
}
|
|
|
|
public static Complex Cos(Complex value) {
|
|
double a = value.m_real;
|
|
double b = value.m_imaginary;
|
|
return new Complex(Math.Cos(a) * Math.Cosh(b), - (Math.Sin(a) * Math.Sinh(b)));
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cosh", Justification = "Microsoft: Existing Name")]
|
|
public static Complex Cosh(Complex value) /* Hyperbolic cos */
|
|
{
|
|
double a = value.m_real;
|
|
double b = value.m_imaginary;
|
|
return new Complex(Math.Cosh(a) * Math.Cos(b), Math.Sinh(a) * Math.Sin(b));
|
|
}
|
|
public static Complex Acos(Complex value) /* Arccos */
|
|
{
|
|
if ((value.m_imaginary == 0 && value.m_real > 0) || value.m_imaginary < 0)
|
|
{
|
|
return System.Math.PI - Acos(-value);
|
|
}
|
|
return (-ImaginaryOne) * Log(value + ImaginaryOne*Sqrt(One - (value * value)));
|
|
|
|
}
|
|
public static Complex Tan(Complex value) {
|
|
return (Sin(value) / Cos(value));
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Tanh", Justification = "Microsoft: Existing Name")]
|
|
public static Complex Tanh(Complex value) /* Hyperbolic tan */
|
|
{
|
|
return (Sinh(value) / Cosh(value));
|
|
}
|
|
public static Complex Atan(Complex value) /* Arctan */
|
|
{
|
|
Complex Two = new Complex(2.0, 0.0);
|
|
return (ImaginaryOne / Two) * (Log(One - ImaginaryOne * value) - Log(One + ImaginaryOne * value));
|
|
}
|
|
|
|
// --------------SECTION: Other numerical functions -------------- //
|
|
|
|
public static Complex Log(Complex value) /* Log of the complex number value to the base of 'e' */
|
|
{
|
|
return (new Complex((Math.Log(Abs(value))), (Math.Atan2(value.m_imaginary, value.m_real))));
|
|
|
|
}
|
|
public static Complex Log(Complex value, Double baseValue) /* Log of the complex number to a the base of a double */
|
|
{
|
|
return (Log(value) / Log(baseValue));
|
|
}
|
|
public static Complex Log10(Complex value) /* Log to the base of 10 of the complex number */
|
|
{
|
|
|
|
Complex temp_log = Log(value);
|
|
return (Scale(temp_log, (Double)LOG_10_INV));
|
|
|
|
}
|
|
public static Complex Exp(Complex value) /* The complex number raised to e */
|
|
{
|
|
Double temp_factor = Math.Exp(value.m_real);
|
|
Double result_re = temp_factor * Math.Cos(value.m_imaginary);
|
|
Double result_im = temp_factor * Math.Sin(value.m_imaginary);
|
|
return (new Complex(result_re, result_im));
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sqrt", Justification = "Microsoft: Existing Name")]
|
|
public static Complex Sqrt(Complex value) /* Square root ot the complex number */
|
|
{
|
|
return Complex.FromPolarCoordinates(Math.Sqrt(value.Magnitude), value.Phase / 2.0);
|
|
}
|
|
|
|
public static Complex Pow(Complex value, Complex power) /* A complex number raised to another complex number */
|
|
{
|
|
|
|
if (power == Complex.Zero) {
|
|
return Complex.One;
|
|
}
|
|
|
|
if (value == Complex.Zero) {
|
|
return Complex.Zero;
|
|
}
|
|
|
|
double a = value.m_real;
|
|
double b = value.m_imaginary;
|
|
double c = power.m_real;
|
|
double d = power.m_imaginary;
|
|
|
|
double rho = Complex.Abs(value);
|
|
double theta = Math.Atan2(b, a);
|
|
double newRho = c * theta + d * Math.Log(rho);
|
|
|
|
double t = Math.Pow(rho, c) * Math.Pow(Math.E, -d * theta);
|
|
|
|
return new Complex(t * Math.Cos(newRho), t * Math.Sin(newRho));
|
|
}
|
|
|
|
public static Complex Pow(Complex value, Double power) // A complex number raised to a real number
|
|
{
|
|
return Pow(value, new Complex(power, 0));
|
|
}
|
|
|
|
|
|
|
|
//--------------- SECTION: Private member functions for internal use -----------------------------------//
|
|
|
|
private static Complex Scale(Complex value, Double factor) {
|
|
|
|
Double result_re = factor * value.m_real;
|
|
Double result_im = factor * value.m_imaginary;
|
|
return (new Complex(result_re, result_im));
|
|
}
|
|
|
|
}
|
|
}
|