File: System\Numerics\Complex.Generic.cs
Web Access
Project: src\runtime\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.Buffers;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;

namespace System.Numerics
{
    /// <summary>
    /// A generic complex number z = x + yi, where x and y are of type <typeparamref name="T"/>.
    /// </summary>
    /// <typeparam name="T">The floating-point type used for the real and imaginary components.</typeparam>
    public readonly struct Complex<T>
        : IEquatable<Complex<T>>,
          IFormattable,
          INumberBase<Complex<T>>,
          ISignedNumber<Complex<T>>,
          IUtf8SpanFormattable
        where T : IFloatingPointIeee754<T>, IMinMaxValue<T>
    {
        public static Complex<T> Zero => new(T.Zero, T.Zero);
        public static Complex<T> One => new(T.One, T.Zero);
        public static Complex<T> ImaginaryOne => new(T.Zero, T.One);
        public static Complex<T> NaN => new(T.NaN, T.NaN);
        public static Complex<T> Infinity => new(T.PositiveInfinity, T.PositiveInfinity);

        // 1 / Log(10)
        private static readonly T s_inverseOfLog10 = T.One / T.Log(T.CreateChecked(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 T s_sqrtRescaleThreshold = T.MaxValue / (T.Sqrt(T.CreateChecked(2)) + T.One);

        // 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 T s_asinOverflowThreshold = T.Sqrt(T.MaxValue) / T.CreateChecked(2);

        // This value is used inside Asin and Acos.
        private static readonly T s_log2 = T.Log(T.CreateChecked(2));

        private readonly T m_real;
        private readonly T m_imaginary;

        public Complex(T real, T imaginary)
        {
            m_real = real;
            m_imaginary = imaginary;
        }

        public T Real => m_real;
        public T Imaginary => m_imaginary;

        public T GetMagnitude() => Abs(this);
        public T GetPhase() => T.Atan2(m_imaginary, m_real);

        public static Complex<T> FromPolarCoordinates(T magnitude, T phase)
        {
            (T sin, T cos) = T.SinCos(phase);
            return new Complex<T>(magnitude * cos, magnitude * sin);
        }

        public static Complex<T> Negate(Complex<T> value)
        {
            return -value;
        }

        public static Complex<T> Add(Complex<T> left, Complex<T> right)
        {
            return left + right;
        }

        public static Complex<T> Add(Complex<T> left, T right)
        {
            return left + right;
        }

        public static Complex<T> Add(T left, Complex<T> right)
        {
            return left + right;
        }

        public static Complex<T> Subtract(Complex<T> left, Complex<T> right)
        {
            return left - right;
        }

        public static Complex<T> Subtract(Complex<T> left, T right)
        {
            return left - right;
        }

        public static Complex<T> Subtract(T left, Complex<T> right)
        {
            return left - right;
        }

        public static Complex<T> Multiply(Complex<T> left, Complex<T> right)
        {
            return left * right;
        }

        public static Complex<T> Multiply(Complex<T> left, T right)
        {
            return left * right;
        }

        public static Complex<T> Multiply(T left, Complex<T> right)
        {
            return left * right;
        }

        public static Complex<T> Divide(Complex<T> dividend, Complex<T> divisor)
        {
            return dividend / divisor;
        }

        public static Complex<T> Divide(Complex<T> dividend, T divisor)
        {
            return dividend / divisor;
        }

        public static Complex<T> Divide(T dividend, Complex<T> divisor)
        {
            return dividend / divisor;
        }

        public static Complex<T> operator -(Complex<T> value)
        {
            return new Complex<T>(-value.m_real, -value.m_imaginary);
        }

        public static Complex<T> operator +(Complex<T> left, Complex<T> right)
        {
            return new Complex<T>(left.m_real + right.m_real, left.m_imaginary + right.m_imaginary);
        }

        public static Complex<T> operator +(Complex<T> left, T right)
        {
            return new Complex<T>(left.m_real + right, left.m_imaginary);
        }

        public static Complex<T> operator +(T left, Complex<T> right)
        {
            return new Complex<T>(left + right.m_real, right.m_imaginary);
        }

        public static Complex<T> operator -(Complex<T> left, Complex<T> right)
        {
            return new Complex<T>(left.m_real - right.m_real, left.m_imaginary - right.m_imaginary);
        }

        public static Complex<T> operator -(Complex<T> left, T right)
        {
            return new Complex<T>(left.m_real - right, left.m_imaginary);
        }

        public static Complex<T> operator -(T left, Complex<T> right)
        {
            return new Complex<T>(left - right.m_real, -right.m_imaginary);
        }

        public static Complex<T> operator *(Complex<T> left, Complex<T> right)
        {
            T a = left.m_real;
            T b = left.m_imaginary;
            T c = right.m_real;
            T d = right.m_imaginary;

            // Multiplication:  (a + bi)(c + di) = (ac - bd) + (bc + ad)i
            T x = (a * c) - (b * d);
            T y = (b * c) + (a * d);

            if (T.IsNaN(x) && T.IsNaN(y))
            {
                // Outlined so the naive common path stays small enough to inline.
                return MultiplyNaNRecovery(a, b, c, d, x, y);
            }

            return new Complex<T>(x, y);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static Complex<T> MultiplyNaNRecovery(T a, T b, T c, T d, T x, T y)
        {
            // C23 Annex G.5.1: recover a directed infinity that overflowed into a
            // spurious NaN from the naive multiply above.
            bool recalc = false;

            if (T.IsInfinity(a) || T.IsInfinity(b))
            {
                // left is infinite; normalize its parts to a signed 1/0
                a = T.CopySign(T.IsInfinity(a) ? T.One : T.Zero, a);
                b = T.CopySign(T.IsInfinity(b) ? T.One : T.Zero, b);

                if (T.IsNaN(c))
                {
                    c = T.CopySign(T.Zero, c);
                }

                if (T.IsNaN(d))
                {
                    d = T.CopySign(T.Zero, d);
                }

                recalc = true;
            }

            if (T.IsInfinity(c) || T.IsInfinity(d))
            {
                // right is infinite; normalize its parts to a signed 1/0
                c = T.CopySign(T.IsInfinity(c) ? T.One : T.Zero, c);
                d = T.CopySign(T.IsInfinity(d) ? T.One : T.Zero, d);

                if (T.IsNaN(a))
                {
                    a = T.CopySign(T.Zero, a);
                }

                if (T.IsNaN(b))
                {
                    b = T.CopySign(T.Zero, b);
                }

                recalc = true;
            }

            if (!recalc && (T.IsInfinity(a * c) || T.IsInfinity(b * d) || T.IsInfinity(a * d) || T.IsInfinity(b * c)))
            {
                // neither operand is infinite, but a product overflowed with a NaN
                // operand; treat the NaN as a signed zero and recover.
                if (T.IsNaN(a))
                {
                    a = T.CopySign(T.Zero, a);
                }

                if (T.IsNaN(b))
                {
                    b = T.CopySign(T.Zero, b);
                }

                if (T.IsNaN(c))
                {
                    c = T.CopySign(T.Zero, c);
                }

                if (T.IsNaN(d))
                {
                    d = T.CopySign(T.Zero, d);
                }

                recalc = true;
            }

            if (recalc)
            {
                T inf = T.PositiveInfinity;
                x = inf * ((a * c) - (b * d));
                y = inf * ((b * c) + (a * d));
            }

            return new Complex<T>(x, y);
        }

        public static Complex<T> operator *(Complex<T> left, T right)
        {
            // Promote to (right + 0i) so Annex G special-value behavior stays consistent
            // with the Complex/Complex operator.
            return left * new Complex<T>(right, T.Zero);
        }

        public static Complex<T> operator *(T left, Complex<T> right)
        {
            return new Complex<T>(left, T.Zero) * right;
        }

        public static Complex<T> operator /(Complex<T> left, Complex<T> right)
        {
            // Division: Smith's formula (Smith 1962), with the C23 Annex G.5.1 recovery
            // layered on top to restore directed infinities/zeros that Smith's formula
            // loses to a spurious NaN. Smith avoids the c*c + d*d overflow and stays
            // accurate for large-magnitude dividends where the pure Annex G reference
            // algorithm would overflow.
            T a = left.m_real;
            T b = left.m_imaginary;
            T c = right.m_real;
            T d = right.m_imaginary;

            T x, y;

            if (T.Abs(d) < T.Abs(c))
            {
                T doc = d / c;
                T denominator = c + (d * doc);
                x = (a + (b * doc)) / denominator;
                y = (b - (a * doc)) / denominator;
            }
            else
            {
                T cod = c / d;
                T denominator = d + (c * cod);
                x = (b + (a * cod)) / denominator;
                y = (-a + (b * cod)) / denominator;
            }

            if (T.IsNaN(x) && T.IsNaN(y))
            {
                // Outlined so the Smith common path stays small enough to inline.
                return DivideNaNRecovery(a, b, c, d, x, y);
            }

            return new Complex<T>(x, y);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static Complex<T> DivideNaNRecovery(T a, T b, T c, T d, T x, T y)
        {
            // C23 Annex G.5.1 recovery for the directed infinities/zeros that Smith's
            // formula loses to a spurious NaN.
            if ((c == T.Zero) && (d == T.Zero) && (!T.IsNaN(a) || !T.IsNaN(b)))
            {
                // Divisor is zero and the dividend is not fully NaN: directed infinity.
                T inf = T.CopySign(T.PositiveInfinity, c);
                x = inf * a;
                y = inf * b;
            }
            else if ((T.IsInfinity(a) || T.IsInfinity(b)) && T.IsFinite(c) && T.IsFinite(d))
            {
                // Infinite dividend, finite divisor: infinity.
                a = T.CopySign(T.IsInfinity(a) ? T.One : T.Zero, a);
                b = T.CopySign(T.IsInfinity(b) ? T.One : T.Zero, b);
                T inf = T.PositiveInfinity;
                x = inf * ((a * c) + (b * d));
                y = inf * ((b * c) - (a * d));
            }
            else if ((T.IsInfinity(c) || T.IsInfinity(d)) && T.IsFinite(a) && T.IsFinite(b))
            {
                // Finite dividend, infinite divisor: zero.
                c = T.CopySign(T.IsInfinity(c) ? T.One : T.Zero, c);
                d = T.CopySign(T.IsInfinity(d) ? T.One : T.Zero, d);
                x = T.Zero * ((a * c) + (b * d));
                y = T.Zero * ((b * c) - (a * d));
            }

            return new Complex<T>(x, y);
        }

        public static Complex<T> operator /(Complex<T> left, T right)
        {
            // Promote to (right + 0i) so Annex G special-value behavior stays consistent
            // with the Complex/Complex operator.
            return left / new Complex<T>(right, T.Zero);
        }

        public static Complex<T> operator /(T left, Complex<T> right)
        {
            // Promote to a full complex dividend so the C23 Annex G.5.1 recovery in the
            // Complex/Complex operator applies for a zero or infinite divisor.
            return new Complex<T>(left, T.Zero) / right;
        }

        public static T Abs(Complex<T> value)
        {
            return T.Hypot(value.m_real, value.m_imaginary);
        }

        private static T Log1P(T 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 >= T.Zero) || T.IsNaN(x));

            T xp1 = T.One + x;
            if (xp1 == T.One)
            {
                return x;
            }
            else if (x < T.CreateChecked(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 * T.Log(xp1) / (xp1 - T.One);
            }
            else
            {
                return T.Log(xp1);
            }
        }

        public static Complex<T> Conjugate(Complex<T> value)
        {
            // Conjugate of a Complex number: the conjugate of x+i*y is x-i*y
            return new Complex<T>(value.m_real, -value.m_imaginary);
        }

        public static Complex<T> Reciprocal(Complex<T> value)
        {
            // Reciprocal of a Complex number : the reciprocal of x+i*y is 1/(x+i*y)
            if (value.m_real == T.Zero && value.m_imaginary == T.Zero)
            {
                return Zero;
            }
            return One / value;
        }

        public static bool operator ==(Complex<T> left, Complex<T> right)
        {
            return left.m_real == right.m_real && left.m_imaginary == right.m_imaginary;
        }

        public static bool operator !=(Complex<T> left, Complex<T> 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<T> other && Equals(other);
        }

        public bool Equals(Complex<T> 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<T> Sin(Complex<T> value)
        {
            if (!IsFinite(value))
            {
                // sin(z) = -i sinh(iz); Sinh carries the Annex G special values.
                Complex<T> sinh = Sinh(new Complex<T>(-value.m_imaginary, value.m_real));
                return new Complex<T>(sinh.m_imaginary, -sinh.m_real);
            }

            (T sin, T cos) = T.SinCos(value.m_real);

            // Known limitation (finite-input accuracy, outside Annex G's special-value scope): a large
            // imaginary part overflows Cosh/Sinh even when sin/cos is small enough that the product would
            // be representable, so e.g. Sin((0.01, 711.0)) yields (+inf, +inf) instead of (~3.0e306, +inf).
            return new Complex<T>(sin * T.Cosh(value.m_imaginary), cos * T.Sinh(value.m_imaginary));
        }

        public static Complex<T> Sinh(Complex<T> value)
        {
            T real = value.m_real;
            T imaginary = value.m_imaginary;

            // IEEE 754 / C23 Annex G.6.2.5 special values. sinh is odd and csinh(conj(z)) == conj(csinh(z)).
            if (!T.IsFinite(real))
            {
                if (T.IsNaN(real))
                {
                    // NaN + i0 -> NaN + i0; NaN + iy (y != 0) -> NaN + iNaN.
                    return (imaginary == T.Zero) ? new Complex<T>(real, imaginary) : new Complex<T>(T.NaN, T.NaN);
                }

                // real is +-INF.
                if (imaginary == T.Zero)
                {
                    // +-INF + i0 -> +-INF + i0.
                    return new Complex<T>(real, imaginary);
                }

                if (!T.IsFinite(imaginary))
                {
                    // +-INF + i(INF|NaN) -> +-INF + iNaN.
                    return new Complex<T>(real, T.NaN);
                }

                // +-INF + iy (finite nonzero) -> +-INF * cis(y).
                (T sinInf, T cosInf) = T.SinCos(imaginary);
                return new Complex<T>(T.Sinh(real) * cosInf, T.Cosh(real) * sinInf);
            }

            if (!T.IsFinite(imaginary))
            {
                // real is finite. +-0 + i(INF|NaN) -> +-0 + iNaN; x + i(INF|NaN) (x != 0) -> NaN + iNaN.
                return (real == T.Zero) ? new Complex<T>(real, T.NaN) : new Complex<T>(T.NaN, T.NaN);
            }

            // Use sinh(z) = -i sin(iz) to compute via sin(z).
            Complex<T> sin = Sin(new Complex<T>(-imaginary, real));
            return new Complex<T>(sin.m_imaginary, -sin.m_real);
        }

        public static Complex<T> Asin(Complex<T> value)
        {
            if (!IsFinite(value))
            {
                // asin(z) = -i casinh(iz); AsinhSpecialValue carries the Annex G G.6.2.2 special values.
                Complex<T> s = AsinhSpecialValue(-value.m_imaginary, value.m_real);
                return new Complex<T>(s.m_imaginary, -s.m_real);
            }

            Asin_Internal(T.Abs(value.Real), T.Abs(value.Imaginary), out T b, out T bPrime, out T v);

            T u;
            if (bPrime < T.Zero)
            {
                u = T.Asin(b);
            }
            else
            {
                u = T.Atan(bPrime);
            }

            if (value.Real < T.Zero)
            {
                u = -u;
            }

            if (value.Imaginary < T.Zero)
            {
                v = -v;
            }

            return new Complex<T>(u, v);
        }

        // IEEE 754 / C23 Annex G.6.2.2 casinh special values. At least one component must be non-finite.
        // casinh is odd and casinh(conj(z)) == conj(casinh(z)), so the real part is odd in the real
        // component and the imaginary part is odd in the imaginary component.
        private static Complex<T> AsinhSpecialValue(T a, T b)
        {
            T absB = T.Abs(b);
            T re, im;

            if (T.IsInfinity(a))
            {
                // +-INF + i(INF|finite|NaN).
                re = T.PositiveInfinity;
                im = T.IsInfinity(absB) ? T.Pi / T.CreateChecked(4) :
                     T.IsNaN(absB) ? T.NaN : T.Zero;
            }
            else if (T.IsNaN(a))
            {
                if (absB == T.Zero)
                {
                    return new Complex<T>(T.NaN, b);
                }
                // NaN + iINF -> +-INF + iNaN (real sign unspecified); NaN + i(finite nonzero|NaN) -> NaN + iNaN.
                re = T.IsInfinity(absB) ? T.PositiveInfinity : T.NaN;
                im = T.NaN;
            }
            else
            {
                // a is finite; b is non-finite. x + iINF -> +INF + iPi/2; x + iNaN -> NaN + iNaN.
                re = T.IsInfinity(absB) ? T.PositiveInfinity : T.NaN;
                im = T.IsInfinity(absB) ? T.Pi / T.CreateChecked(2) : T.NaN;
            }

            re = T.IsNaN(re) || T.IsNaN(a) ? re : T.CopySign(re, a);
            im = T.IsNaN(im) || T.IsNaN(b) ? im : T.CopySign(im, b);
            return new Complex<T>(re, im);
        }

        public static Complex<T> Cos(Complex<T> value)
        {
            if (!IsFinite(value))
            {
                // cos(z) = cosh(iz); Cosh carries the Annex G special values.
                return Cosh(new Complex<T>(-value.m_imaginary, value.m_real));
            }

            (T sin, T cos) = T.SinCos(value.m_real);

            // Same finite-input overflow limitation as Sin: a large imaginary part overflows Cosh/Sinh
            // even when cos/sin is small enough that the product would otherwise be representable.
            return new Complex<T>(cos * T.Cosh(value.m_imaginary), -sin * T.Sinh(value.m_imaginary));
        }

        public static Complex<T> Cosh(Complex<T> value)
        {
            T real = value.m_real;
            T imaginary = value.m_imaginary;

            // IEEE 754 / C23 Annex G.6.2.4 special values. cosh is even and ccosh(conj(z)) == conj(ccosh(z)).
            if (!T.IsFinite(real))
            {
                if (T.IsNaN(real))
                {
                    // NaN + i0 -> NaN + i0; NaN + iy (y != 0) -> NaN + iNaN.
                    return (imaginary == T.Zero) ? new Complex<T>(T.NaN, imaginary) : new Complex<T>(T.NaN, T.NaN);
                }

                // real is +-INF; cosh is even so the real part of the result is +INF.
                if (imaginary == T.Zero)
                {
                    // +-INF + i0 -> +INF + i0, imaginary sign = sign(real) XOR sign(imaginary).
                    T imag = (T.IsNegative(real) ^ T.IsNegative(imaginary)) ? -T.Zero : T.Zero;
                    return new Complex<T>(T.PositiveInfinity, imag);
                }

                if (!T.IsFinite(imaginary))
                {
                    // +-INF + i(INF|NaN) -> +INF + iNaN.
                    return new Complex<T>(T.PositiveInfinity, T.NaN);
                }

                // +-INF + iy (finite nonzero) -> +INF * cis(y), sinh carrying the sign of the real part.
                (T sinInf, T cosInf) = T.SinCos(imaginary);
                return new Complex<T>(T.Cosh(real) * cosInf, T.Sinh(real) * sinInf);
            }

            if (!T.IsFinite(imaginary))
            {
                // real is finite. +-0 + i(INF|NaN) -> NaN + i(+-0); x + i(INF|NaN) (x != 0) -> NaN + iNaN.
                return (real == T.Zero) ? new Complex<T>(T.NaN, T.CopySign(T.Zero, real)) : new Complex<T>(T.NaN, T.NaN);
            }

            // Use cosh(z) = cos(iz) to compute via cos(z).
            return Cos(new Complex<T>(-imaginary, real));
        }

        public static Complex<T> Acos(Complex<T> value)
        {
            if (!IsFinite(value))
            {
                return AcosSpecialValue(value.m_real, value.m_imaginary);
            }

            Asin_Internal(T.Abs(value.Real), T.Abs(value.Imaginary), out T b, out T bPrime, out T v);

            T u;
            if (bPrime < T.Zero)
            {
                u = T.Acos(b);
            }
            else
            {
                u = T.Atan(T.One / bPrime);
            }

            if (value.Real < T.Zero)
            {
                u = T.Pi - u;
            }

            if (value.Imaginary > T.Zero)
            {
                v = -v;
            }

            return new Complex<T>(u, v);
        }

        // IEEE 754 / C23 Annex G.6.1.1 cacos special values. At least one component must be non-finite.
        // cacos(conj(z)) == conj(cacos(z)), so the imaginary part is odd in the imaginary component.
        private static Complex<T> AcosSpecialValue(T x, T y)
        {
            T absY = T.Abs(y);
            T re, im;

            if (T.IsInfinity(x))
            {
                if (T.IsNegative(x))
                {
                    // -INF + i(INF|finite|NaN).
                    re = T.IsInfinity(absY) ? T.Pi * T.CreateChecked(0.75) : T.IsNaN(absY) ? T.NaN : T.Pi;
                }
                else
                {
                    // +INF + i(INF|finite|NaN).
                    re = T.IsInfinity(absY) ? T.Pi / T.CreateChecked(4) : T.IsNaN(absY) ? T.NaN : T.Zero;
                }
                im = T.IsNaN(absY) ? T.PositiveInfinity : T.NegativeInfinity;
            }
            else if (T.IsNaN(x))
            {
                // NaN + iINF -> NaN - iINF; NaN + i(finite|NaN) -> NaN + iNaN.
                re = T.NaN;
                im = T.IsInfinity(absY) ? T.NegativeInfinity : T.NaN;
            }
            else
            {
                // x is finite; y is non-finite. x + iINF -> Pi/2 - iINF; +-0 + iNaN -> Pi/2 + iNaN;
                // x + iNaN (x nonzero) -> NaN + iNaN.
                if (T.IsInfinity(absY))
                {
                    re = T.Pi / T.CreateChecked(2);
                    im = T.NegativeInfinity;
                }
                else
                {
                    re = (x == T.Zero) ? T.Pi / T.CreateChecked(2) : T.NaN;
                    im = T.NaN;
                }
            }

            // The imaginary part is odd in y. Only flip when y carries a determinate sign.
            if (!T.IsNaN(im) && !T.IsNaN(y) && T.IsNegative(y))
            {
                im = -im;
            }
            return new Complex<T>(re, im);
        }

        public static Complex<T> Tan(Complex<T> value)
        {
            if (!IsFinite(value))
            {
                // tan(z) = -i tanh(iz); Tanh carries the Annex G special values.
                Complex<T> tanh = Tanh(new Complex<T>(-value.m_imaginary, value.m_real));
                return new Complex<T>(tanh.m_imaginary, -tanh.m_real);
            }

            // 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.

            T two = T.CreateChecked(2);
            T x2 = two * value.m_real;
            T y2 = two * value.m_imaginary;
            (T sin, T cos) = T.SinCos(x2);
            T cosh = T.Cosh(y2);
            if (T.Abs(value.m_imaginary) <= T.CreateChecked(4))
            {
                T D = cos + cosh;
                return new Complex<T>(sin / D, T.Sinh(y2) / D);
            }
            else
            {
                T D = T.One + cos / cosh;
                return new Complex<T>(sin / cosh / D, T.Tanh(y2) / D);
            }
        }

        public static Complex<T> Tanh(Complex<T> value)
        {
            T real = value.m_real;
            T imaginary = value.m_imaginary;

            // IEEE 754 / C23 Annex G.6.2.6 special values. tanh is odd and ctanh(conj(z)) == conj(ctanh(z)).
            if (!T.IsFinite(real))
            {
                if (T.IsNaN(real))
                {
                    // NaN + i0 -> NaN + i0; NaN + iy (y != 0) -> NaN + iNaN.
                    return (imaginary == T.Zero) ? new Complex<T>(real, imaginary) : new Complex<T>(T.NaN, T.NaN);
                }

                // real is +-INF -> +-1 + i0, with the imaginary sign taken from sin(2y).
                T re = T.CopySign(T.One, real);
                if (T.IsFinite(imaginary))
                {
                    // Compute sin(2y) as 2*sin(y)*cos(y) so the sign stays stable even when
                    // 2*y would overflow to an infinity (which sin() maps to a NaN).
                    (T sin, T cos) = T.SinCos(imaginary);
                    return new Complex<T>(re, T.CopySign(T.Zero, sin * cos));
                }
                return new Complex<T>(re, T.CopySign(T.Zero, imaginary));
            }

            if (!T.IsFinite(imaginary))
            {
                // real is finite. +-0 + i(INF|NaN) -> +-0 + iNaN; x + i(INF|NaN) (x != 0) -> NaN + iNaN.
                return (real == T.Zero) ? new Complex<T>(real, T.NaN) : new Complex<T>(T.NaN, T.NaN);
            }

            // Use tanh(z) = -i tan(iz) to compute via tan(z).
            Complex<T> tan = Tan(new Complex<T>(-imaginary, real));
            return new Complex<T>(tan.m_imaginary, -tan.m_real);
        }

        public static Complex<T> Atan(Complex<T> value)
        {
            if (!IsFinite(value))
            {
                // atan(z) = -i catanh(iz); AtanhSpecialValue carries the Annex G G.6.2.3 special values.
                Complex<T> t = AtanhSpecialValue(-value.m_imaginary, value.m_real);
                return new Complex<T>(t.m_imaginary, -t.m_real);
            }

            Complex<T> two = new(T.CreateChecked(2), T.Zero);
            return (ImaginaryOne / two) * (Log(One - ImaginaryOne * value) - Log(One + ImaginaryOne * value));
        }

        // IEEE 754 / C23 Annex G.6.2.3 catanh special values. At least one component must be non-finite.
        // catanh is odd and catanh(conj(z)) == conj(catanh(z)), so the real part is odd in the real
        // component and the imaginary part is odd in the imaginary component.
        private static Complex<T> AtanhSpecialValue(T a, T b)
        {
            T absB = T.Abs(b);
            T re, im;

            if (T.IsInfinity(a))
            {
                // +-INF + i(INF|finite|NaN) -> +-0 + i(Pi/2 or NaN).
                re = T.Zero;
                im = T.IsNaN(absB) ? T.NaN : T.Pi / T.CreateChecked(2);
            }
            else if (T.IsNaN(a))
            {
                // NaN + iINF -> +-0 + iPi/2 (real sign unspecified); NaN + i(finite|NaN) -> NaN + iNaN.
                re = T.IsInfinity(absB) ? T.Zero : T.NaN;
                im = T.IsInfinity(absB) ? T.Pi / T.CreateChecked(2) : T.NaN;
            }
            else
            {
                // a is finite; b is non-finite. x + iINF -> +0 + iPi/2; +-0 + iNaN -> +-0 + iNaN;
                // x + iNaN (x nonzero) -> NaN + iNaN.
                if (T.IsInfinity(absB))
                {
                    re = T.Zero;
                    im = T.Pi / T.CreateChecked(2);
                }
                else
                {
                    re = (a == T.Zero) ? a : T.NaN;
                    im = T.NaN;
                }
            }

            re = T.IsNaN(re) || T.IsNaN(a) ? re : T.CopySign(re, a);
            im = T.IsNaN(im) || T.IsNaN(b) ? im : T.CopySign(im, b);
            return new Complex<T>(re, im);
        }

        private static void Asin_Internal(T x, T y, out T b, out T bPrime, out T 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)

            Debug.Assert((x >= T.Zero) || T.IsNaN(x));
            Debug.Assert((y >= T.Zero) || T.IsNaN(y));

            if ((x > s_asinOverflowThreshold) || (y > s_asinOverflowThreshold))
            {
                b = -T.One;
                bPrime = x / y;

                T small, big;
                if (x < y)
                {
                    small = x;
                    big = y;
                }
                else
                {
                    small = y;
                    big = x;
                }
                T ratio = small / big;
                v = s_log2 + T.Log(big) + Log1P(ratio * ratio) / T.CreateChecked(2);
            }
            else
            {
                T r = T.Hypot(x + T.One, y);
                T s = T.Hypot(x - T.One, y);

                T a = (r + s) / T.CreateChecked(2);
                b = x / a;

                if (b > T.CreateChecked(0.75))
                {
                    if (x <= T.One)
                    {
                        T amx = (y * y / (r + (x + T.One)) + (s + (T.One - x))) / T.CreateChecked(2);
                        bPrime = x / T.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.
                        T t = (T.One / (r + (x + T.One)) + T.One / (s + (x - T.One))) / T.CreateChecked(2);
                        bPrime = x / y / T.Sqrt((a + x) * t);
                    }
                }
                else
                {
                    bPrime = -T.One;
                }

                if (a < T.CreateChecked(1.5))
                {
                    if (x < T.One)
                    {
                        // 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.
                        T t = (T.One / (r + (x + T.One)) + T.One / (s + (T.One - x))) / T.CreateChecked(2);
                        T am1 = y * y * t;
                        v = Log1P(am1 + y * T.Sqrt(t * (a + T.One)));
                    }
                    else
                    {
                        T am1 = (y * y / (r + (x + T.One)) + (s + (x - T.One))) / T.CreateChecked(2);
                        v = Log1P(am1 + T.Sqrt(am1 * (a + T.One)));
                    }
                }
                else
                {
                    // Because of the test above, we can be sure that a * a will not overflow.
                    v = T.Log(a + T.Sqrt((a - T.One) * (a + T.One)));
                }
            }
        }

        public static bool IsFinite(Complex<T> value) => T.IsFinite(value.m_real) && T.IsFinite(value.m_imaginary);

        public static bool IsInfinity(Complex<T> value) => T.IsInfinity(value.m_real) || T.IsInfinity(value.m_imaginary);

        public static bool IsNaN(Complex<T> value) => !IsInfinity(value) && !IsFinite(value);

        public static Complex<T> Log(Complex<T> value)
        {
            return new Complex<T>(T.Log(Abs(value)), T.Atan2(value.m_imaginary, value.m_real));
        }

        public static Complex<T> Log(Complex<T> value, T baseValue)
        {
            return Log(value) / Log(new Complex<T>(baseValue, T.Zero));
        }

        public static Complex<T> Log10(Complex<T> value)
        {
            Complex<T> tempLog = Log(value);
            return Scale(tempLog, s_inverseOfLog10);
        }

        public static Complex<T> Exp(Complex<T> value)
        {
            T real = value.m_real;
            T imaginary = value.m_imaginary;

            // IEEE 754 / C23 Annex G.6.3.1 special values. cexp(conj(z)) == conj(cexp(z)).
            // The general formula below (e^x * cis(y)) already yields the correct results for finite
            // real parts and for -INF real parts with finite imaginary parts, so only the remaining
            // infinite / NaN real cases need explicit handling.

            if (T.IsInfinity(real))
            {
                if (T.IsNegative(real))
                {
                    if (!T.IsFinite(imaginary))
                    {
                        // -INF + iINF or -INF + iNaN -> +-0 +- 0i (signs unspecified).
                        return new Complex<T>(T.Zero, T.Zero);
                    }
                    // -INF + iy (finite y) -> +0 * cis(y); handled by the general formula.
                }
                else
                {
                    if (imaginary == T.Zero)
                    {
                        // +INF + i0 -> +INF + i0.
                        return new Complex<T>(real, imaginary);
                    }

                    if (!T.IsFinite(imaginary))
                    {
                        // +INF + iINF or +INF + iNaN -> +-INF + iNaN (sign of the real part is unspecified).
                        return new Complex<T>(T.PositiveInfinity, T.NaN);
                    }
                    // +INF + iy (finite nonzero y) -> +INF * cis(y); handled by the general formula.
                }
            }
            else if (T.IsNaN(real))
            {
                // NaN + i0 -> NaN + i0; NaN + iy (y != 0) and NaN + iNaN -> NaN + iNaN.
                return (imaginary == T.Zero) ? new Complex<T>(T.NaN, imaginary) : new Complex<T>(T.NaN, T.NaN);
            }

            T expReal = T.Exp(real);
            return FromPolarCoordinates(expReal, imaginary);
        }

        public static Complex<T> Sqrt(Complex<T> value)
        {
            T real = value.m_real;
            T imaginary = value.m_imaginary;

            // IEEE 754 / C23 Annex G.6.4.2 special values. csqrt is continuous onto the branch
            // cut along the negative real axis taking the sign of the imaginary part into account,
            // and csqrt(conj(z)) == conj(csqrt(z)), so the sign of the imaginary part is preserved.

            if (T.IsInfinity(imaginary))
            {
                // x + iINF -> +INF + iINF for any x (including NaN).
                return new Complex<T>(T.PositiveInfinity, imaginary);
            }

            if (T.IsInfinity(real))
            {
                if (T.IsNegative(real))
                {
                    // -INF + iy -> +0 + iINF (finite y); -INF + iNaN -> NaN +- iINF (sign unspecified).
                    if (T.IsNaN(imaginary))
                    {
                        return new Complex<T>(T.NaN, T.PositiveInfinity);
                    }

                    return new Complex<T>(T.Zero, T.CopySign(T.PositiveInfinity, imaginary));
                }

                // +INF + iy -> +INF + i0 (finite y); +INF + iNaN -> +INF + iNaN.
                return new Complex<T>(T.PositiveInfinity, T.IsNaN(imaginary) ? T.NaN : T.CopySign(T.Zero, imaginary));
            }

            if (T.IsNaN(real) || T.IsNaN(imaginary))
            {
                // NaN + iy or x + iNaN with the other part finite -> NaN + iNaN.
                return new Complex<T>(T.NaN, T.NaN);
            }

            if (imaginary == T.Zero)
            {
                // On the real axis, propagate the sign of the zero imaginary part.
                if (T.IsNegative(real))
                {
                    return new Complex<T>(T.Zero, T.CopySign(T.Sqrt(-real), imaginary));
                }

                return new Complex<T>(T.Sqrt(real), T.CopySign(T.Zero, imaginary));
            }

            // 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;
            T realCopy = real;
            T imaginaryCopy = imaginary;
            if ((T.Abs(realCopy) >= s_sqrtRescaleThreshold) || (T.Abs(imaginaryCopy) >= s_sqrtRescaleThreshold))
            {
                T quarter = T.CreateChecked(0.25);
                realCopy *= quarter;
                imaginaryCopy *= quarter;
                rescale = true;
            }

            // This is the core of the algorithm. Everything else is special case handling.
            T x, y;
            T half = T.CreateChecked(0.5);
            if (realCopy >= T.Zero)
            {
                x = T.Sqrt((T.Hypot(realCopy, imaginaryCopy) + realCopy) * half);
                y = imaginaryCopy / (T.CreateChecked(2) * x);
            }
            else
            {
                y = T.Sqrt((T.Hypot(realCopy, imaginaryCopy) - realCopy) * half);
                if (imaginaryCopy < T.Zero)
                {
                    y = -y;
                }
                x = imaginaryCopy / (T.CreateChecked(2) * y);
            }

            if (rescale)
            {
                x *= T.CreateChecked(2);
                y *= T.CreateChecked(2);
            }

            return new Complex<T>(x, y);
        }

        public static Complex<T> Pow(Complex<T> value, Complex<T> power)
        {
            if (power == Zero)
            {
                return One;
            }

            if (value == Zero)
            {
                return Zero;
            }

            if (IsFinite(value) && IsFinite(power))
            {
                T rho = Abs(value);

                if (T.IsFinite(rho))
                {
                    T valueImaginary = value.m_imaginary;
                    T valueReal = value.m_real;
                    T powerReal = power.m_real;
                    T powerImaginary = power.m_imaginary;

                    T theta = T.Atan2(valueImaginary, valueReal);
                    T newRho = powerReal * theta + powerImaginary * T.Log(rho);

                    T t = T.Pow(rho, powerReal) * T.Exp(-powerImaginary * theta);

                    return FromPolarCoordinates(t, newRho);
                }
            }

            // C23: cpow(z, w) special values are those of cexp(w * clog(z)). The polar
            // core above loses them, so defer to the conformant Exp/Log for any input
            // that is non-finite or whose magnitude overflows.
            return Exp(power * Log(value));
        }

        public static Complex<T> Pow(Complex<T> value, T power)
        {
            return Pow(value, new Complex<T>(power, T.Zero));
        }

        private static Complex<T> Scale(Complex<T> value, T factor)
        {
            T realResult = factor * value.m_real;
            T imaginaryResult = factor * value.m_imaginary;
            return new Complex<T>(realResult, imaginaryResult);
        }

        //
        // Implicit Conversions To Complex<T>
        //

        public static implicit operator Complex<T>(T value)
        {
            return new Complex<T>(value, T.Zero);
        }

        //
        // IAdditiveIdentity
        //

        /// <inheritdoc cref="IAdditiveIdentity{TSelf, TResult}.AdditiveIdentity" />
        static Complex<T> IAdditiveIdentity<Complex<T>, Complex<T>>.AdditiveIdentity => Zero;

        //
        // IDecrementOperators
        //

        /// <inheritdoc cref="IDecrementOperators{TSelf}.op_Decrement(TSelf)" />
        public static Complex<T> operator --(Complex<T> value) => value - One;

        //
        // IIncrementOperators
        //

        /// <inheritdoc cref="IIncrementOperators{TSelf}.op_Increment(TSelf)" />
        public static Complex<T> operator ++(Complex<T> value) => value + One;

        //
        // IMultiplicativeIdentity
        //

        /// <inheritdoc cref="IMultiplicativeIdentity{TSelf, TResult}.MultiplicativeIdentity" />
        static Complex<T> IMultiplicativeIdentity<Complex<T>, Complex<T>>.MultiplicativeIdentity => One;

        //
        // INumberBase
        //

        /// <inheritdoc cref="INumberBase{TSelf}.Radix" />
        static int INumberBase<Complex<T>>.Radix => T.Radix;

        /// <inheritdoc cref="INumberBase{TSelf}.Abs(TSelf)" />
        static Complex<T> INumberBase<Complex<T>>.Abs(Complex<T> value) => Abs(value);

        /// <inheritdoc cref="INumberBase{TSelf}.CreateChecked{TOther}(TOther)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Complex<T> CreateChecked<TOther>(TOther value)
            where TOther : INumberBase<TOther>
        {
            Complex<T> result;

            if (typeof(TOther) == typeof(Complex<T>))
            {
                result = (Complex<T>)(object)value;
            }
            else if (!TryConvertFromCheckedCore(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<T> CreateSaturating<TOther>(TOther value)
            where TOther : INumberBase<TOther>
        {
            Complex<T> result;

            if (typeof(TOther) == typeof(Complex<T>))
            {
                result = (Complex<T>)(object)value;
            }
            else if (!TryConvertFromSaturatingCore(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<T> CreateTruncating<TOther>(TOther value)
            where TOther : INumberBase<TOther>
        {
            Complex<T> result;

            if (typeof(TOther) == typeof(Complex<T>))
            {
                result = (Complex<T>)(object)value;
            }
            else if (!TryConvertFromTruncatingCore(value, out result) && !TOther.TryConvertToTruncating(value, out result))
            {
                ThrowHelper.ThrowNotSupportedException();
            }

            return result;
        }

        /// <inheritdoc cref="INumberBase{TSelf}.IsCanonical(TSelf)" />
        static bool INumberBase<Complex<T>>.IsCanonical(Complex<T> value) => true;

        /// <inheritdoc cref="INumberBase{TSelf}.IsComplexNumber(TSelf)" />
        public static bool IsComplexNumber(Complex<T> value) => (value.m_real != T.Zero) && (value.m_imaginary != T.Zero);

        /// <inheritdoc cref="INumberBase{TSelf}.IsEvenInteger(TSelf)" />
        public static bool IsEvenInteger(Complex<T> value) => (value.m_imaginary == T.Zero) && T.IsEvenInteger(value.m_real);

        /// <inheritdoc cref="INumberBase{TSelf}.IsImaginaryNumber(TSelf)" />
        public static bool IsImaginaryNumber(Complex<T> value) => (value.m_real == T.Zero) && T.IsRealNumber(value.m_imaginary);

        /// <inheritdoc cref="INumberBase{TSelf}.IsInteger(TSelf)" />
        public static bool IsInteger(Complex<T> value) => (value.m_imaginary == T.Zero) && T.IsInteger(value.m_real);

        /// <inheritdoc cref="INumberBase{TSelf}.IsNegative(TSelf)" />
        public static bool IsNegative(Complex<T> 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 == T.Zero) && T.IsNegative(value.m_real);
        }

        /// <inheritdoc cref="INumberBase{TSelf}.IsNegativeInfinity(TSelf)" />
        public static bool IsNegativeInfinity(Complex<T> 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 == T.Zero) && T.IsNegativeInfinity(value.m_real);
        }

        /// <inheritdoc cref="INumberBase{TSelf}.IsNormal(TSelf)" />
        public static bool IsNormal(Complex<T> value)
        {
            // much as IsFinite requires both parts to be finite, we require both
            // parts to be "normal" (finite, non-zero, and non-subnormal) to be true

            return T.IsNormal(value.m_real)
                && ((value.m_imaginary == T.Zero) || T.IsNormal(value.m_imaginary));
        }

        /// <inheritdoc cref="INumberBase{TSelf}.IsOddInteger(TSelf)" />
        public static bool IsOddInteger(Complex<T> value) => (value.m_imaginary == T.Zero) && T.IsOddInteger(value.m_real);

        /// <inheritdoc cref="INumberBase{TSelf}.IsPositive(TSelf)" />
        public static bool IsPositive(Complex<T> 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 == T.Zero) && T.IsPositive(value.m_real);
        }

        /// <inheritdoc cref="INumberBase{TSelf}.IsPositiveInfinity(TSelf)" />
        public static bool IsPositiveInfinity(Complex<T> 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 == T.Zero) && T.IsPositiveInfinity(value.m_real);
        }

        /// <inheritdoc cref="INumberBase{TSelf}.IsRealNumber(TSelf)" />
        public static bool IsRealNumber(Complex<T> value) => (value.m_imaginary == T.Zero) && T.IsRealNumber(value.m_real);

        /// <inheritdoc cref="INumberBase{TSelf}.IsSubnormal(TSelf)" />
        public static bool IsSubnormal(Complex<T> 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 T.IsSubnormal(value.m_real) || T.IsSubnormal(value.m_imaginary);
        }

        /// <inheritdoc cref="INumberBase{TSelf}.IsZero(TSelf)" />
        static bool INumberBase<Complex<T>>.IsZero(Complex<T> value) => (value.m_real == T.Zero) && (value.m_imaginary == T.Zero);

        /// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitude(TSelf, TSelf)" />
        public static Complex<T> MaxMagnitude(Complex<T> x, Complex<T> 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.

            T ax = Abs(x);
            T ay = Abs(y);

            if ((ax > ay) || T.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 (T.IsNegative(y.m_real))
                {
                    if (T.IsNegative(y.m_imaginary))
                    {
                        return x;
                    }
                    else
                    {
                        if (T.IsNegative(x.m_real))
                        {
                            return y;
                        }
                        else
                        {
                            return x;
                        }
                    }
                }
                else if (T.IsNegative(y.m_imaginary))
                {
                    if (T.IsNegative(x.m_real))
                    {
                        return y;
                    }
                    else
                    {
                        return x;
                    }
                }
            }

            return y;
        }

        internal static Complex<T> MaxMagnitudeNumber(Complex<T> x, Complex<T> 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.

            T ax = Abs(x);
            T ay = Abs(y);

            if ((ax > ay) || T.IsNaN(ay))
            {
                return x;
            }

            if (ax == ay)
            {
                if (T.IsNegative(y.m_real))
                {
                    if (T.IsNegative(y.m_imaginary))
                    {
                        return x;
                    }
                    else
                    {
                        if (T.IsNegative(x.m_real))
                        {
                            return y;
                        }
                        else
                        {
                            return x;
                        }
                    }
                }
                else if (T.IsNegative(y.m_imaginary))
                {
                    if (T.IsNegative(x.m_real))
                    {
                        return y;
                    }
                    else
                    {
                        return x;
                    }
                }
            }

            return y;
        }

        /// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitudeNumber(TSelf, TSelf)" />
        static Complex<T> INumberBase<Complex<T>>.MaxMagnitudeNumber(Complex<T> x, Complex<T> y)
            => MaxMagnitudeNumber(x, y);

        /// <inheritdoc cref="INumberBase{TSelf}.MinMagnitude(TSelf, TSelf)" />
        public static Complex<T> MinMagnitude(Complex<T> x, Complex<T> 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 MinMagnitude

            // 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.

            T ax = Abs(x);
            T ay = Abs(y);

            if ((ax < ay) || T.IsNaN(ax))
            {
                return x;
            }

            if (ax == ay)
            {
                if (T.IsNegative(y.m_real))
                {
                    if (T.IsNegative(y.m_imaginary))
                    {
                        return y;
                    }
                    else
                    {
                        if (T.IsNegative(x.m_real))
                        {
                            return x;
                        }
                        else
                        {
                            return y;
                        }
                    }
                }
                else if (T.IsNegative(y.m_imaginary))
                {
                    if (T.IsNegative(x.m_real))
                    {
                        return x;
                    }
                    else
                    {
                        return y;
                    }
                }
                else
                {
                    return x;
                }
            }

            return y;
        }

        internal static Complex<T> MinMagnitudeNumber(Complex<T> x, Complex<T> 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.

            T ax = Abs(x);
            T ay = Abs(y);

            if ((ax < ay) || T.IsNaN(ay))
            {
                return x;
            }

            if (ax == ay)
            {
                if (T.IsNegative(y.m_real))
                {
                    if (T.IsNegative(y.m_imaginary))
                    {
                        return y;
                    }
                    else
                    {
                        if (T.IsNegative(x.m_real))
                        {
                            return x;
                        }
                        else
                        {
                            return y;
                        }
                    }
                }
                else if (T.IsNegative(y.m_imaginary))
                {
                    if (T.IsNegative(x.m_real))
                    {
                        return x;
                    }
                    else
                    {
                        return y;
                    }
                }
                else
                {
                    return x;
                }
            }

            return y;
        }

        /// <inheritdoc cref="INumberBase{TSelf}.MinMagnitudeNumber(TSelf, TSelf)" />
        static Complex<T> INumberBase<Complex<T>>.MinMagnitudeNumber(Complex<T> x, Complex<T> y)
            => MinMagnitudeNumber(x, y);

        internal static Complex<T> MultiplyAddEstimate(Complex<T> left, Complex<T> right, Complex<T> addend)
        {
            // Multiplication:  (a + bi)(c + di) = (ac - bd) + (bc + ad)i
            // Addition:        (a + bi) + (c + di) = (a + c) + (b + d)i

            T result_realpart = addend.m_real;
            result_realpart = T.MultiplyAddEstimate(-left.m_imaginary, right.m_imaginary, result_realpart);
            result_realpart = T.MultiplyAddEstimate(left.m_real, right.m_real, result_realpart);

            T result_imaginarypart = addend.m_imaginary;
            result_imaginarypart = T.MultiplyAddEstimate(left.m_real, right.m_imaginary, result_imaginarypart);
            result_imaginarypart = T.MultiplyAddEstimate(left.m_imaginary, right.m_real, result_imaginarypart);

            return new Complex<T>(result_realpart, result_imaginarypart);
        }

        /// <inheritdoc cref="INumberBase{TSelf}.MultiplyAddEstimate(TSelf, TSelf, TSelf)" />
        static Complex<T> INumberBase<Complex<T>>.MultiplyAddEstimate(Complex<T> left, Complex<T> right, Complex<T> addend)
            => MultiplyAddEstimate(left, right, addend);

        /// <inheritdoc cref="INumberBase{TSelf}.Parse(ReadOnlySpan{char}, NumberStyles, IFormatProvider?)" />
        public static Complex<T> Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider)
        {
            if (!TryParse(s, style, provider, out Complex<T> result))
            {
                ThrowHelper.ThrowOverflowException();
            }
            return result;
        }

        /// <inheritdoc cref="INumberBase{TSelf}.Parse(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?)" />
        public static Complex<T> Parse(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider)
        {
            if (!TryParse(utf8Text, style, provider, out Complex<T> result))
            {
                ThrowHelper.ThrowOverflowException();
            }
            return result;
        }

        /// <inheritdoc cref="INumberBase{TSelf}.Parse(string, NumberStyles, IFormatProvider?)" />
        public static Complex<T> 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<T>>.TryConvertFromChecked<TOther>(TOther value, out Complex<T> result)
        {
            return TryConvertFromCheckedCore(value, out result);
        }

        /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromSaturating{TOther}(TOther, out TSelf)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<Complex<T>>.TryConvertFromSaturating<TOther>(TOther value, out Complex<T> result)
        {
            return TryConvertFromSaturatingCore(value, out result);
        }

        /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromTruncating{TOther}(TOther, out TSelf)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<Complex<T>>.TryConvertFromTruncating<TOther>(TOther value, out Complex<T> result)
        {
            return TryConvertFromTruncatingCore(value, out result);
        }

        internal static bool TryConvertFromCheckedCore<TOther>(TOther value, out Complex<T> result)
            where TOther : INumberBase<TOther>
        {
            if (typeof(TOther) == typeof(T))
            {
                result = new Complex<T>((T)(object)value, T.Zero);
                return true;
            }

            if (typeof(TOther) == typeof(Complex))
            {
                Complex actualValue = (Complex)(object)value;
                result = new Complex<T>(T.CreateChecked(actualValue.Real), T.CreateChecked(actualValue.Imaginary));
                return true;
            }

            if (T.TryConvertFromChecked(value, out T? realResult) && realResult is not null)
            {
                result = new Complex<T>(realResult, T.Zero);
                return true;
            }

            if (TOther.TryConvertToChecked<T>(value, out T? realResult2) && realResult2 is not null)
            {
                result = new Complex<T>(realResult2, T.Zero);
                return true;
            }

            result = default;
            return false;
        }

        internal static bool TryConvertFromSaturatingCore<TOther>(TOther value, out Complex<T> result)
            where TOther : INumberBase<TOther>
        {
            if (typeof(TOther) == typeof(T))
            {
                result = new Complex<T>((T)(object)value, T.Zero);
                return true;
            }

            if (typeof(TOther) == typeof(Complex))
            {
                Complex actualValue = (Complex)(object)value;
                result = new Complex<T>(T.CreateSaturating(actualValue.Real), T.CreateSaturating(actualValue.Imaginary));
                return true;
            }

            if (T.TryConvertFromSaturating(value, out T? realResult) && realResult is not null)
            {
                result = new Complex<T>(realResult, T.Zero);
                return true;
            }

            if (TOther.TryConvertToSaturating<T>(value, out T? realResult2) && realResult2 is not null)
            {
                result = new Complex<T>(realResult2, T.Zero);
                return true;
            }

            result = default;
            return false;
        }

        internal static bool TryConvertFromTruncatingCore<TOther>(TOther value, out Complex<T> result)
            where TOther : INumberBase<TOther>
        {
            if (typeof(TOther) == typeof(T))
            {
                result = new Complex<T>((T)(object)value, T.Zero);
                return true;
            }

            if (typeof(TOther) == typeof(Complex))
            {
                Complex actualValue = (Complex)(object)value;
                result = new Complex<T>(T.CreateTruncating(actualValue.Real), T.CreateTruncating(actualValue.Imaginary));
                return true;
            }

            if (T.TryConvertFromTruncating(value, out T? realResult) && realResult is not null)
            {
                result = new Complex<T>(realResult, T.Zero);
                return true;
            }

            if (TOther.TryConvertToTruncating<T>(value, out T? realResult2) && realResult2 is not null)
            {
                result = new Complex<T>(realResult2, T.Zero);
                return true;
            }

            result = default;
            return false;
        }

        /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<Complex<T>>.TryConvertToChecked<TOther>(Complex<T> value, [MaybeNullWhen(false)] out TOther result)
        {
            if (typeof(TOther) == typeof(Complex<T>))
            {
                result = (TOther)(object)value;
                return true;
            }

            return TryConvertToCheckedCore(value, out result);
        }

        /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<Complex<T>>.TryConvertToSaturating<TOther>(Complex<T> value, [MaybeNullWhen(false)] out TOther result)
        {
            if (typeof(TOther) == typeof(Complex<T>))
            {
                result = (TOther)(object)value;
                return true;
            }

            return TryConvertToSaturatingCore(value, out result);
        }

        /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<Complex<T>>.TryConvertToTruncating<TOther>(Complex<T> value, [MaybeNullWhen(false)] out TOther result)
        {
            if (typeof(TOther) == typeof(Complex<T>))
            {
                result = (TOther)(object)value;
                return true;
            }

            return TryConvertToTruncatingCore(value, out result);
        }

        internal static bool TryConvertToCheckedCore<TOther>(Complex<T> value, [MaybeNullWhen(false)] out TOther result)
            where TOther : INumberBase<TOther>
        {
            if (typeof(TOther) == typeof(T))
            {
                // T is always IFloatingPointIeee754<T>, so NaN is valid for the imaginary case.
                result = (TOther)(object)((value.m_imaginary != T.Zero) ? T.NaN : value.m_real);
                return true;
            }

            if (typeof(TOther) == typeof(Complex))
            {
                result = (TOther)(object)new Complex(double.CreateChecked(value.m_real), double.CreateChecked(value.m_imaginary));
                return true;
            }

            if (typeof(TOther) == typeof(BigInteger))
            {
                if (value.m_imaginary != T.Zero)
                {
                    ThrowHelper.ThrowOverflowException();
                }

                BigInteger actualResult = checked((BigInteger)double.CreateChecked(value.m_real));
                result = (TOther)(object)actualResult;
                return true;
            }

            // A complex number with a non-zero imaginary part cannot be exactly represented as a real number.
            // For floating-point types, we return NaN; for integer/decimal types, we throw.
            if (value.m_imaginary != T.Zero)
            {
                if (!T.TryConvertToChecked(T.NaN, out result))
                {
                    ThrowHelper.ThrowOverflowException();
                }
                return result is not null;
            }

            if (T.TryConvertToChecked(value.m_real, out result))
            {
                return true;
            }

            return TOther.TryConvertFromChecked<T>(value.m_real, out result);
        }

        internal static bool TryConvertToSaturatingCore<TOther>(Complex<T> value, [MaybeNullWhen(false)] out TOther result)
            where TOther : INumberBase<TOther>
        {
            if (typeof(TOther) == typeof(T))
            {
                result = (TOther)(object)value.m_real;
                return true;
            }

            if (typeof(TOther) == typeof(Complex))
            {
                result = (TOther)(object)new Complex(double.CreateSaturating(value.m_real), double.CreateSaturating(value.m_imaginary));
                return true;
            }

            if (typeof(TOther) == typeof(BigInteger))
            {
                BigInteger actualResult = (BigInteger)double.CreateSaturating(value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }

            // For saturating conversion, ignore the imaginary part and just saturate the real part
            if (T.TryConvertToSaturating(value.m_real, out result))
            {
                return true;
            }

            return TOther.TryConvertFromSaturating<T>(value.m_real, out result);
        }

        internal static bool TryConvertToTruncatingCore<TOther>(Complex<T> value, [MaybeNullWhen(false)] out TOther result)
            where TOther : INumberBase<TOther>
        {
            if (typeof(TOther) == typeof(T))
            {
                result = (TOther)(object)value.m_real;
                return true;
            }

            if (typeof(TOther) == typeof(Complex))
            {
                result = (TOther)(object)new Complex(double.CreateTruncating(value.m_real), double.CreateTruncating(value.m_imaginary));
                return true;
            }

            if (typeof(TOther) == typeof(BigInteger))
            {
                BigInteger actualResult = (BigInteger)double.CreateTruncating(value.m_real);
                result = (TOther)(object)actualResult;
                return true;
            }

            // For truncating conversion, ignore the imaginary part and just truncate the real part
            if (T.TryConvertToTruncating(value.m_real, out result))
            {
                return true;
            }

            return TOther.TryConvertFromTruncating<T>(value.m_real, out result);
        }

        /// <inheritdoc cref="INumberBase{TSelf}.TryParse(ReadOnlySpan{char}, NumberStyles, IFormatProvider?, out TSelf)" />
        public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out Complex<T> result)
            => TryParse(MemoryMarshal.Cast<char, Utf16Char>(s), style, provider, out result, out _);

        /// <inheritdoc cref="INumberBase{TSelf}.TryParse(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?, out TSelf)" />
        public static bool TryParse(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider, out Complex<T> result)
            => TryParse(MemoryMarshal.Cast<byte, Utf8Char>(utf8Text), style, provider, out result, out _);

        /// <inheritdoc cref="INumberBase{TSelf}.TryParsePartial(string, NumberStyles, IFormatProvider?, out TSelf, out int)" />
        public static bool TryParsePartial([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Complex<T> result, out int charsConsumed)
            => TryParsePartial(MemoryMarshal.Cast<char, Utf16Char>(s.AsSpan()), style, provider, out result, out charsConsumed);

        /// <inheritdoc cref="INumberBase{TSelf}.TryParsePartial(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?, out TSelf, out int)" />
        public static bool TryParsePartial(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider, out Complex<T> result, out int bytesConsumed)
            => TryParsePartial(MemoryMarshal.Cast<byte, Utf8Char>(utf8Text), style, provider, out result, out bytesConsumed);

        /// <inheritdoc cref="INumberBase{TSelf}.TryParsePartial(ReadOnlySpan{char}, NumberStyles, IFormatProvider?, out TSelf, out int)" />
        public static bool TryParsePartial(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out Complex<T> result, out int charsConsumed)
            => TryParsePartial(MemoryMarshal.Cast<char, Utf16Char>(s), style, provider, out result, out charsConsumed);

        internal static bool TryParse<TChar>(ReadOnlySpan<TChar> text, NumberStyles style, IFormatProvider? provider, out Complex<T> result, out int elementsConsumed)
            where TChar : unmanaged, IUtfChar<TChar>
        {
            ValidateParseStyleFloatingPoint(style);
            return TryParseCore(text, style, provider, out result, out elementsConsumed);
        }

        // Used by the INumberBase.TryParsePartial implementations. The user-provided style is validated first so the
        // internal-only AllowTrailingInvalidCharacters sentinel can't be smuggled in through a public entry point; it
        // is only layered on afterwards, once the style is known to be valid.
        internal static bool TryParsePartial<TChar>(ReadOnlySpan<TChar> text, NumberStyles style, IFormatProvider? provider, out Complex<T> result, out int elementsConsumed)
            where TChar : unmanaged, IUtfChar<TChar>
        {
            ValidateParseStyleFloatingPoint(style);
            return TryParseCore(text, style | Number.AllowTrailingInvalidCharacters, provider, out result, out elementsConsumed);
        }

        private static bool TryParseCore<TChar>(ReadOnlySpan<TChar> text, NumberStyles style, IFormatProvider? provider, out Complex<T> result, out int elementsConsumed)
            where TChar : unmanaged, IUtfChar<TChar>
        {
            int openBracket = text.IndexOf(TChar.CastFrom('<'));
            int semicolon = text.IndexOf(TChar.CastFrom(';'));
            int closeBracket = text.IndexOf(TChar.CastFrom('>'));

            if ((text.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 to find an open bracket, a semicolon, and a closing bracket in that order

                result = default;
                elementsConsumed = 0;
                return false;
            }

            if ((openBracket != 0) && (((style & NumberStyles.AllowLeadingWhite) == 0) || !text.Slice(0, openBracket).IsWhiteSpace(out _)))
            {
                // 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;
                elementsConsumed = 0;
                return false;
            }

            // The real and imaginary components are exactly delimited by the ';' and '>' separators,
            // so AllowTrailingInvalidCharacters only applies after the closing bracket, not within a
            // component. Otherwise something like "<1.5x;2>" would incorrectly parse as (1.5, 2).
            NumberStyles componentStyle = style & ~Number.AllowTrailingInvalidCharacters;

            ReadOnlySpan<TChar> slice = text.Slice(openBracket + 1, semicolon - openBracket - 1);

            if ((typeof(TChar) == typeof(Utf8Char))
                ? !T.TryParse(Unsafe.BitCast<ReadOnlySpan<TChar>, ReadOnlySpan<byte>>(slice), componentStyle, provider, out T? real)
                : !T.TryParse(Unsafe.BitCast<ReadOnlySpan<TChar>, ReadOnlySpan<char>>(slice), componentStyle, provider, out real))
            {
                result = default;
                elementsConsumed = 0;
                return false;
            }

            if (Number.DecodeFromUtfChar(text[(semicolon + 1)..], out Rune rune, out int elemsConsumed) == OperationStatus.Done)
            {
                if (Rune.IsWhiteSpace(rune))
                {
                    // 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 += elemsConsumed;
                }
            }

            slice = text.Slice(semicolon + 1, closeBracket - semicolon - 1);

            if ((typeof(TChar) == typeof(Utf8Char))
                ? !T.TryParse(Unsafe.BitCast<ReadOnlySpan<TChar>, ReadOnlySpan<byte>>(slice), componentStyle, provider, out T? imaginary)
                : !T.TryParse(Unsafe.BitCast<ReadOnlySpan<TChar>, ReadOnlySpan<char>>(slice), componentStyle, provider, out imaginary))
            {
                result = default;
                elementsConsumed = 0;
                return false;
            }

            int trailingWhiteLength = 0;
            if (closeBracket != (text.Length - 1))
            {
                bool isInvalid = true;

                if ((style & NumberStyles.AllowTrailingWhite) != 0)
                {
                    if (text.Slice(closeBracket + 1).IsWhiteSpace(out trailingWhiteLength))
                    {
                        isInvalid = false;
                    }
                }

                if (isInvalid && ((style & Number.AllowTrailingInvalidCharacters) == 0))
                {
                    // 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;
                    elementsConsumed = 0;
                    return false;
                }
            }

            result = new Complex<T>(real!, imaginary!);
            elementsConsumed = closeBracket + 1 + trailingWhiteLength;
            return true;
        }

        private static void ValidateParseStyleFloatingPoint(NumberStyles style)
        {
            // Check for undefined flags or hex number
            if ((style & (Complex.InvalidNumberStyles | NumberStyles.AllowHexSpecifier)) != 0)
            {
                ThrowInvalid(style);

                static void ThrowInvalid(NumberStyles value)
                {
                    if ((value & Complex.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<T> 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<T> Parse(string s, IFormatProvider? provider) => Parse(s, Complex.DefaultNumberStyle, provider);

        /// <inheritdoc cref="IParsable{TSelf}.TryParse(string?, IFormatProvider?, out TSelf)" />
        public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out Complex<T> result) => TryParse(s, Complex.DefaultNumberStyle, provider, out result);

        //
        // ISignedNumber
        //

        /// <inheritdoc cref="ISignedNumber{TSelf}.NegativeOne" />
        static Complex<T> ISignedNumber<Complex<T>>.NegativeOne => new(-T.One, T.Zero);

        //
        // 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) =>
            TryFormat(MemoryMarshal.Cast<char, Utf16Char>(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) =>
            TryFormat(MemoryMarshal.Cast<byte, Utf8Char>(utf8Destination), out bytesWritten, format, provider);

        private bool TryFormat<TChar>(Span<TChar> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider)
            where TChar : unmanaged, IUtfChar<TChar>
        {
            Debug.Assert(typeof(TChar) == typeof(Utf8Char) || typeof(TChar) == typeof(Utf16Char));

            // We have at least 6 more characters for: <0; 0>
            if (destination.Length >= 6)
            {
                if ((typeof(TChar) == typeof(Utf8Char))
                    ? m_real.TryFormat(Unsafe.BitCast<Span<TChar>, Span<byte>>(destination.Slice(1)), out int realChars, format, provider)
                    : m_real.TryFormat(Unsafe.BitCast<Span<TChar>, Span<char>>(destination.Slice(1)), out realChars, format, provider))
                {
                    destination[0] = TChar.CastFrom('<');
                    destination = destination.Slice(1 + realChars); // + 1 for <

                    // We have at least 4 more characters for: ; 0>
                    if (destination.Length >= 4)
                    {
                        if ((typeof(TChar) == typeof(Utf8Char))
                            ? m_imaginary.TryFormat(Unsafe.BitCast<Span<TChar>, Span<byte>>(destination.Slice(2)), out int imaginaryChars, format, provider)
                            : m_imaginary.TryFormat(Unsafe.BitCast<Span<TChar>, Span<char>>(destination.Slice(2)), out imaginaryChars, format, provider))
                        {
                            // We have 1 more character for: >
                            if ((uint)(2 + imaginaryChars) < (uint)destination.Length)
                            {
                                destination[0] = TChar.CastFrom(';');
                                destination[1] = TChar.CastFrom(' ');
                                destination[2 + imaginaryChars] = TChar.CastFrom('>');

                                charsWritten = realChars + imaginaryChars + 4;
                                return true;
                            }
                        }
                    }
                }
            }

            charsWritten = 0;
            return false;
        }

        //
        // ISpanParsable
        //

        /// <inheritdoc cref="ISpanParsable{TSelf}.Parse(ReadOnlySpan{char}, IFormatProvider?)" />
        public static Complex<T> Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => Parse(s, Complex.DefaultNumberStyle, provider);

        /// <inheritdoc cref="ISpanParsable{TSelf}.TryParse(ReadOnlySpan{char}, IFormatProvider?, out TSelf)" />
        public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out Complex<T> result) => TryParse(s, Complex.DefaultNumberStyle, provider, out result);

        //
        // IUnaryPlusOperators
        //

        /// <inheritdoc cref="IUnaryPlusOperators{TSelf, TResult}.op_UnaryPlus(TSelf)" />
        public static Complex<T> operator +(Complex<T> value) => value;

        //
        // IUtf8SpanParsable
        //

        /// <inheritdoc cref="IUtf8SpanParsable{TSelf}.Parse(ReadOnlySpan{byte}, IFormatProvider?)" />
        public static Complex<T> Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider) => Parse(utf8Text, Complex.DefaultNumberStyle, provider);

        /// <inheritdoc cref="IUtf8SpanParsable{TSelf}.TryParse(ReadOnlySpan{byte}, IFormatProvider?, out TSelf)" />
        public static bool TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider, out Complex<T> result) => TryParse(utf8Text, Complex.DefaultNumberStyle, provider, out result);
    }
}