File: System\Numerics\BigInteger.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.Buffers;
using System.Buffers.Binary;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
 
namespace System.Numerics
{
    [Serializable]
    [TypeForwardedFrom("System.Numerics, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089")]
    [DebuggerDisplay("{DebuggerDisplay,nq}")]
    public readonly struct BigInteger
        : ISpanFormattable,
          IComparable,
          IComparable<BigInteger>,
          IEquatable<BigInteger>,
          IBinaryInteger<BigInteger>,
          ISignedNumber<BigInteger>
    {
        internal const uint kuMaskHighBit = unchecked((uint)int.MinValue);
        internal const int kcbitUint = 32;
        internal const int kcbitUlong = 64;
        internal const int DecimalScaleFactorMask = 0x00FF0000;
 
        // Various APIs only allow up to int.MaxValue bits, so we will restrict ourselves
        // to fit within this given our underlying storage representation and the maximum
        // array length. This gives us just shy of 256MB as the largest allocation size.
        //
        // Such a value allows for almost 646,456,974 digits, which is more than large enough
        // for typical scenarios. If user code requires more than this, they should likely
        // roll their own type that utilizes native memory and other specialized techniques.
        internal static int MaxLength => Array.MaxLength / kcbitUint;
 
        // For values int.MinValue < n <= int.MaxValue, the value is stored in sign
        // and _bits is null. For all other values, sign is +1 or -1 and the bits are in _bits
        internal readonly int _sign; // Do not rename (binary serialization)
        internal readonly uint[]? _bits; // Do not rename (binary serialization)
 
        // We have to make a choice of how to represent int.MinValue. This is the one
        // value that fits in an int, but whose negation does not fit in an int.
        // We choose to use a large representation, so we're symmetric with respect to negation.
        private static readonly BigInteger s_bnMinInt = new BigInteger(-1, new uint[] { kuMaskHighBit });
        private static readonly BigInteger s_bnOneInt = new BigInteger(1);
        private static readonly BigInteger s_bnZeroInt = new BigInteger(0);
        private static readonly BigInteger s_bnMinusOneInt = new BigInteger(-1);
 
        public BigInteger(int value)
        {
            if (value == int.MinValue)
                this = s_bnMinInt;
            else
            {
                _sign = value;
                _bits = null;
            }
            AssertValid();
        }
 
        [CLSCompliant(false)]
        public BigInteger(uint value)
        {
            if (value <= int.MaxValue)
            {
                _sign = (int)value;
                _bits = null;
            }
            else
            {
                _sign = +1;
                _bits = new uint[1];
                _bits[0] = value;
            }
            AssertValid();
        }
 
        public BigInteger(long value)
        {
            if (int.MinValue < value && value <= int.MaxValue)
            {
                _sign = (int)value;
                _bits = null;
            }
            else if (value == int.MinValue)
            {
                this = s_bnMinInt;
            }
            else
            {
                ulong x;
                if (value < 0)
                {
                    x = unchecked((ulong)-value);
                    _sign = -1;
                }
                else
                {
                    x = (ulong)value;
                    _sign = +1;
                }
 
                if (x <= uint.MaxValue)
                {
                    _bits = new uint[1];
                    _bits[0] = (uint)x;
                }
                else
                {
                    _bits = new uint[2];
                    _bits[0] = unchecked((uint)x);
                    _bits[1] = (uint)(x >> kcbitUint);
                }
            }
 
            AssertValid();
        }
 
        [CLSCompliant(false)]
        public BigInteger(ulong value)
        {
            if (value <= int.MaxValue)
            {
                _sign = (int)value;
                _bits = null;
            }
            else if (value <= uint.MaxValue)
            {
                _sign = +1;
                _bits = new uint[1];
                _bits[0] = (uint)value;
            }
            else
            {
                _sign = +1;
                _bits = new uint[2];
                _bits[0] = unchecked((uint)value);
                _bits[1] = (uint)(value >> kcbitUint);
            }
 
            AssertValid();
        }
 
        public BigInteger(float value) : this((double)value)
        {
        }
 
        public BigInteger(double value)
        {
            if (!double.IsFinite(value))
            {
                if (double.IsInfinity(value))
                {
                    throw new OverflowException(SR.Overflow_BigIntInfinity);
                }
                else // NaN
                {
                    throw new OverflowException(SR.Overflow_NotANumber);
                }
            }
 
            _sign = 0;
            _bits = null;
 
            int sign, exp;
            ulong man;
            NumericsHelpers.GetDoubleParts(value, out sign, out exp, out man, out _);
            Debug.Assert(sign == +1 || sign == -1);
 
            if (man == 0)
            {
                this = Zero;
                return;
            }
 
            Debug.Assert(man < (1UL << 53));
            Debug.Assert(exp <= 0 || man >= (1UL << 52));
 
            if (exp <= 0)
            {
                if (exp <= -kcbitUlong)
                {
                    this = Zero;
                    return;
                }
                this = man >> -exp;
                if (sign < 0)
                    _sign = -_sign;
            }
            else if (exp <= 11)
            {
                this = man << exp;
                if (sign < 0)
                    _sign = -_sign;
            }
            else
            {
                // Overflow into at least 3 uints.
                // Move the leading 1 to the high bit.
                man <<= 11;
                exp -= 11;
 
                // Compute cu and cbit so that exp == 32 * cu - cbit and 0 <= cbit < 32.
                int cu = (exp - 1) / kcbitUint + 1;
                int cbit = cu * kcbitUint - exp;
                Debug.Assert(0 <= cbit && cbit < kcbitUint);
                Debug.Assert(cu >= 1);
 
                // Populate the uints.
                _bits = new uint[cu + 2];
                _bits[cu + 1] = (uint)(man >> (cbit + kcbitUint));
                _bits[cu] = unchecked((uint)(man >> cbit));
                if (cbit > 0)
                    _bits[cu - 1] = unchecked((uint)man) << (kcbitUint - cbit);
                _sign = sign;
            }
 
            AssertValid();
        }
 
        public BigInteger(decimal value)
        {
            // First truncate to get scale to 0 and extract bits
            Span<int> bits = stackalloc int[4];
            decimal.GetBits(decimal.Truncate(value), bits);
 
            Debug.Assert(bits.Length == 4 && (bits[3] & DecimalScaleFactorMask) == 0);
 
            const int signMask = unchecked((int)kuMaskHighBit);
            int size = 3;
            while (size > 0 && bits[size - 1] == 0)
                size--;
            if (size == 0)
            {
                this = s_bnZeroInt;
            }
            else if (size == 1 && bits[0] > 0)
            {
                // bits[0] is the absolute value of this decimal
                // if bits[0] < 0 then it is too large to be packed into _sign
                _sign = bits[0];
                _sign *= ((bits[3] & signMask) != 0) ? -1 : +1;
                _bits = null;
            }
            else
            {
                _bits = new uint[size];
 
                unchecked
                {
                    _bits[0] = (uint)bits[0];
                    if (size > 1)
                        _bits[1] = (uint)bits[1];
                    if (size > 2)
                        _bits[2] = (uint)bits[2];
                }
 
                _sign = ((bits[3] & signMask) != 0) ? -1 : +1;
            }
            AssertValid();
        }
 
        /// <summary>
        /// Creates a BigInteger from a little-endian twos-complement byte array.
        /// </summary>
        /// <param name="value"></param>
        [CLSCompliant(false)]
        public BigInteger(byte[] value) :
            this(new ReadOnlySpan<byte>(value ?? throw new ArgumentNullException(nameof(value))))
        {
        }
 
        public BigInteger(ReadOnlySpan<byte> value, bool isUnsigned = false, bool isBigEndian = false)
        {
            int byteCount = value.Length;
 
            bool isNegative;
            if (byteCount > 0)
            {
                byte mostSignificantByte = isBigEndian ? value[0] : value[byteCount - 1];
                isNegative = (mostSignificantByte & 0x80) != 0 && !isUnsigned;
 
                if (mostSignificantByte == 0)
                {
                    // Try to conserve space as much as possible by checking for wasted leading byte[] entries
                    if (isBigEndian)
                    {
                        int offset = 1;
 
                        while (offset < byteCount && value[offset] == 0)
                        {
                            offset++;
                        }
 
                        value = value.Slice(offset);
                        byteCount = value.Length;
                    }
                    else
                    {
                        byteCount -= 2;
 
                        while (byteCount >= 0 && value[byteCount] == 0)
                        {
                            byteCount--;
                        }
 
                        byteCount++;
                    }
                }
            }
            else
            {
                isNegative = false;
            }
 
            if (byteCount == 0)
            {
                // BigInteger.Zero
                _sign = 0;
                _bits = null;
                AssertValid();
                return;
            }
 
            if (byteCount <= 4)
            {
                _sign = isNegative ? unchecked((int)0xffffffff) : 0;
 
                if (isBigEndian)
                {
                    for (int i = 0; i < byteCount; i++)
                    {
                        _sign = (_sign << 8) | value[i];
                    }
                }
                else
                {
                    for (int i = byteCount - 1; i >= 0; i--)
                    {
                        _sign = (_sign << 8) | value[i];
                    }
                }
 
                _bits = null;
                if (_sign < 0 && !isNegative)
                {
                    // Int32 overflow
                    // Example: Int64 value 2362232011 (0xCB, 0xCC, 0xCC, 0x8C, 0x0)
                    // can be naively packed into 4 bytes (due to the leading 0x0)
                    // it overflows into the int32 sign bit
                    _bits = new uint[1] { unchecked((uint)_sign) };
                    _sign = +1;
                }
                if (_sign == int.MinValue)
                {
                    this = s_bnMinInt;
                }
            }
            else
            {
                int wholeUInt32Count = Math.DivRem(byteCount, 4, out int unalignedBytes);
                uint[] val = new uint[wholeUInt32Count + (unalignedBytes == 0 ? 0 : 1)];
 
                // Copy the bytes to the uint array, apart from those which represent the
                // most significant uint if it's not a full four bytes.
                // The uints are stored in 'least significant first' order.
                if (isBigEndian)
                {
                    // The bytes parameter is in big-endian byte order.
                    // We need to read the uints out in reverse.
 
                    Span<byte> uintBytes = MemoryMarshal.AsBytes(val.AsSpan(0, wholeUInt32Count));
 
                    // We need to slice off the remainder from the beginning.
                    value.Slice(unalignedBytes).CopyTo(uintBytes);
 
                    uintBytes.Reverse();
                }
                else
                {
                    // The bytes parameter is in little-endian byte order.
                    // We can just copy the bytes directly into the uint array.
 
                    value.Slice(0, wholeUInt32Count * 4).CopyTo(MemoryMarshal.AsBytes<uint>(val));
                }
 
                // In both of the above cases on big-endian architecture, we need to perform
                // an endianness swap on the resulting uints.
                if (!BitConverter.IsLittleEndian)
                {
                    BinaryPrimitives.ReverseEndianness(val.AsSpan(0, wholeUInt32Count), val);
                }
 
                // Copy the last uint specially if it's not aligned
                if (unalignedBytes != 0)
                {
                    if (isNegative)
                    {
                        val[wholeUInt32Count] = 0xffffffff;
                    }
 
                    if (isBigEndian)
                    {
                        for (int curByte = 0; curByte < unalignedBytes; curByte++)
                        {
                            byte curByteValue = value[curByte];
                            val[wholeUInt32Count] = (val[wholeUInt32Count] << 8) | curByteValue;
                        }
                    }
                    else
                    {
                        for (int curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
                        {
                            byte curByteValue = value[curByte];
                            val[wholeUInt32Count] = (val[wholeUInt32Count] << 8) | curByteValue;
                        }
                    }
                }
 
                if (isNegative)
                {
                    NumericsHelpers.DangerousMakeTwosComplement(val); // Mutates val
 
                    // Pack _bits to remove any wasted space after the twos complement
                    int len = val.Length - 1;
                    while (len >= 0 && val[len] == 0) len--;
                    len++;
 
                    if (len == 1)
                    {
                        switch (val[0])
                        {
                            case 1: // abs(-1)
                                this = s_bnMinusOneInt;
                                return;
 
                            case kuMaskHighBit: // abs(Int32.MinValue)
                                this = s_bnMinInt;
                                return;
 
                            default:
                                if (unchecked((int)val[0]) > 0)
                                {
                                    _sign = (-1) * ((int)val[0]);
                                    _bits = null;
                                    AssertValid();
                                    return;
                                }
 
                                break;
                        }
                    }
 
                    if (len != val.Length)
                    {
                        _sign = -1;
                        _bits = new uint[len];
                        Array.Copy(val, _bits, len);
                    }
                    else
                    {
                        _sign = -1;
                        _bits = val;
                    }
                }
                else
                {
                    _sign = +1;
                    _bits = val;
                }
            }
            AssertValid();
        }
 
        internal BigInteger(int n, uint[]? rgu)
        {
            if ((rgu is not null) && (rgu.Length > MaxLength))
            {
                ThrowHelper.ThrowOverflowException();
            }
 
            _sign = n;
            _bits = rgu;
 
            AssertValid();
        }
 
        /// <summary>
        /// Constructor used during bit manipulation and arithmetic.
        /// When possible the value will be packed into  _sign to conserve space.
        /// </summary>
        /// <param name="value">The absolute value of the number</param>
        /// <param name="negative">The bool indicating the sign of the value.</param>
        internal BigInteger(ReadOnlySpan<uint> value, bool negative)
        {
            // Try to conserve space as much as possible by checking for wasted leading span entries
            // sometimes the span has leading zeros from bit manipulation operations & and ^
 
            int length = value.LastIndexOfAnyExcept(0u) + 1;
            value = value[..length];
 
            if (value.Length > MaxLength)
            {
                ThrowHelper.ThrowOverflowException();
            }
 
            if (value.Length == 0)
            {
                this = default;
            }
            else if (value.Length == 1 && value[0] < kuMaskHighBit)
            {
                // Values like (Int32.MaxValue+1) are stored as "0x80000000" and as such cannot be packed into _sign
                _sign = negative ? -(int)value[0] : (int)value[0];
                _bits = null;
                if (_sign == int.MinValue)
                {
                    // Although Int32.MinValue fits in _sign, we represent this case differently for negate
                    this = s_bnMinInt;
                }
            }
            else
            {
                _sign = negative ? -1 : +1;
                _bits = value.ToArray();
            }
            AssertValid();
        }
 
        /// <summary>
        /// Create a BigInteger from a little-endian twos-complement UInt32 span.
        /// </summary>
        /// <param name="value"></param>
        private BigInteger(Span<uint> value)
        {
            bool isNegative;
            int length;
 
            if ((value.Length > 0) && ((int)value[^1] < 0))
            {
                isNegative = true;
                length = value.LastIndexOfAnyExcept(uint.MaxValue) + 1;
 
                if ((length == 0) || ((int)value[length - 1] > 0))
                {
                    // We ne need to preserve the sign bit
                    length++;
                }
                Debug.Assert((int)value[length - 1] < 0);
            }
            else
            {
                isNegative = false;
                length = value.LastIndexOfAnyExcept(0u) + 1;
            }
            value = value[..length];
 
            if (value.Length > MaxLength)
            {
                ThrowHelper.ThrowOverflowException();
            }
 
            if (value.Length == 0)
            {
                // 0
                this = s_bnZeroInt;
            }
            else if (value.Length == 1)
            {
                if (isNegative)
                {
                    if (value[0] == uint.MaxValue)
                    {
                        // -1
                        this = s_bnMinusOneInt;
                    }
                    else if (value[0] == kuMaskHighBit)
                    {
                        // int.MinValue
                        this = s_bnMinInt;
                    }
                    else
                    {
                        _sign = unchecked((int)value[0]);
                        _bits = null;
                    }
                }
                else if (unchecked((int)value[0]) < 0)
                {
                    _sign = +1;
                    _bits = [value[0]];
                }
                else
                {
                    _sign = unchecked((int)value[0]);
                    _bits = null;
                }
            }
            else
            {
                if (isNegative)
                {
                    NumericsHelpers.DangerousMakeTwosComplement(value);
 
                    // Retrim any leading zeros carried from the sign
                    length = value.LastIndexOfAnyExcept(0u) + 1;
                    value = value[..length];
 
                    _sign = -1;
                }
                else
                {
                    _sign = +1;
                }
                _bits = value.ToArray();
            }
            AssertValid();
        }
 
        public static BigInteger Zero { get { return s_bnZeroInt; } }
 
        public static BigInteger One { get { return s_bnOneInt; } }
 
        public static BigInteger MinusOne { get { return s_bnMinusOneInt; } }
 
        public bool IsPowerOfTwo
        {
            get
            {
                AssertValid();
 
                if (_bits == null)
                    return BitOperations.IsPow2(_sign);
 
                if (_sign != 1)
                    return false;
 
                int iu = _bits.Length - 1;
 
                return BitOperations.IsPow2(_bits[iu]) && !_bits.AsSpan(0, iu).ContainsAnyExcept(0u);
            }
        }
 
        public bool IsZero { get { AssertValid(); return _sign == 0; } }
 
        public bool IsOne { get { AssertValid(); return _sign == 1 && _bits == null; } }
 
        public bool IsEven { get { AssertValid(); return _bits == null ? (_sign & 1) == 0 : (_bits[0] & 1) == 0; } }
 
        public int Sign
        {
            get { AssertValid(); return (_sign >> (kcbitUint - 1)) - (-_sign >> (kcbitUint - 1)); }
        }
 
        public static BigInteger Parse(string value)
        {
            return Parse(value, NumberStyles.Integer);
        }
 
        public static BigInteger Parse(string value, NumberStyles style)
        {
            return Parse(value, style, NumberFormatInfo.CurrentInfo);
        }
 
        public static BigInteger Parse(string value, IFormatProvider? provider)
        {
            return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
        }
 
        public static BigInteger Parse(string value, NumberStyles style, IFormatProvider? provider)
        {
            ArgumentNullException.ThrowIfNull(value);
            return Parse(value.AsSpan(), style, NumberFormatInfo.GetInstance(provider));
        }
 
        public static bool TryParse([NotNullWhen(true)] string? value, out BigInteger result)
        {
            return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
        }
 
        public static bool TryParse([NotNullWhen(true)] string? value, NumberStyles style, IFormatProvider? provider, out BigInteger result)
        {
            return TryParse(value.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result);
        }
 
        public static BigInteger Parse(ReadOnlySpan<char> value, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null)
        {
            return Number.ParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider));
        }
 
        public static bool TryParse(ReadOnlySpan<char> value, out BigInteger result)
        {
            return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
        }
 
        public static bool TryParse(ReadOnlySpan<char> value, NumberStyles style, IFormatProvider? provider, out BigInteger result)
        {
            return Number.TryParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
        }
 
        public static int Compare(BigInteger left, BigInteger right)
        {
            return left.CompareTo(right);
        }
 
        public static BigInteger Abs(BigInteger value)
        {
            return (value >= Zero) ? value : -value;
        }
 
        public static BigInteger Add(BigInteger left, BigInteger right)
        {
            return left + right;
        }
 
        public static BigInteger Subtract(BigInteger left, BigInteger right)
        {
            return left - right;
        }
 
        public static BigInteger Multiply(BigInteger left, BigInteger right)
        {
            return left * right;
        }
 
        public static BigInteger Divide(BigInteger dividend, BigInteger divisor)
        {
            return dividend / divisor;
        }
 
        public static BigInteger Remainder(BigInteger dividend, BigInteger divisor)
        {
            return dividend % divisor;
        }
 
        public static BigInteger DivRem(BigInteger dividend, BigInteger divisor, out BigInteger remainder)
        {
            dividend.AssertValid();
            divisor.AssertValid();
 
            bool trivialDividend = dividend._bits == null;
            bool trivialDivisor = divisor._bits == null;
 
            if (trivialDividend && trivialDivisor)
            {
                BigInteger quotient;
                (quotient, remainder) = Math.DivRem(dividend._sign, divisor._sign);
                return quotient;
            }
 
            if (trivialDividend)
            {
                // The divisor is non-trivial
                // and therefore the bigger one
                remainder = dividend;
                return s_bnZeroInt;
            }
 
            Debug.Assert(dividend._bits != null);
 
            if (trivialDivisor)
            {
                uint rest;
 
                uint[]? bitsFromPool = null;
                int size = dividend._bits.Length;
                Span<uint> quotient = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                    ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                    : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                try
                {
                    // may throw DivideByZeroException
                    BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign), quotient, out rest);
 
                    remainder = dividend._sign < 0 ? -1 * rest : rest;
                    return new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0));
                }
                finally
                {
                    if (bitsFromPool != null)
                        ArrayPool<uint>.Shared.Return(bitsFromPool);
                }
            }
 
            Debug.Assert(divisor._bits != null);
 
            if (dividend._bits.Length < divisor._bits.Length)
            {
                remainder = dividend;
                return s_bnZeroInt;
            }
            else
            {
                uint[]? remainderFromPool = null;
                int size = dividend._bits.Length;
                Span<uint> rest = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : remainderFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                uint[]? quotientFromPool = null;
                size = dividend._bits.Length - divisor._bits.Length + 1;
                Span<uint> quotient = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                    ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                    : quotientFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                BigIntegerCalculator.Divide(dividend._bits, divisor._bits, quotient, rest);
 
                remainder = new BigInteger(rest, dividend._sign < 0);
                var result = new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0));
 
                if (remainderFromPool != null)
                    ArrayPool<uint>.Shared.Return(remainderFromPool);
 
                if (quotientFromPool != null)
                    ArrayPool<uint>.Shared.Return(quotientFromPool);
 
                return result;
            }
        }
 
        public static BigInteger Negate(BigInteger value)
        {
            return -value;
        }
 
        public static double Log(BigInteger value)
        {
            return Log(value, Math.E);
        }
 
        public static double Log(BigInteger value, double baseValue)
        {
            if (value._sign < 0 || baseValue == 1.0D)
                return double.NaN;
            if (baseValue == double.PositiveInfinity)
                return value.IsOne ? 0.0D : double.NaN;
            if (baseValue == 0.0D && !value.IsOne)
                return double.NaN;
            if (value._bits == null)
                return Math.Log(value._sign, baseValue);
 
            ulong h = value._bits[value._bits.Length - 1];
            ulong m = value._bits.Length > 1 ? value._bits[value._bits.Length - 2] : 0;
            ulong l = value._bits.Length > 2 ? value._bits[value._bits.Length - 3] : 0;
 
            // Measure the exact bit count
            int c = BitOperations.LeadingZeroCount((uint)h);
            long b = (long)value._bits.Length * 32 - c;
 
            // Extract most significant bits
            ulong x = (h << 32 + c) | (m << c) | (l >> 32 - c);
 
            // Let v = value, b = bit count, x = v/2^b-64
            // log ( v/2^b-64 * 2^b-64 ) = log ( x ) + log ( 2^b-64 )
            return Math.Log(x, baseValue) + (b - 64) / Math.Log(baseValue, 2);
        }
 
        public static double Log10(BigInteger value)
        {
            return Log(value, 10);
        }
 
        public static BigInteger GreatestCommonDivisor(BigInteger left, BigInteger right)
        {
            left.AssertValid();
            right.AssertValid();
 
            bool trivialLeft = left._bits == null;
            bool trivialRight = right._bits == null;
 
            if (trivialLeft && trivialRight)
            {
                return BigIntegerCalculator.Gcd(NumericsHelpers.Abs(left._sign), NumericsHelpers.Abs(right._sign));
            }
 
            if (trivialLeft)
            {
                Debug.Assert(right._bits != null);
                return left._sign != 0
                    ? BigIntegerCalculator.Gcd(right._bits, NumericsHelpers.Abs(left._sign))
                    : new BigInteger(right._bits, negative: false);
            }
 
            if (trivialRight)
            {
                Debug.Assert(left._bits != null);
                return right._sign != 0
                    ? BigIntegerCalculator.Gcd(left._bits, NumericsHelpers.Abs(right._sign))
                    : new BigInteger(left._bits, negative: false);
            }
 
            Debug.Assert(left._bits != null && right._bits != null);
 
            if (BigIntegerCalculator.Compare(left._bits, right._bits) < 0)
            {
                return GreatestCommonDivisor(right._bits, left._bits);
            }
            else
            {
                return GreatestCommonDivisor(left._bits, right._bits);
            }
        }
 
        private static BigInteger GreatestCommonDivisor(ReadOnlySpan<uint> leftBits, ReadOnlySpan<uint> rightBits)
        {
            Debug.Assert(BigIntegerCalculator.Compare(leftBits, rightBits) >= 0);
 
            uint[]? bitsFromPool = null;
            BigInteger result;
 
            // Short circuits to spare some allocations...
            if (rightBits.Length == 1)
            {
                uint temp = BigIntegerCalculator.Remainder(leftBits, rightBits[0]);
                result = BigIntegerCalculator.Gcd(rightBits[0], temp);
            }
            else if (rightBits.Length == 2)
            {
                Span<uint> bits = (leftBits.Length <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(leftBits.Length)).Slice(0, leftBits.Length);
 
                BigIntegerCalculator.Remainder(leftBits, rightBits, bits);
 
                ulong left = ((ulong)rightBits[1] << 32) | rightBits[0];
                ulong right = ((ulong)bits[1] << 32) | bits[0];
 
                result = BigIntegerCalculator.Gcd(left, right);
            }
            else
            {
                Span<uint> bits = (leftBits.Length <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(leftBits.Length)).Slice(0, leftBits.Length);
 
                BigIntegerCalculator.Gcd(leftBits, rightBits, bits);
                result = new BigInteger(bits, negative: false);
            }
 
            if (bitsFromPool != null)
                ArrayPool<uint>.Shared.Return(bitsFromPool);
 
            return result;
        }
 
        public static BigInteger Max(BigInteger left, BigInteger right)
        {
            if (left.CompareTo(right) < 0)
                return right;
            return left;
        }
 
        public static BigInteger Min(BigInteger left, BigInteger right)
        {
            if (left.CompareTo(right) <= 0)
                return left;
            return right;
        }
 
        public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigInteger modulus)
        {
            ArgumentOutOfRangeException.ThrowIfNegative(exponent.Sign, nameof(exponent));
 
            value.AssertValid();
            exponent.AssertValid();
            modulus.AssertValid();
 
            bool trivialValue = value._bits == null;
            bool trivialExponent = exponent._bits == null;
            bool trivialModulus = modulus._bits == null;
 
            BigInteger result;
 
            if (trivialModulus)
            {
                uint bits = trivialValue && trivialExponent ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), NumericsHelpers.Abs(modulus._sign)) :
                            trivialValue ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), exponent._bits!, NumericsHelpers.Abs(modulus._sign)) :
                            trivialExponent ? BigIntegerCalculator.Pow(value._bits!, NumericsHelpers.Abs(exponent._sign), NumericsHelpers.Abs(modulus._sign)) :
                            BigIntegerCalculator.Pow(value._bits!, exponent._bits!, NumericsHelpers.Abs(modulus._sign));
 
                result = value._sign < 0 && !exponent.IsEven ? -1 * bits : bits;
            }
            else
            {
                int size = (modulus._bits?.Length ?? 1) << 1;
                uint[]? bitsFromPool = null;
                Span<uint> bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
                bits.Clear();
                if (trivialValue)
                {
                    if (trivialExponent)
                    {
                        BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), modulus._bits!, bits);
                    }
                    else
                    {
                        BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), exponent._bits!, modulus._bits!, bits);
                    }
                }
                else if (trivialExponent)
                {
                    BigIntegerCalculator.Pow(value._bits!, NumericsHelpers.Abs(exponent._sign), modulus._bits!, bits);
                }
                else
                {
                    BigIntegerCalculator.Pow(value._bits!, exponent._bits!, modulus._bits!, bits);
                }
 
                result = new BigInteger(bits, value._sign < 0 && !exponent.IsEven);
 
                if (bitsFromPool != null)
                    ArrayPool<uint>.Shared.Return(bitsFromPool);
            }
 
            return result;
        }
 
        public static BigInteger Pow(BigInteger value, int exponent)
        {
            ArgumentOutOfRangeException.ThrowIfNegative(exponent);
 
            value.AssertValid();
 
            if (exponent == 0)
                return s_bnOneInt;
            if (exponent == 1)
                return value;
 
            bool trivialValue = value._bits == null;
 
            uint power = NumericsHelpers.Abs(exponent);
            uint[]? bitsFromPool = null;
            BigInteger result;
 
            if (trivialValue)
            {
                if (value._sign == 1)
                    return value;
                if (value._sign == -1)
                    return (exponent & 1) != 0 ? value : s_bnOneInt;
                if (value._sign == 0)
                    return value;
 
                int size = BigIntegerCalculator.PowBound(power, 1);
                Span<uint> bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
                bits.Clear();
 
                BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), power, bits);
                result = new BigInteger(bits, value._sign < 0 && (exponent & 1) != 0);
            }
            else
            {
                int size = BigIntegerCalculator.PowBound(power, value._bits!.Length);
                Span<uint> bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
                bits.Clear();
 
                BigIntegerCalculator.Pow(value._bits, power, bits);
                result = new BigInteger(bits, value._sign < 0 && (exponent & 1) != 0);
            }
 
            if (bitsFromPool != null)
                ArrayPool<uint>.Shared.Return(bitsFromPool);
 
            return result;
        }
 
        public override int GetHashCode()
        {
            AssertValid();
 
            if (_bits is null)
                return _sign;
 
            HashCode hash = default;
            hash.AddBytes(MemoryMarshal.AsBytes(_bits.AsSpan()));
            hash.Add(_sign);
            return hash.ToHashCode();
        }
 
        public override bool Equals([NotNullWhen(true)] object? obj)
        {
            AssertValid();
 
            return obj is BigInteger other && Equals(other);
        }
 
        public bool Equals(long other)
        {
            AssertValid();
 
            if (_bits == null)
                return _sign == other;
 
            int cu;
            if ((_sign ^ other) < 0 || (cu = _bits.Length) > 2)
                return false;
 
            ulong uu = other < 0 ? (ulong)-other : (ulong)other;
            if (cu == 1)
                return _bits[0] == uu;
 
            return NumericsHelpers.MakeUInt64(_bits[1], _bits[0]) == uu;
        }
 
        [CLSCompliant(false)]
        public bool Equals(ulong other)
        {
            AssertValid();
 
            if (_sign < 0)
                return false;
            if (_bits == null)
                return (ulong)_sign == other;
 
            int cu = _bits.Length;
            if (cu > 2)
                return false;
            if (cu == 1)
                return _bits[0] == other;
            return NumericsHelpers.MakeUInt64(_bits[1], _bits[0]) == other;
        }
 
        public bool Equals(BigInteger other)
        {
            AssertValid();
            other.AssertValid();
 
            return _sign == other._sign && _bits.AsSpan().SequenceEqual(other._bits);
        }
 
        public int CompareTo(long other)
        {
            AssertValid();
 
            if (_bits == null)
                return ((long)_sign).CompareTo(other);
            int cu;
            if ((_sign ^ other) < 0 || (cu = _bits.Length) > 2)
                return _sign;
            ulong uu = other < 0 ? (ulong)-other : (ulong)other;
            ulong uuTmp = cu == 2 ? NumericsHelpers.MakeUInt64(_bits[1], _bits[0]) : _bits[0];
            return _sign * uuTmp.CompareTo(uu);
        }
 
        [CLSCompliant(false)]
        public int CompareTo(ulong other)
        {
            AssertValid();
 
            if (_sign < 0)
                return -1;
            if (_bits == null)
                return ((ulong)_sign).CompareTo(other);
            int cu = _bits.Length;
            if (cu > 2)
                return +1;
            ulong uuTmp = cu == 2 ? NumericsHelpers.MakeUInt64(_bits[1], _bits[0]) : _bits[0];
            return uuTmp.CompareTo(other);
        }
 
        public int CompareTo(BigInteger other)
        {
            AssertValid();
            other.AssertValid();
 
            if ((_sign ^ other._sign) < 0)
            {
                // Different signs, so the comparison is easy.
                return _sign < 0 ? -1 : +1;
            }
 
            // Same signs
            if (_bits == null)
            {
                if (other._bits == null)
                    return _sign < other._sign ? -1 : _sign > other._sign ? +1 : 0;
                return -other._sign;
            }
 
            if (other._bits == null)
                return _sign;
 
            int bitsResult = BigIntegerCalculator.Compare(_bits, other._bits);
            return _sign < 0 ? -bitsResult : bitsResult;
        }
 
        public int CompareTo(object? obj)
        {
            if (obj == null)
                return 1;
            if (obj is not BigInteger bigInt)
                throw new ArgumentException(SR.Argument_MustBeBigInt, nameof(obj));
            return CompareTo(bigInt);
        }
 
        /// <summary>
        /// Returns the value of this BigInteger as a little-endian twos-complement
        /// byte array, using the fewest number of bytes possible. If the value is zero,
        /// return an array of one byte whose element is 0x00.
        /// </summary>
        /// <returns></returns>
        public byte[] ToByteArray() => ToByteArray(isUnsigned: false, isBigEndian: false);
 
        /// <summary>
        /// Returns the value of this BigInteger as a byte array using the fewest number of bytes possible.
        /// If the value is zero, returns an array of one byte whose element is 0x00.
        /// </summary>
        /// <param name="isUnsigned">Whether or not an unsigned encoding is to be used</param>
        /// <param name="isBigEndian">Whether or not to write the bytes in a big-endian byte order</param>
        /// <returns></returns>
        /// <exception cref="OverflowException">
        ///   If <paramref name="isUnsigned"/> is <c>true</c> and <see cref="Sign"/> is negative.
        /// </exception>
        /// <remarks>
        /// The integer value <c>33022</c> can be exported as four different arrays.
        ///
        /// <list type="bullet">
        ///   <item>
        ///     <description>
        ///       <c>(isUnsigned: false, isBigEndian: false)</c> => <c>new byte[] { 0xFE, 0x80, 0x00 }</c>
        ///     </description>
        ///   </item>
        ///   <item>
        ///     <description>
        ///       <c>(isUnsigned: false, isBigEndian: true)</c> => <c>new byte[] { 0x00, 0x80, 0xFE }</c>
        ///     </description>
        ///   </item>
        ///   <item>
        ///     <description>
        ///       <c>(isUnsigned: true, isBigEndian: false)</c> => <c>new byte[] { 0xFE, 0x80 }</c>
        ///     </description>
        ///   </item>
        ///   <item>
        ///     <description>
        ///       <c>(isUnsigned: true, isBigEndian: true)</c> => <c>new byte[] { 0x80, 0xFE }</c>
        ///     </description>
        ///   </item>
        /// </list>
        /// </remarks>
        public byte[] ToByteArray(bool isUnsigned = false, bool isBigEndian = false)
        {
            int ignored = 0;
            return TryGetBytes(GetBytesMode.AllocateArray, default, isUnsigned, isBigEndian, ref ignored)!;
        }
 
        /// <summary>
        /// Copies the value of this BigInteger as little-endian twos-complement
        /// bytes, using the fewest number of bytes possible. If the value is zero,
        /// outputs one byte whose element is 0x00.
        /// </summary>
        /// <param name="destination">The destination span to which the resulting bytes should be written.</param>
        /// <param name="bytesWritten">The number of bytes written to <paramref name="destination"/>.</param>
        /// <param name="isUnsigned">Whether or not an unsigned encoding is to be used</param>
        /// <param name="isBigEndian">Whether or not to write the bytes in a big-endian byte order</param>
        /// <returns>true if the bytes fit in <paramref name="destination"/>; false if not all bytes could be written due to lack of space.</returns>
        /// <exception cref="OverflowException">If <paramref name="isUnsigned"/> is <c>true</c> and <see cref="Sign"/> is negative.</exception>
        public bool TryWriteBytes(Span<byte> destination, out int bytesWritten, bool isUnsigned = false, bool isBigEndian = false)
        {
            bytesWritten = 0;
            if (TryGetBytes(GetBytesMode.Span, destination, isUnsigned, isBigEndian, ref bytesWritten) == null)
            {
                bytesWritten = 0;
                return false;
            }
            return true;
        }
 
        internal bool TryWriteOrCountBytes(Span<byte> destination, out int bytesWritten, bool isUnsigned = false, bool isBigEndian = false)
        {
            bytesWritten = 0;
            return TryGetBytes(GetBytesMode.Span, destination, isUnsigned, isBigEndian, ref bytesWritten) != null;
        }
 
        /// <summary>Gets the number of bytes that will be output by <see cref="ToByteArray(bool, bool)"/> and <see cref="TryWriteBytes(Span{byte}, out int, bool, bool)"/>.</summary>
        /// <returns>The number of bytes.</returns>
        public int GetByteCount(bool isUnsigned = false)
        {
            int count = 0;
            // Big or Little Endian doesn't matter for the byte count.
            const bool IsBigEndian = false;
            TryGetBytes(GetBytesMode.Count, default(Span<byte>), isUnsigned, IsBigEndian, ref count);
            return count;
        }
 
        /// <summary>Mode used to enable sharing <see cref="TryGetBytes(GetBytesMode, Span{byte}, bool, bool, ref int)"/> for multiple purposes.</summary>
        private enum GetBytesMode
        {
            AllocateArray,
            Count,
            Span
        }
 
        /// <summary>Shared logic for <see cref="ToByteArray(bool, bool)"/>, <see cref="TryWriteBytes(Span{byte}, out int, bool, bool)"/>, and <see cref="GetByteCount"/>.</summary>
        /// <param name="mode">Which entry point is being used.</param>
        /// <param name="destination">The destination span, if mode is <see cref="GetBytesMode.Span"/>.</param>
        /// <param name="isUnsigned">True to never write a padding byte, false to write it if the high bit is set.</param>
        /// <param name="isBigEndian">True for big endian byte ordering, false for little endian byte ordering.</param>
        /// <param name="bytesWritten">
        /// If <paramref name="mode"/>==<see cref="GetBytesMode.AllocateArray"/>, ignored.
        /// If <paramref name="mode"/>==<see cref="GetBytesMode.Count"/>, the number of bytes that would be written.
        /// If <paramref name="mode"/>==<see cref="GetBytesMode.Span"/>, the number of bytes written to the span or that would be written if it were long enough.
        /// </param>
        /// <returns>
        /// If <paramref name="mode"/>==<see cref="GetBytesMode.AllocateArray"/>, the result array.
        /// If <paramref name="mode"/>==<see cref="GetBytesMode.Count"/>, null.
        /// If <paramref name="mode"/>==<see cref="GetBytesMode.Span"/>, non-null if the span was long enough, null if there wasn't enough room.
        /// </returns>
        /// <exception cref="OverflowException">If <paramref name="isUnsigned"/> is <c>true</c> and <see cref="Sign"/> is negative.</exception>
        private byte[]? TryGetBytes(GetBytesMode mode, Span<byte> destination, bool isUnsigned, bool isBigEndian, ref int bytesWritten)
        {
            Debug.Assert(mode == GetBytesMode.AllocateArray || mode == GetBytesMode.Count || mode == GetBytesMode.Span, $"Unexpected mode {mode}.");
            Debug.Assert(mode == GetBytesMode.Span || destination.IsEmpty, $"If we're not in span mode, we shouldn't have been passed a destination.");
 
            int sign = _sign;
            if (sign == 0)
            {
                switch (mode)
                {
                    case GetBytesMode.AllocateArray:
                        return new byte[] { 0 };
                    case GetBytesMode.Count:
                        bytesWritten = 1;
                        return null;
                    default: // case GetBytesMode.Span:
                        bytesWritten = 1;
                        if (destination.Length != 0)
                        {
                            destination[0] = 0;
                            return Array.Empty<byte>();
                        }
                        return null;
                }
            }
 
            if (isUnsigned && sign < 0)
            {
                throw new OverflowException(SR.Overflow_Negative_Unsigned);
            }
 
            byte highByte;
            int nonZeroDwordIndex = 0;
            uint highDword;
            uint[]? bits = _bits;
            if (bits == null)
            {
                highByte = (byte)((sign < 0) ? 0xff : 0x00);
                highDword = unchecked((uint)sign);
            }
            else if (sign == -1)
            {
                highByte = 0xff;
 
                // If sign is -1, we will need to two's complement bits.
                // Previously this was accomplished via NumericsHelpers.DangerousMakeTwosComplement(),
                // however, we can do the two's complement on the stack so as to avoid
                // creating a temporary copy of bits just to hold the two's complement.
                // One special case in DangerousMakeTwosComplement() is that if the array
                // is all zeros, then it would allocate a new array with the high-order
                // uint set to 1 (for the carry). In our usage, we will not hit this case
                // because a bits array of all zeros would represent 0, and this case
                // would be encoded as _bits = null and _sign = 0.
                Debug.Assert(bits.Length > 0);
                Debug.Assert(bits[bits.Length - 1] != 0);
                while (bits[nonZeroDwordIndex] == 0U)
                {
                    nonZeroDwordIndex++;
                }
 
                highDword = ~bits[bits.Length - 1];
                if (bits.Length - 1 == nonZeroDwordIndex)
                {
                    // This will not overflow because highDword is less than or equal to uint.MaxValue - 1.
                    Debug.Assert(highDword <= uint.MaxValue - 1);
                    highDword += 1U;
                }
            }
            else
            {
                Debug.Assert(sign == 1);
                highByte = 0x00;
                highDword = bits[bits.Length - 1];
            }
 
            byte msb;
            int msbIndex;
            if ((msb = unchecked((byte)(highDword >> 24))) != highByte)
            {
                msbIndex = 3;
            }
            else if ((msb = unchecked((byte)(highDword >> 16))) != highByte)
            {
                msbIndex = 2;
            }
            else if ((msb = unchecked((byte)(highDword >> 8))) != highByte)
            {
                msbIndex = 1;
            }
            else
            {
                msb = unchecked((byte)highDword);
                msbIndex = 0;
            }
 
            // Ensure high bit is 0 if positive, 1 if negative
            bool needExtraByte = (msb & 0x80) != (highByte & 0x80) && !isUnsigned;
            int length = msbIndex + 1 + (needExtraByte ? 1 : 0);
            if (bits != null)
            {
                length = checked(4 * (bits.Length - 1) + length);
            }
 
            byte[] array;
            switch (mode)
            {
                case GetBytesMode.AllocateArray:
                    destination = array = new byte[length];
                    break;
                case GetBytesMode.Count:
                    bytesWritten = length;
                    return null;
                default: // case GetBytesMode.Span:
                    bytesWritten = length;
                    if (destination.Length < length)
                    {
                        return null;
                    }
                    array = Array.Empty<byte>();
                    break;
            }
 
            int curByte = isBigEndian ? length - 1 : 0;
            int increment = isBigEndian ? -1 : 1;
 
            if (bits != null)
            {
                for (int i = 0; i < bits.Length - 1; i++)
                {
                    uint dword = bits[i];
 
                    if (sign == -1)
                    {
                        dword = ~dword;
                        if (i <= nonZeroDwordIndex)
                        {
                            dword = unchecked(dword + 1U);
                        }
                    }
 
                    destination[curByte] = unchecked((byte)dword);
                    curByte += increment;
                    destination[curByte] = unchecked((byte)(dword >> 8));
                    curByte += increment;
                    destination[curByte] = unchecked((byte)(dword >> 16));
                    curByte += increment;
                    destination[curByte] = unchecked((byte)(dword >> 24));
                    curByte += increment;
                }
            }
 
            Debug.Assert(msbIndex >= 0 && msbIndex <= 3);
            destination[curByte] = unchecked((byte)highDword);
            if (msbIndex != 0)
            {
                curByte += increment;
                destination[curByte] = unchecked((byte)(highDword >> 8));
                if (msbIndex != 1)
                {
                    curByte += increment;
                    destination[curByte] = unchecked((byte)(highDword >> 16));
                    if (msbIndex != 2)
                    {
                        curByte += increment;
                        destination[curByte] = unchecked((byte)(highDword >> 24));
                    }
                }
            }
 
            // Assert we're big endian, or little endian consistency holds.
            Debug.Assert(isBigEndian || (!needExtraByte && curByte == length - 1) || (needExtraByte && curByte == length - 2));
            // Assert we're little endian, or big endian consistency holds.
            Debug.Assert(!isBigEndian || (!needExtraByte && curByte == 0) || (needExtraByte && curByte == 1));
 
            if (needExtraByte)
            {
                curByte += increment;
                destination[curByte] = highByte;
            }
 
            return array;
        }
 
        /// <summary>
        /// Converts the value of this BigInteger to a little-endian twos-complement
        /// uint span allocated by the caller using the fewest number of uints possible.
        /// </summary>
        /// <param name="buffer">Pre-allocated buffer by the caller.</param>
        /// <returns>The actual number of copied elements.</returns>
        private int WriteTo(Span<uint> buffer)
        {
            Debug.Assert(_bits is null || _sign == 0 ? buffer.Length == 2 : buffer.Length >= _bits.Length + 1);
 
            uint highDWord;
 
            if (_bits is null)
            {
                buffer[0] = unchecked((uint)_sign);
                highDWord = (_sign < 0) ? uint.MaxValue : 0;
            }
            else
            {
                _bits.CopyTo(buffer);
                buffer = buffer.Slice(0, _bits.Length + 1);
                if (_sign == -1)
                {
                    NumericsHelpers.DangerousMakeTwosComplement(buffer.Slice(0, buffer.Length - 1));  // Mutates dwords
                    highDWord = uint.MaxValue;
                }
                else
                    highDWord = 0;
            }
 
            // Find highest significant byte and ensure high bit is 0 if positive, 1 if negative
            int msb = buffer.Length - 2;
            while (msb > 0 && buffer[msb] == highDWord)
            {
                msb--;
            }
 
            // Ensure high bit is 0 if positive, 1 if negative
            bool needExtraByte = (buffer[msb] & 0x80000000) != (highDWord & 0x80000000);
            int count;
 
            if (needExtraByte)
            {
                count = msb + 2;
                buffer = buffer.Slice(0, count);
                buffer[buffer.Length - 1] = highDWord;
            }
            else
            {
                count = msb + 1;
            }
 
            return count;
        }
 
        public override string ToString()
        {
            return Number.FormatBigInteger(this, null, NumberFormatInfo.CurrentInfo);
        }
 
        public string ToString(IFormatProvider? provider)
        {
            return Number.FormatBigInteger(this, null, NumberFormatInfo.GetInstance(provider));
        }
 
        public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format)
        {
            return Number.FormatBigInteger(this, format, NumberFormatInfo.CurrentInfo);
        }
 
        public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider)
        {
            return Number.FormatBigInteger(this, format, NumberFormatInfo.GetInstance(provider));
        }
 
        private string DebuggerDisplay
        {
            get
            {
                // For very big numbers, ToString can be too long or even timeout for Visual Studio to display
                // Display a fast estimated value instead
 
                // Use ToString for small values
 
                if ((_bits is null) || (_bits.Length <= 4))
                {
                    return ToString();
                }
 
                // Estimate the value x as `L * 2^n`, while L is the value of high bits, and n is the length of low bits
                // Represent L as `k * 10^i`, then `x = L * 2^n = k * 10^(i + (n * log10(2)))`
                // Let `m = n * log10(2)`, the final result would be `x = (k * 10^(m - [m])) * 10^(i+[m])`
 
                const double log10Of2 = 0.3010299956639812; // Log10(2)
                ulong highBits = ((ulong)_bits[^1] << kcbitUint) + _bits[^2];
                double lowBitsCount32 = _bits.Length - 2; // if Length > int.MaxValue/32, counting in bits can cause overflow
                double exponentLow = lowBitsCount32 * kcbitUint * log10Of2;
 
                // Max possible length of _bits is int.MaxValue of bytes,
                // thus max possible value of BigInteger is 2^(8*Array.MaxLength)-1 which is larger than 10^(2^33)
                // Use long to avoid potential overflow
                long exponent = (long)exponentLow;
                double significand = (double)highBits * Math.Pow(10, exponentLow - exponent);
 
                // scale significand to [1, 10)
                double log10 = Math.Log10(significand);
                if (log10 >= 1)
                {
                    exponent += (long)log10;
                    significand /= Math.Pow(10, Math.Floor(log10));
                }
 
                // The digits can be incorrect because of floating point errors and estimation in Log and Exp
                // Keep some digits in the significand. 8 is arbitrarily chosen, about half of the precision of double
                significand = Math.Round(significand, 8);
 
                if (significand >= 10.0)
                {
                    // 9.9999999999999 can be rounded to 10, make the display to be more natural
                    significand /= 10.0;
                    exponent++;
                }
 
                string signStr = _sign < 0 ? NumberFormatInfo.CurrentInfo.NegativeSign : "";
 
                // Use about a half of the precision of double
                return $"{signStr}{significand:F8}e+{exponent}";
            }
        }
 
        public bool TryFormat(Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
        {
            return Number.TryFormatBigInteger(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten);
        }
 
        private static BigInteger Add(ReadOnlySpan<uint> leftBits, int leftSign, ReadOnlySpan<uint> rightBits, int rightSign)
        {
            bool trivialLeft = leftBits.IsEmpty;
            bool trivialRight = rightBits.IsEmpty;
 
            Debug.Assert(!(trivialLeft && trivialRight), "Trivial cases should be handled on the caller operator");
 
            BigInteger result;
            uint[]? bitsFromPool = null;
 
            if (trivialLeft)
            {
                Debug.Assert(!rightBits.IsEmpty);
 
                int size = rightBits.Length + 1;
                Span<uint> bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                BigIntegerCalculator.Add(rightBits, NumericsHelpers.Abs(leftSign), bits);
                result = new BigInteger(bits, leftSign < 0);
            }
            else if (trivialRight)
            {
                Debug.Assert(!leftBits.IsEmpty);
 
                int size = leftBits.Length + 1;
                Span<uint> bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                BigIntegerCalculator.Add(leftBits, NumericsHelpers.Abs(rightSign), bits);
                result = new BigInteger(bits, leftSign < 0);
            }
            else if (leftBits.Length < rightBits.Length)
            {
                Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty);
 
                int size = rightBits.Length + 1;
                Span<uint> bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                BigIntegerCalculator.Add(rightBits, leftBits, bits);
                result = new BigInteger(bits, leftSign < 0);
            }
            else
            {
                Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty);
 
                int size = leftBits.Length + 1;
                Span<uint> bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                BigIntegerCalculator.Add(leftBits, rightBits, bits);
                result = new BigInteger(bits, leftSign < 0);
            }
 
            if (bitsFromPool != null)
                    ArrayPool<uint>.Shared.Return(bitsFromPool);
 
            return result;
        }
 
        public static BigInteger operator -(BigInteger left, BigInteger right)
        {
            left.AssertValid();
            right.AssertValid();
 
            if (left._bits == null && right._bits == null)
                return (long)left._sign - right._sign;
 
            if (left._sign < 0 != right._sign < 0)
                return Add(left._bits, left._sign, right._bits, -1 * right._sign);
            return Subtract(left._bits, left._sign, right._bits, right._sign);
        }
 
        private static BigInteger Subtract(ReadOnlySpan<uint> leftBits, int leftSign, ReadOnlySpan<uint> rightBits, int rightSign)
        {
            bool trivialLeft = leftBits.IsEmpty;
            bool trivialRight = rightBits.IsEmpty;
 
            Debug.Assert(!(trivialLeft && trivialRight), "Trivial cases should be handled on the caller operator");
 
            BigInteger result;
            uint[]? bitsFromPool = null;
 
            if (trivialLeft)
            {
                Debug.Assert(!rightBits.IsEmpty);
 
                int size = rightBits.Length;
                Span<uint> bits = (size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                BigIntegerCalculator.Subtract(rightBits, NumericsHelpers.Abs(leftSign), bits);
                result = new BigInteger(bits, leftSign >= 0);
            }
            else if (trivialRight)
            {
                Debug.Assert(!leftBits.IsEmpty);
 
                int size = leftBits.Length;
                Span<uint> bits = (size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                BigIntegerCalculator.Subtract(leftBits, NumericsHelpers.Abs(rightSign), bits);
                result = new BigInteger(bits, leftSign < 0);
            }
            else if (BigIntegerCalculator.Compare(leftBits, rightBits) < 0)
            {
                int size = rightBits.Length;
                Span<uint> bits = (size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                BigIntegerCalculator.Subtract(rightBits, leftBits, bits);
                result = new BigInteger(bits, leftSign >= 0);
            }
            else
            {
                Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty);
 
                int size = leftBits.Length;
                Span<uint> bits = (size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                BigIntegerCalculator.Subtract(leftBits, rightBits, bits);
                result = new BigInteger(bits, leftSign < 0);
            }
 
            if (bitsFromPool != null)
                ArrayPool<uint>.Shared.Return(bitsFromPool);
 
            return result;
        }
 
        //
        // Explicit Conversions From BigInteger
        //
 
        public static explicit operator byte(BigInteger value)
        {
            return checked((byte)((int)value));
        }
 
        /// <summary>Explicitly converts a big integer to a <see cref="char" /> value.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to <see cref="char" /> value.</returns>
        public static explicit operator char(BigInteger value)
        {
            return checked((char)((int)value));
        }
 
        public static explicit operator decimal(BigInteger value)
        {
            value.AssertValid();
            if (value._bits == null)
                return value._sign;
 
            int length = value._bits.Length;
            if (length > 3) throw new OverflowException(SR.Overflow_Decimal);
 
            int lo = 0, mi = 0, hi = 0;
 
            unchecked
            {
                if (length > 2) hi = (int)value._bits[2];
                if (length > 1) mi = (int)value._bits[1];
                if (length > 0) lo = (int)value._bits[0];
            }
 
            return new decimal(lo, mi, hi, value._sign < 0, 0);
        }
 
        public static explicit operator double(BigInteger value)
        {
            value.AssertValid();
 
            int sign = value._sign;
            uint[]? bits = value._bits;
 
            if (bits == null)
                return sign;
 
            int length = bits.Length;
 
            // The maximum exponent for doubles is 1023, which corresponds to a uint bit length of 32.
            // All BigIntegers with bits[] longer than 32 evaluate to Double.Infinity (or NegativeInfinity).
            // Cases where the exponent is between 1024 and 1035 are handled in NumericsHelpers.GetDoubleFromParts.
            const int InfinityLength = 1024 / kcbitUint;
 
            if (length > InfinityLength)
            {
                if (sign == 1)
                    return double.PositiveInfinity;
                else
                    return double.NegativeInfinity;
            }
 
            ulong h = bits[length - 1];
            ulong m = length > 1 ? bits[length - 2] : 0;
            ulong l = length > 2 ? bits[length - 3] : 0;
 
            int z = BitOperations.LeadingZeroCount((uint)h);
 
            int exp = (length - 2) * 32 - z;
            ulong man = (h << 32 + z) | (m << z) | (l >> 32 - z);
 
            return NumericsHelpers.GetDoubleFromParts(sign, exp, man);
        }
 
        /// <summary>Explicitly converts a big integer to a <see cref="Half" /> value.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to <see cref="Half" /> value.</returns>
        public static explicit operator Half(BigInteger value)
        {
            return (Half)(double)value;
        }
 
        public static explicit operator short(BigInteger value)
        {
            return checked((short)((int)value));
        }
 
        public static explicit operator int(BigInteger value)
        {
            value.AssertValid();
            if (value._bits == null)
            {
                return value._sign;  // Value packed into int32 sign
            }
            if (value._bits.Length > 1)
            {
                // More than 32 bits
                throw new OverflowException(SR.Overflow_Int32);
            }
            if (value._sign > 0)
            {
                return checked((int)value._bits[0]);
            }
            if (value._bits[0] > kuMaskHighBit)
            {
                // Value > Int32.MinValue
                throw new OverflowException(SR.Overflow_Int32);
            }
            return unchecked(-(int)value._bits[0]);
        }
 
        public static explicit operator long(BigInteger value)
        {
            value.AssertValid();
            if (value._bits == null)
            {
                return value._sign;
            }
 
            int len = value._bits.Length;
            if (len > 2)
            {
                throw new OverflowException(SR.Overflow_Int64);
            }
 
            ulong uu;
            if (len > 1)
            {
                uu = NumericsHelpers.MakeUInt64(value._bits[1], value._bits[0]);
            }
            else
            {
                uu = value._bits[0];
            }
 
            long ll = value._sign > 0 ? unchecked((long)uu) : unchecked(-(long)uu);
            if ((ll > 0 && value._sign > 0) || (ll < 0 && value._sign < 0))
            {
                // Signs match, no overflow
                return ll;
            }
            throw new OverflowException(SR.Overflow_Int64);
        }
 
        /// <summary>Explicitly converts a big integer to a <see cref="Int128" /> value.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to <see cref="Int128" /> value.</returns>
        public static explicit operator Int128(BigInteger value)
        {
            value.AssertValid();
 
            if (value._bits is null)
            {
                return value._sign;
            }
 
            int len = value._bits.Length;
 
            if (len > 4)
            {
                throw new OverflowException(SR.Overflow_Int128);
            }
 
            UInt128 uu;
 
            if (len > 2)
            {
                uu = new UInt128(
                    NumericsHelpers.MakeUInt64((len > 3) ? value._bits[3] : 0, value._bits[2]),
                    NumericsHelpers.MakeUInt64(value._bits[1], value._bits[0])
                );
            }
            else if (len > 1)
            {
                uu = NumericsHelpers.MakeUInt64(value._bits[1], value._bits[0]);
            }
            else
            {
                uu = value._bits[0];
            }
 
            Int128 ll = (value._sign > 0) ? unchecked((Int128)uu) : unchecked(-(Int128)uu);
 
            if (((ll > 0) && (value._sign > 0)) || ((ll < 0) && (value._sign < 0)))
            {
                // Signs match, no overflow
                return ll;
            }
            throw new OverflowException(SR.Overflow_Int128);
        }
 
        /// <summary>Explicitly converts a big integer to a <see cref="IntPtr" /> value.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to <see cref="IntPtr" /> value.</returns>
        public static explicit operator nint(BigInteger value)
        {
            if (Environment.Is64BitProcess)
            {
                return (nint)(long)value;
            }
            else
            {
                return (int)value;
            }
        }
 
        [CLSCompliant(false)]
        public static explicit operator sbyte(BigInteger value)
        {
            return checked((sbyte)((int)value));
        }
 
        public static explicit operator float(BigInteger value)
        {
            return (float)((double)value);
        }
 
        [CLSCompliant(false)]
        public static explicit operator ushort(BigInteger value)
        {
            return checked((ushort)((int)value));
        }
 
        [CLSCompliant(false)]
        public static explicit operator uint(BigInteger value)
        {
            value.AssertValid();
            if (value._bits == null)
            {
                return checked((uint)value._sign);
            }
            else if (value._bits.Length > 1 || value._sign < 0)
            {
                throw new OverflowException(SR.Overflow_UInt32);
            }
            else
            {
                return value._bits[0];
            }
        }
 
        [CLSCompliant(false)]
        public static explicit operator ulong(BigInteger value)
        {
            value.AssertValid();
            if (value._bits == null)
            {
                return checked((ulong)value._sign);
            }
 
            int len = value._bits.Length;
            if (len > 2 || value._sign < 0)
            {
                throw new OverflowException(SR.Overflow_UInt64);
            }
 
            if (len > 1)
            {
                return NumericsHelpers.MakeUInt64(value._bits[1], value._bits[0]);
            }
            return value._bits[0];
        }
 
        /// <summary>Explicitly converts a big integer to a <see cref="UInt128" /> value.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to <see cref="UInt128" /> value.</returns>
        [CLSCompliant(false)]
        public static explicit operator UInt128(BigInteger value)
        {
            value.AssertValid();
 
            if (value._bits is null)
            {
                return checked((UInt128)value._sign);
            }
 
            int len = value._bits.Length;
 
            if ((len > 4) || (value._sign < 0))
            {
                throw new OverflowException(SR.Overflow_UInt128);
            }
 
            if (len > 2)
            {
                return new UInt128(
                    NumericsHelpers.MakeUInt64((len > 3) ? value._bits[3] : 0, value._bits[2]),
                    NumericsHelpers.MakeUInt64(value._bits[1], value._bits[0])
                );
            }
            else if (len > 1)
            {
                return NumericsHelpers.MakeUInt64(value._bits[1], value._bits[0]);
            }
            return value._bits[0];
        }
 
        /// <summary>Explicitly converts a big integer to a <see cref="UIntPtr" /> value.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to <see cref="UIntPtr" /> value.</returns>
        [CLSCompliant(false)]
        public static explicit operator nuint(BigInteger value)
        {
            if (Environment.Is64BitProcess)
            {
                return (nuint)(ulong)value;
            }
            else
            {
                return (uint)value;
            }
        }
 
        //
        // Explicit Conversions To BigInteger
        //
 
        public static explicit operator BigInteger(decimal value)
        {
            return new BigInteger(value);
        }
 
        public static explicit operator BigInteger(double value)
        {
            return new BigInteger(value);
        }
 
        /// <summary>Explicitly converts a <see cref="Half" /> value to a big integer.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to a big integer.</returns>
        public static explicit operator BigInteger(Half value)
        {
            return new BigInteger((float)value);
        }
 
        /// <summary>Explicitly converts a <see cref="Complex" /> value to a big integer.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to a big integer.</returns>
        public static explicit operator BigInteger(Complex value)
        {
            if (value.Imaginary != 0)
            {
                ThrowHelper.ThrowOverflowException();
            }
            return (BigInteger)value.Real;
        }
 
        public static explicit operator BigInteger(float value)
        {
            return new BigInteger(value);
        }
 
        //
        // Implicit Conversions To BigInteger
        //
 
        public static implicit operator BigInteger(byte value)
        {
            return new BigInteger(value);
        }
 
        /// <summary>Implicitly converts a <see cref="char" /> value to a big integer.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to a big integer.</returns>
        public static implicit operator BigInteger(char value)
        {
            return new BigInteger(value);
        }
 
        public static implicit operator BigInteger(short value)
        {
            return new BigInteger(value);
        }
 
        public static implicit operator BigInteger(int value)
        {
            return new BigInteger(value);
        }
 
        public static implicit operator BigInteger(long value)
        {
            return new BigInteger(value);
        }
 
        /// <summary>Implicitly converts a <see cref="Int128" /> value to a big integer.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to a big integer.</returns>
        public static implicit operator BigInteger(Int128 value)
        {
            int sign;
            uint[]? bits;
 
            if ((int.MinValue < value) && (value <= int.MaxValue))
            {
                sign = (int)value;
                bits = null;
            }
            else if (value == int.MinValue)
            {
                return s_bnMinInt;
            }
            else
            {
                UInt128 x;
                if (value < 0)
                {
                    x = unchecked((UInt128)(-value));
                    sign = -1;
                }
                else
                {
                    x = (UInt128)value;
                    sign = +1;
                }
 
                if (x <= uint.MaxValue)
                {
                    bits = new uint[1];
                    bits[0] = (uint)(x >> (kcbitUint * 0));
                }
                else if (x <= ulong.MaxValue)
                {
                    bits = new uint[2];
                    bits[0] = (uint)(x >> (kcbitUint * 0));
                    bits[1] = (uint)(x >> (kcbitUint * 1));
                }
                else if (x <= new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))
                {
                    bits = new uint[3];
                    bits[0] = (uint)(x >> (kcbitUint * 0));
                    bits[1] = (uint)(x >> (kcbitUint * 1));
                    bits[2] = (uint)(x >> (kcbitUint * 2));
                }
                else
                {
                    bits = new uint[4];
                    bits[0] = (uint)(x >> (kcbitUint * 0));
                    bits[1] = (uint)(x >> (kcbitUint * 1));
                    bits[2] = (uint)(x >> (kcbitUint * 2));
                    bits[3] = (uint)(x >> (kcbitUint * 3));
                }
            }
 
            return new BigInteger(sign, bits);
        }
 
        /// <summary>Implicitly converts a <see cref="IntPtr" /> value to a big integer.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to a big integer.</returns>
        public static implicit operator BigInteger(nint value)
        {
            if (Environment.Is64BitProcess)
            {
                return new BigInteger(value);
            }
            else
            {
                return new BigInteger((int)value);
            }
        }
 
        [CLSCompliant(false)]
        public static implicit operator BigInteger(sbyte value)
        {
            return new BigInteger(value);
        }
 
        [CLSCompliant(false)]
        public static implicit operator BigInteger(ushort value)
        {
            return new BigInteger(value);
        }
 
        [CLSCompliant(false)]
        public static implicit operator BigInteger(uint value)
        {
            return new BigInteger(value);
        }
 
        [CLSCompliant(false)]
        public static implicit operator BigInteger(ulong value)
        {
            return new BigInteger(value);
        }
 
        /// <summary>Implicitly converts a <see cref="UInt128" /> value to a big integer.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to a big integer.</returns>
        [CLSCompliant(false)]
        public static implicit operator BigInteger(UInt128 value)
        {
            int sign = +1;
            uint[]? bits;
 
            if (value <= (uint)int.MaxValue)
            {
                sign = (int)value;
                bits = null;
            }
            else if (value <= uint.MaxValue)
            {
                bits = new uint[1];
                bits[0] = (uint)(value >> (kcbitUint * 0));
            }
            else if (value <= ulong.MaxValue)
            {
                bits = new uint[2];
                bits[0] = (uint)(value >> (kcbitUint * 0));
                bits[1] = (uint)(value >> (kcbitUint * 1));
            }
            else if (value <= new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))
            {
                bits = new uint[3];
                bits[0] = (uint)(value >> (kcbitUint * 0));
                bits[1] = (uint)(value >> (kcbitUint * 1));
                bits[2] = (uint)(value >> (kcbitUint * 2));
            }
            else
            {
                bits = new uint[4];
                bits[0] = (uint)(value >> (kcbitUint * 0));
                bits[1] = (uint)(value >> (kcbitUint * 1));
                bits[2] = (uint)(value >> (kcbitUint * 2));
                bits[3] = (uint)(value >> (kcbitUint * 3));
            }
 
            return new BigInteger(sign, bits);
        }
 
        /// <summary>Implicitly converts a <see cref="UIntPtr" /> value to a big integer.</summary>
        /// <param name="value">The value to convert.</param>
        /// <returns><paramref name="value" /> converted to a big integer.</returns>
        [CLSCompliant(false)]
        public static implicit operator BigInteger(nuint value)
        {
            if (Environment.Is64BitProcess)
            {
                return new BigInteger(value);
            }
            else
            {
                return new BigInteger((uint)value);
            }
        }
 
        public static BigInteger operator &(BigInteger left, BigInteger right)
        {
            if (left.IsZero || right.IsZero)
            {
                return Zero;
            }
 
            if (left._bits is null && right._bits is null)
            {
                return left._sign & right._sign;
            }
 
            uint xExtend = (left._sign < 0) ? uint.MaxValue : 0;
            uint yExtend = (right._sign < 0) ? uint.MaxValue : 0;
 
            uint[]? leftBufferFromPool = null;
            int size = (left._bits?.Length ?? 1) + 1;
            Span<uint> x = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                         ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                         : leftBufferFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
            x = x.Slice(0, left.WriteTo(x));
 
            uint[]? rightBufferFromPool = null;
            size = (right._bits?.Length ?? 1) + 1;
            Span<uint> y = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                         ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                         : rightBufferFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
            y = y.Slice(0, right.WriteTo(y));
 
            uint[]? resultBufferFromPool = null;
            size = Math.Max(x.Length, y.Length);
            Span<uint> z = (size <= BigIntegerCalculator.StackAllocThreshold
                         ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                         : resultBufferFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
            for (int i = 0; i < z.Length; i++)
            {
                uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend;
                uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend;
                z[i] = xu & yu;
            }
 
            if (leftBufferFromPool != null)
                ArrayPool<uint>.Shared.Return(leftBufferFromPool);
 
            if (rightBufferFromPool != null)
                ArrayPool<uint>.Shared.Return(rightBufferFromPool);
 
            var result = new BigInteger(z);
 
            if (resultBufferFromPool != null)
                ArrayPool<uint>.Shared.Return(resultBufferFromPool);
 
            return result;
        }
 
        public static BigInteger operator |(BigInteger left, BigInteger right)
        {
            if (left.IsZero)
                return right;
            if (right.IsZero)
                return left;
 
            if (left._bits is null && right._bits is null)
            {
                return left._sign | right._sign;
            }
 
            uint xExtend = (left._sign < 0) ? uint.MaxValue : 0;
            uint yExtend = (right._sign < 0) ? uint.MaxValue : 0;
 
            uint[]? leftBufferFromPool = null;
            int size = (left._bits?.Length ?? 1) + 1;
            Span<uint> x = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                         ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                         : leftBufferFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
            x = x.Slice(0, left.WriteTo(x));
 
            uint[]? rightBufferFromPool = null;
            size = (right._bits?.Length ?? 1) + 1;
            Span<uint> y = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                         ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                         : rightBufferFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
            y = y.Slice(0, right.WriteTo(y));
 
            uint[]? resultBufferFromPool = null;
            size = Math.Max(x.Length, y.Length);
            Span<uint> z = (size <= BigIntegerCalculator.StackAllocThreshold
                         ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                         : resultBufferFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
            for (int i = 0; i < z.Length; i++)
            {
                uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend;
                uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend;
                z[i] = xu | yu;
            }
 
            if (leftBufferFromPool != null)
                ArrayPool<uint>.Shared.Return(leftBufferFromPool);
 
            if (rightBufferFromPool != null)
                ArrayPool<uint>.Shared.Return(rightBufferFromPool);
 
            var result = new BigInteger(z);
 
            if (resultBufferFromPool != null)
                ArrayPool<uint>.Shared.Return(resultBufferFromPool);
 
            return result;
        }
 
        public static BigInteger operator ^(BigInteger left, BigInteger right)
        {
            if (left._bits is null && right._bits is null)
            {
                return left._sign ^ right._sign;
            }
 
            uint xExtend = (left._sign < 0) ? uint.MaxValue : 0;
            uint yExtend = (right._sign < 0) ? uint.MaxValue : 0;
 
            uint[]? leftBufferFromPool = null;
            int size = (left._bits?.Length ?? 1) + 1;
            Span<uint> x = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                         ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                         : leftBufferFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
            x = x.Slice(0, left.WriteTo(x));
 
            uint[]? rightBufferFromPool = null;
            size = (right._bits?.Length ?? 1) + 1;
            Span<uint> y = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                         ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                         : rightBufferFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
            y = y.Slice(0, right.WriteTo(y));
 
            uint[]? resultBufferFromPool = null;
            size = Math.Max(x.Length, y.Length);
            Span<uint> z = (size <= BigIntegerCalculator.StackAllocThreshold
                         ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                         : resultBufferFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
            for (int i = 0; i < z.Length; i++)
            {
                uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend;
                uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend;
                z[i] = xu ^ yu;
            }
 
            if (leftBufferFromPool != null)
                ArrayPool<uint>.Shared.Return(leftBufferFromPool);
 
            if (rightBufferFromPool != null)
                ArrayPool<uint>.Shared.Return(rightBufferFromPool);
 
            var result = new BigInteger(z);
 
            if (resultBufferFromPool != null)
                ArrayPool<uint>.Shared.Return(resultBufferFromPool);
 
            return result;
        }
 
        public static BigInteger operator <<(BigInteger value, int shift)
        {
            if (shift == 0)
                return value;
 
            if (shift == int.MinValue)
                return ((value >> int.MaxValue) >> 1);
 
            if (shift < 0)
                return value >> -shift;
 
            (int digitShift, int smallShift) = Math.DivRem(shift, kcbitUint);
 
            uint[]? xdFromPool = null;
            int xl = value._bits?.Length ?? 1;
            Span<uint> xd = (xl <= BigIntegerCalculator.StackAllocThreshold
                          ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                          : xdFromPool = ArrayPool<uint>.Shared.Rent(xl)).Slice(0, xl);
            bool negx = value.GetPartsForBitManipulation(xd);
 
            int zl = xl + digitShift + 1;
            uint[]? zdFromPool = null;
            Span<uint> zd = ((uint)zl <= BigIntegerCalculator.StackAllocThreshold
                          ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                          : zdFromPool = ArrayPool<uint>.Shared.Rent(zl)).Slice(0, zl);
            zd.Clear();
 
            uint carry = 0;
            if (smallShift == 0)
            {
                for (int i = 0; i < xd.Length; i++)
                {
                    zd[i + digitShift] = xd[i];
                }
            }
            else
            {
                int carryShift = kcbitUint - smallShift;
                int i;
                for (i = 0; i < xd.Length; i++)
                {
                    uint rot = xd[i];
                    zd[i + digitShift] = rot << smallShift | carry;
                    carry = rot >> carryShift;
                }
            }
 
            zd[zd.Length - 1] = carry;
 
            var result = new BigInteger(zd, negx);
 
            if (xdFromPool != null)
                ArrayPool<uint>.Shared.Return(xdFromPool);
            if (zdFromPool != null)
                ArrayPool<uint>.Shared.Return(zdFromPool);
 
            return result;
        }
 
        public static BigInteger operator >>(BigInteger value, int shift)
        {
            if (shift == 0)
                return value;
 
            if (shift == int.MinValue)
                return ((value << int.MaxValue) << 1);
 
            if (shift < 0)
                return value << -shift;
 
            (int digitShift, int smallShift) = Math.DivRem(shift, kcbitUint);
 
            BigInteger result;
 
            uint[]? xdFromPool = null;
            int xl = value._bits?.Length ?? 1;
            Span<uint> xd = (xl <= BigIntegerCalculator.StackAllocThreshold
                          ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                          : xdFromPool = ArrayPool<uint>.Shared.Rent(xl)).Slice(0, xl);
 
            bool negx = value.GetPartsForBitManipulation(xd);
            bool trackSignBit = false;
 
            if (negx)
            {
                if (shift >= ((long)kcbitUint * xd.Length))
                {
                    result = MinusOne;
                    goto exit;
                }
 
                NumericsHelpers.DangerousMakeTwosComplement(xd); // Mutates xd
 
                // For a shift of N x 32 bit,
                // We check for a special case where its sign bit could be outside the uint array after 2's complement conversion.
                // For example given [0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF], its 2's complement is [0x01, 0x00, 0x00]
                // After a 32 bit right shift, it becomes [0x00, 0x00] which is [0x00, 0x00] when converted back.
                // The expected result is [0x00, 0x00, 0xFFFFFFFF] (2's complement) or [0x00, 0x00, 0x01] when converted back
                // If the 2's component's last element is a 0, we will track the sign externally
                trackSignBit = smallShift == 0 && xd[xd.Length - 1] == 0;
            }
 
            uint[]? zdFromPool = null;
            int zl = Math.Max(xl - digitShift, 0) + (trackSignBit ? 1 : 0);
            Span<uint> zd = ((uint)zl <= BigIntegerCalculator.StackAllocThreshold
                          ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                          : zdFromPool = ArrayPool<uint>.Shared.Rent(zl)).Slice(0, zl);
            zd.Clear();
 
            if (smallShift == 0)
            {
                for (int i = xd.Length - 1; i >= digitShift; i--)
                {
                    zd[i - digitShift] = xd[i];
                }
            }
            else
            {
                int carryShift = kcbitUint - smallShift;
                uint carry = 0;
                for (int i = xd.Length - 1; i >= digitShift; i--)
                {
                    uint rot = xd[i];
                    if (negx && i == xd.Length - 1)
                        // Sign-extend the first shift for negative ints then let the carry propagate
                        zd[i - digitShift] = (rot >> smallShift) | (0xFFFFFFFF << carryShift);
                    else
                        zd[i - digitShift] = (rot >> smallShift) | carry;
                    carry = rot << carryShift;
                }
            }
 
            if (negx)
            {
                // Set the tracked sign to the last element
                if (trackSignBit)
                    zd[zd.Length - 1] = 0xFFFFFFFF;
 
                NumericsHelpers.DangerousMakeTwosComplement(zd); // Mutates zd
            }
 
            result = new BigInteger(zd, negx);
 
            if (zdFromPool != null)
                ArrayPool<uint>.Shared.Return(zdFromPool);
        exit:
            if (xdFromPool != null)
                ArrayPool<uint>.Shared.Return(xdFromPool);
 
            return result;
        }
 
        public static BigInteger operator ~(BigInteger value)
        {
            return -(value + One);
        }
 
        public static BigInteger operator -(BigInteger value)
        {
            value.AssertValid();
            return new BigInteger(-value._sign, value._bits);
        }
 
        public static BigInteger operator +(BigInteger value)
        {
            value.AssertValid();
            return value;
        }
 
        public static BigInteger operator ++(BigInteger value)
        {
            return value + One;
        }
 
        public static BigInteger operator --(BigInteger value)
        {
            return value - One;
        }
 
        public static BigInteger operator +(BigInteger left, BigInteger right)
        {
            left.AssertValid();
            right.AssertValid();
 
            if (left._bits == null && right._bits == null)
                return (long)left._sign + right._sign;
 
            if (left._sign < 0 != right._sign < 0)
                return Subtract(left._bits, left._sign, right._bits, -1 * right._sign);
            return Add(left._bits, left._sign, right._bits, right._sign);
        }
 
        public static BigInteger operator *(BigInteger left, BigInteger right)
        {
            left.AssertValid();
            right.AssertValid();
 
            if (left._bits == null && right._bits == null)
                return (long)left._sign * right._sign;
 
            return Multiply(left._bits, left._sign, right._bits, right._sign);
        }
 
        private static BigInteger Multiply(ReadOnlySpan<uint> left, int leftSign, ReadOnlySpan<uint> right, int rightSign)
        {
            bool trivialLeft = left.IsEmpty;
            bool trivialRight = right.IsEmpty;
 
            Debug.Assert(!(trivialLeft && trivialRight), "Trivial cases should be handled on the caller operator");
 
            BigInteger result;
            uint[]? bitsFromPool = null;
 
            if (trivialLeft)
            {
                Debug.Assert(!right.IsEmpty);
 
                int size = right.Length + 1;
                Span<uint> bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                BigIntegerCalculator.Multiply(right, NumericsHelpers.Abs(leftSign), bits);
                result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0));
            }
            else if (trivialRight)
            {
                Debug.Assert(!left.IsEmpty);
 
                int size = left.Length + 1;
                Span<uint> bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                BigIntegerCalculator.Multiply(left, NumericsHelpers.Abs(rightSign), bits);
                result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0));
            }
            else if (left == right)
            {
                int size = left.Length + right.Length;
                Span<uint> bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                BigIntegerCalculator.Square(left, bits);
                result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0));
            }
            else if (left.Length < right.Length)
            {
                Debug.Assert(!left.IsEmpty && !right.IsEmpty);
 
                int size = left.Length + right.Length;
                Span<uint> bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
                bits.Clear();
 
                BigIntegerCalculator.Multiply(right, left, bits);
                result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0));
            }
            else
            {
                Debug.Assert(!left.IsEmpty && !right.IsEmpty);
 
                int size = left.Length + right.Length;
                Span<uint> bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
                bits.Clear();
 
                BigIntegerCalculator.Multiply(left, right, bits);
                result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0));
            }
 
            if (bitsFromPool != null)
                ArrayPool<uint>.Shared.Return(bitsFromPool);
 
            return result;
        }
 
        public static BigInteger operator /(BigInteger dividend, BigInteger divisor)
        {
            dividend.AssertValid();
            divisor.AssertValid();
 
            bool trivialDividend = dividend._bits == null;
            bool trivialDivisor = divisor._bits == null;
 
            if (trivialDividend && trivialDivisor)
            {
                return dividend._sign / divisor._sign;
            }
 
            if (trivialDividend)
            {
                // The divisor is non-trivial
                // and therefore the bigger one
                return s_bnZeroInt;
            }
 
            uint[]? quotientFromPool = null;
 
            if (trivialDivisor)
            {
                Debug.Assert(dividend._bits != null);
 
                int size = dividend._bits.Length;
                Span<uint> quotient = ((uint)size <= BigIntegerCalculator.StackAllocThreshold
                                    ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                    : quotientFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                try
                {
                    //may throw DivideByZeroException
                    BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign), quotient);
                    return new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0));
                }
                finally
                {
                    if (quotientFromPool != null)
                        ArrayPool<uint>.Shared.Return(quotientFromPool);
                }
            }
 
            Debug.Assert(dividend._bits != null && divisor._bits != null);
 
            if (dividend._bits.Length < divisor._bits.Length)
            {
                return s_bnZeroInt;
            }
            else
            {
                int size = dividend._bits.Length - divisor._bits.Length + 1;
                Span<uint> quotient = ((uint)size < BigIntegerCalculator.StackAllocThreshold
                                    ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                                    : quotientFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
                BigIntegerCalculator.Divide(dividend._bits, divisor._bits, quotient);
                var result = new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0));
 
                if (quotientFromPool != null)
                    ArrayPool<uint>.Shared.Return(quotientFromPool);
 
                return result;
            }
        }
 
        public static BigInteger operator %(BigInteger dividend, BigInteger divisor)
        {
            dividend.AssertValid();
            divisor.AssertValid();
 
            bool trivialDividend = dividend._bits == null;
            bool trivialDivisor = divisor._bits == null;
 
            if (trivialDividend && trivialDivisor)
            {
                return dividend._sign % divisor._sign;
            }
 
            if (trivialDividend)
            {
                // The divisor is non-trivial
                // and therefore the bigger one
                return dividend;
            }
 
            if (trivialDivisor)
            {
                Debug.Assert(dividend._bits != null);
                uint remainder = BigIntegerCalculator.Remainder(dividend._bits, NumericsHelpers.Abs(divisor._sign));
                return dividend._sign < 0 ? -1 * remainder : remainder;
            }
 
            Debug.Assert(dividend._bits != null && divisor._bits != null);
 
            if (dividend._bits.Length < divisor._bits.Length)
            {
                return dividend;
            }
 
            uint[]? bitsFromPool = null;
            int size = dividend._bits.Length;
            Span<uint> bits = (size <= BigIntegerCalculator.StackAllocThreshold
                            ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                            : bitsFromPool = ArrayPool<uint>.Shared.Rent(size)).Slice(0, size);
 
            BigIntegerCalculator.Remainder(dividend._bits, divisor._bits, bits);
            var result = new BigInteger(bits, dividend._sign < 0);
 
            if (bitsFromPool != null)
                ArrayPool<uint>.Shared.Return(bitsFromPool);
 
            return result;
        }
 
        public static bool operator <(BigInteger left, BigInteger right)
        {
            return left.CompareTo(right) < 0;
        }
 
        public static bool operator <=(BigInteger left, BigInteger right)
        {
            return left.CompareTo(right) <= 0;
        }
 
        public static bool operator >(BigInteger left, BigInteger right)
        {
            return left.CompareTo(right) > 0;
        }
        public static bool operator >=(BigInteger left, BigInteger right)
        {
            return left.CompareTo(right) >= 0;
        }
 
        public static bool operator ==(BigInteger left, BigInteger right)
        {
            return left.Equals(right);
        }
 
        public static bool operator !=(BigInteger left, BigInteger right)
        {
            return !left.Equals(right);
        }
 
        public static bool operator <(BigInteger left, long right)
        {
            return left.CompareTo(right) < 0;
        }
 
        public static bool operator <=(BigInteger left, long right)
        {
            return left.CompareTo(right) <= 0;
        }
 
        public static bool operator >(BigInteger left, long right)
        {
            return left.CompareTo(right) > 0;
        }
 
        public static bool operator >=(BigInteger left, long right)
        {
            return left.CompareTo(right) >= 0;
        }
 
        public static bool operator ==(BigInteger left, long right)
        {
            return left.Equals(right);
        }
 
        public static bool operator !=(BigInteger left, long right)
        {
            return !left.Equals(right);
        }
 
        public static bool operator <(long left, BigInteger right)
        {
            return right.CompareTo(left) > 0;
        }
 
        public static bool operator <=(long left, BigInteger right)
        {
            return right.CompareTo(left) >= 0;
        }
 
        public static bool operator >(long left, BigInteger right)
        {
            return right.CompareTo(left) < 0;
        }
 
        public static bool operator >=(long left, BigInteger right)
        {
            return right.CompareTo(left) <= 0;
        }
 
        public static bool operator ==(long left, BigInteger right)
        {
            return right.Equals(left);
        }
 
        public static bool operator !=(long left, BigInteger right)
        {
            return !right.Equals(left);
        }
 
        [CLSCompliant(false)]
        public static bool operator <(BigInteger left, ulong right)
        {
            return left.CompareTo(right) < 0;
        }
 
        [CLSCompliant(false)]
        public static bool operator <=(BigInteger left, ulong right)
        {
            return left.CompareTo(right) <= 0;
        }
 
        [CLSCompliant(false)]
        public static bool operator >(BigInteger left, ulong right)
        {
            return left.CompareTo(right) > 0;
        }
 
        [CLSCompliant(false)]
        public static bool operator >=(BigInteger left, ulong right)
        {
            return left.CompareTo(right) >= 0;
        }
 
        [CLSCompliant(false)]
        public static bool operator ==(BigInteger left, ulong right)
        {
            return left.Equals(right);
        }
 
        [CLSCompliant(false)]
        public static bool operator !=(BigInteger left, ulong right)
        {
            return !left.Equals(right);
        }
 
        [CLSCompliant(false)]
        public static bool operator <(ulong left, BigInteger right)
        {
            return right.CompareTo(left) > 0;
        }
 
        [CLSCompliant(false)]
        public static bool operator <=(ulong left, BigInteger right)
        {
            return right.CompareTo(left) >= 0;
        }
 
        [CLSCompliant(false)]
        public static bool operator >(ulong left, BigInteger right)
        {
            return right.CompareTo(left) < 0;
        }
 
        [CLSCompliant(false)]
        public static bool operator >=(ulong left, BigInteger right)
        {
            return right.CompareTo(left) <= 0;
        }
 
        [CLSCompliant(false)]
        public static bool operator ==(ulong left, BigInteger right)
        {
            return right.Equals(left);
        }
 
        [CLSCompliant(false)]
        public static bool operator !=(ulong left, BigInteger right)
        {
            return !right.Equals(left);
        }
 
        /// <summary>
        /// Gets the number of bits required for shortest two's complement representation of the current instance without the sign bit.
        /// </summary>
        /// <returns>The minimum non-negative number of bits in two's complement notation without the sign bit.</returns>
        /// <remarks>This method returns 0 iff the value of current object is equal to <see cref="Zero"/> or <see cref="MinusOne"/>. For positive integers the return value is equal to the ordinary binary representation string length.</remarks>
        public long GetBitLength()
        {
            AssertValid();
 
            uint highValue;
            int bitsArrayLength;
            int sign = _sign;
            uint[]? bits = _bits;
 
            if (bits == null)
            {
                bitsArrayLength = 1;
                highValue = (uint)(sign < 0 ? -sign : sign);
            }
            else
            {
                bitsArrayLength = bits.Length;
                highValue = bits[bitsArrayLength - 1];
            }
 
            long bitLength = bitsArrayLength * 32L - BitOperations.LeadingZeroCount(highValue);
 
            if (sign >= 0)
                return bitLength;
 
            // When negative and IsPowerOfTwo, the answer is (bitLength - 1)
 
            // Check highValue
            if ((highValue & (highValue - 1)) != 0)
                return bitLength;
 
            // Check the rest of the bits (if present)
            for (int i = bitsArrayLength - 2; i >= 0; i--)
            {
                // bits array is always non-null when bitsArrayLength >= 2
                if (bits![i] == 0)
                    continue;
 
                return bitLength;
            }
 
            return bitLength - 1;
        }
 
        /// <summary>
        /// Encapsulate the logic of normalizing the "small" and "large" forms of BigInteger
        /// into the "large" form so that Bit Manipulation algorithms can be simplified.
        /// </summary>
        /// <param name="xd">
        /// The UInt32 array containing the entire big integer in "large" (denormalized) form.
        /// E.g., the number one (1) and negative one (-1) are both stored as 0x00000001
        /// BigInteger values Int32.MinValue &lt; x &lt;= Int32.MaxValue are converted to this
        /// format for convenience.
        /// </param>
        /// <returns>True for negative numbers.</returns>
        private bool GetPartsForBitManipulation(Span<uint> xd)
        {
            Debug.Assert(_bits is null ? xd.Length == 1 : xd.Length == _bits.Length);
 
            if (_bits is null)
            {
                xd[0] = (uint)(_sign < 0 ? -_sign : _sign);
            }
            else
            {
                _bits.CopyTo(xd);
            }
            return _sign < 0;
        }
 
        [Conditional("DEBUG")]
        private void AssertValid()
        {
            if (_bits != null)
            {
                // _sign must be +1 or -1 when _bits is non-null
                Debug.Assert(_sign == 1 || _sign == -1);
                // _bits must contain at least 1 element or be null
                Debug.Assert(_bits.Length > 0);
                // Wasted space: _bits[0] could have been packed into _sign
                Debug.Assert(_bits.Length > 1 || _bits[0] >= kuMaskHighBit);
                // Wasted space: leading zeros could have been truncated
                Debug.Assert(_bits[_bits.Length - 1] != 0);
                // Arrays larger than this can't fit into a Span<byte>
                Debug.Assert(_bits.Length <= MaxLength);
            }
            else
            {
                // Int32.MinValue should not be stored in the _sign field
                Debug.Assert(_sign > int.MinValue);
            }
        }
 
        //
        // IAdditiveIdentity
        //
 
        /// <inheritdoc cref="IAdditiveIdentity{TSelf, TResult}.AdditiveIdentity" />
        static BigInteger IAdditiveIdentity<BigInteger, BigInteger>.AdditiveIdentity => Zero;
 
        //
        // IBinaryInteger
        //
 
        /// <inheritdoc cref="IBinaryInteger{TSelf}.DivRem(TSelf, TSelf)" />
        public static (BigInteger Quotient, BigInteger Remainder) DivRem(BigInteger left, BigInteger right)
        {
            BigInteger quotient = DivRem(left, right, out BigInteger remainder);
            return (quotient, remainder);
        }
 
        /// <inheritdoc cref="IBinaryInteger{TSelf}.LeadingZeroCount(TSelf)" />
        public static BigInteger LeadingZeroCount(BigInteger value)
        {
            value.AssertValid();
 
            if (value._bits is null)
            {
                return int.LeadingZeroCount(value._sign);
            }
 
            // When the value is positive, we just need to get the lzcnt of the most significant bits
            // Otherwise, we're negative and the most significant bit is always set.
 
            return (value._sign >= 0) ? uint.LeadingZeroCount(value._bits[^1]) : 0;
        }
 
        /// <inheritdoc cref="IBinaryInteger{TSelf}.PopCount(TSelf)" />
        public static BigInteger PopCount(BigInteger value)
        {
            value.AssertValid();
 
            if (value._bits is null)
            {
                return int.PopCount(value._sign);
            }
 
            ulong result = 0;
 
            if (value._sign >= 0)
            {
                // When the value is positive, we simply need to do a popcount for all bits
 
                for (int i = 0; i < value._bits.Length; i++)
                {
                    uint part = value._bits[i];
                    result += uint.PopCount(part);
                }
            }
            else
            {
                // When the value is negative, we need to popcount the two's complement representation
                // We'll do this "inline" to avoid needing to unnecessarily allocate.
 
                int i = 0;
                uint part;
 
                do
                {
                    // Simply process bits, adding the carry while the previous value is zero
 
                    part = ~value._bits[i] + 1;
                    result += uint.PopCount(part);
 
                    i++;
                }
                while ((part == 0) && (i < value._bits.Length));
 
                while (i < value._bits.Length)
                {
                    // Then process the remaining bits only utilizing the one's complement
 
                    part = ~value._bits[i];
                    result += uint.PopCount(part);
 
                    i++;
                }
            }
 
            return result;
        }
 
        /// <inheritdoc cref="IBinaryInteger{TSelf}.RotateLeft(TSelf, int)" />
        public static BigInteger RotateLeft(BigInteger value, int rotateAmount)
        {
            value.AssertValid();
            int byteCount = (value._bits is null) ? sizeof(int) : (value._bits.Length * 4);
 
            // Normalize the rotate amount to drop full rotations
            rotateAmount = (int)(rotateAmount % (byteCount * 8L));
 
            if (rotateAmount == 0)
                return value;
 
            if (rotateAmount == int.MinValue)
                return RotateRight(RotateRight(value, int.MaxValue), 1);
 
            if (rotateAmount < 0)
                return RotateRight(value, -rotateAmount);
 
            (int digitShift, int smallShift) = Math.DivRem(rotateAmount, kcbitUint);
 
            uint[]? xdFromPool = null;
            int xl = value._bits?.Length ?? 1;
 
            Span<uint> xd = (xl <= BigIntegerCalculator.StackAllocThreshold)
                          ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                          : xdFromPool = ArrayPool<uint>.Shared.Rent(xl);
            xd = xd.Slice(0, xl);
 
            bool negx = value.GetPartsForBitManipulation(xd);
 
            int zl = xl;
            uint[]? zdFromPool = null;
 
            Span<uint> zd = (zl <= BigIntegerCalculator.StackAllocThreshold)
                          ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                          : zdFromPool = ArrayPool<uint>.Shared.Rent(zl);
            zd = zd.Slice(0, zl);
 
            zd.Clear();
 
            if (negx)
            {
                NumericsHelpers.DangerousMakeTwosComplement(xd);
            }
 
            if (smallShift == 0)
            {
                int dstIndex = 0;
                int srcIndex = xd.Length - digitShift;
 
                do
                {
                    // Copy last digitShift elements from xd to the start of zd
                    zd[dstIndex] = xd[srcIndex];
 
                    dstIndex++;
                    srcIndex++;
                }
                while (srcIndex < xd.Length);
 
                srcIndex = 0;
 
                while (dstIndex < zd.Length)
                {
                    // Copy remaining elements from start of xd to end of zd
                    zd[dstIndex] = xd[srcIndex];
 
                    dstIndex++;
                    srcIndex++;
                }
            }
            else
            {
                int carryShift = kcbitUint - smallShift;
 
                int dstIndex = 0;
                int srcIndex = 0;
 
                uint carry = 0;
 
                if (digitShift == 0)
                {
                    carry = xd[^1] >> carryShift;
                }
                else
                {
                    srcIndex = xd.Length - digitShift;
                    carry = xd[srcIndex - 1] >> carryShift;
                }
 
                do
                {
                    uint part = xd[srcIndex];
 
                    zd[dstIndex] = (part << smallShift) | carry;
                    carry = part >> carryShift;
 
                    dstIndex++;
                    srcIndex++;
                }
                while (srcIndex < xd.Length);
 
                srcIndex = 0;
 
                while (dstIndex < zd.Length)
                {
                    uint part = xd[srcIndex];
 
                    zd[dstIndex] = (part << smallShift) | carry;
                    carry = part >> carryShift;
 
                    dstIndex++;
                    srcIndex++;
                }
            }
 
            if (negx && (int)zd[^1] < 0)
            {
                NumericsHelpers.DangerousMakeTwosComplement(zd);
            }
            else
            {
                negx = false;
            }
 
            var result = new BigInteger(zd, negx);
 
            if (xdFromPool != null)
                ArrayPool<uint>.Shared.Return(xdFromPool);
            if (zdFromPool != null)
                ArrayPool<uint>.Shared.Return(zdFromPool);
 
            return result;
        }
 
        /// <inheritdoc cref="IBinaryInteger{TSelf}.RotateRight(TSelf, int)" />
        public static BigInteger RotateRight(BigInteger value, int rotateAmount)
        {
            value.AssertValid();
            int byteCount = (value._bits is null) ? sizeof(int) : (value._bits.Length * 4);
 
            // Normalize the rotate amount to drop full rotations
            rotateAmount = (int)(rotateAmount % (byteCount * 8L));
 
            if (rotateAmount == 0)
                return value;
 
            if (rotateAmount == int.MinValue)
                return RotateLeft(RotateLeft(value, int.MaxValue), 1);
 
            if (rotateAmount < 0)
                return RotateLeft(value, -rotateAmount);
 
            (int digitShift, int smallShift) = Math.DivRem(rotateAmount, kcbitUint);
 
            uint[]? xdFromPool = null;
            int xl = value._bits?.Length ?? 1;
 
            Span<uint> xd = (xl <= BigIntegerCalculator.StackAllocThreshold)
                          ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                          : xdFromPool = ArrayPool<uint>.Shared.Rent(xl);
            xd = xd.Slice(0, xl);
 
            bool negx = value.GetPartsForBitManipulation(xd);
 
            int zl = xl;
            uint[]? zdFromPool = null;
 
            Span<uint> zd = (zl <= BigIntegerCalculator.StackAllocThreshold)
                          ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                          : zdFromPool = ArrayPool<uint>.Shared.Rent(zl);
            zd = zd.Slice(0, zl);
 
            zd.Clear();
 
            if (negx)
            {
                NumericsHelpers.DangerousMakeTwosComplement(xd);
            }
 
            if (smallShift == 0)
            {
                int dstIndex = 0;
                int srcIndex = digitShift;
 
                do
                {
                    // Copy first digitShift elements from xd to the end of zd
                    zd[dstIndex] = xd[srcIndex];
 
                    dstIndex++;
                    srcIndex++;
                }
                while (srcIndex < xd.Length);
 
                srcIndex = 0;
 
                while (dstIndex < zd.Length)
                {
                    // Copy remaining elements from end of xd to start of zd
                    zd[dstIndex] = xd[srcIndex];
 
                    dstIndex++;
                    srcIndex++;
                }
            }
            else
            {
                int carryShift = kcbitUint - smallShift;
 
                int dstIndex = 0;
                int srcIndex = digitShift;
 
                uint carry = 0;
 
                if (digitShift == 0)
                {
                    carry = xd[^1] << carryShift;
                }
                else
                {
                    carry = xd[srcIndex - 1] << carryShift;
                }
 
                do
                {
                    uint part = xd[srcIndex];
 
                    zd[dstIndex] = (part >> smallShift) | carry;
                    carry = part << carryShift;
 
                    dstIndex++;
                    srcIndex++;
                }
                while (srcIndex < xd.Length);
 
                srcIndex = 0;
 
                while (dstIndex < zd.Length)
                {
                    uint part = xd[srcIndex];
 
                    zd[dstIndex] = (part >> smallShift) | carry;
                    carry = part << carryShift;
 
                    dstIndex++;
                    srcIndex++;
                }
            }
 
            if (negx && (int)zd[^1] < 0)
            {
                NumericsHelpers.DangerousMakeTwosComplement(zd);
            }
            else
            {
                negx = false;
            }
 
            var result = new BigInteger(zd, negx);
 
            if (xdFromPool != null)
                ArrayPool<uint>.Shared.Return(xdFromPool);
            if (zdFromPool != null)
                ArrayPool<uint>.Shared.Return(zdFromPool);
 
            return result;
        }
 
        /// <inheritdoc cref="IBinaryInteger{TSelf}.TrailingZeroCount(TSelf)" />
        public static BigInteger TrailingZeroCount(BigInteger value)
        {
            value.AssertValid();
 
            if (value._bits is null)
            {
                return int.TrailingZeroCount(value._sign);
            }
 
            ulong result = 0;
 
            // Both positive values and their two's-complement negative representation will share the same TrailingZeroCount,
            // so the sign of value does not matter and both cases can be handled in the same way
 
            uint part = value._bits[0];
 
            for (int i = 1; (part == 0) && (i < value._bits.Length); i++)
            {
                part = value._bits[i];
                result += (sizeof(uint) * 8);
            }
 
            result += uint.TrailingZeroCount(part);
 
            return result;
        }
 
        /// <inheritdoc cref="IBinaryInteger{TSelf}.TryReadBigEndian(ReadOnlySpan{byte}, bool, out TSelf)" />
        static bool IBinaryInteger<BigInteger>.TryReadBigEndian(ReadOnlySpan<byte> source, bool isUnsigned, out BigInteger value)
        {
            value = new BigInteger(source, isUnsigned, isBigEndian: true);
            return true;
        }
 
        /// <inheritdoc cref="IBinaryInteger{TSelf}.TryReadLittleEndian(ReadOnlySpan{byte}, bool, out TSelf)" />
        static bool IBinaryInteger<BigInteger>.TryReadLittleEndian(ReadOnlySpan<byte> source, bool isUnsigned, out BigInteger value)
        {
            value = new BigInteger(source, isUnsigned, isBigEndian: false);
            return true;
        }
 
        /// <inheritdoc cref="IBinaryInteger{TSelf}.GetShortestBitLength()" />
        int IBinaryInteger<BigInteger>.GetShortestBitLength()
        {
            AssertValid();
            uint[]? bits = _bits;
 
            if (bits is null)
            {
                int value = _sign;
 
                if (value >= 0)
                {
                    return (sizeof(int) * 8) - BitOperations.LeadingZeroCount((uint)(value));
                }
                else
                {
                    return (sizeof(int) * 8) + 1 - BitOperations.LeadingZeroCount((uint)(~value));
                }
            }
 
            int result = (bits.Length - 1) * 32;
 
            if (_sign >= 0)
            {
                result += (sizeof(uint) * 8) - BitOperations.LeadingZeroCount(bits[^1]);
            }
            else
            {
                uint part = ~bits[^1] + 1;
 
                // We need to remove the "carry" (the +1) if any of the initial
                // bytes are not zero. This ensures we get the correct two's complement
                // part for the computation.
 
                for (int index = 0; index < bits.Length - 1; index++)
                {
                    if (bits[index] != 0)
                    {
                        part -= 1;
                        break;
                    }
                }
 
                result += (sizeof(uint) * 8) + 1 - BitOperations.LeadingZeroCount(~part);
            }
 
            return result;
        }
 
        /// <inheritdoc cref="IBinaryInteger{TSelf}.GetByteCount()" />
        int IBinaryInteger<BigInteger>.GetByteCount() => GetGenericMathByteCount();
 
        /// <inheritdoc cref="IBinaryInteger{TSelf}.TryWriteBigEndian(Span{byte}, out int)" />
        bool IBinaryInteger<BigInteger>.TryWriteBigEndian(Span<byte> destination, out int bytesWritten)
        {
            AssertValid();
            uint[]? bits = _bits;
 
            int byteCount = GetGenericMathByteCount();
 
            if (destination.Length >= byteCount)
            {
                if (bits is null)
                {
                    int value = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(_sign) : _sign;
                    Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value);
                }
                else if (_sign >= 0)
                {
                    // When the value is positive, we simply need to copy all bits as big endian
 
                    ref byte startAddress = ref MemoryMarshal.GetReference(destination);
                    ref byte address = ref Unsafe.Add(ref startAddress, (bits.Length - 1) * sizeof(uint));
 
                    for (int i = 0; i < bits.Length; i++)
                    {
                        uint part = bits[i];
 
                        if (BitConverter.IsLittleEndian)
                        {
                            part = BinaryPrimitives.ReverseEndianness(part);
                        }
 
                        Unsafe.WriteUnaligned(ref address, part);
                        address = ref Unsafe.Subtract(ref address, sizeof(uint));
                    }
                }
                else
                {
                    // When the value is negative, we need to copy the two's complement representation
                    // We'll do this "inline" to avoid needing to unnecessarily allocate.
 
                    ref byte startAddress = ref MemoryMarshal.GetReference(destination);
                    ref byte address = ref Unsafe.Add(ref startAddress, byteCount - sizeof(uint));
 
                    int i = 0;
                    uint part;
 
                    do
                    {
                        // first do complement and +1 as long as carry is needed
                        part = ~bits[i] + 1;
 
                        if (BitConverter.IsLittleEndian)
                        {
                            part = BinaryPrimitives.ReverseEndianness(part);
                        }
 
                        Unsafe.WriteUnaligned(ref address, part);
                        address = ref Unsafe.Subtract(ref address, sizeof(uint));
 
                        i++;
                    }
                    while ((part == 0) && (i < bits.Length));
 
                    while (i < bits.Length)
                    {
                        // now ones complement is sufficient
                        part = ~bits[i];
 
                        if (BitConverter.IsLittleEndian)
                        {
                            part = BinaryPrimitives.ReverseEndianness(part);
                        }
 
                        Unsafe.WriteUnaligned(ref address, part);
                        address = ref Unsafe.Subtract(ref address, sizeof(uint));
 
                        i++;
                    }
 
                    if (Unsafe.AreSame(ref address, ref startAddress))
                    {
                        // We need one extra part to represent the sign as the most
                        // significant bit of the two's complement value was 0.
                        Unsafe.WriteUnaligned(ref address, uint.MaxValue);
                    }
                    else
                    {
                        // Otherwise we should have been precisely one part behind address
                        Debug.Assert(Unsafe.AreSame(ref startAddress, ref Unsafe.Add(ref address, sizeof(uint))));
                    }
                }
 
                bytesWritten = byteCount;
                return true;
            }
            else
            {
                bytesWritten = 0;
                return false;
            }
        }
 
        /// <inheritdoc cref="IBinaryInteger{TSelf}.TryWriteLittleEndian(Span{byte}, out int)" />
        bool IBinaryInteger<BigInteger>.TryWriteLittleEndian(Span<byte> destination, out int bytesWritten)
        {
            AssertValid();
            uint[]? bits = _bits;
 
            int byteCount = GetGenericMathByteCount();
 
            if (destination.Length >= byteCount)
            {
                if (bits is null)
                {
                    int value = BitConverter.IsLittleEndian ? _sign : BinaryPrimitives.ReverseEndianness(_sign);
                    Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value);
                }
                else if (_sign >= 0)
                {
                    // When the value is positive, we simply need to copy all bits as little endian
 
                    ref byte address = ref MemoryMarshal.GetReference(destination);
 
                    for (int i = 0; i < bits.Length; i++)
                    {
                        uint part = bits[i];
 
                        if (!BitConverter.IsLittleEndian)
                        {
                            part = BinaryPrimitives.ReverseEndianness(part);
                        }
 
                        Unsafe.WriteUnaligned(ref address, part);
                        address = ref Unsafe.Add(ref address, sizeof(uint));
                    }
                }
                else
                {
                    // When the value is negative, we need to copy the two's complement representation
                    // We'll do this "inline" to avoid needing to unnecessarily allocate.
 
                    ref byte address = ref MemoryMarshal.GetReference(destination);
                    ref byte lastAddress = ref Unsafe.Add(ref address, byteCount - sizeof(uint));
 
                    int i = 0;
                    uint part;
 
                    do
                    {
                        // first do complement and +1 as long as carry is needed
                        part = ~bits[i] + 1;
 
                        if (!BitConverter.IsLittleEndian)
                        {
                            part = BinaryPrimitives.ReverseEndianness(part);
                        }
 
                        Unsafe.WriteUnaligned(ref address, part);
                        address = ref Unsafe.Add(ref address, sizeof(uint));
 
                        i++;
                    }
                    while ((part == 0) && (i < bits.Length));
 
                    while (i < bits.Length)
                    {
                        // now ones complement is sufficient
                        part = ~bits[i];
 
                        if (!BitConverter.IsLittleEndian)
                        {
                            part = BinaryPrimitives.ReverseEndianness(part);
                        }
 
                        Unsafe.WriteUnaligned(ref address, part);
                        address = ref Unsafe.Add(ref address, sizeof(uint));
 
                        i++;
                    }
 
                    if (Unsafe.AreSame(ref address, ref lastAddress))
                    {
                        // We need one extra part to represent the sign as the most
                        // significant bit of the two's complement value was 0.
                        Unsafe.WriteUnaligned(ref address, uint.MaxValue);
                    }
                    else
                    {
                        // Otherwise we should have been precisely one part ahead address
                        Debug.Assert(Unsafe.AreSame(ref lastAddress, ref Unsafe.Subtract(ref address, sizeof(uint))));
                    }
                }
 
                bytesWritten = byteCount;
                return true;
            }
            else
            {
                bytesWritten = 0;
                return false;
            }
        }
 
        private int GetGenericMathByteCount()
        {
            AssertValid();
            uint[]? bits = _bits;
 
            if (bits is null)
            {
                return sizeof(int);
            }
 
            int result = bits.Length * 4;
 
            if (_sign < 0)
            {
                uint part = ~bits[^1] + 1;
 
                // We need to remove the "carry" (the +1) if any of the initial
                // bytes are not zero. This ensures we get the correct two's complement
                // part for the computation.
 
                for (int index = 0; index < bits.Length - 1; index++)
                {
                    if (bits[index] != 0)
                    {
                        part -= 1;
                        break;
                    }
                }
 
                if ((int)part >= 0)
                {
                    // When the most significant bit of the part is zero
                    // we need another part to represent the value.
                    result += sizeof(uint);
                }
            }
 
            return result;
        }
 
        //
        // IBinaryNumber
        //
 
        /// <inheritdoc cref="IBinaryNumber{TSelf}.AllBitsSet" />
        static BigInteger IBinaryNumber<BigInteger>.AllBitsSet => MinusOne;
 
        /// <inheritdoc cref="IBinaryNumber{TSelf}.IsPow2(TSelf)" />
        public static bool IsPow2(BigInteger value) => value.IsPowerOfTwo;
 
        /// <inheritdoc cref="IBinaryNumber{TSelf}.Log2(TSelf)" />
        public static BigInteger Log2(BigInteger value)
        {
            value.AssertValid();
 
            if (IsNegative(value))
            {
                ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
            }
 
            if (value._bits is null)
            {
                return 31 ^ uint.LeadingZeroCount((uint)(value._sign | 1));
            }
 
            return ((value._bits.Length * 32) - 1) ^ uint.LeadingZeroCount(value._bits[^1]);
        }
 
        //
        // IMultiplicativeIdentity
        //
 
        /// <inheritdoc cref="IMultiplicativeIdentity{TSelf, TResult}.MultiplicativeIdentity" />
        static BigInteger IMultiplicativeIdentity<BigInteger, BigInteger>.MultiplicativeIdentity => One;
 
        //
        // INumber
        //
 
        /// <inheritdoc cref="INumber{TSelf}.Clamp(TSelf, TSelf, TSelf)" />
        public static BigInteger Clamp(BigInteger value, BigInteger min, BigInteger max)
        {
            value.AssertValid();
 
            min.AssertValid();
            max.AssertValid();
 
            if (min > max)
            {
                ThrowMinMaxException(min, max);
            }
 
            if (value < min)
            {
                return min;
            }
            else if (value > max)
            {
                return max;
            }
 
            return value;
 
            [DoesNotReturn]
            static void ThrowMinMaxException<T>(T min, T max)
            {
                throw new ArgumentException(SR.Format(SR.Argument_MinMaxValue, min, max));
            }
        }
 
        /// <inheritdoc cref="INumber{TSelf}.CopySign(TSelf, TSelf)" />
        public static BigInteger CopySign(BigInteger value, BigInteger sign)
        {
            value.AssertValid();
            sign.AssertValid();
 
            int currentSign = value._sign;
 
            if (value._bits is null)
            {
                currentSign = (currentSign >= 0) ? 1 : -1;
            }
 
            int targetSign = sign._sign;
 
            if (sign._bits is null)
            {
                targetSign = (targetSign >= 0) ? 1 : -1;
            }
 
            return (currentSign == targetSign) ? value : -value;
        }
 
        /// <inheritdoc cref="INumber{TSelf}.MaxNumber(TSelf, TSelf)" />
        static BigInteger INumber<BigInteger>.MaxNumber(BigInteger x, BigInteger y) => Max(x, y);
 
        /// <inheritdoc cref="INumber{TSelf}.MinNumber(TSelf, TSelf)" />
        static BigInteger INumber<BigInteger>.MinNumber(BigInteger x, BigInteger y) => Min(x, y);
 
        /// <inheritdoc cref="INumber{TSelf}.Sign(TSelf)" />
        static int INumber<BigInteger>.Sign(BigInteger value)
        {
            value.AssertValid();
 
            if (value._bits is null)
            {
                return int.Sign(value._sign);
            }
 
            return value._sign;
        }
 
        //
        // INumberBase
        //
 
        /// <inheritdoc cref="INumberBase{TSelf}.Radix" />
        static int INumberBase<BigInteger>.Radix => 2;
 
        /// <inheritdoc cref="INumberBase{TSelf}.CreateChecked{TOther}(TOther)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static BigInteger CreateChecked<TOther>(TOther value)
            where TOther : INumberBase<TOther>
        {
            BigInteger result;
 
            if (typeof(TOther) == typeof(BigInteger))
            {
                result = (BigInteger)(object)value;
            }
            else if (!TryConvertFromChecked(value, out result) && !TOther.TryConvertToChecked(value, out result))
            {
                ThrowHelper.ThrowNotSupportedException();
            }
 
            return result;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.CreateSaturating{TOther}(TOther)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static BigInteger CreateSaturating<TOther>(TOther value)
            where TOther : INumberBase<TOther>
        {
            BigInteger result;
 
            if (typeof(TOther) == typeof(BigInteger))
            {
                result = (BigInteger)(object)value;
            }
            else if (!TryConvertFromSaturating(value, out result) && !TOther.TryConvertToSaturating(value, out result))
            {
                ThrowHelper.ThrowNotSupportedException();
            }
 
            return result;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.CreateTruncating{TOther}(TOther)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static BigInteger CreateTruncating<TOther>(TOther value)
            where TOther : INumberBase<TOther>
        {
            BigInteger result;
 
            if (typeof(TOther) == typeof(BigInteger))
            {
                result = (BigInteger)(object)value;
            }
            else if (!TryConvertFromTruncating(value, out result) && !TOther.TryConvertToTruncating(value, out result))
            {
                ThrowHelper.ThrowNotSupportedException();
            }
 
            return result;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsCanonical(TSelf)" />
        static bool INumberBase<BigInteger>.IsCanonical(BigInteger value) => true;
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsComplexNumber(TSelf)" />
        static bool INumberBase<BigInteger>.IsComplexNumber(BigInteger value) => false;
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsEvenInteger(TSelf)" />
        public static bool IsEvenInteger(BigInteger value)
        {
            value.AssertValid();
 
            if (value._bits is null)
            {
                return (value._sign & 1) == 0;
            }
            return (value._bits[0] & 1) == 0;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsFinite(TSelf)" />
        static bool INumberBase<BigInteger>.IsFinite(BigInteger value) => true;
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsImaginaryNumber(TSelf)" />
        static bool INumberBase<BigInteger>.IsImaginaryNumber(BigInteger value) => false;
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsInfinity(TSelf)" />
        static bool INumberBase<BigInteger>.IsInfinity(BigInteger value) => false;
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsInteger(TSelf)" />
        static bool INumberBase<BigInteger>.IsInteger(BigInteger value) => true;
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsNaN(TSelf)" />
        static bool INumberBase<BigInteger>.IsNaN(BigInteger value) => false;
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsNegative(TSelf)" />
        public static bool IsNegative(BigInteger value)
        {
            value.AssertValid();
            return value._sign < 0;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsNegativeInfinity(TSelf)" />
        static bool INumberBase<BigInteger>.IsNegativeInfinity(BigInteger value) => false;
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsNormal(TSelf)" />
        static bool INumberBase<BigInteger>.IsNormal(BigInteger value) => (value != 0);
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsOddInteger(TSelf)" />
        public static bool IsOddInteger(BigInteger value)
        {
            value.AssertValid();
 
            if (value._bits is null)
            {
                return (value._sign & 1) != 0;
            }
            return (value._bits[0] & 1) != 0;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsPositive(TSelf)" />
        public static bool IsPositive(BigInteger value)
        {
            value.AssertValid();
            return value._sign >= 0;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsPositiveInfinity(TSelf)" />
        static bool INumberBase<BigInteger>.IsPositiveInfinity(BigInteger value) => false;
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsRealNumber(TSelf)" />
        static bool INumberBase<BigInteger>.IsRealNumber(BigInteger value) => true;
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsSubnormal(TSelf)" />
        static bool INumberBase<BigInteger>.IsSubnormal(BigInteger value) => false;
 
        /// <inheritdoc cref="INumberBase{TSelf}.IsZero(TSelf)" />
        static bool INumberBase<BigInteger>.IsZero(BigInteger value)
        {
            value.AssertValid();
            return value._sign == 0;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitude(TSelf, TSelf)" />
        public static BigInteger MaxMagnitude(BigInteger x, BigInteger y)
        {
            x.AssertValid();
            y.AssertValid();
 
            BigInteger ax = Abs(x);
            BigInteger ay = Abs(y);
 
            if (ax > ay)
            {
                return x;
            }
 
            if (ax == ay)
            {
                return IsNegative(x) ? y : x;
            }
 
            return y;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitudeNumber(TSelf, TSelf)" />
        static BigInteger INumberBase<BigInteger>.MaxMagnitudeNumber(BigInteger x, BigInteger y) => MaxMagnitude(x, y);
 
        /// <inheritdoc cref="INumberBase{TSelf}.MinMagnitude(TSelf, TSelf)" />
        public static BigInteger MinMagnitude(BigInteger x, BigInteger y)
        {
            x.AssertValid();
            y.AssertValid();
 
            BigInteger ax = Abs(x);
            BigInteger ay = Abs(y);
 
            if (ax < ay)
            {
                return x;
            }
 
            if (ax == ay)
            {
                return IsNegative(x) ? x : y;
            }
 
            return y;
        }
 
        /// <inheritdoc cref="INumberBase{TSelf}.MinMagnitudeNumber(TSelf, TSelf)" />
        static BigInteger INumberBase<BigInteger>.MinMagnitudeNumber(BigInteger x, BigInteger y) => MinMagnitude(x, y);
 
        /// <inheritdoc cref="INumberBase{TSelf}.MultiplyAddEstimate(TSelf, TSelf, TSelf)" />
        static BigInteger INumberBase<BigInteger>.MultiplyAddEstimate(BigInteger left, BigInteger right, BigInteger addend) => (left * right) + addend;
 
        /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromChecked{TOther}(TOther, out TSelf)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<BigInteger>.TryConvertFromChecked<TOther>(TOther value, out BigInteger result) => TryConvertFromChecked(value, out result);
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static bool TryConvertFromChecked<TOther>(TOther value, out BigInteger result)
            where TOther : INumberBase<TOther>
        {
            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 = (BigInteger)actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(double))
            {
                double actualValue = (double)(object)value;
                result = checked((BigInteger)actualValue);
                return true;
            }
            else if (typeof(TOther) == typeof(Half))
            {
                Half actualValue = (Half)(object)value;
                result = checked((BigInteger)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 = 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 = checked((BigInteger)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 = 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}.TryConvertFromSaturating{TOther}(TOther, out TSelf)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<BigInteger>.TryConvertFromSaturating<TOther>(TOther value, out BigInteger result) => TryConvertFromSaturating(value, out result);
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static bool TryConvertFromSaturating<TOther>(TOther value, out BigInteger result)
            where TOther : INumberBase<TOther>
        {
            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 = (BigInteger)actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(double))
            {
                double actualValue = (double)(object)value;
                result = double.IsNaN(actualValue) ? Zero : (BigInteger)actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(Half))
            {
                Half actualValue = (Half)(object)value;
                result = Half.IsNaN(actualValue) ? Zero : (BigInteger)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 = 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 = float.IsNaN(actualValue) ? Zero : (BigInteger)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 = 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}.TryConvertFromTruncating{TOther}(TOther, out TSelf)" />
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool INumberBase<BigInteger>.TryConvertFromTruncating<TOther>(TOther value, out BigInteger result) => TryConvertFromTruncating(value, out result);
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static bool TryConvertFromTruncating<TOther>(TOther value, out BigInteger result)
            where TOther : INumberBase<TOther>
        {
            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 = (BigInteger)actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(double))
            {
                double actualValue = (double)(object)value;
                result = double.IsNaN(actualValue) ? Zero : (BigInteger)actualValue;
                return true;
            }
            else if (typeof(TOther) == typeof(Half))
            {
                Half actualValue = (Half)(object)value;
                result = Half.IsNaN(actualValue) ? Zero : (BigInteger)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 = 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 = float.IsNaN(actualValue) ? Zero : (BigInteger)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 = 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<BigInteger>.TryConvertToChecked<TOther>(BigInteger value, [MaybeNullWhen(false)] out TOther result)
        {
            if (typeof(TOther) == typeof(byte))
            {
                byte actualResult = checked((byte)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(char))
            {
                char actualResult = checked((char)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(decimal))
            {
                decimal actualResult = checked((decimal)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(double))
            {
                double actualResult = (double)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Half))
            {
                Half actualResult = (Half)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(short))
            {
                short actualResult = checked((short)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(int))
            {
                int actualResult = checked((int)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(long))
            {
                long actualResult = checked((long)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Int128))
            {
                Int128 actualResult = checked((Int128)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(nint))
            {
                nint actualResult = checked((nint)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Complex))
            {
                Complex actualResult = (Complex)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(sbyte))
            {
                sbyte actualResult = checked((sbyte)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(float))
            {
                float actualResult = checked((float)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(ushort))
            {
                ushort actualResult = checked((ushort)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(uint))
            {
                uint actualResult = checked((uint)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(ulong))
            {
                ulong actualResult = checked((ulong)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(UInt128))
            {
                UInt128 actualResult = checked((UInt128)value);
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(nuint))
            {
                nuint actualResult = checked((nuint)value);
                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<BigInteger>.TryConvertToSaturating<TOther>(BigInteger value, [MaybeNullWhen(false)] out TOther result)
        {
            if (typeof(TOther) == typeof(byte))
            {
                byte actualResult;
 
                if (value._bits is not null)
                {
                    actualResult = IsNegative(value) ? byte.MinValue : byte.MaxValue;
                }
                else
                {
                    actualResult = (value._sign >= byte.MaxValue) ? byte.MaxValue :
                                   (value._sign <= byte.MinValue) ? byte.MinValue : (byte)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(char))
            {
                char actualResult;
 
                if (value._bits is not null)
                {
                    actualResult = IsNegative(value) ? char.MinValue : char.MaxValue;
                }
                else
                {
                    actualResult = (value._sign >= char.MaxValue) ? char.MaxValue :
                                   (value._sign <= char.MinValue) ? char.MinValue : (char)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(decimal))
            {
                decimal actualResult = (value >= new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)) ? decimal.MaxValue :
                                       (value <= new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001)) ? decimal.MinValue : (decimal)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(double))
            {
                double actualResult = (double)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Half))
            {
                Half actualResult = (Half)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(short))
            {
                short actualResult;
 
                if (value._bits is not null)
                {
                    actualResult = IsNegative(value) ? short.MinValue : short.MaxValue;
                }
                else
                {
                    actualResult = (value._sign >= short.MaxValue) ? short.MaxValue :
                                   (value._sign <= short.MinValue) ? short.MinValue : (short)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(int))
            {
                int actualResult;
 
                if (value._bits is not null)
                {
                    actualResult = IsNegative(value) ? int.MinValue : int.MaxValue;
                }
                else
                {
                    actualResult = (value._sign >= int.MaxValue) ? int.MaxValue :
                                   (value._sign <= int.MinValue) ? int.MinValue : (int)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(long))
            {
                long actualResult = (value >= long.MaxValue) ? long.MaxValue :
                                    (value <= long.MinValue) ? long.MinValue : (long)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Int128))
            {
                Int128 actualResult = (value >= Int128.MaxValue) ? Int128.MaxValue :
                                      (value <= Int128.MinValue) ? Int128.MinValue : (Int128)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(nint))
            {
                nint actualResult = (value >= nint.MaxValue) ? nint.MaxValue :
                                    (value <= nint.MinValue) ? nint.MinValue : (nint)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Complex))
            {
                Complex actualResult = (Complex)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(sbyte))
            {
                sbyte actualResult;
 
                if (value._bits is not null)
                {
                    actualResult = IsNegative(value) ? sbyte.MinValue : sbyte.MaxValue;
                }
                else
                {
                    actualResult = (value._sign >= sbyte.MaxValue) ? sbyte.MaxValue :
                                   (value._sign <= sbyte.MinValue) ? sbyte.MinValue : (sbyte)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(float))
            {
                float actualResult = (float)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(ushort))
            {
                ushort actualResult;
 
                if (value._bits is not null)
                {
                    actualResult = IsNegative(value) ? ushort.MinValue : ushort.MaxValue;
                }
                else
                {
                    actualResult = (value._sign >= ushort.MaxValue) ? ushort.MaxValue :
                                   (value._sign <= ushort.MinValue) ? ushort.MinValue : (ushort)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(uint))
            {
                uint actualResult = (value >= uint.MaxValue) ? uint.MaxValue :
                                    IsNegative(value) ? uint.MinValue : (uint)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(ulong))
            {
                ulong actualResult = (value >= ulong.MaxValue) ? ulong.MaxValue :
                                     IsNegative(value) ? ulong.MinValue : (ulong)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(UInt128))
            {
                UInt128 actualResult = (value >= UInt128.MaxValue) ? UInt128.MaxValue :
                                       IsNegative(value) ? UInt128.MinValue : (UInt128)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(nuint))
            {
                nuint actualResult = (value >= nuint.MaxValue) ? nuint.MaxValue :
                                     IsNegative(value) ? nuint.MinValue : (nuint)value;
                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<BigInteger>.TryConvertToTruncating<TOther>(BigInteger value, [MaybeNullWhen(false)] out TOther result)
        {
            if (typeof(TOther) == typeof(byte))
            {
                byte actualResult;
 
                if (value._bits is not null)
                {
                    uint bits = value._bits[0];
 
                    if (IsNegative(value))
                    {
                        bits = ~bits + 1;
                    }
 
                    actualResult = (byte)bits;
                }
                else
                {
                    actualResult = (byte)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(char))
            {
                char actualResult;
 
                if (value._bits is not null)
                {
                    uint bits = value._bits[0];
 
                    if (IsNegative(value))
                    {
                        bits = ~bits + 1;
                    }
 
                    actualResult = (char)bits;
                }
                else
                {
                    actualResult = (char)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(decimal))
            {
                decimal actualResult = (value >= new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)) ? decimal.MaxValue :
                                       (value <= new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001)) ? decimal.MinValue : (decimal)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(double))
            {
                double actualResult = (double)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Half))
            {
                Half actualResult = (Half)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(short))
            {
                short actualResult;
 
                if (value._bits is not null)
                {
                    actualResult = IsNegative(value) ? (short)(~value._bits[0] + 1) : (short)value._bits[0];
                }
                else
                {
                    actualResult = (short)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(int))
            {
                int actualResult;
 
                if (value._bits is not null)
                {
                    actualResult = IsNegative(value) ? (int)(~value._bits[0] + 1) : (int)value._bits[0];
                }
                else
                {
                    actualResult = value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(long))
            {
                long actualResult;
 
                if (value._bits is not null)
                {
                    ulong bits = 0;
 
                    if (value._bits.Length >= 2)
                    {
                        bits = value._bits[1];
                        bits <<= 32;
                    }
 
                    bits |= value._bits[0];
 
                    if (IsNegative(value))
                    {
                        bits = ~bits + 1;
                    }
 
                    actualResult = (long)bits;
                }
                else
                {
                    actualResult = value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Int128))
            {
                Int128 actualResult;
 
                if (value._bits is not null)
                {
                    ulong lowerBits = 0;
                    ulong upperBits = 0;
 
                    if (value._bits.Length >= 4)
                    {
                        upperBits = value._bits[3];
                        upperBits <<= 32;
                    }
 
                    if (value._bits.Length >= 3)
                    {
                        upperBits |= value._bits[2];
                    }
 
                    if (value._bits.Length >= 2)
                    {
                        lowerBits = value._bits[1];
                        lowerBits <<= 32;
                    }
 
                    lowerBits |= value._bits[0];
 
                    UInt128 bits = new UInt128(upperBits, lowerBits);
 
                    if (IsNegative(value))
                    {
                        bits = ~bits + 1;
                    }
 
                    actualResult = (Int128)bits;
                }
                else
                {
                    actualResult = value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(nint))
            {
                nint actualResult;
 
                if (value._bits is not null)
                {
                    nuint bits = 0;
 
                    if (Environment.Is64BitProcess && (value._bits.Length >= 2))
                    {
                        bits = value._bits[1];
                        bits <<= 32;
                    }
 
                    bits |= value._bits[0];
 
                    if (IsNegative(value))
                    {
                        bits = ~bits + 1;
                    }
 
                    actualResult = (nint)bits;
                }
                else
                {
                    actualResult = value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(Complex))
            {
                Complex actualResult = (Complex)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(sbyte))
            {
                sbyte actualResult;
 
                if (value._bits is not null)
                {
                    actualResult = IsNegative(value) ? (sbyte)(~value._bits[0] + 1) : (sbyte)value._bits[0];
                }
                else
                {
                    actualResult = (sbyte)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(float))
            {
                float actualResult = (float)value;
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(ushort))
            {
                ushort actualResult;
 
                if (value._bits is not null)
                {
                    uint bits = value._bits[0];
 
                    if (IsNegative(value))
                    {
                        bits = ~bits + 1;
                    }
 
                    actualResult = (ushort)bits;
                }
                else
                {
                    actualResult = (ushort)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(uint))
            {
                uint actualResult;
 
                if (value._bits is not null)
                {
                    uint bits = value._bits[0];
 
                    if (IsNegative(value))
                    {
                        bits = ~bits + 1;
                    }
 
                    actualResult = bits;
                }
                else
                {
                    actualResult = (uint)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(ulong))
            {
                ulong actualResult;
 
                if (value._bits is not null)
                {
                    ulong bits = 0;
 
                    if (value._bits.Length >= 2)
                    {
                        bits = value._bits[1];
                        bits <<= 32;
                    }
 
                    bits |= value._bits[0];
 
                    if (IsNegative(value))
                    {
                        bits = ~bits + 1;
                    }
 
                    actualResult = bits;
                }
                else
                {
                    actualResult = (ulong)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(UInt128))
            {
                UInt128 actualResult;
 
                if (value._bits is not null)
                {
                    ulong lowerBits = 0;
                    ulong upperBits = 0;
 
                    if (value._bits.Length >= 4)
                    {
                        upperBits = value._bits[3];
                        upperBits <<= 32;
                    }
 
                    if (value._bits.Length >= 3)
                    {
                        upperBits |= value._bits[2];
                    }
 
                    if (value._bits.Length >= 2)
                    {
                        lowerBits = value._bits[1];
                        lowerBits <<= 32;
                    }
 
                    lowerBits |= value._bits[0];
 
                    UInt128 bits = new UInt128(upperBits, lowerBits);
 
                    if (IsNegative(value))
                    {
                        bits = ~bits + 1;
                    }
 
                    actualResult = bits;
                }
                else
                {
                    actualResult = (UInt128)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else if (typeof(TOther) == typeof(nuint))
            {
                nuint actualResult;
 
                if (value._bits is not null)
                {
                    nuint bits = 0;
 
                    if (Environment.Is64BitProcess && (value._bits.Length >= 2))
                    {
                        bits = value._bits[1];
                        bits <<= 32;
                    }
 
                    bits |= value._bits[0];
 
                    if (IsNegative(value))
                    {
                        bits = ~bits + 1;
                    }
 
                    actualResult = bits;
                }
                else
                {
                    actualResult = (nuint)value._sign;
                }
 
                result = (TOther)(object)actualResult;
                return true;
            }
            else
            {
                result = default;
                return false;
            }
        }
 
        //
        // IParsable
        //
 
        /// <inheritdoc cref="IParsable{TSelf}.TryParse(string?, IFormatProvider?, out TSelf)" />
        public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out BigInteger result) => TryParse(s, NumberStyles.Integer, provider, out result);
 
        //
        // IShiftOperators
        //
 
        /// <inheritdoc cref="IShiftOperators{TSelf, TOther, TResult}.op_UnsignedRightShift(TSelf, TOther)" />
        public static BigInteger operator >>>(BigInteger value, int shiftAmount)
        {
            value.AssertValid();
 
            if (shiftAmount == 0)
                return value;
 
            if (shiftAmount == int.MinValue)
                return ((value << int.MaxValue) << 1);
 
            if (shiftAmount < 0)
                return value << -shiftAmount;
 
            (int digitShift, int smallShift) = Math.DivRem(shiftAmount, kcbitUint);
 
            BigInteger result;
 
            uint[]? xdFromPool = null;
            int xl = value._bits?.Length ?? 1;
            Span<uint> xd = (xl <= BigIntegerCalculator.StackAllocThreshold
                          ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                          : xdFromPool = ArrayPool<uint>.Shared.Rent(xl)).Slice(0, xl);
 
            bool negx = value.GetPartsForBitManipulation(xd);
 
            if (negx)
            {
                if (shiftAmount >= ((long)kcbitUint * xd.Length))
                {
                    result = MinusOne;
                    goto exit;
                }
 
                NumericsHelpers.DangerousMakeTwosComplement(xd); // Mutates xd
            }
 
            uint[]? zdFromPool = null;
            int zl = Math.Max(xl - digitShift, 0);
            Span<uint> zd = ((uint)zl <= BigIntegerCalculator.StackAllocThreshold
                          ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
                          : zdFromPool = ArrayPool<uint>.Shared.Rent(zl)).Slice(0, zl);
            zd.Clear();
 
            if (smallShift == 0)
            {
                for (int i = xd.Length - 1; i >= digitShift; i--)
                {
                    zd[i - digitShift] = xd[i];
                }
            }
            else
            {
                int carryShift = kcbitUint - smallShift;
                uint carry = 0;
                for (int i = xd.Length - 1; i >= digitShift; i--)
                {
                    uint rot = xd[i];
                    zd[i - digitShift] = (rot >>> smallShift) | carry;
                    carry = rot << carryShift;
                }
            }
 
            if (negx && (int)zd[^1] < 0)
            {
                NumericsHelpers.DangerousMakeTwosComplement(zd);
            }
            else
            {
                negx = false;
            }
 
            result = new BigInteger(zd, negx);
 
            if (zdFromPool != null)
                ArrayPool<uint>.Shared.Return(zdFromPool);
            exit:
            if (xdFromPool != null)
                ArrayPool<uint>.Shared.Return(xdFromPool);
 
            return result;
        }
 
        //
        // ISignedNumber
        //
 
        /// <inheritdoc cref="ISignedNumber{TSelf}.NegativeOne" />
        static BigInteger ISignedNumber<BigInteger>.NegativeOne => MinusOne;
 
        //
        // ISpanParsable
        //
 
        /// <inheritdoc cref="ISpanParsable{TSelf}.Parse(ReadOnlySpan{char}, IFormatProvider?)" />
        public static BigInteger Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider);
 
        /// <inheritdoc cref="ISpanParsable{TSelf}.TryParse(ReadOnlySpan{char}, IFormatProvider?, out TSelf)" />
        public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out BigInteger result) => TryParse(s, NumberStyles.Integer, provider, out result);
    }
}