/* ******************************************************************************* * Copyright (C) 1996-2009, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ package com.ibm.icu.dev.test.util; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import com.ibm.icu.dev.test.TestBoilerplate; import com.ibm.icu.dev.test.TestFmwk; import com.ibm.icu.impl.Utility; import com.ibm.icu.lang.UCharacter; import com.ibm.icu.lang.UProperty; import com.ibm.icu.text.UnicodeSet; public class TestUtilities extends TestFmwk { static final int LIMIT = 0x15; // limit to make testing more realistic in terms of collisions static final int ITERATIONS = 1000000; static final boolean SHOW_PROGRESS = false; static final boolean DEBUG = false; public static void main(String[] args) throws Exception { new TestUtilities().run(args); } UnicodeMap map1 = new UnicodeMap(); Map map2 = new HashMap(); Map map3 = new TreeMap(); SortedSet log = new TreeSet(); static String[] TEST_VALUES = {null, "A", "B", "C", "D", "E", "F"}; static Random random = new Random(12345); public void TestUnicodeMap() { random.setSeed(12345); // do random change to both, then compare logln("Comparing against HashMap"); for (int counter = 0; counter < ITERATIONS; ++counter) { int start = random.nextInt(LIMIT); String value = TEST_VALUES[random.nextInt(TEST_VALUES.length)]; String logline = Utility.hex(start) + "\t" + value; if (SHOW_PROGRESS) logln(counter + "\t" + logline); log.add(logline); if (DEBUG && counter == 144) { System.out.println(" debug"); } map1.put(start, value); map2.put(new Integer(start), value); check(counter); } checkNext(LIMIT); logln("Setting General Category"); map1 = new UnicodeMap(); map2 = new TreeMap(); for (int cp = 0; cp <= SET_LIMIT; ++cp) { int enumValue = UCharacter.getIntPropertyValue(cp, propEnum); //if (enumValue <= 0) continue; // for smaller set String value = UCharacter.getPropertyValueName(propEnum,enumValue, UProperty.NameChoice.LONG); map1.put(cp, value); map2.put(new Integer(cp), value); } checkNext(Integer.MAX_VALUE); logln("Comparing General Category"); check(-1); logln("Comparing Values"); Set values1 = (Set) map1.getAvailableValues(new TreeSet()); Set values2 = new TreeSet(map2.values()); if (!TestBoilerplate.verifySetsIdentical(this, values1, values2)) { throw new IllegalArgumentException("Halting"); } logln("Comparing Sets"); for (Iterator it = values1.iterator(); it.hasNext();) { Object value = it.next(); logln(value == null ? "null" : value.toString()); UnicodeSet set1 = map1.keySet(value); UnicodeSet set2 = TestBoilerplate.getSet(map2, value); if (!TestBoilerplate.verifySetsIdentical(this, set1, set2)) { throw new IllegalArgumentException("Halting"); } } logln("Getting Scripts"); UnicodeMap scripts = ICUPropertyFactory.make().getProperty("script").getUnicodeMap_internal(); UnicodeMap.Composer composer = new UnicodeMap.Composer() { public Object compose(int codepoint, String string, Object a, Object b) { return a.toString() + "_" + b.toString(); } }; logln("Trying Compose"); UnicodeMap composed = ((UnicodeMap)scripts.cloneAsThawed()).composeWith(map1, composer); Object last = ""; for (int i = 0; i < 0x10FFFF; ++i) { Object comp = composed.getValue(i); Object gc = map1.getValue(i); Object sc = scripts.getValue(i); if (!comp.equals(composer.compose(i, null, gc, sc))) { errln("Failed compose at: " + i); } if (!last.equals(comp)) { logln(Utility.hex(i) + "\t" + comp); last = comp; } } // check boilerplate List argList = new ArrayList(); argList.add("TestMain"); if (params.nothrow) argList.add("-nothrow"); if (params.verbose) argList.add("-verbose"); String[] args = new String[argList.size()]; argList.toArray(args); new UnicodeMapBoilerplate().run(args); // TODO: the following is not being reached new UnicodeSetBoilerplate().run(args); } public void TestCollectionUtilitySpeed() { TreeSet ts1 = new TreeSet(); TreeSet ts2 = new TreeSet(); int size = 1000; int iterations = 1000; String prefix = "abc"; String postfix = "nop"; for (int i = 0; i < size; ++i) { ts1.add(prefix + String.valueOf(i) + postfix); ts2.add(prefix + String.valueOf(i) + postfix); } // warm up CollectionUtilities.containsAll(ts1, ts2); ts1.containsAll(ts2); timeAndCompare(ts1, ts2, iterations, true, .75); // now different sets ts1.add("Able"); timeAndCompare(ts1, ts2, iterations, true, .75); timeAndCompare(ts2, ts1, iterations*100, false, 1.05); } private void timeAndCompare(TreeSet ts1, TreeSet ts2, int iterations, boolean expected, double factorOfStandard) { double utilityTimeSorted = timeUtilityContainsAll(iterations, ts1, ts2, expected)/(double)iterations; double standardTimeSorted = timeStandardContainsAll(iterations, ts1, ts2, expected)/(double)iterations; if (utilityTimeSorted < standardTimeSorted*factorOfStandard) { logln("Sorted: Utility time (" + utilityTimeSorted + ") << Standard duration (" + standardTimeSorted + "); " + 100*(utilityTimeSorted/standardTimeSorted) + "%"); } else { errln("Sorted: Utility time (" + utilityTimeSorted + ") !<< Standard duration (" + standardTimeSorted + "); " + 100*(utilityTimeSorted/standardTimeSorted) + "%"); } } private long timeStandardContainsAll(int iterations, Set hs1, Set hs2, boolean expected) { long standardTime; { long start, end; boolean temp = false; start = System.currentTimeMillis(); for (int i = 0; i < iterations; ++i) { temp = hs1.containsAll(hs2); if (temp != expected) { errln("Bad result"); } } end = System.currentTimeMillis(); standardTime = end - start; } return standardTime; } private long timeUtilityContainsAll(int iterations, Set hs1, Set hs2, boolean expected) { long utilityTime; { long start, end; boolean temp = false; start = System.currentTimeMillis(); for (int i = 0; i < iterations; ++i) { temp = CollectionUtilities.containsAll(hs1, hs2); if (temp != expected) { errln("Bad result"); } } end = System.currentTimeMillis(); utilityTime = end - start; } return utilityTime; } public void TestCollectionUtilities() { String[][] test = {{"a", "c", "e", "g", "h", "z"}, {"b", "d", "f", "h", "w"}, { "a", "b" }, { "a", "d" }, {"d"}, {}}; // int resultMask = 0; for (int i = 0; i < test.length; ++i) { Collection a = new TreeSet(Arrays.asList(test[i])); for (int j = 0; j < test.length; ++j) { Collection b = new TreeSet(Arrays.asList(test[j])); int relation = CollectionUtilities.getContainmentRelation(a, b); resultMask |= (1 << relation); switch (relation) { case CollectionUtilities.ALL_EMPTY: checkContainment(a.size() == 0 && b.size() == 0, a, relation, b); break; case CollectionUtilities.NOT_A_SUPERSET_B: checkContainment(a.size() == 0 && b.size() != 0, a, relation, b); break; case CollectionUtilities.NOT_A_DISJOINT_B: checkContainment(a.equals(b) && a.size() != 0, a, relation, b); break; case CollectionUtilities.NOT_A_SUBSET_B: checkContainment(a.size() != 0 && b.size() == 0, a, relation, b); break; case CollectionUtilities.A_PROPER_SUBSET_OF_B: checkContainment(b.containsAll(a) && !a.equals(b), a, relation, b); break; case CollectionUtilities.NOT_A_EQUALS_B: checkContainment(!CollectionUtilities.containsSome(a, b) && a.size() != 0 && b.size() != 0, a, relation, b); break; case CollectionUtilities.A_PROPER_SUPERSET_B: checkContainment(a.containsAll(b) && !a.equals(b), a, relation, b); break; case CollectionUtilities.A_PROPER_OVERLAPS_B: checkContainment(!b.containsAll(a) && !a.containsAll(b) && CollectionUtilities.containsSome(a, b), a, relation, b); break; } } } if (resultMask != 0xFF) { String missing = ""; for (int i = 0; i < 8; ++i) { if ((resultMask & (1 << i)) == 0) { if (missing.length() != 0) missing += ", "; missing += RelationName[i]; } } errln("Not all ContainmentRelations checked: " + missing); } } static final String[] RelationName = {"ALL_EMPTY", "NOT_A_SUPERSET_B", "NOT_A_DISJOINT_B", "NOT_A_SUBSET_B", "A_PROPER_SUBSET_OF_B", "A_PROPER_DISJOINT_B", "A_PROPER_SUPERSET_B", "A_PROPER_OVERLAPS_B"}; /** * */ private void checkContainment(boolean c, Collection a, int relation, Collection b) { if (!c) { errln("Fails relation: " + a + " \t" + RelationName[relation] + " \t" + b); } } private void checkNext(int limit) { logln("Comparing nextRange"); UnicodeMapIterator mi = new UnicodeMapIterator(map1); Map localMap = new TreeMap(); while (mi.nextRange()) { logln(Utility.hex(mi.codepoint) + ".." + Utility.hex(mi.codepointEnd) + " => " + mi.value); for (int i = mi.codepoint; i <= mi.codepointEnd; ++i) { if (i >= limit) continue; localMap.put(new Integer(i), mi.value); } } checkMap(map2, localMap); logln("Comparing next"); mi.reset(); localMap = new TreeMap(); Object lastValue = new Object(); while (mi.next()) { if (!UnicodeMap.areEqual(lastValue, mi.value)) { // System.out.println("Change: " + Utility.hex(mi.codepoint) + " => " + mi.value); lastValue = mi.value; } if (mi.codepoint >= limit) continue; localMap.put(new Integer(mi.codepoint), mi.value); } checkMap(map2, localMap); } public void check(int counter) { for (int i = 0; i < LIMIT; ++i) { Object value1 = map1.getValue(i); Object value2 = map2.get(new Integer(i)); if (!UnicodeMap.areEqual(value1, value2)) { errln(counter + " Difference at " + Utility.hex(i) + "\t UnicodeMap: " + value1 + "\t HashMap: " + value2); errln("UnicodeMap: " + map1); errln("Log: " + TestBoilerplate.show(log)); errln("HashMap: " + TestBoilerplate.show(map2)); } } } void checkMap(Map m1, Map m2) { if (m1.equals(m2)) return; StringBuffer buffer = new StringBuffer(); Set m1entries = m1.entrySet(); Set m2entries = m2.entrySet(); getEntries("\r\nIn First, and not Second", m1entries, m2entries, buffer, 20); getEntries("\r\nIn Second, and not First", m2entries, m1entries, buffer, 20); errln(buffer.toString()); } static Comparator ENTRY_COMPARATOR = new Comparator() { public int compare(Object o1, Object o2) { if (o1 == o2) return 0; if (o1 == null) return -1; if (o2 == null) return 1; Map.Entry a = (Map.Entry) o1; Map.Entry b = (Map.Entry) o2; int result = compare2(a.getKey(), b.getKey()); if (result != 0) return result; return compare2(a.getValue(), b.getValue()); } private int compare2(Object o1, Object o2) { if (o1 == o2) return 0; if (o1 == null) return -1; if (o2 == null) return 1; return ((Comparable)o1).compareTo(o2); } }; private void getEntries(String title, Set m1entries, Set m2entries, StringBuffer buffer, int limit) { Set m1_m2 = new TreeSet(ENTRY_COMPARATOR); m1_m2.addAll(m1entries); m1_m2.removeAll(m2entries); buffer.append(title + ": " + m1_m2.size() + "\r\n"); for (Iterator it = m1_m2.iterator(); it.hasNext();) { if (limit-- < 0) return; Map.Entry entry = (Map.Entry) it.next(); buffer.append(entry.getKey()).append(" => ") .append(entry.getValue()).append("\r\n"); } } static final int SET_LIMIT = 0x10FFFF; static final int CHECK_LIMIT = 0xFFFF; static final NumberFormat pf = NumberFormat.getPercentInstance(); static final NumberFormat nf = NumberFormat.getInstance(); public void TestTime() { double hashTime, umTime, icuTime, treeTime; umTime = checkSetTime(20, 0); hashTime = checkSetTime(20, 1); logln("Percentage: " + pf.format(hashTime/umTime)); treeTime = checkSetTime(20, 3); logln("Percentage: " + pf.format(treeTime/umTime)); //logln(map1.toString()); umTime = checkGetTime(1000, 0); hashTime = checkGetTime(1000, 1); logln("Percentage: " + pf.format(hashTime/umTime)); icuTime = checkGetTime(1000, 2); logln("Percentage: " + pf.format(icuTime/umTime)); treeTime = checkGetTime(1000, 3); logln("Percentage: " + pf.format(treeTime/umTime)); } int propEnum = UProperty.GENERAL_CATEGORY; double checkSetTime(int iterations, int type) { _checkSetTime(1,type); double result = _checkSetTime(iterations, type); logln((type == 0 ? "UnicodeMap" : type == 1 ? "HashMap" : type == 2 ? "ICU" : "TreeMap") + "\t" + nf.format(result)); return result; } double _checkSetTime(int iterations, int type) { map1 = new UnicodeMap(); map2 = new HashMap(); System.gc(); double start = System.currentTimeMillis(); for (int j = 0; j < iterations; ++j) for (int cp = 0; cp <= SET_LIMIT; ++cp) { int enumValue = UCharacter.getIntPropertyValue(cp, propEnum); if (enumValue <= 0) continue; // for smaller set String value = UCharacter.getPropertyValueName(propEnum,enumValue, UProperty.NameChoice.LONG); switch(type) { case 0: map1.put(cp, value); break; case 1: map2.put(new Integer(cp), value); break; case 3: map3.put(new Integer(cp), value); break; } } double end = System.currentTimeMillis(); return (end-start)/1000/iterations; } double checkGetTime(int iterations, int type) { _checkGetTime(1,type); double result = _checkGetTime(iterations, type); logln((type == 0 ? "UnicodeMap" : type == 1 ? "HashMap" : type == 2 ? "ICU" : "TreeMap") + "\t" + nf.format(result)); return result; } double _checkGetTime(int iterations, int type) { System.gc(); double start = System.currentTimeMillis(); for (int j = 0; j < iterations; ++j) for (int cp = 0; cp < CHECK_LIMIT; ++cp) { switch (type) { case 0: map1.getValue(cp); break; case 1: map2.get(new Integer(cp)); break; case 2: int enumValue = UCharacter.getIntPropertyValue(cp, propEnum); //if (enumValue <= 0) continue; UCharacter.getPropertyValueName(propEnum,enumValue, UProperty.NameChoice.LONG); break; case 3: map3.get(new Integer(cp)); break; } } double end = System.currentTimeMillis(); return (end-start)/1000/iterations; } static class UnicodeMapBoilerplate extends TestBoilerplate { /* * @see com.ibm.icu.dev.test.TestBoilerplate#_hasSameBehavior(java.lang.Object, java.lang.Object) */ protected boolean _hasSameBehavior(Object a, Object b) { // we are pretty confident in the equals method, so won't bother with this right now. return true; } /* * @see com.ibm.icu.dev.test.TestBoilerplate#_createTestObject() */ protected boolean _addTestObject(List list) { if (list.size() > 30) return false; UnicodeMap result = new UnicodeMap(); for (int i = 0; i < 50; ++i) { int start = random.nextInt(25); String value = TEST_VALUES[random.nextInt(TEST_VALUES.length)]; result.put(start, value); } list.add(result); return true; } } static class StringBoilerplate extends TestBoilerplate { /* * @see com.ibm.icu.dev.test.TestBoilerplate#_hasSameBehavior(java.lang.Object, java.lang.Object) */ protected boolean _hasSameBehavior(Object a, Object b) { // we are pretty confident in the equals method, so won't bother with this right now. return true; } /* * @see com.ibm.icu.dev.test.TestBoilerplate#_createTestObject() */ protected boolean _addTestObject(List list) { if (list.size() > 31) return false; StringBuffer result = new StringBuffer(); for (int i = 0; i < 10; ++i) { result.append((char)random.nextInt(0xFF)); } list.add(result.toString()); return true; } } static class UnicodeSetBoilerplate extends TestBoilerplate { /* * @see com.ibm.icu.dev.test.TestBoilerplate#_hasSameBehavior(java.lang.Object, java.lang.Object) */ protected boolean _hasSameBehavior(Object a, Object b) { // we are pretty confident in the equals method, so won't bother with this right now. return true; } /* * @see com.ibm.icu.dev.test.TestBoilerplate#_createTestObject() */ protected boolean _addTestObject(List list) { if (list.size() > 32) return false; UnicodeSet result = new UnicodeSet(); for (int i = 0; i < 50; ++i) { result.add(random.nextInt(100)); } list.add(result.toString()); return true; } } }