You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1020 lines
40 KiB
1020 lines
40 KiB
5 years ago
|
#if NET20 || NET30 || NET35 || !NET_4_6
|
||
|
|
||
|
using System.Collections.Generic;
|
||
|
using System.Diagnostics.Contracts;
|
||
|
using System.Globalization;
|
||
|
using System.Text;
|
||
|
using LinqInternal.Collections.Specialized;
|
||
|
using LinqInternal.Core;
|
||
|
|
||
|
namespace System.Numerics
|
||
|
{
|
||
|
public partial struct BigInteger
|
||
|
{
|
||
|
internal static string FormatBigInteger(BigInteger value, string format, NumberFormatInfo info)
|
||
|
{
|
||
|
int digits;
|
||
|
var fmt = ParseFormatSpecifier(format, out digits);
|
||
|
if (fmt == 'x' || fmt == 'X')
|
||
|
{
|
||
|
return FormatBigIntegerToHexString(value, fmt, digits, info);
|
||
|
}
|
||
|
|
||
|
if (fmt == 'e' || fmt == 'E')
|
||
|
{
|
||
|
var precision = digits != -1 ? digits : 6;
|
||
|
|
||
|
if (value.InternalBits == null)
|
||
|
{
|
||
|
return value.InternalSign.ToString(format, info);
|
||
|
}
|
||
|
|
||
|
var scale = (int)Math.Floor(Log10(value));
|
||
|
// ---
|
||
|
if (scale > precision + 10)
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
value /= 1000000000;
|
||
|
} while (Log10(value) > precision + 10);
|
||
|
}
|
||
|
while (Log10(value) > precision + 2)
|
||
|
{
|
||
|
value /= 10;
|
||
|
}
|
||
|
if ((Log10(value) > precision + 1))
|
||
|
{
|
||
|
var round = value % 10 >= 5;
|
||
|
value = (value / 10) + (round ? One : Zero);
|
||
|
}
|
||
|
|
||
|
ReverseStringBuilder builder;
|
||
|
|
||
|
if (value.InternalBits == null)
|
||
|
{
|
||
|
builder = new ReverseStringBuilder(10);
|
||
|
builder.Prepend(value.InternalSign.ToString("D"));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
builder = CreateBuilder(value, info, false, 0);
|
||
|
}
|
||
|
|
||
|
// ---
|
||
|
var decimalSeparator = info.NumberDecimalSeparator;
|
||
|
|
||
|
var result = new StringBuilder(builder.Length + 6);
|
||
|
|
||
|
var extra = 0;
|
||
|
|
||
|
if (precision >= builder.Length)
|
||
|
{
|
||
|
extra = precision - (builder.Length - 1);
|
||
|
precision = builder.Length - 1;
|
||
|
}
|
||
|
result.Append(builder.ToString(builder.Length, 1));
|
||
|
result.Append(decimalSeparator);
|
||
|
result.Append(builder.ToString(builder.Length - 1, precision));
|
||
|
result.Append(new string('0', extra));
|
||
|
result.Append(fmt);
|
||
|
result.Append(info.PositiveSign);
|
||
|
if (scale < 10)
|
||
|
{
|
||
|
result.Append("00");
|
||
|
}
|
||
|
else if (scale < 100)
|
||
|
{
|
||
|
result.Append('0');
|
||
|
}
|
||
|
result.Append(scale);
|
||
|
|
||
|
return result.ToString();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var decimalFmt = (fmt == 'g' || fmt == 'G' || fmt == 'd' || fmt == 'D' || fmt == 'r' || fmt == 'R');
|
||
|
if (value.InternalBits == null)
|
||
|
{
|
||
|
if (fmt == 'g' || fmt == 'G' || fmt == 'r' || fmt == 'R')
|
||
|
{
|
||
|
if (digits > 0)
|
||
|
{
|
||
|
format = "D" + digits.ToString(CultureInfo.InvariantCulture);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
format = "D";
|
||
|
}
|
||
|
}
|
||
|
return value.InternalSign.ToString(format, info);
|
||
|
}
|
||
|
var builder = CreateBuilder(value, info, decimalFmt, digits);
|
||
|
if (decimalFmt)
|
||
|
{
|
||
|
// Format Round-trip decimal
|
||
|
// This format is supported for integral types only. The number is converted to a string of
|
||
|
// decimal digits (0-9), prefixed by a minus sign if the number is negative. The precision
|
||
|
// specifier indicates the minimum number of digits desired in the resulting string. If required,
|
||
|
// the number is padded with zeros to its left to produce the number of digits given by the
|
||
|
// precision specifier.
|
||
|
while (digits > 0 && digits >= builder.Length)
|
||
|
{
|
||
|
builder.Prepend('0');
|
||
|
digits--;
|
||
|
}
|
||
|
if (value.InternalSign < 0)
|
||
|
{
|
||
|
builder.Prepend(info.NegativeSign);
|
||
|
}
|
||
|
return builder.ToString();
|
||
|
}
|
||
|
// 'c', 'C', 'e', 'E', 'f', 'F', 'n', 'N', 'p', 'P', custom
|
||
|
var precision = -1;
|
||
|
var groupingSizes = new[] { 3 };
|
||
|
var groupingSeparator = info.NumberGroupSeparator;
|
||
|
var decimalSeparator = info.NumberDecimalSeparator;
|
||
|
var groups = false;
|
||
|
var type = 0;
|
||
|
if (fmt == '\0')
|
||
|
{
|
||
|
// parse custom
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (fmt == 'c' || fmt == 'C')
|
||
|
{
|
||
|
decimalSeparator = info.CurrencyDecimalSeparator;
|
||
|
precision = digits != -1 ? digits : info.CurrencyDecimalDigits;
|
||
|
groupingSeparator = info.CurrencyGroupSeparator;
|
||
|
groupingSizes = info.CurrencyGroupSizes;
|
||
|
groups = true;
|
||
|
type = 1;
|
||
|
}
|
||
|
else if (fmt == 'f' || fmt == 'F')
|
||
|
{
|
||
|
precision = digits != -1 ? digits : info.NumberDecimalDigits;
|
||
|
}
|
||
|
else if (fmt == 'n' || fmt == 'N')
|
||
|
{
|
||
|
precision = digits != -1 ? digits : info.NumberDecimalDigits;
|
||
|
groups = true;
|
||
|
}
|
||
|
else if (fmt == 'p' || fmt == 'P')
|
||
|
{
|
||
|
decimalSeparator = info.PercentDecimalSeparator;
|
||
|
precision = digits != -1 ? digits : info.PercentDecimalDigits;
|
||
|
groups = true;
|
||
|
type = 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw new NotImplementedException();
|
||
|
}
|
||
|
}
|
||
|
var result = new StringBuilder(builder.Length + 20);
|
||
|
var close = SetWrap(value, info, type, result);
|
||
|
var append = builder;
|
||
|
if (groups)
|
||
|
{
|
||
|
var extra = groupingSizes.Length - 1;
|
||
|
if (groupingSizes[groupingSizes.Length - 1] != 0)
|
||
|
{
|
||
|
var totalDigits = builder.Length;
|
||
|
extra += (int)Math.Ceiling(totalDigits * 1.0 / groupingSizes[groupingSizes.Length - 1]);
|
||
|
}
|
||
|
var length = extra + builder.Length;
|
||
|
if (type == 2)
|
||
|
{
|
||
|
length += 2;
|
||
|
append = StringWithGroups(length, new ExtendedEnumerable<char>(new[] { '0', '0' }, builder), groupingSizes, groupingSeparator);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
append = StringWithGroups(extra + builder.Length, builder, groupingSizes, groupingSeparator);
|
||
|
}
|
||
|
}
|
||
|
result.Append(append);
|
||
|
if (precision > 0)
|
||
|
{
|
||
|
result.Append(decimalSeparator);
|
||
|
result.Append(new string('0', precision));
|
||
|
}
|
||
|
result.Append(close);
|
||
|
return result.ToString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal static BigInteger ParseBigInteger(string value, NumberStyles style, NumberFormatInfo info)
|
||
|
{
|
||
|
ArgumentException argumentException;
|
||
|
if (value == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("value");
|
||
|
}
|
||
|
if (!TryValidateParseStyleInteger(style, out argumentException))
|
||
|
{
|
||
|
throw argumentException;
|
||
|
}
|
||
|
BigInteger zero;
|
||
|
if (!TryParseBigInteger(value, style, info, out zero))
|
||
|
{
|
||
|
throw new FormatException("The value could not be parsed.");
|
||
|
}
|
||
|
return zero;
|
||
|
}
|
||
|
|
||
|
internal static char ParseFormatSpecifier(string format, out int digits)
|
||
|
{
|
||
|
digits = -1;
|
||
|
if (string.IsNullOrEmpty(format))
|
||
|
{
|
||
|
return 'R';
|
||
|
}
|
||
|
var index = 0;
|
||
|
var chr = format[index];
|
||
|
if (chr >= 'A' && chr <= 'Z' || chr >= 'a' && chr <= 'z')
|
||
|
{
|
||
|
index++;
|
||
|
if (index < format.Length)
|
||
|
{
|
||
|
var tmp = format[index];
|
||
|
if (tmp >= '0' && tmp <= '9')
|
||
|
{
|
||
|
index++;
|
||
|
digits = tmp - '0';
|
||
|
do
|
||
|
{
|
||
|
if (index >= format.Length || format[index] < '0' || format[index] > '9')
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
digits = digits * 10 + (format[index] - '0');
|
||
|
index++;
|
||
|
} while (digits < 10);
|
||
|
}
|
||
|
}
|
||
|
if (index >= format.Length || format[index] == 0)
|
||
|
{
|
||
|
return chr;
|
||
|
}
|
||
|
}
|
||
|
return '\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)
|
||
|
{
|
||
|
number.Negative |= reader.Read(negativeSign);
|
||
|
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 == 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)
|
||
|
{
|
||
|
number.Negative |= reader.Read(negativeSign);
|
||
|
positive |= 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal static bool TryParseBigInteger(string value, NumberStyles style, NumberFormatInfo info, out BigInteger result)
|
||
|
{
|
||
|
result = Zero;
|
||
|
ArgumentException e;
|
||
|
if (!TryValidateParseStyleInteger(style, out 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;
|
||
|
}
|
||
|
|
||
|
internal static bool TryValidateParseStyleInteger(NumberStyles style, out ArgumentException e)
|
||
|
{
|
||
|
if (((int)style & -1024) != (int)NumberStyles.None)
|
||
|
{
|
||
|
e = new ArgumentException("An undefined NumberStyles value is being used.", "style");
|
||
|
return false;
|
||
|
}
|
||
|
if ((style & NumberStyles.AllowHexSpecifier) == NumberStyles.None || (style & (NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign | NumberStyles.AllowParentheses | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands | NumberStyles.AllowExponent | NumberStyles.AllowCurrencySymbol)) == NumberStyles.None)
|
||
|
{
|
||
|
e = null;
|
||
|
return true;
|
||
|
}
|
||
|
e = new ArgumentException("With the AllowHexSpecifier bit set in the enum bit field, the only other valid bits that can be combined into the enum value must be a subset of those in HexNumber.");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private static ReverseStringBuilder CreateBuilder(BigInteger value, NumberFormatInfo info, bool decimalFmt, int digits)
|
||
|
{
|
||
|
// First convert to base 10^9.
|
||
|
const uint NumericBase = 1000000000; // 10^9
|
||
|
const int NumericBaseLog10 = 9;
|
||
|
var sourceLength = Length(value.InternalBits);
|
||
|
int maxConvertedLength;
|
||
|
try
|
||
|
{
|
||
|
maxConvertedLength = checked(sourceLength * 10 / 9 + 2);
|
||
|
}
|
||
|
catch (OverflowException e)
|
||
|
{
|
||
|
throw new FormatException("The value is too large to be represented by this format specifier.", e);
|
||
|
}
|
||
|
var converted = new uint[maxConvertedLength];
|
||
|
var convertedLength = 0;
|
||
|
for (var sourceIndex = sourceLength; --sourceIndex >= 0;)
|
||
|
{
|
||
|
// Take a cipher from the source
|
||
|
var carry = value.InternalBits[sourceIndex];
|
||
|
// Add it to converted
|
||
|
for (var convertedIndex = 0; convertedIndex < convertedLength; convertedIndex++)
|
||
|
{
|
||
|
var cipherBlock = NumericsHelpers.MakeUlong(converted[convertedIndex], carry);
|
||
|
converted[convertedIndex] = (uint)(cipherBlock % NumericBase);
|
||
|
carry = (uint)(cipherBlock / NumericBase);
|
||
|
}
|
||
|
if (carry != 0)
|
||
|
{
|
||
|
converted[convertedLength++] = carry % NumericBase;
|
||
|
carry /= NumericBase;
|
||
|
if (carry != 0)
|
||
|
{
|
||
|
converted[convertedLength++] = carry;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
int stringCapacity;
|
||
|
try
|
||
|
{
|
||
|
// Each uint contributes at most 9 digits to the decimal representation.
|
||
|
stringCapacity = checked(convertedLength * NumericBaseLog10);
|
||
|
}
|
||
|
catch (OverflowException e)
|
||
|
{
|
||
|
throw new FormatException("The value is too large to be represented by this format specifier.", e);
|
||
|
}
|
||
|
if (decimalFmt)
|
||
|
{
|
||
|
if (digits > 0 && stringCapacity < digits)
|
||
|
{
|
||
|
stringCapacity = digits;
|
||
|
}
|
||
|
if (value.InternalSign < 0)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
// Leave an extra slot for a minus sign.
|
||
|
stringCapacity = checked(stringCapacity + info.NegativeSign.Length);
|
||
|
}
|
||
|
catch (OverflowException e)
|
||
|
{
|
||
|
throw new FormatException("The value is too large to be represented by this format specifier.", e);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
var result = new ReverseStringBuilder(stringCapacity);
|
||
|
for (var stringIndex = 0; stringIndex < convertedLength - 1; stringIndex++)
|
||
|
{
|
||
|
var cipherBlock = converted[stringIndex];
|
||
|
for (var cch = NumericBaseLog10; --cch >= 0;)
|
||
|
{
|
||
|
result.Prepend((char)('0' + cipherBlock % 10));
|
||
|
cipherBlock /= 10;
|
||
|
}
|
||
|
}
|
||
|
for (var cipherBlock = converted[convertedLength - 1]; cipherBlock != 0;)
|
||
|
{
|
||
|
result.Prepend((char)('0' + cipherBlock % 10));
|
||
|
cipherBlock /= 10;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
private static string FormatBigIntegerToHexString(BigInteger value, char format, int digits, NumberFormatInfo info)
|
||
|
{
|
||
|
var stringBuilder = new StringBuilder();
|
||
|
var byteArray = value.ToByteArray();
|
||
|
string str1;
|
||
|
var length = byteArray.Length - 1;
|
||
|
if (length > -1)
|
||
|
{
|
||
|
var flag = false;
|
||
|
var num = byteArray[length];
|
||
|
if (num > 247)
|
||
|
{
|
||
|
num = (byte)(num - 240);
|
||
|
flag = true;
|
||
|
}
|
||
|
if (num < 8 || flag)
|
||
|
{
|
||
|
str1 = string.Format(CultureInfo.InvariantCulture, "{0}1", format);
|
||
|
stringBuilder.Append(num.ToString(str1, info));
|
||
|
length--;
|
||
|
}
|
||
|
}
|
||
|
if (length > -1)
|
||
|
{
|
||
|
str1 = string.Format(CultureInfo.InvariantCulture, "{0}2", format);
|
||
|
while (length > -1)
|
||
|
{
|
||
|
var num1 = length;
|
||
|
length = num1 - 1;
|
||
|
stringBuilder.Append(byteArray[num1].ToString(str1, info));
|
||
|
}
|
||
|
}
|
||
|
if (digits > 0 && digits > stringBuilder.Length)
|
||
|
{
|
||
|
var stringBuilder1 = stringBuilder;
|
||
|
string str;
|
||
|
if (value.InternalSign < 0)
|
||
|
{
|
||
|
str = (format != 'x' ? "F" : "f");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
str = "0";
|
||
|
}
|
||
|
stringBuilder1.Insert(0, str, digits - stringBuilder.Length);
|
||
|
}
|
||
|
return stringBuilder.ToString();
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
private static string SetWrap(BigInteger value, NumberFormatInfo info, int type, StringBuilder result)
|
||
|
{
|
||
|
var close = string.Empty;
|
||
|
switch (type)
|
||
|
{
|
||
|
case 1:
|
||
|
if (value.InternalSign < 0)
|
||
|
{
|
||
|
switch (info.CurrencyNegativePattern)
|
||
|
{
|
||
|
case 0:
|
||
|
result.Append('(');
|
||
|
result.Append(info.CurrencySymbol);
|
||
|
close = ")";
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
result.Append(info.NegativeSign);
|
||
|
result.Append(info.CurrencySymbol);
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
result.Append(info.CurrencySymbol);
|
||
|
result.Append(info.NegativeSign);
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
result.Append(info.CurrencySymbol);
|
||
|
close = info.NegativeSign;
|
||
|
break;
|
||
|
|
||
|
case 4:
|
||
|
result.Append('(');
|
||
|
close = info.CurrencySymbol + ")";
|
||
|
break;
|
||
|
|
||
|
case 5:
|
||
|
result.Append(info.NegativeSign);
|
||
|
close = info.CurrencySymbol;
|
||
|
break;
|
||
|
|
||
|
case 6:
|
||
|
close = info.NegativeSign + info.CurrencySymbol;
|
||
|
break;
|
||
|
|
||
|
case 7:
|
||
|
close = info.CurrencySymbol + info.NegativeSign;
|
||
|
break;
|
||
|
|
||
|
case 8:
|
||
|
result.Append(info.NegativeSign);
|
||
|
close = " " + info.CurrencySymbol;
|
||
|
break;
|
||
|
|
||
|
case 9:
|
||
|
result.Append(info.NegativeSign);
|
||
|
result.Append(info.CurrencySymbol);
|
||
|
result.Append(" ");
|
||
|
break;
|
||
|
|
||
|
case 10:
|
||
|
close = " " + info.CurrencySymbol + info.NegativeSign;
|
||
|
break;
|
||
|
|
||
|
case 11:
|
||
|
result.Append(info.CurrencySymbol);
|
||
|
result.Append(" ");
|
||
|
close = info.NegativeSign;
|
||
|
break;
|
||
|
|
||
|
case 12:
|
||
|
result.Append(info.CurrencySymbol);
|
||
|
result.Append(" ");
|
||
|
result.Append(info.NegativeSign);
|
||
|
break;
|
||
|
|
||
|
case 13:
|
||
|
result.Append(info.CurrencySymbol);
|
||
|
result.Append(info.NegativeSign);
|
||
|
result.Append(" ");
|
||
|
break;
|
||
|
|
||
|
case 14:
|
||
|
result.Append('(');
|
||
|
result.Append(info.CurrencySymbol);
|
||
|
result.Append(" ");
|
||
|
close = ")";
|
||
|
break;
|
||
|
|
||
|
case 15:
|
||
|
result.Append('(');
|
||
|
close = " " + info.CurrencySymbol + ")";
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch (info.CurrencyPositivePattern)
|
||
|
{
|
||
|
case 0:
|
||
|
result.Append(info.CurrencySymbol);
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
close = info.CurrencySymbol;
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
result.Append(info.CurrencySymbol);
|
||
|
result.Append(" ");
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
close = " " + info.CurrencySymbol;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
if (value.InternalSign < 0)
|
||
|
{
|
||
|
switch (info.PercentNegativePattern)
|
||
|
{
|
||
|
case 0:
|
||
|
result.Append(info.NegativeSign);
|
||
|
close = " " + info.PercentSymbol;
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
result.Append(info.NegativeSign);
|
||
|
close = info.PercentSymbol;
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
result.Append(info.NegativeSign);
|
||
|
result.Append(info.PercentSymbol);
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
result.Append(info.PercentSymbol);
|
||
|
result.Append(info.NegativeSign);
|
||
|
break;
|
||
|
|
||
|
case 4:
|
||
|
result.Append(info.PercentSymbol);
|
||
|
close = info.NegativeSign;
|
||
|
break;
|
||
|
|
||
|
case 5:
|
||
|
close = info.NegativeSign + info.PercentSymbol;
|
||
|
break;
|
||
|
|
||
|
case 6:
|
||
|
close = info.PercentSymbol + info.NegativeSign;
|
||
|
break;
|
||
|
|
||
|
case 7:
|
||
|
result.Append(info.NegativeSign);
|
||
|
result.Append(info.PercentSymbol);
|
||
|
result.Append(" ");
|
||
|
break;
|
||
|
|
||
|
case 8:
|
||
|
close = " " + info.PercentSymbol + info.NegativeSign;
|
||
|
break;
|
||
|
|
||
|
case 9:
|
||
|
result.Append(info.PercentSymbol);
|
||
|
result.Append(" ");
|
||
|
close = info.NegativeSign;
|
||
|
break;
|
||
|
|
||
|
case 10:
|
||
|
result.Append(info.PercentSymbol);
|
||
|
result.Append(" ");
|
||
|
result.Append(info.NegativeSign);
|
||
|
break;
|
||
|
|
||
|
case 11:
|
||
|
close = info.NegativeSign + " " + info.PercentSymbol;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch (info.CurrencyPositivePattern)
|
||
|
{
|
||
|
case 0:
|
||
|
close = " " + info.PercentSymbol;
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
close = info.PercentSymbol;
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
result.Append(info.PercentSymbol);
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
result.Append(info.PercentSymbol);
|
||
|
result.Append(" ");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
if (value.InternalSign < 0)
|
||
|
{
|
||
|
result.Append(info.NegativeSign);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
return close;
|
||
|
}
|
||
|
|
||
|
private static ReverseStringBuilder StringWithGroups(int capacity, IEnumerable<char> builder, int[] groupingSizes, string groupingSeparator)
|
||
|
{
|
||
|
var newBuffer = new ReverseStringBuilder(capacity);
|
||
|
using (var enumerator = builder.GetEnumerator())
|
||
|
{
|
||
|
if (!enumerator.MoveNext())
|
||
|
{
|
||
|
return newBuffer;
|
||
|
}
|
||
|
foreach (var size in groupingSizes)
|
||
|
{
|
||
|
for (var count = size - 1; count >= 0; count--)
|
||
|
{
|
||
|
newBuffer.Prepend(enumerator.Current);
|
||
|
if (!enumerator.MoveNext())
|
||
|
{
|
||
|
return newBuffer;
|
||
|
}
|
||
|
}
|
||
|
newBuffer.Prepend(groupingSeparator);
|
||
|
}
|
||
|
{
|
||
|
var size = groupingSizes[groupingSizes.Length - 1];
|
||
|
if (size != 0)
|
||
|
{
|
||
|
while (true)
|
||
|
{
|
||
|
for (var count = size - 1; count >= 0; count--)
|
||
|
{
|
||
|
newBuffer.Prepend(enumerator.Current);
|
||
|
if (!enumerator.MoveNext())
|
||
|
{
|
||
|
return newBuffer;
|
||
|
}
|
||
|
}
|
||
|
newBuffer.Prepend(groupingSeparator);
|
||
|
}
|
||
|
}
|
||
|
while (true)
|
||
|
{
|
||
|
newBuffer.Prepend(enumerator.Current);
|
||
|
if (!enumerator.MoveNext())
|
||
|
{
|
||
|
return newBuffer;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|