Merge remote-tracking branch 'upstream/master'
[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                         char[] newBuf = new char [len];
561                         Array.Copy (_cbuf, newBuf, _ind);
562                         _cbuf = newBuf;
563                 }
564
565                 private void Append (char c)
566                 {
567                         if (_ind == _cbuf.Length)
568                                 Resize (_ind + 10);
569                         _cbuf [_ind++] = c;
570                 }
571
572                 private void Append (char c, int cnt)
573                 {
574                         if (_ind + cnt > _cbuf.Length)
575                                 Resize (_ind + cnt + 10);
576                         while (cnt-- > 0)
577                                 _cbuf [_ind++] = c;
578                 }
579
580                 private void Append (string s)
581                 {
582                         int slen = s.Length;
583                         if (_ind + slen > _cbuf.Length)
584                                 Resize (_ind + slen + 10);
585                         for (int i = 0; i < slen; i++)
586                                 _cbuf [_ind++] = s [i];
587                 }
588
589                 #endregion Inner String Buffer
590
591                 #region Helper properties
592
593                 private NumberFormatInfo GetNumberFormatInstance (IFormatProvider fp)
594                 {
595                         if (_nfi != null && fp == null)
596                                 return _nfi;
597                         return NumberFormatInfo.GetInstance (fp);
598                 }
599
600                 public CultureInfo CurrentCulture {
601                         set {
602                                 if (value != null && value.IsReadOnly)
603                                         _nfi = value.NumberFormat;
604                                 else
605                                         _nfi = null;
606                         }
607                 }
608
609                 private int IntegerDigits {
610                         get { return _decPointPos > 0 ? _decPointPos : 1; }
611                 }
612
613                 private int DecimalDigits {
614                         get { return _digitsLen > _decPointPos ? _digitsLen - _decPointPos : 0; }
615                 }
616
617                 private bool IsFloatingSource {
618                         get { return _defPrecision == DoubleDefPrecision || _defPrecision == SingleDefPrecision; }
619                 }
620
621                 private bool IsZero {
622                         get { return _digitsLen == 0; }
623                 }
624
625                 private bool IsZeroInteger {
626                         get { return _digitsLen == 0 || _decPointPos <= 0; }
627                 }
628
629                 #endregion Helper properties
630
631                 #region Round
632
633                 private void RoundPos (int pos)
634                 {
635                         RoundBits (_digitsLen - pos);
636                 }
637
638                 private bool RoundDecimal (int decimals)
639                 {
640                         return RoundBits (_digitsLen - _decPointPos - decimals);
641                 }
642
643                 private bool RoundBits (int shift)
644                 {
645                         if (shift <= 0)
646                                 return false;
647
648                         if (shift > _digitsLen) {
649                                 _digitsLen = 0;
650                                 _decPointPos = 1;
651                                 _val1 = _val2 = _val3 = _val4 = 0;
652                                 _positive = true;
653                                 return false;
654                         }
655                         shift += _offset;
656                         _digitsLen += _offset;
657                         while (shift > 8) {
658                                 _val1 = _val2;
659                                 _val2 = _val3;
660                                 _val3 = _val4;
661                                 _val4 = 0;
662                                 _digitsLen -= 8;
663                                 shift -= 8;
664                         }
665                         shift = (shift - 1) << 2;
666                         uint v = _val1 >> shift;
667                         uint rem16 = v & 0xf;
668                         _val1 = (v ^ rem16) << shift;
669                         bool res = false;
670                         if (rem16 >= 0x5) {
671                                 _val1 |= 0x99999999 >> (28 - shift);
672                                 AddOneToDecHex ();
673                                 int newlen = DecHexLen ();
674                                 res = newlen != _digitsLen;
675                                 _decPointPos = _decPointPos + newlen - _digitsLen;
676                                 _digitsLen = newlen;
677                         }
678                         RemoveTrailingZeros ();
679                         return res;
680                 }
681
682                 private void RemoveTrailingZeros ()
683                 {
684                         _offset = CountTrailingZeros ();
685                         _digitsLen -= _offset;
686                         if (_digitsLen == 0) {
687                                 _offset = 0;
688                                 _decPointPos = 1;
689                                 _positive = true;
690                         }
691                 }
692
693                 private void AddOneToDecHex ()
694                 {
695                         if (_val1 == 0x99999999) {
696                                 _val1 = 0;
697                                 if (_val2 == 0x99999999) {
698                                         _val2 = 0;
699                                         if (_val3 == 0x99999999) {
700                                                 _val3 = 0;
701                                                 _val4 = AddOneToDecHex (_val4);
702                                         }
703                                         else
704                                                 _val3 = AddOneToDecHex (_val3);
705                                 }
706                                 else
707                                         _val2 = AddOneToDecHex (_val2);
708                         }
709                         else
710                                 _val1 = AddOneToDecHex (_val1);
711                 }
712
713                 // Assume val != 0x99999999
714                 private static uint AddOneToDecHex (uint val)
715                 {
716                         if ((val & 0xffff) == 0x9999)
717                                 if ((val & 0xffffff) == 0x999999)
718                                         if ((val & 0xfffffff) == 0x9999999)
719                                                 return val + 0x06666667;
720                                         else
721                                                 return val + 0x00666667;
722                                 else if ((val & 0xfffff) == 0x99999)
723                                         return val + 0x00066667;
724                                 else
725                                         return val + 0x00006667;
726                         else if ((val & 0xff) == 0x99)
727                                 if ((val & 0xfff) == 0x999)
728                                         return val + 0x00000667;
729                                 else
730                                         return val + 0x00000067;
731                         else if ((val & 0xf) == 0x9)
732                                 return val + 0x00000007;
733                         else
734                                 return val + 1;
735                 }
736
737                 private int CountTrailingZeros ()
738                 {
739                         if (_val1 != 0)
740                                 return CountTrailingZeros (_val1);
741                         if (_val2 != 0)
742                                 return CountTrailingZeros (_val2) + 8;
743                         if (_val3 != 0)
744                                 return CountTrailingZeros (_val3) + 16;
745                         if (_val4 != 0)
746                                 return CountTrailingZeros (_val4) + 24;
747                         return _digitsLen;
748                 }
749
750                 private static int CountTrailingZeros (uint val)
751                 {
752                         if ((val & 0xffff) == 0)
753                                 if ((val & 0xffffff) == 0)
754                                         if ((val & 0xfffffff) == 0)
755                                                 return 7;
756                                         else
757                                                 return 6;
758                                 else if ((val & 0xfffff) == 0)
759                                         return 5;
760                                 else
761                                         return 4;
762                         else if ((val & 0xff) == 0)
763                                 if ((val & 0xfff) == 0)
764                                         return 3;
765                                 else
766                                         return 2;
767                         else if ((val & 0xf) == 0)
768                                 return 1;
769                         else
770                                 return 0;
771                 }
772
773                 #endregion Round
774
775                 #region public number formatting methods
776
777                 [ThreadStatic]
778                 static NumberFormatter threadNumberFormatter;
779
780                 private static NumberFormatter GetInstance()
781                 {
782                         NumberFormatter res = threadNumberFormatter;
783                         threadNumberFormatter = null;
784                         if (res == null)
785                                 return new NumberFormatter (Thread.CurrentThread);
786                         return res;
787                 }
788
789                 private void Release()
790                 {
791                         threadNumberFormatter = this;
792                 }
793
794                 internal static void SetThreadCurrentCulture (CultureInfo culture)
795                 {
796                         if (threadNumberFormatter != null)
797                                 threadNumberFormatter.CurrentCulture = culture;
798                 }
799
800                 public static string NumberToString (string format, sbyte value, IFormatProvider fp)
801                 {
802                         NumberFormatter inst = GetInstance();
803                         inst.Init (format, value, Int8DefPrecision);
804                         string res = inst.IntegerToString (format, fp);
805                         inst.Release();
806                         return res;
807                 }
808
809                 public static string NumberToString (string format, byte value, IFormatProvider fp)
810                 {
811                         NumberFormatter inst = GetInstance();
812                         inst.Init (format, value, UInt8DefPrecision);
813                         string res = inst.IntegerToString (format, fp);
814                         inst.Release();
815                         return res;
816                 }
817
818                 public static string NumberToString (string format, ushort value, IFormatProvider fp)
819                 {
820                         NumberFormatter inst = GetInstance();
821                         inst.Init (format, value, Int16DefPrecision);
822                         string res = inst.IntegerToString (format, fp);
823                         inst.Release();
824                         return res;
825                 }
826
827                 public static string NumberToString (string format, short value, IFormatProvider fp)
828                 {
829                         NumberFormatter inst = GetInstance();
830                         inst.Init (format, value, UInt16DefPrecision);
831                         string res = inst.IntegerToString (format, fp);
832                         inst.Release();
833                         return res;
834                 }
835
836                 public static string NumberToString (string format, uint value, IFormatProvider fp)
837                 {
838                         NumberFormatter inst = GetInstance();
839                         inst.Init (format, value, Int32DefPrecision);
840                         string res = inst.IntegerToString (format, fp);
841                         inst.Release();
842                         return res;
843                 }
844
845                 public static string NumberToString (string format, int value, IFormatProvider fp)
846                 {
847                         NumberFormatter inst = GetInstance();
848                         inst.Init (format, value, UInt32DefPrecision);
849                         string res = inst.IntegerToString (format, fp);
850                         inst.Release();
851                         return res;
852                 }
853
854                 public static string NumberToString (string format, ulong value, IFormatProvider fp)
855                 {
856                         NumberFormatter inst = GetInstance();
857                         inst.Init (format, value);
858                         string res = inst.IntegerToString (format, fp);
859                         inst.Release();
860                         return res;
861                 }
862
863                 public static string NumberToString (string format, long value, IFormatProvider fp)
864                 {
865                         NumberFormatter inst = GetInstance();
866                         inst.Init (format, value);
867                         string res = inst.IntegerToString (format, fp);
868                         inst.Release();
869                         return res;
870                 }
871
872                 public static string NumberToString (string format, float value, IFormatProvider fp)
873                 {
874                         NumberFormatter inst = GetInstance();
875                         inst.Init (format, value, SingleDefPrecision);
876                         NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
877                         string res;
878                         if (inst._NaN)
879                                 res = nfi.NaNSymbol;
880                         else if (inst._infinity)
881                                 if (inst._positive)
882                                         res = nfi.PositiveInfinitySymbol;
883                                 else
884                                         res = nfi.NegativeInfinitySymbol;
885                         else if (inst._specifier == 'R')
886                                 res = inst.FormatRoundtrip (value, nfi);
887                         else
888                                 res = inst.NumberToString (format, nfi);
889                         inst.Release();
890                         return res;
891                 }
892
893                 public static string NumberToString (string format, double value, IFormatProvider fp)
894                 {
895                         NumberFormatter inst = GetInstance();
896                         inst.Init (format, value, DoubleDefPrecision);
897                         NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
898                         string res;
899                         if (inst._NaN)
900                                 res = nfi.NaNSymbol;
901                         else if (inst._infinity)
902                                 if (inst._positive)
903                                         res = nfi.PositiveInfinitySymbol;
904                                 else
905                                         res = nfi.NegativeInfinitySymbol;
906                         else if (inst._specifier == 'R')
907                                 res = inst.FormatRoundtrip (value, nfi);
908                         else
909                                 res = inst.NumberToString (format, nfi);
910                         inst.Release();
911                         return res;
912                 }
913
914                 public static string NumberToString (string format, decimal value, IFormatProvider fp)
915                 {
916                         NumberFormatter inst = GetInstance();
917                         inst.Init (format, value);
918                         string res = inst.NumberToString (format, inst.GetNumberFormatInstance (fp));
919                         inst.Release();
920                         return res;
921                 }
922
923                 public static string NumberToString (uint value, IFormatProvider fp)
924                 {
925                         if (value >= HundredMillion)
926                                 return NumberToString (null, value, fp);
927
928                         NumberFormatter inst = GetInstance();
929                         string res = inst.FastIntegerToString ((int)value, fp);
930                         inst.Release();
931                         return res;
932                 }
933
934                 public static string NumberToString (int value, IFormatProvider fp)
935                 {
936                         if (value >= HundredMillion || value <= -HundredMillion)
937                                 return NumberToString (null, value, fp);
938
939                         NumberFormatter inst = GetInstance();
940                         string res = inst.FastIntegerToString (value, fp);
941                         inst.Release();
942                         return res;
943                 }
944
945                 public static string NumberToString (ulong value, IFormatProvider fp)
946                 {
947                         if (value >= HundredMillion)
948                                 return NumberToString (null, value, fp);
949
950                         NumberFormatter inst = GetInstance();
951                         string res = inst.FastIntegerToString ((int)value, fp);
952                         inst.Release();
953                         return res;
954                 }
955
956                 public static string NumberToString (long value, IFormatProvider fp)
957                 {
958                         if (value >= HundredMillion || value <= -HundredMillion)
959                                 return NumberToString (null, value, fp);
960
961                         NumberFormatter inst = GetInstance();
962                         string res = inst.FastIntegerToString ((int)value, fp);
963                         inst.Release();
964                         return res;
965                 }
966
967                 public static string NumberToString (float value, IFormatProvider fp)
968                 {
969                         NumberFormatter inst = GetInstance();
970                         inst.Init (null, value, SingleDefPrecision);
971                         NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
972                         string res;
973                         if (inst._NaN)
974                                 res = nfi.NaNSymbol;
975                         else if (inst._infinity)
976                                 if (inst._positive)
977                                         res = nfi.PositiveInfinitySymbol;
978                                 else
979                                         res = nfi.NegativeInfinitySymbol;
980                         else
981                                 res = inst.FormatGeneral (-1, nfi);
982                         inst.Release();
983                         return res;
984                 }
985
986                 public static string NumberToString (double value, IFormatProvider fp)
987                 {
988                         NumberFormatter inst = GetInstance();
989                         NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
990                         inst.Init (null, value, DoubleDefPrecision);
991                         string res;
992                         if (inst._NaN)
993                                 res = nfi.NaNSymbol;
994                         else if (inst._infinity)
995                                 if (inst._positive)
996                                         res = nfi.PositiveInfinitySymbol;
997                                 else
998                                         res = nfi.NegativeInfinitySymbol;
999                         else
1000                                 res = inst.FormatGeneral (-1, nfi);
1001                         inst.Release();
1002                         return res;
1003                 }
1004
1005                 private string FastIntegerToString (int value, IFormatProvider fp)
1006                 {
1007                         if (value < 0) {
1008                                 string sign = GetNumberFormatInstance(fp).NegativeSign;
1009                                 ResetCharBuf (8 + sign.Length);
1010                                 value = -value;
1011                                 Append (sign);
1012                         }
1013                         else
1014                                 ResetCharBuf (8);
1015
1016                         if (value >= 10000) {
1017                                 int v = value / 10000;
1018                                 FastAppendDigits (v, false);
1019                                 FastAppendDigits (value - v * 10000, true);
1020                         }
1021                         else
1022                                 FastAppendDigits (value, false);
1023
1024                         return new string (_cbuf, 0, _ind);
1025                 }
1026
1027                 private string IntegerToString (string format, IFormatProvider fp)
1028                 {
1029                         NumberFormatInfo nfi = GetNumberFormatInstance (fp);
1030                         switch (_specifier) {
1031                         case 'C':
1032                                 return FormatCurrency (_precision, nfi);
1033                         case 'D':
1034                                 return FormatDecimal (_precision, nfi);
1035                         case 'E':
1036                                 return FormatExponential (_precision, nfi);
1037                         case 'F':
1038                                 return FormatFixedPoint (_precision, nfi);
1039                         case 'G':
1040                                 if (_precision <= 0)
1041                                         return FormatDecimal (-1, nfi);
1042                                 return FormatGeneral (_precision, nfi);
1043                         case 'N':
1044                                 return FormatNumber (_precision, nfi);
1045                         case 'P':
1046                                 return FormatPercent (_precision, nfi);
1047                         case 'X':
1048                                 return FormatHexadecimal (_precision);
1049                         default:
1050                                 if (_isCustomFormat)
1051                                         return FormatCustom (format, nfi);
1052                                 throw new FormatException ("The specified format '" + format + "' is invalid");
1053                         }
1054                 }
1055
1056                 private string NumberToString (string format, NumberFormatInfo nfi)
1057                 {
1058                         switch (_specifier) {
1059                         case 'C':
1060                                 return FormatCurrency (_precision, nfi);
1061                         case 'E':
1062                                 return FormatExponential (_precision, nfi);
1063                         case 'F':
1064                                 return FormatFixedPoint (_precision, nfi);
1065                         case 'G':
1066                                 return FormatGeneral (_precision, nfi);
1067                         case 'N':
1068                                 return FormatNumber (_precision, nfi);
1069                         case 'P':
1070                                 return FormatPercent (_precision, nfi);
1071                         case 'X':
1072                         default:
1073                                 if (_isCustomFormat)
1074                                         return FormatCustom (format, nfi);
1075                                 throw new FormatException ("The specified format '" + format + "' is invalid");
1076                         }
1077                 }
1078
1079                 public string FormatCurrency (int precision, NumberFormatInfo nfi)
1080                 {
1081                         precision = (precision >= 0 ? precision : nfi.CurrencyDecimalDigits);
1082                         RoundDecimal (precision);
1083                         ResetCharBuf (IntegerDigits * 2 + precision * 2 + 16);
1084
1085                         if (_positive) {
1086                                 switch (nfi.CurrencyPositivePattern) {
1087                                 case 0:
1088                                         Append (nfi.CurrencySymbol);
1089                                         break;
1090                                 case 2:
1091                                         Append (nfi.CurrencySymbol);
1092                                         Append (' ');
1093                                         break;
1094                                 }
1095                         }
1096                         else {
1097                                 switch (nfi.CurrencyNegativePattern) {
1098                                 case 0:
1099                                         Append ('(');
1100                                         Append (nfi.CurrencySymbol);
1101                                         break;
1102                                 case 1:
1103                                         Append (nfi.NegativeSign);
1104                                         Append (nfi.CurrencySymbol);
1105                                         break;
1106                                 case 2:
1107                                         Append (nfi.CurrencySymbol);
1108                                         Append (nfi.NegativeSign);
1109                                         break;
1110                                 case 3:
1111                                         Append (nfi.CurrencySymbol);
1112                                         break;
1113                                 case 4:
1114                                         Append ('(');
1115                                         break;
1116                                 case 5:
1117                                         Append (nfi.NegativeSign);
1118                                         break;
1119                                 case 8:
1120                                         Append (nfi.NegativeSign);
1121                                         break;
1122                                 case 9:
1123                                         Append (nfi.NegativeSign);
1124                                         Append (nfi.CurrencySymbol);
1125                                         Append (' ');
1126                                         break;
1127                                 case 11:
1128                                         Append (nfi.CurrencySymbol);
1129                                         Append (' ');
1130                                         break;
1131                                 case 12:
1132                                         Append (nfi.CurrencySymbol);
1133                                         Append (' ');
1134                                         Append (nfi.NegativeSign);
1135                                         break;
1136                                 case 14:
1137                                         Append ('(');
1138                                         Append (nfi.CurrencySymbol);
1139                                         Append (' ');
1140                                         break;
1141                                 case 15:
1142                                         Append ('(');
1143                                         break;
1144                                 }
1145                         }
1146
1147                         AppendIntegerStringWithGroupSeparator (nfi.RawCurrencyGroupSizes, nfi.CurrencyGroupSeparator);
1148
1149                         if (precision > 0) {
1150                                 Append (nfi.CurrencyDecimalSeparator);
1151                                 AppendDecimalString (precision);
1152                         }
1153
1154                         if (_positive) {
1155                                 switch (nfi.CurrencyPositivePattern) {
1156                                 case 1:
1157                                         Append (nfi.CurrencySymbol);
1158                                         break;
1159                                 case 3:
1160                                         Append (' ');
1161                                         Append (nfi.CurrencySymbol);
1162                                         break;
1163                                 }
1164                         }
1165                         else {
1166                                 switch (nfi.CurrencyNegativePattern) {
1167                                 case 0:
1168                                         Append (')');
1169                                         break;
1170                                 case 3:
1171                                         Append (nfi.NegativeSign);
1172                                         break;
1173                                 case 4:
1174                                         Append (nfi.CurrencySymbol);
1175                                         Append (')');
1176                                         break;
1177                                 case 5:
1178                                         Append (nfi.CurrencySymbol);
1179                                         break;
1180                                 case 6:
1181                                         Append (nfi.NegativeSign);
1182                                         Append (nfi.CurrencySymbol);
1183                                         break;
1184                                 case 7:
1185                                         Append (nfi.CurrencySymbol);
1186                                         Append (nfi.NegativeSign);
1187                                         break;
1188                                 case 8:
1189                                         Append (' ');
1190                                         Append (nfi.CurrencySymbol);
1191                                         break;
1192                                 case 10:
1193                                         Append (' ');
1194                                         Append (nfi.CurrencySymbol);
1195                                         Append (nfi.NegativeSign);
1196                                         break;
1197                                 case 11:
1198                                         Append (nfi.NegativeSign);
1199                                         break;
1200                                 case 13:
1201                                         Append (nfi.NegativeSign);
1202                                         Append (' ');
1203                                         Append (nfi.CurrencySymbol);
1204                                         break;
1205                                 case 14:
1206                                         Append (')');
1207                                         break;
1208                                 case 15:
1209                                         Append (' ');
1210                                         Append (nfi.CurrencySymbol);
1211                                         Append (')');
1212                                         break;
1213                                 }
1214                         }
1215
1216                         return new string (_cbuf, 0, _ind);
1217                 }
1218
1219                 private string FormatDecimal (int precision, NumberFormatInfo nfi)
1220                 {
1221                         if (precision < _digitsLen)
1222                                 precision = _digitsLen;
1223                         if (precision == 0)
1224                                 return "0";
1225
1226                         ResetCharBuf (precision + 1);
1227                         if (!_positive)
1228                                 Append (nfi.NegativeSign);
1229                         AppendDigits (0, precision);
1230
1231                         return new string (_cbuf, 0, _ind);
1232                 }
1233
1234 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1235                 unsafe
1236 #endif
1237                 private string FormatHexadecimal (int precision)
1238                 {
1239                         int size = Math.Max (precision, _decPointPos);
1240 #if UNSAFE_TABLES
1241                         char* digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1242 #else
1243                         char[] digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1244 #endif
1245                         ResetCharBuf (size);
1246                         _ind = size;
1247                         ulong val = _val1 | ((ulong)_val2 << 32);
1248                         while (size > 0) {
1249                                 _cbuf [--size] = digits [val & 0xf];
1250                                 val >>= 4;
1251                         }
1252                         return new string (_cbuf, 0, _ind);
1253                 }
1254
1255                 public string FormatFixedPoint (int precision, NumberFormatInfo nfi)
1256                 {
1257                         if (precision == -1)
1258                                 precision = nfi.NumberDecimalDigits;
1259
1260                         RoundDecimal (precision);
1261
1262                         ResetCharBuf (IntegerDigits + precision + 2);
1263
1264                         if (!_positive)
1265                                 Append (nfi.NegativeSign);
1266
1267                         AppendIntegerString (IntegerDigits);
1268
1269                         if (precision > 0) {
1270                                 Append (nfi.NumberDecimalSeparator);
1271                                 AppendDecimalString (precision);
1272                         }
1273
1274                         return new string (_cbuf, 0, _ind);
1275                 }
1276
1277                 private string FormatRoundtrip (double origval, NumberFormatInfo nfi)
1278                 {
1279                         NumberFormatter nfc = GetClone ();
1280                         if (origval >= MinRoundtripVal && origval <= MaxRoundtripVal) {
1281                                 string shortRep = FormatGeneral (_defPrecision, nfi);
1282                                 if (origval == Double.Parse (shortRep, nfi))
1283                                         return shortRep;
1284                         }
1285                         return nfc.FormatGeneral (_defPrecision + 2, nfi);
1286                 }
1287
1288                 private string FormatRoundtrip (float origval, NumberFormatInfo nfi)
1289                 {
1290                         NumberFormatter nfc = GetClone ();
1291                         string shortRep = FormatGeneral (_defPrecision, nfi);
1292                         // Check roundtrip only for "normal" double values.
1293                         if (origval == Single.Parse (shortRep, nfi))
1294                                 return shortRep;
1295                         return nfc.FormatGeneral (_defPrecision + 2, nfi);
1296                 }
1297
1298                 private string FormatGeneral (int precision, NumberFormatInfo nfi)
1299                 {
1300                         bool enableExp;
1301                         if (precision == -1) {
1302                                 enableExp = IsFloatingSource;
1303                                 precision = _defPrecision;
1304                         }
1305                         else {
1306                                 enableExp = true;
1307                                 if (precision == 0)
1308                                         precision = _defPrecision;
1309                                 RoundPos (precision);
1310                         }
1311
1312                         int intDigits = _decPointPos;
1313                         int digits = _digitsLen;
1314                         int decDigits = digits - intDigits;
1315
1316                         if ((intDigits > precision || intDigits <= -4) && enableExp)
1317                                 return FormatExponential (digits - 1, nfi, 2);
1318
1319                         if (decDigits < 0)
1320                                 decDigits = 0;
1321                         if (intDigits < 0)
1322                                 intDigits = 0;
1323                         ResetCharBuf (decDigits + intDigits + 3);
1324
1325                         if (!_positive)
1326                                 Append (nfi.NegativeSign);
1327
1328                         if (intDigits == 0)
1329                                 Append ('0');
1330                         else
1331                                 AppendDigits (digits - intDigits, digits);
1332
1333                         if (decDigits > 0) {
1334                                 Append (nfi.NumberDecimalSeparator);
1335                                 AppendDigits (0, decDigits);
1336                         }
1337
1338                         return new string (_cbuf, 0, _ind);
1339                 }
1340
1341                 public string FormatNumber (int precision, NumberFormatInfo nfi)
1342                 {
1343                         precision = (precision >= 0 ? precision : nfi.NumberDecimalDigits);
1344                         ResetCharBuf (IntegerDigits * 3 + precision);
1345                         RoundDecimal (precision);
1346
1347                         if (!_positive) {
1348                                 switch (nfi.NumberNegativePattern) {
1349                                 case 0:
1350                                         Append ('(');
1351                                         break;
1352                                 case 1:
1353                                         Append (nfi.NegativeSign);
1354                                         break;
1355                                 case 2:
1356                                         Append (nfi.NegativeSign);
1357                                         Append (' ');
1358                                         break;
1359                                 }
1360                         }
1361
1362                         AppendIntegerStringWithGroupSeparator (nfi.RawNumberGroupSizes, nfi.NumberGroupSeparator);
1363
1364                         if (precision > 0) {
1365                                 Append (nfi.NumberDecimalSeparator);
1366                                 AppendDecimalString (precision);
1367                         }
1368
1369                         if (!_positive) {
1370                                 switch (nfi.NumberNegativePattern) {
1371                                 case 0:
1372                                         Append (')');
1373                                         break;
1374                                 case 3:
1375                                         Append (nfi.NegativeSign);
1376                                         break;
1377                                 case 4:
1378                                         Append (' ');
1379                                         Append (nfi.NegativeSign);
1380                                         break;
1381                                 }
1382                         }
1383
1384                         return new string (_cbuf, 0, _ind);
1385                 }
1386
1387                 public string FormatPercent (int precision, NumberFormatInfo nfi)
1388                 {
1389                         precision = (precision >= 0 ? precision : nfi.PercentDecimalDigits);
1390                         Multiply10(2);
1391                         RoundDecimal (precision);
1392                         ResetCharBuf (IntegerDigits * 2 + precision + 16);
1393
1394                         if (_positive) {
1395                                 if (nfi.PercentPositivePattern == 2)
1396                                         Append (nfi.PercentSymbol);
1397                         }
1398                         else {
1399                                 switch (nfi.PercentNegativePattern) {
1400                                 case 0:
1401                                         Append (nfi.NegativeSign);
1402                                         break;
1403                                 case 1:
1404                                         Append (nfi.NegativeSign);
1405                                         break;
1406                                 case 2:
1407                                         Append (nfi.NegativeSign);
1408                                         Append (nfi.PercentSymbol);
1409                                         break;
1410                                 }
1411                         }
1412
1413                         AppendIntegerStringWithGroupSeparator (nfi.RawPercentGroupSizes, nfi.PercentGroupSeparator);
1414
1415                         if (precision > 0) {
1416                                 Append (nfi.PercentDecimalSeparator);
1417                                 AppendDecimalString (precision);
1418                         }
1419
1420                         if (_positive) {
1421                                 switch (nfi.PercentPositivePattern) {
1422                                 case 0:
1423                                         Append (' ');
1424                                         Append (nfi.PercentSymbol);
1425                                         break;
1426                                 case 1:
1427                                         Append (nfi.PercentSymbol);
1428                                         break;
1429                                 }
1430                         }
1431                         else {
1432                                 switch (nfi.PercentNegativePattern) {
1433                                 case 0:
1434                                         Append (' ');
1435                                         Append (nfi.PercentSymbol);
1436                                         break;
1437                                 case 1:
1438                                         Append (nfi.PercentSymbol);
1439                                         break;
1440                                 }
1441                         }
1442
1443                         return new string (_cbuf, 0, _ind);
1444                 }
1445
1446                 public string FormatExponential (int precision, NumberFormatInfo nfi)
1447                 {
1448                         if (precision == -1)
1449                                 precision = DefaultExpPrecision;
1450
1451                         RoundPos (precision + 1);
1452                         return FormatExponential (precision, nfi, 3);
1453                 }
1454
1455                 private string FormatExponential (int precision, NumberFormatInfo nfi, int expDigits)
1456                 {
1457                         int decDigits = _decPointPos;
1458                         int digits = _digitsLen;
1459                         int exponent = decDigits - 1;
1460                         decDigits = _decPointPos = 1;
1461
1462                         ResetCharBuf (precision + 8);
1463
1464                         if (!_positive)
1465                                 Append (nfi.NegativeSign);
1466
1467                         AppendOneDigit (digits - 1);
1468
1469                         if (precision > 0) {
1470                                 Append (nfi.NumberDecimalSeparator);
1471                                 AppendDigits (digits - precision - 1, digits - _decPointPos);
1472                         }
1473
1474                         AppendExponent (nfi, exponent, expDigits);
1475
1476                         return new string (_cbuf, 0, _ind);
1477                 }
1478
1479                 public string FormatCustom (string format, NumberFormatInfo nfi)
1480                 {
1481                         bool p = _positive;
1482                         int offset = 0;
1483                         int length = 0;
1484                         CustomInfo.GetActiveSection (format, ref p, IsZero, ref offset, ref length);
1485                         if (length == 0)
1486                                 return _positive ? string.Empty : nfi.NegativeSign;
1487                         _positive = p;
1488
1489                         CustomInfo info = CustomInfo.Parse (format, offset, length, nfi);
1490 #if false
1491                         Console.WriteLine ("Format : {0}",format);
1492                         Console.WriteLine ("DecimalDigits : {0}",info.DecimalDigits);
1493                         Console.WriteLine ("DecimalPointPos : {0}",info.DecimalPointPos);
1494                         Console.WriteLine ("DecimalTailSharpDigits : {0}",info.DecimalTailSharpDigits);
1495                         Console.WriteLine ("IntegerDigits : {0}",info.IntegerDigits);
1496                         Console.WriteLine ("IntegerHeadSharpDigits : {0}",info.IntegerHeadSharpDigits);
1497                         Console.WriteLine ("IntegerHeadPos : {0}",info.IntegerHeadPos);
1498                         Console.WriteLine ("UseExponent : {0}",info.UseExponent);
1499                         Console.WriteLine ("ExponentDigits : {0}",info.ExponentDigits);
1500                         Console.WriteLine ("ExponentTailSharpDigits : {0}",info.ExponentTailSharpDigits);
1501                         Console.WriteLine ("ExponentNegativeSignOnly : {0}",info.ExponentNegativeSignOnly);
1502                         Console.WriteLine ("DividePlaces : {0}",info.DividePlaces);
1503                         Console.WriteLine ("Percents : {0}",info.Percents);
1504                         Console.WriteLine ("Permilles : {0}",info.Permilles);
1505 #endif
1506                         StringBuilder sb_int = new StringBuilder (info.IntegerDigits * 2);
1507                         StringBuilder sb_dec = new StringBuilder (info.DecimalDigits * 2);
1508                         StringBuilder sb_exp = (info.UseExponent ? new StringBuilder (info.ExponentDigits * 2) : null);
1509
1510                         int diff = 0;
1511                         if (info.Percents > 0)
1512                                 Multiply10(2 * info.Percents);
1513                         if (info.Permilles > 0)
1514                                 Multiply10(3 * info.Permilles);
1515                         if (info.DividePlaces > 0)
1516                                 Divide10(info.DividePlaces);
1517
1518                         bool expPositive = true;
1519                         if (info.UseExponent && (info.DecimalDigits > 0 || info.IntegerDigits > 0)) {
1520                                 if (!IsZero) {
1521                                         RoundPos (info.DecimalDigits + info.IntegerDigits);
1522                                         diff -= _decPointPos - info.IntegerDigits;
1523                                         _decPointPos = info.IntegerDigits;
1524                                 }
1525
1526                                 expPositive = diff <= 0;
1527                                 AppendNonNegativeNumber (sb_exp, diff < 0 ? -diff : diff);
1528                         }
1529                         else
1530                                 RoundDecimal (info.DecimalDigits);
1531
1532                         if (info.IntegerDigits != 0 || !IsZeroInteger)
1533                                 AppendIntegerString (IntegerDigits, sb_int);
1534
1535                         AppendDecimalString (DecimalDigits, sb_dec);
1536
1537                         if (info.UseExponent) {
1538                                 if (info.DecimalDigits <= 0 && info.IntegerDigits <= 0)
1539                                         _positive = true;
1540
1541                                 if (sb_int.Length < info.IntegerDigits)
1542                                         sb_int.Insert (0, "0", info.IntegerDigits - sb_int.Length);
1543
1544                                 while (sb_exp.Length < info.ExponentDigits - info.ExponentTailSharpDigits)
1545                                         sb_exp.Insert (0, '0');
1546
1547                                 if (expPositive && !info.ExponentNegativeSignOnly)
1548                                         sb_exp.Insert (0, nfi.PositiveSign);
1549                                 else if (!expPositive)
1550                                         sb_exp.Insert (0, nfi.NegativeSign);
1551                         }
1552                         else {
1553                                 if (sb_int.Length < info.IntegerDigits - info.IntegerHeadSharpDigits)
1554                                         sb_int.Insert (0, "0", info.IntegerDigits - info.IntegerHeadSharpDigits - sb_int.Length);
1555                                 if (info.IntegerDigits == info.IntegerHeadSharpDigits && IsZeroOnly (sb_int))
1556                                         sb_int.Remove (0, sb_int.Length);
1557                         }
1558
1559                         ZeroTrimEnd (sb_dec, true);
1560                         while (sb_dec.Length < info.DecimalDigits - info.DecimalTailSharpDigits)
1561                                 sb_dec.Append ('0');
1562                         if (sb_dec.Length > info.DecimalDigits)
1563                                 sb_dec.Remove (info.DecimalDigits, sb_dec.Length - info.DecimalDigits);
1564
1565                         return info.Format (format, offset, length, nfi, _positive, sb_int, sb_dec, sb_exp);
1566                 }
1567                 #endregion public number formatting methods
1568
1569                 #region StringBuilder formatting helpers
1570
1571                 private static void ZeroTrimEnd (StringBuilder sb, bool canEmpty)
1572                 {
1573                         int len = 0;
1574                         for (int i = sb.Length - 1; (canEmpty ? i >= 0 : i > 0); i--) {
1575                                 if (sb [i] != '0')
1576                                         break;
1577                                 len++;
1578                         }
1579
1580                         if (len > 0)
1581                                 sb.Remove (sb.Length - len, len);
1582                 }
1583
1584                 private static bool IsZeroOnly (StringBuilder sb)
1585                 {
1586                         for (int i = 0; i < sb.Length; i++)
1587                                 if (char.IsDigit (sb [i]) && sb [i] != '0')
1588                                         return false;
1589                         return true;
1590                 }
1591
1592                 private static void AppendNonNegativeNumber (StringBuilder sb, int v)
1593                 {
1594                         if (v < 0)
1595                                 throw new ArgumentException ();
1596
1597                         int i = ScaleOrder (v) - 1;
1598                         do {
1599                                 int n = v / (int)GetTenPowerOf (i);
1600                                 sb.Append ((char)('0' | n));
1601                                 v -= (int)GetTenPowerOf (i--) * n;
1602                         } while (i >= 0);
1603                 }
1604
1605                 #endregion StringBuilder formatting helpers
1606
1607                 #region Append helpers
1608
1609                 private void AppendIntegerString (int minLength, StringBuilder sb)
1610                 {
1611                         if (_decPointPos <= 0) {
1612                                 sb.Append ('0', minLength);
1613                                 return;
1614                         }
1615
1616                         if (_decPointPos < minLength)
1617                                 sb.Append ('0', minLength - _decPointPos);
1618
1619                         AppendDigits (_digitsLen - _decPointPos, _digitsLen, sb);
1620                 }
1621
1622                 private void AppendIntegerString (int minLength)
1623                 {
1624                         if (_decPointPos <= 0) {
1625                                 Append ('0', minLength);
1626                                 return;
1627                         }
1628
1629                         if (_decPointPos < minLength)
1630                                 Append ('0', minLength - _decPointPos);
1631
1632                         AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1633                 }
1634
1635                 private void AppendDecimalString (int precision, StringBuilder sb)
1636                 {
1637                         AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos, sb);
1638                 }
1639
1640                 private void AppendDecimalString (int precision)
1641                 {
1642                         AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos);
1643                 }
1644
1645                 private void AppendIntegerStringWithGroupSeparator (int[] groups, string groupSeparator)
1646                 {
1647                         if (IsZeroInteger) {
1648                                 Append ('0');
1649                                 return;
1650                         }
1651
1652                         int total = 0;
1653                         int groupIndex = 0;
1654                         for (int i = 0; i < groups.Length; i++) {
1655                                 total += groups [i];
1656                                 if (total <= _decPointPos)
1657                                         groupIndex = i;
1658                                 else
1659                                         break;
1660                         }
1661
1662                         if (groups.Length > 0 && total > 0) {
1663                                 int counter;
1664                                 int groupSize = groups [groupIndex];
1665                                 int fraction = _decPointPos > total ? _decPointPos - total : 0;
1666                                 if (groupSize == 0) {
1667                                         while (groupIndex >= 0 && groups [groupIndex] == 0)
1668                                                 groupIndex--;
1669
1670                                         groupSize = fraction > 0 ? fraction : groups [groupIndex];
1671                                 }
1672                                 if (fraction == 0)
1673                                         counter = groupSize;
1674                                 else {
1675                                         groupIndex += fraction / groupSize;
1676                                         counter = fraction % groupSize;
1677                                         if (counter == 0)
1678                                                 counter = groupSize;
1679                                         else
1680                                                 groupIndex++;
1681                                 }
1682
1683                                 if (total >= _decPointPos) {
1684                                         int lastGroupSize = groups [0];
1685                                         if (total > lastGroupSize) {
1686                                                 int lastGroupDiff = -(lastGroupSize - _decPointPos);
1687                                                 int lastGroupMod;
1688
1689                                                 if (lastGroupDiff < lastGroupSize)
1690                                                         counter = lastGroupDiff;
1691                                                 else if (lastGroupSize > 0 && (lastGroupMod = _decPointPos % lastGroupSize) > 0)
1692                                                         counter = lastGroupMod;
1693                                         }
1694                                 }
1695                                 
1696                                 for (int i = 0; ;) {
1697                                         if ((_decPointPos - i) <= counter || counter == 0) {
1698                                                 AppendDigits (_digitsLen - _decPointPos, _digitsLen - i);
1699                                                 break;
1700                                         }
1701                                         AppendDigits (_digitsLen - i - counter, _digitsLen - i);
1702                                         i += counter;
1703                                         Append (groupSeparator);
1704                                         if (--groupIndex < groups.Length && groupIndex >= 0)
1705                                                 groupSize = groups [groupIndex];
1706                                         counter = groupSize;
1707                                 }
1708                         }
1709                         else {
1710                                 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1711                         }
1712                 }
1713
1714                 // minDigits is in the range 1..3
1715                 private void AppendExponent (NumberFormatInfo nfi, int exponent, int minDigits)
1716                 {
1717                         if (_specifierIsUpper || _specifier == 'R')
1718                                 Append ('E');
1719                         else
1720                                 Append ('e');
1721
1722                         if (exponent >= 0)
1723                                 Append (nfi.PositiveSign);
1724                         else {
1725                                 Append (nfi.NegativeSign);
1726                                 exponent = -exponent;
1727                         }
1728
1729                         if (exponent == 0)
1730                                 Append ('0', minDigits);
1731                         else if (exponent < 10) {
1732                                 Append ('0', minDigits - 1);
1733                                 Append ((char)('0' | exponent));
1734                         }
1735                         else {
1736                                 uint hexDigit = FastToDecHex (exponent);
1737                                 if (exponent >= 100 || minDigits == 3)
1738                                         Append ((char)('0' | (hexDigit >> 8)));
1739                                 Append ((char)('0' | ((hexDigit >> 4) & 0xf)));
1740                                 Append ((char)('0' | (hexDigit & 0xf)));
1741                         }
1742                 }
1743
1744                 private void AppendOneDigit (int start)
1745                 {
1746                         if (_ind == _cbuf.Length)
1747                                 Resize (_ind + 10);
1748
1749                         start += _offset;
1750                         uint v;
1751                         if (start < 0)
1752                                 v = 0;
1753                         else if (start < 8)
1754                                 v = _val1;
1755                         else if (start < 16)
1756                                 v = _val2;
1757                         else if (start < 24)
1758                                 v = _val3;
1759                         else if (start < 32)
1760                                 v = _val4;
1761                         else
1762                                 v = 0;
1763                         v >>= (start & 0x7) << 2;
1764                         _cbuf [_ind++] = (char)('0' | v & 0xf);
1765                 }
1766
1767 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1768                 unsafe
1769 #endif
1770                 private void FastAppendDigits (int val, bool force)
1771                 {
1772                         int i = _ind;
1773                         int digits;
1774                         if (force || val >= 100) {
1775                                 int v = (val * 5243) >> 19;
1776                                 digits = DecHexDigits [v];
1777                                 if (force || val >= 1000)
1778                                         _cbuf [i++] = (char)('0' | digits >> 4);
1779                                 _cbuf [i++] = (char)('0' | (digits & 0xf));
1780                                 digits = DecHexDigits [val - v * 100];
1781                         }
1782                         else
1783                                 digits = DecHexDigits [val];
1784
1785                         if (force || val >= 10)
1786                                 _cbuf [i++] = (char)('0' | digits >> 4);
1787                         _cbuf [i++] = (char)('0' | (digits & 0xf));
1788                         _ind = i;
1789                 }
1790
1791                 private void AppendDigits (int start, int end)
1792                 {
1793                         if (start >= end)
1794                                 return;
1795
1796                         int i = _ind + (end - start);
1797                         if (i > _cbuf.Length)
1798                                 Resize (i + 10);
1799                         _ind = i;
1800
1801                         end += _offset;
1802                         start += _offset;
1803
1804                         for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1805                                 uint v;
1806                                 if (next == 8)
1807                                         v = _val1;
1808                                 else if (next == 16)
1809                                         v = _val2;
1810                                 else if (next == 24)
1811                                         v = _val3;
1812                                 else if (next == 32)
1813                                         v = _val4;
1814                                 else
1815                                         v = 0;
1816                                 v >>= (start & 0x7) << 2;
1817                                 if (next > end)
1818                                         next = end;
1819
1820                                 _cbuf [--i] = (char)('0' | v & 0xf);
1821                                 switch (next - start) {
1822                                 case 8:
1823                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1824                                         goto case 7;
1825                                 case 7:
1826                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1827                                         goto case 6;
1828                                 case 6:
1829                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1830                                         goto case 5;
1831                                 case 5:
1832                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1833                                         goto case 4;
1834                                 case 4:
1835                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1836                                         goto case 3;
1837                                 case 3:
1838                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1839                                         goto case 2;
1840                                 case 2:
1841                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1842                                         goto case 1;
1843                                 case 1:
1844                                         if (next == end)
1845                                                 return;
1846                                         continue;
1847                                 }
1848                         }
1849                 }
1850
1851                 private void AppendDigits (int start, int end, StringBuilder sb)
1852                 {
1853                         if (start >= end)
1854                                 return;
1855
1856                         int i = sb.Length + (end - start);
1857                         sb.Length = i;
1858
1859                         end += _offset;
1860                         start += _offset;
1861
1862                         for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1863                                 uint v;
1864                                 if (next == 8)
1865                                         v = _val1;
1866                                 else if (next == 16)
1867                                         v = _val2;
1868                                 else if (next == 24)
1869                                         v = _val3;
1870                                 else if (next == 32)
1871                                         v = _val4;
1872                                 else
1873                                         v = 0;
1874                                 v >>= (start & 0x7) << 2;
1875                                 if (next > end)
1876                                         next = end;
1877                                 sb [--i] = (char)('0' | v & 0xf);
1878                                 switch (next - start) {
1879                                 case 8:
1880                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1881                                         goto case 7;
1882                                 case 7:
1883                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1884                                         goto case 6;
1885                                 case 6:
1886                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1887                                         goto case 5;
1888                                 case 5:
1889                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1890                                         goto case 4;
1891                                 case 4:
1892                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1893                                         goto case 3;
1894                                 case 3:
1895                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1896                                         goto case 2;
1897                                 case 2:
1898                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1899                                         goto case 1;
1900                                 case 1:
1901                                         if (next == end)
1902                                                 return;
1903                                         continue;
1904                                 }
1905                         }
1906                 }
1907
1908                 #endregion Append helpers
1909
1910                 #region others
1911
1912                 private void Multiply10(int count)
1913                 {
1914                         if (count <= 0 || _digitsLen == 0)
1915                                 return;
1916
1917                         _decPointPos += count;
1918                 }
1919
1920                 private void Divide10(int count)
1921                 {
1922                         if (count <= 0 || _digitsLen == 0)
1923                                 return;
1924
1925                         _decPointPos -= count;
1926                 }
1927
1928                 private NumberFormatter GetClone ()
1929                 {
1930                         return (NumberFormatter)this.MemberwiseClone ();
1931                 }
1932
1933                 #endregion others
1934
1935                 #region custom
1936
1937                 private class CustomInfo
1938                 {
1939                         public bool UseGroup = false;
1940                         public int DecimalDigits = 0;
1941                         public int DecimalPointPos = -1;
1942                         public int DecimalTailSharpDigits = 0;
1943                         public int IntegerDigits = 0;
1944                         public int IntegerHeadSharpDigits = 0;
1945                         public int IntegerHeadPos = 0;
1946                         public bool UseExponent = false;
1947                         public int ExponentDigits = 0;
1948                         public int ExponentTailSharpDigits = 0;
1949                         public bool ExponentNegativeSignOnly = true;
1950                         public int DividePlaces = 0;
1951                         public int Percents = 0;
1952                         public int Permilles = 0;
1953
1954                         public static void GetActiveSection (string format, ref bool positive, bool zero, ref int offset, ref int length)
1955                         {
1956                                 int[] lens = new int [3];
1957                                 int index = 0;
1958                                 int lastPos = 0;
1959                                 bool quoted = false;
1960
1961                                 for (int i = 0; i < format.Length; i++) {
1962                                         char c = format [i];
1963
1964                                         if (c == '\"' || c == '\'') {
1965                                                 if (i == 0 || format [i - 1] != '\\')
1966                                                         quoted = !quoted;
1967
1968                                                 continue;
1969                                         }
1970
1971                                         if (c == ';' && !quoted && (i == 0 || format [i - 1] != '\\')) {
1972                                                 lens [index++] = i - lastPos;
1973                                                 lastPos = i + 1;
1974                                                 if (index == 3)
1975                                                         break;
1976                                         }
1977                                 }
1978
1979                                 if (index == 0) {
1980                                         offset = 0;
1981                                         length = format.Length;
1982                                         return;
1983                                 }
1984                                 if (index == 1) {
1985                                         if (positive || zero) {
1986                                                 offset = 0;
1987                                                 length = lens [0];
1988                                                 return;
1989                                         }
1990                                         if (lens [0] + 1 < format.Length) {
1991                                                 positive = true;
1992                                                 offset = lens [0] + 1;
1993                                                 length = format.Length - offset;
1994                                                 return;
1995                                         }
1996                                         else {
1997                                                 offset = 0;
1998                                                 length = lens [0];
1999                                                 return;
2000                                         }
2001                                 }
2002                                 if (index == 2) {
2003                                         if (zero) {
2004                                                 offset = lens [0] + lens [1] + 2;
2005                                                 length = format.Length - offset;
2006                                                 return;
2007                                         }
2008                                         if (positive) {
2009                                                 offset = 0;
2010                                                 length = lens [0];
2011                                                 return;
2012                                         }
2013                                         if (lens [1] > 0) {
2014                                                 positive = true;
2015                                                 offset = lens [0] + 1;
2016                                                 length = lens [1];
2017                                                 return;
2018                                         }
2019                                         else {
2020                                                 offset = 0;
2021                                                 length = lens [0];
2022                                                 return;
2023                                         }
2024                                 }
2025                                 if (index == 3) {
2026                                         if (zero) {
2027                                                 offset = lens [0] + lens [1] + 2;
2028                                                 length = lens [2];
2029                                                 return;
2030                                         }
2031                                         if (positive) {
2032                                                 offset = 0;
2033                                                 length = lens [0];
2034                                                 return;
2035                                         }
2036                                         if (lens [1] > 0) {
2037                                                 positive = true;
2038                                                 offset = lens [0] + 1;
2039                                                 length = lens [1];
2040                                                 return;
2041                                         }
2042                                         else {
2043                                                 offset = 0;
2044                                                 length = lens [0];
2045                                                 return;
2046                                         }
2047                                 }
2048
2049                                 throw new ArgumentException ();
2050                         }
2051
2052                         public static CustomInfo Parse (string format, int offset, int length, NumberFormatInfo nfi)
2053                         {
2054                                 char literal = '\0';
2055                                 bool integerArea = true;
2056                                 bool decimalArea = false;
2057                                 bool exponentArea = false;
2058                                 bool sharpContinues = true;
2059
2060                                 CustomInfo info = new CustomInfo ();
2061                                 int groupSeparatorCounter = 0;
2062
2063                                 for (int i = offset; i - offset < length; i++) {
2064                                         char c = format [i];
2065
2066                                         if (c == literal && c != '\0') {
2067                                                 literal = '\0';
2068                                                 continue;
2069                                         }
2070                                         if (literal != '\0')
2071                                                 continue;
2072
2073                                         if (exponentArea && (c != '\0' && c != '0' && c != '#')) {
2074                                                 exponentArea = false;
2075                                                 integerArea = (info.DecimalPointPos < 0);
2076                                                 decimalArea = !integerArea;
2077                                                 i--;
2078                                                 continue;
2079                                         }
2080
2081                                         switch (c) {
2082                                         case '\\':
2083                                                 i++;
2084                                                 continue;
2085                                         case '\'':
2086                                         case '\"':
2087                                                 if (c == '\"' || c == '\'') {
2088                                                         literal = c;
2089                                                 }
2090                                                 continue;
2091                                         case '#':
2092                                                 if (sharpContinues && integerArea)
2093                                                         info.IntegerHeadSharpDigits++;
2094                                                 else if (decimalArea)
2095                                                         info.DecimalTailSharpDigits++;
2096                                                 else if (exponentArea)
2097                                                         info.ExponentTailSharpDigits++;
2098
2099                                                 goto case '0';
2100                                         case '0':
2101                                                 if (c != '#') {
2102                                                         sharpContinues = false;
2103                                                         if (decimalArea)
2104                                                                 info.DecimalTailSharpDigits = 0;
2105                                                         else if (exponentArea)
2106                                                                 info.ExponentTailSharpDigits = 0;
2107                                                 }
2108                                                 if (info.IntegerHeadPos == -1)
2109                                                         info.IntegerHeadPos = i;
2110
2111                                                 if (integerArea) {
2112                                                         info.IntegerDigits++;
2113                                                         if (groupSeparatorCounter > 0)
2114                                                                 info.UseGroup = true;
2115                                                         groupSeparatorCounter = 0;
2116                                                 }
2117                                                 else if (decimalArea)
2118                                                         info.DecimalDigits++;
2119                                                 else if (exponentArea)
2120                                                         info.ExponentDigits++;
2121                                                 break;
2122                                         case 'e':
2123                                         case 'E':
2124                                                 if (info.UseExponent)
2125                                                         break;
2126
2127                                                 info.UseExponent = true;
2128                                                 integerArea = false;
2129                                                 decimalArea = false;
2130                                                 exponentArea = true;
2131                                                 if (i + 1 - offset < length) {
2132                                                         char nc = format [i + 1];
2133                                                         if (nc == '+')
2134                                                                 info.ExponentNegativeSignOnly = false;
2135                                                         if (nc == '+' || nc == '-')
2136                                                                 i++;
2137                                                         else if (nc != '0' && nc != '#') {
2138                                                                 info.UseExponent = false;
2139                                                                 if (info.DecimalPointPos < 0)
2140                                                                         integerArea = true;
2141                                                         }
2142                                                 }
2143
2144                                                 break;
2145                                         case '.':
2146                                                 integerArea = false;
2147                                                 decimalArea = true;
2148                                                 exponentArea = false;
2149                                                 if (info.DecimalPointPos == -1)
2150                                                         info.DecimalPointPos = i;
2151                                                 break;
2152                                         case '%':
2153                                                 info.Percents++;
2154                                                 break;
2155                                         case '\u2030':
2156                                                 info.Permilles++;
2157                                                 break;
2158                                         case ',':
2159                                                 if (integerArea && info.IntegerDigits > 0)
2160                                                         groupSeparatorCounter++;
2161                                                 break;
2162                                         default:
2163                                                 break;
2164                                         }
2165                                 }
2166
2167                                 if (info.ExponentDigits == 0)
2168                                         info.UseExponent = false;
2169                                 else
2170                                         info.IntegerHeadSharpDigits = 0;
2171
2172                                 if (info.DecimalDigits == 0)
2173                                         info.DecimalPointPos = -1;
2174
2175                                 info.DividePlaces += groupSeparatorCounter * 3;
2176
2177                                 return info;
2178                         }
2179
2180                         public string Format (string format, int offset, int length, NumberFormatInfo nfi, bool positive, StringBuilder sb_int, StringBuilder sb_dec, StringBuilder sb_exp)
2181                         {
2182                                 StringBuilder sb = new StringBuilder ();
2183                                 char literal = '\0';
2184                                 bool integerArea = true;
2185                                 bool decimalArea = false;
2186                                 int intSharpCounter = 0;
2187                                 int sb_int_index = 0;
2188                                 int sb_dec_index = 0;
2189
2190                                 int[] groups = nfi.RawNumberGroupSizes;
2191                                 string groupSeparator = nfi.NumberGroupSeparator;
2192                                 int intLen = 0, total = 0, groupIndex = 0, counter = 0, groupSize = 0;
2193                                 if (UseGroup && groups.Length > 0) {
2194                                         intLen = sb_int.Length;
2195                                         for (int i = 0; i < groups.Length; i++) {
2196                                                 total += groups [i];
2197                                                 if (total <= intLen)
2198                                                         groupIndex = i;
2199                                         }
2200                                         groupSize = groups [groupIndex];
2201                                         int fraction = intLen > total ? intLen - total : 0;
2202                                         if (groupSize == 0) {
2203                                                 while (groupIndex >= 0 && groups [groupIndex] == 0)
2204                                                         groupIndex--;
2205
2206                                                 groupSize = fraction > 0 ? fraction : groups [groupIndex];
2207                                         }
2208                                         if (fraction == 0)
2209                                                 counter = groupSize;
2210                                         else {
2211                                                 groupIndex += fraction / groupSize;
2212                                                 counter = fraction % groupSize;
2213                                                 if (counter == 0)
2214                                                         counter = groupSize;
2215                                                 else
2216                                                         groupIndex++;
2217                                         }
2218                                 }
2219                                 else
2220                                         UseGroup = false;
2221
2222                                 for (int i = offset; i - offset < length; i++) {
2223                                         char c = format [i];
2224
2225                                         if (c == literal && c != '\0') {
2226                                                 literal = '\0';
2227                                                 continue;
2228                                         }
2229                                         if (literal != '\0') {
2230                                                 sb.Append (c);
2231                                                 continue;
2232                                         }
2233
2234                                         switch (c) {
2235                                         case '\\':
2236                                                 i++;
2237                                                 if (i - offset < length)
2238                                                         sb.Append (format [i]);
2239                                                 continue;
2240                                         case '\'':
2241                                         case '\"':
2242                                                 if (c == '\"' || c == '\'')
2243                                                         literal = c;
2244                                                 continue;
2245                                         case '#':
2246                                                 goto case '0';
2247                                         case '0':
2248                                                 if (integerArea) {
2249                                                         intSharpCounter++;
2250                                                         if (IntegerDigits - intSharpCounter < sb_int.Length + sb_int_index || c == '0')
2251                                                                 while (IntegerDigits - intSharpCounter + sb_int_index < sb_int.Length) {
2252                                                                         sb.Append (sb_int [sb_int_index++]);
2253                                                                         if (UseGroup && --intLen > 0 && --counter == 0) {
2254                                                                                 sb.Append (groupSeparator);
2255                                                                                 if (--groupIndex < groups.Length && groupIndex >= 0)
2256                                                                                         groupSize = groups [groupIndex];
2257                                                                                 counter = groupSize;
2258                                                                         }
2259                                                                 }
2260                                                         break;
2261                                                 }
2262                                                 else if (decimalArea) {
2263                                                         if (sb_dec_index < sb_dec.Length)
2264                                                                 sb.Append (sb_dec [sb_dec_index++]);
2265                                                         break;
2266                                                 }
2267
2268                                                 sb.Append (c);
2269                                                 break;
2270                                         case 'e':
2271                                         case 'E':
2272                                                 if (sb_exp == null || !UseExponent) {
2273                                                         sb.Append (c);
2274                                                         break;
2275                                                 }
2276
2277                                                 bool flag1 = true;
2278                                                 bool flag2 = false;
2279
2280                                                 int q;
2281                                                 for (q = i + 1; q - offset < length; q++) {
2282                                                         if (format [q] == '0') {
2283                                                                 flag2 = true;
2284                                                                 continue;
2285                                                         }
2286                                                         if (q == i + 1 && (format [q] == '+' || format [q] == '-'))
2287                                                                 continue;
2288                                                         if (!flag2)
2289                                                                 flag1 = false;
2290                                                         break;
2291                                                 }
2292
2293                                                 if (flag1) {
2294                                                         i = q - 1;
2295                                                         integerArea = (DecimalPointPos < 0);
2296                                                         decimalArea = !integerArea;
2297
2298                                                         sb.Append (c);
2299                                                         sb.Append (sb_exp);
2300                                                         sb_exp = null;
2301                                                 }
2302                                                 else
2303                                                         sb.Append (c);
2304
2305                                                 break;
2306                                         case '.':
2307                                                 if (DecimalPointPos == i) {
2308                                                         if (DecimalDigits > 0) {
2309                                                                 while (sb_int_index < sb_int.Length)
2310                                                                         sb.Append (sb_int [sb_int_index++]);
2311                                                         }
2312                                                         if (sb_dec.Length > 0)
2313                                                                 sb.Append (nfi.NumberDecimalSeparator);
2314                                                 }
2315                                                 integerArea = false;
2316                                                 decimalArea = true;
2317                                                 break;
2318                                         case ',':
2319                                                 break;
2320                                         case '%':
2321                                                 sb.Append (nfi.PercentSymbol);
2322                                                 break;
2323                                         case '\u2030':
2324                                                 sb.Append (nfi.PerMilleSymbol);
2325                                                 break;
2326                                         default:
2327                                                 sb.Append (c);
2328                                                 break;
2329                                         }
2330                                 }
2331
2332                                 if (!positive)
2333                                         sb.Insert (0, nfi.NegativeSign);
2334
2335                                 return sb.ToString ();
2336                         }
2337                 }
2338
2339                 #endregion
2340         }
2341 }