| caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 1 | #include <assert.h> |
| 2 | #include <math.h> |
| 3 | #include "CubicUtilities.h" |
| 4 | #include "Intersection_Tests.h" |
| caryclark@google.com | 73ca624 | 2013-01-17 21:02:47 +0000 | [diff] [blame] | 5 | #include "QuadraticUtilities.h" |
| 6 | #include "QuarticRoot.h" |
| caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 7 | |
| 8 | double mulA[] = {-3, -1, 1, 3}; |
| 9 | size_t mulACount = sizeof(mulA) / sizeof(mulA[0]); |
| 10 | double rootB[] = {-9, -6, -3, -1, 0, 1, 3, 6, 9}; |
| 11 | size_t rootBCount = sizeof(rootB) / sizeof(rootB[0]); |
| 12 | double rootC[] = {-8, -6, -2, -1, 0, 1, 2, 6, 8}; |
| 13 | size_t rootCCount = sizeof(rootC) / sizeof(rootC[0]); |
| 14 | double rootD[] = {-7, -4, -1, 0, 1, 2, 5}; |
| 15 | size_t rootDCount = sizeof(rootD) / sizeof(rootD[0]); |
| 16 | double rootE[] = {-5, -1, 0, 1, 7}; |
| 17 | size_t rootECount = sizeof(rootE) / sizeof(rootE[0]); |
| 18 | |
| caryclark@google.com | 73ca624 | 2013-01-17 21:02:47 +0000 | [diff] [blame] | 19 | |
| caryclark@google.com | 9f60291 | 2013-01-24 21:47:16 +0000 | [diff] [blame^] | 20 | static void quadraticTest(bool limit) { |
| caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 21 | // (x - a)(x - b) == x^2 - (a + b)x + ab |
| 22 | for (size_t aIndex = 0; aIndex < mulACount; ++aIndex) { |
| 23 | for (size_t bIndex = 0; bIndex < rootBCount; ++bIndex) { |
| 24 | for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) { |
| 25 | const double A = mulA[aIndex]; |
| caryclark@google.com | 9f60291 | 2013-01-24 21:47:16 +0000 | [diff] [blame^] | 26 | double B = rootB[bIndex]; |
| 27 | double C = rootC[cIndex]; |
| 28 | if (limit) { |
| 29 | B = (B - 6) / 12; |
| 30 | C = (C - 6) / 12; |
| 31 | } |
| caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 32 | const double b = A * (B + C); |
| 33 | const double c = A * B * C; |
| 34 | double roots[2]; |
| caryclark@google.com | 9f60291 | 2013-01-24 21:47:16 +0000 | [diff] [blame^] | 35 | const int rootCount = limit ? quadraticRootsValidT(A, b, c, roots) |
| 36 | : quadraticRootsReal(A, b, c, roots); |
| 37 | int expected; |
| 38 | if (limit) { |
| 39 | expected = B <= 0 && B >= -1; |
| 40 | expected += B != C && C <= 0 && C >= -1; |
| 41 | } else { |
| 42 | expected = 1 + (B != C); |
| 43 | } |
| caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 44 | assert(rootCount == expected); |
| caryclark@google.com | 9f60291 | 2013-01-24 21:47:16 +0000 | [diff] [blame^] | 45 | if (!rootCount) { |
| 46 | continue; |
| 47 | } |
| caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 48 | assert(approximately_equal(roots[0], -B) |
| 49 | || approximately_equal(roots[0], -C)); |
| caryclark@google.com | 9f60291 | 2013-01-24 21:47:16 +0000 | [diff] [blame^] | 50 | if (expected > 1) { |
| caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 51 | assert(!approximately_equal(roots[0], roots[1])); |
| 52 | assert(approximately_equal(roots[1], -B) |
| 53 | || approximately_equal(roots[1], -C)); |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | } |
| 58 | } |
| 59 | |
| caryclark@google.com | 9f60291 | 2013-01-24 21:47:16 +0000 | [diff] [blame^] | 60 | static void testOneCubic(bool limit, size_t aIndex, size_t bIndex, size_t cIndex, size_t dIndex) { |
| 61 | const double A = mulA[aIndex]; |
| 62 | double B = rootB[bIndex]; |
| 63 | double C = rootC[cIndex]; |
| 64 | double D = rootD[dIndex]; |
| 65 | if (limit) { |
| 66 | B = (B - 6) / 12; |
| 67 | C = (C - 6) / 12; |
| 68 | D = (C - 2) / 6; |
| 69 | } |
| 70 | const double b = A * (B + C + D); |
| 71 | const double c = A * (B * C + C * D + B * D); |
| 72 | const double d = A * B * C * D; |
| 73 | double roots[3]; |
| 74 | const int rootCount = limit ? cubicRootsValidT(A, b, c, d, roots) |
| 75 | : cubicRootsReal(A, b, c, d, roots); |
| 76 | int expected; |
| 77 | if (limit) { |
| 78 | expected = B <= 0 && B >= -1; |
| 79 | expected += B != C && C <= 0 && C >= -1; |
| 80 | expected += B != D && C != D && D <= 0 && D >= -1; |
| 81 | } else { |
| 82 | expected = 1 + (B != C) + (B != D && C != D); |
| 83 | } |
| 84 | assert(rootCount == expected); |
| 85 | if (!rootCount) { |
| 86 | return; |
| 87 | } |
| 88 | assert(approximately_equal(roots[0], -B) |
| 89 | || approximately_equal(roots[0], -C) |
| 90 | || approximately_equal(roots[0], -D)); |
| 91 | if (expected <= 1) { |
| 92 | return; |
| 93 | } |
| 94 | assert(!approximately_equal(roots[0], roots[1])); |
| 95 | assert(approximately_equal(roots[1], -B) |
| 96 | || approximately_equal(roots[1], -C) |
| 97 | || approximately_equal(roots[1], -D)); |
| 98 | if (expected <= 2) { |
| 99 | return; |
| 100 | } |
| 101 | assert(!approximately_equal(roots[0], roots[2]) |
| 102 | && !approximately_equal(roots[1], roots[2])); |
| 103 | assert(approximately_equal(roots[2], -B) |
| 104 | || approximately_equal(roots[2], -C) |
| 105 | || approximately_equal(roots[2], -D)); |
| 106 | } |
| 107 | |
| 108 | static void cubicTest(bool limit) { |
| caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 109 | // (x - a)(x - b)(x - c) == x^3 - (a + b + c)x^2 + (ab + bc + ac)x - abc |
| 110 | for (size_t aIndex = 0; aIndex < mulACount; ++aIndex) { |
| 111 | for (size_t bIndex = 0; bIndex < rootBCount; ++bIndex) { |
| 112 | for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) { |
| 113 | for (size_t dIndex = 0; dIndex < rootDCount; ++dIndex) { |
| caryclark@google.com | 9f60291 | 2013-01-24 21:47:16 +0000 | [diff] [blame^] | 114 | testOneCubic(limit, aIndex, bIndex, cIndex, dIndex); |
| caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 115 | } |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | } |
| 120 | |
| caryclark@google.com | 9f60291 | 2013-01-24 21:47:16 +0000 | [diff] [blame^] | 121 | static void testOneQuartic(size_t aIndex, size_t bIndex, size_t cIndex, size_t dIndex, |
| 122 | size_t eIndex) { |
| 123 | const double A = mulA[aIndex]; |
| 124 | const double B = rootB[bIndex]; |
| 125 | const double C = rootC[cIndex]; |
| 126 | const double D = rootD[dIndex]; |
| 127 | const double E = rootE[eIndex]; |
| 128 | const double b = A * (B + C + D + E); |
| 129 | const double c = A * (B * C + C * D + B * D + B * E + C * E + D * E); |
| 130 | const double d = A * (B * C * D + B * C * E + B * D * E + C * D * E); |
| 131 | const double e = A * B * C * D * E; |
| 132 | double roots[4]; |
| 133 | bool oneHint = approximately_zero(A + b + c + d + e); |
| 134 | int rootCount = reducedQuarticRoots(A, b, c, d, e, oneHint, roots); |
| 135 | if (rootCount < 0) { |
| 136 | rootCount = quarticRootsReal(A, b, c, d, e, roots); |
| 137 | } |
| 138 | const int expected = 1 + (B != C) + (B != D && C != D) + (B != E && C != E && D != E); |
| 139 | assert(rootCount == expected); |
| 140 | assert(AlmostEqualUlps(roots[0], -B) |
| 141 | || AlmostEqualUlps(roots[0], -C) |
| 142 | || AlmostEqualUlps(roots[0], -D) |
| 143 | || AlmostEqualUlps(roots[0], -E)); |
| 144 | if (expected <= 1) { |
| 145 | return; |
| 146 | } |
| 147 | assert(!AlmostEqualUlps(roots[0], roots[1])); |
| 148 | assert(AlmostEqualUlps(roots[1], -B) |
| 149 | || AlmostEqualUlps(roots[1], -C) |
| 150 | || AlmostEqualUlps(roots[1], -D) |
| 151 | || AlmostEqualUlps(roots[1], -E)); |
| 152 | if (expected <= 2) { |
| 153 | return; |
| 154 | } |
| 155 | assert(!AlmostEqualUlps(roots[0], roots[2]) |
| 156 | && !AlmostEqualUlps(roots[1], roots[2])); |
| 157 | assert(AlmostEqualUlps(roots[2], -B) |
| 158 | || AlmostEqualUlps(roots[2], -C) |
| 159 | || AlmostEqualUlps(roots[2], -D) |
| 160 | || AlmostEqualUlps(roots[2], -E)); |
| 161 | if (expected <= 3) { |
| 162 | return; |
| 163 | } |
| 164 | assert(!AlmostEqualUlps(roots[0], roots[3]) |
| 165 | && !AlmostEqualUlps(roots[1], roots[3]) |
| 166 | && !AlmostEqualUlps(roots[2], roots[3])); |
| 167 | assert(AlmostEqualUlps(roots[3], -B) |
| 168 | || AlmostEqualUlps(roots[3], -C) |
| 169 | || AlmostEqualUlps(roots[3], -D) |
| 170 | || AlmostEqualUlps(roots[3], -E)); |
| 171 | } |
| 172 | |
| caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 173 | static void quarticTest() { |
| 174 | // (x - a)(x - b)(x - c)(x - d) == x^4 - (a + b + c + d)x^3 |
| 175 | // + (ab + bc + cd + ac + bd + cd)x^2 - (abc + bcd + abd + acd) * x + abcd |
| 176 | for (size_t aIndex = 0; aIndex < mulACount; ++aIndex) { |
| 177 | for (size_t bIndex = 0; bIndex < rootBCount; ++bIndex) { |
| 178 | for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) { |
| 179 | for (size_t dIndex = 0; dIndex < rootDCount; ++dIndex) { |
| 180 | for (size_t eIndex = 0; eIndex < rootECount; ++eIndex) { |
| caryclark@google.com | 9f60291 | 2013-01-24 21:47:16 +0000 | [diff] [blame^] | 181 | testOneQuartic(aIndex, bIndex, cIndex, dIndex, eIndex); |
| caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 182 | } |
| 183 | } |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | void QuarticRoot_Test() { |
| caryclark@google.com | 9f60291 | 2013-01-24 21:47:16 +0000 | [diff] [blame^] | 190 | testOneCubic(false, 0, 5, 5, 4); |
| 191 | testOneQuartic(0, 0, 2, 4, 3); |
| 192 | quadraticTest(true); |
| 193 | quadraticTest(false); |
| 194 | cubicTest(true); |
| 195 | cubicTest(false); |
| caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 196 | quarticTest(); |
| 197 | } |