# Standalone TieredQ codec and exhaustive in-memory index.
cmake_minimum_required(VERSION 3.18)
project(TieredQ VERSION 0.1.1 LANGUAGES CXX)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set(TIEREDQ_STANDALONE OFF)
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
  set(TIEREDQ_STANDALONE ON)
endif()
option(TIEREDQ_TESTS "Build TieredQ tests" ${TIEREDQ_STANDALONE})
option(TIEREDQ_EXAMPLES "Build standalone example/benchmark" ${TIEREDQ_STANDALONE})
if(APPLE)
  execute_process(COMMAND brew --prefix libomp OUTPUT_VARIABLE LIBOMP_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
  if(LIBOMP_PREFIX)
    list(APPEND CMAKE_PREFIX_PATH "${LIBOMP_PREFIX}")
  endif()
endif()
find_package(OpenMP REQUIRED)
# Training's covariance (syrk) and eigendecomposition (syevd) run about 10x faster on an optimized BLAS/LAPACK than on
# the reference one, so OpenBLAS, then sequential MKL, are preferred unless BLA_VENDOR names one.
if(NOT DEFINED BLA_VENDOR)
  foreach(vendor OpenBLAS Intel10_64lp_seq)
    set(BLA_VENDOR ${vendor})
    find_package(LAPACK QUIET)
    if(LAPACK_FOUND)
      break()
    endif()
  endforeach()
  if(NOT LAPACK_FOUND)
    unset(BLA_VENDOR)
  endif()
endif()
find_package(LAPACK REQUIRED)
set(TIEREDQ_BLA_VENDOR "${BLA_VENDOR}")
message(STATUS "TieredQ LAPACK: ${BLA_VENDOR} ${LAPACK_LIBRARIES}")
add_library(dv_tieredq STATIC src/tieredq.cpp src/tieredq_index.cpp src/tieredq_index_io.cpp src/tieredq_ivf.cpp src/tieredq_refinement.cpp src/tieredq_ivf_rows.cpp src/tieredq_nested.cpp src/tieredq_nested_flat.cpp src/tieredq_blocks.cpp)
add_library(TieredQ::TieredQ ALIAS dv_tieredq)
set_target_properties(dv_tieredq PROPERTIES POSITION_INDEPENDENT_CODE ON EXPORT_NAME TieredQ)
target_compile_features(dv_tieredq PUBLIC cxx_std_20)
target_include_directories(dv_tieredq PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
target_compile_options(dv_tieredq PRIVATE -O3 -Wall -Wextra -Wno-misleading-indentation -ffp-contract=off)
target_link_libraries(dv_tieredq PUBLIC LAPACK::LAPACK OpenMP::OpenMP_CXX ${CMAKE_DL_LIBS})
# Optional io_uring for concurrent file-tier reads (TieredQFileRows queue_depth > 1); without it reads are serial.
option(TIEREDQ_URING "Use liburing for concurrent tier reads when found" ON)
if(TIEREDQ_URING)
  find_package(PkgConfig QUIET)
  if(PkgConfig_FOUND)
    pkg_check_modules(LIBURING QUIET IMPORTED_TARGET liburing)
  endif()
  if(LIBURING_FOUND)
    target_link_libraries(dv_tieredq PUBLIC PkgConfig::LIBURING)
    target_compile_definitions(dv_tieredq PRIVATE DV_TIEREDQ_URING)
  endif()
endif()
message(STATUS "TieredQ io_uring tier reads: ${LIBURING_FOUND}")
if(TIEREDQ_TESTS)
  enable_testing()
  add_executable(test_tieredq tests/test_tieredq.cpp tests/test_tieredq_model.cpp tests/test_tieredq_encoder.cpp tests/test_tieredq_neon.cpp tests/test_tieredq_lookup.cpp tests/test_tieredq_projection.cpp tests/test_tieredq_search.cpp tests/test_tieredq_fast_mode.cpp tests/test_tieredq_range_checkpoint.cpp tests/test_tieredq_additive.cpp tests/test_tieredq_metric.cpp tests/test_tieredq_ivf.cpp tests/test_tieredq_refinement.cpp tests/test_tieredq_nested.cpp tests/test_tieredq_nested_flat.cpp tests/test_tieredq_blocks.cpp tests/test_tieredq_fast_fine.cpp)
  target_include_directories(test_tieredq PRIVATE src)
  target_compile_options(test_tieredq PRIVATE -ffp-contract=off)
  target_link_libraries(test_tieredq PRIVATE TieredQ::TieredQ)
  add_test(NAME tieredq COMMAND test_tieredq)
  add_executable(test_training_rng tests/test_training_rng.cpp)
  target_compile_features(test_training_rng PRIVATE cxx_std_20)
  target_include_directories(test_training_rng PRIVATE src)
  add_test(NAME training_rng COMMAND test_training_rng)
endif()
if(TIEREDQ_EXAMPLES)
  add_executable(tieredq_scan_benchmark examples/scan_benchmark.cpp)
  target_link_libraries(tieredq_scan_benchmark PRIVATE TieredQ::TieredQ)
  target_compile_options(tieredq_scan_benchmark PRIVATE -O3 -ffp-contract=off)
  add_executable(tieredq_fast_scan_benchmark examples/fast_scan_benchmark.cpp)
  target_link_libraries(tieredq_fast_scan_benchmark PRIVATE TieredQ::TieredQ)
  add_executable(tieredq_example examples/search.cpp)
  target_link_libraries(tieredq_example PRIVATE TieredQ::TieredQ)
endif()
install(TARGETS dv_tieredq EXPORT TieredQTargets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES LICENSE NOTICE DESTINATION ${CMAKE_INSTALL_DATADIR}/TieredQ)
install(EXPORT TieredQTargets NAMESPACE TieredQ:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/TieredQ)
configure_package_config_file(cmake/TieredQConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/TieredQConfig.cmake" INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/TieredQ)
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/TieredQConfigVersion.cmake" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/TieredQConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/TieredQConfigVersion.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/TieredQ)

if(SKBUILD AND TIEREDQ_STANDALONE)
  include(cmake/Python.cmake)
endif()
