# Option MAXBUTWIDTH

if (COMPILER_SUPPORTS_SVE)
  set(SLEEFDFT_MAXBUTWIDTH 6 CACHE STRING "Log_2 (Maximum butterfly length) of butterflies")
else()
  set(SLEEFDFT_MAXBUTWIDTH 4 CACHE STRING "Log_2 (Maximum butterfly length) of butterflies")
endif()

if (SLEEFDFT_MAXBUTWIDTH GREATER 7)
  message(FATAL_ERROR "SLEEFDFT_MAXBUTWIDTH has to be smaller than 8." )
endif()

# Option

option(SLEEFDFT_ENABLE_STREAM "Streaming instructions are utilized in DFT." OFF)
option(SLEEFDFT_ENABLE_LONGDOUBLE "Long double routines will be compiled in." OFF)
option(SLEEFDFT_ENABLE_QUAD "Quad precision routines will be compiled in." OFF)

# Compiler properties

set(CMAKE_C_FLAGS ORG_CMAKE_C_FLAGS)
string(CONCAT CMAKE_C_FLAGS ${DFT_C_FLAGS})
set(COMMON_TARGET_PROPERTIES 
  C_STANDARD 99                  # -std=gnu99
  )

if (BUILD_SHARED_LIBS)
  list(APPEND COMMON_TARGET_PROPERTIES POSITION_INDEPENDENT_CODE ON)   # -fPIC
endif()

set(COMMON_TARGET_DEFINITIONS ${COMMON_TARGET_DEFINITIONS} MAXBUTWIDTH=${SLEEFDFT_MAXBUTWIDTH})

if (SLEEFDFT_ENABLE_STREAM)
  set(COMMON_TARGET_DEFINITIONS ${COMMON_TARGET_DEFINITIONS} ENABLE_STREAM=1)
else()
  set(COMMON_TARGET_DEFINITIONS ${COMMON_TARGET_DEFINITIONS} ENABLE_STREAM=0)
endif()

if (COMPILER_SUPPORTS_FLOAT128)
  set(COMMON_TARGET_DEFINITIONS ${COMMON_TARGET_DEFINITIONS} ENABLEFLOAT128)
endif(COMPILER_SUPPORTS_FLOAT128)

if (COMPILER_SUPPORTS_LONG_DOUBLE)
  set(COMMON_TARGET_DEFINITIONS ${COMMON_TARGET_DEFINITIONS} ENABLE_LONGDOUBLE)
endif (COMPILER_SUPPORTS_LONG_DOUBLE)

if(COMPILER_SUPPORTS_OPENMP)
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
endif(COMPILER_SUPPORTS_OPENMP)


# Include directories

include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${PROJECT_BINARY_DIR}/include)
include_directories(${CMAKE_CURRENT_BINARY_DIR})

# Constants definition

set(LISTSHORTTYPENAME "dp" "sp" "ld" "qp")
set(LISTLONGTYPENAME "double" "float" "longdouble" "Sleef_quad")
set(LISTTYPEID "1" "2" "3" "4")

set(MACRODEF_vecextdp BASETYPEID=1 ENABLE_VECEXT CONFIG=1)
set(CFLAGS_vecextdp ${FLAGS_ENABLE_VECEXT})
set(MACRODEF_vecextsp BASETYPEID=2 ENABLE_VECEXT CONFIG=1)
set(CFLAGS_vecextsp ${FLAGS_ENABLE_VECEXT})
set(MACRODEF_vecextld BASETYPEID=3 ENABLE_VECEXT CONFIG=1)
set(CFLAGS_vecextld ${FLAGS_ENABLE_VECEXT})
set(MACRODEF_vecextqp BASETYPEID=4 ENABLE_VECEXT CONFIG=1)
set(CFLAGS_vecextqp ${FLAGS_ENABLE_VECEXT})
set(MACRODEF_purecdp BASETYPEID=1 ENABLE_PUREC CONFIG=1)
set(CFLAGS_purecdp ${FLAGS_ENABLE_PUREC})
set(MACRODEF_purecsp BASETYPEID=2 ENABLE_PUREC CONFIG=1)
set(CFLAGS_purecsp ${FLAGS_ENABLE_PUREC})
set(MACRODEF_purecld BASETYPEID=3 ENABLE_PUREC CONFIG=1)
set(CFLAGS_purecld ${FLAGS_ENABLE_PUREC})
set(MACRODEF_purecqp BASETYPEID=4 ENABLE_PUREC CONFIG=1)
set(CFLAGS_purecqp ${FLAGS_ENABLE_PUREC})
set(MACRODEF_sse2dp BASETYPEID=1 ENABLE_SSE2 CONFIG=4)
set(CFLAGS_sse2dp ${FLAGS_ENABLE_SSE4})
set(MACRODEF_sse2sp BASETYPEID=2 ENABLE_SSE2 CONFIG=4)
set(CFLAGS_sse2sp ${FLAGS_ENABLE_SSE4})
set(MACRODEF_avxdp BASETYPEID=1 ENABLE_AVX CONFIG=1)
set(CFLAGS_avxdp ${FLAGS_ENABLE_AVX})
set(MACRODEF_avxsp BASETYPEID=2 ENABLE_AVX CONFIG=1)
set(CFLAGS_avxsp ${FLAGS_ENABLE_AVX})
set(MACRODEF_avx2dp BASETYPEID=1 ENABLE_AVX2 CONFIG=1)
set(CFLAGS_avx2dp ${FLAGS_ENABLE_AVX2})
set(MACRODEF_avx2sp BASETYPEID=2 ENABLE_AVX2 CONFIG=1)
set(CFLAGS_avx2sp ${FLAGS_ENABLE_AVX2})
set(MACRODEF_avx512fdp BASETYPEID=1 ENABLE_AVX512F CONFIG=1)
set(CFLAGS_avx512fdp ${FLAGS_ENABLE_AVX512F})
set(MACRODEF_avx512fsp BASETYPEID=2 ENABLE_AVX512F CONFIG=1)
set(CFLAGS_avx512fsp ${FLAGS_ENABLE_AVX512F})
set(MACRODEF_advsimddp BASETYPEID=1 ENABLE_ADVSIMD CONFIG=1)
set(CFLAGS_advsimddp ${FLAGS_ENABLE_ADVSIMD})
set(MACRODEF_advsimdsp BASETYPEID=2 ENABLE_ADVSIMD CONFIG=1)
set(CFLAGS_advsimdsp ${FLAGS_ENABLE_ADVSIMD})
set(MACRODEF_neon32sp BASETYPEID=2 ENABLE_NEON32 CONFIG=1)
set(CFLAGS_neon32sp ${FLAGS_ENABLE_NEON32})
set(MACRODEF_sve256dp BASETYPEID=1 ENABLE_SVE CONFIG=8)
set(CFLAGS_sve256dp ${FLAGS_ENABLE_SVE})
set(MACRODEF_sve256sp BASETYPEID=2 ENABLE_SVE CONFIG=8)
set(CFLAGS_sve256sp ${FLAGS_ENABLE_SVE})
set(MACRODEF_sve512dp BASETYPEID=1 ENABLE_SVE CONFIG=9)
set(CFLAGS_sve512dp ${FLAGS_ENABLE_SVE})
set(MACRODEF_sve512sp BASETYPEID=2 ENABLE_SVE CONFIG=9)
set(CFLAGS_sve512sp ${FLAGS_ENABLE_SVE})
set(MACRODEF_sve1024dp BASETYPEID=1 ENABLE_SVE CONFIG=10)
set(CFLAGS_sve1024dp ${FLAGS_ENABLE_SVE})
set(MACRODEF_sve1024sp BASETYPEID=2 ENABLE_SVE CONFIG=10)
set(CFLAGS_sve1024sp ${FLAGS_ENABLE_SVE})
set(MACRODEF_sve2048dp BASETYPEID=1 ENABLE_SVE CONFIG=11)
set(CFLAGS_sve2048dp ${FLAGS_ENABLE_SVE})
set(MACRODEF_sve2048sp BASETYPEID=2 ENABLE_SVE CONFIG=11)
set(CFLAGS_sve2048sp ${FLAGS_ENABLE_SVE})
set(MACRODEF_vsxdp BASETYPEID=1 ENABLE_VSX CONFIG=1)
set(CFLAGS_vsxdp ${FLAGS_ENABLE_VSX})
set(MACRODEF_vsxsp BASETYPEID=2 ENABLE_VSX CONFIG=1)
set(CFLAGS_vsxsp ${FLAGS_ENABLE_VSX})

# List all available scalar data types

set(ISALIST_SP purecsp)
set(ISALIST_DP purecdp)

set(LIST_SUPPORTED_FPTYPE 0 1)
if(CMAKE_C_COMPILER_ID MATCHES "(GNU|Clang)")
  set(ISALIST_SP vecextsp)
  set(ISALIST_DP vecextdp)
endif(CMAKE_C_COMPILER_ID MATCHES "(GNU|Clang)")

if (COMPILER_SUPPORTS_LONG_DOUBLE AND SLEEFDFT_ENABLE_LONGDOUBLE)
  set(LIST_SUPPORTED_FPTYPE ${LIST_SUPPORTED_FPTYPE} 2)
  set(ISALIST_QP purecld)
  if(CMAKE_C_COMPILER_ID MATCHES "(GNU|Clang)")
    set(ISALIST_LD vecextld)
  endif(CMAKE_C_COMPILER_ID MATCHES "(GNU|Clang)")
endif(COMPILER_SUPPORTS_LONG_DOUBLE AND SLEEFDFT_ENABLE_LONGDOUBLE)

if (COMPILER_SUPPORTS_FLOAT128 AND SLEEFDFT_ENABLE_QUAD)
  set(LIST_SUPPORTED_FPTYPE ${LIST_SUPPORTED_FPTYPE} 3)
  set(ISALIST_QP purecqp)
  if(CMAKE_C_COMPILER_ID MATCHES "(GNU|Clang)")
    set(ISALIST_QP vecextqp)
  endif(CMAKE_C_COMPILER_ID MATCHES "(GNU|Clang)")
endif(COMPILER_SUPPORTS_FLOAT128 AND SLEEFDFT_ENABLE_QUAD)

# List all available vector data types

if (COMPILER_SUPPORTS_SSE4)
  set(ISALIST_SP ${ISALIST_SP} sse2sp)
  set(ISALIST_DP ${ISALIST_DP} sse2dp)
endif(COMPILER_SUPPORTS_SSE4)

if (COMPILER_SUPPORTS_AVX)
  set(ISALIST_SP ${ISALIST_SP} avxsp)
  set(ISALIST_DP ${ISALIST_DP} avxdp)
endif(COMPILER_SUPPORTS_AVX)

if (COMPILER_SUPPORTS_AVX2)
  set(ISALIST_SP ${ISALIST_SP} avx2sp)
  set(ISALIST_DP ${ISALIST_DP} avx2dp)
endif(COMPILER_SUPPORTS_AVX2)

if (COMPILER_SUPPORTS_AVX512F)
  set(ISALIST_SP ${ISALIST_SP} avx512fsp)
  set(ISALIST_DP ${ISALIST_DP} avx512fdp)
endif(COMPILER_SUPPORTS_AVX512F)

if (COMPILER_SUPPORTS_ADVSIMD)
  set(ISALIST_SP ${ISALIST_SP} advsimdsp)
  set(ISALIST_DP ${ISALIST_DP} advsimddp)
endif(COMPILER_SUPPORTS_ADVSIMD)

if (COMPILER_SUPPORTS_SVE)
  set(ISALIST_SP ${ISALIST_SP} sve256sp sve512sp sve1024sp sve2048sp)
  set(ISALIST_DP ${ISALIST_DP} sve256dp sve512dp sve1024dp sve2048dp)
endif(COMPILER_SUPPORTS_SVE)

if (COMPILER_SUPPORTS_NEON32)
  set(ISALIST_SP ${ISALIST_SP} neon32sp)
endif(COMPILER_SUPPORTS_NEON32)

if (COMPILER_SUPPORTS_VSX)
  set(ISALIST_SP ${ISALIST_SP} vsxsp)
  set(ISALIST_DP ${ISALIST_DP} vsxdp)
endif(COMPILER_SUPPORTS_VSX)

if(SLEEFDFT_ENABLE_STREAM)
  set(NLIST 0 1 2 3)
else()
  set(NLIST 0 2)
endif()

# Target mkunroll

set(TARGET_MKUNROLL "mkunroll")
add_host_executable(${TARGET_MKUNROLL} mkunroll.c)
set_target_properties(${TARGET_MKUNROLL} PROPERTIES ${COMMON_TARGET_PROPERTIES})
if (NOT CMAKE_CROSSCOMPILING)
  target_compile_definitions(${TARGET_MKUNROLL} PRIVATE ${COMMON_TARGET_DEFINITIONS})
endif()

# Target mkdispatch

set(TARGET_MKDISPATCH "mkdispatch")
add_host_executable(${TARGET_MKDISPATCH} mkdispatch.c)
set_target_properties(${TARGET_MKDISPATCH} PROPERTIES ${COMMON_TARGET_PROPERTIES})
if (NOT CMAKE_CROSSCOMPILING)
  target_compile_definitions(${TARGET_MKDISPATCH} PRIVATE ${COMMON_TARGET_DEFINITIONS})
endif()

# Target dispatchparam.h

add_custom_command(OUTPUT dispatchparam.h
  COMMENT "Generating dispatchparam.h"
  COMMAND $<TARGET_FILE:${TARGET_MKDISPATCH}> paramonly ${SLEEFDFT_MAXBUTWIDTH} ${ISALIST_DP} > ${CMAKE_CURRENT_BINARY_DIR}/dispatchparam.h
  DEPENDS ${TARGET_MKDISPATCH}
  )
add_custom_target(dispatchparam.h_generated SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dispatchparam.h)

# Target dispatch*.h

foreach(T ${LIST_SUPPORTED_FPTYPE})
  list(GET LISTSHORTTYPENAME ${T} ST)                       # ST is "dp", for example
  string(TOUPPER ${ST} CST)                                 # CST is "DP"
  list(GET LISTLONGTYPENAME ${T} LT)                        # LT is "double"
  list(GET LISTTYPEID ${T} ID)                              # ID is 1
  
  string(CONCAT S "dispatch" ${ST} ".h")                    # S is dispatchdp.h
  add_custom_command(OUTPUT ${S}
    COMMENT "Generating ${S}"
    COMMAND $<TARGET_FILE:${TARGET_MKDISPATCH}> ${LT} ${SLEEFDFT_MAXBUTWIDTH} ${ISALIST_${CST}} > ${S}
    DEPENDS ${TARGET_MKDISPATCH}
    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
    )

  string(CONCAT G ${S} "_generated")                        # G is dispatchdp.h_generated
  add_custom_target(${G} SOURCES ${S})
endforeach()

# Target dftcommon.o

add_library(dftcommon_obj OBJECT dftcommon.c dftcommon.h ${CMAKE_CURRENT_BINARY_DIR}/dispatchparam.h ${CMAKE_BINARY_DIR}/include/sleef.h)
add_dependencies(dftcommon_obj ${TARGET_HEADERS} dispatchparam.h_generated)
set_source_files_properties(${CMAKE_BINARY_DIR}/include/sleef.h PROPERTIES GENERATED TRUE)
set_target_properties(dftcommon_obj PROPERTIES ${COMMON_TARGET_PROPERTIES})
target_compile_definitions(dftcommon_obj PRIVATE ${COMMON_TARGET_DEFINITIONS})

# Target dft*.o

foreach(T ${LIST_SUPPORTED_FPTYPE})
  list(GET LISTSHORTTYPENAME ${T} ST)                       # ST is "dp", for example
  
  string(CONCAT G "dft" ${ST} "_obj")                       # G is "dftdp_obj"
  string(CONCAT S "dispatch" ${ST} ".h")                    # S is "dispatchdp.h"
  add_library(${G} OBJECT dft.c dftcommon.h ${S})
  string(CONCAT SG ${S} "_generated")                       # SG is "dispatchdp.h_generated"
  add_dependencies(${G} ${SG} ${TARGET_HEADERS})
  set_target_properties(${G} PROPERTIES ${COMMON_TARGET_PROPERTIES})
  list(GET LISTTYPEID ${T} ID)                              # ID is 1
  target_compile_definitions(${G} PRIVATE BASETYPEID=${ID} ${COMMON_TARGET_DEFINITIONS})
endforeach()

# Copy unroll0.org to ${CMAKE_CURRENT_BINARY_DIR}

add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/unroll0.org
  COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/unroll0.org ${CMAKE_CURRENT_BINARY_DIR}
  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/unroll0.org)
add_custom_target(unroll0.org.copied DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/unroll0.org)

# Target unroll*.c

foreach(T ${LIST_SUPPORTED_FPTYPE})
  list(GET LISTSHORTTYPENAME ${T} ST)                       # ST is "dp", for example
  string(TOUPPER ${ST} CST)                                 # CST is "DP"
  list(GET LISTLONGTYPENAME ${T} LT)                        # LT is "double"

  foreach(E ${ISALIST_${CST}})                              # E is "sse2dp"
    foreach(N ${NLIST})
      string(CONCAT UC unroll_ ${N} _ ${E} ".c")            # UC is "unroll_0_sse2dp.c"
      set(UNROLL_TARGET_${CST} ${UNROLL_TARGET_${CST}} ${UC})
    endforeach()
  endforeach()
  message(STATUS "Unroll target for ${CST} : ${UNROLL_TARGET_${CST}}")

  if(UNROLL_TARGET_${CST})
    add_custom_command(OUTPUT ${UNROLL_TARGET_${CST}}
      COMMENT "Generating ${UNROLL_TARGET_${CST}}"
      COMMAND $<TARGET_FILE:${TARGET_MKUNROLL}> ${LT} ${ISALIST_${CST}}
      WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
      DEPENDS ${TARGET_MKUNROLL} unroll0.org.copied
      )
    add_custom_target(unroll_target_${ST} DEPENDS ${UNROLL_TARGET_${CST}})
  endif()
endforeach()

# Target unroll*.o

foreach(T ${LIST_SUPPORTED_FPTYPE})
  list(GET LISTSHORTTYPENAME ${T} ST)                       # ST is "dp", for example
  string(TOUPPER ${ST} CST)                                 # CST is "DP"
  list(GET LISTLONGTYPENAME ${T} LT)                        # LT is "double"

  foreach(E ${ISALIST_${CST}})                              # E is "sse2dp"
    foreach(N ${NLIST})
      string(CONCAT U unroll_ ${N} _ ${E})                  # U is "unroll_0_sse2dp"
      string(CONCAT UG ${U} "_obj")                         # UG is "unroll_0_sse2dp_obj"
      string(CONCAT UC ${U} ".c")                           # UC is "unroll_0_sse2dp.c"
      add_library(${UG} OBJECT ${UC})
      set_target_properties(${UG} PROPERTIES ${COMMON_TARGET_PROPERTIES})
      target_include_directories(${UG} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
      target_compile_definitions(${UG} PRIVATE ${COMMON_TARGET_DEFINITIONS} ${MACRODEF_${E}})
      target_compile_options(${UG} PRIVATE ${CFLAGS_${E}})
      add_dependencies(${UG} ${TARGET_HEADERS} unroll_target_${ST})
    endforeach()
  endforeach()
endforeach()

# Target libdft

add_library(${TARGET_LIBDFT} $<TARGET_OBJECTS:dftcommon_obj> $<TARGET_OBJECTS:${TARGET_LIBARRAYMAP_OBJ}>)
target_link_libraries(${TARGET_LIBDFT} ${TARGET_LIBSLEEF} ${LIBM})

foreach(T ${LIST_SUPPORTED_FPTYPE})
  list(GET LISTSHORTTYPENAME ${T} ST)                       # ST is "dp", for example
  
  string(CONCAT G "dft" ${ST} "_obj")                       # G is "dftdp_obj"
  target_sources(${TARGET_LIBDFT} PRIVATE $<TARGET_OBJECTS:${G}>)
endforeach()

foreach(T ${LIST_SUPPORTED_FPTYPE})
  list(GET LISTSHORTTYPENAME ${T} ST)                       # ST is "dp", for example
  string(TOUPPER ${ST} CST)                                 # CST is "DP"

  foreach(E ${ISALIST_${CST}})                              # E is "sse2dp"
    foreach(N ${NLIST})
      string(CONCAT UG unroll_ ${N} _ ${E} "_obj")          # U is "unroll_0_sse2dp_obj"
      target_sources(${TARGET_LIBDFT} PRIVATE $<TARGET_OBJECTS:${UG}>)
    endforeach()
  endforeach()
endforeach()

set_target_properties(${TARGET_LIBDFT} PROPERTIES
  VERSION ${SLEEF_VERSION}
  SOVERSION ${SLEEF_SOVERSION}
  ${COMMON_TARGET_PROPERTIES}
  ) 

# Install

install(FILES ${PROJECT_SOURCE_DIR}/include/sleefdft.h DESTINATION include)
install(TARGETS ${TARGET_LIBDFT} DESTINATION lib)
