/* * Copyright (c) 2016 Martin Davis. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v. 1.0 which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * * http://www.eclipse.org/org/documents/edl-v10.php. */ package org.locationtech.jts.math; 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(String args[]) { TestRunner.run(DDBasicTest.class); } public DDBasicTest(String name) { super(name); } public void testNaN() { assertTrue(DD.valueOf(1).divide(DD.valueOf(0)).isNaN()); assertTrue(DD.valueOf(1).multiply(DD.NaN).isNaN()); } public void testAddMult2() { checkAddMult2(new DD(3)); checkAddMult2(DD.PI); } 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 testDivideMultiply() { checkDivideMultiply(DD.PI, DD.E, 1e-30); checkDivideMultiply(new DD(39.4), new DD(10), 1e-30); } 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); } private void checkSqrt(DD x, double errBound) { DD sqrt = x.sqrt(); DD x2 = sqrt.multiply(sqrt); checkErrorBound("Sqrt", x, x2, errBound); } 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)); } private void checkTrunc(DD x, DD expected) { DD trunc = x.trunc(); boolean isEqual = trunc.equals(expected); assertTrue(isEqual); } 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 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); } private void checkAddMult2(DD dd) { DD sum = dd.add(dd); DD prod = dd.multiply(new DD(2.0)); checkErrorBound("AddMult2", sum, prod, 0.0); } private void checkMultiplyDivide(DD a, DD b, double errBound) { DD a2 = a.multiply(b).divide(b); checkErrorBound("MultiplyDivide", a, a2, errBound); } private void checkDivideMultiply(DD a, DD b, double errBound) { DD a2 = a.divide(b).multiply(b); checkErrorBound("DivideMultiply", a, a2, errBound); } private DD delta(DD x, DD y) { return x.subtract(y).abs(); } private void checkErrorBound(String tag, DD x, DD y, double errBound) { DD err = x.subtract(y).abs(); //System.out.println(tag + " err=" + err); boolean isWithinEps = err.doubleValue() <= errBound; assertTrue(isWithinEps); } /** * 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(double a, double b) { // binomial square DD add = new DD(a); DD bdd = new DD(b); DD aPlusb = add.add(bdd); DD abSq = aPlusb.multiply(aPlusb); // System.out.println("(a+b)^2 = " + abSq); // expansion DD a2dd = add.multiply(add); DD b2dd = bdd.multiply(bdd); DD ab = add.multiply(bdd); DD sum = b2dd.add(ab).add(ab); // System.out.println("2ab+b^2 = " + sum); DD diff = abSq.subtract(a2dd); // System.out.println("(a+b)^2 - a^2 = " + diff); 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); boolean isSame = diff.equals(sum); assertTrue(isSame); boolean isDeltaZero = delta.isZero(); assertTrue(isDeltaZero); } void printBinomialSquareDouble(double a, double b) { double sum = 2*a*b + b*b; 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)); } 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); } void checkBinomial2(double a, double b) { // binomial product DD add = new DD(a); DD bdd = new DD(b); DD aPlusb = add.add(bdd); DD aSubb = add.subtract(bdd); DD abProd = aPlusb.multiply(aSubb); // System.out.println("(a+b)^2 = " + abSq); // expansion DD a2dd = add.multiply(add); DD b2dd = bdd.multiply(bdd); // System.out.println("2ab+b^2 = " + sum); // this should equal b^2 DD diff = abProd.subtract(a2dd).negate(); // System.out.println("(a+b)^2 - a^2 = " + diff); 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); boolean isSame = diff.equals(b2dd); assertTrue(isSame); boolean isDeltaZero = delta.isZero(); assertTrue(isDeltaZero); } private void checkReciprocal(double x, double errBound) { DD xdd = new DD(x); DD rr = xdd.reciprocal().reciprocal(); 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 checkPow(double x, int exp, double errBound) { DD xdd = new DD(x); DD pow = xdd.pow(exp); //System.out.println("Pow(" + x + ", " + exp + ") = " + pow); DD pow2 = slowPow(xdd, exp); double err = pow.subtract(pow2).doubleValue(); boolean isOK = err < errBound; if (! isOK) System.out.println("Test slowPow value " + pow2); assertTrue(err <= errBound); } private DD slowPow(DD x, int exp) { if (exp == 0) return DD.valueOf(1.0); 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; } }