# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

cmake_minimum_required(VERSION 3.14)

# Apply SIMD ISA flags and Highway linkage from parent.
set(FAST_MATH_EXTRA_FLAGS ${DISPENSO_FAST_MATH_SIMD_FLAGS})
set(FAST_MATH_EXTRA_LIBS "")
if(DISPENSO_FAST_MATH_HIGHWAY AND TARGET hwy)
  list(APPEND FAST_MATH_EXTRA_LIBS hwy)
  # Treat Highway as a system include to suppress warnings from its headers.
  if(MSVC)
    list(APPEND FAST_MATH_EXTRA_FLAGS /external:I ${hwy_SOURCE_DIR})
  else()
    list(APPEND FAST_MATH_EXTRA_FLAGS -isystem${hwy_SOURCE_DIR})
  endif()
endif()

# Scalar function tests (link eval.cpp helper)
file(GLOB FAST_MATH_TEST_FILES CONFIGURE_DEPENDS "*_test.cpp")

# Separate SIMD backend tests (no eval.cpp dependency)
set(SIMD_TEST_FILES
  ${CMAKE_CURRENT_SOURCE_DIR}/sse_test.cpp
  ${CMAKE_CURRENT_SOURCE_DIR}/avx_test.cpp
  ${CMAKE_CURRENT_SOURCE_DIR}/avx512_test.cpp
  ${CMAKE_CURRENT_SOURCE_DIR}/neon_test.cpp
  ${CMAKE_CURRENT_SOURCE_DIR}/hwy_test.cpp
)

foreach(SIMD_FILE ${SIMD_TEST_FILES})
  list(REMOVE_ITEM FAST_MATH_TEST_FILES ${SIMD_FILE})
endforeach()

# Scalar / util tests: each links eval.cpp
foreach(TEST_FILE ${FAST_MATH_TEST_FILES})
  set(TEST_NAME)
  get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE)
  set(TEST_NAME "fast_math_${TEST_NAME}")
  add_executable(${TEST_NAME} ${TEST_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/eval.cpp)
  target_compile_features(${TEST_NAME} PRIVATE cxx_std_17)
  target_compile_options(${TEST_NAME} PRIVATE
    $<$<CXX_COMPILER_ID:MSVC>:/W3 /WX>
    $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -pedantic -Wconversion -Wno-sign-conversion -Werror>
    ${FAST_MATH_EXTRA_FLAGS}
  )
  target_link_libraries(${TEST_NAME} gmock_main gtest dispenso ${FAST_MATH_EXTRA_LIBS})
  gtest_discover_tests(${TEST_NAME}
    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
    LABELS "fast_math")
  set_target_properties(${TEST_NAME} PROPERTIES FOLDER tests/fast_math)
endforeach()

# Standalone evaluation tools (not gtests — no eval.cpp, no gtest linkage).
foreach(EVAL_FILE
    ${CMAKE_CURRENT_SOURCE_DIR}/ulp_eval.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/pow_ulp_eval.cpp)
  if(NOT EXISTS ${EVAL_FILE})
    continue()
  endif()
  get_filename_component(EVAL_NAME ${EVAL_FILE} NAME_WE)
  set(EVAL_NAME "fast_math_${EVAL_NAME}")
  add_executable(${EVAL_NAME} ${EVAL_FILE})
  target_compile_features(${EVAL_NAME} PRIVATE cxx_std_17)
  target_compile_options(${EVAL_NAME} PRIVATE
    $<$<CXX_COMPILER_ID:MSVC>:/W3>
    $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -pedantic -Wconversion -Wno-sign-conversion>
    ${FAST_MATH_EXTRA_FLAGS}
  )
  target_link_libraries(${EVAL_NAME} dispenso ${FAST_MATH_EXTRA_LIBS})
  set_target_properties(${EVAL_NAME} PROPERTIES FOLDER tests/fast_math)
endforeach()

# SIMD backend tests: no eval.cpp dependency.
foreach(TEST_FILE ${SIMD_TEST_FILES})
  set(TEST_NAME)
  get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE)
  set(TEST_NAME "fast_math_${TEST_NAME}")
  add_executable(${TEST_NAME} ${TEST_FILE})
  target_compile_features(${TEST_NAME} PRIVATE cxx_std_17)
  target_compile_options(${TEST_NAME} PRIVATE
    $<$<CXX_COMPILER_ID:MSVC>:/W3 /WX>
    $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -pedantic -Wconversion -Wno-sign-conversion -Werror>
    ${FAST_MATH_EXTRA_FLAGS}
  )
  target_link_libraries(${TEST_NAME} gmock_main gtest dispenso ${FAST_MATH_EXTRA_LIBS})
  gtest_discover_tests(${TEST_NAME}
    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
    LABELS "fast_math")
  set_target_properties(${TEST_NAME} PROPERTIES FOLDER tests/fast_math)
endforeach()

# CUDA tests: compile .cu files with nvcc, verify fast_math works on GPU.
if(DISPENSO_FAST_MATH_CUDA)
  # Minimal compile test — verifies headers compile under nvcc.
  add_executable(fast_math_cuda_minimal_test
    ${CMAKE_CURRENT_SOURCE_DIR}/cuda_minimal_test.cu)
  target_compile_features(fast_math_cuda_minimal_test PRIVATE cxx_std_17)
  target_compile_options(fast_math_cuda_minimal_test PRIVATE
    $<$<COMPILE_LANGUAGE:CUDA>:--expt-relaxed-constexpr --expt-extended-lambda -Werror=cross-execution-space-call>)
  target_link_libraries(fast_math_cuda_minimal_test dispenso CUDA::cudart_static)
  set_target_properties(fast_math_cuda_minimal_test PROPERTIES
    CUDA_SEPARABLE_COMPILATION OFF
    FOLDER tests/fast_math)
  add_test(NAME fast_math_cuda_minimal_test COMMAND fast_math_cuda_minimal_test)
  set_tests_properties(fast_math_cuda_minimal_test PROPERTIES LABELS "fast_math")

  # Full correctness test — runs fast_math functions on GPU, compares to CPU.
  add_executable(fast_math_cuda_test
    ${CMAKE_CURRENT_SOURCE_DIR}/cuda_test.cu)
  target_compile_features(fast_math_cuda_test PRIVATE cxx_std_17)
  target_compile_options(fast_math_cuda_test PRIVATE
    $<$<COMPILE_LANGUAGE:CUDA>:--expt-relaxed-constexpr --expt-extended-lambda -Werror=cross-execution-space-call>)
  target_link_libraries(fast_math_cuda_test gmock_main gtest dispenso CUDA::cudart_static)
  gtest_discover_tests(fast_math_cuda_test
    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
    LABELS "fast_math")
  set_target_properties(fast_math_cuda_test PROPERTIES
    CUDA_SEPARABLE_COMPILATION OFF
    FOLDER tests/fast_math)
endif()
