/*

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package org.apache.batik.gvt.flow;

import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.HashSet;
import java.util.Set;

import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;

/**
 *
 * @version $Id: TextLineBreaks.java 498740 2007-01-22 18:35:57Z dvholten $
 */
public class TextLineBreaks {
    public static final AttributedCharacterIterator.Attribute WORD_LIMIT =
            new GVTAttributedCharacterIterator.TextAttribute("WORD_LIMIT");

    public static final AttributedCharacterIterator.Attribute FLOW_PARAGRAPH
        = GVTAttributedCharacterIterator.TextAttribute.FLOW_PARAGRAPH;

    public static final AttributedCharacterIterator.Attribute FLOW_LINE_BREAK
        = GVTAttributedCharacterIterator.TextAttribute.FLOW_LINE_BREAK;

    static Set lineBrks = new HashSet();

    static {
        lineBrks.add(FLOW_PARAGRAPH);
        lineBrks.add(FLOW_LINE_BREAK);
    }

    // placeholder function for complex break analysis
    static int findComplexBreak(AttributedCharacterIterator aci)
    {
        int cnt = 0;
        for(char ch = aci.current();
            ch == AttributedCharacterIterator.DONE;
            ch = aci.next(), cnt++) {

            // .. do complex break analysis here Right now we aren't
            // do any, we just find the end of the run of
            // CHAR_CLASS_SA.

            if (getCharCharClass(ch) != CHAR_CLASS_SA)
                break;
        }
        return cnt;
    }

    // handle spaces separately, all others by table
    // as - Attributed string to attribute with Word extents.
    public static void findLineBrk(AttributedString as) {
        AttributedCharacterIterator aci = as.getIterator();
        if (aci.getEndIndex() == 0)
            return;
        char ch = aci.current(), prevCh = (char)-1;
        byte         cls = getCharCharClass(ch);
        if (cls == CHAR_CLASS_LF) cls = CHAR_CLASS_BK;
        byte      curCls = cls;
        byte     prevCls = cls;
        byte prevPrevCls = -1;
        int  wordCnt = 0;
        int  wordBegin = aci.getBeginIndex();
        // loop over all pairs in the string
        int ich = wordBegin+1;
        int lineEnd = aci.getRunLimit(lineBrks);

        // handle case where input starts with an LF
        if (cls >= CHAR_CLASS_CM) cls = CHAR_CLASS_AL;

        for (ch = aci.next();
             ch != AttributedCharacterIterator.DONE;
             ich++, prevCh = ch, ch = aci.next(),
             prevPrevCls = prevCls, prevCls = curCls) {

            if (ich == lineEnd) {
                as.addAttribute(WORD_LIMIT, new Integer(wordCnt++),
                                wordBegin, ich);
                wordBegin = ich;

                cls    = getCharCharClass(ch);
                curCls = cls;
                prevCls = cls;
                if (cls >= CHAR_CLASS_CM) cls = CHAR_CLASS_AL;

                lineEnd = aci.getRunLimit(lineBrks);
                continue;
            }

            // handle spaces
            curCls = getCharCharClass(ch);
            if (curCls == CHAR_CLASS_SP) {
                // pbrk[ich-1] = BREAK_ACTION_PROHIBITED;
                continue;
            }

            // handle complex scripts
            if (curCls == CHAR_CLASS_SA) {
                ich += findComplexBreak(aci);
                ch = aci.previous();
                if (ch != AttributedCharacterIterator.DONE)
                    prevCls = getCharCharClass(ch);
                ch = aci.next();
                if (ch != AttributedCharacterIterator.DONE)
                    curCls = cls = getCharCharClass(ch);
                continue;
            }

            // This isn't in the Unicode line breaking alg.  but it
            // seems needed as otherwise it does produce a break.
            if ((ch == CHAR_ZERO_WIDTH_JOINER) ||
                (prevCh == CHAR_ZERO_WIDTH_JOINER))
                continue; // Don't allow break around JOINER.

            if ((curCls == CHAR_CLASS_BK) || (curCls == CHAR_CLASS_LF)) {
                as.addAttribute(WORD_LIMIT, new Integer(wordCnt++),
                                wordBegin, ich);
                wordBegin = ich;
                cls = CHAR_CLASS_BK;
                continue;
            }
            if (prevCls == CHAR_CLASS_CR) {
                as.addAttribute(WORD_LIMIT, new Integer(wordCnt++),
                                wordBegin, ich-1);
                wordBegin = ich-1;
                cls = CHAR_CLASS_BK;
                continue;
            }
            if (curCls == CHAR_CLASS_CR) {
                continue;
            }

            // handle combining marks
            if (curCls == CHAR_CLASS_CM) {
                if (prevCls == CHAR_CLASS_SP) {
                    cls = CHAR_CLASS_ID;
                    if (prevPrevCls != -1) {
                        if (brkPairs[prevPrevCls][CHAR_CLASS_ID] ==
                            BREAK_ACTION_DIRECT) {
                            as.addAttribute(WORD_LIMIT, new Integer(wordCnt++),
                                            wordBegin, ich-1);
                            wordBegin = ich-1;
                            // pbrk[ich-2] = BREAK_ACTION_DIRECT;
                        } else {
                            // pbrk[ich-2] = BREAK_ACTION_PROHIBITED;
                        }
                    }
                }
                // pbrk[ich-1] = BREAK_ACTION_PROHIBITED;
                continue;
            }

            if (cls == CHAR_CLASS_BK) {
                cls = curCls;
                continue;
            }

            // lookup pair table information
            byte brk = brkPairs[cls][curCls];

            if (brk == BREAK_ACTION_DIRECT) {
                as.addAttribute(WORD_LIMIT, new Integer(wordCnt++),
                                wordBegin, ich);
                wordBegin = ich;
                // pbrk[ich-1] = brk;
            } else if (brk == BREAK_ACTION_INDIRECT) {
                if (prevCls == CHAR_CLASS_SP) {
                    as.addAttribute(WORD_LIMIT, new Integer(wordCnt++),
                                    wordBegin, ich);
                    wordBegin = ich;
                }
                // pbrk[ich-1] = ((prevCls == CHAR_CLASS_SP) ?
                //                BREAK_ACTION_INDIRECT :
                //                BREAK_ACTION_PROHIBITED);
            }
            cls = curCls;
        }

        // always break at the end
        as.addAttribute(WORD_LIMIT, new Integer(wordCnt++),
                        wordBegin, ich);
        wordBegin = ich;
        // pbrk[ich-1] = BREAK_ACTION_DIRECT;

        return;
   }

    public static byte[] stringToLineBreakClasses(String s) {
        int len = s.length();
        byte[] ret = new byte[len];
        for (int i=0; i<len; i++) {
            ret[i] = getCharCharClass(s.charAt(i));
        }
        return ret;
    }

    public static byte getCharCharClass(char ch) {
        if (ch < QUICK_LUT_SIZE) {
            if (quickLut == null) buildQuickLut();
            return quickLut[ch];
        }
        int len = raw_data.length;
        int l = 0;
        int r = (len/2)-1;
        int entry = (l+r)/2;
        while(l <= r) {
            char min = raw_data[2*entry];
            char max = raw_data[2*entry+1];
            if      (ch < min) r = entry-1;
            else if (ch > max) l = entry+1;
            else break;
            entry = (l+r)/2;
        }
        return raw_classes[entry];
    }

    public static final char CHAR_ZERO_WIDTH_JOINER = 0x200D;

    protected static final int QUICK_LUT_SIZE = 256;

    protected static void buildQuickLut() {
        int entry = 0;
        quickLut = new byte[QUICK_LUT_SIZE];
        int i=0;
        while (i<QUICK_LUT_SIZE) {
            int max = raw_data[2*entry+1];
            byte cls = raw_classes[entry];
            while (i<=max) {
                quickLut[i] = cls;
                i++;
                if (i>=QUICK_LUT_SIZE) break;
            }
            entry++;
        }
    }

    // direct break     (blank in table)
    public static final byte BREAK_ACTION_DIRECT     = 0;
    // indirect break   (% in table)
    public static final byte BREAK_ACTION_INDIRECT   = 1;
    // prohibited break (^ in table)
    public static final byte BREAK_ACTION_PROHIBITED = 2;

    public static final String [] brkStrs = { "DB", "IB", "PB" };

    //
    public static final byte CHAR_CLASS_OP =  0;
    public static final byte CHAR_CLASS_CL =  1;
    public static final byte CHAR_CLASS_QU =  2;
    public static final byte CHAR_CLASS_GL =  3;
    public static final byte CHAR_CLASS_NS =  4;
    public static final byte CHAR_CLASS_EX =  5;
    public static final byte CHAR_CLASS_SY =  6;
    public static final byte CHAR_CLASS_IS =  7;
    public static final byte CHAR_CLASS_PR =  8;
    public static final byte CHAR_CLASS_PO =  9;
    public static final byte CHAR_CLASS_NU = 10;
    public static final byte CHAR_CLASS_AL = 11;
    public static final byte CHAR_CLASS_ID = 12;
    public static final byte CHAR_CLASS_IN = 13;
    public static final byte CHAR_CLASS_HY = 14;
    public static final byte CHAR_CLASS_BA = 15;
    public static final byte CHAR_CLASS_BB = 16;
    public static final byte CHAR_CLASS_B2 = 17;
    public static final byte CHAR_CLASS_ZW = 18;
    public static final byte CHAR_CLASS_CM = 19;

    public static final byte CHAR_CLASS_SA = 20;
    public static final byte CHAR_CLASS_SP = 21;
    public static final byte CHAR_CLASS_BK = 22;
    public static final byte CHAR_CLASS_AI = CHAR_CLASS_AL; // 23;
    public static final byte CHAR_CLASS_CR = 24; // Can't occur (space res)
    public static final byte CHAR_CLASS_LF = 25; // Can't occur (space res)
    public static final byte CHAR_CLASS_SG = CHAR_CLASS_AL; // 26;
    public static final byte CHAR_CLASS_XX = CHAR_CLASS_AL; // 27;
    public static final byte CHAR_CLASS_CB = 28;

    public static final String [] clsStrs = {
        "OP", "CL", "QU", "GL", "NS", "EX", "SY", "IS", "PR", "PO",
        "NU", "AL", "ID", "IN", "HY", "BA", "BB", "B2", "ZW", "CM",
        "SA", "SP", "BK", "AI", "CR", "LF", "SG", "XX", "CB" };

    static byte [][]brkPairs =
        // ^, ^, ^, ^, ^, ^, ^, ^, ^, ^, ^, ^, ^, ^, ^, ^, ^, ^, ^, %
    {   {  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 }, //  0

        // _, ^, %, %, ^, ^, ^, ^, _, %, _, _, _, _, %, %, _, _, ^, %
        {  0, 2, 1, 1, 2, 2, 2, 2, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 2, 1 }, //  1

        // ^, ^, %, %, %, ^, ^, ^, %, %, %, %, %, %, %, %, %, %, ^, %
        {  2, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1 }, //  2

        // %, ^, %, %, %, ^, ^, ^, %, %, %, %, %, %, %, %, %, %, ^, %
        {  1, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1 }, //  3

        // _, ^, %, %, %, ^, ^, ^, _, _, _, _, _, _, %, %, _, _, ^, %
        {  0, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 2, 1 }, //  4

        // _, ^, %, %, %, ^, ^, ^, _, _, _, _, _, _, %, %, _, _, ^, %
        {  0, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 2, 1 }, //  5

        // _, ^, %, %, %, ^, ^, ^, _, _, %, _, _, _, %, %, _, _, ^, %
        {  0, 2, 1, 1, 1, 2, 2, 2, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 1 }, //  6

        // _, ^, %, %, %, ^, ^, ^, _, _, %, _, _, _, %, %, _, _, ^, %
        {  0, 2, 1, 1, 1, 2, 2, 2, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 1 }, //  7

        // %, ^, %, %, %, ^, ^, ^, _, _, %, %, %, _, %, %, _, _, ^, %
        {  1, 2, 1, 1, 1, 2, 2, 2, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 2, 1 }, //  8

        // _, ^, %, %, %, ^, ^, ^, _, _, _, _, _, _, %, %, _, _, ^, %
        {  0, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 2, 1 }, //  9

        // _, ^, %, %, %, ^, ^, ^, _, %, %, %, _, %, %, %, _, _, ^, %
        {  0, 2, 1, 1, 1, 2, 2, 2, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 2, 1 }, // 10

        // _, ^, %, %, %, ^, ^, ^, _, _, %, %, _, %, %, %, _, _, ^, %
        {  0, 2, 1, 1, 1, 2, 2, 2, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 2, 1 }, // 11

        // _, ^, %, %, %, ^, ^, ^, _, %, _, _, _, %, %, %, _, _, ^, %
        {  0, 2, 1, 1, 1, 2, 2, 2, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 2, 1 }, // 12

        // _, ^, %, %, %, ^, ^, ^, _, _, _, _, _, %, %, %, _, _, ^, %
        {  0, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 2, 1 }, // 13

        // _, ^, %, %, %, ^, ^, ^, _, _, _, _, _, _, %, %, _, _, ^, %
        {  0, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 2, 1 }, // 14

        // _, ^, %, %, %, ^, ^, ^, _, _, _, _, _, _, %, %, _, _, ^, %
        {  0, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 2, 1 }, // 15

        // %, ^, %, %, %, ^, ^, ^, %, %, %, %, %, %, %, %, %, %, ^, %
        {  1, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1 }, // 16

        // _, ^, %, %, %, ^, ^, ^, _, _, _, _, _, _, %, %, _, ^, ^, %
        {  0, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 2, 2, 1 }, // 17

        // _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, ^, %
        {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1 }, // 18

        //  _, ^, %, %, %, ^, ^, ^, _, _, %, %, _, %, %, %, _, _, ^, %
        {  0, 2, 1, 1, 1, 2, 2, 2, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 2, 1 }};// 19


    static byte [] quickLut = null;

    static final char [] raw_data = {
        0x0000, 0x0008,
        0x0009, 0x0009,
        0x000A, 0x000A,
        0x000B, 0x000B,
        0x000C, 0x000C,
        0x000D, 0x000D,
        0x000E, 0x001F,
        0x0020, 0x0020,
        0x0021, 0x0021,
        0x0022, 0x0022,
        0x0023, 0x0023,
        0x0024, 0x0024,
        0x0025, 0x0025,
        0x0026, 0x0026,
        0x0027, 0x0027,
        0x0028, 0x0028,
        0x0029, 0x0029,
        0x002A, 0x002A,
        0x002B, 0x002B,
        0x002C, 0x002C,
        0x002D, 0x002D,
        0x002E, 0x002E,
        0x002F, 0x002F,
        0x0030, 0x0039,
        0x003A, 0x003B,
        0x003C, 0x003E,
        0x003F, 0x003F,
        0x0040, 0x005A,
        0x005B, 0x005B,
        0x005C, 0x005C,
        0x005D, 0x005D,
        0x005E, 0x007A,
        0x007B, 0x007B,
        0x007C, 0x007C,
        0x007D, 0x007D,
        0x007E, 0x007E,
        0x007F, 0x009F,
        0x00A0, 0x00A0,
        0x00A1, 0x00A1,
        0x00A2, 0x00A2,
        0x00A3, 0x00A5,
        0x00A6, 0x00A6,
        0x00A7, 0x00A8,
        0x00A9, 0x00A9,
        0x00AA, 0x00AA,
        0x00AB, 0x00AB,
        0x00AC, 0x00AC,
        0x00AD, 0x00AD,
        0x00AE, 0x00AF,
        0x00B0, 0x00B0,
        0x00B1, 0x00B1,
        0x00B2, 0x00B3,
        0x00B4, 0x00B4,
        0x00B5, 0x00B5,
        0x00B6, 0x00BA,
        0x00BB, 0x00BB,
        0x00BC, 0x00BF,
        0x00C0, 0x00C5,
        0x00C6, 0x00C6,
        0x00C7, 0x00CF,
        0x00D0, 0x00D0,
        0x00D1, 0x00D6,
        0x00D7, 0x00D8,
        0x00D9, 0x00DD,
        0x00DE, 0x00E1,
        0x00E2, 0x00E5,
        0x00E6, 0x00E6,
        0x00E7, 0x00E7,
        0x00E8, 0x00EA,
        0x00EB, 0x00EB,
        0x00EC, 0x00ED,
        0x00EE, 0x00EF,
        0x00F0, 0x00F0,
        0x00F1, 0x00F1,
        0x00F2, 0x00F3,
        0x00F4, 0x00F6,
        0x00F7, 0x00FA,
        0x00FB, 0x00FB,
        0x00FC, 0x00FC,
        0x00FD, 0x00FD,
        0x00FE, 0x00FE,
        0x00FF, 0x0100,
        0x0101, 0x0101,
        0x0102, 0x0110,
        0x0111, 0x0111,
        0x0112, 0x0112,
        0x0113, 0x0113,
        0x0114, 0x011A,
        0x011B, 0x011B,
        0x011C, 0x0125,
        0x0126, 0x0127,
        0x0128, 0x012A,
        0x012B, 0x012B,
        0x012C, 0x0130,
        0x0131, 0x0133,
        0x0134, 0x0137,
        0x0138, 0x0138,
        0x0139, 0x013E,
        0x013F, 0x0142,
        0x0143, 0x0143,
        0x0144, 0x0144,
        0x0145, 0x0147,
        0x0148, 0x014A,
        0x014B, 0x014C,
        0x014D, 0x014D,
        0x014E, 0x0151,
        0x0152, 0x0153,
        0x0154, 0x0165,
        0x0166, 0x0167,
        0x0168, 0x016A,
        0x016B, 0x016B,
        0x016C, 0x01CD,
        0x01CE, 0x01CE,
        0x01CF, 0x01CF,
        0x01D0, 0x01D0,
        0x01D1, 0x01D1,
        0x01D2, 0x01D2,
        0x01D3, 0x01D3,
        0x01D4, 0x01D4,
        0x01D5, 0x01D5,
        0x01D6, 0x01D6,
        0x01D7, 0x01D7,
        0x01D8, 0x01D8,
        0x01D9, 0x01D9,
        0x01DA, 0x01DA,
        0x01DB, 0x01DB,
        0x01DC, 0x01DC,
        0x01DD, 0x0250,
        0x0251, 0x0251,
        0x0252, 0x0260,
        0x0261, 0x0261,
        0x0262, 0x02C6,
        0x02C7, 0x02C7,
        0x02C8, 0x02C8,
        0x02C9, 0x02CB,
        0x02CC, 0x02CC,
        0x02CD, 0x02CD,
        0x02CE, 0x02CF,
        0x02D0, 0x02D0,
        0x02D1, 0x02D7,
        0x02D8, 0x02DB,
        0x02DC, 0x02DC,
        0x02DD, 0x02DD,
        0x02DE, 0x02EE,
        0x0300, 0x036F,
        0x0374, 0x0390,
        0x0391, 0x03A9,
        0x03AA, 0x03B0,
        0x03B1, 0x03C1,
        0x03C2, 0x03C2,
        0x03C3, 0x03C9,
        0x03CA, 0x0400,
        0x0401, 0x0401,
        0x0402, 0x040F,
        0x0410, 0x044F,
        0x0450, 0x0450,
        0x0451, 0x0451,
        0x0452, 0x0482,
        0x0483, 0x0489,
        0x048A, 0x0587,
        0x0589, 0x0589,
        0x058A, 0x058A,
        0x0591, 0x05BD,
        0x05BE, 0x05BE,
        0x05BF, 0x05BF,
        0x05C0, 0x05C0,
        0x05C1, 0x05C2,
        0x05C3, 0x05C3,
        0x05C4, 0x05C4,
        0x05D0, 0x064A,
        0x064B, 0x0655,
        0x0660, 0x0669,
        0x066A, 0x066F,
        0x0670, 0x0670,
        0x0671, 0x06D5,
        0x06D6, 0x06E4,
        0x06E5, 0x06E6,
        0x06E7, 0x06E8,
        0x06E9, 0x06E9,
        0x06EA, 0x06ED,
        0x06F0, 0x06F9,
        0x06FA, 0x070D,
        0x070F, 0x070F,
        0x0710, 0x0710,
        0x0711, 0x0711,
        0x0712, 0x072C,
        0x0730, 0x074A,
        0x0780, 0x07A5,
        0x07A6, 0x07B0,
        0x07B1, 0x07B1,
        0x0901, 0x0903,
        0x0905, 0x0939,
        0x093C, 0x093C,
        0x093D, 0x093D,
        0x093E, 0x094D,
        0x0950, 0x0950,
        0x0951, 0x0954,
        0x0958, 0x0961,
        0x0962, 0x0963,
        0x0964, 0x0965,
        0x0966, 0x096F,
        0x0970, 0x0970,
        0x0981, 0x0983,
        0x0985, 0x09B9,
        0x09BC, 0x09D7,
        0x09DC, 0x09E1,
        0x09E2, 0x09E3,
        0x09E6, 0x09EF,
        0x09F0, 0x09F1,
        0x09F2, 0x09F3,
        0x09F4, 0x09FA,
        0x0A02, 0x0A02,
        0x0A05, 0x0A39,
        0x0A3C, 0x0A4D,
        0x0A59, 0x0A5E,
        0x0A66, 0x0A6F,
        0x0A70, 0x0A71,
        0x0A72, 0x0A74,
        0x0A81, 0x0A83,
        0x0A85, 0x0AB9,
        0x0ABC, 0x0ABC,
        0x0ABD, 0x0ABD,
        0x0ABE, 0x0ACD,
        0x0AD0, 0x0AE0,
        0x0AE6, 0x0AEF,
        0x0B01, 0x0B03,
        0x0B05, 0x0B39,
        0x0B3C, 0x0B3C,
        0x0B3D, 0x0B3D,
        0x0B3E, 0x0B57,
        0x0B5C, 0x0B61,
        0x0B66, 0x0B6F,
        0x0B70, 0x0B70,
        0x0B82, 0x0B82,
        0x0B83, 0x0BB9,
        0x0BBE, 0x0BD7,
        0x0BE7, 0x0BEF,
        0x0BF0, 0x0BF2,
        0x0C01, 0x0C03,
        0x0C05, 0x0C39,
        0x0C3E, 0x0C56,
        0x0C60, 0x0C61,
        0x0C66, 0x0C6F,
        0x0C82, 0x0C83,
        0x0C85, 0x0CB9,
        0x0CBE, 0x0CD6,
        0x0CDE, 0x0CE1,
        0x0CE6, 0x0CEF,
        0x0D02, 0x0D03,
        0x0D05, 0x0D39,
        0x0D3E, 0x0D57,
        0x0D60, 0x0D61,
        0x0D66, 0x0D6F,
        0x0D82, 0x0D83,
        0x0D85, 0x0DC6,
        0x0DCA, 0x0DF3,
        0x0DF4, 0x0DF4,
        0x0E01, 0x0E30,
        0x0E31, 0x0E31,
        0x0E32, 0x0E33,
        0x0E34, 0x0E3A,
        0x0E3F, 0x0E3F,
        0x0E40, 0x0E46,
        0x0E47, 0x0E4E,
        0x0E4F, 0x0E4F,
        0x0E50, 0x0E59,
        0x0E5A, 0x0E5B,
        0x0E81, 0x0EB0,
        0x0EB1, 0x0EB1,
        0x0EB2, 0x0EB3,
        0x0EB4, 0x0EBC,
        0x0EBD, 0x0EC6,
        0x0EC8, 0x0ECD,
        0x0ED0, 0x0ED9,
        0x0EDC, 0x0EDD,
        0x0F00, 0x0F0A,
        0x0F0B, 0x0F0B,
        0x0F0C, 0x0F0C,
        0x0F0D, 0x0F17,
        0x0F18, 0x0F19,
        0x0F1A, 0x0F1F,
        0x0F20, 0x0F29,
        0x0F2A, 0x0F34,
        0x0F35, 0x0F35,
        0x0F36, 0x0F36,
        0x0F37, 0x0F37,
        0x0F38, 0x0F38,
        0x0F39, 0x0F39,
        0x0F3A, 0x0F3A,
        0x0F3B, 0x0F3B,
        0x0F3C, 0x0F3C,
        0x0F3D, 0x0F3D,
        0x0F3E, 0x0F3F,
        0x0F40, 0x0F6A,
        0x0F71, 0x0F84,
        0x0F85, 0x0F85,
        0x0F86, 0x0F87,
        0x0F88, 0x0F8B,
        0x0F90, 0x0FBC,
        0x0FBE, 0x0FC5,
        0x0FC6, 0x0FC6,
        0x0FC7, 0x0FCF,
        0x1000, 0x102A,
        0x102C, 0x1039,
        0x1040, 0x1049,
        0x104A, 0x104F,
        0x1050, 0x1055,
        0x1056, 0x1059,
        0x10A0, 0x10FB,
        0x1100, 0x115F,
        0x1160, 0x11F9,
        0x1200, 0x135A,
        0x1361, 0x1361,
        0x1362, 0x1368,
        0x1369, 0x1371,
        0x1372, 0x1676,
        0x1680, 0x1680,
        0x1681, 0x169A,
        0x169B, 0x169B,
        0x169C, 0x169C,
        0x16A0, 0x1711,
        0x1712, 0x1714,
        0x1720, 0x1731,
        0x1732, 0x1734,
        0x1735, 0x1751,
        0x1752, 0x1753,
        0x1760, 0x1770,
        0x1772, 0x1773,
        0x1780, 0x17B3,
        0x17B4, 0x17D3,
        0x17D4, 0x17D4,
        0x17D5, 0x17D5,
        0x17D6, 0x17DA,
        0x17DB, 0x17DB,
        0x17DC, 0x17DC,
        0x17E0, 0x17E9,
        0x1800, 0x1805,
        0x1806, 0x1806,
        0x1807, 0x180A,
        0x180B, 0x180E,
        0x1810, 0x1819,
        0x1820, 0x18A8,
        0x18A9, 0x18A9,
        0x1E00, 0x1FFE,
        0x2000, 0x2006,
        0x2007, 0x2007,
        0x2008, 0x200A,
        0x200B, 0x200B,
        0x200C, 0x200F,
        0x2010, 0x2010,
        0x2011, 0x2011,
        0x2012, 0x2013,
        0x2014, 0x2014,
        0x2015, 0x2016,
        0x2017, 0x2017,
        0x2018, 0x2019,
        0x201A, 0x201A,
        0x201B, 0x201D,
        0x201E, 0x201E,
        0x201F, 0x201F,
        0x2020, 0x2021,
        0x2022, 0x2023,
        0x2024, 0x2026,
        0x2027, 0x2027,
        0x2028, 0x2029,
        0x202A, 0x202E,
        0x202F, 0x202F,
        0x2030, 0x2037,
        0x2038, 0x2038,
        0x2039, 0x203A,
        0x203B, 0x203B,
        0x203C, 0x203C,
        0x203D, 0x2043,
        0x2044, 0x2044,
        0x2045, 0x2045,
        0x2046, 0x2046,
        0x2047, 0x2057,
        0x205F, 0x205F,
        0x2060, 0x2060,
        0x2061, 0x2063,
        0x206A, 0x206F,
        0x2070, 0x2071,
        0x2074, 0x2074,
        0x2075, 0x207C,
        0x207D, 0x207D,
        0x207E, 0x207E,
        0x207F, 0x207F,
        0x2080, 0x2080,
        0x2081, 0x2084,
        0x2085, 0x208C,
        0x208D, 0x208D,
        0x208E, 0x208E,
        0x20A0, 0x20A6,
        0x20A7, 0x20A7,
        0x20A8, 0x20B1,
        0x20D0, 0x20EA,
        0x2100, 0x2102,
        0x2103, 0x2103,
        0x2104, 0x2104,
        0x2105, 0x2105,
        0x2106, 0x2108,
        0x2109, 0x2109,
        0x210A, 0x2112,
        0x2113, 0x2113,
        0x2114, 0x2115,
        0x2116, 0x2116,
        0x2117, 0x2120,
        0x2121, 0x2122,
        0x2123, 0x2125,
        0x2126, 0x2126,
        0x2127, 0x212A,
        0x212B, 0x212B,
        0x212C, 0x213F,
        0x2140, 0x2140,
        0x2141, 0x2153,
        0x2154, 0x2155,
        0x2156, 0x215A,
        0x215B, 0x215B,
        0x215C, 0x215D,
        0x215E, 0x215E,
        0x215F, 0x215F,
        0x2160, 0x216B,
        0x216C, 0x216F,
        0x2170, 0x2179,
        0x217A, 0x2183,
        0x2190, 0x2199,
        0x219A, 0x21D1,
        0x21D2, 0x21D2,
        0x21D3, 0x21D3,
        0x21D4, 0x21D4,
        0x21D5, 0x21FF,
        0x2200, 0x2200,
        0x2201, 0x2201,
        0x2202, 0x2203,
        0x2204, 0x2206,
        0x2207, 0x2208,
        0x2209, 0x220A,
        0x220B, 0x220B,
        0x220C, 0x220E,
        0x220F, 0x220F,
        0x2210, 0x2210,
        0x2211, 0x2211,
        0x2212, 0x2213,
        0x2214, 0x2214,
        0x2215, 0x2215,
        0x2216, 0x2219,
        0x221A, 0x221A,
        0x221B, 0x221C,
        0x221D, 0x2220,
        0x2221, 0x2222,
        0x2223, 0x2223,
        0x2224, 0x2224,
        0x2225, 0x2225,
        0x2226, 0x2226,
        0x2227, 0x222C,
        0x222D, 0x222D,
        0x222E, 0x222E,
        0x222F, 0x2233,
        0x2234, 0x2237,
        0x2238, 0x223B,
        0x223C, 0x223D,
        0x223E, 0x2247,
        0x2248, 0x2248,
        0x2249, 0x224B,
        0x224C, 0x224C,
        0x224D, 0x2251,
        0x2252, 0x2252,
        0x2253, 0x225F,
        0x2260, 0x2261,
        0x2262, 0x2263,
        0x2264, 0x2267,
        0x2268, 0x2269,
        0x226A, 0x226B,
        0x226C, 0x226D,
        0x226E, 0x226F,
        0x2270, 0x2281,
        0x2282, 0x2283,
        0x2284, 0x2285,
        0x2286, 0x2287,
        0x2288, 0x2294,
        0x2295, 0x2295,
        0x2296, 0x2298,
        0x2299, 0x2299,
        0x229A, 0x22A4,
        0x22A5, 0x22A5,
        0x22A6, 0x22BE,
        0x22BF, 0x22BF,
        0x22C0, 0x2311,
        0x2312, 0x2312,
        0x2313, 0x2328,
        0x2329, 0x2329,
        0x232A, 0x232A,
        0x232B, 0x23B3,
        0x23B4, 0x23B4,
        0x23B5, 0x23B5,
        0x23B6, 0x23B6,
        0x23B7, 0x244A,
        0x2460, 0x24BF,
        0x24C0, 0x24CF,
        0x24D0, 0x24E9,
        0x24EA, 0x24EA,
        0x24EB, 0x254B,
        0x254C, 0x254F,
        0x2550, 0x2574,
        0x2575, 0x257F,
        0x2580, 0x258F,
        0x2590, 0x2591,
        0x2592, 0x2595,
        0x2596, 0x259F,
        0x25A0, 0x25A1,
        0x25A2, 0x25A2,
        0x25A3, 0x25A9,
        0x25AA, 0x25B1,
        0x25B2, 0x25B3,
        0x25B4, 0x25B5,
        0x25B6, 0x25B7,
        0x25B8, 0x25BB,
        0x25BC, 0x25BD,
        0x25BE, 0x25BF,
        0x25C0, 0x25C1,
        0x25C2, 0x25C5,
        0x25C6, 0x25C8,
        0x25C9, 0x25CA,
        0x25CB, 0x25CB,
        0x25CC, 0x25CD,
        0x25CE, 0x25D1,
        0x25D2, 0x25E1,
        0x25E2, 0x25E5,
        0x25E6, 0x25EE,
        0x25EF, 0x25EF,
        0x25F0, 0x2604,
        0x2605, 0x2606,
        0x2607, 0x2608,
        0x2609, 0x2609,
        0x260A, 0x260D,
        0x260E, 0x260F,
        0x2610, 0x2613,
        0x2616, 0x2617,
        0x2619, 0x261B,
        0x261C, 0x261C,
        0x261D, 0x261D,
        0x261E, 0x261E,
        0x261F, 0x263F,
        0x2640, 0x2640,
        0x2641, 0x2641,
        0x2642, 0x2642,
        0x2643, 0x265F,
        0x2660, 0x2661,
        0x2662, 0x2662,
        0x2663, 0x2665,
        0x2666, 0x2666,
        0x2667, 0x266A,
        0x266B, 0x266B,
        0x266C, 0x266D,
        0x266E, 0x266E,
        0x266F, 0x266F,
        0x2670, 0x275A,
        0x275B, 0x275E,
        0x2761, 0x2761,
        0x2762, 0x2763,
        0x2764, 0x2767,
        0x2768, 0x2768,
        0x2769, 0x2769,
        0x276A, 0x276A,
        0x276B, 0x276B,
        0x276C, 0x276C,
        0x276D, 0x276D,
        0x276E, 0x276E,
        0x276F, 0x276F,
        0x2770, 0x2770,
        0x2771, 0x2771,
        0x2772, 0x2772,
        0x2773, 0x2773,
        0x2774, 0x2774,
        0x2775, 0x2775,
        0x2776, 0x27E5,
        0x27E6, 0x27E6,
        0x27E7, 0x27E7,
        0x27E8, 0x27E8,
        0x27E9, 0x27E9,
        0x27EA, 0x27EA,
        0x27EB, 0x27EB,
        0x27F0, 0x2982,
        0x2983, 0x2983,
        0x2984, 0x2984,
        0x2985, 0x2985,
        0x2986, 0x2986,
        0x2987, 0x2987,
        0x2988, 0x2988,
        0x2989, 0x2989,
        0x298A, 0x298A,
        0x298B, 0x298B,
        0x298C, 0x298C,
        0x298D, 0x298D,
        0x298E, 0x298E,
        0x298F, 0x298F,
        0x2990, 0x2990,
        0x2991, 0x2991,
        0x2992, 0x2992,
        0x2993, 0x2993,
        0x2994, 0x2994,
        0x2995, 0x2995,
        0x2996, 0x2996,
        0x2997, 0x2997,
        0x2998, 0x2998,
        0x2999, 0x29D7,
        0x29D8, 0x29D8,
        0x29D9, 0x29D9,
        0x29DA, 0x29DA,
        0x29DB, 0x29DB,
        0x29DC, 0x29FB,
        0x29FC, 0x29FC,
        0x29FD, 0x29FD,
        0x29FE, 0x2AFF,
        0x2E80, 0x3000,
        0x3001, 0x3002,
        0x3003, 0x3004,
        0x3005, 0x3005,
        0x3006, 0x3007,
        0x3008, 0x3008,
        0x3009, 0x3009,
        0x300A, 0x300A,
        0x300B, 0x300B,
        0x300C, 0x300C,
        0x300D, 0x300D,
        0x300E, 0x300E,
        0x300F, 0x300F,
        0x3010, 0x3010,
        0x3011, 0x3011,
        0x3012, 0x3013,
        0x3014, 0x3014,
        0x3015, 0x3015,
        0x3016, 0x3016,
        0x3017, 0x3017,
        0x3018, 0x3018,
        0x3019, 0x3019,
        0x301A, 0x301A,
        0x301B, 0x301B,
        0x301C, 0x301C,
        0x301D, 0x301D,
        0x301E, 0x301F,
        0x3020, 0x3029,
        0x302A, 0x302F,
        0x3030, 0x303A,
        0x303B, 0x303C,
        0x303D, 0x303F,
        0x3041, 0x3041,
        0x3042, 0x3042,
        0x3043, 0x3043,
        0x3044, 0x3044,
        0x3045, 0x3045,
        0x3046, 0x3046,
        0x3047, 0x3047,
        0x3048, 0x3048,
        0x3049, 0x3049,
        0x304A, 0x3062,
        0x3063, 0x3063,
        0x3064, 0x3082,
        0x3083, 0x3083,
        0x3084, 0x3084,
        0x3085, 0x3085,
        0x3086, 0x3086,
        0x3087, 0x3087,
        0x3088, 0x308D,
        0x308E, 0x308E,
        0x308F, 0x3094,
        0x3095, 0x3096,
        0x3099, 0x309A,
        0x309B, 0x309E,
        0x309F, 0x309F,
        0x30A0, 0x30A1,
        0x30A2, 0x30A2,
        0x30A3, 0x30A3,
        0x30A4, 0x30A4,
        0x30A5, 0x30A5,
        0x30A6, 0x30A6,
        0x30A7, 0x30A7,
        0x30A8, 0x30A8,
        0x30A9, 0x30A9,
        0x30AA, 0x30C2,
        0x30C3, 0x30C3,
        0x30C4, 0x30E2,
        0x30E3, 0x30E3,
        0x30E4, 0x30E4,
        0x30E5, 0x30E5,
        0x30E6, 0x30E6,
        0x30E7, 0x30E7,
        0x30E8, 0x30ED,
        0x30EE, 0x30EE,
        0x30EF, 0x30F4,
        0x30F5, 0x30F6,
        0x30F7, 0x30FA,
        0x30FB, 0x30FB,
        0x30FC, 0x30FC,
        0x30FD, 0x30FD,
        0x30FE, 0x31B7,
        0x31F0, 0x31FF,
        0x3200, 0xD7A3,
        0xD800, 0xDFFF,
        0xE000, 0xF8FF,
        0xF900, 0xFA6A,
        0xFB00, 0xFB1D,
        0xFB1E, 0xFB1E,
        0xFB1F, 0xFD3D,
        0xFD3E, 0xFD3E,
        0xFD3F, 0xFD3F,
        0xFD50, 0xFDFB,
        0xFDFC, 0xFDFC,
        0xFE00, 0xFE23,
        0xFE30, 0xFE34,
        0xFE35, 0xFE35,
        0xFE36, 0xFE36,
        0xFE37, 0xFE37,
        0xFE38, 0xFE38,
        0xFE39, 0xFE39,
        0xFE3A, 0xFE3A,
        0xFE3B, 0xFE3B,
        0xFE3C, 0xFE3C,
        0xFE3D, 0xFE3D,
        0xFE3E, 0xFE3E,
        0xFE3F, 0xFE3F,
        0xFE40, 0xFE40,
        0xFE41, 0xFE41,
        0xFE42, 0xFE42,
        0xFE43, 0xFE43,
        0xFE44, 0xFE44,
        0xFE45, 0xFE4F,
        0xFE50, 0xFE50,
        0xFE51, 0xFE51,
        0xFE52, 0xFE52,
        0xFE54, 0xFE55,
        0xFE56, 0xFE57,
        0xFE58, 0xFE58,
        0xFE59, 0xFE59,
        0xFE5A, 0xFE5A,
        0xFE5B, 0xFE5B,
        0xFE5C, 0xFE5C,
        0xFE5D, 0xFE5D,
        0xFE5E, 0xFE5E,
        0xFE5F, 0xFE68,
        0xFE69, 0xFE69,
        0xFE6A, 0xFE6A,
        0xFE6B, 0xFE6B,
        0xFE70, 0xFEFC,
        0xFEFF, 0xFEFF,
        0xFF01, 0xFF01,
        0xFF02, 0xFF03,
        0xFF04, 0xFF04,
        0xFF05, 0xFF05,
        0xFF06, 0xFF07,
        0xFF08, 0xFF08,
        0xFF09, 0xFF09,
        0xFF0A, 0xFF0B,
        0xFF0C, 0xFF0C,
        0xFF0D, 0xFF0D,
        0xFF0E, 0xFF0E,
        0xFF0F, 0xFF19,
        0xFF1A, 0xFF1B,
        0xFF1C, 0xFF1E,
        0xFF1F, 0xFF1F,
        0xFF20, 0xFF3A,
        0xFF3B, 0xFF3B,
        0xFF3C, 0xFF3C,
        0xFF3D, 0xFF3D,
        0xFF3E, 0xFF5A,
        0xFF5B, 0xFF5B,
        0xFF5C, 0xFF5C,
        0xFF5D, 0xFF5D,
        0xFF5E, 0xFF5E,
        0xFF5F, 0xFF5F,
        0xFF60, 0xFF61,
        0xFF62, 0xFF62,
        0xFF63, 0xFF64,
        0xFF65, 0xFF65,
        0xFF66, 0xFF66,
        0xFF67, 0xFF70,
        0xFF71, 0xFF9D,
        0xFF9E, 0xFF9F,
        0xFFA0, 0xFFDC,
        0xFFE0, 0xFFE0,
        0xFFE1, 0xFFE1,
        0xFFE2, 0xFFE4,
        0xFFE5, 0xFFE6,
        0xFFE8, 0xFFEE,
        0xFFF9, 0xFFFB,
        0xFFFC, 0xFFFC,
        0xFFFD, 0xFFFF,
    };
    static final byte [] raw_classes = {
        CHAR_CLASS_CM,
        CHAR_CLASS_BA,
        CHAR_CLASS_LF,
        CHAR_CLASS_CM,
        CHAR_CLASS_BK,
        CHAR_CLASS_CR,
        CHAR_CLASS_CM,
        CHAR_CLASS_SP,
        CHAR_CLASS_EX,
        CHAR_CLASS_QU,
        CHAR_CLASS_AL,
        CHAR_CLASS_PR,
        CHAR_CLASS_PO,
        CHAR_CLASS_AL,
        CHAR_CLASS_QU,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_AL,
        CHAR_CLASS_PR,
        CHAR_CLASS_IS,
        CHAR_CLASS_HY,
        CHAR_CLASS_IS,
        CHAR_CLASS_SY,
        CHAR_CLASS_NU,
        CHAR_CLASS_IS,
        CHAR_CLASS_AL,
        CHAR_CLASS_EX,
        CHAR_CLASS_AL,
        CHAR_CLASS_OP,
        CHAR_CLASS_PR,
        CHAR_CLASS_CL,
        CHAR_CLASS_AL,
        CHAR_CLASS_OP,
        CHAR_CLASS_BA,
        CHAR_CLASS_CL,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_GL,
        CHAR_CLASS_AI,
        CHAR_CLASS_PO,
        CHAR_CLASS_PR,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_QU,
        CHAR_CLASS_AL,
        CHAR_CLASS_BA,
        CHAR_CLASS_AL,
        CHAR_CLASS_PO,
        CHAR_CLASS_PR,
        CHAR_CLASS_AI,
        CHAR_CLASS_BB,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_QU,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_BB,
        CHAR_CLASS_AI,
        CHAR_CLASS_BB,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_IS,
        CHAR_CLASS_BA,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_NU,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_NU,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_NU,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_NU,
        CHAR_CLASS_AL,
        CHAR_CLASS_PR,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_NU,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_NU,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_NU,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_NU,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_NU,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_NU,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_NU,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_SA,
        CHAR_CLASS_CM,
        CHAR_CLASS_SA,
        CHAR_CLASS_CM,
        CHAR_CLASS_PR,
        CHAR_CLASS_SA,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_NU,
        CHAR_CLASS_NS,
        CHAR_CLASS_SA,
        CHAR_CLASS_CM,
        CHAR_CLASS_SA,
        CHAR_CLASS_CM,
        CHAR_CLASS_SA,
        CHAR_CLASS_CM,
        CHAR_CLASS_NU,
        CHAR_CLASS_SA,
        CHAR_CLASS_AL,
        CHAR_CLASS_BA,
        CHAR_CLASS_GL,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_NU,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_SA,
        CHAR_CLASS_CM,
        CHAR_CLASS_NU,
        CHAR_CLASS_AL,
        CHAR_CLASS_SA,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_ID,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_BA,
        CHAR_CLASS_AL,
        CHAR_CLASS_NU,
        CHAR_CLASS_AL,
        CHAR_CLASS_BA,
        CHAR_CLASS_AL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_SA,
        CHAR_CLASS_CM,
        CHAR_CLASS_NS,
        CHAR_CLASS_BA,
        CHAR_CLASS_NS,
        CHAR_CLASS_PR,
        CHAR_CLASS_AL,
        CHAR_CLASS_NU,
        CHAR_CLASS_AL,
        CHAR_CLASS_BB,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_NU,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_BA,
        CHAR_CLASS_GL,
        CHAR_CLASS_BA,
        CHAR_CLASS_ZW,
        CHAR_CLASS_CM,
        CHAR_CLASS_BA,
        CHAR_CLASS_GL,
        CHAR_CLASS_BA,
        CHAR_CLASS_B2,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_QU,
        CHAR_CLASS_OP,
        CHAR_CLASS_QU,
        CHAR_CLASS_OP,
        CHAR_CLASS_QU,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_IN,
        CHAR_CLASS_BA,
        CHAR_CLASS_BK,
        CHAR_CLASS_CM,
        CHAR_CLASS_GL,
        CHAR_CLASS_PO,
        CHAR_CLASS_AL,
        CHAR_CLASS_QU,
        CHAR_CLASS_AI,
        CHAR_CLASS_NS,
        CHAR_CLASS_AL,
        CHAR_CLASS_NS,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_AL,
        CHAR_CLASS_BA,
        CHAR_CLASS_GL,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_PR,
        CHAR_CLASS_PO,
        CHAR_CLASS_PR,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_PO,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_PO,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_PR,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_PO,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_PR,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_AL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_QU,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_AI,
        CHAR_CLASS_AL,
        CHAR_CLASS_QU,
        CHAR_CLASS_AL,
        CHAR_CLASS_EX,
        CHAR_CLASS_AL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_AL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_AL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_AL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_AL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_AL,
        CHAR_CLASS_ID,
        CHAR_CLASS_CL,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_ID,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_NS,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_ID,
        CHAR_CLASS_CM,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_CM,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_SG,
        CHAR_CLASS_XX,
        CHAR_CLASS_ID,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_AL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_AL,
        CHAR_CLASS_PO,
        CHAR_CLASS_CM,
        CHAR_CLASS_ID,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_ID,
        CHAR_CLASS_CL,
        CHAR_CLASS_ID,
        CHAR_CLASS_CL,
        CHAR_CLASS_NS,
        CHAR_CLASS_EX,
        CHAR_CLASS_ID,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_ID,
        CHAR_CLASS_PR,
        CHAR_CLASS_PO,
        CHAR_CLASS_ID,
        CHAR_CLASS_AL,
        CHAR_CLASS_GL,
        CHAR_CLASS_EX,
        CHAR_CLASS_ID,
        CHAR_CLASS_PR,
        CHAR_CLASS_PO,
        CHAR_CLASS_ID,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_ID,
        CHAR_CLASS_CL,
        CHAR_CLASS_ID,
        CHAR_CLASS_CL,
        CHAR_CLASS_ID,
        CHAR_CLASS_NS,
        CHAR_CLASS_ID,
        CHAR_CLASS_EX,
        CHAR_CLASS_ID,
        CHAR_CLASS_OP,
        CHAR_CLASS_ID,
        CHAR_CLASS_CL,
        CHAR_CLASS_ID,
        CHAR_CLASS_OP,
        CHAR_CLASS_ID,
        CHAR_CLASS_CL,
        CHAR_CLASS_ID,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_OP,
        CHAR_CLASS_CL,
        CHAR_CLASS_NS,
        CHAR_CLASS_AL,
        CHAR_CLASS_NS,
        CHAR_CLASS_AL,
        CHAR_CLASS_NS,
        CHAR_CLASS_AL,
        CHAR_CLASS_PO,
        CHAR_CLASS_PR,
        CHAR_CLASS_ID,
        CHAR_CLASS_PR,
        CHAR_CLASS_AL,
        CHAR_CLASS_CM,
        CHAR_CLASS_CB,
        CHAR_CLASS_AI };
}
