12d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// © 2016 and later: Unicode, Inc. and others.
22d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License
37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
503f16b04d95bbaa98f702b69791b0de29ac75915Yoshito Umaoka * Copyright (C) 2007-2016, International Business Machines Corporation and
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * others. All Rights Reserved.
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.text;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.IOException;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.NotSerializableException;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.ObjectInputStream;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.ObjectOutputStream;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.ObjectStreamException;
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.Serializable;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.ParseException;
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.ArrayList;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Collection;
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Collections;
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashSet;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Iterator;
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.LinkedHashSet;
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.List;
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Locale;
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Set;
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.TreeSet;
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.regex.Pattern;
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.PluralRulesLoader;
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Output;
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale;
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Defines rules for mapping non-negative numeric values onto a small set of keywords.
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Rules are constructed from a text description, consisting of a series of keywords and conditions. The {@link #select}
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * method examines each condition in order and returns the keyword for the first condition that matches the number. If
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * none match, {@link #KEYWORD_OTHER} is returned.
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * A PluralRules object is immutable. It contains caches for sample values, but those are synchronized.
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * PluralRules is Serializable so that it can be used in formatters, which are serializable.
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For more information, details, and tips for writing rules, see the <a
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * href="http://www.unicode.org/draft/reports/tr35/tr35.html#Language_Plural_Rules">LDML spec, C.11 Language Plural
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Rules</a>
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Examples:
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
572d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre>
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * &quot;one: n is 1; few: n in 2..4&quot;
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre>
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This defines two rules, for 'one' and 'few'. The condition for 'one' is "n is 1" which means that the number must be
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * equal to 1 for this condition to pass. The condition for 'few' is "n in 2..4" which means that the number must be
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * between 2 and 4 inclusive - and be an integer - for this condition to pass. All other numbers are assigned the
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * keyword "other" by the default rule.
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
672d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre>
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * &quot;zero: n is 0; one: n is 1; zero: n mod 100 in 1..19&quot;
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre>
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This illustrates that the same keyword can be defined multiple times. Each rule is examined in order, and the first
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * keyword whose condition passes is the one returned. Also notes that a modulus is applied to n in the last rule. Thus
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * its condition holds for 119, 219, 319...
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
762d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre>
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * &quot;one: n is 1; few: n mod 10 in 2..4 and n mod 100 not in 12..14&quot;
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre>
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This illustrates conjunction and negation. The condition for 'few' has two parts, both of which must be met:
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "n mod 10 in 2..4" and "n mod 100 not in 12..14". The first part applies a modulus to n before the test as in the
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * previous example. The second part applies a different modulus and also uses negation, thus it matches all numbers
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * _not_ in 12, 13, 14, 112, 113, 114, 212, 213, 214...
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Syntax:
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre>
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * rules         = rule (';' rule)*
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * rule          = keyword ':' condition
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * keyword       = &lt;identifier&gt;
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * condition     = and_condition ('or' and_condition)*
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and_condition = relation ('and' relation)*
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * relation      = not? expr not? rel not? range_list
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * expr          = ('n' | 'i' | 'f' | 'v' | 't') (mod value)?
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * not           = 'not' | '!'
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * rel           = 'in' | 'is' | '=' | '≠' | 'within'
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * mod           = 'mod' | '%'
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * range_list    = (range | value) (',' range_list)*
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * value         = digit+
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * digit         = 0|1|2|3|4|5|6|7|8|9
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * range         = value'..'value
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre>
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>Each <b>not</b> term inverts the meaning; however, there should not be more than one of them.</p>
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The i, f, t, and v values are defined as follows:
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <ul>
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>i to be the integer digits.</li>
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>f to be the visible decimal digits, as an integer.</li>
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>t to be the visible decimal digits—without trailing zeros—as an integer.</li>
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>v to be the number of visible fraction digits.</li>
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>j is defined to only match integers. That is j is 3 fails if v != 0 (eg for 3.1 or 3.0).</li>
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </ul>
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Examples are in the following table:
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <table border='1' style="border-collapse:collapse">
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tbody>
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr>
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <th>n</th>
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <th>i</th>
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <th>f</th>
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <th>v</th>
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </tr>
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr>
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>1.0</td>
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>1</td>
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td align="right">0</td>
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>1</td>
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </tr>
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr>
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>1.00</td>
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>1</td>
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td align="right">0</td>
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>2</td>
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </tr>
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr>
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>1.3</td>
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>1</td>
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td align="right">3</td>
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>1</td>
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </tr>
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr>
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>1.03</td>
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>1</td>
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td align="right">3</td>
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>2</td>
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </tr>
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr>
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>1.23</td>
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>1</td>
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td align="right">23</td>
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>2</td>
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </tr>
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </tbody>
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </table>
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * An "identifier" is a sequence of characters that do not have the Unicode Pattern_Syntax or Pattern_White_Space
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * properties.
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The difference between 'in' and 'within' is that 'in' only includes integers in the specified range, while 'within'
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * includes all values. Using 'within' with a range_list consisting entirely of values is the same as using 'in' (it's
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * not an error).
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
1672d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class PluralRules implements Serializable {
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final UnicodeSet ALLOWED_ID = new UnicodeSet("[a-z]").freeze();
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // TODO Remove RulesList by moving its API and fields into PluralRules.
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final String CATEGORY_SEPARATOR = ";  ";
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final String KEYWORD_RULE_SEPARATOR = ": ";
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final long serialVersionUID = 1;
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private final RuleList rules;
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private final transient Set<String> keywords;
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Provides a factory for returning plural rules
1952d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static abstract class Factory {
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Sole constructor
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected Factory() {
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Provides access to the predefined <code>PluralRules</code> for a given locale and the plural type.
2122d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert         *
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p>
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * ICU defines plural rules for many locales based on CLDR <i>Language Plural Rules</i>. For these predefined
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * rules, see CLDR page at http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
2162d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert         *
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param locale
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *            The locale for which a <code>PluralRules</code> object is returned.
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param type
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *            The plural type (e.g., cardinal or ordinal).
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return The predefined <code>PluralRules</code> object for this locale. If there's no predefined rules for
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *         this locale, the rules for the closest parent in the locale hierarchy that has one will be returned.
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *         The final fallback always returns the default rules.
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public abstract PluralRules forLocale(ULocale locale, PluralType type);
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Utility for getting CARDINAL rules.
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param locale the locale
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return plural rules.
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final PluralRules forLocale(ULocale locale) {
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return forLocale(locale, PluralType.CARDINAL);
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Returns the locales for which there is plurals data.
2442d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert         *
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public abstract ULocale[] getAvailableULocales();
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Returns the 'functionally equivalent' locale with respect to plural rules. Calling PluralRules.forLocale with
253bee65486a185907111f3be60992433e133ec0e32Scott Russell         * the functionally equivalent locale, and with the provided locale, returns rules that behave the same. <br>
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * All locales with the same functionally equivalent locale have plural rules that behave the same. This is not
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * exaustive; there may be other locales whose plural rules behave the same that do not have the same equivalent
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * locale.
2572d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert         *
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param locale
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *            the locale to check
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param isAvailable
261bee65486a185907111f3be60992433e133ec0e32Scott Russell         *            if not null and of length &gt; 0, this will hold 'true' at index 0 if locale is directly defined
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *            (without fallback) as having plural rules
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return the functionally-equivalent locale
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public abstract ULocale getFunctionalEquivalent(ULocale locale, boolean[] isAvailable);
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Returns the default factory.
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static PluralRulesLoader getDefaultFactory() {
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return PluralRulesLoader.loader;
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Returns whether or not there are overrides.
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public abstract boolean hasOverride(ULocale locale);
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Standard keywords.
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Common name for the 'zero' plural form.
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final String KEYWORD_ZERO = "zero";
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Common name for the 'singular' plural form.
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final String KEYWORD_ONE = "one";
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Common name for the 'dual' plural form.
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final String KEYWORD_TWO = "two";
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Common name for the 'paucal' or other special plural form.
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final String KEYWORD_FEW = "few";
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Common name for the arabic (11 to 99) plural form.
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final String KEYWORD_MANY = "many";
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Common name for the default plural form.  This name is returned
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * for values to which no other form in the rule applies.  It
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * can additionally be assigned rules of its own.
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final String KEYWORD_OTHER = "other";
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Value returned by {@link #getUniqueKeywordValue} when there is no
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * unique value to return.
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.8
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final double NO_UNIQUE_VALUE = -0.00123456777;
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Type of plurals and PluralRules.
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public enum PluralType {
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Plural rules for cardinal numbers: 1 file vs. 2 files.
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 50
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        CARDINAL,
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Plural rules for ordinal numbers: 1st file, 2nd file, 3rd file, 4th file, etc.
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 50
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ORDINAL
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    };
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The default constraint that is always satisfied.
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final Constraint NO_CONSTRAINT = new Constraint() {
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final long serialVersionUID = 9163464945387899416L;
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3582d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isFulfilled(FixedDecimal n) {
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return true;
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3632d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isLimited(SampleType sampleType) {
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3682d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return "";
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    };
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3752d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final Rule DEFAULT_RULE = new Rule("other", NO_CONSTRAINT, null, null);
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Parses a plural rules description and returns a PluralRules.
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param description the rule description.
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws ParseException if the description cannot be parsed.
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    The exception index is typically not set, it will be -1.
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static PluralRules parseDescription(String description)
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throws ParseException {
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        description = description.trim();
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return description.length() == 0 ? DEFAULT : new PluralRules(parseRuleChain(description));
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a PluralRules from a description if it is parsable,
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * otherwise returns null.
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param description the rule description.
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the PluralRules
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static PluralRules createRules(String description) {
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return parseDescription(description);
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch(Exception e) {
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The default rules that accept any number and return
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@link #KEYWORD_OTHER}.
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final PluralRules DEFAULT = new PluralRules(new RuleList().addRule(DEFAULT_RULE));
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private enum Operand {
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        n,
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        i,
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        f,
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        t,
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        v,
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        w,
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* deprecated */
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        j;
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static class FixedDecimal extends Number implements Comparable<FixedDecimal> {
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final long serialVersionUID = -4756200506571685661L;
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final double source;
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final int visibleDecimalDigitCount;
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final int visibleDecimalDigitCountWithoutTrailingZeros;
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final long decimalDigits;
4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final long decimalDigitsWithoutTrailingZeros;
4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final long integerValue;
4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final boolean hasIntegerValue;
4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final boolean isNegative;
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final int baseFactor;
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public double getSource() {
4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return source;
4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int getVisibleDecimalDigitCount() {
4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return visibleDecimalDigitCount;
4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int getVisibleDecimalDigitCountWithoutTrailingZeros() {
5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return visibleDecimalDigitCountWithoutTrailingZeros;
5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public long getDecimalDigits() {
5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return decimalDigits;
5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public long getDecimalDigitsWithoutTrailingZeros() {
5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return decimalDigitsWithoutTrailingZeros;
5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public long getIntegerValue() {
5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return integerValue;
5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isHasIntegerValue() {
5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return hasIntegerValue;
5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isNegative() {
5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return isNegative;
5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int getBaseFactor() {
5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return baseFactor;
5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        static final long MAX = (long)1E18;
5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param n is the original number
5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param v number of digits to the right of the decimal place. e.g 1.00 = 2 25. = 0
5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param f Corresponds to f in the plural rules grammar.
5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *   The digits to the right of the decimal place as an integer. e.g 1.10 = 10
5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public FixedDecimal(double n, int v, long f) {
5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            isNegative = n < 0;
5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            source = isNegative ? -n : n;
5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            visibleDecimalDigitCount = v;
5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            decimalDigits = f;
5802d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            integerValue = n > MAX
5812d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    ? MAX
5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            : (long)n;
5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            hasIntegerValue = source == integerValue;
5847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // check values. TODO make into unit test.
5852d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            //
5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //            long visiblePower = (int) Math.pow(10, v);
5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //            if (fractionalDigits > visiblePower) {
5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //                throw new IllegalArgumentException();
5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //            }
5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //            double fraction = intValue + (fractionalDigits / (double) visiblePower);
5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //            if (fraction != source) {
5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //                double diff = Math.abs(fraction - source)/(Math.abs(fraction) + Math.abs(source));
5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //                if (diff > 0.00000001d) {
5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //                    throw new IllegalArgumentException();
5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //                }
5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //            }
5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (f == 0) {
5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                decimalDigitsWithoutTrailingZeros = 0;
5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                visibleDecimalDigitCountWithoutTrailingZeros = 0;
6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long fdwtz = f;
6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int trimmedCount = v;
6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                while ((fdwtz%10) == 0) {
6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    fdwtz /= 10;
6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    --trimmedCount;
6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                decimalDigitsWithoutTrailingZeros = fdwtz;
6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                visibleDecimalDigitCountWithoutTrailingZeros = trimmedCount;
6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            baseFactor = (int) Math.pow(10, v);
6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
6177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
6187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public FixedDecimal(double n, int v) {
6197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this(n,v,getFractionalDigits(n, v));
6207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static int getFractionalDigits(double n, int v) {
6237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (v == 0) {
6247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return 0;
6257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
6267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (n < 0) {
6277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    n = -n;
6287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int baseFactor = (int) Math.pow(10, v);
6307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long scaled = Math.round(n * baseFactor);
6317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return (int) (scaled % baseFactor);
6327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
6367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
6377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
6387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
6397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
6407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public FixedDecimal(double n) {
6417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this(n, decimals(n));
6427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
6457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
6467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
6477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
6487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
6497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public FixedDecimal(long n) {
6507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this(n,0);
6517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final long MAX_INTEGER_PART = 1000000000;
6547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
6557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Return a guess as to the number of decimals that would be displayed. This is only a guess; callers should
6567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * always supply the decimals explicitly if possible. Currently, it is up to 6 decimals (without trailing zeros).
6577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Returns 0 for infinities and nans.
6587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
6597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
6602d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert         *
6617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
6627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
6637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static int decimals(double n) {
6647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Ugly...
6657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (Double.isInfinite(n) || Double.isNaN(n)) {
6667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return 0;
6677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (n < 0) {
6697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                n = -n;
6707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (n == Math.floor(n)) {
6727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return 0;
6737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (n < MAX_INTEGER_PART) {
6757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long temp = (long)(n * 1000000) % 1000000; // get 6 decimals
6767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int mask = 10, digits = 6; digits > 0; mask *= 10, --digits) {
6777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if ((temp % mask) != 0) {
6787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        return digits;
6797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
6807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return 0;
6827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
6837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String buf = String.format(Locale.ENGLISH, "%1.15e", n);
6847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int ePos = buf.lastIndexOf('e');
6857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int expNumPos = ePos + 1;
6867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (buf.charAt(expNumPos) == '+') {
6877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    expNumPos++;
6887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String exponentStr = buf.substring(expNumPos);
6907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int exponent = Integer.parseInt(exponentStr);
6917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int numFractionDigits = ePos - 2 - exponent;
6927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (numFractionDigits < 0) {
6937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return 0;
6947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i=ePos-1; numFractionDigits > 0; --i) {
6967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (buf.charAt(i) != '0') {
6977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        break;
6987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
6992d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    --numFractionDigits;
7007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
7017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return numFractionDigits;
7027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
7067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
7077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
7087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
7097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
7107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public FixedDecimal (String n) {
7117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Ugly, but for samples we don't care.
7127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this(Double.parseDouble(n), getVisibleFractionCount(n));
7137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static int getVisibleFractionCount(String value) {
7167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            value = value.trim();
7177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int decimalPos = value.indexOf('.') + 1;
7187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (decimalPos == 0) {
7197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return 0;
7207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
7217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return value.length() - decimalPos;
7227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
7267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
7277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
7287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
7297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
7307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public double get(Operand operand) {
7317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            switch(operand) {
7327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            default: return source;
7337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case i: return integerValue;
7347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case f: return decimalDigits;
7357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case t: return decimalDigitsWithoutTrailingZeros;
7367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case v: return visibleDecimalDigitCount;
7377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case w: return visibleDecimalDigitCountWithoutTrailingZeros;
7387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
7427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
7437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
7447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
7457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
7467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static Operand getOperand(String t) {
7477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return Operand.valueOf(t);
7487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
7517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * We're not going to care about NaN.
7527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
7537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
7547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
7552d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
7567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
7577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int compareTo(FixedDecimal other) {
7587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (integerValue != other.integerValue) {
7597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return integerValue < other.integerValue ? -1 : 1;
7607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (source != other.source) {
7627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return source < other.source ? -1 : 1;
7637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (visibleDecimalDigitCount != other.visibleDecimalDigitCount) {
7657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return visibleDecimalDigitCount < other.visibleDecimalDigitCount ? -1 : 1;
7667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long diff = decimalDigits - other.decimalDigits;
7687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (diff != 0) {
7697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return diff < 0 ? -1 : 1;
7707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return 0;
7727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
7757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
7767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
7777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
7787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
7797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Override
7807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean equals(Object arg0) {
7817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (arg0 == null) {
7827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return false;
7837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (arg0 == this) {
7857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return true;
7867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!(arg0 instanceof FixedDecimal)) {
7887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return false;
7897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            FixedDecimal other = (FixedDecimal)arg0;
7917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return source == other.source && visibleDecimalDigitCount == other.visibleDecimalDigitCount && decimalDigits == other.decimalDigits;
7927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
7957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
7967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
7977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
7987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
7997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Override
8007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int hashCode() {
8017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // TODO Auto-generated method stub
8027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return (int)(decimalDigits + 37 * (visibleDecimalDigitCount + (int)(37 * source)));
8037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
8067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
8077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
8087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
8097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
8107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Override
8117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
8127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return String.format("%." + visibleDecimalDigitCount + "f", source);
8137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
8167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
8177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
8187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
8197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
8207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean hasIntegerValue() {
8217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return hasIntegerValue;
8227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
8257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
8267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
8277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
8287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
8297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Override
8307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int intValue() {
8317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // TODO Auto-generated method stub
8327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return (int)integerValue;
8337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
8367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
8377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
8387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
8397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
8407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Override
8417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public long longValue() {
8427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return integerValue;
8437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
8467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
8477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
8487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
8497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
8507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Override
8517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public float floatValue() {
8527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return (float) source;
8537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
8567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
8577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
8587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
8597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
8607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Override
8617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public double doubleValue() {
8627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return isNegative ? -source : source;
8637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
8667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
8677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
8687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
8697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
8707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public long getShiftedValue() {
8717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return integerValue * baseFactor + decimalDigits;
8727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private void writeObject(
8757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ObjectOutputStream out)
8767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        throws IOException {
8777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new NotSerializableException();
8787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private void readObject(ObjectInputStream in
8817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ) throws IOException, ClassNotFoundException {
8827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new NotSerializableException();
8837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Selection parameter for either integer-only or decimal-only.
8887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
8897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
8907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
8927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public enum SampleType {
8937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
8947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
8957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
8967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
8977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
8987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        INTEGER,
8997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
9007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
9017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
9027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
9037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
9047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DECIMAL
9057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * A range of NumberInfo that includes all values with the same visibleFractionDigitCount.
9097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
9107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
9117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
9137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static class FixedDecimalRange {
9147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
9157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
9167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
9177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
9187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
9197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final FixedDecimal start;
9207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
9217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
9227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
9237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
9247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
9257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final FixedDecimal end;
9267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
9277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
9287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
9297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
9307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
9317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public FixedDecimalRange(FixedDecimal start, FixedDecimal end) {
9327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (start.visibleDecimalDigitCount != end.visibleDecimalDigitCount) {
9337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllegalArgumentException("Ranges must have the same number of visible decimals: " + start + "~" + end);
9347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
9357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.start = start;
9367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.end = end;
9377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
9397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
9407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
9417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
9427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
9437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Override
9447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
9457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return start + (end == start ? "" : "~" + end);
9467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * A list of NumberInfo that includes all values with the same visibleFractionDigitCount.
9517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
9527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
9537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
9557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static class FixedDecimalSamples {
9567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
9577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
9587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
9597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
9607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
9617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final SampleType sampleType;
9627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
9637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
9647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
9657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
9667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
9677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final Set<FixedDecimalRange> samples;
9687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
9697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
9707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
9717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
9727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
9737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public final boolean bounded;
9747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
9757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * The samples must be immutable.
9767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param sampleType
9777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param samples
9787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
9797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private FixedDecimalSamples(SampleType sampleType, Set<FixedDecimalRange> samples, boolean bounded) {
9807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super();
9817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.sampleType = sampleType;
9827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.samples = samples;
9837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.bounded = bounded;
9847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /*
9867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Parse a list of the form described in CLDR. The source must be trimmed.
9877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
9887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        static FixedDecimalSamples parse(String source) {
9897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            SampleType sampleType2;
9907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean bounded2 = true;
9917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean haveBound = false;
9927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Set<FixedDecimalRange> samples2 = new LinkedHashSet<FixedDecimalRange>();
9937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (source.startsWith("integer")) {
9957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                sampleType2 = SampleType.INTEGER;
9967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (source.startsWith("decimal")) {
9977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                sampleType2 = SampleType.DECIMAL;
9987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
9997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllegalArgumentException("Samples must start with 'integer' or 'decimal'");
10007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            source = source.substring(7).trim(); // remove both
10027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (String range : COMMA_SEPARATED.split(source)) {
10047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (range.equals("…") || range.equals("...")) {
10057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    bounded2 = false;
10067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    haveBound = true;
10077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    continue;
10087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
10097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (haveBound) {
10107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    throw new IllegalArgumentException("Can only have … at the end of samples: " + range);
10117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
10127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String[] rangeParts = TILDE_SEPARATED.split(range);
10137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                switch (rangeParts.length) {
10142d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                case 1:
10157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    FixedDecimal sample = new FixedDecimal(rangeParts[0]);
10167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    checkDecimal(sampleType2, sample);
10177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    samples2.add(new FixedDecimalRange(sample, sample));
10187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
10197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                case 2:
10207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    FixedDecimal start = new FixedDecimal(rangeParts[0]);
10217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    FixedDecimal end = new FixedDecimal(rangeParts[1]);
10227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    checkDecimal(sampleType2, start);
10237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    checkDecimal(sampleType2, end);
10247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    samples2.add(new FixedDecimalRange(start, end));
10257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
10267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                default: throw new IllegalArgumentException("Ill-formed number range: " + range);
10277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
10287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return new FixedDecimalSamples(sampleType2, Collections.unmodifiableSet(samples2), bounded2);
10307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static void checkDecimal(SampleType sampleType2, FixedDecimal sample) {
10337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ((sampleType2 == SampleType.INTEGER) != (sample.getVisibleDecimalDigitCount() == 0)) {
10342d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                throw new IllegalArgumentException("Ill-formed number range: " + sample);
10357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
10397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
10407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
10417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
10427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
10437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Set<Double> addSamples(Set<Double> result) {
10447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (FixedDecimalRange item : samples) {
10457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // we have to convert to longs so we don't get strange double issues
10467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long startDouble = item.start.getShiftedValue();
10477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long endDouble = item.end.getShiftedValue();
10487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (long d = startDouble; d <= endDouble; d += 1) {
10507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result.add(d/(double)item.start.baseFactor);
10517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
10527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result;
10547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
10577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
10587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
10597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
10607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
10617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Override
10627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
10637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuilder b = new StringBuilder("@").append(sampleType.toString().toLowerCase(Locale.ENGLISH));
10647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean first = true;
10657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (FixedDecimalRange item : samples) {
10667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (first) {
10677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    first = false;
10687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
10697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    b.append(",");
10707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
10717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                b.append(' ').append(item);
10727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!bounded) {
10747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                b.append(", …");
10757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return b.toString();
10777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
10807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
10817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
10827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
10837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
10847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Set<FixedDecimalRange> getSamples() {
10857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return samples;
10867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
10897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
10907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
10917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
10927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
10937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void getStartEndSamples(Set<FixedDecimal> target) {
10947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (FixedDecimalRange item : samples) {
10957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                target.add(item.start);
10967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                target.add(item.end);
10977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
11007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
11027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * A constraint on a number.
11037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
11047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private interface Constraint extends Serializable {
11057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /*
11067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Returns true if the number fulfills the constraint.
11077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param n the number to test, >= 0.
11087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
11097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean isFulfilled(FixedDecimal n);
11107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /*
11127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Returns false if an unlimited number of values fulfills the
11137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * constraint.
11147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
11157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean isLimited(SampleType sampleType);
11167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
11177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static class SimpleTokenizer {
11197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        static final UnicodeSet BREAK_AND_IGNORE = new UnicodeSet(0x09, 0x0a, 0x0c, 0x0d, 0x20, 0x20).freeze();
11207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        static final UnicodeSet BREAK_AND_KEEP = new UnicodeSet('!', '!', '%', '%', ',', ',', '.', '.', '=', '=').freeze();
11217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        static String[] split(String source) {
11227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int last = -1;
11237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            List<String> result = new ArrayList<String>();
11247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < source.length(); ++i) {
11257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                char ch = source.charAt(i);
11267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (BREAK_AND_IGNORE.contains(ch)) {
11277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (last >= 0) {
11287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        result.add(source.substring(last,i));
11297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        last = -1;
11307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
11317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (BREAK_AND_KEEP.contains(ch)) {
11327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (last >= 0) {
11337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        result.add(source.substring(last,i));
11347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
11357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result.add(source.substring(i,i+1));
11367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    last = -1;
11377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (last < 0) {
11387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    last = i;
11397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
11407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (last >= 0) {
11427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.add(source.substring(last));
11437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result.toArray(new String[result.size()]);
11457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
11477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
11497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * syntax:
11507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * condition :       or_condition
11517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                   and_condition
11527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or_condition :    and_condition 'or' condition
11537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and_condition :   relation
11547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                   relation 'and' relation
11557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * relation :        in_relation
11567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                   within_relation
11577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * in_relation :     not? expr not? in not? range
11587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * within_relation : not? expr not? 'within' not? range
11597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * not :             'not'
11607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                   '!'
11617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * expr :            'n'
11627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                   'n' mod value
11637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * mod :             'mod'
11647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                   '%'
11657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * in :              'in'
11667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                   'is'
11677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                   '='
11687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                   '≠'
11697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * value :           digit+
11707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * digit :           0|1|2|3|4|5|6|7|8|9
11717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * range :           value'..'value
11727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
11737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static Constraint parseConstraint(String description)
11747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throws ParseException {
11757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Constraint result = null;
11777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String[] or_together = OR_SEPARATED.split(description);
11787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < or_together.length; ++i) {
11797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Constraint andConstraint = null;
11807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String[] and_together = AND_SEPARATED.split(or_together[i]);
11817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int j = 0; j < and_together.length; ++j) {
11827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Constraint newConstraint = NO_CONSTRAINT;
11837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String condition = and_together[j].trim();
11857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String[] tokens = SimpleTokenizer.split(condition);
11867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int mod = 0;
11887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                boolean inRange = true;
11897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                boolean integersOnly = true;
11907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                double lowBound = Long.MAX_VALUE;
11917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                double highBound = Long.MIN_VALUE;
11927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long[] vals = null;
11937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int x = 0;
11957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String t = tokens[x++];
11967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                boolean hackForCompatibility = false;
11977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Operand operand;
11987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                try {
11997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    operand = FixedDecimal.getOperand(t);
12007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (Exception e) {
12017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    throw unexpected(t, condition);
12027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
12037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (x < tokens.length) {
12047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    t = tokens[x++];
12057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if ("mod".equals(t) || "%".equals(t)) {
12067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        mod = Integer.parseInt(tokens[x++]);
12077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        t = nextToken(tokens, x++, condition);
12087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
12097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if ("not".equals(t)) {
12107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        inRange = !inRange;
12117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        t = nextToken(tokens, x++, condition);
12127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if ("=".equals(t)) {
12137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            throw unexpected(t, condition);
12147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
12157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else if ("!".equals(t)) {
12167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        inRange = !inRange;
12177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        t = nextToken(tokens, x++, condition);
12187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (!"=".equals(t)) {
12197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            throw unexpected(t, condition);
12207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
12217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
12227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if ("is".equals(t) || "in".equals(t) || "=".equals(t)) {
12237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        hackForCompatibility = "is".equals(t);
12247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (hackForCompatibility && !inRange) {
12257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            throw unexpected(t, condition);
12267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
12277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        t = nextToken(tokens, x++, condition);
12287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else if ("within".equals(t)) {
12297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        integersOnly = false;
12307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        t = nextToken(tokens, x++, condition);
12317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
12327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        throw unexpected(t, condition);
12337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
12347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if ("not".equals(t)) {
12357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (!hackForCompatibility && !inRange) {
12367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            throw unexpected(t, condition);
12377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
12387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        inRange = !inRange;
12397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        t = nextToken(tokens, x++, condition);
12407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
12417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    List<Long> valueList = new ArrayList<Long>();
12437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // the token t is always one item ahead
12457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    while (true) {
12467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        long low = Long.parseLong(t);
12477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        long high = low;
12487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (x < tokens.length) {
12497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            t = nextToken(tokens, x++, condition);
12507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (t.equals(".")) {
12517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                t = nextToken(tokens, x++, condition);
12527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if (!t.equals(".")) {
12537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    throw unexpected(t, condition);
12547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
12557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                t = nextToken(tokens, x++, condition);
12567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                high = Long.parseLong(t);
12577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                if (x < tokens.length) {
12587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    t = nextToken(tokens, x++, condition);
12597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    if (!t.equals(",")) { // adjacent number: 1 2
12607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        // no separator, fail
12617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                        throw unexpected(t, condition);
12622d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    }
12637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                }
12647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else if (!t.equals(",")) { // adjacent number: 1 2
12657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                // no separator, fail
12667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                throw unexpected(t, condition);
12677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
12687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
12697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // at this point, either we are out of tokens, or t is ','
12707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (low > high) {
12717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            throw unexpected(low + "~" + high, condition);
12727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else if (mod != 0 && high >= mod) {
12737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            throw unexpected(high + ">mod=" + mod, condition);
12747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
12757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        valueList.add(low);
12767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        valueList.add(high);
12777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        lowBound = Math.min(lowBound, low);
12787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        highBound = Math.max(highBound, high);
12797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (x >= tokens.length) {
12807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            break;
12817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
12827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        t = nextToken(tokens, x++, condition);
12837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
12847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (t.equals(",")) {
12867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        throw unexpected(t, condition);
12877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
12887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (valueList.size() == 2) {
12907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        vals = null;
12917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
12927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        vals = new long[valueList.size()];
12937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        for (int k = 0; k < vals.length; ++k) {
12947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            vals[k] = valueList.get(k);
12957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
12967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
12977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Hack to exclude "is not 1,2"
12997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (lowBound != highBound && hackForCompatibility && !inRange) {
13007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        throw unexpected("is not <range>", condition);
13017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
13027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    newConstraint =
13047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            new RangeConstraint(mod, inRange, operand, integersOnly, lowBound, highBound, vals);
13057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
13067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (andConstraint == null) {
13087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    andConstraint = newConstraint;
13097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
13107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    andConstraint = new AndConstraint(andConstraint,
13117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            newConstraint);
13127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
13137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
13147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (result == null) {
13167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result = andConstraint;
13177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
13187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result = new OrConstraint(result, andConstraint);
13197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
13207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
13227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
13237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final Pattern AT_SEPARATED = Pattern.compile("\\s*\\Q\\E@\\s*");
13257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final Pattern OR_SEPARATED = Pattern.compile("\\s*or\\s*");
13267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final Pattern AND_SEPARATED = Pattern.compile("\\s*and\\s*");
13277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final Pattern COMMA_SEPARATED = Pattern.compile("\\s*,\\s*");
13287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final Pattern DOTDOT_SEPARATED = Pattern.compile("\\s*\\Q..\\E\\s*");
13297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final Pattern TILDE_SEPARATED = Pattern.compile("\\s*~\\s*");
13307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final Pattern SEMI_SEPARATED = Pattern.compile("\\s*;\\s*");
13317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* Returns a parse exception wrapping the token and context strings. */
13347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static ParseException unexpected(String token, String context) {
13357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new ParseException("unexpected token '" + token +
13367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                "' in '" + context + "'", -1);
13377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
13387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
13407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the token at x if available, else throws a parse exception.
13417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
13427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String nextToken(String[] tokens, int x, String context)
13437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throws ParseException {
13447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (x < tokens.length) {
13457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return tokens[x];
13467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        throw new ParseException("missing token at end of '" + context + "'", -1);
13487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
13497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
13517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Syntax:
13527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * rule : keyword ':' condition
13537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * keyword: <identifier>
13547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
13557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static Rule parseRule(String description) throws ParseException {
13567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (description.length() == 0) {
13577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return DEFAULT_RULE;
13587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        description = description.toLowerCase(Locale.ENGLISH);
13617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int x = description.indexOf(':');
13637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (x == -1) {
13647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new ParseException("missing ':' in rule description '" +
13657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    description + "'", 0);
13667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String keyword = description.substring(0, x).trim();
13697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!isValidKeyword(keyword)) {
13707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new ParseException("keyword '" + keyword +
13717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    " is not valid", 0);
13727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        description = description.substring(x+1).trim();
13757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String[] constraintOrSamples = AT_SEPARATED.split(description);
13767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean sampleFailure = false;
13777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        FixedDecimalSamples integerSamples = null, decimalSamples = null;
13787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        switch (constraintOrSamples.length) {
13797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        case 1: break;
13802d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        case 2:
13817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            integerSamples = FixedDecimalSamples.parse(constraintOrSamples[1]);
13827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (integerSamples.sampleType == SampleType.DECIMAL) {
13837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                decimalSamples = integerSamples;
13847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                integerSamples = null;
13857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
13867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            break;
13877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        case 3:
13887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            integerSamples = FixedDecimalSamples.parse(constraintOrSamples[1]);
13897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            decimalSamples = FixedDecimalSamples.parse(constraintOrSamples[2]);
13907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (integerSamples.sampleType != SampleType.INTEGER || decimalSamples.sampleType != SampleType.DECIMAL) {
13917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllegalArgumentException("Must have @integer then @decimal in " + description);
13927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
13937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            break;
13942d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        default:
13957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("Too many samples in " + description);
13967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (sampleFailure) {
13987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("Ill-formed samples—'@' characters.");
13997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 'other' is special, and must have no rules; all other keywords must have rules.
14027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean isOther = keyword.equals("other");
14037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (isOther != (constraintOrSamples[0].length() == 0)) {
14047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("The keyword 'other' must have no constraints, just samples.");
14057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Constraint constraint;
14087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (isOther) {
14097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            constraint = NO_CONSTRAINT;
14107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
14117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            constraint = parseConstraint(constraintOrSamples[0]);
14127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new Rule(keyword, constraint, integerSamples, decimalSamples);
14147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
14157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
14187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Syntax:
14197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * rules : rule
14207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         rule ';' rules
14217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
14227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static RuleList parseRuleChain(String description)
14237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throws ParseException {
14247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        RuleList result = new RuleList();
14257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // remove trailing ;
14262d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        if (description.endsWith(";")) {
14277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            description = description.substring(0,description.length()-1);
14287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String[] rules = SEMI_SEPARATED.split(description);
14307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < rules.length; ++i) {
14317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Rule rule = parseRule(rules[i].trim());
14327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.hasExplicitBoundingInfo |= rule.integerSamples != null || rule.decimalSamples != null;
14337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.addRule(rule);
14347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result.finish();
14367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
14377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
14397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * An implementation of Constraint representing a modulus,
14407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a range of values, and include/exclude. Provides lots of
14417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * convenience factory methods.
14427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
14437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static class RangeConstraint implements Constraint, Serializable {
14447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final long serialVersionUID = 1;
14457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final int mod;
14477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final boolean inRange;
14487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final boolean integersOnly;
14497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final double lowerBound;
14507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final double upperBound;
14517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final long[] range_list;
14527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final Operand operand;
14537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        RangeConstraint(int mod, boolean inRange, Operand operand, boolean integersOnly,
14557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                double lowBound, double highBound, long[] vals) {
14567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.mod = mod;
14577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.inRange = inRange;
14587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.integersOnly = integersOnly;
14597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.lowerBound = lowBound;
14607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.upperBound = highBound;
14617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.range_list = vals;
14627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.operand = operand;
14637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14652d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
14667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isFulfilled(FixedDecimal number) {
14677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            double n = number.get(operand);
14687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ((integersOnly && (n - (long)n) != 0.0
14697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    || operand == Operand.j && number.visibleDecimalDigitCount != 0)) {
14707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return !inRange;
14717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (mod != 0) {
14737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                n = n % mod;    // java % handles double numerator the way we want
14747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean test = n >= lowerBound && n <= upperBound;
14767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (test && range_list != null) {
14777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                test = false;
14787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = 0; !test && i < range_list.length; i += 2) {
14797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    test = n >= range_list[i] && n <= range_list[i+1];
14807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
14817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return inRange == test;
14837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14852d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
14867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isLimited(SampleType sampleType) {
14877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean valueIsZero = lowerBound == upperBound && lowerBound == 0d;
14882d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            boolean hasDecimals =
14897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    (operand == Operand.v || operand == Operand.w || operand == Operand.f || operand == Operand.t)
14907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    && inRange != valueIsZero; // either NOT f = zero or f = non-zero
14917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            switch (sampleType) {
14922d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            case INTEGER:
14937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return hasDecimals // will be empty
14947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        || (operand == Operand.n || operand == Operand.i || operand == Operand.j)
14952d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        && mod == 0
14967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        && inRange;
14977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            case DECIMAL:
14997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return  (!hasDecimals || operand == Operand.n || operand == Operand.j)
15007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        && (integersOnly || lowerBound == upperBound)
15012d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        && mod == 0
15027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        && inRange;
15037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
15057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15072d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
15087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
15097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuilder result = new StringBuilder();
15107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.append(operand);
15117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (mod != 0) {
15127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append(" % ").append(mod);
15137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean isList = lowerBound != upperBound;
15157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.append(
15167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    !isList ? (inRange ? " = " : " != ")
15177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            : integersOnly ? (inRange ? " = " : " != ")
15182d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                                    : (inRange ? " within " : " not within ")
15197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    );
15207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (range_list != null) {
15217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = 0; i < range_list.length; i += 2) {
15227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    addRange(result, range_list[i], range_list[i+1], i != 0);
15237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
15247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
15257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                addRange(result, lowerBound, upperBound, false);
15267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result.toString();
15287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static void addRange(StringBuilder result, double lb, double ub, boolean addSeparator) {
15327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (addSeparator) {
15337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.append(",");
15347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (lb == ub) {
15367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.append(format(lb));
15377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
15387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.append(format(lb) + ".." + format(ub));
15397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String format(double lb) {
15437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long lbi = (long) lb;
15447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return lb == lbi ? String.valueOf(lbi) : String.valueOf(lb);
15457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* Convenience base class for and/or constraints. */
15487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static abstract class BinaryConstraint implements Constraint,
15497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    Serializable {
15507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final long serialVersionUID = 1;
15517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected final Constraint a;
15527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected final Constraint b;
15537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected BinaryConstraint(Constraint a, Constraint b) {
15557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.a = a;
15567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.b = b;
15577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* A constraint representing the logical and of two constraints. */
15617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static class AndConstraint extends BinaryConstraint {
15627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final long serialVersionUID = 7766999779862263523L;
15637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        AndConstraint(Constraint a, Constraint b) {
15657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super(a, b);
15667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15682d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
15697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isFulfilled(FixedDecimal n) {
15702d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return a.isFulfilled(n)
15717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    && b.isFulfilled(n);
15727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15742d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
15757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isLimited(SampleType sampleType) {
15767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // we ignore the case where both a and b are unlimited but no values
15777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // satisfy both-- we still consider this 'unlimited'
15782d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return a.isLimited(sampleType)
15797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    || b.isLimited(sampleType);
15807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15822d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
15837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
15847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return a.toString() + " and " + b.toString();
15857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* A constraint representing the logical or of two constraints. */
15897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static class OrConstraint extends BinaryConstraint {
15907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final long serialVersionUID = 1405488568664762222L;
15917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        OrConstraint(Constraint a, Constraint b) {
15937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super(a, b);
15947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15962d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
15977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isFulfilled(FixedDecimal n) {
15982d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return a.isFulfilled(n)
15997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    || b.isFulfilled(n);
16007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16022d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
16037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isLimited(SampleType sampleType) {
16042d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return a.isLimited(sampleType)
16057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    && b.isLimited(sampleType);
16067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16082d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
16097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
16107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return a.toString() + " or " + b.toString();
16117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
16157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Implementation of Rule that uses a constraint.
16167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Provides 'and' and 'or' to combine constraints.  Immutable.
16177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
16187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static class Rule implements Serializable {
16192d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        // TODO - Findbugs: Class com.ibm.icu.text.PluralRules$Rule defines non-transient
16202d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        // non-serializable instance field integerSamples. See ticket#10494.
16217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final long serialVersionUID = 1;
16227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final String keyword;
16237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final Constraint constraint;
16247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final FixedDecimalSamples integerSamples;
16257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final FixedDecimalSamples decimalSamples;
16267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Rule(String keyword, Constraint constraint, FixedDecimalSamples integerSamples, FixedDecimalSamples decimalSamples) {
16287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.keyword = keyword;
16297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.constraint = constraint;
16307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.integerSamples = integerSamples;
16317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.decimalSamples = decimalSamples;
16327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @SuppressWarnings("unused")
16357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Rule and(Constraint c) {
16367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return new Rule(keyword, new AndConstraint(constraint, c), integerSamples, decimalSamples);
16377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @SuppressWarnings("unused")
16407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Rule or(Constraint c) {
16417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return new Rule(keyword, new OrConstraint(constraint, c), integerSamples, decimalSamples);
16427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String getKeyword() {
16457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return keyword;
16467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean appliesTo(FixedDecimal n) {
16497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return constraint.isFulfilled(n);
16507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isLimited(SampleType sampleType) {
16537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return constraint.isLimited(sampleType);
16547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16562d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
16577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
16582d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return keyword + ": " + constraint.toString()
16597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    + (integerSamples == null ? "" : " " + integerSamples.toString())
16607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    + (decimalSamples == null ? "" : " " + decimalSamples.toString());
16617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
16647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @internal
16657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @deprecated This API is ICU internal only.
16667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
16677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Deprecated
16687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        @Override
16697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int hashCode() {
16707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return keyword.hashCode() ^ constraint.hashCode();
16717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String getConstraint() {
16747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return constraint.toString();
16757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static class RuleList implements Serializable {
16797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private boolean hasExplicitBoundingInfo = false;
16807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final long serialVersionUID = 1;
16817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final List<Rule> rules = new ArrayList<Rule>();
16827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public RuleList addRule(Rule nextRule) {
16847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String keyword = nextRule.getKeyword();
16857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Rule rule : rules) {
16867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (keyword.equals(rule.getKeyword())) {
16877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    throw new IllegalArgumentException("Duplicate keyword: " + keyword);
16887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
16897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            rules.add(nextRule);
16917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
16927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public RuleList finish() throws ParseException {
16957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // make sure that 'other' is present, and at the end.
16967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Rule otherRule = null;
16977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Iterator<Rule> it = rules.iterator(); it.hasNext();) {
16987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Rule rule = it.next();
16997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ("other".equals(rule.getKeyword())) {
17007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    otherRule = rule;
17017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    it.remove();
17027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (otherRule == null) {
17057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                otherRule = parseRule("other:"); // make sure we have always have an 'other' a rule
17067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            rules.add(otherRule);
17087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
17097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private Rule selectRule(FixedDecimal n) {
17127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Rule rule : rules) {
17137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (rule.appliesTo(n)) {
17147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return rule;
17157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
17187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String select(FixedDecimal n) {
17217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (Double.isInfinite(n.source) || Double.isNaN(n.source)) {
17227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return KEYWORD_OTHER;
17237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Rule r = selectRule(n);
17257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return r.getKeyword();
17267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Set<String> getKeywords() {
17297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Set<String> result = new LinkedHashSet<String>();
17307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Rule rule : rules) {
17317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.add(rule.getKeyword());
17327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // since we have explict 'other', we don't need this.
17347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //result.add(KEYWORD_OTHER);
17357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result;
17367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isLimited(String keyword, SampleType sampleType) {
17397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (hasExplicitBoundingInfo) {
17407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                FixedDecimalSamples mySamples = getDecimalSamples(keyword, sampleType);
17417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return mySamples == null ? true : mySamples.bounded;
17427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return computeLimited(keyword, sampleType);
17457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean computeLimited(String keyword, SampleType sampleType) {
17487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // if all rules with this keyword are limited, it's limited,
17497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // and if there's no rule with this keyword, it's unlimited
17507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean result = false;
17517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Rule rule : rules) {
17527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (keyword.equals(rule.getKeyword())) {
17537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (!rule.isLimited(sampleType)) {
17547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        return false;
17557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
17567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result = true;
17577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result;
17607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17622d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        @Override
17637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
17647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuilder builder = new StringBuilder();
17657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Rule rule : rules) {
17667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (builder.length() != 0) {
17677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    builder.append(CATEGORY_SEPARATOR);
17687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                builder.append(rule);
17707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return builder.toString();
17727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String getRules(String keyword) {
17757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Rule rule : rules) {
17767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (rule.getKeyword().equals(keyword)) {
17777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return rule.getConstraint();
17787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
17817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean select(FixedDecimal sample, String keyword) {
17847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Rule rule : rules) {
17857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (rule.getKeyword().equals(keyword) && rule.appliesTo(sample)) {
17867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return true;
17877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
17907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public FixedDecimalSamples getDecimalSamples(String keyword, SampleType sampleType) {
17937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Rule rule : rules) {
17947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (rule.getKeyword().equals(keyword)) {
17957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return sampleType == SampleType.INTEGER ? rule.integerSamples : rule.decimalSamples;
17967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
17997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
18007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @SuppressWarnings("unused")
18037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private boolean addConditional(Set<FixedDecimal> toAddTo, Set<FixedDecimal> others, double trial) {
18047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean added;
18057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        FixedDecimal toAdd = new FixedDecimal(trial);
18067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!toAddTo.contains(toAdd) && !others.contains(toAdd)) {
18077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            others.add(toAdd);
18087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            added = true;
18097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
18107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            added = false;
18117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
18127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return added;
18137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // -------------------------------------------------------------------------
18187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Static class methods.
18197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // -------------------------------------------------------------------------
18207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Provides access to the predefined cardinal-number <code>PluralRules</code> for a given
18237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * locale.
18247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Same as forLocale(locale, PluralType.CARDINAL).
18257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
18267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>ICU defines plural rules for many locales based on CLDR <i>Language Plural Rules</i>.
18277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For these predefined rules, see CLDR page at
18287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
18297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
18307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale The locale for which a <code>PluralRules</code> object is
18317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   returned.
18327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The predefined <code>PluralRules</code> object for this locale.
18337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   If there's no predefined rules for this locale, the rules
18347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   for the closest parent in the locale hierarchy that has one will
18357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   be returned.  The final fallback always returns the default
18367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   rules.
18377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
18387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static PluralRules forLocale(ULocale locale) {
18407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return Factory.getDefaultFactory().forLocale(locale, PluralType.CARDINAL);
18417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Provides access to the predefined cardinal-number <code>PluralRules</code> for a given
184503f16b04d95bbaa98f702b69791b0de29ac75915Yoshito Umaoka     * {@link java.util.Locale}.
18467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Same as forLocale(locale, PluralType.CARDINAL).
18477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
18487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>ICU defines plural rules for many locales based on CLDR <i>Language Plural Rules</i>.
18497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For these predefined rules, see CLDR page at
18507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
18517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
185203f16b04d95bbaa98f702b69791b0de29ac75915Yoshito Umaoka     * @param locale The locale for which a <code>PluralRules</code> object is
18537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   returned.
18547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The predefined <code>PluralRules</code> object for this locale.
18557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   If there's no predefined rules for this locale, the rules
18567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   for the closest parent in the locale hierarchy that has one will
18577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   be returned.  The final fallback always returns the default
18587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   rules.
1859bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * @stable ICU 54
18607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static PluralRules forLocale(Locale locale) {
18627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return forLocale(ULocale.forLocale(locale));
18637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Provides access to the predefined <code>PluralRules</code> for a given
18677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * locale and the plural type.
18687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
18697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>ICU defines plural rules for many locales based on CLDR <i>Language Plural Rules</i>.
18707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For these predefined rules, see CLDR page at
18717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
18727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
18737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale The locale for which a <code>PluralRules</code> object is
18747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   returned.
18757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param type The plural type (e.g., cardinal or ordinal).
18767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The predefined <code>PluralRules</code> object for this locale.
18777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   If there's no predefined rules for this locale, the rules
18787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   for the closest parent in the locale hierarchy that has one will
18797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   be returned.  The final fallback always returns the default
18807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   rules.
18817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
18827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static PluralRules forLocale(ULocale locale, PluralType type) {
18847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return Factory.getDefaultFactory().forLocale(locale, type);
18857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
18887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Provides access to the predefined <code>PluralRules</code> for a given
188903f16b04d95bbaa98f702b69791b0de29ac75915Yoshito Umaoka     * {@link java.util.Locale} and the plural type.
18907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
18917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>ICU defines plural rules for many locales based on CLDR <i>Language Plural Rules</i>.
18927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For these predefined rules, see CLDR page at
18937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
18947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
189503f16b04d95bbaa98f702b69791b0de29ac75915Yoshito Umaoka     * @param locale The locale for which a <code>PluralRules</code> object is
18967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   returned.
18977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param type The plural type (e.g., cardinal or ordinal).
18987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The predefined <code>PluralRules</code> object for this locale.
18997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   If there's no predefined rules for this locale, the rules
19007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   for the closest parent in the locale hierarchy that has one will
19017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   be returned.  The final fallback always returns the default
19027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   rules.
1903bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * @stable ICU 54
19047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static PluralRules forLocale(Locale locale, PluralType type) {
19067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return forLocale(ULocale.forLocale(locale), type);
19077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
19107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Checks whether a token is a valid keyword.
19117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
19127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param token the token to be checked
19137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return true if the token is a valid keyword.
19147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static boolean isValidKeyword(String token) {
19167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return ALLOWED_ID.containsAll(token);
19177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
19207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new <code>PluralRules</code> object.  Immutable.
19217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private PluralRules(RuleList rules) {
19237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.rules = rules;
19247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.keywords = Collections.unmodifiableSet(rules.getKeywords());
19257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
19287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
19297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
19307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
19327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
19337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int hashCode() {
19347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rules.hashCode();
19357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
19377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Given a number, returns the keyword of the first rule that applies to
19387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the number.
19397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
19407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param number The number for which the rule has to be determined.
19417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The keyword of the selected rule.
19427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
19437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String select(double number) {
19457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rules.select(new FixedDecimal(number));
19467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
19497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Given a number, returns the keyword of the first rule that applies to
19507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the number.
19517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
19527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param number The number for which the rule has to be determined.
19537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The keyword of the selected rule.
19547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
19557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
19567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
19587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String select(double number, int countVisibleFractionDigits, long fractionaldigits) {
19597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rules.select(new FixedDecimal(number, countVisibleFractionDigits, fractionaldigits));
19607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
19637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Given a number information, returns the keyword of the first rule that applies to
19647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the number.
19657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
196682027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer     * @param number The number information for which the rule has to be determined.
19677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The keyword of the selected rule.
19687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
19697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
19707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
197282027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer    public String select(FixedDecimal number) {
197382027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer        return rules.select(number);
19747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
197682027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer    /**
19777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Given a number information, and keyword, return whether the keyword would match the number.
19787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
19797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param sample The number information for which the rule has to be determined.
19807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword The keyword to filter on
19817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
19827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
19837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
19857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean matches(FixedDecimal sample, String keyword) {
19867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rules.select(sample, keyword);
19877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
19897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
19907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a set of all rule keywords used in this <code>PluralRules</code>
19917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * object.  The rule "other" is always present by default.
19927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
19937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The set of keywords.
19947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
19957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
19967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Set<String> getKeywords() {
19977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return keywords;
19987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
19997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
20017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the unique value that this keyword matches, or {@link #NO_UNIQUE_VALUE}
20027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * if the keyword matches multiple values or is not defined for this PluralRules.
20037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
20047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword to check for a unique value
20057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The unique value for the keyword, or NO_UNIQUE_VALUE.
20067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.8
20077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
20087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public double getUniqueKeywordValue(String keyword) {
20097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Collection<Double> values = getAllKeywordValues(keyword);
20107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (values != null && values.size() == 1) {
20117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return values.iterator().next();
20127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
20137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return NO_UNIQUE_VALUE;
20147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
20157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
20177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns all the values that trigger this keyword, or null if the number of such
20187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * values is unlimited.
20197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
20207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword
20217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the values that trigger this keyword, or null.  The returned collection
20227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * is immutable. It will be empty if the keyword is not defined.
20237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.8
20247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
20257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Collection<Double> getAllKeywordValues(String keyword) {
20267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getAllKeywordValues(keyword, SampleType.INTEGER);
20277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
20287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
20307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns all the values that trigger this keyword, or null if the number of such
20317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * values is unlimited.
20327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
20337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword
20347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param type the type of samples requested, INTEGER or DECIMAL
20357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the values that trigger this keyword, or null.  The returned collection
20367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * is immutable. It will be empty if the keyword is not defined.
20372d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
20387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
20397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
20407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
20417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
20427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Collection<Double> getAllKeywordValues(String keyword, SampleType type) {
20437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!isLimited(keyword, type)) {
20447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
20457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
20467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Collection<Double> samples = getSamples(keyword, type);
20477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return samples == null ? null : Collections.unmodifiableCollection(samples);
20487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
20497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
20517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a list of integer values for which select() would return that keyword,
20527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or null if the keyword is not defined. The returned collection is unmodifiable.
20537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The returned list is not complete, and there might be additional values that
20547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * would return the keyword.
20557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
20567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword to test
20577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return a list of values matching the keyword.
20587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.8
20597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
20607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Collection<Double> getSamples(String keyword) {
20617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getSamples(keyword, SampleType.INTEGER);
20627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
20637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
20657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a list of values for which select() would return that keyword,
20667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or null if the keyword is not defined.
20677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The returned collection is unmodifiable.
20687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The returned list is not complete, and there might be additional values that
20697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * would return the keyword. The keyword might be defined, and yet have an empty set of samples,
20707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * IF there are samples for the other sampleType.
20717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
20727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword to test
20737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param sampleType the type of samples requested, INTEGER or DECIMAL
20747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return a list of values matching the keyword.
20757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
20767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated ICU internal only
20777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
20787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
20797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Collection<Double> getSamples(String keyword, SampleType sampleType) {
20807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!keywords.contains(keyword)) {
20817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
20827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
20837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Set<Double> result = new TreeSet<Double>();
20847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (rules.hasExplicitBoundingInfo) {
20867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            FixedDecimalSamples samples = rules.getDecimalSamples(keyword, sampleType);
20877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return samples == null ? Collections.unmodifiableSet(result)
20887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    : Collections.unmodifiableSet(samples.addSamples(result));
20897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
20907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // hack in case the rule is created without explicit samples
20927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int maxCount = isLimited(keyword, sampleType) ? Integer.MAX_VALUE : 20;
20937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
20947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        switch (sampleType) {
20957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        case INTEGER:
20967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < 200; ++i) {
20977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!addSample(keyword, i, maxCount, result)) {
20987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
20997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
21007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
21017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            addSample(keyword, 1000000, maxCount, result); // hack for Welsh
21027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            break;
21037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        case DECIMAL:
21047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < 2000; ++i) {
21057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (!addSample(keyword, new FixedDecimal(i/10d, 1), maxCount, result)) {
21067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
21077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
21087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
21097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            addSample(keyword, new FixedDecimal(1000000d, 1), maxCount, result); // hack for Welsh
21107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            break;
21117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
21127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result.size() == 0 ? null : Collections.unmodifiableSet(result);
21137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
21147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
21157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
21167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
21177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
21187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
21197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
21207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean addSample(String keyword, Number sample, int maxCount, Set<Double> result) {
21217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String selectedKeyword = sample instanceof FixedDecimal ? select((FixedDecimal)sample) : select(sample.doubleValue());
21227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (selectedKeyword.equals(keyword)) {
21237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.add(sample.doubleValue());
21247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (--maxCount < 0) {
21257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return false;
21267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
21277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
21287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return true;
21297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
21307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
21317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
21327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns a list of values for which select() would return that keyword,
21337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * or null if the keyword is not defined or no samples are available.
21347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The returned collection is unmodifiable.
21357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The returned list is not complete, and there might be additional values that
21367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * would return the keyword.
21377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
21387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword the keyword to test
21397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param sampleType the type of samples requested, INTEGER or DECIMAL
21407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return a list of values matching the keyword.
21417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
21427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
21437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
21447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
21457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public FixedDecimalSamples getDecimalSamples(String keyword, SampleType sampleType) {
21467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rules.getDecimalSamples(keyword, sampleType);
21477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
21487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
21497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
21507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the set of locales for which PluralRules are known.
21517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the set of locales for which PluralRules are known, as a list
21527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @draft ICU 4.2 (retain)
21537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @provisional This API might change or be removed in a future release.
21547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
21557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale[] getAvailableULocales() {
21567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return Factory.getDefaultFactory().getAvailableULocales();
21577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
21587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
21597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
21607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the 'functionally equivalent' locale with respect to
21617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * plural rules.  Calling PluralRules.forLocale with the functionally equivalent
21627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * locale, and with the provided locale, returns rules that behave the same.
2163bee65486a185907111f3be60992433e133ec0e32Scott Russell     * <br>
21647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * All locales with the same functionally equivalent locale have
21657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * plural rules that behave the same.  This is not exaustive;
21667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * there may be other locales whose plural rules behave the same
21677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * that do not have the same equivalent locale.
21687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
21697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale the locale to check
2170bee65486a185907111f3be60992433e133ec0e32Scott Russell     * @param isAvailable if not null and of length &gt; 0, this will hold 'true' at
21717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * index 0 if locale is directly defined (without fallback) as having plural rules
21727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the functionally-equivalent locale
21737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @draft ICU 4.2 (retain)
21747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @provisional This API might change or be removed in a future release.
21757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
21767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static ULocale getFunctionalEquivalent(ULocale locale, boolean[] isAvailable) {
21777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return Factory.getDefaultFactory().getFunctionalEquivalent(locale, isAvailable);
21787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
21797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
21807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
21817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@inheritDoc}
21827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
21837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
21842d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Override
21857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String toString() {
21867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rules.toString();
21877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
21887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
21897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
21907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@inheritDoc}
21917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
21927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
21932d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Override
21947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean equals(Object rhs) {
21957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rhs instanceof PluralRules && equals((PluralRules)rhs);
21967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
21977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
21987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
21997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns true if rhs is equal to this.
22007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param rhs the PluralRules to compare to.
22017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return true if this and rhs are equal.
22027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
22037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
22047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // TODO Optimize this
22057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean equals(PluralRules rhs) {
22067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rhs != null && toString().equals(rhs.toString());
22077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
22087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
22097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
22107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Status of the keyword for the rules, given a set of explicit values.
22112d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
22127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @draft ICU 50
22137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @provisional This API might change or be removed in a future release.
22147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
22157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public enum KeywordStatus {
22167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
22177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * The keyword is not valid for the rules.
22182d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert         *
22197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @draft ICU 50
22207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @provisional This API might change or be removed in a future release.
22217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
22227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        INVALID,
22237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
22247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * The keyword is valid, but unused (it is covered by the explicit values, OR has no values for the given {@link SampleType}).
22252d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert         *
22267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @draft ICU 50
22277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @provisional This API might change or be removed in a future release.
22287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
22297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        SUPPRESSED,
22307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
22317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * The keyword is valid, used, and has a single possible value (before considering explicit values).
22322d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert         *
22337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @draft ICU 50
22347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @provisional This API might change or be removed in a future release.
22357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
22367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        UNIQUE,
22377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
22387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * The keyword is valid, used, not unique, and has a finite set of values.
22392d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert         *
22407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @draft ICU 50
22417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @provisional This API might change or be removed in a future release.
22427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
22437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        BOUNDED,
22447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
22457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * The keyword is valid but not bounded; there indefinitely many matching values.
22462d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert         *
22477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @draft ICU 50
22487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @provisional This API might change or be removed in a future release.
22497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
22507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        UNBOUNDED
22517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
22527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
22537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
22547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Find the status for the keyword, given a certain set of explicit values.
22552d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
22567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword
22577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            the particular keyword (call rules.getKeywords() to get the valid ones)
22587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param offset
22597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            the offset used, or 0.0d if not. Internally, the offset is subtracted from each explicit value before
22607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            checking against the keyword values.
22617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param explicits
22627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            a set of Doubles that are used explicitly (eg [=0], "[=1]"). May be empty or null.
22637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param uniqueValue
22647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            If non null, set to the unique value.
22657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the KeywordStatus
22667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @draft ICU 50
22677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @provisional This API might change or be removed in a future release.
22687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
22697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public KeywordStatus getKeywordStatus(String keyword, int offset, Set<Double> explicits,
22707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Output<Double> uniqueValue) {
22717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getKeywordStatus(keyword, offset, explicits, uniqueValue, SampleType.INTEGER);
22727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
22737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
22747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Find the status for the keyword, given a certain set of explicit values.
22752d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     *
22767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param keyword
22777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            the particular keyword (call rules.getKeywords() to get the valid ones)
22787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param offset
22797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            the offset used, or 0.0d if not. Internally, the offset is subtracted from each explicit value before
22807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            checking against the keyword values.
22817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param explicits
22827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            a set of Doubles that are used explicitly (eg [=0], "[=1]"). May be empty or null.
22837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param sampleType
22847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            request KeywordStatus relative to INTEGER or DECIMAL values
22857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param uniqueValue
22867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            If non null, set to the unique value.
22877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the KeywordStatus
22887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
22897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
22907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
22917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
22927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public KeywordStatus getKeywordStatus(String keyword, int offset, Set<Double> explicits,
22937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Output<Double> uniqueValue, SampleType sampleType) {
22947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (uniqueValue != null) {
22957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            uniqueValue.value = null;
22967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
22977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
22987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!keywords.contains(keyword)) {
22997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return KeywordStatus.INVALID;
23007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
23017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!isLimited(keyword, sampleType)) {
23037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return KeywordStatus.UNBOUNDED;
23047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
23057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Collection<Double> values = getSamples(keyword, sampleType);
23077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int originalSize = values.size();
23097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (explicits == null) {
23117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            explicits = Collections.emptySet();
23127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
23137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Quick check on whether there are multiple elements
23157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (originalSize > explicits.size()) {
23177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (originalSize == 1) {
23187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (uniqueValue != null) {
23197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    uniqueValue.value = values.iterator().next();
23207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
23217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return KeywordStatus.UNIQUE;
23227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
23237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return KeywordStatus.BOUNDED;
23247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
23257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Compute if the quick test is insufficient.
23277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        HashSet<Double> subtractedSet = new HashSet<Double>(values);
23297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Double explicit : explicits) {
23307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            subtractedSet.remove(explicit - offset);
23317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
23327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (subtractedSet.size() == 0) {
23337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return KeywordStatus.SUPPRESSED;
23347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
23357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (uniqueValue != null && subtractedSet.size() == 1) {
23377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            uniqueValue.value = subtractedSet.iterator().next();
23387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
23397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return originalSize == 1 ? KeywordStatus.UNIQUE : KeywordStatus.BOUNDED;
23417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
23427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
23447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
23457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
23467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
23477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
23487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getRules(String keyword) {
23497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rules.getRules(keyword);
23507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
23517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void writeObject(
23537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ObjectOutputStream out)
23547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    throws IOException {
23557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        throw new NotSerializableException();
23567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
23577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void readObject(ObjectInputStream in
23597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ) throws IOException, ClassNotFoundException {
23607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        throw new NotSerializableException();
23617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
23627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Object writeReplace() throws ObjectStreamException {
23647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new PluralRulesSerialProxy(toString());
23657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
23667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
23687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
23697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated internal
23707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
23717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
23727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int compareTo(PluralRules other) {
23737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return toString().compareTo(other.toString());
23747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
23757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
23777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
23787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated internal
23797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
23807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
23817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Boolean isLimited(String keyword) {
23827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rules.isLimited(keyword, SampleType.INTEGER);
23837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
23847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
23867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
23877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated internal
23887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
23897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
23907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean isLimited(String keyword, SampleType sampleType) {
23917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rules.isLimited(keyword, sampleType);
23927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
23937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
23947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
23957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
23967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated internal
23977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
23987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
23997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean computeLimited(String keyword, SampleType sampleType) {
24007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rules.computeLimited(keyword, sampleType);
24017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
24027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
2403