CMAKE_MINIMUM_REQUIRED(VERSION 3.0)

FIND_PROGRAM(CMAKE_C_COMPILER NAMES $ENV{CC} gcc PATHS ENV PATH NO_DEFAULT_PATH)
FIND_PROGRAM(CMAKE_CXX_COMPILER NAMES $ENV{CXX} g++ PATHS ENV PATH NO_DEFAULT_PATH)

OPTION(USE_CORE "Core" ON)
OPTION(USE_DMRG "DMRG" ON)
OPTION(USE_SCI "SCI" OFF)
OPTION(USE_BIG_SITE "BigSite" ON)
OPTION(USE_SP_DMRG "StochasticPDMRG" ON)
OPTION(USE_IC "InternalContraction" ON)
OPTION(USE_KSYMM "KSymmetry" OFF)
OPTION(USE_COMPLEX "ComplexNumber" OFF)
OPTION(USE_SG "SpinGeneral" OFF)

# Project Name (must be python module name)

SET(PROJECT_NAME block2)
PROJECT(${PROJECT_NAME} VERSION 2.0)

# Check Python3 and Pybind11

SET(Python_ADDITIONAL_VERSIONS 3)
FIND_PACKAGE(PythonInterp)

IF (NOT PYTHONINTERP_FOUND)
    MESSAGE(FATAL_ERROR "Python3 not found.")
ENDIF()

FIND_PACKAGE(PythonLibs ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR})

EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "import distutils.sysconfig; \
        print(distutils.sysconfig.get_config_var('LIBDIR'))"
        OUTPUT_VARIABLE PYTHON_LIB_PATH OUTPUT_STRIP_TRAILING_WHITESPACE)
EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "import distutils.sysconfig; \
        print(distutils.sysconfig.get_config_var('INCLUDEDIR'))"
        OUTPUT_VARIABLE PYTHON_INCLUDE_PATH OUTPUT_STRIP_TRAILING_WHITESPACE)

IF (NOT PYTHONLIBS_FOUND)
    EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "import distutils.sysconfig; \
        import os; print(os.path.join(distutils.sysconfig.get_config_var('LIBDIR'), \
        distutils.sysconfig.get_config_var('LDLIBRARY')))"
        OUTPUT_VARIABLE PYTHON_LIBRARIES OUTPUT_STRIP_TRAILING_WHITESPACE)
    EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_inc; \
        print(get_python_inc())"
        OUTPUT_VARIABLE PYTHON_INCLUDE_DIRS OUTPUT_STRIP_TRAILING_WHITESPACE)
ENDIF()

EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "import importlib.machinery;print(importlib.machinery.EXTENSION_SUFFIXES[0])"
    OUTPUT_VARIABLE PYLIB_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE)
EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "import pybind11;print(pybind11.get_include())"
    OUTPUT_VARIABLE PYBIND_INCLUDE_DIRS OUTPUT_STRIP_TRAILING_WHITESPACE)

MESSAGE(STATUS "PROJECT_NAME = ${PROJECT_NAME}")
MESSAGE(STATUS "PYTHON_VERSION_MAJOR = ${PYTHON_VERSION_MAJOR}")
MESSAGE(STATUS "PYTHON_VERSION_MINOR = ${PYTHON_VERSION_MINOR}")
MESSAGE(STATUS "PYTHON_LIBRARIES = ${PYTHON_LIBRARIES}")
MESSAGE(STATUS "PYTHON_EXECUTABLE = ${PYTHON_EXECUTABLE}")
MESSAGE(STATUS "PYTHON_EXECUTABLE_HINT = ${PYTHON_EXECUTABLE_HINT}")
MESSAGE(STATUS "PYTHON_INCLUDE_DIRS = ${PYTHON_INCLUDE_DIRS}")
MESSAGE(STATUS "PYLIB_SUFFIX = ${PYLIB_SUFFIX}")
MESSAGE(STATUS "PYBIND_INCLUDE_DIRS = ${PYBIND_INCLUDE_DIRS}")

IF (${PYTHON_EXECUTABLE_HINT})
    IF (NOT (${PYTHON_EXECUTABLE_HINT} EQUAL ${PYTHON_EXECUTABLE}))
        MESSAGE(FATAL_ERROR "Python3 used by cmake (${PYTHON_EXECUTABLE}) does not match Python3 \
            used by setup.py (${PYTHON_EXECUTABLE_HINT})!")
    ENDIF()
ENDIF()

SET(CMAKE_CXX_STANDARD 11)
SET(CMAKE_CXX_STANDARD_REQUIRED True)

IF("${EXP_TMPL}" STREQUAL "NONE")
    SET(SRCS "")
    SET(PYBIND_SRCS "")
    SET(TMPL_FLAG "")
ELSE()
    FILE(GLOB_RECURSE SRCS src/instantiation/core/*.cpp)
    FILE(GLOB_RECURSE PYBIND_SRCS src/pybind/core/*.cpp)
    IF(${USE_KSYMM})
        FILE(GLOB_RECURSE SRCS_K src/instantiation/core_k/*.cpp)
        FILE(GLOB_RECURSE PYBIND_SRCS_K src/pybind/core_k/*.cpp)
        SET(SRCS ${SRCS_K} ${SRCS})
        SET(PYBIND_SRCS ${PYBIND_SRCS_K} ${PYBIND_SRCS})
    ENDIF()
    IF(${USE_SG})
        FILE(GLOB_RECURSE SRCS_G src/instantiation/core_g/*.cpp)
        FILE(GLOB_RECURSE PYBIND_SRCS_G src/pybind/core_g/*.cpp)
        SET(SRCS ${SRCS_G} ${SRCS})
        SET(PYBIND_SRCS ${PYBIND_SRCS_G} ${PYBIND_SRCS})
    ENDIF()
    IF(${USE_COMPLEX})
        FILE(GLOB_RECURSE SRCS_Z src/instantiation/core_z/*.cpp)
        FILE(GLOB_RECURSE PYBIND_SRCS_Z src/pybind/core_z/*.cpp)
        SET(SRCS ${SRCS_Z} ${SRCS})
        SET(PYBIND_SRCS ${PYBIND_SRCS_Z} ${PYBIND_SRCS})
    ENDIF()
    IF(${USE_COMPLEX} AND ${USE_KSYMM})
        FILE(GLOB_RECURSE SRCS_KZ src/instantiation/core_kz/*.cpp)
        FILE(GLOB_RECURSE PYBIND_SRCS_KZ src/pybind/core_kz/*.cpp)
        SET(SRCS ${SRCS_KZ} ${SRCS})
        SET(PYBIND_SRCS ${PYBIND_SRCS_KZ} ${PYBIND_SRCS})
    ENDIF()
    IF(${USE_COMPLEX} AND ${USE_SG})
        FILE(GLOB_RECURSE SRCS_GZ src/instantiation/core_gz/*.cpp)
        FILE(GLOB_RECURSE PYBIND_SRCS_GZ src/pybind/core_gz/*.cpp)
        SET(SRCS ${SRCS_GZ} ${SRCS})
        SET(PYBIND_SRCS ${PYBIND_SRCS_GZ} ${PYBIND_SRCS})
    ENDIF()
    SET(TMPL_FLAG "-D_EXPLICIT_TEMPLATE")
ENDIF()

IF (${LARGE_BOND})
    SET(BOND_FLAG "-D_LARGE_BOND")
ELSEIF (${SMALL_BOND})
    SET(BOND_FLAG "-D_SMALL_BOND")
ENDIF()

IF (${USE_CORE})
    SET(CORE_FLAG "-D_USE_CORE")
ELSE()
    SET(CORE_FLAG "")
ENDIF()

IF (${USE_DMRG})
    IF(NOT ("${EXP_TMPL}" STREQUAL "NONE"))
        FILE(GLOB_RECURSE DMRG_SRCS src/instantiation/dmrg/*.cpp)
        FILE(GLOB_RECURSE DMRG_PYBIND_SRCS src/pybind/dmrg/*.cpp)
        SET(SRCS ${DMRG_SRCS} ${SRCS})
        SET(PYBIND_SRCS ${DMRG_PYBIND_SRCS} ${PYBIND_SRCS})
        IF(${USE_KSYMM})
            FILE(GLOB_RECURSE DMRG_SRCS_K src/instantiation/dmrg_k/*.cpp)
            FILE(GLOB_RECURSE DMRG_PYBIND_SRCS_K src/pybind/dmrg_k/*.cpp)
            SET(SRCS ${DMRG_SRCS_K} ${SRCS})
            SET(PYBIND_SRCS ${DMRG_PYBIND_SRCS_K} ${PYBIND_SRCS})
        ENDIF()
        IF(${USE_SG})
            FILE(GLOB_RECURSE DMRG_SRCS_G src/instantiation/dmrg_g/*.cpp)
            FILE(GLOB_RECURSE DMRG_PYBIND_SRCS_G src/pybind/dmrg_g/*.cpp)
            SET(SRCS ${DMRG_SRCS_G} ${SRCS})
            SET(PYBIND_SRCS ${DMRG_PYBIND_SRCS_G} ${PYBIND_SRCS})
        ENDIF()
        IF(${USE_COMPLEX})
            FILE(GLOB_RECURSE DMRG_SRCS_Z src/instantiation/dmrg_z/*.cpp)
            FILE(GLOB_RECURSE DMRG_PYBIND_SRCS_Z src/pybind/dmrg_z/*.cpp)
            SET(SRCS ${DMRG_SRCS_Z} ${SRCS})
            SET(PYBIND_SRCS ${DMRG_PYBIND_SRCS_Z} ${PYBIND_SRCS})
        ENDIF()
        IF(${USE_COMPLEX} AND ${USE_KSYMM})
            FILE(GLOB_RECURSE DMRG_SRCS_KZ src/instantiation/dmrg_kz/*.cpp)
            FILE(GLOB_RECURSE DMRG_PYBIND_SRCS_KZ src/pybind/dmrg_kz/*.cpp)
            SET(SRCS ${DMRG_SRCS_KZ} ${SRCS})
            SET(PYBIND_SRCS ${DMRG_PYBIND_SRCS_KZ} ${PYBIND_SRCS})
        ENDIF()
        IF(${USE_COMPLEX} AND ${USE_SG})
            FILE(GLOB_RECURSE DMRG_SRCS_GZ src/instantiation/dmrg_gz/*.cpp)
            FILE(GLOB_RECURSE DMRG_PYBIND_SRCS_GZ src/pybind/dmrg_gz/*.cpp)
            SET(SRCS ${DMRG_SRCS_GZ} ${SRCS})
            SET(PYBIND_SRCS ${DMRG_PYBIND_SRCS_GZ} ${PYBIND_SRCS})
        ENDIF()
    ENDIF()
    SET(DMRG_FLAG "-D_USE_DMRG")
ELSE()
    SET(DMRG_FLAG "")
ENDIF()

IF (${USE_BIG_SITE})
    FILE(GLOB_RECURSE BIG_SITE_SRCS src/big_site/*.cpp)
    SET(SRCS ${BIG_SITE_SRCS} ${SRCS})
    SET(BIG_SITE_FLAG "-D_USE_BIG_SITE")
ELSE()
    SET(BIG_SITE_FLAG "")
ENDIF()

IF (${USE_SP_DMRG})
    SET(SP_DMRG_FLAG "-D_USE_SP_DMRG")
ELSE()
    SET(SP_DMRG_FLAG "")
ENDIF()

IF (${USE_IC})
    SET(IC_FLAG "-D_USE_IC")
ELSE()
    SET(IC_FLAG "")
ENDIF()

IF (${USE_KSYMM})
    SET(KSYMM_FLAG "-D_USE_KSYMM")
ELSE()
    SET(KSYMM_FLAG "")
ENDIF()

IF (${USE_SG})
    SET(SG_FLAG "-D_USE_SG")
ELSE()
    SET(SG_FLAG "")
ENDIF()

IF (${USE_COMPLEX})
    SET(COMPLEX_FLAG "-D_USE_COMPLEX")
ELSE()
    SET(COMPLEX_FLAG "")
ENDIF()

IF (${USE_SCI})
    IF (NOT ${USE_DMRG})
        MESSAGE(FATAL_ERROR "USE_DMRG must be ON for USE_SCI=ON.")
    ENDIF()
    SET(SCI_FLAG "-D_USE_SCI")
    SET(SCI_INCLUDE_DIR "")
    IF (${SCI_WRAPPER2})
        SET(SCI_FLAG ${SCI_FLAG} -D_SCI_WRAPPER2)
        FIND_PATH(SCI_INCLUDE_DIR NAMES pybind_sci.hpp HINTS $ENV{SCI})
        MESSAGE(STATUS "SCI_INCLUDE_DIR = ${SCI_INCLUDE_DIR}")
    ENDIF()
ELSE()
    SET(SCI_INCLUDE_DIR "")
    SET(SCI_FLAG "")
ENDIF()

MESSAGE(STATUS "BUILD_LIB = ${BUILD_LIB}")

IF (NOT(APPLE) AND NOT(WIN32))
    SET(NO_AS_NEEDED -Wl,--no-as-needed)
ELSEIF (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
    SET(XPREP -Xpreprocessor)
    INCLUDE_DIRECTORIES(/usr/local/include)
ENDIF()

SET(PTHREAD pthread)
CMAKE_POLICY(SET CMP0054 NEW) # quoted variable for STREQUAL

IF (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
    SET(OMP_FLAG -qopenmp)
ELSEIF (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
    SET(OMP_FLAG -openmp)
ELSE()
    SET(OMP_FLAG -fopenmp)
ENDIF()

IF (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
    SET(OMP_LIB_NAME "")
    SET(PTHREAD "")
    IF("${OMP_LIB}" STREQUAL "SEQ")
        SET(MKL_OMP_LIB_NAME mkl_sequential)
        SET(MKL_OMP_VALUE 0)
    ELSEIF ("${OMP_LIB}" STREQUAL "INTEL")
        SET(MKL_OMP_LIB_NAME mkl_intel_thread)
        SET(MKL_OMP_VALUE 2)
    ELSEIF ("${OMP_LIB}" STREQUAL "TBB")
        SET(MKL_OMP_LIB_NAME mkl_tbb_thread)
        SET(MKL_OMP_VALUE 3)
    ELSE()
        SET(OMP_LIB INTEL)
        SET(MKL_OMP_LIB_NAME mkl_intel_thread)
        FIND_LIBRARY(OMP_LIB_NAME NAMES libiomp5md PATHS $ENV{OMPROOT} $ENV{OMPROOT}/lib $ENV{OMPROOT}/lib/intel64_win)
        SET(MKL_OMP_VALUE 2)
    ENDIF()
ELSEIF("${OMP_LIB}" STREQUAL "SEQ")
    FIND_LIBRARY(OMP_LIB_NAME NAMES gomp PATHS /usr/local/lib /usr/lib64)
    IF(NOT OMP_LIB_NAME)
        SET(OMP_LIB_NAME "")
        SET(PTHREAD "")
    ENDIF()
    SET(MKL_OMP_LIB_NAME mkl_sequential)
    SET(MKL_OMP_VALUE 0)
ELSEIF ("${OMP_LIB}" STREQUAL "INTEL")
    FIND_LIBRARY(OMP_LIB_NAME NAMES iomp5 PATHS /usr/local/lib /usr/lib64)
    SET(MKL_OMP_LIB_NAME mkl_intel_thread)
    SET(MKL_OMP_VALUE 2)
ELSEIF ("${OMP_LIB}" STREQUAL "TBB")
    FIND_LIBRARY(OMP_LIB_NAME NAMES gomp PATHS /usr/local/lib /usr/lib64)
    IF(NOT OMP_LIB_NAME)
        SET(OMP_LIB_NAME "")
        SET(PTHREAD "")
    ENDIF()
    SET(MKL_OMP_LIB_NAME mkl_tbb_thread)
    SET(MKL_OMP_VALUE 3)
ELSEIF (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
    FIND_LIBRARY(OMP_LIB_NAME NAMES iomp5 PATHS /usr/local/lib /usr/lib64 ${PYTHON_LIB_PATH})
    SET(MKL_OMP_LIB_NAME mkl_intel_thread)
    SET(MKL_OMP_VALUE 2)
ELSEIF (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
    FIND_LIBRARY(OMP_LIB_NAME NAMES iomp5 PATHS /usr/local/lib /usr/lib64 ${PYTHON_LIB_PATH})
    SET(MKL_OMP_LIB_NAME mkl_intel_thread)
    SET(MKL_OMP_VALUE 2)
ELSE()
    SET(OMP_LIB GNU)
    FIND_LIBRARY(OMP_LIB_NAME NAMES gomp PATHS /usr/local/lib /usr/lib64 ${PYTHON_LIB_PATH})
    IF (NOT OMP_LIB_NAME)
        EXECUTE_PROCESS(
            COMMAND ${CMAKE_CXX_COMPILER} -print-search-dirs
            COMMAND grep "^lib" COMMAND awk -F "=" "{print $2}" COMMAND tr ":" ";"    
            OUTPUT_VARIABLE OMP_LIB_HINT OUTPUT_STRIP_TRAILING_WHITESPACE)
        FIND_LIBRARY(OMP_LIB_NAME NAMES gomp PATHS ${OMP_LIB_HINT})
    ENDIF()
    SET(MKL_OMP_LIB_NAME mkl_gnu_thread)
    SET(MKL_OMP_VALUE 1)
ENDIF()

IF (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
    SET(OPT_FLAG ${OMP_FLAG} -bigobj -MP)
ELSEIF("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
    SET(OPT_FLAG -O0 -g ${XPREP} ${OMP_FLAG} -fstack-check -fno-omit-frame-pointer -fno-optimize-sibling-calls -Werror -Werror=return-type)
ELSE()
    SET(OPT_FLAG -O3 -funroll-loops ${XPREP} ${OMP_FLAG} -Werror -Werror=return-type)
ENDIF()

IF (${USE_MKL})
    SET(USE_MKL_ANY ON)
ELSEIF(${USE_MKL64})
    SET(USE_MKL_ANY ON)
ELSE()
    SET(USE_MKL_ANY OFF)
ENDIF()

IF (${TBB})
    FIND_PATH(TBB_INCLUDE_DIR NAMES tbb/tbb.h
        HINTS $ENV{MKLROOT}/include /usr/local/include $ENV{TBBROOT}/include)
    FIND_LIBRARY(TBB_LIB NAMES tbb
        PATHS $ENV{MKLROOT}/lib $ENV{MKLROOT}/lib/intel64 /usr/local/lib $ENV{TBBROOT}/lib)
    FIND_LIBRARY(TBB_LIB_MAL NAMES tbbmalloc
        PATHS $ENV{MKLROOT}/lib $ENV{MKLROOT}/lib/intel64 /usr/local/lib $ENV{TBBROOT}/lib)
    FIND_LIBRARY(TBB_LIBS_MALP NAMES tbbmalloc_proxy
        PATHS $ENV{MKLROOT}/lib $ENV{MKLROOT}/lib/intel64 /usr/local/lib $ENV{TBBROOT}/lib)
    SET(TBB_LIBS ${TBB_LIB} ${TBB_LIB_MAL} ${TBB_LIBS_MALP})
    SET(TBB_FLAG "-D_HAS_TBB")
ENDIF()

IF (${USE_MKL_ANY})
    SET(CMAKE_FIND_LIBRARY_SUFFIXES_BKP ${CMAKE_FIND_LIBRARY_SUFFIXES})
    SET(CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_FIND_LIBRARY_SUFFIXES_BKP};.so.1;.1.dylib")
    FIND_PATH(MKL_INCLUDE_DIR NAMES mkl.h HINTS $ENV{MKLROOT}/include /usr/local/include ${PYTHON_INCLUDE_PATH})
    IF (${USE_MKL})
        FIND_LIBRARY(MKL_LIB_LP NAMES mkl_intel_lp64
            PATHS $ENV{MKLROOT}/lib $ENV{MKLROOT}/lib/intel64 /usr/local/lib ${PYTHON_LIB_PATH} NO_DEFAULT_PATH)
        SET(MKL_FLAG "-D_HAS_INTEL_MKL=${MKL_OMP_VALUE}")
    ELSE()
        FIND_LIBRARY(MKL_LIB_LP NAMES mkl_intel_ilp64
            PATHS $ENV{MKLROOT}/lib $ENV{MKLROOT}/lib/intel64 /usr/local/lib ${PYTHON_LIB_PATH} NO_DEFAULT_PATH)
        SET(MKL_FLAG "-D_HAS_INTEL_MKL=${MKL_OMP_VALUE}" "-DMKL_ILP64")
    ENDIF()
    FIND_LIBRARY(MKL_LIB_CORE NAMES mkl_core
        PATHS $ENV{MKLROOT}/lib $ENV{MKLROOT}/lib/intel64 /usr/local/lib ${PYTHON_LIB_PATH} NO_DEFAULT_PATH)
    FIND_LIBRARY(MKL_LIB_GT NAMES ${MKL_OMP_LIB_NAME}
        PATHS $ENV{MKLROOT}/lib $ENV{MKLROOT}/lib/intel64 /usr/local/lib ${PYTHON_LIB_PATH} NO_DEFAULT_PATH)
    # AVX and AVX512 are required to avoid setting LD_PRELOAD for python
    FIND_LIBRARY(MKL_LIB_AVX NAMES mkl_avx2
        PATHS $ENV{MKLROOT}/lib $ENV{MKLROOT}/lib/intel64 /usr/local/lib ${PYTHON_LIB_PATH} NO_DEFAULT_PATH)
    FIND_LIBRARY(MKL_LIB_AVX512 NAMES mkl_avx512
        PATHS $ENV{MKLROOT}/lib $ENV{MKLROOT}/lib/intel64 /usr/local/lib ${PYTHON_LIB_PATH} NO_DEFAULT_PATH)
    IF (NOT MKL_LIB_AVX)
        SET(MKL_LIB_AVX "")
    ENDIF()
    IF (NOT MKL_LIB_AVX512)
        SET(MKL_LIB_AVX512 "")
    ENDIF()
    SET(MKL_LIBS ${NO_AS_NEEDED} ${PTHREADS} ${OMP_LIB_NAME} ${MKL_LIB_LP} ${MKL_LIB_CORE} ${MKL_LIB_GT} ${MKL_LIB_AVX} ${MKL_LIB_AVX512})
    SET(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_BKP})
    MESSAGE(STATUS "MKL_INCLUDE_DIR = ${MKL_INCLUDE_DIR}")
    MESSAGE(STATUS "MKL_LIBS = ${MKL_LIBS}")
ELSE()
    # Check LAPACK and BLAS
    FIND_PACKAGE(BLAS REQUIRED)
    FIND_PACKAGE(LAPACK REQUIRED)

    SET(MKL_INCLUDE_DIR "")
    SET(MKL_LIBS "")
    SET(MKL_FLAG "")
ENDIF()

IF (${BUILD_LIB})
    ADD_LIBRARY(${PROJECT_NAME} MODULE ${PYBIND_SRCS} src/pybind.cpp ${SRCS})
    SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SUFFIX "${PYLIB_SUFFIX}" PREFIX "")
    # SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time")
    IF (APPLE)
        TARGET_LINK_LIBRARIES(${PROJECT_NAME} PUBLIC -Wl,-undefined,dynamic_lookup)
    ENDIF()
    IF (NOT(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC"))
        SET(OPT_FLAG ${OPT_FLAG} -fvisibility=hidden -Wno-error=attributes -Wno-attributes)
    ELSE()
        GET_FILENAME_COMPONENT(PYTHON_LIB_DIR ${PYTHON_LIBRARIES} DIRECTORY)
        FIND_LIBRARY(PYTHON_LIB_X NAMES python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR} HINTS "${PYTHON_LIB_DIR}")
        MESSAGE(STATUS "PYTHON_LIB_X = ${PYTHON_LIB_X}")
        TARGET_LINK_LIBRARIES(${PROJECT_NAME} PUBLIC ${PYTHON_LIB_X})
    ENDIF()
ELSEIF (${BUILD_TEST})
    ADD_EXECUTABLE(${PROJECT_NAME} unit_test/debug_main.cpp)
ELSE()
    IF (NOT ${USE_DMRG})
        MESSAGE(FATAL_ERROR "-DUSE_DMRG must be ON for compiling src/main.cpp.")
    ENDIF()
    ADD_EXECUTABLE(${PROJECT_NAME} src/main.cpp ${SRCS})
ENDIF()

TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PUBLIC src ${MKL_INCLUDE_DIR} ${SCI_INCLUDE_DIR})

IF ((NOT APPLE) AND (NOT WIN32))
    TARGET_LINK_LIBRARIES(${PROJECT_NAME} PUBLIC rt)
ENDIF()

IF (${MPI})
    FIND_PACKAGE(MPI)

    IF (NOT MPI_FOUND)
        MESSAGE(FATAL_ERROR "MPI not found. Make sure CMAKE_CXX_COMPILER, MPI_CXX_COMPILER and MPI_C_COMPILER are set.")
    ENDIF()

    MESSAGE(STATUS "MPI_COMPILE_FLAGS = ${MPI_COMPILE_FLAGS}")
    MESSAGE(STATUS "MPI_LINK_FLAGS = ${MPI_LINK_FLAGS}")
    MESSAGE(STATUS "MPI_INCLUDE_PATH = ${MPI_INCLUDE_PATH}")
    MESSAGE(STATUS "MPI_CXX_LIBRARIES = ${MPI_CXX_LIBRARIES}")
    SET(MPI_INCLUDE_DIR ${MPI_INCLUDE_PATH})
    SET(MPI_LIBS ${MPI_CXX_LIBRARIES})
    SET(MPI_FLAG "-D_HAS_MPI" ${MPI_COMPILE_FLAGS})
ELSE()
    SET(MPI_LINK_FLAGS "")
    SET(MPI_COMPILE_FLAGS "")
    SET(MPI_INCLUDE_DIR "")
    SET(MPI_LIBS "")
    SET(MPI_FLAG "")
ENDIF()

TARGET_LINK_LIBRARIES(${PROJECT_NAME} PUBLIC ${OMP_LIB_NAME} ${PTHREAD})
TARGET_LINK_LIBRARIES(${PROJECT_NAME} PUBLIC ${PTHREAD} ${LAPACK_LIBRARIES} ${BLAS_LIBRARIES} ${MKL_LIBS} ${MPI_LIBS} ${TBB_LIBS})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES LINK_FLAGS "${MPI_LINK_FLAGS}")

MESSAGE(STATUS "SRCS = ${SRCS}")
MESSAGE(STATUS "PYBIND_SRCS = ${PYBIND_SRCS}")
MESSAGE(STATUS "OPT_FLAG = ${OPT_FLAG}")
MESSAGE(STATUS "BOND_FLAG = ${BOND_FLAG}")
MESSAGE(STATUS "MKL_FLAG = ${MKL_FLAG}")
MESSAGE(STATUS "CORE_FLAG = ${CORE_FLAG}")
MESSAGE(STATUS "DMRG_FLAG = ${DMRG_FLAG}")
MESSAGE(STATUS "BIG_SITE_FLAG = ${BIG_SITE_FLAG}")
MESSAGE(STATUS "SP_DMRG_FLAG = ${SP_DMRG_FLAG}")
MESSAGE(STATUS "IC_FLAG = ${IC_FLAG}")
MESSAGE(STATUS "KSYMM_FLAG = ${KSYMM_FLAG}")
MESSAGE(STATUS "SG_FLAG = ${SG_FLAG}")
MESSAGE(STATUS "COMPLEX_FLAG = ${COMPLEX_FLAG}")
MESSAGE(STATUS "SCI_FLAG = ${SCI_FLAG}")
MESSAGE(STATUS "TBB_FLAG = ${TBB_FLAG}")
MESSAGE(STATUS "MPI_FLAG = ${MPI_FLAG}")
MESSAGE(STATUS "OMP_LIB = ${OMP_LIB_NAME}")
MESSAGE(STATUS "MKL_OMP_LIB_NAME = ${MKL_OMP_LIB_NAME}")
MESSAGE(STATUS "TBB_LIBS = ${TBB_LIBS}")

TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PUBLIC ${PYTHON_INCLUDE_DIRS} ${PYBIND_INCLUDE_DIRS}
    ${MKL_INCLUDE_DIR} ${MPI_INCLUDE_DIR} ${TBB_INCLUDE_DIR})
TARGET_COMPILE_OPTIONS(${PROJECT_NAME} BEFORE PUBLIC ${OPT_FLAG} ${MKL_FLAG} ${MPI_FLAG}
    ${TMPL_FLAG} ${BOND_FLAG} ${SCI_FLAG} ${CORE_FLAG} ${DMRG_FLAG} ${BIG_SITE_FLAG}
    ${SP_DMRG_FLAG} ${IC_FLAG} ${KSYMM_FLAG} ${SG_FLAG} ${COMPLEX_FLAG} ${TBB_FLAG})

IF (${BUILD_TEST})
    ENABLE_TESTING()
    FIND_PACKAGE(GTest REQUIRED)
    INCLUDE_DIRECTORIES(${GTEST_INCLUDE_DIRS})

    FILE(GLOB TSRCS unit_test/test_*.cpp)
    MESSAGE(STATUS "TSRCS = ${TSRCS}")

    ADD_EXECUTABLE(${PROJECT_NAME}_tests ${TSRCS} ${SRCS})
    TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME}_tests PUBLIC src ${MKL_INCLUDE_DIR} ${MPI_INCLUDE_DIR} ${TBB_INCLUDE_DIR})
    TARGET_LINK_LIBRARIES(${PROJECT_NAME}_tests ${GTEST_BOTH_LIBRARIES} ${PTHREAD} ${MPI_LIBS} ${TBB_LIBS})
    TARGET_COMPILE_OPTIONS(${PROJECT_NAME}_tests BEFORE PUBLIC ${OPT_FLAG} ${MKL_FLAG} ${MPI_FLAG}
        ${TMPL_FLAG} ${BOND_FLAG} ${SCI_FLAG} ${CORE_FLAG} ${DMRG_FLAG} ${BIG_SITE_FLAG}
        ${SP_DMRG_FLAG} ${IC_FLAG} ${KSYMM_FLAG} ${SG_FLAG} ${COMPLEX_FLAG} ${TBB_FLAG})
    SET_TARGET_PROPERTIES(${PROJECT_NAME}_tests PROPERTIES LINK_FLAGS "${MPI_LINK_FLAGS}")

    IF ((NOT APPLE) AND (NOT WIN32))
        TARGET_LINK_LIBRARIES(${PROJECT_NAME}_tests rt)
    ENDIF()
    TARGET_LINK_LIBRARIES(${PROJECT_NAME}_tests ${OMP_LIB_NAME} ${PTHREAD} ${LAPACK_LIBRARIES} ${BLAS_LIBRARIES} ${MKL_LIBS})

    ADD_CUSTOM_COMMAND(TARGET ${PROJECT_NAME}_tests POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_directory
            ${CMAKE_SOURCE_DIR}/data ${CMAKE_CURRENT_BINARY_DIR}/data)

    ADD_TEST(NAME Test COMMAND ${PROJECT_NAME}_tests)
ENDIF()
