find_path(GMPFRXX_MKII_TEST_QD_INCLUDE_DIR NAMES qd/dd_real.h qd/qd_real.h)
find_path(GMPFRXX_MKII_TEST_QD_DS_INCLUDE_DIR NAMES qd/ds_real.h)
find_path(GMPFRXX_MKII_TEST_QD_TS_INCLUDE_DIR NAMES qd/ts_real.h)
find_path(GMPFRXX_MKII_TEST_QD_QS_INCLUDE_DIR NAMES qd/qs_real.h)
find_path(GMPFRXX_MKII_TEST_QD_TD_INCLUDE_DIR NAMES qd/td_real.h)
find_path(GMPFRXX_MKII_TEST_QD_GENERATED_INCLUDE_DIR NAMES qd/qd_config.h)
find_library(GMPFRXX_MKII_TEST_QD_LIBRARY NAMES qd)
set(GMPFRXX_MKII_TEST_QD_FOUND OFF)
if(GMPFRXX_MKII_TEST_QD_INCLUDE_DIR AND GMPFRXX_MKII_TEST_QD_LIBRARY)
    set(GMPFRXX_MKII_TEST_QD_FOUND ON)
endif()
set(GMPFRXX_MKII_TEST_QD_SINGLE_FOUND OFF)
if(GMPFRXX_MKII_TEST_QD_DS_INCLUDE_DIR AND
   GMPFRXX_MKII_TEST_QD_TS_INCLUDE_DIR AND
   GMPFRXX_MKII_TEST_QD_QS_INCLUDE_DIR AND
   GMPFRXX_MKII_TEST_QD_TD_INCLUDE_DIR AND
   GMPFRXX_MKII_TEST_QD_LIBRARY)
    set(GMPFRXX_MKII_TEST_QD_SINGLE_FOUND ON)
endif()

function(add_phase0_test name target)
    add_executable(${name} ${name}.cpp)
    target_link_libraries(${name} PRIVATE ${target})
    add_test(NAME ${name} COMMAND ${name})
endfunction()

function(add_qd_phase0_test name target)
    if(GMPFRXX_MKII_TEST_QD_FOUND)
        add_phase0_test(${name} ${target})
        target_include_directories(${name} PRIVATE
            "${GMPFRXX_MKII_TEST_QD_INCLUDE_DIR}"
            "${GMPFRXX_MKII_TEST_QD_GENERATED_INCLUDE_DIR}")
        target_link_libraries(${name} PRIVATE "${GMPFRXX_MKII_TEST_QD_LIBRARY}")
    endif()
endfunction()

function(add_qd_single_phase0_test name target)
    if(GMPFRXX_MKII_TEST_QD_SINGLE_FOUND)
        add_phase0_test(${name} ${target})
        target_include_directories(${name} PRIVATE
            "${GMPFRXX_MKII_TEST_QD_INCLUDE_DIR}"
            "${GMPFRXX_MKII_TEST_QD_DS_INCLUDE_DIR}"
            "${GMPFRXX_MKII_TEST_QD_TS_INCLUDE_DIR}"
            "${GMPFRXX_MKII_TEST_QD_QS_INCLUDE_DIR}"
            "${GMPFRXX_MKII_TEST_QD_TD_INCLUDE_DIR}"
            "${GMPFRXX_MKII_TEST_QD_GENERATED_INCLUDE_DIR}")
        target_link_libraries(${name} PRIVATE "${GMPFRXX_MKII_TEST_QD_LIBRARY}")
    endif()
endfunction()

function(enable_qd_for_phase0_test name)
    if(GMPFRXX_MKII_TEST_QD_FOUND)
        target_compile_definitions(${name} PRIVATE GMPFRXX_MKII_TEST_HAVE_QD=1)
        target_include_directories(${name} PRIVATE
            "${GMPFRXX_MKII_TEST_QD_INCLUDE_DIR}"
            "${GMPFRXX_MKII_TEST_QD_GENERATED_INCLUDE_DIR}")
        target_link_libraries(${name} PRIVATE "${GMPFRXX_MKII_TEST_QD_LIBRARY}")
    endif()
endfunction()

function(add_gmp_component_tests)
    add_qd_single_phase0_test(test_qd_single_real_no_mpfr gmpxx_mkII)
    add_phase0_test(test_gmp_header_smoke gmpxx_mkII)
    add_phase0_test(test_integer_model_diagnostics gmpxx_mkII)
    add_phase0_test(test_gmpxx_mkII gmpxx_mkII)
    add_phase0_test(test_comparisons gmpxx_mkII)
    add_phase0_test(test_compound_assign gmpxx_mkII)
    add_phase0_test(test_exception_support gmpxx_mkII)
    add_phase0_test(test_random gmpxx_mkII)
    add_phase0_test(test_type_conversions gmpxx_mkII)
    add_phase0_test(test_user_defined_literals gmpxx_mkII)
    add_phase0_test(test_et_contract_mpf gmpxx_mkII)
    add_phase0_test(test_mpf_basic gmpxx_mkII)
    add_phase0_test(test_mpf_precision_policy gmpxx_mkII)
    add_phase0_test(test_mpf_default_precision_env gmpxx_mkII)
    set_tests_properties(test_mpf_default_precision_env PROPERTIES
        ENVIRONMENT "GMPXX_DEFAULT_MPF_PRECISION_BITS=1024"
    )
    add_executable(test_mpf_default_precision_invalid_env test_mpf_default_precision_invalid_env.cpp)
    target_link_libraries(test_mpf_default_precision_invalid_env PRIVATE gmpxx_mkII)
    add_test(NAME test_mpf_default_precision_invalid_env COMMAND test_mpf_default_precision_invalid_env)
    set_tests_properties(test_mpf_default_precision_invalid_env PROPERTIES
        ENVIRONMENT "GMPXX_DEFAULT_MPF_PRECISION_BITS=abc"
    )
    add_test(NAME test_mpf_default_precision_overflow_env COMMAND test_mpf_default_precision_invalid_env)
    set_tests_properties(test_mpf_default_precision_overflow_env PROPERTIES
        ENVIRONMENT "GMPXX_DEFAULT_MPF_PRECISION_BITS=18446744073709551616"
    )
    add_phase0_test(test_mpf_external_provider gmpxx_mkII_default_context_provider)
    add_phase0_test(test_mpf_aliasing gmpxx_mkII)
    add_phase0_test(test_mpf_string_io gmpxx_mkII)
    add_phase0_test(test_mpf_alloc_count gmpxx_mkII)
    set_tests_properties(test_mpf_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
    add_phase0_test(test_mpf_scalar_alloc_count gmpxx_mkII)
    set_tests_properties(test_mpf_scalar_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
    add_phase0_test(test_mpf_fixed_precision_materialization gmpxx_mkII)
    target_compile_definitions(test_mpf_fixed_precision_materialization PRIVATE
        GMPFRXX_MKII_FAST_FIXED_PREC
    )
    add_phase0_test(test_mpf_fixed_precision_tls_scratch gmpxx_mkII)
    target_compile_definitions(test_mpf_fixed_precision_tls_scratch PRIVATE
        GMPFRXX_MKII_FAST_FIXED_PREC
    )
    set_tests_properties(test_mpf_fixed_precision_tls_scratch PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
    add_phase0_test(test_mpf_long_width_dispatch gmpxx_mkII)
    add_phase0_test(test_mpf_numeric_equivalence gmpxx_mkII)
    add_phase0_test(test_mpf_power_of_two_fusion gmpxx_mkII)
    add_phase0_test(test_mpf_decomposition_functions gmpxx_mkII)
    add_phase0_test(test_mpf_thread_safety gmpxx_mkII)
    add_phase0_test(test_mpf_pi gmpxx_mkII)
    add_phase0_test(test_mpf_transcendent_functions gmpxx_mkII)
    add_phase0_test(test_mpf_math_functions gmpxx_mkII)
    add_phase0_test(test_mpf_extended_transcendent_functions gmpxx_mkII)
    add_phase0_test(test_et_contract_mpfc gmpxx_mkII)
    add_phase0_test(test_mpfc_basic gmpxx_mkII)
    add_phase0_test(test_mpfc_division_smoke gmpxx_mkII)
    add_phase0_test(test_mpfc_precision_policy gmpxx_mkII)
    set_tests_properties(test_mpfc_precision_policy PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
    add_phase0_test(test_mpfc_header_boundary gmpxx_mkII)
    add_phase0_test(test_mpfc_math gmpxx_mkII)
    add_phase0_test(test_mpfc_io gmpxx_mkII)
    add_phase0_test(test_et_contract_zq_mpf gmpxx_mkII)
    add_phase0_test(test_mixed_zq_mpf_promotion gmpxx_mkII)
    add_phase0_test(test_mpz_basic gmpxx_mkII)
    add_phase0_test(test_mpz_arithmetic gmpxx_mkII)
    add_phase0_test(test_mpq_basic gmpxx_mkII)
    add_phase0_test(test_mpq_arithmetic gmpxx_mkII)
    add_phase0_test(test_mpq_canonicalization gmpxx_mkII)
    add_phase0_test(test_zq_string_io gmpxx_mkII)
endfunction()

function(add_mpfr_component_tests)
    add_qd_phase0_test(test_dd_real_adapter mpfrxx_mkII)
    add_qd_phase0_test(test_qd_real_adapter mpfrxx_mkII)
    add_qd_single_phase0_test(test_single_real_adapters mpfrxx_mkII)
    add_phase0_test(test_mpfr_header_smoke mpfrxx_mkII)
    add_phase0_test(test_namespace_aliases mpfrxx_mkII)
    add_phase0_test(test_et_contract_mpfr mpfrxx_mkII)
    add_phase0_test(test_mpfr_expression_eval mpfrxx_mkII)
    add_phase0_test(test_mpfr_precision_policy mpfrxx_mkII)
    add_phase0_test(test_mpfr_aliasing mpfrxx_mkII)
    add_phase0_test(test_mpfr_random mpfrxx_mkII)
    add_phase0_test(test_et_contract_mpfr_scalar mpfrxx_mkII)
    add_phase0_test(test_mpfr_scalar_eval mpfrxx_mkII)
    add_phase0_test(test_mpfr_long_width_dispatch mpfrxx_mkII)
    add_phase0_test(test_mpfr_alloc_count mpfrxx_mkII)
    set_tests_properties(test_mpfr_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
    add_phase0_test(test_mpfr_fixed_precision_materialization mpfrxx_mkII)
    target_compile_definitions(test_mpfr_fixed_precision_materialization PRIVATE
        GMPFRXX_MKII_FAST_FIXED_PREC
        GMPFRXX_MKII_ENABLE_FMA
    )
    add_phase0_test(test_mpfr_fixed_precision_fma_alloc_count mpfrxx_mkII)
    target_compile_definitions(test_mpfr_fixed_precision_fma_alloc_count PRIVATE
        GMPFRXX_MKII_FAST_FIXED_PREC
        GMPFRXX_MKII_ENABLE_FMA
    )
    set_tests_properties(test_mpfr_fixed_precision_fma_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
    add_phase0_test(test_mpfr_fixed_precision_tls_scratch mpfrxx_mkII)
    target_compile_definitions(test_mpfr_fixed_precision_tls_scratch PRIVATE
        GMPFRXX_MKII_FAST_FIXED_PREC
    )
    set_tests_properties(test_mpfr_fixed_precision_tls_scratch PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
    add_phase0_test(test_mpfr_type_conversions mpfrxx_mkII)
    add_phase0_test(test_mpfr_comparisons mpfrxx_mkII)
    add_phase0_test(test_mpfr_compound_assign mpfrxx_mkII)
    target_compile_definitions(test_mpfr_compound_assign PRIVATE
        GMPFRXX_MKII_ENABLE_FMA
    )
    add_phase0_test(test_mpfr_rounding_context mpfrxx_mkII)
    add_executable(test_mpfr_rounding_context_fixed_precision test_mpfr_rounding_context.cpp)
    target_link_libraries(test_mpfr_rounding_context_fixed_precision PRIVATE mpfrxx_mkII)
    target_compile_definitions(test_mpfr_rounding_context_fixed_precision PRIVATE
        GMPFRXX_MKII_FAST_FIXED_PREC
    )
    add_test(
        NAME test_mpfr_rounding_context_fixed_precision
        COMMAND test_mpfr_rounding_context_fixed_precision
    )
    add_phase0_test(test_mpfr_rounding_scope mpfrxx_mkII)
    add_executable(test_mpfr_rounding_scope_stable test_mpfr_rounding_scope.cpp)
    target_link_libraries(test_mpfr_rounding_scope_stable PRIVATE mpfrxx_mkII)
    target_compile_definitions(test_mpfr_rounding_scope_stable PRIVATE
        GMPFRXX_MKII_FAST_STABLE_RND
    )
    add_test(NAME test_mpfr_rounding_scope_stable COMMAND test_mpfr_rounding_scope_stable)
    add_phase0_test(test_mpfr_defaults mpfrxx_mkII)
    add_phase0_test(test_mpfr_initialization_sentinel mpfrxx_mkII)
    add_phase0_test(test_mpfr_environment mpfrxx_mkII)
    add_executable(test_mpfr_environment_first_use test_mpfr_environment_first_use.cpp)
    target_link_libraries(test_mpfr_environment_first_use PRIVATE mpfrxx_mkII)
    add_test(NAME test_mpfr_environment_first_use_with_precision COMMAND test_mpfr_environment_first_use with_precision)
    add_test(NAME test_mpfr_environment_first_use_explicit_precision_constructor COMMAND test_mpfr_environment_first_use explicit_precision_constructor)
    add_test(NAME test_mpfr_environment_first_use_current_eval_context COMMAND test_mpfr_environment_first_use current_eval_context)
    add_test(NAME test_mpfr_environment_first_use_initialize_thread_defaults COMMAND test_mpfr_environment_first_use initialize_thread_defaults)
    add_test(NAME test_mpfr_environment_first_use_raw_rounding_preserved COMMAND test_mpfr_environment_first_use raw_rounding_preserved)
    add_executable(test_mpfr_environment_first_use_stable test_mpfr_environment_first_use.cpp)
    target_link_libraries(test_mpfr_environment_first_use_stable PRIVATE mpfrxx_mkII)
    target_compile_definitions(test_mpfr_environment_first_use_stable PRIVATE
        GMPFRXX_MKII_FAST_STABLE_RND
    )
    add_test(NAME test_mpfr_environment_first_use_stable_with_precision COMMAND test_mpfr_environment_first_use_stable with_precision)
    add_test(NAME test_mpfr_environment_first_use_stable_explicit_precision_constructor COMMAND test_mpfr_environment_first_use_stable explicit_precision_constructor)
    add_test(NAME test_mpfr_environment_first_use_stable_current_eval_context COMMAND test_mpfr_environment_first_use_stable current_eval_context)
    add_test(NAME test_mpfr_environment_first_use_stable_initialize_thread_defaults COMMAND test_mpfr_environment_first_use_stable initialize_thread_defaults)
    add_test(NAME test_mpfr_environment_first_use_stable_raw_rounding_preserved COMMAND test_mpfr_environment_first_use_stable raw_rounding_preserved)
    add_phase0_test(test_mpfr_environment_invalid mpfrxx_mkII)
    add_phase0_test(test_mpfr_string_io mpfrxx_mkII)
    add_phase0_test(test_mpfr_math mpfrxx_mkII)
    add_phase0_test(test_mpfr_transcendent_functions mpfrxx_mkII)
    add_phase0_test(test_et_contract_zq_mpfr mpfrxx_mkII)
    add_phase0_test(test_mpz_addmul_fusion mpfrxx_mkII)
    add_phase0_test(test_mpz_addmul_alloc_count mpfrxx_mkII)
    set_tests_properties(test_mpz_addmul_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
    add_phase0_test(test_mpz_mpq_alloc_count mpfrxx_mkII)
    set_tests_properties(test_mpz_mpq_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
    add_phase0_test(test_mixed_zq_mpfr_promotion mpfrxx_mkII)
    add_phase0_test(test_mixed_zq_mpfr_exact_subexpression mpfrxx_mkII)
    add_phase0_test(test_mpfr_numeric_equivalence mpfrxx_mkII)
    add_phase0_test(test_mpfr_power_of_two_fusion mpfrxx_mkII)
    add_phase0_test(test_mpfr_decomposition_functions mpfrxx_mkII)
    add_phase0_test(test_mpfr_scalar_alloc_count mpfrxx_mkII)
    set_tests_properties(test_mpfr_scalar_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
    add_phase0_test(test_mpfr_thread_safety mpfrxx_mkII)
endfunction()

if(NOT GMPFRXX_MKII_ENABLE_MPC)
    add_gmp_component_tests()
    if(GMPFRXX_MKII_ENABLE_MPFR)
        add_mpfr_component_tests()
    endif()
    return()
endif()

add_qd_single_phase0_test(test_qd_single_real_no_mpfr gmpxx_mkII)
add_phase0_test(test_gmp_header_smoke gmpxx_mkII)
add_phase0_test(test_mpfr_header_smoke mpfrxx_mkII)
add_phase0_test(test_mpc_header_smoke mpcxx_mkII)
add_qd_phase0_test(test_dd_real_adapter mpfrxx_mkII)
add_qd_phase0_test(test_qd_real_adapter mpfrxx_mkII)
add_qd_single_phase0_test(test_single_real_adapters mpfrxx_mkII)
add_phase0_test(test_mpf_binary_real_adapters mpfrxx_mkII)
add_phase0_test(test_complex_compare_adapters gmpfrxx_mkII)
	enable_qd_for_phase0_test(test_complex_compare_adapters)
add_phase0_test(test_mplapack_default_512 gmpfrxx_mkII)
add_phase0_test(test_mplapack_wrapper_compat gmpfrxx_mkII)
add_phase0_test(test_aggregator_header_smoke gmpfrxx_mkII)
add_phase0_test(test_namespace_aliases mpfrxx_mkII)
add_phase0_test(test_header_boundaries gmpfrxx_mkII)
target_compile_definitions(test_header_boundaries PRIVATE
    GMPFRXX_MKII_SOURCE_DIR="${CMAKE_SOURCE_DIR}"
)
add_phase0_test(test_library_tls_detection gmpfrxx_mkII)
add_phase0_test(test_version_info gmpfrxx_mkII)
add_phase0_test(test_integer_model_diagnostics gmpxx_mkII)
add_phase0_test(test_gmpxx_mkII gmpxx_mkII)
add_phase0_test(test_mpfrxx_mkII mpcxx_mkII)
add_phase0_test(test_abi_fingerprint gmpfrxx_mkII)
add_phase0_test(test_alias_safety gmpfrxx_mkII)
add_phase0_test(test_temporary_expression_lifetime gmpfrxx_mkII)
add_phase0_test(test_leaf_single_rounding gmpfrxx_mkII)
add_phase0_test(test_expr_unary_move_folding gmpfrxx_mkII)
add_phase0_test(test_comparisons gmpxx_mkII)
add_phase0_test(test_compound_assign gmpxx_mkII)
add_phase0_test(test_common_type gmpfrxx_mkII)
add_phase0_test(test_exception_support gmpxx_mkII)
add_phase0_test(test_random gmpxx_mkII)
add_phase0_test(test_type_conversions gmpxx_mkII)
add_phase0_test(test_mpq_floating_conversion gmpfrxx_mkII)
add_phase0_test(test_unary_minus_simplification gmpfrxx_mkII)
add_phase0_test(test_user_defined_literals gmpxx_mkII)
add_phase0_test(test_et_contract_mpfr mpfrxx_mkII)
add_phase0_test(test_mpfr_user_defined_literals mpcxx_mkII)
add_phase0_test(test_mpfr_expression_eval mpfrxx_mkII)
add_phase0_test(test_mpfr_precision_policy mpfrxx_mkII)
add_phase0_test(test_mpfr_aliasing mpfrxx_mkII)
add_phase0_test(test_mpfr_random mpfrxx_mkII)
add_phase0_test(test_et_contract_mpfr_scalar mpfrxx_mkII)
add_phase0_test(test_mpfr_scalar_eval mpfrxx_mkII)
add_phase0_test(test_mpfr_long_width_dispatch mpfrxx_mkII)
add_phase0_test(test_mpfr_alloc_count mpfrxx_mkII)
set_tests_properties(test_mpfr_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
add_phase0_test(test_mpfr_fixed_precision_materialization mpfrxx_mkII)
target_compile_definitions(test_mpfr_fixed_precision_materialization PRIVATE
    GMPFRXX_MKII_FAST_FIXED_PREC
    GMPFRXX_MKII_ENABLE_FMA
)
add_phase0_test(test_mpfr_fixed_precision_fma_alloc_count mpfrxx_mkII)
target_compile_definitions(test_mpfr_fixed_precision_fma_alloc_count PRIVATE
    GMPFRXX_MKII_FAST_FIXED_PREC
    GMPFRXX_MKII_ENABLE_FMA
)
set_tests_properties(test_mpfr_fixed_precision_fma_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
add_phase0_test(test_mpfr_fixed_precision_tls_scratch mpfrxx_mkII)
target_compile_definitions(test_mpfr_fixed_precision_tls_scratch PRIVATE
    GMPFRXX_MKII_FAST_FIXED_PREC
)
set_tests_properties(test_mpfr_fixed_precision_tls_scratch PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
add_phase0_test(test_mpfr_type_conversions mpfrxx_mkII)
add_phase0_test(test_mpfr_comparisons mpfrxx_mkII)
add_phase0_test(test_mpfr_compound_assign mpfrxx_mkII)
target_compile_definitions(test_mpfr_compound_assign PRIVATE
    GMPFRXX_MKII_ENABLE_FMA
)
add_phase0_test(test_mpfr_rounding_context mpfrxx_mkII)
add_executable(test_mpfr_rounding_context_fixed_precision test_mpfr_rounding_context.cpp)
target_link_libraries(test_mpfr_rounding_context_fixed_precision PRIVATE mpfrxx_mkII)
target_compile_definitions(test_mpfr_rounding_context_fixed_precision PRIVATE
    GMPFRXX_MKII_FAST_FIXED_PREC
)
add_test(
    NAME test_mpfr_rounding_context_fixed_precision
    COMMAND test_mpfr_rounding_context_fixed_precision
)
add_phase0_test(test_mpfr_rounding_scope mpfrxx_mkII)
add_executable(test_mpfr_rounding_scope_stable test_mpfr_rounding_scope.cpp)
target_link_libraries(test_mpfr_rounding_scope_stable PRIVATE mpfrxx_mkII)
target_compile_definitions(test_mpfr_rounding_scope_stable PRIVATE
    GMPFRXX_MKII_FAST_STABLE_RND
)
add_test(NAME test_mpfr_rounding_scope_stable COMMAND test_mpfr_rounding_scope_stable)
add_phase0_test(test_mpfr_exception_support mpcxx_mkII)
add_phase0_test(test_mpfr_defaults mpfrxx_mkII)
add_phase0_test(test_mpfr_initialization_sentinel mpfrxx_mkII)
add_phase0_test(test_mpfr_environment mpfrxx_mkII)
add_executable(test_mpfr_environment_first_use test_mpfr_environment_first_use.cpp)
target_link_libraries(test_mpfr_environment_first_use PRIVATE mpfrxx_mkII)
add_test(
    NAME test_mpfr_environment_first_use_with_precision
    COMMAND test_mpfr_environment_first_use with_precision
)
add_test(
    NAME test_mpfr_environment_first_use_explicit_precision_constructor
    COMMAND test_mpfr_environment_first_use explicit_precision_constructor
)
add_test(
    NAME test_mpfr_environment_first_use_current_eval_context
    COMMAND test_mpfr_environment_first_use current_eval_context
)
add_test(
    NAME test_mpfr_environment_first_use_initialize_thread_defaults
    COMMAND test_mpfr_environment_first_use initialize_thread_defaults
)
add_test(
    NAME test_mpfr_environment_first_use_raw_rounding_preserved
    COMMAND test_mpfr_environment_first_use raw_rounding_preserved
)
add_executable(test_mpfr_environment_first_use_stable test_mpfr_environment_first_use.cpp)
target_link_libraries(test_mpfr_environment_first_use_stable PRIVATE mpfrxx_mkII)
target_compile_definitions(test_mpfr_environment_first_use_stable PRIVATE
    GMPFRXX_MKII_FAST_STABLE_RND
)
add_test(
    NAME test_mpfr_environment_first_use_stable_with_precision
    COMMAND test_mpfr_environment_first_use_stable with_precision
)
add_test(
    NAME test_mpfr_environment_first_use_stable_explicit_precision_constructor
    COMMAND test_mpfr_environment_first_use_stable explicit_precision_constructor
)
add_test(
    NAME test_mpfr_environment_first_use_stable_current_eval_context
    COMMAND test_mpfr_environment_first_use_stable current_eval_context
)
add_test(
    NAME test_mpfr_environment_first_use_stable_initialize_thread_defaults
    COMMAND test_mpfr_environment_first_use_stable initialize_thread_defaults
)
add_test(
    NAME test_mpfr_environment_first_use_stable_raw_rounding_preserved
    COMMAND test_mpfr_environment_first_use_stable raw_rounding_preserved
)
add_phase0_test(test_mpfr_environment_invalid mpfrxx_mkII)
add_phase0_test(test_mpfr_string_io mpfrxx_mkII)
add_phase0_test(test_mpfr_math mpfrxx_mkII)
add_phase0_test(test_mpfr_transcendent_functions mpfrxx_mkII)
add_phase0_test(test_et_contract_mpf gmpxx_mkII)
add_phase0_test(test_mpf_basic gmpxx_mkII)
add_phase0_test(test_mpf_precision_policy gmpxx_mkII)
add_phase0_test(test_mpf_default_precision_env gmpxx_mkII)
set_tests_properties(test_mpf_default_precision_env PROPERTIES
    ENVIRONMENT "GMPXX_DEFAULT_MPF_PRECISION_BITS=1024"
)
add_executable(test_mpf_default_precision_invalid_env test_mpf_default_precision_invalid_env.cpp)
target_link_libraries(test_mpf_default_precision_invalid_env PRIVATE gmpxx_mkII)
add_test(NAME test_mpf_default_precision_invalid_env COMMAND test_mpf_default_precision_invalid_env)
set_tests_properties(test_mpf_default_precision_invalid_env PROPERTIES
    ENVIRONMENT "GMPXX_DEFAULT_MPF_PRECISION_BITS=abc"
)
add_test(NAME test_mpf_default_precision_overflow_env COMMAND test_mpf_default_precision_invalid_env)
set_tests_properties(test_mpf_default_precision_overflow_env PROPERTIES
    ENVIRONMENT "GMPXX_DEFAULT_MPF_PRECISION_BITS=18446744073709551616"
)
add_phase0_test(test_mpf_external_provider gmpxx_mkII_default_context_provider)
add_phase0_test(test_mpf_aliasing gmpxx_mkII)
add_phase0_test(test_mpf_string_io gmpxx_mkII)
add_phase0_test(test_mpf_alloc_count gmpxx_mkII)
set_tests_properties(test_mpf_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
add_phase0_test(test_mpf_scalar_alloc_count gmpxx_mkII)
set_tests_properties(test_mpf_scalar_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
add_phase0_test(test_mpf_fixed_precision_materialization gmpxx_mkII)
target_compile_definitions(test_mpf_fixed_precision_materialization PRIVATE
    GMPFRXX_MKII_FAST_FIXED_PREC
)
add_phase0_test(test_mpf_fixed_precision_tls_scratch gmpxx_mkII)
target_compile_definitions(test_mpf_fixed_precision_tls_scratch PRIVATE
    GMPFRXX_MKII_FAST_FIXED_PREC
)
set_tests_properties(test_mpf_fixed_precision_tls_scratch PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
add_phase0_test(test_mpf_long_width_dispatch gmpxx_mkII)
add_phase0_test(test_mpf_numeric_equivalence gmpxx_mkII)
add_phase0_test(test_mpf_power_of_two_fusion gmpxx_mkII)
add_phase0_test(test_mpf_decomposition_functions gmpxx_mkII)
add_phase0_test(test_mpf_thread_safety gmpxx_mkII)
add_phase0_test(test_mpf_pi gmpxx_mkII)
add_phase0_test(test_mpf_transcendent_functions gmpxx_mkII)
add_phase0_test(test_mpf_math_functions gmpxx_mkII)
add_phase0_test(test_mpf_extended_transcendent_functions gmpxx_mkII)
add_phase0_test(test_construction_copy gmpfrxx_mkII)
add_phase0_test(test_et_contract_mpfc gmpxx_mkII)
add_phase0_test(test_mpfc_basic gmpxx_mkII)
add_phase0_test(test_mpfc_division_smoke gmpxx_mkII)
add_phase0_test(test_mpfc_precision_policy gmpxx_mkII)
set_tests_properties(test_mpfc_precision_policy PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
add_phase0_test(test_mpfc_header_boundary gmpxx_mkII)
add_phase0_test(test_mpfc_math gmpxx_mkII)
add_phase0_test(test_mpfc_io gmpxx_mkII)
add_phase0_test(test_et_contract_zq_mpf gmpxx_mkII)
add_phase0_test(test_mixed_zq_mpf_promotion gmpxx_mkII)
add_phase0_test(test_mixed_type_arithmetic gmpfrxx_mkII)
add_phase0_test(test_et_contract_zq_mpfr mpfrxx_mkII)
add_phase0_test(test_mpz_basic gmpxx_mkII)
add_phase0_test(test_mpz_arithmetic gmpxx_mkII)
add_phase0_test(test_mpz_addmul_fusion mpfrxx_mkII)
add_phase0_test(test_mpz_addmul_alloc_count mpfrxx_mkII)
set_tests_properties(test_mpz_addmul_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
add_phase0_test(test_mpq_basic gmpxx_mkII)
add_phase0_test(test_mpq_arithmetic gmpxx_mkII)
add_phase0_test(test_mpq_canonicalization gmpxx_mkII)
add_phase0_test(test_zq_string_io gmpxx_mkII)
add_phase0_test(test_mpz_mpq_alloc_count mpfrxx_mkII)
set_tests_properties(test_mpz_mpq_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
add_phase0_test(test_mixed_zq_mpfr_promotion mpfrxx_mkII)
add_phase0_test(test_mixed_zq_mpfr_exact_subexpression mpfrxx_mkII)
add_phase0_test(test_mpfr_numeric_equivalence mpfrxx_mkII)
add_phase0_test(test_mpfr_power_of_two_fusion mpfrxx_mkII)
add_phase0_test(test_mpfr_decomposition_functions mpfrxx_mkII)
add_phase0_test(test_mpfr_scalar_alloc_count mpfrxx_mkII)
set_tests_properties(test_mpfr_scalar_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)
if(WIN32)
    # Exact GMP allocation counts are CRT/linkage-dependent on MinGW.
    set_tests_properties(test_mpf_scalar_alloc_count test_mpfr_scalar_alloc_count PROPERTIES DISABLED TRUE)
endif()
add_phase0_test(test_mpfr_thread_safety mpfrxx_mkII)
add_phase0_test(test_et_contract_mpc mpcxx_mkII)
add_phase0_test(test_mpc_basic mpcxx_mkII)
add_phase0_test(test_mpc_aliasing mpcxx_mkII)
add_phase0_test(test_mpc_defaults mpcxx_mkII)
add_phase0_test(test_mpc_environment mpcxx_mkII)
add_executable(test_mpc_environment_first_use test_mpc_environment_first_use.cpp)
target_link_libraries(test_mpc_environment_first_use PRIVATE mpcxx_mkII)
add_test(
    NAME test_mpc_environment_first_use_share_mpfr
    COMMAND test_mpc_environment_first_use share_mpfr
)
add_test(
    NAME test_mpc_environment_first_use_mpc_env
    COMMAND test_mpc_environment_first_use mpc_env
)
add_test(
    NAME test_mpc_environment_first_use_component_env
    COMMAND test_mpc_environment_first_use component_env
)
add_test(
    NAME test_mpc_environment_first_use_asymmetric_override
    COMMAND test_mpc_environment_first_use asymmetric_override
)
add_phase0_test(test_mpc_environment_invalid mpcxx_mkII)
add_phase0_test(test_mpc_precision_policy mpcxx_mkII)
add_phase0_test(test_mpc_evaluation_context mpcxx_mkII)
add_executable(test_mpc_evaluation_context_fixed_precision test_mpc_evaluation_context.cpp)
target_link_libraries(test_mpc_evaluation_context_fixed_precision PRIVATE mpcxx_mkII)
target_compile_definitions(test_mpc_evaluation_context_fixed_precision PRIVATE
    GMPFRXX_MKII_FAST_FIXED_PREC
)
add_test(
    NAME test_mpc_evaluation_context_fixed_precision
    COMMAND test_mpc_evaluation_context_fixed_precision
)
add_phase0_test(test_mpc_io mpcxx_mkII)
add_phase0_test(test_mpc_math mpcxx_mkII)
add_phase0_test(test_mpc_alloc_count mpcxx_mkII)
target_compile_definitions(test_mpc_alloc_count PRIVATE
    GMPFRXX_MKII_FAST_FIXED_PREC
)
set_tests_properties(test_mpc_alloc_count PROPERTIES RUN_SERIAL TRUE LABELS alloc-count)

set(GMPFRXX_MKII_MANUAL_COMPILE_FLAGS)
if(APPLE AND CMAKE_OSX_SYSROOT)
    list(APPEND GMPFRXX_MKII_MANUAL_COMPILE_FLAGS
        -isysroot "${CMAKE_OSX_SYSROOT}")
endif()

add_test(
    NAME compile_pass_harness_control
    COMMAND ${CMAKE_CXX_COMPILER}
        -std=c++17
        ${GMPFRXX_MKII_MANUAL_COMPILE_FLAGS}
        -I${CMAKE_SOURCE_DIR}/include
        -I${CMAKE_BINARY_DIR}/generated
        -I${GMP_INCLUDE_DIR}
        -I${MPFR_INCLUDE_DIR}
        -I${MPC_INCLUDE_DIR}
        -c "${CMAKE_CURRENT_SOURCE_DIR}/compile_pass_harness_control.cpp"
        -o "${CMAKE_CURRENT_BINARY_DIR}/compile_pass_harness_control.o"
)

function(add_compile_fail_test name expected_diagnostic)
    set(source "${CMAKE_CURRENT_SOURCE_DIR}/compile_fail/${name}.cpp")
    set(output "${CMAKE_CURRENT_BINARY_DIR}/${name}.o")
    set(include_dirs
        "${CMAKE_SOURCE_DIR}/include"
        "${CMAKE_BINARY_DIR}/generated"
        "${GMP_INCLUDE_DIR}"
        "${MPFR_INCLUDE_DIR}"
        "${MPC_INCLUDE_DIR}")
    add_test(
        NAME compile_fail_${name}
        COMMAND ${CMAKE_COMMAND}
            -DCXX_COMPILER=${CMAKE_CXX_COMPILER}
            "-DCXX_FLAGS=${GMPFRXX_MKII_MANUAL_COMPILE_FLAGS}"
            "-DINCLUDE_DIRS=${include_dirs}"
            -DSOURCE=${source}
            -DOUTPUT=${output}
            "-DEXPECTED_DIAGNOSTIC=${expected_diagnostic}"
            -P "${CMAKE_CURRENT_SOURCE_DIR}/compile_fail_driver.cmake"
    )
endfunction()

set(COMPILE_FAIL_HEADER_DIAGNOSTIC "not.*member|has not been declared|does not name|no type named|unknown type name|undeclared|incomplete type")
set(COMPILE_FAIL_OPERATOR_DIAGNOSTIC "no match|invalid operands|no viable|no matching|deleted")
set(COMPILE_FAIL_SCALAR_DIAGNOSTIC "no match|invalid operands|no viable|no matching")
set(COMPILE_FAIL_GUARD_DIAGNOSTIC "default_mpf_precision_guard|not.*member|not.*declared|no matching|deleted")

add_compile_fail_test(mpfr_header_must_not_expose_mpf "${COMPILE_FAIL_HEADER_DIAGNOSTIC}")
add_compile_fail_test(mpfr_header_must_not_expose_mpfc "${COMPILE_FAIL_HEADER_DIAGNOSTIC}")
add_compile_fail_test(mpfr_header_must_not_expose_mpc "${COMPILE_FAIL_HEADER_DIAGNOSTIC}")
add_compile_fail_test(gmp_header_must_not_expose_mpfr_or_mpc "${COMPILE_FAIL_HEADER_DIAGNOSTIC}")
add_compile_fail_test(test_bool_scalar "${COMPILE_FAIL_SCALAR_DIAGNOSTIC}")
add_compile_fail_test(test_wchar_scalar "${COMPILE_FAIL_SCALAR_DIAGNOSTIC}")
add_compile_fail_test(test_char16_scalar "${COMPILE_FAIL_SCALAR_DIAGNOSTIC}")
add_compile_fail_test(test_char32_scalar "${COMPILE_FAIL_SCALAR_DIAGNOSTIC}")
add_compile_fail_test(test_long_double_scalar "${COMPILE_FAIL_SCALAR_DIAGNOSTIC}")
if(GMPFRXX_MKII_HAVE_CXX_INT128)
    add_compile_fail_test(test_int128_scalar "${COMPILE_FAIL_SCALAR_DIAGNOSTIC}")
endif()
add_compile_fail_test(test_scalar_scalar_et_operator "${COMPILE_FAIL_OPERATOR_DIAGNOSTIC}")
add_compile_fail_test(test_mpf_precision_guard_frozen_env "${COMPILE_FAIL_GUARD_DIAGNOSTIC}")
add_compile_fail_test(test_mpf_plus_mpfr "${COMPILE_FAIL_OPERATOR_DIAGNOSTIC}")
add_compile_fail_test(test_mpfr_plus_mpf "${COMPILE_FAIL_OPERATOR_DIAGNOSTIC}")
add_compile_fail_test(test_mpf_plus_mpc "${COMPILE_FAIL_OPERATOR_DIAGNOSTIC}")
add_compile_fail_test(test_mpc_plus_mpf "${COMPILE_FAIL_OPERATOR_DIAGNOSTIC}")
add_compile_fail_test(test_mpfc_plus_mpfr "${COMPILE_FAIL_OPERATOR_DIAGNOSTIC}")
add_compile_fail_test(test_mpfr_plus_mpfc "${COMPILE_FAIL_OPERATOR_DIAGNOSTIC}")
add_compile_fail_test(test_mpfc_plus_mpc "${COMPILE_FAIL_OPERATOR_DIAGNOSTIC}")
add_compile_fail_test(test_mpc_plus_mpfc "${COMPILE_FAIL_OPERATOR_DIAGNOSTIC}")

add_test(
    NAME link_fail_mpf_external_provider_missing
    COMMAND ${CMAKE_CXX_COMPILER}
        -std=c++17
        -DGMPXX_MKII_DEFAULT_CONTEXT_MODE=GMPXX_MKII_DEFAULT_CONTEXT_EXTERNAL_PROVIDER
        -I${CMAKE_SOURCE_DIR}/include
        -I${CMAKE_BINARY_DIR}/generated
        -I${GMP_INCLUDE_DIR}
        "${CMAKE_CURRENT_SOURCE_DIR}/test_mpf_external_provider_missing.cpp"
        ${GMP_LIBRARY}
        -o "${CMAKE_CURRENT_BINARY_DIR}/test_mpf_external_provider_missing"
)
set_tests_properties(link_fail_mpf_external_provider_missing PROPERTIES WILL_FAIL TRUE)
