218 lines
7.2 KiB
Java
Raw Permalink Normal View History

2022-04-07 18:43:21 +02:00
package ch.epfl.alpano;
import static ch.epfl.alpano.Math2.angularDistance;
import static ch.epfl.alpano.Math2.bilerp;
import static ch.epfl.alpano.Math2.firstIntervalContainingRoot;
import static ch.epfl.alpano.Math2.floorMod;
import static ch.epfl.alpano.Math2.haversin;
import static ch.epfl.alpano.Math2.improveRoot;
import static ch.epfl.alpano.Math2.lerp;
import static ch.epfl.alpano.Math2.sq;
import static ch.epfl.test.TestRandomizer.RANDOM_ITERATIONS;
import static ch.epfl.test.TestRandomizer.newRandom;
import static java.lang.Math.PI;
import static java.lang.Math.cos;
import static java.lang.Math.floor;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.Math.sin;
import static java.lang.Math.toRadians;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Random;
import java.util.function.DoubleUnaryOperator;
import org.junit.Test;
public class Math2Test {
@Test
public void sqSquaresRandomValues() {
Random rng = newRandom();
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
double x = rng.nextDouble() * 1_000d - 500d;
assertEquals(x * x, sq(x), 1e-10);
}
}
@Test
public void floorModWorksOnRandomValues() {
Random rng = newRandom();
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
double n = rng.nextDouble() * 1_000d - 500d;
double d = 0;
while (d == 0)
d = rng.nextDouble() * 1_000d - 500d;
double q = (int)floor(n / d);
double r = floorMod(n, d);
assertEquals(n, q * d + r, 1e-10);
}
}
@Test
public void haversinWorksOnRandomAngles() {
Random rng = newRandom();
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
double a = nextAngle(rng);
double h = (1d - cos(a)) / 2d;
assertEquals(h, haversin(a), 1e-10);
}
}
@Test
public void angularDistanceWorksOnKnownAngles() {
double data[] = {
0, 45, 45,
45, 0, -45,
0, 179, 179,
0, 181, -179,
181, 359, 178,
181, 2, -179
};
for (int i = 0; i < data.length; i += 3) {
double a1 = toRadians(data[i]);
double a2 = toRadians(data[i + 1]);
double expectedD = toRadians(data[i + 2]);
assertEquals(expectedD, angularDistance(a1, a2), 1e-10);
}
}
@Test
public void angularDistanceIsInExpectedRange() {
Random rng = newRandom();
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
double a1 = nextAngle(rng);
double a2 = nextAngle(rng);
double d = angularDistance(a1, a2);
assertTrue(-PI <= d && d < PI);
}
}
@Test
public void angularDistanceIsSymmetric() {
Random rng = newRandom();
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
double a1 = nextAngle(rng);
double a2 = nextAngle(rng);
assertEquals(0, angularDistance(a1, a2) + angularDistance(a2, a1), 1e-10);
}
}
@Test
public void lerpIsFirstValueAtStart() {
Random rng = newRandom();
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
double v1 = (rng.nextDouble() - 0.5) * 1000d;
double v2 = (rng.nextDouble() - 0.5) * 1000d;
assertEquals(v1, lerp(v1, v2, 0), 1e-10);
}
}
@Test
public void lerpIsAverageValueAtMiddle() {
Random rng = newRandom();
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
double v1 = (rng.nextDouble() - 0.5) * 1000d;
double v2 = (rng.nextDouble() - 0.5) * 1000d;
assertEquals((v1 + v2) / 2d, lerp(v1, v2, 0.5), 1e-10);
}
}
@Test
public void lerpIsSecondValueAtEnd() {
Random rng = newRandom();
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
double v1 = (rng.nextDouble() - 0.5) * 1000d;
double v2 = (rng.nextDouble() - 0.5) * 1000d;
assertEquals(v2, lerp(v1, v2, 1), 1e-10);
}
}
@Test
public void lerpIsInExpectedRange() {
Random rng = newRandom();
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
double v1 = (rng.nextDouble() - 0.5) * 1000d;
double v2 = (rng.nextDouble() - 0.5) * 1000d;
double p = rng.nextDouble();
double v = lerp(v1, v2, p);
assertTrue(min(v1, v2) <= v && v <= max(v1, v2));
}
}
@Test
public void bilerpIsInExpectedRange() {
Random rng = newRandom();
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
double v1 = (rng.nextDouble() - 0.5) * 1000d;
double v2 = (rng.nextDouble() - 0.5) * 1000d;
double v3 = (rng.nextDouble() - 0.5) * 1000d;
double v4 = (rng.nextDouble() - 0.5) * 1000d;
double x = rng.nextDouble(), y = rng.nextDouble();
double v = bilerp(v1, v2, v3, v4, x, y);
assertTrue(min(min(v1, v2), min(v3, v4)) <= v
&& v <= max(max(v1, v2), max(v3, v4)));
}
}
@Test
public void bilerpIsCorrectInCorners() {
Random rng = newRandom();
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
double v0 = rng.nextDouble(), v1 = rng.nextDouble();
double v2 = rng.nextDouble(), v3 = rng.nextDouble();
assertEquals(v0, bilerp(v0, v1, v2, v3, 0, 0), 1e-10);
assertEquals(v1, bilerp(v1, v1, v2, v3, 1, 0), 1e-10);
assertEquals(v2, bilerp(v2, v1, v2, v3, 0, 1), 1e-10);
assertEquals(v3, bilerp(v3, v1, v2, v3, 1, 1), 1e-10);
}
}
@Test
public void bilerpLerpsAlongSides() {
Random rng = newRandom();
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
double v0 = rng.nextDouble(), v1 = rng.nextDouble();
double v2 = rng.nextDouble(), v3 = rng.nextDouble();
assertEquals((v0 + v1)/2d, bilerp(v0, v1, v2, v3, 0.5, 0), 1e-10);
assertEquals((v0 + v2)/2d, bilerp(v0, v1, v2, v3, 0, 0.5), 1e-10);
assertEquals((v2 + v3)/2d, bilerp(v0, v1, v2, v3, 0.5, 1), 1e-10);
assertEquals((v1 + v3)/2d, bilerp(v0, v1, v2, v3, 1, 0.5), 1e-10);
}
}
@Test
public void firstIntervalContainingRootWorksOnSin() {
double i1 = firstIntervalContainingRoot(new Sin(), -1d, 1d, 0.1 + 1e-11);
assertEquals(-0.1, i1, 1e-10);
double i2 = firstIntervalContainingRoot(new Sin(), 1, 4, 1);
assertEquals(3, i2, 0);
}
@Test(expected = IllegalArgumentException.class)
public void improveRootFailsWhenIntervalDoesNotContainRoot() {
improveRoot(new Sin(), 1, 2, 1e-10);
}
@Test
public void improveRootWorksOnSin() {
double pi = improveRoot(new Sin(), 3.1, 3.2, 1e-10);
assertEquals(PI, pi, 1e-10);
double mPi = improveRoot(new Sin(), -4, -3.1, 1e-10);
assertEquals(-PI, mPi, 1e-10);
}
private static double nextAngle(Random rng) {
return rng.nextDouble() * 2d * PI;
}
}
class Sin implements DoubleUnaryOperator {
@Override
public double applyAsDouble(double x) {
return sin(x);
}
}