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