Import message format.

Imported from mscherer's SVN repository:

svn checkout http://source.icu-project.org/repos/icu/icu4j/branches/markus/simplemsg

at revision 35802:

Local changes include :
- Removal of commented code.
- Addition of android makefiles
- JUnit test + caliper benchmark.

Scenario{vm=app_process, trial=0, benchmark=Genders} 45870.31 ns; σ=58.09 ns @ 3 trials
Scenario{vm=app_process, trial=0, benchmark=Plurals} 109590.42 ns; σ=209.97 ns @ 3 trials

Change-Id: I868f18bd3cbeb2581e799ef68ff2ad4e701e033e
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..b55e954
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := messageformat
+LOCAL_SRC_FILES := $(call all-java-files-under, src/)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := messageformat-tests
+LOCAL_STATIC_JAVA_LIBRARIES := messageformat junit-targetdex
+LOCAL_SRC_FILES := $(call all-java-files-under, tests/src/)
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Also build a host side library
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src/)
+
+LOCAL_MODULE := messageformat_host
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/benchmarks/com/android/messageformat/MessageFormatBenchmark.java b/benchmarks/com/android/messageformat/MessageFormatBenchmark.java
new file mode 100644
index 0000000..79847cb
--- /dev/null
+++ b/benchmarks/com/android/messageformat/MessageFormatBenchmark.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.messageformat;
+
+import com.google.caliper.SimpleBenchmark;
+import java.util.Locale;
+
+public class MessageFormatBenchmark extends SimpleBenchmark {
+    public void timePlurals(int nreps) throws Exception {
+        final Locale sr = new Locale("sr");
+        for (int i = 0; i < nreps; ++i) {
+            String msg = "{num,plural,offset:1 =1{only {name}}=2{{name} and one other}" +
+                    "one{{name} and #-one others}few{{name} and #-few others}" +
+                    "other{{name} and #... others}}";
+            MessageFormat.formatNamedArgs(sr, msg, "num", 1, "name", "Peter");
+        }
+    }
+
+    public void timeGenders(int nreps) throws Exception {
+        for (int i = 0; i < nreps; ++i) {
+            String msg = "{gender,select,female{her book}male{his book}other{their book}}";
+            MessageFormat.formatNamedArgs(Locale.US, msg, "gender", "female");
+        }
+    }
+}
diff --git a/import.sh b/import.sh
new file mode 100644
index 0000000..b3f5093
--- /dev/null
+++ b/import.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+TOP=$1
+mkdir -p src/com/ibm/icu/impl
+mkdir -p src/com/ibm/icu/simple
+mkdir -p src/com/ibm/icu/text
+mkdir -p src/com/ibm/icu/util
+cp ${TOP}/main/classes/core/src/com/ibm/icu/impl/PatternProps.java src/com/ibm/icu/impl
+cp ${TOP}/main/classes/core/src/com/ibm/icu/impl/ICUConfig.java src/com/ibm/icu/impl
+cp ${TOP}/main/classes/core/src/com/ibm/icu/impl/ICUData.java src/com/ibm/icu/impl
+cp ${TOP}/main/classes/core/src/com/ibm/icu/simple/*.java src/com/ibm/icu/simple/
+cp ${TOP}/main/classes/core/src/com/ibm/icu/text/MessagePattern.java src/com/ibm/icu/text
+cp ${TOP}/main/classes/core/src/com/ibm/icu/text/SelectFormat.java src/com/ibm/icu/text
+cp ${TOP}/main/classes/core/src/com/ibm/icu/util/ICUUncheckedIOException.java src/com/ibm/icu/util
+cp ${TOP}/main/classes/core/src/com/ibm/icu/util/ICUCloneNotSupportedException.java src/com/ibm/icu/util
+cp ${TOP}/main/classes/core/src/com/ibm/icu/util/ICUException.java src/com/ibm/icu/util
+cp ${TOP}/main/classes/core/src/com/ibm/icu/util/Output.java src/com/ibm/icu/util
+cp ${TOP}/main/classes/core/src/com/ibm/icu/util/Freezable.java src/com/ibm/icu/util
diff --git a/src/com/android/messageformat/MessageFormat.java b/src/com/android/messageformat/MessageFormat.java
new file mode 100644
index 0000000..a12b7ff
--- /dev/null
+++ b/src/com/android/messageformat/MessageFormat.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.messageformat;
+
+import java.util.Locale;
+
+public final class MessageFormat {
+  /**
+   * Formats a message pattern string with a variable number of name/value pair arguments.
+   * Creates an ICU MessageFormat for the locale and pattern,
+   * and formats with the arguments.
+   *
+   * @param locale Locale for number formatting and plural selection etc.
+   * @param msg an ICU-MessageFormat-syntax string
+   * @param nameValuePairs (argument name, argument value) pairs
+   */
+  public static final String formatNamedArgs(Locale locale, String msg, Object... nameValuePairs) {
+    return com.ibm.icu.simple.MessageFormat.formatNamedArgs(locale, msg, nameValuePairs);
+  }
+
+  // Non instantiable
+  private MessageFormat() {
+  }
+}
diff --git a/src/com/ibm/icu/impl/ICUConfig.java b/src/com/ibm/icu/impl/ICUConfig.java
new file mode 100644
index 0000000..b875286
--- /dev/null
+++ b/src/com/ibm/icu/impl/ICUConfig.java
@@ -0,0 +1,77 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2008-2010, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ */
+package com.ibm.icu.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.MissingResourceException;
+import java.util.Properties;
+
+/**
+ * ICUConfig is a class used for accessing ICU4J runtime configuration.
+ */
+public class ICUConfig {
+    public static final String CONFIG_PROPS_FILE = "/com/ibm/icu/ICUConfig.properties";
+    private static final Properties CONFIG_PROPS;
+
+    static {
+        CONFIG_PROPS = new Properties();
+        try {
+            InputStream is = ICUData.getStream(CONFIG_PROPS_FILE);
+            if (is != null) {
+                CONFIG_PROPS.load(is);
+            }
+        } catch (MissingResourceException mre) {
+            // If it does not exist, ignore.
+        } catch (IOException ioe) {
+            // Any IO errors, ignore
+        }
+    }
+
+    /**
+     * Get ICU configuration property value for the given name.
+     * @param name The configuration property name
+     * @return The configuration property value, or null if it does not exist.
+     */
+    public static String get(String name) {
+        return get(name, null);
+    }
+
+    /**
+     * Get ICU configuration property value for the given name.
+     * @param name The configuration property name
+     * @param def The default value
+     * @return The configuration property value.  If the property does not
+     * exist, <code>def</code> is returned.
+     */
+    public static String get(String name, String def) {
+        String val = null;
+        final String fname = name;
+        if (System.getSecurityManager() != null) {
+            try {
+                val = AccessController.doPrivileged(new PrivilegedAction<String>() {
+                    public String run() {
+                        return System.getProperty(fname);
+                    }
+                });
+            } catch (AccessControlException e) {
+                // ignore
+                // TODO log this message
+            }
+        } else {
+            val = System.getProperty(name);
+        }
+
+        if (val == null) {
+            val = CONFIG_PROPS.getProperty(name, def);
+        }
+        return val;
+    }
+}
diff --git a/src/com/ibm/icu/impl/ICUData.java b/src/com/ibm/icu/impl/ICUData.java
new file mode 100644
index 0000000..b47b278
--- /dev/null
+++ b/src/com/ibm/icu/impl/ICUData.java
@@ -0,0 +1,113 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2004-2009, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * Created on Feb 4, 2004
+ *
+ */
+package com.ibm.icu.impl;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.MissingResourceException;
+
+/**
+ * Provides access to ICU data files as InputStreams.  Implements security checking.
+ */
+public final class ICUData {
+    /*
+     * Return a URL to the ICU resource names resourceName.  The
+     * resource name should either be an absolute path, or a path relative to
+     * com.ibm.icu.impl (e.g., most likely it is 'data/foo').  If required
+     * is true, throw an MissingResourceException instead of returning a null result.
+     */
+    public static boolean exists(final String resourceName) {
+        URL i = null;
+        if (System.getSecurityManager() != null) {
+            i = AccessController.doPrivileged(new PrivilegedAction<URL>() {
+                    public URL run() {
+                        return ICUData.class.getResource(resourceName);
+                    }
+                });
+        } else {
+            i = ICUData.class.getResource(resourceName);
+        }
+        return i != null;
+    }
+        
+    private static InputStream getStream(final Class<?> root, final String resourceName, boolean required) {
+        InputStream i = null;
+        
+        if (System.getSecurityManager() != null) {
+            i = AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
+                    public InputStream run() {
+                        return root.getResourceAsStream(resourceName);
+                    }
+                });
+        } else {
+            i = root.getResourceAsStream(resourceName);
+        }
+
+        if (i == null && required) {
+            throw new MissingResourceException("could not locate data " +resourceName, root.getPackage().getName(), resourceName);
+        }
+        return i;
+    }
+
+    private static InputStream getStream(final ClassLoader loader, final String resourceName, boolean required) {
+        InputStream i = null;
+        if (System.getSecurityManager() != null) {
+            i = AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
+                    public InputStream run() {
+                        return loader.getResourceAsStream(resourceName);
+                    }
+                });
+        } else {
+            i = loader.getResourceAsStream(resourceName);
+        }
+        if (i == null && required) {
+            throw new MissingResourceException("could not locate data", loader.toString(), resourceName);
+        }
+        return i;
+    }
+    
+    public static InputStream getStream(ClassLoader loader, String resourceName){
+        return getStream(loader,resourceName, false);   
+    }
+
+    public static InputStream getRequiredStream(ClassLoader loader, String resourceName){
+        return getStream(loader, resourceName, true);
+    }
+
+    /*
+     * Convenience override that calls getStream(ICUData.class, resourceName, false);
+     */
+    public static InputStream getStream(String resourceName) {
+        return getStream(ICUData.class, resourceName, false);
+    }
+        
+    /*
+     * Convenience method that calls getStream(ICUData.class, resourceName, true).
+     */
+    public static InputStream getRequiredStream(String resourceName) {
+        return getStream(ICUData.class, resourceName, true);
+    }
+
+    /*
+     * Convenience override that calls getStream(root, resourceName, false);
+     */
+    public static InputStream getStream(Class<?> root, String resourceName) {
+        return getStream(root, resourceName, false);
+    }
+    
+    /*
+     * Convenience method that calls getStream(root, resourceName, true).
+     */
+    public static InputStream getRequiredStream(Class<?> root, String resourceName) {
+        return getStream(root, resourceName, true);
+    }
+}
diff --git a/src/main/com/android/i18n/PatternProps.java b/src/com/ibm/icu/impl/PatternProps.java
similarity index 100%
rename from src/main/com/android/i18n/PatternProps.java
rename to src/com/ibm/icu/impl/PatternProps.java
diff --git a/src/com/ibm/icu/simple/LocaleElements_plurals.java b/src/com/ibm/icu/simple/LocaleElements_plurals.java
new file mode 100644
index 0000000..31161a3
--- /dev/null
+++ b/src/com/ibm/icu/simple/LocaleElements_plurals.java
@@ -0,0 +1,2308 @@
+package com.ibm.icu.simple;
+
+import java.util.ListResourceBundle;
+
+public class LocaleElements_plurals extends ListResourceBundle {
+
+    /**
+     * Overrides ListResourceBundle 
+     */
+    public final Object[][] getContents() { 
+          return  contents;
+    }
+
+    private static Object[][] contents = {
+                {
+                    "locales",
+                    new Object[][]{
+                        {
+                            "af",
+                            "set8",
+                        },
+                        {
+                            "ak",
+                            "set6",
+                        },
+                        {
+                            "am",
+                            "set1",
+                        },
+                        {
+                            "ar",
+                            "set33",
+                        },
+                        {
+                            "asa",
+                            "set8",
+                        },
+                        {
+                            "ast",
+                            "set3",
+                        },
+                        {
+                            "az",
+                            "set8",
+                        },
+                        {
+                            "be",
+                            "set26",
+                        },
+                        {
+                            "bem",
+                            "set8",
+                        },
+                        {
+                            "bez",
+                            "set8",
+                        },
+                        {
+                            "bg",
+                            "set8",
+                        },
+                        {
+                            "bh",
+                            "set6",
+                        },
+                        {
+                            "bm",
+                            "set0",
+                        },
+                        {
+                            "bn",
+                            "set1",
+                        },
+                        {
+                            "bo",
+                            "set0",
+                        },
+                        {
+                            "br",
+                            "set30",
+                        },
+                        {
+                            "brx",
+                            "set8",
+                        },
+                        {
+                            "bs",
+                            "set20",
+                        },
+                        {
+                            "ca",
+                            "set3",
+                        },
+                        {
+                            "cgg",
+                            "set8",
+                        },
+                        {
+                            "chr",
+                            "set8",
+                        },
+                        {
+                            "ckb",
+                            "set8",
+                        },
+                        {
+                            "cs",
+                            "set24",
+                        },
+                        {
+                            "cy",
+                            "set34",
+                        },
+                        {
+                            "da",
+                            "set10",
+                        },
+                        {
+                            "de",
+                            "set3",
+                        },
+                        {
+                            "dv",
+                            "set8",
+                        },
+                        {
+                            "dz",
+                            "set0",
+                        },
+                        {
+                            "ee",
+                            "set8",
+                        },
+                        {
+                            "el",
+                            "set8",
+                        },
+                        {
+                            "en",
+                            "set3",
+                        },
+                        {
+                            "eo",
+                            "set8",
+                        },
+                        {
+                            "es",
+                            "set8",
+                        },
+                        {
+                            "et",
+                            "set3",
+                        },
+                        {
+                            "eu",
+                            "set8",
+                        },
+                        {
+                            "fa",
+                            "set1",
+                        },
+                        {
+                            "ff",
+                            "set2",
+                        },
+                        {
+                            "fi",
+                            "set3",
+                        },
+                        {
+                            "fil",
+                            "set13",
+                        },
+                        {
+                            "fo",
+                            "set8",
+                        },
+                        {
+                            "fr",
+                            "set2",
+                        },
+                        {
+                            "fur",
+                            "set8",
+                        },
+                        {
+                            "fy",
+                            "set3",
+                        },
+                        {
+                            "ga",
+                            "set31",
+                        },
+                        {
+                            "gd",
+                            "set21",
+                        },
+                        {
+                            "gl",
+                            "set3",
+                        },
+                        {
+                            "gsw",
+                            "set8",
+                        },
+                        {
+                            "gu",
+                            "set1",
+                        },
+                        {
+                            "guw",
+                            "set6",
+                        },
+                        {
+                            "gv",
+                            "set32",
+                        },
+                        {
+                            "ha",
+                            "set8",
+                        },
+                        {
+                            "haw",
+                            "set8",
+                        },
+                        {
+                            "he",
+                            "set23",
+                        },
+                        {
+                            "hi",
+                            "set1",
+                        },
+                        {
+                            "hr",
+                            "set20",
+                        },
+                        {
+                            "hu",
+                            "set8",
+                        },
+                        {
+                            "hy",
+                            "set2",
+                        },
+                        {
+                            "id",
+                            "set0",
+                        },
+                        {
+                            "ig",
+                            "set0",
+                        },
+                        {
+                            "ii",
+                            "set0",
+                        },
+                        {
+                            "in",
+                            "set0",
+                        },
+                        {
+                            "is",
+                            "set11",
+                        },
+                        {
+                            "it",
+                            "set3",
+                        },
+                        {
+                            "iu",
+                            "set17",
+                        },
+                        {
+                            "iw",
+                            "set23",
+                        },
+                        {
+                            "ja",
+                            "set0",
+                        },
+                        {
+                            "jbo",
+                            "set0",
+                        },
+                        {
+                            "jgo",
+                            "set8",
+                        },
+                        {
+                            "ji",
+                            "set3",
+                        },
+                        {
+                            "jmc",
+                            "set8",
+                        },
+                        {
+                            "jv",
+                            "set0",
+                        },
+                        {
+                            "jw",
+                            "set0",
+                        },
+                        {
+                            "ka",
+                            "set8",
+                        },
+                        {
+                            "kab",
+                            "set2",
+                        },
+                        {
+                            "kaj",
+                            "set8",
+                        },
+                        {
+                            "kcg",
+                            "set8",
+                        },
+                        {
+                            "kde",
+                            "set0",
+                        },
+                        {
+                            "kea",
+                            "set0",
+                        },
+                        {
+                            "kk",
+                            "set8",
+                        },
+                        {
+                            "kkj",
+                            "set8",
+                        },
+                        {
+                            "kl",
+                            "set8",
+                        },
+                        {
+                            "km",
+                            "set0",
+                        },
+                        {
+                            "kn",
+                            "set1",
+                        },
+                        {
+                            "ko",
+                            "set0",
+                        },
+                        {
+                            "ks",
+                            "set8",
+                        },
+                        {
+                            "ksb",
+                            "set8",
+                        },
+                        {
+                            "ksh",
+                            "set16",
+                        },
+                        {
+                            "ku",
+                            "set8",
+                        },
+                        {
+                            "kw",
+                            "set17",
+                        },
+                        {
+                            "ky",
+                            "set8",
+                        },
+                        {
+                            "lag",
+                            "set15",
+                        },
+                        {
+                            "lb",
+                            "set8",
+                        },
+                        {
+                            "lg",
+                            "set8",
+                        },
+                        {
+                            "lkt",
+                            "set0",
+                        },
+                        {
+                            "ln",
+                            "set6",
+                        },
+                        {
+                            "lo",
+                            "set0",
+                        },
+                        {
+                            "lt",
+                            "set27",
+                        },
+                        {
+                            "lv",
+                            "set14",
+                        },
+                        {
+                            "mas",
+                            "set8",
+                        },
+                        {
+                            "mg",
+                            "set6",
+                        },
+                        {
+                            "mgo",
+                            "set8",
+                        },
+                        {
+                            "mk",
+                            "set12",
+                        },
+                        {
+                            "ml",
+                            "set8",
+                        },
+                        {
+                            "mn",
+                            "set8",
+                        },
+                        {
+                            "mo",
+                            "set19",
+                        },
+                        {
+                            "mr",
+                            "set1",
+                        },
+                        {
+                            "ms",
+                            "set0",
+                        },
+                        {
+                            "mt",
+                            "set28",
+                        },
+                        {
+                            "my",
+                            "set0",
+                        },
+                        {
+                            "nah",
+                            "set8",
+                        },
+                        {
+                            "naq",
+                            "set17",
+                        },
+                        {
+                            "nb",
+                            "set8",
+                        },
+                        {
+                            "nd",
+                            "set8",
+                        },
+                        {
+                            "ne",
+                            "set8",
+                        },
+                        {
+                            "nl",
+                            "set3",
+                        },
+                        {
+                            "nn",
+                            "set8",
+                        },
+                        {
+                            "nnh",
+                            "set8",
+                        },
+                        {
+                            "no",
+                            "set8",
+                        },
+                        {
+                            "nqo",
+                            "set0",
+                        },
+                        {
+                            "nr",
+                            "set8",
+                        },
+                        {
+                            "nso",
+                            "set6",
+                        },
+                        {
+                            "ny",
+                            "set8",
+                        },
+                        {
+                            "nyn",
+                            "set8",
+                        },
+                        {
+                            "om",
+                            "set8",
+                        },
+                        {
+                            "or",
+                            "set8",
+                        },
+                        {
+                            "os",
+                            "set8",
+                        },
+                        {
+                            "pa",
+                            "set6",
+                        },
+                        {
+                            "pap",
+                            "set8",
+                        },
+                        {
+                            "pl",
+                            "set25",
+                        },
+                        {
+                            "prg",
+                            "set14",
+                        },
+                        {
+                            "ps",
+                            "set8",
+                        },
+                        {
+                            "pt",
+                            "set4",
+                        },
+                        {
+                            "pt_PT",
+                            "set9",
+                        },
+                        {
+                            "rm",
+                            "set8",
+                        },
+                        {
+                            "ro",
+                            "set19",
+                        },
+                        {
+                            "rof",
+                            "set8",
+                        },
+                        {
+                            "root",
+                            "set0",
+                        },
+                        {
+                            "ru",
+                            "set29",
+                        },
+                        {
+                            "rwk",
+                            "set8",
+                        },
+                        {
+                            "sah",
+                            "set0",
+                        },
+                        {
+                            "saq",
+                            "set8",
+                        },
+                        {
+                            "se",
+                            "set17",
+                        },
+                        {
+                            "seh",
+                            "set8",
+                        },
+                        {
+                            "ses",
+                            "set0",
+                        },
+                        {
+                            "sg",
+                            "set0",
+                        },
+                        {
+                            "sh",
+                            "set20",
+                        },
+                        {
+                            "shi",
+                            "set18",
+                        },
+                        {
+                            "si",
+                            "set5",
+                        },
+                        {
+                            "sk",
+                            "set24",
+                        },
+                        {
+                            "sl",
+                            "set22",
+                        },
+                        {
+                            "sma",
+                            "set17",
+                        },
+                        {
+                            "smi",
+                            "set17",
+                        },
+                        {
+                            "smj",
+                            "set17",
+                        },
+                        {
+                            "smn",
+                            "set17",
+                        },
+                        {
+                            "sms",
+                            "set17",
+                        },
+                        {
+                            "sn",
+                            "set8",
+                        },
+                        {
+                            "so",
+                            "set8",
+                        },
+                        {
+                            "sq",
+                            "set8",
+                        },
+                        {
+                            "sr",
+                            "set20",
+                        },
+                        {
+                            "ss",
+                            "set8",
+                        },
+                        {
+                            "ssy",
+                            "set8",
+                        },
+                        {
+                            "st",
+                            "set8",
+                        },
+                        {
+                            "sv",
+                            "set3",
+                        },
+                        {
+                            "sw",
+                            "set3",
+                        },
+                        {
+                            "syr",
+                            "set8",
+                        },
+                        {
+                            "ta",
+                            "set8",
+                        },
+                        {
+                            "te",
+                            "set8",
+                        },
+                        {
+                            "teo",
+                            "set8",
+                        },
+                        {
+                            "th",
+                            "set0",
+                        },
+                        {
+                            "ti",
+                            "set6",
+                        },
+                        {
+                            "tig",
+                            "set8",
+                        },
+                        {
+                            "tk",
+                            "set8",
+                        },
+                        {
+                            "tl",
+                            "set13",
+                        },
+                        {
+                            "tn",
+                            "set8",
+                        },
+                        {
+                            "to",
+                            "set0",
+                        },
+                        {
+                            "tr",
+                            "set8",
+                        },
+                        {
+                            "ts",
+                            "set8",
+                        },
+                        {
+                            "tzm",
+                            "set7",
+                        },
+                        {
+                            "ug",
+                            "set8",
+                        },
+                        {
+                            "uk",
+                            "set29",
+                        },
+                        {
+                            "ur",
+                            "set3",
+                        },
+                        {
+                            "uz",
+                            "set8",
+                        },
+                        {
+                            "ve",
+                            "set8",
+                        },
+                        {
+                            "vi",
+                            "set0",
+                        },
+                        {
+                            "vo",
+                            "set8",
+                        },
+                        {
+                            "vun",
+                            "set8",
+                        },
+                        {
+                            "wa",
+                            "set6",
+                        },
+                        {
+                            "wae",
+                            "set8",
+                        },
+                        {
+                            "wo",
+                            "set0",
+                        },
+                        {
+                            "xh",
+                            "set8",
+                        },
+                        {
+                            "xog",
+                            "set8",
+                        },
+                        {
+                            "yi",
+                            "set3",
+                        },
+                        {
+                            "yo",
+                            "set0",
+                        },
+                        {
+                            "zh",
+                            "set0",
+                        },
+                        {
+                            "zu",
+                            "set1",
+                        },
+                    },
+                },
+                {
+                    "locales_ordinals",
+                    new Object[][]{
+                        {
+                            "af",
+                            "set35",
+                        },
+                        {
+                            "am",
+                            "set35",
+                        },
+                        {
+                            "ar",
+                            "set35",
+                        },
+                        {
+                            "az",
+                            "set48",
+                        },
+                        {
+                            "bg",
+                            "set35",
+                        },
+                        {
+                            "bn",
+                            "set50",
+                        },
+                        {
+                            "ca",
+                            "set46",
+                        },
+                        {
+                            "cs",
+                            "set35",
+                        },
+                        {
+                            "cy",
+                            "set51",
+                        },
+                        {
+                            "da",
+                            "set35",
+                        },
+                        {
+                            "de",
+                            "set35",
+                        },
+                        {
+                            "el",
+                            "set35",
+                        },
+                        {
+                            "en",
+                            "set44",
+                        },
+                        {
+                            "es",
+                            "set35",
+                        },
+                        {
+                            "et",
+                            "set35",
+                        },
+                        {
+                            "eu",
+                            "set35",
+                        },
+                        {
+                            "fa",
+                            "set35",
+                        },
+                        {
+                            "fi",
+                            "set35",
+                        },
+                        {
+                            "fil",
+                            "set37",
+                        },
+                        {
+                            "fr",
+                            "set37",
+                        },
+                        {
+                            "fy",
+                            "set35",
+                        },
+                        {
+                            "gl",
+                            "set35",
+                        },
+                        {
+                            "gu",
+                            "set49",
+                        },
+                        {
+                            "he",
+                            "set35",
+                        },
+                        {
+                            "hi",
+                            "set49",
+                        },
+                        {
+                            "hr",
+                            "set35",
+                        },
+                        {
+                            "hu",
+                            "set38",
+                        },
+                        {
+                            "hy",
+                            "set37",
+                        },
+                        {
+                            "id",
+                            "set35",
+                        },
+                        {
+                            "in",
+                            "set35",
+                        },
+                        {
+                            "is",
+                            "set35",
+                        },
+                        {
+                            "it",
+                            "set41",
+                        },
+                        {
+                            "iw",
+                            "set35",
+                        },
+                        {
+                            "ja",
+                            "set35",
+                        },
+                        {
+                            "ka",
+                            "set42",
+                        },
+                        {
+                            "kk",
+                            "set40",
+                        },
+                        {
+                            "km",
+                            "set35",
+                        },
+                        {
+                            "kn",
+                            "set35",
+                        },
+                        {
+                            "ko",
+                            "set35",
+                        },
+                        {
+                            "ky",
+                            "set35",
+                        },
+                        {
+                            "lo",
+                            "set37",
+                        },
+                        {
+                            "lt",
+                            "set35",
+                        },
+                        {
+                            "lv",
+                            "set35",
+                        },
+                        {
+                            "mk",
+                            "set47",
+                        },
+                        {
+                            "ml",
+                            "set35",
+                        },
+                        {
+                            "mn",
+                            "set35",
+                        },
+                        {
+                            "mo",
+                            "set37",
+                        },
+                        {
+                            "mr",
+                            "set45",
+                        },
+                        {
+                            "ms",
+                            "set37",
+                        },
+                        {
+                            "my",
+                            "set35",
+                        },
+                        {
+                            "nb",
+                            "set35",
+                        },
+                        {
+                            "ne",
+                            "set39",
+                        },
+                        {
+                            "nl",
+                            "set35",
+                        },
+                        {
+                            "pa",
+                            "set35",
+                        },
+                        {
+                            "pl",
+                            "set35",
+                        },
+                        {
+                            "prg",
+                            "set35",
+                        },
+                        {
+                            "pt",
+                            "set35",
+                        },
+                        {
+                            "ro",
+                            "set37",
+                        },
+                        {
+                            "root",
+                            "set35",
+                        },
+                        {
+                            "ru",
+                            "set35",
+                        },
+                        {
+                            "sh",
+                            "set35",
+                        },
+                        {
+                            "si",
+                            "set35",
+                        },
+                        {
+                            "sk",
+                            "set35",
+                        },
+                        {
+                            "sl",
+                            "set35",
+                        },
+                        {
+                            "sq",
+                            "set43",
+                        },
+                        {
+                            "sr",
+                            "set35",
+                        },
+                        {
+                            "sv",
+                            "set36",
+                        },
+                        {
+                            "sw",
+                            "set35",
+                        },
+                        {
+                            "ta",
+                            "set35",
+                        },
+                        {
+                            "te",
+                            "set35",
+                        },
+                        {
+                            "th",
+                            "set35",
+                        },
+                        {
+                            "tl",
+                            "set37",
+                        },
+                        {
+                            "tr",
+                            "set35",
+                        },
+                        {
+                            "uk",
+                            "set35",
+                        },
+                        {
+                            "ur",
+                            "set35",
+                        },
+                        {
+                            "uz",
+                            "set35",
+                        },
+                        {
+                            "vi",
+                            "set37",
+                        },
+                        {
+                            "zh",
+                            "set35",
+                        },
+                        {
+                            "zu",
+                            "set35",
+                        },
+                    },
+                },
+                {
+                    "rules",
+                    new Object[][]{
+                        {
+                            "set0",
+                            new Object[][]{
+                                {
+                                    "other",
+                                    " @integer 0~15, 100, 1000, 10000, 100000, 1" +
+                                    "000000, … @decimal 0.0~1.5, 10.0, 100.0, " +
+                                    "1000.0, 10000.0, 100000.0, 1000000.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set1",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1" +
+                                    ".0, 0.00~0.04",
+                                },
+                                {
+                                    "other",
+                                    " @integer 2~17, 100, 1000, 10000, 100000, 1" +
+                                    "000000, … @decimal 1.1~2.6, 10.0, 100.0, " +
+                                    "1000.0, 10000.0, 100000.0, 1000000.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set10",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "n = 1 or t != 0 and i = 0,1 @integer 1 @dec" +
+                                    "imal 0.1~1.6",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 2~16, 100, 1000, 10000, 100000" +
+                                    ", 1000000, … @decimal 0.0, 2.0~3.4, 10.0," +
+                                    " 100.0, 1000.0, 10000.0, 100000.0, 1000000." +
+                                    "0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set11",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "t = 0 and i % 10 = 1 and i % 100 != 11 or t" +
+                                    " != 0 @integer 1, 21, 31, 41, 51, 61, 71, 8" +
+                                    "1, 101, 1001, … @decimal 0.1~1.6, 10.1, 1" +
+                                    "00.1, 1000.1, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 2~16, 100, 1000, 10000, 100000" +
+                                    ", 1000000, … @decimal 0.0, 2.0, 3.0, 4.0," +
+                                    " 5.0, 6.0, 7.0, 8.0, 10.0, 100.0, 1000.0, 1" +
+                                    "0000.0, 100000.0, 1000000.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set12",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "v = 0 and i % 10 = 1 or f % 10 = 1 @integer" +
+                                    " 1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, " +
+                                    "… @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, " +
+                                    "6.1, 7.1, 10.1, 100.1, 1000.1, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 2~10, 12~17, 100, 1000, 10000," +
+                                    " 100000, 1000000, … @decimal 0.0, 0.2~1.0" +
+                                    ", 1.2~1.7, 10.0, 100.0, 1000.0, 10000.0, 10" +
+                                    "0000.0, 1000000.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set13",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "v = 0 and i = 1,2,3 or v = 0 and i % 10 != " +
+                                    "4,6,9 or v != 0 and f % 10 != 4,6,9 @intege" +
+                                    "r 0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, " +
+                                    "100, 1000, 10000, 100000, 1000000, … @dec" +
+                                    "imal 0.0~0.3, 0.5, 0.7, 0.8, 1.0~1.3, 1.5, " +
+                                    "1.7, 1.8, 2.0, 2.1, 10.0, 100.0, 1000.0, 10" +
+                                    "000.0, 100000.0, 1000000.0, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 4, 6, 9, 14, 16, 19, 24, 26, 104," +
+                                    " 1004, … @decimal 0.4, 0.6, 0.9, 1.4, 1.6" +
+                                    ", 1.9, 2.4, 2.6, 10.4, 100.4, 1000.4, …",
+                                },
+                            },
+                        },
+                        {
+                            "set14",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "n % 10 = 1 and n % 100 != 11 or v = 2 and f" +
+                                    " % 10 = 1 and f % 100 != 11 or v != 2 and f" +
+                                    " % 10 = 1 @integer 1, 21, 31, 41, 51, 61, 7" +
+                                    "1, 81, 101, 1001, … @decimal 0.1, 1.0, 1." +
+                                    "1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100." +
+                                    "1, 1000.1, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 2~9, 22~29, 102, 1002, … @decim" +
+                                    "al 0.2~0.9, 1.2~1.9, 10.2, 100.2, 1000.2, …",
+                                },
+                                {
+                                    "zero",
+                                    "n % 10 = 0 or n % 100 = 11..19 or v = 2 and" +
+                                    " f % 100 = 11..19 @integer 0, 10~20, 30, 40" +
+                                    ", 50, 60, 100, 1000, 10000, 100000, 1000000" +
+                                    ", … @decimal 0.0, 10.0, 11.0, 12.0, 13.0," +
+                                    " 14.0, 15.0, 16.0, 100.0, 1000.0, 10000.0, " +
+                                    "100000.0, 1000000.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set15",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "i = 0,1 and n != 0 @integer 1 @decimal 0.1~1.6",
+                                },
+                                {
+                                    "other",
+                                    " @integer 2~17, 100, 1000, 10000, 100000, 1" +
+                                    "000000, … @decimal 2.0~3.5, 10.0, 100.0, " +
+                                    "1000.0, 10000.0, 100000.0, 1000000.0, …",
+                                },
+                                {
+                                    "zero",
+                                    "n = 0 @integer 0 @decimal 0.0, 0.00, 0.000," +
+                                    " 0.0000",
+                                },
+                            },
+                        },
+                        {
+                            "set16",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000," +
+                                    " 1.0000",
+                                },
+                                {
+                                    "other",
+                                    " @integer 2~17, 100, 1000, 10000, 100000, 1" +
+                                    "000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0" +
+                                    ", 100.0, 1000.0, 10000.0, 100000.0, 1000000" +
+                                    ".0, …",
+                                },
+                                {
+                                    "zero",
+                                    "n = 0 @integer 0 @decimal 0.0, 0.00, 0.000," +
+                                    " 0.0000",
+                                },
+                            },
+                        },
+                        {
+                            "set17",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000," +
+                                    " 1.0000",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 3~17, 100, 1000, 10000, 100000" +
+                                    ", 1000000, … @decimal 0.0~0.9, 1.1~1.6, 1" +
+                                    "0.0, 100.0, 1000.0, 10000.0, 100000.0, 1000" +
+                                    "000.0, …",
+                                },
+                                {
+                                    "two",
+                                    "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000," +
+                                    " 2.0000",
+                                },
+                            },
+                        },
+                        {
+                            "set18",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n = 2..10 @integer 2~10 @decimal 2.0, 3.0, " +
+                                    "4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 2.00, 3" +
+                                    ".00, 4.00, 5.00, 6.00, 7.00, 8.00",
+                                },
+                                {
+                                    "one",
+                                    "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1" +
+                                    ".0, 0.00~0.04",
+                                },
+                                {
+                                    "other",
+                                    " @integer 11~26, 100, 1000, 10000, 100000, " +
+                                    "1000000, … @decimal 1.1~1.9, 2.1~2.7, 10." +
+                                    "1, 100.0, 1000.0, 10000.0, 100000.0, 100000" +
+                                    "0.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set19",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "v != 0 or n = 0 or n != 1 and n % 100 = 1.." +
+                                    "19 @integer 0, 2~16, 101, 1001, … @decima" +
+                                    "l 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 10" +
+                                    "0000.0, 1000000.0, …",
+                                },
+                                {
+                                    "one",
+                                    "i = 1 and v = 0 @integer 1",
+                                },
+                                {
+                                    "other",
+                                    " @integer 20~35, 100, 1000, 10000, 100000, " +
+                                    "1000000, …",
+                                },
+                            },
+                        },
+                        {
+                            "set2",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "i = 0,1 @integer 0, 1 @decimal 0.0~1.5",
+                                },
+                                {
+                                    "other",
+                                    " @integer 2~17, 100, 1000, 10000, 100000, 1" +
+                                    "000000, … @decimal 2.0~3.5, 10.0, 100.0, " +
+                                    "1000.0, 10000.0, 100000.0, 1000000.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set20",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "v = 0 and i % 10 = 2..4 and i % 100 != 12.." +
+                                    "14 or f % 10 = 2..4 and f % 100 != 12..14 @" +
+                                    "integer 2~4, 22~24, 32~34, 42~44, 52~54, 62" +
+                                    ", 102, 1002, … @decimal 0.2~0.4, 1.2~1.4," +
+                                    " 2.2~2.4, 3.2~3.4, 4.2~4.4, 5.2, 10.2, 100." +
+                                    "2, 1000.2, …",
+                                },
+                                {
+                                    "one",
+                                    "v = 0 and i % 10 = 1 and i % 100 != 11 or f" +
+                                    " % 10 = 1 and f % 100 != 11 @integer 1, 21," +
+                                    " 31, 41, 51, 61, 71, 81, 101, 1001, … @de" +
+                                    "cimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7." +
+                                    "1, 10.1, 100.1, 1000.1, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 5~19, 100, 1000, 10000, 100000" +
+                                    ", 1000000, … @decimal 0.0, 0.5~1.0, 1.5~2" +
+                                    ".0, 2.5~2.7, 10.0, 100.0, 1000.0, 10000.0, " +
+                                    "100000.0, 1000000.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set21",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n = 3..10,13..19 @integer 3~10, 13~19 @deci" +
+                                    "mal 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0" +
+                                    ", 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0," +
+                                    " 3.00",
+                                },
+                                {
+                                    "one",
+                                    "n = 1,11 @integer 1, 11 @decimal 1.0, 11.0," +
+                                    " 1.00, 11.00, 1.000, 11.000, 1.0000",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 20~34, 100, 1000, 10000, 10000" +
+                                    "0, 1000000, … @decimal 0.0~0.9, 1.1~1.6, " +
+                                    "10.1, 100.0, 1000.0, 10000.0, 100000.0, 100" +
+                                    "0000.0, …",
+                                },
+                                {
+                                    "two",
+                                    "n = 2,12 @integer 2, 12 @decimal 2.0, 12.0," +
+                                    " 2.00, 12.00, 2.000, 12.000, 2.0000",
+                                },
+                            },
+                        },
+                        {
+                            "set22",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "v = 0 and i % 100 = 3..4 or v != 0 @integer" +
+                                    " 3, 4, 103, 104, 203, 204, 303, 304, 403, 4" +
+                                    "04, 503, 504, 603, 604, 703, 704, 1003, …" +
+                                    " @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 100" +
+                                    "00.0, 100000.0, 1000000.0, …",
+                                },
+                                {
+                                    "one",
+                                    "v = 0 and i % 100 = 1 @integer 1, 101, 201," +
+                                    " 301, 401, 501, 601, 701, 1001, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 5~19, 100, 1000, 10000, 100000" +
+                                    ", 1000000, …",
+                                },
+                                {
+                                    "two",
+                                    "v = 0 and i % 100 = 2 @integer 2, 102, 202," +
+                                    " 302, 402, 502, 602, 702, 1002, …",
+                                },
+                            },
+                        },
+                        {
+                            "set23",
+                            new Object[][]{
+                                {
+                                    "many",
+                                    "v = 0 and n != 0..10 and n % 10 = 0 @intege" +
+                                    "r 20, 30, 40, 50, 60, 70, 80, 90, 100, 1000" +
+                                    ", 10000, 100000, 1000000, …",
+                                },
+                                {
+                                    "one",
+                                    "i = 1 and v = 0 @integer 1",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 3~17, 101, 1001, … @decimal " +
+                                    "0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 1000" +
+                                    "00.0, 1000000.0, …",
+                                },
+                                {
+                                    "two",
+                                    "i = 2 and v = 0 @integer 2",
+                                },
+                            },
+                        },
+                        {
+                            "set24",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "i = 2..4 and v = 0 @integer 2~4",
+                                },
+                                {
+                                    "many",
+                                    "v != 0   @decimal 0.0~1.5, 10.0, 100.0, 100" +
+                                    "0.0, 10000.0, 100000.0, 1000000.0, …",
+                                },
+                                {
+                                    "one",
+                                    "i = 1 and v = 0 @integer 1",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 5~19, 100, 1000, 10000, 100000" +
+                                    ", 1000000, …",
+                                },
+                            },
+                        },
+                        {
+                            "set25",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "v = 0 and i % 10 = 2..4 and i % 100 != 12.." +
+                                    "14 @integer 2~4, 22~24, 32~34, 42~44, 52~54" +
+                                    ", 62, 102, 1002, …",
+                                },
+                                {
+                                    "many",
+                                    "v = 0 and i != 1 and i % 10 = 0..1 or v = 0" +
+                                    " and i % 10 = 5..9 or v = 0 and i % 100 = 1" +
+                                    "2..14 @integer 0, 5~19, 100, 1000, 10000, 1" +
+                                    "00000, 1000000, …",
+                                },
+                                {
+                                    "one",
+                                    "i = 1 and v = 0 @integer 1",
+                                },
+                                {
+                                    "other",
+                                    "   @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 1" +
+                                    "0000.0, 100000.0, 1000000.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set26",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n % 10 = 2..4 and n % 100 != 12..14 @intege" +
+                                    "r 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102," +
+                                    " 1002, … @decimal 2.0, 3.0, 4.0, 22.0, 23" +
+                                    ".0, 24.0, 32.0, 33.0, 102.0, 1002.0, …",
+                                },
+                                {
+                                    "many",
+                                    "n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11" +
+                                    "..14 @integer 0, 5~19, 100, 1000, 10000, 10" +
+                                    "0000, 1000000, … @decimal 0.0, 5.0, 6.0, " +
+                                    "7.0, 8.0, 9.0, 10.0, 11.0, 100.0, 1000.0, 1" +
+                                    "0000.0, 100000.0, 1000000.0, …",
+                                },
+                                {
+                                    "one",
+                                    "n % 10 = 1 and n % 100 != 11 @integer 1, 21" +
+                                    ", 31, 41, 51, 61, 71, 81, 101, 1001, … @d" +
+                                    "ecimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 7" +
+                                    "1.0, 81.0, 101.0, 1001.0, …",
+                                },
+                                {
+                                    "other",
+                                    "   @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.1, " +
+                                    "1000.1, …",
+                                },
+                            },
+                        },
+                        {
+                            "set27",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n % 10 = 2..9 and n % 100 != 11..19 @intege" +
+                                    "r 2~9, 22~29, 102, 1002, … @decimal 2.0, " +
+                                    "3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 22.0, 10" +
+                                    "2.0, 1002.0, …",
+                                },
+                                {
+                                    "many",
+                                    "f != 0   @decimal 0.1~0.9, 1.1~1.7, 10.1, 1" +
+                                    "00.1, 1000.1, …",
+                                },
+                                {
+                                    "one",
+                                    "n % 10 = 1 and n % 100 != 11..19 @integer 1" +
+                                    ", 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61." +
+                                    "0, 71.0, 81.0, 101.0, 1001.0, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 10~20, 30, 40, 50, 60, 100, 10" +
+                                    "00, 10000, 100000, 1000000, … @decimal 0." +
+                                    "0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0" +
+                                    ", 100.0, 1000.0, 10000.0, 100000.0, 1000000" +
+                                    ".0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set28",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n = 0 or n % 100 = 2..10 @integer 0, 2~10, " +
+                                    "102~107, 1002, … @decimal 0.0, 2.0, 3.0, " +
+                                    "4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 102.0, 1002." +
+                                    "0, …",
+                                },
+                                {
+                                    "many",
+                                    "n % 100 = 11..19 @integer 11~19, 111~117, 1" +
+                                    "011, … @decimal 11.0, 12.0, 13.0, 14.0, 1" +
+                                    "5.0, 16.0, 17.0, 18.0, 111.0, 1011.0, …",
+                                },
+                                {
+                                    "one",
+                                    "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000," +
+                                    " 1.0000",
+                                },
+                                {
+                                    "other",
+                                    " @integer 20~35, 100, 1000, 10000, 100000, " +
+                                    "1000000, … @decimal 0.1~0.9, 1.1~1.7, 10." +
+                                    "1, 100.0, 1000.0, 10000.0, 100000.0, 100000" +
+                                    "0.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set29",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "v = 0 and i % 10 = 2..4 and i % 100 != 12.." +
+                                    "14 @integer 2~4, 22~24, 32~34, 42~44, 52~54" +
+                                    ", 62, 102, 1002, …",
+                                },
+                                {
+                                    "many",
+                                    "v = 0 and i % 10 = 0 or v = 0 and i % 10 = " +
+                                    "5..9 or v = 0 and i % 100 = 11..14 @integer" +
+                                    " 0, 5~19, 100, 1000, 10000, 100000, 1000000" +
+                                    ", …",
+                                },
+                                {
+                                    "one",
+                                    "v = 0 and i % 10 = 1 and i % 100 != 11 @int" +
+                                    "eger 1, 21, 31, 41, 51, 61, 71, 81, 101, 10" +
+                                    "01, …",
+                                },
+                                {
+                                    "other",
+                                    "   @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 1" +
+                                    "0000.0, 100000.0, 1000000.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set3",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "i = 1 and v = 0 @integer 1",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 2~16, 100, 1000, 10000, 100000" +
+                                    ", 1000000, … @decimal 0.0~1.5, 10.0, 100." +
+                                    "0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set30",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n % 10 = 3..4,9 and n % 100 != 10..19,70..7" +
+                                    "9,90..99 @integer 3, 4, 9, 23, 24, 29, 33, " +
+                                    "34, 39, 43, 44, 49, 103, 1003, … @decimal" +
+                                    " 3.0, 4.0, 9.0, 23.0, 24.0, 29.0, 33.0, 34." +
+                                    "0, 103.0, 1003.0, …",
+                                },
+                                {
+                                    "many",
+                                    "n != 0 and n % 1000000 = 0 @integer 1000000" +
+                                    ", … @decimal 1000000.0, 1000000.00, 10000" +
+                                    "00.000, …",
+                                },
+                                {
+                                    "one",
+                                    "n % 10 = 1 and n % 100 != 11,71,91 @integer" +
+                                    " 1, 21, 31, 41, 51, 61, 81, 101, 1001, … " +
+                                    "@decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0," +
+                                    " 81.0, 101.0, 1001.0, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 5~8, 10~20, 100, 1000, 10000, " +
+                                    "100000, … @decimal 0.0~0.9, 1.1~1.6, 10.0" +
+                                    ", 100.0, 1000.0, 10000.0, 100000.0, …",
+                                },
+                                {
+                                    "two",
+                                    "n % 10 = 2 and n % 100 != 12,72,92 @integer" +
+                                    " 2, 22, 32, 42, 52, 62, 82, 102, 1002, … " +
+                                    "@decimal 2.0, 22.0, 32.0, 42.0, 52.0, 62.0," +
+                                    " 82.0, 102.0, 1002.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set31",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n = 3..6 @integer 3~6 @decimal 3.0, 4.0, 5." +
+                                    "0, 6.0, 3.00, 4.00, 5.00, 6.00, 3.000, 4.00" +
+                                    "0, 5.000, 6.000, 3.0000, 4.0000, 5.0000, 6." +
+                                    "0000",
+                                },
+                                {
+                                    "many",
+                                    "n = 7..10 @integer 7~10 @decimal 7.0, 8.0, " +
+                                    "9.0, 10.0, 7.00, 8.00, 9.00, 10.00, 7.000, " +
+                                    "8.000, 9.000, 10.000, 7.0000, 8.0000, 9.000" +
+                                    "0, 10.0000",
+                                },
+                                {
+                                    "one",
+                                    "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000," +
+                                    " 1.0000",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 11~25, 100, 1000, 10000, 10000" +
+                                    "0, 1000000, … @decimal 0.0~0.9, 1.1~1.6, " +
+                                    "10.1, 100.0, 1000.0, 10000.0, 100000.0, 100" +
+                                    "0000.0, …",
+                                },
+                                {
+                                    "two",
+                                    "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000," +
+                                    " 2.0000",
+                                },
+                            },
+                        },
+                        {
+                            "set32",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "v = 0 and i % 100 = 0,20,40,60,80 @integer " +
+                                    "0, 20, 40, 60, 80, 100, 120, 140, 1000, 100" +
+                                    "00, 100000, 1000000, …",
+                                },
+                                {
+                                    "many",
+                                    "v != 0   @decimal 0.0~1.5, 10.0, 100.0, 100" +
+                                    "0.0, 10000.0, 100000.0, 1000000.0, …",
+                                },
+                                {
+                                    "one",
+                                    "v = 0 and i % 10 = 1 @integer 1, 11, 21, 31" +
+                                    ", 41, 51, 61, 71, 101, 1001, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 3~10, 13~19, 23, 103, 1003, …",
+                                },
+                                {
+                                    "two",
+                                    "v = 0 and i % 10 = 2 @integer 2, 12, 22, 32" +
+                                    ", 42, 52, 62, 72, 102, 1002, …",
+                                },
+                            },
+                        },
+                        {
+                            "set33",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n % 100 = 3..10 @integer 3~10, 103~110, 100" +
+                                    "3, … @decimal 3.0, 4.0, 5.0, 6.0, 7.0, 8." +
+                                    "0, 9.0, 10.0, 103.0, 1003.0, …",
+                                },
+                                {
+                                    "many",
+                                    "n % 100 = 11..99 @integer 11~26, 111, 1011," +
+                                    " … @decimal 11.0, 12.0, 13.0, 14.0, 15.0," +
+                                    " 16.0, 17.0, 18.0, 111.0, 1011.0, …",
+                                },
+                                {
+                                    "one",
+                                    "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000," +
+                                    " 1.0000",
+                                },
+                                {
+                                    "other",
+                                    " @integer 100~102, 200~202, 300~302, 400~40" +
+                                    "2, 500~502, 600, 1000, 10000, 100000, 10000" +
+                                    "00, … @decimal 0.1~0.9, 1.1~1.7, 10.1, 10" +
+                                    "0.0, 1000.0, 10000.0, 100000.0, 1000000.0, " +
+                                    "…",
+                                },
+                                {
+                                    "two",
+                                    "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000," +
+                                    " 2.0000",
+                                },
+                                {
+                                    "zero",
+                                    "n = 0 @integer 0 @decimal 0.0, 0.00, 0.000," +
+                                    " 0.0000",
+                                },
+                            },
+                        },
+                        {
+                            "set34",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n = 3 @integer 3 @decimal 3.0, 3.00, 3.000," +
+                                    " 3.0000",
+                                },
+                                {
+                                    "many",
+                                    "n = 6 @integer 6 @decimal 6.0, 6.00, 6.000," +
+                                    " 6.0000",
+                                },
+                                {
+                                    "one",
+                                    "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000," +
+                                    " 1.0000",
+                                },
+                                {
+                                    "other",
+                                    " @integer 4, 5, 7~20, 100, 1000, 10000, 100" +
+                                    "000, 1000000, … @decimal 0.1~0.9, 1.1~1.7" +
+                                    ", 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1" +
+                                    "000000.0, …",
+                                },
+                                {
+                                    "two",
+                                    "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000," +
+                                    " 2.0000",
+                                },
+                                {
+                                    "zero",
+                                    "n = 0 @integer 0 @decimal 0.0, 0.00, 0.000," +
+                                    " 0.0000",
+                                },
+                            },
+                        },
+                        {
+                            "set35",
+                            new Object[][]{
+                                {
+                                    "other",
+                                    " @integer 0~15, 100, 1000, 10000, 100000, 1" +
+                                    "000000, …",
+                                },
+                            },
+                        },
+                        {
+                            "set36",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "n % 10 = 1,2 and n % 100 != 11,12 @integer " +
+                                    "1, 2, 21, 22, 31, 32, 41, 42, 51, 52, 61, 6" +
+                                    "2, 71, 72, 81, 82, 101, 1001, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 3~17, 100, 1000, 10000, 100000" +
+                                    ", 1000000, …",
+                                },
+                            },
+                        },
+                        {
+                            "set37",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "n = 1 @integer 1",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 2~16, 100, 1000, 10000, 100000" +
+                                    ", 1000000, …",
+                                },
+                            },
+                        },
+                        {
+                            "set38",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "n = 1,5 @integer 1, 5",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 2~4, 6~17, 100, 1000, 10000, 1" +
+                                    "00000, 1000000, …",
+                                },
+                            },
+                        },
+                        {
+                            "set39",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "n = 1..4 @integer 1~4",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 5~19, 100, 1000, 10000, 100000" +
+                                    ", 1000000, …",
+                                },
+                            },
+                        },
+                        {
+                            "set4",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "i = 1 and v = 0 or i = 0 and t = 1 @integer" +
+                                    " 1 @decimal 0.1, 0.01, 0.10, 0.001, 0.010, " +
+                                    "0.100, 0.0001, 0.0010, 0.0100, 0.1000",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 2~16, 100, 1000, 10000, 100000" +
+                                    ", 1000000, … @decimal 0.0, 0.2~1.6, 10.0," +
+                                    " 100.0, 1000.0, 10000.0, 100000.0, 1000000." +
+                                    "0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set40",
+                            new Object[][]{
+                                {
+                                    "many",
+                                    "n % 10 = 6 or n % 10 = 9 or n % 10 = 0 and " +
+                                    "n != 0 @integer 6, 9, 10, 16, 19, 20, 26, 2" +
+                                    "9, 30, 36, 39, 40, 100, 1000, 10000, 100000" +
+                                    ", 1000000, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0~5, 7, 8, 11~15, 17, 18, 21, 101" +
+                                    ", 1001, …",
+                                },
+                            },
+                        },
+                        {
+                            "set41",
+                            new Object[][]{
+                                {
+                                    "many",
+                                    "n = 11,8,80,800 @integer 8, 11, 80, 800",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0~7, 9, 10, 12~17, 100, 1000, 100" +
+                                    "00, 100000, 1000000, …",
+                                },
+                            },
+                        },
+                        {
+                            "set42",
+                            new Object[][]{
+                                {
+                                    "many",
+                                    "i = 0 or i % 100 = 2..20,40,60,80 @integer " +
+                                    "0, 2~16, 102, 1002, …",
+                                },
+                                {
+                                    "one",
+                                    "i = 1 @integer 1",
+                                },
+                                {
+                                    "other",
+                                    " @integer 21~36, 100, 1000, 10000, 100000, " +
+                                    "1000000, …",
+                                },
+                            },
+                        },
+                        {
+                            "set43",
+                            new Object[][]{
+                                {
+                                    "many",
+                                    "n % 10 = 4 and n % 100 != 14 @integer 4, 24" +
+                                    ", 34, 44, 54, 64, 74, 84, 104, 1004, …",
+                                },
+                                {
+                                    "one",
+                                    "n = 1 @integer 1",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 2, 3, 5~17, 100, 1000, 10000, " +
+                                    "100000, 1000000, …",
+                                },
+                            },
+                        },
+                        {
+                            "set44",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n % 10 = 3 and n % 100 != 13 @integer 3, 23" +
+                                    ", 33, 43, 53, 63, 73, 83, 103, 1003, …",
+                                },
+                                {
+                                    "one",
+                                    "n % 10 = 1 and n % 100 != 11 @integer 1, 21" +
+                                    ", 31, 41, 51, 61, 71, 81, 101, 1001, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 4~18, 100, 1000, 10000, 100000" +
+                                    ", 1000000, …",
+                                },
+                                {
+                                    "two",
+                                    "n % 10 = 2 and n % 100 != 12 @integer 2, 22" +
+                                    ", 32, 42, 52, 62, 72, 82, 102, 1002, …",
+                                },
+                            },
+                        },
+                        {
+                            "set45",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n = 4 @integer 4",
+                                },
+                                {
+                                    "one",
+                                    "n = 1 @integer 1",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 5~19, 100, 1000, 10000, 100000" +
+                                    ", 1000000, …",
+                                },
+                                {
+                                    "two",
+                                    "n = 2,3 @integer 2, 3",
+                                },
+                            },
+                        },
+                        {
+                            "set46",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n = 4 @integer 4",
+                                },
+                                {
+                                    "one",
+                                    "n = 1,3 @integer 1, 3",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 5~19, 100, 1000, 10000, 100000" +
+                                    ", 1000000, …",
+                                },
+                                {
+                                    "two",
+                                    "n = 2 @integer 2",
+                                },
+                            },
+                        },
+                        {
+                            "set47",
+                            new Object[][]{
+                                {
+                                    "many",
+                                    "i % 10 = 7,8 and i % 100 != 17,18 @integer " +
+                                    "7, 8, 27, 28, 37, 38, 47, 48, 57, 58, 67, 6" +
+                                    "8, 77, 78, 87, 88, 107, 1007, …",
+                                },
+                                {
+                                    "one",
+                                    "i % 10 = 1 and i % 100 != 11 @integer 1, 21" +
+                                    ", 31, 41, 51, 61, 71, 81, 101, 1001, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 3~6, 9~19, 100, 1000, 10000, 1" +
+                                    "00000, 1000000, …",
+                                },
+                                {
+                                    "two",
+                                    "i % 10 = 2 and i % 100 != 12 @integer 2, 22" +
+                                    ", 32, 42, 52, 62, 72, 82, 102, 1002, …",
+                                },
+                            },
+                        },
+                        {
+                            "set48",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "i % 10 = 3,4 or i % 1000 = 100,200,300,400," +
+                                    "500,600,700,800,900 @integer 3, 4, 13, 14, " +
+                                    "23, 24, 33, 34, 43, 44, 53, 54, 63, 64, 73," +
+                                    " 74, 100, 1003, …",
+                                },
+                                {
+                                    "many",
+                                    "i = 0 or i % 10 = 6 or i % 100 = 40,60,90 @" +
+                                    "integer 0, 6, 16, 26, 36, 40, 46, 56, 106, " +
+                                    "1006, …",
+                                },
+                                {
+                                    "one",
+                                    "i % 10 = 1,2,5,7,8 or i % 100 = 20,50,70,80" +
+                                    " @integer 1, 2, 5, 7, 8, 11, 12, 15, 17, 18" +
+                                    ", 20~22, 25, 101, 1001, …",
+                                },
+                                {
+                                    "other",
+                                    " @integer 9, 10, 19, 29, 30, 39, 49, 59, 69" +
+                                    ", 79, 109, 1000, 10000, 100000, 1000000, …",
+                                },
+                            },
+                        },
+                        {
+                            "set49",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n = 4 @integer 4",
+                                },
+                                {
+                                    "many",
+                                    "n = 6 @integer 6",
+                                },
+                                {
+                                    "one",
+                                    "n = 1 @integer 1",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 5, 7~20, 100, 1000, 10000, 100" +
+                                    "000, 1000000, …",
+                                },
+                                {
+                                    "two",
+                                    "n = 2,3 @integer 2, 3",
+                                },
+                            },
+                        },
+                        {
+                            "set5",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "n = 0,1 or i = 0 and f = 1 @integer 0, 1 @d" +
+                                    "ecimal 0.0, 0.1, 1.0, 0.00, 0.01, 1.00, 0.0" +
+                                    "00, 0.001, 1.000, 0.0000, 0.0001, 1.0000",
+                                },
+                                {
+                                    "other",
+                                    " @integer 2~17, 100, 1000, 10000, 100000, 1" +
+                                    "000000, … @decimal 0.2~0.9, 1.1~1.8, 10.0" +
+                                    ", 100.0, 1000.0, 10000.0, 100000.0, 1000000" +
+                                    ".0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set50",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n = 4 @integer 4",
+                                },
+                                {
+                                    "many",
+                                    "n = 6 @integer 6",
+                                },
+                                {
+                                    "one",
+                                    "n = 1,5,7,8,9,10 @integer 1, 5, 7~10",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 11~25, 100, 1000, 10000, 10000" +
+                                    "0, 1000000, …",
+                                },
+                                {
+                                    "two",
+                                    "n = 2,3 @integer 2, 3",
+                                },
+                            },
+                        },
+                        {
+                            "set51",
+                            new Object[][]{
+                                {
+                                    "few",
+                                    "n = 3,4 @integer 3, 4",
+                                },
+                                {
+                                    "many",
+                                    "n = 5,6 @integer 5, 6",
+                                },
+                                {
+                                    "one",
+                                    "n = 1 @integer 1",
+                                },
+                                {
+                                    "other",
+                                    " @integer 10~25, 100, 1000, 10000, 100000, " +
+                                    "1000000, …",
+                                },
+                                {
+                                    "two",
+                                    "n = 2 @integer 2",
+                                },
+                                {
+                                    "zero",
+                                    "n = 0,7,8,9 @integer 0, 7~9",
+                                },
+                            },
+                        },
+                        {
+                            "set6",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0" +
+                                    ".00, 1.00, 0.000, 1.000, 0.0000, 1.0000",
+                                },
+                                {
+                                    "other",
+                                    " @integer 2~17, 100, 1000, 10000, 100000, 1" +
+                                    "000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0" +
+                                    ", 100.0, 1000.0, 10000.0, 100000.0, 1000000" +
+                                    ".0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set7",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "n = 0..1 or n = 11..99 @integer 0, 1, 11~24" +
+                                    " @decimal 0.0, 1.0, 11.0, 12.0, 13.0, 14.0," +
+                                    " 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, " +
+                                    "22.0, 23.0, 24.0",
+                                },
+                                {
+                                    "other",
+                                    " @integer 2~10, 100~106, 1000, 10000, 10000" +
+                                    "0, 1000000, … @decimal 0.1~0.9, 1.1~1.7, " +
+                                    "10.0, 100.0, 1000.0, 10000.0, 100000.0, 100" +
+                                    "0000.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set8",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000," +
+                                    " 1.0000",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 2~16, 100, 1000, 10000, 100000" +
+                                    ", 1000000, … @decimal 0.0~0.9, 1.1~1.6, 1" +
+                                    "0.0, 100.0, 1000.0, 10000.0, 100000.0, 1000" +
+                                    "000.0, …",
+                                },
+                            },
+                        },
+                        {
+                            "set9",
+                            new Object[][]{
+                                {
+                                    "one",
+                                    "n = 1 and v = 0 @integer 1",
+                                },
+                                {
+                                    "other",
+                                    " @integer 0, 2~16, 100, 1000, 10000, 100000" +
+                                    ", 1000000, … @decimal 0.0~1.5, 10.0, 100." +
+                                    "0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
+                                },
+                            },
+                        },
+                    },
+                },
+    };
+}
diff --git a/src/main/com/android/i18n/MessageFormat.java b/src/com/ibm/icu/simple/MessageFormat.java
similarity index 90%
rename from src/main/com/android/i18n/MessageFormat.java
rename to src/com/ibm/icu/simple/MessageFormat.java
index 98510fc..0a883dd 100644
--- a/src/main/com/android/i18n/MessageFormat.java
+++ b/src/com/ibm/icu/simple/MessageFormat.java
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-* Copyright (c) 2004-2013, International Business Machines
+* Copyright (c) 2004-2014, International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Alan Liu
@@ -8,38 +8,40 @@
 * Since: ICU 3.0
 **********************************************************************
 */
-package com.ibm.icu.text;
+package com.ibm.icu.simple;
 
 import java.io.IOException;
 import java.io.InvalidObjectException;
-import java.io.ObjectInputStream;
 import java.text.AttributedCharacterIterator;
 import java.text.AttributedCharacterIterator.Attribute;
 import java.text.AttributedString;
 import java.text.CharacterIterator;
 import java.text.ChoiceFormat;
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
 import java.text.FieldPosition;
 import java.text.Format;
+import java.text.NumberFormat;
 import java.text.ParseException;
 import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
 import com.ibm.icu.impl.PatternProps;
-import com.ibm.icu.impl.Utility;
+import com.ibm.icu.simple.PluralRules.PluralType;
+import com.ibm.icu.text.MessagePattern;
 import com.ibm.icu.text.MessagePattern.ArgType;
 import com.ibm.icu.text.MessagePattern.Part;
-import com.ibm.icu.text.PluralRules.FixedDecimal;
-import com.ibm.icu.text.PluralRules.PluralType;
-import com.ibm.icu.util.ULocale;
-import com.ibm.icu.util.ULocale.Category;
+import com.ibm.icu.text.SelectFormat;
+import com.ibm.icu.util.ICUUncheckedIOException;
 
 /**
  * {@icuenhanced java.text.MessageFormat}.{@icu _usage_}
@@ -330,12 +332,28 @@
  * @author       Markus Scherer
  * @stable ICU 3.0
  */
-public class MessageFormat extends UFormat {
+public class MessageFormat extends Format {
 
     // Incremented by 1 for ICU 4.8's new format.
     static final long serialVersionUID = 7136212545847378652L;
 
     /**
+     * Formats a message pattern string with a variable number of name/value pair arguments.
+     * Creates an ICU MessageFormat for the locale and pattern,
+     * and formats with the arguments.
+     *
+     * @param locale Locale for number formatting and plural selection etc.
+     * @param msg an ICU-MessageFormat-syntax string
+     * @param nameValuePairs (argument name, argument value) pairs
+     */
+    public static final String formatNamedArgs(Locale locale, String msg, Object... nameValuePairs) {
+        StringBuilder result = new StringBuilder(msg.length());
+        new MessageFormat(msg, locale).format(0, null, null, null, nameValuePairs,
+                new AppendableWrapper(result), null);
+        return result.toString();
+    }
+
+    /**
      * Constructs a MessageFormat for the default <code>FORMAT</code> locale and the
      * specified pattern.
      * Sets the locale and calls applyPattern(pattern).
@@ -346,7 +364,7 @@
      * @stable ICU 3.0
      */
     public MessageFormat(String pattern) {
-        this.ulocale = ULocale.getDefault(Category.FORMAT);
+        locale_ = Locale.getDefault();  // Category.FORMAT
         applyPattern(pattern);
     }
 
@@ -361,82 +379,21 @@
      * @stable ICU 3.0
      */
     public MessageFormat(String pattern, Locale locale) {
-        this(pattern, ULocale.forLocale(locale));
-    }
-
-    /**
-     * Constructs a MessageFormat for the specified locale and
-     * pattern.
-     * Sets the locale and calls applyPattern(pattern).
-     *
-     * @param pattern the pattern for this message format
-     * @param locale the locale for this message format
-     * @exception IllegalArgumentException if the pattern is invalid
-     * @stable ICU 3.2
-     */
-    public MessageFormat(String pattern, ULocale locale) {
-        this.ulocale = locale;
+        locale_ = locale;
         applyPattern(pattern);
     }
 
     /**
-     * Sets the locale to be used for creating argument Format objects.
-     * This affects subsequent calls to the {@link #applyPattern applyPattern}
-     * method as well as to the <code>format</code> and
-     * {@link #formatToCharacterIterator formatToCharacterIterator} methods.
-     *
-     * @param locale the locale to be used when creating or comparing subformats
-     * @stable ICU 3.0
-     */
-    public void setLocale(Locale locale) {
-        setLocale(ULocale.forLocale(locale));
-    }
-
-    /**
-     * Sets the locale to be used for creating argument Format objects.
-     * This affects subsequent calls to the {@link #applyPattern applyPattern}
-     * method as well as to the <code>format</code> and
-     * {@link #formatToCharacterIterator formatToCharacterIterator} methods.
-     *
-     * @param locale the locale to be used when creating or comparing subformats
-     * @stable ICU 3.2
-     */
-    public void setLocale(ULocale locale) {
-        /* Save the pattern, and then reapply so that */
-        /* we pick up any changes in locale specific */
-        /* elements */
-        String existingPattern = toPattern();                       /*ibm.3550*/
-        this.ulocale = locale;
-        // Invalidate all stock formatters. They are no longer valid since
-        // the locale has changed.
-        stockDateFormatter = null;
-        stockNumberFormatter = null;
-        pluralProvider = null;
-        ordinalProvider = null;
-        applyPattern(existingPattern);                              /*ibm.3550*/
-    }
-
-    /**
      * Returns the locale that's used when creating or comparing subformats.
      *
      * @return the locale used when creating or comparing subformats
      * @stable ICU 3.0
      */
     public Locale getLocale() {
-        return ulocale.toLocale();
+        return locale_;
     }
 
     /**
-     * {@icu} Returns the locale that's used when creating argument Format objects.
-     *
-     * @return the locale used when creating or comparing subformats
-     * @stable ICU 3.2
-     */
-    public ULocale getULocale() {
-        return ulocale;
-    }
-    
-    /**
      * Sets the pattern used by this message format.
      * Parses the pattern and caches Format objects for simple argument types.
      * Patterns and their interpretation are specified in the
@@ -1414,46 +1371,6 @@
     /**
      * {@inheritDoc}
      * @stable ICU 3.0
-     */
-    @Override
-    public Object clone() {
-        MessageFormat other = (MessageFormat) super.clone();
-
-        if (customFormatArgStarts != null) {
-            other.customFormatArgStarts = new HashSet<Integer>();
-            for (Integer key : customFormatArgStarts) {
-                other.customFormatArgStarts.add(key);
-            }
-        } else {
-            other.customFormatArgStarts = null;
-        }
-        
-        if (cachedFormatters != null) {
-            other.cachedFormatters = new HashMap<Integer, Format>();
-            Iterator<Map.Entry<Integer, Format>> it = cachedFormatters.entrySet().iterator();
-            while (it.hasNext()){
-                Map.Entry<Integer, Format> entry = it.next();
-                other.cachedFormatters.put(entry.getKey(), entry.getValue());
-            }
-        } else {
-            other.cachedFormatters = null;
-        }
-        
-        other.msgPattern = msgPattern == null ? null : (MessagePattern)msgPattern.clone();
-        other.stockDateFormatter =
-                stockDateFormatter == null ? null : (DateFormat) stockDateFormatter.clone();
-        other.stockNumberFormatter =
-                stockNumberFormatter == null ? null : (NumberFormat) stockNumberFormatter.clone();
-
-        other.pluralProvider = null;
-        other.ordinalProvider = null;
-        return other;
-    }
-
-    /**
-     * {@inheritDoc}
-     * @stable ICU 3.0
-     */
     @Override
     public boolean equals(Object obj) {
         if (this == obj)                      // quick check
@@ -1468,6 +1385,7 @@
         // Note: It might suffice to only compare custom formatters
         // rather than all formatters.
     }
+     */
 
     /**
      * {@inheritDoc}
@@ -1541,7 +1459,7 @@
     /**
      * The locale to use for formatting numbers and dates.
      */
-    private transient ULocale ulocale;
+    private transient Locale locale_;
 
     /**
      * The MessagePattern which contains the parsed structure of the pattern string.
@@ -1571,13 +1489,13 @@
     private DateFormat getStockDateFormatter() {
         if (stockDateFormatter == null) {
             stockDateFormatter = DateFormat.getDateTimeInstance(
-                    DateFormat.SHORT, DateFormat.SHORT, ulocale);//fix
+                    DateFormat.SHORT, DateFormat.SHORT, locale_);//fix
         }
         return stockDateFormatter;
     }
     private NumberFormat getStockNumberFormatter() {
         if (stockNumberFormatter == null) {
-            stockNumberFormatter = NumberFormat.getInstance(ulocale);
+            stockNumberFormatter = NumberFormat.getInstance(locale_);
         }
         return stockNumberFormatter;
     }
@@ -1601,7 +1519,7 @@
      * @param fp            Field position status.
      */
     private void format(int msgStart, PluralSelectorContext pluralNumber,
-                        Object[] args, Map<String, Object> argsMap,
+                        Object[] args, Map<String, Object> argsMap, Object[] nameValuePairs,
                         AppendableWrapper dest, FieldPosition fp) {
         String msgString=msgPattern.getPatternString();
         int prevIndex=msgPattern.getPart(msgStart).getLimit();
@@ -1646,6 +1564,20 @@
                     arg=null;
                     noArg=true;
                 }
+            } else if(nameValuePairs!=null) {
+                argId = argName;
+                for(int nvIndex=0;; nvIndex+=2) {
+                    if(nvIndex<nameValuePairs.length) {
+                        if(argName.equals(nameValuePairs[nvIndex].toString())) {
+                            arg=nameValuePairs[nvIndex+1];
+                            break;
+                        }
+                    } else {
+                        arg=null;
+                        noArg=true;
+                        break;
+                    }
+                }
             } else {
                 argId = argName;
                 if(argsMap!=null && argsMap.containsKey(argName)) {
@@ -1673,28 +1605,7 @@
                 }
             } else if(cachedFormatters!=null && (formatter=cachedFormatters.get(i - 2))!=null) {
                 // Handles all ArgType.SIMPLE, and formatters from setFormat() and its siblings.
-                if (    formatter instanceof ChoiceFormat ||
-                        formatter instanceof PluralFormat ||
-                        formatter instanceof SelectFormat) {
-                    // We only handle nested formats here if they were provided via setFormat() or its siblings.
-                    // Otherwise they are not cached and instead handled below according to argType.
-                    String subMsgString = formatter.format(arg);
-                    if (subMsgString.indexOf('{') >= 0 ||
-                            (subMsgString.indexOf('\'') >= 0 && !msgPattern.jdkAposMode())) {
-                        MessageFormat subMsgFormat = new MessageFormat(subMsgString, ulocale);
-                        subMsgFormat.format(0, null, args, argsMap, dest, null);
-                    } else if (dest.attributes == null) {
-                        dest.append(subMsgString);
-                    } else {
-                        // This formats the argument twice, once above to get the subMsgString
-                        // and then once more here.
-                        // It only happens in formatToCharacterIterator()
-                        // on a complex Format set via setFormat(),
-                        // and only when the selected subMsgString does not need further formatting.
-                        // This imitates ICU 4.6 behavior.
-                        dest.formatAndAppend(formatter, arg);
-                    }
-                } else {
+                {
                     dest.formatAndAppend(formatter, arg);
                 }
             } else if(
@@ -1717,7 +1628,7 @@
                 }
                 double number = ((Number)arg).doubleValue();
                 int subMsgStart=findChoiceSubMessage(msgPattern, i, number);
-                formatComplexSubMessage(subMsgStart, null, args, argsMap, dest);
+                formatComplexSubMessage(subMsgStart, null, args, argsMap, nameValuePairs, dest);
             } else if(argType.hasPluralStyle()) {
                 if (!(arg instanceof Number)) {
                     throw new IllegalArgumentException("'" + arg + "' is not a Number");
@@ -1740,10 +1651,10 @@
                         new PluralSelectorContext(i, argName, number, offset);
                 int subMsgStart=PluralFormat.findSubMessage(
                         msgPattern, i, selector, context, number.doubleValue());
-                formatComplexSubMessage(subMsgStart, context, args, argsMap, dest);
+                formatComplexSubMessage(subMsgStart, context, args, argsMap, nameValuePairs, dest);
             } else if(argType==ArgType.SELECT) {
                 int subMsgStart=SelectFormat.findSubMessage(msgPattern, i, arg.toString());
-                formatComplexSubMessage(subMsgStart, null, args, argsMap, dest);
+                formatComplexSubMessage(subMsgStart, null, args, argsMap, nameValuePairs, dest);
             } else {
                 // This should never happen.
                 throw new IllegalStateException("unexpected argType "+argType);
@@ -1756,13 +1667,15 @@
 
     private void formatComplexSubMessage(
             int msgStart, PluralSelectorContext pluralNumber,
-            Object[] args, Map<String, Object> argsMap,
+            Object[] args, Map<String, Object> argsMap, Object[] nameValuePairs,
             AppendableWrapper dest) {
         if (!msgPattern.jdkAposMode()) {
-            format(msgStart, pluralNumber, args, argsMap, dest, null);
+            format(msgStart, pluralNumber, args, argsMap, nameValuePairs, dest, null);
             return;
         }
         // JDK compatibility mode: (see JDK MessageFormat.format() API docs)
+        throw new UnsupportedOperationException("JDK apostrophe mode not supported");
+        /*
         // - remove SKIP_SYNTAX; that is, remove half of the apostrophes
         // - if the result string contains an open curly brace '{' then
         //   instantiate a temporary MessageFormat object and format again;
@@ -1815,6 +1728,7 @@
         } else {
             dest.append(subMsgString);
         }
+        */
     }
 
     /**
@@ -2080,7 +1994,7 @@
         }
         public String select(Object ctx, double number) {
             if(rules == null) {
-                rules = PluralRules.forLocale(msgFormat.ulocale, type);
+                rules = PluralRules.forLocale(msgFormat.locale_, type);
             }
             // Select a sub-message according to how the number is formatted,
             // which is specified in the selected sub-message.
@@ -2100,10 +2014,11 @@
             }
             assert context.number.doubleValue() == number;  // argument number minus the offset
             context.numberString = context.formatter.format(context.number);
+            /* TODO: Try to get FixedDecimal from formatted string.
             if(context.formatter instanceof DecimalFormat) {
                 FixedDecimal dec = ((DecimalFormat)context.formatter).getFixedDecimal(number);
                 return rules.select(dec);
-            } else {
+            } else */ {
                 return rules.select(number);
             }
         }
@@ -2135,7 +2050,7 @@
                 "This method is not available in MessageFormat objects " +
                 "that use alphanumeric argument names.");
         }
-        format(0, null, arguments, argsMap, dest, fp);
+        format(0, null, arguments, argsMap, null, dest, fp);
     }
 
     private void resetPattern() {
@@ -2186,67 +2101,68 @@
         case TYPE_NUMBER:
             switch (findKeyword(style, modifierList)) {
             case MODIFIER_EMPTY:
-                newFormat = NumberFormat.getInstance(ulocale);
+                newFormat = NumberFormat.getInstance(locale_);
                 break;
             case MODIFIER_CURRENCY:
-                newFormat = NumberFormat.getCurrencyInstance(ulocale);
+                newFormat = NumberFormat.getCurrencyInstance(locale_);
                 break;
             case MODIFIER_PERCENT:
-                newFormat = NumberFormat.getPercentInstance(ulocale);
+                newFormat = NumberFormat.getPercentInstance(locale_);
                 break;
             case MODIFIER_INTEGER:
-                newFormat = NumberFormat.getIntegerInstance(ulocale);
+                newFormat = NumberFormat.getIntegerInstance(locale_);
                 break;
             default: // pattern
                 newFormat = new DecimalFormat(style,
-                        new DecimalFormatSymbols(ulocale));
+                        new DecimalFormatSymbols(locale_));
                 break;
             }
             break;
         case TYPE_DATE:
             switch (findKeyword(style, dateModifierList)) {
             case DATE_MODIFIER_EMPTY:
-                newFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, ulocale);
+                newFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, locale_);
                 break;
             case DATE_MODIFIER_SHORT:
-                newFormat = DateFormat.getDateInstance(DateFormat.SHORT, ulocale);
+                newFormat = DateFormat.getDateInstance(DateFormat.SHORT, locale_);
                 break;
             case DATE_MODIFIER_MEDIUM:
-                newFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, ulocale);
+                newFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, locale_);
                 break;
             case DATE_MODIFIER_LONG:
-                newFormat = DateFormat.getDateInstance(DateFormat.LONG, ulocale);
+                newFormat = DateFormat.getDateInstance(DateFormat.LONG, locale_);
                 break;
             case DATE_MODIFIER_FULL:
-                newFormat = DateFormat.getDateInstance(DateFormat.FULL, ulocale);
+                newFormat = DateFormat.getDateInstance(DateFormat.FULL, locale_);
                 break;
             default:
-                newFormat = new SimpleDateFormat(style, ulocale);
+                newFormat = new SimpleDateFormat(style, locale_);
                 break;
             }
             break;
         case TYPE_TIME:
             switch (findKeyword(style, dateModifierList)) {
             case DATE_MODIFIER_EMPTY:
-                newFormat = DateFormat.getTimeInstance(DateFormat.DEFAULT, ulocale);
+                newFormat = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale_);
                 break;
             case DATE_MODIFIER_SHORT:
-                newFormat = DateFormat.getTimeInstance(DateFormat.SHORT, ulocale);
+                newFormat = DateFormat.getTimeInstance(DateFormat.SHORT, locale_);
                 break;
             case DATE_MODIFIER_MEDIUM:
-                newFormat = DateFormat.getTimeInstance(DateFormat.DEFAULT, ulocale);
+                newFormat = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale_);
                 break;
             case DATE_MODIFIER_LONG:
-                newFormat = DateFormat.getTimeInstance(DateFormat.LONG, ulocale);
+                newFormat = DateFormat.getTimeInstance(DateFormat.LONG, locale_);
                 break;
             case DATE_MODIFIER_FULL:
-                newFormat = DateFormat.getTimeInstance(DateFormat.FULL, ulocale);
+                newFormat = DateFormat.getTimeInstance(DateFormat.FULL, locale_);
                 break;
             default:
-                newFormat = new SimpleDateFormat(style, ulocale);
+                newFormat = new SimpleDateFormat(style, locale_);
                 break;
             }
             break;
+        /* There is no java.text.RuleBasedNumberFormat --
         case TYPE_SPELLOUT:
             {
                 RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(ulocale,
@@ -2295,6 +2211,7 @@
                 newFormat = rbnf;
             }
             break;
+        */
         default:
             throw new IllegalArgumentException("Unknown format type \"" + type + "\"");
         }
@@ -2312,80 +2229,6 @@
         return -1;
     }
 
-    /**
-     * Custom serialization, new in ICU 4.8.
-     * We do not want to use default serialization because we only have a small
-     * amount of persistent state which is better expressed explicitly
-     * rather than via writing field objects.
-     * @param out The output stream.
-     * @serialData Writes the locale as a BCP 47 language tag string,
-     * the MessagePattern.ApostropheMode as an object,
-     * and the pattern string (null if none was applied).
-     * Followed by an int with the number of (int formatIndex, Object formatter) pairs,
-     * and that many such pairs, corresponding to previous setFormat() calls for custom formats.
-     * Followed by an int with the number of (int, Object) pairs,
-     * and that many such pairs, for future (post-ICU 4.8) extension of the serialization format.
-     */
-    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
-        out.defaultWriteObject();
-        // ICU 4.8 custom serialization.
-        // locale as a BCP 47 language tag
-        out.writeObject(ulocale.toLanguageTag());
-        // ApostropheMode
-        if (msgPattern == null) {
-            msgPattern = new MessagePattern();
-        }
-        out.writeObject(msgPattern.getApostropheMode());
-        // message pattern string
-        out.writeObject(msgPattern.getPatternString());
-        // custom formatters
-        if (customFormatArgStarts == null || customFormatArgStarts.isEmpty()) {
-            out.writeInt(0);
-        } else {
-            out.writeInt(customFormatArgStarts.size());
-            int formatIndex = 0;
-            for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
-                if (customFormatArgStarts.contains(partIndex)) {
-                    out.writeInt(formatIndex);
-                    out.writeObject(cachedFormatters.get(partIndex));
-                }
-                ++formatIndex;
-            }
-        }
-        // number of future (int, Object) pairs
-        out.writeInt(0);
-    }
-
-    /**
-     * Custom deserialization, new in ICU 4.8. See comments on writeObject().
-     * @throws InvalidObjectException if the objects read from the stream is invalid.
-     */
-    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
-        in.defaultReadObject();
-        // ICU 4.8 custom deserialization.
-        String languageTag = (String)in.readObject();
-        ulocale = ULocale.forLanguageTag(languageTag);
-        MessagePattern.ApostropheMode aposMode = (MessagePattern.ApostropheMode)in.readObject();
-        if (msgPattern == null || aposMode != msgPattern.getApostropheMode()) {
-            msgPattern = new MessagePattern(aposMode);
-        }
-        String msg = (String)in.readObject();
-        if (msg != null) {
-            applyPattern(msg);
-        }
-        // custom formatters
-        for (int numFormatters = in.readInt(); numFormatters > 0; --numFormatters) {
-            int formatIndex = in.readInt();
-            Format formatter = (Format)in.readObject();
-            setFormat(formatIndex, formatter);
-        }
-        // skip future (int, Object) pairs
-        for (int numPairs = in.readInt(); numPairs > 0; --numPairs) {
-            in.readInt();
-            in.readObject();
-        }
-    }
-
     private void cacheExplicitFormats() {
         if (cachedFormatters != null) {
             cachedFormatters.clear();
@@ -2570,7 +2413,7 @@
                 app.append(s);
                 length += s.length();
             } catch(IOException e) {
-                throw new RuntimeException(e);
+                throw new ICUUncheckedIOException(e);
             }
         }
 
@@ -2579,7 +2422,7 @@
                 app.append(s, start, limit);
                 length += limit - start;
             } catch(IOException e) {
-                throw new RuntimeException(e);
+                throw new ICUUncheckedIOException(e);
             }
         }
 
@@ -2600,7 +2443,7 @@
                 }
                 return length;
             } catch(IOException e) {
-                throw new RuntimeException(e);
+                throw new ICUUncheckedIOException(e);
             }
         }
 
diff --git a/src/com/ibm/icu/simple/PluralFormat.java b/src/com/ibm/icu/simple/PluralFormat.java
new file mode 100644
index 0000000..67debd7
--- /dev/null
+++ b/src/com/ibm/icu/simple/PluralFormat.java
@@ -0,0 +1,474 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2007-2014, International Business Machines Corporation and
+ * others. All Rights Reserved.
+ *******************************************************************************
+ */
+
+package com.ibm.icu.simple;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+import java.util.Map;
+
+import com.ibm.icu.simple.PluralRules.FixedDecimal;
+import com.ibm.icu.simple.PluralRules.PluralType;
+import com.ibm.icu.text.MessagePattern;
+
+/**
+ * <p>
+ * <code>PluralFormat</code> supports the creation of internationalized
+ * messages with plural inflection. It is based on <i>plural
+ * selection</i>, i.e. the caller specifies messages for each
+ * plural case that can appear in the user's language and the
+ * <code>PluralFormat</code> selects the appropriate message based on
+ * the number.
+ * </p>
+ * <h4>The Problem of Plural Forms in Internationalized Messages</h4>
+ * <p>
+ * Different languages have different ways to inflect
+ * plurals. Creating internationalized messages that include plural
+ * forms is only feasible when the framework is able to handle plural
+ * forms of <i>all</i> languages correctly. <code>ChoiceFormat</code>
+ * doesn't handle this well, because it attaches a number interval to
+ * each message and selects the message whose interval contains a
+ * given number. This can only handle a finite number of
+ * intervals. But in some languages, like Polish, one plural case
+ * applies to infinitely many intervals (e.g., the paucal case applies to
+ * numbers ending with 2, 3, or 4 except those ending with 12, 13, or
+ * 14). Thus <code>ChoiceFormat</code> is not adequate.
+ * </p><p>
+ * <code>PluralFormat</code> deals with this by breaking the problem
+ * into two parts:
+ * <ul>
+ * <li>It uses <code>PluralRules</code> that can define more complex
+ *     conditions for a plural case than just a single interval. These plural
+ *     rules define both what plural cases exist in a language, and to
+ *     which numbers these cases apply.
+ * <li>It provides predefined plural rules for many languages. Thus, the programmer
+ *     need not worry about the plural cases of a language and
+ *     does not have to define the plural cases; they can simply
+ *     use the predefined keywords. The whole plural formatting of messages can
+ *     be done using localized patterns from resource bundles. For predefined plural 
+ *     rules, see the CLDR <i>Language Plural Rules</i> page at
+ *    http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html 
+ * </ul>
+ * </p>
+ * <h4>Usage of <code>PluralFormat</code></h4>
+ * <p>Note: Typically, plural formatting is done via <code>MessageFormat</code>
+ * with a <code>plural</code> argument type,
+ * rather than using a stand-alone <code>PluralFormat</code>.
+ * </p><p>
+ * This discussion assumes that you use <code>PluralFormat</code> with
+ * a predefined set of plural rules. You can create one using one of
+ * the constructors that takes a <code>ULocale</code> object. To
+ * specify the message pattern, you can either pass it to the
+ * constructor or set it explicitly using the
+ * <code>applyPattern()</code> method. The <code>format()</code>
+ * method takes a number object and selects the message of the
+ * matching plural case. This message will be returned.
+ * </p>
+ * <h5>Patterns and Their Interpretation</h5>
+ * <p>
+ * The pattern text defines the message output for each plural case of the
+ * specified locale. Syntax:
+ * <blockquote><pre>
+ * pluralStyle = [offsetValue] (selector '{' message '}')+
+ * offsetValue = "offset:" number
+ * selector = explicitValue | keyword
+ * explicitValue = '=' number  // adjacent, no white space in between
+ * keyword = [^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+
+ * message: see {@link MessageFormat}
+ * </pre></blockquote>
+ * Pattern_White_Space between syntax elements is ignored, except
+ * between the {curly braces} and their sub-message,
+ * and between the '=' and the number of an explicitValue.
+ *
+ * </p><p>
+ * There are 6 predefined case keywords in CLDR/ICU - 'zero', 'one', 'two', 'few', 'many' and 
+ * 'other'. You always have to define a message text for the default plural case 
+ * "<code>other</code>" which is contained in every rule set.
+ * If you do not specify a message text for a particular plural case, the
+ * message text of the plural case "<code>other</code>" gets assigned to this
+ * plural case.
+ * </p><p>
+ * When formatting, the input number is first matched against the explicitValue clauses.
+ * If there is no exact-number match, then a keyword is selected by calling
+ * the <code>PluralRules</code> with the input number <em>minus the offset</em>.
+ * (The offset defaults to 0 if it is omitted from the pattern string.)
+ * If there is no clause with that keyword, then the "other" clauses is returned.
+ * </p><p>
+ * An unquoted pound sign (<code>#</code>) in the selected sub-message
+ * itself (i.e., outside of arguments nested in the sub-message)
+ * is replaced by the input number minus the offset.
+ * The number-minus-offset value is formatted using a
+ * <code>NumberFormat</code> for the <code>PluralFormat</code>'s locale. If you
+ * need special number formatting, you have to use a <code>MessageFormat</code>
+ * and explicitly specify a <code>NumberFormat</code> argument.
+ * <strong>Note:</strong> That argument is formatting without subtracting the offset!
+ * If you need a custom format and have a non-zero offset, then you need to pass the
+ * number-minus-offset value as a separate parameter.
+ * </p>
+ * For a usage example, see the {@link MessageFormat} class documentation.
+ *
+ * <h4>Defining Custom Plural Rules</h4>
+ * <p>If you need to use <code>PluralFormat</code> with custom rules, you can
+ * create a <code>PluralRules</code> object and pass it to
+ * <code>PluralFormat</code>'s constructor. If you also specify a locale in this
+ * constructor, this locale will be used to format the number in the message
+ * texts.
+ * </p><p>
+ * For more information about <code>PluralRules</code>, see
+ * {@link PluralRules}.
+ * </p>
+ *
+ * @author tschumann (Tim Schumann)
+ * @stable ICU 3.8
+ */
+public class PluralFormat /* extends UFormat */ {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * The locale used for standard number formatting and getting the predefined
+     * plural rules (if they were not defined explicitely).
+     * @serial
+     */
+    private Locale locale_ = null;
+
+    /**
+     * The plural rules used for plural selection.
+     * @serial
+     */
+    private PluralRules pluralRules = null;
+
+    /**
+     * The applied pattern string.
+     * @serial
+     */
+    private String pattern = null;
+
+    /**
+     * The MessagePattern which contains the parsed structure of the pattern string.
+     */
+    transient private MessagePattern msgPattern;
+    
+    /**
+     * Obsolete with use of MessagePattern since ICU 4.8. Used to be:
+     * The format messages for each plural case. It is a mapping:
+     *  <code>String</code>(plural case keyword) --&gt; <code>String</code>
+     *  (message for this plural case).
+     * @serial
+     */
+    private Map<String, String> parsedValues = null;
+
+    /**
+     * This <code>NumberFormat</code> is used for the standard formatting of
+     * the number inserted into the message.
+     * @serial
+     */
+    private NumberFormat numberFormat = null;
+
+    /**
+     * The offset to subtract before invoking plural rules.
+     */
+    transient private double offset = 0;
+
+    /**
+     * Creates a new cardinal-number <code>PluralFormat</code> for the default <code>FORMAT</code> locale.
+     * This locale will be used to get the set of plural rules and for standard
+     * number formatting.
+     * @see Category#FORMAT
+     * @stable ICU 3.8
+     */
+    public PluralFormat() {
+        init(null, PluralType.CARDINAL, Locale.getDefault());  // Category.FORMAT
+    }
+
+    /**
+     * Creates a new cardinal-number <code>PluralFormat</code> for a given locale.
+     * @param locale the <code>PluralFormat</code> will be configured with
+     *        rules for this locale. This locale will also be used for standard
+     *        number formatting.
+     * @stable ICU 3.8
+     */
+    public PluralFormat(Locale locale) {
+        init(null, PluralType.CARDINAL, locale);
+    }
+
+    /**
+     * Creates a new <code>PluralFormat</code> for the plural type.
+     * The standard number formatting will be done using the given locale.
+     * @param locale the default number formatting will be done using this
+     *        locale.
+     * @param type The plural type (e.g., cardinal or ordinal).
+     * @stable ICU 50
+     */
+    public PluralFormat(Locale locale, PluralType type) {
+        init(null, type, locale);
+    }
+
+    /*
+     * Initializes the <code>PluralRules</code> object.
+     * Postcondition:<br/>
+     *   <code>ulocale</code>    :  is <code>locale</code><br/>
+     *   <code>pluralRules</code>:  if <code>rules</code> != <code>null</code>
+     *                              it's set to rules, otherwise it is the
+     *                              predefined plural rule set for the locale
+     *                              <code>ulocale</code>.<br/>
+     *   <code>parsedValues</code>: is <code>null</code><br/>
+     *   <code>pattern</code>:      is <code>null</code><br/>
+     *   <code>numberFormat</code>: a <code>NumberFormat</code> for the locale
+     *                              <code>ulocale</code>.
+     */
+    private void init(PluralRules rules, PluralType type, Locale locale) {
+        locale_ = locale;
+        pluralRules = (rules == null) ? PluralRules.forLocale(locale, type)
+                                      : rules;
+        resetPattern();
+        numberFormat = NumberFormat.getInstance(locale);
+    }
+
+    private void resetPattern() {
+        pattern = null;
+        if(msgPattern != null) {
+            msgPattern.clear();
+        }
+        offset = 0;
+    }
+
+    /**
+     * Sets the pattern used by this plural format.
+     * The method parses the pattern and creates a map of format strings
+     * for the plural rules.
+     * Patterns and their interpretation are specified in the class description.
+     *
+     * @param pattern the pattern for this plural format.
+     * @throws IllegalArgumentException if the pattern is invalid.
+     * @stable ICU 3.8
+     */
+    public void applyPattern(String pattern) {
+        this.pattern = pattern;
+        if (msgPattern == null) {
+            msgPattern = new MessagePattern();
+        }
+        try {
+            msgPattern.parsePluralStyle(pattern);
+            offset = msgPattern.getPluralOffset(0);
+        } catch(RuntimeException e) {
+            resetPattern();
+            throw e;
+        }
+    }
+
+    /**
+     * Returns the pattern for this PluralFormat.
+     *
+     * @return the pattern string
+     * @stable ICU 4.2
+     */
+    public String toPattern() {
+        return pattern;
+    }
+
+    /**
+     * Finds the PluralFormat sub-message for the given number, or the "other" sub-message.
+     * @param pattern A MessagePattern.
+     * @param partIndex the index of the first PluralFormat argument style part.
+     * @param selector the PluralSelector for mapping the number (minus offset) to a keyword.
+     * @param context worker object for the selector.
+     * @param number a number to be matched to one of the PluralFormat argument's explicit values,
+     *        or mapped via the PluralSelector.
+     * @return the sub-message start part index.
+     */
+    /*package*/ static int findSubMessage(
+            MessagePattern pattern, int partIndex,
+            PluralSelector selector, Object context, double number) {
+        int count=pattern.countParts();
+        double offset;
+        MessagePattern.Part part=pattern.getPart(partIndex);
+        if(part.getType().hasNumericValue()) {
+            offset=pattern.getNumericValue(part);
+            ++partIndex;
+        } else {
+            offset=0;
+        }
+        // The keyword is null until we need to match against a non-explicit, not-"other" value.
+        // Then we get the keyword from the selector.
+        // (In other words, we never call the selector if we match against an explicit value,
+        // or if the only non-explicit keyword is "other".)
+        String keyword=null;
+        // When we find a match, we set msgStart>0 and also set this boolean to true
+        // to avoid matching the keyword again (duplicates are allowed)
+        // while we continue to look for an explicit-value match.
+        boolean haveKeywordMatch=false;
+        // msgStart is 0 until we find any appropriate sub-message.
+        // We remember the first "other" sub-message if we have not seen any
+        // appropriate sub-message before.
+        // We remember the first matching-keyword sub-message if we have not seen
+        // one of those before.
+        // (The parser allows [does not check for] duplicate keywords.
+        // We just have to make sure to take the first one.)
+        // We avoid matching the keyword twice by also setting haveKeywordMatch=true
+        // at the first keyword match.
+        // We keep going until we find an explicit-value match or reach the end of the plural style.
+        int msgStart=0;
+        // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples
+        // until ARG_LIMIT or end of plural-only pattern.
+        do {
+            part=pattern.getPart(partIndex++);
+            MessagePattern.Part.Type type=part.getType();
+            if(type==MessagePattern.Part.Type.ARG_LIMIT) {
+                break;
+            }
+            assert type==MessagePattern.Part.Type.ARG_SELECTOR;
+            // part is an ARG_SELECTOR followed by an optional explicit value, and then a message
+            if(pattern.getPartType(partIndex).hasNumericValue()) {
+                // explicit value like "=2"
+                part=pattern.getPart(partIndex++);
+                if(number==pattern.getNumericValue(part)) {
+                    // matches explicit value
+                    return partIndex;
+                }
+            } else if(!haveKeywordMatch) {
+                // plural keyword like "few" or "other"
+                // Compare "other" first and call the selector if this is not "other".
+                if(pattern.partSubstringMatches(part, "other")) {
+                    if(msgStart==0) {
+                        msgStart=partIndex;
+                        if(keyword!=null && keyword.equals("other")) {
+                            // This is the first "other" sub-message,
+                            // and the selected keyword is also "other".
+                            // Do not match "other" again.
+                            haveKeywordMatch=true;
+                        }
+                    }
+                } else {
+                    if(keyword==null) {
+                        keyword=selector.select(context, number-offset);
+                        if(msgStart!=0 && keyword.equals("other")) {
+                            // We have already seen an "other" sub-message.
+                            // Do not match "other" again.
+                            haveKeywordMatch=true;
+                            // Skip keyword matching but do getLimitPartIndex().
+                        }
+                    }
+                    if(!haveKeywordMatch && pattern.partSubstringMatches(part, keyword)) {
+                        // keyword matches
+                        msgStart=partIndex;
+                        // Do not match this keyword again.
+                        haveKeywordMatch=true;
+                    }
+                }
+            }
+            partIndex=pattern.getLimitPartIndex(partIndex);
+        } while(++partIndex<count);
+        return msgStart;
+    }
+
+    /**
+     * Interface for selecting PluralFormat keywords for numbers.
+     * The PluralRules class was intended to implement this interface,
+     * but there is no public API that uses a PluralSelector,
+     * only MessageFormat and PluralFormat have PluralSelector implementations.
+     * Therefore, PluralRules is not marked to implement this non-public interface,
+     * to avoid confusing users.
+     * @internal
+     */
+    /*package*/ interface PluralSelector {
+        /**
+         * Given a number, returns the appropriate PluralFormat keyword.
+         *
+         * @param context worker object for the selector.
+         * @param number The number to be plural-formatted.
+         * @return The selected PluralFormat keyword.
+         */
+        public String select(Object context, double number);
+    }
+
+    // See PluralSelector:
+    // We could avoid this adapter class if we made PluralSelector public
+    // (or at least publicly visible) and had PluralRules implement PluralSelector.
+    private final class PluralSelectorAdapter implements PluralSelector {
+        public String select(Object context, double number) {
+            FixedDecimal dec = (FixedDecimal) context;
+            assert dec.source == number;
+            return pluralRules.select(dec);
+        }
+    }
+    transient private PluralSelectorAdapter pluralRulesWrapper = new PluralSelectorAdapter();
+
+    /**
+     * This method is not yet supported by <code>PluralFormat</code>.
+     * @param text the string to be parsed.
+     * @param parsePosition defines the position where parsing is to begin,
+     * and upon return, the position where parsing left off.  If the position
+     * has not changed upon return, then parsing failed.
+     * @return nothing because this method is not yet implemented.
+     * @throws UnsupportedOperationException will always be thrown by this method.
+     * @stable ICU 3.8
+     */
+    public Number parse(String text, ParsePosition parsePosition) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * This method is not yet supported by <code>PluralFormat</code>.
+     * @param source the string to be parsed.
+     * @param pos defines the position where parsing is to begin,
+     * and upon return, the position where parsing left off.  If the position
+     * has not changed upon return, then parsing failed.
+     * @return nothing because this method is not yet implemented.
+     * @throws UnsupportedOperationException will always be thrown by this method.
+     * @stable ICU 3.8
+     */
+    public Object parseObject(String source, ParsePosition pos) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns true if this equals the provided PluralFormat.
+     * @param rhs the PluralFormat to compare against
+     * @return true if this equals rhs
+     * @stable ICU 3.8
+     */
+    public boolean equals(PluralFormat rhs) {
+        return equals((Object)rhs);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @stable ICU 3.8
+     */
+    @Override
+    public int hashCode() {
+        return pluralRules.hashCode() ^ parsedValues.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @stable ICU 3.8
+     */
+    @Override
+    public String toString() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("locale=" + locale_);
+        buf.append(", rules='" + pluralRules + "'");
+        buf.append(", pattern='" + pattern + "'");
+        buf.append(", format='" + numberFormat + "'");
+        return buf.toString();
+    }
+
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        pluralRulesWrapper = new PluralSelectorAdapter();
+        // Ignore the parsedValues from an earlier class version (before ICU 4.8)
+        // and rebuild the msgPattern.
+        parsedValues = null;
+        if (pattern != null) {
+            applyPattern(pattern);
+        }
+    }
+}
diff --git a/src/main/com/android/i18n/PluralRules.java b/src/com/ibm/icu/simple/PluralRules.java
similarity index 91%
rename from src/main/com/android/i18n/PluralRules.java
rename to src/com/ibm/icu/simple/PluralRules.java
index 10e0be0..ab0039d 100644
--- a/src/main/com/android/i18n/PluralRules.java
+++ b/src/com/ibm/icu/simple/PluralRules.java
@@ -1,17 +1,16 @@
 /*
  *******************************************************************************
- * Copyright (C) 2007-2013, International Business Machines Corporation and
+ * Copyright (C) 2007-2014, International Business Machines Corporation and
  * others. All Rights Reserved.
  *******************************************************************************
  */
 
-package com.ibm.icu.text;
+package com.ibm.icu.simple;
 
 import java.io.IOException;
 import java.io.NotSerializableException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
-import java.io.ObjectStreamException;
 import java.io.Serializable;
 import java.text.ParseException;
 import java.util.ArrayList;
@@ -26,9 +25,7 @@
 import java.util.TreeSet;
 import java.util.regex.Pattern;
 
-import com.ibm.icu.impl.PluralRulesLoader;
 import com.ibm.icu.util.Output;
-import com.ibm.icu.util.ULocale;
 
 /**
  * <p>
@@ -167,18 +164,20 @@
  */
 public class PluralRules implements Serializable {
 
-    static final UnicodeSet ALLOWED_ID = new UnicodeSet("[a-z]").freeze();
+    // static final UnicodeSet ALLOWED_ID = new UnicodeSet("[a-z]").freeze();
 
     // TODO Remove RulesList by moving its API and fields into PluralRules.
     /**
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     public static final String CATEGORY_SEPARATOR = ";  ";
     /**
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     public static final String KEYWORD_RULE_SEPARATOR = ": ";
 
     private static final long serialVersionUID = 1;
@@ -189,9 +188,10 @@
     /**
      * Provides a factory for returning plural rules
      * 
-     * @deprecated This API is ICU internal only.
      * @internal
+     * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     public static abstract class Factory {
         /**
          * Provides access to the predefined <code>PluralRules</code> for a given locale and the plural type.
@@ -207,29 +207,32 @@
          * @return The predefined <code>PluralRules</code> object for this locale. If there's no predefined rules for
          *         this locale, the rules for the closest parent in the locale hierarchy that has one will be returned.
          *         The final fallback always returns the default rules.
-         * @deprecated This API is ICU internal only.
          * @internal
+         * @deprecated This API is ICU internal only.
          */
-        public abstract PluralRules forLocale(ULocale locale, PluralType type);
+        @Deprecated
+        public abstract PluralRules forLocale(Locale locale, PluralType type);
 
         /**
          * Utility for getting CARDINAL rules.
          * @param locale the locale
          * @return plural rules.
-         * @deprecated This API is ICU internal only.
          * @internal
+         * @deprecated This API is ICU internal only.
          */
-        public final PluralRules forLocale(ULocale locale) {
+        @Deprecated
+        public final PluralRules forLocale(Locale locale) {
             return forLocale(locale, PluralType.CARDINAL);
         }
 
         /**
          * Returns the locales for which there is plurals data.
          * 
-         * @deprecated This API is ICU internal only.
          * @internal
-         */
+         * @deprecated This API is ICU internal only.
+        @Deprecated
         public abstract ULocale[] getAvailableULocales();
+         */
 
         /**
          * Returns the 'functionally equivalent' locale with respect to plural rules. Calling PluralRules.forLocale with
@@ -244,26 +247,29 @@
          *            if not null and of length > 0, this will hold 'true' at index 0 if locale is directly defined
          *            (without fallback) as having plural rules
          * @return the functionally-equivalent locale
-         * @deprecated This API is ICU internal only.
          * @internal
-         */
+         * @deprecated This API is ICU internal only.
+        @Deprecated
         public abstract ULocale getFunctionalEquivalent(ULocale locale, boolean[] isAvailable);
+         */
 
         /**
          * Returns the default factory.
-         * @deprecated This API is ICU internal only.
          * @internal
+         * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public static PluralRulesLoader getDefaultFactory() {
             return PluralRulesLoader.loader;
         }
 
         /**
          * Returns whether or not there are overrides.
-         * @deprecated This API is ICU internal only.
          * @internal
-         */
+         * @deprecated This API is ICU internal only.
+        @Deprecated
         public abstract boolean hasOverride(ULocale locale);
+         */
     }
     // Standard keywords.
 
@@ -396,7 +402,7 @@
         t,
         v,
         w,
-        /**@deprecated*/
+        /* deprecated */
         j;
     }
 
@@ -404,47 +410,56 @@
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     public static class FixedDecimal extends Number implements Comparable<FixedDecimal> {
         private static final long serialVersionUID = -4756200506571685661L;
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public final double source;
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public final int visibleDecimalDigitCount;
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public final int visibleDecimalDigitCountWithoutTrailingZeros;
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public final long decimalDigits;
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public final long decimalDigitsWithoutTrailingZeros;
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public final long integerValue;
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public final boolean hasIntegerValue;
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public final boolean isNegative;
         private final int baseFactor;
 
@@ -452,6 +467,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public double getSource() {
             return source;
         }
@@ -460,6 +476,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public int getVisibleDecimalDigitCount() {
             return visibleDecimalDigitCount;
         }
@@ -468,6 +485,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public int getVisibleDecimalDigitCountWithoutTrailingZeros() {
             return visibleDecimalDigitCountWithoutTrailingZeros;
         }
@@ -476,6 +494,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public long getDecimalDigits() {
             return decimalDigits;
         }
@@ -484,6 +503,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public long getDecimalDigitsWithoutTrailingZeros() {
             return decimalDigitsWithoutTrailingZeros;
         }
@@ -492,6 +512,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public long getIntegerValue() {
             return integerValue;
         }
@@ -500,6 +521,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public boolean isHasIntegerValue() {
             return hasIntegerValue;
         }
@@ -508,6 +530,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public boolean isNegative() {
             return isNegative;
         }
@@ -516,10 +539,13 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public int getBaseFactor() {
             return baseFactor;
         }
 
+        static final long MAX = (long)1E18;
+
         /**
          * @internal
          * @deprecated This API is ICU internal only.
@@ -528,12 +554,15 @@
          * @param f Corresponds to f in the plural rules grammar.
          *   The digits to the right of the decimal place as an integer. e.g 1.10 = 10
          */
+        @Deprecated
         public FixedDecimal(double n, int v, long f) {
             isNegative = n < 0;
             source = isNegative ? -n : n;
             visibleDecimalDigitCount = v;
             decimalDigits = f;
-            integerValue = (long)n;
+            integerValue = n > MAX 
+                    ? MAX 
+                            : (long)n;
             hasIntegerValue = source == integerValue;
             // check values. TODO make into unit test.
             //            
@@ -568,6 +597,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public FixedDecimal(double n, int v) {
             this(n,v,getFractionalDigits(n, v));
         }
@@ -576,6 +606,9 @@
             if (v == 0) {
                 return 0;
             } else {
+                if (n < 0) {
+                    n = -n;
+                }
                 int baseFactor = (int) Math.pow(10, v);
                 long scaled = Math.round(n * baseFactor);
                 return (int) (scaled % baseFactor);
@@ -586,6 +619,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public FixedDecimal(double n) {
             this(n, decimals(n));
         }
@@ -594,24 +628,65 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public FixedDecimal(long n) {
             this(n,0);
         }
 
+        private static final long MAX_INTEGER_PART = 1000000000;
         /**
+         * Return a guess as to the number of decimals that would be displayed. This is only a guess; callers should
+         * always supply the decimals explicitly if possible. Currently, it is up to 6 decimals (without trailing zeros).
+         * Returns 0 for infinities and nans.
          * @internal
          * @deprecated This API is ICU internal only.
+         * 
          */
+        @Deprecated
         public static int decimals(double n) {
             // Ugly...
-            String temp = String.valueOf(n);
-            return temp.endsWith(".0") ? 0 : temp.length() - temp.indexOf('.') - 1;
+            if (Double.isInfinite(n) || Double.isNaN(n)) {
+                return 0;
+            }
+            if (n < 0) {
+                n = -n;
+            }
+            if (n < MAX_INTEGER_PART) {
+                long temp = (long)(n * 1000000) % 1000000; // get 6 decimals
+                for (int mask = 10, digits = 6; digits > 0; mask *= 10, --digits) {
+                    if ((temp % mask) != 0) {
+                        return digits;
+                    }
+                }
+                return 0;
+            } else {
+                String buf = String.format(Locale.ENGLISH, "%1.15e", n);
+                int ePos = buf.lastIndexOf('e');
+                int expNumPos = ePos + 1;
+                if (buf.charAt(expNumPos) == '+') {
+                    expNumPos++;
+                }
+                String exponentStr = buf.substring(expNumPos);
+                int exponent = Integer.parseInt(exponentStr);
+                int numFractionDigits = ePos - 2 - exponent;
+                if (numFractionDigits < 0) {
+                    return 0;
+                }
+                for (int i=ePos-1; numFractionDigits > 0; --i) {
+                    if (buf.charAt(i) != '0') {
+                        break;
+                    }
+                    --numFractionDigits; 
+                }
+                return numFractionDigits;
+            }
         }
 
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public FixedDecimal (String n) {
             // Ugly, but for samples we don't care.
             this(Double.parseDouble(n), getVisibleFractionCount(n));
@@ -631,6 +706,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public double get(Operand operand) {
             switch(operand) {
             default: return source;
@@ -646,6 +722,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public static Operand getOperand(String t) {
             return Operand.valueOf(t);
         }
@@ -655,6 +732,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public int compareTo(FixedDecimal other) {
             if (integerValue != other.integerValue) {
                 return integerValue < other.integerValue ? -1 : 1;
@@ -676,6 +754,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         @Override
         public boolean equals(Object arg0) {
             if (arg0 == null) {
@@ -695,6 +774,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         @Override
         public int hashCode() {
             // TODO Auto-generated method stub
@@ -705,6 +785,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         @Override
         public String toString() {
             return String.format("%." + visibleDecimalDigitCount + "f", source);
@@ -714,6 +795,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public boolean hasIntegerValue() {
             return hasIntegerValue;
         }
@@ -722,6 +804,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         @Override
         public int intValue() {
             // TODO Auto-generated method stub
@@ -732,6 +815,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         @Override
         public long longValue() {
             return integerValue;
@@ -741,6 +825,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         @Override
         public float floatValue() {
             return (float) source;
@@ -750,15 +835,17 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         @Override
         public double doubleValue() {
-            return source;
+            return isNegative ? -source : source;
         }
 
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public long getShiftedValue() {
             return integerValue * baseFactor + decimalDigits;
         }
@@ -780,28 +867,46 @@
      * @internal
      * @deprecated This API is ICU internal only.
      */
-    public enum SampleType {INTEGER, DECIMAL}
+    @Deprecated
+    public enum SampleType {
+        /**
+         * @internal
+         * @deprecated This API is ICU internal only.
+         */
+        @Deprecated
+        INTEGER,
+        /**
+         * @internal
+         * @deprecated This API is ICU internal only.
+         */
+        @Deprecated
+        DECIMAL
+    }
 
     /**
      * A range of NumberInfo that includes all values with the same visibleFractionDigitCount.
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     public static class FixedDecimalRange {
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public final FixedDecimal start;
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public final FixedDecimal end;
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public FixedDecimalRange(FixedDecimal start, FixedDecimal end) {
             if (start.visibleDecimalDigitCount != end.visibleDecimalDigitCount) {
                 throw new IllegalArgumentException("Ranges must have the same number of visible decimals: " + start + "~" + end);
@@ -813,6 +918,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         @Override
         public String toString() {
             return start + (end == start ? "" : "~" + end);
@@ -824,21 +930,25 @@
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     public static class FixedDecimalSamples {
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public final SampleType sampleType;
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public final Set<FixedDecimalRange> samples;
         /**
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public final boolean bounded;
         /**
          * The samples must be immutable.
@@ -908,6 +1018,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public Set<Double> addSamples(Set<Double> result) {
             for (FixedDecimalRange item : samples) {
                 // we have to convert to longs so we don't get strange double issues
@@ -925,6 +1036,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         @Override
         public String toString() {
             StringBuilder b = new StringBuilder("@").append(sampleType.toString().toLowerCase(Locale.ENGLISH));
@@ -947,6 +1059,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public Set<FixedDecimalRange> getSamples() {
             return samples;
         }
@@ -955,6 +1068,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         public void getStartEndSamples(Set<FixedDecimal> target) {
             for (FixedDecimalRange item : samples) {
                 target.add(item.start);
@@ -980,20 +1094,26 @@
         boolean isLimited(SampleType sampleType);
     }
 
+    private static final boolean isBreakAndIgnore(char c) {
+        return c <= 0x20 && (c == 0x20 || c == 9 || c == 0xa || c == 0xc || c == 0xd);
+    }
+    private static final boolean isBreakAndKeep(char c) {
+        return c <= '=' && c >= '!' && (c == '!' || c == '%' || c == ',' || c == '.' || c == '=');
+    }
     static class SimpleTokenizer {
-        static final UnicodeSet BREAK_AND_IGNORE = new UnicodeSet(0x09, 0x0a, 0x0c, 0x0d, 0x20, 0x20).freeze();
-        static final UnicodeSet BREAK_AND_KEEP = new UnicodeSet('!', '!', '%', '%', ',', ',', '.', '.', '=', '=').freeze();
+        // static final UnicodeSet BREAK_AND_IGNORE = new UnicodeSet(0x09, 0x0a, 0x0c, 0x0d, 0x20, 0x20).freeze();
+        // static final UnicodeSet BREAK_AND_KEEP = new UnicodeSet('!', '!', '%', '%', ',', ',', '.', '.', '=', '=').freeze();
         static String[] split(String source) {
             int last = -1;
             List<String> result = new ArrayList<String>();
             for (int i = 0; i < source.length(); ++i) {
                 char ch = source.charAt(i);
-                if (BREAK_AND_IGNORE.contains(ch)) {
+                if (isBreakAndIgnore(ch) /* BREAK_AND_IGNORE.contains(ch) */) {
                     if (last >= 0) {
                         result.add(source.substring(last,i));
                         last = -1;
                     }
-                } else if (BREAK_AND_KEEP.contains(ch)) {
+                } else if (isBreakAndKeep(ch) /* BREAK_AND_KEEP.contains(ch) */) {
                     if (last >= 0) {
                         result.add(source.substring(last,i));
                     }
@@ -1517,6 +1637,7 @@
          * @internal
          * @deprecated This API is ICU internal only.
          */
+        @Deprecated
         @Override
         public int hashCode() {
             return keyword.hashCode() ^ constraint.hashCode();
@@ -1570,11 +1691,10 @@
         }
 
         public String select(FixedDecimal n) {
+            if (Double.isInfinite(n.source) || Double.isNaN(n.source)) {
+                return KEYWORD_OTHER;
+            }
             Rule r = selectRule(n);
-            // since we have explict 'other', we don't need this.
-            //            if (r == null) {
-            //                return KEYWORD_OTHER;
-            //            }
             return r.getKeyword();
         }
 
@@ -1655,12 +1775,43 @@
      * @deprecated This API is ICU internal only.
      * @internal
      */
+    @Deprecated
     public enum StandardPluralCategories {
+        /**
+         * @internal
+         * @deprecated This API is ICU internal only.
+         */
+        @Deprecated
         zero,
+        /**
+         * @internal
+         * @deprecated This API is ICU internal only.
+         */
+        @Deprecated
         one,
+        /**
+         * @internal
+         * @deprecated This API is ICU internal only.
+         */
+        @Deprecated
         two,
+        /**
+         * @internal
+         * @deprecated This API is ICU internal only.
+         */
+        @Deprecated
         few,
+        /**
+         * @internal
+         * @deprecated This API is ICU internal only.
+         */
+        @Deprecated
         many,
+        /**
+         * @internal
+         * @deprecated This API is ICU internal only.
+         */
+        @Deprecated
         other;
         static StandardPluralCategories forString(String s) {
             StandardPluralCategories a;
@@ -1710,8 +1861,8 @@
      *   rules.
      * @stable ICU 3.8
      */
-    public static PluralRules forLocale(ULocale locale) {
-        return Factory.getDefaultFactory().forLocale(locale, PluralType.CARDINAL);
+    public static PluralRules forLocale(Locale locale) {
+        return forLocale(locale, PluralType.CARDINAL);
     }
 
     /**
@@ -1732,7 +1883,7 @@
      *   rules.
      * @stable ICU 50
      */
-    public static PluralRules forLocale(ULocale locale, PluralType type) {
+    public static PluralRules forLocale(Locale locale, PluralType type) {
         return Factory.getDefaultFactory().forLocale(locale, type);
     }
 
@@ -1743,7 +1894,14 @@
      * @return true if the token is a valid keyword.
      */
     private static boolean isValidKeyword(String token) {
-        return ALLOWED_ID.containsAll(token);
+        // return ALLOWED_ID.containsAll(token);
+        for (int i = 0; i < token.length(); ++i) {
+            char c = token.charAt(i);
+            if (!('a' <= c && c <= 'z')) {
+                return false;
+            }
+        }
+        return true;
     }
 
     /*
@@ -1758,6 +1916,7 @@
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     @Override
     public int hashCode() {
         return rules.hashCode();
@@ -1783,6 +1942,7 @@
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     public String select(double number, int countVisibleFractionDigits, long fractionaldigits) {
         return rules.select(new FixedDecimal(number, countVisibleFractionDigits, fractionaldigits));
     }
@@ -1796,6 +1956,7 @@
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     public String select(FixedDecimal sample) {
         return rules.select(sample);
     }
@@ -1809,6 +1970,7 @@
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     public boolean matches(FixedDecimal sample, String keyword) {
         return rules.select(sample, keyword);
     }
@@ -1858,12 +2020,14 @@
      * values is unlimited.
      *
      * @param keyword the keyword
+     * @param type the type of samples requested, INTEGER or DECIMAL
      * @return the values that trigger this keyword, or null.  The returned collection
      * is immutable. It will be empty if the keyword is not defined.
      * 
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     public Collection<Double> getAllKeywordValues(String keyword, SampleType type) {
         if (!isLimited(keyword, type)) {
             return null;
@@ -1873,7 +2037,7 @@
     }
 
     /**
-     * Returns a list of values for which select() would return that keyword,
+     * Returns a list of integer values for which select() would return that keyword,
      * or null if the keyword is not defined. The returned collection is unmodifiable.
      * The returned list is not complete, and there might be additional values that
      * would return the keyword.
@@ -1895,10 +2059,12 @@
      * IF there are samples for the other sampleType.
      *
      * @param keyword the keyword to test
+     * @param sampleType the type of samples requested, INTEGER or DECIMAL
      * @return a list of values matching the keyword.
+     * @internal
      * @deprecated ICU internal only
-     * @internal 
      */
+    @Deprecated
     public Collection<Double> getSamples(String keyword, SampleType sampleType) {
         if (!keywords.contains(keyword)) {
             return null;
@@ -1939,6 +2105,7 @@
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     public boolean addSample(String keyword, Number sample, int maxCount, Set<Double> result) {
         String selectedKeyword = sample instanceof FixedDecimal ? select((FixedDecimal)sample) : select(sample.doubleValue());
         if (selectedKeyword.equals(keyword)) {
@@ -1958,10 +2125,12 @@
      * would return the keyword.
      *
      * @param keyword the keyword to test
+     * @param sampleType the type of samples requested, INTEGER or DECIMAL
      * @return a list of values matching the keyword.
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     public FixedDecimalSamples getDecimalSamples(String keyword, SampleType sampleType) {
         return rules.getDecimalSamples(keyword, sampleType);
     }
@@ -1971,10 +2140,10 @@
      * @return the set of locales for which PluralRules are known, as a list
      * @draft ICU 4.2
      * @provisional This API might change or be removed in a future release.
-     */
     public static ULocale[] getAvailableULocales() {
         return Factory.getDefaultFactory().getAvailableULocales();
     }
+     */
 
     /**
      * Returns the 'functionally equivalent' locale with respect to
@@ -1992,10 +2161,10 @@
      * @return the functionally-equivalent locale
      * @draft ICU 4.2
      * @provisional This API might change or be removed in a future release.
-     */
     public static ULocale getFunctionalEquivalent(ULocale locale, boolean[] isAvailable) {
         return Factory.getDefaultFactory().getFunctionalEquivalent(locale, isAvailable);
     }
+     */
 
     /**
      * {@inheritDoc}
@@ -2098,6 +2267,8 @@
      *            checking against the keyword values.
      * @param explicits
      *            a set of Doubles that are used explicitly (eg [=0], "[=1]"). May be empty or null.
+     * @param sampleType
+     *            request KeywordStatus relative to INTEGER or DECIMAL values
      * @param uniqueValue
      *            If non null, set to the unique value.
      * @return the KeywordStatus
@@ -2159,10 +2330,11 @@
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
     public String getRules(String keyword) {
         return rules.getRules(keyword);
     }
-
+    /*
     private void writeObject(
             ObjectOutputStream out)
                     throws IOException {
@@ -2177,11 +2349,12 @@
     private Object writeReplace() throws ObjectStreamException {
         return new PluralRulesSerialProxy(toString());
     }
-
+    */
     /**
      * @internal
      * @deprecated internal
      */
+    @Deprecated
     public int compareTo(PluralRules other) {
         return toString().compareTo(other.toString());
     }
@@ -2190,6 +2363,7 @@
      * @internal
      * @deprecated internal
      */
+    @Deprecated
     public Boolean isLimited(String keyword) {
         return rules.isLimited(keyword, SampleType.INTEGER);
     }
@@ -2198,6 +2372,7 @@
      * @internal
      * @deprecated internal
      */
+    @Deprecated
     public boolean isLimited(String keyword, SampleType sampleType) {
         return rules.isLimited(keyword, sampleType);
     }
@@ -2206,6 +2381,7 @@
      * @internal
      * @deprecated internal
      */
+    @Deprecated
     public boolean computeLimited(String keyword, SampleType sampleType) {
         return rules.computeLimited(keyword, sampleType);
     }
diff --git a/src/com/ibm/icu/simple/PluralRulesLoader.java b/src/com/ibm/icu/simple/PluralRulesLoader.java
new file mode 100644
index 0000000..23383ea
--- /dev/null
+++ b/src/com/ibm/icu/simple/PluralRulesLoader.java
@@ -0,0 +1,180 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2008-2013, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ */
+package com.ibm.icu.simple;
+
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.TreeMap;
+
+import com.ibm.icu.simple.PluralRules.PluralType;
+
+/**
+ * Loader for plural rules data.
+ */
+public class PluralRulesLoader extends PluralRules.Factory {
+    // Data created from ICU4C with the command
+    // ~/svn.icu/trunk/bld$ LD_LIBRARY_PATH=lib bin/genrb --write-java UTF-8 --java-package com.ibm.icu.simple -s ../src/source/data/misc/ plurals.txt -d /tmp/icu
+    private static final ResourceBundle DATA_RB = new LocaleElements_plurals();
+
+    private final Map<String, PluralRules> rulesIdToRules;
+    // lazy init, use getLocaleIdToRulesIdMap to access
+    private Map<String, String> localeIdToCardinalRulesId;
+    private Map<String, String> localeIdToOrdinalRulesId;
+
+    /**
+     * Access through singleton.
+     */
+    private PluralRulesLoader() {
+        rulesIdToRules = new HashMap<String, PluralRules>();
+    }
+
+   /**
+     * Returns the lazily-constructed map.
+     */
+    private Map<String, String> getLocaleIdToRulesIdMap(PluralType type) {
+        checkBuildRulesIdMaps();
+        return (type == PluralType.CARDINAL) ? localeIdToCardinalRulesId : localeIdToOrdinalRulesId;
+    }
+
+    /**
+     * Lazily constructs the localeIdToRulesId and rulesIdToEquivalentULocale
+     * maps if necessary. These exactly reflect the contents of the locales
+     * resource in plurals.res.
+     */
+    private void checkBuildRulesIdMaps() {
+        boolean haveMap;
+        synchronized (this) {
+            haveMap = localeIdToCardinalRulesId != null;
+        }
+        if (!haveMap) {
+            Map<String, String> tempLocaleIdToCardinalRulesId;
+            Map<String, String> tempLocaleIdToOrdinalRulesId;
+            try {
+                ResourceBundle pluralb = DATA_RB;
+                // Read cardinal-number rules.
+                Object[][] localeb = (Object[][]) pluralb.getObject("locales");
+
+                // sort for convenience of getAvailableULocales
+                tempLocaleIdToCardinalRulesId = new TreeMap<String, String>();
+
+                for (Object[] langAndId : localeb) {
+                    String id = (String) langAndId[0];
+                    String value = (String) langAndId[1];
+                    tempLocaleIdToCardinalRulesId.put(id, value);
+                }
+
+                // Read ordinal-number rules.
+                localeb = (Object[][]) pluralb.getObject("locales_ordinals");
+                tempLocaleIdToOrdinalRulesId = new TreeMap<String, String>();
+                for (Object[] langAndId : localeb) {
+                    String id = (String) langAndId[0];
+                    String value = (String) langAndId[1];
+                    tempLocaleIdToOrdinalRulesId.put(id, value);
+                }
+            } catch (MissingResourceException e) {
+                // dummy so we don't try again
+                tempLocaleIdToCardinalRulesId = Collections.emptyMap();
+                tempLocaleIdToOrdinalRulesId = Collections.emptyMap();
+            }
+            
+            synchronized(this) {
+                if (localeIdToCardinalRulesId == null) {
+                    localeIdToCardinalRulesId = tempLocaleIdToCardinalRulesId;
+                    localeIdToOrdinalRulesId = tempLocaleIdToOrdinalRulesId;
+                }
+            }
+        }
+    }
+
+    /**
+     * Gets the rulesId from the locale,with locale fallback. If there is no
+     * rulesId, return null. The rulesId might be the empty string if the rule
+     * is the default rule.
+     */
+    public String getRulesIdForLocale(Locale locale, PluralType type) {
+        Map<String, String> idMap = getLocaleIdToRulesIdMap(type);
+        String lang = locale.getLanguage();
+        String rulesId = idMap.get(lang);
+        return rulesId;
+    }
+
+    /**
+     * Gets the rule from the rulesId. If there is no rule for this rulesId,
+     * return null.
+     */
+    public PluralRules getRulesForRulesId(String rulesId) {
+        // synchronize on the map.  release the lock temporarily while we build the rules.
+        PluralRules rules = null;
+        boolean hasRules;  // Separate boolean because stored rules can be null.
+        synchronized (rulesIdToRules) {
+            hasRules = rulesIdToRules.containsKey(rulesId);
+            if (hasRules) {
+                rules = rulesIdToRules.get(rulesId);  // can be null
+            }
+        }
+        if (!hasRules) {
+            try {
+                ResourceBundle pluralb = DATA_RB;
+                Object[][] rulesb = (Object[][]) pluralb.getObject("rules");
+                Object[][] setb = null;
+                for (Object[] idAndRule : rulesb) {  // Unbounded loop: We must find the rulesId.
+                    if (rulesId.equals(idAndRule[0])) {
+                        setb = (Object[][]) idAndRule[1];
+                        break;
+                    }
+                }
+
+                StringBuilder sb = new StringBuilder();
+                for (Object[] keywordAndRule : setb) {
+                    if (sb.length() > 0) {
+                        sb.append("; ");
+                    }
+                    sb.append((String) keywordAndRule[0]);
+                    sb.append(": ");
+                    sb.append((String) keywordAndRule[1]);
+                }
+                rules = PluralRules.parseDescription(sb.toString());
+            } catch (ParseException e) {
+            } catch (MissingResourceException e) {
+            }
+            synchronized (rulesIdToRules) {
+                if (rulesIdToRules.containsKey(rulesId)) {
+                    rules = rulesIdToRules.get(rulesId);
+                } else {
+                    rulesIdToRules.put(rulesId, rules);  // can be null
+                }
+            }
+        }
+        return rules;
+    }
+
+    /**
+     * Returns the plural rules for the the locale. If we don't have data,
+     * com.ibm.icu.text.PluralRules.DEFAULT is returned.
+     */
+    public PluralRules forLocale(Locale locale, PluralRules.PluralType type) {
+        String rulesId = getRulesIdForLocale(locale, type);
+        if (rulesId == null || rulesId.trim().length() == 0) {
+            return PluralRules.DEFAULT;
+        }
+        PluralRules rules = getRulesForRulesId(rulesId);
+        if (rules == null) {
+            rules = PluralRules.DEFAULT;
+        }
+        return rules;
+    }
+
+    /**
+     * The only instance of the loader.
+     */
+    public static final PluralRulesLoader loader = new PluralRulesLoader();
+}
diff --git a/src/main/com/android/i18n/MessagePattern.java b/src/com/ibm/icu/text/MessagePattern.java
similarity index 99%
rename from src/main/com/android/i18n/MessagePattern.java
rename to src/com/ibm/icu/text/MessagePattern.java
index bcb84b1..228a292 100644
--- a/src/main/com/android/i18n/MessagePattern.java
+++ b/src/com/ibm/icu/text/MessagePattern.java
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-*   Copyright (C) 2010-2013, International Business Machines
+*   Copyright (C) 2010-2014, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *******************************************************************************
 *   created on: 2010aug21
@@ -15,6 +15,7 @@
 import com.ibm.icu.impl.ICUConfig;
 import com.ibm.icu.impl.PatternProps;
 import com.ibm.icu.util.Freezable;
+import com.ibm.icu.util.ICUCloneNotSupportedException;
 
 //Note: Minimize ICU dependencies, only use a very small part of the ICU core.
 //In particular, do not depend on *Format classes.
@@ -308,7 +309,7 @@
      * @return true if getApostropheMode() == ApostropheMode.DOUBLE_REQUIRED
      * @internal
      */
-    /* package */ boolean jdkAposMode() {
+    public boolean jdkAposMode() {
         return aposMode == ApostropheMode.DOUBLE_REQUIRED;
     }
 
@@ -876,7 +877,7 @@
         try {
             newMsg=(MessagePattern)super.clone();
         } catch (CloneNotSupportedException e) {
-            throw new RuntimeException(e);
+            throw new ICUCloneNotSupportedException(e);
         }
         newMsg.parts=(ArrayList<Part>)parts.clone();
         if(numericValues!=null) {
diff --git a/src/com/ibm/icu/text/SelectFormat.java b/src/com/ibm/icu/text/SelectFormat.java
new file mode 100644
index 0000000..c062744
--- /dev/null
+++ b/src/com/ibm/icu/text/SelectFormat.java
@@ -0,0 +1,384 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2004-2011, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ * Copyright (C) 2009 , Yahoo! Inc.                                            *
+ *******************************************************************************
+ */
+package com.ibm.icu.text;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.ParsePosition;
+
+import com.ibm.icu.impl.PatternProps;
+
+/**
+ * <p><code>SelectFormat</code> supports the creation of  internationalized
+ * messages by selecting phrases based on keywords. The pattern  specifies
+ * how to map keywords to phrases and provides a default phrase. The
+ * object provided to the format method is a string that's matched
+ * against the keywords. If there is a match, the corresponding phrase
+ * is selected; otherwise, the default phrase is used.</p>
+ *
+ * <h4>Using <code>SelectFormat</code> for Gender Agreement</h4>
+ *
+ * <p>Note: Typically, select formatting is done via <code>MessageFormat</code>
+ * with a <code>select</code> argument type,
+ * rather than using a stand-alone <code>SelectFormat</code>.</p>
+ *
+ * <p>The main use case for the select format is gender based  inflection.
+ * When names or nouns are inserted into sentences, their gender can  affect pronouns,
+ * verb forms, articles, and adjectives. Special care needs to be
+ * taken for the case where the gender cannot be determined.
+ * The impact varies between languages:</p>
+ *
+ * <ul>
+ * <li>English has three genders, and unknown gender is handled as a  special
+ * case. Names use the gender of the named person (if known), nouns  referring
+ * to people use natural gender, and inanimate objects are usually  neutral.
+ * The gender only affects pronouns: "he", "she", "it", "they".
+ *
+ * <li>German differs from English in that the gender of nouns is  rather
+ * arbitrary, even for nouns referring to people ("M&#u00E4;dchen", girl, is  neutral).
+ * The gender affects pronouns ("er", "sie", "es"), articles ("der",  "die",
+ * "das"), and adjective forms ("guter Mann", "gute Frau", "gutes  M&#u00E4;dchen").
+ *
+ * <li>French has only two genders; as in German the gender of nouns
+ * is rather arbitrary - for sun and moon, the genders
+ * are the opposite of those in German. The gender affects
+ * pronouns ("il", "elle"), articles ("le", "la"),
+ * adjective forms ("bon", "bonne"), and sometimes
+ * verb forms ("all&#u00E9;", "all&#u00E9e;").
+ *
+ * <li>Polish distinguishes five genders (or noun classes),
+ * human masculine, animate non-human masculine, inanimate masculine,
+ * feminine, and neuter.
+ * </ul>
+ *
+ * <p>Some other languages have noun classes that are not related to  gender,
+ * but similar in grammatical use.
+ * Some African languages have around 20 noun classes.</p>
+ *
+ * <p><b>Note:</b>For the gender of a <i>person</i> in a given sentence,
+ * we usually need to distinguish only between female, male and other/unknown.</p>
+ *
+ * <p>To enable localizers to create sentence patterns that take their
+ * language's gender dependencies into consideration, software has to  provide
+ * information about the gender associated with a noun or name to
+ * <code>MessageFormat</code>.
+ * Two main cases can be distinguished:</p>
+ *
+ * <ul>
+ * <li>For people, natural gender information should be maintained  for each person.
+ * Keywords like "male", "female", "mixed" (for groups of people)
+ * and "unknown" could be used.
+ *
+ * <li>For nouns, grammatical gender information should be maintained  for
+ * each noun and per language, e.g., in resource bundles.
+ * The keywords "masculine", "feminine", and "neuter" are commonly  used,
+ * but some languages may require other keywords.
+ * </ul>
+ *
+ * <p>The resulting keyword is provided to <code>MessageFormat</code>  as a
+ * parameter separate from the name or noun it's associated with. For  example,
+ * to generate a message such as "Jean went to Paris", three separate  arguments
+ * would be provided: The name of the person as argument 0, the  gender of
+ * the person as argument 1, and the name of the city as argument 2.
+ * The sentence pattern for English, where the gender of the person has
+ * no impact on this simple sentence, would not refer to argument 1  at all:</p>
+ *
+ * <pre>{0} went to {2}.</pre>
+ *
+ * <p><b>Note:</b> The entire sentence should be included (and partially repeated)
+ * inside each phrase. Otherwise translators would have to be trained on how to
+ * move bits of the sentence in and out of the select argument of a message.
+ * (The examples below do not follow this recommendation!)</p>
+ * 
+ * <p>The sentence pattern for French, where the gender of the person affects
+ * the form of the participle, uses a select format based on argument 1:</p>
+ *
+ * <pre>{0} est {1, select, female {all&#u00E9;e} other {all&#u00E9;}} &#u00E0; {2}.</pre>
+ *
+ * <p>Patterns can be nested, so that it's possible to handle  interactions of
+ * number and gender where necessary. For example, if the above  sentence should
+ * allow for the names of several people to be inserted, the  following sentence
+ * pattern can be used (with argument 0 the list of people's names,  
+ * argument 1 the number of people, argument 2 their combined gender, and  
+ * argument 3 the city name):</p>
+ *
+ * <pre>{0} {1, plural, 
+ * one {est {2, select, female {all&#u00E9;e} other  {all&#u00E9;}}}
+ * other {sont {2, select, female {all&#u00E9;es} other {all&#u00E9;s}}}
+ * }&#u00E0; {3}.</pre>
+ *
+ * <h4>Patterns and Their Interpretation</h4>
+ *
+ * <p>The <code>SelectFormat</code> pattern string defines the phrase  output
+ * for each user-defined keyword.
+ * The pattern is a sequence of (keyword, message) pairs.
+ * A keyword is a "pattern identifier": [^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+</p>
+ *
+ * <p>Each message is a MessageFormat pattern string enclosed in {curly braces}.</p>
+ *
+ * <p>You always have to define a phrase for the default keyword
+ * <code>other</code>; this phrase is returned when the keyword  
+ * provided to
+ * the <code>format</code> method matches no other keyword.
+ * If a pattern does not provide a phrase for <code>other</code>, the  method
+ * it's provided to returns the error  <code>U_DEFAULT_KEYWORD_MISSING</code>.
+ * <br/>
+ * Pattern_White_Space between keywords and messages is ignored.
+ * Pattern_White_Space within a message is preserved and output.</p>
+ *
+ * <p><pre>Example:
+ * MessageFormat msgFmt = new MessageFormat("{0} est " +
+ *     "{1, select, female {all&#u00E9;e} other {all&#u00E9;}} &#u00E0; Paris.",
+ *     new ULocale("fr"));
+ * Object args[] = {"Kirti","female"};
+ * System.out.println(msgFmt.format(args));
+ * </pre>
+ * <p>
+ * Produces the output:<br/>
+ * <code>Kirti est all&#u00E9;e &#u00E0; Paris.</code>
+ * </p>
+ *
+ * @stable ICU 4.4
+ */
+
+public class SelectFormat extends Format{
+    // Generated by serialver from JDK 1.5
+    private static final long serialVersionUID = 2993154333257524984L;
+
+    /*
+     * The applied pattern string.
+     */
+    private String pattern = null;
+
+    /**
+     * The MessagePattern which contains the parsed structure of the pattern string.
+     */
+    transient private MessagePattern msgPattern;
+    
+    /**
+     * Creates a new <code>SelectFormat</code> for a given pattern string.
+     * @param  pattern the pattern for this <code>SelectFormat</code>.
+     * @stable ICU 4.4
+     */
+    public SelectFormat(String pattern) {
+        applyPattern(pattern);
+    }
+
+    /*
+     * Resets the <code>SelectFormat</code> object.
+     */
+    private void reset() {
+        pattern = null;
+        if(msgPattern != null) {
+            msgPattern.clear();
+        }
+    }
+
+    /**
+     * Sets the pattern used by this select format.
+     * Patterns and their interpretation are specified in the class description.
+     *
+     * @param pattern the pattern for this select format.
+     * @throws IllegalArgumentException when the pattern is not a valid select format pattern.
+     * @stable ICU 4.4
+     */
+    public void applyPattern(String pattern) {
+        this.pattern = pattern;
+        if (msgPattern == null) {
+            msgPattern = new MessagePattern();
+        }
+        try {
+            msgPattern.parseSelectStyle(pattern);
+        } catch(RuntimeException e) {
+            reset();
+            throw e;
+        }
+    }
+
+    /**
+     * Returns the pattern for this <code>SelectFormat</code>
+     *
+     * @return the pattern string
+     * @stable ICU 4.4
+     */
+    public String toPattern() {
+        return pattern;
+    }
+
+    /**
+     * Finds the SelectFormat sub-message for the given keyword, or the "other" sub-message.
+     * @param pattern A MessagePattern.
+     * @param partIndex the index of the first SelectFormat argument style part.
+     * @param keyword a keyword to be matched to one of the SelectFormat argument's keywords.
+     * @return the sub-message start part index.
+     */
+    public static int findSubMessage(MessagePattern pattern, int partIndex, String keyword) {
+        int count=pattern.countParts();
+        int msgStart=0;
+        // Iterate over (ARG_SELECTOR, message) pairs until ARG_LIMIT or end of select-only pattern.
+        do {
+            MessagePattern.Part part=pattern.getPart(partIndex++);
+            MessagePattern.Part.Type type=part.getType();
+            if(type==MessagePattern.Part.Type.ARG_LIMIT) {
+                break;
+            }
+            assert type==MessagePattern.Part.Type.ARG_SELECTOR;
+            // part is an ARG_SELECTOR followed by a message
+            if(pattern.partSubstringMatches(part, keyword)) {
+                // keyword matches
+                return partIndex;
+            } else if(msgStart==0 && pattern.partSubstringMatches(part, "other")) {
+                msgStart=partIndex;
+            }
+            partIndex=pattern.getLimitPartIndex(partIndex);
+        } while(++partIndex<count);
+        return msgStart;
+    }
+
+    /**
+     * Selects the phrase for the given keyword.
+     *
+     * @param keyword a phrase selection keyword.
+     * @return the string containing the formatted select message.
+     * @throws IllegalArgumentException when the given keyword is not a "pattern identifier"
+     * @stable ICU 4.4
+     */
+    public final String format(String keyword) {
+        //Check for the validity of the keyword
+        if (!PatternProps.isIdentifier(keyword)) {
+            throw new IllegalArgumentException("Invalid formatting argument.");
+        }
+        // If no pattern was applied, throw an exception
+        if (msgPattern == null || msgPattern.countParts() == 0) {
+            throw new IllegalStateException("Invalid format error.");
+        }
+
+        // Get the appropriate sub-message.
+        int msgStart = findSubMessage(msgPattern, 0, keyword);
+        if (!msgPattern.jdkAposMode()) {
+            int msgLimit = msgPattern.getLimitPartIndex(msgStart);
+            return msgPattern.getPatternString().substring(msgPattern.getPart(msgStart).getLimit(),
+                                                           msgPattern.getPatternIndex(msgLimit));
+        }
+        // JDK compatibility mode: Remove SKIP_SYNTAX.
+        StringBuilder result = null;
+        int prevIndex = msgPattern.getPart(msgStart).getLimit();
+        for (int i = msgStart;;) {
+            MessagePattern.Part part = msgPattern.getPart(++i);
+            MessagePattern.Part.Type type = part.getType();
+            int index = part.getIndex();
+            if (type == MessagePattern.Part.Type.MSG_LIMIT) {
+                if (result == null) {
+                    return pattern.substring(prevIndex, index);
+                } else {
+                    return result.append(pattern, prevIndex, index).toString();
+                }
+            } else if (type == MessagePattern.Part.Type.SKIP_SYNTAX) {
+                if (result == null) {
+                    result = new StringBuilder();
+                }
+                result.append(pattern, prevIndex, index);
+                prevIndex = part.getLimit();
+            } else if (type == MessagePattern.Part.Type.ARG_START) {
+                if (result == null) {
+                    result = new StringBuilder();
+                }
+                result.append(pattern, prevIndex, index);
+                prevIndex = index;
+                i = msgPattern.getLimitPartIndex(i);
+                index = msgPattern.getPart(i).getLimit();
+                MessagePattern.appendReducedApostrophes(pattern, prevIndex, index, result);
+                prevIndex = index;
+            }
+        }
+    }
+
+    /**
+     * Selects the phrase for the given keyword.
+     * and appends the formatted message to the given <code>StringBuffer</code>.
+     * @param keyword a phrase selection keyword.
+     * @param toAppendTo the selected phrase will be appended to this
+     *        <code>StringBuffer</code>.
+     * @param pos will be ignored by this method.
+     * @throws IllegalArgumentException when the given keyword is not a String
+     *         or not a "pattern identifier"
+     * @return the string buffer passed in as toAppendTo, with formatted text
+     *         appended.
+     * @stable ICU 4.4
+     */
+    public StringBuffer format(Object keyword, StringBuffer toAppendTo,
+            FieldPosition pos) {
+        if (keyword instanceof String) {
+            toAppendTo.append(format( (String)keyword));
+        }else{
+            throw new IllegalArgumentException("'" + keyword + "' is not a String");
+        }
+        return toAppendTo;
+    }
+
+    /**
+     * This method is not supported by <code>SelectFormat</code>.
+     * @param source the string to be parsed.
+     * @param pos defines the position where parsing is to begin,
+     * and upon return, the position where parsing left off.  If the position
+     * has not changed upon return, then parsing failed.
+     * @return nothing because this method is not supported.
+     * @throws UnsupportedOperationException thrown always.
+     * @stable ICU 4.4
+     */
+    public Object parseObject(String source, ParsePosition pos) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @stable ICU 4.4
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if(this == obj) {
+            return true;
+        }
+        if(obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        SelectFormat sf = (SelectFormat) obj;
+        return msgPattern == null ? sf.msgPattern == null : msgPattern.equals(sf.msgPattern);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @stable ICU 4.4
+     */
+    @Override
+    public int hashCode() {
+        if (pattern != null) {
+            return pattern.hashCode();
+        }
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @stable ICU 4.4
+     */
+    @Override
+    public String toString() {
+        return "pattern='" + pattern + "'";
+    }
+
+    private void readObject(ObjectInputStream in)
+        throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        if (pattern != null) {
+            applyPattern(pattern);
+        }
+    }
+}
diff --git a/src/com/ibm/icu/util/Freezable.java b/src/com/ibm/icu/util/Freezable.java
new file mode 100644
index 0000000..4cf37dd
--- /dev/null
+++ b/src/com/ibm/icu/util/Freezable.java
@@ -0,0 +1,320 @@
+/*
+ ******************************************************************************
+ * Copyright (C) 2005-2011, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                               *
+ ******************************************************************************
+*/
+package com.ibm.icu.util;
+
+/**
+ * Provides a flexible mechanism for controlling access, without requiring that
+ * a class be immutable. Once frozen, an object can never be unfrozen, so it is
+ * thread-safe from that point onward. Once the object has been frozen, 
+ * it must guarantee that no changes can be made to it. Any attempt to alter 
+ * it must raise an UnsupportedOperationException exception. This means that when 
+ * the object returns internal objects, or if anyone has references to those internal
+ * objects, that those internal objects must either be immutable, or must also
+ * raise exceptions if any attempt to modify them is made. Of course, the object
+ * can return clones of internal objects, since those are safe.
+ * <h2>Background</h2>
+ * <p>
+ * There are often times when you need objects to be objects 'safe', so that
+ * they can't be modified. Examples are when objects need to be thread-safe, or
+ * in writing robust code, or in caches. If you are only creating your own
+ * objects, you can guarantee this, of course -- but only if you don't make a
+ * mistake. If you have objects handed into you, or are creating objects using
+ * others handed into you, it is a different story. It all comes down to whether
+ * you want to take the Blanche Dubois approach (&quot;depend on the kindness of
+ * strangers&quot;) or the Andy Grove approach (&quot;Only the Paranoid
+ * Survive&quot;).
+ * </p>
+ * <p>
+ * For example, suppose we have a simple class:
+ * </p>
+ * 
+ * <pre>
+ * public class A {
+ *      protected Collection b;
+ * 
+ *      protected Collection c;
+ * 
+ *      public Collection get_b() {
+ *              return b;
+ *      }
+ * 
+ *      public Collection get_c() {
+ *              return c;
+ *      }
+ * 
+ *      public A(Collection new_b, Collection new_c) {
+ *              b = new_b;
+ *              c = new_c;
+ *      }
+ * }
+ * </pre>
+ * 
+ * <p>
+ * Since the class doesn't have any setters, someone might think that it is
+ * immutable. You know where this is leading, of course; this class is unsafe in
+ * a number of ways. The following illustrates that.
+ * </p>
+ * 
+ * <pre>
+ *  public test1(SupposedlyImmutableClass x, SafeStorage y) {
+ *    // unsafe getter
+ *    A a = x.getA();
+ *    Collection col = a.get_b();
+ *    col.add(something); // a has now been changed, and x too
+ *
+ *    // unsafe constructor
+ *    a = new A(col, col);
+ *    y.store(a);
+ *    col.add(something); // a has now been changed, and y too
+ *  }
+ * </pre>
+ * 
+ * <p>
+ * There are a few different techniques for having safe classes.
+ * </p>
+ * <ol>
+ * <li>Const objects. In C++, you can declare parameters const.</li>
+ * <li>Immutable wrappers. For example, you can put a collection in an
+ * immutable wrapper.</li>
+ * <li>Always-Immutable objects. Java uses this approach, with a few
+ * variations. Examples:
+ * <ol>
+ * <li>Simple. Once a Color is created (eg from R, G, and B integers) it is
+ * immutable.</li>
+ * <li>Builder Class. There is a separate 'builder' class. For example,
+ * modifiable Strings are created using StringBuffer (which doesn't have the
+ * full String API available). Once you want an immutable form, you create one
+ * with toString().</li>
+ * <li>Primitives. These are always safe, since they are copied on input/output
+ * from methods.</li>
+ * </ol>
+ * </li>
+ * <li>Cloning. Where you need an object to be safe, you clone it.</li>
+ * </ol>
+ * <p>
+ * There are advantages and disadvantages of each of these.
+ * </p>
+ * <ol>
+ * <li>Const provides a certain level of protection, but since const can be and
+ * is often cast away, it only protects against most inadvertent mistakes. It
+ * also offers no threading protection, since anyone who has a pointer to the
+ * (unconst) object in another thread can mess you up.</li>
+ * <li>Immutable wrappers are safer than const in that the constness can't be
+ * cast away. But other than that they have all the same problems: not safe if
+ * someone else keeps hold of the original object, or if any of the objects
+ * returned by the class are mutable.</li>
+ * <li>Always-Immutable Objects are safe, but usage can require excessive
+ * object creation.</li>
+ * <li>Cloning is only safe if the object truly has a 'safe' clone; defined as
+ * one that <i>ensures that no change to the clone affects the original</i>.
+ * Unfortunately, many objects don't have a 'safe' clone, and always cloning can
+ * require excessive object creation.</li>
+ * </ol>
+ * <h2>Freezable Model</h2>
+ * <p>
+ * The <code>Freezable</code> model supplements these choices by giving you
+ * the ability to build up an object by calling various methods, then when it is
+ * in a final state, you can <i>make</i> it immutable. Once immutable, an
+ * object cannot <i>ever </i>be modified, and is completely thread-safe: that
+ * is, multiple threads can have references to it without any synchronization.
+ * If someone needs a mutable version of an object, they can use
+ * <code>cloneAsThawed()</code>, and modify the copy. This provides a simple,
+ * effective mechanism for safe classes in circumstances where the alternatives
+ * are insufficient or clumsy. (If an object is shared before it is immutable,
+ * then it is the responsibility of each thread to mutex its usage (as with
+ * other objects).)
+ * </p>
+ * <p>
+ * Here is what needs to be done to implement this interface, depending on the
+ * type of the object.
+ * </p>
+ * <h3><b>Immutable Objects</b></h3>
+ * <p>
+ * These are the easiest. You just use the interface to reflect that, by adding
+ * the following:
+ * </p>
+ * 
+ * <pre>
+ *  public class A implements Freezable<A> {
+ *   ...
+ *   public final boolean isFrozen() {return true;}
+ *   public final A freeze() {return this;}
+ *   public final A cloneAsThawed() { return this; }
+ *   }
+ * </pre>
+ * 
+ * <p>
+ * These can be final methods because subclasses of immutable objects must
+ * themselves be immutable. (Note: <code>freeze</code> is returning
+ * <code>this</code> for chaining.)
+ * </p>
+ * <h3><b>Mutable Objects</b></h3>
+ * <p>
+ * Add a protected 'flagging' field:
+ * </p>
+ * 
+ * <pre>
+ * protected boolean immutable;
+ * </pre>
+ * 
+ * <p>
+ * Add the following methods:
+ * </p>
+ * 
+ * <pre>
+ * public final boolean isFrozen() {
+ *      return frozen;
+ * };
+ * 
+ * public A freeze() {
+ *      frozen = true;
+ *      return this;
+ * }
+ * </pre>
+ * 
+ * <p>
+ * Add a <code>cloneAsThawed()</code> method following the normal pattern for
+ * <code>clone()</code>, except that <code>frozen=false</code> in the new
+ * clone.
+ * </p>
+ * <p>
+ * Then take the setters (that is, any method that can change the internal state
+ * of the object), and add the following as the first statement:
+ * </p>
+ * 
+ * <pre>
+ * if (isFrozen()) {
+ *      throw new UnsupportedOperationException(&quot;Attempt to modify frozen object&quot;);
+ * }
+ * </pre>
+ * 
+ * <h4><b>Subclassing</b></h4>
+ * <p>
+ * Any subclass of a <code>Freezable</code> will just use its superclass's
+ * flagging field. It must override <code>freeze()</code> and
+ * <code>cloneAsThawed()</code> to call the superclass, but normally does not
+ * override <code>isFrozen()</code>. It must then just pay attention to its
+ * own getters, setters and fields.
+ * </p>
+ * <h4><b>Internal Caches</b></h4>
+ * <p>
+ * Internal caches are cases where the object is logically unmodified, but
+ * internal state of the object changes. For example, there are const C++
+ * functions that cast away the const on the &quot;this&quot; pointer in order
+ * to modify an object cache. These cases are handled by mutexing the internal
+ * cache to ensure thread-safety. For example, suppose that UnicodeSet had an
+ * internal marker to the last code point accessed. In this case, the field is
+ * not externally visible, so the only thing you need to do is to synchronize
+ * the field for thread safety.
+ * </p>
+ * <h4>Unsafe Internal Access</h4>
+ * <p>
+ * Internal fields are called <i>safe</i> if they are either
+ * <code>frozen</code> or immutable (such as String or primitives). If you've
+ * never allowed internal access to these, then you are all done. For example,
+ * converting UnicodeSet to be <code>Freezable</code> is just accomplished
+ * with the above steps. But remember that you <i><b>have</b></i> allowed
+ * access to unsafe internals if you have any code like the following, in a
+ * getter, setter, or constructor:
+ * </p>
+ * 
+ * <pre>
+ * Collection getStuff() {
+ *      return stuff;
+ * } // caller could keep reference &amp; modify
+ * 
+ * void setStuff(Collection x) {
+ *      stuff = x;
+ * } // caller could keep reference &amp; modify
+ * 
+ * MyClass(Collection x) {
+ *      stuff = x;
+ * } // caller could keep reference &amp; modify
+ * </pre>
+ * 
+ * <p>
+ * These also illustrated in the code sample in <b>Background</b> above.
+ * </p>
+ * <p>
+ * To deal with unsafe internals, the simplest course of action is to do the
+ * work in the <code>freeze()</code> function. Just make all of your internal
+ * fields frozen, and set the frozen flag. Any subsequent getter/setter will
+ * work properly. Here is an example:
+ * </p>
+ * 
+ * <pre>
+ * public A freeze() {
+ *      if (!frozen) {
+ *              foo.freeze();
+ *              frozen = true;
+ *      }
+ *      return this;
+ * }
+ * </pre>
+ * 
+ * <p>
+ * If the field is a <code>Collection</code> or <code>Map</code>, then to
+ * make it frozen you have two choices. If you have never allowed access to the
+ * collection from outside your object, then just wrap it to prevent future
+ * modification.
+ * </p>
+ * 
+ * <pre>
+ * zone_to_country = Collections.unmodifiableMap(zone_to_country);
+ * </pre>
+ * 
+ * <p>
+ * If you have <i>ever</i> allowed access, then do a <code>clone()</code>
+ * before wrapping it.
+ * </p>
+ * 
+ * <pre>
+ * zone_to_country = Collections.unmodifiableMap(zone_to_country.clone());
+ * </pre>
+ * 
+ * <p>
+ * If a collection <i>(or any other container of objects)</i> itself can
+ * contain mutable objects, then for a safe clone you need to recurse through it
+ * to make the entire collection immutable. The recursing code should pick the
+ * most specific collection available, to avoid the necessity of later
+ * downcasing.
+ * </p>
+ * <blockquote>
+ * <p>
+ * <b>Note: </b>An annoying flaw in Java is that the generic collections, like
+ * <code>Map</code> or <code>Set</code>, don't have a <code>clone()</code>
+ * operation. When you don't know the type of the collection, the simplest
+ * course is to just create a new collection:
+ * </p>
+ * 
+ * <pre>
+ * zone_to_country = Collections.unmodifiableMap(new HashMap(zone_to_country));
+ * </pre>
+ * 
+ * </blockquote>
+ * @stable ICU 3.8
+ */
+public interface Freezable<T> extends Cloneable {
+    /**
+     * Determines whether the object has been frozen or not.
+     * @stable ICU 3.8
+     */
+    public boolean isFrozen();
+
+    /**
+     * Freezes the object.
+     * @return the object itself.
+     * @stable ICU 3.8
+     */
+    public T freeze();
+
+    /**
+     * Provides for the clone operation. Any clone is initially unfrozen.
+     * @stable ICU 3.8
+     */
+    public T cloneAsThawed();
+}
diff --git a/src/com/ibm/icu/util/ICUCloneNotSupportedException.java b/src/com/ibm/icu/util/ICUCloneNotSupportedException.java
new file mode 100644
index 0000000..7be1b91
--- /dev/null
+++ b/src/com/ibm/icu/util/ICUCloneNotSupportedException.java
@@ -0,0 +1,62 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2014, International Business Machines Corporation and
+ * others. All Rights Reserved.
+ *******************************************************************************
+ */
+package com.ibm.icu.util;
+
+/**
+ * Unchecked version of {@link CloneNotSupportedException}.
+ * Some ICU APIs do not throw the standard exception but instead wrap it
+ * into this unchecked version.
+ *
+ * @draft ICU 53
+ * @provisional This API might change or be removed in a future release.
+ */
+public class ICUCloneNotSupportedException extends ICUException {
+    private static final long serialVersionUID = -4824446458488194964L;
+
+    /**
+     * Default constructor.
+     *
+     * @draft ICU 53
+     * @provisional This API might change or be removed in a future release.
+     */
+    public ICUCloneNotSupportedException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message exception message string
+     * @draft ICU 53
+     * @provisional This API might change or be removed in a future release.
+     */
+    public ICUCloneNotSupportedException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param cause original exception (normally a {@link CloneNotSupportedException})
+     * @draft ICU 53
+     * @provisional This API might change or be removed in a future release.
+     */
+    public ICUCloneNotSupportedException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message exception message string
+     * @param cause original exception (normally a {@link CloneNotSupportedException})
+     * @draft ICU 53
+     * @provisional This API might change or be removed in a future release.
+     */
+    public ICUCloneNotSupportedException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/com/ibm/icu/util/ICUException.java b/src/com/ibm/icu/util/ICUException.java
new file mode 100644
index 0000000..e37a97b
--- /dev/null
+++ b/src/com/ibm/icu/util/ICUException.java
@@ -0,0 +1,60 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2014, International Business Machines Corporation and
+ * others. All Rights Reserved.
+ *******************************************************************************
+ */
+package com.ibm.icu.util;
+
+/**
+ * Base class for unchecked, ICU-specific exceptions.
+ *
+ * @draft ICU 53
+ * @provisional This API might change or be removed in a future release.
+ */
+public class ICUException extends RuntimeException {
+    private static final long serialVersionUID = -3067399656455755650L;
+
+    /**
+     * Default constructor.
+     *
+     * @draft ICU 53
+     * @provisional This API might change or be removed in a future release.
+     */
+    public ICUException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message exception message string
+     * @draft ICU 53
+     * @provisional This API might change or be removed in a future release.
+     */
+    public ICUException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param cause original exception
+     * @draft ICU 53
+     * @provisional This API might change or be removed in a future release.
+     */
+    public ICUException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message exception message string
+     * @param cause original exception
+     * @draft ICU 53
+     * @provisional This API might change or be removed in a future release.
+     */
+    public ICUException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/com/ibm/icu/util/ICUUncheckedIOException.java b/src/com/ibm/icu/util/ICUUncheckedIOException.java
new file mode 100644
index 0000000..fd2a162
--- /dev/null
+++ b/src/com/ibm/icu/util/ICUUncheckedIOException.java
@@ -0,0 +1,66 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2014, International Business Machines Corporation and
+ * others. All Rights Reserved.
+ *******************************************************************************
+ */
+package com.ibm.icu.util;
+
+/**
+ * Unchecked version of {@link java.io.IOException}.
+ * Some ICU APIs do not throw the standard exception but instead wrap it
+ * into this unchecked version.
+ *
+ * <p>This currently extends {@link RuntimeException},
+ * but when ICU can rely on Java 8 this class should be changed to extend
+ * java.io.UncheckedIOException instead.
+ *
+ * @draft ICU 53
+ * @provisional This API might change or be removed in a future release.
+ */
+public class ICUUncheckedIOException extends RuntimeException {
+    private static final long serialVersionUID = 1210263498513384449L;
+
+    /**
+     * Default constructor.
+     *
+     * @draft ICU 53
+     * @provisional This API might change or be removed in a future release.
+     */
+    public ICUUncheckedIOException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message exception message string
+     * @draft ICU 53
+     * @provisional This API might change or be removed in a future release.
+     */
+    public ICUUncheckedIOException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param cause original exception (normally a {@link java.io.IOException})
+     * @draft ICU 53
+     * @provisional This API might change or be removed in a future release.
+     */
+    public ICUUncheckedIOException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message exception message string
+     * @param cause original exception (normally a {@link java.io.IOException})
+     * @draft ICU 53
+     * @provisional This API might change or be removed in a future release.
+     */
+    public ICUUncheckedIOException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/com/ibm/icu/util/Output.java b/src/com/ibm/icu/util/Output.java
new file mode 100644
index 0000000..2f40475
--- /dev/null
+++ b/src/com/ibm/icu/util/Output.java
@@ -0,0 +1,45 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2011-2012, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ */
+package com.ibm.icu.util;
+
+/**
+ * Simple struct-like class for output parameters.
+ * @param <T> The type of the parameter.
+ * @stable ICU 4.8
+ */
+public class Output<T> {
+    /**
+     * The value field
+     * @stable ICU 4.8
+     */
+    public T value;
+
+    /**
+     * {@inheritDoc}
+     * @stable ICU 4.8
+     */
+    public String toString() {
+        return value == null ? "null" : value.toString();
+    }
+
+    /**
+     * Constructs an empty <code>Output</code>
+     * @stable ICU 4.8
+     */
+    public Output() {
+        
+    }
+
+    /**
+     * Constructs an <code>Output</code> withe the given value.
+     * @param value the initial value
+     * @stable ICU 4.8
+     */
+    public Output(T value) {
+        this.value = value;
+    }
+}
diff --git a/tests/src/com/android/messageformat/SimpleMessageFormatTest.java b/tests/src/com/android/messageformat/SimpleMessageFormatTest.java
new file mode 100644
index 0000000..e132b14
--- /dev/null
+++ b/tests/src/com/android/messageformat/SimpleMessageFormatTest.java
@@ -0,0 +1,82 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2014, International Business Machines Corporation and
+ * others. All Rights Reserved.
+ *******************************************************************************
+ */
+package com.android.messageformat;
+
+import java.util.Date;
+import java.util.Locale;
+import junit.framework.TestCase;
+
+public class SimpleMessageFormatTest extends TestCase {
+    public void testBasic() {
+        assertEquals("one simple argument", "Going to Germany and back",
+                MessageFormat.formatNamedArgs(
+                        Locale.US, "Going to {place} and back", "place", "Germany"));
+    }
+
+    public void testSelect() {
+        String msg = "{gender,select,female{her book}male{his book}other{their book}}";
+        assertEquals("select female", "her book",
+                MessageFormat.formatNamedArgs(Locale.US, msg, "gender", "female"));
+        assertEquals("select male", "his book",
+                MessageFormat.formatNamedArgs(Locale.US, msg, "gender", "male"));
+        assertEquals("select neutral", "their book",
+                MessageFormat.formatNamedArgs(Locale.US, msg, "gender", "unknown"));
+    }
+
+    public void testPlural() {
+        // Using Serbian, see
+        // http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
+        Locale sr = new Locale("sr");
+        String msg =
+                "{num,plural,offset:1 =1{only {name}}=2{{name} and one other}" +
+                        "one{{name} and #-one others}few{{name} and #-few others}" +
+                        "other{{name} and #... others}}";
+        assertEquals("plural 1", "only Peter",
+                MessageFormat.formatNamedArgs(sr, msg, "num", 1, "name", "Peter"));
+        assertEquals("plural 2", "Paul and one other",
+                MessageFormat.formatNamedArgs(sr, msg, "num", 2, "name", "Paul"));
+        assertEquals("plural 22", "Mary and 21-one others",
+                MessageFormat.formatNamedArgs(sr, msg, "num", 22, "name", "Mary"));
+        assertEquals("plural 33", "John and 32-few others",
+                MessageFormat.formatNamedArgs(sr, msg, "num", 33, "name", "John"));
+        assertEquals("plural 6", "Yoko and 5... others",
+                MessageFormat.formatNamedArgs(sr, msg, "num", 6, "name", "Yoko"));
+    }
+
+    public void testSelectAndPlural() {
+        Locale ja = Locale.JAPANESE;  // always "other"
+        String msg =
+                "{gender,select,female{" +
+                        "{num,plural,=1{her book}other{her # books}}" +
+                        "}male{" +
+                        "{num,plural,=1{his book}other{his # books}}" +
+                        "}other{" +
+                        "{num,plural,=1{their book}other{their # books}}" +
+                        "}}";
+        assertEquals("female 1", "her book",
+                MessageFormat.formatNamedArgs(ja, msg, "gender", "female", "num", 1));
+        assertEquals("male 2", "his 2 books",
+                MessageFormat.formatNamedArgs(ja, msg, "gender", "male", "num", 2));
+        assertEquals("unknown 3000", "their 3,000 books",
+                MessageFormat.formatNamedArgs(ja, msg, "gender", "?", "num", 3000));
+    }
+
+    public void testSelectOrdinal() {
+        Locale en = Locale.ENGLISH;
+        String msg =
+                "{num,selectordinal,one{#st floor}two{#nd floor}few{#rd floor}other{#th floor}}";
+        assertEquals("91", "91st floor",
+                MessageFormat.formatNamedArgs(en, msg, "num", 91));
+        assertEquals("22", "22nd floor",
+                MessageFormat.formatNamedArgs(en, msg, "num", 22));
+        assertEquals("33", "33rd floor",
+                MessageFormat.formatNamedArgs(en, msg, "num", 33));
+        assertEquals("11", "11th floor",
+                MessageFormat.formatNamedArgs(en, msg, "num", 11));
+    }
+}
+