# Copyright (c) 2026
#      Nakata, Maho
#      All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

option(GMPFRXX_MKII_ENABLE_OPENMP_BENCHMARKS
    "Build benchmark targets whose sources include omp.h"
    ON)

if(GMPFRXX_MKII_ENABLE_OPENMP_BENCHMARKS)
    find_path(GMPFRXX_MKII_OPENMP_INCLUDE_DIR
        NAMES omp.h
        HINTS
            /opt/local/include/libomp
            /opt/local/include
            /opt/homebrew/opt/libomp/include
            /usr/local/opt/libomp/include
    )
    find_library(GMPFRXX_MKII_OPENMP_LIBRARY
        NAMES omp gomp
        HINTS
            /opt/local/lib/libomp
            /opt/local/lib/libgcc
            /opt/homebrew/opt/libomp/lib
            /usr/local/opt/libomp/lib
    )

    if(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang|Clang"
            AND GMPFRXX_MKII_OPENMP_INCLUDE_DIR
            AND GMPFRXX_MKII_OPENMP_LIBRARY)
        set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${GMPFRXX_MKII_OPENMP_INCLUDE_DIR}" CACHE STRING
            "OpenMP flags for C++" FORCE)
        set(OpenMP_CXX_INCLUDE_DIR "${GMPFRXX_MKII_OPENMP_INCLUDE_DIR}" CACHE PATH
            "OpenMP include directory for C++" FORCE)
        set(OpenMP_CXX_LIB_NAMES "omp" CACHE STRING
            "OpenMP library names for C++" FORCE)
        set(OpenMP_omp_LIBRARY "${GMPFRXX_MKII_OPENMP_LIBRARY}" CACHE FILEPATH
            "OpenMP runtime library" FORCE)
        set(OpenMP_libomp_LIBRARY "${GMPFRXX_MKII_OPENMP_LIBRARY}" CACHE FILEPATH
            "OpenMP runtime library" FORCE)
    endif()

    find_package(OpenMP)
else()
    set(OpenMP_CXX_FOUND OFF)
endif()

if(OpenMP_CXX_FOUND)
    message(STATUS "OpenMP benchmark targets enabled")
else()
    message(STATUS "OpenMP benchmark targets disabled; OpenMP_CXX was not found")
endif()
find_library(BENCHMARK_GMPXX_LIBRARY NAMES gmpxx)
find_package(Python3 COMPONENTS Interpreter QUIET)
find_program(GMPFRXX_MKII_OBJDUMP_EXECUTABLE objdump)

option(GMPFRXX_MKII_BENCHMARK_COUNT_MPF_OPERATIONS
    "Count mpf_init, mpf_clear, and selected mpf arithmetic calls in instrumented GMP benchmarks"
    OFF)
option(GMPFRXX_MKII_BENCHMARK_COUNT_MPFR_OPERATIONS
    "Count mpfr_init, mpfr_init2, mpfr_clear, mpfr_add, and mpfr_mul calls in instrumented MPFR benchmarks"
    OFF)

if(NOT BENCHMARK_GMPXX_LIBRARY)
    message(WARNING "libgmpxx was not found; upstream gmpxx.h orig benchmark variants will not be built")
endif()

function(skip_openmp_benchmark_if_needed target)
    if(target MATCHES "openmp" AND NOT OpenMP_CXX_FOUND)
        set(GMPFRXX_MKII_SKIP_OPENMP_TARGET TRUE PARENT_SCOPE)
        return()
    endif()
    set(GMPFRXX_MKII_SKIP_OPENMP_TARGET FALSE PARENT_SCOPE)
endfunction()

function(configure_eager_benchmark target subdir)
    target_include_directories(${target} PRIVATE
        "${CMAKE_CURRENT_SOURCE_DIR}/common"
        "${CMAKE_CURRENT_SOURCE_DIR}/${subdir}")
    set_target_properties(${target} PROPERTIES
        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${subdir}")
    if(target MATCHES "openmp" AND OpenMP_CXX_FOUND)
        target_link_libraries(${target} PRIVATE OpenMP::OpenMP_CXX)
    endif()
    if(GMPFRXX_MKII_BENCHMARK_COUNT_MPF_OPERATIONS)
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_BENCHMARK_COUNT_MPF_OPERATIONS)
    endif()
    if(GMPFRXX_MKII_BENCHMARK_COUNT_MPFR_OPERATIONS)
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_BENCHMARK_COUNT_MPFR_OPERATIONS)
    endif()
endfunction()

function(add_mkii_variant subdir source base suffix)
    set(target "${base}_${suffix}")
    skip_openmp_benchmark_if_needed(${target})
    if(GMPFRXX_MKII_SKIP_OPENMP_TARGET)
        return()
    endif()
    add_executable(${target} "${subdir}/${source}")
    configure_eager_benchmark(${target} ${subdir})
    target_link_libraries(${target} PRIVATE gmpxx_mkII::gmpxx_mkII)
    if(suffix STREQUAL "mkII_FIXED_PRECISION_FASTPATH")
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_FAST_FIXED_PREC)
    endif()
endfunction()

function(add_gmpxx_variant subdir source base)
    if(NOT BENCHMARK_GMPXX_LIBRARY)
        return()
    endif()
    set(target "${base}_orig")
    skip_openmp_benchmark_if_needed(${target})
    if(GMPFRXX_MKII_SKIP_OPENMP_TARGET)
        return()
    endif()
    add_executable(${target} "${subdir}/${source}")
    configure_eager_benchmark(${target} ${subdir})
    target_compile_definitions(${target} PRIVATE USE_ORIGINAL_GMPXX)
    target_link_libraries(${target} PRIVATE "${BENCHMARK_GMPXX_LIBRARY}" GMP::GMP)
endfunction()

function(add_kernel_variants subdir source base)
    add_gmpxx_variant(${subdir} ${source} ${base})
    add_mkii_variant(${subdir} ${source} ${base} mkII)
    add_mkii_variant(${subdir} ${source} ${base} mkII_FIXED_PRECISION_FASTPATH)
endfunction()

function(add_rdot_kernel_variants subdir source base)
    add_gmpxx_variant(${subdir} ${source} ${base})
    add_mkii_variant(${subdir} ${source} ${base} mkII)
    add_mkii_variant(${subdir} ${source} ${base} mkII_FIXED_PRECISION_FASTPATH)
endfunction()

function(add_native_benchmark subdir source target)
    skip_openmp_benchmark_if_needed(${target})
    if(GMPFRXX_MKII_SKIP_OPENMP_TARGET)
        return()
    endif()
    add_executable(${target} "${subdir}/${source}")
    configure_eager_benchmark(${target} ${subdir})
    target_link_libraries(${target} PRIVATE gmpxx_mkII::gmpxx_mkII GMP::GMP)
endfunction()

function(add_mpfr_mkii_variant subdir source base suffix)
    set(target "${base}_${suffix}")
    skip_openmp_benchmark_if_needed(${target})
    if(GMPFRXX_MKII_SKIP_OPENMP_TARGET)
        return()
    endif()
    add_executable(${target} "${subdir}/${source}")
    configure_eager_benchmark(${target} ${subdir})
    target_link_libraries(${target} PRIVATE mpfrxx_mkII::mpfrxx_mkII)
    if(suffix MATCHES "STABLE_ROUNDING")
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_FAST_STABLE_RND)
    endif()
    if(suffix MATCHES "FIXED_PRECISION_FASTPATH")
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_FAST_FIXED_PREC)
    endif()
    if(suffix MATCHES "FMA")
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_ENABLE_FMA)
    endif()
endfunction()

function(add_mpfr_kernel_variants subdir source base)
    add_mpfr_mkii_variant(${subdir} ${source} ${base} mkII)
    string(REPLACE ".cpp" "_ROUNDING.cpp" rounding_source "${source}")
    add_mpfr_mkii_variant(${subdir} ${rounding_source} ${base} mkII_ROUNDING)
    add_mpfr_mkii_variant(${subdir} ${rounding_source} ${base} mkII_ROUNDING_FIXED_PRECISION_FASTPATH)
    add_mpfr_mkii_variant(${subdir} ${rounding_source} ${base} mkII_ROUNDING_FIXED_PRECISION_FASTPATH_FMA)
endfunction()

function(add_mpfr_rdot_cpp_target subdir source target)
    skip_openmp_benchmark_if_needed(${target})
    if(GMPFRXX_MKII_SKIP_OPENMP_TARGET)
        return()
    endif()
    add_executable(${target} "${subdir}/${source}")
    configure_eager_benchmark(${target} ${subdir})
    target_link_libraries(${target} PRIVATE mpfrxx_mkII::mpfrxx_mkII)
    if(target MATCHES "PRECISION")
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_FAST_FIXED_PREC)
    endif()
    if(target MATCHES "_FMA$")
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_ENABLE_FMA)
    endif()
endfunction()

function(add_mpfr_rdot_base_variants subdir source base)
    add_mpfr_rdot_cpp_target(${subdir} ${source} ${base})
    add_mpfr_rdot_cpp_target(${subdir} ${source} ${base}_PRECISION)
endfunction()

function(add_mpfr_rdot_rounding_variants subdir source base)
    add_mpfr_rdot_cpp_target(${subdir} ${source} ${base}_ROUNDING)
    add_mpfr_rdot_cpp_target(${subdir} ${source} ${base}_ROUNDING_PRECISION)
endfunction()

function(add_mpfr_rdot_fma_variants subdir source base)
    add_mpfr_rdot_cpp_target(${subdir} ${source} ${base}_PRECISION_FMA)
endfunction()

function(add_mpfr_rdot_variant_family subdir base)
    add_mpfr_rdot_base_variants(${subdir} ${base}.cpp ${base})
    add_mpfr_rdot_rounding_variants(${subdir} ${base}_ROUNDING.cpp ${base})
endfunction()

function(add_mpfr_raxpy_cpp_target subdir source target)
    skip_openmp_benchmark_if_needed(${target})
    if(GMPFRXX_MKII_SKIP_OPENMP_TARGET)
        return()
    endif()
    add_executable(${target} "${subdir}/${source}")
    configure_eager_benchmark(${target} ${subdir})
    target_link_libraries(${target} PRIVATE mpfrxx_mkII::mpfrxx_mkII)
    if(target MATCHES "PRECISION")
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_FAST_FIXED_PREC)
    endif()
    if(target MATCHES "_FMA$")
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_ENABLE_FMA)
    endif()
endfunction()

function(add_mpfr_raxpy_variant_family subdir base)
    add_mpfr_raxpy_cpp_target(${subdir} ${base}.cpp ${base})
    add_mpfr_raxpy_cpp_target(${subdir} ${base}.cpp ${base}_PRECISION)
    add_mpfr_raxpy_cpp_target(${subdir} ${base}_ROUNDING.cpp ${base}_ROUNDING)
    add_mpfr_raxpy_cpp_target(${subdir} ${base}_ROUNDING.cpp ${base}_ROUNDING_PRECISION)
endfunction()

function(add_mpfr_raxpy_fma_variant_family subdir base)
    add_mpfr_raxpy_variant_family(${subdir} ${base})
    add_mpfr_raxpy_cpp_target(${subdir} ${base}.cpp ${base}_PRECISION_FMA)
    add_mpfr_raxpy_cpp_target(${subdir} ${base}_ROUNDING.cpp ${base}_ROUNDING_PRECISION_FMA)
endfunction()

function(add_mpfr_rgemv_cpp_target subdir source target)
    skip_openmp_benchmark_if_needed(${target})
    if(GMPFRXX_MKII_SKIP_OPENMP_TARGET)
        return()
    endif()
    add_executable(${target} "${subdir}/${source}")
    configure_eager_benchmark(${target} ${subdir})
    target_link_libraries(${target} PRIVATE mpfrxx_mkII::mpfrxx_mkII)
    if(target MATCHES "PRECISION")
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_FAST_FIXED_PREC)
    endif()
    if(target MATCHES "_FMA$")
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_ENABLE_FMA)
    endif()
endfunction()

function(add_mpfr_rgemv_base_variants subdir source base)
    add_mpfr_rgemv_cpp_target(${subdir} ${source} ${base})
    add_mpfr_rgemv_cpp_target(${subdir} ${source} ${base}_PRECISION)
endfunction()

function(add_mpfr_rgemv_rounding_variants subdir source base)
    add_mpfr_rgemv_cpp_target(${subdir} ${source} ${base}_ROUNDING)
    add_mpfr_rgemv_cpp_target(${subdir} ${source} ${base}_ROUNDING_PRECISION)
endfunction()

function(add_mpfr_rgemv_fma_capture_variants subdir source base)
    add_mpfr_rgemv_cpp_target(${subdir} ${source} ${base}_ROUNDING_FMA_CAPTURE_FMA)
    add_mpfr_rgemv_cpp_target(${subdir} ${source} ${base}_ROUNDING_FMA_CAPTURE_PRECISION_FMA)
endfunction()

function(add_mpfr_rgemv_variant_family subdir base)
    add_mpfr_rgemv_base_variants(${subdir} ${base}.cpp ${base})
    add_mpfr_rgemv_rounding_variants(${subdir} ${base}_ROUNDING.cpp ${base})
    add_mpfr_rgemv_fma_capture_variants(${subdir} ${base}_ROUNDING_FMA_CAPTURE.cpp ${base})
endfunction()

function(add_mpfr_rgemm_cpp_target subdir source target)
    skip_openmp_benchmark_if_needed(${target})
    if(GMPFRXX_MKII_SKIP_OPENMP_TARGET)
        return()
    endif()
    add_executable(${target} "${subdir}/${source}")
    configure_eager_benchmark(${target} ${subdir})
    target_link_libraries(${target} PRIVATE mpfrxx_mkII::mpfrxx_mkII)
    if(target MATCHES "PRECISION")
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_FAST_FIXED_PREC)
    endif()
    if(target MATCHES "_FMA$")
        target_compile_definitions(${target} PRIVATE
            GMPFRXX_MKII_ENABLE_FMA)
    endif()
endfunction()

function(add_mpfr_rgemm_base_variants subdir source base)
    add_mpfr_rgemm_cpp_target(${subdir} ${source} ${base}_mkII)
    add_mpfr_rgemm_cpp_target(${subdir} ${source} ${base}_mkII_PRECISION)
endfunction()

function(add_mpfr_rgemm_rounding_variants subdir source base)
    add_mpfr_rgemm_cpp_target(${subdir} ${source} ${base}_mkII_ROUNDING)
    add_mpfr_rgemm_cpp_target(${subdir} ${source} ${base}_mkII_ROUNDING_PRECISION)
endfunction()

function(add_mpfr_rgemm_fma_variants subdir source base)
    add_mpfr_rgemm_cpp_target(${subdir} ${source} ${base}_mkII_ROUNDING_FMA)
    add_mpfr_rgemm_cpp_target(${subdir} ${source} ${base}_mkII_ROUNDING_PRECISION_FMA)
endfunction()

function(add_mpfr_rgemm_variant_family subdir base)
    add_mpfr_rgemm_base_variants(${subdir} ${base}.cpp ${base})
    add_mpfr_rgemm_rounding_variants(${subdir} ${base}_ROUNDING.cpp ${base})
endfunction()

function(add_mpfr_rgemm_fma_variant_family subdir base)
    add_mpfr_rgemm_variant_family(${subdir} ${base})
    add_mpfr_rgemm_fma_variants(${subdir} ${base}_ROUNDING_FMA.cpp ${base})
endfunction()

function(add_mpfr_native_benchmark subdir source target)
    skip_openmp_benchmark_if_needed(${target})
    if(GMPFRXX_MKII_SKIP_OPENMP_TARGET)
        return()
    endif()
    add_executable(${target} "${subdir}/${source}")
    configure_eager_benchmark(${target} ${subdir})
    target_link_libraries(${target} PRIVATE mpfrxx_mkII::mpfrxx_mkII)
endfunction()

add_native_benchmark(gmp/00_Rdot Rdot_gmp_C_native_01.cpp
    Rdot_gmp_C_native_01)
add_native_benchmark(gmp/00_Rdot Rdot_gmp_C_native_02.cpp
    Rdot_gmp_C_native_02)
add_native_benchmark(gmp/00_Rdot Rdot_gmp_C_native_03.cpp
    Rdot_gmp_C_native_03)
add_native_benchmark(gmp/00_Rdot Rdot_gmp_C_native_04.cpp
    Rdot_gmp_C_native_04)
add_native_benchmark(gmp/00_Rdot Rdot_gmp_C_native_05.cpp
    Rdot_gmp_C_native_05)
add_native_benchmark(gmp/00_Rdot Rdot_gmp_C_native_06.cpp
    Rdot_gmp_C_native_06)
add_native_benchmark(gmp/00_Rdot Rdot_gmp_C_native_openmp_01.cpp
    Rdot_gmp_C_native_openmp_01)
add_native_benchmark(gmp/00_Rdot Rdot_gmp_C_native_openmp_02.cpp
    Rdot_gmp_C_native_openmp_02)
add_native_benchmark(gmp/00_Rdot Rdot_gmp_C_native_openmp_03.cpp
    Rdot_gmp_C_native_openmp_03)
add_native_benchmark(gmp/00_Rdot Rdot_gmp_C_native_openmp_04.cpp
    Rdot_gmp_C_native_openmp_04)
add_native_benchmark(gmp/00_Rdot Rdot_gmp_C_native_openmp_05.cpp
    Rdot_gmp_C_native_openmp_05)
add_native_benchmark(gmp/00_Rdot Rdot_gmp_C_native_openmp_06.cpp
    Rdot_gmp_C_native_openmp_06)
add_rdot_kernel_variants(gmp/00_Rdot Rdot_gmp_kernel_01.cpp Rdot_gmp_kernel_01)
add_rdot_kernel_variants(gmp/00_Rdot Rdot_gmp_kernel_02.cpp Rdot_gmp_kernel_02)
add_rdot_kernel_variants(gmp/00_Rdot Rdot_gmp_kernel_03.cpp Rdot_gmp_kernel_03)
add_rdot_kernel_variants(gmp/00_Rdot Rdot_gmp_kernel_04.cpp Rdot_gmp_kernel_04)
add_rdot_kernel_variants(gmp/00_Rdot Rdot_gmp_kernel_05.cpp Rdot_gmp_kernel_05)
add_rdot_kernel_variants(gmp/00_Rdot Rdot_gmp_kernel_06.cpp Rdot_gmp_kernel_06)
add_rdot_kernel_variants(gmp/00_Rdot Rdot_gmp_kernel_openmp_01.cpp
    Rdot_gmp_kernel_openmp_01)
add_rdot_kernel_variants(gmp/00_Rdot Rdot_gmp_kernel_openmp_02.cpp
    Rdot_gmp_kernel_openmp_02)
add_rdot_kernel_variants(gmp/00_Rdot Rdot_gmp_kernel_openmp_03.cpp
    Rdot_gmp_kernel_openmp_03)
add_rdot_kernel_variants(gmp/00_Rdot Rdot_gmp_kernel_openmp_04.cpp
    Rdot_gmp_kernel_openmp_04)
add_rdot_kernel_variants(gmp/00_Rdot Rdot_gmp_kernel_openmp_05.cpp
    Rdot_gmp_kernel_openmp_05)
add_rdot_kernel_variants(gmp/00_Rdot Rdot_gmp_kernel_openmp_06.cpp
    Rdot_gmp_kernel_openmp_06)

add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_01.cpp
    Rdot_mpfr_C_native_01)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_02.cpp
    Rdot_mpfr_C_native_02)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_03.cpp
    Rdot_mpfr_C_native_03)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_04.cpp
    Rdot_mpfr_C_native_04)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_05.cpp
    Rdot_mpfr_C_native_05)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_05_FMA.cpp
    Rdot_mpfr_C_native_05_FMA)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_06.cpp
    Rdot_mpfr_C_native_06)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_06_FMA.cpp
    Rdot_mpfr_C_native_06_FMA)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_01_FMA.cpp
    Rdot_mpfr_C_native_01_FMA)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_openmp_01.cpp
    Rdot_mpfr_C_native_openmp_01)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_openmp_02.cpp
    Rdot_mpfr_C_native_openmp_02)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_openmp_03.cpp
    Rdot_mpfr_C_native_openmp_03)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_openmp_04.cpp
    Rdot_mpfr_C_native_openmp_04)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_openmp_05.cpp
    Rdot_mpfr_C_native_openmp_05)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_openmp_05_FMA.cpp
    Rdot_mpfr_C_native_openmp_05_FMA)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_openmp_06.cpp
    Rdot_mpfr_C_native_openmp_06)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_openmp_06_FMA.cpp
    Rdot_mpfr_C_native_openmp_06_FMA)
add_mpfr_native_benchmark(mpfr/00_Rdot Rdot_mpfr_C_native_openmp_01_FMA.cpp
    Rdot_mpfr_C_native_openmp_01_FMA)
foreach(variant 01 02 03 04 05 06)
    add_mpfr_rdot_variant_family(mpfr/00_Rdot Rdot_mpfr_kernel_${variant})
endforeach()
foreach(variant 01 05 06)
    add_mpfr_rdot_fma_variants(mpfr/00_Rdot Rdot_mpfr_kernel_${variant}.cpp
        Rdot_mpfr_kernel_${variant})
    add_mpfr_rdot_fma_variants(mpfr/00_Rdot Rdot_mpfr_kernel_${variant}_ROUNDING.cpp
        Rdot_mpfr_kernel_${variant}_ROUNDING)
endforeach()
foreach(variant 01 02 03 04 05 06)
    add_mpfr_rdot_variant_family(mpfr/00_Rdot Rdot_mpfr_kernel_openmp_${variant})
endforeach()
foreach(variant 01 05 06)
    add_mpfr_rdot_fma_variants(mpfr/00_Rdot Rdot_mpfr_kernel_openmp_${variant}.cpp
        Rdot_mpfr_kernel_openmp_${variant})
    add_mpfr_rdot_fma_variants(mpfr/00_Rdot Rdot_mpfr_kernel_openmp_${variant}_ROUNDING.cpp
        Rdot_mpfr_kernel_openmp_${variant}_ROUNDING)
endforeach()

add_mpfr_native_benchmark(mpfr/01_Raxpy Raxpy_mpfr_C_native_01.cpp
    Raxpy_mpfr_C_native_01)
add_mpfr_native_benchmark(mpfr/01_Raxpy Raxpy_mpfr_C_native_02.cpp
    Raxpy_mpfr_C_native_02)
add_mpfr_native_benchmark(mpfr/01_Raxpy Raxpy_mpfr_C_native_03.cpp
    Raxpy_mpfr_C_native_03)
add_mpfr_native_benchmark(mpfr/01_Raxpy Raxpy_mpfr_C_native_04.cpp
    Raxpy_mpfr_C_native_04)
add_mpfr_native_benchmark(mpfr/01_Raxpy Raxpy_mpfr_C_native_01_FMA.cpp
    Raxpy_mpfr_C_native_01_FMA)
add_mpfr_native_benchmark(mpfr/01_Raxpy Raxpy_mpfr_C_native_packed_custom_layout_FMA.cpp
    Raxpy_mpfr_C_native_packed_custom_layout_FMA)
add_mpfr_native_benchmark(mpfr/01_Raxpy Raxpy_mpfr_C_native_openmp_01.cpp
    Raxpy_mpfr_C_native_openmp_01)
add_mpfr_native_benchmark(mpfr/01_Raxpy Raxpy_mpfr_C_native_openmp_02.cpp
    Raxpy_mpfr_C_native_openmp_02)
add_mpfr_native_benchmark(mpfr/01_Raxpy Raxpy_mpfr_C_native_openmp_03.cpp
    Raxpy_mpfr_C_native_openmp_03)
add_mpfr_native_benchmark(mpfr/01_Raxpy Raxpy_mpfr_C_native_openmp_04.cpp
    Raxpy_mpfr_C_native_openmp_04)
add_mpfr_native_benchmark(mpfr/01_Raxpy Raxpy_mpfr_C_native_openmp_01_FMA.cpp
    Raxpy_mpfr_C_native_openmp_01_FMA)
add_mpfr_raxpy_fma_variant_family(mpfr/01_Raxpy Raxpy_mpfr_kernel_01)
add_mpfr_raxpy_variant_family(mpfr/01_Raxpy Raxpy_mpfr_kernel_02)
add_mpfr_raxpy_variant_family(mpfr/01_Raxpy Raxpy_mpfr_kernel_03)
add_mpfr_raxpy_variant_family(mpfr/01_Raxpy Raxpy_mpfr_kernel_04)
add_mpfr_raxpy_fma_variant_family(mpfr/01_Raxpy Raxpy_mpfr_kernel_openmp_01)
add_mpfr_raxpy_variant_family(mpfr/01_Raxpy Raxpy_mpfr_kernel_openmp_02)
add_mpfr_raxpy_variant_family(mpfr/01_Raxpy Raxpy_mpfr_kernel_openmp_03)
add_mpfr_raxpy_variant_family(mpfr/01_Raxpy Raxpy_mpfr_kernel_openmp_04)

add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_01.cpp
    Rgemv_mpfr_C_native_01)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_01_FMA.cpp
    Rgemv_mpfr_C_native_01_FMA)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_02.cpp
    Rgemv_mpfr_C_native_02)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_02_FMA.cpp
    Rgemv_mpfr_C_native_02_FMA)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_03.cpp
    Rgemv_mpfr_C_native_03)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_04.cpp
    Rgemv_mpfr_C_native_04)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_openmp_01.cpp
    Rgemv_mpfr_C_native_openmp_01)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_openmp_01_FMA.cpp
    Rgemv_mpfr_C_native_openmp_01_FMA)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_openmp_02.cpp
    Rgemv_mpfr_C_native_openmp_02)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_openmp_02_FMA.cpp
    Rgemv_mpfr_C_native_openmp_02_FMA)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_openmp_03.cpp
    Rgemv_mpfr_C_native_openmp_03)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_openmp_04.cpp
    Rgemv_mpfr_C_native_openmp_04)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_openmp_04_FMA.cpp
    Rgemv_mpfr_C_native_openmp_04_FMA)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_openmp_05.cpp
    Rgemv_mpfr_C_native_openmp_05)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_openmp_05_FMA.cpp
    Rgemv_mpfr_C_native_openmp_05_FMA)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_openmp_06.cpp
    Rgemv_mpfr_C_native_openmp_06)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_openmp_06_FMA.cpp
    Rgemv_mpfr_C_native_openmp_06_FMA)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_openmp_07.cpp
    Rgemv_mpfr_C_native_openmp_07)
add_mpfr_native_benchmark(mpfr/02_Rgemv Rgemv_mpfr_C_native_openmp_07_FMA.cpp
    Rgemv_mpfr_C_native_openmp_07_FMA)
foreach(variant 01 02 03 04)
    add_mpfr_rgemv_variant_family(mpfr/02_Rgemv Rgemv_mpfr_kernel_${variant})
endforeach()
foreach(variant 01 02 03 04 05 06 07)
    add_mpfr_rgemv_variant_family(mpfr/02_Rgemv Rgemv_mpfr_kernel_openmp_${variant})
endforeach()

foreach(variant 01 02 03 04 05 06)
    add_mpfr_native_benchmark(mpfr/03_Rgemm Rgemm_mpfr_C_native_${variant}.cpp
        Rgemm_mpfr_C_native_${variant})
    add_mpfr_native_benchmark(mpfr/03_Rgemm Rgemm_mpfr_C_native_openmp_${variant}.cpp
        Rgemm_mpfr_C_native_openmp_${variant})
endforeach()

foreach(variant 03 04 05 06)
    add_mpfr_native_benchmark(mpfr/03_Rgemm Rgemm_mpfr_C_native_${variant}_FMA.cpp
        Rgemm_mpfr_C_native_${variant}_FMA)
    add_mpfr_native_benchmark(mpfr/03_Rgemm Rgemm_mpfr_C_native_openmp_${variant}_FMA.cpp
        Rgemm_mpfr_C_native_openmp_${variant}_FMA)
endforeach()

foreach(variant 01 02)
    add_mpfr_rgemm_variant_family(mpfr/03_Rgemm Rgemm_mpfr_kernel_${variant})
    add_mpfr_rgemm_variant_family(mpfr/03_Rgemm Rgemm_mpfr_kernel_openmp_${variant})
endforeach()

foreach(variant 03 04 05 06)
    add_mpfr_rgemm_fma_variant_family(mpfr/03_Rgemm Rgemm_mpfr_kernel_${variant})
    add_mpfr_rgemm_fma_variant_family(mpfr/03_Rgemm Rgemm_mpfr_kernel_openmp_${variant})
endforeach()

add_native_benchmark(gmp/01_Raxpy Raxpy_gmp_C_native_01.cpp
    Raxpy_gmp_C_native_01)
add_native_benchmark(gmp/01_Raxpy Raxpy_gmp_C_native_02.cpp
    Raxpy_gmp_C_native_02)
add_native_benchmark(gmp/01_Raxpy Raxpy_gmp_C_native_03.cpp
    Raxpy_gmp_C_native_03)
add_native_benchmark(gmp/01_Raxpy Raxpy_gmp_C_native_04.cpp
    Raxpy_gmp_C_native_04)
add_native_benchmark(gmp/01_Raxpy Raxpy_gmp_C_native_openmp_01.cpp
    Raxpy_gmp_C_native_openmp_01)
add_native_benchmark(gmp/01_Raxpy Raxpy_gmp_C_native_openmp_02.cpp
    Raxpy_gmp_C_native_openmp_02)
add_native_benchmark(gmp/01_Raxpy Raxpy_gmp_C_native_openmp_03.cpp
    Raxpy_gmp_C_native_openmp_03)
add_native_benchmark(gmp/01_Raxpy Raxpy_gmp_C_native_openmp_04.cpp
    Raxpy_gmp_C_native_openmp_04)
add_kernel_variants(gmp/01_Raxpy Raxpy_gmp_kernel_01.cpp Raxpy_gmp_kernel_01)
add_kernel_variants(gmp/01_Raxpy Raxpy_gmp_kernel_02.cpp Raxpy_gmp_kernel_02)
add_kernel_variants(gmp/01_Raxpy Raxpy_gmp_kernel_03.cpp Raxpy_gmp_kernel_03)
add_kernel_variants(gmp/01_Raxpy Raxpy_gmp_kernel_04.cpp Raxpy_gmp_kernel_04)
add_kernel_variants(gmp/01_Raxpy Raxpy_gmp_kernel_openmp_01.cpp
    Raxpy_gmp_kernel_openmp_01)
add_kernel_variants(gmp/01_Raxpy Raxpy_gmp_kernel_openmp_02.cpp
    Raxpy_gmp_kernel_openmp_02)
add_kernel_variants(gmp/01_Raxpy Raxpy_gmp_kernel_openmp_03.cpp
    Raxpy_gmp_kernel_openmp_03)
add_kernel_variants(gmp/01_Raxpy Raxpy_gmp_kernel_openmp_04.cpp
    Raxpy_gmp_kernel_openmp_04)

add_native_benchmark(gmp/02_Rgemv Rgemv_gmp_C_native_01.cpp
    Rgemv_gmp_C_native_01)
add_native_benchmark(gmp/02_Rgemv Rgemv_gmp_C_native_02.cpp
    Rgemv_gmp_C_native_02)
add_native_benchmark(gmp/02_Rgemv Rgemv_gmp_C_native_03.cpp
    Rgemv_gmp_C_native_03)
add_native_benchmark(gmp/02_Rgemv Rgemv_gmp_C_native_04.cpp
    Rgemv_gmp_C_native_04)
add_native_benchmark(gmp/02_Rgemv Rgemv_gmp_C_native_openmp_01.cpp
    Rgemv_gmp_C_native_openmp_01)
add_native_benchmark(gmp/02_Rgemv Rgemv_gmp_C_native_openmp_02.cpp
    Rgemv_gmp_C_native_openmp_02)
add_native_benchmark(gmp/02_Rgemv Rgemv_gmp_C_native_openmp_03.cpp
    Rgemv_gmp_C_native_openmp_03)
add_native_benchmark(gmp/02_Rgemv Rgemv_gmp_C_native_openmp_04.cpp
    Rgemv_gmp_C_native_openmp_04)
add_native_benchmark(gmp/02_Rgemv Rgemv_gmp_C_native_openmp_05.cpp
    Rgemv_gmp_C_native_openmp_05)
add_native_benchmark(gmp/02_Rgemv Rgemv_gmp_C_native_openmp_06.cpp
    Rgemv_gmp_C_native_openmp_06)
add_native_benchmark(gmp/02_Rgemv Rgemv_gmp_C_native_openmp_07.cpp
    Rgemv_gmp_C_native_openmp_07)
add_kernel_variants(gmp/02_Rgemv Rgemv_gmp_kernel_01.cpp Rgemv_gmp_kernel_01)
add_kernel_variants(gmp/02_Rgemv Rgemv_gmp_kernel_02.cpp Rgemv_gmp_kernel_02)
add_kernel_variants(gmp/02_Rgemv Rgemv_gmp_kernel_03.cpp Rgemv_gmp_kernel_03)
add_kernel_variants(gmp/02_Rgemv Rgemv_gmp_kernel_04.cpp Rgemv_gmp_kernel_04)
add_kernel_variants(gmp/02_Rgemv Rgemv_gmp_kernel_openmp_01.cpp
    Rgemv_gmp_kernel_openmp_01)
add_kernel_variants(gmp/02_Rgemv Rgemv_gmp_kernel_openmp_02.cpp
    Rgemv_gmp_kernel_openmp_02)
add_kernel_variants(gmp/02_Rgemv Rgemv_gmp_kernel_openmp_03.cpp
    Rgemv_gmp_kernel_openmp_03)
add_kernel_variants(gmp/02_Rgemv Rgemv_gmp_kernel_openmp_04.cpp
    Rgemv_gmp_kernel_openmp_04)
add_kernel_variants(gmp/02_Rgemv Rgemv_gmp_kernel_openmp_05.cpp
    Rgemv_gmp_kernel_openmp_05)
add_kernel_variants(gmp/02_Rgemv Rgemv_gmp_kernel_openmp_06.cpp
    Rgemv_gmp_kernel_openmp_06)
add_kernel_variants(gmp/02_Rgemv Rgemv_gmp_kernel_openmp_07.cpp
    Rgemv_gmp_kernel_openmp_07)

add_native_benchmark(gmp/03_Rgemm Rgemm_gmp_C_native_01.cpp
    Rgemm_gmp_C_native_01)
add_native_benchmark(gmp/03_Rgemm Rgemm_gmp_C_native_02.cpp
    Rgemm_gmp_C_native_02)
add_native_benchmark(gmp/03_Rgemm Rgemm_gmp_C_native_03.cpp
    Rgemm_gmp_C_native_03)
add_native_benchmark(gmp/03_Rgemm Rgemm_gmp_C_native_04.cpp
    Rgemm_gmp_C_native_04)
add_native_benchmark(gmp/03_Rgemm Rgemm_gmp_C_native_05.cpp
    Rgemm_gmp_C_native_05)
add_native_benchmark(gmp/03_Rgemm Rgemm_gmp_C_native_06.cpp
    Rgemm_gmp_C_native_06)
add_native_benchmark(gmp/03_Rgemm Rgemm_gmp_C_native_openmp_01.cpp
    Rgemm_gmp_C_native_openmp_01)
add_native_benchmark(gmp/03_Rgemm Rgemm_gmp_C_native_openmp_02.cpp
    Rgemm_gmp_C_native_openmp_02)
add_native_benchmark(gmp/03_Rgemm Rgemm_gmp_C_native_openmp_03.cpp
    Rgemm_gmp_C_native_openmp_03)
add_native_benchmark(gmp/03_Rgemm Rgemm_gmp_C_native_openmp_04.cpp
    Rgemm_gmp_C_native_openmp_04)
add_native_benchmark(gmp/03_Rgemm Rgemm_gmp_C_native_openmp_05.cpp
    Rgemm_gmp_C_native_openmp_05)
add_native_benchmark(gmp/03_Rgemm Rgemm_gmp_C_native_openmp_06.cpp
    Rgemm_gmp_C_native_openmp_06)
add_kernel_variants(gmp/03_Rgemm Rgemm_gmp_kernel_01.cpp Rgemm_gmp_kernel_01)
add_kernel_variants(gmp/03_Rgemm Rgemm_gmp_kernel_02.cpp Rgemm_gmp_kernel_02)
add_kernel_variants(gmp/03_Rgemm Rgemm_gmp_kernel_03.cpp Rgemm_gmp_kernel_03)
add_kernel_variants(gmp/03_Rgemm Rgemm_gmp_kernel_04.cpp Rgemm_gmp_kernel_04)
add_kernel_variants(gmp/03_Rgemm Rgemm_gmp_kernel_05.cpp Rgemm_gmp_kernel_05)
add_kernel_variants(gmp/03_Rgemm Rgemm_gmp_kernel_06.cpp Rgemm_gmp_kernel_06)
add_kernel_variants(gmp/03_Rgemm Rgemm_gmp_kernel_openmp_01.cpp
                    Rgemm_gmp_kernel_openmp_01)
add_kernel_variants(gmp/03_Rgemm Rgemm_gmp_kernel_openmp_02.cpp
                    Rgemm_gmp_kernel_openmp_02)
add_kernel_variants(gmp/03_Rgemm Rgemm_gmp_kernel_openmp_03.cpp
                    Rgemm_gmp_kernel_openmp_03)
add_kernel_variants(gmp/03_Rgemm Rgemm_gmp_kernel_openmp_04.cpp
                    Rgemm_gmp_kernel_openmp_04)
add_kernel_variants(gmp/03_Rgemm Rgemm_gmp_kernel_openmp_05.cpp
                    Rgemm_gmp_kernel_openmp_05)
add_kernel_variants(gmp/03_Rgemm Rgemm_gmp_kernel_openmp_06.cpp
                    Rgemm_gmp_kernel_openmp_06)

if(Python3_Interpreter_FOUND
   AND GMPFRXX_MKII_OBJDUMP_EXECUTABLE
   AND CMAKE_SYSTEM_NAME STREQUAL "Linux"
   AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
    add_test(
        NAME benchmark_disasm_equivalence
        COMMAND
            "${Python3_EXECUTABLE}"
            "${CMAKE_CURRENT_SOURCE_DIR}/check_disasm_equivalence.py"
            --build-dir "${CMAKE_CURRENT_BINARY_DIR}"
            --objdump "${GMPFRXX_MKII_OBJDUMP_EXECUTABLE}"
    )
    set_tests_properties(benchmark_disasm_equivalence PROPERTIES
        LABELS "benchmark;disasm")
endif()
