cmake_minimum_required(VERSION 3.16)

project(DeriWeave VERSION 0.1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Ne pas construire les outils de developpement chez un projet consommateur.
set(_deriweave_development_default OFF)
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
    set(_deriweave_development_default ON)
endif()
option(DERIWEAVE_BUILD_EXAMPLES "Build DeriWeave examples" ${_deriweave_development_default})
option(DERIWEAVE_BUILD_TESTS "Build DeriWeave tests (also requires BUILD_TESTING)" ${_deriweave_development_default})
option(DERIWEAVE_ENABLE_OPENMP "Enable OpenMP when available" ON)

include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
find_package(Eigen3 3.4 REQUIRED NO_MODULE)

# Le mode sequentiel reste disponible sans OpenMP.
set(DERIWEAVE_WITH_OPENMP OFF)
if(DERIWEAVE_ENABLE_OPENMP)
    find_package(OpenMP QUIET COMPONENTS CXX)
    if(OpenMP_CXX_FOUND AND NOT CMAKE_DISABLE_FIND_PACKAGE_OpenMP)
        set(DERIWEAVE_WITH_OPENMP ON)
    endif()
endif()

if(DERIWEAVE_BUILD_TESTS)
    include(CTest)
endif()

set(DERIWEAVE_SOURCES
    src/cashflow/cashflow.cpp
    src/market/underlying.cpp
    src/market/market_data.cpp
    src/market/market_path.cpp
    src/model/black_scholes_model.cpp
    src/model/heston_model.cpp
    src/model/merton_model.cpp
    src/model/kou_model.cpp
    src/model/variance_gamma_model.cpp
    src/model/nig_model.cpp
    src/pricing/black_scholes_spot_control.cpp
    src/pricing/merton_spot_control.cpp
    src/pricing/kou_spot_control.cpp
    src/pricing/variance_gamma_spot_control.cpp
    src/pricing/nig_spot_control.cpp
    src/pricing/pricing_context.cpp
    src/pricing/simulation_dates.cpp
    src/pricing/pricer.cpp
    src/pricing/longstaff_schwartz.cpp
    src/monte_carlo/simulation_config.cpp
    src/monte_carlo/block_seed.cpp
    src/monte_carlo/pseudo_random_source.cpp
    src/monte_carlo/random_source.cpp
    src/monte_carlo/poisson_transform.cpp
    src/monte_carlo/erlang_transform.cpp
    src/monte_carlo/gamma_transform.cpp
    src/monte_carlo/inverse_gaussian_transform.cpp
    src/monte_carlo/antithetic_random_source.cpp
    src/monte_carlo/statistics.cpp
    src/monte_carlo/bivariate_statistics.cpp
    src/monte_carlo/control_variate.cpp
    src/product/product.cpp
    src/product/structured/autocall_product.cpp
    src/product/structured/exercise_contract.cpp
    src/product/structured/exercise_policy_product.cpp
    src/product/composition/value_expression.cpp
    src/product/composition/terminal_value.cpp
    src/product/composition/variance_value.cpp
    src/product/composition/basket.cpp
    src/product/composition/average_value.cpp
    src/product/composition/call.cpp
    src/product/composition/put.cpp
    src/product/composition/digital.cpp
    src/product/composition/constant_value.cpp
    src/product/composition/conditional_value.cpp
    src/product/composition/scaled_value.cpp
    src/product/composition/worst_of.cpp
    src/product/composition/best_of.cpp
    src/product/composition/cashflow_rule.cpp
    src/product/composition/composed_product.cpp
    src/product/features/product_feature.cpp
    src/product/features/discrete_barrier.cpp
    src/product/aggregation/arithmetic_average.cpp
    src/product/aggregation/geometric_average.cpp
)

# Une seule compilation des sources, partagee par les exemples et les tests.
add_library(deriweave_core STATIC ${DERIWEAVE_SOURCES})
add_library(DeriWeave::deriweave_core ALIAS deriweave_core)
set(DERIWEAVE_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/deriweave")
target_include_directories(deriweave_core PUBLIC
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
    "$<INSTALL_INTERFACE:${DERIWEAVE_INSTALL_INCLUDEDIR}>"
)
target_compile_features(deriweave_core PUBLIC cxx_std_20)
target_link_libraries(deriweave_core PUBLIC Eigen3::Eigen)
set_target_properties(deriweave_core PROPERTIES POSITION_INDEPENDENT_CODE ON)
if(DERIWEAVE_WITH_OPENMP)
    target_link_libraries(deriweave_core PUBLIC OpenMP::OpenMP_CXX)
endif()

# Package installable et deplacable, sans chemins absolus vers les sources.
set(_deriweave_package_dir "${CMAKE_INSTALL_LIBDIR}/cmake/DeriWeave")
configure_package_config_file(cmake/DeriWeaveConfig.cmake.in
    "${CMAKE_CURRENT_BINARY_DIR}/DeriWeaveConfig.cmake"
    INSTALL_DESTINATION "${_deriweave_package_dir}"
    PATH_VARS DERIWEAVE_INSTALL_INCLUDEDIR
)
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/DeriWeaveConfigVersion.cmake"
    VERSION "${PROJECT_VERSION}" COMPATIBILITY ExactVersion
)
install(TARGETS deriweave_core EXPORT DeriWeaveTargets
    ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
)
install(DIRECTORY include/ DESTINATION "${DERIWEAVE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.hpp")
install(EXPORT DeriWeaveTargets NAMESPACE DeriWeave:: DESTINATION "${_deriweave_package_dir}")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/DeriWeaveConfig.cmake"
              "${CMAKE_CURRENT_BINARY_DIR}/DeriWeaveConfigVersion.cmake"
    DESTINATION "${_deriweave_package_dir}"
)
install(FILES LICENSE DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/DeriWeave")

# Outil PDE interne : necessaire aux exemples OU aux tests, jamais exporte.
if(DERIWEAVE_BUILD_EXAMPLES OR (DERIWEAVE_BUILD_TESTS AND BUILD_TESTING))
    add_library(heston_reference STATIC examples/support/heston_reference.cpp)
    target_compile_features(heston_reference PUBLIC cxx_std_20)
    target_link_libraries(heston_reference PRIVATE Eigen3::Eigen)
endif()

if(DERIWEAVE_BUILD_EXAMPLES)
    add_executable(nig_example examples/nig_example.cpp)
    target_link_libraries(nig_example PRIVATE deriweave_core)
    add_executable(nig_basket_example examples/nig_basket_example.cpp)
    target_link_libraries(nig_basket_example PRIVATE deriweave_core)
    add_executable(variance_gamma_example examples/variance_gamma_example.cpp)
    target_link_libraries(variance_gamma_example PRIVATE deriweave_core)
    add_executable(variance_gamma_basket_example examples/variance_gamma_basket_example.cpp)
    target_link_libraries(variance_gamma_basket_example PRIVATE deriweave_core)
    add_executable(variance_gamma_phoenix_example examples/variance_gamma_phoenix_example.cpp)
    target_link_libraries(variance_gamma_phoenix_example PRIVATE deriweave_core)

    add_executable(kou_example examples/kou_example.cpp)
    target_link_libraries(kou_example PRIVATE deriweave_core)

    add_executable(quick_start examples/quick_start.cpp)
    target_link_libraries(quick_start PRIVATE deriweave_core)

    add_executable(monte_carlo_example examples/monte_carlo_example.cpp)
    target_link_libraries(monte_carlo_example PRIVATE deriweave_core)

    add_executable(monte_carlo_convergence examples/monte_carlo_convergence.cpp)
    target_link_libraries(monte_carlo_convergence PRIVATE deriweave_core)

    add_executable(basket_example examples/basket_example.cpp)
    target_link_libraries(basket_example PRIVATE deriweave_core)

    add_executable(composed_asian_basket_example examples/composed_asian_basket_example.cpp)
    target_link_libraries(composed_asian_basket_example PRIVATE deriweave_core)

    add_executable(discrete_barrier_example examples/discrete_barrier_example.cpp)
    target_link_libraries(discrete_barrier_example PRIVATE deriweave_core)

    add_executable(conditional_coupons_example examples/conditional_coupons_example.cpp)
    target_link_libraries(conditional_coupons_example PRIVATE deriweave_core)

    add_executable(worst_of_coupons_example examples/worst_of_coupons_example.cpp)
    target_link_libraries(worst_of_coupons_example PRIVATE deriweave_core)

    add_executable(autocall_example examples/autocall_example.cpp)
    target_link_libraries(autocall_example PRIVATE deriweave_core)

    add_executable(protected_autocall_example examples/protected_autocall_example.cpp)
    target_link_libraries(protected_autocall_example PRIVATE deriweave_core)

    add_executable(memory_autocall_example examples/memory_autocall_example.cpp)
    target_link_libraries(memory_autocall_example PRIVATE deriweave_core)

    add_executable(lookback_example examples/lookback_example.cpp)
    target_link_libraries(lookback_example PRIVATE deriweave_core)

    add_executable(american_put_example examples/american_put_example.cpp)
    target_link_libraries(american_put_example PRIVATE deriweave_core)

    add_executable(american_put_convergence examples/american_put_convergence.cpp)
    target_link_libraries(american_put_convergence PRIVATE deriweave_core)

    add_executable(heston_example examples/heston_example.cpp)
    target_link_libraries(heston_example PRIVATE deriweave_core)

    add_executable(merton_phoenix_example examples/merton_phoenix_example.cpp)
    target_link_libraries(merton_phoenix_example PRIVATE deriweave_core)

    add_executable(merton_integration_example examples/merton_integration_example.cpp)
    target_link_libraries(merton_integration_example PRIVATE deriweave_core)

    add_executable(merton_example examples/merton_example.cpp)
    target_link_libraries(merton_example PRIVATE deriweave_core)

    add_executable(heston_american_put_example examples/heston_american_put_example.cpp)
    target_link_libraries(heston_american_put_example PRIVATE deriweave_core)

    add_executable(heston_american_convergence examples/heston_american_convergence.cpp)
    target_link_libraries(heston_american_convergence PRIVATE deriweave_core heston_reference)

endif()

if(DERIWEAVE_BUILD_TESTS AND BUILD_TESTING)
    add_executable(inverse_gaussian_transform_tests tests/inverse_gaussian_transform_tests.cpp)
    target_link_libraries(inverse_gaussian_transform_tests PRIVATE deriweave_core)
    add_test(NAME inverse_gaussian_transform COMMAND inverse_gaussian_transform_tests)
    add_executable(nig_model_tests tests/nig_model_tests.cpp)
    target_link_libraries(nig_model_tests PRIVATE deriweave_core)
    add_test(NAME nig_model COMMAND nig_model_tests)
    add_executable(nig_pricing_tests tests/nig_pricing_tests.cpp)
    target_link_libraries(nig_pricing_tests PRIVATE deriweave_core)
    add_test(NAME nig_pricing COMMAND nig_pricing_tests)
    add_executable(gamma_transform_tests tests/gamma_transform_tests.cpp)
    target_link_libraries(gamma_transform_tests PRIVATE deriweave_core)
    add_test(NAME gamma_transform COMMAND gamma_transform_tests)
    add_executable(variance_gamma_model_tests tests/variance_gamma_model_tests.cpp)
    target_link_libraries(variance_gamma_model_tests PRIVATE deriweave_core)
    add_test(NAME variance_gamma_model COMMAND variance_gamma_model_tests)
    add_executable(variance_gamma_pricing_tests tests/variance_gamma_pricing_tests.cpp)
    target_link_libraries(variance_gamma_pricing_tests PRIVATE deriweave_core)
    add_test(NAME variance_gamma_pricing COMMAND variance_gamma_pricing_tests)
    add_executable(erlang_transform_tests tests/erlang_transform_tests.cpp)
    target_link_libraries(erlang_transform_tests PRIVATE deriweave_core)
    add_test(NAME erlang_transform COMMAND erlang_transform_tests)
    add_executable(kou_model_tests tests/kou_model_tests.cpp)
    target_link_libraries(kou_model_tests PRIVATE deriweave_core)
    add_test(NAME kou_model COMMAND kou_model_tests)
    add_executable(kou_pricing_tests tests/kou_pricing_tests.cpp)
    target_link_libraries(kou_pricing_tests PRIVATE deriweave_core)
    add_test(NAME kou_pricing COMMAND kou_pricing_tests)

    if(DERIWEAVE_BUILD_EXAMPLES)
        add_test(NAME merton_phoenix_contract COMMAND merton_phoenix_example --check-contract)
        add_test(NAME variance_gamma_phoenix_contract COMMAND variance_gamma_phoenix_example --check-contract)
    endif()
    add_executable(merton_product_integration_tests tests/merton_product_integration_tests.cpp)
    target_link_libraries(merton_product_integration_tests PRIVATE deriweave_core)
    add_test(NAME merton_product_integration COMMAND merton_product_integration_tests)
    add_executable(merton_multi_asset_tests tests/merton_multi_asset_tests.cpp)
    target_link_libraries(merton_multi_asset_tests PRIVATE deriweave_core)
    add_test(NAME merton_multi_asset COMMAND merton_multi_asset_tests)
    add_executable(merton_early_exercise_tests tests/merton_early_exercise_tests.cpp)
    target_link_libraries(merton_early_exercise_tests PRIVATE deriweave_core)
    add_test(NAME merton_early_exercise COMMAND merton_early_exercise_tests)
    add_executable(merton_spot_control_tests tests/merton_spot_control_tests.cpp)
    target_link_libraries(merton_spot_control_tests PRIVATE deriweave_core)
    add_test(NAME merton_spot_control COMMAND merton_spot_control_tests)
    add_executable(poisson_transform_tests tests/poisson_transform_tests.cpp)
    target_link_libraries(poisson_transform_tests PRIVATE deriweave_core)
    add_test(NAME poisson_transform COMMAND poisson_transform_tests)
    add_executable(merton_model_tests tests/merton_model_tests.cpp)
    target_link_libraries(merton_model_tests PRIVATE deriweave_core)
    add_test(NAME merton_model COMMAND merton_model_tests)
    add_executable(heston_reference_tests tests/heston_reference_tests.cpp)
    target_link_libraries(heston_reference_tests PRIVATE heston_reference)
    add_test(NAME heston_reference COMMAND heston_reference_tests)
    add_executable(multivariate_lsm_tests tests/multivariate_lsm_tests.cpp)
    target_link_libraries(multivariate_lsm_tests PRIVATE deriweave_core)
    add_test(NAME multivariate_lsm COMMAND multivariate_lsm_tests)

    add_executable(market_path_tests tests/market_path_tests.cpp)
    target_link_libraries(market_path_tests PRIVATE deriweave_core)
    add_test(NAME market_path COMMAND market_path_tests)

    add_executable(heston_model_tests tests/heston_model_tests.cpp)
    target_link_libraries(heston_model_tests PRIVATE deriweave_core)
    add_test(NAME heston_model COMMAND heston_model_tests)

    add_executable(early_exercise_tests tests/early_exercise_tests.cpp)
    target_link_libraries(early_exercise_tests PRIVATE deriweave_core)
    add_test(NAME early_exercise COMMAND early_exercise_tests)

    add_executable(lookback_composition_tests tests/lookback_composition_tests.cpp)
    target_link_libraries(lookback_composition_tests PRIVATE deriweave_core)
    add_test(NAME lookback_composition COMMAND lookback_composition_tests)

    add_executable(coupon_memory_tests tests/coupon_memory_tests.cpp)
    target_link_libraries(coupon_memory_tests PRIVATE deriweave_core)
    add_test(NAME coupon_memory COMMAND coupon_memory_tests)

    add_executable(conditional_value_tests tests/conditional_value_tests.cpp)
    target_link_libraries(conditional_value_tests PRIVATE deriweave_core)
    add_test(NAME conditional_value COMMAND conditional_value_tests)

    add_executable(autocall_product_tests tests/autocall_product_tests.cpp)
    target_link_libraries(autocall_product_tests PRIVATE deriweave_core)
    add_test(NAME autocall_product COMMAND autocall_product_tests)

    add_executable(extrema_expression_tests tests/extrema_expression_tests.cpp)
    target_link_libraries(extrema_expression_tests PRIVATE deriweave_core)
    add_test(NAME extrema_expression COMMAND extrema_expression_tests)

    add_executable(digital_expression_tests tests/digital_expression_tests.cpp)
    target_link_libraries(digital_expression_tests PRIVATE deriweave_core)
    add_test(NAME digital_expression COMMAND digital_expression_tests)

    add_executable(expression_barrier_tests tests/expression_barrier_tests.cpp)
    target_link_libraries(expression_barrier_tests PRIVATE deriweave_core)
    add_test(NAME expression_barrier COMMAND expression_barrier_tests)

    add_executable(discrete_barrier_tests tests/discrete_barrier_tests.cpp)
    target_link_libraries(discrete_barrier_tests PRIVATE deriweave_core)
    add_test(NAME discrete_barrier COMMAND discrete_barrier_tests)

    add_executable(put_expression_tests tests/put_expression_tests.cpp)
    target_link_libraries(put_expression_tests PRIVATE deriweave_core)
    add_test(NAME put_expression COMMAND put_expression_tests)

    add_executable(composed_product_tests tests/composed_product_tests.cpp)
    target_link_libraries(composed_product_tests PRIVATE deriweave_core)
    add_test(NAME composed_product COMMAND composed_product_tests)

    add_executable(average_aggregation_tests tests/average_aggregation_tests.cpp)
    target_link_libraries(average_aggregation_tests PRIVATE deriweave_core)
    add_test(NAME average_aggregation COMMAND average_aggregation_tests)

    add_executable(basket_composition_tests tests/basket_composition_tests.cpp)
    target_link_libraries(basket_composition_tests PRIVATE deriweave_core)
    add_test(NAME basket_composition COMMAND basket_composition_tests)

    add_executable(asian_composition_tests tests/asian_composition_tests.cpp)
    target_link_libraries(asian_composition_tests PRIVATE deriweave_core)
    add_test(NAME asian_composition COMMAND asian_composition_tests)

    add_executable(market_data_tests tests/market_data_tests.cpp)
    target_link_libraries(market_data_tests PRIVATE deriweave_core)
    add_test(NAME market_data COMMAND market_data_tests)

    add_executable(pseudo_random_source_tests tests/pseudo_random_source_tests.cpp)
    target_link_libraries(pseudo_random_source_tests PRIVATE deriweave_core)
    add_test(NAME pseudo_random_source COMMAND pseudo_random_source_tests)

    add_executable(random_source_tests tests/random_source_tests.cpp)
    target_link_libraries(random_source_tests PRIVATE deriweave_core)
    add_test(NAME random_source COMMAND random_source_tests)

    add_executable(antithetic_random_source_tests tests/antithetic_random_source_tests.cpp)
    target_link_libraries(antithetic_random_source_tests PRIVATE deriweave_core)
    add_test(NAME antithetic_random_source COMMAND antithetic_random_source_tests)

    add_executable(black_scholes_model_tests tests/black_scholes_model_tests.cpp)
    target_link_libraries(black_scholes_model_tests PRIVATE deriweave_core)
    add_test(NAME black_scholes_model COMMAND black_scholes_model_tests)

    add_executable(statistics_tests tests/statistics_tests.cpp)
    target_link_libraries(statistics_tests PRIVATE deriweave_core)
    add_test(NAME statistics COMMAND statistics_tests)

    add_executable(bivariate_statistics_tests tests/bivariate_statistics_tests.cpp)
    target_link_libraries(bivariate_statistics_tests PRIVATE deriweave_core)
    add_test(NAME bivariate_statistics COMMAND bivariate_statistics_tests)

    add_executable(control_variate_tests tests/control_variate_tests.cpp)
    target_link_libraries(control_variate_tests PRIVATE deriweave_core)
    add_test(NAME control_variate COMMAND control_variate_tests)

    add_executable(black_scholes_spot_control_tests tests/black_scholes_spot_control_tests.cpp)
    target_link_libraries(black_scholes_spot_control_tests PRIVATE deriweave_core)
    add_test(NAME black_scholes_spot_control COMMAND black_scholes_spot_control_tests)

    add_executable(simulation_config_tests tests/simulation_config_tests.cpp)
    target_link_libraries(simulation_config_tests PRIVATE deriweave_core)
    add_test(NAME simulation_config COMMAND simulation_config_tests)

    add_executable(pricing_context_tests tests/pricing_context_tests.cpp)
    target_link_libraries(pricing_context_tests PRIVATE deriweave_core)
    add_test(NAME pricing_context COMMAND pricing_context_tests)

    add_executable(simulation_dates_tests tests/simulation_dates_tests.cpp)
    target_link_libraries(simulation_dates_tests PRIVATE deriweave_core)
    add_test(NAME simulation_dates COMMAND simulation_dates_tests)

    add_executable(pricer_tests tests/pricer_tests.cpp)
    target_link_libraries(pricer_tests PRIVATE deriweave_core)
    add_test(NAME pricer COMMAND pricer_tests)

    add_executable(blocked_pricer_tests tests/blocked_pricer_tests.cpp)
    target_link_libraries(blocked_pricer_tests PRIVATE deriweave_core)
    add_test(NAME blocked_pricer COMMAND blocked_pricer_tests)

    add_executable(openmp_pricer_tests tests/openmp_pricer_tests.cpp)
    target_link_libraries(openmp_pricer_tests PRIVATE deriweave_core)
    add_test(NAME openmp_pricer COMMAND openmp_pricer_tests)
endif()
