Package com.ibm.icu.impl

Source Code of com.ibm.icu.impl.NormalizerImpl$AuxTrieImpl

/*
*******************************************************************************
* Copyright (C) 1996-2008, International Business Machines Corporation and    *
* others. All Rights Reserved.                                                *
*******************************************************************************
*/
package com.ibm.icu.impl;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.util.MissingResourceException;

import com.ibm.icu.text.Normalizer;
import com.ibm.icu.text.UTF16;   
import com.ibm.icu.text.UnicodeSet;
import com.ibm.icu.text.UnicodeSetIterator;
import com.ibm.icu.util.RangeValueIterator;
import com.ibm.icu.util.VersionInfo;
import com.ibm.icu.lang.UCharacter;

/**
* @version     1.0
* @author  Ram Viswanadha
*/
public final class NormalizerImpl {
    // Static block for the class to initialize its own self
    static final NormalizerImpl IMPL;
   
    static
    {
        try
        {
            IMPL = new NormalizerImpl();
        }
        catch (Exception e)
        {
            throw new MissingResourceException(e.getMessage(), "", "");
        }
    }
   
    static final int UNSIGNED_BYTE_MASK =0xFF;
    static final long UNSIGNED_INT_MASK = 0xffffffffL;
    /*
     * This new implementation of the normalization code loads its data from
     * unorm.icu, which is generated with the gennorm tool.
     * The format of that file is described at the end of this file.
     */
    private static final String DATA_FILE_NAME = ICUResourceBundle.ICU_BUNDLE+"/unorm.icu";
   
    // norm32 value constants
   
    // quick check flags 0..3 set mean "no" for their forms
    public static final int QC_NFC=0x11;          /* no|maybe */
    public static final int QC_NFKC=0x22;         /* no|maybe */
    public static final int QC_NFD=4;             /* no */
    public static final int QC_NFKD=8;            /* no */
   
    public static final int QC_ANY_NO=0xf;

    /* quick check flags 4..5 mean "maybe" for their forms;
     * test flags>=QC_MAYBE
     */
    public static final int QC_MAYBE=0x10;
    public static final int QC_ANY_MAYBE=0x30;

    public static final int QC_MASK=0x3f;

    private static final int COMBINES_FWD=0x40;
    private static final int COMBINES_BACK=0x80;
    public  static final int COMBINES_ANY=0xc0;
    // UnicodeData.txt combining class in bits 15.
    private static final int CC_SHIFT=8;                    
    public  static final int CC_MASK=0xff00;
    // 16 bits for the index to UChars and other extra data
    private static final int EXTRA_SHIFT=16;
    // start of surrogate specials after shift               
    //private static final int EXTRA_INDEX_TOP=0xfc00;      

    //private static final int EXTRA_SURROGATE_MASK=0x3ff;
    //private static final int EXTRA_SURROGATE_TOP=0x3f0;    /* hangul etc. */

    //private static final int EXTRA_HANGUL=EXTRA_SURROGATE_TOP;
    //private static final int EXTRA_JAMO_L=EXTRA_SURROGATE_TOP+1;/* ### not used */
    //private static final int EXTRA_JAMO_V=EXTRA_SURROGATE_TOP+2;
    //private static final int EXTRA_JAMO_T=EXTRA_SURROGATE_TOP+3;
   
    /* norm32 value constants using >16 bits */
    private static final long  MIN_SPECIAL    =  (long)(0xfc000000 & UNSIGNED_INT_MASK);
    private static final long  SURROGATES_TOP =  (long)(0xfff00000 & UNSIGNED_INT_MASK);
    private static final long  MIN_HANGUL     =  (long)(0xfff00000 & UNSIGNED_INT_MASK);
    //private static final long  MIN_JAMO_V     =  (long)(0xfff20000 & UNSIGNED_INT_MASK);
    private static final long  JAMO_V_TOP     =  (long)(0xfff30000 & UNSIGNED_INT_MASK);
   
   
    /* indexes[] value names */
    /* number of bytes in normalization trie */
    static final int INDEX_TRIE_SIZE           = 0;
     /* number of chars in extra data */    
    static final int INDEX_CHAR_COUNT           = 1;   
    /* number of uint16_t words for combining data */
    static final int INDEX_COMBINE_DATA_COUNT = 2;
    /* number of code points that combine forward */    
    static final int INDEX_COMBINE_FWD_COUNT  = 3;
    /* number of code points that combine forward and backward */    
    static final int INDEX_COMBINE_BOTH_COUNT = 4;
    /* number of code points that combine backward */    
    static final int INDEX_COMBINE_BACK_COUNT = 5;    
     /* first code point with quick check NFC NO/MAYBE */
    public static final int INDEX_MIN_NFC_NO_MAYBE   = 6;
    /* first code point with quick check NFKC NO/MAYBE */   
    public static final int INDEX_MIN_NFKC_NO_MAYBE  = 7;
     /* first code point with quick check NFD NO/MAYBE */    
    public static final int INDEX_MIN_NFD_NO_MAYBE   = 8;
    /* first code point with quick check NFKD NO/MAYBE */   
    public static final int INDEX_MIN_NFKD_NO_MAYBE  = 9;    
    /* number of bytes in FCD trie */
    static final int INDEX_FCD_TRIE_SIZE      = 10;
    /* number of bytes in the auxiliary trie */   
    static final int INDEX_AUX_TRIE_SIZE      = 11;
    /* number of uint16_t in the array of serialized USet */   
    static final int INDEX_CANON_SET_COUNT    = 12;   
    /* changing this requires a new formatVersion */
    static final int INDEX_TOP                = 32;   
   
   
    /* AUX constants */
    /* value constants for auxTrie */   
    private static final int AUX_UNSAFE_SHIFT           = 11;
    private static final int AUX_COMP_EX_SHIFT           = 10;
    private static final int AUX_NFC_SKIPPABLE_F_SHIFT = 12;
   
    private static final int AUX_MAX_FNC          =   ((int)1<<AUX_COMP_EX_SHIFT);
    private static final int AUX_UNSAFE_MASK      =   (int)((1<<AUX_UNSAFE_SHIFT) & UNSIGNED_INT_MASK);
    private static final int AUX_FNC_MASK         =   (int)((AUX_MAX_FNC-1) & UNSIGNED_INT_MASK);
    private static final int AUX_COMP_EX_MASK     =   (int)((1<<AUX_COMP_EX_SHIFT) & UNSIGNED_INT_MASK);
    private static final long AUX_NFC_SKIP_F_MASK =   ((UNSIGNED_INT_MASK&1)<<AUX_NFC_SKIPPABLE_F_SHIFT);
   
    /* canonStartSets[0..31] contains indexes for what is in the array */
    /* number of uint16_t in canonical starter sets */
    static final int SET_INDEX_CANON_SETS_LENGTH        = 0;
    /* number of uint16_t in the BMP search table (contains pairs) */
    static final int SET_INDEX_CANON_BMP_TABLE_LENGTH    = 1;
    /* number of uint16_t in the supplementary search table(contains triplets)*/
    static final int SET_INDEX_CANON_SUPP_TABLE_LENGTH  = 2;
    /* changing this requires a new formatVersion */
    static final int SET_INDEX_TOP                        = 32;
   
    static final int CANON_SET_INDICIES_INDEX              = 0;
    static final int CANON_SET_START_SETS_INDEX            = 1;
    static final int CANON_SET_BMP_TABLE_INDEX            = 2;
    static final int CANON_SET_SUPP_TABLE_INDEX            = 3;
    /* 14 bit indexes to canonical USerializedSets */
    static final int CANON_SET_MAX_CANON_SETS             = 0x4000;
    /* single-code point BMP sets are encoded directly in the search table
     * except if result=0x4000..0x7fff
     */
    static final int CANON_SET_BMP_MASK                    = 0xc000;
    static final int CANON_SET_BMP_IS_INDEX                = 0x4000;
   
    private static final int MAX_BUFFER_SIZE                    = 20;
   
    /**
     * Internal option for cmpEquivFold() for decomposing.
     * If not set, just do strcasecmp().
     * @internal
     */
     public static final int COMPARE_EQUIV = 0x80000;
   
    /*******************************/

    /* Wrappers for Trie implementations */
    static final class NormTrieImpl implements Trie.DataManipulate{
        static IntTrie normTrie= null;
       /**
        * Called by com.ibm.icu.util.Trie to extract from a lead surrogate's
        * data the index array offset of the indexes for that lead surrogate.
        * @param property data value for a surrogate from the trie, including
        *         the folding offset
        * @return data offset or 0 if there is no data for the lead surrogate
        */
        /* normTrie: 32-bit trie result may contain a special extraData index with the folding offset */
        public int getFoldingOffset(int value){
            return  BMP_INDEX_LENGTH+
                    ((value>>(EXTRA_SHIFT-SURROGATE_BLOCK_BITS))&
                    (0x3ff<<SURROGATE_BLOCK_BITS));
        }
       
    }
    static final class FCDTrieImpl implements Trie.DataManipulate{
        static CharTrie fcdTrie=null;
       /**
        * Called by com.ibm.icu.util.Trie to extract from a lead surrogate's
        * data the index array offset of the indexes for that lead surrogate.
        * @param property data value for a surrogate from the trie, including
        *         the folding offset
        * @return data offset or 0 if there is no data for the lead surrogate
        */
        /* fcdTrie: the folding offset is the lead FCD value itself */
        public int getFoldingOffset(int value){
            return value;
        }
    }
   
    static final class AuxTrieImpl implements Trie.DataManipulate{
        static CharTrie auxTrie = null;
       /**
        * Called by com.ibm.icu.util.Trie to extract from a lead surrogate's
        * data the index array offset of the indexes for that lead surrogate.
        * @param property data value for a surrogate from the trie, including
        *        the folding offset
        * @return data offset or 0 if there is no data for the lead surrogate
        */
        /* auxTrie: the folding offset is in bits 9..0 of the 16-bit trie result */
        public int getFoldingOffset(int value){
            return (int)(value &AUX_FNC_MASK)<<SURROGATE_BLOCK_BITS;
        }
    }
        
    /****************************************************/
   
   
    private static FCDTrieImpl fcdTrieImpl;
    private static NormTrieImpl normTrieImpl;
    private static AuxTrieImpl auxTrieImpl;
    private static int[] indexes;
    private static char[] combiningTable;
    private static char[] extraData;
    private static Object[] canonStartSets;
   
    private static boolean isDataLoaded;
    private static boolean isFormatVersion_2_1;
    private static boolean isFormatVersion_2_2;
    private static byte[] unicodeVersion;
   
    /**
     * Default buffer size of datafile
     */
    private static final int DATA_BUFFER_SIZE = 25000;
   
    /**
     * FCD check: everything below this code point is known to have a 0
     * lead combining class
     */
    public static final int MIN_WITH_LEAD_CC=0x300;


    /**
     * Bit 7 of the length byte for a decomposition string in extra data is
     * a flag indicating whether the decomposition string is
     * preceded by a 16-bit word with the leading and trailing cc
     * of the decomposition (like for A-umlaut);
     * if not, then both cc's are zero (like for compatibility ideographs).
     */
    private static final int DECOMP_FLAG_LENGTH_HAS_CC=0x80;
    /**
     * Bits 6..0 of the length byte contain the actual length.
     */
    private static final int DECOMP_LENGTH_MASK=0x7f;  
   
    /** Length of the BMP portion of the index (stage 1) array. */
    private static final int BMP_INDEX_LENGTH=0x10000>>Trie.INDEX_STAGE_1_SHIFT_;
    /** Number of bits of a trail surrogate that are used in index table
     * lookups.
     */
    private static final int SURROGATE_BLOCK_BITS=10-Trie.INDEX_STAGE_1_SHIFT_;


   // public utility
   public static int getFromIndexesArr(int index){
        return indexes[index];
   }
  
   // protected constructor ---------------------------------------------
   
    /**
    * Constructor
    * @exception thrown when data reading fails or data corrupted
    */
    private NormalizerImpl() throws IOException {
        //data should be loaded only once
        if(!isDataLoaded){
           
            // jar access
            InputStream i = ICUData.getRequiredStream(DATA_FILE_NAME);
            BufferedInputStream b = new BufferedInputStream(i,DATA_BUFFER_SIZE);
            NormalizerDataReader reader = new NormalizerDataReader(b);
           
            // read the indexes           
            indexes = reader.readIndexes(NormalizerImpl.INDEX_TOP);
           
            byte[] normBytes = new byte[indexes[NormalizerImpl.INDEX_TRIE_SIZE]];
           
            int combiningTableTop = indexes[NormalizerImpl.INDEX_COMBINE_DATA_COUNT];
            combiningTable = new char[combiningTableTop];
           
            int extraDataTop = indexes[NormalizerImpl.INDEX_CHAR_COUNT];
            extraData = new char[extraDataTop];

            byte[] fcdBytes = new byte[indexes[NormalizerImpl.INDEX_FCD_TRIE_SIZE]];
            byte[] auxBytes = new byte[indexes[NormalizerImpl.INDEX_AUX_TRIE_SIZE]];
            canonStartSets=new Object[NormalizerImpl.CANON_SET_MAX_CANON_SETS];
           
            fcdTrieImpl = new FCDTrieImpl();
            normTrieImpl = new NormTrieImpl();
            auxTrieImpl = new AuxTrieImpl();
                       
            // load the rest of the data data and initialize the data members
            reader.read(normBytes, fcdBytes,auxBytes, extraData, combiningTable,
                        canonStartSets);
                                      
            NormTrieImpl.normTrie = new IntTrie( new ByteArrayInputStream(normBytes),normTrieImpl );
            FCDTrieImpl.fcdTrie   = new CharTrie( new ByteArrayInputStream(fcdBytes),fcdTrieImpl  );
            AuxTrieImpl.auxTrie   = new CharTrie( new ByteArrayInputStream(auxBytes),auxTrieImpl  );
           
            // we reached here without any exceptions so the data is fully
            // loaded set the variable to true
            isDataLoaded = true;
           
            // get the data format version                          
            byte[] formatVersion = reader.getDataFormatVersion();
           
            isFormatVersion_2_1 =( formatVersion[0]>2
                                    ||
                                   (formatVersion[0]==2 && formatVersion[1]>=1)
                                 );
            isFormatVersion_2_2 =( formatVersion[0]>2
                                    ||
                                   (formatVersion[0]==2 && formatVersion[1]>=2)
                                 );
            unicodeVersion = reader.getUnicodeVersion();
            b.close();
        }
    }
       
    /* ---------------------------------------------------------------------- */
   
    /* Korean Hangul and Jamo constants */
   
    public static final int JAMO_L_BASE=0x1100;     /* "lead" jamo */
    public static final int JAMO_V_BASE=0x1161;     /* "vowel" jamo */
    public static final int JAMO_T_BASE=0x11a7;     /* "trail" jamo */
   
    public static final int HANGUL_BASE=0xac00;
   
    public static final int JAMO_L_COUNT=19;
    public static final int JAMO_V_COUNT=21;
    public static final int JAMO_T_COUNT=28;
    public  static final int HANGUL_COUNT=JAMO_L_COUNT*JAMO_V_COUNT*JAMO_T_COUNT;
   
    private static boolean isHangulWithoutJamoT(char c) {
        c-=HANGUL_BASE;
        return c<HANGUL_COUNT && c%JAMO_T_COUNT==0;
    }
   
    /* norm32 helpers */
   
    /* is this a norm32 with a regular index? */
    private static boolean isNorm32Regular(long norm32) {
        return norm32<MIN_SPECIAL;
    }
   
    /* is this a norm32 with a special index for a lead surrogate? */
    private static boolean isNorm32LeadSurrogate(long norm32) {
        return MIN_SPECIAL<=norm32 && norm32<SURROGATES_TOP;
    }
   
    /* is this a norm32 with a special index for a Hangul syllable or a Jamo? */
    private static boolean isNorm32HangulOrJamo(long norm32) {
        return norm32>=MIN_HANGUL;
    }
   
    /*
     * Given isNorm32HangulOrJamo(),
     * is this a Hangul syllable or a Jamo?
     */
//    private static  boolean isHangulJamoNorm32HangulOrJamoL(long norm32) {
//        return norm32<MIN_JAMO_V;
//    }
   
    /*
     * Given norm32 for Jamo V or T,
     * is this a Jamo V?
     */
    private static boolean isJamoVTNorm32JamoV(long norm32) {
        return norm32<JAMO_V_TOP;
    }
   
    /* data access primitives ----------------------------------------------- */
   
    public static long/*unsigned*/ getNorm32(char c) {
        return ((UNSIGNED_INT_MASK) & (NormTrieImpl.normTrie.getLeadValue(c)));
    }
   
    public static long/*unsigned*/ getNorm32FromSurrogatePair(long norm32,
                                                               char c2) {
        /*
         * the surrogate index in norm32 stores only the number of the surrogate
         * index block see gennorm/store.c/getFoldedNormValue()
         */
        return ((UNSIGNED_INT_MASK) &
                    NormTrieImpl.normTrie.getTrailValue((int)norm32, c2));
    }
    private static long getNorm32(int c){
        return (UNSIGNED_INT_MASK&(NormTrieImpl.normTrie.getCodePointValue(c)));
    }
   
//    private static long getNorm32(int c,int mask){
//        long/*unsigned*/ norm32= getNorm32(UTF16.getLeadSurrogate(c));
//        if(((norm32&mask)>0) && isNorm32LeadSurrogate(norm32)) {
//            /* c is a lead surrogate, get the real norm32 */
//            norm32=getNorm32FromSurrogatePair(norm32,UTF16.getTrailSurrogate(c));
//        }
//        return norm32;
//    }
   
    /*
     * get a norm32 from text with complete code points
     * (like from decompositions)
     */
    private static long/*unsigned*/ getNorm32(char[] p,int start,
                                              int/*unsigned*/ mask) {
        long/*unsigned*/ norm32= getNorm32(p[start]);
        if(((norm32&mask)>0) && isNorm32LeadSurrogate(norm32)) {
            /* *p is a lead surrogate, get the real norm32 */
            norm32=getNorm32FromSurrogatePair(norm32, p[start+1]);
        }
        return norm32;
    }
    public static VersionInfo getUnicodeVersion(){
        return VersionInfo.getInstance(unicodeVersion[0], unicodeVersion[1],
                                       unicodeVersion[2], unicodeVersion[3]);
    }
    public static char    getFCD16(char c) {
        return  FCDTrieImpl.fcdTrie.getLeadValue(c);
    }
   
    public static char getFCD16FromSurrogatePair(char fcd16, char c2) {
        /* the surrogate index in fcd16 is an absolute offset over the
         * start of stage 1
         * */
        return FCDTrieImpl.fcdTrie.getTrailValue(fcd16, c2);
    }
    public static int getFCD16(int c) {
        return  FCDTrieImpl.fcdTrie.getCodePointValue(c);
    }
       
    private static int getExtraDataIndex(long norm32) {
        return (int)(norm32>>EXTRA_SHIFT);
    }
   
    private static final class DecomposeArgs{
        int /*unsigned byte*/ cc;
        int /*unsigned byte*/ trailCC;
        int length;
    }
    /**
     *
     * get the canonical or compatibility decomposition for one character
     *
     * @return index into the extraData array
     */
    private static int/*index*/ decompose(long/*unsigned*/ norm32,
                                          int/*unsigned*/ qcMask,
                                          DecomposeArgs args) {
        int p= getExtraDataIndex(norm32);
        args.length=extraData[p++];
   
        if((norm32&qcMask&QC_NFKD)!=0 && args.length>=0x100) {
            /* use compatibility decomposition, skip canonical data */
            p+=((args.length>>7)&1)+(args.length&DECOMP_LENGTH_MASK);
            args.length>>=8;
        }
   
        if((args.length&DECOMP_FLAG_LENGTH_HAS_CC)>0) {
            /* get the lead and trail cc's */
            char bothCCs=extraData[p++];
            args.cc=(UNSIGNED_BYTE_MASK) & (bothCCs>>8);
            args.trailCC=(UNSIGNED_BYTE_MASK) & bothCCs;
        } else {
            /* lead and trail cc's are both 0 */
            args.cc=args.trailCC=0;
        }
   
        args.length&=DECOMP_LENGTH_MASK;
        return p;
    }
   
      
    /**
     * get the canonical decomposition for one character
     * @return index into the extraData array
     */
    private static int decompose(long/*unsigned*/ norm32,
                                 DecomposeArgs args) {
                            
        int p= getExtraDataIndex(norm32);
        args.length=extraData[p++];
   
        if((args.length&DECOMP_FLAG_LENGTH_HAS_CC)>0) {
            /* get the lead and trail cc's */
            char bothCCs=extraData[p++];
            args.cc=(UNSIGNED_BYTE_MASK) & (bothCCs>>8);
            args.trailCC=(UNSIGNED_BYTE_MASK) & bothCCs;
        } else {
            /* lead and trail cc's are both 0 */
            args.cc=args.trailCC=0;
        }
   
        args.length&=DECOMP_LENGTH_MASK;
        return p;
    }
   
   
    private static final class NextCCArgs{
        char[] source;
        int next;
        int limit;
        char c;
        char c2;
    }
   
    /*
     * get the combining class of (c, c2)= args.source[args.next++]
     * before: args.next<args.limit  after: args.next<=args.limit
     * if only one code unit is used, then c2==0
     */
    private static int /*unsigned byte*/ getNextCC(NextCCArgs args) {
        long /*unsigned*/ norm32;
   
        args.c=args.source[args.next++];
       
        norm32= getNorm32(args.c);
        if((norm32 & CC_MASK)==0) {
            args.c2=0;
            return 0;
        } else {
            if(!isNorm32LeadSurrogate(norm32)) {
                args.c2=0;
            } else {
                /* c is a lead surrogate, get the real norm32 */
                if(args.next!=args.limit &&
                        UTF16.isTrailSurrogate(args.c2=args.source[args.next])){
                    ++args.next;
                    norm32=getNorm32FromSurrogatePair(norm32, args.c2);
                } else {
                    args.c2=0;
                    return 0;
                }
            }
   
            return (int)((UNSIGNED_BYTE_MASK) & (norm32>>CC_SHIFT));
        }
    }

    private static final class PrevArgs{
        char[] src;
        int start;
        int current;
        char c;
        char c2;
    }
   
    /*
     * read backwards and get norm32
     * return 0 if the character is <minC
     * if c2!=0 then (c2, c) is a surrogate pair (reversed - c2 is first
     * surrogate but read second!)
     */
    private static long /*unsigned*/ getPrevNorm32(PrevArgs args,
                                                      int/*unsigned*/ minC,
                                                      int/*unsigned*/ mask) {
        long/*unsigned*/ norm32;
   
        args.c=args.src[--args.current];
        args.c2=0;
   
        /* check for a surrogate before getting norm32 to see if we need to
         * predecrement further
         */
        if(args.c<minC) {
            return 0;
        } else if(!UTF16.isSurrogate(args.c)) {
            return getNorm32(args.c);
        } else if(UTF16.isLeadSurrogate(args.c)) {
            /* unpaired first surrogate */
            return 0;
        } else if(args.current!=args.start &&
                    UTF16.isLeadSurrogate(args.c2=args.src[args.current-1])) {
            --args.current;
            norm32=getNorm32(args.c2);
   
            if((norm32&mask)==0) {
                /* all surrogate pairs with this lead surrogate have
                 * only irrelevant data
                 */
                return 0;
            } else {
                /* norm32 must be a surrogate special */
                return getNorm32FromSurrogatePair(norm32, args.c);
            }
        } else {
            /* unpaired second surrogate */
            args.c2=0;
            return 0;
        }
    }
   
    /*
     * get the combining class of (c, c2)=*--p
     * before: start<p  after: start<=p
     */
    private static int /*unsigned byte*/ getPrevCC(PrevArgs args) {

        return (int)((UNSIGNED_BYTE_MASK)&(getPrevNorm32(args, MIN_WITH_LEAD_CC,
                                                         CC_MASK)>>CC_SHIFT));
    }

    /*
     * is this a safe boundary character for NF*D?
     * (lead cc==0)
     */
    public static boolean isNFDSafe(long/*unsigned*/ norm32,
                                     int/*unsigned*/ccOrQCMask,
                                     int/*unsigned*/ decompQCMask) {
        if((norm32&ccOrQCMask)==0) {
            return true; /* cc==0 and no decomposition: this is NF*D safe */
        }
   
        /* inspect its decomposition - maybe a Hangul but not a surrogate here*/
        if(isNorm32Regular(norm32) && (norm32&decompQCMask)!=0) {
            DecomposeArgs args=new DecomposeArgs();
            /* decomposes, get everything from the variable-length extra data */
            decompose(norm32, decompQCMask, args);
            return args.cc==0;
        } else {
            /* no decomposition (or Hangul), test the cc directly */
            return (norm32&CC_MASK)==0;
        }
    }
   
    /*
     * is this (or does its decomposition begin with) a "true starter"?
     * (cc==0 and NF*C_YES)
     */
    public static boolean isTrueStarter(long/*unsigned*/ norm32,
                                          int/*unsigned*/ ccOrQCMask,
                                          int/*unsigned*/ decompQCMask) {
        if((norm32&ccOrQCMask)==0) {
            return true; /* this is a true starter (could be Hangul or Jamo L)*/
        }
   
        /* inspect its decomposition - not a Hangul or a surrogate here */
        if((norm32&decompQCMask)!=0) {
            int p; /* index into extra data array */
            DecomposeArgs args=new DecomposeArgs();
            /* decomposes, get everything from the variable-length extra data */
            p=decompose(norm32, decompQCMask, args);
         
            if(args.cc==0) {
                int/*unsigned*/ qcMask=ccOrQCMask&QC_MASK;
   
                /* does it begin with NFC_YES? */
                if((getNorm32(extraData,p, qcMask)&qcMask)==0) {
                    /* yes, the decomposition begins with a true starter */
                    return true;
                }
            }
        }
        return false;
    }

    /* reorder UTF-16 in-place ---------------------------------------------- */
   
    /**
     * simpler, single-character version of mergeOrdered() -
     * bubble-insert one single code point into the preceding string
     * which is already canonically ordered
     * (c, c2) may or may not yet have been inserted at src[current]..src[p]
     *
     * it must be p=current+lengthof(c, c2) i.e. p=current+(c2==0 ? 1 : 2)
     *
     * before: src[start]..src[current] is already ordered, and
     *         src[current]..src[p]     may or may not hold (c, c2) but
     *                          must be exactly the same length as (c, c2)
     * after: src[start]..src[p] is ordered
     *
     * @return the trailing combining class
     */
    private static int/*unsigned byte*/ insertOrdered(char[] source,
                                                      int start,
                                                      int current, int p,
                                                         char c, char c2,
                                                         int/*unsigned byte*/ cc) {
        int back, preBack;
        int r;
        int prevCC, trailCC=cc;
   
        if(start<current && cc!=0) {
            // search for the insertion point where cc>=prevCC
            preBack=back=current;
            PrevArgs prevArgs = new PrevArgs();
            prevArgs.current  = current;
            prevArgs.start    = start;
            prevArgs.src      = source;
            // get the prevCC
            prevCC=getPrevCC(prevArgs);
            preBack = prevArgs.current;
           
            if(cc<prevCC) {
                // this will be the last code point, so keep its cc
                trailCC=prevCC;
                back=preBack;
                while(start<preBack) {
                    prevCC=getPrevCC(prevArgs);
                    preBack=prevArgs.current;
                    if(cc>=prevCC) {
                        break;
                    }
                    back=preBack;
                }
   
               
                // this is where we are right now with all these indicies:
                // [start]..[pPreBack] 0..? code points that we can ignore
                // [pPreBack]..[pBack] 0..1 code points with prevCC<=cc
                // [pBack]..[current] 0..n code points with >cc, move up to insert (c, c2)
                // [current]..[p]         1 code point (c, c2) with cc
                
                // move the code units in between up
                r=p;
                do {
                    source[--r]=source[--current];
                } while(back!=current);
            }
        }
   
        // insert (c, c2)
        source[current]=c;
        if(c2!=0) {
            source[(current+1)]=c2;
        }
   
        // we know the cc of the last code point
        return trailCC;
    }
   
    /**
     * merge two UTF-16 string parts together
     * to canonically order (order by combining classes) their concatenation
     *
     * the two strings may already be adjacent, so that the merging is done
     * in-place if the two strings are not adjacent, then the buffer holding the
     * first one must be large enough
     * the second string may or may not be ordered in itself
     *
     * before: [start]..[current] is already ordered, and
     *         [next]..[limit]    may be ordered in itself, but
     *                          is not in relation to [start..current[
     * after: [start..current+(limit-next)[ is ordered
     *
     * the algorithm is a simple bubble-sort that takes the characters from
     * src[next++] and inserts them in correct combining class order into the
     * preceding part of the string
     *
     * since this function is called much less often than the single-code point
     * insertOrdered(), it just uses that for easier maintenance
     *
     * @return the trailing combining class
     */
    private static int /*unsigned byte*/ mergeOrdered(char[] source,
                                                      int start,
                                                      int current,
                                                      char[] data,
                                                        int next,
                                                        int limit,
                                                        boolean isOrdered) {
            int r;
            int /*unsigned byte*/ cc, trailCC=0;
            boolean adjacent;
       
            adjacent= current==next;
            NextCCArgs ncArgs = new NextCCArgs();
            ncArgs.source = data;
            ncArgs.next   = next;
            ncArgs.limit  = limit;
           
            if(start!=current || !isOrdered) {
                   
                while(ncArgs.next<ncArgs.limit) {
                    cc=getNextCC(ncArgs);
                    if(cc==0) {
                        // does not bubble back
                        trailCC=0;
                        if(adjacent) {
                            current=ncArgs.next;
                        } else {
                            data[current++]=ncArgs.c;
                            if(ncArgs.c2!=0) {
                                data[current++]=ncArgs.c2;
                            }
                        }
                        if(isOrdered) {
                            break;
                        } else {
                            start=current;
                        }
                    } else {
                        r=current+(ncArgs.c2==0 ? 1 : 2);
                        trailCC=insertOrdered(source,start, current, r,
                                              ncArgs.c, ncArgs.c2, cc);
                        current=r;
                    }
                }
            }
       
            if(ncArgs.next==ncArgs.limit) {
                // we know the cc of the last code point
                return trailCC;
            } else {
                if(!adjacent) {
                    // copy the second string part
                    do {
                        source[current++]=data[ncArgs.next++];
                    } while(ncArgs.next!=ncArgs.limit);
                    ncArgs.limit=current;
                }
                PrevArgs prevArgs = new PrevArgs();
                prevArgs.src   = data;
                prevArgs.start = start;
                prevArgs.current =  ncArgs.limit;
                return getPrevCC(prevArgs);
            }

    }
    private static int /*unsigned byte*/ mergeOrdered(char[] source,
                                                      int start,
                                                      int current,
                                                      char[] data,
                                                        final int next,
                                                        final int limit) {
        return mergeOrdered(source,start,current,data,next,limit,true);
    }

   
     
    public static boolean checkFCD(char[] src,int srcStart, int srcLimit,
                                   UnicodeSet nx) {

        char fcd16,c,c2;
        int prevCC=0, cc;
        int i =srcStart, length = srcLimit;
   
        for(;;) {
            for(;;) {
                if(i==length) {
                    return true;
                } else if((c=src[i++])<MIN_WITH_LEAD_CC) {
                    prevCC=(int)-c;
                } else if((fcd16=getFCD16(c))==0) {
                    prevCC=0;
                } else {
                    break;
                }
            }

            // check one above-minimum, relevant code unit
            if(UTF16.isLeadSurrogate(c)) {
                // c is a lead surrogate, get the real fcd16
                if(i!=length && UTF16.isTrailSurrogate(c2=src[i])) {
                    ++i;
                    fcd16=getFCD16FromSurrogatePair(fcd16, c2);
                } else {
                    c2=0;
                    fcd16=0;
                }
            }else{
                c2=0;
            }
           
            if(nx_contains(nx, c, c2)) {
                prevCC=0; /* excluded: fcd16==0 */
                continue;
            }

            // prevCC has values from the following ranges:
            // 0..0xff -the previous trail combining class
            // <0      -the negative value of the previous code unit;
            //          that code unit was <MIN_WITH_LEAD_CC and its getFCD16()
            //          was deferred so that average text is checked faster
            //
   
            // check the combining order
            cc=(int)(fcd16>>8);
            if(cc!=0) {
                if(prevCC<0) {
                    // the previous character was <_NORM_MIN_WITH_LEAD_CC,
                    // we need to get its trail cc
                    //
                    if(!nx_contains(nx, (int)-prevCC)) {
                        prevCC=(int)(FCDTrieImpl.fcdTrie.getBMPValue(
                                             (char)-prevCC)&0xff
                                             );
                    } else {
                        prevCC=0; /* excluded: fcd16==0 */
                    }
                                     
                }
   
                if(cc<prevCC) {
                    return false;
                }
            }
            prevCC=(int)(fcd16&0xff);
        }
    }
   
    public static Normalizer.QuickCheckResult quickCheck(char[] src,
                                                            int srcStart,
                                                            int srcLimit,
                                                            int minNoMaybe,
                                                            int qcMask,
                                                            int options,
                                                            boolean allowMaybe,
                                                            UnicodeSet nx){

        int ccOrQCMask;
        long norm32;
        char c, c2;
        char cc, prevCC;
        long qcNorm32;
        Normalizer.QuickCheckResult result;
        ComposePartArgs args = new ComposePartArgs();
        char[] buffer ;
        int start = srcStart;
       
        if(!isDataLoaded) {
            return Normalizer.MAYBE;
        }
        // initialize
        ccOrQCMask=CC_MASK|qcMask;
        result=Normalizer.YES;
        prevCC=0;
               
        for(;;) {
            for(;;) {
                if(srcStart==srcLimit) {
                    return result;
                } else if((c=src[srcStart++])>=minNoMaybe &&
                                  (( norm32=getNorm32(c)) & ccOrQCMask)!=0) {
                    break;
                }
                prevCC=0;
            }
           
   
            // check one above-minimum, relevant code unit
            if(isNorm32LeadSurrogate(norm32)) {
                // c is a lead surrogate, get the real norm32
                if(srcStart!=srcLimit&& UTF16.isTrailSurrogate(c2=src[srcStart])) {
                    ++srcStart;
                    norm32=getNorm32FromSurrogatePair(norm32,c2);
                } else {
                    norm32=0;
                    c2=0;
                }
            }else{
                c2=0;
            }
            if(nx_contains(nx, c, c2)) {
                /* excluded: norm32==0 */
                norm32=0;
            }
   
            // check the combining order
            cc=(char)((norm32>>CC_SHIFT)&0xFF);
            if(cc!=0 && cc<prevCC) {
                return Normalizer.NO;
            }
            prevCC=cc;
   
            // check for "no" or "maybe" quick check flags
            qcNorm32 = norm32 & qcMask;
            if((qcNorm32& QC_ANY_NO)>=1) {
                result= Normalizer.NO;
                break;
            } else if(qcNorm32!=0) {
                // "maybe" can only occur for NFC and NFKC
                if(allowMaybe){
                    result=Normalizer.MAYBE;
                }else{
                    // normalize a section around here to see if it is really
                    // normalized or not
                    int prevStarter;
                    int/*unsigned*/ decompQCMask;
   
                    decompQCMask=(qcMask<<2)&0xf; // decomposition quick check mask
   
                    // find the previous starter

                    // set prevStarter to the beginning of the current character
                    prevStarter=srcStart-1;
                    if(UTF16.isTrailSurrogate(src[prevStarter])) {
                        // safe because unpaired surrogates do not result
                        // in "maybe"
                        --prevStarter;
                    }

                    prevStarter=findPreviousStarter(src, start, prevStarter,
                                                    ccOrQCMask, decompQCMask,
                                                    (char)minNoMaybe);
   
                    // find the next true starter in [src..limit[ - modifies
                    // src to point to the next starter
                    srcStart=findNextStarter(src,srcStart, srcLimit, qcMask,
                                             decompQCMask,(char) minNoMaybe);
                   
                    //set the args for compose part
                    args.prevCC = prevCC;
                      
                    // decompose and recompose [prevStarter..src[
                    buffer = composePart(args,prevStarter,src,srcStart,srcLimit,options,nx);
   
                    // compare the normalized version with the original
                    if(0!=strCompare(buffer,0,args.length,src,prevStarter,srcStart, false)) {
                        result=Normalizer.NO; // normalization differs
                        break;
                    }
   
                    // continue after the next starter
                }
            }
        }
        return result;
    }
      
    //------------------------------------------------------
    // make NFD & NFKD
    //------------------------------------------------------
    public static int getDecomposition(int c /*UTF-32*/ ,
                                        boolean compat,
                                           char[] dest,
                                           int destStart,
                                           int destCapacity) {
           
        if( (UNSIGNED_INT_MASK & c)<=0x10ffff) {
            long /*unsigned*/ norm32;
            int qcMask;
            int minNoMaybe;
            int length;
   
            // initialize
            if(!compat) {
                minNoMaybe=(int)indexes[INDEX_MIN_NFD_NO_MAYBE];
                qcMask=QC_NFD;
            } else {
                minNoMaybe=(int)indexes[INDEX_MIN_NFKD_NO_MAYBE];
                qcMask=QC_NFKD;
            }
   
            if(c<minNoMaybe) {
                // trivial case
                if(destCapacity>0) {
                    dest[0]=(char)c;
                }
                return -1;
            }
   
            /* data lookup */
            norm32=getNorm32(c);
            if((norm32&qcMask)==0) {
                /* simple case: no decomposition */
                if(c<=0xffff) {
                    if(destCapacity>0) {
                        dest[0]=(char)c;
                    }
                    return -1;
                } else {
                    if(destCapacity>=2) {
                        dest[0]=UTF16.getLeadSurrogate(c);
                        dest[1]=UTF16.getTrailSurrogate(c);
                    }
                    return -2;
                }
            } else if(isNorm32HangulOrJamo(norm32)) {
                /* Hangul syllable: decompose algorithmically */
                char c2;
   
                c-=HANGUL_BASE;
   
                c2=(char)(c%JAMO_T_COUNT);
                c/=JAMO_T_COUNT;
                if(c2>0) {
                    if(destCapacity>=3) {
                        dest[2]=(char)(JAMO_T_BASE+c2);
                    }
                    length=3;
                } else {
                    length=2;
                }
   
                if(destCapacity>=2) {
                    dest[1]=(char)(JAMO_V_BASE+c%JAMO_V_COUNT);
                    dest[0]=(char)(JAMO_L_BASE+c/JAMO_V_COUNT);
                }
                return length;
            } else {
                /* c decomposes, get everything from the variable-length extra
                 * data
                 */
                int p, limit;
                DecomposeArgs args = new DecomposeArgs();
                /* the index into extra data array*/                
                p=decompose(norm32, qcMask, args);
                if(args.length<=destCapacity) {
                    limit=p+args.length;
                    do {
                        dest[destStart++]=extraData[p++];
                    } while(p<limit);
                }
                return args.length;
            }
        } else {
            return 0;
        }
    }

   
    public static int decompose(char[] src,int srcStart,int srcLimit,
                                char[] dest,int destStart,int destLimit,
                                 boolean compat,int[] outTrailCC,
                                 UnicodeSet nx) {
                               
        char[] buffer = new char[3];
        int prevSrc;
        long norm32;
        int ccOrQCMask, qcMask;
        int reorderStartIndex, length;
        char c, c2, minNoMaybe;
        int/*unsigned byte*/ cc, prevCC, trailCC;
        char[] p;
        int pStart;
        int destIndex = destStart;
        int srcIndex = srcStart;
        if(!compat) {
            minNoMaybe=(char)indexes[INDEX_MIN_NFD_NO_MAYBE];
            qcMask=QC_NFD;
        } else {
            minNoMaybe=(char)indexes[INDEX_MIN_NFKD_NO_MAYBE];
            qcMask=QC_NFKD;
        }
   
        /* initialize */
        ccOrQCMask=CC_MASK|qcMask;
        reorderStartIndex=0;
        prevCC=0;
        norm32=0;
        c=0;
        pStart=0;
       
        cc=trailCC=-1;//initialize to bogus value
       
        for(;;) {
            /* count code units below the minimum or with irrelevant data for
             * the quick check
             */
            prevSrc=srcIndex;

            while(srcIndex!=srcLimit &&((c=src[srcIndex])<minNoMaybe ||
                                        ((norm32=getNorm32(c))&ccOrQCMask)==0)){
                prevCC=0;
                ++srcIndex;
            }

            /* copy these code units all at once */
            if(srcIndex!=prevSrc) {
                length=(int)(srcIndex-prevSrc);
                if((destIndex+length)<=destLimit) {
                    System.arraycopy(src,prevSrc,dest,destIndex,length);
                }
             
                destIndex+=length;
                reorderStartIndex=destIndex;
            }
   
            /* end of source reached? */
            if(srcIndex==srcLimit) {
                break;
            }
   
            /* c already contains *src and norm32 is set for it, increment src*/
            ++srcIndex;
   
            /* check one above-minimum, relevant code unit */
            /*
             * generally, set p and length to the decomposition string
             * in simple cases, p==NULL and (c, c2) will hold the length code
             * units to append in all cases, set cc to the lead and trailCC to
             * the trail combining class
             *
             * the following merge-sort of the current character into the
             * preceding, canonically ordered result text will use the
             * optimized insertOrdered()
             * if there is only one single code point to process;
             * this is indicated with p==NULL, and (c, c2) is the character to
             * insert
             * ((c, 0) for a BMP character and (lead surrogate, trail surrogate)
             * for a supplementary character)
             * otherwise, p[length] is merged in with _mergeOrdered()
             */
            if(isNorm32HangulOrJamo(norm32)) {
                if(nx_contains(nx, c)) {
                    c2=0;
                    p=null;
                    length=1;
                } else {
                    // Hangul syllable: decompose algorithmically
                    p=buffer;
                    pStart=0;
                    cc=trailCC=0;
   
                    c-=HANGUL_BASE;
   
                    c2=(char)(c%JAMO_T_COUNT);
                    c/=JAMO_T_COUNT;
                    if(c2>0) {
                        buffer[2]=(char)(JAMO_T_BASE+c2);
                        length=3;
                    } else {
                        length=2;
                    }
   
                    buffer[1]=(char)(JAMO_V_BASE+c%JAMO_V_COUNT);
                    buffer[0]=(char)(JAMO_L_BASE+c/JAMO_V_COUNT);
                }
            } else {
                if(isNorm32Regular(norm32)) {
                    c2=0;
                    length=1;
                } else {
                    // c is a lead surrogate, get the real norm32
                    if(srcIndex!=srcLimit &&
                                    UTF16.isTrailSurrogate(c2=src[srcIndex])) {
                        ++srcIndex;
                        length=2;
                        norm32=getNorm32FromSurrogatePair(norm32, c2);
                    } else {
                        c2=0;
                        length=1;
                        norm32=0;
                    }
                }
   
                /* get the decomposition and the lead and trail cc's */
                if(nx_contains(nx, c, c2)) {
                    /* excluded: norm32==0 */
                    cc=trailCC=0;
                    p=null;
                } else if((norm32&qcMask)==0) {
                    /* c does not decompose */
                    cc=trailCC=(int)((UNSIGNED_BYTE_MASK) & (norm32>>CC_SHIFT));
                    p=null;
                    pStart=-1;
                } else {
                    DecomposeArgs arg = new DecomposeArgs();
                    /* c decomposes, get everything from the variable-length
                     * extra data
                     */
                    pStart=decompose(norm32, qcMask, arg);
                    p=extraData;
                    length=arg.length;
                    cc=arg.cc;
                    trailCC=arg.trailCC;
                    if(length==1) {
                        /* fastpath a single code unit from decomposition */
                        c=p[pStart];
                        c2=0;
                        p=null;
                        pStart=-1;
                    }
                }
            }
   
            /* append the decomposition to the destination buffer, assume
             * length>0
             */
            if((destIndex+length)<=destLimit) {
                int reorderSplit=destIndex;
                if(p==null) {
                    /* fastpath: single code point */
                    if(cc!=0 && cc<prevCC) {
                        /* (c, c2) is out of order with respect to the preceding
                         *  text
                         */
                        destIndex+=length;
                        trailCC=insertOrdered(dest,reorderStartIndex,
                                            reorderSplit, destIndex, c, c2, cc);
                    } else {
                        /* just append (c, c2) */
                        dest[destIndex++]=c;
                        if(c2!=0) {
                            dest[destIndex++]=c2;
                        }
                    }
                } else {
                    /* general: multiple code points (ordered by themselves)
                     * from decomposition
                     */
                    if(cc!=0 && cc<prevCC) {
                        /* the decomposition is out of order with respect to the
                         *  preceding text
                         */
                        destIndex+=length;
                        trailCC=mergeOrdered(dest,reorderStartIndex,
                                          reorderSplit,p, pStart,pStart+length);
                    } else {
                        /* just append the decomposition */
                        do {
                            dest[destIndex++]=p[pStart++];
                        } while(--length>0);
                    }
                }
            } else {
                /* buffer overflow */
                /* keep incrementing the destIndex for preflighting */
                destIndex+=length;
            }
   
            prevCC=trailCC;
            if(prevCC==0) {
                reorderStartIndex=destIndex;
            }
        }
   
        outTrailCC[0]=prevCC;

        return destIndex - destStart;
    }
   
    /* make NFC & NFKC ------------------------------------------------------ */
    private static final class NextCombiningArgs{
        char[] source;
        int start;
        //int limit;
        char c;
        char c2;
        int/*unsigned*/ combiningIndex;
        char /*unsigned byte*/ cc;
    }
   
    /* get the composition properties of the next character */
    private static int /*unsigned*/    getNextCombining(NextCombiningArgs args,
                                                    int limit,
                                                    UnicodeSet nx) {
        long/*unsigned*/ norm32;
        int combineFlags;
        /* get properties */
        args.c=args.source[args.start++];
        norm32=getNorm32(args.c);
       
        /* preset output values for most characters */
        args.c2=0;
        args.combiningIndex=0;
        args.cc=0;
       
        if((norm32&(CC_MASK|COMBINES_ANY))==0) {
            return 0;
        } else {
            if(isNorm32Regular(norm32)) {
                /* set cc etc. below */
            } else if(isNorm32HangulOrJamo(norm32)) {
                /* a compatibility decomposition contained Jamos */
                args.combiningIndex=(int)((UNSIGNED_INT_MASK)&(0xfff0|
                                                        (norm32>>EXTRA_SHIFT)));
                return (int)(norm32&COMBINES_ANY);
            } else {
                /* c is a lead surrogate, get the real norm32 */
                if(args.start!=limit && UTF16.isTrailSurrogate(args.c2=
                                                     args.source[args.start])) {
                    ++args.start;
                    norm32=getNorm32FromSurrogatePair(norm32, args.c2);
                } else {
                    args.c2=0;
                    return 0;
                }
            }
           
            if(nx_contains(nx, args.c, args.c2)) {
                return 0; /* excluded: norm32==0 */
            }
   
            args.cc= (char)((norm32>>CC_SHIFT)&0xff);
       
            combineFlags=(int)(norm32&COMBINES_ANY);
            if(combineFlags!=0) {
                int index = getExtraDataIndex(norm32);
                args.combiningIndex=index>0 ? extraData[(index-1)] :0;
            }
   
            return combineFlags;
        }
    }
   
    /*
     * given a composition-result starter (c, c2) - which means its cc==0,
     * it combines forward, it has extra data, its norm32!=0,
     * it is not a Hangul or Jamo,
     * get just its combineFwdIndex
     *
     * norm32(c) is special if and only if c2!=0
     */
    private static int/*unsigned*/ getCombiningIndexFromStarter(char c,char c2){
        long/*unsigned*/ norm32;
   
        norm32=getNorm32(c);
        if(c2!=0) {
            norm32=getNorm32FromSurrogatePair(norm32, c2);
        }
        return extraData[(getExtraDataIndex(norm32)-1)];
    }
   
    /*
     * Find the recomposition result for
     * a forward-combining character
     * (specified with a pointer to its part of the combiningTable[])
     * and a backward-combining character
     * (specified with its combineBackIndex).
     *
     * If these two characters combine, then set (value, value2)
     * with the code unit(s) of the composition character.
     *
     * Return value:
     * 0    do not combine
     * 1    combine
     * >1   combine, and the composition is a forward-combining starter
     *
     * See unormimp.h for a description of the composition table format.
     */
    private static int/*unsigned*/ combine(char[]table,int tableStart,
                                   int/*unsinged*/ combineBackIndex,
                                    int[] outValues) {
        int/*unsigned*/ key;
        int value,value2;
       
        if(outValues.length<2){
            throw new IllegalArgumentException();
        }
       
        /* search in the starter's composition table */
        for(;;) {
            key=table[tableStart++];
            if(key>=combineBackIndex) {
                break;
            }
            tableStart+= ((table[tableStart]&0x8000) != 0)? 2 : 1;
        }
   
        /* mask off bit 15, the last-entry-in-the-list flag */
        if((key&0x7fff)==combineBackIndex) {
            /* found! combine! */
            value=table[tableStart];
   
            /* is the composition a starter that combines forward? */
            key=(int)((UNSIGNED_INT_MASK)&((value&0x2000)+1));
   
            /* get the composition result code point from the variable-length
             * result value
             */
            if((value&0x8000) != 0) {
                if((value&0x4000) != 0) {
                    /* surrogate pair composition result */
                    value=(int)((UNSIGNED_INT_MASK)&((value&0x3ff)|0xd800));
                    value2=table[tableStart+1];
                } else {
                    /* BMP composition result U+2000..U+ffff */
                    value=table[tableStart+1];
                    value2=0;
                }
            } else {
                /* BMP composition result U+0000..U+1fff */
                value&=0x1fff;
                value2=0;
            }
            outValues[0]=value;
            outValues[1]=value2;   
            return key;
        } else {
            /* not found */
            return 0;
        }
    }
   
   
    private static final class RecomposeArgs{
        char[] source;
        int start;
        int limit;
    }
    /*
     * recompose the characters in [p..limit[
     * (which is in NFD - decomposed and canonically ordered),
     * adjust limit, and return the trailing cc
     *
     * since for NFKC we may get Jamos in decompositions, we need to
     * recompose those too
     *
     * note that recomposition never lengthens the text:
     * any character consists of either one or two code units;
     * a composition may contain at most one more code unit than the original
     * starter, while the combining mark that is removed has at least one code
     * unit
     */
    private static char/*unsigned byte*/ recompose(RecomposeArgs args, int options, UnicodeSet nx) {
        int  remove, q, r;
        int /*unsigned*/ combineFlags;
        int /*unsigned*/ combineFwdIndex, combineBackIndex;
        int /*unsigned*/ result, value=0, value2=0;
        int /*unsigned byte*/  prevCC;
        boolean starterIsSupplementary;
        int starter;
        int[] outValues = new int[2];
        starter=-1;                   /* no starter */
        combineFwdIndex=0;            /* will not be used until starter!=NULL */
        starterIsSupplementary=false; /* will not be used until starter!=NULL */
        prevCC=0;
       
        NextCombiningArgs ncArg = new NextCombiningArgs();
        ncArg.source  = args.source;
       
        ncArg.cc      =0;
        ncArg.c2      =0;   

        for(;;) {
            ncArg.start = args.start;
            combineFlags=getNextCombining(ncArg,args.limit,nx);
            combineBackIndex=ncArg.combiningIndex;
            args.start = ncArg.start;
                       
            if(((combineFlags&COMBINES_BACK)!=0) && starter!=-1) {
                if((combineBackIndex&0x8000)!=0) {
                    /* c is a Jamo V/T, see if we can compose it with the
                     * previous character
                     */
                    /* for the PRI #29 fix, check that there is no intervening combining mark */
                    if((options&BEFORE_PRI_29)!=0 || prevCC==0) {
                        remove=-1; /* NULL while no Hangul composition */
                        combineFlags=0;
                        ncArg.c2=args.source[starter];
                        if(combineBackIndex==0xfff2) {
                            /* Jamo V, compose with previous Jamo L and following
                             * Jamo T
                             */
                            ncArg.c2=(char)(ncArg.c2-JAMO_L_BASE);
                            if(ncArg.c2<JAMO_L_COUNT) {
                                remove=args.start-1;
                                ncArg.c=(char)(HANGUL_BASE+(ncArg.c2*JAMO_V_COUNT+
                                               (ncArg.c-JAMO_V_BASE))*JAMO_T_COUNT);
                                if(args.start!=args.limit &&
                                            (ncArg.c2=(char)(args.source[args.start]
                                             -JAMO_T_BASE))<JAMO_T_COUNT) {
                                    ++args.start;
                                    ncArg.c+=ncArg.c2;
                                 } else {
                                     /* the result is an LV syllable, which is a starter (unlike LVT) */
                                     combineFlags=COMBINES_FWD;
                                }
                                if(!nx_contains(nx, ncArg.c)) {
                                    args.source[starter]=ncArg.c;
                                   } else {
                                    /* excluded */
                                    if(!isHangulWithoutJamoT(ncArg.c)) {
                                        --args.start; /* undo the ++args.start from reading the Jamo T */
                                    }
                                    /* c is modified but not used any more -- c=*(p-1); -- re-read the Jamo V/T */
                                    remove=args.start;
                                }
                            }

                        /*
                         * Normally, the following can not occur:
                         * Since the input is in NFD, there are no Hangul LV syllables that
                         * a Jamo T could combine with.
                         * All Jamo Ts are combined above when handling Jamo Vs.
                         *
                         * However, before the PRI #29 fix, this can occur due to
                         * an intervening combining mark between the Hangul LV and the Jamo T.
                         */
                        } else {
                            /* Jamo T, compose with previous Hangul that does not have a Jamo T */
                            if(isHangulWithoutJamoT(ncArg.c2)) {
                                ncArg.c2+=ncArg.c-JAMO_T_BASE;
                                if(!nx_contains(nx, ncArg.c2)) {
                                    remove=args.start-1;
                                    args.source[starter]=ncArg.c2;
                                }
                            }
                        }
       
                        if(remove!=-1) {
                            /* remove the Jamo(s) */
                            q=remove;
                            r=args.start;
                            while(r<args.limit) {
                                args.source[q++]=args.source[r++];
                            }
                            args.start=remove;
                            args.limit=q;
                        }
       
                        ncArg.c2=0; /* c2 held *starter temporarily */

                        if(combineFlags!=0) {
                            /*
                             * not starter=NULL because the composition is a Hangul LV syllable
                             * and might combine once more (but only before the PRI #29 fix)
                             */

                            /* done? */
                            if(args.start==args.limit) {
                                return (char)prevCC;
                            }

                            /* the composition is a Hangul LV syllable which is a starter that combines forward */
                            combineFwdIndex=0xfff0;

                            /* we combined; continue with looking for compositions */
                            continue;
                        }
                    }

                    /*
                     * now: cc==0 and the combining index does not include
                     * "forward" -> the rest of the loop body will reset starter
                     * to NULL; technically, a composed Hangul syllable is a
                     * starter, but it does not combine forward now that we have
                     * consumed all eligible Jamos; for Jamo V/T, combineFlags
                     * does not contain _NORM_COMBINES_FWD
                     */
   
                } else if(
                    /* the starter is not a Hangul LV or Jamo V/T and */
                    !((combineFwdIndex&0x8000)!=0) &&
                    /* the combining mark is not blocked and */
                    ((options&BEFORE_PRI_29)!=0 ?
                        (prevCC!=ncArg.cc || prevCC==0) :
                        (prevCC<ncArg.cc || prevCC==0)) &&
                    /* the starter and the combining mark (c, c2) do combine */
                    0!=(result=combine(combiningTable,combineFwdIndex,
                                       combineBackIndex, outValues)) &&
                    /* the composition result is not excluded */
                    !nx_contains(nx, (char)value, (char)value2)
                ) {
                    value=outValues[0];
                    value2=outValues[1];
                    /* replace the starter with the composition, remove the
                     * combining mark
                     */
                    remove= ncArg.c2==0 ? args.start-1 : args.start-2; /* index to the combining mark */
   
                    /* replace the starter with the composition */
                    args.source[starter]=(char)value;
                    if(starterIsSupplementary) {
                        if(value2!=0) {
                            /* both are supplementary */
                            args.source[starter+1]=(char)value2;
                        } else {
                            /* the composition is shorter than the starter,
                             * move the intermediate characters forward one */
                            starterIsSupplementary=false;
                            q=starter+1;
                            r=q+1;
                            while(r<remove) {
                                args.source[q++]=args.source[r++];
                            }
                            --remove;
                        }
                    } else if(value2!=0) {
                        /* the composition is longer than the starter,
                         * move the intermediate characters back one */
                        starterIsSupplementary=true;
                        /* temporarily increment for the loop boundary */
                        ++starter;
                        q=remove;
                        r=++remove;
                        while(starter<q) {
                            args.source[--r]=args.source[--q];
                        }
                        args.source[starter]=(char)value2;
                        --starter; /* undo the temporary increment */
                    /* } else { both are on the BMP, nothing more to do */
                    }
   
                    /* remove the combining mark by moving the following text
                     * over it */
                    if(remove<args.start) {
                        q=remove;
                        r=args.start;
                        while(r<args.limit) {
                            args.source[q++]=args.source[r++];
                        }
                        args.start=remove;
                        args.limit=q;
                    }
   
                    /* keep prevCC because we removed the combining mark */
   
                    /* done? */
                    if(args.start==args.limit) {
                        return (char)prevCC;
                    }
   
                    /* is the composition a starter that combines forward? */
                    if(result>1) {
                       combineFwdIndex=getCombiningIndexFromStarter((char)value,
                                                                  (char)value2);
                    } else {
                       starter=-1;
                    }
   
                    /* we combined; continue with looking for compositions */
                    continue;
                }
            }
   
            /* no combination this time */
            prevCC=ncArg.cc;
            if(args.start==args.limit) {
                return (char)prevCC;
            }
   
            /* if (c, c2) did not combine, then check if it is a starter */
            if(ncArg.cc==0) {
                /* found a new starter; combineFlags==0 if (c, c2) is excluded */
                if((combineFlags&COMBINES_FWD)!=0) {
                    /* it may combine with something, prepare for it */
                    if(ncArg.c2==0) {
                        starterIsSupplementary=false;
                        starter=args.start-1;
                    } else {
                        starterIsSupplementary=false;
                        starter=args.start-2;
                    }
                    combineFwdIndex=combineBackIndex;
                } else {
                    /* it will not combine with anything */
                    starter=-1;
                }
            } else if((options&OPTIONS_COMPOSE_CONTIGUOUS)!=0) {
                /* FCC: no discontiguous compositions; any intervening character blocks */
                starter=-1;
            }
        }
    }
  
    // find the last true starter between src[start]....src[current] going
    // backwards and return its index
    private static int findPreviousStarter(char[]src, int srcStart, int current,
                                          int/*unsigned*/ ccOrQCMask,
                                          int/*unsigned*/ decompQCMask,
                                          char minNoMaybe) {
       long norm32;
       PrevArgs args = new PrevArgs();
       args.src = src;
       args.start = srcStart;
       args.current = current;
      
       while(args.start<args.current) {
           norm32= getPrevNorm32(args, minNoMaybe, ccOrQCMask|decompQCMask);
           if(isTrueStarter(norm32, ccOrQCMask, decompQCMask)) {
               break;
           }
       }
       return args.current;
    }
   
    /* find the first true starter in [src..limit[ and return the
     * pointer to it
     */
    private static int/*index*/    findNextStarter(char[] src,int start,int limit,
                                                 int/*unsigned*/ qcMask,
                                                 int/*unsigned*/ decompQCMask,
                                                 char minNoMaybe) {
        int p;
        long/*unsigned*/ norm32;
        int ccOrQCMask;
        char c, c2;
   
        ccOrQCMask=CC_MASK|qcMask;
       
        DecomposeArgs decompArgs = new DecomposeArgs();

        for(;;) {
            if(start==limit) {
                break; /* end of string */
            }
            c=src[start];
            if(c<minNoMaybe) {
                break; /* catches NUL terminater, too */
            }
   
            norm32=getNorm32(c);
            if((norm32&ccOrQCMask)==0) {
                break; /* true starter */
            }
   
            if(isNorm32LeadSurrogate(norm32)) {
                /* c is a lead surrogate, get the real norm32 */
                if((start+1)==limit ||
                                   !UTF16.isTrailSurrogate(c2=(src[start+1]))){
                    /* unmatched first surrogate: counts as a true starter */                 
                    break;
                }
                norm32=getNorm32FromSurrogatePair(norm32, c2);
   
                if((norm32&ccOrQCMask)==0) {
                    break; /* true starter */
                }
            } else {
                c2=0;
            }
   
            /* (c, c2) is not a true starter but its decomposition may be */
            if((norm32&decompQCMask)!=0) {
                /* (c, c2) decomposes, get everything from the variable-length
                 *  extra data */
                p=decompose(norm32, decompQCMask, decompArgs);
   
                /* get the first character's norm32 to check if it is a true
                 * starter */
                if(decompArgs.cc==0 && (getNorm32(extraData,p, qcMask)&qcMask)==0) {
                    break; /* true starter */
                }
            }
   
            start+= c2==0 ? 1 : 2; /* not a true starter, continue */
        }
   
        return start;
    }
   
   
    private static final class ComposePartArgs{
        int prevCC;
        int length;   /* length of decomposed part */
    }
       
     /* decompose and recompose [prevStarter..src[ */
    private static char[] composePart(ComposePartArgs args,
                                      int prevStarter,
                                         char[] src, int start, int limit,
                                       int options,
                                       UnicodeSet nx) {
        int recomposeLimit;
        boolean compat =((options&OPTIONS_COMPAT)!=0);
       
        /* decompose [prevStarter..src[ */
        int[] outTrailCC = new int[1];
        char[] buffer = new char[(limit-prevStarter)*MAX_BUFFER_SIZE];

        for(;;){
            args.length=decompose(src,prevStarter,(start),
                                      buffer,0,buffer.length,
                                      compat,outTrailCC,nx);
            if(args.length<=buffer.length){
                break;
            }else{
                buffer = new char[args.length];
            }
        }
   
        /* recompose the decomposition */
        recomposeLimit=args.length;
         
        if(args.length>=2) {
            RecomposeArgs rcArgs = new RecomposeArgs();
            rcArgs.source    = buffer;
            rcArgs.start    = 0;
            rcArgs.limit    = recomposeLimit;
            args.prevCC=recompose(rcArgs, options, nx);
            recomposeLimit = rcArgs.limit;
        }
       
        /* return with a pointer to the recomposition and its length */
        args.length=recomposeLimit;
        return buffer;
    }
   
    private static boolean composeHangul(char prev, char c,
                                         long/*unsigned*/ norm32,
                                         char[] src,int[] srcIndex, int limit,
                                            boolean compat,
                                         char[] dest,int destIndex,
                                         UnicodeSet nx) {
        int start=srcIndex[0];
        if(isJamoVTNorm32JamoV(norm32)) {
            /* c is a Jamo V, compose with previous Jamo L and
             * following Jamo T */
            prev=(char)(prev-JAMO_L_BASE);
            if(prev<JAMO_L_COUNT) {
                c=(char)(HANGUL_BASE+(prev*JAMO_V_COUNT+
                                                 (c-JAMO_V_BASE))*JAMO_T_COUNT);
   
                /* check if the next character is a Jamo T (normal or
                 * compatibility) */
                if(start!=limit) {
                    char next, t;
   
                    next=src[start];
                    if((t=(char)(next-JAMO_T_BASE))<JAMO_T_COUNT) {
                        /* normal Jamo T */
                        ++start;
                        c+=t;
                    } else if(compat) {
                        /* if NFKC, then check for compatibility Jamo T
                         * (BMP only) */
                        norm32=getNorm32(next);
                        if(isNorm32Regular(norm32) && ((norm32&QC_NFKD)!=0)) {
                            int p /*index into extra data array*/;
                            DecomposeArgs dcArgs = new DecomposeArgs();
                            p=decompose(norm32, QC_NFKD, dcArgs);
                            if(dcArgs.length==1 &&
                                   (t=(char)(extraData[p]-JAMO_T_BASE))
                                                   <JAMO_T_COUNT) {
                                /* compatibility Jamo T */
                                ++start;
                                c+=t;
                            }
                        }
                    }
                }
                if(nx_contains(nx, c)) {
                    if(!isHangulWithoutJamoT(c)) {
                        --start; /* undo ++start from reading the Jamo T */
                    }
                    return false;
                }
                dest[destIndex]=c;
                srcIndex[0]=start;
                return true;
            }
        } else if(isHangulWithoutJamoT(prev)) {
            /* c is a Jamo T, compose with previous Hangul LV that does not
             * contain a Jamo T */
            c=(char)(prev+(c-JAMO_T_BASE));
            if(nx_contains(nx, c)) {
                return false;
            }
            dest[destIndex]=c;
            srcIndex[0]=start;
            return true;
        }
        return false;
    }
    /*
    public static int compose(char[] src, char[] dest,boolean compat, UnicodeSet nx){
        return compose(src,0,src.length,dest,0,dest.length,compat, nx);
    }
    */
   
    public static int compose(char[] src, int srcStart, int srcLimit,
                              char[] dest,int destStart,int destLimit,
                              int options,UnicodeSet nx) {
       
        int prevSrc, prevStarter;
        long/*unsigned*/ norm32;
        int ccOrQCMask, qcMask;
        int  reorderStartIndex, length;
        char c, c2, minNoMaybe;
        int/*unsigned byte*/ cc, prevCC;
        int[] ioIndex = new int[1];
        int destIndex = destStart;
        int srcIndex = srcStart;
       
        if((options&OPTIONS_COMPAT)!=0) {
            minNoMaybe=(char)indexes[INDEX_MIN_NFKC_NO_MAYBE];
            qcMask=QC_NFKC;
        } else {
            minNoMaybe=(char)indexes[INDEX_MIN_NFC_NO_MAYBE];
            qcMask=QC_NFC;
        }
   
        /*
         * prevStarter points to the last character before the current one
         * that is a "true" starter with cc==0 and quick check "yes".
         *
         * prevStarter will be used instead of looking for a true starter
         * while incrementally decomposing [prevStarter..prevSrc[
         * in _composePart(). Having a good prevStarter allows to just decompose
         * the entire [prevStarter..prevSrc[.
         *
         * When _composePart() backs out from prevSrc back to prevStarter,
         * then it also backs out destIndex by the same amount.
         * Therefore, at all times, the (prevSrc-prevStarter) source units
         * must correspond 1:1 to destination units counted with destIndex,
         * except for reordering.
         * This is true for the qc "yes" characters copied in the fast loop,
         * and for pure reordering.
         * prevStarter must be set forward to src when this is not true:
         * In _composePart() and after composing a Hangul syllable.
         *
         * This mechanism relies on the assumption that the decomposition of a
         * true starter also begins with a true starter. gennorm/store.c checks
         * for this.
         */
        prevStarter=srcIndex;
   
        ccOrQCMask=CC_MASK|qcMask;
        /*destIndex=*/reorderStartIndex=0;/* ####TODO#### check this **/
        prevCC=0;
   
        /* avoid compiler warnings */
        norm32=0;
        c=0;
   
        for(;;) {
            /* count code units below the minimum or with irrelevant data for
             * the quick check */
            prevSrc=srcIndex;

            while(srcIndex!=srcLimit && ((c=src[srcIndex])<minNoMaybe ||
                     ((norm32=getNorm32(c))&ccOrQCMask)==0)) {
                prevCC=0;
                ++srcIndex;
            }

   
            /* copy these code units all at once */
            if(srcIndex!=prevSrc) {
                length=(int)(srcIndex-prevSrc);
                if((destIndex+length)<=destLimit) {
                    System.arraycopy(src,prevSrc,dest,destIndex,length);
                }
                destIndex+=length;
                reorderStartIndex=destIndex;
   
                /* set prevStarter to the last character in the quick check
                 * loop */
                prevStarter=srcIndex-1;
                if(UTF16.isTrailSurrogate(src[prevStarter]) &&
                    prevSrc<prevStarter &&
                    UTF16.isLeadSurrogate(src[(prevStarter-1)])) {
                    --prevStarter;
                }
   
                prevSrc=srcIndex;
            }
   
            /* end of source reached? */
            if(srcIndex==srcLimit) {
                break;
            }
   
            /* c already contains *src and norm32 is set for it, increment src*/
            ++srcIndex;
   
            /*
             * source buffer pointers:
             *
             *  all done      quick check   current char  not yet
             *                "yes" but     (c, c2)       processed
             *                may combine
             *                forward
             * [-------------[-------------[-------------[-------------[
             * |             |             |             |             |
             * start         prevStarter   prevSrc       src           limit
             *
             *
             * destination buffer pointers and indexes:
             *
             *  all done      might take    not filled yet
             *                characters for
             *                reordering
             * [-------------[-------------[-------------[
             * |             |             |             |
             * dest      reorderStartIndex destIndex     destCapacity
             */
   
            /* check one above-minimum, relevant code unit */
            /*
             * norm32 is for c=*(src-1), and the quick check flag is "no" or
             * "maybe", and/or cc!=0
             * check for Jamo V/T, then for surrogates and regular characters
             * c is not a Hangul syllable or Jamo L because
             * they are not marked with no/maybe for NFC & NFKC(and their cc==0)
             */
            if(isNorm32HangulOrJamo(norm32)) {
                /*
                 * c is a Jamo V/T:
                 * try to compose with the previous character, Jamo V also with
                 * a following Jamo T, and set values here right now in case we
                 * just continue with the main loop
                 */
                prevCC=cc=0;
                reorderStartIndex=destIndex;
                ioIndex[0]=srcIndex;
                if(
                    destIndex>0 &&
                    composeHangul(src[(prevSrc-1)], c, norm32,src, ioIndex,
                                  srcLimit, (options&OPTIONS_COMPAT)!=0, dest,
                                  destIndex<=destLimit ? destIndex-1: 0,
                                  nx)
                ) {
                    srcIndex=ioIndex[0];
                    prevStarter=srcIndex;
                    continue;
                }
               
                srcIndex = ioIndex[0];
   
                /* the Jamo V/T did not compose into a Hangul syllable, just
                 * append to dest */
                c2=0;
                length=1;
                prevStarter=prevSrc;
            } else {
                if(isNorm32Regular(norm32)) {
                    c2=0;
                    length=1;
                } else {
                    /* c is a lead surrogate, get the real norm32 */
                    if(srcIndex!=srcLimit &&
                                     UTF16.isTrailSurrogate(c2=src[srcIndex])) {
                        ++srcIndex;
                        length=2;
                        norm32=getNorm32FromSurrogatePair(norm32, c2);
                    } else {
                        /* c is an unpaired lead surrogate, nothing to do */
                        c2=0;
                        length=1;
                        norm32=0;
                    }
                }
                ComposePartArgs args =new ComposePartArgs();
               
                /* we are looking at the character (c, c2) at [prevSrc..src[ */
                if(nx_contains(nx, c, c2)) {
                    /* excluded: norm32==0 */
                    cc=0;
                } else if((norm32&qcMask)==0) {
                    cc=(int)((UNSIGNED_BYTE_MASK)&(norm32>>CC_SHIFT));
                } else {
                    char[] p;
   
                    /*
                     * find appropriate boundaries around this character,
                     * decompose the source text from between the boundaries,
                     * and recompose it
                     *
                     * this puts the intermediate text into the side buffer because
                     * it might be longer than the recomposition end result,
                     * or the destination buffer may be too short or missing
                     *
                     * note that destIndex may be adjusted backwards to account
                     * for source text that passed the quick check but needed to
                     * take part in the recomposition
                     */
                    int decompQCMask=(qcMask<<2)&0xf; /* decomposition quick check mask */
                    /*
                     * find the last true starter in [prevStarter..src[
                     * it is either the decomposition of the current character (at prevSrc),
                     * or prevStarter
                     */
                    if(isTrueStarter(norm32, CC_MASK|qcMask, decompQCMask)) {
                        prevStarter=prevSrc;
                    } else {
                        /* adjust destIndex: back out what had been copied with qc "yes" */
                        destIndex-=prevSrc-prevStarter;
                    }
               
                    /* find the next true starter in [src..limit[ */
                    srcIndex=findNextStarter(src, srcIndex,srcLimit, qcMask,
                                               decompQCMask, minNoMaybe);
                    //args.prevStarter = prevStarter;
                    args.prevCC    = prevCC;                   
                    //args.destIndex = destIndex;
                    args.length = length;
                    p=composePart(args,prevStarter,src,srcIndex,srcLimit,options,nx);
                       
                    if(p==null) {
                        /* an error occurred (out of memory) */
                        break;
                    }
                   
                    prevCC      = args.prevCC;
                    length      = args.length;
                   
                    /* append the recomposed buffer contents to the destination
                     * buffer */
                    if((destIndex+args.length)<=destLimit) {
                        int i=0;
                        while(i<args.length) {
                            dest[destIndex++]=p[i++];
                            --length;
                        }
                    } else {
                        /* buffer overflow */
                        /* keep incrementing the destIndex for preflighting */
                        destIndex+=length;
                    }
   
                    prevStarter=srcIndex;
                    continue;
                }
            }
   
            /* append the single code point (c, c2) to the destination buffer */
            if((destIndex+length)<=destLimit) {
                if(cc!=0 && cc<prevCC) {
                    /* (c, c2) is out of order with respect to the preceding
                     * text */
                    int reorderSplit= destIndex;
                    destIndex+=length;
                    prevCC=insertOrdered(dest,reorderStartIndex, reorderSplit,
                                         destIndex, c, c2, cc);
                } else {
                    /* just append (c, c2) */
                    dest[destIndex++]=c;
                    if(c2!=0) {
                        dest[destIndex++]=c2;
                    }
                    prevCC=cc;
                }
            } else {
                /* buffer overflow */
                /* keep incrementing the destIndex for preflighting */
                destIndex+=length;
                prevCC=cc;
            }
        }

        return destIndex - destStart;
    }
    /* make FCD --------------------------------------------------------------*/
   
    private static int/*index*/ findSafeFCD(char[] src, int start, int limit,
                                            char fcd16) {
        char c, c2;
   
        /*
         * find the first position in [src..limit[ after some cc==0 according
         * to FCD data
         *
         * at the beginning of the loop, we have fcd16 from before src
         *
         * stop at positions:
         * - after trail cc==0
         * - at the end of the source
         * - before lead cc==0
         */
        for(;;) {
            /* stop if trail cc==0 for the previous character */
            if((fcd16&0xff)==0) {
                break;
            }
   
            /* get c=*src - stop at end of string */
            if(start==limit) {
                break;
            }
            c=src[start];
   
            /* stop if lead cc==0 for this character */
            if(c<MIN_WITH_LEAD_CC || (fcd16=getFCD16(c))==0) {
                break; /* catches terminating NUL, too */
            }
   
            if(!UTF16.isLeadSurrogate(c)) {
                if(fcd16<=0xff) {
                    break;
                }
                ++start;
            } else if(start+1!=limit &&
                                    (UTF16.isTrailSurrogate(c2=src[start+1]))) {
                /* c is a lead surrogate, get the real fcd16 */
                fcd16=getFCD16FromSurrogatePair(fcd16, c2);
                if(fcd16<=0xff) {
                    break;
                }
                start+=2;
            } else {
                /* c is an unpaired first surrogate, lead cc==0 */
                break;
            }
        }
   
        return start;
    }
   
    private static int/*unsigned byte*/ decomposeFCD(char[] src,
                                                     int start,int decompLimit,
                                                     char[] dest,
                                                     int[] destIndexArr,
                                                     UnicodeSet nx) {
        char[] p=null;
        int pStart=-1;
       
        long /*unsigned int*/ norm32;
        int reorderStartIndex;
        char c, c2;
        int/*unsigned byte*/ prevCC;
        DecomposeArgs args = new DecomposeArgs();
        int destIndex = destIndexArr[0];
        /*
         * canonically decompose [src..decompLimit[
         *
         * all characters in this range have some non-zero cc,
         * directly or in decomposition,
         * so that we do not need to check in the following for quick-check
         * limits etc.
         *
         * there _are_ _no_ Hangul syllables or Jamos in here because they are
         * FCD-safe (cc==0)!
         *
         * we also do not need to check for c==0 because we have an established
         * decompLimit
         */
        reorderStartIndex=destIndex;
        prevCC=0;

        while(start<decompLimit) {
            c=src[start++];
            norm32=getNorm32(c);
            if(isNorm32Regular(norm32)) {
                c2=0;
                args.length=1;
            } else {
                /*
                 * reminder: this function is called with [src..decompLimit[
                 * not containing any Hangul/Jamo characters,
                 * therefore the only specials are lead surrogates
                 */
                /* c is a lead surrogate, get the real norm32 */
                if(start!=decompLimit && UTF16.isTrailSurrogate(c2=src[start])){
                    ++start;
                    args.length=2;
                    norm32=getNorm32FromSurrogatePair(norm32, c2);
                } else {
                    c2=0;
                    args.length=1;
                    norm32=0;
                }
            }
   
            /* get the decomposition and the lead and trail cc's */
            if(nx_contains(nx, c, c2)) {
                /* excluded: norm32==0 */
                args.cc=args.trailCC=0;
                p=null;
            } else if((norm32&QC_NFD)==0) {
                /* c does not decompose */
                args.cc=args.trailCC=(int)((UNSIGNED_BYTE_MASK)&
                                                            (norm32>>CC_SHIFT));
                p=null;
            } else {
                /* c decomposes, get everything from the variable-length extra
                 * data */
                pStart=decompose(norm32, args);
                p=extraData;
                if(args.length==1) {
                    /* fastpath a single code unit from decomposition */
                    c=p[pStart];
                    c2=0;
                    p=null;
                }
            }
   
            /* append the decomposition to the destination buffer, assume
             * length>0 */
            if((destIndex+args.length)<=dest.length) {
                int reorderSplit=destIndex;
                if(p==null) {
                    /* fastpath: single code point */
                    if(args.cc!=0 && args.cc<prevCC) {
                        /* (c, c2) is out of order with respect to the preceding
                         *  text */
                        destIndex+=args.length;
                        args.trailCC=insertOrdered(dest,reorderStartIndex,
                                                   reorderSplit, destIndex,
                                                   c, c2, args.cc);
                    } else {
                        /* just append (c, c2) */
                        dest[destIndex++]=c;
                        if(c2!=0) {
                            dest[destIndex++]=c2;
                        }
                    }
                } else {
                    /* general: multiple code points (ordered by themselves)
                     * from decomposition */
                    if(args.cc!=0 && args.cc<prevCC) {
                        /* the decomposition is out of order with respect to
                         * the preceding text */
                        destIndex+=args.length;
                        args.trailCC=mergeOrdered(dest,reorderStartIndex,
                                                  reorderSplit, p, pStart,
                                                  pStart+args.length);
                    } else {
                        /* just append the decomposition */
                        do {
                            dest[destIndex++]=p[pStart++];
                        } while(--args.length>0);
                    }
                }
            } else {
                /* buffer overflow */
                /* keep incrementing the destIndex for preflighting */
                destIndex+=args.length;
            }
   
            prevCC=args.trailCC;
            if(prevCC==0) {
                reorderStartIndex=destIndex;
            }
        }
        destIndexArr[0]=destIndex;
        return prevCC;
    }
   
    public static int makeFCD(char[] src,  int srcStart,  int srcLimit,
                              char[] dest, int destStart, int destLimit,
                              UnicodeSet nx) {
                          
        int prevSrc, decompStart;
        int destIndex, length;
        char c, c2;
        int /* unsigned int*/ fcd16;
        int prevCC, cc;
   
        /* initialize */
        decompStart=srcStart;
        destIndex=destStart;
        prevCC=0;
        c=0;
        fcd16=0;
        int[] destIndexArr = new int[1];
        destIndexArr[0]=destIndex;
       
        for(;;) {
            /* skip a run of code units below the minimum or with irrelevant
             * data for the FCD check */
            prevSrc=srcStart;

            for(;;) {
                if(srcStart==srcLimit) {
                    break;
                } else if((c=src[srcStart])<MIN_WITH_LEAD_CC) {
                    prevCC=(int)-c;
                } else if((fcd16=getFCD16(c))==0) {
                    prevCC=0;
                } else {
                    break;
                }
                ++srcStart;
            }

   
            /*
             * prevCC has values from the following ranges:
             * 0..0xff - the previous trail combining class
             * <0      - the negative value of the previous code unit;
             *           that code unit was <_NORM_MIN_WITH_LEAD_CC and its
             *           getFCD16()
             *           was deferred so that average text is checked faster
             */
   
            /* copy these code units all at once */
            if(srcStart!=prevSrc) {
                length=(int)(srcStart-prevSrc);
                if((destIndex+length)<=destLimit) {
                    System.arraycopy(src,prevSrc,dest,destIndex,length);
                }
                destIndex+=length;
                prevSrc=srcStart;
   
                /* prevCC<0 is only possible from the above loop, i.e., only if
                 *  prevSrc<src */
                if(prevCC<0) {
                    /* the previous character was <_NORM_MIN_WITH_LEAD_CC, we
                     * need to get its trail cc */
                    if(!nx_contains(nx, (int)-prevCC)) {
                        prevCC=(int)(getFCD16((int)-prevCC)&0xff);
                    } else {
                        prevCC=0; /* excluded: fcd16==0 */
                    }
                    /*
                     * set a pointer to this below-U+0300 character;
                     * if prevCC==0 then it will moved to after this character
                     * below
                     */
                    decompStart=prevSrc-1;
                }
            }
            /*
             * now:
             * prevSrc==src - used later to adjust destIndex before
             *          decomposition
             * prevCC>=0
             */
   
            /* end of source reached? */
            if(srcStart==srcLimit) {
                break;
            }
   
            /* set a pointer to after the last source position where prevCC==0*/
            if(prevCC==0) {
                decompStart=prevSrc;
            }
   
            /* c already contains *src and fcd16 is set for it, increment src */
            ++srcStart;
   
            /* check one above-minimum, relevant code unit */
            if(UTF16.isLeadSurrogate(c)) {
                /* c is a lead surrogate, get the real fcd16 */
                if(srcStart!=srcLimit &&
                                     UTF16.isTrailSurrogate(c2=src[srcStart])) {
                    ++srcStart;
                    fcd16=getFCD16FromSurrogatePair((char)fcd16, c2);
                } else {
                    c2=0;
                    fcd16=0;
                }
            } else {
                c2=0;
            }
   
            /* we are looking at the character (c, c2) at [prevSrc..src[ */
            if(nx_contains(nx, c, c2)) {
                fcd16=0; /* excluded: fcd16==0 */
            }
            /* check the combining order, get the lead cc */
            cc=(int)(fcd16>>8);
            if(cc==0 || cc>=prevCC) {
                /* the order is ok */
                if(cc==0) {
                    decompStart=prevSrc;
                }
                prevCC=(int)(fcd16&0xff);
   
                /* just append (c, c2) */
                length= c2==0 ? 1 : 2;
                if((destIndex+length)<=destLimit) {
                    dest[destIndex++]=c;
                    if(c2!=0) {
                        dest[destIndex++]=c2;
                    }
                } else {
                    destIndex+=length;
                }
            } else {
                /*
                 * back out the part of the source that we copied already but
                 * is now going to be decomposed;
                 * prevSrc is set to after what was copied
                 */
                destIndex-=(int)(prevSrc-decompStart);
   
                /*
                 * find the part of the source that needs to be decomposed;
                 * to be safe and simple, decompose to before the next character
                 * with lead cc==0
                 */
                srcStart=findSafeFCD(src,srcStart, srcLimit, (char)fcd16);
   
                /*
                 * the source text does not fulfill the conditions for FCD;
                 * decompose and reorder a limited piece of the text
                 */
                destIndexArr[0] = destIndex;
                prevCC=decomposeFCD(src,decompStart, srcStart,dest,
                                    destIndexArr,nx);
                decompStart=srcStart;
                destIndex=destIndexArr[0];
            }
        }
       
        return destIndex - destStart;
   
    }

    public static int getCombiningClass(int c) {
        long norm32;
        norm32=getNorm32(c);
        return (char)((norm32>>CC_SHIFT)&0xFF);
    }
   
    public static boolean isFullCompositionExclusion(int c) {
        if(isFormatVersion_2_1) {
            int aux =AuxTrieImpl.auxTrie.getCodePointValue(c);
            return (boolean)((aux & AUX_COMP_EX_MASK)!=0);
        } else {
            return false;
        }
    }
   
    public static boolean isCanonSafeStart(int c) {
        if(isFormatVersion_2_1) {
            int aux = AuxTrieImpl.auxTrie.getCodePointValue(c);
            return (boolean)((aux & AUX_UNSAFE_MASK)==0);
        } else {
            return false;
        }
    }
   
    public static boolean getCanonStartSet(int c, USerializedSet fillSet) {

        if(fillSet!=null && canonStartSets!=null) {
             /*
             * binary search for c
             *
             * There are two search tables,
             * one for BMP code points and one for supplementary ones.
             * See unormimp.h for details.
             */
            char[] table;
            int i=0, start, limit;
           
            int[] idxs = (int[]) canonStartSets[CANON_SET_INDICIES_INDEX];
            char[] startSets = (char[]) canonStartSets[CANON_SET_START_SETS_INDEX];
           
            if(c<=0xffff) {
                table=(char[]) canonStartSets[CANON_SET_BMP_TABLE_INDEX];
                start=0;
                limit=table.length;
   
                /* each entry is a pair { c, result } */
                while(start<limit-2) {
                    i=(char)(((start+limit)/4)*2);
                    if(c<table[i]) {
                        limit=i;
                    } else {
                        start=i;
                    }
                }
                //System.out.println(i);
                /* found? */
                if(c==table[start]) {
                    i=table[start+1];
                    if((i & CANON_SET_BMP_MASK)==CANON_SET_BMP_IS_INDEX) {
                        /* result 01xxxxxx xxxxxx contains index x to a
                         * USerializedSet */
                        i&=(CANON_SET_MAX_CANON_SETS-1);
                        return fillSet.getSet(startSets,(i-idxs.length));
                    } else {
                        /* other result values are BMP code points for
                         * single-code point sets */
                        fillSet.setToOne(i);
                        return true;
                    }
                }
            } else {
                char high, low, h,j=0;
   
                table=(char[]) canonStartSets[CANON_SET_SUPP_TABLE_INDEX];
                start=0;
                limit=table.length;
   
                high=(char)(c>>16);
                low=(char)c;
   
                /* each entry is a triplet { high(c), low(c), result } */
                while(start<limit-3) {
                    /* (start+limit)/2 and address triplets */
                    i=(char)(((start+limit)/6)*3);
                    j=(char)(table[i]&0x1f); /* high word */
                    int tableVal = table[i+1];
                    int lowInt = low;
                    if(high<j || ((tableVal>lowInt) && (high==j))) {
                        limit=i;
                    } else {
                        start=i;
                    }
                   
                    //System.err.println("\t((high==j) && (table[i+1]>low)) == " + ((high==j) && (tableVal>lowInt)) );
                   
                    // KLUDGE: IBM JIT in 1.4.0 is sooo broken
                    // The below lines make TestExhaustive pass
                    if(ICUDebug.enabled()){
                        System.err.println("\t\t j = " + Utility.hex(j,4) +
                                           "\t i = " + Utility.hex(i,4) +
                                           "\t high = "+ Utility.hex(high+
                                           "\t low = "  + Utility.hex(lowInt,4)   +
                                           "\t table[i+1]: "+ Utility.hex(tableVal,4)
                                           );
                    }
                  
                }

                /* found? */
                h=table[start];

                //System.err.println("c: \\U"+ Integer.toHexString(c)+" i : "+Integer.toHexString(i) +" h : " + Integer.toHexString(h));
                int tableVal1 = table[start+1];
                int lowInt = low;

                if(high==(h&0x1f) && lowInt==tableVal1) {
                    int tableVal2 = table[start+2];
                    i=tableVal2;
                    if((h&0x8000)==0) {
                        /* the result is an index to a USerializedSet */
                        return fillSet.getSet(startSets,(i-idxs.length));
                    } else {
                        /*
                         * single-code point set {x} in
                         * triplet { 100xxxxx 000hhhhh  llllllll llllllll  xxxxxxxx xxxxxxxx }
                         */
                        //i|=((int)h & 0x1f00)<<8; /* add high bits from high(c) */
                        int temp = ((int)h & 0x1f00)<<8;
                        i|=temp; /* add high bits from high(c) */
                        fillSet.setToOne((int)i);
                        return true;
                    }
                }
            }
        }
   
        return false; /* not found */
    }
   
    public static int getFC_NFKC_Closure(int c, char[] dest) {
       
        int destCapacity;
        
        if(dest==null ) {
            destCapacity=0;
        }else{
            destCapacity = dest.length;
        }
       
        int aux =AuxTrieImpl.auxTrie.getCodePointValue(c);

        aux&= AUX_FNC_MASK;
        if(aux!=0) {
            int s;
            int index=aux;
            int length;
           
            s =extraData[index];
            if(s<0xff00) {
                /* s points to the single-unit string */
                length=1;
            } else {
                length=s&0xff;
                ++index;
            }
            if(0<length && length<=destCapacity) {
                System.arraycopy(extraData,index,dest,0,length);
            }
            return length;
        } else {
            return 0;
        }
    }


    /* Is c an NF<mode>-skippable code point? See unormimp.h. */
    public static boolean isNFSkippable(int c, Normalizer.Mode mode, long mask) {
        long /*unsigned int*/ norm32;
        mask = mask & UNSIGNED_INT_MASK;
        char aux;
  
        /* check conditions (a)..(e), see unormimp.h */
        norm32 = getNorm32(c);

        if((norm32&mask)!=0) {
            return false; /* fails (a)..(e), not skippable */
        }
   
        if(mode == Normalizer.NFD || mode == Normalizer.NFKD || mode == Normalizer.NONE){
            return true; /* NF*D, passed (a)..(c), is skippable */
        }
        /* check conditions (a)..(e), see unormimp.h */

        /* NF*C/FCC, passed (a)..(e) */
        if((norm32& QC_NFD)==0) {
            return true; /* no canonical decomposition, is skippable */
        }
   
        /* check Hangul syllables algorithmically */
        if(isNorm32HangulOrJamo(norm32)) {
            /* Jamo passed (a)..(e) above, must be Hangul */
            return !isHangulWithoutJamoT((char)c); /* LVT are skippable, LV are not */
        }
   
        /* if(mode<=UNORM_NFKC) { -- enable when implementing FCC */
        /* NF*C, test (f) flag */
        if(!isFormatVersion_2_2) {
            return false; /* no (f) data, say not skippable to be safe */
        }
   

        aux = AuxTrieImpl.auxTrie.getCodePointValue(c);
        return (aux&AUX_NFC_SKIP_F_MASK)==0; /* TRUE=skippable if the (f) flag is not set */
   
        /* } else { FCC, test fcd<=1 instead of the above } */
    }
   
    /*
        private static final boolean
    _enumPropertyStartsRange(const void *context, UChar32 start, UChar32 limit, uint32_t value) {
        // add the start code point to the USet
        uset_add((USet *)context, start);
        return TRUE;
    }
    */

    public static UnicodeSet addPropertyStarts(UnicodeSet set) {
        int c;
      
        /* add the start code point of each same-value range of each trie */
        //utrie_enum(&normTrie, NULL, _enumPropertyStartsRange, set);
        TrieIterator normIter = new TrieIterator(NormTrieImpl.normTrie);
        RangeValueIterator.Element normResult = new RangeValueIterator.Element();
       
        while(normIter.next(normResult)){
            set.add(normResult.start);
        }
       
        //utrie_enum(&fcdTrie, NULL, _enumPropertyStartsRange, set);
        TrieIterator fcdIter  = new TrieIterator(FCDTrieImpl.fcdTrie);
        RangeValueIterator.Element fcdResult = new RangeValueIterator.Element();

        while(fcdIter.next(fcdResult)){
            set.add(fcdResult.start);
        }
       
        if(isFormatVersion_2_1){
            //utrie_enum(&auxTrie, NULL, _enumPropertyStartsRange, set);
            TrieIterator auxIter  = new TrieIterator(AuxTrieImpl.auxTrie);
            RangeValueIterator.Element auxResult = new RangeValueIterator.Element();
            while(auxIter.next(auxResult)){
                set.add(auxResult.start);
            }
        }
        /* add Hangul LV syllables and LV+1 because of skippables */
        for(c=HANGUL_BASE; c<HANGUL_BASE+HANGUL_COUNT; c+=JAMO_T_COUNT) {
            set.add(c);
            set.add(c+1);
        }
        set.add(HANGUL_BASE+HANGUL_COUNT); /* add Hangul+1 to continue with other properties */
        return set; // for chaining
    }

    /**
     * Internal API, used in UCharacter.getIntPropertyValue().
     * @internal
     * @param c code point
     * @param modeValue numeric value compatible with Mode
     * @return numeric value compatible with QuickCheck
     */
    public static final int quickCheck(int c, int modeValue) {
        final int qcMask[/*UNORM_MODE_COUNT*/]={
            0, 0, QC_NFD, QC_NFKD, QC_NFC, QC_NFKC
        };

        int norm32=(int)getNorm32(c)&qcMask[modeValue];

        if(norm32==0) {
            return 1; // YES
        } else if((norm32&QC_ANY_NO)!=0) {
            return 0; // NO
        } else /* _NORM_QC_ANY_MAYBE */ {
            return 2; // MAYBE;
        }
    }

    /**
     * Internal API, used by collation code.
     * Get access to the internal FCD trie table to be able to perform
     * incremental, per-code unit, FCD checks in collation.
     * One pointer is sufficient because the trie index values are offset
     * by the index size, so that the same pointer is used to access the trie
     * data.
     * @internal
     */
    ///CLOVER:OFF
    public CharTrie getFCDTrie(){
        return FCDTrieImpl.fcdTrie;
    }
    ///CLOVER:ON


   
   /* compare canonically equivalent ---------------------------------------- */

    /*
     * Compare two strings for canonical equivalence.
     * Further options include case-insensitive comparison and
     * code point order (as opposed to code unit order).
     *
     * In this function, canonical equivalence is optional as well.
     * If canonical equivalence is tested, then both strings must fulfill
     * the FCD check.
     *
     * Semantically, this is equivalent to
     *   strcmp[CodePointOrder](foldCase(NFD(s1)), foldCase(NFD(s2)))
     * where code point order, NFD and foldCase are all optional.
     *
     * String comparisons almost always yield results before processing both
     * strings completely.
     * They are generally more efficient working incrementally instead of
     * performing the sub-processing (strlen, normalization, case-folding)
     * on the entire strings first.
     *
     * It is also unnecessary to not normalize identical characters.
     *
     * This function works in principle as follows:
     *
     * loop {
     *   get one code unit c1 from s1 (-1 if end of source)
     *   get one code unit c2 from s2 (-1 if end of source)
     *
     *   if(either string finished) {
     *     return result;
     *   }
     *   if(c1==c2) {
     *     continue;
     *   }
     *
     *   // c1!=c2
     *   try to decompose/case-fold c1/c2, and continue if one does;
     *
     *   // still c1!=c2 and neither decomposes/case-folds, return result
     *   return c1-c2;
     * }
     *
     * When a character decomposes, then the pointer for that source changes to
     * the decomposition, pushing the previous pointer onto a stack.
     * When the end of the decomposition is reached, then the code unit reader
     * pops the previous source from the stack.
     * (Same for case-folding.)
     *
     * This is complicated further by operating on variable-width UTF-16.
     * The top part of the loop works on code units, while lookups for decomposition
     * and case-folding need code points.
     * Code points are assembled after the equality/end-of-source part.
     * The source pointer is only advanced beyond all code units when the code point
     * actually decomposes/case-folds.
     *
     * If we were on a trail surrogate unit when assembling a code point,
     * and the code point decomposes/case-folds, then the decomposition/folding
     * result must be compared with the part of the other string that corresponds to
     * this string's lead surrogate.
     * Since we only assemble a code point when hitting a trail unit when the
     * preceding lead units were identical, we back up the other string by one unit
     * in such a case.
     *
     * The optional code point order comparison at the end works with
     * the same fix-up as the other code point order comparison functions.
     * See ustring.c and the comment near the end of this function.
     *
     * Assumption: A decomposition or case-folding result string never contains
     * a single surrogate. This is a safe assumption in the Unicode Standard.
     * Therefore, we do not need to check for surrogate pairs across
     * decomposition/case-folding boundaries.
     * Further assumptions (see verifications tstnorm.cpp):
     * The API function checks for FCD first, while the core function
     * first case-folds and then decomposes. This requires that case-folding does not
     * un-FCD any strings.
     *
     * The API function may also NFD the input and turn off decomposition.
     * This requires that case-folding does not un-NFD strings either.
     *
     * TODO If any of the above two assumptions is violated,
     * then this entire code must be re-thought.
     * If this happens, then a simple solution is to case-fold both strings up front
     * and to turn off UNORM_INPUT_IS_FCD.
     * We already do this when not both strings are in FCD because makeFCD
     * would be a partial NFD before the case folding, which does not work.
     * Note that all of this is only a problem when case-folding _and_
     * canonical equivalence come together.
     *
     * This function could be moved to a different source file, at increased cost
     * for calling the decomposition access function.
     */
   
    // stack element for previous-level source/decomposition pointers
    private static class CmpEquivLevel {
        char[] source;
        int start;
        int s;
        int limit;
    }
   
    /**
     * Get the canonical decomposition for one code point.
     * @param c code point
     * @param buffer out-only buffer for algorithmic decompositions of Hangul
     * @param length out-only, takes the length of the decomposition, if any
     * @return index into the extraData array, or 0 if none
     * @internal
     */
     private static int decompose(int c, char[] buffer) {
       
        long norm32;
        int length=0;
        norm32 = (long) ((UNSIGNED_INT_MASK) & NormTrieImpl.normTrie.getCodePointValue(c));
        if((norm32 & QC_NFD)!=0) {
            if(isNorm32HangulOrJamo(norm32)) {
                /* Hangul syllable: decompose algorithmically */
                char c2;
   
                c-=HANGUL_BASE;
   
                c2=(char)(c%JAMO_T_COUNT);
                c/=JAMO_T_COUNT;
                if(c2>0) {
                    buffer[2]=(char)(JAMO_T_BASE+c2);
                    length=3;
                } else {
                    length=2;
                }
                buffer[1]=(char)(JAMO_V_BASE+c%JAMO_V_COUNT);
                buffer[0]=(char)(JAMO_L_BASE+c/JAMO_V_COUNT);
                return length;
            } else {
                /* normal decomposition */
                DecomposeArgs  args = new DecomposeArgs();
                int index = decompose(norm32, args);
                System.arraycopy(extraData,index,buffer,0,args.length);
                return args.length ;
            }
        } else {
            return 0;
        }
    }

    private static int foldCase(int c, char[] dest, int destStart, int destLimit,
                                 int options){
        String src = UTF16.valueOf(c);
        String foldedStr = UCharacter.foldCase(src,options);
        char[] foldedC = foldedStr.toCharArray();
        for(int i=0;i<foldedC.length;i++){
            if(destStart<destLimit){
                dest[destStart]=foldedC[i];
            }
            // always increment destStart so that we can return
            // the required length
            destStart++;
        }
        return (c==UTF16.charAt(foldedStr,0)) ? -destStart : destStart;
    }
   
    /*
     private static int foldCase(char[] src,int srcStart,int srcLimit,
                                char[] dest, int destStart, int destLimit,
                                int options){
        String source =new String(src,srcStart,(srcLimit-srcStart));
        String foldedStr = UCharacter.foldCase(source,options);
        char[] foldedC = foldedStr.toCharArray();
        for(int i=0;i<foldedC.length;i++){
            if(destStart<destLimit){
                dest[destStart]=foldedC[i];
            }
            // always increment destStart so that we can return
            // the required length
            destStart++;
           
        }
        return destStart;
    }
    */
    public static int cmpEquivFold(String s1, String s2,int options){
        if ((options & Normalizer.COMPARE_IGNORE_CASE) !=0) {
            return cmpSimpleEquivFold(s1, s2, options);
        }
        else {
            return cmpEquivFold(s1.toCharArray(),0,s1.length(),
                                s2.toCharArray(),0,s2.length(),
                                options);
        }
    }


    private static int cmpSimpleEquivFold(String s1, String s2, int options) {
        int cmp = 0;
        int i=0, j=0;
        String foldS1=null;
        String foldS2=null;
        int offset1=1;
        int offset2=1;
        while ((i+offset1<=s1.length() && j+offset2<=s2.length())) {
            if ((cmp!=0) || (s1.charAt(i) != s2.charAt(j))) {
                if(i>0 && j>0 &&
                   (UTF16.isLeadSurrogate((char)s1.charAt(i-1)) ||
                    UTF16.isLeadSurrogate((char)s2.charAt(j-1)))) {
                    // Current codepoint may be the low surrogate pair.
                    return cmpEquivFold(s1.toCharArray(),i-1,s1.length(),
                            s2.toCharArray(),j-1,s2.length(),
                            options);
                }
                else if (UTF16.isLeadSurrogate((char)s1.charAt(i))||
                         UTF16.isLeadSurrogate((char)s2.charAt(j))) {
                    return cmpEquivFold(s1.toCharArray(),i,s1.length(),
                            s2.toCharArray(),j,s2.length(),
                            options);
                }
                else {
                    if ( offset1 > 0 ) {
                        foldS1 = UCharacter.foldCase(s1.substring(i, i+offset1),options);
                    }
                    if ( offset2 > 0 ) {
                        foldS2 = UCharacter.foldCase(s2.substring(j, j+offset2),options);
                    }
                    cmp = foldS1.compareTo(foldS2);
                    if (cmp==0) {
                        i = moveToNext(i, offset1);
                        j = moveToNext(j, offset2);
                        offset1 = offset2 = 1;
                        continue;
                    }
                }
                if (foldS1.length()==foldS2.length()) {
                    return cmp;
                }
                if (foldS1.length()<foldS2.length()) {
                    offset1++;
                    offset2=0;
                }
                else {
                    offset1=0;
                    offset2++;
                }
                continue;
            }
            i++;
            j++;
        }
        if (cmp!=0) {
            return cmp;
        }
        if (i+offset1-1==s1.length()) {
            if (j+offset2-1==s2.length()) {
                return 0;
            }
            else {
                return -1;
            }
        }
        else {
            return 1;
        }
    }
   
    private static int moveToNext(int pos, int offset) {
        if (offset>0) {
            return pos+offset;
        }
        else {
            return pos+1;
        }
    }
   
   
    // internal function
    public static int cmpEquivFold(char[] s1, int s1Start,int s1Limit,
                                   char[] s2, int s2Start,int s2Limit,
                                   int options) {
        // current-level start/limit - s1/s2 as current
        int start1, start2, limit1, limit2;
        char[] cSource1, cSource2;
       
        cSource1 = s1;
        cSource2 = s2;
        // decomposition variables
        int length;
   
        // stacks of previous-level start/current/limit
        CmpEquivLevel[] stack1 = new CmpEquivLevel[]{
                                                    new CmpEquivLevel(),
                                                    new CmpEquivLevel()
                                                  };
        CmpEquivLevel[] stack2 = new CmpEquivLevel[]{
                                                    new CmpEquivLevel(),
                                                    new CmpEquivLevel()
                                                  };
   
        // decomposition buffers for Hangul
        char[] decomp1 = new char[8];
        char[] decomp2 = new char[8];
   
        // case folding buffers, only use current-level start/limit
        char[] fold1 = new char[32];
        char[] fold2 = new char[32];
   
        // track which is the current level per string
        int level1, level2;
   
        // current code units, and code points for lookups
        int c1, c2;
        int cp1, cp2;
   
        // no argument error checking because this itself is not an API
   
        // assume that at least one of the options COMPARE_EQUIV and
        // COMPARE_IGNORE_CASE is set
        // otherwise this function must behave exactly as uprv_strCompare()
        // not checking for that here makes testing this function easier

   
        // initialize
        start1=s1Start;
        limit1=s1Limit;
       
        start2=s2Start;
        limit2=s2Limit;
       
        level1=level2=0;
        c1=c2=-1;
        cp1=cp2=-1;
        // comparison loop
        for(;;) {
            // here a code unit value of -1 means "get another code unit"
            // below it will mean "this source is finished"
   
            if(c1<0) {
                // get next code unit from string 1, post-increment
                for(;;) {
                    if(s1Start>=limit1) {
                        if(level1==0) {
                            c1=-1;
                            break;
                        }
                    } else {
                        c1=cSource1[s1Start];
                        ++s1Start;
                        break;
                    }
   
                    // reached end of level buffer, pop one level
                    do {
                        --level1;
                        start1=stack1[level1].start;
                    } while(start1==-1); //###### check this
                    s1Start=stack1[level1].s;
                    limit1=stack1[level1].limit;
                    cSource1=stack1[level1].source;
                }
            }
   
            if(c2<0) {
                // get next code unit from string 2, post-increment
                for(;;) {                   
                    if(s2Start>=limit2) {
                        if(level2==0) {
                            c2=-1;
                            break;
                        }
                    } else {
                        c2=cSource2[s2Start];
                        ++s2Start;
                        break;
                    }
   
                    // reached end of level buffer, pop one level
                    do {
                        --level2;
                        start2=stack2[level2].start;
                    } while(start2==-1);
                    s2Start=stack2[level2].s;
                    limit2=stack2[level2].limit;
                    cSource2=stack2[level2].source;
                }
            }
   
            // compare c1 and c2
            // either variable c1, c2 is -1 only if the corresponding string
            // is finished
            if(c1==c2) {
                if(c1<0) {
                    return 0;   // c1==c2==-1 indicating end of strings
                }
                c1=c2=-1;       // make us fetch new code units
                continue;
            } else if(c1<0) {
                return -1;      // string 1 ends before string 2
            } else if(c2<0) {
                return 1;       // string 2 ends before string 1
            }
            // c1!=c2 && c1>=0 && c2>=0
   
            // get complete code points for c1, c2 for lookups if either is a
            // surrogate
            cp1=c1;
            if(UTF16.isSurrogate((char)c1)) {
                char c;
   
                if(UTF16.isLeadSurrogate((char)c1)) {
                    if(    s1Start!=limit1 &&
                           UTF16.isTrailSurrogate(c=cSource1[s1Start])
                      ) {
                        // advance ++s1; only below if cp1 decomposes/case-folds
                        cp1=UCharacterProperty.getRawSupplementary((char)c1, c);
                    }
                } else /* isTrail(c1) */ {
                    if(    start1<=(s1Start-2) &&
                            UTF16.isLeadSurrogate(c=cSource1[(s1Start-2)])
                      ) {
                        cp1=UCharacterProperty.getRawSupplementary(c, (char)c1);
                    }
                }
            }
            cp2=c2;
            if(UTF16.isSurrogate((char)c2)) {
                char c;
   
                if(UTF16.isLeadSurrogate((char)c2)) {
                    if(    s2Start!=limit2 &&
                           UTF16.isTrailSurrogate(c=cSource2[s2Start])
                      ) {
                        // advance ++s2; only below if cp2 decomposes/case-folds
                        cp2=UCharacterProperty.getRawSupplementary((char)c2, c);
                    }
                } else /* isTrail(c2) */ {
                    if(    start2<=(s2Start-2) && 
                           UTF16.isLeadSurrogate(c=cSource2[s2Start-2])
                      ) {
                        cp2=UCharacterProperty.getRawSupplementary(c, (char)c2);
                    }
                }
            }
   
            // go down one level for each string
            // continue with the main loop as soon as there is a real change
            if( level1<2 && ((options & Normalizer.COMPARE_IGNORE_CASE)!=0)&&
                (length=foldCase(cp1, fold1, 0,32,options))>=0
            ) {
                // cp1 case-folds to fold1[length]
                if(UTF16.isSurrogate((char)c1)) {
                    if(UTF16.isLeadSurrogate((char)c1)) {
                        // advance beyond source surrogate pair if it
                        // case-folds
                        ++s1Start;
                    } else /* isTrail(c1) */ {
                        // we got a supplementary code point when hitting its
                        // trail surrogate, therefore the lead surrogate must
                        // have been the same as in the other string;
                        // compare this decomposition with the lead surrogate
                        // in the other string
                        --s2Start;
                        c2=cSource2[(s2Start-1)];
                    }
                }
   
                // push current level pointers
                stack1[0].start=start1;
                stack1[0].s=s1Start;
                stack1[0].limit=limit1;
                stack1[0].source=cSource1;
                ++level1;
   
                cSource1 = fold1;
                start1=s1Start=0;
                limit1=length;
   
                // get ready to read from decomposition, continue with loop
                c1=-1;
                continue;
            }
   
            if( level2<2 && ((options& Normalizer.COMPARE_IGNORE_CASE)!=0) &&
                (length=foldCase(cp2, fold2,0,32, options))>=0
            ) {
                // cp2 case-folds to fold2[length]
                if(UTF16.isSurrogate((char)c2)) {
                    if(UTF16.isLeadSurrogate((char)c2)) {
                        // advance beyond source surrogate pair if it
                        // case-folds
                        ++s2Start;
                    } else /* isTrail(c2) */ {
                        // we got a supplementary code point when hitting its
                        // trail surrogate, therefore the lead surrogate must
                        // have been the same as in the other string;
                        // compare this decomposition with the lead surrogate
                        // in the other string
                        --s1Start;
                        c1=cSource1[(s1Start-1)];
                    }
                }
   
                // push current level pointers
                stack2[0].start=start2;
                stack2[0].s=s2Start;
                stack2[0].limit=limit2;
                stack2[0].source=cSource2;
                ++level2;
               
                cSource2 = fold2;
                start2=s2Start=0;
                limit2=length;
   
                // get ready to read from decomposition, continue with loop
                c2=-1;
                continue;
            }
           
            if( level1<2 && ((options&COMPARE_EQUIV)!=0) &&
                0!=(length=decompose(cp1,decomp1))
            ) {
                // cp1 decomposes into p[length]
                if(UTF16.isSurrogate((char)c1)) {
                    if(UTF16.isLeadSurrogate((char)c1)) {
                        // advance beyond source surrogate pair if it
                        //decomposes
                        ++s1Start;
                    } else /* isTrail(c1) */ {
                        // we got a supplementary code point when hitting
                        // its trail surrogate, therefore the lead surrogate
                        // must have been the same as in the other string;
                        // compare this decomposition with the lead surrogate
                        // in the other string
                        --s2Start;
                        c2=cSource2[(s2Start-1)];
                    }
                }
   
                // push current level pointers
                stack1[level1].start=start1;
                stack1[level1].s=s1Start;
                stack1[level1].limit=limit1;
                stack1[level1].source=cSource1;
                ++level1;
   
                // set next level pointers to decomposition
                cSource1 = decomp1;
                start1=s1Start=0;
                limit1=length;
               
                // set empty intermediate level if skipped
                if(level1<2) {
                    stack1[level1++].start=-1;
                }
                // get ready to read from decomposition, continue with loop
                c1=-1;
                continue;
            }
   
            if( level2<2 && ((options&COMPARE_EQUIV)!=0) &&
                0!=(length=decompose(cp2, decomp2))
            ) {
                // cp2 decomposes into p[length]
                if(UTF16.isSurrogate((char)c2)) {
                    if(UTF16.isLeadSurrogate((char)c2)) {
                        // advance beyond source surrogate pair if it
                        // decomposes
                        ++s2Start;
                    } else /* isTrail(c2) */ {
                        // we got a supplementary code point when hitting its
                        // trail surrogate, therefore the lead surrogate must
                        // have been the same as in the other string;
                        // compare this decomposition with the lead surrogate
                        // in the other string
                        --s1Start;
                        c1=cSource1[(s1Start-1)];
                    }
                }
   
                // push current level pointers
                stack2[level2].start=start2;
                stack2[level2].s=s2Start;
                stack2[level2].limit=limit2;
                stack2[level2].source=cSource2;
                ++level2;
   
                // set next level pointers to decomposition
                cSource2=decomp2;
                start2=s2Start=0;
                limit2=length;
               
                // set empty intermediate level if skipped
                if(level2<2) {
                    stack2[level2++].start=-1;
                }
               
                // get ready to read from decomposition, continue with loop
                c2=-1;
                continue;
            }
   
   
            // no decomposition/case folding, max level for both sides:
            // return difference result
   
            // code point order comparison must not just return cp1-cp2
            // because when single surrogates are present then the surrogate
            // pairs that formed cp1 and cp2 may be from different string
            // indexes
   
            // example: { d800 d800 dc01 } vs. { d800 dc00 }, compare at
            // second code units
            // c1=d800 cp1=10001 c2=dc00 cp2=10000
            // cp1-cp2>0 but c1-c2<0 and in fact in UTF-32
            // it is { d800 10001 } < { 10000 }
            // therefore fix-up
   
            if(     c1>=0xd800 && c2>=0xd800 &&
                    ((options&Normalizer.COMPARE_CODE_POINT_ORDER)!=0)
              ) {
                /* subtract 0x2800 from BMP code points to make them smaller
                 * than supplementary ones */
                if(
                    (    c1<=0xdbff && s1Start!=limit1
                         &&
                         UTF16.isTrailSurrogate(cSource1[s1Start])
                    )
                     ||
                    (    UTF16.isTrailSurrogate((char)c1) && start1!=(s1Start-1)
                         &&
                         UTF16.isLeadSurrogate(cSource1[(s1Start-2)])
                    )
                ) {
                    /* part of a surrogate pair, leave >=d800 */
                } else {
                    /* BMP code point - may be surrogate code point -
                     * make <d800 */
                    c1-=0x2800;
                }
   
                if(
                    (    c2<=0xdbff && s2Start!=limit2
                         &&
                         UTF16.isTrailSurrogate(cSource2[s2Start])
                    )
                     ||
                    (    UTF16.isTrailSurrogate((char)c2) && start2!=(s2Start-1)
                         &&
                         UTF16.isLeadSurrogate(cSource2[(s2Start-2)])
                    )
                ) {
                    /* part of a surrogate pair, leave >=d800 */
                } else {
                    /* BMP code point - may be surrogate code point -
                     * make <d800 */
                    c2-=0x2800;
                }
            }
   
            return c1-c2;
        }
    }
    private static int strCompare(char[] s1, int s1Start, int s1Limit,
                                  char[] s2, int s2Start, int s2Limit,
                                  boolean codePointOrder) {
                       
        int start1, start2, limit1, limit2;
        char c1, c2;
   
        /* setup for fix-up */
        start1=s1Start;
        start2=s2Start;
       
        int length1, length2;
       
        length1 = s1Limit - s1Start;
        length2 = s2Limit - s2Start;
           
        int lengthResult;

        if(length1<length2) {
            lengthResult=-1;
            limit1=start1+length1;
        } else if(length1==length2) {
            lengthResult=0;
            limit1=start1+length1;
        } else /* length1>length2 */ {
            lengthResult=1;
            limit1=start1+length2;
        }

        if(s1==s2) {
            return lengthResult;
        }

        for(;;) {
            /* check pseudo-limit */
            if(s1Start==limit1) {
                return lengthResult;
            }

            c1=s1[s1Start];
            c2=s2[s2Start];
            if(c1!=c2) {
                break;
            }
            ++s1Start;
            ++s2Start;
        }

        /* setup for fix-up */
        limit1=start1+length1;
        limit2=start2+length2;

   
        /* if both values are in or above the surrogate range, fix them up */
        if(c1>=0xd800 && c2>=0xd800 && codePointOrder) {
            /* subtract 0x2800 from BMP code points to make them smaller than
             *  supplementary ones */
            if(
                ( c1<=0xdbff && (s1Start+1)!=limit1 &&
                  UTF16.isTrailSurrogate(s1[(s1Start+1)])
                ) ||
                ( UTF16.isTrailSurrogate(c1) && start1!=s1Start &&
                  UTF16.isLeadSurrogate(s1[(s1Start-1)])
                )
            ) {
                /* part of a surrogate pair, leave >=d800 */
            } else {
                /* BMP code point - may be surrogate code point - make <d800 */
                c1-=0x2800;
            }
   
            if(
                ( c2<=0xdbff && (s2Start+1)!=limit2 &&
                  UTF16.isTrailSurrogate(s2[(s2Start+1)])
                ) ||
                ( UTF16.isTrailSurrogate(c2) && start2!=s2Start &&
                  UTF16.isLeadSurrogate(s2[(s2Start-1)])
                )
            ) {
                /* part of a surrogate pair, leave >=d800 */
            } else {
                /* BMP code point - may be surrogate code point - make <d800 */
                c2-=0x2800;
            }
        }
   
        /* now c1 and c2 are in UTF-32-compatible order */
        return (int)c1-(int)c2;
    }


    /*
     * Status of tailored normalization
     *
     * This was done initially for investigation on Unicode public review issue 7
     * (http://www.unicode.org/review/). See Jitterbug 2481.
     * While the UTC at meeting #94 (2003mar) did not take up the issue, this is
     * a permanent feature in ICU 2.6 in support of IDNA which requires true
     * Unicode 3.2 normalization.
     * (NormalizationCorrections are rolled into IDNA mapping tables.)
     *
     * Tailored normalization as implemented here allows to "normalize less"
     * than full Unicode normalization would.
     * Based internally on a UnicodeSet of code points that are
     * "excluded from normalization", the normalization functions leave those
     * code points alone ("inert"). This means that tailored normalization
     * still transforms text into a canonically equivalent form.
     * It does not add decompositions to code points that do not have any or
     * change decomposition results.
     *
     * Any function that searches for a safe boundary has not been touched,
     * which means that these functions will be over-pessimistic when
     * exclusions are applied.
     * This should not matter because subsequent checks and normalizations
     * do apply the exclusions; only a little more of the text may be processed
     * than necessary under exclusions.
     *
     * Normalization exclusions have the following effect on excluded code points c:
     * - c is not decomposed
     * - c is not a composition target
     * - c does not combine forward or backward for composition
     *   except that this is not implemented for Jamo
     * - c is treated as having a combining class of 0
     */
    
    /*
     * Constants for the bit fields in the options bit set parameter.
     * These need not be public.
     * A user only needs to know the currently assigned values.
     * The number and positions of reserved bits per field can remain private.
     */
    private static final int OPTIONS_NX_MASK=0x1f;
    private static final int OPTIONS_UNICODE_MASK=0xe0;
    public  static final int OPTIONS_SETS_MASK=0xff;
    //private static final int OPTIONS_UNICODE_SHIFT=5;
    private static final UnicodeSet[] nxCache = new UnicodeSet[OPTIONS_SETS_MASK+1];
    
    /* Constants for options flags for normalization.*/

    /**
     * Options bit 0, do not decompose Hangul syllables.
     */
    private static final int NX_HANGUL = 1;
    /**
     * Options bit 1, do not decompose CJK compatibility characters.
     */
    private static final int NX_CJK_COMPAT=2;
    /**
     * Options bit 8, use buggy recomposition described in
     * Unicode Public Review Issue #29
     * at http://www.unicode.org/review/resolved-pri.html#pri29
     *
     * Used in IDNA implementation according to strict interpretation
     * of IDNA definition based on Unicode 3.2 which predates PRI #29.
     *
     * See ICU4C unormimp.h
     */
    public static final int BEFORE_PRI_29=0x100;

    /*
     * The following options are used only in some composition functions.
     * They use bits 12 and up to preserve lower bits for the available options
     * space in unorm_compare() -
     * see documentation for UNORM_COMPARE_NORM_OPTIONS_SHIFT.
     */

    /** Options bit 12, for compatibility vs. canonical decomposition. */
    public static final int OPTIONS_COMPAT=0x1000;
    /** Options bit 13, no discontiguous composition (FCC vs. NFC). */
    public static final int OPTIONS_COMPOSE_CONTIGUOUS=0x2000;

    /* normalization exclusion sets --------------------------------------------- */
   
    /*
     * Normalization exclusion UnicodeSets are used for tailored normalization;
     * see the comment near the beginning of this file.
     *
     * By specifying one or several sets of code points,
     * those code points become inert for normalization.
     */
    private static final synchronized UnicodeSet internalGetNXHangul() {
        /* internal function, does not check for incoming U_FAILURE */
   
        if(nxCache[NX_HANGUL]==null) {
             nxCache[NX_HANGUL]=new UnicodeSet(0xac00, 0xd7a3);
        }
        return nxCache[NX_HANGUL];
    }
   
    private static final synchronized UnicodeSet internalGetNXCJKCompat() {
        /* internal function, does not check for incoming U_FAILURE */
   
        if(nxCache[NX_CJK_COMPAT]==null) {

            /* build a set from [CJK Ideographs]&[has canonical decomposition] */
            UnicodeSet set, hasDecomp;
   
            set=new UnicodeSet("[:Ideographic:]");
   
            /* start with an empty set for [has canonical decomposition] */
            hasDecomp=new UnicodeSet();
   
            /* iterate over all ideographs and remember which canonically decompose */
            UnicodeSetIterator it = new UnicodeSetIterator(set);
            int start, end;
            long norm32;
   
            while(it.nextRange() && (it.codepoint != UnicodeSetIterator.IS_STRING)) {
                start=it.codepoint;
                end=it.codepointEnd;
                while(start<=end) {
                    norm32 = getNorm32(start);
                    if((norm32 & QC_NFD)>0) {
                        hasDecomp.add(start);
                    }
                    ++start;
                }
            }
   
            /* hasDecomp now contains all ideographs that decompose canonically */
             nxCache[NX_CJK_COMPAT]=hasDecomp;
        
        }
   
        return nxCache[NX_CJK_COMPAT];
    }
   
    private static final synchronized UnicodeSet internalGetNXUnicode(int options) {
        options &= OPTIONS_UNICODE_MASK;
        if(options==0) {
            return null;
        }
   
        if(nxCache[options]==null) {
            /* build a set with all code points that were not designated by the specified Unicode version */
            UnicodeSet set = new UnicodeSet();

            switch(options) {
            case Normalizer.UNICODE_3_2:
                set.applyPattern("[:^Age=3.2:]");
                break;
            default:
                return null;
            }
           
            nxCache[options]=set;
        }
   
        return nxCache[options];
    }
   
    /* Get a decomposition exclusion set. The data must be loaded. */
    private static final synchronized UnicodeSet internalGetNX(int options) {
        options&=OPTIONS_SETS_MASK;
   
        if(nxCache[options]==null) {
            /* return basic sets */           
            if(options==NX_HANGUL) {
                return internalGetNXHangul();
            }
            if(options==NX_CJK_COMPAT) {
                return internalGetNXCJKCompat();
            }
            if((options & OPTIONS_UNICODE_MASK)!=0 && (options & OPTIONS_NX_MASK)==0) {
                return internalGetNXUnicode(options);
            }
   
            /* build a set from multiple subsets */
            UnicodeSet set;
            UnicodeSet other;
   
            set=new UnicodeSet();

   
            if((options & NX_HANGUL)!=0 && null!=(other=internalGetNXHangul())) {
                set.addAll(other);
            }
            if((options&NX_CJK_COMPAT)!=0 && null!=(other=internalGetNXCJKCompat())) {
                set.addAll(other);
            }
            if((options&OPTIONS_UNICODE_MASK)!=0 && null!=(other=internalGetNXUnicode(options))) {
                set.addAll(other);
            }

               nxCache[options]=set;
        }
        return nxCache[options];
    }
   
    public static final UnicodeSet getNX(int options) {
        if((options&=OPTIONS_SETS_MASK)==0) {
            /* incoming failure, or no decomposition exclusions requested */
            return null;
        } else {
            return internalGetNX(options);
        }
    }
   
    private static final boolean nx_contains(UnicodeSet nx, int c) {
        return nx!=null && nx.contains(c);
    }
   
    private static final boolean nx_contains(UnicodeSet nx, char c, char c2) {
        return nx!=null && nx.contains(c2==0 ? c : UCharacterProperty.getRawSupplementary(c, c2));
    }


}
TOP

Related Classes of com.ibm.icu.impl.NormalizerImpl$AuxTrieImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.