Open Source Repository

Home /itextpdf/itextpdf-5.1.2 | Repository Home



com/itextpdf/text/pdf/BidiOrder.java
/*
 * $Id: BidiOrder.java 4784 2011-03-15 08:33:00Z blowagie $
 *
 * This file is part of the iText (R) project.
 * Copyright (c) 1998-2011 1T3XT BVBA
 * Authors: Bruno Lowagie, Paulo Soares, et al.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
 * http://itextpdf.com/terms-of-use/
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License,
 * a covered work must retain the producer line in every PDF that is created
 * or manipulated using iText.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the iText software without
 * disclosing the source code of your own applications.
 * These activities include: offering paid services to customers as an ASP,
 * serving PDFs on the fly in a web application, shipping iText with a closed
 * source product.
 *
 * For more information, please contact iText Software Corp. at this
 * address: [email protected]
 */

/*
 * (C) Copyright IBM Corp. 1999, All Rights Reserved
 *
 * version 1.1
 */

/*
 * As stated in the Javadoc comments below, materials from Unicode.org
 * are used in this class. The following license applies to these materials:
 * http://www.unicode.org/copyright.html#Exhibit1
 
 * EXHIBIT 1
 * UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
 
 * Unicode Data Files include all data files under the directories
 * http://www.unicode.org/Public/, http://www.unicode.org/reports/,
 * and http://www.unicode.org/cldr/data/ .
 * Unicode Software includes any source code published in the Unicode Standard
 * or under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/,
 * and http://www.unicode.org/cldr/data/.
 
 * NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING,
 * INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES ("DATA FILES"),
 * AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY,
 * ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
 * DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
 
 * COPYRIGHT AND PERMISSION NOTICE
 * Copyright (C) 1991-2007 Unicode, Inc. All rights reserved. Distributed under
 * the Terms of Use in http://www.unicode.org/copyright.html.
 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of the Unicode data files and any associated documentation (the "Data Files")
 * or Unicode software and any associated documentation (the "Software") to deal
 * in the Data Files or Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, and/or sell copies
 * of the Data Files or Software, and to permit persons to whom the Data Files
 * or Software are furnished to do so, provided that (a) the above copyright
 * notice(s) and this permission notice appear with all copies of the Data Files
 * or Software, (b) both the above copyright notice(s) and this permission notice
 * appear in associated documentation, and (c) there is clear notice in each
 * modified Data File or in the Software as well as in the documentation associated
 * with the Data File(s) or Software that the data or software has been modified.
 
 * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE
 * LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY
 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
 
 * Except as contained in this notice, the name of a copyright holder shall not
 * be used in advertising or otherwise to promote the sale, use or other dealings
 * in these Data Files or Software without prior written authorization of the
 * copyright holder.
 */
package com.itextpdf.text.pdf;

/**
 * Reference implementation of the Unicode 3.0 Bidi algorithm.
 *
 <p>
 * This implementation is not optimized for performance.  It is intended
 * as a reference implementation that closely follows the specification
 * of the Bidirectional Algorithm in The Unicode Standard version 3.0.
 <p>
 <b>Input:</b><br>
 * There are two levels of input to the algorithm, since clients may prefer
 * to supply some information from out-of-band sources rather than relying on
 * the default behavior.
 <ol>
 <li>unicode type array
 <li>unicode type array, with externally supplied base line direction
 </ol>
 <p><b>Output:</b><br>
 * Output is separated into several stages as well, to better enable clients
 * to evaluate various aspects of implementation conformance.
 <ol>
 <li>levels array over entire paragraph
 <li>reordering array over entire paragraph
 <li>levels array over line
 <li>reordering array over line
 </ol>
 * Note that for conformance, algorithms are only required to generate correct
 * reordering and character directionality (odd or even levels) over a line.
 * Generating identical level arrays over a line is not required.  Bidi
 * explicit format codes (LRE, RLE, LRO, RLO, PDF) and BN can be assigned
 * arbitrary levels and positions as long as the other text matches.
 <p>
 * As the algorithm is defined to operate on a single paragraph at a time,
 * this implementation is written to handle single paragraphs.  Thus
 * rule P1 is presumed by this implementation-- the data provided to the
 * implementation is assumed to be a single paragraph, and either contains no
 * 'B' codes, or a single 'B' code at the end of the input.  'B' is allowed
 * as input to illustrate how the algorithm assigns it a level.
 <p>
 * Also note that rules L3 and L4 depend on the rendering engine that uses
 * the result of the bidi algorithm.  This implementation assumes that the
 * rendering engine expects combining marks in visual order (e.g. to the
 * left of their base character in RTL runs) and that it adjust the glyphs
 * used to render mirrored characters that are in RTL runs so that they
 * render appropriately.
 *
 @author Doug Felt
 */

import com.itextpdf.text.error_messages.MessageLocalization;

public final class BidiOrder {
    private byte[] initialTypes;
    private byte[] embeddings; // generated from processing format codes
    private byte paragraphEmbeddingLevel = -1// undefined
    
    private int textLength; // for convenience
    private byte[] resultTypes; // for paragraph, not lines
    private byte[] resultLevels; // for paragraph, not lines
    
    // The bidi types
    
    /** Left-to-right*/
    public static final byte L = 0;
    
    /** Left-to-Right Embedding */
    public static final byte LRE = 1;
    
    /** Left-to-Right Override */
    public static final byte LRO = 2;
    
    /** Right-to-Left */
    public static final byte R = 3;
    
    /** Right-to-Left Arabic */
    public static final byte AL = 4;
    
    /** Right-to-Left Embedding */
    public static final byte RLE = 5;
    
    /** Right-to-Left Override */
    public static final byte RLO = 6;
    
    /** Pop Directional Format */
    public static final byte PDF = 7;
    
    /** European Number */
    public static final byte EN = 8;
    
    /** European Number Separator */
    public static final byte ES = 9;
    
    /** European Number Terminator */
    public static final byte ET = 10;
    
    /** Arabic Number */
    public static final byte AN = 11;
    
    /** Common Number Separator */
    public static final byte CS = 12;
    
    /** Non-Spacing Mark */
    public static final byte NSM = 13;
    
    /** Boundary Neutral */
    public static final byte BN = 14;
    
    /** Paragraph Separator */
    public static final byte B = 15;
    
    /** Segment Separator */
    public static final byte S = 16;
    
    /** Whitespace */
    public static final byte WS = 17;
    
    /** Other Neutrals */
    public static final byte ON = 18;
    
    /** Minimum bidi type value. */
    public static final byte TYPE_MIN = 0;
    
    /** Maximum bidi type value. */
    public static final byte TYPE_MAX = 18;
    
    //
    // Input
    //
    
    /**
     * Initialize using an array of direction types.  Types range from TYPE_MIN to TYPE_MAX inclusive
     * and represent the direction codes of the characters in the text.
     *
     @param types the types array
     */
    public BidiOrder(byte[] types) {
        validateTypes(types);
        
        this.initialTypes = (byte[])types.clone()// client type array remains unchanged
        
        runAlgorithm();
    }
    
    /**
     * Initialize using an array of direction types and an externally supplied paragraph embedding level.
     * The embedding level may be -1, 0, or 1.  -1 means to apply the default algorithm (rules P2 and P3),
     * 0 is for LTR paragraphs, and 1 is for RTL paragraphs.
     *
     @param types the types array
     @param paragraphEmbeddingLevel the externally supplied paragraph embedding level.
     */
    public BidiOrder(byte[] types, byte paragraphEmbeddingLevel) {
        validateTypes(types);
        validateParagraphEmbeddingLevel(paragraphEmbeddingLevel);
        
        this.initialTypes = (byte[])types.clone()// client type array remains unchanged
        this.paragraphEmbeddingLevel = paragraphEmbeddingLevel;
        
        runAlgorithm();
    }
    
    public BidiOrder(char text[]int offset, int length, byte paragraphEmbeddingLevel) {
        initialTypes = new byte[length];
        for (int k = 0; k < length; ++k) {
            initialTypes[k= rtypes[text[offset + k]];
        }
        validateParagraphEmbeddingLevel(paragraphEmbeddingLevel);
        
        this.paragraphEmbeddingLevel = paragraphEmbeddingLevel;
        
        runAlgorithm();
    }
    
    public final static byte getDirection(char c) {
        return rtypes[c];
    }
    
    /**
     * The algorithm.
     * Does not include line-based processing (Rules L1, L2).
     * These are applied later in the line-based phase of the algorithm.
     */
    private void runAlgorithm() {
        textLength = initialTypes.length;
        
        // Initialize output types.
        // Result types initialized to input types.
        resultTypes = (byte[])initialTypes.clone();
        
        
        // 1) determining the paragraph level
        // Rule P1 is the requirement for entering this algorithm.
        // Rules P2, P3.
        // If no externally supplied paragraph embedding level, use default.
        if (paragraphEmbeddingLevel == -1) {
            determineParagraphEmbeddingLevel();
        }
        
        // Initialize result levels to paragraph embedding level.
        resultLevels = new byte[textLength];
        setLevels(0, textLength, paragraphEmbeddingLevel);
        
        // 2) Explicit levels and directions
        // Rules X1-X8.
        determineExplicitEmbeddingLevels();
        
        // Rule X9.
        textLength = removeExplicitCodes();
        
        // Rule X10.
        // Run remainder of algorithm one level run at a time
        byte prevLevel = paragraphEmbeddingLevel;
        int start = 0;
        while (start < textLength) {
            byte level = resultLevels[start];
            byte prevType = typeForLevel(Math.max(prevLevel, level));
            
            int limit = start + 1;
            while (limit < textLength && resultLevels[limit== level) {
                ++limit;
            }
            
            byte succLevel = limit < textLength ? resultLevels[limit: paragraphEmbeddingLevel;
            byte succType = typeForLevel(Math.max(succLevel, level));
            
            // 3) resolving weak types
            // Rules W1-W7.
            resolveWeakTypes(start, limit, level, prevType, succType);
            
            // 4) resolving neutral types
            // Rules N1-N3.
            resolveNeutralTypes(start, limit, level, prevType, succType);
            
            // 5) resolving implicit embedding levels
            // Rules I1, I2.
            resolveImplicitLevels(start, limit, level, prevType, succType);
            
            prevLevel = level;
            start = limit;
        }
        
        // Reinsert explicit codes and assign appropriate levels to 'hide' them.
        // This is for convenience, so the resulting level array maps 1-1
        // with the initial array.
        // See the implementation suggestions section of TR#9 for guidelines on
        // how to implement the algorithm without removing and reinserting the codes.
        textLength = reinsertExplicitCodes(textLength);
    }
    
    /**
     * 1) determining the paragraph level.
     <p>
     * Rules P2, P3.
     <p>
     * At the end of this function, the member variable paragraphEmbeddingLevel is set to either 0 or 1.
     */
    private void determineParagraphEmbeddingLevel() {
        byte strongType = -1// unknown
        
        // Rule P2.
        for (int i = 0; i < textLength; ++i) {
            byte t = resultTypes[i];
            if (t == L || t == AL || t == R) {
                strongType = t;
                break;
            }
        }
        
        // Rule P3.
        if (strongType == -1) { // none found
            // default embedding level when no strong types found is 0.
            paragraphEmbeddingLevel = 0;
        else if (strongType == L) {
            paragraphEmbeddingLevel = 0;
        else // AL, R
            paragraphEmbeddingLevel = 1;
        }
    }
    
    /**
     * Process embedding format codes.
     <p>
     * Calls processEmbeddings to generate an embedding array from the explicit format codes.  The
     * embedding overrides in the array are then applied to the result types, and the result levels are
     * initialized.
     @see #processEmbeddings
     */
    private void determineExplicitEmbeddingLevels() {
        embeddings = processEmbeddings(resultTypes, paragraphEmbeddingLevel);
        
        for (int i = 0; i < textLength; ++i) {
            byte level = embeddings[i];
            if ((level & 0x80!= 0) {
                level &= 0x7f;
                resultTypes[i= typeForLevel(level);
            }
            resultLevels[i= level;
        }
    }
    
    /**
     * Rules X9.
     * Remove explicit codes so that they may be ignored during the remainder
     * of the main portion of the algorithm.  The length of the resulting text
     * is returned.
     @return the length of the data excluding explicit codes and BN.
     */
    private int removeExplicitCodes() {
        int w = 0;
        for (int i = 0; i < textLength; ++i) {
            byte t = initialTypes[i];
            if (!(t == LRE || t == RLE || t == LRO || t == RLO || t == PDF || t == BN)) {
                embeddings[w= embeddings[i];
                resultTypes[w= resultTypes[i];
                resultLevels[w= resultLevels[i];
                w++;
            }
        }
        return w; // new textLength while explicit levels are removed
    }
    
    /**
     * Reinsert levels information for explicit codes.
     * This is for ease of relating the level information
     * to the original input data.  Note that the levels
     * assigned to these codes are arbitrary, they're
     * chosen so as to avoid breaking level runs.
     @param textLength the length of the data after compression
     @return the length of the data (original length of
     * types array supplied to constructor)
     */
    private int reinsertExplicitCodes(int textLength) {
        for (int i = initialTypes.length; --i >= 0;) {
            byte t = initialTypes[i];
            if (t == LRE || t == RLE || t == LRO || t == RLO || t == PDF || t == BN) {
                embeddings[i0;
                resultTypes[i= t;
                resultLevels[i= -1;
            else {
                --textLength;
                embeddings[i= embeddings[textLength];
                resultTypes[i= resultTypes[textLength];
                resultLevels[i= resultLevels[textLength];
            }
        }
        
        // now propagate forward the levels information (could have
        // propagated backward, the main thing is not to introduce a level
        // break where one doesn't already exist).
        
        if (resultLevels[0== -1) {
            resultLevels[0= paragraphEmbeddingLevel;
        }
        for (int i = 1; i < initialTypes.length; ++i) {
            if (resultLevels[i== -1) {
                resultLevels[i= resultLevels[i-1];
            }
        }
        
        // Embedding information is for informational purposes only
        // so need not be adjusted.
        
        return initialTypes.length;
    }
    
    /**
     * 2) determining explicit levels
     * Rules X1 - X8
     *
     * The interaction of these rules makes handling them a bit complex.
     * This examines resultTypes but does not modify it.  It returns embedding and
     * override information in the result array.  The low 7 bits are the level, the high
     * bit is set if the level is an override, and clear if it is an embedding.
     */
    private static byte[] processEmbeddings(byte[] resultTypes, byte paragraphEmbeddingLevel) {
        final int EXPLICIT_LEVEL_LIMIT = 62;
        
        int textLength = resultTypes.length;
        byte[] embeddings = new byte[textLength];
        
        // This stack will store the embedding levels and override status in a single byte
        // as described above.
        byte[] embeddingValueStack = new byte[EXPLICIT_LEVEL_LIMIT];
        int stackCounter = 0;
        
        // An LRE or LRO at level 60 is invalid, since the new level 62 is invalid.  But
        // an RLE at level 60 is valid, since the new level 61 is valid.  The current wording
        // of the rules requires that the RLE remain valid even if a previous LRE is invalid.
        // This keeps track of ignored LRE or LRO codes at level 60, so that the matching PDFs
        // will not try to pop the stack.
        int overflowAlmostCounter = 0;
        
        // This keeps track of ignored pushes at level 61 or higher, so that matching PDFs will
        // not try to pop the stack.
        int overflowCounter = 0;
        
        // Rule X1.
        
        // Keep the level separate from the value (level | override status flag) for ease of access.
        byte currentEmbeddingLevel = paragraphEmbeddingLevel;
        byte currentEmbeddingValue = paragraphEmbeddingLevel;
        
        // Loop through types, handling all remaining rules
        for (int i = 0; i < textLength; ++i) {
            
            embeddings[i= currentEmbeddingValue;
            
            byte t = resultTypes[i];
            
            // Rules X2, X3, X4, X5
            switch (t) {
                case RLE:
                case LRE:
                case RLO:
                case LRO:
                    // Only need to compute new level if current level is valid
                    if (overflowCounter == 0) {
                        byte newLevel;
                        if (t == RLE || t == RLO) {
                            newLevel = (byte)((currentEmbeddingLevel + 11)// least greater odd
                        else // t == LRE || t == LRO
                            newLevel = (byte)((currentEmbeddingLevel + 2& ~1)// least greater even
                        }
                        
                        // If the new level is valid, push old embedding level and override status
                        // No check for valid stack counter, since the level check suffices.
                        if (newLevel < EXPLICIT_LEVEL_LIMIT) {
                            embeddingValueStack[stackCounter= currentEmbeddingValue;
                            stackCounter++;
                            
                            currentEmbeddingLevel = newLevel;
                            if (t == LRO || t == RLO) { // override
                                currentEmbeddingValue = (byte)(newLevel | 0x80);
                            else {
                                currentEmbeddingValue = newLevel;
                            }
                            
                            // Adjust level of format mark (for expositional purposes only, this gets
                            // removed later).
                            embeddings[i= currentEmbeddingValue;
                            break;
                        }
                        
                        // Otherwise new level is invalid, but a valid level can still be achieved if this
                        // level is 60 and we encounter an RLE or RLO further on.  So record that we
                        // 'almost' overflowed.
                        if (currentEmbeddingLevel == 60) {
                            overflowAlmostCounter++;
                            break;
                        }
                    }
                    
                    // Otherwise old or new level is invalid.
                    overflowCounter++;
                    break;
                    
                case PDF:
                    // The only case where this did not actually overflow but may have almost overflowed
                    // is when there was an RLE or RLO on level 60, which would result in level 61.  So we
                    // only test the almost overflow condition in that case.
                    //
                    // Also note that there may be a PDF without any pushes at all.
                    
                    if (overflowCounter > 0) {
                        --overflowCounter;
                    else if (overflowAlmostCounter > && currentEmbeddingLevel != 61) {
                        --overflowAlmostCounter;
                    else if (stackCounter > 0) {
                        --stackCounter;
                        currentEmbeddingValue = embeddingValueStack[stackCounter];
                        currentEmbeddingLevel = (byte)(currentEmbeddingValue & 0x7f);
                    }
                    break;
                    
                case B:
                    // Rule X8.
                    
                    // These values are reset for clarity, in this implementation B can only
                    // occur as the last code in the array.
                    stackCounter = 0;
                    overflowCounter = 0;
                    overflowAlmostCounter = 0;
                    currentEmbeddingLevel = paragraphEmbeddingLevel;
                    currentEmbeddingValue = paragraphEmbeddingLevel;
                    
                    embeddings[i= paragraphEmbeddingLevel;
                    break;
                    
                default:
                    break;
            }
        }
        
        return embeddings;
    }
    
    
    /**
     * 3) resolving weak types
     * Rules W1-W7.
     *
     * Note that some weak types (EN, AN) remain after this processing is complete.
     */
    private void resolveWeakTypes(int start, int limit, byte level, byte sor, byte eor) {
        
        // Rule W1.
        // Changes all NSMs.
        byte preceedingCharacterType = sor;
        for (int i = start; i < limit; ++i) {
            byte t = resultTypes[i];
            if (t == NSM) {
                resultTypes[i= preceedingCharacterType;
            else {
                preceedingCharacterType = t;
            }
        }
        
        // Rule W2.
        // EN does not change at the start of the run, because sor != AL.
        for (int i = start; i < limit; ++i) {
            if (resultTypes[i== EN) {
                for (int j = i - 1; j >= start; --j) {
                    byte t = resultTypes[j];
                    if (t == L || t == R || t == AL) {
                        if (t == AL) {
                            resultTypes[i= AN;
                        }
                        break;
                    }
                }
            }
        }
        
        // Rule W3.
        for (int i = start; i < limit; ++i) {
            if (resultTypes[i== AL) {
                resultTypes[i= R;
            }
        }
        
        // Rule W4.
        // Since there must be values on both sides for this rule to have an
        // effect, the scan skips the first and last value.
        //
        // Although the scan proceeds left to right, and changes the type values
        // in a way that would appear to affect the computations later in the scan,
        // there is actually no problem.  A change in the current value can only
        // affect the value to its immediate right, and only affect it if it is
        // ES or CS.  But the current value can only change if the value to its
        // right is not ES or CS.  Thus either the current value will not change,
        // or its change will have no effect on the remainder of the analysis.
        
        for (int i = start + 1; i < limit - 1; ++i) {
            if (resultTypes[i== ES || resultTypes[i== CS) {
                byte prevSepType = resultTypes[i-1];
                byte succSepType = resultTypes[i+1];
                if (prevSepType == EN && succSepType == EN) {
                    resultTypes[i= EN;
                else if (resultTypes[i== CS && prevSepType == AN && succSepType == AN) {
                    resultTypes[i= AN;
                }
            }
        }
        
        // Rule W5.
        for (int i = start; i < limit; ++i) {
            if (resultTypes[i== ET) {
                // locate end of sequence
                int runstart = i;
                int runlimit = findRunLimit(runstart, limit, new byte[] { ET });
                
                // check values at ends of sequence
                byte t = runstart == start ? sor : resultTypes[runstart - 1];
                
                if (t != EN) {
                    t = runlimit == limit ? eor : resultTypes[runlimit];
                }
                
                if (t == EN) {
                    setTypes(runstart, runlimit, EN);
                }
                
                // continue at end of sequence
                i = runlimit;
            }
        }
        
        // Rule W6.
        for (int i = start; i < limit; ++i) {
            byte t = resultTypes[i];
            if (t == ES || t == ET || t == CS) {
                resultTypes[i= ON;
            }
        }
        
        // Rule W7.
        for (int i = start; i < limit; ++i) {
            if (resultTypes[i== EN) {
                // set default if we reach start of run
                byte prevStrongType = sor;
                for (int j = i - 1; j >= start; --j) {
                    byte t = resultTypes[j];
                    if (t == L || t == R) { // AL's have been removed
                        prevStrongType = t;
                        break;
                    }
                }
                if (prevStrongType == L) {
                    resultTypes[i= L;
                }
            }
        }
    }
    
    /**
     * 6) resolving neutral types
     * Rules N1-N2.
     */
    private void resolveNeutralTypes(int start, int limit, byte level, byte sor, byte eor) {
        
        for (int i = start; i < limit; ++i) {
            byte t = resultTypes[i];
            if (t == WS || t == ON || t == B || t == S) {
                // find bounds of run of neutrals
                int runstart = i;
                int runlimit = findRunLimit(runstart, limit, new byte[] {B, S, WS, ON});
                
                // determine effective types at ends of run
                byte leadingType;
                byte trailingType;
                
                if (runstart == start) {
                    leadingType = sor;
                else {
                    leadingType = resultTypes[runstart - 1];
                    if (leadingType == L || leadingType == R) {
                        // found the strong type
                    else if (leadingType == AN) {
                        leadingType = R;
                    else if (leadingType == EN) {
                        // Since EN's with previous strong L types have been changed
                        // to L in W7, the leadingType must be R.
                        leadingType = R;
                    }
                }
                
                if (runlimit == limit) {
                    trailingType = eor;
                else {
                    trailingType = resultTypes[runlimit];
                    if (trailingType == L || trailingType == R) {
                        // found the strong type
                    else if (trailingType == AN) {
                        trailingType = R;
                    else if (trailingType == EN) {
                        trailingType = R;
                    }
                }
                
                byte resolvedType;
                if (leadingType == trailingType) {
                    // Rule N1.
                    resolvedType = leadingType;
                else {
                    // Rule N2.
                    // Notice the embedding level of the run is used, not
                    // the paragraph embedding level.
                    resolvedType = typeForLevel(level);
                }
                
                setTypes(runstart, runlimit, resolvedType);
                
                // skip over run of (former) neutrals
                i = runlimit;
            }
        }
    }
    
    /**
     * 7) resolving implicit embedding levels
     * Rules I1, I2.
     */
    private void resolveImplicitLevels(int start, int limit, byte level, byte sor, byte eor) {
        if ((level & 1== 0) { // even level
            for (int i = start; i < limit; ++i) {
                byte t = resultTypes[i];
                // Rule I1.
                if (t == L ) {
                    // no change
                else if (t == R) {
                    resultLevels[i+= 1;
                else // t == AN || t == EN
                    resultLevels[i+= 2;
                }
            }
        else // odd level
            for (int i = start; i < limit; ++i) {
                byte t = resultTypes[i];
                // Rule I2.
                if (t == R) {
                    // no change
                else // t == L || t == AN || t == EN
                    resultLevels[i+= 1;
                }
            }
        }
    }
    
    //
    // Output
    //
    
    public byte[] getLevels() {
        return getLevels(new int[]{textLength});
    }
    
    /**
     * Return levels array breaking lines at offsets in linebreaks. <br>
     * Rule L1.
     <p>
     * The returned levels array contains the resolved level for each
     * bidi code passed to the constructor.
     <p>
     * The linebreaks array must include at least one value.
     * The values must be in strictly increasing order (no duplicates)
     * between 1 and the length of the text, inclusive.  The last value
     * must be the length of the text.
     *
     @param linebreaks the offsets at which to break the paragraph
     @return the resolved levels of the text
     */
    public byte[] getLevels(int[] linebreaks) {
        
        // Note that since the previous processing has removed all
        // P, S, and WS values from resultTypes, the values referred to
        // in these rules are the initial types, before any processing
        // has been applied (including processing of overrides).
        //
        // This example implementation has reinserted explicit format codes
        // and BN, in order that the levels array correspond to the
        // initial text.  Their final placement is not normative.
        // These codes are treated like WS in this implementation,
        // so they don't interrupt sequences of WS.
        
        validateLineBreaks(linebreaks, textLength);
        
        byte[] result = (byte[])resultLevels.clone()// will be returned to caller
        
        // don't worry about linebreaks since if there is a break within
        // a series of WS values preceding S, the linebreak itself
        // causes the reset.
        for (int i = 0; i < result.length; ++i) {
            byte t = initialTypes[i];
            if (t == B || t == S) {
                // Rule L1, clauses one and two.
                result[i= paragraphEmbeddingLevel;
                
                // Rule L1, clause three.
                for (int j = i - 1; j >= 0; --j) {
                    if (isWhitespace(initialTypes[j])) { // including format codes
                        result[j= paragraphEmbeddingLevel;
                    else {
                        break;
                    }
                }
            }
        }
        
        // Rule L1, clause four.
        int start = 0;
        for (int i = 0; i < linebreaks.length; ++i) {
            int limit = linebreaks[i];
            for (int j = limit - 1; j >= start; --j) {
                if (isWhitespace(initialTypes[j])) { // including format codes
                    result[j= paragraphEmbeddingLevel;
                else {
                    break;
                }
            }
            
            start = limit;
        }
        
        return result;
    }
    
    /**
     * Return reordering array breaking lines at offsets in linebreaks.
     <p>
     * The reordering array maps from a visual index to a logical index.
     * Lines are concatenated from left to right.  So for example, the
     * fifth character from the left on the third line is
     <pre> getReordering(linebreaks)[linebreaks[1] + 4]</pre>
     * (linebreaks[1] is the position after the last character of the
     * second line, which is also the index of the first character on the
     * third line, and adding four gets the fifth character from the left).
     <p>
     * The linebreaks array must include at least one value.
     * The values must be in strictly increasing order (no duplicates)
     * between 1 and the length of the text, inclusive.  The last value
     * must be the length of the text.
     *
     @param linebreaks the offsets at which to break the paragraph.
     */
    public int[] getReordering(int[] linebreaks) {
        validateLineBreaks(linebreaks, textLength);
        
        byte[] levels = getLevels(linebreaks);
        
        return computeMultilineReordering(levels, linebreaks);
    }
    
    /**
     * Return multiline reordering array for a given level array.
     * Reordering does not occur across a line break.
     */
    private static int[] computeMultilineReordering(byte[] levels, int[] linebreaks) {
        int[] result = new int[levels.length];
        
        int start = 0;
        for (int i = 0; i < linebreaks.length; ++i) {
            int limit = linebreaks[i];
            
            byte[] templevels = new byte[limit - start];
            System.arraycopy(levels, start, templevels, 0, templevels.length);
            
            int[] temporder = computeReordering(templevels);
            for (int j = 0; j < temporder.length; ++j) {
                result[start + j= temporder[j+ start;
            }
            
            start = limit;
        }
        
        return result;
    }
    
    /**
     * Return reordering array for a given level array.  This reorders a single line.
     * The reordering is a visual to logical map.  For example,
     * the leftmost char is string.charAt(order[0]).
     * Rule L2.
     */
    private static int[] computeReordering(byte[] levels) {
        int lineLength = levels.length;
        
        int[] result = new int[lineLength];
        
        // initialize order
        for (int i = 0; i < lineLength; ++i) {
            result[i= i;
        }
        
        // locate highest level found on line.
        // Note the rules say text, but no reordering across line bounds is performed,
        // so this is sufficient.
        byte highestLevel = 0;
        byte lowestOddLevel = 63;
        for (int i = 0; i < lineLength; ++i) {
            byte level = levels[i];
            if (level > highestLevel) {
                highestLevel = level;
            }
            if (((level & 1!= 0&& level < lowestOddLevel) {
                lowestOddLevel = level;
            }
        }
        
        for (int level = highestLevel; level >= lowestOddLevel; --level) {
            for (int i = 0; i < lineLength; ++i) {
                if (levels[i>= level) {
                    // find range of text at or above this level
                    int start = i;
                    int limit = i + 1;
                    while (limit < lineLength && levels[limit>= level) {
                        ++limit;
                    }
                    
                    // reverse run
                    for (int j = start, k = limit - 1; j < k; ++j, --k) {
                        int temp = result[j];
                        result[j= result[k];
                        result[k= temp;
                    }
                    
                    // skip to end of level run
                    i = limit;
                }
            }
        }
        
        return result;
    }
    
    /**
     * Return the base level of the paragraph.
     */
    public byte getBaseLevel() {
        return paragraphEmbeddingLevel;
    }
    
    // --- internal utilities -------------------------------------------------
    
    /**
     * Return true if the type is considered a whitespace type for the line break rules.
     */
    private static boolean isWhitespace(byte biditype) {
        switch (biditype) {
            case LRE:
            case RLE:
            case LRO:
            case RLO:
            case PDF:
            case BN:
            case WS:
                return true;
            default:
                return false;
        }
    }
    
    /**
     * Return the strong type (L or R) corresponding to the level.
     */
    private static byte typeForLevel(int level) {
        return ((level & 0x1== 0? L : R;
    }
    
    /**
     * Return the limit of the run starting at index that includes only resultTypes in validSet.
     * This checks the value at index, and will return index if that value is not in validSet.
     */
    private int findRunLimit(int index, int limit, byte[] validSet) {
        --index;
        loop:
            while (++index < limit) {
                byte t = resultTypes[index];
                for (int i = 0; i < validSet.length; ++i) {
                    if (t == validSet[i]) {
                        continue loop;
                    }
                }
                // didn't find a match in validSet
                return index;
            }
            return limit;
    }
    
    /**
     * Return the start of the run including index that includes only resultTypes in validSet.
     * This assumes the value at index is valid, and does not check it.
     */
    private int findRunStart(int index, byte[] validSet) {
        loop:
            while (--index >= 0) {
                byte t = resultTypes[index];
                for (int i = 0; i < validSet.length; ++i) {
                    if (t == validSet[i]) {
                        continue loop;
                    }
                }
                return index + 1;
            }
            return 0;
    }
    
    /**
     * Set resultTypes from start up to (but not including) limit to newType.
     */
    private void setTypes(int start, int limit, byte newType) {
        for (int i = start; i < limit; ++i) {
            resultTypes[i= newType;
        }
    }
    
    /**
     * Set resultLevels from start up to (but not including) limit to newLevel.
     */
    private void setLevels(int start, int limit, byte newLevel) {
        for (int i = start; i < limit; ++i) {
            resultLevels[i= newLevel;
        }
    }
    
    // --- input validation ---------------------------------------------------
    
    /**
     * Throw exception if type array is invalid.
     */
    private static void validateTypes(byte[] types) {
        if (types == null) {
            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("types.is.null"));
        }
        for (int i = 0; i < types.length; ++i) {
            if (types[i< TYPE_MIN || types[i> TYPE_MAX) {
                throw new IllegalArgumentException(MessageLocalization.getComposedMessage("illegal.type.value.at.1.2", String.valueOf(i), String.valueOf(types[i])));
            }
        }
        for (int i = 0; i < types.length - 1; ++i) {
            if (types[i== B) {
                throw new IllegalArgumentException(MessageLocalization.getComposedMessage("b.type.before.end.of.paragraph.at.index.1", i));
            }
        }
    }
    
    /**
     * Throw exception if paragraph embedding level is invalid. Special allowance for -1 so that
     * default processing can still be performed when using this API.
     */
    private static void validateParagraphEmbeddingLevel(byte paragraphEmbeddingLevel) {
        if (paragraphEmbeddingLevel != -&&
        paragraphEmbeddingLevel != &&
        paragraphEmbeddingLevel != 1) {
            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("illegal.paragraph.embedding.level.1", paragraphEmbeddingLevel));
        }
    }
    
    /**
     * Throw exception if line breaks array is invalid.
     */
    private static void validateLineBreaks(int[] linebreaks, int textLength) {
        int prev = 0;
        for (int i = 0; i < linebreaks.length; ++i) {
            int next = linebreaks[i];
            if (next <= prev) {
                throw new IllegalArgumentException(MessageLocalization.getComposedMessage("bad.linebreak.1.at.index.2", String.valueOf(next), String.valueOf(i)));
            }
            prev = next;
        }
        if (prev != textLength) {
            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("last.linebreak.must.be.at.1", textLength));
        }
    }
    
    private static final byte rtypes[] new byte[0x10000];
    
    private static char baseTypes[] {
        08(char)BN, 99(char)S, 1010(char)B, 1111(char)S, 1212(char)WS, 1313(char)B,
        1427(char)BN, 2830(char)B, 3131(char)S, 3232(char)WS, 3334(char)ON, 3537(char)ET,
        3842(char)ON, 4343(char)ET, 4444(char)CS, 4545(char)ET, 4646(char)CS, 4747(char)ES,
        4857(char)EN, 5858(char)CS, 5964(char)ON, 6590(char)L, 9196(char)ON, 97122(char)L,
        123126(char)ON, 127132(char)BN, 133133(char)B, 134159(char)BN, 160160(char)CS,
        161161(char)ON, 162165(char)ET, 166169(char)ON, 170170(char)L, 171175(char)ON,
        176177(char)ET, 178179(char)EN, 180180(char)ON, 181181(char)L, 182184(char)ON,
        185185(char)EN, 186186(char)L, 187191(char)ON, 192214(char)L, 215215(char)ON,
        216246(char)L, 247247(char)ON, 248696(char)L, 697698(char)ON, 699705(char)L,
        706719(char)ON, 720721(char)L, 722735(char)ON, 736740(char)L, 741749(char)ON,
        750750(char)L, 751767(char)ON, 768855(char)NSM, 856860(char)L, 861879(char)NSM,
        880883(char)L, 884885(char)ON, 886893(char)L, 894894(char)ON, 895899(char)L,
        900901(char)ON, 902902(char)L, 903903(char)ON, 9041013(char)L, 10141014(char)ON,
        10151154(char)L, 11551158(char)NSM, 11591159(char)L, 11601161(char)NSM,
        11621417(char)L, 14181418(char)ON, 14191424(char)L, 14251441(char)NSM,
        14421442(char)L, 14431465(char)NSM, 14661466(char)L, 14671469(char)NSM,
        14701470(char)R, 14711471(char)NSM, 14721472(char)R, 14731474(char)NSM,
        14751475(char)R, 14761476(char)NSM, 14771487(char)L, 14881514(char)R,
        15151519(char)L, 15201524(char)R, 15251535(char)L, 15361539(char)AL,
        15401547(char)L, 15481548(char)CS, 15491549(char)AL, 15501551(char)ON,
        15521557(char)NSM, 15581562(char)L, 15631563(char)AL, 15641566(char)L,
        15671567(char)AL, 15681568(char)L, 15691594(char)AL, 15951599(char)L,
        16001610(char)AL, 16111624(char)NSM, 16251631(char)L, 16321641(char)AN,
        16421642(char)ET, 16431644(char)AN, 16451647(char)AL, 16481648(char)NSM,
        16491749(char)AL, 17501756(char)NSM, 17571757(char)AL, 17581764(char)NSM,
        17651766(char)AL, 17671768(char)NSM, 17691769(char)ON, 17701773(char)NSM,
        17741775(char)AL, 17761785(char)EN, 17861805(char)AL, 18061806(char)L,
        18071807(char)BN, 18081808(char)AL, 18091809(char)NSM, 18101839(char)AL,
        18401866(char)NSM, 18671868(char)L, 18691871(char)AL, 18721919(char)L,
        19201957(char)AL, 19581968(char)NSM, 19691969(char)AL, 19702304(char)L,
        23052306(char)NSM, 23072363(char)L, 23642364(char)NSM, 23652368(char)L,
        23692376(char)NSM, 23772380(char)L, 23812381(char)NSM, 23822384(char)L,
        23852388(char)NSM, 23892401(char)L, 24022403(char)NSM, 24042432(char)L,
        24332433(char)NSM, 24342491(char)L, 24922492(char)NSM, 24932496(char)L,
        24972500(char)NSM, 25012508(char)L, 25092509(char)NSM, 25102529(char)L,
        25302531(char)NSM, 25322545(char)L, 25462547(char)ET, 25482560(char)L,
        25612562(char)NSM, 25632619(char)L, 26202620(char)NSM, 26212624(char)L,
        26252626(char)NSM, 26272630(char)L, 26312632(char)NSM, 26332634(char)L,
        26352637(char)NSM, 26382671(char)L, 26722673(char)NSM, 26742688(char)L,
        26892690(char)NSM, 26912747(char)L, 27482748(char)NSM, 27492752(char)L,
        27532757(char)NSM, 27582758(char)L, 27592760(char)NSM, 27612764(char)L,
        27652765(char)NSM, 27662785(char)L, 27862787(char)NSM, 27882800(char)L,
        28012801(char)ET, 28022816(char)L, 28172817(char)NSM, 28182875(char)L,
        28762876(char)NSM, 28772878(char)L, 28792879(char)NSM, 28802880(char)L,
        28812883(char)NSM, 28842892(char)L, 28932893(char)NSM, 28942901(char)L,
        29022902(char)NSM, 29032945(char)L, 29462946(char)NSM, 29473007(char)L,
        30083008(char)NSM, 30093020(char)L, 30213021(char)NSM, 30223058(char)L,
        30593064(char)ON, 30653065(char)ET, 30663066(char)ON, 30673133(char)L,
        31343136(char)NSM, 31373141(char)L, 31423144(char)NSM, 31453145(char)L,
        31463149(char)NSM, 31503156(char)L, 31573158(char)NSM, 31593259(char)L,
        32603260(char)NSM, 32613275(char)L, 32763277(char)NSM, 32783392(char)L,
        33933395(char)NSM, 33963404(char)L, 34053405(char)NSM, 34063529(char)L,
        35303530(char)NSM, 35313537(char)L, 35383540(char)NSM, 35413541(char)L,
        35423542(char)NSM, 35433632(char)L, 36333633(char)NSM, 36343635(char)L,
        36363642(char)NSM, 36433646(char)L, 36473647(char)ET, 36483654(char)L,
        36553662(char)NSM, 36633760(char)L, 37613761(char)NSM, 37623763(char)L,
        37643769(char)NSM, 37703770(char)L, 37713772(char)NSM, 37733783(char)L,
        37843789(char)NSM, 37903863(char)L, 38643865(char)NSM, 38663892(char)L,
        38933893(char)NSM, 38943894(char)L, 38953895(char)NSM, 38963896(char)L,
        38973897(char)NSM, 38983901(char)ON, 39023952(char)L, 39533966(char)NSM,
        39673967(char)L, 39683972(char)NSM, 39733973(char)L, 39743975(char)NSM,
        39763983(char)L, 39843991(char)NSM, 39923992(char)L, 39934028(char)NSM,
        40294037(char)L, 40384038(char)NSM, 40394140(char)L, 41414144(char)NSM,
        41454145(char)L, 41464146(char)NSM, 41474149(char)L, 41504151(char)NSM,
        41524152(char)L, 41534153(char)NSM, 41544183(char)L, 41844185(char)NSM,
        41865759(char)L, 57605760(char)WS, 57615786(char)L, 57875788(char)ON,
        57895905(char)L, 59065908(char)NSM, 59095937(char)L, 59385940(char)NSM,
        59415969(char)L, 59705971(char)NSM, 59726001(char)L, 60026003(char)NSM,
        60046070(char)L, 60716077(char)NSM, 60786085(char)L, 60866086(char)NSM,
        60876088(char)L, 60896099(char)NSM, 61006106(char)L, 61076107(char)ET,
        61086108(char)L, 61096109(char)NSM, 61106127(char)L, 61286137(char)ON,
        61386143(char)L, 61446154(char)ON, 61556157(char)NSM, 61586158(char)WS,
        61596312(char)L, 63136313(char)NSM, 63146431(char)L, 64326434(char)NSM,
        64356438(char)L, 64396443(char)NSM, 64446449(char)L, 64506450(char)NSM,
        64516456(char)L, 64576459(char)NSM, 64606463(char)L, 64646464(char)ON,
        64656467(char)L, 64686469(char)ON, 64706623(char)L, 66246655(char)ON,
        66568124(char)L, 81258125(char)ON, 81268126(char)L, 81278129(char)ON,
        81308140(char)L, 81418143(char)ON, 81448156(char)L, 81578159(char)ON,
        81608172(char)L, 81738175(char)ON, 81768188(char)L, 81898190(char)ON,
        81918191(char)L, 81928202(char)WS, 82038205(char)BN, 82068206(char)L,
        82078207(char)R, 82088231(char)ON, 82328232(char)WS, 82338233(char)B,
        82348234(char)LRE, 82358235(char)RLE, 82368236(char)PDF, 82378237(char)LRO,
        82388238(char)RLO, 82398239(char)WS, 82408244(char)ET, 82458276(char)ON,
        82778278(char)L, 82798279(char)ON, 82808286(char)L, 82878287(char)WS,
        82888291(char)BN, 82928297(char)L, 82988303(char)BN, 83048304(char)EN,
        83058307(char)L, 83088313(char)EN, 83148315(char)ET, 83168318(char)ON,
        83198319(char)L, 83208329(char)EN, 83308331(char)ET, 83328334(char)ON,
        83358351(char)L, 83528369(char)ET, 83708399(char)L, 84008426(char)NSM,
        84278447(char)L, 84488449(char)ON, 84508450(char)L, 84518454(char)ON,
        84558455(char)L, 84568457(char)ON, 84588467(char)L, 84688468(char)ON,
        84698469(char)L, 84708472(char)ON, 84738477(char)L, 84788483(char)ON,
        84848484(char)L, 84858485(char)ON, 84868486(char)L, 84878487(char)ON,
        84888488(char)L, 84898489(char)ON, 84908493(char)L, 84948494(char)ET,
        84958497(char)L, 84988498(char)ON, 84998505(char)L, 85068507(char)ON,
        85088511(char)L, 85128516(char)ON, 85178521(char)L, 85228523(char)ON,
        85248530(char)L, 85318543(char)ON, 85448591(char)L, 85928721(char)ON,
        87228723(char)ET, 87249013(char)ON, 90149082(char)L, 90839108(char)ON,
        91099109(char)L, 91109168(char)ON, 91699215(char)L, 92169254(char)ON,
        92559279(char)L, 92809290(char)ON, 92919311(char)L, 93129371(char)EN,
        93729449(char)L, 94509450(char)EN, 94519751(char)ON, 97529752(char)L,
        97539853(char)ON, 98549855(char)L, 98569873(char)ON, 98749887(char)L,
        98889889(char)ON, 98909984(char)L, 99859988(char)ON, 99899989(char)L,
        99909993(char)ON, 99949995(char)L, 999610023(char)ON, 1002410024(char)L,
        1002510059(char)ON, 1006010060(char)L, 1006110061(char)ON, 1006210062(char)L,
        1006310066(char)ON, 1006710069(char)L, 1007010070(char)ON, 1007110071(char)L,
        1007210078(char)ON, 1007910080(char)L, 1008110132(char)ON, 1013310135(char)L,
        1013610159(char)ON, 1016010160(char)L, 1016110174(char)ON, 1017510191(char)L,
        1019210219(char)ON, 1022010223(char)L, 1022411021(char)ON, 1102211903(char)L,
        1190411929(char)ON, 1193011930(char)L, 1193112019(char)ON, 1202012031(char)L,
        1203212245(char)ON, 1224612271(char)L, 1227212283(char)ON, 1228412287(char)L,
        1228812288(char)WS, 1228912292(char)ON, 1229312295(char)L, 1229612320(char)ON,
        1232112329(char)L, 1233012335(char)NSM, 1233612336(char)ON, 1233712341(char)L,
        1234212343(char)ON, 1234412348(char)L, 1234912351(char)ON, 1235212440(char)L,
        1244112442(char)NSM, 1244312444(char)ON, 1244512447(char)L, 1244812448(char)ON,
        1244912538(char)L, 1253912539(char)ON, 1254012828(char)L, 1282912830(char)ON,
        1283112879(char)L, 1288012895(char)ON, 1289612923(char)L, 1292412925(char)ON,
        1292612976(char)L, 1297712991(char)ON, 1299213003(char)L, 1300413007(char)ON,
        1300813174(char)L, 1317513178(char)ON, 1317913277(char)L, 1327813279(char)ON,
        1328013310(char)L, 1331113311(char)ON, 1331219903(char)L, 1990419967(char)ON,
        1996842127(char)L, 4212842182(char)ON, 4218364284(char)L, 6428564285(char)R,
        6428664286(char)NSM, 6428764296(char)R, 6429764297(char)ET, 6429864310(char)R,
        6431164311(char)L, 6431264316(char)R, 6431764317(char)L, 6431864318(char)R,
        6431964319(char)L, 6432064321(char)R, 6432264322(char)L, 6432364324(char)R,
        6432564325(char)L, 6432664335(char)R, 6433664433(char)AL, 6443464466(char)L,
        6446764829(char)AL, 6483064831(char)ON, 6483264847(char)L, 6484864911(char)AL,
        6491264913(char)L, 6491464967(char)AL, 6496865007(char)L, 6500865020(char)AL,
        6502165021(char)ON, 6502265023(char)L, 6502465039(char)NSM, 6504065055(char)L,
        6505665059(char)NSM, 6506065071(char)L, 6507265103(char)ON, 6510465104(char)CS,
        6510565105(char)ON, 6510665106(char)CS, 6510765107(char)L, 6510865108(char)ON,
        6510965109(char)CS, 6511065118(char)ON, 6511965119(char)ET, 6512065121(char)ON,
        6512265123(char)ET, 6512465126(char)ON, 6512765127(char)L, 6512865128(char)ON,
        6512965130(char)ET, 6513165131(char)ON, 6513265135(char)L, 6513665140(char)AL,
        6514165141(char)L, 6514265276(char)AL, 6527765278(char)L, 6527965279(char)BN,
        6528065280(char)L, 6528165282(char)ON, 6528365285(char)ET, 6528665290(char)ON,
        6529165291(char)ET, 6529265292(char)CS, 6529365293(char)ET, 6529465294(char)CS,
        6529565295(char)ES, 6529665305(char)EN, 6530665306(char)CS, 6530765312(char)ON,
        6531365338(char)L, 6533965344(char)ON, 6534565370(char)L, 6537165381(char)ON,
        6538265503(char)L, 6550465505(char)ET, 6550665508(char)ON, 6550965510(char)ET,
        6551165511(char)L, 6551265518(char)ON, 6551965528(char)L, 6552965531(char)BN,
        6553265533(char)ON, 6553465535(char)L};
        
    static {
        for (int k = 0; k < baseTypes.length; ++k) {
            int start = baseTypes[k];
            int end = baseTypes[++k];
            byte b = (byte)baseTypes[++k];
            while (start <= end)
                rtypes[start++= b;
        }
    }        
}