File: System\Numerics\Complex.cs
Web Access
Project: src\src\libraries\System.Runtime.Numerics\src\System.Runtime.Numerics.csproj (System.Runtime.Numerics)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
 
namespace System.Numerics
{
    /// <summary>
    /// A complex number z is a number of the form z = x + yi, where x and y
    /// are real numbers, and i is the imaginary unit, with the property i2= -1.
    /// </summary>
    [Serializable]
    [TypeForwardedFrom("System.Numerics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
    public readonly struct Complex
        : IEquatable<Complex>,
          IFormattable,
          INumberBase<Complex>,
          ISignedNumber<Complex>,
          IUtf8SpanFormattable
    {
        private const NumberStyles DefaultNumberStyle = NumberStyles.Float | NumberStyles.AllowThousands;
 
        private const NumberStyles InvalidNumberStyles = ~(NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite
                                                         | NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign
                                                         | NumberStyles.AllowParentheses | NumberStyles.AllowDecimalPoint
                                                         | NumberStyles.AllowThousands | NumberStyles.AllowExponent
                                                         | NumberStyles.AllowCurrencySymbol | NumberStyles.AllowHexSpecifier);
 
        public static readonly Complex Zero = new Complex(0.0, 0.0);
        public static readonly Complex One = new Complex(1.0, 0.0);
        public static readonly Complex ImaginaryOne = new Complex(0.0, 1.0);
        public static readonly Complex NaN = new Complex(double.NaN, double.NaN);
        public static readonly Complex Infinity = new Complex(double.PositiveInfinity, double.PositiveInfinity);
 
        private const double InverseOfLog10 = 0.43429448190325; // 1 / Log(10)
 
        // This is the largest x for which (Hypot(x,x) + x) will not overflow. It is used for branching inside Sqrt.
        private static readonly double s_sqrtRescaleThreshold = double.MaxValue / (Math.Sqrt(2.0) + 1.0);
 
        // This is the largest x for which 2 x^2 will not overflow. It is used for branching inside Asin and Acos.
        private static readonly double s_asinOverflowThreshold = Math.Sqrt(double.MaxValue) / 2.0;
 
        // This value is used inside Asin and Acos.
        private static readonly double s_log2 = Math.Log(2.0);
 
        // Do not rename, these fields are needed for binary serialization
        private readonly double m_real; // Do not rename (binary serialization)
        private readonly double m_imaginary; // Do not rename (binary serialization)
 
        public Complex(double real, double imaginary)
        {
            m_real = real;
            m_imaginary = imaginary;
        }
 
        public double Real { get { return m_real; } }
        public double Imaginary { get { return m_imaginary; } }
 
        public double Magnitude { get { return Abs(this); } }
        public double Phase { get { return Math.Atan2(m_imaginary, m_real); } }
 
        public static Complex FromPolarCoordinates(double magnitude, double phase)
        {
            (double sin, double cos) = Math.SinCos(phase);
            return new Complex(magnitude * cos, magnitude * sin);
        }
 
        public static Complex Negate(Complex value)
        {
            return -value;
        }
 
        public static Complex Add(Complex left, Complex right)
        {
            return left + right;
        }
 
        public static Complex Add(Complex left, double right)
        {
            return left + right;
        }
 
        public static Complex Add(double left, Complex right)
        {
            return left + right;
        }
 
        public static Complex Subtract(Complex left, Complex right)
        {
            return left - right;
        }
 
        public static Complex Subtract(Complex left, double right)
        {
            return left - right;
        }
 
        public static Complex Subtract(double left, Complex right)
        {
            return left - right;
        }
 
        public static Complex Multiply(Complex left, Complex right)
        {
            return left * right;
        }
 
        public static Complex Multiply(Complex left, double right)
        {
            return left * right;
        }
 
        public static Complex Multiply(double left, Complex right)
        {
            return left * right;
        }
 
        public static Complex Divide(Complex dividend, Complex divisor)
        {
            return dividend / divisor;
        }
 
        public static Complex Divide(Complex dividend, double divisor)
        {
            return dividend / divisor;
        }
 
        public static Complex Divide(double dividend, Complex divisor)
        {
            return dividend / divisor;
        }
 
        public static Complex operator -(Complex value)  /* Unary negation of a complex number */
        {
            return new Complex(-value.m_real, -value.m_imaginary);
        }
 
        public static Complex operator +(Complex left, Complex right)
        {
            return new Complex(left.m_real + right.m_real, left.m_imaginary + right.m_imaginary);
        }
 
        public static Complex operator +(Complex left, double right)
        {
            return new Complex(left.m_real + right, left.m_imaginary);
        }
 
        public static Complex operator +(double left, Complex right)
        {
            return new Complex(left + right.m_real, right.m_imaginary);
        }
 
        public static Complex operator -(Complex left, Complex right)
        {
            return new Complex(left.m_real - right.m_real, left.m_imaginary - right.m_imaginary);
        }
 
        public static Complex operator -(Complex left, double right)
        {
            return new Complex(left.m_real - right, left.m_imaginary);
        }
 
        public static Complex operator -(double left, Complex right)
        {
            return new Complex(left - right.m_real, -right.m_imaginary);
        }
 
        public static Complex operator *(Complex left, Complex right)
        {
            // Multiplication:  (a + bi)(c + di) = (ac -bd) + (bc + ad)i
            double result_realpart = (left.m_real * right.m_real) - (left.m_imaginary * right.m_imaginary);
            double result_imaginarypart = (left.m_imaginary * right.m_real) + (left.m_real * right.m_imaginary);
            return new Complex(result_realpart, result_imaginarypart);
        }
 
        public static Complex operator *(Complex left, double right)
        {
            if (!double.IsFinite(left.m_real))
            {
                if (!double.IsFinite(left.m_imaginary))
                {
                    return new Complex(double.NaN, double.NaN);
                }
 
                return new Complex(left.m_real * right, double.NaN);
            }
 
            if (!double.IsFinite(left.m_imaginary))
            {
                return new Complex(double.NaN, left.m_imaginary * right);
            }
 
            return new Complex(left.m_real * right, left.m_imaginary * right);
        }
 
        public static Complex operator *(double left, Complex right)
        {
            if (!double.IsFinite(right.m_real))
            {
                if (!double.IsFinite(right.m_imaginary))
                {
                    return new Complex(double.NaN, double.NaN);
                }
 
                return new Complex(left * right.m_real, double.NaN);
            }
 
            if (!double.IsFinite(right.m_imaginary))
            {
                return new Complex(double.NaN, left * right.m_imaginary);
            }
 
            return new Complex(left * right.m_real, left * right.m_imaginary);
        }
 
        public static Complex operator /(Complex left, Complex right)
        {
            // Division : Smith's formula.
            double a = left.m_real;
            double b = left.m_imaginary;
            double c = right.m_real;
            double d = right.m_imaginary;
 
            // Computing c * c + d * d will overflow even in cases where the actual result of the division does not overflow.
            if (Math.Abs(d) < Math.Abs(c))
            {
                double doc = d / c;
                return new Complex((a + b * doc) / (c + d * doc), (b - a * doc) / (c + d * doc));
            }
            else
            {
                double cod = c / d;
                return new Complex((b + a * cod) / (d + c * cod), (-a + b * cod) / (d + c * cod));
            }
        }
 
        public static Complex operator /(Complex left, double right)
        {
            // IEEE prohibit optimizations which are value changing
            // so we make sure that behaviour for the simplified version exactly match
            // full version.
            if (right == 0)
            {
                return new Complex(double.NaN, double.NaN);
            }
 
            if (!double.IsFinite(left.m_real))
            {
                if (!double.IsFinite(left.m_imaginary))
                {
                    return new Complex(double.NaN, double.NaN);
                }
 
                return new Complex(left.m_real / right, double.NaN);
            }
 
            if (!double.IsFinite(left.m_imaginary))
            {
                return new Complex(double.NaN, left.m_imaginary / right);
            }
 
            // Here the actual optimized version of code.
            return new Complex(left.m_real / right, left.m_imaginary / right);
        }
 
        public static Complex operator /(double left, Complex right)
        {
            // Division : Smith's formula.
            double a = left;
            double c = right.m_real;
            double d = right.m_imaginary;
 
            // Computing c * c + d * d will overflow even in cases where the actual result of the division does not overflow.
            if (Math.Abs(d) < Math.Abs(c))
            {
                double doc = d / c;
                return new Complex(a / (c + d * doc), (-a * doc) / (c + d * doc));
            }
            else
            {
                double cod = c / d;
                return new Complex(a * cod / (d + c * cod), -a / (d + c * cod));
            }
        }
 
        public static double Abs(Complex value)
        {
            return double.Hypot(value.m_real, value.m_imaginary);
        }
 
        private static double Log1P(double x)
        {
            // Compute log(1 + x) without loss of accuracy when x is small.
 
            // Our only use case so far is for positive values, so this isn't coded to handle negative values.
            Debug.Assert((x >= 0.0) || double.IsNaN(x));
 
            double xp1 = 1.0 + x;
            if (xp1 == 1.0)
            {
                return x;
            }
            else if (x < 0.75)
            {
                // This is accurate to within 5 ulp with any floating-point system that uses a guard digit,
                // as proven in Theorem 4 of "What Every Computer Scientist Should Know About Floating-Point
                // Arithmetic" (https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html)
                return x * Math.Log(xp1) / (xp1 - 1.0);
            }
            else
            {
                return Math.Log(xp1);
            }
 
        }
 
        public static Complex Conjugate(Complex value)
        {
            // Conjugate of a Complex number: the conjugate of x+i*y is x-i*y
            return new Complex(value.m_real, -value.m_imaginary);
        }
 
        public static Complex Reciprocal(Complex value)
        {
            // Reciprocal of a Complex number : the reciprocal of x+i*y is 1/(x+i*y)
            if (value.m_real == 0 && value.m_imaginary == 0)
            {
                return Zero;
            }
            return One / value;
        }
 
        public static bool operator ==(Complex left, Complex right)
        {
            return left.m_real == right.m_real && left.m_imaginary == right.m_imaginary;
        }
 
        public static bool operator !=(Complex left, Complex right)
        {
            return left.m_real != right.m_real || left.m_imaginary != right.m_imaginary;
        }
 
        public override bool Equals([NotNullWhen(true)] object? obj)
        {
            return obj is Complex other && Equals(other);
        }
 
        public bool Equals(Complex value)
        {
            return m_real.Equals(value.m_real) && m_imaginary.Equals(value.m_imaginary);
        }
 
        public override int GetHashCode() => HashCode.Combine(m_real, m_imaginary);
 
        public override string ToString() => ToString(null, null);
 
        public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) => ToString(format, null);
 
        public string ToString(IFormatProvider? provider) => ToString(null, provider);
 
        public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider)
        {
            // $"<{m_real.ToString(format, provider)}; {m_imaginary.ToString(format, provider)}>";
            var handler = new DefaultInterpolatedStringHandler(4, 2, provider, stackalloc char[512]);
            handler.AppendLiteral("<");
            handler.AppendFormatted(m_real, format);
            handler.AppendLiteral("; ");
            handler.AppendFormatted(m_imaginary, format);
            handler.AppendLiteral(">");
            return handler.ToStringAndClear();
        }
 
        public static Complex Sin(Complex value)
        {
            (double sin, double cos) = Math.SinCos(value.m_real);
            return new Complex(sin * Math.Cosh(value.m_imaginary), cos * Math.Sinh(value.m_imaginary));
            // There is a known limitation with this algorithm: inputs that cause sinh and cosh to overflow, but for
            // which sin or cos are small enough that sin * cosh or cos * sinh are still representable, nonetheless
            // produce overflow. For example, Sin((0.01, 711.0)) should produce (~3.0E306, PositiveInfinity), but
            // instead produces (PositiveInfinity, PositiveInfinity).
        }
 
        public static Complex Sinh(Complex value)
        {
            // Use sinh(z) = -i sin(iz) to compute via sin(z).
            Complex sin = Sin(new Complex(-value.m_imaginary, value.m_real));
            return new Complex(sin.m_imaginary, -sin.m_real);
        }
 
        public static Complex Asin(Complex value)
        {
            double b, bPrime, v;
            Asin_Internal(Math.Abs(value.Real), Math.Abs(value.Imaginary), out b, out bPrime, out v);
 
            double u;
            if (bPrime < 0.0)
            {
                u = Math.Asin(b);
            }
            else
            {
                u = Math.Atan(bPrime);
            }
 
            if (value.Real < 0.0) u = -u;
            if (value.Imaginary < 0.0) v = -v;
 
            return new Complex(u, v);
        }
 
        public static Complex Cos(Complex value)
        {
            (double sin, double cos) = Math.SinCos(value.m_real);
            return new Complex(cos * Math.Cosh(value.m_imaginary), -sin * Math.Sinh(value.m_imaginary));
        }
 
        public static Complex Cosh(Complex value)
        {
            // Use cosh(z) = cos(iz) to compute via cos(z).
            return Cos(new Complex(-value.m_imaginary, value.m_real));
        }
 
        public static Complex Acos(Complex value)
        {
            double b, bPrime, v;
            Asin_Internal(Math.Abs(value.Real), Math.Abs(value.Imaginary), out b, out bPrime, out v);
 
            double u;
            if (bPrime < 0.0)
            {
                u = Math.Acos(b);
            }
            else
            {
                u = Math.Atan(1.0 / bPrime);
            }
 
            if (value.Real < 0.0) u = Math.PI - u;
            if (value.Imaginary > 0.0) v = -v;
 
            return new Complex(u, v);
        }
 
        public static Complex Tan(Complex value)
        {
            // tan z = sin z / cos z, but to avoid unnecessary repeated trig computations, use
            //   tan z = (sin(2x) + i sinh(2y)) / (cos(2x) + cosh(2y))
            // (see Abramowitz & Stegun 4.3.57 or derive by hand), and compute trig functions here.
 
            // This approach does not work for |y| > ~355, because sinh(2y) and cosh(2y) overflow,
            // even though their ratio does not. In that case, divide through by cosh to get:
            //   tan z = (sin(2x) / cosh(2y) + i \tanh(2y)) / (1 + cos(2x) / cosh(2y))
            // which correctly computes the (tiny) real part and the (normal-sized) imaginary part.
 
            double x2 = 2.0 * value.m_real;
            double y2 = 2.0 * value.m_imaginary;
            (double sin, double cos) = Math.SinCos(x2);
            double cosh = Math.Cosh(y2);
            if (Math.Abs(value.m_imaginary) <= 4.0)
            {
                double D = cos + cosh;
                return new Complex(sin / D, Math.Sinh(y2) / D);
            }
            else
            {
                double D = 1.0 + cos / cosh;
                return new Complex(sin / cosh / D, Math.Tanh(y2) / D);
            }
        }
 
        public static Complex Tanh(Complex value)
        {
            // Use tanh(z) = -i tan(iz) to compute via tan(z).
            Complex tan = Tan(new Complex(-value.m_imaginary, value.m_real));
            return new Complex(tan.m_imaginary, -tan.m_real);
        }
 
        public static Complex Atan(Complex value)
        {
            Complex two = new Complex(2.0, 0.0);
            return (ImaginaryOne / two) * (Log(One - ImaginaryOne * value) - Log(One + ImaginaryOne * value));
        }
 
        private static void Asin_Internal(double x, double y, out double b, out double bPrime, out double v)
        {
 
            // This method for the inverse complex sine (and cosine) is described in Hull, Fairgrieve,
            // and Tang, "Implementing the Complex Arcsine and Arccosine Functions Using Exception Handling",
            // ACM Transactions on Mathematical Software (1997)
            // (https://www.researchgate.net/profile/Ping_Tang3/publication/220493330_Implementing_the_Complex_Arcsine_and_Arccosine_Functions_Using_Exception_Handling/links/55b244b208ae9289a085245d.pdf)
 
            // First, the basics: start with sin(w) = (e^{iw} - e^{-iw}) / (2i) = z. Here z is the input
            // and w is the output. To solve for w, define t = e^{i w} and multiply through by t to
            // get the quadratic equation t^2 - 2 i z t - 1 = 0. The solution is t = i z + sqrt(1 - z^2), so
            //   w = arcsin(z) = - i log( i z + sqrt(1 - z^2) )
            // Decompose z = x + i y, multiply out i z + sqrt(1 - z^2), use log(s) = |s| + i arg(s), and do a
            // bunch of algebra to get the components of w = arcsin(z) = u + i v
            //   u = arcsin(beta)  v = sign(y) log(alpha + sqrt(alpha^2 - 1))
            // where
            //   alpha = (rho + sigma) / 2      beta = (rho - sigma) / 2
            //   rho = sqrt((x + 1)^2 + y^2)    sigma = sqrt((x - 1)^2 + y^2)
            // These formulas appear in DLMF section 4.23. (http://dlmf.nist.gov/4.23), along with the analogous
            //   arccos(w) = arccos(beta) - i sign(y) log(alpha + sqrt(alpha^2 - 1))
            // So alpha and beta together give us arcsin(w) and arccos(w).
 
            // As written, alpha is not susceptible to cancelation errors, but beta is. To avoid cancelation, note
            //   beta = (rho^2 - sigma^2) / (rho + sigma) / 2 = (2 x) / (rho + sigma) = x / alpha
            // which is not subject to cancelation. Note alpha >= 1 and |beta| <= 1.
 
            // For alpha ~ 1, the argument of the log is near unity, so we compute (alpha - 1) instead,
            // write the argument as 1 + (alpha - 1) + sqrt((alpha - 1)(alpha + 1)), and use the log1p function
            // to compute the log without loss of accuracy.
            // For beta ~ 1, arccos does not accurately resolve small angles, so we compute the tangent of the angle
            // instead.
            // Hull, Fairgrieve, and Tang derive formulas for (alpha - 1) and beta' = tan(u) that do not suffer
            // from cancelation in these cases.
 
            // For simplicity, we assume all positive inputs and return all positive outputs. The caller should
            // assign signs appropriate to the desired cut conventions. We return v directly since its magnitude
            // is the same for both arcsin and arccos. Instead of u, we usually return beta and sometimes beta'.
            // If beta' is not computed, it is set to -1; if it is computed, it should be used instead of beta
            // to determine u. Compute u = arcsin(beta) or u = arctan(beta') for arcsin, u = arccos(beta)
            // or arctan(1/beta') for arccos.
 
            Debug.Assert((x >= 0.0) || double.IsNaN(x));
            Debug.Assert((y >= 0.0) || double.IsNaN(y));
 
            // For x or y large enough to overflow alpha^2, we can simplify our formulas and avoid overflow.
            if ((x > s_asinOverflowThreshold) || (y > s_asinOverflowThreshold))
            {
                b = -1.0;
                bPrime = x / y;
 
                double small, big;
                if (x < y)
                {
                    small = x;
                    big = y;
                }
                else
                {
                    small = y;
                    big = x;
                }
                double ratio = small / big;
                v = s_log2 + Math.Log(big) + 0.5 * Log1P(ratio * ratio);
            }
            else
            {
                double r = double.Hypot((x + 1.0), y);
                double s = double.Hypot((x - 1.0), y);
 
                double a = (r + s) * 0.5;
                b = x / a;
 
                if (b > 0.75)
                {
                    if (x <= 1.0)
                    {
                        double amx = (y * y / (r + (x + 1.0)) + (s + (1.0 - x))) * 0.5;
                        bPrime = x / Math.Sqrt((a + x) * amx);
                    }
                    else
                    {
                        // In this case, amx ~ y^2. Since we take the square root of amx, we should
                        // pull y out from under the square root so we don't lose its contribution
                        // when y^2 underflows.
                        double t = (1.0 / (r + (x + 1.0)) + 1.0 / (s + (x - 1.0))) * 0.5;
                        bPrime = x / y / Math.Sqrt((a + x) * t);
                    }
                }
                else
                {
                    bPrime = -1.0;
                }
 
                if (a < 1.5)
                {
                    if (x < 1.0)
                    {
                        // This is another case where our expression is proportional to y^2 and
                        // we take its square root, so again we pull out a factor of y from
                        // under the square root.
                        double t = (1.0 / (r + (x + 1.0)) + 1.0 / (s + (1.0 - x))) * 0.5;
                        double am1 = y * y * t;
                        v = Log1P(am1 + y * Math.Sqrt(t * (a + 1.0)));
                    }
                    else
                    {
                        double am1 = (y * y / (r + (x + 1.0)) + (s + (x - 1.0))) * 0.5;
                        v = Log1P(am1 + Math.Sqrt(am1 * (a + 1.0)));
                    }
                }
                else
                {
                    // Because of the test above, we can be sure that a * a will not overflow.
                    v = Math.Log(a + Math.Sqrt((a - 1.0) * (a + 1.0)));
                }
            }
        }
 
        public static bool IsFinite(Complex value) => double.IsFinite(value.m_real) && double.IsFinite(value.m_imaginary);
 
        public static bool IsInfinity(Complex value) => double.IsInfinity(value.m_real) || double.IsInfinity(value.m_imaginary);
 
        public static bool IsNaN(Complex value) => !IsInfinity(value) && !IsFinite(value);
 
        public static Complex Log(Complex value)
        {
            return new Complex(Math.Log(Abs(value)), Math.Atan2(value.m_imaginary, value.m_real));
        }
 
        public static Complex Log(Complex value, double baseValue)
        {
            return Log(value) / Log(baseValue);
        }
 
        public static Complex Log10(Complex value)
        {
            Complex tempLog = Log(value);
            return Scale(tempLog, InverseOfLog10);
        }
 
        public static Complex Exp(Complex value)
        {
            double expReal = Math.Exp(value.m_real);
            return FromPolarCoordinates(expReal, value.m_imaginary);
        }
 
        public static Complex Sqrt(Complex value)
        {
 
            // Handle NaN input cases according to IEEE 754
            if (double.IsNaN(value.m_real))
            {
                if (double.IsInfinity(value.m_imaginary))
                {
                    return new Complex(double.PositiveInfinity, value.m_imaginary);
                }
                return new Complex(double.NaN, double.NaN);
            }
            if (double.IsNaN(value.m_imaginary))
            {
                if (double.IsPositiveInfinity(value.m_real))
                {
                    return new Complex(double.NaN, double.PositiveInfinity);
                }
                if (double.IsNegativeInfinity(value.m_real))
                {
                    return new Complex(double.PositiveInfinity, double.NaN);
                }
                return new Complex(double.NaN, double.NaN);
            }
 
            if (value.m_imaginary == 0.0)
            {
                // Handle the trivial case quickly.
                if (value.m_real < 0.0)
                {
                    return new Complex(0.0, Math.Sqrt(-value.m_real));
                }
 
                return new Complex(Math.Sqrt(value.m_real), 0.0);
            }
 
            // One way to compute Sqrt(z) is just to call Pow(z, 0.5), which coverts to polar coordinates
            // (sqrt + atan), halves the phase, and reconverts to cartesian coordinates (cos + sin).
            // Not only is this more expensive than necessary, it also fails to preserve certain expected
            // symmetries, such as that the square root of a pure negative is a pure imaginary, and that the
            // square root of a pure imaginary has exactly equal real and imaginary parts. This all goes
            // back to the fact that Math.PI is not stored with infinite precision, so taking half of Math.PI
            // does not land us on an argument with cosine exactly equal to zero.
 
            // To find a fast and symmetry-respecting formula for complex square root,
            // note x + i y = \sqrt{a + i b} implies x^2 + 2 i x y - y^2 = a + i b,
            // so x^2 - y^2 = a and 2 x y = b. Cross-substitute and use the quadratic formula to obtain
            //   x = \sqrt{\frac{\sqrt{a^2 + b^2} + a}{2}}  y = \pm \sqrt{\frac{\sqrt{a^2 + b^2} - a}{2}}
            // There is just one complication: depending on the sign on a, either x or y suffers from
            // cancelation when |b| << |a|. We can get around this by noting that our formulas imply
            // x^2 y^2 = b^2 / 4, so |x| |y| = |b| / 2. So after computing the one that doesn't suffer
            // from cancelation, we can compute the other with just a division. This is basically just
            // the right way to evaluate the quadratic formula without cancelation.
 
            // All this reduces our total cost to two sqrts and a few flops, and it respects the desired
            // symmetries. Much better than atan + cos + sin!
 
            // The signs are a matter of choice of branch cut, which is traditionally taken so x > 0 and sign(y) = sign(b).
 
            // If the components are too large, Hypot will overflow, even though the subsequent sqrt would
            // make the result representable. To avoid this, we re-scale (by exact powers of 2 for accuracy)
            // when we encounter very large components to avoid intermediate infinities.
            bool rescale = false;
            double realCopy = value.m_real;
            double imaginaryCopy = value.m_imaginary;
            if ((Math.Abs(realCopy) >= s_sqrtRescaleThreshold) || (Math.Abs(imaginaryCopy) >= s_sqrtRescaleThreshold))
            {
                if (double.IsInfinity(value.m_imaginary))
                {
                    // We need to handle infinite imaginary parts specially because otherwise
                    // our formulas below produce inf/inf = NaN.
                    return (new Complex(double.PositiveInfinity, imaginaryCopy));
                }
 
                realCopy *= 0.25;
                imaginaryCopy *= 0.25;
                rescale = true;
            }
 
            // This is the core of the algorithm. Everything else is special case handling.
            double x, y;
            if (realCopy >= 0.0)
            {
                x = Math.Sqrt((double.Hypot(realCopy, imaginaryCopy) + realCopy) * 0.5);
                y = imaginaryCopy / (2.0 * x);
            }
            else
            {
                y = Math.Sqrt((double.Hypot(realCopy, imaginaryCopy) - realCopy) * 0.5);
                if (imaginaryCopy < 0.0) y = -y;
                x = imaginaryCopy / (2.0 * y);
            }
 
            if (rescale)
            {
                x *= 2.0;
                y *= 2.0;
            }
 
            return new Complex(x, y);
        }
 
        public static Complex Pow(Complex value, Complex power)
        {
            if (power == Zero)
            {
                return One;
            }
 
            if (value == Zero)
            {
                return Zero;
            }
 
            double valueReal = value.m_real;
            double valueImaginary = value.m_imaginary;
            double powerReal = power.m_real;
            double powerImaginary = power.m_imaginary;
 
            double rho = Abs(value);
            double theta = Math.Atan2(valueImaginary, valueReal);
            double newRho = powerReal * theta + powerImaginary * Math.Log(rho);
 
            double t = Math.Pow(rho, powerReal) * Math.Exp(-powerImaginary * theta);
 
            return FromPolarCoordinates(t, newRho);
        }
 
        public static Complex Pow(Complex value, double power)
        {
            return Pow(value, new Complex(power, 0));
        }
 
        private static Complex Scale(Complex value, double factor)
        {
            double realResult = factor * value.m_real;
            double imaginaryResuilt = factor * value.m_imaginary;
            return new Complex(realResult, imaginaryResuilt);
        }
 
        //
        // Explicit Conversions To Complex
        //
 
        public static explicit operator Complex(decimal value)
        {
            return new Complex((double)value, 0.0);
        }
 
        /// <summary>Explicitly converts a <see cref="Int128" /> value to a double-precision complex number.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to a double-precision complex number.</returns>
        public static explicit operator Complex(Int128 value)
        {
            return new Complex((double)value, 0.0);
        }
 
        public static explicit operator Complex(BigInteger value)
        {
            return new Complex((double)value, 0.0);
        }
 
        /// <summary>Explicitly converts a <see cref="UInt128" /> value to a double-precision complex number.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to a double-precision complex number.</returns>
        [CLSCompliant(false)]
        public static explicit operator Complex(UInt128 value)
        {
            return new Complex((double)value, 0.0);
        }
 
        //
        // Implicit Conversions To Complex
        //
 
        public static implicit operator Complex(byte value)
        {
            return new Complex(value, 0.0);
        }
 
        /// <summary>Implicitly converts a <see cref="char" /> value to a double-precision complex number.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to a double-precision complex number.</returns>
        public static implicit operator Complex(char value)
        {
            return new Complex(value, 0.0);
        }
 
        public static implicit operator Complex(double value)
        {
            return new Complex(value, 0.0);
        }
 
        /// <summary>Implicitly converts a <see cref="Half" /> value to a double-precision complex number.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to a double-precision complex number.</returns>
        public static implicit operator Complex(Half value)
        {
            return new Complex((double)value, 0.0);
        }
 
        public static implicit operator Complex(short value)
        {
            return new Complex(value, 0.0);
        }
 
        public static implicit operator Complex(int value)
        {
            return new Complex(value, 0.0);
        }
 
        public static implicit operator Complex(long value)
        {
            return new Complex(value, 0.0);
        }
 
        /// <summary>Implicitly converts a <see cref="IntPtr" /> value to a double-precision complex number.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to a double-precision complex number.</returns>
        public static implicit operator Complex(nint value)
        {
            return new Complex(value, 0.0);
        }
 
        [CLSCompliant(false)]
        public static implicit operator Complex(sbyte value)
        {
            return new Complex(value, 0.0);
        }
 
        public static implicit operator Complex(float value)
        {
            return new Complex(value, 0.0);
        }
 
        [CLSCompliant(false)]
        public static implicit operator Complex(ushort value)
        {
            return new Complex(value, 0.0);
        }
 
        [CLSCompliant(false)]
        public static implicit operator Complex(uint value)
        {
            return new Complex(value, 0.0);
        }
 
        [CLSCompliant(false)]
        public static implicit operator Complex(ulong value)
        {
            return new Complex(value, 0.0);
        }
 
        /// <summary>Implicitly converts a <see cref="UIntPtr" /> value to a double-precision complex number.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to a double-precision complex number.</returns>
        [CLSCompliant(false)]
        public static implicit operator Complex(nuint value)
        {
            return new Complex(value, 0.0);
        }
 
        //
        // IAdditiveIdentity
        //
 
        /// <inheritdoc cref="IAdditiveIdentity{TSelf, TResult}.AdditiveIdentity" />
        static Complex IAdditiveIdentity<Complex, Complex>.AdditiveIdentity => new Complex(0.0, 0.0);
 
        //
        // IDecrementOperators
        //
 
        /// <inheritdoc cref="IDecrementOperators{TSelf}.op_Decrement(TSelf)" />
        public static Complex operator --(Complex value) => value - One;
 
        //
        // IIncrementOperators
        //
 
        /// <inheritdoc cref="IIncrementOperators{TSelf}.op_Increment(TSelf)" />
        public static Complex operator ++(Complex value) => value + One;
 
        //
        // IMultiplicativeIdentity
        //
 
        /// <inheritdoc cref="IMultiplicativeIdentity{TSelf, TResult}.MultiplicativeIdentity" />
        static Complex IMultiplicativeIdentity<Complex, Complex>.MultiplicativeIdentity => new Complex(1.0, 0.0);
 
        //
        // INumberBase
        //
 
        /// <inheritdoc cref="INumberBase{TSelf}.One" />
        static Complex INumberBase<Complex>.One => new Complex(1.0, 0.0);
 
        /// <inheritdoc cref="INumberBase{TSelf}.Radix" />
        static int INumberBase<Complex>.Radix => 2;
 
        /// <inheritdoc cref="INumberBase{TSelf}.Zero" />
        static Complex INumberBase<Complex>.Zero => new Complex(0.0, 0.0);
 
        /// <inheritdoc cref="INumberBase{TSelf}.Abs(TSelf)" />
        static Complex INumberBase<Complex>.Abs(Complex value) => Abs(value);
 
        /// <inheritdoc cref="INumberBase{TSelf}.CreateChecked{TOther}(TOther)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Complex CreateChecked<TOther>(TOther value)
            where TOther : INumberBase<TOther>
        {
            Complex result;
 
            if (typeof(TOther) == typeof(Complex))
            {
                result = (Complex)(object)value;
            }
            else if (!TryConvertFrom(value, out result) && !TOther.TryConvertToChecked(value, out result))
            {
                ThrowHelper.ThrowNotSupportedException();
            }
 
            return result;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.CreateSaturating{TOther}(TOther)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Complex CreateSaturating<TOther>(TOther value)
            where TOther : INumberBase<TOther>
        {
            Complex result;
 
            if (typeof(TOther) == typeof(Complex))
            {
                result = (Complex)(object)value;
            }
            else if (!TryConvertFrom(value, out result) && !TOther.TryConvertToSaturating(value, out result))
            {
                ThrowHelper.ThrowNotSupportedException();
            }
 
            return result;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.CreateTruncating{TOther}(TOther)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Complex CreateTruncating<TOther>(TOther value)
            where TOther : INumberBase<TOther>
        {
            Complex result;
 
            if (typeof(TOther) == typeof(Complex))
            {
                result = (Complex)(object)value;
            }
            else if (!TryConvertFrom(value, out result) && !TOther.TryConvertToTruncating(value, out result))
            {
                ThrowHelper.ThrowNotSupportedException();
            }
 
            return result;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsCanonical(TSelf)" />
        static bool INumberBase<Complex>.IsCanonical(Complex value) => true;
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsComplexNumber(TSelf)" />
        public static bool IsComplexNumber(Complex value) => (value.m_real != 0.0) && (value.m_imaginary != 0.0);
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsEvenInteger(TSelf)" />
        public static bool IsEvenInteger(Complex value) => (value.m_imaginary == 0) && double.IsEvenInteger(value.m_real);
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsImaginaryNumber(TSelf)" />
        public static bool IsImaginaryNumber(Complex value) => (value.m_real == 0.0) && double.IsRealNumber(value.m_imaginary);
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsInteger(TSelf)" />
        public static bool IsInteger(Complex value) => (value.m_imaginary == 0) && double.IsInteger(value.m_real);
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsNegative(TSelf)" />
        public static bool IsNegative(Complex value)
        {
            // since complex numbers do not have a well-defined concept of
            // negative we report false if this value has an imaginary part
 
            return (value.m_imaginary == 0.0) && double.IsNegative(value.m_real);
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsNegativeInfinity(TSelf)" />
        public static bool IsNegativeInfinity(Complex value)
        {
            // since complex numbers do not have a well-defined concept of
            // negative we report false if this value has an imaginary part
 
            return (value.m_imaginary == 0.0) && double.IsNegativeInfinity(value.m_real);
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsNormal(TSelf)" />
        public static bool IsNormal(Complex value)
        {
            // much as IsFinite requires both part to be finite, we require both
            // part to be "normal" (finite, non-zero, and non-subnormal) to be true
 
            return double.IsNormal(value.m_real)
                && ((value.m_imaginary == 0.0) || double.IsNormal(value.m_imaginary));
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsOddInteger(TSelf)" />
        public static bool IsOddInteger(Complex value) => (value.m_imaginary == 0) && double.IsOddInteger(value.m_real);
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsPositive(TSelf)" />
        public static bool IsPositive(Complex value)
        {
            // since complex numbers do not have a well-defined concept of
            // negative we report false if this value has an imaginary part
 
            return (value.m_imaginary == 0.0) && double.IsPositive(value.m_real);
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsPositiveInfinity(TSelf)" />
        public static bool IsPositiveInfinity(Complex value)
        {
            // since complex numbers do not have a well-defined concept of
            // positive we report false if this value has an imaginary part
 
            return (value.m_imaginary == 0.0) && double.IsPositiveInfinity(value.m_real);
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsRealNumber(TSelf)" />
        public static bool IsRealNumber(Complex value) => (value.m_imaginary == 0.0) && double.IsRealNumber(value.m_real);
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsSubnormal(TSelf)" />
        public static bool IsSubnormal(Complex value)
        {
            // much as IsInfinite allows either part to be infinite, we allow either
            // part to be "subnormal" (finite, non-zero, and non-normal) to be true
 
            return double.IsSubnormal(value.m_real) || double.IsSubnormal(value.m_imaginary);
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsZero(TSelf)" />
        static bool INumberBase<Complex>.IsZero(Complex value) => (value.m_real == 0.0) && (value.m_imaginary == 0.0);
 
        /// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitude(TSelf, TSelf)" />
        public static Complex MaxMagnitude(Complex x, Complex y)
        {
            // complex numbers are not normally comparable, however every complex
            // number has a real magnitude (absolute value) and so we can provide
            // an implementation for MaxMagnitude
 
            // This matches the IEEE 754:2019 `maximumMagnitude` function
            //
            // It propagates NaN inputs back to the caller and
            // otherwise returns the input with a larger magnitude.
            // It treats +0 as larger than -0 as per the specification.
 
            double ax = Abs(x);
            double ay = Abs(y);
 
            if ((ax > ay) || double.IsNaN(ax))
            {
                return x;
            }
 
            if (ax == ay)
            {
                // We have two equal magnitudes which means we have two of the following
                //   `+a + ib`
                //   `-a + ib`
                //   `+a - ib`
                //   `-a - ib`
                //
                // We want to treat `+a + ib` as greater than everything and `-a - ib` as
                // lesser. For `-a + ib` and `+a - ib` its "ambiguous" which should be preferred
                // so we will just preference `+a - ib` since that's the most correct choice
                // in the face of something like `+a - i0.0` vs `-a + i0.0`. This is the "most
                // correct" choice because both represent real numbers and `+a` is preferred
                // over `-a`.
 
                if (double.IsNegative(y.m_real))
                {
                    if (double.IsNegative(y.m_imaginary))
                    {
                        // when `y` is `-a - ib` we always prefer `x` (its either the same as
                        // `x` or some part of `x` is positive).
 
                        return x;
                    }
                    else
                    {
                        if (double.IsNegative(x.m_real))
                        {
                            // when `y` is `-a + ib` and `x` is `-a + ib` or `-a - ib` then
                            // we either have same value or both parts of `x` are negative
                            // and we want to prefer `y`.
 
                            return y;
                        }
                        else
                        {
                            // when `y` is `-a + ib` and `x` is `+a + ib` or `+a - ib` then
                            // we want to prefer `x` because either both parts are positive
                            // or we want to prefer `+a - ib` due to how it handles when `x`
                            // represents a real number.
 
                            return x;
                        }
                    }
                }
                else if (double.IsNegative(y.m_imaginary))
                {
                    if (double.IsNegative(x.m_real))
                    {
                        // when `y` is `+a - ib` and `x` is `-a + ib` or `-a - ib` then
                        // we either both parts of `x` are negative or we want to prefer
                        // `+a - ib` due to how it handles when `y` represents a real number.
 
                        return y;
                    }
                    else
                    {
                        // when `y` is `+a - ib` and `x` is `+a + ib` or `+a - ib` then
                        // we want to prefer `x` because either both parts are positive
                        // or they represent the same value.
 
                        return x;
                    }
                }
            }
 
            return y;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitudeNumber(TSelf, TSelf)" />
        static Complex INumberBase<Complex>.MaxMagnitudeNumber(Complex x, Complex y)
        {
            // complex numbers are not normally comparable, however every complex
            // number has a real magnitude (absolute value) and so we can provide
            // an implementation for MaxMagnitudeNumber
 
            // This matches the IEEE 754:2019 `maximumMagnitudeNumber` function
            //
            // It does not propagate NaN inputs back to the caller and
            // otherwise returns the input with a larger magnitude.
            // It treats +0 as larger than -0 as per the specification.
 
            double ax = Abs(x);
            double ay = Abs(y);
 
            if ((ax > ay) || double.IsNaN(ay))
            {
                return x;
            }
 
            if (ax == ay)
            {
                // We have two equal magnitudes which means we have two of the following
                //   `+a + ib`
                //   `-a + ib`
                //   `+a - ib`
                //   `-a - ib`
                //
                // We want to treat `+a + ib` as greater than everything and `-a - ib` as
                // lesser. For `-a + ib` and `+a - ib` its "ambiguous" which should be preferred
                // so we will just preference `+a - ib` since that's the most correct choice
                // in the face of something like `+a - i0.0` vs `-a + i0.0`. This is the "most
                // correct" choice because both represent real numbers and `+a` is preferred
                // over `-a`.
 
                if (double.IsNegative(y.m_real))
                {
                    if (double.IsNegative(y.m_imaginary))
                    {
                        // when `y` is `-a - ib` we always prefer `x` (its either the same as
                        // `x` or some part of `x` is positive).
 
                        return x;
                    }
                    else
                    {
                        if (double.IsNegative(x.m_real))
                        {
                            // when `y` is `-a + ib` and `x` is `-a + ib` or `-a - ib` then
                            // we either have same value or both parts of `x` are negative
                            // and we want to prefer `y`.
 
                            return y;
                        }
                        else
                        {
                            // when `y` is `-a + ib` and `x` is `+a + ib` or `+a - ib` then
                            // we want to prefer `x` because either both parts are positive
                            // or we want to prefer `+a - ib` due to how it handles when `x`
                            // represents a real number.
 
                            return x;
                        }
                    }
                }
                else if (double.IsNegative(y.m_imaginary))
                {
                    if (double.IsNegative(x.m_real))
                    {
                        // when `y` is `+a - ib` and `x` is `-a + ib` or `-a - ib` then
                        // we either both parts of `x` are negative or we want to prefer
                        // `+a - ib` due to how it handles when `y` represents a real number.
 
                        return y;
                    }
                    else
                    {
                        // when `y` is `+a - ib` and `x` is `+a + ib` or `+a - ib` then
                        // we want to prefer `x` because either both parts are positive
                        // or they represent the same value.
 
                        return x;
                    }
                }
            }
 
            return y;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.MinMagnitude(TSelf, TSelf)" />
        public static Complex MinMagnitude(Complex x, Complex y)
        {
            // complex numbers are not normally comparable, however every complex
            // number has a real magnitude (absolute value) and so we can provide
            // an implementation for MaxMagnitude
 
            // This matches the IEEE 754:2019 `minimumMagnitude` function
            //
            // It propagates NaN inputs back to the caller and
            // otherwise returns the input with a smaller magnitude.
            // It treats -0 as smaller than +0 as per the specification.
 
            double ax = Abs(x);
            double ay = Abs(y);
 
            if ((ax < ay) || double.IsNaN(ax))
            {
                return x;
            }
 
            if (ax == ay)
            {
                // We have two equal magnitudes which means we have two of the following
                //   `+a + ib`
                //   `-a + ib`
                //   `+a - ib`
                //   `-a - ib`
                //
                // We want to treat `+a + ib` as greater than everything and `-a - ib` as
                // lesser. For `-a + ib` and `+a - ib` its "ambiguous" which should be preferred
                // so we will just preference `-a + ib` since that's the most correct choice
                // in the face of something like `+a - i0.0` vs `-a + i0.0`. This is the "most
                // correct" choice because both represent real numbers and `-a` is preferred
                // over `+a`.
 
                if (double.IsNegative(y.m_real))
                {
                    if (double.IsNegative(y.m_imaginary))
                    {
                        // when `y` is `-a - ib` we always prefer `y` as both parts are negative
                        return y;
                    }
                    else
                    {
                        if (double.IsNegative(x.m_real))
                        {
                            // when `y` is `-a + ib` and `x` is `-a + ib` or `-a - ib` then
                            // we either have same value or both parts of `x` are negative
                            // and we want to prefer it.
 
                            return x;
                        }
                        else
                        {
                            // when `y` is `-a + ib` and `x` is `+a + ib` or `+a - ib` then
                            // we want to prefer `y` because either both parts of 'x' are positive
                            // or we want to prefer `-a - ib` due to how it handles when `y`
                            // represents a real number.
 
                            return y;
                        }
                    }
                }
                else if (double.IsNegative(y.m_imaginary))
                {
                    if (double.IsNegative(x.m_real))
                    {
                        // when `y` is `+a - ib` and `x` is `-a + ib` or `-a - ib` then
                        // either both parts of `x` are negative or we want to prefer
                        // `-a - ib` due to how it handles when `x` represents a real number.
 
                        return x;
                    }
                    else
                    {
                        // when `y` is `+a - ib` and `x` is `+a + ib` or `+a - ib` then
                        // we want to prefer `y` because either both parts of x are positive
                        // or they represent the same value.
 
                        return y;
                    }
                }
                else
                {
                    return x;
                }
            }
 
            return y;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.MinMagnitudeNumber(TSelf, TSelf)" />
        static Complex INumberBase<Complex>.MinMagnitudeNumber(Complex x, Complex y)
        {
            // complex numbers are not normally comparable, however every complex
            // number has a real magnitude (absolute value) and so we can provide
            // an implementation for MinMagnitudeNumber
 
            // This matches the IEEE 754:2019 `minimumMagnitudeNumber` function
            //
            // It does not propagate NaN inputs back to the caller and
            // otherwise returns the input with a smaller magnitude.
            // It treats -0 as smaller than +0 as per the specification.
 
            double ax = Abs(x);
            double ay = Abs(y);
 
            if ((ax < ay) || double.IsNaN(ay))
            {
                return x;
            }
 
            if (ax == ay)
            {
                // We have two equal magnitudes which means we have two of the following
                //   `+a + ib`
                //   `-a + ib`
                //   `+a - ib`
                //   `-a - ib`
                //
                // We want to treat `+a + ib` as greater than everything and `-a - ib` as
                // lesser. For `-a + ib` and `+a - ib` its "ambiguous" which should be preferred
                // so we will just preference `-a + ib` since that's the most correct choice
                // in the face of something like `+a - i0.0` vs `-a + i0.0`. This is the "most
                // correct" choice because both represent real numbers and `-a` is preferred
                // over `+a`.
 
                if (double.IsNegative(y.m_real))
                {
                    if (double.IsNegative(y.m_imaginary))
                    {
                        // when `y` is `-a - ib` we always prefer `y` as both parts are negative
                        return y;
                    }
                    else
                    {
                        if (double.IsNegative(x.m_real))
                        {
                            // when `y` is `-a + ib` and `x` is `-a + ib` or `-a - ib` then
                            // we either have same value or both parts of `x` are negative
                            // and we want to prefer it.
 
                            return x;
                        }
                        else
                        {
                            // when `y` is `-a + ib` and `x` is `+a + ib` or `+a - ib` then
                            // we want to prefer `y` because either both parts of 'x' are positive
                            // or we want to prefer `-a - ib` due to how it handles when `y`
                            // represents a real number.
 
                            return y;
                        }
                    }
                }
                else if (double.IsNegative(y.m_imaginary))
                {
                    if (double.IsNegative(x.m_real))
                    {
                        // when `y` is `+a - ib` and `x` is `-a + ib` or `-a - ib` then
                        // either both parts of `x` are negative or we want to prefer
                        // `-a - ib` due to how it handles when `x` represents a real number.
 
                        return x;
                    }
                    else
                    {
                        // when `y` is `+a - ib` and `x` is `+a + ib` or `+a - ib` then
                        // we want to prefer `y` because either both parts of x are positive
                        // or they represent the same value.
 
                        return y;
                    }
                }
                else
                {
                    return x;
                }
            }
 
            return y;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.MultiplyAddEstimate(TSelf, TSelf, TSelf)" />
        static Complex INumberBase<Complex>.MultiplyAddEstimate(Complex left, Complex right, Complex addend)
        {
            // Multiplication:  (a + bi)(c + di) = (ac - bd) + (bc + ad)i
            // Addition:        (a + bi) + (c + di) = (a + c) + (b + d)i
 
            double result_realpart = addend.m_real;
            result_realpart = double.MultiplyAddEstimate(-left.m_imaginary, right.m_imaginary, result_realpart);
            result_realpart = double.MultiplyAddEstimate(left.m_real, right.m_real, result_realpart);
 
            double result_imaginarypart = addend.m_imaginary;
            result_imaginarypart = double.MultiplyAddEstimate(left.m_real, right.m_imaginary, result_imaginarypart);
            result_imaginarypart = double.MultiplyAddEstimate(left.m_imaginary, right.m_real, result_imaginarypart);
 
            return new Complex(result_realpart, result_imaginarypart);
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.Parse(ReadOnlySpan{char}, NumberStyles, IFormatProvider?)" />
        public static Complex Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider)
        {
            if (!TryParse(s, style, provider, out Complex result))
            {
                ThrowHelper.ThrowOverflowException();
            }
            return result;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.Parse(string, NumberStyles, IFormatProvider?)" />
        public static Complex Parse(string s, NumberStyles style, IFormatProvider? provider)
        {
            ArgumentNullException.ThrowIfNull(s);
            return Parse(s.AsSpan(), style, provider);
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromChecked{TOther}(TOther, out TSelf)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<Complex>.TryConvertFromChecked<TOther>(TOther value, out Complex result)
        {
            return TryConvertFrom<TOther>(value, out result);
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromSaturating{TOther}(TOther, out TSelf)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<Complex>.TryConvertFromSaturating<TOther>(TOther value, out Complex result)
        {
            return TryConvertFrom<TOther>(value, out result);
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromTruncating{TOther}(TOther, out TSelf)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<Complex>.TryConvertFromTruncating<TOther>(TOther value, out Complex result)
        {
            return TryConvertFrom<TOther>(value, out result);
        }
 
        private static bool TryConvertFrom<TOther>(TOther value, out Complex result)
            where TOther : INumberBase<TOther>
        {
            // We don't want to defer to `double.Create*(value)` because some type might have its own
            // `TOther.ConvertTo*(value, out Complex result)` handling that would end up bypassed.
 
            if (typeof(TOther) == typeof(byte))
            {
                byte actualValue = (byte)(object)value;
                result = actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(char))
            {
                char actualValue = (char)(object)value;
                result = actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(decimal))
            {
                decimal actualValue = (decimal)(object)value;
                result = (Complex)actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(double))
            {
                double actualValue = (double)(object)value;
                result = actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(Half))
            {
                Half actualValue = (Half)(object)value;
                result = actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(short))
            {
                short actualValue = (short)(object)value;
                result = actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(int))
            {
                int actualValue = (int)(object)value;
                result = actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(long))
            {
                long actualValue = (long)(object)value;
                result = actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(Int128))
            {
                Int128 actualValue = (Int128)(object)value;
                result = (Complex)actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(nint))
            {
                nint actualValue = (nint)(object)value;
                result = actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(sbyte))
            {
                sbyte actualValue = (sbyte)(object)value;
                result = actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(float))
            {
                float actualValue = (float)(object)value;
                result = actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(ushort))
            {
                ushort actualValue = (ushort)(object)value;
                result = actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(uint))
            {
                uint actualValue = (uint)(object)value;
                result = actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(ulong))
            {
                ulong actualValue = (ulong)(object)value;
                result = actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(UInt128))
            {
                UInt128 actualValue = (UInt128)(object)value;
                result = (Complex)actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(nuint))
            {
                nuint actualValue = (nuint)(object)value;
                result = actualValue;
                return true;
            }
            else
            {
                result = default;
                return false;
            }
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<Complex>.TryConvertToChecked<TOther>(Complex value, [MaybeNullWhen(false)] out TOther result)
        {
            // Complex numbers with an imaginary part can't be represented as a "real number"
            // so we'll throw an OverflowException for this scenario for integer types and
            // for decimal. However, we will convert it to NaN for the floating-point types,
            // since that's what Sqrt(-1) (which is `new Complex(0, 1)`) results in.
 
            if (typeof(TOther) == typeof(byte))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                byte actualResult = checked((byte)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(char))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                char actualResult = checked((char)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(decimal))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                decimal actualResult = checked((decimal)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(double))
            {
                double actualResult = (value.m_imaginary != 0) ? double.NaN : value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Half))
            {
                Half actualResult = (value.m_imaginary != 0) ? Half.NaN : (Half)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(short))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                short actualResult = checked((short)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(int))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                int actualResult = checked((int)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(long))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                long actualResult = checked((long)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Int128))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                Int128 actualResult = checked((Int128)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(nint))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                nint actualResult = checked((nint)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(BigInteger))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                BigInteger actualResult = checked((BigInteger)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(sbyte))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                sbyte actualResult = checked((sbyte)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(float))
            {
                float actualResult = (value.m_imaginary != 0) ? float.NaN : (float)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(ushort))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                ushort actualResult = checked((ushort)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(uint))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                uint actualResult = checked((uint)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(ulong))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                ulong actualResult = checked((ulong)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(UInt128))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                UInt128 actualResult = checked((UInt128)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(nuint))
            {
                if (value.m_imaginary != 0)
                {
                    ThrowHelper.ThrowOverflowException();
                }
 
                nuint actualResult = checked((nuint)value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }
            else
            {
                result = default;
                return false;
            }
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<Complex>.TryConvertToSaturating<TOther>(Complex value, [MaybeNullWhen(false)] out TOther result)
        {
            // Complex numbers with an imaginary part can't be represented as a "real number"
            // and there isn't really a well-defined way to "saturate" to just a real value.
            //
            // The two potential options are that we either treat complex numbers with a non-
            // zero imaginary part as NaN and then convert that to 0 -or- we ignore the imaginary
            // part and only consider the real part.
            //
            // We use the latter below since that is "more useful" given an unknown number type.
            // Users who want 0 instead can always check `IsComplexNumber` and special-case the
            // handling.
 
            if (typeof(TOther) == typeof(byte))
            {
                byte actualResult = (value.m_real >= byte.MaxValue) ? byte.MaxValue :
                                    (value.m_real <= byte.MinValue) ? byte.MinValue : (byte)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(char))
            {
                char actualResult = (value.m_real >= char.MaxValue) ? char.MaxValue :
                                    (value.m_real <= char.MinValue) ? char.MinValue : (char)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(decimal))
            {
                decimal actualResult = (value.m_real >= (double)decimal.MaxValue) ? decimal.MaxValue :
                                       (value.m_real <= (double)decimal.MinValue) ? decimal.MinValue : (decimal)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(double))
            {
                double actualResult = value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Half))
            {
                Half actualResult = (Half)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(short))
            {
                short actualResult = (value.m_real >= short.MaxValue) ? short.MaxValue :
                                     (value.m_real <= short.MinValue) ? short.MinValue : (short)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(int))
            {
                int actualResult = (value.m_real >= int.MaxValue) ? int.MaxValue :
                                   (value.m_real <= int.MinValue) ? int.MinValue : (int)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(long))
            {
                long actualResult = (value.m_real >= long.MaxValue) ? long.MaxValue :
                                    (value.m_real <= long.MinValue) ? long.MinValue : (long)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Int128))
            {
                Int128 actualResult = (value.m_real >= +170141183460469231731687303715884105727.0) ? Int128.MaxValue :
                                      (value.m_real <= -170141183460469231731687303715884105728.0) ? Int128.MinValue : (Int128)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(nint))
            {
                nint actualResult = (value.m_real >= nint.MaxValue) ? nint.MaxValue :
                                    (value.m_real <= nint.MinValue) ? nint.MinValue : (nint)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(BigInteger))
            {
                BigInteger actualResult = (BigInteger)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(sbyte))
            {
                sbyte actualResult = (value.m_real >= sbyte.MaxValue) ? sbyte.MaxValue :
                                     (value.m_real <= sbyte.MinValue) ? sbyte.MinValue : (sbyte)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(float))
            {
                float actualResult = (float)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(ushort))
            {
                ushort actualResult = (value.m_real >= ushort.MaxValue) ? ushort.MaxValue :
                                      (value.m_real <= ushort.MinValue) ? ushort.MinValue : (ushort)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(uint))
            {
                uint actualResult = (value.m_real >= uint.MaxValue) ? uint.MaxValue :
                                    (value.m_real <= uint.MinValue) ? uint.MinValue : (uint)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(ulong))
            {
                ulong actualResult = (value.m_real >= ulong.MaxValue) ? ulong.MaxValue :
                                     (value.m_real <= ulong.MinValue) ? ulong.MinValue : (ulong)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(UInt128))
            {
                UInt128 actualResult = (value.m_real >= 340282366920938463463374607431768211455.0) ? UInt128.MaxValue :
                                       (value.m_real <= 0.0) ? UInt128.MinValue : (UInt128)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(nuint))
            {
                nuint actualResult = (value.m_real >= nuint.MaxValue) ? nuint.MaxValue :
                                     (value.m_real <= nuint.MinValue) ? nuint.MinValue : (nuint)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else
            {
                result = default;
                return false;
            }
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<Complex>.TryConvertToTruncating<TOther>(Complex value, [MaybeNullWhen(false)] out TOther result)
        {
            // Complex numbers with an imaginary part can't be represented as a "real number"
            // so we'll only consider the real part for the purposes of truncation.
 
            if (typeof(TOther) == typeof(byte))
            {
                byte actualResult = (value.m_real >= byte.MaxValue) ? byte.MaxValue :
                                    (value.m_real <= byte.MinValue) ? byte.MinValue : (byte)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(char))
            {
                char actualResult = (value.m_real >= char.MaxValue) ? char.MaxValue :
                                    (value.m_real <= char.MinValue) ? char.MinValue : (char)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(decimal))
            {
                decimal actualResult = (value.m_real >= (double)decimal.MaxValue) ? decimal.MaxValue :
                                       (value.m_real <= (double)decimal.MinValue) ? decimal.MinValue : (decimal)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(double))
            {
                double actualResult = value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Half))
            {
                Half actualResult = (Half)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(short))
            {
                short actualResult = (value.m_real >= short.MaxValue) ? short.MaxValue :
                                     (value.m_real <= short.MinValue) ? short.MinValue : (short)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(int))
            {
                int actualResult = (value.m_real >= int.MaxValue) ? int.MaxValue :
                                   (value.m_real <= int.MinValue) ? int.MinValue : (int)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(long))
            {
                long actualResult = (value.m_real >= long.MaxValue) ? long.MaxValue :
                                    (value.m_real <= long.MinValue) ? long.MinValue : (long)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Int128))
            {
                Int128 actualResult = (value.m_real >= +170141183460469231731687303715884105727.0) ? Int128.MaxValue :
                                      (value.m_real <= -170141183460469231731687303715884105728.0) ? Int128.MinValue : (Int128)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(nint))
            {
                nint actualResult = (value.m_real >= nint.MaxValue) ? nint.MaxValue :
                                    (value.m_real <= nint.MinValue) ? nint.MinValue : (nint)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(BigInteger))
            {
                BigInteger actualResult = (BigInteger)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(sbyte))
            {
                sbyte actualResult = (value.m_real >= sbyte.MaxValue) ? sbyte.MaxValue :
                                     (value.m_real <= sbyte.MinValue) ? sbyte.MinValue : (sbyte)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(float))
            {
                float actualResult = (float)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(ushort))
            {
                ushort actualResult = (value.m_real >= ushort.MaxValue) ? ushort.MaxValue :
                                      (value.m_real <= ushort.MinValue) ? ushort.MinValue : (ushort)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(uint))
            {
                uint actualResult = (value.m_real >= uint.MaxValue) ? uint.MaxValue :
                                    (value.m_real <= uint.MinValue) ? uint.MinValue : (uint)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(ulong))
            {
                ulong actualResult = (value.m_real >= ulong.MaxValue) ? ulong.MaxValue :
                                     (value.m_real <= ulong.MinValue) ? ulong.MinValue : (ulong)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(UInt128))
            {
                UInt128 actualResult = (value.m_real >= 340282366920938463463374607431768211455.0) ? UInt128.MaxValue :
                                       (value.m_real <= 0.0) ? UInt128.MinValue : (UInt128)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(nuint))
            {
                nuint actualResult = (value.m_real >= nuint.MaxValue) ? nuint.MaxValue :
                                     (value.m_real <= nuint.MinValue) ? nuint.MinValue : (nuint)value.m_real;
                result = (TOther)(object)actualResult;
                return true;
            }
            else
            {
                result = default;
                return false;
            }
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.TryParse(ReadOnlySpan{char}, NumberStyles, IFormatProvider?, out TSelf)" />
        public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out Complex result)
        {
            ValidateParseStyleFloatingPoint(style);
 
            int openBracket = s.IndexOf('<');
            int semicolon = s.IndexOf(';');
            int closeBracket = s.IndexOf('>');
 
            if ((s.Length < 5) || (openBracket == -1) || (semicolon == -1) || (closeBracket == -1) || (openBracket > semicolon) || (openBracket > closeBracket) || (semicolon > closeBracket))
            {
                // We need at least 5 characters for `<0;0>`
                // We also expect a to find an open bracket, a semicolon, and a closing bracket in that order
 
                result = default;
                return false;
            }
 
            if ((openBracket != 0) && (((style & NumberStyles.AllowLeadingWhite) == 0) || !s.Slice(0, openBracket).IsWhiteSpace()))
            {
                // The opening bracket wasn't the first and we either didn't allow leading whitespace
                // or one of the leading characters wasn't whitespace at all.
 
                result = default;
                return false;
            }
 
            if (!double.TryParse(s.Slice(openBracket + 1, semicolon - openBracket - 1), style, provider, out double real))
            {
                result = default;
                return false;
            }
 
            if (char.IsWhiteSpace(s[semicolon + 1]))
            {
                // We allow a single whitespace after the semicolon regardless of style, this is so that
                // the output of `ToString` can be correctly parsed by default and values will roundtrip.
                semicolon += 1;
            }
 
            if (!double.TryParse(s.Slice(semicolon + 1, closeBracket - semicolon - 1), style, provider, out double imaginary))
            {
                result = default;
                return false;
            }
 
            if ((closeBracket != (s.Length - 1)) && (((style & NumberStyles.AllowTrailingWhite) == 0) || !s.Slice(closeBracket).IsWhiteSpace()))
            {
                // The closing bracket wasn't the last and we either didn't allow trailing whitespace
                // or one of the trailing characters wasn't whitespace at all.
 
                result = default;
                return false;
            }
 
            result = new Complex(real, imaginary);
            return true;
 
            static void ValidateParseStyleFloatingPoint(NumberStyles style)
            {
                // Check for undefined flags or hex number
                if ((style & (InvalidNumberStyles | NumberStyles.AllowHexSpecifier)) != 0)
                {
                    ThrowInvalid(style);
 
                    static void ThrowInvalid(NumberStyles value)
                    {
                        if ((value & InvalidNumberStyles) != 0)
                        {
                            throw new ArgumentException(SR.Argument_InvalidNumberStyles, nameof(style));
                        }
 
                        throw new ArgumentException(SR.Arg_HexStyleNotSupported);
                    }
                }
            }
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.TryParse(string, NumberStyles, IFormatProvider?, out TSelf)" />
        public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Complex result)
        {
            if (s is null)
            {
                result = default;
                return false;
            }
            return TryParse(s.AsSpan(), style, provider, out result);
        }
 
        //
        // IParsable
        //
 
        /// <inheritdoc cref="IParsable{TSelf}.Parse(string, IFormatProvider?)" />
        public static Complex Parse(string s, IFormatProvider? provider) => Parse(s, DefaultNumberStyle, provider);
 
        /// <inheritdoc cref="IParsable{TSelf}.TryParse(string?, IFormatProvider?, out TSelf)" />
        public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out Complex result) => TryParse(s, DefaultNumberStyle, provider, out result);
 
        //
        // ISignedNumber
        //
 
        /// <inheritdoc cref="ISignedNumber{TSelf}.NegativeOne" />
        static Complex ISignedNumber<Complex>.NegativeOne => new Complex(-1.0, 0.0);
 
        //
        // ISpanFormattable
        //
 
        /// <inheritdoc cref="ISpanFormattable.TryFormat(Span{char}, out int, ReadOnlySpan{char}, IFormatProvider?)" />
        public bool TryFormat(Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null) =>
            TryFormatCore(destination, out charsWritten, format, provider);
 
        /// <inheritdoc cref="IUtf8SpanFormattable.TryFormat(Span{byte}, out int, ReadOnlySpan{char}, IFormatProvider?)" />
        public bool TryFormat(Span<byte> utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null) =>
            TryFormatCore(utf8Destination, out bytesWritten, format, provider);
 
        private bool TryFormatCore<TChar>(Span<TChar> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider) where TChar : unmanaged, IBinaryInteger<TChar>
        {
            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 
            // We have at least 6 more characters for: <0; 0>
            if (destination.Length >= 6)
            {
                int realChars;
                if (typeof(TChar) == typeof(char) ?
                    m_real.TryFormat(Unsafe.BitCast<Span<TChar>, Span<char>>(destination.Slice(1)), out realChars, format, provider) :
                    m_real.TryFormat(Unsafe.BitCast<Span<TChar>, Span<byte>>(destination.Slice(1)), out realChars, format, provider))
                {
                    destination[0] = TChar.CreateTruncating('<');
                    destination = destination.Slice(1 + realChars); // + 1 for <
 
                    // We have at least 4 more characters for: ; 0>
                    if (destination.Length >= 4)
                    {
                        int imaginaryChars;
                        if (typeof(TChar) == typeof(char) ?
                            m_imaginary.TryFormat(Unsafe.BitCast<Span<TChar>, Span<char>>(destination.Slice(2)), out imaginaryChars, format, provider) :
                            m_imaginary.TryFormat(Unsafe.BitCast<Span<TChar>, Span<byte>>(destination.Slice(2)), out imaginaryChars, format, provider))
                        {
                            // We have 1 more character for: >
                            if ((uint)(2 + imaginaryChars) < (uint)destination.Length)
                            {
                                destination[0] = TChar.CreateTruncating(';');
                                destination[1] = TChar.CreateTruncating(' ');
                                destination[2 + imaginaryChars] = TChar.CreateTruncating('>');
 
                                charsWritten = realChars + imaginaryChars + 4;
                                return true;
                            }
                        }
                    }
                }
            }
 
            charsWritten = 0;
            return false;
        }
 
        //
        // ISpanParsable
        //
 
        /// <inheritdoc cref="ISpanParsable{TSelf}.Parse(ReadOnlySpan{char}, IFormatProvider?)" />
        public static Complex Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => Parse(s, DefaultNumberStyle, provider);
 
        /// <inheritdoc cref="ISpanParsable{TSelf}.TryParse(ReadOnlySpan{char}, IFormatProvider?, out TSelf)" />
        public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out Complex result) => TryParse(s, DefaultNumberStyle, provider, out result);
 
        //
        // IUnaryPlusOperators
        //
 
        /// <inheritdoc cref="IUnaryPlusOperators{TSelf, TResult}.op_UnaryPlus(TSelf)" />
        public static Complex operator +(Complex value) => value;
    }
}