Merge pull request #901 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / corlib / System / NumberFormatter.cs
1 //
2 // System.NumberFormatter.cs
3 //
4 // Author:
5 //   Kazuki Oikawa (kazuki@panicode.com)
6 //   Eyal Alaluf (eyala@mainsoft.com)
7 //
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
9 // Copyright (C) 2008 Mainsoft Co. (http://www.mainsoft.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 // NumberFormatter is shared with Grasshopper and hence the #if TARGET_JVM for
32 // marking the use of unsafe code that is not supported in Grasshopper.
33 #if !TARGET_JVM
34 #define UNSAFE_TABLES
35 #endif
36
37 using System.Globalization;
38 using System.Text;
39 using System.Threading;
40 using System.Runtime.CompilerServices;
41
42 namespace System
43 {
44         internal sealed partial class NumberFormatter
45         {
46                 #region Static Fields
47
48                 const int DefaultExpPrecision = 6;
49                 const int HundredMillion = 100000000;
50                 const long SeventeenDigitsThreshold = 10000000000000000;
51                 const ulong ULongDivHundredMillion = UInt64.MaxValue / HundredMillion;
52                 const ulong ULongModHundredMillion = 1 + UInt64.MaxValue % HundredMillion;
53
54                 const int DoubleBitsExponentShift = 52;
55                 const int DoubleBitsExponentMask = 0x7ff;
56                 const long DoubleBitsMantissaMask = 0xfffffffffffff;
57                 const int DecimalBitsScaleMask = 0x1f0000;
58
59                 const int SingleDefPrecision = 7;
60                 const int DoubleDefPrecision = 15;
61                 const int Int8DefPrecision = 3;
62                 const int UInt8DefPrecision = 3;
63                 const int Int16DefPrecision = 5;
64                 const int UInt16DefPrecision = 5;
65                 const int Int32DefPrecision = 10;
66                 const int UInt32DefPrecision = 10;
67                 const int Int64DefPrecision = 19;
68                 const int UInt64DefPrecision = 20;
69                 const int DecimalDefPrecision = 100;
70                 const int TenPowersListLength = 19;
71
72                 const double MinRoundtripVal = -1.79769313486231E+308;
73                 const double MaxRoundtripVal = 1.79769313486231E+308;
74
75 #if UNSAFE_TABLES
76                 // The below arrays are taken from mono/metatdata/number-formatter.h
77
78                 private static readonly unsafe ulong* MantissaBitsTable;
79                 private static readonly unsafe int* TensExponentTable;
80                 private static readonly unsafe char* DigitLowerTable;
81                 private static readonly unsafe char* DigitUpperTable;
82                 private static readonly unsafe long* TenPowersList;
83
84                 // DecHexDigits s a translation table from a decimal number to its
85                 // digits hexadecimal representation (e.g. DecHexDigits [34] = 0x34).
86                 private static readonly unsafe int* DecHexDigits;
87
88                 [MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)]
89                 private unsafe static extern void GetFormatterTables (out ulong* MantissaBitsTable, out int* TensExponentTable,
90                                 out char* DigitLowerTable, out char* DigitUpperTable,
91                                 out long* TenPowersList, out int* DecHexDigits);
92
93                 unsafe static NumberFormatter()
94                 {
95                         GetFormatterTables (out MantissaBitsTable, out TensExponentTable,
96                                 out DigitLowerTable, out DigitUpperTable, out TenPowersList, out DecHexDigits);
97                 }
98
99                 unsafe
100 #endif
101                 static long GetTenPowerOf(int i)
102                 {
103                         return TenPowersList [i];
104                 }
105                 #endregion Static Fields
106
107                 #region Fields
108
109                 private NumberFormatInfo _nfi;
110
111                 //part of the private stringbuffer
112                 private char[] _cbuf;
113
114                 private bool _NaN;
115                 private bool _infinity;
116                 private bool _isCustomFormat;
117                 private bool _specifierIsUpper;
118                 private bool _positive;
119                 private char _specifier;
120                 private int _precision;
121                 private int _defPrecision;
122
123                 private int _digitsLen;
124                 private int _offset; // Represent the first digit offset.
125                 private int _decPointPos;
126
127                 // The following fields are a hexadeimal representation of the digits.
128                 // For instance _val = 0x234 represents the digits '2', '3', '4'.
129                 private uint _val1; // Digits 0 - 7.
130                 private uint _val2; // Digits 8 - 15.
131                 private uint _val3; // Digits 16 - 23.
132                 private uint _val4; // Digits 23 - 31. Only needed for decimals.
133
134                 #endregion Fields
135
136                 #region Constructor Helpers
137
138                 // Translate an unsigned int to hexadecimal digits.
139                 // i.e. 123456789 is represented by _val1 = 0x23456789 and _val2 = 0x1
140                 private void InitDecHexDigits (uint value)
141                 {
142                         if (value >= HundredMillion) {
143                                 int div1 = (int)(value / HundredMillion);
144                                 value -= HundredMillion * (uint)div1;
145                                 _val2 = FastToDecHex (div1);
146                         }
147                         _val1 = ToDecHex ((int)value);
148                 }
149
150                 // Translate an unsigned long to hexadecimal digits.
151                 private void InitDecHexDigits (ulong value)
152                 {
153                         if (value >= HundredMillion) {
154                                 long div1 = (long)(value / HundredMillion);
155                                 value -= HundredMillion * (ulong)div1;
156                                 if (div1 >= HundredMillion) {
157                                         int div2 = (int)(div1 / HundredMillion);
158                                         div1 = div1 - div2 * (long)HundredMillion;
159                                         _val3 = ToDecHex (div2);
160                                 }
161                                 if (div1 != 0)
162                                         _val2 = ToDecHex ((int)(div1));
163                         }
164                         if (value != 0)
165                                 _val1 = ToDecHex ((int)value);
166                 }
167
168                 // Translate a decimal integer to hexadecimal digits.
169                 // The decimal integer is 96 digits and its value is hi * 2^64 + lo.
170                 // is the lower 64 bits.
171                 private void InitDecHexDigits (uint hi, ulong lo)
172                 {
173                         if (hi == 0) {
174                                 InitDecHexDigits (lo); // Only the lower 64 bits matter.
175                                 return;
176                         }
177
178                         // Compute (hi, lo) = (hi , lo) / HundredMillion.
179                         uint divhi = hi / HundredMillion;
180                         ulong remhi = hi - divhi * HundredMillion;
181                         ulong divlo = lo / HundredMillion;
182                         ulong remlo = lo - divlo * HundredMillion + remhi * ULongModHundredMillion;
183                         hi = divhi;
184                         lo = divlo + remhi * ULongDivHundredMillion;
185                         divlo = remlo / HundredMillion;
186                         remlo -= divlo * HundredMillion;
187                         lo += divlo;
188                         _val1 = ToDecHex ((int)remlo);
189
190                         // Divide hi * 2 ^ 64 + lo by HundredMillion using the fact that
191                         // hi < HundredMillion.
192                         divlo = lo / HundredMillion;
193                         remlo = lo - divlo * HundredMillion;
194                         lo = divlo;
195                         if (hi != 0) {
196                                 lo += hi * ULongDivHundredMillion;
197                                 remlo += hi * ULongModHundredMillion;
198                                 divlo = remlo / HundredMillion;
199                                 lo += divlo;
200                                 remlo -= divlo * HundredMillion;
201                         }
202                         _val2 = ToDecHex ((int)remlo);
203
204                         // Now we are left with 64 bits store in lo.
205                         if (lo >= HundredMillion) {
206                                 divlo = lo / HundredMillion;
207                                 lo -= divlo * HundredMillion;
208                                 _val4 = ToDecHex ((int)divlo);
209                         }
210                         _val3 = ToDecHex ((int)lo);
211                 }
212
213                 // Helper to translate an int in the range 0 .. 9999 to its
214                 // Hexadecimal digits representation.
215 #if UNSAFE_TABLES
216                 unsafe
217 #endif
218                 private static uint FastToDecHex (int val)
219                 {
220                         if (val < 100)
221                                 return (uint)DecHexDigits [val];
222
223                         // Uses 2^19 (524288) to compute val / 100 for val < 10000.
224                         int v = (val * 5243) >> 19;
225                         return (uint)((DecHexDigits [v] << 8) | DecHexDigits [val - v * 100]);
226                 }
227
228                 // Helper to translate an int in the range 0 .. 99999999 to its
229                 // Hexadecimal digits representation.
230                 private static uint ToDecHex (int val)
231                 {
232                         uint res = 0;
233                         if (val >= 10000) {
234                                 int v = val / 10000;
235                                 val -= v * 10000;
236                                 res = FastToDecHex (v) << 16;
237                         }
238                         return res | FastToDecHex (val);
239                 }
240
241                 // Helper to count number of hexadecimal digits in a number.
242                 private static int FastDecHexLen (int val)
243                 {
244                         if (val < 0x100)
245                                 if (val < 0x10)
246                                         return 1;
247                                 else
248                                         return 2;
249                         else if (val < 0x1000)
250                                 return 3;
251                         else
252                                 return 4;
253                 }
254
255                 private static int DecHexLen (uint val)
256                 {
257                         if (val < 0x10000)
258                                 return FastDecHexLen ((int)val);
259                         return 4 + FastDecHexLen ((int)(val >> 16));
260                 }
261
262                 // Count number of hexadecimal digits stored in _val1 .. _val4.
263                 private int DecHexLen ()
264                 {
265                         if (_val4 != 0)
266                                 return DecHexLen (_val4) + 24;
267                         else if (_val3 != 0)
268                                 return DecHexLen (_val3) + 16;
269                         else if (_val2 != 0)
270                                 return DecHexLen (_val2) + 8;
271                         else if (_val1 != 0)
272                                 return DecHexLen (_val1);
273                         else
274                                 return 0;
275                 }
276
277                 // Helper to count the 10th scale (number of digits) in a number
278                 private static int ScaleOrder (long hi)
279                 {
280                         for (int i = TenPowersListLength - 1; i >= 0; i--)
281                                 if (hi >= GetTenPowerOf (i))
282                                         return i + 1;
283                         return 1;
284                 }
285
286                 // Compute the initial precision for rounding a floating number
287                 // according to the used format.
288                 int InitialFloatingPrecision ()
289                 {
290                         if (_specifier == 'R')
291                                 return _defPrecision + 2;
292                         if (_precision < _defPrecision)
293                                 return _defPrecision;
294                         if (_specifier == 'G')
295                                 return Math.Min (_defPrecision + 2, _precision);
296                         if (_specifier == 'E')
297                                 return Math.Min (_defPrecision + 2, _precision + 1);
298                         return _defPrecision;
299                 }
300
301                 // Parse the given format and extract the precision in it.
302                 // Returns -1 for empty formats and -2 to indicate that the format
303                 // is a custom format.
304                 private static int ParsePrecision (string format)
305                 {
306                         int precision = 0;
307                         for (int i = 1; i < format.Length; i++) {
308                                 int val = format [i] - '0';
309                                 precision = precision * 10 + val;
310                                 if (val < 0 || val > 9 || precision > 99)
311                                         return -2;
312                         }
313                         return precision;
314                 }
315
316                 #endregion Constructor Helpers
317
318                 #region Constructors
319
320                 // Parse the given format and initialize the following fields:
321                 //   _isCustomFormat, _specifierIsUpper, _specifier & _precision.
322                 public NumberFormatter (Thread current)
323                 {
324                         _cbuf = EmptyArray<char>.Value;
325                         if (current == null)
326                                 return;
327                         CurrentCulture = current.CurrentCulture;
328                 }
329
330                 private void Init (string format)
331                 {
332                         _val1 = _val2 = _val3 = _val4 = 0;
333                         _offset = 0;
334                         _NaN = _infinity = false;
335                         _isCustomFormat = false;
336                         _specifierIsUpper = true;
337                         _precision = -1;
338
339                         if (format == null || format.Length == 0) {
340                                 _specifier = 'G';
341                                 return;
342                         }
343
344                         char specifier = format [0];
345                         if (specifier >= 'a' && specifier <= 'z') {
346                                 specifier = (char)(specifier - 'a' + 'A');
347                                 _specifierIsUpper = false;
348                         }
349                         else if (specifier < 'A' || specifier > 'Z') {
350                                 _isCustomFormat = true;
351                                 _specifier = '0';
352                                 return;
353                         }
354                         _specifier = specifier;
355                         if (format.Length > 1) {
356                                 _precision = ParsePrecision (format);
357                                 if (_precision == -2) { // Is it a custom format?
358                                         _isCustomFormat = true;
359                                         _specifier = '0';
360                                         _precision = -1;
361                                 }
362                         }
363                 }
364
365                 private void InitHex (ulong value)
366                 {
367                         switch (_defPrecision) {
368                                 case Int8DefPrecision:  value = (byte) value;    break;
369                                 case Int16DefPrecision: value = (ushort) value;  break;
370                                 case Int32DefPrecision: value = (uint) value;    break;
371                         }
372                         _val1 = (uint)value;
373                         _val2 = (uint)(value >> 32);
374                         _decPointPos = _digitsLen = DecHexLen ();
375                         if (value == 0)
376                                 _decPointPos = 1;
377                 }
378
379                 private void Init (string format, int value, int defPrecision)
380                 {
381                         Init (format);
382                         _defPrecision = defPrecision;
383                         _positive = value >= 0;
384
385                         if (value == 0 || _specifier == 'X') {
386                                 InitHex ((ulong)value);
387                                 return;
388                         }
389
390                         if (value < 0)
391                                 value = -value;
392                         InitDecHexDigits ((uint)value);
393                         _decPointPos = _digitsLen = DecHexLen ();
394                 }
395
396                 private void Init (string format, uint value, int defPrecision)
397                 {
398                         Init (format);
399                         _defPrecision = defPrecision;
400                         _positive = true;
401
402                         if (value == 0 || _specifier == 'X') {
403                                 InitHex (value);
404                                 return;
405                         }
406
407                         InitDecHexDigits (value);
408                         _decPointPos = _digitsLen = DecHexLen ();
409                 }
410
411                 private void Init (string format, long value)
412                 {
413                         Init (format);
414                         _defPrecision = Int64DefPrecision;
415                         _positive = value >= 0;
416
417                         if (value == 0 || _specifier == 'X') {
418                                 InitHex ((ulong)value);
419                                 return;
420                         }
421
422                         if (value < 0)
423                                 value = -value;
424                         InitDecHexDigits ((ulong)value);
425                         _decPointPos = _digitsLen = DecHexLen ();
426                 }
427
428                 private void Init (string format, ulong value)
429                 {
430                         Init (format);
431                         _defPrecision = UInt64DefPrecision;
432                         _positive = true;
433
434                         if (value == 0 || _specifier == 'X') {
435                                 InitHex ((ulong)value);
436                                 return;
437                         }
438
439                         InitDecHexDigits (value);
440                         _decPointPos = _digitsLen = DecHexLen ();
441                 }
442
443 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
444                 unsafe
445 #endif
446                 private void Init (string format, double value, int defPrecision)
447                 {
448                         Init (format);
449
450                         _defPrecision = defPrecision;
451                         long bits = BitConverter.DoubleToInt64Bits (value);
452                         _positive = bits >= 0;
453                         bits &= Int64.MaxValue;
454                         if (bits == 0) {
455                                 _decPointPos = 1;
456                                 _digitsLen = 0;
457                                 _positive = true;
458                                 return;
459                         }
460
461                         int e = (int)(bits >> DoubleBitsExponentShift);
462                         long m = bits & DoubleBitsMantissaMask;
463                         if (e == DoubleBitsExponentMask) {
464                                 _NaN = m != 0;
465                                 _infinity = m == 0;
466                                 return;
467                         }
468
469                         int expAdjust = 0;
470                         if (e == 0) {
471                                 // We need 'm' to be large enough so we won't lose precision.
472                                 e = 1;
473                                 int scale = ScaleOrder (m);
474                                 if (scale < DoubleDefPrecision) {
475                                         expAdjust = scale - DoubleDefPrecision;
476                                         m *= GetTenPowerOf (-expAdjust);
477                                 }
478                         }
479                         else {
480                                 m = (m + DoubleBitsMantissaMask + 1) * 10;
481                                 expAdjust = -1;
482                         }
483
484                         // multiply the mantissa by 10 ^ N
485                         ulong lo = (uint)m;
486                         ulong hi = (ulong)m >> 32;
487                         ulong lo2 = MantissaBitsTable [e];
488                         ulong hi2 = lo2 >> 32;
489                         lo2 = (uint)lo2;
490                         ulong mm = hi * lo2 + lo * hi2 + ((lo * lo2) >> 32);
491                         long res = (long)(hi * hi2 + (mm >> 32));
492                         while (res < SeventeenDigitsThreshold) {
493                                 mm = (mm & UInt32.MaxValue) * 10;
494                                 res = res * 10 + (long)(mm >> 32);
495                                 expAdjust--;
496                         }
497                         if ((mm & 0x80000000) != 0)
498                                 res++;
499
500                         int order = DoubleDefPrecision + 2;
501                         _decPointPos = TensExponentTable [e] + expAdjust + order;
502
503                         // Rescale 'res' to the initial precision (15-17 for doubles).
504                         int initialPrecision = InitialFloatingPrecision ();
505                         if (order > initialPrecision) {
506                                 long val = GetTenPowerOf (order - initialPrecision);
507                                 res = (res + (val >> 1)) / val;
508                                 order = initialPrecision;
509                         }
510                         if (res >= GetTenPowerOf (order)) {
511                                 order++;
512                                 _decPointPos++;
513                         }
514
515                         InitDecHexDigits ((ulong)res);
516                         _offset = CountTrailingZeros ();
517                         _digitsLen = order - _offset;
518                 }
519
520                 private void Init (string format, decimal value)
521                 {
522                         Init (format);
523                         _defPrecision = DecimalDefPrecision;
524
525                         int[] bits = decimal.GetBits (value);
526                         int scale = (bits [3] & DecimalBitsScaleMask) >> 16;
527                         _positive = bits [3] >= 0;
528                         if (bits [0] == 0 && bits [1] == 0 && bits [2] == 0) {
529                                 _decPointPos = -scale;
530                                 _positive = true;
531                                 _digitsLen = 0;
532                                 return;
533                         }
534
535                         InitDecHexDigits ((uint)bits [2], ((ulong)bits [1] << 32) | (uint)bits [0]);
536                         _digitsLen = DecHexLen ();
537                         _decPointPos = _digitsLen - scale;
538                         if (_precision != -1 || _specifier != 'G') {
539                                 _offset = CountTrailingZeros ();
540                                 _digitsLen -= _offset;
541                         }
542                 }
543
544                 #endregion Constructors
545
546                 #region Inner String Buffer
547
548                 //_cbuf moved to before other fields to improve layout
549                 private int _ind;
550
551                 private void ResetCharBuf (int size)
552                 {
553                         _ind = 0;
554                         if (_cbuf.Length < size)
555                                 _cbuf = new char [size];
556                 }
557
558                 private void Resize (int len)
559                 {
560                         Array.Resize (ref _cbuf, len);
561                 }
562
563                 private void Append (char c)
564                 {
565                         if (_ind == _cbuf.Length)
566                                 Resize (_ind + 10);
567                         _cbuf [_ind++] = c;
568                 }
569
570                 private void Append (char c, int cnt)
571                 {
572                         if (_ind + cnt > _cbuf.Length)
573                                 Resize (_ind + cnt + 10);
574                         while (cnt-- > 0)
575                                 _cbuf [_ind++] = c;
576                 }
577
578                 private void Append (string s)
579                 {
580                         int slen = s.Length;
581                         if (_ind + slen > _cbuf.Length)
582                                 Resize (_ind + slen + 10);
583                         for (int i = 0; i < slen; i++)
584                                 _cbuf [_ind++] = s [i];
585                 }
586
587                 #endregion Inner String Buffer
588
589                 #region Helper properties
590
591                 private NumberFormatInfo GetNumberFormatInstance (IFormatProvider fp)
592                 {
593                         if (_nfi != null && fp == null)
594                                 return _nfi;
595                         return NumberFormatInfo.GetInstance (fp);
596                 }
597
598                 public CultureInfo CurrentCulture {
599                         set {
600                                 if (value != null && value.IsReadOnly)
601                                         _nfi = value.NumberFormat;
602                                 else
603                                         _nfi = null;
604                         }
605                 }
606
607                 private int IntegerDigits {
608                         get { return _decPointPos > 0 ? _decPointPos : 1; }
609                 }
610
611                 private int DecimalDigits {
612                         get { return _digitsLen > _decPointPos ? _digitsLen - _decPointPos : 0; }
613                 }
614
615                 private bool IsFloatingSource {
616                         get { return _defPrecision == DoubleDefPrecision || _defPrecision == SingleDefPrecision; }
617                 }
618
619                 private bool IsZero {
620                         get { return _digitsLen == 0; }
621                 }
622
623                 private bool IsZeroInteger {
624                         get { return _digitsLen == 0 || _decPointPos <= 0; }
625                 }
626
627                 #endregion Helper properties
628
629                 #region Round
630
631                 private void RoundPos (int pos)
632                 {
633                         RoundBits (_digitsLen - pos);
634                 }
635
636                 private bool RoundDecimal (int decimals)
637                 {
638                         return RoundBits (_digitsLen - _decPointPos - decimals);
639                 }
640
641                 private bool RoundBits (int shift)
642                 {
643                         if (shift <= 0)
644                                 return false;
645
646                         if (shift > _digitsLen) {
647                                 _digitsLen = 0;
648                                 _decPointPos = 1;
649                                 _val1 = _val2 = _val3 = _val4 = 0;
650                                 _positive = true;
651                                 return false;
652                         }
653                         shift += _offset;
654                         _digitsLen += _offset;
655                         while (shift > 8) {
656                                 _val1 = _val2;
657                                 _val2 = _val3;
658                                 _val3 = _val4;
659                                 _val4 = 0;
660                                 _digitsLen -= 8;
661                                 shift -= 8;
662                         }
663                         shift = (shift - 1) << 2;
664                         uint v = _val1 >> shift;
665                         uint rem16 = v & 0xf;
666                         _val1 = (v ^ rem16) << shift;
667                         bool res = false;
668                         if (rem16 >= 0x5) {
669                                 _val1 |= 0x99999999 >> (28 - shift);
670                                 AddOneToDecHex ();
671                                 int newlen = DecHexLen ();
672                                 res = newlen != _digitsLen;
673                                 _decPointPos = _decPointPos + newlen - _digitsLen;
674                                 _digitsLen = newlen;
675                         }
676                         RemoveTrailingZeros ();
677                         return res;
678                 }
679
680                 private void RemoveTrailingZeros ()
681                 {
682                         _offset = CountTrailingZeros ();
683                         _digitsLen -= _offset;
684                         if (_digitsLen == 0) {
685                                 _offset = 0;
686                                 _decPointPos = 1;
687                                 _positive = true;
688                         }
689                 }
690
691                 private void AddOneToDecHex ()
692                 {
693                         if (_val1 == 0x99999999) {
694                                 _val1 = 0;
695                                 if (_val2 == 0x99999999) {
696                                         _val2 = 0;
697                                         if (_val3 == 0x99999999) {
698                                                 _val3 = 0;
699                                                 _val4 = AddOneToDecHex (_val4);
700                                         }
701                                         else
702                                                 _val3 = AddOneToDecHex (_val3);
703                                 }
704                                 else
705                                         _val2 = AddOneToDecHex (_val2);
706                         }
707                         else
708                                 _val1 = AddOneToDecHex (_val1);
709                 }
710
711                 // Assume val != 0x99999999
712                 private static uint AddOneToDecHex (uint val)
713                 {
714                         if ((val & 0xffff) == 0x9999)
715                                 if ((val & 0xffffff) == 0x999999)
716                                         if ((val & 0xfffffff) == 0x9999999)
717                                                 return val + 0x06666667;
718                                         else
719                                                 return val + 0x00666667;
720                                 else if ((val & 0xfffff) == 0x99999)
721                                         return val + 0x00066667;
722                                 else
723                                         return val + 0x00006667;
724                         else if ((val & 0xff) == 0x99)
725                                 if ((val & 0xfff) == 0x999)
726                                         return val + 0x00000667;
727                                 else
728                                         return val + 0x00000067;
729                         else if ((val & 0xf) == 0x9)
730                                 return val + 0x00000007;
731                         else
732                                 return val + 1;
733                 }
734
735                 private int CountTrailingZeros ()
736                 {
737                         if (_val1 != 0)
738                                 return CountTrailingZeros (_val1);
739                         if (_val2 != 0)
740                                 return CountTrailingZeros (_val2) + 8;
741                         if (_val3 != 0)
742                                 return CountTrailingZeros (_val3) + 16;
743                         if (_val4 != 0)
744                                 return CountTrailingZeros (_val4) + 24;
745                         return _digitsLen;
746                 }
747
748                 private static int CountTrailingZeros (uint val)
749                 {
750                         if ((val & 0xffff) == 0)
751                                 if ((val & 0xffffff) == 0)
752                                         if ((val & 0xfffffff) == 0)
753                                                 return 7;
754                                         else
755                                                 return 6;
756                                 else if ((val & 0xfffff) == 0)
757                                         return 5;
758                                 else
759                                         return 4;
760                         else if ((val & 0xff) == 0)
761                                 if ((val & 0xfff) == 0)
762                                         return 3;
763                                 else
764                                         return 2;
765                         else if ((val & 0xf) == 0)
766                                 return 1;
767                         else
768                                 return 0;
769                 }
770
771                 #endregion Round
772
773                 #region public number formatting methods
774
775                 [ThreadStatic]
776                 static NumberFormatter threadNumberFormatter;
777
778                 static NumberFormatter userFormatProvider;
779
780                 private static NumberFormatter GetInstance (IFormatProvider fp)
781                 {
782                         if (fp != null) {
783                                 if (userFormatProvider == null) {
784                                         Interlocked.CompareExchange (ref userFormatProvider, new NumberFormatter (null), null);
785                                 }
786
787                                 return userFormatProvider;
788                         }
789
790                         NumberFormatter res = threadNumberFormatter;
791                         threadNumberFormatter = null;
792                         if (res == null)
793                                 return new NumberFormatter (Thread.CurrentThread);
794                         res.CurrentCulture = Thread.CurrentThread.CurrentCulture;
795                         return res;
796                 }
797
798                 private void Release()
799                 {
800                         if (this != userFormatProvider)
801                                 threadNumberFormatter = this;
802                 }
803
804                 public static string NumberToString (string format, sbyte value, IFormatProvider fp)
805                 {
806                         NumberFormatter inst = GetInstance (fp);
807                         inst.Init (format, value, Int8DefPrecision);
808                         string res = inst.IntegerToString (format, fp);
809                         inst.Release();
810                         return res;
811                 }
812
813                 public static string NumberToString (string format, byte value, IFormatProvider fp)
814                 {
815                         NumberFormatter inst = GetInstance (fp);
816                         inst.Init (format, value, UInt8DefPrecision);
817                         string res = inst.IntegerToString (format, fp);
818                         inst.Release();
819                         return res;
820                 }
821
822                 public static string NumberToString (string format, ushort value, IFormatProvider fp)
823                 {
824                         NumberFormatter inst = GetInstance (fp);
825                         inst.Init (format, value, Int16DefPrecision);
826                         string res = inst.IntegerToString (format, fp);
827                         inst.Release();
828                         return res;
829                 }
830
831                 public static string NumberToString (string format, short value, IFormatProvider fp)
832                 {
833                         NumberFormatter inst = GetInstance (fp);
834                         inst.Init (format, value, UInt16DefPrecision);
835                         string res = inst.IntegerToString (format, fp);
836                         inst.Release();
837                         return res;
838                 }
839
840                 public static string NumberToString (string format, uint value, IFormatProvider fp)
841                 {
842                         NumberFormatter inst = GetInstance (fp);
843                         inst.Init (format, value, Int32DefPrecision);
844                         string res = inst.IntegerToString (format, fp);
845                         inst.Release();
846                         return res;
847                 }
848
849                 public static string NumberToString (string format, int value, IFormatProvider fp)
850                 {
851                         NumberFormatter inst = GetInstance (fp);
852                         inst.Init (format, value, UInt32DefPrecision);
853                         string res = inst.IntegerToString (format, fp);
854                         inst.Release();
855                         return res;
856                 }
857
858                 public static string NumberToString (string format, ulong value, IFormatProvider fp)
859                 {
860                         NumberFormatter inst = GetInstance (fp);
861                         inst.Init (format, value);
862                         string res = inst.IntegerToString (format, fp);
863                         inst.Release();
864                         return res;
865                 }
866
867                 public static string NumberToString (string format, long value, IFormatProvider fp)
868                 {
869                         NumberFormatter inst = GetInstance (fp);
870                         inst.Init (format, value);
871                         string res = inst.IntegerToString (format, fp);
872                         inst.Release();
873                         return res;
874                 }
875
876                 public static string NumberToString (string format, float value, IFormatProvider fp)
877                 {
878                         NumberFormatter inst = GetInstance (fp);
879                         inst.Init (format, value, SingleDefPrecision);
880                         NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
881                         string res;
882                         if (inst._NaN)
883                                 res = nfi.NaNSymbol;
884                         else if (inst._infinity)
885                                 if (inst._positive)
886                                         res = nfi.PositiveInfinitySymbol;
887                                 else
888                                         res = nfi.NegativeInfinitySymbol;
889                         else if (inst._specifier == 'R')
890                                 res = inst.FormatRoundtrip (value, nfi);
891                         else
892                                 res = inst.NumberToString (format, nfi);
893                         inst.Release();
894                         return res;
895                 }
896
897                 public static string NumberToString (string format, double value, IFormatProvider fp)
898                 {
899                         NumberFormatter inst = GetInstance (fp);
900                         inst.Init (format, value, DoubleDefPrecision);
901                         NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
902                         string res;
903                         if (inst._NaN)
904                                 res = nfi.NaNSymbol;
905                         else if (inst._infinity)
906                                 if (inst._positive)
907                                         res = nfi.PositiveInfinitySymbol;
908                                 else
909                                         res = nfi.NegativeInfinitySymbol;
910                         else if (inst._specifier == 'R')
911                                 res = inst.FormatRoundtrip (value, nfi);
912                         else
913                                 res = inst.NumberToString (format, nfi);
914                         inst.Release();
915                         return res;
916                 }
917
918                 public static string NumberToString (string format, decimal value, IFormatProvider fp)
919                 {
920                         NumberFormatter inst = GetInstance (fp);
921                         inst.Init (format, value);
922                         string res = inst.NumberToString (format, inst.GetNumberFormatInstance (fp));
923                         inst.Release();
924                         return res;
925                 }
926
927                 public static string NumberToString (uint value, IFormatProvider fp)
928                 {
929                         if (value >= HundredMillion)
930                                 return NumberToString (null, value, fp);
931
932                         NumberFormatter inst = GetInstance (fp);
933                         string res = inst.FastIntegerToString ((int)value, fp);
934                         inst.Release();
935                         return res;
936                 }
937
938                 public static string NumberToString (int value, IFormatProvider fp)
939                 {
940                         if (value >= HundredMillion || value <= -HundredMillion)
941                                 return NumberToString (null, value, fp);
942
943                         NumberFormatter inst = GetInstance (fp);
944                         string res = inst.FastIntegerToString (value, fp);
945                         inst.Release();
946                         return res;
947                 }
948
949                 public static string NumberToString (ulong value, IFormatProvider fp)
950                 {
951                         if (value >= HundredMillion)
952                                 return NumberToString (null, value, fp);
953
954                         NumberFormatter inst = GetInstance (fp);
955                         string res = inst.FastIntegerToString ((int)value, fp);
956                         inst.Release();
957                         return res;
958                 }
959
960                 public static string NumberToString (long value, IFormatProvider fp)
961                 {
962                         if (value >= HundredMillion || value <= -HundredMillion)
963                                 return NumberToString (null, value, fp);
964
965                         NumberFormatter inst = GetInstance (fp);
966                         string res = inst.FastIntegerToString ((int)value, fp);
967                         inst.Release();
968                         return res;
969                 }
970
971                 public static string NumberToString (float value, IFormatProvider fp)
972                 {
973                         NumberFormatter inst = GetInstance (fp);
974                         inst.Init (null, value, SingleDefPrecision);
975                         NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
976                         string res;
977                         if (inst._NaN)
978                                 res = nfi.NaNSymbol;
979                         else if (inst._infinity)
980                                 if (inst._positive)
981                                         res = nfi.PositiveInfinitySymbol;
982                                 else
983                                         res = nfi.NegativeInfinitySymbol;
984                         else
985                                 res = inst.FormatGeneral (-1, nfi);
986                         inst.Release();
987                         return res;
988                 }
989
990                 public static string NumberToString (double value, IFormatProvider fp)
991                 {
992                         NumberFormatter inst = GetInstance (fp);
993                         NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
994                         inst.Init (null, value, DoubleDefPrecision);
995                         string res;
996                         if (inst._NaN)
997                                 res = nfi.NaNSymbol;
998                         else if (inst._infinity)
999                                 if (inst._positive)
1000                                         res = nfi.PositiveInfinitySymbol;
1001                                 else
1002                                         res = nfi.NegativeInfinitySymbol;
1003                         else
1004                                 res = inst.FormatGeneral (-1, nfi);
1005                         inst.Release();
1006                         return res;
1007                 }
1008
1009                 private string FastIntegerToString (int value, IFormatProvider fp)
1010                 {
1011                         if (value < 0) {
1012                                 string sign = GetNumberFormatInstance(fp).NegativeSign;
1013                                 ResetCharBuf (8 + sign.Length);
1014                                 value = -value;
1015                                 Append (sign);
1016                         }
1017                         else
1018                                 ResetCharBuf (8);
1019
1020                         if (value >= 10000) {
1021                                 int v = value / 10000;
1022                                 FastAppendDigits (v, false);
1023                                 FastAppendDigits (value - v * 10000, true);
1024                         }
1025                         else
1026                                 FastAppendDigits (value, false);
1027
1028                         return new string (_cbuf, 0, _ind);
1029                 }
1030
1031                 private string IntegerToString (string format, IFormatProvider fp)
1032                 {
1033                         NumberFormatInfo nfi = GetNumberFormatInstance (fp);
1034                         switch (_specifier) {
1035                         case 'C':
1036                                 return FormatCurrency (_precision, nfi);
1037                         case 'D':
1038                                 return FormatDecimal (_precision, nfi);
1039                         case 'E':
1040                                 return FormatExponential (_precision, nfi);
1041                         case 'F':
1042                                 return FormatFixedPoint (_precision, nfi);
1043                         case 'G':
1044                                 if (_precision <= 0)
1045                                         return FormatDecimal (-1, nfi);
1046                                 return FormatGeneral (_precision, nfi);
1047                         case 'N':
1048                                 return FormatNumber (_precision, nfi);
1049                         case 'P':
1050                                 return FormatPercent (_precision, nfi);
1051                         case 'X':
1052                                 return FormatHexadecimal (_precision);
1053                         default:
1054                                 if (_isCustomFormat)
1055                                         return FormatCustom (format, nfi);
1056                                 throw new FormatException ("The specified format '" + format + "' is invalid");
1057                         }
1058                 }
1059
1060                 private string NumberToString (string format, NumberFormatInfo nfi)
1061                 {
1062                         switch (_specifier) {
1063                         case 'C':
1064                                 return FormatCurrency (_precision, nfi);
1065                         case 'E':
1066                                 return FormatExponential (_precision, nfi);
1067                         case 'F':
1068                                 return FormatFixedPoint (_precision, nfi);
1069                         case 'G':
1070                                 return FormatGeneral (_precision, nfi);
1071                         case 'N':
1072                                 return FormatNumber (_precision, nfi);
1073                         case 'P':
1074                                 return FormatPercent (_precision, nfi);
1075                         case 'X':
1076                         default:
1077                                 if (_isCustomFormat)
1078                                         return FormatCustom (format, nfi);
1079                                 throw new FormatException ("The specified format '" + format + "' is invalid");
1080                         }
1081                 }
1082
1083                 public string FormatCurrency (int precision, NumberFormatInfo nfi)
1084                 {
1085                         precision = (precision >= 0 ? precision : nfi.CurrencyDecimalDigits);
1086                         RoundDecimal (precision);
1087                         ResetCharBuf (IntegerDigits * 2 + precision * 2 + 16);
1088
1089                         if (_positive) {
1090                                 switch (nfi.CurrencyPositivePattern) {
1091                                 case 0:
1092                                         Append (nfi.CurrencySymbol);
1093                                         break;
1094                                 case 2:
1095                                         Append (nfi.CurrencySymbol);
1096                                         Append (' ');
1097                                         break;
1098                                 }
1099                         }
1100                         else {
1101                                 switch (nfi.CurrencyNegativePattern) {
1102                                 case 0:
1103                                         Append ('(');
1104                                         Append (nfi.CurrencySymbol);
1105                                         break;
1106                                 case 1:
1107                                         Append (nfi.NegativeSign);
1108                                         Append (nfi.CurrencySymbol);
1109                                         break;
1110                                 case 2:
1111                                         Append (nfi.CurrencySymbol);
1112                                         Append (nfi.NegativeSign);
1113                                         break;
1114                                 case 3:
1115                                         Append (nfi.CurrencySymbol);
1116                                         break;
1117                                 case 4:
1118                                         Append ('(');
1119                                         break;
1120                                 case 5:
1121                                         Append (nfi.NegativeSign);
1122                                         break;
1123                                 case 8:
1124                                         Append (nfi.NegativeSign);
1125                                         break;
1126                                 case 9:
1127                                         Append (nfi.NegativeSign);
1128                                         Append (nfi.CurrencySymbol);
1129                                         Append (' ');
1130                                         break;
1131                                 case 11:
1132                                         Append (nfi.CurrencySymbol);
1133                                         Append (' ');
1134                                         break;
1135                                 case 12:
1136                                         Append (nfi.CurrencySymbol);
1137                                         Append (' ');
1138                                         Append (nfi.NegativeSign);
1139                                         break;
1140                                 case 14:
1141                                         Append ('(');
1142                                         Append (nfi.CurrencySymbol);
1143                                         Append (' ');
1144                                         break;
1145                                 case 15:
1146                                         Append ('(');
1147                                         break;
1148                                 }
1149                         }
1150
1151                         AppendIntegerStringWithGroupSeparator (nfi.RawCurrencyGroupSizes, nfi.CurrencyGroupSeparator);
1152
1153                         if (precision > 0) {
1154                                 Append (nfi.CurrencyDecimalSeparator);
1155                                 AppendDecimalString (precision);
1156                         }
1157
1158                         if (_positive) {
1159                                 switch (nfi.CurrencyPositivePattern) {
1160                                 case 1:
1161                                         Append (nfi.CurrencySymbol);
1162                                         break;
1163                                 case 3:
1164                                         Append (' ');
1165                                         Append (nfi.CurrencySymbol);
1166                                         break;
1167                                 }
1168                         }
1169                         else {
1170                                 switch (nfi.CurrencyNegativePattern) {
1171                                 case 0:
1172                                         Append (')');
1173                                         break;
1174                                 case 3:
1175                                         Append (nfi.NegativeSign);
1176                                         break;
1177                                 case 4:
1178                                         Append (nfi.CurrencySymbol);
1179                                         Append (')');
1180                                         break;
1181                                 case 5:
1182                                         Append (nfi.CurrencySymbol);
1183                                         break;
1184                                 case 6:
1185                                         Append (nfi.NegativeSign);
1186                                         Append (nfi.CurrencySymbol);
1187                                         break;
1188                                 case 7:
1189                                         Append (nfi.CurrencySymbol);
1190                                         Append (nfi.NegativeSign);
1191                                         break;
1192                                 case 8:
1193                                         Append (' ');
1194                                         Append (nfi.CurrencySymbol);
1195                                         break;
1196                                 case 10:
1197                                         Append (' ');
1198                                         Append (nfi.CurrencySymbol);
1199                                         Append (nfi.NegativeSign);
1200                                         break;
1201                                 case 11:
1202                                         Append (nfi.NegativeSign);
1203                                         break;
1204                                 case 13:
1205                                         Append (nfi.NegativeSign);
1206                                         Append (' ');
1207                                         Append (nfi.CurrencySymbol);
1208                                         break;
1209                                 case 14:
1210                                         Append (')');
1211                                         break;
1212                                 case 15:
1213                                         Append (' ');
1214                                         Append (nfi.CurrencySymbol);
1215                                         Append (')');
1216                                         break;
1217                                 }
1218                         }
1219
1220                         return new string (_cbuf, 0, _ind);
1221                 }
1222
1223                 private string FormatDecimal (int precision, NumberFormatInfo nfi)
1224                 {
1225                         if (precision < _digitsLen)
1226                                 precision = _digitsLen;
1227                         if (precision == 0)
1228                                 return "0";
1229
1230                         ResetCharBuf (precision + 1);
1231                         if (!_positive)
1232                                 Append (nfi.NegativeSign);
1233                         AppendDigits (0, precision);
1234
1235                         return new string (_cbuf, 0, _ind);
1236                 }
1237
1238 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1239                 unsafe
1240 #endif
1241                 private string FormatHexadecimal (int precision)
1242                 {
1243                         int size = Math.Max (precision, _decPointPos);
1244 #if UNSAFE_TABLES
1245                         char* digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1246 #else
1247                         char[] digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1248 #endif
1249                         ResetCharBuf (size);
1250                         _ind = size;
1251                         ulong val = _val1 | ((ulong)_val2 << 32);
1252                         while (size > 0) {
1253                                 _cbuf [--size] = digits [val & 0xf];
1254                                 val >>= 4;
1255                         }
1256                         return new string (_cbuf, 0, _ind);
1257                 }
1258
1259                 public string FormatFixedPoint (int precision, NumberFormatInfo nfi)
1260                 {
1261                         if (precision == -1)
1262                                 precision = nfi.NumberDecimalDigits;
1263
1264                         RoundDecimal (precision);
1265
1266                         ResetCharBuf (IntegerDigits + precision + 2);
1267
1268                         if (!_positive)
1269                                 Append (nfi.NegativeSign);
1270
1271                         AppendIntegerString (IntegerDigits);
1272
1273                         if (precision > 0) {
1274                                 Append (nfi.NumberDecimalSeparator);
1275                                 AppendDecimalString (precision);
1276                         }
1277
1278                         return new string (_cbuf, 0, _ind);
1279                 }
1280
1281                 private string FormatRoundtrip (double origval, NumberFormatInfo nfi)
1282                 {
1283                         NumberFormatter nfc = GetClone ();
1284                         if (origval >= MinRoundtripVal && origval <= MaxRoundtripVal) {
1285                                 string shortRep = FormatGeneral (_defPrecision, nfi);
1286                                 if (origval == Double.Parse (shortRep, nfi))
1287                                         return shortRep;
1288                         }
1289                         return nfc.FormatGeneral (_defPrecision + 2, nfi);
1290                 }
1291
1292                 private string FormatRoundtrip (float origval, NumberFormatInfo nfi)
1293                 {
1294                         NumberFormatter nfc = GetClone ();
1295                         string shortRep = FormatGeneral (_defPrecision, nfi);
1296                         // Check roundtrip only for "normal" double values.
1297                         if (origval == Single.Parse (shortRep, nfi))
1298                                 return shortRep;
1299                         return nfc.FormatGeneral (_defPrecision + 2, nfi);
1300                 }
1301
1302                 private string FormatGeneral (int precision, NumberFormatInfo nfi)
1303                 {
1304                         bool enableExp;
1305                         if (precision == -1) {
1306                                 enableExp = IsFloatingSource;
1307                                 precision = _defPrecision;
1308                         }
1309                         else {
1310                                 enableExp = true;
1311                                 if (precision == 0)
1312                                         precision = _defPrecision;
1313                                 RoundPos (precision);
1314                         }
1315
1316                         int intDigits = _decPointPos;
1317                         int digits = _digitsLen;
1318                         int decDigits = digits - intDigits;
1319
1320                         if ((intDigits > precision || intDigits <= -4) && enableExp)
1321                                 return FormatExponential (digits - 1, nfi, 2);
1322
1323                         if (decDigits < 0)
1324                                 decDigits = 0;
1325                         if (intDigits < 0)
1326                                 intDigits = 0;
1327                         ResetCharBuf (decDigits + intDigits + 3);
1328
1329                         if (!_positive)
1330                                 Append (nfi.NegativeSign);
1331
1332                         if (intDigits == 0)
1333                                 Append ('0');
1334                         else
1335                                 AppendDigits (digits - intDigits, digits);
1336
1337                         if (decDigits > 0) {
1338                                 Append (nfi.NumberDecimalSeparator);
1339                                 AppendDigits (0, decDigits);
1340                         }
1341
1342                         return new string (_cbuf, 0, _ind);
1343                 }
1344
1345                 public string FormatNumber (int precision, NumberFormatInfo nfi)
1346                 {
1347                         precision = (precision >= 0 ? precision : nfi.NumberDecimalDigits);
1348                         ResetCharBuf (IntegerDigits * 3 + precision);
1349                         RoundDecimal (precision);
1350
1351                         if (!_positive) {
1352                                 switch (nfi.NumberNegativePattern) {
1353                                 case 0:
1354                                         Append ('(');
1355                                         break;
1356                                 case 1:
1357                                         Append (nfi.NegativeSign);
1358                                         break;
1359                                 case 2:
1360                                         Append (nfi.NegativeSign);
1361                                         Append (' ');
1362                                         break;
1363                                 }
1364                         }
1365
1366                         AppendIntegerStringWithGroupSeparator (nfi.RawNumberGroupSizes, nfi.NumberGroupSeparator);
1367
1368                         if (precision > 0) {
1369                                 Append (nfi.NumberDecimalSeparator);
1370                                 AppendDecimalString (precision);
1371                         }
1372
1373                         if (!_positive) {
1374                                 switch (nfi.NumberNegativePattern) {
1375                                 case 0:
1376                                         Append (')');
1377                                         break;
1378                                 case 3:
1379                                         Append (nfi.NegativeSign);
1380                                         break;
1381                                 case 4:
1382                                         Append (' ');
1383                                         Append (nfi.NegativeSign);
1384                                         break;
1385                                 }
1386                         }
1387
1388                         return new string (_cbuf, 0, _ind);
1389                 }
1390
1391                 public string FormatPercent (int precision, NumberFormatInfo nfi)
1392                 {
1393                         precision = (precision >= 0 ? precision : nfi.PercentDecimalDigits);
1394                         Multiply10(2);
1395                         RoundDecimal (precision);
1396                         ResetCharBuf (IntegerDigits * 2 + precision + 16);
1397
1398                         if (_positive) {
1399                                 if (nfi.PercentPositivePattern == 2)
1400                                         Append (nfi.PercentSymbol);
1401                         }
1402                         else {
1403                                 switch (nfi.PercentNegativePattern) {
1404                                 case 0:
1405                                         Append (nfi.NegativeSign);
1406                                         break;
1407                                 case 1:
1408                                         Append (nfi.NegativeSign);
1409                                         break;
1410                                 case 2:
1411                                         Append (nfi.NegativeSign);
1412                                         Append (nfi.PercentSymbol);
1413                                         break;
1414                                 }
1415                         }
1416
1417                         AppendIntegerStringWithGroupSeparator (nfi.RawPercentGroupSizes, nfi.PercentGroupSeparator);
1418
1419                         if (precision > 0) {
1420                                 Append (nfi.PercentDecimalSeparator);
1421                                 AppendDecimalString (precision);
1422                         }
1423
1424                         if (_positive) {
1425                                 switch (nfi.PercentPositivePattern) {
1426                                 case 0:
1427                                         Append (' ');
1428                                         Append (nfi.PercentSymbol);
1429                                         break;
1430                                 case 1:
1431                                         Append (nfi.PercentSymbol);
1432                                         break;
1433                                 }
1434                         }
1435                         else {
1436                                 switch (nfi.PercentNegativePattern) {
1437                                 case 0:
1438                                         Append (' ');
1439                                         Append (nfi.PercentSymbol);
1440                                         break;
1441                                 case 1:
1442                                         Append (nfi.PercentSymbol);
1443                                         break;
1444                                 }
1445                         }
1446
1447                         return new string (_cbuf, 0, _ind);
1448                 }
1449
1450                 public string FormatExponential (int precision, NumberFormatInfo nfi)
1451                 {
1452                         if (precision == -1)
1453                                 precision = DefaultExpPrecision;
1454
1455                         RoundPos (precision + 1);
1456                         return FormatExponential (precision, nfi, 3);
1457                 }
1458
1459                 private string FormatExponential (int precision, NumberFormatInfo nfi, int expDigits)
1460                 {
1461                         int decDigits = _decPointPos;
1462                         int digits = _digitsLen;
1463                         int exponent = decDigits - 1;
1464                         decDigits = _decPointPos = 1;
1465
1466                         ResetCharBuf (precision + 8);
1467
1468                         if (!_positive)
1469                                 Append (nfi.NegativeSign);
1470
1471                         AppendOneDigit (digits - 1);
1472
1473                         if (precision > 0) {
1474                                 Append (nfi.NumberDecimalSeparator);
1475                                 AppendDigits (digits - precision - 1, digits - _decPointPos);
1476                         }
1477
1478                         AppendExponent (nfi, exponent, expDigits);
1479
1480                         return new string (_cbuf, 0, _ind);
1481                 }
1482
1483                 public string FormatCustom (string format, NumberFormatInfo nfi)
1484                 {
1485                         bool p = _positive;
1486                         int offset = 0;
1487                         int length = 0;
1488                         CustomInfo.GetActiveSection (format, ref p, IsZero, ref offset, ref length);
1489                         if (length == 0)
1490                                 return _positive ? string.Empty : nfi.NegativeSign;
1491                         _positive = p;
1492
1493                         CustomInfo info = CustomInfo.Parse (format, offset, length, nfi);
1494 #if false
1495                         Console.WriteLine ("Format : {0}",format);
1496                         Console.WriteLine ("DecimalDigits : {0}",info.DecimalDigits);
1497                         Console.WriteLine ("DecimalPointPos : {0}",info.DecimalPointPos);
1498                         Console.WriteLine ("DecimalTailSharpDigits : {0}",info.DecimalTailSharpDigits);
1499                         Console.WriteLine ("IntegerDigits : {0}",info.IntegerDigits);
1500                         Console.WriteLine ("IntegerHeadSharpDigits : {0}",info.IntegerHeadSharpDigits);
1501                         Console.WriteLine ("IntegerHeadPos : {0}",info.IntegerHeadPos);
1502                         Console.WriteLine ("UseExponent : {0}",info.UseExponent);
1503                         Console.WriteLine ("ExponentDigits : {0}",info.ExponentDigits);
1504                         Console.WriteLine ("ExponentTailSharpDigits : {0}",info.ExponentTailSharpDigits);
1505                         Console.WriteLine ("ExponentNegativeSignOnly : {0}",info.ExponentNegativeSignOnly);
1506                         Console.WriteLine ("DividePlaces : {0}",info.DividePlaces);
1507                         Console.WriteLine ("Percents : {0}",info.Percents);
1508                         Console.WriteLine ("Permilles : {0}",info.Permilles);
1509 #endif
1510                         StringBuilder sb_int = new StringBuilder (info.IntegerDigits * 2);
1511                         StringBuilder sb_dec = new StringBuilder (info.DecimalDigits * 2);
1512                         StringBuilder sb_exp = (info.UseExponent ? new StringBuilder (info.ExponentDigits * 2) : null);
1513
1514                         int diff = 0;
1515                         if (info.Percents > 0)
1516                                 Multiply10(2 * info.Percents);
1517                         if (info.Permilles > 0)
1518                                 Multiply10(3 * info.Permilles);
1519                         if (info.DividePlaces > 0)
1520                                 Divide10(info.DividePlaces);
1521
1522                         bool expPositive = true;
1523                         if (info.UseExponent && (info.DecimalDigits > 0 || info.IntegerDigits > 0)) {
1524                                 if (!IsZero) {
1525                                         RoundPos (info.DecimalDigits + info.IntegerDigits);
1526                                         diff -= _decPointPos - info.IntegerDigits;
1527                                         _decPointPos = info.IntegerDigits;
1528                                 }
1529
1530                                 expPositive = diff <= 0;
1531                                 AppendNonNegativeNumber (sb_exp, diff < 0 ? -diff : diff);
1532                         }
1533                         else
1534                                 RoundDecimal (info.DecimalDigits);
1535
1536                         if (info.IntegerDigits != 0 || !IsZeroInteger)
1537                                 AppendIntegerString (IntegerDigits, sb_int);
1538
1539                         AppendDecimalString (DecimalDigits, sb_dec);
1540
1541                         if (info.UseExponent) {
1542                                 if (info.DecimalDigits <= 0 && info.IntegerDigits <= 0)
1543                                         _positive = true;
1544
1545                                 if (sb_int.Length < info.IntegerDigits)
1546                                         sb_int.Insert (0, "0", info.IntegerDigits - sb_int.Length);
1547
1548                                 while (sb_exp.Length < info.ExponentDigits - info.ExponentTailSharpDigits)
1549                                         sb_exp.Insert (0, '0');
1550
1551                                 if (expPositive && !info.ExponentNegativeSignOnly)
1552                                         sb_exp.Insert (0, nfi.PositiveSign);
1553                                 else if (!expPositive)
1554                                         sb_exp.Insert (0, nfi.NegativeSign);
1555                         }
1556                         else {
1557                                 if (sb_int.Length < info.IntegerDigits - info.IntegerHeadSharpDigits)
1558                                         sb_int.Insert (0, "0", info.IntegerDigits - info.IntegerHeadSharpDigits - sb_int.Length);
1559                                 if (info.IntegerDigits == info.IntegerHeadSharpDigits && IsZeroOnly (sb_int))
1560                                         sb_int.Remove (0, sb_int.Length);
1561                         }
1562
1563                         ZeroTrimEnd (sb_dec, true);
1564                         while (sb_dec.Length < info.DecimalDigits - info.DecimalTailSharpDigits)
1565                                 sb_dec.Append ('0');
1566                         if (sb_dec.Length > info.DecimalDigits)
1567                                 sb_dec.Remove (info.DecimalDigits, sb_dec.Length - info.DecimalDigits);
1568
1569                         return info.Format (format, offset, length, nfi, _positive, sb_int, sb_dec, sb_exp);
1570                 }
1571                 #endregion public number formatting methods
1572
1573                 #region StringBuilder formatting helpers
1574
1575                 private static void ZeroTrimEnd (StringBuilder sb, bool canEmpty)
1576                 {
1577                         int len = 0;
1578                         for (int i = sb.Length - 1; (canEmpty ? i >= 0 : i > 0); i--) {
1579                                 if (sb [i] != '0')
1580                                         break;
1581                                 len++;
1582                         }
1583
1584                         if (len > 0)
1585                                 sb.Remove (sb.Length - len, len);
1586                 }
1587
1588                 private static bool IsZeroOnly (StringBuilder sb)
1589                 {
1590                         for (int i = 0; i < sb.Length; i++)
1591                                 if (char.IsDigit (sb [i]) && sb [i] != '0')
1592                                         return false;
1593                         return true;
1594                 }
1595
1596                 private static void AppendNonNegativeNumber (StringBuilder sb, int v)
1597                 {
1598                         if (v < 0)
1599                                 throw new ArgumentException ();
1600
1601                         int i = ScaleOrder (v) - 1;
1602                         do {
1603                                 int n = v / (int)GetTenPowerOf (i);
1604                                 sb.Append ((char)('0' | n));
1605                                 v -= (int)GetTenPowerOf (i--) * n;
1606                         } while (i >= 0);
1607                 }
1608
1609                 #endregion StringBuilder formatting helpers
1610
1611                 #region Append helpers
1612
1613                 private void AppendIntegerString (int minLength, StringBuilder sb)
1614                 {
1615                         if (_decPointPos <= 0) {
1616                                 sb.Append ('0', minLength);
1617                                 return;
1618                         }
1619
1620                         if (_decPointPos < minLength)
1621                                 sb.Append ('0', minLength - _decPointPos);
1622
1623                         AppendDigits (_digitsLen - _decPointPos, _digitsLen, sb);
1624                 }
1625
1626                 private void AppendIntegerString (int minLength)
1627                 {
1628                         if (_decPointPos <= 0) {
1629                                 Append ('0', minLength);
1630                                 return;
1631                         }
1632
1633                         if (_decPointPos < minLength)
1634                                 Append ('0', minLength - _decPointPos);
1635
1636                         AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1637                 }
1638
1639                 private void AppendDecimalString (int precision, StringBuilder sb)
1640                 {
1641                         AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos, sb);
1642                 }
1643
1644                 private void AppendDecimalString (int precision)
1645                 {
1646                         AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos);
1647                 }
1648
1649                 private void AppendIntegerStringWithGroupSeparator (int[] groups, string groupSeparator)
1650                 {
1651                         if (IsZeroInteger) {
1652                                 Append ('0');
1653                                 return;
1654                         }
1655
1656                         int total = 0;
1657                         int groupIndex = 0;
1658                         for (int i = 0; i < groups.Length; i++) {
1659                                 total += groups [i];
1660                                 if (total <= _decPointPos)
1661                                         groupIndex = i;
1662                                 else
1663                                         break;
1664                         }
1665
1666                         if (groups.Length > 0 && total > 0) {
1667                                 int counter;
1668                                 int groupSize = groups [groupIndex];
1669                                 int fraction = _decPointPos > total ? _decPointPos - total : 0;
1670                                 if (groupSize == 0) {
1671                                         while (groupIndex >= 0 && groups [groupIndex] == 0)
1672                                                 groupIndex--;
1673
1674                                         groupSize = fraction > 0 ? fraction : groups [groupIndex];
1675                                 }
1676                                 if (fraction == 0)
1677                                         counter = groupSize;
1678                                 else {
1679                                         groupIndex += fraction / groupSize;
1680                                         counter = fraction % groupSize;
1681                                         if (counter == 0)
1682                                                 counter = groupSize;
1683                                         else
1684                                                 groupIndex++;
1685                                 }
1686
1687                                 if (total >= _decPointPos) {
1688                                         int lastGroupSize = groups [0];
1689                                         if (total > lastGroupSize) {
1690                                                 int lastGroupDiff = -(lastGroupSize - _decPointPos);
1691                                                 int lastGroupMod;
1692
1693                                                 if (lastGroupDiff < lastGroupSize)
1694                                                         counter = lastGroupDiff;
1695                                                 else if (lastGroupSize > 0 && (lastGroupMod = _decPointPos % lastGroupSize) > 0)
1696                                                         counter = lastGroupMod;
1697                                         }
1698                                 }
1699                                 
1700                                 for (int i = 0; ;) {
1701                                         if ((_decPointPos - i) <= counter || counter == 0) {
1702                                                 AppendDigits (_digitsLen - _decPointPos, _digitsLen - i);
1703                                                 break;
1704                                         }
1705                                         AppendDigits (_digitsLen - i - counter, _digitsLen - i);
1706                                         i += counter;
1707                                         Append (groupSeparator);
1708                                         if (--groupIndex < groups.Length && groupIndex >= 0)
1709                                                 groupSize = groups [groupIndex];
1710                                         counter = groupSize;
1711                                 }
1712                         }
1713                         else {
1714                                 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1715                         }
1716                 }
1717
1718                 // minDigits is in the range 1..3
1719                 private void AppendExponent (NumberFormatInfo nfi, int exponent, int minDigits)
1720                 {
1721                         if (_specifierIsUpper || _specifier == 'R')
1722                                 Append ('E');
1723                         else
1724                                 Append ('e');
1725
1726                         if (exponent >= 0)
1727                                 Append (nfi.PositiveSign);
1728                         else {
1729                                 Append (nfi.NegativeSign);
1730                                 exponent = -exponent;
1731                         }
1732
1733                         if (exponent == 0)
1734                                 Append ('0', minDigits);
1735                         else if (exponent < 10) {
1736                                 Append ('0', minDigits - 1);
1737                                 Append ((char)('0' | exponent));
1738                         }
1739                         else {
1740                                 uint hexDigit = FastToDecHex (exponent);
1741                                 if (exponent >= 100 || minDigits == 3)
1742                                         Append ((char)('0' | (hexDigit >> 8)));
1743                                 Append ((char)('0' | ((hexDigit >> 4) & 0xf)));
1744                                 Append ((char)('0' | (hexDigit & 0xf)));
1745                         }
1746                 }
1747
1748                 private void AppendOneDigit (int start)
1749                 {
1750                         if (_ind == _cbuf.Length)
1751                                 Resize (_ind + 10);
1752
1753                         start += _offset;
1754                         uint v;
1755                         if (start < 0)
1756                                 v = 0;
1757                         else if (start < 8)
1758                                 v = _val1;
1759                         else if (start < 16)
1760                                 v = _val2;
1761                         else if (start < 24)
1762                                 v = _val3;
1763                         else if (start < 32)
1764                                 v = _val4;
1765                         else
1766                                 v = 0;
1767                         v >>= (start & 0x7) << 2;
1768                         _cbuf [_ind++] = (char)('0' | v & 0xf);
1769                 }
1770
1771 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1772                 unsafe
1773 #endif
1774                 private void FastAppendDigits (int val, bool force)
1775                 {
1776                         int i = _ind;
1777                         int digits;
1778                         if (force || val >= 100) {
1779                                 int v = (val * 5243) >> 19;
1780                                 digits = DecHexDigits [v];
1781                                 if (force || val >= 1000)
1782                                         _cbuf [i++] = (char)('0' | digits >> 4);
1783                                 _cbuf [i++] = (char)('0' | (digits & 0xf));
1784                                 digits = DecHexDigits [val - v * 100];
1785                         }
1786                         else
1787                                 digits = DecHexDigits [val];
1788
1789                         if (force || val >= 10)
1790                                 _cbuf [i++] = (char)('0' | digits >> 4);
1791                         _cbuf [i++] = (char)('0' | (digits & 0xf));
1792                         _ind = i;
1793                 }
1794
1795                 private void AppendDigits (int start, int end)
1796                 {
1797                         if (start >= end)
1798                                 return;
1799
1800                         int i = _ind + (end - start);
1801                         if (i > _cbuf.Length)
1802                                 Resize (i + 10);
1803                         _ind = i;
1804
1805                         end += _offset;
1806                         start += _offset;
1807
1808                         for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1809                                 uint v;
1810                                 if (next == 8)
1811                                         v = _val1;
1812                                 else if (next == 16)
1813                                         v = _val2;
1814                                 else if (next == 24)
1815                                         v = _val3;
1816                                 else if (next == 32)
1817                                         v = _val4;
1818                                 else
1819                                         v = 0;
1820                                 v >>= (start & 0x7) << 2;
1821                                 if (next > end)
1822                                         next = end;
1823
1824                                 _cbuf [--i] = (char)('0' | v & 0xf);
1825                                 switch (next - start) {
1826                                 case 8:
1827                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1828                                         goto case 7;
1829                                 case 7:
1830                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1831                                         goto case 6;
1832                                 case 6:
1833                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1834                                         goto case 5;
1835                                 case 5:
1836                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1837                                         goto case 4;
1838                                 case 4:
1839                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1840                                         goto case 3;
1841                                 case 3:
1842                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1843                                         goto case 2;
1844                                 case 2:
1845                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1846                                         goto case 1;
1847                                 case 1:
1848                                         if (next == end)
1849                                                 return;
1850                                         continue;
1851                                 }
1852                         }
1853                 }
1854
1855                 private void AppendDigits (int start, int end, StringBuilder sb)
1856                 {
1857                         if (start >= end)
1858                                 return;
1859
1860                         int i = sb.Length + (end - start);
1861                         sb.Length = i;
1862
1863                         end += _offset;
1864                         start += _offset;
1865
1866                         for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1867                                 uint v;
1868                                 if (next == 8)
1869                                         v = _val1;
1870                                 else if (next == 16)
1871                                         v = _val2;
1872                                 else if (next == 24)
1873                                         v = _val3;
1874                                 else if (next == 32)
1875                                         v = _val4;
1876                                 else
1877                                         v = 0;
1878                                 v >>= (start & 0x7) << 2;
1879                                 if (next > end)
1880                                         next = end;
1881                                 sb [--i] = (char)('0' | v & 0xf);
1882                                 switch (next - start) {
1883                                 case 8:
1884                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1885                                         goto case 7;
1886                                 case 7:
1887                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1888                                         goto case 6;
1889                                 case 6:
1890                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1891                                         goto case 5;
1892                                 case 5:
1893                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1894                                         goto case 4;
1895                                 case 4:
1896                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1897                                         goto case 3;
1898                                 case 3:
1899                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1900                                         goto case 2;
1901                                 case 2:
1902                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1903                                         goto case 1;
1904                                 case 1:
1905                                         if (next == end)
1906                                                 return;
1907                                         continue;
1908                                 }
1909                         }
1910                 }
1911
1912                 #endregion Append helpers
1913
1914                 #region others
1915
1916                 private void Multiply10(int count)
1917                 {
1918                         if (count <= 0 || _digitsLen == 0)
1919                                 return;
1920
1921                         _decPointPos += count;
1922                 }
1923
1924                 private void Divide10(int count)
1925                 {
1926                         if (count <= 0 || _digitsLen == 0)
1927                                 return;
1928
1929                         _decPointPos -= count;
1930                 }
1931
1932                 private NumberFormatter GetClone ()
1933                 {
1934                         return (NumberFormatter)this.MemberwiseClone ();
1935                 }
1936
1937                 #endregion others
1938
1939                 #region custom
1940
1941                 private class CustomInfo
1942                 {
1943                         public bool UseGroup = false;
1944                         public int DecimalDigits = 0;
1945                         public int DecimalPointPos = -1;
1946                         public int DecimalTailSharpDigits = 0;
1947                         public int IntegerDigits = 0;
1948                         public int IntegerHeadSharpDigits = 0;
1949                         public int IntegerHeadPos = 0;
1950                         public bool UseExponent = false;
1951                         public int ExponentDigits = 0;
1952                         public int ExponentTailSharpDigits = 0;
1953                         public bool ExponentNegativeSignOnly = true;
1954                         public int DividePlaces = 0;
1955                         public int Percents = 0;
1956                         public int Permilles = 0;
1957
1958                         public static void GetActiveSection (string format, ref bool positive, bool zero, ref int offset, ref int length)
1959                         {
1960                                 int[] lens = new int [3];
1961                                 int index = 0;
1962                                 int lastPos = 0;
1963                                 bool quoted = false;
1964
1965                                 for (int i = 0; i < format.Length; i++) {
1966                                         char c = format [i];
1967
1968                                         if (c == '\"' || c == '\'') {
1969                                                 if (i == 0 || format [i - 1] != '\\')
1970                                                         quoted = !quoted;
1971
1972                                                 continue;
1973                                         }
1974
1975                                         if (c == ';' && !quoted && (i == 0 || format [i - 1] != '\\')) {
1976                                                 lens [index++] = i - lastPos;
1977                                                 lastPos = i + 1;
1978                                                 if (index == 3)
1979                                                         break;
1980                                         }
1981                                 }
1982
1983                                 if (index == 0) {
1984                                         offset = 0;
1985                                         length = format.Length;
1986                                         return;
1987                                 }
1988                                 if (index == 1) {
1989                                         if (positive || zero) {
1990                                                 offset = 0;
1991                                                 length = lens [0];
1992                                                 return;
1993                                         }
1994                                         if (lens [0] + 1 < format.Length) {
1995                                                 positive = true;
1996                                                 offset = lens [0] + 1;
1997                                                 length = format.Length - offset;
1998                                                 return;
1999                                         }
2000                                         else {
2001                                                 offset = 0;
2002                                                 length = lens [0];
2003                                                 return;
2004                                         }
2005                                 }
2006                                 if (index == 2) {
2007                                         if (zero) {
2008                                                 offset = lens [0] + lens [1] + 2;
2009                                                 length = format.Length - offset;
2010                                                 return;
2011                                         }
2012                                         if (positive) {
2013                                                 offset = 0;
2014                                                 length = lens [0];
2015                                                 return;
2016                                         }
2017                                         if (lens [1] > 0) {
2018                                                 positive = true;
2019                                                 offset = lens [0] + 1;
2020                                                 length = lens [1];
2021                                                 return;
2022                                         }
2023                                         else {
2024                                                 offset = 0;
2025                                                 length = lens [0];
2026                                                 return;
2027                                         }
2028                                 }
2029                                 if (index == 3) {
2030                                         if (zero) {
2031                                                 offset = lens [0] + lens [1] + 2;
2032                                                 length = lens [2];
2033                                                 return;
2034                                         }
2035                                         if (positive) {
2036                                                 offset = 0;
2037                                                 length = lens [0];
2038                                                 return;
2039                                         }
2040                                         if (lens [1] > 0) {
2041                                                 positive = true;
2042                                                 offset = lens [0] + 1;
2043                                                 length = lens [1];
2044                                                 return;
2045                                         }
2046                                         else {
2047                                                 offset = 0;
2048                                                 length = lens [0];
2049                                                 return;
2050                                         }
2051                                 }
2052
2053                                 throw new ArgumentException ();
2054                         }
2055
2056                         public static CustomInfo Parse (string format, int offset, int length, NumberFormatInfo nfi)
2057                         {
2058                                 char literal = '\0';
2059                                 bool integerArea = true;
2060                                 bool decimalArea = false;
2061                                 bool exponentArea = false;
2062                                 bool sharpContinues = true;
2063
2064                                 CustomInfo info = new CustomInfo ();
2065                                 int groupSeparatorCounter = 0;
2066
2067                                 for (int i = offset; i - offset < length; i++) {
2068                                         char c = format [i];
2069
2070                                         if (c == literal && c != '\0') {
2071                                                 literal = '\0';
2072                                                 continue;
2073                                         }
2074                                         if (literal != '\0')
2075                                                 continue;
2076
2077                                         if (exponentArea && (c != '\0' && c != '0' && c != '#')) {
2078                                                 exponentArea = false;
2079                                                 integerArea = (info.DecimalPointPos < 0);
2080                                                 decimalArea = !integerArea;
2081                                                 i--;
2082                                                 continue;
2083                                         }
2084
2085                                         switch (c) {
2086                                         case '\\':
2087                                                 i++;
2088                                                 continue;
2089                                         case '\'':
2090                                         case '\"':
2091                                                 if (c == '\"' || c == '\'') {
2092                                                         literal = c;
2093                                                 }
2094                                                 continue;
2095                                         case '#':
2096                                                 if (sharpContinues && integerArea)
2097                                                         info.IntegerHeadSharpDigits++;
2098                                                 else if (decimalArea)
2099                                                         info.DecimalTailSharpDigits++;
2100                                                 else if (exponentArea)
2101                                                         info.ExponentTailSharpDigits++;
2102
2103                                                 goto case '0';
2104                                         case '0':
2105                                                 if (c != '#') {
2106                                                         sharpContinues = false;
2107                                                         if (decimalArea)
2108                                                                 info.DecimalTailSharpDigits = 0;
2109                                                         else if (exponentArea)
2110                                                                 info.ExponentTailSharpDigits = 0;
2111                                                 }
2112                                                 if (info.IntegerHeadPos == -1)
2113                                                         info.IntegerHeadPos = i;
2114
2115                                                 if (integerArea) {
2116                                                         info.IntegerDigits++;
2117                                                         if (groupSeparatorCounter > 0)
2118                                                                 info.UseGroup = true;
2119                                                         groupSeparatorCounter = 0;
2120                                                 }
2121                                                 else if (decimalArea)
2122                                                         info.DecimalDigits++;
2123                                                 else if (exponentArea)
2124                                                         info.ExponentDigits++;
2125                                                 break;
2126                                         case 'e':
2127                                         case 'E':
2128                                                 if (info.UseExponent)
2129                                                         break;
2130
2131                                                 info.UseExponent = true;
2132                                                 integerArea = false;
2133                                                 decimalArea = false;
2134                                                 exponentArea = true;
2135                                                 if (i + 1 - offset < length) {
2136                                                         char nc = format [i + 1];
2137                                                         if (nc == '+')
2138                                                                 info.ExponentNegativeSignOnly = false;
2139                                                         if (nc == '+' || nc == '-')
2140                                                                 i++;
2141                                                         else if (nc != '0' && nc != '#') {
2142                                                                 info.UseExponent = false;
2143                                                                 if (info.DecimalPointPos < 0)
2144                                                                         integerArea = true;
2145                                                         }
2146                                                 }
2147
2148                                                 break;
2149                                         case '.':
2150                                                 integerArea = false;
2151                                                 decimalArea = true;
2152                                                 exponentArea = false;
2153                                                 if (info.DecimalPointPos == -1)
2154                                                         info.DecimalPointPos = i;
2155                                                 break;
2156                                         case '%':
2157                                                 info.Percents++;
2158                                                 break;
2159                                         case '\u2030':
2160                                                 info.Permilles++;
2161                                                 break;
2162                                         case ',':
2163                                                 if (integerArea && info.IntegerDigits > 0)
2164                                                         groupSeparatorCounter++;
2165                                                 break;
2166                                         default:
2167                                                 break;
2168                                         }
2169                                 }
2170
2171                                 if (info.ExponentDigits == 0)
2172                                         info.UseExponent = false;
2173                                 else
2174                                         info.IntegerHeadSharpDigits = 0;
2175
2176                                 if (info.DecimalDigits == 0)
2177                                         info.DecimalPointPos = -1;
2178
2179                                 info.DividePlaces += groupSeparatorCounter * 3;
2180
2181                                 return info;
2182                         }
2183
2184                         public string Format (string format, int offset, int length, NumberFormatInfo nfi, bool positive, StringBuilder sb_int, StringBuilder sb_dec, StringBuilder sb_exp)
2185                         {
2186                                 StringBuilder sb = new StringBuilder ();
2187                                 char literal = '\0';
2188                                 bool integerArea = true;
2189                                 bool decimalArea = false;
2190                                 int intSharpCounter = 0;
2191                                 int sb_int_index = 0;
2192                                 int sb_dec_index = 0;
2193
2194                                 int[] groups = nfi.RawNumberGroupSizes;
2195                                 string groupSeparator = nfi.NumberGroupSeparator;
2196                                 int intLen = 0, total = 0, groupIndex = 0, counter = 0, groupSize = 0;
2197                                 if (UseGroup && groups.Length > 0) {
2198                                         intLen = sb_int.Length;
2199                                         for (int i = 0; i < groups.Length; i++) {
2200                                                 total += groups [i];
2201                                                 if (total <= intLen)
2202                                                         groupIndex = i;
2203                                         }
2204                                         groupSize = groups [groupIndex];
2205                                         int fraction = intLen > total ? intLen - total : 0;
2206                                         if (groupSize == 0) {
2207                                                 while (groupIndex >= 0 && groups [groupIndex] == 0)
2208                                                         groupIndex--;
2209
2210                                                 groupSize = fraction > 0 ? fraction : groups [groupIndex];
2211                                         }
2212                                         if (fraction == 0)
2213                                                 counter = groupSize;
2214                                         else {
2215                                                 groupIndex += fraction / groupSize;
2216                                                 counter = fraction % groupSize;
2217                                                 if (counter == 0)
2218                                                         counter = groupSize;
2219                                                 else
2220                                                         groupIndex++;
2221                                         }
2222                                 }
2223                                 else
2224                                         UseGroup = false;
2225
2226                                 for (int i = offset; i - offset < length; i++) {
2227                                         char c = format [i];
2228
2229                                         if (c == literal && c != '\0') {
2230                                                 literal = '\0';
2231                                                 continue;
2232                                         }
2233                                         if (literal != '\0') {
2234                                                 sb.Append (c);
2235                                                 continue;
2236                                         }
2237
2238                                         switch (c) {
2239                                         case '\\':
2240                                                 i++;
2241                                                 if (i - offset < length)
2242                                                         sb.Append (format [i]);
2243                                                 continue;
2244                                         case '\'':
2245                                         case '\"':
2246                                                 if (c == '\"' || c == '\'')
2247                                                         literal = c;
2248                                                 continue;
2249                                         case '#':
2250                                                 goto case '0';
2251                                         case '0':
2252                                                 if (integerArea) {
2253                                                         intSharpCounter++;
2254                                                         if (IntegerDigits - intSharpCounter < sb_int.Length + sb_int_index || c == '0')
2255                                                                 while (IntegerDigits - intSharpCounter + sb_int_index < sb_int.Length) {
2256                                                                         sb.Append (sb_int [sb_int_index++]);
2257                                                                         if (UseGroup && --intLen > 0 && --counter == 0) {
2258                                                                                 sb.Append (groupSeparator);
2259                                                                                 if (--groupIndex < groups.Length && groupIndex >= 0)
2260                                                                                         groupSize = groups [groupIndex];
2261                                                                                 counter = groupSize;
2262                                                                         }
2263                                                                 }
2264                                                         break;
2265                                                 }
2266                                                 else if (decimalArea) {
2267                                                         if (sb_dec_index < sb_dec.Length)
2268                                                                 sb.Append (sb_dec [sb_dec_index++]);
2269                                                         break;
2270                                                 }
2271
2272                                                 sb.Append (c);
2273                                                 break;
2274                                         case 'e':
2275                                         case 'E':
2276                                                 if (sb_exp == null || !UseExponent) {
2277                                                         sb.Append (c);
2278                                                         break;
2279                                                 }
2280
2281                                                 bool flag1 = true;
2282                                                 bool flag2 = false;
2283
2284                                                 int q;
2285                                                 for (q = i + 1; q - offset < length; q++) {
2286                                                         if (format [q] == '0') {
2287                                                                 flag2 = true;
2288                                                                 continue;
2289                                                         }
2290                                                         if (q == i + 1 && (format [q] == '+' || format [q] == '-'))
2291                                                                 continue;
2292                                                         if (!flag2)
2293                                                                 flag1 = false;
2294                                                         break;
2295                                                 }
2296
2297                                                 if (flag1) {
2298                                                         i = q - 1;
2299                                                         integerArea = (DecimalPointPos < 0);
2300                                                         decimalArea = !integerArea;
2301
2302                                                         sb.Append (c);
2303                                                         sb.Append (sb_exp);
2304                                                         sb_exp = null;
2305                                                 }
2306                                                 else
2307                                                         sb.Append (c);
2308
2309                                                 break;
2310                                         case '.':
2311                                                 if (DecimalPointPos == i) {
2312                                                         if (DecimalDigits > 0) {
2313                                                                 while (sb_int_index < sb_int.Length)
2314                                                                         sb.Append (sb_int [sb_int_index++]);
2315                                                         }
2316                                                         if (sb_dec.Length > 0)
2317                                                                 sb.Append (nfi.NumberDecimalSeparator);
2318                                                 }
2319                                                 integerArea = false;
2320                                                 decimalArea = true;
2321                                                 break;
2322                                         case ',':
2323                                                 break;
2324                                         case '%':
2325                                                 sb.Append (nfi.PercentSymbol);
2326                                                 break;
2327                                         case '\u2030':
2328                                                 sb.Append (nfi.PerMilleSymbol);
2329                                                 break;
2330                                         default:
2331                                                 sb.Append (c);
2332                                                 break;
2333                                         }
2334                                 }
2335
2336                                 if (!positive)
2337                                         sb.Insert (0, nfi.NegativeSign);
2338
2339                                 return sb.ToString ();
2340                         }
2341                 }
2342
2343                 #endregion
2344         }
2345 }