//
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
//
// Copyright (c) 2009-2010 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.
//
using System;
using System.Collections.Generic;
using Complex = System.Numerics.Complex;
#if !NETSTANDARD1_3
using System.Runtime;
#endif
namespace IStation.Numerics
{
///
/// Extension methods for the Complex type provided by System.Numerics
///
public static class ComplexExtensions
{
///
/// Gets the squared magnitude of the Complex number.
///
/// The number to perform this operation on.
/// The squared magnitude of the Complex number.
public static double MagnitudeSquared(this Complex32 complex)
{
return (complex.Real * complex.Real) + (complex.Imaginary * complex.Imaginary);
}
///
/// Gets the squared magnitude of the Complex number.
///
/// The number to perform this operation on.
/// The squared magnitude of the Complex number.
public static double MagnitudeSquared(this Complex complex)
{
return (complex.Real * complex.Real) + (complex.Imaginary * complex.Imaginary);
}
///
/// Gets the unity of this complex (same argument, but on the unit circle; exp(I*arg))
///
/// The unity of this Complex.
public static Complex Sign(this Complex complex)
{
if (double.IsPositiveInfinity(complex.Real) && double.IsPositiveInfinity(complex.Imaginary))
{
return new Complex(Constants.Sqrt1Over2, Constants.Sqrt1Over2);
}
if (double.IsPositiveInfinity(complex.Real) && double.IsNegativeInfinity(complex.Imaginary))
{
return new Complex(Constants.Sqrt1Over2, -Constants.Sqrt1Over2);
}
if (double.IsNegativeInfinity(complex.Real) && double.IsPositiveInfinity(complex.Imaginary))
{
return new Complex(-Constants.Sqrt1Over2, -Constants.Sqrt1Over2);
}
if (double.IsNegativeInfinity(complex.Real) && double.IsNegativeInfinity(complex.Imaginary))
{
return new Complex(-Constants.Sqrt1Over2, Constants.Sqrt1Over2);
}
// don't replace this with "Magnitude"!
var mod = SpecialFunctions.Hypotenuse(complex.Real, complex.Imaginary);
if (mod == 0.0d)
{
return Complex.Zero;
}
return new Complex(complex.Real / mod, complex.Imaginary / mod);
}
///
/// Gets the conjugate of the Complex number.
///
/// The number to perform this operation on.
///
/// 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
///
///
/// The conjugate of the number.
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public static Complex Conjugate(this Complex complex)
{
return Complex.Conjugate(complex);
}
///
/// Returns the multiplicative inverse of a complex number.
///
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public static Complex Reciprocal(this Complex complex)
{
return Complex.Reciprocal(complex);
}
///
/// Exponential of this Complex (exp(x), E^x).
///
/// The number to perform this operation on.
///
/// The exponential of this complex number.
///
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public static Complex Exp(this Complex complex)
{
return Complex.Exp(complex);
}
///
/// Natural Logarithm of this Complex (Base E).
///
/// The number to perform this operation on.
///
/// The natural logarithm of this complex number.
///
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public static Complex Ln(this Complex complex)
{
return Complex.Log(complex);
}
///
/// Common Logarithm of this Complex (Base 10).
///
/// The common logarithm of this complex number.
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public static Complex Log10(this Complex complex)
{
return Complex.Log10(complex);
}
///
/// Logarithm of this Complex with custom base.
///
/// The logarithm of this complex number.
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public static Complex Log(this Complex complex, double baseValue)
{
return Complex.Log(complex, baseValue);
}
///
/// Raise this Complex to the given value.
///
/// The number to perform this operation on.
///
/// The exponent.
///
///
/// The complex number raised to the given exponent.
///
public static Complex Power(this Complex complex, Complex exponent)
{
if (complex.IsZero())
{
if (exponent.IsZero())
{
return Complex.One;
}
if (exponent.Real > 0d)
{
return Complex.Zero;
}
if (exponent.Real < 0d)
{
return exponent.Imaginary == 0d
? new Complex(double.PositiveInfinity, 0d)
: new Complex(double.PositiveInfinity, double.PositiveInfinity);
}
return new Complex(double.NaN, double.NaN);
}
return Complex.Pow(complex, exponent);
}
///
/// Raise this Complex to the inverse of the given value.
///
/// The number to perform this operation on.
///
/// The root exponent.
///
///
/// The complex raised to the inverse of the given exponent.
///
public static Complex Root(this Complex complex, Complex rootExponent)
{
return Complex.Pow(complex, 1 / rootExponent);
}
///
/// The Square (power 2) of this Complex
///
/// The number to perform this operation on.
///
/// The square of this complex number.
///
public static Complex Square(this Complex complex)
{
if (complex.IsReal())
{
return new Complex(complex.Real * complex.Real, 0.0);
}
return new Complex((complex.Real * complex.Real) - (complex.Imaginary * complex.Imaginary), 2 * complex.Real * complex.Imaginary);
}
///
/// The Square Root (power 1/2) of this Complex
///
/// The number to perform this operation on.
///
/// The square root of this complex number.
///
public static Complex SquareRoot(this Complex complex)
{
// Note: the following code should be equivalent to Complex.Sqrt(complex),
// but it turns out that is implemented poorly in System.Numerics,
// hence we provide our own implementation here. Do not replace.
if (complex.IsRealNonNegative())
{
return new Complex(Math.Sqrt(complex.Real), 0.0);
}
Complex result;
var absReal = Math.Abs(complex.Real);
var absImag = Math.Abs(complex.Imaginary);
double w;
if (absReal >= absImag)
{
var ratio = complex.Imaginary / complex.Real;
w = Math.Sqrt(absReal) * Math.Sqrt(0.5 * (1.0 + Math.Sqrt(1.0 + (ratio * ratio))));
}
else
{
var ratio = complex.Real / complex.Imaginary;
w = Math.Sqrt(absImag) * Math.Sqrt(0.5 * (Math.Abs(ratio) + Math.Sqrt(1.0 + (ratio * ratio))));
}
if (complex.Real >= 0.0)
{
result = new Complex(w, complex.Imaginary / (2.0 * w));
}
else if (complex.Imaginary >= 0.0)
{
result = new Complex(absImag / (2.0 * w), w);
}
else
{
result = new Complex(absImag / (2.0 * w), -w);
}
return result;
}
///
/// Evaluate all square roots of this Complex.
///
public static Tuple SquareRoots(this Complex complex)
{
var principal = SquareRoot(complex);
return new Tuple(principal, -principal);
}
///
/// Evaluate all cubic roots of this Complex.
///
public static Tuple CubicRoots(this Complex complex)
{
var r = Math.Pow(complex.Magnitude, 1d/3d);
var theta = complex.Phase/3;
const double shift = Constants.Pi2/3;
return new Tuple(
Complex.FromPolarCoordinates(r, theta),
Complex.FromPolarCoordinates(r, theta + shift),
Complex.FromPolarCoordinates(r, theta - shift));
}
///
/// Gets a value indicating whether the Complex32 is zero.
///
/// The number to perform this operation on.
/// true if this instance is zero; otherwise, false.
public static bool IsZero(this Complex complex)
{
return complex.Real == 0.0 && complex.Imaginary == 0.0;
}
///
/// Gets a value indicating whether the Complex32 is one.
///
/// The number to perform this operation on.
/// true if this instance is one; otherwise, false.
public static bool IsOne(this Complex complex)
{
return complex.Real == 1.0 && complex.Imaginary == 0.0;
}
///
/// Gets a value indicating whether the Complex32 is the imaginary unit.
///
/// true if this instance is ImaginaryOne; otherwise, false.
/// The number to perform this operation on.
public static bool IsImaginaryOne(this Complex complex)
{
return complex.Real == 0.0 && complex.Imaginary == 1.0;
}
///
/// Gets a value indicating whether the provided Complex32evaluates
/// to a value that is not a number.
///
/// The number to perform this operation on.
///
/// true if this instance is NaN; otherwise,
/// false.
///
public static bool IsNaN(this Complex complex)
{
return double.IsNaN(complex.Real) || double.IsNaN(complex.Imaginary);
}
///
/// Gets a value indicating whether the provided Complex32 evaluates to an
/// infinite value.
///
/// The number to perform this operation on.
///
/// true if this instance is infinite; otherwise, false.
///
///
/// True if it either evaluates to a complex infinity
/// or to a directed infinity.
///
public static bool IsInfinity(this Complex complex)
{
return double.IsInfinity(complex.Real) || double.IsInfinity(complex.Imaginary);
}
///
/// Gets a value indicating whether the provided Complex32 is real.
///
/// The number to perform this operation on.
/// true if this instance is a real number; otherwise, false.
public static bool IsReal(this Complex complex)
{
return complex.Imaginary == 0.0;
}
///
/// Gets a value indicating whether the provided Complex32 is real and not negative, that is >= 0.
///
/// The number to perform this operation on.
///
/// true if this instance is real nonnegative number; otherwise, false.
///
public static bool IsRealNonNegative(this Complex complex)
{
return complex.Imaginary == 0.0f && complex.Real >= 0;
}
///
/// Returns a Norm of a value of this type, which is appropriate for measuring how
/// close this value is to zero.
///
public static double Norm(this Complex complex)
{
return complex.MagnitudeSquared();
}
///
/// Returns a Norm of a value of this type, which is appropriate for measuring how
/// close this value is to zero.
///
public static double Norm(this Complex32 complex)
{
return complex.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.
///
public static double NormOfDifference(this Complex complex, Complex otherValue)
{
return (complex - otherValue).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.
///
public static double NormOfDifference(this Complex32 complex, Complex32 otherValue)
{
return (complex - otherValue).MagnitudeSquared;
}
///
/// 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 double.
///
///
/// A complex number containing the value specified by the given string.
///
///
/// The string to parse.
///
public static Complex ToComplex(this string value)
{
return value.ToComplex(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 double.
///
///
/// A complex number containing the value specified by the given string.
///
///
/// the string to parse.
///
///
/// An that supplies culture-specific
/// formatting information.
///
public static Complex ToComplex(this string value, IFormatProvider formatProvider)
{
if (value == null)
{
throw new ArgumentNullException(nameof(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 Complex(0, leftPart) : new Complex(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 Complex(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 Complex(rightPart, leftPart) : new Complex(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 double.
///
private static double 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();
}
}
var 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;
}
}
#if NETSTANDARD1_3
var value = GlobalizationHelper.ParseDouble(ref token);
#else
var value = GlobalizationHelper.ParseDouble(ref token, format.GetCultureInfo());
#endif
// 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 double-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 Complex.Zero. This parameter is passed uninitialized.
///
public static bool TryToComplex(this string value, out Complex result)
{
return value.TryToComplex(null, out result);
}
///
/// Converts the string representation of a complex number to double-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 TryToComplex(this string value, IFormatProvider formatProvider, out Complex result)
{
bool ret;
try
{
result = value.ToComplex(formatProvider);
ret = true;
}
catch (ArgumentNullException)
{
result = Complex.Zero;
ret = false;
}
catch (FormatException)
{
result = Complex.Zero;
ret = false;
}
return ret;
}
///
/// Creates a Complex32 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 double.
///
///
/// A complex number containing the value specified by the given string.
///
///
/// the string to parse.
///
public static Complex32 ToComplex32(this string value)
{
return Complex32.Parse(value);
}
///
/// Creates a Complex32 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 double.
///
///
/// 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 ToComplex32(this string value, IFormatProvider formatProvider)
{
return Complex32.Parse(value, formatProvider);
}
///
/// 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 TryToComplex32(this string value, out Complex32 result)
{
return Complex32.TryParse(value, 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 Complex.Zero. This parameter is passed uninitialized.
///
public static bool TryToComplex32(this string value, IFormatProvider formatProvider, out Complex32 result)
{
return Complex32.TryParse(value, formatProvider, out result);
}
}
}