package com.revolsys.geometry.test.old.math; import com.revolsys.geometry.math.DD; import junit.framework.TestCase; import junit.textui.TestRunner; /** * Tests basic arithmetic operations for {@link DD}s. * * @author Martin Davis * */ public class DDBasicTest extends TestCase { public static void main(final String args[]) { TestRunner.run(DDBasicTest.class); } public DDBasicTest(final String name) { super(name); } private void checkAddMult2(final DD dd) { final DD sum = dd.add(dd); final DD prod = dd.multiply(new DD(2.0)); checkErrorBound("AddMult2", sum, prod, 0.0); } void checkBinomial2(final double a, final double b) { // binomial product final DD add = new DD(a); final DD bdd = new DD(b); final DD aPlusb = add.add(bdd); final DD aSubb = add.subtract(bdd); final DD abProd = aPlusb.multiply(aSubb); // System.out.println("(a+b)^2 = " + abSq); // expansion final DD a2dd = add.multiply(add); final DD b2dd = bdd.multiply(bdd); // System.out.println("2ab+b^2 = " + sum); // this should equal b^2 final DD diff = abProd.subtract(a2dd).negate(); // System.out.println("(a+b)^2 - a^2 = " + diff); final DD delta = diff.subtract(b2dd); // System.out.println(); // System.out.println("A = " + a + ", B = " + b); // System.out.println("[DD] (a+b)(a-b) = " + abProd // + " -((a^2 - b^2) - a^2) = " + diff + " delta = " + delta); // printBinomialSquareDouble(a,b); final boolean isSame = diff.equals(b2dd); assertTrue(isSame); final boolean isDeltaZero = delta.isZero(); assertTrue(isDeltaZero); } /** * Computes (a+b)^2 in two different ways and compares the result. * For correct results, a and b should be integers. * * @param a * @param b */ void checkBinomialSquare(final double a, final double b) { // binomial square final DD add = new DD(a); final DD bdd = new DD(b); final DD aPlusb = add.add(bdd); final DD abSq = aPlusb.multiply(aPlusb); // System.out.println("(a+b)^2 = " + abSq); // expansion final DD a2dd = add.multiply(add); final DD b2dd = bdd.multiply(bdd); final DD ab = add.multiply(bdd); final DD sum = b2dd.add(ab).add(ab); // System.out.println("2ab+b^2 = " + sum); final DD diff = abSq.subtract(a2dd); // System.out.println("(a+b)^2 - a^2 = " + diff); final DD delta = diff.subtract(sum); // System.out.println(); // System.out.println("A = " + a + ", B = " + b); // System.out.println("[DD] 2ab+b^2 = " + sum + " (a+b)^2 - a^2 = " // + diff + " delta = " + delta); printBinomialSquareDouble(a, b); final boolean isSame = diff.equals(sum); assertTrue(isSame); final boolean isDeltaZero = delta.isZero(); assertTrue(isDeltaZero); } private void checkDivideMultiply(final DD a, final DD b, final double errBound) { final DD a2 = a.divide(b).multiply(b); checkErrorBound("DivideMultiply", a, a2, errBound); } private void checkErrorBound(final String tag, final DD x, final DD y, final double errBound) { final DD err = x.subtract(y).abs(); // System.out.println(tag + " err=" + err); final boolean isWithinEps = err.doubleValue() <= errBound; assertTrue(isWithinEps); } private void checkMultiplyDivide(final DD a, final DD b, final double errBound) { final DD a2 = a.multiply(b).divide(b); checkErrorBound("MultiplyDivide", a, a2, errBound); } private void checkPow(final double x, final int exp, final double errBound) { final DD xdd = new DD(x); final DD pow = xdd.pow(exp); // System.out.println("Pow(" + x + ", " + exp + ") = " + pow); final DD pow2 = slowPow(xdd, exp); final double err = pow.subtract(pow2).doubleValue(); final boolean isOK = err < errBound; if (!isOK) { // System.out.println("Test slowPow value " + pow2); } assertTrue(err <= errBound); } private void checkReciprocal(final double x, final double errBound) { final DD xdd = new DD(x); final DD rr = xdd.reciprocal().reciprocal(); final double err = xdd.subtract(rr).doubleValue(); // System.out.println("DD Recip = " + xdd + " DD delta= " + err // + " double recip delta= " + (x - 1.0 / (1.0 / x))); assertTrue(err <= errBound); } private void checkSqrt(final DD x, final double errBound) { final DD sqrt = x.sqrt(); final DD x2 = sqrt.multiply(sqrt); checkErrorBound("Sqrt", x, x2, errBound); } private void checkTrunc(final DD x, final DD expected) { final DD trunc = x.trunc(); final boolean isEqual = trunc.equals(expected); assertTrue(isEqual); } void printBinomialSquareDouble(final double a, final double b) { final double sum = 2 * a * b + b * b; final double diff = (a + b) * (a + b) - a * a; // System.out.println("[double] 2ab+b^2= " + sum + " (a+b)^2-a^2= " + diff // + " delta= " + (sum - diff)); } private DD slowPow(final DD x, final int exp) { if (exp == 0) { return DD.valueOf(1.0); } final int n = Math.abs(exp); // MD - could use binary exponentiation for better precision & speed DD pow = new DD(x); for (int i = 1; i < n; i++) { pow = pow.multiply(x); } if (exp < 0) { return pow.reciprocal(); } return pow; } public void testAddMult2() { checkAddMult2(new DD(3)); checkAddMult2(DD.PI); } public void testBinom() { checkBinomialSquare(100.0, 1.0); checkBinomialSquare(1000.0, 1.0); checkBinomialSquare(10000.0, 1.0); checkBinomialSquare(100000.0, 1.0); checkBinomialSquare(1000000.0, 1.0); checkBinomialSquare(1e8, 1.0); checkBinomialSquare(1e10, 1.0); checkBinomialSquare(1e14, 1.0); // Following call will fail, because it requires 32 digits of precision // checkBinomialSquare(1e16, 1.0); checkBinomialSquare(1e14, 291.0); checkBinomialSquare(5e14, 291.0); checkBinomialSquare(5e14, 345291.0); } public void testBinomial2() { checkBinomial2(100.0, 1.0); checkBinomial2(1000.0, 1.0); checkBinomial2(10000.0, 1.0); checkBinomial2(100000.0, 1.0); checkBinomial2(1000000.0, 1.0); checkBinomial2(1e8, 1.0); checkBinomial2(1e10, 1.0); checkBinomial2(1e14, 1.0); checkBinomial2(1e14, 291.0); checkBinomial2(5e14, 291.0); checkBinomial2(5e14, 345291.0); } public void testDivideMultiply() { checkDivideMultiply(DD.PI, DD.E, 1e-30); checkDivideMultiply(new DD(39.4), new DD(10), 1e-30); } public void testMultiplyDivide() { checkMultiplyDivide(DD.PI, DD.E, 1e-30); checkMultiplyDivide(DD.TWO_PI, DD.E, 1e-30); checkMultiplyDivide(DD.PI_2, DD.E, 1e-30); checkMultiplyDivide(new DD(39.4), new DD(10), 1e-30); } public void testNaN() { assertTrue(DD.valueOf(1).divide(DD.valueOf(0)).isNaN()); assertTrue(DD.valueOf(1).multiply(DD.NaN).isNaN()); } public void testPow() { checkPow(0, 3, 16 * DD.EPS); checkPow(14, 3, 16 * DD.EPS); checkPow(3, -5, 16 * DD.EPS); checkPow(-3, 5, 16 * DD.EPS); checkPow(-3, -5, 16 * DD.EPS); checkPow(0.12345, -5, 1e5 * DD.EPS); } public void testReciprocal() { // error bounds are chosen to be "close enough" (i.e. heuristically) // for some reason many reciprocals are exact checkReciprocal(3.0, 0); checkReciprocal(99.0, 1e-29); checkReciprocal(999.0, 0); checkReciprocal(314159269.0, 0); } public void testSqrt() { // the appropriate error bound is determined empirically checkSqrt(DD.PI, 1e-30); checkSqrt(DD.E, 1e-30); checkSqrt(new DD(999.0), 1e-28); } public void testTrunc() { checkTrunc(DD.valueOf(1e16).subtract(DD.valueOf(1)), DD.valueOf(1e16).subtract(DD.valueOf(1))); // the appropriate error bound is determined empirically checkTrunc(DD.PI, DD.valueOf(3)); checkTrunc(DD.valueOf(999.999), DD.valueOf(999)); checkTrunc(DD.E.negate(), DD.valueOf(-2)); checkTrunc(DD.valueOf(-999.999), DD.valueOf(-999)); } }