//===-- Unittests for shared math check exception functions ---------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "shared/math_check_exceptions.h"
#include "src/__support/CPP/array.h"
#include "src/__support/math/check/exp_exceptions.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"

template <typename T>
class CheckExpTest : public LIBC_NAMESPACE::testing::Test {

  DECLARE_SPECIAL_CONSTANTS(T)

public:
  void run_test() {
    using LIBC_NAMESPACE::shared::check::exp_exceptions;
    using IntArray = LIBC_NAMESPACE::cpp::array<int, 4>;
    using Bounds = LIBC_NAMESPACE::math::check::exp_internal::Bounds<T>;

    constexpr IntArray ROUNDING_MODES = {FE_TONEAREST, FE_UPWARD, FE_DOWNWARD,
                                         FE_TOWARDZERO};
    for (auto rm : ROUNDING_MODES) {
      EXPECT_EQ(FE_INVALID, exp_exceptions(sNaN, rm));
      EXPECT_EQ(FE_INVALID, exp_exceptions(neg_sNaN, rm));
      EXPECT_EQ(0, exp_exceptions(aNaN, rm));
      EXPECT_EQ(0, exp_exceptions(neg_aNaN, rm));
      EXPECT_EQ(0, exp_exceptions(inf, rm));
      EXPECT_EQ(0, exp_exceptions(neg_inf, rm));
      EXPECT_EQ(0, exp_exceptions(zero, rm));
      EXPECT_EQ(0, exp_exceptions(neg_zero, rm));
      EXPECT_EQ(FE_UNDERFLOW,
                exp_exceptions(neg_max_normal, rm) & FE_UNDERFLOW);
      EXPECT_EQ(FE_UNDERFLOW, exp_exceptions(Bounds::LOWER, rm) & FE_UNDERFLOW);
      EXPECT_EQ(0, exp_exceptions(T(1), rm) & (~FE_INEXACT));
    }

    EXPECT_EQ(FE_OVERFLOW,
              exp_exceptions(max_normal, FE_TONEAREST) & FE_OVERFLOW);
    EXPECT_EQ(FE_OVERFLOW, exp_exceptions(max_normal, FE_UPWARD) & FE_OVERFLOW);
    EXPECT_EQ(0, exp_exceptions(max_normal, FE_DOWNWARD) & FE_OVERFLOW);
    EXPECT_EQ(0, exp_exceptions(max_normal, FE_TOWARDZERO) & FE_OVERFLOW);

    EXPECT_EQ(FE_OVERFLOW,
              exp_exceptions(Bounds::UPPER, FE_TONEAREST) & FE_OVERFLOW);
    EXPECT_EQ(FE_OVERFLOW,
              exp_exceptions(Bounds::UPPER, FE_UPWARD) & FE_OVERFLOW);
    EXPECT_EQ(0, exp_exceptions(Bounds::UPPER, FE_DOWNWARD) & FE_OVERFLOW);
    EXPECT_EQ(0, exp_exceptions(Bounds::UPPER, FE_TOWARDZERO) & FE_OVERFLOW);
  }
};

using LlvmLibcCheckExpTestFloat = CheckExpTest<float>;
using LlvmLibcCheckExpTestDouble = CheckExpTest<double>;

TEST_F(LlvmLibcCheckExpTestFloat, CheckExpfExceptions) { run_test(); }
TEST_F(LlvmLibcCheckExpTestDouble, CheckExpExceptions) { run_test(); }
