context: //
  // System.NumberFormatter.cs
  //
  // Author:
  //   Kazuki Oikawa (kazuki@panicode.com)
  //   Eyal Alaluf (eyala@mainsoft.com)
  //
  // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  // Copyright (C) 2008 Mainsoft Co. (http://www.mainsoft.com)
  //
  // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  // 
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  
  using System.Globalization;
  using System.Text;
  using System.Threading;
  using System.Runtime.CompilerServices;
  
  namespace System
  {
  	internal sealed partial class NumberFormatter
  	{
  		#region Static Fields
  
  		const int DefaultExpPrecision = 6;
  		const int HundredMillion = 100000000;
  		const long SeventeenDigitsThreshold = 10000000000000000;
  		const ulong ULongDivHundredMillion = UInt64.MaxValue / HundredMillion;
  		const ulong ULongModHundredMillion = 1 + UInt64.MaxValue % HundredMillion;
  
  		const int DoubleBitsExponentShift = 52;
  		const int DoubleBitsExponentMask = 0x7ff;
  		const long DoubleBitsMantissaMask = 0xfffffffffffff;
  		const int DecimalBitsScaleMask = 0x1f0000;
  
  		const int SingleDefPrecision = 7;
  		const int DoubleDefPrecision = 15;
  		const int Int32DefPrecision = 10;
  		const int UInt32DefPrecision = 10;
  		const int Int64DefPrecision = 19;
  		const int UInt64DefPrecision = 20;
  		const int DecimalDefPrecision = 100;
  		const int TenPowersListLength = 19;
  
  		const double MinRoundtripVal = -1.79769313486231E+308;
  		const double MaxRoundtripVal = 1.79769313486231E+308;
  
  		// The below arrays are taken from mono/metatdata/number-formatter.h
  
  		private static readonly unsafe ulong* MantissaBitsTable;
  		private static readonly unsafe int* TensExponentTable;
  		private static readonly unsafe char* DigitLowerTable;
  		private static readonly unsafe char* DigitUpperTable;
  		private static readonly unsafe long* TenPowersList;
  
  		// DecHexDigits s a translation table from a decimal number to its
  		// digits hexadecimal representation (e.g. DecHexDigits [34] = 0x34).
  		private static readonly unsafe int* DecHexDigits;
  
  		[MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)]
  		private unsafe static extern void GetFormatterTables (out ulong* MantissaBitsTable, out int* TensExponentTable,
  				out char* DigitLowerTable, out char* DigitUpperTable,
  				out long* TenPowersList, out int* DecHexDigits);
  
  		unsafe static NumberFormatter()
  		{
  			GetFormatterTables (out MantissaBitsTable, out TensExponentTable,
  				out DigitLowerTable, out DigitUpperTable, out TenPowersList, out DecHexDigits);
  		}
  
  		unsafe static long GetTenPowerOf(int i)
  		{
  			return TenPowersList [i];
  		}
  		#endregion Static Fields
  
  		#region Fields
  
  		private NumberFormatInfo _nfi;
  
  		//part of the private stringbuffer
  		private char[] _cbuf;
  
  		private bool _NaN;
  		private bool _infinity;
  		private bool _isCustomFormat;
  		private bool _specifierIsUpper;
  		private bool _positive;
  		private char _specifier;
  		private int _precision;
  		private int _defPrecision;
  
  		private int _digitsLen;
  		private int _offset; // Represent the first digit offset.
  		private int _decPointPos;
  
  		// The following fields are a hexadeimal representation of the digits.
  		// For instance _val = 0x234 represents the digits '2', '3', '4'.
  		private uint _val1; // Digits 0 - 7.
  		private uint _val2; // Digits 8 - 15.
  		private uint _val3; // Digits 16 - 23.
  		private uint _val4; // Digits 23 - 31. Only needed for decimals.
  
  		#endregion Fields
  
  		#region Constructor Helpers
  
  		// Translate an unsigned int to hexadecimal digits.
  		// i.e. 123456789 is represented by _val1 = 0x23456789 and _val2 = 0x1
  		private void InitDecHexDigits (uint value)
  		{
  			if (value >= HundredMillion) {
  				int div1 = (int)(value / HundredMillion);
  				value -= HundredMillion * (uint)div1;
  				_val2 = FastToDecHex (div1);
  			}
  			_val1 = ToDecHex ((int)value);
  		}
  
  		// Translate an unsigned long to hexadecimal digits.
  		private void InitDecHexDigits (ulong value)
  		{
  			if (value >= HundredMillion) {
  				long div1 = (long)(value / HundredMillion);
  				value -= HundredMillion * (ulong)div1;
  				if (div1 >= HundredMillion) {
  					int div2 = (int)(div1 / HundredMillion);
  					div1 = div1 - div2 * (long)HundredMillion;
  					_val3 = ToDecHex (div2);
  				}
  				if (div1 != 0)
  					_val2 = ToDecHex ((int)(div1));
  			}
  			if (value != 0)
  				_val1 = ToDecHex ((int)value);
  		}
  
  		// Translate a decimal integer to hexadecimal digits.
  		// The decimal integer is 96 digits and its value is hi * 2^64 + lo.
  		// is the lower 64 bits.
  		private void InitDecHexDigits (uint hi, ulong lo)
  		{
  			if (hi == 0) {
  				InitDecHexDigits (lo); // Only the lower 64 bits matter.
  				return;
  			}
  
  			// Compute (hi, lo) = (hi , lo) / HundredMillion.
  			uint divhi = hi / HundredMillion;
  			ulong remhi = hi - divhi * HundredMillion;
  			ulong divlo = lo / HundredMillion;
  			ulong remlo = lo - divlo * HundredMillion + remhi * ULongModHundredMillion;
  			hi = divhi;
  			lo = divlo + remhi * ULongDivHundredMillion;
  			divlo = remlo / HundredMillion;
  			remlo -= divlo * HundredMillion;
  			lo += divlo;
  			_val1 = ToDecHex ((int)remlo);
  
  			// Divide hi * 2 ^ 64 + lo by HundredMillion using the fact that
  			// hi < HundredMillion.
  			divlo = lo / HundredMillion;
  			remlo = lo - divlo * HundredMillion;
  			lo = divlo;
  			if (hi != 0) {
  				lo += hi * ULongDivHundredMillion;
  				remlo += hi * ULongModHundredMillion;
  				divlo = remlo / HundredMillion;
  				lo += divlo;
  				remlo -= divlo * HundredMillion;
  			}
  			_val2 = ToDecHex ((int)remlo);
  
  			// Now we are left with 64 bits store in lo.
  			if (lo >= HundredMillion) {
  				divlo = lo / HundredMillion;
  				lo -= divlo * HundredMillion;
  				_val4 = ToDecHex ((int)divlo);
  			}
  			_val3 = ToDecHex ((int)lo);
  		}
  
  		// Helper to translate an int in the range 0 .. 9999 to its
  		// Hexadecimal digits representation.
  		unsafe private static uint FastToDecHex (int val)
  		{
  			if (val < 100)
  				return (uint)DecHexDigits [val];
  
  			// Uses 2^19 (524288) to compute val / 100 for val < 10000.
  			int v = (val * 5243) >> 19;
  			return (uint)((DecHexDigits [v] << 8) | DecHexDigits [val - v * 100]);
  		}
  
  		// Helper to translate an int in the range 0 .. 99999999 to its
  		// Hexadecimal digits representation.
  		private static uint ToDecHex (int val)
  		{
  			uint res = 0;
  			if (val >= 10000) {
  				int v = val / 10000;
  				val -= v * 10000;
  				res = FastToDecHex (v) << 16;
  			}
  			return res | FastToDecHex (val);
  		}
  
  		// Helper to count number of hexadecimal digits in a number.
  		private static int FastDecHexLen (int val)
  		{
  			if (val < 0x100)
  				if (val < 0x10)
  					return 1;
  				else
  					return 2;
  			else if (val < 0x1000)
  				return 3;
  			else
  				return 4;
  		}
  
  		private static int DecHexLen (uint val)
  		{
  			if (val < 0x10000)
  				return FastDecHexLen ((int)val);
  			return 4 + FastDecHexLen ((int)(val >> 16));
  		}
  
  		// Count number of hexadecimal digits stored in _val1 .. _val4.
  		private int DecHexLen ()
  		{
  			if (_val4 != 0)
  				return DecHexLen (_val4) + 24;
  			else if (_val3 != 0)
  				return DecHexLen (_val3) + 16;
  			else if (_val2 != 0)
  				return DecHexLen (_val2) + 8;
  			else if (_val1 != 0)
  				return DecHexLen (_val1);
  			else
  				return 0;
  		}
  
  		// Helper to count the 10th scale (number of digits) in a number
  		private static int ScaleOrder (long hi)
  		{
  			for (int i = TenPowersListLength - 1; i >= 0; i--)
  				if (hi >= GetTenPowerOf (i))
  					return i + 1;
  			return 1;
  		}
  
  		// Compute the initial precision for rounding a floating number
  		// according to the used format.
  		int InitialFloatingPrecision ()
  		{
  			if (_specifier == 'R')
  				return _defPrecision + 2;
  			if (_precision < _defPrecision)
  				return _defPrecision;
  			if (_specifier == 'G')
  				return Math.Min (_defPrecision + 2, _precision);
  			if (_specifier == 'E')
  				return Math.Min (_defPrecision + 2, _precision + 1);
  			return _defPrecision;
  		}
  
  		// Parse the given format and extract the precision in it.
  		// Returns -1 for empty formats and -2 to indicate that the format
  		// is a custom format.
  		private static int ParsePrecision (string format)
  		{
  			int precision = 0;
  			for (int i = 1; i < format.Length; i++) {
  				int val = format [i] - '0';
  				precision = precision * 10 + val;
  				if (val < 0 || val > 9 || precision > 99)
  					return -2;
  			}
  			return precision;
  		}
  
  		#endregion Constructor Helpers
  
  		#region Constructors
  
  		// Parse the given format and initialize the following fields:
  		//   _isCustomFormat, _specifierIsUpper, _specifier & _precision.
  		NumberFormatter (Thread current)
  		{
  			_cbuf = EmptyArray<char>.Value;
  			if (current == null)
  				return;
  			CurrentCulture = current.CurrentCulture;
  		}
  
  		private void Init (string format)
  		{
  			_val1 = _val2 = _val3 = _val4 = 0;
  			_offset = 0;
  			_NaN = _infinity = false;
  			_isCustomFormat = false;
  			_specifierIsUpper = true;
  			_precision = -1;
  
  			if (format == null || format.Length == 0) {
  				_specifier = 'G';
  				return;
  			}
  
  			char specifier = format [0];
  			if (specifier >= 'a' && specifier <= 'z') {
  				specifier = (char)(specifier - 'a' + 'A');
  				_specifierIsUpper = false;
  			}
  			else if (specifier < 'A' || specifier > 'Z') {
  				_isCustomFormat = true;
  				_specifier = '0';
  				return;
  			}
  			_specifier = specifier;
  			if (format.Length > 1) {
  				_precision = ParsePrecision (format);
  				if (_precision == -2) { // Is it a custom format?
  					_isCustomFormat = true;
  					_specifier = '0';
  					_precision = -1;
  				}
  			}
  		}
  
  		private void InitHex (ulong value)
  		{
  			switch (_defPrecision) {
  				case Int32DefPrecision: value = (uint) value;    break;
  			}
  			_val1 = (uint)value;
  			_val2 = (uint)(value >> 32);
  			_decPointPos = _digitsLen = DecHexLen ();
  			if (value == 0)
  				_decPointPos = 1;
  		}
  
  		private void Init (string format, int value, int defPrecision)
  		{
  			Init (format);
  			_defPrecision = defPrecision;
  			_positive = value >= 0;
  
  			if (value == 0 || _specifier == 'X') {
  				InitHex ((ulong)value);
  				return;
  			}
  
  			if (value < 0)
  				value = -value;
  			InitDecHexDigits ((uint)value);
  			_decPointPos = _digitsLen = DecHexLen ();
  		}
  
  		private void Init (string format, uint value, int defPrecision)
  		{
  			Init (format);
  			_defPrecision = defPrecision;
  			_positive = true;
  
  			if (value == 0 || _specifier == 'X') {
  				InitHex (value);
  				return;
  			}
  
  			InitDecHexDigits (value);
  			_decPointPos = _digitsLen = DecHexLen ();
  		}
  
  		private void Init (string format, long value)
  		{
  			Init (format);
  			_defPrecision = Int64DefPrecision;
  			_positive = value >= 0;
  
  			if (value == 0 || _specifier == 'X') {
  				InitHex ((ulong)value);
  				return;
  			}
  
  			if (value < 0)
  				value = -value;
  			InitDecHexDigits ((ulong)value);
  			_decPointPos = _digitsLen = DecHexLen ();
  		}
  
  		private void Init (string format, ulong value)
  		{
  			Init (format);
  			_defPrecision = UInt64DefPrecision;
  			_positive = true;
  
  			if (value == 0 || _specifier == 'X') {
  				InitHex ((ulong)value);
  				return;
  			}
  
  			InitDecHexDigits (value);
  			_decPointPos = _digitsLen = DecHexLen ();
  		}
  
  		unsafe private void Init (string format, double value, int defPrecision)
  		{
  			Init (format);
  
  			_defPrecision = defPrecision;
  			long bits = BitConverter.DoubleToInt64Bits (value);
  		   	_positive = bits >= 0;
  			bits &= Int64.MaxValue;
  			if (bits == 0) {
  				_decPointPos = 1;
  				_digitsLen = 0;
  				_positive = true;
  				return;
  			}
  
  			int e = (int)(bits >> DoubleBitsExponentShift);
  			long m = bits & DoubleBitsMantissaMask;
  			if (e == DoubleBitsExponentMask) {
  				_NaN = m != 0;
  				_infinity = m == 0;
  				return;
  			}
  
  			int expAdjust = 0;
  			if (e == 0) {
  				// We need 'm' to be large enough so we won't lose precision.
  				e = 1;
  				int scale = ScaleOrder (m);
  				if (scale < DoubleDefPrecision) {
  					expAdjust = scale - DoubleDefPrecision;
  					m *= GetTenPowerOf (-expAdjust);
  				}
  			}
  			else {
  				m = (m + DoubleBitsMantissaMask + 1) * 10;
  				expAdjust = -1;
  			}
  
  			// multiply the mantissa by 10 ^ N
  			ulong lo = (uint)m;
  			ulong hi = (ulong)m >> 32;
  			ulong lo2 = MantissaBitsTable [e];
  			ulong hi2 = lo2 >> 32;
  			lo2 = (uint)lo2;
  			ulong mm = hi * lo2 + lo * hi2 + ((lo * lo2) >> 32);
  			long res = (long)(hi * hi2 + (mm >> 32));
  			while (res < SeventeenDigitsThreshold) {
  				mm = (mm & UInt32.MaxValue) * 10;
  				res = res * 10 + (long)(mm >> 32);
  				expAdjust--;
  			}
  			if ((mm & 0x80000000) != 0)
  				res++;
  
  			int order = DoubleDefPrecision + 2;
  			_decPointPos = TensExponentTable [e] + expAdjust + order;
  
  			// Rescale 'res' to the initial precision (15-17 for doubles).
  			int initialPrecision = InitialFloatingPrecision ();
  			if (order > initialPrecision) {
  				long val = GetTenPowerOf (order - initialPrecision);
  				res = (res + (val >> 1)) / val;
  				order = initialPrecision;
  			}
  			if (res >= GetTenPowerOf (order)) {
  				order++;
  				_decPointPos++;
  			}
  
  		   	InitDecHexDigits ((ulong)res);
  			_offset = CountTrailingZeros ();
  			_digitsLen = order - _offset;
  		}
  
  		private void Init (string format, decimal value)
  		{
  			Init (format);
  			_defPrecision = DecimalDefPrecision;
  
  			int[] bits = decimal.GetBits (value);
  			int scale = (bits [3] & DecimalBitsScaleMask) >> 16;
  			_positive = bits [3] >= 0;
  			if (bits [0] == 0 && bits [1] == 0 && bits [2] == 0) {
  				_decPointPos = -scale;
  				_positive = true;
  				_digitsLen = 0;
  				return;
  			}
  
  		   	InitDecHexDigits ((uint)bits [2], ((ulong)bits [1] << 32) | (uint)bits [0]);
  			_digitsLen = DecHexLen ();
  			_decPointPos = _digitsLen - scale;
  			if (_precision != -1 || _specifier != 'G') {
  				_offset = CountTrailingZeros ();
  				_digitsLen -= _offset;
  			}
  		}
  
  		#endregion Constructors
  
  		#region Inner String Buffer
  
  		//_cbuf moved to before other fields to improve layout
  		private int _ind;
  
  		private void ResetCharBuf (int size)
  		{
  			_ind = 0;
  			if (_cbuf.Length < size)
  				_cbuf = new char [size];
  		}
  
  		private void Resize (int len)
  		{
  			Array.Resize (ref _cbuf, len);
  		}
  
  		private void Append (char c)
  		{
  			if (_ind == _cbuf.Length)
  				Resize (_ind + 10);
  			_cbuf [_ind++] = c;
  		}
  
  		private void Append (char c, int cnt)
  		{
  			if (_ind + cnt > _cbuf.Length)
  				Resize (_ind + cnt + 10);
  			while (cnt-- > 0)
  				_cbuf [_ind++] = c;
  		}
  
  		private void Append (string s)
  		{
  			int slen = s.Length;
  			if (_ind + slen > _cbuf.Length)
  				Resize (_ind + slen + 10);
  			for (int i = 0; i < slen; i++)
  				_cbuf [_ind++] = s [i];
  		}
  
  		#endregion Inner String Buffer
  
  		#region Helper properties
  
  		private NumberFormatInfo GetNumberFormatInstance (IFormatProvider fp)
  		{
  			if (_nfi != null && fp == null)
  				return _nfi;
  			return NumberFormatInfo.GetInstance (fp);
  		}
  
  		CultureInfo CurrentCulture {
  			set {
  				if (value != null && value.IsReadOnly)
  					_nfi = value.NumberFormat;
  				else
  					_nfi = null;
  			}
  		}
  
  		private int IntegerDigits {
  			get { return _decPointPos > 0 ? _decPointPos : 1; }
  		}
  
  		private int DecimalDigits {
  			get { return _digitsLen > _decPointPos ? _digitsLen - _decPointPos : 0; }
  		}
  
  		private bool IsFloatingSource {
  			get { return _defPrecision == DoubleDefPrecision || _defPrecision == SingleDefPrecision; }
  		}
  
  		private bool IsZero {
  			get { return _digitsLen == 0; }
  		}
  
  		private bool IsZeroInteger {
  			get { return _digitsLen == 0 || _decPointPos <= 0; }
  		}
  
  		#endregion Helper properties
  
  		#region Round
  
  		private void RoundPos (int pos)
  		{
  			RoundBits (_digitsLen - pos);
  		}
  
  		private bool RoundDecimal (int decimals)
  		{
  			return RoundBits (_digitsLen - _decPointPos - decimals);
  		}
  
  		private bool RoundBits (int shift)
  		{
  			if (shift <= 0)
  				return false;
  
  			if (shift > _digitsLen) {
  				_digitsLen = 0;
  				_decPointPos = 1;
  				_val1 = _val2 = _val3 = _val4 = 0;
  				_positive = true;
  				return false;
  			}
  			shift += _offset;
  			_digitsLen += _offset;
  			while (shift > 8) {
  				_val1 = _val2;
  				_val2 = _val3;
  				_val3 = _val4;
  				_val4 = 0;
  				_digitsLen -= 8;
  				shift -= 8;
  			}
  			shift = (shift - 1) << 2;
  			uint v = _val1 >> shift;
  			uint rem16 = v & 0xf;
  			_val1 = (v ^ rem16) << shift;
  			bool res = false;
  			if (rem16 >= 0x5) {
  				_val1 |= 0x99999999 >> (28 - shift);
  				AddOneToDecHex ();
  				int newlen = DecHexLen ();
  				res = newlen != _digitsLen;
  				_decPointPos = _decPointPos + newlen - _digitsLen;
  				_digitsLen = newlen;
  			}
  			RemoveTrailingZeros ();
  			return res;
  		}
  
  		private void RemoveTrailingZeros ()
  		{
  			_offset = CountTrailingZeros ();
  			_digitsLen -= _offset;
  			if (_digitsLen == 0) {
  				_offset = 0;
  				_decPointPos = 1;
  				_positive = true;
  			}
  		}
  
  		private void AddOneToDecHex ()
  		{
  			if (_val1 == 0x99999999) {
  				_val1 = 0;
  				if (_val2 == 0x99999999) {
  					_val2 = 0;
  					if (_val3 == 0x99999999) {
  						_val3 = 0;
  						_val4 = AddOneToDecHex (_val4);
  					}
  					else
  						_val3 = AddOneToDecHex (_val3);
  				}
  				else
  					_val2 = AddOneToDecHex (_val2);
  			}
  			else
  				_val1 = AddOneToDecHex (_val1);
  		}
  
  		// Assume val != 0x99999999
  		private static uint AddOneToDecHex (uint val)
  		{
  			if ((val & 0xffff) == 0x9999)
  				if ((val & 0xffffff) == 0x999999)
  					if ((val & 0xfffffff) == 0x9999999)
  						return val + 0x06666667;
  					else
  						return val + 0x00666667;
  				else if ((val & 0xfffff) == 0x99999)
  					return val + 0x00066667;
  				else
  					return val + 0x00006667;
  			else if ((val & 0xff) == 0x99)
  				if ((val & 0xfff) == 0x999)
  					return val + 0x00000667;
  				else
  					return val + 0x00000067;
  			else if ((val & 0xf) == 0x9)
  				return val + 0x00000007;
  			else
  				return val + 1;
  		}
  
  		private int CountTrailingZeros ()
  		{
  			if (_val1 != 0)
  				return CountTrailingZeros (_val1);
  			if (_val2 != 0)
  				return CountTrailingZeros (_val2) + 8;
  			if (_val3 != 0)
  				return CountTrailingZeros (_val3) + 16;
  			if (_val4 != 0)
  				return CountTrailingZeros (_val4) + 24;
  			return _digitsLen;
  		}
  
  		private static int CountTrailingZeros (uint val)
  		{
  			if ((val & 0xffff) == 0)
  				if ((val & 0xffffff) == 0)
  					if ((val & 0xfffffff) == 0)
  						return 7;
  					else
  						return 6;
  				else if ((val & 0xfffff) == 0)
  					return 5;
  				else
  					return 4;
  			else if ((val & 0xff) == 0)
  				if ((val & 0xfff) == 0)
  					return 3;
  				else
  					return 2;
  			else if ((val & 0xf) == 0)
  				return 1;
  			else
  				return 0;
  		}
  
  		#endregion Round
  
  		#region public number formatting methods
  
  		[ThreadStatic]
  		static NumberFormatter threadNumberFormatter;
  
  		[ThreadStatic]
  		static NumberFormatter userFormatProvider;
  
  		private static NumberFormatter GetInstance (IFormatProvider fp)
  		{
  			if (fp != null) {
  				if (userFormatProvider == null) {
  					Interlocked.CompareExchange (ref userFormatProvider, new NumberFormatter (null), null);
  				}
  
  				return userFormatProvider;
  			}
  
  			NumberFormatter res = threadNumberFormatter;
  			threadNumberFormatter = null;
  			if (res == null)
  				return new NumberFormatter (Thread.CurrentThread);
  			res.CurrentCulture = Thread.CurrentThread.CurrentCulture;
  			return res;
  		}
  
  		private void Release()
  		{
  			if (this != userFormatProvider)
  				threadNumberFormatter = this;
  		}
  
  		public static string NumberToString (string format, uint value, IFormatProvider fp)
  		{
  			NumberFormatter inst = GetInstance (fp);
  			inst.Init (format, value, Int32DefPrecision);
  			string res = inst.IntegerToString (format, fp);
  			inst.Release();
  			return res;
  		}
  
  		public static string NumberToString (string format, int value, IFormatProvider fp)
  		{
  			NumberFormatter inst = GetInstance (fp);
  			inst.Init (format, value, UInt32DefPrecision);
  			string res = inst.IntegerToString (format, fp);
  			inst.Release();
  			return res;
  		}
  
  		public static string NumberToString (string format, ulong value, IFormatProvider fp)
  		{
  			NumberFormatter inst = GetInstance (fp);
  			inst.Init (format, value);
  			string res = inst.IntegerToString (format, fp);
  			inst.Release();
  			return res;
  		}
  
  		public static string NumberToString (string format, long value, IFormatProvider fp)
  		{
  			NumberFormatter inst = GetInstance (fp);
  			inst.Init (format, value);
  			string res = inst.IntegerToString (format, fp);
  			inst.Release();
  			return res;
  		}
  
  		public static string NumberToString (string format, float value, IFormatProvider fp)
  		{
  			NumberFormatter inst = GetInstance (fp);
  			inst.Init (format, value, SingleDefPrecision);
  			NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
  			string res;
  			if (inst._NaN)
  				res = nfi.NaNSymbol;
  			else if (inst._infinity)
  				if (inst._positive)
  					res = nfi.PositiveInfinitySymbol;
  				else
  					res = nfi.NegativeInfinitySymbol;
  			else if (inst._specifier == 'R')
  				res = inst.FormatRoundtrip (value, nfi);
  			else
  				res = inst.NumberToString (format, nfi);
  			inst.Release();
  			return res;
  		}
  
  		public static string NumberToString (string format, double value, IFormatProvider fp)
  		{
  			NumberFormatter inst = GetInstance (fp);
  			inst.Init (format, value, DoubleDefPrecision);
  			NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
  			string res;
  			if (inst._NaN)
  				res = nfi.NaNSymbol;
  			else if (inst._infinity)
  				if (inst._positive)
  					res = nfi.PositiveInfinitySymbol;
  				else
  					res = nfi.NegativeInfinitySymbol;
  			else if (inst._specifier == 'R')
  				res = inst.FormatRoundtrip (value, nfi);
  			else
  				res = inst.NumberToString (format, nfi);
  			inst.Release();
  			return res;
  		}
  
  		public static string NumberToString (string format, decimal value, IFormatProvider fp)
  		{
  			NumberFormatter inst = GetInstance (fp);
  			inst.Init (format, value);
  			string res = inst.NumberToString (format, inst.GetNumberFormatInstance (fp));
  			inst.Release();
  			return res;
  		}
  
  		private string IntegerToString (string format, IFormatProvider fp)
  		{
  			NumberFormatInfo nfi = GetNumberFormatInstance (fp);
  			switch (_specifier) {
  			case 'C':
  				return FormatCurrency (_precision, nfi);
  			case 'D':
  				return FormatDecimal (_precision, nfi);
  			case 'E':
  				return FormatExponential (_precision, nfi);
  			case 'F':
  				return FormatFixedPoint (_precision, nfi);
  			case 'G':
  				if (_precision <= 0)
  					return FormatDecimal (-1, nfi);
  				return FormatGeneral (_precision, nfi);
  			case 'N':
  				return FormatNumber (_precision, nfi);
  			case 'P':
  				return FormatPercent (_precision, nfi);
  			case 'X':
  				return FormatHexadecimal (_precision);
  			default:
  				if (_isCustomFormat)
  					return FormatCustom (format, nfi);
  				throw new FormatException ("The specified format '" + format + "' is invalid");
  			}
  		}
  
  		private string NumberToString (string format, NumberFormatInfo nfi)
  		{
  			switch (_specifier) {
  			case 'C':
  				return FormatCurrency (_precision, nfi);
  			case 'E':
  				return FormatExponential (_precision, nfi);
  			case 'F':
  				return FormatFixedPoint (_precision, nfi);
  			case 'G':
  				return FormatGeneral (_precision, nfi);
  			case 'N':
  				return FormatNumber (_precision, nfi);
  			case 'P':
  				return FormatPercent (_precision, nfi);
  			case 'X':
  			default:
  				if (_isCustomFormat)
  					return FormatCustom (format, nfi);
  				throw new FormatException ("The specified format '" + format + "' is invalid");
  			}
  		}
  
  		string FormatCurrency (int precision, NumberFormatInfo nfi)
  		{
  			precision = (precision >= 0 ? precision : nfi.CurrencyDecimalDigits);
  			RoundDecimal (precision);
  			ResetCharBuf (IntegerDigits * 2 + precision * 2 + 16);
  
  			if (_positive) {
  				switch (nfi.CurrencyPositivePattern) {
  				case 0:
  					Append (nfi.CurrencySymbol);
  					break;
  				case 2:
  					Append (nfi.CurrencySymbol);
  					Append (' ');
  					break;
  				}
  			}
  			else {
  				switch (nfi.CurrencyNegativePattern) {
  				case 0:
  					Append ('(');
  					Append (nfi.CurrencySymbol);
  					break;
  				case 1:
  					Append (nfi.NegativeSign);
  					Append (nfi.CurrencySymbol);
  					break;
  				case 2:
  					Append (nfi.CurrencySymbol);
  					Append (nfi.NegativeSign);
  					break;
  				case 3:
  					Append (nfi.CurrencySymbol);
  					break;
  				case 4:
  					Append ('(');
  					break;
  				case 5:
  					Append (nfi.NegativeSign);
  					break;
  				case 8:
  					Append (nfi.NegativeSign);
  					break;
  				case 9:
  					Append (nfi.NegativeSign);
  					Append (nfi.CurrencySymbol);
  					Append (' ');
  					break;
  				case 11:
  					Append (nfi.CurrencySymbol);
  					Append (' ');
  					break;
  				case 12:
  					Append (nfi.CurrencySymbol);
  					Append (' ');
  					Append (nfi.NegativeSign);
  					break;
  				case 14:
  					Append ('(');
  					Append (nfi.CurrencySymbol);
  					Append (' ');
  					break;
  				case 15:
  					Append ('(');
  					break;
  				}
  			}
  
  			AppendIntegerStringWithGroupSeparator (nfi.CurrencyGroupSizes, nfi.CurrencyGroupSeparator);
  
  			if (precision > 0) {
  				Append (nfi.CurrencyDecimalSeparator);
  				AppendDecimalString (precision);
  			}
  
  			if (_positive) {
  				switch (nfi.CurrencyPositivePattern) {
  				case 1:
  					Append (nfi.CurrencySymbol);
  					break;
  				case 3:
  					Append (' ');
  					Append (nfi.CurrencySymbol);
  					break;
  				}
  			}
  			else {
  				switch (nfi.CurrencyNegativePattern) {
  				case 0:
  					Append (')');
  					break;
  				case 3:
  					Append (nfi.NegativeSign);
  					break;
  				case 4:
  					Append (nfi.CurrencySymbol);
  					Append (')');
  					break;
  				case 5:
  					Append (nfi.CurrencySymbol);
  					break;
  				case 6:
  					Append (nfi.NegativeSign);
  					Append (nfi.CurrencySymbol);
  					break;
  				case 7:
  					Append (nfi.CurrencySymbol);
  					Append (nfi.NegativeSign);
  					break;
  				case 8:
  					Append (' ');
  					Append (nfi.CurrencySymbol);
  					break;
  				case 10:
  					Append (' ');
  					Append (nfi.CurrencySymbol);
  					Append (nfi.NegativeSign);
  					break;
  				case 11:
  					Append (nfi.NegativeSign);
  					break;
  				case 13:
  					Append (nfi.NegativeSign);
  					Append (' ');
  					Append (nfi.CurrencySymbol);
  					break;
  				case 14:
  					Append (')');
  					break;
  				case 15:
  
gt: 					Append (' ');