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