package org.rascalmpl.value;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import org.rascalmpl.value.IInteger;
import org.rascalmpl.value.INumber;
import org.rascalmpl.value.IReal;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.value.impl.reference.ValueFactory;
import junit.framework.TestCase;
public class TestBigDecimalCalculations extends TestCase {
private static IValueFactory vf = ValueFactory.getInstance();
private static void assertClose(INumber param, IReal actual, double expected) {
assertClose(param, actual, expected, 6);
}
private static void assertClose(INumber param, IReal actual, double expected, int significantDigits) {
long order = 0;
if (Math.abs(expected) > 0.00001) {
order = Math.round(Math.floor(Math.log10(Math.abs(expected))));
}
double maxError = Math.pow(10, order - significantDigits);
assertTrue("failed for "+param+" real:" + actual + " double: " + expected,
Math.abs(actual.doubleValue() - expected) < maxError);
}
public void testSinComparableToFloatingPoint() {
IReal start = vf.real(-100);
IReal stop = start.negate();
IReal increments = vf.real("0.1");
for (IReal param = start; !stop.less(param).getValue(); param = param.add(increments)) {
assertClose(param, param.sin(vf.getPrecision()), Math.sin(param.doubleValue()));
}
}
public void testCosComparableToFloatingPoint() {
IReal start = vf.real(-100);
IReal stop = start.negate();
IReal increments = vf.real("0.1");
for (IReal param = start; !stop.less(param).getValue(); param = param.add(increments)) {
assertClose(param, param.cos(vf.getPrecision()), Math.cos(param.doubleValue()));
}
}
public void testTanComparableToFloatingPoint() {
IReal start = vf.pi(vf.getPrecision()).divide(vf.real(2.0), vf.getPrecision()).negate();
IReal stop = start.negate();
IReal increments = vf.real("0.01");
// around pi/2 tan is undefined so we skip checking around that.
start = start.add(increments);
stop = stop.subtract(increments);
for (IReal param = start; !stop.less(param).getValue() ; param = param.add(increments)) {
assertClose(param, param.tan(vf.getPrecision()), Math.tan(param.doubleValue()));
}
}
private static double log2(double x) {
return Math.log(x)/Math.log(2);
}
public void testLog2ComparableToFloatingPoint() {
IReal start = vf.real(0);
IReal stop = vf.real(100);
IReal increments = vf.real("0.1");
start = start.add(increments);
for (IReal param = start; !stop.less(param).getValue(); param = param.add(increments)) {
assertClose(param, param.log(vf.integer(2), vf.getPrecision()), log2(param.doubleValue()));
}
}
public void testLog10ComparableToFloatingPoint() {
IReal start = vf.real(0);
IReal stop = vf.real(100);
IReal increments = vf.real("0.1");
start = start.add(increments);
for (IReal param = start; !stop.less(param).getValue(); param = param.add(increments)) {
assertClose(param, param.log(vf.integer(10), vf.getPrecision()), Math.log10(param.doubleValue()));
}
}
public void testLnComparableToFloatingPoint() {
IReal start = vf.real(0);
IReal stop = vf.real(100);
IReal increments = vf.real("0.1");
start = start.add(increments);
for (IReal param = start; !stop.less(param).getValue(); param = param.add(increments)) {
assertClose(param, param.ln(vf.getPrecision()), Math.log(param.doubleValue()));
}
}
public void testPowAllNumbers() {
IReal start = vf.real(-10);
IReal stop = start.negate();
IReal increments = vf.real("0.1");
IReal x = vf.pi(10);
for (IReal param = start; !stop.less(param).getValue(); param = param.add(increments)) {
assertClose(param, x.pow(param, vf.getPrecision()), Math.pow(x.doubleValue(), param.doubleValue()));
}
}
public void testPowNaturalNumbers() {
IInteger start = vf.integer(-10);
IInteger stop = start.negate();
IInteger increments = vf.integer(1);
IReal x = vf.pi(10);
for (IInteger param = start; !stop.less(param).getValue(); param = param.add(increments)) {
assertClose(param, x.pow(param), Math.pow(x.doubleValue(), param.doubleValue()));
}
}
public void testExpComparableToFloatingPoint() {
IReal start = vf.real(-100);
IReal stop = start.negate();
IReal increments = vf.real("0.1");
for (IReal param = start; !stop.less(param).getValue(); param = param.add(increments)) {
assertClose(param, param.exp(vf.getPrecision()), Math.exp(param.doubleValue()));
}
}
private void assertTakesLessThan(final int seconds, String call, final Runnable x) {
final Semaphore done = new Semaphore(0);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
x.run();
}
finally {
done.release();
}
}
});
try {
t.start();
if (!done.tryAcquire(seconds, TimeUnit.SECONDS)) {
t.interrupt();
assertTrue(call + " took more than 2 second.", false);
}
} catch (InterruptedException e) {
}
}
public void testExpPerformance() {
// exp(x) is small for negative x
IReal start = vf.pi(20).multiply(vf.real(10));
IReal stop = start.subtract(start.multiply(vf.real(100)));
IReal increments = vf.real(1);
for (IReal param = start; stop.less(param).getValue(); param = param.subtract(increments)) {
final IReal currentParam = param;
assertTakesLessThan(2, "exp(" + param + ")", new Runnable() {
@Override
public void run() {
currentParam.exp(vf.getPrecision());
}
});
}
}
public void testLnPerformance() {
// ln(x) is small for low x
IReal start = vf.pi(50).multiply(vf.real(10).pow(vf.integer(8)));
IReal stop = start.divide(vf.real(10).pow(vf.integer(30)), vf.getPrecision());
IReal increments = vf.real(10);
for (IReal param = start; stop.less(param).getValue(); param = param.divide(increments, vf.getPrecision())) {
final IReal currentParam = param;
assertTakesLessThan(2, "ln(" + param + ")", new Runnable() {
@Override
public void run() {
currentParam.ln(vf.getPrecision());
}
});
}
}
}