Example #1
0
            public bool          sign; // negative sign exists

            public static BigNumberBuffer Create()
            {
                BigNumberBuffer number = default;

                number.digits = new StringBuilder();
                return(number);
            }
Example #2
0
        private static bool NumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value)
        {
            int scale = number.scale;
            int num2  = 0;

            value = 0;
            while (--scale >= 0)
            {
                value *= 10;
                if (number.digits[num2] != '\0')
                {
                    value += number.digits[num2++] - '0';
                }
            }
            while (number.digits[num2] != '\0')
            {
                if (number.digits[num2++] != '0')
                {
                    return(false);
                }
            }
            if (number.sign)
            {
                value = -value;
            }
            return(true);
        }
Example #3
0
        internal static bool TryParseBigInteger(string value, NumberStyles style, NumberFormatInfo info, out BigInteger result)
        {
            result = Zero;
            if (!TryValidateParseStyleInteger(style, out ArgumentException e))
            {
                throw e; // TryParse still throws ArgumentException on invalid NumberStyles
            }
            if (value == null)
            {
                return(false);
            }
            var number = BigNumberBuffer.Create();

            if (!ParseNumber(new StringProcessor(value), style, number, info))
            {
                return(false);
            }
            if ((style & NumberStyles.AllowHexSpecifier) != 0)
            {
                if (!HexNumberToBigInteger(number, ref result))
                {
                    return(false);
                }
            }
            else
            {
                if (!NumberToBigInteger(number, out result))
                {
                    return(false);
                }
            }
            return(true);
        }
Example #4
0
        private static unsafe bool NumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value)
        {
            int i   = number.scale;
            int cur = 0;

            BigInteger ten = 10;

            value = 0;
            while (--i >= 0)
            {
                value *= ten;
                if (number.digits[cur] != '\0')
                {
                    value += number.digits[cur++] - '0';
                }
            }
            while (number.digits[cur] != '\0')
            {
                if (number.digits[cur++] != '0')
                {
                    return(false);                             // Disallow non-zero trailing decimal places
                }
            }
            if (number.sign)
            {
                value = -value;
            }
            return(true);
        }
Example #5
0
        internal static bool TryParseBigInteger(ReadOnlySpan <char> value, NumberStyles style, NumberFormatInfo info, out BigInteger result)
        {
            unsafe
            {
                result = BigInteger.Zero;
                ArgumentException e;
                if (!TryValidateParseStyleInteger(style, out e))
                {
                    throw e; // TryParse still throws ArgumentException on invalid NumberStyles
                }
                BigNumberBuffer bignumber = BigNumberBuffer.Create();
                if (!FormatProvider.TryStringToBigInteger(value, style, info, bignumber.digits, out bignumber.precision, out bignumber.scale, out bignumber.sign))
                {
                    return(false);
                }

                if ((style & NumberStyles.AllowHexSpecifier) != 0)
                {
                    if (!HexNumberToBigInteger(ref bignumber, ref result))
                    {
                        return(false);
                    }
                }
                else
                {
                    if (!NumberToBigInteger(ref bignumber, ref result))
                    {
                        return(false);
                    }
                }
                return(true);
            }
        }
Example #6
0
        internal static unsafe bool TryParseBigInteger(string value, NumberStyles style, NumberFormatInfo info, out BigInteger result)
        {
            ArgumentException exception;

            result = BigInteger.Zero;
            if (!TryValidateParseStyleInteger(style, out exception))
            {
                throw exception;
            }
            BigNumberBuffer number      = BigNumberBuffer.Create();
            byte *          stackBuffer = stackalloc byte[0x72];

            Number.NumberBuffer buffer2 = new Number.NumberBuffer(stackBuffer);
            result = 0;
            if (!Number.TryStringToNumber(value, style, ref buffer2, number.digits, info, false))
            {
                return(false);
            }
            number.precision = buffer2.precision;
            number.scale     = buffer2.scale;
            number.sign      = buffer2.sign;
            if ((style & NumberStyles.AllowHexSpecifier) != NumberStyles.None)
            {
                if (!HexNumberToBigInteger(ref number, ref result))
                {
                    return(false);
                }
            }
            else if (!NumberToBigInteger(ref number, ref result))
            {
                return(false);
            }
            return(true);
        }
Example #7
0
        private unsafe static Boolean NumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value)
        {
            Int32 i   = number.scale;
            Int32 cur = 0;

            value = 0;
            while (--i >= 0)
            {
                value *= 10;
                if (number.digits[cur] != '\0')
                {
                    value += (Int32)(number.digits[cur++] - '0');
                }
            }
            while (number.digits[cur] != '\0')
            {
                if (number.digits[cur++] != '0')
                {
                    return(false);                             // disallow non-zero trailing decimal places
                }
            }
            if (number.sign)
            {
                value = -value;
            }
            return(true);
        }
Example #8
0
        private static unsafe bool HexNumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value)
        {
            if (number.digits == null || number.digits.Length == 0)
            {
                return(false);
            }

            int len = number.digits.Length - 1; // Ignore trailing '\0'

            byte[] bits = new byte[(len / 2) + (len % 2)];

            bool shift      = false;
            bool isNegative = false;
            int  bitIndex   = 0;

            // Parse the string into a little-endian two's complement byte array
            // string value     : O F E B 7 \0
            // string index (i) : 0 1 2 3 4 5 <--
            // byte[] (bitIndex): 2 1 1 0 0 <--
            //
            for (int i = len - 1; i > -1; i--)
            {
                char c = number.digits[i];

                byte b;
                if (c >= '0' && c <= '9')
                {
                    b = (byte)(c - '0');
                }
                else if (c >= 'A' && c <= 'F')
                {
                    b = (byte)((c - 'A') + 10);
                }
                else
                {
                    Debug.Assert(c >= 'a' && c <= 'f');
                    b = (byte)((c - 'a') + 10);
                }
                if (i == 0 && (b & 0x08) == 0x08)
                {
                    isNegative = true;
                }

                if (shift)
                {
                    bits[bitIndex] = (byte)(bits[bitIndex] | (b << 4));
                    bitIndex++;
                }
                else
                {
                    bits[bitIndex] = isNegative ? (byte)(b | 0xF0) : (b);
                }
                shift = !shift;
            }

            value = new BigInteger(bits);
            return(true);
        }
Example #9
0
        private static bool HexNumberToBigInteger(BigNumberBuffer number, ref BigInteger value)
        {
            if (number.Digits == null || number.Digits.Length == 0)
            {
                return(false);
            }

            var len  = number.Digits.Length; // there is no trailing '\0'
            var bits = new byte[(len / 2) + (len % 2)];

            var shift      = false;
            var isNegative = false;
            var bitIndex   = 0;

            // parse the string into a little-endian two's complement byte array
            // string value     : O F E B 7 \0
            // string index (i) : 0 1 2 3 4 5 <--
            // byte[] (bitIndex): 2 1 1 0 0 <--
            //
            for (var i = len - 1; i > -1; i--)
            {
                var c = number.Digits[i];

                byte b;
                if (c >= '0' && c <= '9')
                {
                    b = (byte)(c - '0');
                }
                else if (c >= 'A' && c <= 'F')
                {
                    b = (byte)((c - 'A') + 10);
                }
                else
                {
                    Contract.Assert(c >= 'a' && c <= 'f');
                    b = (byte)((c - 'a') + 10);
                }
                isNegative |= (i == 0 && (b & 0x08) == 0x08);

                if (shift)
                {
                    bits[bitIndex] = (byte)(bits[bitIndex] | (b << 4));
                    bitIndex++;
                }
                else
                {
                    bits[bitIndex] = isNegative ? (byte)(b | 0xF0) : (b);
                }
                shift = !shift;
            }

            value = new BigInteger(bits);
            return(true);
        }
Example #10
0
        private static bool NumberToBigInteger(BigNumberBuffer number, out BigInteger value)
        {
            var cur = 0;

            if (number.Scale > number.Digits.Length)
            {
                var i = number.Digits.Length;
                value = 0;
                while (--i >= 0)
                {
                    value *= 10;
                    value += (number.Digits[cur++] - '0');
                }
                var adjust = number.Scale - number.Digits.Length;
                while (adjust > 9)
                {
                    value  *= 1000000000;
                    adjust -= 9;
                }
                while (adjust > 0)
                {
                    value *= 10;
                    adjust--;
                }
            }
            else
            {
                var i = number.Scale;
                value = 0;
                while (--i >= 0)
                {
                    value *= 10;
                    value += (number.Digits[cur++] - '0');
                }
                for (; cur < number.Digits.Length - 1; cur++)
                {
                    if (number.Digits[cur++] != '0')
                    {
                        return(false);
                    }
                }
            }
            if (number.Negative)
            {
                value = -value;
            }
            return(true);
        }
Example #11
0
        private static bool HexNumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value)
        {
            if ((number.digits == null) || (number.digits.Length == 0))
            {
                return(false);
            }
            int num = number.digits.Length - 1;

            byte[] buffer = new byte[(num / 2) + (num % 2)];
            bool   flag   = false;
            bool   flag2  = false;
            int    index  = 0;

            for (int i = num - 1; i > -1; i--)
            {
                byte num4;
                char ch = number.digits[i];
                if ((ch >= '0') && (ch <= '9'))
                {
                    num4 = (byte)(ch - '0');
                }
                else if ((ch >= 'A') && (ch <= 'F'))
                {
                    num4 = (byte)((ch - 'A') + 10);
                }
                else
                {
                    num4 = (byte)((ch - 'a') + 10);
                }
                if ((i == 0) && ((num4 & 8) == 8))
                {
                    flag2 = true;
                }
                if (flag)
                {
                    buffer[index] = (byte)(buffer[index] | (num4 << 4));
                    index++;
                }
                else
                {
                    buffer[index] = flag2 ? ((byte)(num4 | 240)) : num4;
                }
                flag = !flag;
            }
            value = new BigInteger(buffer);
            return(true);
        }
Example #12
0
        internal unsafe static Boolean TryParseBigInteger(String value, NumberStyles style, NumberFormatInfo info, out BigInteger result)
        {
            result = BigInteger.Zero;
            ArgumentException e;

            if (!TryValidateParseStyleInteger(style, out e))
            {
                throw e; // TryParse still throws ArgumentException on invalid NumberStyles
            }
            BigNumberBuffer bignumber         = BigNumberBuffer.Create();
            Byte *          numberBufferBytes = stackalloc Byte[Number.NumberBuffer.NumberBufferBytes];

            Number.NumberBuffer number = new Number.NumberBuffer(numberBufferBytes);
            result = 0;

            if (!Number.TryStringToNumber(value, style, ref number, bignumber.digits, info, false))
            {
                return(false);
            }
            bignumber.precision = number.precision;
            bignumber.scale     = number.scale;
            bignumber.sign      = number.sign;

            if ((style & NumberStyles.AllowHexSpecifier) != 0)
            {
                if (!HexNumberToBigInteger(ref bignumber, ref result))
                {
                    return(false);
                }
            }
            else
            {
                if (!NumberToBigInteger(ref bignumber, ref result))
                {
                    return(false);
                }
            }
            return(true);
        }
Example #13
0
 private static bool NumberToBigInteger(BigNumberBuffer number, out BigInteger value)
 {
     var cur = 0;
     if (number.Scale > number.Digits.Length)
     {
         var i = number.Digits.Length;
         value = 0;
         while (--i >= 0)
         {
             value *= 10;
             value += (number.Digits[cur++] - '0');
         }
         var adjust = number.Scale - number.Digits.Length;
         while (adjust > 9)
         {
             value *= 1000000000;
             adjust -= 9;
         }
         while (adjust > 0)
         {
             value *= 10;
             adjust--;
         }
     }
     else
     {
         var i = number.Scale;
         value = 0;
         while (--i >= 0)
         {
             value *= 10;
             value += (number.Digits[cur++] - '0');
         }
         for (; cur < number.Digits.Length - 1; cur++)
         {
             if (number.Digits[cur++] != '0')
             {
                 return false;
             }
         }
     }
     if (number.Negative)
     {
         value = -value;
     }
     return true;
 }
Example #14
0
        private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInteger result)
        {
            if (number.digits == null || number.digits.Length == 0)
            {
                result = default;
                return(false);
            }

            const int DigitsPerBlock = 8;

            int totalDigitCount = number.digits.Length - 1;   // Ignore trailing '\0'
            int blockCount, partialDigitCount;

            blockCount = Math.DivRem(totalDigitCount, DigitsPerBlock, out int remainder);
            if (remainder == 0)
            {
                partialDigitCount = 0;
            }
            else
            {
                blockCount       += 1;
                partialDigitCount = DigitsPerBlock - remainder;
            }

            bool isNegative   = HexConverter.FromChar(number.digits[0]) >= 8;
            uint partialValue = (isNegative && partialDigitCount > 0) ? 0xFFFFFFFFu : 0;

            int[]? arrayFromPool = null;

            Span <uint> bitsBuffer = (blockCount <= BigInteger.StackallocUInt32Limit)
                ? stackalloc uint[blockCount]
                : MemoryMarshal.Cast <int, uint>((arrayFromPool = ArrayPool <int> .Shared.Rent(blockCount)).AsSpan(0, blockCount));

            int bitsBufferPos = blockCount - 1;

            try
            {
                foreach (ReadOnlyMemory <char> digitsChunkMem in number.digits.GetChunks())
                {
                    ReadOnlySpan <char> chunkDigits = digitsChunkMem.Span;
                    for (int i = 0; i < chunkDigits.Length; i++)
                    {
                        char digitChar = chunkDigits[i];
                        if (digitChar == '\0')
                        {
                            break;
                        }

                        int hexValue = HexConverter.FromChar(digitChar);
                        Debug.Assert(hexValue != 0xFF);

                        partialValue = (partialValue << 4) | (uint)hexValue;
                        partialDigitCount++;

                        if (partialDigitCount == DigitsPerBlock)
                        {
                            bitsBuffer[bitsBufferPos] = partialValue;
                            bitsBufferPos--;
                            partialValue      = 0;
                            partialDigitCount = 0;
                        }
                    }
                }

                Debug.Assert(partialDigitCount == 0 && bitsBufferPos == -1);

                // BigInteger requires leading zero blocks to be truncated.
                bitsBuffer = bitsBuffer.TrimEnd(0u);

                int sign;
                uint[]? bits;

                if (bitsBuffer.IsEmpty)
                {
                    sign = 0;
                    bits = null;
                }
                else if (bitsBuffer.Length == 1)
                {
                    sign = (int)bitsBuffer[0];
                    bits = null;

                    if ((!isNegative && sign < 0) || sign == int.MinValue)
                    {
                        sign = isNegative ? -1 : 1;
                        bits = new[] { (uint)sign };
                    }
                }
                else
                {
                    sign = isNegative ? -1 : 1;
                    bits = bitsBuffer.ToArray();

                    if (isNegative)
                    {
                        NumericsHelpers.DangerousMakeTwosComplement(bits);
                    }
                }

                result = new BigInteger(sign, bits);
                return(true);
            }
            finally
            {
                if (arrayFromPool != null)
                {
                    ArrayPool <int> .Shared.Return(arrayFromPool);
                }
            }
        }
 private static bool NumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value)
 {
     int scale = number.scale;
     int num2 = 0;
     value = 0;
     while (--scale >= 0)
     {
         value *= 10;
         if (number.digits[num2] != '\0')
         {
             value += number.digits[num2++] - '0';
         }
     }
     while (number.digits[num2] != '\0')
     {
         if (number.digits[num2++] != '0')
         {
             return false;
         }
     }
     if (number.sign)
     {
         value = -value;
     }
     return true;
 }
 private static bool HexNumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value)
 {
     if ((number.digits == null) || (number.digits.Length == 0))
     {
         return false;
     }
     int num = number.digits.Length - 1;
     byte[] buffer = new byte[(num / 2) + (num % 2)];
     bool flag = false;
     bool flag2 = false;
     int index = 0;
     for (int i = num - 1; i > -1; i--)
     {
         byte num4;
         char ch = number.digits[i];
         if ((ch >= '0') && (ch <= '9'))
         {
             num4 = (byte) (ch - '0');
         }
         else if ((ch >= 'A') && (ch <= 'F'))
         {
             num4 = (byte) ((ch - 'A') + 10);
         }
         else
         {
             num4 = (byte) ((ch - 'a') + 10);
         }
         if ((i == 0) && ((num4 & 8) == 8))
         {
             flag2 = true;
         }
         if (flag)
         {
             buffer[index] = (byte) (buffer[index] | (num4 << 4));
             index++;
         }
         else
         {
             buffer[index] = flag2 ? ((byte) (num4 | 240)) : num4;
         }
         flag = !flag;
     }
     value = new BigInteger(buffer);
     return true;
 }
Example #17
0
        private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigInteger result)
        {
            int currentBufferSize = 0;

            int totalDigitCount = 0;
            int numberScale     = number.scale;

            const int  MaxPartialDigits = 9;
            const uint TenPowMaxPartial = 1000000000;

            int[]? arrayFromPoolForResultBuffer = null;
            if (numberScale < 0)
            {
                result = default;
                return(false);
            }

            try
            {
                if (number.digits.Length <= s_naiveThreshold)
                {
                    return(Naive(ref number, out result));
                }
                else
                {
                    return(DivideAndConquer(ref number, out result));
                }
            }
            finally
            {
                if (arrayFromPoolForResultBuffer != null)
                {
                    ArrayPool <int> .Shared.Return(arrayFromPoolForResultBuffer);
                }
            }

            bool Naive(ref BigNumberBuffer number, out BigInteger result)
            {
                Span <uint> stackBuffer       = stackalloc uint[BigIntegerCalculator.StackAllocThreshold];
                Span <uint> currentBuffer     = stackBuffer;
                uint        partialValue      = 0;
                int         partialDigitCount = 0;

                foreach (ReadOnlyMemory <char> digitsChunk in number.digits.GetChunks())
                {
                    if (!ProcessChunk(digitsChunk.Span, ref currentBuffer))
                    {
                        result = default;
                        return(false);
                    }
                }

                if (partialDigitCount > 0)
                {
                    MultiplyAdd(ref currentBuffer, s_uint32PowersOfTen[partialDigitCount], partialValue);
                }

                result = NumberBufferToBigInteger(currentBuffer, number.sign);
                return(true);

                bool ProcessChunk(ReadOnlySpan <char> chunkDigits, ref Span <uint> currentBuffer)
                {
                    int remainingIntDigitCount        = Math.Max(numberScale - totalDigitCount, 0);
                    ReadOnlySpan <char> intDigitsSpan = chunkDigits.Slice(0, Math.Min(remainingIntDigitCount, chunkDigits.Length));

                    bool endReached = false;

                    // Storing these captured variables in locals for faster access in the loop.
                    uint _partialValue      = partialValue;
                    int  _partialDigitCount = partialDigitCount;
                    int  _totalDigitCount   = totalDigitCount;

                    for (int i = 0; i < intDigitsSpan.Length; i++)
                    {
                        char digitChar = chunkDigits[i];
                        if (digitChar == '\0')
                        {
                            endReached = true;
                            break;
                        }

                        _partialValue = _partialValue * 10 + (uint)(digitChar - '0');
                        _partialDigitCount++;
                        _totalDigitCount++;

                        // Update the buffer when enough partial digits have been accumulated.
                        if (_partialDigitCount == MaxPartialDigits)
                        {
                            MultiplyAdd(ref currentBuffer, TenPowMaxPartial, _partialValue);
                            _partialValue      = 0;
                            _partialDigitCount = 0;
                        }
                    }

                    // Check for nonzero digits after the decimal point.
                    if (!endReached)
                    {
                        ReadOnlySpan <char> fracDigitsSpan = chunkDigits.Slice(intDigitsSpan.Length);
                        for (int i = 0; i < fracDigitsSpan.Length; i++)
                        {
                            char digitChar = fracDigitsSpan[i];
                            if (digitChar == '\0')
                            {
                                break;
                            }
                            if (digitChar != '0')
                            {
                                return(false);
                            }
                        }
                    }

                    partialValue      = _partialValue;
                    partialDigitCount = _partialDigitCount;
                    totalDigitCount   = _totalDigitCount;

                    return(true);
                }
            }

            bool DivideAndConquer(ref BigNumberBuffer number, out BigInteger result)
            {
                Span <uint> currentBuffer;

                int[]? arrayFromPoolForMultiplier = null;
                try
                {
                    totalDigitCount = Math.Min(number.digits.Length - 1, numberScale);
                    int bufferSize = (totalDigitCount + MaxPartialDigits - 1) / MaxPartialDigits;

                    Span <uint> buffer = new uint[bufferSize];
                    arrayFromPoolForResultBuffer = ArrayPool <int> .Shared.Rent(bufferSize);

                    Span <uint> newBuffer = MemoryMarshal.Cast <int, uint>(arrayFromPoolForResultBuffer).Slice(0, bufferSize);
                    newBuffer.Clear();

                    // Separate every MaxPartialDigits digits and store them in the buffer.
                    // Buffers are treated as little-endian. That means, the array { 234567890, 1 }
                    // represents the number 1234567890.
                    int  bufferIndex            = bufferSize - 1;
                    uint currentBlock           = 0;
                    int  shiftUntil             = (totalDigitCount - 1) % MaxPartialDigits;
                    int  remainingIntDigitCount = totalDigitCount;
                    foreach (ReadOnlyMemory <char> digitsChunk in number.digits.GetChunks())
                    {
                        ReadOnlySpan <char> digitsChunkSpan = digitsChunk.Span;
                        ReadOnlySpan <char> intDigitsSpan   = digitsChunkSpan.Slice(0, Math.Min(remainingIntDigitCount, digitsChunkSpan.Length));

                        for (int i = 0; i < intDigitsSpan.Length; i++)
                        {
                            char digitChar = intDigitsSpan[i];
                            Debug.Assert(char.IsDigit(digitChar));
                            currentBlock *= 10;
                            currentBlock += unchecked ((uint)(digitChar - '0'));
                            if (shiftUntil == 0)
                            {
                                buffer[bufferIndex] = currentBlock;
                                currentBlock        = 0;
                                bufferIndex--;
                                shiftUntil = MaxPartialDigits;
                            }
                            shiftUntil--;
                        }
                        remainingIntDigitCount -= intDigitsSpan.Length;
                        Debug.Assert(0 <= remainingIntDigitCount);

                        ReadOnlySpan <char> fracDigitsSpan = digitsChunkSpan.Slice(intDigitsSpan.Length);
                        for (int i = 0; i < fracDigitsSpan.Length; i++)
                        {
                            char digitChar = fracDigitsSpan[i];
                            if (digitChar == '\0')
                            {
                                break;
                            }
                            if (digitChar != '0')
                            {
                                result = default;
                                return(false);
                            }
                        }
                    }
                    Debug.Assert(currentBlock == 0);
                    Debug.Assert(bufferIndex == -1);

                    int blockSize = 1;
                    arrayFromPoolForMultiplier = ArrayPool <int> .Shared.Rent(blockSize);

                    Span <uint> multiplier = MemoryMarshal.Cast <int, uint>(arrayFromPoolForMultiplier).Slice(0, blockSize);
                    multiplier[0] = TenPowMaxPartial;

                    // This loop is executed ceil(log_2(bufferSize)) times.
                    while (true)
                    {
                        // merge each block pairs.
                        // When buffer represents:
                        // |     A     |     B     |     C     |     D     |
                        // Make newBuffer like:
                        // |  A + B * multiplier   |  C + D * multiplier   |
                        for (int i = 0; i < bufferSize; i += blockSize * 2)
                        {
                            Span <uint> curBuffer    = buffer.Slice(i);
                            Span <uint> curNewBuffer = newBuffer.Slice(i);

                            int len      = Math.Min(bufferSize - i, blockSize * 2);
                            int lowerLen = Math.Min(len, blockSize);
                            int upperLen = len - lowerLen;
                            if (upperLen != 0)
                            {
                                Debug.Assert(blockSize == lowerLen);
                                Debug.Assert(blockSize == multiplier.Length);
                                Debug.Assert(multiplier.Length == lowerLen);
                                BigIntegerCalculator.Multiply(multiplier, curBuffer.Slice(blockSize, upperLen), curNewBuffer.Slice(0, len));
                            }

                            long carry = 0;
                            int  j     = 0;
                            for (; j < lowerLen; j++)
                            {
                                long digit = (curBuffer[j] + carry) + curNewBuffer[j];
                                curNewBuffer[j] = unchecked ((uint)digit);
                                carry           = digit >> 32;
                            }
                            if (carry != 0)
                            {
                                while (true)
                                {
                                    curNewBuffer[j]++;
                                    if (curNewBuffer[j] != 0)
                                    {
                                        break;
                                    }
                                    j++;
                                }
                            }
                        }

                        Span <uint> tmp = buffer;
                        buffer     = newBuffer;
                        newBuffer  = tmp;
                        blockSize *= 2;

                        if (bufferSize <= blockSize)
                        {
                            break;
                        }
                        newBuffer.Clear();
                        int[]? arrayToReturn = arrayFromPoolForMultiplier;

                        arrayFromPoolForMultiplier = ArrayPool <int> .Shared.Rent(blockSize);

                        Span <uint> newMultiplier = MemoryMarshal.Cast <int, uint>(arrayFromPoolForMultiplier).Slice(0, blockSize);
                        newMultiplier.Clear();
                        BigIntegerCalculator.Square(multiplier, newMultiplier);
                        multiplier = newMultiplier;
                        if (arrayToReturn is not null)
                        {
                            ArrayPool <int> .Shared.Return(arrayToReturn);
                        }
                    }

                    // shrink buffer to the currently used portion.
                    // First, calculate the rough size of the buffer from the ratio that the number
                    // of digits follows. Then, shrink the size until there is no more space left.
                    // The Ratio is calculated as: log_{2^32}(10^9)
                    const double digitRatio = 0.934292276687070661;
                    currentBufferSize = Math.Min((int)(bufferSize * digitRatio) + 1, bufferSize);
                    Debug.Assert(buffer.Length == currentBufferSize || buffer[currentBufferSize] == 0);
                    while (0 < currentBufferSize && buffer[currentBufferSize - 1] == 0)
                    {
                        currentBufferSize--;
                    }
                    currentBuffer = buffer.Slice(0, currentBufferSize);
                    result        = NumberBufferToBigInteger(currentBuffer, number.sign);
                }
                finally
                {
                    if (arrayFromPoolForMultiplier != null)
                    {
                        ArrayPool <int> .Shared.Return(arrayFromPoolForMultiplier);
                    }
                }
                return(true);
            }

            BigInteger NumberBufferToBigInteger(Span <uint> currentBuffer, bool signa)
            {
                int trailingZeroCount = numberScale - totalDigitCount;

                while (trailingZeroCount >= MaxPartialDigits)
                {
                    MultiplyAdd(ref currentBuffer, TenPowMaxPartial, 0);
                    trailingZeroCount -= MaxPartialDigits;
                }

                if (trailingZeroCount > 0)
                {
                    MultiplyAdd(ref currentBuffer, s_uint32PowersOfTen[trailingZeroCount], 0);
                }

                int sign;

                uint[]? bits;

                if (currentBufferSize == 0)
                {
                    sign = 0;
                    bits = null;
                }
                else if (currentBufferSize == 1 && currentBuffer[0] <= int.MaxValue)
                {
                    sign = (int)(signa ? -currentBuffer[0] : currentBuffer[0]);
                    bits = null;
                }
                else
                {
                    sign = signa ? -1 : 1;
                    bits = currentBuffer.Slice(0, currentBufferSize).ToArray();
                }

                return(new BigInteger(sign, bits));
            }

            // This function should only be used for result buffer.
            void MultiplyAdd(ref Span <uint> currentBuffer, uint multiplier, uint addValue)
            {
                Span <uint> curBits = currentBuffer.Slice(0, currentBufferSize);
                uint        carry   = addValue;

                for (int i = 0; i < curBits.Length; i++)
                {
                    ulong p = (ulong)multiplier * curBits[i] + carry;
                    curBits[i] = (uint)p;
                    carry      = (uint)(p >> 32);
                }

                if (carry == 0)
                {
                    return;
                }

                if (currentBufferSize == currentBuffer.Length)
                {
                    int[]? arrayToReturn = arrayFromPoolForResultBuffer;

                    arrayFromPoolForResultBuffer = ArrayPool <int> .Shared.Rent(checked (currentBufferSize * 2));

                    Span <uint> newBuffer = MemoryMarshal.Cast <int, uint>(arrayFromPoolForResultBuffer);
                    currentBuffer.CopyTo(newBuffer);
                    currentBuffer = newBuffer;

                    if (arrayToReturn != null)
                    {
                        ArrayPool <int> .Shared.Return(arrayToReturn);
                    }
                }

                currentBuffer[currentBufferSize] = carry;
                currentBufferSize++;
            }
        }
Example #18
0
        private unsafe static bool NumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value)
        {
            int i = number.scale;
            int cur = 0;

            BigInteger ten = 10;
            value = 0;
            while (--i >= 0)
            {
                value *= ten;
                if (number.digits[cur] != '\0')
                {
                    value += number.digits[cur++] - '0';
                }
            }
            while (number.digits[cur] != '\0')
            {
                if (number.digits[cur++] != '0') return false; // Disallow non-zero trailing decimal places
            }
            if (number.sign)
            {
                value = -value;
            }
            return true;
        }
Example #19
0
        private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigInteger result)
        {
            Span <uint> stackBuffer       = stackalloc uint[BigInteger.StackallocUInt32Limit];
            Span <uint> currentBuffer     = stackBuffer;
            int         currentBufferSize = 0;

            int[]? arrayFromPool = null;

            uint partialValue      = 0;
            int  partialDigitCount = 0;
            int  totalDigitCount   = 0;
            int  numberScale       = number.scale;

            const int  MaxPartialDigits = 9;
            const uint TenPowMaxPartial = 1000000000;

            try
            {
                foreach (ReadOnlyMemory <char> digitsChunk in number.digits.GetChunks())
                {
                    if (!ProcessChunk(digitsChunk.Span, ref currentBuffer))
                    {
                        result = default;
                        return(false);
                    }
                }

                if (partialDigitCount > 0)
                {
                    MultiplyAdd(ref currentBuffer, s_uint32PowersOfTen[partialDigitCount], partialValue);
                }

                int trailingZeroCount = numberScale - totalDigitCount;

                while (trailingZeroCount >= MaxPartialDigits)
                {
                    MultiplyAdd(ref currentBuffer, TenPowMaxPartial, 0);
                    trailingZeroCount -= MaxPartialDigits;
                }

                if (trailingZeroCount > 0)
                {
                    MultiplyAdd(ref currentBuffer, s_uint32PowersOfTen[trailingZeroCount], 0);
                }

                int sign;
                uint[]? bits;

                if (currentBufferSize == 0)
                {
                    sign = 0;
                    bits = null;
                }
                else if (currentBufferSize == 1 && currentBuffer[0] <= int.MaxValue)
                {
                    sign = (int)(number.sign ? -currentBuffer[0] : currentBuffer[0]);
                    bits = null;
                }
                else
                {
                    sign = number.sign ? -1 : 1;
                    bits = currentBuffer.Slice(0, currentBufferSize).ToArray();
                }

                result = new BigInteger(sign, bits);
                return(true);
            }
            finally
            {
                if (arrayFromPool != null)
                {
                    ArrayPool <int> .Shared.Return(arrayFromPool);
                }
            }

            bool ProcessChunk(ReadOnlySpan <char> chunkDigits, ref Span <uint> currentBuffer)
            {
                int remainingIntDigitCount        = Math.Max(numberScale - totalDigitCount, 0);
                ReadOnlySpan <char> intDigitsSpan = chunkDigits.Slice(0, Math.Min(remainingIntDigitCount, chunkDigits.Length));

                bool endReached = false;

                // Storing these captured variables in locals for faster access in the loop.
                uint _partialValue      = partialValue;
                int  _partialDigitCount = partialDigitCount;
                int  _totalDigitCount   = totalDigitCount;

                for (int i = 0; i < intDigitsSpan.Length; i++)
                {
                    char digitChar = chunkDigits[i];
                    if (digitChar == '\0')
                    {
                        endReached = true;
                        break;
                    }

                    _partialValue = _partialValue * 10 + (uint)(digitChar - '0');
                    _partialDigitCount++;
                    _totalDigitCount++;

                    // Update the buffer when enough partial digits have been accumulated.
                    if (_partialDigitCount == MaxPartialDigits)
                    {
                        MultiplyAdd(ref currentBuffer, TenPowMaxPartial, _partialValue);
                        _partialValue      = 0;
                        _partialDigitCount = 0;
                    }
                }

                // Check for nonzero digits after the decimal point.
                if (!endReached)
                {
                    ReadOnlySpan <char> fracDigitsSpan = chunkDigits.Slice(intDigitsSpan.Length);
                    for (int i = 0; i < fracDigitsSpan.Length; i++)
                    {
                        char digitChar = fracDigitsSpan[i];
                        if (digitChar == '\0')
                        {
                            break;
                        }
                        if (digitChar != '0')
                        {
                            return(false);
                        }
                    }
                }

                partialValue      = _partialValue;
                partialDigitCount = _partialDigitCount;
                totalDigitCount   = _totalDigitCount;

                return(true);
            }

            void MultiplyAdd(ref Span <uint> currentBuffer, uint multiplier, uint addValue)
            {
                Span <uint> curBits = currentBuffer.Slice(0, currentBufferSize);
                uint        carry   = addValue;

                for (int i = 0; i < curBits.Length; i++)
                {
                    ulong p = (ulong)multiplier * curBits[i] + carry;
                    curBits[i] = (uint)p;
                    carry      = (uint)(p >> 32);
                }

                if (carry == 0)
                {
                    return;
                }

                if (currentBufferSize == currentBuffer.Length)
                {
                    int[]? arrayToReturn = arrayFromPool;

                    arrayFromPool = ArrayPool <int> .Shared.Rent(checked (currentBufferSize * 2));

                    Span <uint> newBuffer = MemoryMarshal.Cast <int, uint>(arrayFromPool);
                    currentBuffer.CopyTo(newBuffer);
                    currentBuffer = newBuffer;

                    if (arrayToReturn != null)
                    {
                        ArrayPool <int> .Shared.Return(arrayToReturn);
                    }
                }

                currentBuffer[currentBufferSize] = carry;
                currentBufferSize++;
            }
        }
Example #20
0
            public bool sign;  // negative sign exists

            public static BigNumberBuffer Create() {
                BigNumberBuffer number = new BigNumberBuffer();
                number.digits = new StringBuilder();
                return number;
            }
Example #21
0
        private unsafe static Boolean HexNumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) {
            if (number.digits == null || number.digits.Length == 0)
                return false;

            int len = number.digits.Length - 1; // ignore trailing '\0'
            byte[] bits = new byte[(len / 2) + (len % 2)];

            bool shift = false;
            bool isNegative = false;
            int bitIndex = 0;

            // parse the string into a little-endian two's complement byte array
            // string value     : O F E B 7 \0
            // string index (i) : 0 1 2 3 4 5 <--
            // byte[] (bitIndex): 2 1 1 0 0 <--
            //
            for (int i = len-1; i > -1; i--) {
                char c = number.digits[i];

                byte b;
                if (c >= '0' && c <= '9') {
                    b = (byte)(c - '0');
                }
                else if (c >= 'A' && c <= 'F') {
                    b = (byte)((c - 'A') + 10);
                }
                else {
                    Contract.Assert(c >= 'a' && c <= 'f');
                    b = (byte)((c - 'a') + 10);
                }
                if (i == 0 && (b & 0x08) == 0x08)
                    isNegative = true;

                if (shift) {
                    bits[bitIndex] = (byte)(bits[bitIndex] | (b << 4));
                    bitIndex++;                    
                }
                else {
                    bits[bitIndex] = isNegative ? (byte)(b | 0xF0) : (b);
                }
                shift = !shift;
            }

            value = new BigInteger(bits);
            return true;
        }
Example #22
0
        private unsafe static Boolean NumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) {
            Int32 i = number.scale;
            Int32 cur = 0;

            value = 0;
            while (--i >= 0) {
                value *= 10;
                if (number.digits[cur] != '\0') {
                    value += (Int32)(number.digits[cur++] - '0');
                }
            }
            while (number.digits[cur] != '\0') {
                if (number.digits[cur++] != '0') return false; // disallow non-zero trailing decimal places
            }
            if (number.sign) {
                value = -value;
            }
            return true;
        }
Example #23
0
        internal static bool ParseNumber(StringProcessor reader, NumberStyles options, BigNumberBuffer number, NumberFormatInfo info)
        {
            if ((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None)
            {
                var allowLeadingWhite  = (options & NumberStyles.AllowLeadingWhite) != NumberStyles.None;
                var allowTrailingWhite = (options & NumberStyles.AllowTrailingWhite) != NumberStyles.None;

                /*
                 * // Assume validated
                 * if (
                 *  (options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None
                 || (options & NumberStyles.AllowLeadingSign) != NumberStyles.None
                 || (options & NumberStyles.AllowParentheses) != NumberStyles.None
                 || (options & NumberStyles.AllowThousands) != NumberStyles.None
                 || (options & NumberStyles.AllowExponent) != NumberStyles.None
                 || (options & NumberStyles.AllowTrailingSign) != NumberStyles.None
                 || )
                 ||{
                 || return false;
                 ||}*/
                number.Negative = false;
                if (allowLeadingWhite)
                {
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }

                while (true)
                {
                    var input =
                        reader.ReadWhile
                        (
                            new[]
                    {
                        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C',
                        'D', 'E', 'F'
                    }
                        );
                    if (input.Length == 0)
                    {
                        break;
                    }

                    number.Scale += input.Length;
                    number.Digits.Append(input.ToUpperInvariant());
                }

                if (allowTrailingWhite)
                {
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }

                return(reader.EndOfString);
            }
            else
            {
                var allowCurrencySymbol = (options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None;
                var allowLeadingWhite   = (options & NumberStyles.AllowLeadingWhite) != NumberStyles.None;
                var allowLeadingSign    = (options & NumberStyles.AllowLeadingSign) != NumberStyles.None;
                var allowParentheses    = (options & NumberStyles.AllowParentheses) != NumberStyles.None;
                var allowThousands      = (options & NumberStyles.AllowThousands) != NumberStyles.None;
                var allowExponent       = (options & NumberStyles.AllowExponent) != NumberStyles.None;
                var allowTrailingWhite  = (options & NumberStyles.AllowTrailingWhite) != NumberStyles.None;
                var allowTrailingSign   = (options & NumberStyles.AllowTrailingSign) != NumberStyles.None;
                var allowDecimalPoint   = (options & NumberStyles.AllowDecimalPoint) != NumberStyles.None;

                var isCurrency = false;
                number.Negative = false;
                var waitingParentheses = false;
                var positive           = false;
                // [ws][$][sign][digits,]digits[E[sign]exponential_digits][ws]
                if (allowLeadingWhite)
                {
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }
                // Percent intentionally not supported
                // After testing with .NET the patterns are ignored... all patterns are welcome

                var currencySymbol = info.CurrencySymbol;
                // [$][sign][digits,]digits[E[sign]exponential_digits][ws]
                if (allowCurrencySymbol && reader.Read(currencySymbol))
                {
                    isCurrency = true;
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }

                var positiveSign = info.PositiveSign;
                var negativeSign = info.NegativeSign;
                // [sign][digits,]digits[E[sign]exponential_digits][ws
                if (allowLeadingSign)
                {
                    number.Negative |= reader.Read(negativeSign);
                    if (!number.Negative)
                    {
                        positive |= reader.Read(positiveSign);
                    }
                }

                if (!number.Negative && allowParentheses && reader.Read('('))
                {
                    // Testing on .NET show that $(n) is allowed, even tho there is no CurrencyNegativePattern for it
                    number.Negative    = true;
                    waitingParentheses = true;
                }

                // ---
                if (!isCurrency && allowCurrencySymbol && reader.Read(currencySymbol)) // If the currency symbol is after the negative sign
                {
                    isCurrency = true;
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }

                // [digits,]digits[E[sign]exponential_digits][ws]
                var failure      = true;
                var digits       = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
                var decimalFound = false;
                while (true)
                {
                    var input = reader.ReadWhile(digits);
                    if (input.Length == 0)
                    {
                        if (allowDecimalPoint && !decimalFound)
                        {
                            if (reader.Read(info.CurrencyDecimalSeparator))
                            {
                                decimalFound = true;
                                continue;
                            }

                            if (reader.Read(info.NumberDecimalSeparator))
                            {
                                if (isCurrency)
                                {
                                    return(false);
                                }

                                decimalFound = true;
                                continue;
                            }
                        }

                        break;
                    }

                    failure = false;
                    if (!decimalFound)
                    {
                        number.Scale += input.Length;
                    }

                    number.Digits.Append(input);
                    if (!allowThousands)
                    {
                        continue;
                    }

                    var currencyGroupSeparator = info.CurrencyGroupSeparator.ToCharArray();
                    // Testing on .NET show that combining currency and number group separators is allowed
                    // But not if the currency symbol has already appeared
                    reader.SkipWhile(currencyGroupSeparator);
                    if (isCurrency)
                    {
                        continue;
                    }

                    var numberGroupSeparator = info.NumberGroupSeparator.ToCharArray();
                    reader.SkipWhile(numberGroupSeparator);
                }

                if (failure)
                {
                    return(false);
                }

                // [E[sign]exponential_digits][ws]
                if (allowExponent && (reader.Read('E') || reader.Read('e')))
                {
                    // [sign]exponential_digits
                    // Testing on .NET show that no pattern is used here, also no parentheses nor group separators supported
                    // The exponent can be big - but anything beyond 9999 is ignored
                    var exponentNegative = reader.Read(negativeSign);
                    if (!exponentNegative)
                    {
                        reader.Read(positiveSign);
                    }

                    var input             = reader.ReadWhile(digits);
                    var exponentMagnitude = int.Parse(input, CultureInfo.InvariantCulture);
                    number.Scale += (exponentNegative ? -1 : 1) * (input.Length > 4 ? 9999 : exponentMagnitude);
                    if (number.Scale < 0)
                    {
                        return(false);
                    }
                }

                // ---
                if (allowTrailingWhite)
                {
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }

                if (!isCurrency && allowCurrencySymbol && reader.Read(currencySymbol))
                {
                    isCurrency = true;
                }

                // ---
                if (!number.Negative && !positive && allowTrailingSign)
                {
                    number.Negative |= reader.Read(negativeSign);
                    if (!number.Negative)
                    {
                        reader.Read(positiveSign);
                    }
                }

                if (waitingParentheses && !reader.Read(')'))
                {
                    return(false);
                }

                // ---
                if (!isCurrency && allowCurrencySymbol && reader.Read(currencySymbol)) // If the currency symbol is after the negative sign
                {
                    /*isCurrency = true; // For completeness sake*/
                }

                // [ws]
                if (allowTrailingWhite)
                {
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }

                return(reader.EndOfString);
            }
        }
Example #24
0
        internal static bool ParseNumber(StringProcessor reader, NumberStyles options, BigNumberBuffer number, NumberFormatInfo info)
        {
            // Percent intentionally not supported
            // After testig with .NET the patterns are ignored... all patterns are welcome

            var currencySymbol = info.CurrencySymbol;
            var numberGroupSeparator = info.NumberGroupSeparator;
            var currencyGroupSeparator = info.CurrencyGroupSeparator;
            var positiveSign = info.PositiveSign;
            var negativeSign = info.NegativeSign;
            if ((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None)
            {
                var allowLeadingWhite = (options & NumberStyles.AllowLeadingWhite) != NumberStyles.None;
                var allowTrailingWhite = (options & NumberStyles.AllowTrailingWhite) != NumberStyles.None;
                /*
                // Assume validated
                if (
                    (options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None
                    || (options & NumberStyles.AllowLeadingSign) != NumberStyles.None
                    || (options & NumberStyles.AllowParentheses) != NumberStyles.None
                    || (options & NumberStyles.AllowThousands) != NumberStyles.None
                    || (options & NumberStyles.AllowExponent) != NumberStyles.None
                    || (options & NumberStyles.AllowTrailingSign) != NumberStyles.None
                    )
                {
                    return false;
                }*/
                number.Negative = false;
                if (allowLeadingWhite)
                {
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }
                while (true)
                {
                    var input =
                        reader.ReadWhile(new[]
                        {
                            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C',
                            'D', 'E', 'F'
                        });
                    if (input == string.Empty)
                    {
                        break;
                    }
                    number.Scale += input.Length;
                    number.Digits.Append(input.ToUpperInvariant());
                }
                if (allowTrailingWhite)
                {
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }
                return reader.EndOfString;
            }
            else
            {
                var allowCurrencySymbol = (options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None;
                var allowLeadingWhite = (options & NumberStyles.AllowLeadingWhite) != NumberStyles.None;
                var allowLeadingSign = (options & NumberStyles.AllowLeadingSign) != NumberStyles.None;
                var allowParentheses = (options & NumberStyles.AllowParentheses) != NumberStyles.None;
                var allowThousands = (options & NumberStyles.AllowThousands) != NumberStyles.None;
                var allowExponent = (options & NumberStyles.AllowExponent) != NumberStyles.None;
                var allowTrailingWhite = (options & NumberStyles.AllowTrailingWhite) != NumberStyles.None;
                var allowTrailingSign = (options & NumberStyles.AllowTrailingSign) != NumberStyles.None;
                var allowDecimalPoint = (options & NumberStyles.AllowDecimalPoint) != NumberStyles.None;

                var isCurrency = false;
                number.Negative = false;
                var waitingParentheses = false;
                var positive = false;
                // [ws][$][sign][digits,]digits[E[sign]exponential_digits][ws]
                if (allowLeadingWhite)
                {
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }
                // [$][sign][digits,]digits[E[sign]exponential_digits][ws]
                if (allowCurrencySymbol && reader.Read(currencySymbol))
                {
                    isCurrency = true;
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }
                // [sign][digits,]digits[E[sign]exponential_digits][ws
                if (allowLeadingSign)
                {
                    if (reader.Read(negativeSign))
                    {
                        number.Negative = true;
                    }
                    if (reader.Read(positiveSign))
                    {
                        positive = true;
                    }
                }
                if (!number.Negative && allowParentheses && reader.Read('('))
                {
                    // Testing on .NET show that $(n) is allowed, even tho there is no CurrencyNegativePattern for it
                    number.Negative = true;
                    waitingParentheses = true;
                }
                // ---
                if (!isCurrency && allowCurrencySymbol && reader.Read(currencySymbol)) // If the currency symbol is after the negative sign
                {
                    isCurrency = true;
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }
                // [digits,]digits[E[sign]exponential_digits][ws]
                var failure = true;
                var digits = new[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
                var decimalFound = false;
                while (true)
                {
                    var input = reader.ReadWhile(digits);
                    if (input == string.Empty)
                    {
                        if (allowDecimalPoint && !decimalFound)
                        {
                            if (reader.Read(info.CurrencyDecimalSeparator))
                            {
                                decimalFound = true;
                                continue;
                            }
                            if (reader.Read(info.NumberDecimalSeparator))
                            {
                                if (isCurrency)
                                {
                                    return false;
                                }
                                decimalFound = true;
                                continue;
                            }
                        }
                        break;
                    }
                    failure = false;
                    if (!decimalFound)
                    {
                        number.Scale += input.Length;
                    }
                    number.Digits.Append(input);
                    if (allowThousands)
                    {
                        // Testing on .NET show that combining currency and number group separators is allowed
                        // But not if the currency symbol has already appeared
                        reader.SkipWhile(currencyGroupSeparator);
                        if (!isCurrency)
                        {
                            reader.SkipWhile(numberGroupSeparator);
                        }
                    }
                }
                if (failure)
                {
                    return false;
                }
                // [E[sign]exponential_digits][ws]
                if (allowExponent && (reader.Read('E') || reader.Read('e')))
                {
                    // [sign]exponential_digits
                    // Testing on .NET show that no pattern is used here, also no parentheses nor group separators supported
                    // The exponent can be big - but anything beyond 9999 is ignored
                    var exponentNegative = reader.Read(negativeSign);
                    if (!exponentNegative)
                    {
                        reader.Read(positiveSign);
                    }
                    var input = reader.ReadWhile(digits);
                    var exponentMagnitude = int.Parse(input, CultureInfo.InvariantCulture);
                    number.Scale += (exponentNegative ? -1 : 1) * (input.Length > 4 ? 9999 : exponentMagnitude);
                    if (number.Scale < 0)
                    {
                        return false;
                    }
                }
                // ---
                if (allowTrailingWhite)
                {
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }
                if (!isCurrency && allowCurrencySymbol && reader.Read(currencySymbol))
                {
                    isCurrency = true;
                }
                // ---
                if (!number.Negative && !positive && allowTrailingSign)
                {
                    if (reader.Read(negativeSign))
                    {
                        number.Negative = true;
                    }
                    if (reader.Read(positiveSign))
                    {
                        positive = true; // For completness sake
                    }
                }
                if (waitingParentheses && !reader.Read(')'))
                {
                    return false;
                }
                // ---
                if (!isCurrency && allowCurrencySymbol && reader.Read(currencySymbol)) // If the currency symbol is after the negative sign
                {
                    isCurrency = true; // For completeness sake
                }
                // [ws]
                if (allowTrailingWhite)
                {
                    reader.SkipWhile(CharHelper.IsClassicWhitespace);
                }
                return reader.EndOfString;
            }
        }