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