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 "src/__support/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   Expm1,
32   Floor,
33   Log,
34   Mod2PI,
35   ModPIOver2,
36   ModPIOver4,
37   Round,
38   Sin,
39   Sqrt,
40   Tan,
41   Trunc,
42   EndUnaryOperationsSingleOutput,
43 
44   // Operations which take a single floating point nubmer as input
45   // but produce two outputs. The first ouput is a floating point
46   // number of the same type as the input. The second output is of type
47   // 'int'.
48   BeginUnaryOperationsTwoOutputs,
49   Frexp, // Floating point output, the first output, is the fractional part.
50   EndUnaryOperationsTwoOutputs,
51 
52   // Operations wich take two floating point nubmers of the same type as
53   // input and produce a single floating point number of the same type as
54   // output.
55   BeginBinaryOperationsSingleOutput,
56   Hypot,
57   EndBinaryOperationsSingleOutput,
58 
59   // Operations which take two floating point numbers of the same type as
60   // input and produce two outputs. The first output is a floating nubmer of
61   // the same type as the inputs. The second output is af type 'int'.
62   BeginBinaryOperationsTwoOutputs,
63   RemQuo, // The first output, the floating point output, is the remainder.
64   EndBinaryOperationsTwoOutputs,
65 
66   // Operations which take three floating point nubmers of the same type as
67   // input and produce a single floating point number of the same type as
68   // output.
69   BeginTernaryOperationsSingleOuput,
70   Fma,
71   EndTernaryOperationsSingleOutput,
72 };
73 
74 template <typename T> struct BinaryInput {
75   static_assert(
76       __llvm_libc::cpp::IsFloatingPointType<T>::Value,
77       "Template parameter of BinaryInput must be a floating point type.");
78 
79   using Type = T;
80   T x, y;
81 };
82 
83 template <typename T> struct TernaryInput {
84   static_assert(
85       __llvm_libc::cpp::IsFloatingPointType<T>::Value,
86       "Template parameter of TernaryInput must be a floating point type.");
87 
88   using Type = T;
89   T x, y, z;
90 };
91 
92 template <typename T> struct BinaryOutput {
93   T f;
94   int i;
95 };
96 
97 namespace internal {
98 
99 template <typename T1, typename T2>
100 struct AreMatchingBinaryInputAndBinaryOutput {
101   static constexpr bool VALUE = false;
102 };
103 
104 template <typename T>
105 struct AreMatchingBinaryInputAndBinaryOutput<BinaryInput<T>, BinaryOutput<T>> {
106   static constexpr bool VALUE = cpp::IsFloatingPointType<T>::Value;
107 };
108 
109 template <typename T>
110 bool compare_unary_operation_single_output(Operation op, T input, T libc_output,
111                                            double t);
112 template <typename T>
113 bool compare_unary_operation_two_outputs(Operation op, T input,
114                                          const BinaryOutput<T> &libc_output,
115                                          double t);
116 template <typename T>
117 bool compare_binary_operation_two_outputs(Operation op,
118                                           const BinaryInput<T> &input,
119                                           const BinaryOutput<T> &libc_output,
120                                           double t);
121 
122 template <typename T>
123 bool compare_binary_operation_one_output(Operation op,
124                                          const BinaryInput<T> &input,
125                                          T libc_output, double t);
126 
127 template <typename T>
128 bool compare_ternary_operation_one_output(Operation op,
129                                           const TernaryInput<T> &input,
130                                           T libc_output, double t);
131 
132 template <typename T>
133 void explain_unary_operation_single_output_error(Operation op, T input,
134                                                  T match_value,
135                                                  testutils::StreamWrapper &OS);
136 template <typename T>
137 void explain_unary_operation_two_outputs_error(
138     Operation op, T input, const BinaryOutput<T> &match_value,
139     testutils::StreamWrapper &OS);
140 template <typename T>
141 void explain_binary_operation_two_outputs_error(
142     Operation op, const BinaryInput<T> &input,
143     const BinaryOutput<T> &match_value, testutils::StreamWrapper &OS);
144 
145 template <typename T>
146 void explain_binary_operation_one_output_error(Operation op,
147                                                const BinaryInput<T> &input,
148                                                T match_value,
149                                                testutils::StreamWrapper &OS);
150 
151 template <typename T>
152 void explain_ternary_operation_one_output_error(Operation op,
153                                                 const TernaryInput<T> &input,
154                                                 T match_value,
155                                                 testutils::StreamWrapper &OS);
156 
157 template <Operation op, typename InputType, typename OutputType>
158 class MPFRMatcher : public testing::Matcher<OutputType> {
159   InputType input;
160   OutputType match_value;
161   double ulp_tolerance;
162 
163 public:
164   MPFRMatcher(InputType testInput, double ulp_tolerance)
165       : input(testInput), ulp_tolerance(ulp_tolerance) {}
166 
167   bool match(OutputType libcResult) {
168     match_value = libcResult;
169     return match(input, match_value, ulp_tolerance);
170   }
171 
172   // This method is marked with NOLINT because it the name `explainError`
173   // does not confirm to the coding style.
174   void explainError(testutils::StreamWrapper &OS) override { // NOLINT
175     explain_error(input, match_value, OS);
176   }
177 
178 private:
179   template <typename T> static bool match(T in, T out, double tolerance) {
180     return compare_unary_operation_single_output(op, in, out, tolerance);
181   }
182 
183   template <typename T>
184   static bool match(T in, const BinaryOutput<T> &out, double tolerance) {
185     return compare_unary_operation_two_outputs(op, in, out, tolerance);
186   }
187 
188   template <typename T>
189   static bool match(const BinaryInput<T> &in, T out, double tolerance) {
190     return compare_binary_operation_one_output(op, in, out, tolerance);
191   }
192 
193   template <typename T>
194   static bool match(BinaryInput<T> in, const BinaryOutput<T> &out,
195                     double tolerance) {
196     return compare_binary_operation_two_outputs(op, in, out, tolerance);
197   }
198 
199   template <typename T>
200   static bool match(const TernaryInput<T> &in, T out, double tolerance) {
201     return compare_ternary_operation_one_output(op, in, out, tolerance);
202   }
203 
204   template <typename T>
205   static void explain_error(T in, T out, testutils::StreamWrapper &OS) {
206     explain_unary_operation_single_output_error(op, in, out, OS);
207   }
208 
209   template <typename T>
210   static void explain_error(T in, const BinaryOutput<T> &out,
211                             testutils::StreamWrapper &OS) {
212     explain_unary_operation_two_outputs_error(op, in, out, OS);
213   }
214 
215   template <typename T>
216   static void explain_error(const BinaryInput<T> &in,
217                             const BinaryOutput<T> &out,
218                             testutils::StreamWrapper &OS) {
219     explain_binary_operation_two_outputs_error(op, in, out, OS);
220   }
221 
222   template <typename T>
223   static void explain_error(const BinaryInput<T> &in, T out,
224                             testutils::StreamWrapper &OS) {
225     explain_binary_operation_one_output_error(op, in, out, OS);
226   }
227 
228   template <typename T>
229   static void explain_error(const TernaryInput<T> &in, T out,
230                             testutils::StreamWrapper &OS) {
231     explain_ternary_operation_one_output_error(op, in, out, OS);
232   }
233 };
234 
235 } // namespace internal
236 
237 // Return true if the input and ouput types for the operation op are valid
238 // types.
239 template <Operation op, typename InputType, typename OutputType>
240 constexpr bool is_valid_operation() {
241   return (Operation::BeginUnaryOperationsSingleOutput < op &&
242           op < Operation::EndUnaryOperationsSingleOutput &&
243           cpp::IsSame<InputType, OutputType>::Value &&
244           cpp::IsFloatingPointType<InputType>::Value) ||
245          (Operation::BeginUnaryOperationsTwoOutputs < op &&
246           op < Operation::EndUnaryOperationsTwoOutputs &&
247           cpp::IsFloatingPointType<InputType>::Value &&
248           cpp::IsSame<OutputType, BinaryOutput<InputType>>::Value) ||
249          (Operation::BeginBinaryOperationsSingleOutput < op &&
250           op < Operation::EndBinaryOperationsSingleOutput &&
251           cpp::IsFloatingPointType<OutputType>::Value &&
252           cpp::IsSame<InputType, BinaryInput<OutputType>>::Value) ||
253          (Operation::BeginBinaryOperationsTwoOutputs < op &&
254           op < Operation::EndBinaryOperationsTwoOutputs &&
255           internal::AreMatchingBinaryInputAndBinaryOutput<InputType,
256                                                           OutputType>::VALUE) ||
257          (Operation::BeginTernaryOperationsSingleOuput < op &&
258           op < Operation::EndTernaryOperationsSingleOutput &&
259           cpp::IsFloatingPointType<OutputType>::Value &&
260           cpp::IsSame<InputType, TernaryInput<OutputType>>::Value);
261 }
262 
263 template <Operation op, typename InputType, typename OutputType>
264 __attribute__((no_sanitize("address")))
265 cpp::EnableIfType<is_valid_operation<op, InputType, OutputType>(),
266                   internal::MPFRMatcher<op, InputType, OutputType>>
267 get_mpfr_matcher(InputType input, OutputType output_unused, double t) {
268   return internal::MPFRMatcher<op, InputType, OutputType>(input, t);
269 }
270 
271 enum class RoundingMode : uint8_t { Upward, Downward, TowardZero, Nearest };
272 
273 template <typename T> T round(T x, RoundingMode mode);
274 
275 template <typename T> bool round_to_long(T x, long &result);
276 template <typename T> bool round_to_long(T x, RoundingMode mode, long &result);
277 
278 } // namespace mpfr
279 } // namespace testing
280 } // namespace __llvm_libc
281 
282 #define EXPECT_MPFR_MATCH(op, input, match_value, tolerance)                   \
283   EXPECT_THAT(match_value, __llvm_libc::testing::mpfr::get_mpfr_matcher<op>(   \
284                                input, match_value, tolerance))
285 
286 #define ASSERT_MPFR_MATCH(op, input, match_value, tolerance)                   \
287   ASSERT_THAT(match_value, __llvm_libc::testing::mpfr::get_mpfr_matcher<op>(   \
288                                input, match_value, tolerance))
289 
290 #endif // LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H
291