1 //===-- MPFRUtils.h ---------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H
10 #define LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H
11 
12 #include "utils/CPP/TypeTraits.h"
13 #include "utils/UnitTest/Test.h"
14 
15 #include <stdint.h>
16 
17 namespace __llvm_libc {
18 namespace testing {
19 namespace mpfr {
20 
21 enum class Operation : int {
22   // Operations with take a single floating point number as input
23   // and produce a single floating point number as output. The input
24   // and output floating point numbers are of the same kind.
25   BeginUnaryOperationsSingleOutput,
26   Abs,
27   Ceil,
28   Cos,
29   Exp,
30   Exp2,
31   Floor,
32   Round,
33   Sin,
34   Sqrt,
35   Tan,
36   Trunc,
37   EndUnaryOperationsSingleOutput,
38 
39   // Operations which take a single floating point nubmer as input
40   // but produce two outputs. The first ouput is a floating point
41   // number of the same type as the input. The second output is of type
42   // 'int'.
43   BeginUnaryOperationsTwoOutputs,
44   Frexp, // Floating point output, the first output, is the fractional part.
45   EndUnaryOperationsTwoOutputs,
46 
47   // Operations wich take two floating point nubmers of the same type as
48   // input and produce a single floating point number of the same type as
49   // output.
50   BeginBinaryOperationsSingleOutput,
51   Hypot,
52   EndBinaryOperationsSingleOutput,
53 
54   // Operations which take two floating point numbers of the same type as
55   // input and produce two outputs. The first output is a floating nubmer of
56   // the same type as the inputs. The second output is af type 'int'.
57   BeginBinaryOperationsTwoOutputs,
58   RemQuo, // The first output, the floating point output, is the remainder.
59   EndBinaryOperationsTwoOutputs,
60 
61   // Operations which take three floating point nubmers of the same type as
62   // input and produce a single floating point number of the same type as
63   // output.
64   BeginTernaryOperationsSingleOuput,
65   Fma,
66   EndTernaryOperationsSingleOutput,
67 };
68 
69 template <typename T> struct BinaryInput {
70   static_assert(
71       __llvm_libc::cpp::IsFloatingPointType<T>::Value,
72       "Template parameter of BinaryInput must be a floating point type.");
73 
74   using Type = T;
75   T x, y;
76 };
77 
78 template <typename T> struct TernaryInput {
79   static_assert(
80       __llvm_libc::cpp::IsFloatingPointType<T>::Value,
81       "Template parameter of TernaryInput must be a floating point type.");
82 
83   using Type = T;
84   T x, y, z;
85 };
86 
87 template <typename T> struct BinaryOutput {
88   T f;
89   int i;
90 };
91 
92 namespace internal {
93 
94 template <typename T1, typename T2>
95 struct AreMatchingBinaryInputAndBinaryOutput {
96   static constexpr bool value = false;
97 };
98 
99 template <typename T>
100 struct AreMatchingBinaryInputAndBinaryOutput<BinaryInput<T>, BinaryOutput<T>> {
101   static constexpr bool value = cpp::IsFloatingPointType<T>::Value;
102 };
103 
104 template <typename T>
105 bool compareUnaryOperationSingleOutput(Operation op, T input, T libcOutput,
106                                        double t);
107 template <typename T>
108 bool compareUnaryOperationTwoOutputs(Operation op, T input,
109                                      const BinaryOutput<T> &libcOutput,
110                                      double t);
111 template <typename T>
112 bool compareBinaryOperationTwoOutputs(Operation op, const BinaryInput<T> &input,
113                                       const BinaryOutput<T> &libcOutput,
114                                       double t);
115 
116 template <typename T>
117 bool compareBinaryOperationOneOutput(Operation op, const BinaryInput<T> &input,
118                                      T libcOutput, double t);
119 
120 template <typename T>
121 bool compareTernaryOperationOneOutput(Operation op,
122                                       const TernaryInput<T> &input,
123                                       T libcOutput, double t);
124 
125 template <typename T>
126 void explainUnaryOperationSingleOutputError(Operation op, T input, T matchValue,
127                                             testutils::StreamWrapper &OS);
128 template <typename T>
129 void explainUnaryOperationTwoOutputsError(Operation op, T input,
130                                           const BinaryOutput<T> &matchValue,
131                                           testutils::StreamWrapper &OS);
132 template <typename T>
133 void explainBinaryOperationTwoOutputsError(Operation op,
134                                            const BinaryInput<T> &input,
135                                            const BinaryOutput<T> &matchValue,
136                                            testutils::StreamWrapper &OS);
137 
138 template <typename T>
139 void explainBinaryOperationOneOutputError(Operation op,
140                                           const BinaryInput<T> &input,
141                                           T matchValue,
142                                           testutils::StreamWrapper &OS);
143 
144 template <typename T>
145 void explainTernaryOperationOneOutputError(Operation op,
146                                            const TernaryInput<T> &input,
147                                            T matchValue,
148                                            testutils::StreamWrapper &OS);
149 
150 template <Operation op, typename InputType, typename OutputType>
151 class MPFRMatcher : public testing::Matcher<OutputType> {
152   InputType input;
153   OutputType matchValue;
154   double ulpTolerance;
155 
156 public:
157   MPFRMatcher(InputType testInput, double ulpTolerance)
158       : input(testInput), ulpTolerance(ulpTolerance) {}
159 
160   bool match(OutputType libcResult) {
161     matchValue = libcResult;
162     return match(input, matchValue, ulpTolerance);
163   }
164 
165   void explainError(testutils::StreamWrapper &OS) override {
166     explainError(input, matchValue, OS);
167   }
168 
169 private:
170   template <typename T> static bool match(T in, T out, double tolerance) {
171     return compareUnaryOperationSingleOutput(op, in, out, tolerance);
172   }
173 
174   template <typename T>
175   static bool match(T in, const BinaryOutput<T> &out, double tolerance) {
176     return compareUnaryOperationTwoOutputs(op, in, out, tolerance);
177   }
178 
179   template <typename T>
180   static bool match(const BinaryInput<T> &in, T out, double tolerance) {
181     return compareBinaryOperationOneOutput(op, in, out, tolerance);
182   }
183 
184   template <typename T>
185   static bool match(BinaryInput<T> in, const BinaryOutput<T> &out,
186                     double tolerance) {
187     return compareBinaryOperationTwoOutputs(op, in, out, tolerance);
188   }
189 
190   template <typename T>
191   static bool match(const TernaryInput<T> &in, T out, double tolerance) {
192     return compareTernaryOperationOneOutput(op, in, out, tolerance);
193   }
194 
195   template <typename T>
196   static void explainError(T in, T out, testutils::StreamWrapper &OS) {
197     explainUnaryOperationSingleOutputError(op, in, out, OS);
198   }
199 
200   template <typename T>
201   static void explainError(T in, const BinaryOutput<T> &out,
202                            testutils::StreamWrapper &OS) {
203     explainUnaryOperationTwoOutputsError(op, in, out, OS);
204   }
205 
206   template <typename T>
207   static void explainError(const BinaryInput<T> &in, const BinaryOutput<T> &out,
208                            testutils::StreamWrapper &OS) {
209     explainBinaryOperationTwoOutputsError(op, in, out, OS);
210   }
211 
212   template <typename T>
213   static void explainError(const BinaryInput<T> &in, T out,
214                            testutils::StreamWrapper &OS) {
215     explainBinaryOperationOneOutputError(op, in, out, OS);
216   }
217 
218   template <typename T>
219   static void explainError(const TernaryInput<T> &in, T out,
220                            testutils::StreamWrapper &OS) {
221     explainTernaryOperationOneOutputError(op, in, out, OS);
222   }
223 };
224 
225 } // namespace internal
226 
227 // Return true if the input and ouput types for the operation op are valid
228 // types.
229 template <Operation op, typename InputType, typename OutputType>
230 constexpr bool isValidOperation() {
231   return (Operation::BeginUnaryOperationsSingleOutput < op &&
232           op < Operation::EndUnaryOperationsSingleOutput &&
233           cpp::IsSame<InputType, OutputType>::Value &&
234           cpp::IsFloatingPointType<InputType>::Value) ||
235          (Operation::BeginUnaryOperationsTwoOutputs < op &&
236           op < Operation::EndUnaryOperationsTwoOutputs &&
237           cpp::IsFloatingPointType<InputType>::Value &&
238           cpp::IsSame<OutputType, BinaryOutput<InputType>>::Value) ||
239          (Operation::BeginBinaryOperationsSingleOutput < op &&
240           op < Operation::EndBinaryOperationsSingleOutput &&
241           cpp::IsFloatingPointType<OutputType>::Value &&
242           cpp::IsSame<InputType, BinaryInput<OutputType>>::Value) ||
243          (Operation::BeginBinaryOperationsTwoOutputs < op &&
244           op < Operation::EndBinaryOperationsTwoOutputs &&
245           internal::AreMatchingBinaryInputAndBinaryOutput<InputType,
246                                                           OutputType>::value) ||
247          (Operation::BeginTernaryOperationsSingleOuput < op &&
248           op < Operation::EndTernaryOperationsSingleOutput &&
249           cpp::IsFloatingPointType<OutputType>::Value &&
250           cpp::IsSame<InputType, TernaryInput<OutputType>>::Value);
251 }
252 
253 template <Operation op, typename InputType, typename OutputType>
254 __attribute__((no_sanitize("address")))
255 cpp::EnableIfType<isValidOperation<op, InputType, OutputType>(),
256                   internal::MPFRMatcher<op, InputType, OutputType>>
257 getMPFRMatcher(InputType input, OutputType outputUnused, double t) {
258   return internal::MPFRMatcher<op, InputType, OutputType>(input, t);
259 }
260 
261 enum class RoundingMode : uint8_t { Upward, Downward, TowardZero, Nearest };
262 
263 template <typename T> T Round(T x, RoundingMode mode);
264 
265 template <typename T> bool RoundToLong(T x, long &result);
266 template <typename T> bool RoundToLong(T x, RoundingMode mode, long &result);
267 
268 } // namespace mpfr
269 } // namespace testing
270 } // namespace __llvm_libc
271 
272 #define EXPECT_MPFR_MATCH(op, input, matchValue, tolerance)                    \
273   EXPECT_THAT(matchValue, __llvm_libc::testing::mpfr::getMPFRMatcher<op>(      \
274                               input, matchValue, tolerance))
275 
276 #define ASSERT_MPFR_MATCH(op, input, matchValue, tolerance)                    \
277   ASSERT_THAT(matchValue, __llvm_libc::testing::mpfr::getMPFRMatcher<op>(      \
278                               input, matchValue, tolerance))
279 
280 #endif // LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H
281