270 lines · c
1//===-- MPCUtils.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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_LIBC_UTILS_MPCWRAPPER_MPCUTILS_H10#define LLVM_LIBC_UTILS_MPCWRAPPER_MPCUTILS_H11 12#include "hdr/stdint_proxy.h"13#include "src/__support/CPP/type_traits.h"14#include "src/__support/complex_type.h"15#include "src/__support/macros/config.h"16#include "src/__support/macros/properties/complex_types.h"17#include "src/__support/macros/properties/types.h"18#include "test/UnitTest/RoundingModeUtils.h"19#include "test/UnitTest/Test.h"20 21namespace LIBC_NAMESPACE_DECL {22namespace testing {23namespace mpc {24 25enum class Operation {26 // Operations which take a single complex floating point number as input27 // and produce a single floating point number as output which has the same28 // floating point type as the real/imaginary part of the input.29 BeginUnaryOperationsSingleOutputDifferentOutputType,30 Carg,31 Cabs,32 EndUnaryOperationsSingleOutputDifferentOutputType,33 34 // Operations which take a single complex floating point number as input35 // and produce a single complex floating point number of the same kind36 // as output.37 BeginUnaryOperationsSingleOutputSameOutputType,38 Cproj,39 Csqrt,40 Clog,41 Cexp,42 Csinh,43 Ccosh,44 Ctanh,45 Casinh,46 Cacosh,47 Catanh,48 Csin,49 Ccos,50 Ctan,51 Casin,52 Cacos,53 Catan,54 EndUnaryOperationsSingleOutputSameOutputType,55 56 // Operations which take two complex floating point numbers as input57 // and produce a single complex floating point number of the same kind58 // as output.59 BeginBinaryOperationsSingleOutput,60 Cpow,61 EndBinaryOperationsSingleOutput,62};63 64using LIBC_NAMESPACE::fputil::testing::RoundingMode;65 66template <typename T> struct BinaryInput {67 static_assert(LIBC_NAMESPACE::cpp::is_complex_v<T>,68 "Template parameter of BinaryInput must be a complex floating "69 "point type.");70 71 using Type = T;72 T x, y;73};74 75namespace internal {76 77template <typename InputType, typename OutputType>78bool compare_unary_operation_single_output_same_type(Operation op,79 InputType input,80 OutputType libc_output,81 double ulp_tolerance,82 RoundingMode rounding);83 84template <typename InputType, typename OutputType>85bool compare_unary_operation_single_output_different_type(86 Operation op, InputType input, OutputType libc_output, double ulp_tolerance,87 RoundingMode rounding);88 89template <typename InputType, typename OutputType>90bool compare_binary_operation_one_output(Operation op,91 const BinaryInput<InputType> &input,92 OutputType libc_output,93 double ulp_tolerance,94 RoundingMode rounding);95 96template <typename InputType, typename OutputType>97void explain_unary_operation_single_output_same_type_error(98 Operation op, InputType input, OutputType match_value, double ulp_tolerance,99 RoundingMode rounding);100 101template <typename InputType, typename OutputType>102void explain_unary_operation_single_output_different_type_error(103 Operation op, InputType input, OutputType match_value, double ulp_tolerance,104 RoundingMode rounding);105 106template <typename InputType, typename OutputType>107void explain_binary_operation_one_output_error(108 Operation op, const BinaryInput<InputType> &input, OutputType match_value,109 double ulp_tolerance, RoundingMode rounding);110 111template <Operation op, typename InputType, typename OutputType>112class MPCMatcher : public testing::Matcher<OutputType> {113private:114 InputType input;115 OutputType match_value;116 double ulp_tolerance;117 RoundingMode rounding;118 119public:120 MPCMatcher(InputType testInput, double ulp_tolerance, RoundingMode rounding)121 : input(testInput), ulp_tolerance(ulp_tolerance), rounding(rounding) {}122 123 bool match(OutputType libcResult) {124 match_value = libcResult;125 return match(input, match_value);126 }127 128 void explainError() override { // NOLINT129 explain_error(input, match_value);130 }131 132private:133 template <typename InType, typename OutType>134 bool match(InType in, OutType out) {135 if (cpp::is_same_v<InType, OutType>) {136 return compare_unary_operation_single_output_same_type(137 op, in, out, ulp_tolerance, rounding);138 } else {139 return compare_unary_operation_single_output_different_type(140 op, in, out, ulp_tolerance, rounding);141 }142 }143 144 template <typename T, typename U>145 bool match(const BinaryInput<T> &in, U out) {146 return compare_binary_operation_one_output(op, in, out, ulp_tolerance,147 rounding);148 }149 150 template <typename InType, typename OutType>151 void explain_error(InType in, OutType out) {152 if (cpp::is_same_v<InType, OutType>) {153 explain_unary_operation_single_output_same_type_error(154 op, in, out, ulp_tolerance, rounding);155 } else {156 explain_unary_operation_single_output_different_type_error(157 op, in, out, ulp_tolerance, rounding);158 }159 }160 161 template <typename T, typename U>162 void explain_error(const BinaryInput<T> &in, U out) {163 explain_binary_operation_one_output_error(op, in, out, ulp_tolerance,164 rounding);165 }166};167 168} // namespace internal169 170// Return true if the input and ouput types for the operation op are valid171// types.172template <Operation op, typename InputType, typename OutputType>173constexpr bool is_valid_operation() {174 return (Operation::BeginBinaryOperationsSingleOutput < op &&175 op < Operation::EndBinaryOperationsSingleOutput &&176 cpp::is_complex_type_same<InputType, OutputType>() &&177 cpp::is_complex_v<InputType>) ||178 (Operation::BeginUnaryOperationsSingleOutputSameOutputType < op &&179 op < Operation::EndUnaryOperationsSingleOutputSameOutputType &&180 cpp::is_complex_type_same<InputType, OutputType>() &&181 cpp::is_complex_v<InputType>) ||182 (Operation::BeginUnaryOperationsSingleOutputDifferentOutputType < op &&183 op < Operation::EndUnaryOperationsSingleOutputDifferentOutputType &&184 cpp::is_same_v<make_real_t<InputType>, OutputType> &&185 cpp::is_complex_v<InputType>);186}187 188template <Operation op, typename InputType, typename OutputType>189cpp::enable_if_t<is_valid_operation<op, InputType, OutputType>(),190 internal::MPCMatcher<op, InputType, OutputType>>191get_mpc_matcher(InputType input, [[maybe_unused]] OutputType output,192 double ulp_tolerance, RoundingMode rounding) {193 return internal::MPCMatcher<op, InputType, OutputType>(input, ulp_tolerance,194 rounding);195}196 197} // namespace mpc198} // namespace testing199} // namespace LIBC_NAMESPACE_DECL200 201#define EXPECT_MPC_MATCH_DEFAULT(op, input, match_value, ulp_tolerance) \202 EXPECT_THAT(match_value, \203 LIBC_NAMESPACE::testing::mpc::get_mpc_matcher<op>( \204 input, match_value, ulp_tolerance, \205 LIBC_NAMESPACE::fputil::testing::RoundingMode::Nearest))206 207#define EXPECT_MPC_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \208 rounding) \209 EXPECT_THAT(match_value, LIBC_NAMESPACE::testing::mpc::get_mpc_matcher<op>( \210 input, match_value, ulp_tolerance, rounding))211 212#define EXPECT_MPC_MATCH_ALL_ROUNDING_HELPER(op, input, match_value, \213 ulp_tolerance, rounding) \214 { \215 MPCRND::ForceRoundingMode __r(rounding); \216 if (__r.success) { \217 EXPECT_MPC_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \218 rounding); \219 } \220 }221 222#define EXPECT_MPC_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance) \223 { \224 namespace MPCRND = LIBC_NAMESPACE::fputil::testing; \225 for (int i = 0; i < 4; i++) { \226 MPCRND::RoundingMode r_mode = static_cast<MPCRND::RoundingMode>(i); \227 EXPECT_MPC_MATCH_ALL_ROUNDING_HELPER(op, input, match_value, \228 ulp_tolerance, r_mode); \229 } \230 }231 232#define TEST_MPC_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \233 rounding) \234 LIBC_NAMESPACE::testing::mpc::get_mpc_matcher<op>(input, match_value, \235 ulp_tolerance, rounding) \236 .match(match_value)237 238#define ASSERT_MPC_MATCH_DEFAULT(op, input, match_value, ulp_tolerance) \239 ASSERT_THAT(match_value, \240 LIBC_NAMESPACE::testing::mpc::get_mpc_matcher<op>( \241 input, match_value, ulp_tolerance, \242 LIBC_NAMESPACE::fputil::testing::RoundingMode::Nearest))243 244#define ASSERT_MPC_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \245 rounding) \246 ASSERT_THAT(match_value, LIBC_NAMESPACE::testing::mpc::get_mpc_matcher<op>( \247 input, match_value, ulp_tolerance, rounding))248 249#define ASSERT_MPC_MATCH_ALL_ROUNDING_HELPER(op, input, match_value, \250 ulp_tolerance, rounding) \251 { \252 MPCRND::ForceRoundingMode __r(rounding); \253 if (__r.success) { \254 ASSERT_MPC_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \255 rounding); \256 } \257 }258 259#define ASSERT_MPC_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance) \260 { \261 namespace MPCRND = LIBC_NAMESPACE::fputil::testing; \262 for (int i = 0; i < 4; i++) { \263 MPCRND::RoundingMode r_mode = static_cast<MPCRND::RoundingMode>(i); \264 ASSERT_MPC_MATCH_ALL_ROUNDING_HELPER(op, input, match_value, \265 ulp_tolerance, r_mode); \266 } \267 }268 269#endif // LLVM_LIBC_UTILS_MPCWRAPPER_MPCUTILS_H270