package com.google.gwt.lang; import com.google.gwt.lang.LongLibBase.LongEmul; import junit.framework.TestCase; import java.util.Random; import java.util.Set; import java.util.TreeSet; public class LongLibTest extends TestCase { private static abstract class BinaryOp extends Op { public BinaryOp(String name) { super(name); } public abstract long ref(long longVal0, long longVal1); public abstract LongEmul test(LongEmul longVal0, LongEmul longVal1); } private static abstract class BooleanOp extends Op { public BooleanOp(String name) { super(name); } public abstract boolean ref(long longVal0, long longVal1); public abstract boolean test(LongEmul longVal0, LongEmul longVal1); } private static abstract class CompareOp extends Op { public CompareOp(String name) { super(name); } public abstract int ref(long longVal0, long longVal1); public abstract int test(LongEmul longVal0, LongEmul longVal1); } private static abstract class Op { String name; public Op(String name) { this.name = name; } public String getName() { return name; } } private static abstract class ShiftOp extends Op { public ShiftOp(String name) { super(name); } public abstract long ref(long longVal, int shift); public abstract LongEmul test(LongEmul longVak, int shift); } private static abstract class UnaryOp extends Op { public UnaryOp(String name) { super(name); } public abstract long ref(long longVal); public abstract LongEmul test(LongEmul longVal); } private static final int BASE_VALUES = 128; private static final BinaryOp OP_ADD = new BinaryOp("ADD") { @Override public long ref(long longVal0, long longVal1) { return longVal0 + longVal1; } @Override public LongEmul test(LongEmul longVal0, LongEmul longVal1) { return LongLib.add(longVal0, longVal1); } }; private static final BinaryOp OP_AND = new BinaryOp("AND") { @Override public long ref(long longVal0, long longVal1) { return longVal0 & longVal1; } @Override public LongEmul test(LongEmul longVal0, LongEmul longVal1) { return LongLib.and(longVal0, longVal1); } }; private static final CompareOp OP_COMPARE = new CompareOp("COMPARE") { @Override public int ref(long longVal0, long longVal1) { if (longVal0 < longVal1) { return -1; } if (longVal0 > longVal1) { return 1; } return 0; } @Override public int test(LongEmul longVal0, LongEmul longVal1) { return LongLib.compare(longVal0, longVal1); } }; private static final BinaryOp OP_DIV = new BinaryOp("DIV") { @Override public long ref(long longVal0, long longVal1) { return longVal0 / longVal1; } @Override public LongEmul test(LongEmul longVal0, LongEmul longVal1) { return LongLib.div(longVal0, longVal1); } }; private static final BooleanOp OP_EQ = new BooleanOp("EQ") { @Override public boolean ref(long longVal0, long longVal1) { return longVal0 == longVal1; } @Override public boolean test(LongEmul longVal0, LongEmul longVal1) { return LongLib.eq(longVal0, longVal1); } }; private static final BooleanOp OP_GT = new BooleanOp("GT") { @Override public boolean ref(long longVal0, long longVal1) { return longVal0 > longVal1; } @Override public boolean test(LongEmul longVal0, LongEmul longVal1) { return LongLib.gt(longVal0, longVal1); } }; private static final BooleanOp OP_GTE = new BooleanOp("GTE") { @Override public boolean ref(long longVal0, long longVal1) { return longVal0 >= longVal1; } @Override public boolean test(LongEmul longVal0, LongEmul longVal1) { return LongLib.gte(longVal0, longVal1); } }; private static final BooleanOp OP_LT = new BooleanOp("LT") { @Override public boolean ref(long longVal0, long longVal1) { return longVal0 < longVal1; } @Override public boolean test(LongEmul longVal0, LongEmul longVal1) { return LongLib.lt(longVal0, longVal1); } }; private static final BooleanOp OP_LTE = new BooleanOp("LTE") { @Override public boolean ref(long longVal0, long longVal1) { return longVal0 <= longVal1; } @Override public boolean test(LongEmul longVal0, LongEmul longVal1) { return LongLib.lte(longVal0, longVal1); } }; private static final BinaryOp OP_MOD = new BinaryOp("MOD") { @Override public long ref(long longVal0, long longVal1) { return longVal0 % longVal1; } @Override public LongEmul test(LongEmul longVal0, LongEmul longVal1) { return LongLib.mod(longVal0, longVal1); } }; private static final BinaryOp OP_MUL = new BinaryOp("MUL") { @Override public long ref(long longVal0, long longVal1) { return longVal0 * longVal1; } @Override public LongEmul test(LongEmul longVal0, LongEmul longVal1) { return LongLib.mul(longVal0, longVal1); } }; private static final UnaryOp OP_NEG = new UnaryOp("NEG") { @Override public long ref(long longVal) { return -longVal; } @Override public LongEmul test(LongEmul longVal) { return LongLib.neg(longVal); } }; private static final BooleanOp OP_NEQ = new BooleanOp("NEQ") { @Override public boolean ref(long longVal0, long longVal1) { return longVal0 != longVal1; } @Override public boolean test(LongEmul longVal0, LongEmul longVal1) { return LongLib.neq(longVal0, longVal1); } }; private static final UnaryOp OP_NOT = new UnaryOp("NOT") { @Override public long ref(long longVal) { return ~longVal; } @Override public LongEmul test(LongEmul longVal) { return LongLib.not(longVal); } }; private static final BinaryOp OP_OR = new BinaryOp("OR") { @Override public long ref(long longVal0, long longVal1) { return longVal0 | longVal1; } @Override public LongEmul test(LongEmul longVal0, LongEmul longVal1) { return LongLib.or(longVal0, longVal1); } }; private static final ShiftOp OP_SHL = new ShiftOp("SHL") { @Override public long ref(long longVal, int shift) { return longVal << shift; } @Override public LongEmul test(LongEmul longVal, int shift) { return LongLib.shl(longVal, shift); } }; private static final ShiftOp OP_SHR = new ShiftOp("SHR") { @Override public long ref(long longVal, int shift) { return longVal >> shift; } @Override public LongEmul test(LongEmul longVal, int shift) { return LongLib.shr(longVal, shift); } }; private static final ShiftOp OP_SHRU = new ShiftOp("SHRU") { @Override public long ref(long longVal, int shift) { return longVal >>> shift; } @Override public LongEmul test(LongEmul longVal, int shift) { return LongLib.shru(longVal, shift); } }; private static final BinaryOp OP_SUB = new BinaryOp("SUB") { @Override public long ref(long longVal0, long longVal1) { return longVal0 - longVal1; } @Override public LongEmul test(LongEmul longVal0, LongEmul longVal1) { return LongLib.sub(longVal0, longVal1); } }; private static final BinaryOp OP_XOR = new BinaryOp("XOR") { @Override public long ref(long longVal0, long longVal1) { return longVal0 ^ longVal1; } @Override public LongEmul test(LongEmul longVal0, LongEmul longVal1) { return LongLib.xor(longVal0, longVal1); } }; private static final Random rand = new Random(1); private static final int RANDOM_TESTS = 1000; private static long[] TEST_VALUES; static { LongLibBase.RUN_IN_JVM = true; } static { Set<Long> testSet = new TreeSet<Long>(); for (long i = 0; i < BASE_VALUES; i++) { testSet.add(i); testSet.add(-i); testSet.add(Long.MIN_VALUE + i); testSet.add(Long.MAX_VALUE - i); testSet.add(i << LongLibBase.BITS / 2); testSet.add(i << LongLibBase.BITS); testSet.add(i << (3 * LongLibBase.BITS) / 2); testSet.add(i << 2 * LongLibBase.BITS); testSet.add(i << (5 * LongLibBase.BITS) / 2); } for (int i = 0; i < 16; i++) { testSet.add(0x1111111111111111L * i); testSet.add(~(0x1111111111111111L * i)); testSet.add(-(0x1111111111111111L * i)); testSet.add(0x1010101010101010L * i); testSet.add(~(0x1010101010101010L * i)); testSet.add(-(0x1010101010101010L * i)); testSet.add(0x0101010101010101L * i); testSet.add(~(0x0101010101010101L * i)); testSet.add(-(0x0101010101010101L * i)); testSet.add(0x123456789ABCDEFFL * i); testSet.add(~(0x123456789ABCDEFFL * i)); testSet.add(-(0x123456789ABCDEFFL * i)); } for (int i = 0; i < 64; i += 4) { testSet.add(0x1L << i); testSet.add(~(0x1L << i)); testSet.add(0x123456789ABCDEFFL >> i); testSet.add(-(0x123456789ABCDEFFL >> i)); // Powers of two and nearby numbers testSet.add(0x1L << i); for (int j = 1; j <= 16; j++) { testSet.add((0x1L << i) + j); testSet.add(-((0x1L << i) + j)); testSet.add(~((0x1L << i) + j)); testSet.add((0x1L << i) - j); testSet.add(-((0x1L << i) - j)); testSet.add(~((0x1L << i) - j)); testSet.add((0x3L << i) + j); testSet.add(-((0x3L << i) + j)); testSet.add(~((0x3L << i) + j)); testSet.add((0x3L << i) - j); testSet.add(-((0x3L << i) - j)); testSet.add(~((0x3L << i) - j)); } } for (int a = 0; a < 19; a++) { testSet.add((long) Math.pow(10, a)); } TEST_VALUES = new long[testSet.size()]; int index = 0; for (long val : testSet) { TEST_VALUES[index++] = val; } System.out.println("VALUES.length = " + index); } public static void testAdd() { doTestBinary(OP_ADD); } public static void testAnd() { doTestBinary(OP_AND); } public static void testBase64() { assertEquals("A", LongLib.toBase64(0x0L)); assertEquals(0x0L, LongLib.longFromBase64("A")); assertEquals("B", LongLib.toBase64(0x1L)); assertEquals(0x1L, LongLib.longFromBase64("B")); assertEquals("BA", LongLib.toBase64(0x40L)); assertEquals(0x40L, LongLib.longFromBase64("BA")); assertEquals("P_________A", LongLib.toBase64(-0x40L)); assertEquals(-0x40L, LongLib.longFromBase64("P_________A")); assertEquals("P__________", LongLib.toBase64(-1L)); assertEquals(-1L, LongLib.longFromBase64("P__________")); // Use all types of base 64 chars long value = 0L; value |= 15L << 60; // 'P' value |= 35L << 54; // 'j' value |= 44L << 48; // 's' value |= 62L << 42; // '$' value |= 26L << 36; // 'a' value |= 9L << 30; // 'J' value |= 18L << 24; // 'S' value |= 25L << 18; // 'Z' value |= 52L << 12; // '0' value |= 57L << 6; // '5' value |= 63L; // '_' String s = "Pjs$aJSZ05_"; assertEquals(s, LongLib.toBase64(value)); assertEquals(value, LongLib.longFromBase64(s)); } public static void testCompare() { doTestCompare(OP_COMPARE); } public static void testDiv() { doTestBinary(OP_DIV); } public static void testEq() { doTestBoolean(OP_EQ); } public static void testGetAsIntArray() { long longVal = 0x123456789abcdef0L; int[] array = LongLib.getAsIntArray(longVal); assertEquals(0x12345, array[2]); assertEquals(0x19e26a, array[1]); assertEquals(0x3cdef0, array[0]); longVal = -longVal; array = LongLib.getAsIntArray(longVal); assertEquals(0xedcba, array[2]); assertEquals(0x261d95, array[1]); assertEquals(0x32110, array[0]); } public static void testGt() { doTestBoolean(OP_GT); } public static void testGte() { doTestBoolean(OP_GTE); } public static void testLt() { doTestBoolean(OP_LT); } public static void testLte() { doTestBoolean(OP_LTE); } public static void testMod() { doTestBinary(OP_MOD); } public static void testMul() { doTestBinary(OP_MUL); } public static void testNeg() { doTestUnary(OP_NEG); } public static void testNeq() { doTestBoolean(OP_NEQ); } public static void testNot() { doTestUnary(OP_NOT); } public static void testNumberOfLeadingZeros() { LongEmul longVal0 = fromLong(0xfedcba9876543210L); for (int i = 0; i <= 64; i++) { assertEquals(i, LongLibBase.numberOfLeadingZeros(longVal0)); longVal0 = LongLib.shru(longVal0, 1); } } public static void testOr() { doTestBinary(OP_OR); } public static void testRoundTrip() { System.out.println("ROUND_TRIP"); for (int i = 0; i < TEST_VALUES.length + RANDOM_TESTS; i++) { long longVal0 = (i < TEST_VALUES.length) ? TEST_VALUES[i] : rand.nextLong(); LongEmul longVal1 = fromLong(longVal0); long l2 = toLong(longVal1); if (longVal0 != l2) { fail("longVal0 = " + longVal0 + ", l2 = " + l2); } if (!toHex(longVal0).equals(toHex(longVal1))) { fail("toHex(longVal0) = " + toHex(longVal0) + ", toHex(longVal1) = " + toHex(longVal1)); } if (!toHex(longVal0).equals(toHex(l2))) { fail("toHex(longVal0) = " + toHex(longVal0) + ", toHex(l2) = " + toHex(l2)); } if (!LongLib.toString(longVal1).equals(Long.toString(longVal0))) { fail("toString(longVal0) = " + Long.toString(longVal0) + ", toString(longVal1) = " + LongLib.toString(longVal1)); } } for (int i = 0; i < TEST_VALUES.length + RANDOM_TESTS; i++) { double d0 = (i < TEST_VALUES.length) ? TEST_VALUES[i] : (rand.nextDouble() - 0.5) * 3.0 * Long.MAX_VALUE; long longVal0 = (long) d0; LongEmul longVal1 = LongLib.fromDouble(d0); long l2 = toLong(longVal1); if (longVal0 != l2) { fail("d0 = " + d0 + ", longVal0 = " + longVal0 + ", l2 = " + l2); } } for (int i = 0; i < TEST_VALUES.length + RANDOM_TESTS; i++) { long longVal0 = i < TEST_VALUES.length ? TEST_VALUES[i] : rand.nextLong(); // Find a round-trip capable value double d0 = longVal0; longVal0 = (long) d0; LongEmul longVal1 = fromLong(longVal0); double d1 = LongLib.toDouble(longVal1); LongEmul l2 = LongLib.fromDouble(d1); long l3 = toLong(l2); if (longVal0 != l3) { fail("longVal0 = " + longVal0 + ", d1 = " + d1 + ", l3 = " + l3); } } for (int i = 0; i < TEST_VALUES.length + RANDOM_TESTS; i++) { int i0 = i < TEST_VALUES.length ? (int) TEST_VALUES[i] : rand.nextInt(); long longVal0 = i0; LongEmul longVal1 = LongLib.fromInt(i0); long l2 = toLong(longVal1); if (longVal0 != l2) { fail("i0 = " + i0 + ", longVal0 = " + longVal0 + ", l2 = " + l2); } } if (toLong(LongLib.fromDouble(Double.NaN)) != 0) { fail("fromDouble(Nan) != 0"); } if (toLong(LongLib.fromDouble(Double.NEGATIVE_INFINITY)) != Long.MIN_VALUE) { fail("fromDouble(-Inf) != MIN_VALUE"); } if (toLong(LongLib.fromDouble(Double.POSITIVE_INFINITY)) != Long.MAX_VALUE) { fail("fromDouble(+Inf) != MAX_VALUE"); } if (LongLib.toDouble(fromLong(Long.MIN_VALUE)) != -9223372036854775808.0) { fail("toDouble(Long.MIN_VALUE) != -9223372036854775808.0"); } if (LongLib.toDouble(fromLong(Long.MAX_VALUE)) != 9223372036854775807.0) { fail("toDouble(Long.MAX_VALUE) != 9223372036854775807.0"); } } public static void testShl() { doTestShift(OP_SHL); } public static void testShr() { doTestShift(OP_SHR); } public static void testShru() { doTestShift(OP_SHRU); } public static void testSub() { doTestBinary(OP_SUB); } public static void testXor() { doTestBinary(OP_XOR); } private static LongEmul copy(LongEmul longVal) { LongEmul result = new LongEmul(); result.l = longVal.l; result.m = longVal.m; result.h = longVal.h; return result; } private static void doTestBinary(BinaryOp op) { System.out.println(op.getName()); for (int i = 0; i < TEST_VALUES.length; i++) { long randomLong = rand.nextLong(); doTestBinary(op, TEST_VALUES[i], randomLong); doTestBinary(op, randomLong, TEST_VALUES[i]); for (int j = 0; j < TEST_VALUES.length; j++) { doTestBinary(op, TEST_VALUES[i], TEST_VALUES[j]); } } for (int i = 0; i < RANDOM_TESTS; i++) { long longVal0 = rand.nextLong(); long longVal1 = rand.nextLong(); if (rand.nextInt(20) == 0) { if (rand.nextInt(2) == 0) { longVal1 = longVal0; } else { longVal1 = -longVal0; } } doTestBinary(op, longVal0, longVal1); } } private static void doTestBinary(BinaryOp op, long longVal0, long longVal1) { boolean refException = false; long ref = -1; try { ref = op.ref(longVal0, longVal1); } catch (ArithmeticException e) { refException = true; } boolean testException = false; long result = -2; try { LongEmul llongVal0 = fromLong(longVal0); LongEmul llongVal1 = fromLong(longVal1); LongEmul save_llongVal0 = copy(llongVal0); LongEmul save_llongVal1 = copy(llongVal1); result = toLong(op.test(llongVal0, llongVal1)); if (!LongLib.eq(llongVal0, save_llongVal0)) { System.out.println("Test altered first argument"); } if (!LongLib.eq(llongVal1, save_llongVal1)) { System.out.println("Test altered second argument"); } } catch (ArithmeticException e) { testException = true; } if (testException && refException) { return; } if (testException != refException) { fail(op.getName() + ": longVal0 = " + longVal0 + ", longVal1 = " + longVal1 + ", testException = " + testException + ", refException = " + refException); return; } if (ref != result) { fail(op.getName() + ": longVal0 = " + longVal0 + ", longVal1 = " + longVal1); toLong(op.test(fromLong(longVal0), fromLong(longVal1))); } } private static void doTestBoolean(BooleanOp op) { System.out.println(op.getName()); for (int i = 0; i < TEST_VALUES.length; i++) { long randomLong = rand.nextLong(); doTestBoolean(op, TEST_VALUES[i], randomLong); doTestBoolean(op, randomLong, TEST_VALUES[i]); for (int j = 0; j < TEST_VALUES.length; j++) { doTestBoolean(op, TEST_VALUES[i], TEST_VALUES[j]); } } for (int i = 0; i < RANDOM_TESTS; i++) { long longVal0 = rand.nextLong(); long longVal1 = rand.nextLong(); if (rand.nextInt(20) == 0) { if (rand.nextInt(2) == 0) { longVal1 = longVal0; } else { longVal1 = -longVal0; } } doTestBoolean(op, longVal0, longVal1); } } private static void doTestBoolean(BooleanOp op, long longVal0, long longVal1) { boolean ref = op.ref(longVal0, longVal1); boolean result = op.test(fromLong(longVal0), fromLong(longVal1)); if (ref != result) { fail(op.getName() + ": longVal0 = " + longVal0 + ", longVal1 = " + longVal1); } } private static void doTestCompare(CompareOp op) { System.out.println(op.getName()); for (int i = 0; i < TEST_VALUES.length; i++) { long randomLong = rand.nextLong(); doTestCompare(op, TEST_VALUES[i], randomLong); doTestCompare(op, randomLong, TEST_VALUES[i]); for (int j = 0; j < TEST_VALUES.length; j++) { doTestCompare(op, TEST_VALUES[i], TEST_VALUES[j]); } } for (int i = 0; i < RANDOM_TESTS; i++) { long longVal0 = rand.nextLong(); long longVal1 = rand.nextLong(); if (rand.nextInt(20) == 0) { if (rand.nextInt(2) == 0) { longVal1 = longVal0; } else { longVal1 = -longVal0; } } doTestCompare(op, longVal0, longVal1); } } private static void doTestCompare(CompareOp op, long longVal0, long longVal1) { int ref = op.ref(longVal0, longVal1); int result = op.test(fromLong(longVal0), fromLong(longVal1)); if (ref < 0) { ref = -1; } else if (ref > 0) { ref = 1; } if (result < 0) { result = -1; } else if (result > 0) { result = 1; } if (ref != result) { fail(op.getName() + ": longVal0 = " + longVal0 + ", longVal1 = " + longVal1 + ", ref = " + ref + ", result = " + result); } } private static void doTestShift(ShiftOp op) { System.out.println(op.getName()); for (int i = 0; i < TEST_VALUES.length; i++) { for (int shift = -64; shift <= 64; shift++) { doTestShift(op, TEST_VALUES[i], shift); } } for (int i = 0; i < RANDOM_TESTS; i++) { long randomLong = rand.nextLong(); for (int shift = -64; shift <= 64; shift++) { doTestShift(op, randomLong, shift); } } } private static void doTestShift(ShiftOp op, long longVal, int shift) { long ref = op.ref(longVal, shift); long result = toLong(op.test(fromLong(longVal), shift)); if (ref != result) { fail(op.getName() + ": longVal = " + longVal + ", shift = " + shift); } } private static void doTestUnary(UnaryOp op) { System.out.println(op.getName()); for (int i = 0; i < TEST_VALUES.length; i++) { doTestUnary(op, TEST_VALUES[i]); } for (int i = 0; i < RANDOM_TESTS; i++) { long randomLong = rand.nextLong(); doTestUnary(op, randomLong); } } private static void doTestUnary(UnaryOp op, long longVal) { long ref = op.ref(longVal); long result = toLong(op.test(fromLong(longVal))); if (ref != result) { fail(op.getName() + ": longVal = " + longVal); } } private static LongEmul fromLong(long longVal) { LongEmul result = new LongEmul(); result.l = (int) (longVal & LongLibBase.MASK); result.m = (int) ((longVal >> LongLibBase.BITS) & LongLibBase.MASK); result.h = (int) ((longVal >> (2 * LongLibBase.BITS)) & LongLibBase.MASK_2); return result; } private static int getBit(LongEmul longVal, int bit) { if (bit < LongLibBase.BITS) { return (longVal.l >> bit) & 0x1; } if (bit < 2 * LongLibBase.BITS) { return (longVal.m >> (bit - LongLibBase.BITS)) & 0x1; } return (longVal.h >> (bit - (2 * LongLibBase.BITS))) & 0x1; } private static String toHex(long longVal) { String result = Long.toHexString(longVal); while (result.length() < 16) { result = "0" + result; } return result; } private static String toHex(LongEmul longVal) { StringBuilder sb = new StringBuilder(); for (int i = 60; i >= 0; i -= 4) { int b0 = getBit(longVal, i); int b1 = getBit(longVal, i + 1); int b2 = getBit(longVal, i + 2); int b3 = getBit(longVal, i + 3); int b = (b3 << 3) + (b2 << 2) + (b1 << 1) + b0; if (b < 10) { sb.append(b); } else { sb.append((char) ('a' + b - 10)); } } return sb.toString(); } private static long toLong(LongEmul longVal) { long b2 = ((long) longVal.h) << (2 * LongLibBase.BITS); long b1 = ((long) longVal.m) << LongLibBase.BITS; return b2 | b1 | longVal.l; } public LongLibTest() { } }