#
# Module to determine if compiler can support Quadruple
# precision math operators.
#


# Search for the header quadmath.h
include(CheckIncludeFileCXX)
check_include_file_cxx(quadmath.h QUADMATH_HEADER)

# Check if compiler flags work as expected
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-Werror -fext-numeric-literals" HAVE_EXTENDED_NUMERIC_LITERALS)
if(HAVE_EXTENDED_NUMERIC_LITERALS)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals")
endif()

# 
# Run test for <quadmath.h> and __float128
# - Save the current CMAKE_REQUIRED_... variables before changing
# - Compile and run test
# - Restore to CMAKE_REQUIRED_... variables
#
include(CMakePushCheckState)
cmake_push_check_state(RESET)
list(APPEND CMAKE_REQUIRED_LIBRARIES "quadmath")

# Check if C++ source compiles
include(CheckCXXSourceCompiles)
CHECK_CXX_SOURCE_COMPILES("
  #include <quadmath.h>
  int main(void){
    __float128 foo = sqrtq(123.456);
    foo = FLT128_MIN;
  }" 
  QUADMATH_COMPILES
)
cmake_pop_check_state()

# 
# Run test for std::numeric_limits<__float128>
# - Save the current CMAKE_REQUIRED_... variables before changing
# - Compile and run test
# - Restore to CMAKE_REQUIRED_... variables
#
cmake_push_check_state(RESET)
list(APPEND CMAKE_REQUIRED_LIBRARIES "quadmath")

# Check if C++ source compiles
CHECK_CXX_SOURCE_COMPILES("
  #include <quadmath.h>
  #include <limits>
  int main(void){
    __float128 x = 1.0;
    auto spc_val = std::numeric_limits<__float128>::is_specialized;
    auto min_val = std::numeric_limits<__float128>::min();
    auto max_val = std::numeric_limits<__float128>::max();
    auto low_val = std::numeric_limits<__float128>::lowest();
  }" 
  STD_HAVE_QUAD_LIMITS
)
cmake_pop_check_state()

# 
# Run test for std::abs<__float128>()
# - Save the current CMAKE_REQUIRED_... variables before changing
# - Compile and run test
# - Restore to CMAKE_REQUIRED_... variables
#
cmake_push_check_state(RESET)
list(APPEND CMAKE_REQUIRED_LIBRARIES "quadmath")

# Check if C++ source compiles
CHECK_CXX_SOURCE_COMPILES("
  #include <quadmath.h>
  #include <cmath>
  int main(void){
    __float128 x = 1.0;     
    x = std::abs(1.0Q);
    x = std::pow(1.0Q, 2);
    x = std::tgamma(1.0Q);
  }" 
  STD_HAVE_QUAD_MATH
)
cmake_pop_check_state()

#
# Set used variables
#
if( QUADMATH_COMPILES )
  set(QUADMATH_FOUND TRUE)
  set(QUADMATH_LIBRARIES "quadmath")
  set(XSTD_USE_QUADMATH TRUE)

# - Already set using CMAKE_CXX_FLAGS (not supposed to do :>(
#  set(QUADMATH_CFLAGS "-fext-numeric-literals")

# - Have no need for yet
#  if( STD_HAVE_QUAD_ABS )       
#    add_definitions(-D STD_HAVE_QUAD_MATH)
#  endif()
endif()

#
# If Required but cannot find then crash
#
if (USE_QUADMATH AND NOT QUADMATH_FOUND)
  message(FATAL_ERROR "Quadruple precision math support was explicitly requested but is unavailable!")
endif()

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Quadmath
  DEFAULT_MSG
  QUADMATH_LIBRARIES
  QUADMATH_FOUND
)

#
# Define Imported Targets
#
if(QUADMATH_FOUND AND NOT TARGET QUADMATH::QUAD)
  add_library(QUADMATH::QUAD IMPORTED INTERFACE)
  set_target_properties(QUADMATH::QUAD PROPERTIES
    INTERFACE_COMPILE_OPTIONS "${QUADMATH_CFLAGS}"
    INTERFACE_LINK_LIBRARIES  "${QUADMATH_LIBRARIES}"
  )
endif()

