context: /// <summary>
  /// Copyright (C) 2004-2014 Free Software Foundation, Inc.
  ///
  /// Author: Alexander Gnauck AG-Software, mailto:gnauck@ag-software.de
  ///
  /// This file is part of GNU Libidn.
  ///
  /// GNU Libidn is free software: you can redistribute it and/or
  /// modify it under the terms of either:
  ///
  ///   * the GNU Lesser General Public License as published by the Free
  ///     Software Foundation; either version 3 of the License, or (at
  ///     your option) any later version.
  ///
  /// or
  ///
  ///   * the GNU General Public License as published by the Free
  ///     Software Foundation; either version 2 of the License, or (at
  ///     your option) any later version.
  ///
  /// or both in parallel, as here.
  ///
  /// GNU Libidn is distributed in the hope that it will be useful,
  /// but WITHOUT ANY WARRANTY; without even the implied warranty of
  /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  /// General Public License for more details.
  ///
  /// You should have received copies of the GNU General Public License and
  /// the GNU Lesser General Public License along with this program.  If
  /// not, see <http://www.gnu.org/licenses/>.
  /// </summary>
  
  using System;
  using System.Text;
  
  namespace Gnu.Inet.Encoding
  {		
  	public class NFKC
  	{
  		/// <summary>
          /// Applies NFKC normalization to a string.
          /// </summary>
  		/// <param name="in">The string to normalize.</param>
  		/// <returns> An NFKC normalized string.</returns>
  		public static string NormalizeNFKC(string sbIn)
  		{
  			StringBuilder sbOut = new StringBuilder();
  			
  			for (int i = 0; i < sbIn.Length; i++)
  			{
  				char code = sbIn[i];
  				
  				// In Unicode 3.0, Hangul was defined as the block from U+AC00
  				// to U+D7A3, however, since Unicode 3.2 the block extends until
  				// U+D7AF. The decomposeHangul function only decomposes until
  				// U+D7A3. Should this be changed?
  				if (code >= 0xAC00 && code <= 0xD7AF)
  				{
  					sbOut.Append(decomposeHangul(code));
  				}
  				else
  				{
  					int index = decomposeIndex(code);
  					if (index == - 1)
  					{
  						sbOut.Append(code);
  					}
  					else
  					{
  						sbOut.Append(DecompositionMappings.m[index]);
  					}
  				}
  			}
  			
  			// Bring the stringbuffer into canonical order.
  			canonicalOrdering(sbOut);
  			
  			// Do the canonical composition.
  			int last_cc = 0;
  			int last_start = 0;
  			
  			for (int i = 0; i < sbOut.Length; i++)
  			{
  				int cc = combiningClass(sbOut[i]);
  				
  				if (i > 0 && (last_cc == 0 || last_cc != cc))
  				{
  					// Try to combine characters
  					char a = sbOut[last_start];
  					char b = sbOut[i];
  					
  					int c = compose(a, b);
  					
  					if (c != - 1)
  					{
  						sbOut[last_start] = (char) c;
  						//sbOut.deleteCharAt(i);
  						sbOut.Remove(i, 1);
  						i--;
  						
  						if (i == last_start)
  						{
  							last_cc = 0;
  						}
  						else
  						{
  							last_cc = combiningClass(sbOut[i - 1]);
  						}
  						continue;
  					}
  				}
  				
  				if (cc == 0)
  				{
  					last_start = i;
  				}
  				
  				last_cc = cc;
  			}
  			
  			return sbOut.ToString();
  		}
  		
  		
  		/// <summary>
          /// Returns the index inside the decomposition table, implemented
  		/// using a binary search.
  		/// </summary>
  		/// <param name="c">Character to look up.</param>
  		/// <returns> Index if found, -1 otherwise.</returns>
  		internal static int decomposeIndex(char c)
  		{
  			int start = 0;
  			int end = DecompositionKeys.k.Length / 2;
  			
  			while (true)
  			{
  				int half = (start + end) / 2;
  				int code = DecompositionKeys.k[half * 2];
  				
  				if (c == code)
  				{
  					return DecompositionKeys.k[half * 2 + 1];
  				}
  				if (half == start)
  				{
  					// Character not found
  					return - 1;
  				}
  				else if (c > code)
  				{
  					start = half;
  				}
  				else
  				{
  					end = half;
  				}
  			}
  		}
  		
  		/// <summary>
          /// Returns the combining class of a given character.
  		/// </summary>
  		/// <param name="c">The character.</param>
  		/// <returns> The combining class.</returns>
  		internal static int combiningClass(char c)
  		{
  			int h = c >> 8;
  			int l = c & 0xff;
  			
  			int i = CombiningClass.i[h];
  			if (i > - 1)
  			{
  				return CombiningClass.c[i, l];
  			}
  			else
  			{
  				return 0;
  			}
  		}
  		
  		/// <summary>
          /// Rearranges characters in a stringbuffer in order to respect the
  		/// canonical ordering properties.
  		/// </summary>
  		/// <param name="The">StringBuffer to rearrange.</param>
  		internal static void  canonicalOrdering(StringBuilder sbIn)
  		{
  			bool isOrdered = false;
  			
  			while (!isOrdered)
  			{
  				isOrdered = true;
  				
  
  				// 24.10.2005
  				int lastCC = 0;
  				if (sbIn.Length > 0)
  					lastCC = combiningClass(sbIn[0]);
  				
  				for (int i = 0; i < sbIn.Length - 1; i++)
  				{
  					int nextCC = combiningClass(sbIn[i + 1]);
  					if (nextCC != 0 && lastCC > nextCC)
  					{
  						for (int j = i + 1; j > 0; j--)
  						{
  							if (combiningClass(sbIn[j - 1]) <= nextCC)
  							{
  								break;
  							}
  							char t = sbIn[j];
  							sbIn[j] = sbIn[j - 1];
  							sbIn[j - 1] = t;
  							isOrdered = false;
  						}
  						nextCC = lastCC;
  					}
  					lastCC = nextCC;
  				}
  			}
  		}
  		
  		/// <summary>
          /// Returns the index inside the composition table.		
  		/// </summary>
  		/// <param name="a">Character to look up.</param>
  		/// <returns> Index if found, -1 otherwise.</returns>
  		internal static int composeIndex(char a)
  		{
  			if (a >> 8 >= Composition.composePage.Length)
  			{
  				return - 1;
  			}
  
gt: 			int ap = Composition.composePage[a >> 8];