//===-- Unittests for the DyadicFloat class -------------------------------===//
//
// 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 "src/__support/FPUtil/dyadic_float.h"
#include "src/__support/big_int.h"
#include "src/__support/macros/properties/types.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"

using Float128 = LIBC_NAMESPACE::fputil::DyadicFloat<128>;
using Float192 = LIBC_NAMESPACE::fputil::DyadicFloat<192>;
using Float256 = LIBC_NAMESPACE::fputil::DyadicFloat<256>;
using LIBC_NAMESPACE::Sign;

TEST(LlvmLibcDyadicFloatTest, BasicConversions) {
  Float128 x(Sign::POS, /*exponent*/ 0,
             /*mantissa*/ Float128::MantissaType(1));
  ASSERT_FP_EQ(1.0f, float(x));
  ASSERT_FP_EQ(1.0, double(x));

  Float128 y(0x1.0p-53);
  ASSERT_FP_EQ(0x1.0p-53f, float(y));
  ASSERT_FP_EQ(0x1.0p-53, double(y));

  Float128 z = quick_add(x, y);

  EXPECT_FP_EQ_ALL_ROUNDING(float(x) + float(y), float(z));
  EXPECT_FP_EQ_ALL_ROUNDING(double(x) + double(y), double(z));
}

TEST(LlvmLibcDyadicFloatTest, QuickAdd) {
  Float192 x(Sign::POS, /*exponent*/ 0,
             /*mantissa*/ Float192::MantissaType(0x123456));
  ASSERT_FP_EQ(0x1.23456p20, double(x));

  Float192 y(0x1.abcdefp-20);
  ASSERT_FP_EQ(0x1.abcdefp-20, double(y));

  Float192 z = quick_add(x, y);
  EXPECT_FP_EQ_ALL_ROUNDING(double(x) + double(y), double(z));
}

TEST(LlvmLibcDyadicFloatTest, QuickMul) {
  Float256 x(Sign::POS, /*exponent*/ 0,
             /*mantissa*/ Float256::MantissaType(0x123456));
  ASSERT_FP_EQ(0x1.23456p20, double(x));

  Float256 y(0x1.abcdefp-25);
  ASSERT_FP_EQ(0x1.abcdefp-25, double(y));

  Float256 z = quick_mul(x, y);
  EXPECT_FP_EQ_ALL_ROUNDING(double(x) * double(y), double(z));
}

#define TEST_EDGE_RANGES(Name, Type)                                           \
  TEST(LlvmLibcDyadicFloatTest, EdgeRanges##Name) {                            \
    using Bits = LIBC_NAMESPACE::fputil::FPBits<Type>;                         \
    using DFType = LIBC_NAMESPACE::fputil::DyadicFloat<Bits::STORAGE_LEN>;     \
    Type max_normal = Bits::max_normal().get_val();                            \
    Type min_normal = Bits::min_normal().get_val();                            \
    Type min_subnormal = Bits::min_subnormal().get_val();                      \
    Type two(2);                                                               \
                                                                               \
    DFType x(min_normal);                                                      \
    EXPECT_FP_EQ_ALL_ROUNDING(min_normal, static_cast<Type>(x));               \
    --x.exponent;                                                              \
    EXPECT_FP_EQ(min_normal / two, static_cast<Type>(x));                      \
                                                                               \
    DFType y(two *min_normal - min_subnormal);                                 \
    --y.exponent;                                                              \
    EXPECT_FP_EQ(min_normal, static_cast<Type>(y));                            \
                                                                               \
    DFType z(min_subnormal);                                                   \
    EXPECT_FP_EQ_ALL_ROUNDING(min_subnormal, static_cast<Type>(z));            \
    --z.exponent;                                                              \
    EXPECT_FP_EQ(Bits::zero().get_val(), static_cast<Type>(z));                \
                                                                               \
    DFType t(max_normal);                                                      \
    EXPECT_FP_EQ_ALL_ROUNDING(max_normal, static_cast<Type>(t));               \
    ++t.exponent;                                                              \
    EXPECT_FP_EQ(Bits::inf().get_val(), static_cast<Type>(t));                 \
  }                                                                            \
  static_assert(true, "Require semicolon.")

TEST_EDGE_RANGES(Float, float);
TEST_EDGE_RANGES(Double, double);
TEST_EDGE_RANGES(LongDouble, long double);
#ifdef LIBC_TYPES_HAS_FLOAT16
TEST_EDGE_RANGES(Float16, float16);
#endif
