17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
3f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert * Copyright (C) 1996-2015, International Business Machines
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Corporation and others.  All Rights Reserved.
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * CollationCompare.java, ported from collationcompare.h/.cpp
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * C++ version created on: 2012feb14 with new and old collation code
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * created by: Markus W. Scherer
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.impl.coll;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.Collator;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic final class CollationCompare /* all static */ {
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static int compareUpToQuaternary(CollationIterator left, CollationIterator right,
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            CollationSettings settings) {
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int options = settings.options;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long variableTop;
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ((options & CollationSettings.ALTERNATE_MASK) == 0) {
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            variableTop = 0;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // +1 so that we can use "<" and primary ignorables test out early.
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            variableTop = settings.variableTop + 1;
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean anyVariable = false;
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Fetch CEs, compare primaries, store secondary & tertiary weights.
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (;;) {
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // We fetch CEs until we get a non-ignorable primary or reach the end.
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long leftPrimary;
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            do {
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long ce = left.nextCE();
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                leftPrimary = ce >>> 32;
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (leftPrimary < variableTop && leftPrimary > Collation.MERGE_SEPARATOR_PRIMARY) {
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Variable CE, shift it to quaternary level.
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Ignore all following primary ignorables, and shift further variable CEs.
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    anyVariable = true;
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    do {
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // Store only the primary of the variable CE.
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        left.setCurrentCE(ce & 0xffffffff00000000L);
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        for (;;) {
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            ce = left.nextCE();
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            leftPrimary = ce >>> 32;
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (leftPrimary == 0) {
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                left.setCurrentCE(0);
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else {
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                break;
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } while (leftPrimary < variableTop && leftPrimary > Collation.MERGE_SEPARATOR_PRIMARY);
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } while (leftPrimary == 0);
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long rightPrimary;
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            do {
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long ce = right.nextCE();
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                rightPrimary = ce >>> 32;
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (rightPrimary < variableTop && rightPrimary > Collation.MERGE_SEPARATOR_PRIMARY) {
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Variable CE, shift it to quaternary level.
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Ignore all following primary ignorables, and shift further variable CEs.
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    anyVariable = true;
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    do {
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // Store only the primary of the variable CE.
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        right.setCurrentCE(ce & 0xffffffff00000000L);
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        for (;;) {
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            ce = right.nextCE();
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            rightPrimary = ce >>> 32;
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (rightPrimary == 0) {
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                right.setCurrentCE(0);
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else {
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                break;
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } while (rightPrimary < variableTop && rightPrimary > Collation.MERGE_SEPARATOR_PRIMARY);
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } while (rightPrimary == 0);
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (leftPrimary != rightPrimary) {
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Return the primary difference, with script reordering.
82f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                if (settings.hasReordering()) {
83f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    leftPrimary = settings.reorder(leftPrimary);
84f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    rightPrimary = settings.reorder(rightPrimary);
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return (leftPrimary < rightPrimary) ? Collation.LESS : Collation.GREATER;
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (leftPrimary == Collation.NO_CE_PRIMARY) {
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Compare the buffered secondary & tertiary weights.
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // We might skip the secondary level but continue with the case level
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // which is turned on separately.
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (CollationSettings.getStrength(options) >= Collator.SECONDARY) {
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ((options & CollationSettings.BACKWARD_SECONDARY) == 0) {
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int leftIndex = 0;
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int rightIndex = 0;
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (;;) {
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int leftSecondary;
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    do {
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        leftSecondary = ((int) left.getCE(leftIndex++)) >>> 16;
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } while (leftSecondary == 0);
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int rightSecondary;
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    do {
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        rightSecondary = ((int) right.getCE(rightIndex++)) >>> 16;
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } while (rightSecondary == 0);
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (leftSecondary != rightSecondary) {
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        return (leftSecondary < rightSecondary) ? Collation.LESS : Collation.GREATER;
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (leftSecondary == Collation.NO_CE_WEIGHT16) {
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        break;
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // The backwards secondary level compares secondary weights backwards
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // within segments separated by the merge separator (U+FFFE, weight 02).
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int leftStart = 0;
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int rightStart = 0;
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (;;) {
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Find the merge separator or the NO_CE terminator.
125f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    long p;
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int leftLimit = leftStart;
127f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    while ((p = left.getCE(leftLimit) >>> 32) > Collation.MERGE_SEPARATOR_PRIMARY
128f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            || p == 0) {
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        ++leftLimit;
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int rightLimit = rightStart;
132f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    while ((p = right.getCE(rightLimit) >>> 32) > Collation.MERGE_SEPARATOR_PRIMARY
133f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            || p == 0) {
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        ++rightLimit;
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Compare the segments.
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int leftIndex = leftLimit;
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int rightIndex = rightLimit;
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (;;) {
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        int leftSecondary = 0;
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        while (leftSecondary == 0 && leftIndex > leftStart) {
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            leftSecondary = ((int) left.getCE(--leftIndex)) >>> 16;
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        int rightSecondary = 0;
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        while (rightSecondary == 0 && rightIndex > rightStart) {
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            rightSecondary = ((int) right.getCE(--rightIndex)) >>> 16;
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (leftSecondary != rightSecondary) {
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            return (leftSecondary < rightSecondary) ? Collation.LESS : Collation.GREATER;
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (leftSecondary == 0) {
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            break;
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Did we reach the end of either string?
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Both strings have the same number of merge separators,
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // or else there would have been a primary-level difference.
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    assert (left.getCE(leftLimit) == right.getCE(rightLimit));
163f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    if (p == Collation.NO_CE_PRIMARY) {
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        break;
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Skip both merge separators and continue.
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    leftStart = leftLimit + 1;
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    rightStart = rightLimit + 1;
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ((options & CollationSettings.CASE_LEVEL) != 0) {
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int strength = CollationSettings.getStrength(options);
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int leftIndex = 0;
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int rightIndex = 0;
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (;;) {
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int leftCase, leftLower32, rightCase;
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (strength == Collator.PRIMARY) {
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Primary+caseLevel: Ignore case level weights of primary ignorables.
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Otherwise we would get a-umlaut > a
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // which is not desirable for accent-insensitive sorting.
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Check for (lower 32 bits) == 0 as well because variable CEs are stored
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // with only primary weights.
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    long ce;
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    do {
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        ce = left.getCE(leftIndex++);
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        leftCase = (int) ce;
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } while ((ce >>> 32) == 0 || leftCase == 0);
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    leftLower32 = leftCase;
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    leftCase &= 0xc000;
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    do {
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        ce = right.getCE(rightIndex++);
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        rightCase = (int) ce;
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } while ((ce >>> 32) == 0 || rightCase == 0);
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    rightCase &= 0xc000;
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Secondary+caseLevel: By analogy with the above,
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // ignore case level weights of secondary ignorables.
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Note: A tertiary CE has uppercase case bits (0.0.ut)
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // to keep tertiary+caseFirst well-formed.
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Tertiary+caseLevel: Also ignore case level weights of secondary ignorables.
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Otherwise a tertiary CE's uppercase would be no greater than
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // a primary/secondary CE's uppercase.
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // (See UCA well-formedness condition 2.)
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // We could construct a special case weight higher than uppercase,
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // but it's simpler to always ignore case weights of secondary ignorables,
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // turning 0.0.ut into 0.0.0.t.
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // (See LDML Collation, Case Parameters.)
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    do {
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        leftCase = (int) left.getCE(leftIndex++);
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } while ((leftCase & 0xffff0000) == 0);
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    leftLower32 = leftCase;
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    leftCase &= 0xc000;
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    do {
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        rightCase = (int) right.getCE(rightIndex++);
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } while ((rightCase & 0xffff0000) == 0);
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    rightCase &= 0xc000;
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // No need to handle NO_CE and MERGE_SEPARATOR specially:
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // There is one case weight for each previous-level weight,
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // so level length differences were handled there.
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (leftCase != rightCase) {
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if ((options & CollationSettings.UPPER_FIRST) == 0) {
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        return (leftCase < rightCase) ? Collation.LESS : Collation.GREATER;
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        return (leftCase < rightCase) ? Collation.GREATER : Collation.LESS;
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ((leftLower32 >>> 16) == Collation.NO_CE_WEIGHT16) {
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (CollationSettings.getStrength(options) <= Collator.SECONDARY) {
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return Collation.EQUAL;
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int tertiaryMask = CollationSettings.getTertiaryMask(options);
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int leftIndex = 0;
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int rightIndex = 0;
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int anyQuaternaries = 0;
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (;;) {
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int leftLower32, leftTertiary;
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            do {
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                leftLower32 = (int) left.getCE(leftIndex++);
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                anyQuaternaries |= leftLower32;
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                assert ((leftLower32 & Collation.ONLY_TERTIARY_MASK) != 0 || (leftLower32 & 0xc0c0) == 0);
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                leftTertiary = leftLower32 & tertiaryMask;
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } while (leftTertiary == 0);
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int rightLower32, rightTertiary;
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            do {
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                rightLower32 = (int) right.getCE(rightIndex++);
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                anyQuaternaries |= rightLower32;
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                assert ((rightLower32 & Collation.ONLY_TERTIARY_MASK) != 0 || (rightLower32 & 0xc0c0) == 0);
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                rightTertiary = rightLower32 & tertiaryMask;
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } while (rightTertiary == 0);
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (leftTertiary != rightTertiary) {
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (CollationSettings.sortsTertiaryUpperCaseFirst(options)) {
268f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    // Pass through NO_CE and keep real tertiary weights larger than that.
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Do not change the artificial uppercase weight of a tertiary CE (0.0.ut),
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // to keep tertiary CEs well-formed.
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Their case+tertiary weights must be greater than those of
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // primary and secondary CEs.
273f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    if (leftTertiary > Collation.NO_CE_WEIGHT16) {
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if ((leftLower32 & 0xffff0000) != 0) {
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            leftTertiary ^= 0xc000;
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else {
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            leftTertiary += 0x4000;
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
280f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    if (rightTertiary > Collation.NO_CE_WEIGHT16) {
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if ((rightLower32 & 0xffff0000) != 0) {
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            rightTertiary ^= 0xc000;
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else {
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            rightTertiary += 0x4000;
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return (leftTertiary < rightTertiary) ? Collation.LESS : Collation.GREATER;
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (leftTertiary == Collation.NO_CE_WEIGHT16) {
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (CollationSettings.getStrength(options) <= Collator.TERTIARY) {
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return Collation.EQUAL;
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!anyVariable && (anyQuaternaries & 0xc0) == 0) {
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // If there are no "variable" CEs and no non-zero quaternary weights,
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // then there are no quaternary differences.
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return Collation.EQUAL;
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        leftIndex = 0;
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        rightIndex = 0;
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (;;) {
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long leftQuaternary;
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            do {
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long ce = left.getCE(leftIndex++);
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                leftQuaternary = ce & 0xffff;
311f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                if (leftQuaternary <= Collation.NO_CE_WEIGHT16) {
312f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    // Variable primary or completely ignorable or NO_CE.
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    leftQuaternary = ce >>> 32;
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Regular CE, not tertiary ignorable.
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Preserve the quaternary weight in bits 7..6.
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    leftQuaternary |= 0xffffff3fL;
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } while (leftQuaternary == 0);
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long rightQuaternary;
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            do {
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long ce = right.getCE(rightIndex++);
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                rightQuaternary = ce & 0xffff;
325f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                if (rightQuaternary <= Collation.NO_CE_WEIGHT16) {
326f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    // Variable primary or completely ignorable or NO_CE.
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    rightQuaternary = ce >>> 32;
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Regular CE, not tertiary ignorable.
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Preserve the quaternary weight in bits 7..6.
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    rightQuaternary |= 0xffffff3fL;
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } while (rightQuaternary == 0);
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (leftQuaternary != rightQuaternary) {
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Return the difference, with script reordering.
337f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                if (settings.hasReordering()) {
338f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    leftQuaternary = settings.reorder(leftQuaternary);
339f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                    rightQuaternary = settings.reorder(rightQuaternary);
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return (leftQuaternary < rightQuaternary) ? Collation.LESS : Collation.GREATER;
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
343f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            if (leftQuaternary == Collation.NO_CE_PRIMARY) {
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return Collation.EQUAL;
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
350