if (NOT TARGET Python::Interpreter)
  message("++ Python module will not be compiled because no Python interpreter was found")
  set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python" CACHE INTERNAL "GUDHI_MISSING_MODULES")
  return()
endif()
if (NOT NUMPY_FOUND)
  message("++ Python module will not be compiled because numpy was not found")
  set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python" CACHE INTERNAL "GUDHI_MISSING_MODULES")
  return()
endif()
if(NOT NANOBIND_FOUND)
  message("++ Python module will not be compiled because Nanobind was not found")
  set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python" CACHE INTERNAL "GUDHI_MISSING_MODULES")
  return()
endif()

function(nanobind_add_module_with_targets)
    if (ARGC LESS 2)
        message (FATAL_ERROR "add_executable_with_targets requires at least 2 arguments.")
    endif()

    list(POP_FRONT ARGN EXECUTABLE_NAME EXECUTABLE_SOURCE)
    # while loop also ends if EXECUTABLE_SOURCE is empty
    while(NOT ${EXECUTABLE_SOURCE} STREQUAL "TARGETS")
      list(APPEND EXE_SOURCES "${EXECUTABLE_SOURCE}")
      list(POP_FRONT ARGN EXECUTABLE_SOURCE)
    endwhile()

    # Do not add_executable if one of the target is not here, except for TBB that is optional
    foreach(USER_TARGET IN LISTS ARGN)
        if(NOT TARGET ${USER_TARGET})
            if(NOT ${USER_TARGET} STREQUAL "TBB::tbb")
                return()
            endif()
        endif()
    endforeach()
    message(DEBUG "nanobind_add_module(${EXECUTABLE_NAME} ${EXE_SOURCES}) + target_link_libraries(${ARGN})")
    # Maintain a list of nanobind modules - Mainly for sphinx purposes to know when the documentation is "buildable"
    set(GUDHI_NANOBIND_MODULES ${GUDHI_NANOBIND_MODULES} ${EXECUTABLE_NAME} CACHE INTERNAL "GUDHI_NANOBIND_MODULES")
    # cf. https://nanobind.readthedocs.io/en/latest/api_cmake.html#high-level-interface
    nanobind_add_module(${EXECUTABLE_NAME} NB_STATIC NOMINSIZE ${EXE_SOURCES})
    if(MSVC)
      target_compile_options(${EXECUTABLE_NAME} PRIVATE "-DNOMINMAX")
    endif()
    foreach(USER_TARGET IN LISTS ARGN)
        # TARGET_NAME_IF_EXISTS is specific to TBB case (optional)
        target_link_libraries(${EXECUTABLE_NAME} PRIVATE $<TARGET_NAME_IF_EXISTS:${USER_TARGET}>)
    endforeach()
    if(MSVC)
        # cf. https://nanobind.readthedocs.io/en/latest/faq.html#importing-fails-due-to-missing-lib-nanobind-dylib-so-dll
        message(DEBUG "${EXECUTABLE_NAME} set LIBRARY_OUTPUT_DIRECTORIES to ${CMAKE_CURRENT_BINARY_DIR}")
        set_target_properties(${EXECUTABLE_NAME} PROPERTIES
                              LIBRARY_OUTPUT_DIRECTORY                ${CMAKE_CURRENT_BINARY_DIR}
                              LIBRARY_OUTPUT_DIRECTORY_RELEASE        ${CMAKE_CURRENT_BINARY_DIR}
                              LIBRARY_OUTPUT_DIRECTORY_DEBUG          ${CMAKE_CURRENT_BINARY_DIR}
                              LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_CURRENT_BINARY_DIR}
                              LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL     ${CMAKE_CURRENT_BINARY_DIR})
    endif(MSVC)
endfunction()

# THE_TEST is the python test file name (without .py extension) containing tests functions
function( add_gudhi_py_test THE_TEST )
  if(PYTEST_FOUND)
    # use ${Python_EXECUTABLE} -B, otherwise a __pycache__ directory is created in sources by python
    # use py.test no cache provider, otherwise a .cache file is created in sources by py.test
    add_test(NAME ${THE_TEST}_py_test
             WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
             COMMAND ${Python_EXECUTABLE} -B -m pytest -p no:cacheprovider ${CMAKE_CURRENT_SOURCE_DIR}/test/${THE_TEST}.py)
  endif()
endfunction( add_gudhi_py_test )

function( add_gudhi_symbolic_links GLOBBING_EXPRESSION )
  # No symbolic links if build in sources (aka. 'cmake .' in sources)
  if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
    return()
  endif()
  file(GLOB GUDHI_GLOB_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${GLOBBING_EXPRESSION})
  foreach(GUDHI_GLOB_FILENAME ${GUDHI_GLOB_FILES})
    # COPY_ON_ERROR is activated because on windows 10, user needs to be root or in dev mode to be able to make symbolic links
    # This case can be problematic, because if you modify the sources, build is still done on the copy
    file(CREATE_LINK "${CMAKE_CURRENT_SOURCE_DIR}/${GUDHI_GLOB_FILENAME}" "${CMAKE_CURRENT_BINARY_DIR}/${GUDHI_GLOB_FILENAME}" COPY_ON_ERROR SYMBOLIC)
  endforeach()
endfunction( add_gudhi_symbolic_links )

# Set gudhi.__debug_info__
# WARNING : to be done before setup.py.in configure_file
function( add_gudhi_debug_info DEBUG_INFO )
  set(GUDHI_PYTHON_DEBUG_INFO "${GUDHI_PYTHON_DEBUG_INFO}    \"${DEBUG_INFO}\\n\" \\\n" PARENT_SCOPE)
endfunction( add_gudhi_debug_info )

function(disable_python_documentation MISSING_MODULE)
  set(GUDHI_PYTHON_DOCUMENTATION_MISSING_MODULES "${GUDHI_PYTHON_DOCUMENTATION_MISSING_MODULES}'${MISSING_MODULE}', " PARENT_SCOPE)
endfunction( disable_python_documentation )

add_gudhi_debug_info("Python version ${Python_VERSION}")
add_gudhi_debug_info("Nanobind version ${NANOBIND_VERSION}")
add_gudhi_debug_info("Numpy version ${NUMPY_VERSION}")

if(PYTEST_FOUND)
  add_gudhi_debug_info("Pytest version ${PYTEST_VERSION}")
endif()
if(MATPLOTLIB_FOUND)
  add_gudhi_debug_info("Matplotlib version ${MATPLOTLIB_VERSION}")
else()
  disable_python_documentation("matplotlib")
endif()
if(SCIPY_FOUND)
  add_gudhi_debug_info("Scipy version ${SCIPY_VERSION}")
else()
  disable_python_documentation("scipy")
endif()
if(SCIKIT-LEARN_FOUND)
  add_gudhi_debug_info("Scikit-learn version ${SCIKIT-LEARN_VERSION}")
else()
  disable_python_documentation("scikit-learn")
endif()
if(POT_FOUND)
  add_gudhi_debug_info("POT version ${POT_VERSION}")
else()
  disable_python_documentation("ot")
endif()
if(HNSWLIB_FOUND)
  add_gudhi_debug_info("HNSWlib version ${HNSWLIB_VERSION}")
endif()
if(TORCH_FOUND)
  add_gudhi_debug_info("PyTorch version ${TORCH_VERSION}")
endif()
if(PYKEOPS_FOUND)
  add_gudhi_debug_info("PyKeOps version ${PYKEOPS_VERSION}")
endif()
if(EAGERPY_FOUND)
  add_gudhi_debug_info("EagerPy version ${EAGERPY_VERSION}")
endif()
if(TENSORFLOW_FOUND)
  add_gudhi_debug_info("TensorFlow version ${TENSORFLOW_VERSION}")
else()
  disable_python_documentation("tensorflow")
endif()
if(SPHINX_FOUND)
  add_gudhi_debug_info("Sphinx version ${SPHINX_VERSION}")
  # Calling sphinx-build executable (that could be found find_program) may use a different version of python and fail
  set(SPHINX_PATH "${Python_EXECUTABLE}" "-m" "sphinx.cmd.build")
else()
  disable_python_documentation("sphinx")
endif()
if(SPHINX_PARAMLINKS_FOUND)
  add_gudhi_debug_info("Sphinx-paramlinks version ${SPHINX_PARAMLINKS_VERSION}")
else()
  disable_python_documentation("sphinx-paramlinks")
endif()
if(PYDATA_SPHINX_THEME_FOUND)
  add_gudhi_debug_info("pydata_sphinx_theme version ${PYDATA_SPHINX_THEME_VERSION}")
else()
  disable_python_documentation("pydata_sphinx_theme")
endif()
if(SPHINXCONTRIB.BIBTEX_FOUND)
  add_gudhi_debug_info("sphinxcontrib-bibtex version ${SPHINXCONTRIB.BIBTEX_VERSION}")
else()
  disable_python_documentation("sphinxcontrib-bibtex")
endif()
if(SPHINX-AUTODOC-TYPEHINTS_FOUND)
  add_gudhi_debug_info("sphinx-autodoc-typehints version ${SPHINX-AUTODOC-TYPEHINTS_VERSION}")
else()
  disable_python_documentation("sphinx-autodoc-typehints")
endif()
if(NETWORKX_FOUND)
  add_gudhi_debug_info("NetworkX version ${NETWORKX_VERSION}")
endif()
if(NOT TARGET CGAL::CGAL)
  disable_python_documentation("CGAL")
endif()
if (NOT TARGET Eigen3::Eigen)
  disable_python_documentation("Eigen3")
endif()

if (TARGET Eigen3::Eigen)
  add_gudhi_debug_info("Eigen3 version ${EIGEN3_VERSION_STRING}")
endif (TARGET Eigen3::Eigen)

add_gudhi_debug_info("Boost version ${Boost_VERSION}")
if(TARGET CGAL::CGAL)
  add_gudhi_debug_info("CGAL version ${CGAL_VERSION}")
  if(GMP_FOUND)
    add_gudhi_debug_info("GMP_LIBRARIES = ${GMP_LIBRARIES}")
    if(GMPXX_FOUND)
      add_gudhi_debug_info("GMPXX_LIBRARIES = ${GMPXX_LIBRARIES}")
    endif(GMPXX_FOUND)
  endif(GMP_FOUND)
  if(MPFR_FOUND)
    add_gudhi_debug_info("MPFR_LIBRARIES = ${MPFR_LIBRARIES}")
  endif(MPFR_FOUND)
endif(TARGET CGAL::CGAL)

if (TBB_FOUND AND WITH_GUDHI_USE_TBB)
  add_gudhi_debug_info("TBB version ${TBB_VERSION} found and used")
  add_gudhi_debug_info("TBB_LIBRARY = ${TBB_LIBRARY}")
  add_gudhi_debug_info("TBB_MALLOC_LIBRARY = ${TBB_MALLOC_LIBRARY}")
endif()

# configure all python files from sources to binary dir forces CMake to copy them when modified
file(GLOB_RECURSE GUDHI_PYTHON_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "gudhi/*.py")
foreach(GUDHI_PYTHON_FILE ${GUDHI_PYTHON_FILES})
  if(NOT GUDHI_PYTHON_FILE MATCHES "gudhi/__init__.py")
    configure_file("${GUDHI_PYTHON_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${GUDHI_PYTHON_FILE}" COPYONLY)
    message(DEBUG "Python configure_file ${GUDHI_PYTHON_FILE}")
  endif()
endforeach()

# Generate gudhi/__init__.py
configure_file("gudhi/__init__.py.in" "${CMAKE_CURRENT_BINARY_DIR}/gudhi/__init__.py" @ONLY)

# Detect the installed nanobind package and import it into CMake
execute_process(
  COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
  OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE nanobind_ROOT)
find_package(nanobind CONFIG REQUIRED)

# Add src/python/include directory
include_directories(BEFORE include)
add_subdirectory("gudhi")
add_subdirectory("gudhi/clustering")
add_subdirectory("gudhi/datasets/generators")

# Path separator management for windows
if (WIN32)
  set(GUDHI_PYTHON_PATH_ENV "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR};$ENV{PYTHONPATH}")
else(WIN32)
  set(GUDHI_PYTHON_PATH_ENV "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}:$ENV{PYTHONPATH}")
endif(WIN32)

# Documentation generation is available through sphinx - requires all modules
# Make it first as sphinx test is by far the longest test which is nice when testing in parallel
if(GUDHI_PYTHON_DOCUMENTATION_MISSING_MODULES)
  # set GUDHI_MISSING_MODULES once and not for each GUDHI_PYTHON_DOCUMENTATION_MISSING_MODULES
  set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES")
  message("++ Python documentation will not be compilable because of the following missing modules: ${GUDHI_PYTHON_DOCUMENTATION_MISSING_MODULES}")
else()
  set (GUDHI_SPHINX_MESSAGE "Generating API documentation with Sphinx in ${CMAKE_CURRENT_BINARY_DIR}/sphinx/")
  # User warning - Sphinx is a static pages generator, and configured to work fine with user_version
  # Images and biblio warnings because not found on developer version
  if (GUDHI_PYTHON_PATH STREQUAL "src/python")
    set (GUDHI_SPHINX_MESSAGE "${GUDHI_SPHINX_MESSAGE} \n WARNING : Sphinx is configured for user version, you run it on developer version. Images and biblio will miss")
  endif()
  # sphinx target requires python modules to be built, because conf.py reads gudhi version from it
  add_custom_target(sphinx
      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc
      COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
      ${SPHINX_PATH} -b html ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/sphinx
      DEPENDS "${GUDHI_NANOBIND_MODULES}"
      COMMENT "${GUDHI_SPHINX_MESSAGE}" VERBATIM)
  add_test(NAME sphinx_py_test
           WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
           COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
           ${SPHINX_PATH} -b doctest ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/doctest)
  # Set missing or not modules
  set(GUDHI_MODULES ${GUDHI_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MODULES")
endif()

# Cubical
add_test(NAME periodic_cubical_complex_barcode_persistence_from_perseus_file_example_py_test
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
         COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
         ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py"
         --no-barcode -f ${CMAKE_SOURCE_DIR}/data/bitmap/CubicalTwoSphere.txt)

add_test(NAME random_cubical_complex_persistence_example_py_test
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
         COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
         ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/random_cubical_complex_persistence_example.py"
         10 10 10)

add_gudhi_py_test(test_cubical_complex)
add_gudhi_py_test(test_cubical_persistence_low_dim)

# Datasets are fetched for these tests
if(SCIKIT-LEARN_FOUND AND WITH_GUDHI_REMOTE_TEST)
  add_gudhi_py_test(test_sklearn_cubical_persistence)

  add_test(NAME cubical_complex_sklearn_itf_py_test
           WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
           COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
           ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/cubical_complex_sklearn_itf.py")
endif()

# Test examples
if (TARGET CGAL::CGAL AND TARGET Eigen3::Eigen)
  # Bottleneck and Alpha
  add_test(NAME alpha_rips_persistence_bottleneck_distance_py_test
           WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
           COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
           ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_rips_persistence_bottleneck_distance.py"
           -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -t 0.15 -d 3)
endif (TARGET CGAL::CGAL AND TARGET Eigen3::Eigen)
if (TARGET CGAL::CGAL AND TARGET Eigen3::Eigen)
  # Tangential
  add_test(NAME tangential_complex_plain_homology_from_off_file_example_py_test
           WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
           COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
           ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/tangential_complex_plain_homology_from_off_file_example.py"
           --no-diagram -i 2 -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off)

  add_gudhi_py_test(test_tangential_complex)

  # Witness complex
  add_test(NAME euclidean_strong_witness_complex_diagram_persistence_from_off_file_example_py_test
           WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
           COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
           ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py"
           --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -a 1.0 -n 20 -d 2)

  add_test(NAME euclidean_witness_complex_diagram_persistence_from_off_file_example_py_test
           WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
           COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
           ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py"
           --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -a 1.0 -n 20 -d 2)

endif (TARGET CGAL::CGAL AND TARGET Eigen3::Eigen)
if (TARGET CGAL::CGAL)
  # Bottleneck
  add_test(NAME bottleneck_basic_example_py_test
           WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
           COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
           ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/bottleneck_basic_example.py")

  add_gudhi_py_test(test_bottleneck_distance)
endif (TARGET CGAL::CGAL)

# Cover complex
file(COPY ${CMAKE_SOURCE_DIR}/data/points/human.off DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/)
file(COPY ${CMAKE_SOURCE_DIR}/data/points/COIL_database/lucky_cat.off DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/)
file(COPY ${CMAKE_SOURCE_DIR}/data/points/COIL_database/lucky_cat_PCA1 DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/)
add_test(NAME cover_complex_nerve_example_py_test
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
         COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
         ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/nerve_of_a_covering.py"
         -f human.off -c 2 -r 10 -g 0.3)

add_test(NAME cover_complex_coordinate_gic_example_py_test
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
         COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
         ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/coordinate_graph_induced_complex.py"
         -f human.off -c 0 -v)

add_test(NAME cover_complex_functional_gic_example_py_test
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
         COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
         ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/functional_graph_induced_complex.py"
         -o lucky_cat.off
         -f lucky_cat_PCA1 -v)

add_test(NAME cover_complex_voronoi_gic_example_py_test
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
         COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
         ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/voronoi_graph_induced_complex.py"
         -f human.off -n 700 -v)

if (TARGET CGAL::CGAL AND TARGET Eigen3::Eigen)
  # Alpha
  add_test(NAME alpha_complex_from_points_example_py_test
           WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
           COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
           ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_complex_from_points_example.py")
  add_test(NAME alpha_complex_from_generated_points_on_sphere_example_py_test
           WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
           COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
           ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_complex_from_generated_points_on_sphere_example.py")
  add_test(NAME alpha_complex_diagram_persistence_from_off_file_example_py_test
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
         COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
         ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_complex_diagram_persistence_from_off_file_example.py"
         --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off)
  add_gudhi_py_test(test_alpha_complex)
  add_gudhi_py_test(test_delaunay_complex)

  # Cech scikit-learn like interface
  if(SCIKIT-LEARN_FOUND)
    add_test(NAME cech_complex_sklearn_itf_py_test
             WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
             COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
             ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/cech_complex_sklearn_itf.py" "--no-plot")

    add_gudhi_py_test(test_sklearn_cech_persistence)
  endif()
endif (TARGET CGAL::CGAL AND TARGET Eigen3::Eigen)

if (TARGET CGAL::CGAL AND TARGET Eigen3::Eigen)
  # Euclidean witness
  add_gudhi_py_test(test_euclidean_witness_complex)

  # Datasets generators
  add_gudhi_py_test(test_datasets_generators) # TODO separate full python datasets generators in another test file independent from CGAL ?

endif (TARGET CGAL::CGAL AND TARGET Eigen3::Eigen)

# Rips
add_test(NAME rips_complex_diagram_persistence_from_distance_matrix_file_example_py_test
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
         COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
         ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/rips_complex_diagram_persistence_from_distance_matrix_file_example.py"
         --no-diagram -f ${CMAKE_SOURCE_DIR}/data/distance_matrix/lower_triangular_distance_matrix.csv -s , -e 12.0 -d 3)

add_test(NAME rips_complex_diagram_persistence_from_off_file_example_py_test
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
         COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
         ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/rips_complex_diagram_persistence_from_off_file_example.py
         --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off  -e 0.25 -d 3)

add_test(NAME rips_complex_from_points_example_py_test
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
         COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
         ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/rips_complex_from_points_example.py)

add_gudhi_py_test(test_rips_complex)

if(SCIKIT-LEARN_FOUND)
  # test_sklearn_rips_persistence is using gudhi.datasets.generators
  if (TARGET CGAL::CGAL AND TARGET Eigen3::Eigen)
    add_gudhi_py_test(test_sklearn_rips_persistence)
  endif()

  # Datasets are fetched for these tests
  if(WITH_GUDHI_REMOTE_TEST)
    add_test(NAME rips_complex_sklearn_itf_py_test
             WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
             COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
             ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/rips_complex_sklearn_itf.py" "--no-plot")
  endif()
endif()

# Simplex tree
add_test(NAME simplex_tree_example_py_test
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
         COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
         ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/simplex_tree_example.py)

add_gudhi_py_test(test_simplex_tree)
add_gudhi_py_test(test_simplex_generators)
add_gudhi_py_test(test_simplex_tree_serialization)

# Edge collapse
if(SCIPY_FOUND)
  add_gudhi_py_test(test_collapse_edges)
endif()

# Subsampling
if (TARGET CGAL::CGAL AND TARGET Eigen3::Eigen)
  add_gudhi_py_test(test_subsampling)
endif()

# Witness
add_test(NAME witness_complex_from_nearest_landmark_table_py_test
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
         COMMAND ${CMAKE_COMMAND} -E env "${GUDHI_PYTHON_PATH_ENV}"
         ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/witness_complex_from_nearest_landmark_table.py)

add_gudhi_py_test(test_witness_complex)

# Reader utils
add_gudhi_py_test(test_reader_utils)
add_gudhi_py_test(test_off)

# Wasserstein
if(POT_FOUND)
  # EagerPy dependency because of enable_autodiff=True
  if(EAGERPY_FOUND)
    add_gudhi_py_test(test_wasserstein_distance)
  endif()
  add_gudhi_py_test(test_wasserstein_barycenter)
  if(TORCH_FOUND AND TENSORFLOW_FOUND AND EAGERPY_FOUND)
    add_gudhi_py_test(test_wasserstein_with_tensors)
  endif()
endif()

# Representations
if(SCIKIT-LEARN_FOUND AND MATPLOTLIB_FOUND AND POT_FOUND AND TARGET CGAL::CGAL)
  add_gudhi_py_test(test_representations)
endif()

# Differentiation
if(TENSORFLOW_FOUND)
  add_gudhi_py_test(test_diff)
endif()

# Perslay
if(TENSORFLOW_FOUND AND SCIKIT-LEARN_FOUND)
  add_gudhi_py_test(test_perslay)
endif()

# Betti curves
if(SCIKIT-LEARN_FOUND AND SCIPY_FOUND)
  add_gudhi_py_test(test_betti_curve_representations)
endif()

# Representations preprocessing
if(SCIKIT-LEARN_FOUND)
  add_gudhi_py_test(test_representations_preprocessing)
endif()

# Time Delay
add_gudhi_py_test(test_time_delay)

# DTM
if(SCIPY_FOUND AND SCIKIT-LEARN_FOUND AND TORCH_FOUND AND HNSWLIB_FOUND AND PYKEOPS_FOUND AND EAGERPY_FOUND)
  add_gudhi_py_test(test_knn)
  add_gudhi_py_test(test_dtm)
endif()

# Tomato
if(SCIPY_FOUND AND SCIKIT-LEARN_FOUND)
  add_gudhi_py_test(test_tomato)
endif()

# Weighted Rips
if(SCIPY_FOUND)
  add_gudhi_py_test(test_weighted_rips_complex)
endif()

# DTM Rips
if(SCIPY_FOUND)
  add_gudhi_py_test(test_dtm_rips_complex)
endif()

# Cover complex
if(SCIKIT-LEARN_FOUND)
  add_gudhi_py_test(test_cover_complex)
endif()

# Fetch remote datasets
if(WITH_GUDHI_REMOTE_TEST)
  add_gudhi_py_test(test_remote_datasets)
endif()

# persistence graphical tools
if(MATPLOTLIB_FOUND)
  add_gudhi_py_test(test_persistence_graphical_tools)
endif()

# Vineyard
add_gudhi_py_test(test_vineyard)

# Set missing or not modules
set(GUDHI_MODULES ${GUDHI_MODULES} "python" CACHE INTERNAL "GUDHI_MODULES")

install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/gudhi" DESTINATION ".")
