From 511b872aedfe9dfb370bb85dc574c2951b0c8d1f Mon Sep 17 00:00:00 2001 From: Marcus Cuda Date: Wed, 4 Nov 2009 20:17:31 +0800 Subject: [PATCH] added complex32 type --- src/Numerics/Complex.cs | 28 +- src/Numerics/Complex32.cs | 1383 +++++++++++++++++ src/Numerics/GlobalizationHelper.cs | 34 + src/Numerics/Numerics.csproj | 1 + src/Numerics/Precision.cs | 77 +- src/UnitTests/AssertHelpers.cs | 22 + .../Complex32Test.TextHandling.cs | 284 ++++ src/UnitTests/ComplexTests/Complex32Test.cs | 572 +++++++ src/UnitTests/ComplexTests/ComplexTest.cs | 10 +- src/UnitTests/PrecisionTest.cs | 2 +- src/UnitTests/UnitTests.csproj | 2 + 11 files changed, 2397 insertions(+), 18 deletions(-) create mode 100644 src/Numerics/Complex32.cs create mode 100644 src/UnitTests/ComplexTests/Complex32Test.TextHandling.cs create mode 100644 src/UnitTests/ComplexTests/Complex32Test.cs diff --git a/src/Numerics/Complex.cs b/src/Numerics/Complex.cs index 71ebd61b..51d3a9b1 100644 --- a/src/Numerics/Complex.cs +++ b/src/Numerics/Complex.cs @@ -1279,7 +1279,17 @@ namespace MathNet.Numerics #region Conversion /// - /// Explicit conversion of a real decimal to a real Complex. + /// Explicit conversion of a Complex32 to a Complex. + /// + /// The decimal value to convert. + /// The result of the conversion. + public static implicit operator Complex(Complex32 value) + { + return new Complex(value.Real, value.Imaginary); + } + + /// + /// Explicit conversion of a real decimal to a Complex. /// /// The decimal value to convert. /// The result of the conversion. @@ -1289,7 +1299,7 @@ namespace MathNet.Numerics } /// - /// Implicit conversion of a real byte to a real Complex. + /// Implicit conversion of a real byte to a Complex. /// /// The byte value to convert. /// The result of the conversion. @@ -1299,7 +1309,7 @@ namespace MathNet.Numerics } /// - /// Implicit conversion of a real short to a real Complex. + /// Implicit conversion of a real short to a Complex. /// /// The short value to convert. /// The result of the conversion. @@ -1309,7 +1319,7 @@ namespace MathNet.Numerics } /// - /// Implicit conversion of a real int to a real Complex. + /// Implicit conversion of a real int to a Complex. /// /// The int value to convert. /// The result of the conversion. @@ -1319,7 +1329,7 @@ namespace MathNet.Numerics } /// - /// Implicit conversion of a real long to a real Complex. + /// Implicit conversion of a real long to a Complex. /// /// The long value to convert. /// The result of the conversion. @@ -1329,7 +1339,7 @@ namespace MathNet.Numerics } /// - /// Implicit conversion of a real uint to a real Complex. + /// Implicit conversion of a real uint to a Complex. /// /// The uint value to convert. /// The result of the conversion. @@ -1339,7 +1349,7 @@ namespace MathNet.Numerics } /// - /// Implicit conversion of a real ulong to a real Complex. + /// Implicit conversion of a real ulong to a Complex. /// /// The ulong value to convert. /// The result of the conversion. @@ -1349,7 +1359,7 @@ namespace MathNet.Numerics } /// - /// Implicit conversion of a real float to a real Complex. + /// Implicit conversion of a real float to a Complex. /// /// The float value to convert. /// The result of the conversion. @@ -1359,7 +1369,7 @@ namespace MathNet.Numerics } /// - /// Implicit conversion of a real double to a real Complex. + /// Implicit conversion of a real double to a Complex. /// /// The double value to convert. /// The result of the conversion. diff --git a/src/Numerics/Complex32.cs b/src/Numerics/Complex32.cs new file mode 100644 index 00000000..0afdb618 --- /dev/null +++ b/src/Numerics/Complex32.cs @@ -0,0 +1,1383 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://mathnet.opensourcedotnet.info +// +// Copyright (c) 2009 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +namespace MathNet.Numerics +{ + using System; + using System.Collections.Generic; + using System.Runtime.InteropServices; + using System.Text; + using Properties; + + /// + /// 32-bit Complex32 numbers class. + /// + /// + /// + /// The class Complex32 provides all elementary operations + /// on complex numbers. All the operators +, -, + /// *, /, ==, != are defined in the + /// canonical way. Additional complex trigonometric functions such + /// as , ... + /// are also provided. Note that the Complex32 structures + /// has two special constant values and + /// . + /// + /// + /// In order to avoid possible ambiguities resulting from a + /// Complex32(float, float) constructor, the static methods + /// and + /// are provided instead. + /// + /// + /// + /// Complex32 x = Complex32.FromRealImaginary(1d, 2d); + /// Complex32 y = Complex32.FromModulusArgument(1d, Math.Pi); + /// Complex32 z = (x + y) / (x - y); + /// + /// + /// + /// For mathematical details about complex numbers, please + /// have a look at the + /// Wikipedia + /// + /// + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Complex32 : IFormattable, IEquatable, IPrecisionSupport + { + #region fields + + /// + /// Represents imaginary unit number. + /// + private static readonly Complex32 _i = new Complex32(0, 1); + + /// + /// Represents a infinite complex number + /// + private static readonly Complex32 _infinity = new Complex32(float.PositiveInfinity, float.PositiveInfinity); + + /// + /// Represents not-a-number. + /// + private static readonly Complex32 _nan = new Complex32(float.NaN, float.NaN); + + /// + /// Representing the one value. + /// + private static readonly Complex32 _one = new Complex32(1.0f, 0.0f); + + /// + /// Representing the zero value. + /// + private static readonly Complex32 _zero = new Complex32(0.0f, 0.0f); + + /// + /// The real component of the complex number. + /// + private readonly float _real; + + /// + /// The imaginary component of the complex number. + /// + private readonly float _imag; + + #endregion fields + + #region Constructor + + /// + /// Initializes a new instance of the Complex32 structure with the given real + /// and imaginary parts. + /// + /// + /// The value for the real component. + /// + /// + /// The value for the imaginary component. + /// + public Complex32(float real, float imaginary) + { + _real = real; + _imag = imaginary; + } + + #endregion + + #region Properties + + /// + /// Gets a value representing the infinity value. This field is constant. + /// + /// The infinity. + /// + /// The semantic associated to this value is a Complex32 of + /// infinite real and imaginary part. If you need more formal complex + /// number handling (according to the Riemann Sphere and the extended + /// complex plane C*, or using directed infinity) please check out the + /// alternative Math.NET symbolics packages instead. + /// + /// A value representing the infinity value. + public static Complex32 Infinity + { + get { return _infinity; } + } + + /// + /// Gets a value representing not-a-number. This field is constant. + /// + /// A value representing not-a-number. + public static Complex32 NaN + { + get { return _nan; } + } + + /// + /// Gets a value representing the imaginary unit number. This field is constant. + /// + /// A value representing the imaginary unit number. + public static Complex32 ImaginaryOne + { + get { return _i; } + } + + /// + /// Gets a value representing the zero value. This field is constant. + /// + /// A value representing the zero value. + public static Complex32 Zero + { + get { return new Complex32(0.0f, 0.0f); } + } + + /// + /// Gets a value representing the 1 value. This field is constant. + /// + /// A value representing the 1 value. + public static Complex32 One + { + get { return _one; } + } + + #endregion Properties + + /// + /// Gets the real component of the complex number. + /// + /// The real component of the complex number. + public float Real + { + get { return _real; } + } + + /// + /// Gets the real imaginary component of the complex number. + /// + /// The real imaginary component of the complex number. + public float Imaginary + { + get { return _imag; } + } + + /// + /// Gets a value indicating whether the Complex32 is zero. + /// + /// true if this instance is zero; otherwise, false. + public bool IsZero + { + get { return _real == 0.0f && _imag == 0.0f; } + } + + /// + /// Gets a value indicating whether the Complex32 is one. + /// + /// true if this instance is one; otherwise, false. + public bool IsOne + { + get { return _real == 1.0f && _imag == 0.0f; } + } + + /// + /// Gets a value indicating whether the Complex32 is the imaginary unit. + /// + /// true if this instance is ImaginaryOne; otherwise, false. + public bool IsImaginaryOne + { + get { return _real == 0.0f && _imag == 1.0f; } + } + + /// + /// Gets a value indicating whether the provided Complex32evaluates + /// to a value that is not a number. + /// + /// + /// true if this instance is ; otherwise, + /// false. + /// + public bool IsNaN + { + get { return float.IsNaN(_real) || float.IsNaN(_imag); } + } + + /// + /// Gets a value indicating whether the provided Complex32 evaluates to an + /// infinite value. + /// + /// + /// true if this instance is infinite; otherwise, false. + /// + /// + /// True if it either evaluates to a complex infinity + /// or to a directed infinity. + /// + public bool IsInfinity + { + get { return float.IsInfinity(_real) || float.IsInfinity(_imag); } + } + + /// + /// Gets a value indicating whether the provided Complex32 is real. + /// + /// true if this instance is a real number; otherwise, false. + public bool IsReal + { + get { return _imag == 0.0f; } + } + + /// + /// Gets a value indicating whether the provided Complex32 is real and not negative, that is >= 0. + /// + /// + /// true if this instance is real nonnegative number; otherwise, false. + /// + public bool IsRealNonNegative + { + get { return _imag == 0.0f && _real >= 0; } + } + + /// + /// Gets the conjugate of this Complex32. + /// + /// + /// The semantic of setting the conjugate is such that + /// + /// // a, b of type Complex32 + /// a.Conjugate = b; + /// + /// is equivalent to + /// + /// // a, b of type Complex32 + /// a = b.Conjugate + /// + /// + public Complex32 Conjugate + { + get { return new Complex32(_real, -_imag); } + } + + /// + /// Gets the magnitude or modulus of this Complex32. + /// + /// + public float Magnitude + { + get { return (float)Math.Sqrt((_real * _real) + (_imag * _imag)); } + } + + /// + /// Gets the squared magnitude of this Complex32. + /// + /// + public float MagnitudeSquared + { + get { return (_real * _real) + (_imag * _imag); } + } + + /// + /// Gets phase or argument of this Complex32. + /// + /// + /// Phase always returns a value bigger than negative Pi and + /// smaller or equal to Pi. If this Complex32 is zero, the Complex32 + /// is assumed to be positive real with an argument of zero. + /// + public float Phase + { + get + { + if (IsReal && _real < 0) + { + return (float)Math.PI; + } + + return IsRealNonNegative ? 0.0f : (float)Math.Atan2(_imag, _real); + } + } + + /// + /// Gets the unity of this complex (same argument, but on the unit circle; exp(I*arg)) + /// + public Complex32 Sign + { + get + { + if (float.IsPositiveInfinity(_real) && float.IsPositiveInfinity(_imag)) + { + return new Complex32((float)Constants.Sqrt1Over2, (float)Constants.Sqrt1Over2); + } + + if (float.IsPositiveInfinity(_real) && float.IsNegativeInfinity(_imag)) + { + return new Complex32((float)Constants.Sqrt1Over2, -(float)Constants.Sqrt1Over2); + } + + if (float.IsNegativeInfinity(_real) && float.IsPositiveInfinity(_imag)) + { + return new Complex32(-(float)Constants.Sqrt1Over2, -(float)Constants.Sqrt1Over2); + } + + if (float.IsNegativeInfinity(_real) && float.IsNegativeInfinity(_imag)) + { + return new Complex32(-(float)Constants.Sqrt1Over2, (float)Constants.Sqrt1Over2); + } + + // don't replace this with "Magnitude"! + var mod = SpecialFunctions.Hypotenuse(_real, _imag); + if (mod == 0.0f) + { + return Zero; + } + + return new Complex32((float)(_real / mod), (float)(_imag / mod)); + } + } + + #region Exponential Functions + + /// + /// Exponential of this Complex32 (exp(x), E^x). + /// + /// + /// The exponential of this complex number. + /// + public Complex32 Exponential() + { + var exp = (float)Math.Exp(_real); + if (IsReal) + { + return new Complex32(exp, 0.0f); + } + + return new Complex32(exp * (float)Trig.Cosine(_imag), exp * (float)Trig.Sine(_imag)); + } + + /// + /// Natural Logarithm of this Complex32 (Base E). + /// + /// + /// The natural logarithm of this complex number. + /// + public Complex32 NaturalLogarithm() + { + if (IsRealNonNegative) + { + return new Complex32((float)Math.Log(_real), 0.0f); + } + + return new Complex32(0.5f * (float)Math.Log(MagnitudeSquared), Phase); + } + + /// + /// Raise this Complex32 to the given value. + /// + /// + /// The exponent. + /// + /// + /// The complex number raised to the given exponent. + /// + public Complex32 Power(Complex32 exponent) + { + if (IsZero) + { + if (exponent.IsZero) + { + return One; + } + + if (exponent.Real > 0.0f) + { + return Zero; + } + + if (exponent.Real < 0) + { + if (exponent.Imaginary == 0.0f) + { + return new Complex32(float.PositiveInfinity, 0.0f); + } + + return new Complex32(float.PositiveInfinity, float.PositiveInfinity); + } + + return NaN; + } + + return (exponent * NaturalLogarithm()).Exponential(); + } + + /// + /// Raise this Complex32 to the inverse of the given value. + /// + /// + /// The root exponent. + /// + /// + /// The complex raised to the inverse of the given exponent. + /// + public Complex32 Root(Complex32 rootExponent) + { + return Power(1 / rootExponent); + } + + /// + /// The Square (power 2) of this Complex32 + /// + /// + /// The square of this complex number. + /// + public Complex32 Square() + { + if (IsReal) + { + return new Complex32(_real * _real, 0.0f); + } + + return new Complex32((_real * _real) - (_imag * _imag), 2 * _real * _imag); + } + + /// + /// The Square Root (power 1/2) of this Complex32 + /// + /// + /// The square root of this complex number. + /// + public Complex32 SquareRoot() + { + if (IsRealNonNegative) + { + return new Complex32((float)Math.Sqrt(_real), 0.0f); + } + + Complex32 result; + + var absReal = Math.Abs(Real); + var absImag = Math.Abs(Imaginary); + double w; + if (absReal >= absImag) + { + var ratio = Imaginary / Real; + w = Math.Sqrt(absReal) * Math.Sqrt(0.5 * (1.0f + Math.Sqrt(1.0f + (ratio * ratio)))); + } + else + { + var ratio = Real / Imaginary; + w = Math.Sqrt(absImag) * Math.Sqrt(0.5 * (Math.Abs(ratio) + Math.Sqrt(1.0f + (ratio * ratio)))); + } + + if (Real >= 0.0f) + { + result = new Complex32((float)w, (float)(Imaginary / (2.0f * w))); + } + else if (Imaginary >= 0.0f) + { + result = new Complex32((float)(absImag / (2.0 * w)), (float)w); + } + else + { + result = new Complex32((float)(absImag / (2.0 * w)), (float)-w); + } + + return result; + } + + #endregion + + #region Static Initializers + + /// + /// Constructs a Complex32 from its real + /// and imaginary parts. + /// + /// + /// The value for the real component. + /// + /// + /// The value for the imaginary component. + /// + /// + /// A new Complex32 with the given values. + /// + public static Complex32 WithRealImaginary(float real, float imaginary) + { + return new Complex32(real, imaginary); + } + + /// + /// Constructs a Complex32 from its modulus and + /// argument. + /// + /// + /// Must be non-negative. + /// + /// + /// Real number. + /// + /// + /// A new Complex32 from the given values. + /// + public static Complex32 WithModulusArgument(float modulus, float argument) + { + if (modulus < 0.0f) + { + throw new ArgumentOutOfRangeException("modulus", modulus, Resources.ArgumentNotNegative); + } + + return new Complex32(modulus * (float)Math.Cos(argument), modulus * (float)Math.Sin(argument)); + } + + #endregion + + #region IFormattable Members + + /// + /// A string representation of this complex number. + /// + /// + /// The string representation of this complex number. + /// + public override string ToString() + { + return ToString(null, null); + } + + /// + /// A string representation of this complex number. + /// + /// + /// The string representation of this complex number formatted as specified by the + /// format string. + /// + /// + /// A format specification. + /// + public string ToString(string format) + { + return ToString(format, null); + } + + /// + /// A string representation of this complex number. + /// + /// + /// The string representation of this complex number formatted as specified by the + /// format provider. + /// + /// + /// An that supplies culture-specific formatting information. + /// + public string ToString(IFormatProvider formatProvider) + { + return ToString(null, formatProvider); + } + + /// + /// A string representation of this complex number. + /// + /// + /// The string representation of this complex number formatted as specified by the + /// format string and format provider. + /// + /// + /// if the n, is not a number. + /// + /// + /// if s, is . + /// + /// + /// A format specification. + /// + /// + /// An that supplies culture-specific formatting information. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + var numberFormatInfo = formatProvider.GetNumberFormatInfo(); + + if (IsNaN) + { + return numberFormatInfo.NaNSymbol; + } + + if (IsInfinity) + { + return numberFormatInfo.PositiveInfinitySymbol; + } + + var ret = new StringBuilder(); + + if (_real != 0.0f) + { + ret.Append(_real.ToString(format, formatProvider)); + } + + if (_imag != 0.0f) + { + if (_real != 0.0f) + { + if (_imag < 0) + { + ret.Append(" "); + } + else + { + ret.Append(" + "); + } + } + + ret.Append(_imag.ToString(format, formatProvider)).Append("i"); + } + + if (ret.Length == 0) + { + ret.Append(0.0f.ToString(format, formatProvider)); + } + + return ret.ToString(); + } + + #endregion + + #region IEquatable Members + + /// + /// Checks if two complex numbers are equal. Two complex numbers are equal if their + /// corresponding real and imaginary components are equal. + /// + /// + /// Returns true if the two objects are the same object, or if their corresponding + /// real and imaginary components are equal, false otherwise. + /// + /// + /// The complex number to compare to with. + /// + public bool Equals(Complex32 other) + { + if (IsNaN || other.IsNaN) + { + return false; + } + + if (IsInfinity && other.IsInfinity) + { + return true; + } + + return _real.AlmostEqual(other._real) && _imag.AlmostEqual(other._imag); + } + + /// + /// The hash code for the complex number. + /// + /// + /// The hash code of the complex number. + /// + /// + /// The hash code is calculated as + /// System.Math.Exp(ComplexMath.Absolute(complexNumber)). + /// + public override int GetHashCode() + { + return _real.GetHashCode() ^ (-_imag.GetHashCode()); + } + + /// + /// Checks if two complex numbers are equal. Two complex numbers are equal if their + /// corresponding real and imaginary components are equal. + /// + /// + /// Returns true if the two objects are the same object, or if their corresponding + /// real and imaginary components are equal, false otherwise. + /// + /// + /// The complex number to compare to with. + /// + public override bool Equals(object obj) + { + return (obj is Complex32) && Equals((Complex32)obj); + } + + #endregion + + #region Operators + + /// + /// Equality test. + /// + /// One of complex numbers to compare. + /// The other complex numbers to compare. + /// true if the real and imaginary components of the two complex numbers are equal; false otherwise. + public static bool operator ==(Complex32 complex1, Complex32 complex2) + { + return complex1.Equals(complex2); + } + + /// + /// Inequality test. + /// + /// One of complex numbers to compare. + /// The other complex numbers to compare. + /// true if the real or imaginary components of the two complex numbers are not equal; false otherwise. + public static bool operator !=(Complex32 complex1, Complex32 complex2) + { + return !complex1.Equals(complex2); + } + + /// + /// Unary addition. + /// + /// The complex number to operate on. + /// Returns the same complex number. + public static Complex32 operator +(Complex32 summand) + { + return summand; + } + + /// + /// Unary minus. + /// + /// The complex number to operate on. + /// The negated value of the . + public static Complex32 operator -(Complex32 subtrahend) + { + return new Complex32(-subtrahend._real, -subtrahend._imag); + } + + /// Addition operator. Adds two complex numbers together. + /// The result of the addition. + /// One of the complex numbers to add. + /// The other complex numbers to add. + public static Complex32 operator +(Complex32 summand1, Complex32 summand2) + { + return new Complex32(summand1._real + summand2._real, summand1._imag + summand2._imag); + } + + /// Subtraction operator. Subtracts two complex numbers. + /// The result of the subtraction. + /// The complex number to subtract from. + /// The complex number to subtract. + public static Complex32 operator -(Complex32 minuend, Complex32 subtrahend) + { + return new Complex32(minuend._real - subtrahend._real, minuend._imag - subtrahend._imag); + } + + /// Addition operator. Adds a complex number and float together. + /// The result of the addition. + /// The complex numbers to add. + /// The float value to add. + public static Complex32 operator +(Complex32 summand1, float summand2) + { + return new Complex32(summand1._real + summand2, summand1._imag); + } + + /// Subtraction operator. Subtracts float value from a complex value. + /// The result of the subtraction. + /// The complex number to subtract from. + /// The float value to subtract. + public static Complex32 operator -(Complex32 minuend, float subtrahend) + { + return new Complex32(minuend._real - subtrahend, minuend._imag); + } + + /// Addition operator. Adds a complex number and float together. + /// The result of the addition. + /// The float value to add. + /// The complex numbers to add. + public static Complex32 operator +(float summand1, Complex32 summand2) + { + return new Complex32(summand2._real + summand1, summand2._imag); + } + + /// Subtraction operator. Subtracts complex value from a float value. + /// The result of the subtraction. + /// The float vale to subtract from. + /// The complex value to subtract. + public static Complex32 operator -(float minuend, Complex32 subtrahend) + { + return new Complex32(minuend - subtrahend._real, -subtrahend._imag); + } + + /// Multiplication operator. Multiplies two complex numbers. + /// The result of the multiplication. + /// One of the complex numbers to multiply. + /// The other complex number to multiply. + public static Complex32 operator *(Complex32 multiplicand, Complex32 multiplier) + { + return new Complex32( + (multiplicand._real * multiplier._real) - (multiplicand._imag * multiplier._imag), + (multiplicand._real * multiplier._imag) + (multiplicand._imag * multiplier._real)); + } + + /// Multiplication operator. Multiplies a complex number with a float value. + /// The result of the multiplication. + /// The float value to multiply. + /// The complex number to multiply. + public static Complex32 operator *(float multiplicand, Complex32 multiplier) + { + return new Complex32(multiplier._real * multiplicand, multiplier._imag * multiplicand); + } + + /// Multiplication operator. Multiplies a complex number with a float value. + /// The result of the multiplication. + /// The complex number to multiply. + /// The float value to multiply. + public static Complex32 operator *(Complex32 multiplicand, float multiplier) + { + return new Complex32(multiplicand._real * multiplier, multiplicand._imag * multiplier); + } + + /// Division operator. Divides a complex number by another. + /// The result of the division. + /// The dividend. + /// The divisor. + public static Complex32 operator /(Complex32 dividend, Complex32 divisor) + { + if (divisor.IsZero) + { + return Infinity; + } + + var modSquared = divisor.MagnitudeSquared; + return new Complex32( + ((dividend._real * divisor._real) + (dividend._imag * divisor._imag)) / modSquared, + ((dividend._imag * divisor._real) - (dividend._real * divisor._imag)) / modSquared); + } + + /// Division operator. Divides a float value by a complex number. + /// The result of the division. + /// The dividend. + /// The divisor. + public static Complex32 operator /(float dividend, Complex32 divisor) + { + if (divisor.IsZero) + { + return Infinity; + } + + var zmod = divisor.MagnitudeSquared; + return new Complex32(dividend * divisor._real / zmod, -dividend * divisor._imag / zmod); + } + + /// Division operator. Divides a complex number by a float value. + /// The result of the division. + /// The dividend. + /// The divisor. + public static Complex32 operator /(Complex32 dividend, float divisor) + { + if (divisor == 0.0f) + { + return Infinity; + } + + return new Complex32(dividend._real / divisor, dividend._imag / divisor); + } + + /// + /// Unary addition. + /// + /// + /// Returns the same complex number. + /// + public Complex32 Plus() + { + return this; + } + + /// + /// Unary minus. + /// + /// + /// The negated value of this complex number. + /// + public Complex32 Negate() + { + return -this; + } + + /// + /// Adds a complex number to this one. + /// + /// + /// The result of the addition. + /// + /// + /// The other complex number to add. + /// + public Complex32 Add(Complex32 other) + { + return this + other; + } + + /// + /// Subtracts a complex number from this one. + /// + /// + /// The result of the subtraction. + /// + /// + /// The other complex number to subtract from this one. + /// + public Complex32 Subtract(Complex32 other) + { + return this - other; + } + + /// + /// Multiplies this complex number with this one. + /// + /// + /// The result of the multiplication. + /// + /// + /// The complex number to multiply. + /// + public Complex32 Multiply(Complex32 multiplier) + { + return this * multiplier; + } + + /// + /// Divides this complex number by another. + /// + /// + /// The result of the division. + /// + /// + /// The divisor. + /// + public Complex32 Divide(Complex32 divisor) + { + return this / divisor; + } + + #endregion + + #region IPrecisionSupport + + /// + /// Returns a Norm of a value of this type, which is appropriate for measuring how + /// close this value is to zero. + /// + /// + /// A norm of this value. + /// + double IPrecisionSupport.Norm() + { + return MagnitudeSquared; + } + + /// + /// Returns a Norm of the difference of two values of this type, which is + /// appropriate for measuring how close together these two values are. + /// + /// + /// The value to compare with. + /// + /// + /// A norm of the difference between this and the other value. + /// + double IPrecisionSupport.NormOfDifference(Complex32 otherValue) + { + return (this - otherValue).MagnitudeSquared; + } + + #endregion + + #region Parse Functions + + /// + /// Creates a complex number based on a string. The string can be in the + /// following formats (without the quotes): 'n', 'ni', 'n +/- ni', + /// 'ni +/- n', 'n,n', 'n,ni,' '(n,n)', or '(n,ni)', where n is a float. + /// + /// + /// A complex number containing the value specified by the given string. + /// + /// + /// The string to parse. + /// + public static Complex32 Parse(string value) + { + return Parse(value, null); + } + + /// + /// Creates a complex number based on a string. The string can be in the + /// following formats (without the quotes): 'n', 'ni', 'n +/- ni', + /// 'ni +/- n', 'n,n', 'n,ni,' '(n,n)', or '(n,ni)', where n is a float. + /// + /// + /// A complex number containing the value specified by the given string. + /// + /// + /// the string to parse. + /// + /// + /// An that supplies culture-specific + /// formatting information. + /// + public static Complex32 Parse(string value, IFormatProvider formatProvider) + { + if (value == null) + { + throw new ArgumentNullException(value); + } + + value = value.Trim(); + if (value.Length == 0) + { + throw new FormatException(); + } + + // strip out parens + if (value.StartsWith("(", StringComparison.Ordinal)) + { + if (!value.EndsWith(")", StringComparison.Ordinal)) + { + throw new FormatException(); + } + + value = value.Substring(1, value.Length - 2).Trim(); + } + + // keywords + var numberFormatInfo = formatProvider.GetNumberFormatInfo(); + var textInfo = formatProvider.GetTextInfo(); + var keywords = + new[] + { + textInfo.ListSeparator, numberFormatInfo.NaNSymbol, + numberFormatInfo.NegativeInfinitySymbol, numberFormatInfo.PositiveInfinitySymbol, + "+", "-", "i", "j" + }; + + // lexing + var tokens = new LinkedList(); + GlobalizationHelper.Tokenize(tokens.AddFirst(value), keywords, 0); + var token = tokens.First; + + // parse the left part + bool isLeftPartImaginary; + var leftPart = ParsePart(ref token, out isLeftPartImaginary, formatProvider); + if (token == null) + { + return isLeftPartImaginary ? new Complex32(0, leftPart) : new Complex32(leftPart, 0); + } + + // parse the right part + if (token.Value == textInfo.ListSeparator) + { + // format: real,imag + token = token.Next; + + if (isLeftPartImaginary) + { + // left must not contain 'i', right doesn't matter. + throw new FormatException(); + } + + bool isRightPartImaginary; + var rightPart = ParsePart(ref token, out isRightPartImaginary, formatProvider); + + return new Complex32(leftPart, rightPart); + } + else + { + // format: real + imag + bool isRightPartImaginary; + var rightPart = ParsePart(ref token, out isRightPartImaginary, formatProvider); + + if (!(isLeftPartImaginary ^ isRightPartImaginary)) + { + // either left or right part must contain 'i', but not both. + throw new FormatException(); + } + + return isLeftPartImaginary ? new Complex32(rightPart, leftPart) : new Complex32(leftPart, rightPart); + } + } + + /// + /// Parse a part (real or complex) from a complex number. + /// + /// Start Token. + /// Is set to true if the part identified itself as being imaginary. + /// + /// An that supplies culture-specific + /// formatting information. + /// + /// Resulting part as float. + /// + private static float ParsePart(ref LinkedListNode token, out bool imaginary, IFormatProvider format) + { + imaginary = false; + if (token == null) + { + throw new FormatException(); + } + + // handle prefix modifiers + if (token.Value == "+") + { + token = token.Next; + + if (token == null) + { + throw new FormatException(); + } + } + + bool negative = false; + if (token.Value == "-") + { + negative = true; + token = token.Next; + + if (token == null) + { + throw new FormatException(); + } + } + + // handle prefix imaginary symbol + if (String.Compare(token.Value, "i", StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(token.Value, "j", StringComparison.OrdinalIgnoreCase) == 0) + { + imaginary = true; + token = token.Next; + + if (token == null) + { + return negative ? -1 : 1; + } + } + + var value = GlobalizationHelper.ParseSingle(ref token, format.GetCultureInfo()); + + // handle suffix imaginary symbol + if (token != null && (String.Compare(token.Value, "i", StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(token.Value, "j", StringComparison.OrdinalIgnoreCase) == 0)) + { + if (imaginary) + { + // only one time allowed: either prefix or suffix, or neither. + throw new FormatException(); + } + + imaginary = true; + token = token.Next; + } + + return negative ? -value : value; + } + + /// + /// Converts the string representation of a complex number to a single-precision complex number equivalent. + /// A return value indicates whether the conversion succeeded or failed. + /// + /// + /// A string containing a complex number to convert. + /// + /// + /// The parsed value. + /// + /// + /// If the conversion succeeds, the result will contain a complex number equivalent to value. + /// Otherwise the result will contain complex32.Zero. This parameter is passed uninitialized + /// + public static bool TryParse(string value, out Complex32 result) + { + return TryParse(value, null, out result); + } + + /// + /// Converts the string representation of a complex number to single-precision complex number equivalent. + /// A return value indicates whether the conversion succeeded or failed. + /// + /// + /// A string containing a complex number to convert. + /// + /// + /// An that supplies culture-specific formatting information about value. + /// + /// + /// The parsed value. + /// + /// + /// If the conversion succeeds, the result will contain a complex number equivalent to value. + /// Otherwise the result will contain complex32.Zero. This parameter is passed uninitialized + /// + public static bool TryParse(string value, IFormatProvider formatProvider, out Complex32 result) + { + bool ret; + try + { + result = Parse(value, formatProvider); + ret = true; + } + catch (ArgumentNullException) + { + result = _zero; + ret = false; + } + catch (FormatException) + { + result = _zero; + ret = false; + } + + return ret; + } + + #endregion + + #region Conversion + /// + /// Explicit conversion of a real decimal to a Complex32. + /// + /// The decimal value to convert. + /// The result of the conversion. + public static explicit operator Complex32(decimal value) + { + return new Complex32((float)value, 0.0f); + } + + /// + /// Explicit conversion of a Complex to a Complex32. + /// + /// The decimal value to convert. + /// The result of the conversion. + public static explicit operator Complex32(Complex value) + { + return new Complex32((float)value.Real, (float)value.Imaginary); + } + + /// + /// Implicit conversion of a real byte to a Complex32. + /// + /// The byte value to convert. + /// The result of the conversion. + public static implicit operator Complex32(byte value) + { + return new Complex32(value, 0.0f); + } + + /// + /// Implicit conversion of a real short to a Complex32. + /// + /// The short value to convert. + /// The result of the conversion. + public static implicit operator Complex32(short value) + { + return new Complex32(value, 0.0f); + } + + /// + /// Implicit conversion of a real int to a Complex32. + /// + /// The int value to convert. + /// The result of the conversion. + public static implicit operator Complex32(int value) + { + return new Complex32(value, 0.0f); + } + + /// + /// Implicit conversion of a real long to a Complex32. + /// + /// The long value to convert. + /// The result of the conversion. + public static implicit operator Complex32(long value) + { + return new Complex32(value, 0.0f); + } + + /// + /// Implicit conversion of a real uint to a Complex32. + /// + /// The uint value to convert. + /// The result of the conversion. + public static implicit operator Complex32(uint value) + { + return new Complex32(value, 0.0f); + } + + /// + /// Implicit conversion of a real ulong to a Complex32. + /// + /// The ulong value to convert. + /// The result of the conversion. + public static implicit operator Complex32(ulong value) + { + return new Complex32(value, 0.0f); + } + + /// + /// Implicit conversion of a real float to a Complex32. + /// + /// The float value to convert. + /// The result of the conversion. + public static implicit operator Complex32(float value) + { + return new Complex32(value, 0.0f); + } + + /// + /// Implicit conversion of a real double to a Complex32. + /// + /// The double value to convert. + /// The result of the conversion. + public static explicit operator Complex32(double value) + { + return new Complex32((float)value, 0.0f); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Numerics/GlobalizationHelper.cs b/src/Numerics/GlobalizationHelper.cs index e619d709..3f01362c 100644 --- a/src/Numerics/GlobalizationHelper.cs +++ b/src/Numerics/GlobalizationHelper.cs @@ -158,5 +158,39 @@ namespace MathNet.Numerics token = token.Next; return value; } + + /// + /// Globalized Parsing: Parse a float number + /// + /// First token of the number. + /// Culture Info. + /// The parsed float number using the given culture information. + /// + internal static float ParseSingle(ref LinkedListNode token, CultureInfo culture) + { + // in case the + and - in scientific notation are separated, join them back together. + if (token.Value.EndsWith("e", true, culture)) + { + if (token.Next == null || token.Next.Next == null) + { + throw new FormatException(); + } + + token.Value = token.Value + token.Next.Value + token.Next.Next.Value; + + var list = token.List; + list.Remove(token.Next.Next); + list.Remove(token.Next); + } + + float value; + if (!Single.TryParse(token.Value, NumberStyles.Any, culture, out value)) + { + throw new FormatException(); + } + + token = token.Next; + return value; + } } } \ No newline at end of file diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 25f1d94f..d9810795 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -72,6 +72,7 @@ + diff --git a/src/Numerics/Precision.cs b/src/Numerics/Precision.cs index 3e2c6726..7af2db1d 100644 --- a/src/Numerics/Precision.cs +++ b/src/Numerics/Precision.cs @@ -95,7 +95,10 @@ namespace MathNet.Numerics private static readonly int _numberOfDecimalPlacesForFloats; /// Value representing 10 * 2^(-52) - private static readonly double _defaultRelativeAccuracy = _doubleMachinePrecision * 10; + private static readonly double _defaultDoubleRelativeAccuracy = _doubleMachinePrecision * 10; + + /// Value representing 10 * 2^(-52) + private static readonly float _defaultSingleRelativeAccuracy = (float)(_singleMachinePrecision * 10); #endregion @@ -691,7 +694,19 @@ namespace MathNet.Numerics public static bool AlmostEqual(this double a, double b) { double diff = a - b; - return AlmostEqualWithError(a, b, diff, _defaultRelativeAccuracy); + return AlmostEqualWithError(a, b, diff, _defaultDoubleRelativeAccuracy); + } + + /// + /// Checks whether two real numbers are almost equal. + /// + /// The first number + /// The second number + /// true if the two values differ by no more than 10 * 2^(-52); false otherwise. + public static bool AlmostEqual(this float a, float b) + { + double diff = a - b; + return AlmostEqualWithError(a, b, diff, _defaultSingleRelativeAccuracy); } /// @@ -705,7 +720,7 @@ namespace MathNet.Numerics where T : IPrecisionSupport { double diff = a.NormOfDifference(b); - return AlmostEqualWithError(a.Norm(), b.Norm(), diff, _defaultRelativeAccuracy); + return AlmostEqualWithError(a.Norm(), b.Norm(), diff, _defaultDoubleRelativeAccuracy); } /// @@ -995,6 +1010,62 @@ namespace MathNet.Numerics return AlmostEqualWithRelativeDecimalPlaces(a, b, decimalPlaces); } + /// + /// Compares two floats and determines if they are equal to within the specified number of decimal places or not. If the numbers + /// are very close to zero an absolute difference is compared, otherwise the relative difference is compared. + /// + /// + /// + /// The values are equal if the difference between the two numbers is smaller than 10^(-numberOfDecimalPlaces). We divide by + /// two so that we have half the range on each side of the numbers, e.g. if == 2, then 0.01 will equal between + /// 0.005 and 0.015, but not 0.02 and not 0.00 + /// + /// + /// The first value. + /// The second value. + /// The number of decimal places. + /// if both doubles are equal to each other within the specified number of decimal places; otherwise . + /// + /// Thrown if is smaller than zero. + /// + public static bool AlmostEqualInDecimalPlaces(this float a, float b, int decimalPlaces) + { + if (decimalPlaces <= 0) + { + // Can't have a negative number of decimal places + throw new ArgumentOutOfRangeException("decimalPlaces"); + } + + // If A or B are a NAN, return false. NANs are equal to nothing, + // not even themselves. + if (double.IsNaN(a) || double.IsNaN(b)) + { + return false; + } + + // If A or B are infinity (positive or negative) then + // only return true if they are exactly equal to each other - + // that is, if they are both infinities of the same sign. + if (double.IsInfinity(a) || double.IsInfinity(b)) + { + return a == b; + } + + if (Math.Abs(a) < _doubleMachinePrecision || Math.Abs(b) < _doubleMachinePrecision) + { + return AlmostEqualWithAbsoluteDecimalPlaces(a, b, decimalPlaces); + } + + // If both numbers are equal, get out now. This should remove the possibility of both numbers being zero + // and any problems associated with that. + if (a.Equals(b)) + { + return true; + } + + return AlmostEqualWithRelativeDecimalPlaces(a, b, decimalPlaces); + } + /// /// Compares two doubles and determines if they are equal to within the specified number of decimal places or not. /// diff --git a/src/UnitTests/AssertHelpers.cs b/src/UnitTests/AssertHelpers.cs index 05f5b971..4088d5eb 100644 --- a/src/UnitTests/AssertHelpers.cs +++ b/src/UnitTests/AssertHelpers.cs @@ -80,6 +80,28 @@ namespace MathNet.Numerics.UnitTests } } + + /// + /// Asserts that the expected value and the actual value are equal up to a certain number of decimal places. + /// + /// The expected value. + /// The actual value. + /// The number of decimal places to agree on. + public static void AlmostEqual(Complex32 expected, Complex32 actual, int decimalPlaces) + { + bool pass = expected.Real.AlmostEqualInDecimalPlaces(actual.Real, decimalPlaces); + if (!pass) + { + Assert.Fail("Real components are not equal within {0} places. Expected:{1}; Actual:{2}", decimalPlaces, expected.Real, actual.Real); + } + + pass = expected.Imaginary.AlmostEqualInDecimalPlaces(actual.Imaginary, decimalPlaces); + if (!pass) + { + Assert.Fail("Imaginary components are not equal within {0} places. Expected:{1}; Actual:{2}", decimalPlaces, expected.Imaginary, actual.Imaginary); + } + } + /// /// Asserts that the expected value and the actual value are equal up to a certain /// maximum error. diff --git a/src/UnitTests/ComplexTests/Complex32Test.TextHandling.cs b/src/UnitTests/ComplexTests/Complex32Test.TextHandling.cs new file mode 100644 index 00000000..f6f168b8 --- /dev/null +++ b/src/UnitTests/ComplexTests/Complex32Test.TextHandling.cs @@ -0,0 +1,284 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://mathnet.opensourcedotnet.info +// +// Copyright (c) 2009 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +namespace MathNet.Numerics.UnitTests.ComplexTests +{ + using System; + using System.Globalization; + using MbUnit.Framework; + + [TestFixture] + public class Complex32TextHandlingTest + { + [Test] + [Row(1, -2, "1 -2i")] + [Row(1, 2, "1 + 2i")] + [Row(1, 0, "1")] + [Row(0, -2, "-2i")] + [Row(0, 2, "2i")] + [Row(0, 2, "2i")] + [Row(0, 0, "0")] + [Row(Double.NaN, Double.NaN, "{1}")] + [Row(Double.NaN, 0, "{1}")] + [Row(0, Double.NaN, "{1}")] + [Row(Double.PositiveInfinity, Double.PositiveInfinity, "{2}")] + [Row(1.1, 0, "1{0}1")] + [Row(-1.1, 0, "-1{0}1")] + [Row(0, 1.1, "1{0}1i")] + [Row(0, -1.1, "-1{0}1i")] + [Row(1.1, 1.1, "1{0}1 + 1{0}1i")] + public void CanFormatComplexToString(float real, float imag, string expected) + { + var numberFormat = NumberFormatInfo.CurrentInfo; + var a = new Complex32(real, imag); + Assert.AreEqual( + String.Format( + expected, + numberFormat.NumberDecimalSeparator, + numberFormat.NaNSymbol, + numberFormat.PositiveInfinitySymbol), + a.ToString()); + } + + [Test] + [MultipleAsserts] + [Row("en-US", "NaN", "Infinity", "1.1")] + [Row("tr-TR", "NaN", "Infinity", "1,1")] + [Row("de-DE", "n. def.", "+unendlich", "1,1")] + [Row("de-CH", "n. def.", "+unendlich", "1.1")] + [Row("he-IL", "לא מספר", "אינסוף חיובי", "1.1")] + public void CanFormatComplexToStringWithCulture( + string cultureName, string nan, string infinity, string number) + { + var provider = CultureInfo.GetCultureInfo(cultureName); + Assert.AreEqual(nan, Complex32.NaN.ToString(provider)); + Assert.AreEqual(infinity, Complex32.Infinity.ToString(provider)); + Assert.AreEqual("0", Complex32.Zero.ToString(provider)); + Assert.AreEqual(String.Format("{0}", number), new Complex32(1.1f, 0.0f).ToString(provider)); + Assert.AreEqual(String.Format("-{0}", number), new Complex32(-1.1f, 0f).ToString(provider)); + Assert.AreEqual(String.Format("-{0}i", number), new Complex32(0.0f, -1.1f).ToString(provider)); + Assert.AreEqual(String.Format("{0}i", number), new Complex32(0.0f, 1.1f).ToString(provider)); + Assert.AreEqual(String.Format("{0} + {0}i", number), new Complex32(1.1f, 1.1f).ToString(provider)); + } + + [Test] + [MultipleAsserts] + public void CanFormatComplexToStringWithFormat() + { + Assert.AreEqual("0", String.Format("{0:G}", Complex32.Zero)); + Assert.AreEqual("1 + 2i", String.Format("{0:G}", new Complex32(1, 2))); + Assert.AreEqual("001 + 002i", String.Format("{0:000;minus 000;zero}", new Complex32(1, 2))); + Assert.AreEqual("minus 002i", String.Format("{0:000;minus 000;zero}", new Complex32(0, -2))); + Assert.AreEqual("zero", String.Format("{0:000;minus 000;zero}", Complex32.Zero)); + + Assert.AreEqual("0", Complex32.Zero.ToString("G")); + Assert.AreEqual("1 + 2i", new Complex32(1, 2).ToString("G")); + Assert.AreEqual("001 + 002i", new Complex32(1, 2).ToString("#000;minus 000;zero")); + Assert.AreEqual("minus 002i", new Complex32(0, -2).ToString("#000;minus 000;zero")); + Assert.AreEqual("zero", Complex32.Zero.ToString("#000;minus 000;zero")); + } + + [Test] + [MultipleAsserts] + public void CanFormatComplexToStringWithFormatInvariant() + { + var culture = CultureInfo.InvariantCulture; + + Assert.AreEqual("NaN", String.Format(culture, "{0:.000}", Complex32.NaN)); + Assert.AreEqual(".000", String.Format(culture, "{0:.000}", Complex32.Zero)); + Assert.AreEqual("1.100", String.Format(culture, "{0:.000}", new Complex32(1.1f, 0.0f))); + Assert.AreEqual("1.100 + 1.100i", String.Format(culture, "{0:.000}", new Complex32(1.1f, 1.1f))); + + Assert.AreEqual("NaN", Complex32.NaN.ToString("#.000", culture)); + Assert.AreEqual("Infinity", Complex32.Infinity.ToString("#.000", culture)); + Assert.AreEqual(".000", Complex32.Zero.ToString("#.000", culture)); + Assert.AreEqual("1.100", new Complex32(1.1f, 0.0f).ToString("#.000", culture)); + Assert.AreEqual("-1.100i", new Complex32(0.0f, -1.1f).ToString("#.000", culture)); + Assert.AreEqual("1.100i", new Complex32(0.0f, 1.1f).ToString("#.000", culture)); + Assert.AreEqual("1.100 + 1.100i", new Complex32(1.1f, 1.1f).ToString("#.000", culture)); + } + + [Test] + [Row("-1 -2i", -1, -2, "en-US")] + [Row("-1 - 2i ", -1, -2, "de-CH")] + public void CanParseStringToComplexWithCulture( + string text, float expectedReal, float expectedImaginary, string cultureName) + { + Complex32 parsed = Complex32.Parse(text, CultureInfo.GetCultureInfo(cultureName)); + Assert.AreEqual(expectedReal, parsed.Real); + Assert.AreEqual(expectedImaginary, parsed.Imaginary); + } + + [Test] + [Row("1", 1, 0)] + [Row("-1", -1, 0)] + [Row("-i", 0, -1)] + [Row("i", 0, 1)] + [Row("2i", 0, 2)] + [Row("1 + 2i", 1, 2)] + [Row("1+2i", 1, 2)] + [Row("1 - 2i", 1, -2)] + [Row("1-2i", 1, -2)] + [Row("1,2 ", 1, 2)] + [Row("1 , 2", 1, 2)] + [Row("1,2i", 1, 2)] + [Row("-1, -2i", -1, -2)] + [Row(" - 1 , - 2 i ", -1, -2)] + [Row("(+1,2i)", 1, 2)] + [Row("(-1 , -2)", -1, -2)] + [Row("(-1 , -2i)", -1, -2)] + [Row("(+1e1 , -2e-2i)", 10, -0.02)] + [Row("(-1E1 -2e2i)", -10, -200)] + [Row("(-1e+1 -2e2i)", -10, -200)] + [Row("(-1e1 -2e+2i)", -10, -200)] + [Row("(-1e-1 -2E2i)", -0.1, -200)] + [Row("(-1e1 -2e-2i)", -10, -0.02)] + [Row("(-1E+1 -2e+2i)", -10, -200)] + [Row("(-1e-1,-2e-2i)", -0.1, -0.02)] + [Row("(+1 +2i)", 1, 2)] + [Row("(-1E+1 -2e+2i)", -10, -200)] + [Row("(-1e-1,-2e-2i)", -0.1, -0.02)] + public void CanTryParseStringToComplexWithInvariant(string str, float expectedReal, float expectedImaginary) + { + var invariantCulture = CultureInfo.InvariantCulture; + Complex32 z; + var ret = Complex32.TryParse(str, invariantCulture, out z); + Assert.IsTrue(ret); + Assert.AreEqual(expectedReal, z.Real); + Assert.AreEqual(expectedImaginary, z.Imaginary); + } + + [Test] + public void ParseThrowsFormatExceptionIfMissingClosingParen() + { + Assert.Throws(() => Complex32.Parse("(1,2")); + } + + [Test] + public void TryParseCanHandleSymbols() + { + Complex32 z; + var ni = NumberFormatInfo.CurrentInfo; + var separator = CultureInfo.CurrentCulture.TextInfo.ListSeparator; + var ret = Complex32.TryParse( + ni.NegativeInfinitySymbol + separator + ni.PositiveInfinitySymbol, out z); + Assert.IsTrue(ret, "A1"); + Assert.AreEqual(float.NegativeInfinity, z.Real, "A2"); + Assert.AreEqual(float.PositiveInfinity, z.Imaginary, "A3"); + + ret = Complex32.TryParse(ni.NaNSymbol + separator + ni.NaNSymbol, out z); + Assert.IsTrue(ret, "B1"); + Assert.AreEqual(float.NaN, z.Real, "B2"); + Assert.AreEqual(float.NaN, z.Imaginary, "B3"); + + ret = Complex32.TryParse(ni.NegativeInfinitySymbol + "+" + ni.PositiveInfinitySymbol + "i", out z); + Assert.IsTrue(ret, "C1"); + Assert.AreEqual(float.NegativeInfinity, z.Real, "C2"); + Assert.AreEqual(float.PositiveInfinity, z.Imaginary, "C3"); + + ret = Complex32.TryParse(ni.NaNSymbol + "+" + ni.NaNSymbol + "i", out z); + Assert.IsTrue(ret, "D1"); + Assert.AreEqual(float.NaN, z.Real, "D2"); + Assert.AreEqual(float.NaN, z.Imaginary, "D3"); + + ret = Complex32.TryParse( + float.MaxValue.ToString("R") + " " + float.MinValue.ToString("R") + "i", + out z); + Assert.IsTrue(ret, "E1"); + Assert.AreEqual(float.MaxValue, z.Real, "E2"); + Assert.AreEqual(float.MinValue, z.Imaginary, "E3"); + } + + [Test] + [Row("en-US")] + [Row("tr-TR")] + [Row("de-DE")] + [Row("de-CH")] + [Row("he-IL")] + public void TryParseCanHandleSymbolsWithCulture(string cultureName) + { + Complex32 z; + var culture = CultureInfo.GetCultureInfo(cultureName); + var ni = culture.NumberFormat; + var separator = culture.TextInfo.ListSeparator; + var ret = Complex32.TryParse( + ni.NegativeInfinitySymbol + separator + ni.PositiveInfinitySymbol, culture, out z); + Assert.IsTrue(ret, "A1"); + Assert.AreEqual(float.NegativeInfinity, z.Real, "A2"); + Assert.AreEqual(float.PositiveInfinity, z.Imaginary, "A3"); + + ret = Complex32.TryParse(ni.NaNSymbol + separator + ni.NaNSymbol, culture, out z); + Assert.IsTrue(ret, "B1"); + Assert.AreEqual(float.NaN, z.Real, "B2"); + Assert.AreEqual(float.NaN, z.Imaginary, "B3"); + + ret = Complex32.TryParse(ni.NegativeInfinitySymbol + "+" + ni.PositiveInfinitySymbol + "i", culture, out z); + Assert.IsTrue(ret, "C1"); + Assert.AreEqual(float.NegativeInfinity, z.Real, "C2"); + Assert.AreEqual(float.PositiveInfinity, z.Imaginary, "C3"); + + ret = Complex32.TryParse(ni.NaNSymbol + "+" + ni.NaNSymbol + "i", culture, out z); + Assert.IsTrue(ret, "D1"); + Assert.AreEqual(float.NaN, z.Real, "D2"); + Assert.AreEqual(float.NaN, z.Imaginary, "D3"); + + ret = Complex32.TryParse( + float.MaxValue.ToString("R", culture) + " " + float.MinValue.ToString("R", culture) + "i", + culture, + out z); + Assert.IsTrue(ret, "E1"); + Assert.AreEqual(float.MaxValue, z.Real, "E2"); + Assert.AreEqual(float.MinValue, z.Imaginary, "E3"); + } + + [Test] + [Row("")] + [Row("+")] + [Row("1-")] + [Row("i+")] + [Row("1/2i")] + [Row("1i+2i")] + [Row("i1i")] + [Row("(1i,2)")] + [Row("1e+")] + [Row("1e")] + [Row("1,")] + [Row(",1")] + [Row(null)] + [Row("()")] + [Row("( )")] + public void TryParseReturnsFalseWhenGivenBadValueWithInvariant(string str) + { + Complex32 z; + var ret = Complex32.TryParse(str, CultureInfo.InvariantCulture, out z); + Assert.IsFalse(ret); + Assert.AreEqual(0, z.Real); + Assert.AreEqual(0, z.Imaginary); + } + } +} \ No newline at end of file diff --git a/src/UnitTests/ComplexTests/Complex32Test.cs b/src/UnitTests/ComplexTests/Complex32Test.cs new file mode 100644 index 00000000..2ad39642 --- /dev/null +++ b/src/UnitTests/ComplexTests/Complex32Test.cs @@ -0,0 +1,572 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://mathnet.opensourcedotnet.info +// +// Copyright (c) 2009 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +namespace MathNet.Numerics.UnitTests.ComplexTests +{ + using System; + using MbUnit.Framework; + + [TestFixture] + public class Complex32Test + { + [Test] + [MultipleAsserts] + public void CanAddComplexNumberAndDoubleUsingOperartor() + { + AssertEx.That(() => (Complex32.NaN + float.NaN).IsNaN); + AssertEx.That(() => (float.NaN + Complex32.NaN).IsNaN); + AssertEx.That(() => (float.PositiveInfinity + Complex32.One).IsInfinity); + AssertEx.That(() => (Complex32.Infinity + 1.0f).IsInfinity); + AssertEx.That(() => (Complex32.One + 0.0f) == Complex32.One); + AssertEx.That(() => (0.0f + Complex32.One) == Complex32.One); + AssertEx.That(() => (new Complex32(1.1f, -2.2f) + 1.1f == new Complex32(2.2f, -2.2f))); + AssertEx.That(() => -2.2f + new Complex32(-1.1f, 2.2f) == new Complex32(-3.3f, 2.2f)); + } + + [Test] + [MultipleAsserts] + public void CanAddSubtractComplexNumbersUsingOperartor() + { + AssertEx.That(() => (Complex32.NaN - Complex32.NaN).IsNaN); + AssertEx.That(() => (Complex32.Infinity - Complex32.One).IsInfinity); + AssertEx.That(() => (Complex32.One - Complex32.Zero) == Complex32.One); + AssertEx.That(() => (new Complex32(1.1f, -2.2f) - new Complex32(1.1f, -2.2f)) == Complex32.Zero); + } + + [Test] + [MultipleAsserts] + public void CanAddTwoComplexNumbers() + { + AssertEx.That(() => Complex32.NaN.Add(Complex32.NaN).IsNaN); + AssertEx.That(() => Complex32.Infinity.Add(Complex32.One).IsInfinity); + AssertEx.That(() => Complex32.One.Add(Complex32.Zero) == Complex32.One); + AssertEx.That(() => new Complex32(1.1f, -2.2f).Add(new Complex32(-1.1f, 2.2f)) == Complex32.Zero); + } + + [Test] + [MultipleAsserts] + public void CanAddTwoComplexNumbersUsingOperartor() + { + AssertEx.That(() => (Complex32.NaN + Complex32.NaN).IsNaN); + AssertEx.That(() => (Complex32.Infinity + Complex32.One).IsInfinity); + AssertEx.That(() => (Complex32.One + Complex32.Zero) == Complex32.One); + AssertEx.That(() => (new Complex32(1.1f, -2.2f) + new Complex32(-1.1f, 2.2f)) == Complex32.Zero); + } + + [Test] + [MultipleAsserts] + public void CanCalculateHashCode() + { + var complex = new Complex32(1, 0); + Assert.AreEqual(1065353216, complex.GetHashCode()); + complex = new Complex32(0, 1); + Assert.AreEqual(-1065353216, complex.GetHashCode()); + complex = new Complex32(1, 1); + Assert.AreEqual(-16777216, complex.GetHashCode()); + } + + [Test] + [Row(0.0f, 0.0f, 1.0f, 0.0f)] + [Row(0.0f, 1.0f, 0.54030230586813977, 0.8414709848078965)] + [Row(-1.0f, 1.0f, 0.19876611034641295, 0.30955987565311222)] + [Row(-111.1, 111.1, -2.3259065941590448e-49, -5.1181940185795617e-49)] + public void CanComputeExponential(float real, float imag, float expectedReal, float expectedImag) + { + var value = new Complex32(real, imag); + var expected = new Complex32(expectedReal, expectedImag); + AssertHelpers.AlmostEqual(expected, value.Exponential(), 7); + } + + + [Test] + [Row(0.0f, 0.0f, float.NegativeInfinity, 0.0f)] + [Row(0.0f, 1.0f, 0.0f, 1.5707963267948966)] + [Row(-1.0f, 1.0f, 0.34657359027997264, 2.3561944901923448)] + [Row(-111.1, 111.1, 5.0570042869255571, 2.3561944901923448)] + [Row(111.1, -111.1, 5.0570042869255571, -0.78539816339744828)] + public void CanComputeNaturalLogarithm(float real, float imag, float expectedReal, float expectedImag) + { + var value = new Complex32(real, imag); + var expected = new Complex32(expectedReal, expectedImag); + AssertHelpers.AlmostEqual(expected, value.NaturalLogarithm(), 7); + } + + [Test] + [MultipleAsserts] + public void CanComputePower() + { + var a = new Complex32(1.19209289550780998537e-7f, 1.19209289550780998537e-7f); + var b = new Complex32(1.19209289550780998537e-7f, 1.19209289550780998537e-7f); + AssertHelpers.AlmostEqual( + new Complex32(9.99998047207974718744e-1f, -1.76553541154378695012e-6f), a.Power(b), 7); + a = new Complex32(0.0f, 1.19209289550780998537e-7f); + b = new Complex32(0.0f, -1.19209289550780998537e-7f); + AssertHelpers.AlmostEqual(new Complex32(1.00000018725172576491f, 1.90048076369011843105e-6f), a.Power(b), 7); + a = new Complex32(0.0f, -1.19209289550780998537e-7f); + b = new Complex32(0.0f, 0.5f); + AssertHelpers.AlmostEqual(new Complex32(-2.56488189382693049636e-1f, -2.17823120666116144959f), a.Power(b), 6); + a = new Complex32(0.0f, 0.5f); + b = new Complex32(0.0f, -0.5f); + AssertHelpers.AlmostEqual(new Complex32(2.06287223508090495171f, 7.45007062179724087859e-1f), a.Power(b), 7); + a = new Complex32(0.0f, -0.5f); + b = new Complex32(0.0f, 1.0f); + AssertHelpers.AlmostEqual(new Complex32(3.70040633557002510874f, -3.07370876701949232239f), a.Power(b), 7); + a = new Complex32(0.0f, 2.0f); + b = new Complex32(0.0f, -2.0f); + AssertHelpers.AlmostEqual(new Complex32(4.24532146387429353891f, -2.27479427903521192648e1f), a.Power(b), 7); + a = new Complex32(0.0f, -8.388608e6f); + b = new Complex32(1.19209289550780998537e-7f, 0.0f); + AssertHelpers.AlmostEqual(new Complex32(1.00000190048219620166f, -1.87253870018168043834e-7f), a.Power(b), 7); + a = new Complex32(0.0f, 0.0f); + b = new Complex32(0.0f, 0.0f); + AssertHelpers.AlmostEqual(new Complex32(1.0f, 0.0f), a.Power(b), 7); + a = new Complex32(0.0f, 0.0f); + b = new Complex32(1.0f, 0.0f); + AssertHelpers.AlmostEqual(new Complex32(0.0f, 0.0f), a.Power(b), 7); + a = new Complex32(0.0f, 0.0f); + b = new Complex32(-1.0f, 0.0f); + AssertHelpers.AlmostEqual(new Complex32(float.PositiveInfinity, 0.0f), a.Power(b), 7); + a = new Complex32(0.0f, 0.0f); + b = new Complex32(-1.0f, 1.0f); + AssertHelpers.AlmostEqual(new Complex32(float.PositiveInfinity, float.PositiveInfinity), a.Power(b), 7); + a = new Complex32(0.0f, 0.0f); + b = new Complex32(0.0f, 1.0f); + AssertEx.That(() => a.Power(b).IsNaN); + } + + [Test] + [MultipleAsserts] + public void CanComputeRoot() + { + var a = new Complex32(1.19209289550780998537e-7f, 1.19209289550780998537e-7f); + var b = new Complex32(1.19209289550780998537e-7f, 1.19209289550780998537e-7f); + AssertHelpers.AlmostEqual(new Complex32(0.0f, 0.0f), a.Root(b), 7); + a = new Complex32(0.0f, -1.19209289550780998537e-7f); + b = new Complex32(0.0f, 0.5f); + AssertHelpers.AlmostEqual(new Complex32(0.038550761943650161f, 0.019526430428319544f), a.Root(b), 6); + a = new Complex32(0.0f, 0.5f); + b = new Complex32(0.0f, -0.5f); + AssertHelpers.AlmostEqual(new Complex32(0.007927894711475968f, -0.042480480425152213f), a.Root(b), 6); + a = new Complex32(0.0f, -0.5f); + b = new Complex32(0.0f, 1.0f); + AssertHelpers.AlmostEqual(new Complex32(0.15990905692806806f, 0.13282699942462053f), a.Root(b), 7); + a = new Complex32(0.0f, 2.0f); + b = new Complex32(0.0f, -2.0f); + AssertHelpers.AlmostEqual(new Complex32(0.42882900629436788f, 0.15487175246424678f), a.Root(b), 7); + a = new Complex32(0.0f, -8.388608e6f); + b = new Complex32(1.19209289550780998537e-7f, 0.0f); + AssertHelpers.AlmostEqual(new Complex32(float.PositiveInfinity, float.NegativeInfinity), a.Root(b), 7); + } + + [Test] + [MultipleAsserts] + public void CanComputeSquare() + { + var complex = new Complex32(1.19209289550780998537e-7f, 1.19209289550780998537e-7f); + AssertHelpers.AlmostEqual(new Complex32(0, 2.8421709430403888e-14f), complex.Square(), 7); + complex = new Complex32(0.0f, 1.19209289550780998537e-7f); + AssertHelpers.AlmostEqual(new Complex32(-1.4210854715201944e-14f, 0.0f), complex.Square(), 7); + complex = new Complex32(0.0f, -1.19209289550780998537e-7f); + AssertHelpers.AlmostEqual(new Complex32(-1.4210854715201944e-14f, 0.0f), complex.Square(), 7); + complex = new Complex32(0.0f, 0.5f); + AssertHelpers.AlmostEqual(new Complex32(-0.25f, 0.0f), complex.Square(), 7); + complex = new Complex32(0.0f, -0.5f); + AssertHelpers.AlmostEqual(new Complex32(-0.25f, 0.0f), complex.Square(), 7); + complex = new Complex32(0.0f, -8.388608e6f); + AssertHelpers.AlmostEqual(new Complex32(-70368744177664.0f, 0.0f), complex.Square(), 7); + } + + [Test] + [MultipleAsserts] + public void CanComputeSquareRoot() + { + var complex = new Complex32(1.19209289550780998537e-7f, 1.19209289550780998537e-7f); + AssertHelpers.AlmostEqual( + new Complex32(0.00037933934912842666f, 0.00015712750315077684f), complex.SquareRoot(), 7); + complex = new Complex32(0.0f, 1.19209289550780998537e-7f); + AssertHelpers.AlmostEqual( + new Complex32(0.00024414062499999973f, 0.00024414062499999976f), complex.SquareRoot(), 7); + complex = new Complex32(0.0f, -1.19209289550780998537e-7f); + AssertHelpers.AlmostEqual( + new Complex32(0.00024414062499999973f, -0.00024414062499999976f), complex.SquareRoot(), 7); + complex = new Complex32(0.0f, 0.5f); + AssertHelpers.AlmostEqual(new Complex32(0.5f, 0.5f), complex.SquareRoot(), 7); + complex = new Complex32(0.0f, -0.5f); + AssertHelpers.AlmostEqual(new Complex32(0.5f, -0.5f), complex.SquareRoot(), 7); + complex = new Complex32(0.0f, -8.388608e6f); + AssertHelpers.AlmostEqual(new Complex32(2048.0f, -2048.0f), complex.SquareRoot(), 7); + complex = new Complex32(8.388608e6f, 1.19209289550780998537e-7f); + AssertHelpers.AlmostEqual(new Complex32(2896.3093757400989f, 2.0579515874459933e-11f), complex.SquareRoot(), 7); + complex = new Complex32(0.0f, 0.0f); + AssertHelpers.AlmostEqual(Complex32.Zero, complex.SquareRoot(), 7); + } + + [Test] + [MultipleAsserts] + public void CanConvertDoubleToComplex() + { + AssertEx.That(() => ((Complex32)float.NaN).IsNaN); + AssertEx.That(() => ((Complex32)float.NegativeInfinity).IsInfinity); + Assert.AreEqual(1.1f, new Complex32(1.1f, 0)); + } + + [Test] + [MultipleAsserts] + public void CanCreateComplexNumberUsingTheConstructor() + { + var complex = new Complex32(1.1f, -2.2f); + Assert.AreEqual(1.1f, complex.Real, "Real part is 1.1f."); + Assert.AreEqual(-2.2f, complex.Imaginary, "Imaginary part is -2.2f."); + } + + [Test] + [MultipleAsserts] + public void CanCreateComplexNumberWithModulusArgument() + { + var complex = Complex32.WithModulusArgument(2, (float)-Math.PI / 6); + Assert.AreApproximatelyEqual((float)Math.Sqrt(3), complex.Real, 1e-7f, "Real part is Sqrt(3)."); + Assert.AreApproximatelyEqual(-1.0f, complex.Imaginary, 1e-7f, "Imaginary part is -1."); + } + + + [Test] + [MultipleAsserts] + public void CanCreateComplexNumberWithRealImaginaryIntializer() + { + var complex = Complex32.WithRealImaginary(1.1f, -2.2f); + Assert.AreEqual(1.1f, complex.Real, "Real part is 1.1f."); + Assert.AreEqual(-2.2f, complex.Imaginary, "Imaginary part is -2.2f."); + } + + [Test] + public void CanDetermineIfImaginaryUnit() + { + var complex = new Complex32(0, 1); + Assert.IsTrue(complex.IsImaginaryOne, "Imaginary unit"); + } + + [Test] + [MultipleAsserts] + public void CanDetermineIfInfinity() + { + var complex = new Complex32(float.PositiveInfinity, 1); + Assert.IsTrue(complex.IsInfinity, "Real part is infinity."); + complex = new Complex32(1, float.NegativeInfinity); + Assert.IsTrue(complex.IsInfinity, "Imaginary part is infinity."); + complex = new Complex32(float.NegativeInfinity, float.PositiveInfinity); + Assert.IsTrue(complex.IsInfinity, "Both parts are infinity."); + } + + [Test] + [MultipleAsserts] + public void CanDetermineIfNaN() + { + var complex = new Complex32(float.NaN, 1); + Assert.IsTrue(complex.IsNaN, "Real part is NaN."); + complex = new Complex32(1, float.NaN); + Assert.IsTrue(complex.IsNaN, "Imaginary part is NaN."); + complex = new Complex32(float.NaN, float.NaN); + Assert.IsTrue(complex.IsNaN, "Both parts are NaN."); + } + + [Test] + public void CanDetermineIfOneValueComplexNumber() + { + var complex = new Complex32(1, 0); + Assert.IsTrue(complex.IsOne, "Complex32 number with a value of one."); + } + + [Test] + public void CanDetermineIfRealNonNegativeNumber() + { + var complex = new Complex32(1, 0); + Assert.IsTrue(complex.IsReal, "Is a real non-negative number."); + } + + [Test] + public void CanDetermineIfRealNumber() + { + var complex = new Complex32(-1, 0); + Assert.IsTrue(complex.IsReal, "Is a real number."); + } + + [Test] + public void CanDetermineIfZeroValueComplexNumber() + { + var complex = new Complex32(0, 0); + Assert.IsTrue(complex.IsZero, "Zero complex number."); + } + + [Test] + [MultipleAsserts] + public void CanDivideComplexNumberAndDoubleUsingOperators() + { + AssertEx.That(() => (Complex32.NaN * 1.0f).IsNaN); + Assert.AreEqual(new Complex32(-2, 2), new Complex32(4, -4) / -2); + Assert.AreEqual(new Complex32(0.25f, 0.25f), 2 / new Complex32(4, -4)); + Assert.AreEqual(Complex32.Infinity, 2.0f / Complex32.Zero); + Assert.AreEqual(Complex32.Infinity, Complex32.One / 0); + } + + [Test] + [MultipleAsserts] + public void CanDivideTwoComplexNumbers() + { + AssertEx.That(() => Complex32.NaN.Multiply(Complex32.One).IsNaN); + Assert.AreEqual(new Complex32(-2, 0), new Complex32(4, -4).Divide(new Complex32(-2, 2))); + Assert.AreEqual(Complex32.Infinity, Complex32.One.Divide(Complex32.Zero)); + } + + [Test] + [MultipleAsserts] + public void CanDivideTwoComplexNumbersUsingOperators() + { + AssertEx.That(() => (Complex32.NaN / Complex32.One).IsNaN); + Assert.AreEqual(new Complex32(-2, 0), new Complex32(4, -4) / new Complex32(-2, 2)); + Assert.AreEqual(Complex32.Infinity, Complex32.One / Complex32.Zero); + } + + [Test] + [MultipleAsserts] + public void CanMultipleComplexNumberAndDoubleUsingOperators() + { + AssertEx.That(() => (Complex32.NaN * 1.0f).IsNaN); + Assert.AreEqual(new Complex32(8, -8), new Complex32(4, -4) * 2); + Assert.AreEqual(new Complex32(8, -8), 2 * new Complex32(4, -4)); + } + + [Test] + [MultipleAsserts] + public void CanMultipleTwoComplexNumbers() + { + AssertEx.That(() => Complex32.NaN.Multiply(Complex32.One).IsNaN); + Assert.AreEqual(new Complex32(0, 16), new Complex32(4, -4).Multiply(new Complex32(-2, 2))); + } + + [Test] + [MultipleAsserts] + public void CanMultipleTwoComplexNumbersUsingOperators() + { + AssertEx.That(() => (Complex32.NaN * Complex32.One).IsNaN); + Assert.AreEqual(new Complex32(0, 16), new Complex32(4, -4) * new Complex32(-2, 2)); + } + + [Test] + public void CanNegateValue() + { + var complex = new Complex32(1.1f, -2.2f); + Assert.AreEqual(new Complex32(-1.1f, 2.2f), complex.Negate()); + } + + [Test] + public void CanNegateValueUsingOperator() + { + var complex = new Complex32(1.1f, -2.2f); + Assert.AreEqual(new Complex32(-1.1f, 2.2f), -complex); + } + + [Test] + [MultipleAsserts] + public void CanSubtractComplexNumberAndDoubleUsingOperartor() + { + AssertEx.That(() => (Complex32.NaN - float.NaN).IsNaN); + AssertEx.That(() => (float.NaN - Complex32.NaN).IsNaN); + AssertEx.That(() => (float.PositiveInfinity - Complex32.One).IsInfinity); + AssertEx.That(() => (Complex32.Infinity - 1.0f).IsInfinity); + AssertEx.That(() => (Complex32.One - 0.0f) == Complex32.One); + AssertEx.That(() => (0.0f - Complex32.One) == -Complex32.One); + AssertEx.That(() => (new Complex32(1.1f, -2.2f) - 1.1f == new Complex32(0.0f, -2.2f))); + AssertEx.That(() => -2.2f - new Complex32(-1.1f, 2.2f) == new Complex32(-1.1f, -2.2f)); + } + + [Test] + [MultipleAsserts] + public void CanSubtractTwoComplexNumbers() + { + AssertEx.That(() => Complex32.NaN.Subtract(Complex32.NaN).IsNaN); + AssertEx.That(() => Complex32.Infinity.Subtract(Complex32.One).IsInfinity); + AssertEx.That(() => Complex32.One.Subtract(Complex32.Zero) == Complex32.One); + AssertEx.That(() => new Complex32(1.1f, -2.2f).Subtract(new Complex32(1.1f, -2.2f)) == Complex32.Zero); + } + + [Test] + [MultipleAsserts] + public void CanTestForEquality() + { + Assert.AreNotEqual(Complex32.NaN, Complex32.NaN); + Assert.AreEqual(Complex32.Infinity, Complex32.Infinity); + Assert.AreEqual(new Complex32(1.1f, -2.2f), new Complex32(1.1f, -2.2f)); + Assert.AreNotEqual(new Complex32(-1.1f, 2.2f), new Complex32(1.1f, -2.2f)); + } + + [Test] + [MultipleAsserts] + public void CanTestForEqualityUsingOperators() + { + AssertEx.That(() => Complex32.NaN != Complex32.NaN); + AssertEx.That(() => Complex32.Infinity == Complex32.Infinity); + AssertEx.That(() => new Complex32(1.1f, -2.2f) == new Complex32(1.1f, -2.2f)); + AssertEx.That(() => new Complex32(-1.1f, 2.2f) != new Complex32(1.1f, -2.2f)); + } + + [Test] + public void CanUsePlus() + { + var complex = new Complex32(1.1f, -2.2f); + Assert.AreEqual(complex, complex.Plus()); + } + + [Test] + public void CanUsePlusOperator() + { + var complex = new Complex32(1.1f, -2.2f); + Assert.AreEqual(complex, +complex); + } + + [Test] + public void WithModulusArgumentThrowsArgumentOutOfRangeException() + { + Assert.Throws( + () => Complex32.WithModulusArgument(-1, 1), "Throws exception because modulus is negative."); + } + + [Test] + [Row(0.0f, 0.0f, 0.0f)] + [Row(0.0f, 1.0f, 1.0f)] + [Row(-1.0f, 1.0f, 1.4142135623730951)] + [Row(-111.1, 111.1, 157.11912677965086)] + public void CanComputeMagnitude(float real, float imag, float expected) + { + Assert.AreEqual(expected, new Complex32(real, imag).Magnitude); + } + + + [Test] + [Row(float.PositiveInfinity, float.PositiveInfinity, Constants.Sqrt1Over2, Constants.Sqrt1Over2)] + [Row(float.PositiveInfinity, float.NegativeInfinity, Constants.Sqrt1Over2, -Constants.Sqrt1Over2)] + [Row(float.NegativeInfinity, float.PositiveInfinity, -Constants.Sqrt1Over2, -Constants.Sqrt1Over2)] + [Row(float.NegativeInfinity, float.NegativeInfinity, -Constants.Sqrt1Over2, Constants.Sqrt1Over2)] + [Row(0.0f, 0.0f, 0.0f, 0.0f)] + [Row(-1.0f, 1.0f, -0.70710678118654746, 0.70710678118654746)] + [Row(-111.1, 111.1, -0.70710678118654746, 0.70710678118654746)] + public void CanComputeSign(float real, float imag, float expectedReal, float expectedImag) + { + Assert.AreEqual(new Complex32(expectedReal, expectedImag), new Complex32(real, imag).Sign); + } + + [Test] + public void CanConvertDecimalToComplex() + { + var orginal = new decimal(1.234567890); + var complex = (Complex32)orginal; + Assert.AreEqual((float)1.234567890, complex.Real); + Assert.AreEqual(0.0f, complex.Imaginary); + } + + [Test] + public void CanConvertByteToComplex() + { + const byte orginal = 123; + var complex = (Complex32)orginal; + Assert.AreEqual(123, complex.Real); + Assert.AreEqual(0.0f, complex.Imaginary); + } + + [Test] + public void CanConvertShortToComplex() + { + const short orginal = 123; + var complex = (Complex32)orginal; + Assert.AreEqual(123, complex.Real); + Assert.AreEqual(0.0f, complex.Imaginary); + } + + [Test] + public void CanConvertIntToComplex() + { + const int orginal = 123; + var complex = (Complex32)orginal; + Assert.AreEqual(123, complex.Real); + Assert.AreEqual(0.0f, complex.Imaginary); + } + + [Test] + public void CanConvertLongToComplex() + { + const long orginal = 123; + var complex = (Complex32)orginal; + Assert.AreEqual(123, complex.Real); + Assert.AreEqual(0.0f, complex.Imaginary); + } + + [Test] + public void CanConvertUIntToComplex() + { + const uint orginal = 123; + var complex = (Complex32)orginal; + Assert.AreEqual(123, complex.Real); + Assert.AreEqual(0.0f, complex.Imaginary); + } + + [Test] + public void CanConvertULongToComplex() + { + const ulong orginal = 123; + var complex = (Complex32)orginal; + Assert.AreEqual(123, complex.Real); + Assert.AreEqual(0.0f, complex.Imaginary); + } + + [Test] + public void CanConvertFloatToComplex() + { + const float orginal = 123.456789f; + var complex = (Complex32)orginal; + Assert.AreEqual(123.456789f, complex.Real); + Assert.AreEqual(0.0f, complex.Imaginary); + } + + [Test] + public void CanConvertComplexToComplex32() + { + var complex32 = new Complex(123.456, -78.9); + var complex = (Complex32)complex32; + Assert.AreEqual(123.456f, complex.Real); + Assert.AreEqual(-78.9f, complex.Imaginary); + } + + [Test] + public void CanGetConjugate() + { + var complex = new Complex(123.456, -78.9); + var conjugate = complex.Conjugate; + Assert.AreEqual(complex.Real, conjugate.Real); + Assert.AreEqual(-complex.Imaginary, conjugate.Imaginary); + } + } +} \ No newline at end of file diff --git a/src/UnitTests/ComplexTests/ComplexTest.cs b/src/UnitTests/ComplexTests/ComplexTest.cs index 3e52783d..454df4f7 100644 --- a/src/UnitTests/ComplexTests/ComplexTest.cs +++ b/src/UnitTests/ComplexTests/ComplexTest.cs @@ -794,12 +794,12 @@ namespace MathNet.Numerics.UnitTests.ComplexTests } [Test] - public void CanConvertDoulbeComplex() + public void CanConvertComplex32ToComplex() { - const double orginal = 123.456789; - var complex = (Complex)orginal; - Assert.AreEqual(123.456789, complex.Real); - Assert.AreEqual(0.0, complex.Imaginary); + var complex32 = new Complex32(123.456f, -78.9f); + var complex = (Complex)complex32; + Assert.AreEqual(123.456f, (float)complex.Real); + Assert.AreEqual(-78.9f, (float)complex.Imaginary); } } } \ No newline at end of file diff --git a/src/UnitTests/PrecisionTest.cs b/src/UnitTests/PrecisionTest.cs index 3dc5cac2..02e00e08 100644 --- a/src/UnitTests/PrecisionTest.cs +++ b/src/UnitTests/PrecisionTest.cs @@ -35,7 +35,7 @@ namespace MathNet.Numerics.UnitTests public sealed class PrecisionTest { private const double _acceptableError = 1e-12; - private readonly double _doublePrecision = System.Math.Pow(2, -53); + private readonly double _doublePrecision = Math.Pow(2, -53); [Test] public void Magnitude() diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index d669620c..fbb4fbf2 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -64,6 +64,8 @@ + +