# ==============================================================================
# sangi library targets
# ==============================================================================

set(LIB23_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include")
set(LIB23_SRC_DIR "${CMAKE_SOURCE_DIR}/src")

# ==============================================================================
# sangi_int — arbitrary-precision integer (Int)
# ==============================================================================
add_library(sangi_int STATIC
    ${LIB23_SRC_DIR}/math/core/mp/Int/IntBase.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Int/IntCombinatorics.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Int/IntFactorTable.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Int/IntGCD.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Int/IntIO.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Int/IntModular.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Int/IntOperators.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Int/IntOps.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Int/IntPrime.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Int/IntAPRCL.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Int/IntFactorization.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Int/IntSpecialStates.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Int/IntSqrt.cpp
)
target_include_directories(sangi_int PUBLIC ${LIB23_INCLUDE_DIR})
# GCC/Clang: pthread (IntGCD.cpp, ThreadPool.hpp use std::thread)
if(NOT MSVC)
    find_package(Threads REQUIRED)
    target_link_libraries(sangi_int PUBLIC Threads::Threads)
endif()

# MASM assembly optimization (MSVC x64 only)
# addmul_1/mul_1 using BMI2 (MULX) + ADX (ADCX/ADOX)
if(MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT CMAKE_SYSTEM_PROCESSOR MATCHES "ARM")
    enable_language(ASM_MASM)
    # Assemble MASM assembly individually via a custom command
    # (because CMake's Visual Studio generator also passes C++ flags to MASM)
    set(SANGI_ASM_SRC ${LIB23_SRC_DIR}/math/core/mp/Int/mpn_x64.asm)
    set(SANGI_ASM_OBJ ${CMAKE_CURRENT_BINARY_DIR}/mpn_x64.obj)
    add_custom_command(
        OUTPUT ${SANGI_ASM_OBJ}
        COMMAND ml64 /c /nologo /Fo${SANGI_ASM_OBJ} ${SANGI_ASM_SRC}
        DEPENDS ${SANGI_ASM_SRC}
        COMMENT "Assembling mpn_x64.asm (MASM)"
        VERBATIM
    )
    # Montgomery CIOS multiplication / REDC
    set(SANGI_MONT_ASM_SRC ${LIB23_SRC_DIR}/math/core/mp/Int/mpn_x64_mont.asm)
    set(SANGI_MONT_ASM_OBJ ${CMAKE_CURRENT_BINARY_DIR}/mpn_x64_mont.obj)
    add_custom_command(
        OUTPUT ${SANGI_MONT_ASM_OBJ}
        COMMAND ml64 /c /nologo /Fo${SANGI_MONT_ASM_OBJ} ${SANGI_MONT_ASM_SRC}
        DEPENDS ${SANGI_MONT_ASM_SRC}
        COMMENT "Assembling mpn_x64_mont.asm (MASM)"
        VERBATIM
    )
    # add_n/sub_n small-size specialization (n=1..4)
    set(SANGI_ADD_ASM_SRC ${LIB23_SRC_DIR}/math/core/mp/Int/mpn_x64_add.asm)
    set(SANGI_ADD_ASM_OBJ ${CMAKE_CURRENT_BINARY_DIR}/mpn_x64_add.obj)
    add_custom_command(
        OUTPUT ${SANGI_ADD_ASM_OBJ}
        COMMAND ml64 /c /nologo /Fo${SANGI_ADD_ASM_OBJ} ${SANGI_ADD_ASM_SRC}
        DEPENDS ${SANGI_ADD_ASM_SRC}
        COMMENT "Assembling mpn_x64_add.asm (MASM)"
        VERBATIM
    )
    # mul_basecase small-size specialization (n×n, n=1..4)
    set(SANGI_MUL_ASM_SRC ${LIB23_SRC_DIR}/math/core/mp/Int/mpn_x64_mul.asm)
    set(SANGI_MUL_ASM_OBJ ${CMAKE_CURRENT_BINARY_DIR}/mpn_x64_mul.obj)
    add_custom_command(
        OUTPUT ${SANGI_MUL_ASM_OBJ}
        COMMAND ml64 /c /nologo /Fo${SANGI_MUL_ASM_OBJ} ${SANGI_MUL_ASM_SRC}
        DEPENDS ${SANGI_MUL_ASM_SRC}
        COMMENT "Assembling mpn_x64_mul.asm (MASM)"
        VERBATIM
    )
    # div_basecase 3-by-2 loop fusion
    set(SANGI_DIV_ASM_SRC ${LIB23_SRC_DIR}/math/core/mp/Int/mpn_x64_div.asm)
    set(SANGI_DIV_ASM_OBJ ${CMAKE_CURRENT_BINARY_DIR}/mpn_x64_div.obj)
    add_custom_command(
        OUTPUT ${SANGI_DIV_ASM_OBJ}
        COMMAND ml64 /c /nologo /Fo${SANGI_DIV_ASM_OBJ} ${SANGI_DIV_ASM_SRC}
        DEPENDS ${SANGI_DIV_ASM_SRC}
        COMMENT "Assembling mpn_x64_div.asm (MASM)"
        VERBATIM
    )
    # 1-limb mod (preinv) — used by IntPrime::isDivisibleBySmallPrime trial division
    set(SANGI_MOD1_ASM_SRC ${LIB23_SRC_DIR}/math/core/mp/Int/mpn_x64_mod1.asm)
    set(SANGI_MOD1_ASM_OBJ ${CMAKE_CURRENT_BINARY_DIR}/mpn_x64_mod1.obj)
    add_custom_command(
        OUTPUT ${SANGI_MOD1_ASM_OBJ}
        COMMAND ml64 /c /nologo /Fo${SANGI_MOD1_ASM_OBJ} ${SANGI_MOD1_ASM_SRC}
        DEPENDS ${SANGI_MOD1_ASM_SRC}
        COMMENT "Assembling mpn_x64_mod1.asm (MASM)"
        VERBATIM
    )
    target_sources(sangi_int PRIVATE ${SANGI_ASM_OBJ} ${SANGI_MONT_ASM_OBJ} ${SANGI_ADD_ASM_OBJ} ${SANGI_MUL_ASM_OBJ} ${SANGI_DIV_ASM_OBJ} ${SANGI_MOD1_ASM_OBJ})
    set_source_files_properties(${SANGI_ASM_OBJ} ${SANGI_MONT_ASM_OBJ} ${SANGI_ADD_ASM_OBJ} ${SANGI_MUL_ASM_OBJ} ${SANGI_DIV_ASM_OBJ} ${SANGI_MOD1_ASM_OBJ} PROPERTIES
        EXTERNAL_OBJECT TRUE
        GENERATED TRUE
    )
    # PUBLIC: all linked TUs see the same inline function definition (ODR-compliant)
    target_compile_definitions(sangi_int PUBLIC SANGI_INT_HAS_ASM)
    message(STATUS "MASM assembly optimization enabled for sangi_int (BMI2+ADX)")
endif()

# ==============================================================================
# sangi_float — arbitrary-precision floating point (Float)
# ==============================================================================
add_library(sangi_float STATIC
    ${LIB23_SRC_DIR}/math/core/mp/Float/Float.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Float/FloatMath.cpp
)
target_include_directories(sangi_float PUBLIC ${LIB23_INCLUDE_DIR})
target_link_libraries(sangi_float PUBLIC sangi_int)

# ==============================================================================
# sangi_rational — rational number (Rational)
# ==============================================================================
add_library(sangi_rational STATIC
    ${LIB23_SRC_DIR}/math/core/mp/Rational/Rational.cpp
    ${LIB23_SRC_DIR}/math/core/mp/Rational/RationalConstants.cpp
)
target_include_directories(sangi_rational PUBLIC ${LIB23_INCLUDE_DIR})
target_link_libraries(sangi_rational PUBLIC sangi_int sangi_float)

# ==============================================================================
# sangi_fft — FFT explicit instantiation (FFT/RealFFT/FFTEngine for float/double)
# ==============================================================================
add_library(sangi_fft STATIC
    ${LIB23_SRC_DIR}/math/fft/fft.cpp
    ${LIB23_SRC_DIR}/math/fft/fft_batch_stub.cpp
)
target_include_directories(sangi_fft PUBLIC ${LIB23_INCLUDE_DIR})
if(MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT CMAKE_SYSTEM_PROCESSOR MATCHES "ARM")
    set(SANGI_FFT_ASM_SRC ${LIB23_SRC_DIR}/math/fft/fft_batch_avx2.asm)
    set(SANGI_FFT_ASM_OBJ ${CMAKE_CURRENT_BINARY_DIR}/fft_batch_avx2.obj)
    add_custom_command(
        OUTPUT ${SANGI_FFT_ASM_OBJ}
        COMMAND ml64 /c /nologo /Fo${SANGI_FFT_ASM_OBJ} ${SANGI_FFT_ASM_SRC}
        DEPENDS ${SANGI_FFT_ASM_SRC}
        COMMENT "Assembling fft_batch_avx2.asm (MASM)"
        VERBATIM
    )
    target_sources(sangi_fft PRIVATE ${SANGI_FFT_ASM_OBJ})
    set_source_files_properties(${SANGI_FFT_ASM_OBJ} PROPERTIES EXTERNAL_OBJECT TRUE GENERATED TRUE)
    target_compile_definitions(sangi_fft PUBLIC SANGI_FFT_HAS_ASM)
endif()

# sangi_int -> sangi_fft: IntMultiplication.hpp uses FFTEngine<double>
target_link_libraries(sangi_int PUBLIC sangi_fft)

# ==============================================================================
# sangi_matrix — Matrix<T>/Vector<T> explicit instantiation (float, double)
# ==============================================================================
add_library(sangi_matrix STATIC
    ${LIB23_SRC_DIR}/math/core/matrix.cpp
    ${LIB23_SRC_DIR}/math/core/vector.cpp
)
target_include_directories(sangi_matrix PUBLIC ${LIB23_INCLUDE_DIR})

# ==============================================================================
# Solution folder
# ==============================================================================
set_target_properties(sangi_int sangi_float sangi_rational sangi_fft sangi_matrix
    PROPERTIES FOLDER "Libraries")

