blob: bd7070a2e191b8b6c9b530fbe50316bcfcd36978 [file] [log] [blame]
/*
* Copyright (C) 2015 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.
*/
// A test for BoundedRationals package.
package com.android.calculator2;
import com.hp.creals.CR;
import com.hp.creals.UnaryCRFunction;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import java.math.BigInteger;
public class BRTest extends TestCase {
private static void check(boolean x, String s) {
if (!x) throw new AssertionFailedError(s);
}
final static int TEST_PREC = -100; // 100 bits to the right of
// binary point.
private static void checkEq(BoundedRational x, CR y, String s) {
check(x.CRValue().compareTo(y, TEST_PREC) == 0, s);
}
private static void checkWeakEq(BoundedRational x, CR y, String s) {
if (x != null) checkEq(x, y, s);
}
private final static UnaryCRFunction ASIN = UnaryCRFunction.asinFunction;
private final static UnaryCRFunction ACOS = UnaryCRFunction.acosFunction;
private final static UnaryCRFunction ATAN = UnaryCRFunction.atanFunction;
private final static UnaryCRFunction TAN = UnaryCRFunction.tanFunction;
private final static BoundedRational BR_0 = new BoundedRational(0);
private final static BoundedRational BR_M1 = new BoundedRational(-1);
private final static BoundedRational BR_2 = new BoundedRational(2);
private final static BoundedRational BR_M2 = new BoundedRational(-2);
private final static BoundedRational BR_15 = new BoundedRational(15);
private final static BoundedRational BR_390 = new BoundedRational(390);
private final static BoundedRational BR_M390 = new BoundedRational(-390);
private final static CR CR_1 = CR.valueOf(1);
private final static CR RADIANS_PER_DEGREE = CR.PI.divide(CR.valueOf(180));
private final static CR DEGREES_PER_RADIAN = CR.valueOf(180).divide(CR.PI);
private final static CR LN10 = CR.valueOf(10).ln();
private static CR toRadians(CR x) {
return x.multiply(RADIANS_PER_DEGREE);
}
private static CR fromRadians(CR x) {
return x.multiply(DEGREES_PER_RADIAN);
}
// We assume that x is simple enough that we don't overflow bounds.
private static void checkBR(BoundedRational x) {
check(x != null, "test data should not be null");
CR xAsCR = x.CRValue();
checkEq(BoundedRational.add(x, BoundedRational.ONE), xAsCR.add(CR_1),
"add 1:" + x);
checkEq(BoundedRational.subtract(x, BoundedRational.MINUS_THIRTY),
xAsCR.subtract(CR.valueOf(-30)), "sub -30:" + x);
checkEq(BoundedRational.multiply(x, BR_15),
xAsCR.multiply(CR.valueOf(15)), "multiply 15:" + x);
checkEq(BoundedRational.divide(x, BR_15),
xAsCR.divide(CR.valueOf(15)), "divide 15:" + x);
checkWeakEq(BoundedRational.sin(x), xAsCR.sin(), "sin:" + x);
checkWeakEq(BoundedRational.cos(x), xAsCR.cos(), "cos:" + x);
checkWeakEq(BoundedRational.tan(x), TAN.execute(xAsCR), "tan:" + x);
checkWeakEq(BoundedRational.degreeSin(x), toRadians(xAsCR).sin(),
"degree sin:" + x);
checkWeakEq(BoundedRational.degreeCos(x), toRadians(xAsCR).cos(),
"degree cos:" + x);
BigInteger big_x = BoundedRational.asBigInteger(x);
long long_x = (big_x == null? 0 : big_x.longValue());
try {
checkWeakEq(BoundedRational.degreeTan(x),
TAN.execute(toRadians(xAsCR)), "degree tan:" + x);
check((long_x - 90) % 180 != 0, "missed undefined tan: " + x);
} catch (ArithmeticException ignored) {
check((long_x - 90) % 180 == 0, "exception on defined tan: " + x);
}
if (x.compareTo(BoundedRational.ONE) <= 0
&& x.compareTo(BoundedRational.MINUS_ONE) >= 0) {
checkWeakEq(BoundedRational.asin(x), ASIN.execute(xAsCR),
"asin:" + x);
checkWeakEq(BoundedRational.acos(x), ACOS.execute(xAsCR),
"acos:" + x);
checkWeakEq(BoundedRational.degreeAsin(x),
fromRadians(ASIN.execute(xAsCR)), "degree asin:" + x);
checkWeakEq(BoundedRational.degreeAcos(x),
fromRadians(ACOS.execute(xAsCR)), "degree acos:" + x);
}
checkWeakEq(BoundedRational.atan(x), fromRadians(ATAN.execute(xAsCR)),
"atan:" + x);
checkWeakEq(BoundedRational.degreeAtan(x),
fromRadians(ATAN.execute(xAsCR)), "degree atan:" + x);
if (x.signum() > 0) {
checkWeakEq(BoundedRational.ln(x), xAsCR.ln(), "ln:" + x);
checkWeakEq(BoundedRational.log(x), xAsCR.ln().divide(LN10),
"log:" + x);
checkWeakEq(BoundedRational.sqrt(x), xAsCR.sqrt(), "sqrt:" + x);
checkEq(BoundedRational.pow(x, BR_15),
xAsCR.ln().multiply(CR.valueOf(15)).exp(),
"pow(x,15):" + x);
}
}
public void testBR() {
checkEq(BR_0, CR.valueOf(0), "0");
checkEq(BR_390, CR.valueOf(390), "390");
checkEq(BR_15, CR.valueOf(15), "15");
checkEq(BR_M390, CR.valueOf(-390), "-390");
checkEq(BR_M1, CR.valueOf(-1), "-1");
checkEq(BR_2, CR.valueOf(2), "2");
checkEq(BR_M2, CR.valueOf(-2), "-2");
check(BR_0.signum() == 0, "signum(0)");
check(BR_M1.signum() == -1, "signum(-1)");
check(BR_2.signum() == 1, "signum(2)");
// We check values that include all interesting degree values.
BoundedRational r = BR_M390;
while (!r.equals(BR_390)) {
check(r != null, "loop counter overflowed!");
checkBR(r);
r = BoundedRational.add(r, BR_15);
}
checkBR(BoundedRational.HALF);
checkBR(BoundedRational.MINUS_HALF);
checkBR(BoundedRational.ONE);
checkBR(BoundedRational.MINUS_ONE);
checkBR(new BoundedRational(1000));
checkBR(new BoundedRational(100));
checkBR(new BoundedRational(4,9));
check(BoundedRational.sqrt(new BoundedRational(4,9)) != null,
"sqrt(4/9) is null");
checkBR(BoundedRational.negate(new BoundedRational(4,9)));
checkBR(new BoundedRational(5,9));
checkBR(new BoundedRational(5,10));
checkBR(new BoundedRational(5,10));
checkBR(new BoundedRational(4,13));
checkBR(new BoundedRational(36));
checkBR(BoundedRational.negate(new BoundedRational(36)));
check(BoundedRational.pow(null, BR_15) == null, "pow(null, 15)");
}
public void testBRexceptions() {
try {
BoundedRational.ln(BR_M1);
check(false, "ln(-1)");
} catch (ArithmeticException ignored) {}
try {
BoundedRational.log(BR_M2);
check(false, "log(-2)");
} catch (ArithmeticException ignored) {}
try {
BoundedRational.sqrt(BR_M1);
check(false, "sqrt(-1)");
} catch (ArithmeticException ignored) {}
try {
BoundedRational.asin(BR_M2);
check(false, "asin(-2)");
} catch (ArithmeticException ignored) {}
try {
BoundedRational.degreeAcos(BR_2);
check(false, "degree acos(2)");
} catch (ArithmeticException ignored) {}
}
public void testBROverflow() {
BoundedRational sum = new BoundedRational(0);
long i;
for (i = 1; i < 1000; ++i) {
sum = BoundedRational.add(sum,
BoundedRational.inverse(new BoundedRational(i)));
if (sum == null) break;
}
// Experimentally, this overflows at 139, which seems
// plausible based on the Wolfram Alpha result.
// This test is robust against minor changes in MAX_SIZE.
check(i > 100, "Harmonic series overflowed at " + i);
check(i < 1000, "Harmonic series didn't overflow");
}
}