//===-- Utility class to test different flavors of getpayload ---*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LIBC_TEST_SRC_MATH_SMOKE_GETPAYLOADTEST_H
#define LIBC_TEST_SRC_MATH_SMOKE_GETPAYLOADTEST_H

#include "test/UnitTest/FEnvSafeTest.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"

using LIBC_NAMESPACE::Sign;

template <typename T>
class GetPayloadTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {

  DECLARE_SPECIAL_CONSTANTS(T)

public:
  typedef T (*GetPayloadFunc)(const T *);

  T funcWrapper(GetPayloadFunc func, T x) { return func(&x); }

  void testNonNaNs(GetPayloadFunc func) {
    EXPECT_FP_EQ(T(-1.0), funcWrapper(func, T(0.0)));
    EXPECT_FP_EQ(T(-1.0), funcWrapper(func, T(-0.0)));
    EXPECT_FP_EQ(T(-1.0), funcWrapper(func, T(0.1)));
    EXPECT_FP_EQ(T(-1.0), funcWrapper(func, T(-0.1)));
    EXPECT_FP_EQ(T(-1.0), funcWrapper(func, T(123.38)));
    EXPECT_FP_EQ(T(-1.0), funcWrapper(func, T(-123.38)));
    EXPECT_FP_EQ(T(-1.0), funcWrapper(func, inf));
    EXPECT_FP_EQ(T(-1.0), funcWrapper(func, neg_inf));
  }

  void testNaNs(GetPayloadFunc func) {
    EXPECT_FP_EQ(T(0.0), funcWrapper(func, aNaN));
    EXPECT_FP_EQ(T(0.0), funcWrapper(func, neg_aNaN));

    // Essentially this:
    //   T default_snan_payload = StorageType(1) << (FPBits::FRACTION_LEN - 2);
    // but supports StorageType being a BigInt.
    FPBits default_snan_payload_bits = FPBits::one();
    default_snan_payload_bits.set_biased_exponent(FPBits::FRACTION_LEN - 2 +
                                                  FPBits::EXP_BIAS);
    T default_snan_payload = default_snan_payload_bits.get_val();

    EXPECT_FP_EQ(default_snan_payload, funcWrapper(func, sNaN));
    EXPECT_FP_EQ(default_snan_payload, funcWrapper(func, neg_sNaN));

    if constexpr (FPBits::FRACTION_LEN - 1 >= 6) {
      // [S] [E..E] [QM..M] -> number of M bits should be at least 6
      // 0x31 = 0b110001 = 6 bits
      T qnan_31 = FPBits::quiet_nan(Sign::POS, 0x31).get_val();
      T neg_qnan_31 = FPBits::quiet_nan(Sign::NEG, 0x31).get_val();
      T snan_31 = FPBits::signaling_nan(Sign::POS, 0x31).get_val();
      T neg_snan_31 = FPBits::signaling_nan(Sign::NEG, 0x31).get_val();
      EXPECT_FP_EQ(T(0x31.0p+0), funcWrapper(func, qnan_31));
      EXPECT_FP_EQ(T(0x31.0p+0), funcWrapper(func, neg_qnan_31));
      EXPECT_FP_EQ(T(0x31.0p+0), funcWrapper(func, snan_31));
      EXPECT_FP_EQ(T(0x31.0p+0), funcWrapper(func, neg_snan_31));

      // 0x15 = 0b10101 = 5 bits
      T qnan_15 = FPBits::quiet_nan(Sign::POS, 0x15).get_val();
      T neg_qnan_15 = FPBits::quiet_nan(Sign::NEG, 0x15).get_val();
      T snan_15 = FPBits::signaling_nan(Sign::POS, 0x15).get_val();
      T neg_snan_15 = FPBits::signaling_nan(Sign::NEG, 0x15).get_val();
      EXPECT_FP_EQ(T(0x15.0p+0), funcWrapper(func, qnan_15));
      EXPECT_FP_EQ(T(0x15.0p+0), funcWrapper(func, neg_qnan_15));
      EXPECT_FP_EQ(T(0x15.0p+0), funcWrapper(func, snan_15));
      EXPECT_FP_EQ(T(0x15.0p+0), funcWrapper(func, neg_snan_15));
    }

    if constexpr (FPBits::FRACTION_LEN - 1 >= 7) {
      T qnan_42 = FPBits::quiet_nan(Sign::POS, 0x42).get_val();
      T neg_qnan_42 = FPBits::quiet_nan(Sign::NEG, 0x42).get_val();
      T snan_42 = FPBits::signaling_nan(Sign::POS, 0x42).get_val();
      T neg_snan_42 = FPBits::signaling_nan(Sign::NEG, 0x42).get_val();
      EXPECT_FP_EQ(T(0x42.0p+0), funcWrapper(func, qnan_42));
      EXPECT_FP_EQ(T(0x42.0p+0), funcWrapper(func, neg_qnan_42));
      EXPECT_FP_EQ(T(0x42.0p+0), funcWrapper(func, snan_42));
      EXPECT_FP_EQ(T(0x42.0p+0), funcWrapper(func, neg_snan_42));
    }

    if constexpr (FPBits::FRACTION_LEN - 1 >= 9) {
      T qnan_123 = FPBits::quiet_nan(Sign::POS, 0x123).get_val();
      T neg_qnan_123 = FPBits::quiet_nan(Sign::NEG, 0x123).get_val();
      T snan_123 = FPBits::signaling_nan(Sign::POS, 0x123).get_val();
      T neg_snan_123 = FPBits::signaling_nan(Sign::NEG, 0x123).get_val();
      EXPECT_FP_EQ(T(0x123.0p+0), funcWrapper(func, qnan_123));
      EXPECT_FP_EQ(T(0x123.0p+0), funcWrapper(func, neg_qnan_123));
      EXPECT_FP_EQ(T(0x123.0p+0), funcWrapper(func, snan_123));
      EXPECT_FP_EQ(T(0x123.0p+0), funcWrapper(func, neg_snan_123));
    }
  }
};

#define LIST_GETPAYLOAD_TESTS(T, func)                                         \
  using LlvmLibcGetPayloadTest = GetPayloadTestTemplate<T>;                    \
  TEST_F(LlvmLibcGetPayloadTest, NonNaNs) { testNonNaNs(&func); }              \
  TEST_F(LlvmLibcGetPayloadTest, NaNs) { testNaNs(&func); }

#endif // LIBC_TEST_SRC_MATH_SMOKE_GETPAYLOADTEST_H
