/src/skia/fuzz/FuzzCubicRoots.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2023 Google LLC |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license that can be |
5 | | * found in the LICENSE file. |
6 | | */ |
7 | | |
8 | | #include "fuzz/Fuzz.h" |
9 | | #include "include/private/base/SkAssert.h" |
10 | | #include "include/private/base/SkFloatingPoint.h" |
11 | | #include "src/base/SkCubics.h" |
12 | | #include "src/base/SkQuads.h" |
13 | | #include "src/base/SkUtils.h" |
14 | | |
15 | | #include <cmath> |
16 | | |
17 | 101 | static void fuzz_cubic_real_roots(double A, double B, double C, double D) { |
18 | 101 | double roots[3]; |
19 | 101 | const int numSolutions = SkCubics::RootsReal(A, B, C, D, roots); |
20 | 101 | SkASSERT_RELEASE(numSolutions >= 0 && numSolutions <= 3); |
21 | 268 | for (int i = 0; i < numSolutions; i++) { |
22 | 167 | SkASSERT_RELEASE(std::isfinite(roots[i])); |
23 | 167 | } |
24 | | // Roots should not be duplicated |
25 | 101 | if (numSolutions >= 2) { |
26 | 53 | SkASSERT_RELEASE(!sk_doubles_nearly_equal_ulps(roots[0], roots[1])); |
27 | 53 | } |
28 | 101 | if (numSolutions == 3) { |
29 | 18 | SkASSERT_RELEASE(!sk_doubles_nearly_equal_ulps(roots[1], roots[2])); |
30 | 18 | SkASSERT_RELEASE(!sk_doubles_nearly_equal_ulps(roots[0], roots[2])); |
31 | 18 | } |
32 | 101 | } |
33 | | |
34 | 101 | static void fuzz_cubic_roots_valid_t(double A, double B, double C, double D) { |
35 | 101 | double roots[3]; |
36 | 101 | const int numSolutions = SkCubics::RootsValidT(A, B, C, D, roots); |
37 | 101 | SkASSERT_RELEASE(numSolutions >= 0 && numSolutions <= 3); |
38 | 237 | for (int i = 0; i < numSolutions; i++) { |
39 | 136 | SkASSERT_RELEASE(std::isfinite(roots[i])); |
40 | 136 | SkASSERT_RELEASE(roots[i] >= 0.0); |
41 | 136 | SkASSERT_RELEASE(roots[i] <= 1.0); |
42 | 136 | } |
43 | | // Roots should not be duplicated |
44 | 101 | if (numSolutions >= 2) { |
45 | 39 | SkASSERT_RELEASE(!sk_doubles_nearly_equal_ulps(roots[0], roots[1])); |
46 | 39 | } |
47 | 101 | if (numSolutions == 3) { |
48 | 8 | SkASSERT_RELEASE(!sk_doubles_nearly_equal_ulps(roots[1], roots[2])); |
49 | 8 | SkASSERT_RELEASE(!sk_doubles_nearly_equal_ulps(roots[0], roots[2])); |
50 | 8 | } |
51 | 101 | } |
52 | | |
53 | 101 | static void fuzz_cubic_roots_binary_search(double A, double B, double C, double D) { |
54 | 101 | double roots[3]; |
55 | 101 | const int numSolutions = SkCubics::BinarySearchRootsValidT(A, B, C, D, roots); |
56 | 101 | SkASSERT_RELEASE(numSolutions >= 0 && numSolutions <= 3); |
57 | 218 | for (int i = 0; i < numSolutions; i++) { |
58 | 117 | SkASSERT_RELEASE(std::isfinite(roots[i])); |
59 | 117 | SkASSERT_RELEASE(roots[i] >= 0.0); |
60 | 117 | SkASSERT_RELEASE(roots[i] <= 1.0); |
61 | 117 | double actual = SkCubics::EvalAt(A, B, C, D, roots[i]); |
62 | | // The binary search algorithm *should* be accurate regardless of the inputs. |
63 | 117 | SkASSERT_RELEASE(std::abs(actual) < 0.001); |
64 | 117 | } |
65 | | // Roots should not be duplicated |
66 | 101 | if (numSolutions >= 2) { |
67 | 32 | SkASSERT_RELEASE(!sk_doubles_nearly_equal_ulps(roots[0], roots[1])); |
68 | 32 | } |
69 | 101 | if (numSolutions == 3) { |
70 | 4 | SkASSERT_RELEASE(!sk_doubles_nearly_equal_ulps(roots[1], roots[2])); |
71 | 4 | SkASSERT_RELEASE(!sk_doubles_nearly_equal_ulps(roots[0], roots[2])); |
72 | 4 | } |
73 | 101 | } |
74 | | |
75 | 101 | DEF_FUZZ(CubicRoots, fuzz) { |
76 | 101 | double A, B, C, D; |
77 | 101 | fuzz->next(&A); |
78 | 101 | fuzz->next(&B); |
79 | 101 | fuzz->next(&C); |
80 | 101 | fuzz->next(&D); |
81 | | |
82 | | // Uncomment for easy test case creation |
83 | | // SkDebugf("A %16e (0x%lx) B %16e (0x%lx) C %16e (0x%lx) D %16e (0x%lx)\n", |
84 | | // A, sk_bit_cast<uint64_t>(A), B, sk_bit_cast<uint64_t>(B), |
85 | | // C, sk_bit_cast<uint64_t>(C), D, sk_bit_cast<uint64_t>(D)); |
86 | 101 | fuzz_cubic_real_roots(A, B, C, D); |
87 | | |
88 | 101 | fuzz_cubic_roots_valid_t(A, B, C, D); |
89 | | |
90 | 101 | fuzz_cubic_roots_binary_search(A, B, C, D); |
91 | 101 | } |