context: //
  // System.Char.cs
  //
  // Authors:
  //   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
  //   Miguel de Icaza (miguel@ximian.com)
  //   Jackson Harper (jackson@ximian.com)
  //
  // (C) Ximian, Inc.  http://www.ximian.com
  // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.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.
  //
  
  // Note about the ToString()'s. ECMA says there's only a ToString() method, 
  // BUT it is just a wrapper for ToString(null). However there is no other ToString
  // in the docs. Turning to the NET framework sdk reveals that there is a 
  // ToString(formatprovider) method, as well as a 'static ToString (char c)' method, 
  // which appears to just be a Convert.ToString(char c) type method. ECMA also
  // doesn't list this class as implementing IFormattable.
  
  using System.Globalization;
  using System.Runtime.CompilerServices;
  using System.Runtime.InteropServices;
  
  namespace System
  {
  	[Serializable]
  	[ComVisible (true)]
  	public struct Char : IComparable, IConvertible, IComparable <char>, IEquatable <char>
  	{
  		public const char MaxValue = (char) 0xffff;
  		public const char MinValue = (char) 0;
  
  		internal char m_value;
  
  		static Char ()
  		{
  			unsafe {
  				GetDataTablePointers (CategoryDataVersion,
  					out category_data, out category_astral_index, out numeric_data,
  					out numeric_data_values, out to_lower_data_low, out to_lower_data_high,
  					out to_upper_data_low, out to_upper_data_high);
  				category_check_pair = category_astral_index != null
  					? (byte)UnicodeCategory.Surrogate
  					: (byte)0xff;
  			}
  		}
  
  		private readonly unsafe static byte *category_data;
  		private readonly unsafe static ushort *category_astral_index;
  		private readonly unsafe static byte *numeric_data;
  		private readonly unsafe static double *numeric_data_values;
  		private readonly unsafe static ushort *to_lower_data_low;
  		private readonly unsafe static ushort *to_lower_data_high;
  		private readonly unsafe static ushort *to_upper_data_low;
  		private readonly unsafe static ushort *to_upper_data_high;
  
  		// UnicodeCategory.Surrogate if astral plane
  		// categories are available, 0xff otherwise.
  		private readonly static byte category_check_pair;
  
  #if NET_4_0
  		private const int CategoryDataVersion = 4;
  #else
  		private const int CategoryDataVersion = 2;
  #endif
  
  		[MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)]
  		private unsafe static extern void GetDataTablePointers (int category_data_version,
  			out byte *category_data, out ushort *category_astral_index, out byte *numeric_data,
  			out double *numeric_data_values, out ushort *to_lower_data_low, out ushort *to_lower_data_high,
  			out ushort *to_upper_data_low, out ushort *to_upper_data_high);
  
  		public int CompareTo (object value)
  		{
  			if (value == null)
  				return 1;
  			
  			if (!(value is System.Char))
  				throw new ArgumentException (Locale.GetText ("Value is not a System.Char"));
  
  			char xv = (char) value;
  			if (m_value == xv)
  				return 0;
  
  			if (m_value > xv)
  				return 1;
  			else
  				return -1;
  		}
  
  		public override bool Equals (object obj)
  		{
  			if (!(obj is Char))
  				return false;
  
  			return ((char) obj) == m_value;
  		}
  
  		public int CompareTo (char value)
  		{
  			if (m_value == value)
  				return 0;
  
  			if (m_value > value)
  				return 1;
  			else
  				return -1;
  		}
  
  		public static string ConvertFromUtf32 (int utf32)
  		{
  			if (utf32 < 0 || utf32 > 0x10FFFF)
  				throw new ArgumentOutOfRangeException ("utf32", "The argument must be from 0 to 0x10FFFF.");
  			if (0xD800 <= utf32 && utf32 <= 0xDFFF)
  				throw new ArgumentOutOfRangeException ("utf32", "The argument must not be in surrogate pair range.");
  			if (utf32 < 0x10000)
  				return new string ((char) utf32, 1);
  			utf32 -= 0x10000;
  			return new string (
  				new char [] {(char) ((utf32 >> 10) + 0xD800),
  				(char) (utf32 % 0x0400 + 0xDC00)});
  		}
  
  		public static int ConvertToUtf32 (char highSurrogate, char lowSurrogate)
  		{
  			if (highSurrogate < 0xD800 || 0xDBFF < highSurrogate)
  				throw new ArgumentOutOfRangeException ("highSurrogate");
  			if (lowSurrogate < 0xDC00 || 0xDFFF < lowSurrogate)
  				throw new ArgumentOutOfRangeException ("lowSurrogate");
  
  			return 0x10000 + ((highSurrogate - 0xD800) << 10) + (lowSurrogate - 0xDC00);
  		}
  
  		public static int ConvertToUtf32 (string s, int index)
  		{
  			CheckParameter (s, index);
  
  			if (!Char.IsSurrogate (s [index]))
  				return s [index];
  			if (!Char.IsHighSurrogate (s [index])
  			    || index == s.Length - 1
  			    || !Char.IsLowSurrogate (s [index + 1]))
  				throw new ArgumentException (String.Format ("The string contains invalid surrogate pair character at {0}", index));
  			return ConvertToUtf32 (s [index], s [index + 1]);
  		}
  
  		public bool Equals (char obj)
  		{
  			return m_value == obj;
  		}
  
  		public static bool IsSurrogatePair (char highSurrogate, char lowSurrogate)
  		{
  			return '\uD800' <= highSurrogate && highSurrogate <= '\uDBFF'
  				&& '\uDC00' <= lowSurrogate && lowSurrogate <= '\uDFFF';
  		}
  
  		public static bool IsSurrogatePair (string s, int index)
  		{
  			CheckParameter (s, index);
  			return index + 1 < s.Length && IsSurrogatePair (s [index], s [index + 1]);
  		}
  
  		public override int GetHashCode ()
  		{
  			return m_value;
  		}
  
  		public static double GetNumericValue (char c)
  		{
  			if (c > (char)0x3289) {
  				if (c >= (char)0xFF10 && c <= (char)0xFF19)
  					return (c - 0xFF10); // Numbers 0-9
  
  				// Default not set data
  				return -1;
  			}
  			else {
  				unsafe {
  					return numeric_data_values [numeric_data [c]];
  				}
  			}
  			
  		}
  
  		public static double GetNumericValue (string s, int index)
  		{
  			CheckParameter (s, index);
  			return GetNumericValue (s[index]);
  		}
  
  		public static UnicodeCategory GetUnicodeCategory (char c)
  		{
  			unsafe {
  				return (UnicodeCategory)(category_data [c]);
  			}
  		}
  
  		public static UnicodeCategory GetUnicodeCategory (string s, int index)
  		{
  			CheckParameter (s, index);
  			UnicodeCategory c = GetUnicodeCategory (s [index]);
  
  			if ((byte)c == category_check_pair &&
  			    IsSurrogatePair (s, index)) {
  				int u = ConvertToUtf32 (s [index], s [index + 1]);
  				unsafe {
  					// ConvertToUtf32 guarantees 0x10000 <= u <= 0x10ffff
  					int x = (category_astral_index [(u - 0x10000) >> 8] << 8) + (u & 0xff);
  
  					c = (UnicodeCategory)category_data [x];
  				}
  			}
  
  			return c;
  		}
  
  		public static bool IsControl (char c)
  		{
  			unsafe {
  				return (category_data [c] == (byte)UnicodeCategory.Control);
  			}
  		}
  
  		public static bool IsControl (string s, int index)
  		{
  			CheckParameter (s, index);
  			return IsControl (s[index]);
  		}	
  
  		public static bool IsDigit (char c)
  		{
  			unsafe {
  				return (category_data [c] == (byte)UnicodeCategory.DecimalDigitNumber);
  			}
  		}
  
  		public static bool IsDigit (string s, int index)
  		{
  			CheckParameter (s, index);
  			return IsDigit (s[index]);
  		}
  
  		public static bool IsHighSurrogate (char c)
  		{
  			return c >= '\uD800' && c <= '\uDBFF';
  		}
  
  		public static bool IsHighSurrogate (string s, int index)
  		{
  			CheckParameter (s, index);
  			return IsHighSurrogate (s [index]);
  		}
  
  		public static bool IsLetter (char c)
  		{
  			unsafe {
  				return category_data [c] <= ((byte)UnicodeCategory.OtherLetter);
  			}
  		}
  
  		public static bool IsLetter (string s, int index)
  		{
  			CheckParameter (s, index);
  			return IsLetter (s[index]);
  		}
  
  		public static bool IsLetterOrDigit (char c)
  		{
  			unsafe {
  				int category = category_data [c];
  				return (category <= ((int)UnicodeCategory.OtherLetter) ||
  				        category == ((int)UnicodeCategory.DecimalDigitNumber));
  			}
  		}
  
  		public static bool IsLetterOrDigit (string s, int index)
  		{
  			CheckParameter (s, index);
  			return IsLetterOrDigit (s[index]);
  		}
  
  		public static bool IsLower (char c)
  		{
  			unsafe {
  				return (category_data [c] == (byte)UnicodeCategory.LowercaseLetter);
  			}
  		}
  
  		public static bool IsLower (string s, int index)
  		{
  			CheckParameter (s, index);
  			return IsLower (s[index]);
  		}
  
  		public static bool IsLowSurrogate (char c)
  		{
  			return c >= '\uDC00' && c <= '\uDFFF';
  		}
  
  		public static bool IsLowSurrogate (string s, int index)
  		{
  			CheckParameter (s, index);
  			return IsLowSurrogate (s [index]);
  		}
  
  		public static bool IsNumber (char c)
  		{
  			unsafe {
  				int category = category_data [c];
  				return (category >= ((int)UnicodeCategory.DecimalDigitNumber) &&
  				        category <= ((int)UnicodeCategory.OtherNumber));
  			}
  		}
  
  		public static bool IsNumber (string s, int index)
  		{
  			CheckParameter (s, index);
  			return IsNumber (s[index]);
  		}
  
  		public static bool IsPunctuation (char c)
  		{
  			unsafe {
  				int category = category_data [c];
  				return (category >= ((int)UnicodeCategory.ConnectorPunctuation) &&
  				        category <= ((int)UnicodeCategory.OtherPunctuation));
  			}
  		}
  
  		public static bool IsPunctuation (string s, int index)
  		{
  			CheckParameter (s, index);
  			return IsPunctuation (s[index]);
  		}
  
  		public static bool IsSeparator (char c)
  		{
  			unsafe {
  				int category = category_data [c];
  				return (category >= ((int)UnicodeCategory.SpaceSeparator) &&
  
gt: 				        category <= ((int)UnicodeCategory.ParagraphSeparator));