diff --git a/CMakeLists.txt b/CMakeLists.txt index 67c4927..f6ced1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,7 @@ cmake_minimum_required(VERSION 3.28) +if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.30.0") + cmake_policy(SET CMP0167 NEW) # use FindBoost from boost installation (as opposed to CMake own) +endif () project(libstrategy VERSION 1.0.9 LANGUAGES CXX C) set(CMAKE_CXX_STANDARD 17) @@ -6,31 +9,24 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) - -include(cmake/warnings.cmake) -include(cmake/sanitizers.cmake) - -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") option(LIBSTRATEGY_TESTS "libstrategy Unit Tests" ON) option(LIBSTRATEGY_OnlyLibrary "Build only the library (no binary utilities)" OFF) -include(cmake/ptrie.cmake) +include(cmake/warnings.cmake) +include(cmake/sanitizers.cmake) include(cmake/nlohmann_json.cmake) +include(cmake/ptrie.cmake) if(NOT LIBSTRATEGY_OnlyLibrary) cmake_policy(SET CMP0069 NEW) # INTERPROCEDURAL_OPTIMIZATIONS flags - if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.30.0") - cmake_policy(SET CMP0167 NEW) # use FindBoost from boost installation (as opposed to CMake own) - endif () - find_package(Boost 1.83 COMPONENTS program_options REQUIRED) endif() add_subdirectory(src) if (LIBSTRATEGY_TESTS) + include(cmake/doctest.cmake) enable_testing() add_subdirectory(test) endif() diff --git a/CMakePresets.json b/CMakePresets.json index 0dd7936..0e20268 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -8,47 +8,115 @@ "include": ["cmake/CommonPresets.json"], "configurePresets": [ { - "name": "libonly", + "name": "quick", "inherits": "multi", - "binaryDir": "${sourceDir}/build-libonly", + "binaryDir": "${sourceDir}/build-quick", "cacheVariables": { "LIBSTRATEGY_TESTS": "OFF" , "LIBSTRATEGY_OnlyLibrary": "ON" }, - "displayName": "Configure Library-Only for Debug and Release" + "displayName": "Configure quick build (no utilities, no testing) for Debug and Release" + }, + { + "name": "quick-san", + "inherits": "multi-san", + "binaryDir": "${sourceDir}/build-quick-san", + "cacheVariables": { + "LIBSTRATEGY_TESTS": "OFF" , + "LIBSTRATEGY_OnlyLibrary": "ON" + }, + "displayName": "Configure quick build (no utilities, no testing) with Sanitizers for Debug and Release" } ], "buildPresets": [ { - "name": "libonly-release", + "name": "quick-release", "inherits": "release", - "configurePreset": "libonly", + "configurePreset": "quick", + "displayName": "Build Library-Only for Release" + }, + { + "name": "quick-debug", + "inherits": "debug", + "configurePreset": "quick", "displayName": "Build Library-Only for Debug" }, { - "name": "libonly-debug", + "name": "quick-san-release", + "inherits": "release", + "configurePreset": "quick-san", + "displayName": "Build Library-Only with Sanitizers for Release" + }, + { + "name": "quick-san-debug", "inherits": "debug", - "configurePreset": "libonly", - "displayName": "Build Library-Only for Release" + "configurePreset": "quick-san", + "displayName": "Build Library-Only with Sanitizers for Debug" } ], "testPresets": [], "workflowPresets": [ { - "name": "libonly", - "displayName": "Configure and Build Library-Only for Debug and Release (no testing)", + "name": "quick", + "displayName": "Configure and Build Library-Only (no testing) for Debug and Release", "steps": [ { "type": "configure", - "name": "libonly" + "name": "quick" + }, + { + "type": "build", + "name": "quick-release" }, { "type": "build", - "name": "libonly-release" + "name": "quick-debug" + } + ] + }, + { + "name": "quick-release", + "displayName": "Configure and Build Library-Only (no testing) for Release", + "steps": [ + { + "type": "configure", + "name": "quick" + }, + { + "type": "build", + "name": "quick-release" + } + ] + }, + { + "name": "quick-san", + "displayName": "Configure and Build Library-Only (no testing) with Sanitizers for Debug and Release", + "steps": [ + { + "type": "configure", + "name": "quick-san" + }, + { + "type": "build", + "name": "quick-san-release" + }, + { + "type": "build", + "name": "quick-san-debug" + } + ] + }, + { + "name": "quick-san-release", + "displayName": "Configure and Build Library-Only (no testing) for Release", + "steps": [ + { + "type": "configure", + "name": "quick-san" }, { "type": "build", - "name": "libonly-debug" + "name": "quick-san-release" } ] } diff --git a/README.md b/README.md index 2d2eed3..29d7fba 100644 --- a/README.md +++ b/README.md @@ -13,22 +13,36 @@ Full project requires: For Ubuntu 24.04 install build tools and library dependencies: ```shell -sudo apt install cmake ninja-build g++ libboost-program-options-dev libboost-test-dev +sudo apt install cmake ninja-build g++ +sudo apt install libboost-program-options-dev libboost-test-dev # optional ``` For macOS install build tools and library dependencies: ```shell -brew install cmake ninja boost +brew install cmake ninja gcc +brew install boost # optional ``` ## Compile and Install -Run minimal compilation (just the library) with `Debug` and `Release` settings into `build-libonly/lib`: +Run minimal compilation (just the library) with `Release` settings into `build-quick/lib`: ```shell -cmake --workflow libonly +cmake --workflow --preset quick-release ``` -Install the `Release` build of `build-libonly` into `$PWD/local` path: +Install the `Release` build of `build-quick` into `$PWD/local` path: ```shell -cmake --install build-libonly --config Release --prefix $PWD/local +cmake --install build-quick --config Release --prefix $PWD/local +``` + +## Example Usage +Directory [example](example) contains minimal example demonstrating how to use the library. + +Test the example with libstrategy installed into [local](local) directory: +```shell +cmake -S example -B build-example -DCMAKE_PREFIX_PATH=$PWD/local -DCMAKE_MODULE_PATH=$PWD/cmake +``` +```shell +cmake --build build-example +ctest --test-dir build-example --output-on-failure ``` ## Other Presets @@ -39,7 +53,7 @@ cmake --workflow --list-presets For example, configure, build and **test** for Development with **Sanitizers** (GCC/Clang/AppleClang): ```shell -cmake --workflow debug-san +cmake --workflow --preset debug-san ``` Other configuration presets: diff --git a/cmake/boost.cmake b/cmake/boost.cmake new file mode 100644 index 0000000..b7f77b6 --- /dev/null +++ b/cmake/boost.cmake @@ -0,0 +1,33 @@ +#set(FETCHCONTENT_QUIET ON) +#set(FETCHCONTENT_UPDATES_DISCONNECTED ON) +include(FetchContent) +FetchContent_Declare(Boost + # URL https://github.com/boostorg/boost/releases/download/boost-1.91.0-1/boost-1.91.0-1-cmake.7z + URL https://people.cs.aau.dk/~marius/mirrors/boost/boost-1.91.0-1-cmake.7z + URL_HASH SHA256=29c7d4f4ac36ad853b6765d03571ea60d90286775df026b4efd9f3281131972b + #GIT_REPOSITORY https://github.com/boostorg/boost.git + #GIT_TAG boost-1.91.0-1 + #GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) + #GIT_PROGRESS TRUE # show download progress in Ninja + USES_TERMINAL_DOWNLOAD TRUE + EXCLUDE_FROM_ALL # don't build if not used + FIND_PACKAGE_ARGS 1.91.0 COMPONENTS unit_test_framework) + +set(BOOST_ENABLE_MPI OFF CACHE BOOL "Boost.MPI and its dependents (requires MPI, CMake 3.10)") +set(BOOST_ENABLE_PYTHON OFF CACHE BOOL "Boost.Python and its dependents (requires Python, CMake 3.14)") +set(BUILD_TESTING OFF CACHE BOOL "Build the tests.") +set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries") + + +FetchContent_MakeAvailable(Boost) + +if (Boost_FOUND) # find_package + message(STATUS "Found Boost: ${Boost_INCLUDE_DIRS}") +else (Boost_program_options_FOUND) # fetch_content + message(STATUS "Got Boost: ${Boost_SOURCE_DIR}") +endif (Boost_FOUND) + +if (TARGET Boost::unit_test_framework) + message(STATUS " Available target: Boost::unit_test_framework") +endif () + diff --git a/cmake/doctest.cmake b/cmake/doctest.cmake new file mode 100644 index 0000000..58287ae --- /dev/null +++ b/cmake/doctest.cmake @@ -0,0 +1,33 @@ +# Downloads and compiles DocTest unit testing framework +include(FetchContent) +#set(FETCHCONTENT_QUIET ON) +#set(FETCHCONTENT_UPDATES_DISCONNECTED ON) + +FetchContent_Declare(doctest + GIT_REPOSITORY https://github.com/doctest/doctest.git + GIT_TAG v2.5.2 # "main" for latest + GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) + GIT_PROGRESS TRUE # show download progress in Ninja + EXCLUDE_FROM_ALL ON # don't build if not used + FIND_PACKAGE_ARGS 2.4.12) + +set(DOCTEST_WITH_TESTS OFF CACHE BOOL "Build tests/examples") +set(DOCTEST_WITH_MAIN_IN_STATIC_LIB ON CACHE BOOL "Build a static lib for doctest::doctest_with_main") +set(DOCTEST_NO_INSTALL OFF CACHE BOOL "Skip the installation process") +set(DOCTEST_USE_STD_HEADERS OFF CACHE BOOL "Use std headers") + +FetchContent_MakeAvailable(doctest) + +if(doctest_FOUND) # find_package + message(STATUS "Found doctest: ${doctest_DIR}") +else(doctest_FOUND) # FetchContent + message(STATUS "Fetched doctest: ${doctest_SOURCE_DIR}") +endif(doctest_FOUND) + +if (TARGET doctest::doctest) + message(STATUS " Available target: doctest::doctest") +endif () + +if (TARGET doctest::doctest_with_main) + message(STATUS " Available target: doctest::doctest_with_main") +endif () diff --git a/cmake/libstrategy.cmake b/cmake/libstrategy.cmake new file mode 100644 index 0000000..9cd602f --- /dev/null +++ b/cmake/libstrategy.cmake @@ -0,0 +1,26 @@ +include(FetchContent) +FetchContent_Declare(libstrategy + GIT_REPOSITORY https://github.com/DEIS-Tools/libstrategy + GIT_TAG fixing_version_check + GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) + GIT_PROGRESS TRUE # show download progress in Ninja + USES_TERMINAL_DOWNLOAD TRUE + EXCLUDE_FROM_ALL # don't build if not used + FIND_PACKAGE_ARGS 1.0.9) + +set(LIBSTRATEGY_TESTS OFF CACHE BOOL "libstrategy Unit Tests") +set(LIBSTRATEGY_OnlyLibrary ON CACHE BOOL "Build only the library (no binary utilities)") +FetchContent_MakeAvailable(libstrategy) + +if (libstrategy_FOUND) # find_package + message(STATUS "Found libstrategy: ${libstrategy_DIR}") +else (libstrategy_FOUND) # fetch_content + message(STATUS "Fetched libstrategy: ${libstrategy_SOURCE_DIR}") +endif (libstrategy_FOUND) + +if (TARGET libstrategy::strategy) + message(STATUS " Available target: libstrategy::strategy") +endif () +if (TARGET libstrategy::strategyStatic) + message(STATUS " Available target: libstrategy::strategyStatic") +endif () diff --git a/cmake/mingw-w64-x86_64.cmake b/cmake/mingw-w64-x86_64.cmake new file mode 100644 index 0000000..2fc3a27 --- /dev/null +++ b/cmake/mingw-w64-x86_64.cmake @@ -0,0 +1,12 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x86_64) + +set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) +set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) +set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) + +set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/cmake/nlohmann_json.cmake b/cmake/nlohmann_json.cmake index cd031cb..35f27b8 100644 --- a/cmake/nlohmann_json.cmake +++ b/cmake/nlohmann_json.cmake @@ -9,6 +9,7 @@ FetchContent_Declare(nlohmann_json GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) GIT_PROGRESS TRUE # show download progress in Ninja USES_TERMINAL_DOWNLOAD TRUE + EXCLUDE_FROM_ALL # don't build if not used FIND_PACKAGE_ARGS 3.12.0) set(NLOHMANN_JSON_BUILD_MODULES OFF CACHE BOOL "Build C++ modules support") @@ -25,11 +26,9 @@ set(JSON_MultipleHeaders ON CACHE BOOL "Use non-amalgamated version set(JSON_SystemInclude OFF CACHE BOOL "Include as system headers (skip for clang-tidy).") FetchContent_MakeAvailable(nlohmann_json) -message(STATUS "Got nlohmann_json: ${nlohmann_json_SOURCE_DIR}") if (nlohmann_json_FOUND) # find_package - get_target_property(nlohmann_json_INCLUDE_DIRS nlohmann_json::nlohmann_json INTERFACE_INCLUDE_DIRECTORIES) - message(STATUS "Found nlohmann_json: ${nlohmann_json_INCLUDE_DIRS}") + message(STATUS "Found nlohmann_json: ${nlohmann_json_DIRS}") else (nlohmann_json_FOUND) # FetchContent message(STATUS "Fetched nlohmann_json: ${nlohmann_json_SOURCE_DIR}") endif (nlohmann_json_FOUND) diff --git a/cmake/ptrie.cmake b/cmake/ptrie.cmake index 854633a..e8d9209 100644 --- a/cmake/ptrie.cmake +++ b/cmake/ptrie.cmake @@ -3,24 +3,26 @@ include(FetchContent) FetchContent_Declare(ptrie GIT_REPOSITORY https://github.com/DEIS-Tools/ptrie - GIT_TAG v1.1.2 + GIT_TAG v1.1.3 GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) GIT_PROGRESS TRUE # show download progress in Ninja USES_TERMINAL_DOWNLOAD TRUE - FIND_PACKAGE_ARGS 1.1.2) + EXCLUDE_FROM_ALL # don't build if not used + FIND_PACKAGE_ARGS 1.1.3) set(PTRIE_BuildTests OFF CACHE BOOL "Build the unit tests when BUILD_TESTING is enabled.") set(PTRIE_BuildBenchmark OFF CACHE BOOL "Build the simple benchmark suite") FetchContent_MakeAvailable(ptrie) if (ptrie_FOUND) # find_package - get_target_property(ptrie_INCLUDE_DIRS ptrie::ptrie INTERFACE_INCLUDE_DIRECTORIES) - message(STATUS "Found ptrie: ${ptrie_INCLUDE_DIRS}") + message(STATUS "Found ptrie: ${ptrie_DIR}") else (ptrie_FOUND) # fetch_content - message(STATUS "Got ptrie: ${ptrie_SOURCE_DIR}") + message(STATUS "Fetched ptrie: ${ptrie_SOURCE_DIR}") # Workaround until ptrie exports proper cmake config: - add_library(ptrie::ptrie INTERFACE IMPORTED GLOBAL) - target_include_directories(ptrie::ptrie INTERFACE ${ptrie_SOURCE_DIR}/src) + if (NOT TARGET ptrie::ptrie) + add_library(ptrie::ptrie INTERFACE IMPORTED GLOBAL) + target_include_directories(ptrie::ptrie INTERFACE ${ptrie_SOURCE_DIR}/src) + endif() endif (ptrie_FOUND) if (TARGET ptrie::ptrie) diff --git a/cmake/warnings.cmake b/cmake/warnings.cmake index 409ab03..3da2ddf 100644 --- a/cmake/warnings.cmake +++ b/cmake/warnings.cmake @@ -1,9 +1,13 @@ +# Consider: -Wconversion -Wsign-conversion -Wshadow -Weffc++ +set(LIBSTRATEGY_COMMON_WARNINGS -Wpedantic -Wall -Wextra -Wold-style-cast -Wcast-align -Wformat=2 -Wimplicit-fallthrough -Wnon-virtual-dtor -Woverloaded-virtual -Wnull-dereference -Wmisleading-indentation) if (CMAKE_CXX_COMPILER_ID MATCHES GNU) - add_compile_options(-Wpedantic -Wall -Wextra) -elseif (CMAKE_CXX_COMPILER_ID MATCHES AppleClang) - add_compile_options(-Wpedantic -Wall -Wextra) + set(LIBSTRATEGY_WARNINGS ${LIBSTRATEGY_COMMON_WARNINGS} -Wuseless-cast -Wduplicated-cond -Wduplicated-branches -Wlogical-op) elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang) - add_compile_options(-Wpedantic -Wall -Wextra) + set(LIBSTRATEGY_WARNINGS ${LIBSTRATEGY_COMMON_WARNINGS}) +elseif (CMAKE_CXX_COMPILER_ID MATCHES AppleClang) + set(LIBSTRATEGY_WARNINGS ${LIBSTRATEGY_COMMON_WARNINGS}) elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC) - add_compile_options(/W4) -endif () \ No newline at end of file + set(LIBSTRATEGY_WARNINGS /W4 /w14619 /w14265 /w14545 /w14546 /w14640 /w14905 /w14906 /w14928) +endif () +# Use target_compile_options to avoid warnings on third party code +# add_compile_options(${LIBSTRATEGY_WARNINGS}) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..5bd12fb --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.28) +project(example VERSION 1.0.9) + +include(ptrie) +include(libstrategy) + +add_executable(example example.cpp) +target_link_libraries(example PRIVATE libstrategy::strategy) +enable_testing() +add_test(NAME example COMMAND example) diff --git a/example/example.cpp b/example/example.cpp new file mode 100644 index 0000000..6240dcf --- /dev/null +++ b/example/example.cpp @@ -0,0 +1,18 @@ +#include + +#include +#include + +int main(int argc, const char* argv[]) +{ + if (argc < 2) { + std::cout << "Expecting a path to a strategy as an argument\n"; + return 1; + } + for (auto i = 1; i < argc; ++i) { + auto strategy_path = argv[i]; + auto is = std::ifstream{strategy_path}; + auto tree = SimpleTree::parse(is, true, false); + tree.print(std::cout); + } +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a08a070..5ab994c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,22 +1,55 @@ +include(GNUInstallDirs) + +set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) + add_library(SimpleTree OBJECT SimpleTree.cpp) -target_link_libraries(SimpleTree PUBLIC ptrie::ptrie nlohmann_json::nlohmann_json) -add_library(strategy SHARED libz2s.cpp ZonotopStrategy.cpp) -target_link_libraries(strategy PUBLIC SimpleTree) +target_link_libraries(SimpleTree PUBLIC ptrie::ptrie PRIVATE nlohmann_json::nlohmann_json) +target_compile_options(SimpleTree PUBLIC ${LIBSTRATEGY_WARNINGS}) +add_library(ZonotopStrategy OBJECT ZonotopStrategy.cpp) +target_link_libraries(ZonotopStrategy PUBLIC ptrie::ptrie PRIVATE nlohmann_json::nlohmann_json) + +add_library(libz2s OBJECT libz2s.cpp) +target_compile_options(libz2s PUBLIC ${LIBSTRATEGY_WARNINGS}) +target_link_libraries(libz2s PUBLIC ptrie::ptrie) + +add_library(strategy SHARED $ $ $) +target_link_libraries(strategy PUBLIC ptrie::ptrie) +target_include_directories(strategy PUBLIC $) +add_library(libstrategy::strategy ALIAS strategy) -add_library(strategyStatic STATIC libz2s.cpp ZonotopStrategy.cpp) -target_link_libraries(strategyStatic PUBLIC SimpleTree) -set_target_properties(strategyStatic PROPERTIES OUTPUT_NAME strategy) +add_library(strategyStatic STATIC $ $ $) +target_link_libraries(strategyStatic PUBLIC ptrie::ptrie) +target_include_directories(strategyStatic PUBLIC $) +set_target_properties(strategyStatic PROPERTIES OUTPUT_NAME strategy-static) +add_library(libstrategy::strategyStatic ALIAS strategyStatic) if(NOT LIBSTRATEGY_OnlyLibrary) - add_executable(z2s main.cpp ZonotopStrategy.cpp) - target_link_libraries(z2s PUBLIC SimpleTree Boost::program_options) + add_executable(z2s main.cpp) + target_compile_options(z2s PUBLIC ${LIBSTRATEGY_WARNINGS}) + target_link_libraries(z2s PUBLIC ZonotopStrategy SimpleTree) endif() -install(TARGETS strategy strategyStatic - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib) -install (FILES errors.h SimpleTree.h ZonotopStrategy.h DESTINATION include/libstrategy) +include(CMakePackageConfigHelpers) + +set(CONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/libstrategy) +configure_package_config_file(libstrategyConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/libstrategyConfig.cmake + INSTALL_DESTINATION ${CONFIG_INSTALL_DIR}) +write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/libstrategyConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion) +install(TARGETS strategy strategyStatic + EXPORT libstrategyTargets) +install(FILES errors.h libz2s.h SimpleTree.h ZonotopStrategy.h + DESTINATION include/libstrategy) +install(EXPORT libstrategyTargets + FILE libstrategyTargets.cmake + NAMESPACE libstrategy:: + DESTINATION ${CONFIG_INSTALL_DIR}) +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/libstrategyConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/libstrategyConfigVersion.cmake + DESTINATION ${CONFIG_INSTALL_DIR}) diff --git a/src/SimpleTree.cpp b/src/SimpleTree.cpp index 12b18e9..be67290 100644 --- a/src/SimpleTree.cpp +++ b/src/SimpleTree.cpp @@ -29,11 +29,82 @@ #include #include +#include +#include +#include +#include #include +#include #include using json = nlohmann::json; +struct SimpleTree::Node : std::enable_shared_from_this +{ + uint32_t _var = std::numeric_limits::max(); + double _limit = -std::numeric_limits::infinity(); + double _cost = std::numeric_limits::infinity(); + std::pair _cost_bounds; + NodePtr _low; + NodePtr _high; + Node* _parent{nullptr}; + void insert(std::vector& key, json& tree, size_t action, SimpleTree& parent, size_t prefix, bool minimize, + double accuracy, std::vector& exactness); + std::ostream& print(std::ostream& out, size_t tabs = 0) const; + bool is_leaf() const { return _low == nullptr && _high == nullptr; } + NodePtr simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent); + void subsumption_reduction(bool minimization, SimpleTree& parent); + void action_nodes(std::vector& nodes, uint32_t low, uint32_t high, uint32_t varid); + std::pair compute_min_max() const; + bool check_tiles(Node* start, std::vector&, std::vector>& bounds, double val, + double minval, double maxval, bool minimization, size_t offset); + bool subsumes(const std::vector>& bounds, std::vector>& obounds, + double val, bool minimization, size_t offset, double& best, std::pair& closest) const; + void get_ranks(std::set>& values, Node* start); + void set_ranks(std::unordered_map& values); + std::ostream& print_c(std::ostream& os, size_t disc, std::unordered_set& printed, + size_t tabs = 0) const; + std::ostream& print_c_nested(std::ostream& os, size_t disc, size_t tabs, std::vector& toprint, + const NodePtr& node) const; + size_t depth() const; + double value(const double* disc, const double* cont, uint32_t action, size_t ndisc) const; + void consistent(size_t) const; + bool cost_intersect(const Node& other) const; + double midcost(const Node& other, double minval, double maxval) const; + bool operator==(const Node& other) const + { + if (_limit != other._limit) + return false; + if (_var != other._var) + return false; + if (_low != other._low) + return false; + if (_high != other._high) + return false; + return true; + } + bool operator<(const Node& other) const + { + if (_limit != other._limit) + return _limit < other._limit; + if (_var != other._var) + return _var < other._var; + if (_low != other._low) + return _low < other._low; + return _high < other._high; + } + NodePtr& operator[](bool b) { return b ? _high : _low; } + operator signature_t() const + { + if (is_leaf()) + return signature_t{_var, _limit, _low.get(), _high.get()}; + return signature_t{0, _cost, nullptr, nullptr}; + } +}; + +SimpleTree& SimpleTree::operator=(SimpleTree&&) noexcept = default; +SimpleTree::~SimpleTree() = default; + const std::vector& SimpleTree::actions() const { return _actions; } SimpleTree SimpleTree::parse(std::istream& input, bool simplify, bool subsumption, double accuracy) @@ -49,7 +120,7 @@ SimpleTree SimpleTree::parse(std::istream& input, bool simplify [[maybe_unused]] if (!raw.is_object()) throw base_error("Input JSON not well formatted"); - if (!raw["version"].is_number_float() || raw["version"].get() != 1.0) + if (!raw["version"].is_number() || raw["version"].get() != 1.0) throw base_error("Input version not supported"); if (!raw["type"].is_string() || raw["type"].get() != "state->regressor") @@ -63,7 +134,7 @@ SimpleTree SimpleTree::parse(std::istream& input, bool simplify [[maybe_unused]] // store actions SimpleTree tree; if (tree._root == nullptr) { - tree._root = std::make_shared(); + tree._root = std::make_shared(); tree._root->_limit = -std::numeric_limits::infinity(); tree._root->_cost = std::numeric_limits::infinity(); tree._root->_var = std::numeric_limits::max(); @@ -126,7 +197,7 @@ SimpleTree SimpleTree::parse(std::istream& input, bool simplify [[maybe_unused]] throw base_error("Action-index is out of bounds"); if (tree._root == nullptr) { - tree._root = std::make_shared(); + tree._root = std::make_shared(); tree._root->_limit = -std::numeric_limits::infinity(); tree._root->_cost = std::numeric_limits::infinity(); tree._root->_cost = (is_minimize ? 1 : -1) * std::numeric_limits::infinity(); @@ -150,7 +221,7 @@ SimpleTree SimpleTree::parse(std::istream& input, bool simplify [[maybe_unused]] return tree; } -std::pair SimpleTree::node_t::compute_min_max() const +std::pair SimpleTree::Node::compute_min_max() const { double mincost = std::numeric_limits::infinity(); double maxcost = -mincost; @@ -172,7 +243,7 @@ std::pair SimpleTree::node_t::compute_min_max() const return std::make_pair(mincost, maxcost); } -void SimpleTree::node_t::action_nodes(std::vector& nodes, uint32_t low, uint32_t high, uint32_t varid) +void SimpleTree::Node::action_nodes(std::vector& nodes, uint32_t low, uint32_t high, uint32_t varid) { if (_var != varid) { if (!is_leaf() || !(std::isnan(_cost) || std::isinf(_cost))) { @@ -189,7 +260,7 @@ void SimpleTree::node_t::action_nodes(std::vector& nodes, uint32_t low } } -void SimpleTree::node_t::subsumption_reduction(bool minimization, SimpleTree& parent) +void SimpleTree::Node::subsumption_reduction(bool minimization, SimpleTree& parent) { if (_var < parent._statevars.size()) { if (_low) @@ -201,11 +272,11 @@ void SimpleTree::node_t::subsumption_reduction(bool minimization, SimpleTree& pa return; } size_t prev_vals = std::numeric_limits::max(); - size_t p = 0; + size_t p [[maybe_unused]] = 0; while (true) { ++p; bool outer = false; - std::vector nodes; + std::vector nodes; action_nodes(nodes, 0, parent._actions.size() - 1, parent._statevars.size()); auto val = std::numeric_limits::infinity(); if (!minimization) @@ -234,9 +305,9 @@ void SimpleTree::node_t::subsumption_reduction(bool minimization, SimpleTree& pa outer |= n->check_tiles(n.get(), nodes, bounds, val, best, worst, minimization, parent._statevars.size() + 1); } - std::set> values; + std::set> values; { - auto nodes = std::vector(parent._actions.size()); + auto nodes = std::vector(parent._actions.size()); action_nodes(nodes, 0, parent._actions.size() - 1, parent._statevars.size()); for (auto& n : nodes) { if (n == nullptr) @@ -260,7 +331,7 @@ void SimpleTree::node_t::subsumption_reduction(bool minimization, SimpleTree& pa } } -void SimpleTree::node_t::set_ranks(std::unordered_map& values) +void SimpleTree::Node::set_ranks(std::unordered_map& values) { if (is_leaf()) { if (std::isinf(_cost) || std::isnan(_cost)) @@ -274,7 +345,7 @@ void SimpleTree::node_t::set_ranks(std::unordered_map& values) } } -void SimpleTree::node_t::get_ranks(std::set>& values, node_t* start) +void SimpleTree::Node::get_ranks(std::set>& values, Node* start) { if (is_leaf()) { if (std::isinf(_cost) || std::isnan(_cost)) @@ -288,10 +359,10 @@ void SimpleTree::node_t::get_ranks(std::set>& values, } } -bool SimpleTree::node_t::subsumes(const std::vector>& bounds, - std::vector>& obounds, const double val, - const bool minimization, size_t offset, double& best, - std::pair& closest) const +bool SimpleTree::Node::subsumes(const std::vector>& bounds, + std::vector>& obounds, const double val, + const bool minimization, size_t offset, double& best, + std::pair& closest) const { if (is_leaf()) { // TODO: continue in other trees here, maybe combination is @@ -339,9 +410,9 @@ bool SimpleTree::node_t::subsumes(const std::vector>& } } -bool SimpleTree::node_t::check_tiles(node_t* start, std::vector& nodes, - std::vector>& bounds, double val, double best_val, - double worst_val, bool minimization, size_t offset) +bool SimpleTree::Node::check_tiles(Node* start, std::vector& nodes, + std::vector>& bounds, double val, double best_val, + double worst_val, bool minimization, size_t offset) { auto obounds = bounds; if (is_leaf()) { @@ -382,7 +453,7 @@ bool SimpleTree::node_t::check_tiles(node_t* start, std::vector& nodes if (!is_leaf()) { double org = _limit; auto& bnd = bounds[_var - offset]; - node_ptr switchnode = nullptr; + NodePtr switchnode = nullptr; if (bnd.second <= _limit) { switchnode = _low; } @@ -435,7 +506,7 @@ bool SimpleTree::node_t::check_tiles(node_t* start, std::vector& nodes // check if one is leaf and sibiling has leaf on same side which // can be merged: double nc = 0; - node_ptr node, other; + NodePtr node, other; if (!_high->is_leaf() && _high->_var == _var && _high->_low->cost_intersect(*_low) && _low->is_leaf() && _high->_low->is_leaf() && std::isinf(_low->_cost) == std::isinf(_high->_low->_cost)) { node = _high; @@ -480,12 +551,12 @@ bool SimpleTree::node_t::check_tiles(node_t* start, std::vector& nodes return false; } -bool SimpleTree::node_t::cost_intersect(const node_t& other) const +bool SimpleTree::Node::cost_intersect(const Node& other) const { return _cost_bounds.first < other._cost_bounds.second && other._cost_bounds.first < _cost_bounds.second; } -double SimpleTree::node_t::midcost(const node_t& other, double minval, double maxval) const +double SimpleTree::Node::midcost(const Node& other, double minval, double maxval) const { if (_cost == other._cost) return _cost; @@ -505,8 +576,8 @@ double SimpleTree::node_t::midcost(const node_t& other, double minval, double ma return c; } -void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t action, SimpleTree& parent, size_t prefix, - bool minimize, double accuracy, std::vector& exactness) +void SimpleTree::Node::insert(std::vector& key, json& tree, size_t action, SimpleTree& parent, size_t prefix, + bool minimize, double accuracy, std::vector& exactness) { if ((tree.is_number() || tree.is_string()) && key.size() < prefix) { if (is_leaf()) { @@ -532,8 +603,8 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act if (std::isinf(_limit)) { // assert(std::isinf(_cost)); assert(_var == std::numeric_limits::max()); - _low = std::make_shared(); - _high = std::make_shared(); + _low = std::make_shared(); + _high = std::make_shared(); _low->_cost = _high->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); if (!std::isinf(_cost)) { std::swap(_low->_cost, _cost); @@ -607,8 +678,8 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act } else { assert(_low->_var > prefix); if (_limit > action) { - auto tmp = std::make_shared(); - tmp->_low = std::make_shared(); + auto tmp = std::make_shared(); + tmp->_low = std::make_shared(); tmp->_cost = tmp->_low->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); tmp->_high = shared_from_this(); tmp->_limit = action; @@ -630,8 +701,8 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act consistent(key.size() + 1); } else if (_high->_var > prefix) { assert(std::isinf(_high->_cost)); - _high->_low = std::make_shared(); - _high->_high = std::make_shared(); + _high->_low = std::make_shared(); + _high->_high = std::make_shared(); _high->_low->_cost = _high->_high->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); _high->_limit = action; @@ -643,8 +714,8 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act _high->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); consistent(key.size() + 1); } else if (_high->_var == prefix && _high->_limit > action) { - auto tmp = std::make_shared(); - tmp->_low = std::make_shared(); + auto tmp = std::make_shared(); + tmp->_low = std::make_shared(); tmp->_low->_cost = tmp->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); tmp->_parent = this; tmp->_high = _high; @@ -677,8 +748,8 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act auto branch = (*this)[b]; if (branch->_var != prefix) { // we need to inject a node here - auto next = std::make_shared(); - (*next)[false] = std::make_shared(); + auto next = std::make_shared(); + (*next)[false] = std::make_shared(); (*next)[false]->_cost = next->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); next->_parent = this; next->_var = prefix; @@ -703,7 +774,7 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act consistent(key.size() + 1); } -void SimpleTree::node_t::consistent(size_t prefix) const +void SimpleTree::Node::consistent(size_t prefix [[maybe_unused]]) const { return; #ifndef NDEBUG @@ -730,7 +801,7 @@ double SimpleTree::value(const double* disc, const double* cont, uint32_t action return std::numeric_limits::infinity(); } -double SimpleTree::node_t::value(const double* disc, const double* cont, uint32_t action, size_t ndisc) const +double SimpleTree::Node::value(const double* disc, const double* cont, uint32_t action, size_t ndisc) const { if (is_leaf()) return _cost; @@ -749,7 +820,7 @@ double SimpleTree::node_t::value(const double* disc, const double* cont, uint32_ return _high ? _high->value(disc, cont, action, ndisc) : _cost; } -SimpleTree::node_ptr SimpleTree::node_t::simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent) +SimpleTree::NodePtr SimpleTree::Node::simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent) { if (_low) _low = _low->simplify(make_dd, nodemap, parent); @@ -833,7 +904,7 @@ SimpleTree::node_ptr SimpleTree::node_t::simplify(bool make_dd, nodemap_t& nodem return shared_from_this(); } -/*void SimpleTree::node_t::rec_insert(double value, +/*void SimpleTree::Node::rec_insert(double value, std::vector>& handled, std::vector >& bounds, bool minimize) { auto nid = _var; assert(nid < bounds.size()); bool reset_low = false; bool reset_high = false; bool best = @@ -871,7 +942,7 @@ false; if( std::isnan(_cost) || (minimize && value < _cost) || } if(_low == nullptr) { - _low = std::make_shared(); + _low = std::make_shared(); _low->_var = nid; _low->_parent = this; _low->_limit = (!handled[nid].first ? bounds[nid].first : @@ -906,7 +977,7 @@ bounds[nid].second); } if(_high == nullptr) { - _high = std::make_shared(); + _high = std::make_shared(); _high->_parent = this; _high->_var = nid; _high->_limit = (!handled[nid].first ? bounds[nid].first : @@ -929,7 +1000,7 @@ handled[_var].first = false; if(reset_high) handled[_var].second = false; } if(_low == nullptr) { - _low = std::make_shared(); + _low = std::make_shared(); _low->_var = nid; _low->_parent = this; _low->_limit = (!handled[nid].first ? bounds[nid].first : @@ -937,7 +1008,7 @@ bounds[nid].second); } if(_high == nullptr) { - _high = std::make_shared(); + _high = std::make_shared(); _high->_parent = this; _high->_var = nid; _high->_limit = (!handled[nid].first ? bounds[nid].first : @@ -957,7 +1028,7 @@ std::ostream& SimpleTree::print(std::ostream& stream) const return stream; } -std::ostream& SimpleTree::node_t::print(std::ostream& out, size_t tabs) const +std::ostream& SimpleTree::Node::print(std::ostream& out, size_t tabs) const { for (size_t i = 0; i < tabs; ++i) out << "\t"; @@ -993,8 +1064,8 @@ std::ostream& SimpleTree::print_c(std::ostream& os, const std::string& name) con os << "\treturn 0 ; \n}\n"; return os; } - auto _idnode = std::unordered_map{}; - auto _nodeid = std::unordered_map{}; + auto _idnode = std::unordered_map{}; + auto _nodeid = std::unordered_map{}; _idnode[0] = _root.get(); _nodeid[_root.get()] = 0; @@ -1055,8 +1126,8 @@ std::ostream& SimpleTree::print_c(std::ostream& os, const std::string& name) con os << "\t// Disc = " << _statevars.size() << std::endl; os << "\t// Cont = " << _pointvars.size() << std::endl; os << "\t// Nodes = " << lid << std::endl; - /*std::unordered_set printed; - std::vector toprint; + /*std::unordered_set printed; + std::vector toprint; if(_root) _root->print_c_nested(stream, _statevars.size(), 1, toprint, _root); auto mm = _root->compute_min_max(); @@ -1087,7 +1158,7 @@ std::ostream& SimpleTree::print_c(std::ostream& os, const std::string& name) con return os; } -size_t SimpleTree::node_t::depth() const +size_t SimpleTree::Node::depth() const { if (_low && _high == nullptr) return 1 + _low->depth(); @@ -1098,15 +1169,15 @@ size_t SimpleTree::node_t::depth() const return 1 + std::max(_low->depth(), _high->depth()); } -std::ostream& SimpleTree::node_t::print_c(std::ostream& os, size_t disc, std::unordered_set& printed, - size_t tabs) const +std::ostream& SimpleTree::Node::print_c(std::ostream& os, size_t disc, std::unordered_set& printed, + size_t tabs) const { if (printed.count(this) > 0) return os; printed.insert(this); // for(size_t i = 0; i < tabs; ++i) stream << "\t"; os << "l" << this << ":\n"; - std::vector toprint; + std::vector toprint; if (is_leaf()) { // for(size_t i = 0; i < tabs+1; ++i) stream << "\t"; if (!std::isinf(_cost)) @@ -1155,8 +1226,8 @@ std::ostream& SimpleTree::node_t::print_c(std::ostream& os, size_t disc, std::un return os; } -std::ostream& SimpleTree::node_t::print_c_nested(std::ostream& os, size_t disc, size_t tabs, - std::vector& toprint, const node_ptr& node) const +std::ostream& SimpleTree::Node::print_c_nested(std::ostream& os, size_t disc, size_t tabs, + std::vector& toprint, const NodePtr& node) const { assert(node.get() == this); if (is_leaf() && std::isinf(_limit)) { diff --git a/src/SimpleTree.h b/src/SimpleTree.h index c0ec46e..fb0a37d 100644 --- a/src/SimpleTree.h +++ b/src/SimpleTree.h @@ -25,19 +25,20 @@ #ifndef SIMPLETREE_H #define SIMPLETREE_H -#include #include // includes iostream :-( #include #include -#include -#include #include class SimpleTree { public: - SimpleTree(const SimpleTree& orig) = default; + SimpleTree(const SimpleTree&) = delete; + SimpleTree(SimpleTree&&) noexcept = default; + SimpleTree& operator=(const SimpleTree&) = delete; + SimpleTree& operator=(SimpleTree&&) noexcept; + ~SimpleTree(); static SimpleTree parse(std::istream&, bool simplify = false, bool subsumption = false, double accuracy = 0); static SimpleTree parse(std::istream&, bool simplify, bool subsumption, double accuracy, std::vector& exactness); @@ -51,89 +52,24 @@ class SimpleTree const std::vector& continous_features() const { return _pointvars; } private: - struct node_t; - using node_ptr = std::shared_ptr; + struct Node; + using NodePtr = std::shared_ptr; struct signature_t { uint32_t _var{0}; double _limit{0}; - node_t* _low{nullptr}; - node_t* _high{nullptr}; + Node* _low{nullptr}; + Node* _high{nullptr}; } __attribute__((packed)); friend struct ptrie::byte_iterator; - using nodemap_t = ptrie::map; - using json = nlohmann::json; + using nodemap_t = ptrie::map; SimpleTree() = default; - struct node_t : std::enable_shared_from_this - { - uint32_t _var = std::numeric_limits::max(); - double _limit = -std::numeric_limits::infinity(); - double _cost = std::numeric_limits::infinity(); - std::pair _cost_bounds; - node_ptr _low; - node_ptr _high; - node_t* _parent{nullptr}; - void insert(std::vector& key, json& tree, size_t action, SimpleTree& parent, size_t prefix, - bool minimize, double accuracy, std::vector& exactness); - std::ostream& print(std::ostream& out, size_t tabs = 0) const; - bool is_leaf() const { return _low == nullptr && _high == nullptr; } - node_ptr simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent); - void subsumption_reduction(bool minimization, SimpleTree& parent); - void action_nodes(std::vector& nodes, uint32_t low, uint32_t high, uint32_t varid); - std::pair compute_min_max() const; - bool check_tiles(node_t* start, std::vector&, std::vector>& bounds, - double val, double minval, double maxval, bool minimization, size_t offset); - bool subsumes(const std::vector>& bounds, - std::vector>& obounds, double val, bool minimization, size_t offset, - double& best, std::pair& closest) const; - void get_ranks(std::set>& values, node_t* start); - void set_ranks(std::unordered_map& values); - std::ostream& print_c(std::ostream& os, size_t disc, std::unordered_set& printed, - size_t tabs = 0) const; - std::ostream& print_c_nested(std::ostream& os, size_t disc, size_t tabs, std::vector& toprint, - const node_ptr& node) const; - size_t depth() const; - double value(const double* disc, const double* cont, uint32_t action, size_t ndisc) const; - void consistent(size_t) const; - bool cost_intersect(const node_t& other) const; - double midcost(const node_t& other, double minval, double maxval) const; - bool operator==(const node_t& other) const - { - if (_limit != other._limit) - return false; - if (_var != other._var) - return false; - if (_low != other._low) - return false; - if (_high != other._high) - return false; - return true; - } - bool operator<(const node_t& other) const - { - if (_limit != other._limit) - return _limit < other._limit; - if (_var != other._var) - return _var < other._var; - if (_low != other._low) - return _low < other._low; - return _high < other._high; - } - node_ptr& operator[](bool b) { return b ? _high : _low; } - operator signature_t() const - { ///< support implicit conversion for map keys - if (is_leaf()) - return signature_t{_var, _limit, _low.get(), _high.get()}; - return signature_t{0, _cost, nullptr, nullptr}; - } - }; - std::vector _actions; std::vector _statevars; std::vector _pointvars; - node_ptr _root; + NodePtr _root; bool _is_minimization = true; }; @@ -155,7 +91,8 @@ struct ptrie::byte_iterator return sizeof(SimpleTree::signature_t); } - static constexpr bool continious() { return true; } + [[deprecated]] static constexpr bool continious() { return true; } + static constexpr bool continuous() { return true; } // add read_blob, write_blob }; diff --git a/src/ZonotopStrategy.cpp b/src/ZonotopStrategy.cpp index 22fc42b..b6b8107 100644 --- a/src/ZonotopStrategy.cpp +++ b/src/ZonotopStrategy.cpp @@ -118,7 +118,7 @@ ZonotopStrategy ZonotopStrategy::parse(std::istream& input) bool ZonotopStrategy::add(std::vector& bounds, size_t state) { if (_root == nullptr) { - _root = std::make_shared(); + _root = std::make_shared(); _root->_varid = 0; _root->_limit = bounds[0]._lower; } @@ -126,8 +126,8 @@ bool ZonotopStrategy::add(std::vector& bounds, size_t state) return rec_insert(_root.get(), bounds, handled, state); } -bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, - std::vector>& handled, size_t state) +bool ZonotopStrategy::rec_insert(Node* node, std::vector& bounds, std::vector>& handled, + size_t state) { auto nid = node->_varid; assert(nid < bounds.size()); @@ -152,7 +152,7 @@ bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, } if (node->_low == nullptr) { - node->_low = std::make_shared(); + node->_low = std::make_shared(); node->_low->_varid = nid; node->_low->_parent = node; node->_low->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); @@ -179,7 +179,7 @@ bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, } if (node->_high == nullptr) { - node->_high = std::make_shared(); + node->_high = std::make_shared(); node->_high->_parent = node; node->_high->_varid = nid; node->_high->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); @@ -189,13 +189,13 @@ bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, handled[node->_varid].first = false; } else { if (node->_low == nullptr) { - node->_low = std::make_shared(); + node->_low = std::make_shared(); node->_low->_varid = nid; node->_low->_parent = node; node->_low->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); } if (node->_high == nullptr) { - node->_high = std::make_shared(); + node->_high = std::make_shared(); node->_high->_parent = node; node->_high->_varid = nid; node->_high->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); @@ -207,7 +207,7 @@ bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, return col; } -void ZonotopStrategy::try_merge(node_t* node, size_t state) +void ZonotopStrategy::try_merge(Node* node, size_t state) { if (node == nullptr) return; @@ -234,7 +234,7 @@ void ZonotopStrategy::try_merge(node_t* node, size_t state) } } -std::ostream& ZonotopStrategy::node_t::print(std::ostream& os, const ZonotopStrategy* parent, size_t tabs) const +std::ostream& ZonotopStrategy::Node::print(std::ostream& os, const ZonotopStrategy* parent, size_t tabs) const { os << Tabs{tabs}; os << "{\"var\":" << _varid << ",\"bound\":" << _limit; @@ -365,7 +365,7 @@ double ZonotopStrategy::get_max(size_t dimen) const { return _root->get_max(dime double ZonotopStrategy::get_min(size_t dimen) const { return _root->get_min(dimen); } -double ZonotopStrategy::node_t::get_max(size_t dimen) const +double ZonotopStrategy::Node::get_max(size_t dimen) const { double maxval = _varid == dimen ? _limit : -std::numeric_limits::infinity(); if (_high) @@ -375,7 +375,7 @@ double ZonotopStrategy::node_t::get_max(size_t dimen) const return maxval; } -double ZonotopStrategy::node_t::get_min(size_t dimen) const +double ZonotopStrategy::Node::get_min(size_t dimen) const { double minval = _varid == dimen ? _limit : std::numeric_limits::infinity(); if (_high) @@ -420,7 +420,7 @@ std::ostream& ZonotopStrategy::print_c(std::ostream& stream, std::string functio return stream; } -std::ostream& ZonotopStrategy::node_t::print_c(std::ostream& os, size_t tabs) const +std::ostream& ZonotopStrategy::Node::print_c(std::ostream& os, size_t tabs) const { if (_low != nullptr || !_low_patterns.empty()) { os << Tabs{tabs}; diff --git a/src/ZonotopStrategy.h b/src/ZonotopStrategy.h index b6dd261..a11303a 100644 --- a/src/ZonotopStrategy.h +++ b/src/ZonotopStrategy.h @@ -35,6 +35,9 @@ class ZonotopStrategy { public: + ~ZonotopStrategy() = default; + ZonotopStrategy(const ZonotopStrategy&) = delete; + ZonotopStrategy& operator=(const ZonotopStrategy&) = delete; ZonotopStrategy(ZonotopStrategy&&) = default; ZonotopStrategy& operator=(ZonotopStrategy&&) = default; static ZonotopStrategy parse(std::istream&); @@ -51,23 +54,22 @@ class ZonotopStrategy ZonotopStrategy() = default; ptrie::set_stable<> _states; size_t _max_length = 0; - struct node_t; - using node_ptr = std::shared_ptr; - struct node_t : std::enable_shared_from_this + struct Node : std::enable_shared_from_this { + using Ptr = std::shared_ptr; uint32_t _varid = 0; double _limit = 0; - node_ptr _low = nullptr; - node_ptr _high = nullptr; + Ptr _low = nullptr; + Ptr _high = nullptr; std::vector _low_patterns; std::vector _high_patterns; - node_t* _parent{nullptr}; + Node* _parent{nullptr}; std::ostream& print(std::ostream& os, const ZonotopStrategy* parent, size_t tabs = 0) const; std::ostream& print_c(std::ostream& os, size_t tabs = 0) const; double get_min(size_t dimen) const; double get_max(size_t dimen) const; }; - node_ptr _root; + Node::Ptr _root; struct bound_t { double _lower = 0; @@ -77,9 +79,9 @@ class ZonotopStrategy }; static std::vector get_bounds(const std::string& zonotop, size_t& vars); bool add(std::vector& bounds, size_t state); - bool rec_insert(node_t* node, std::vector& bounds, std::vector>& handled, + bool rec_insert(Node* node, std::vector& bounds, std::vector>& handled, size_t state); - void try_merge(node_t* node, size_t state); + void try_merge(Node* node, size_t state); }; #endif /* ZONOTOPSTRATEGY_H */ diff --git a/src/libstrategyConfig.cmake.in b/src/libstrategyConfig.cmake.in new file mode 100644 index 0000000..9b57da7 --- /dev/null +++ b/src/libstrategyConfig.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ +find_dependency(ptrie) +include("${CMAKE_CURRENT_LIST_DIR}/libstrategyTargets.cmake") +check_required_components(libstrategy) diff --git a/src/libz2s.cpp b/src/libz2s.cpp index f2a3174..f80117c 100644 --- a/src/libz2s.cpp +++ b/src/libz2s.cpp @@ -1,16 +1,16 @@ /* * Copyright (C) 2020 Peter G. Jensen - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ @@ -24,16 +24,14 @@ void* parse_strategy(const char* file) { - if(file == nullptr) - { + if (file == nullptr) { std::cerr << "Input file must be specified" << std::endl; exit(-1); return nullptr; } std::string fn(file); std::ifstream instream(fn); - if(instream.fail()) - { + if (instream.fail()) { std::cerr << "Could not open infile for reading : " << fn << std::endl; exit(-1); return nullptr; @@ -47,13 +45,13 @@ void* parse_strategy(const char* file) void destroy_strategy(void* ptr) { std::cerr << "destroy " << ptr << std::endl; - delete (ZonotopStrategy*)ptr; + delete static_cast(ptr); } int num_patterns(void* ptr) { - std::cerr << "num_patterns " << ptr << std::endl; - auto pl = ((ZonotopStrategy*)ptr)->num_patterns(); + std::cerr << "num_patterns " << ptr << std::endl; + auto pl = static_cast(ptr)->num_patterns(); std::cerr << pl << std::endl; return pl; } @@ -61,50 +59,47 @@ int num_patterns(void* ptr) int max_pattern_length(void* ptr) { std::cerr << "max_pattern_length " << ptr << std::endl; - auto pl = ((ZonotopStrategy*)ptr)->max_pattern_length(); + auto pl = static_cast(ptr)->max_pattern_length(); std::cerr << pl << std::endl; return pl; } void active(void* ptr, const double* sample, bool* write) { -// std::cerr << "active " << ptr << std::endl; - ((ZonotopStrategy*)ptr)->active(sample, write); + // std::cerr << "active " << ptr << std::endl; + static_cast(ptr)->active(sample, write); } int get_pattern(void* ptr, int el, int* write) { -// std::cerr << "get_pattern " << ptr << std::endl; - return ((ZonotopStrategy*)ptr)->get_pattern(el, write); + // std::cerr << "get_pattern " << ptr << std::endl; + return static_cast(ptr)->get_pattern(el, write); } double get_min(void* ptr, int dimen) { - auto val = ((ZonotopStrategy*)ptr)->get_min(dimen); -std::cerr << "min for " << dimen << " is " << val << std::endl; + auto val = static_cast(ptr)->get_min(dimen); + std::cerr << "min for " << dimen << " is " << val << std::endl; return val; } double get_max(void* ptr, int dimen) { - auto val = ((ZonotopStrategy*)ptr)->get_max(dimen); - std::cerr << "max for " << dimen << " is " << val << std::endl; - return val; + auto val = static_cast(ptr)->get_max(dimen); + std::cerr << "max for " << dimen << " is " << val << std::endl; + return val; } - void* parse_learned(const char* file) { - if(file == nullptr) - { + if (file == nullptr) { std::cerr << "Input file must be specified" << std::endl; exit(-1); return nullptr; } std::string fn(file); std::ifstream instream(fn); - if(instream.fail()) - { + if (instream.fail()) { std::cerr << "Could not open infile for reading : " << fn << std::endl; exit(-1); return nullptr; @@ -115,12 +110,9 @@ void* parse_learned(const char* file) return res; } -void destroy_learned(void* ptr) -{ - delete (SimpleTree*)ptr; -} +void destroy_learned(void* ptr) { delete static_cast(ptr); } double weight(void* ptr, const double* disc, const double* cont, int a) { - return ((SimpleTree*)ptr)->value(disc, cont, a); -} \ No newline at end of file + return static_cast(ptr)->value(disc, cont, a); +} diff --git a/src/main.cpp b/src/main.cpp index d62bb68..89b5225 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,21 +1,21 @@ /* * Copyright (C) 2020 Peter G. Jensen - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ -/* +/* * Author: Peter G. Jensen * * Created on December 13, 2018, 2:11 PM @@ -23,77 +23,140 @@ #include "ZonotopStrategy.h" -#include - #include #include +#include #include #include +#include +#include +#include -namespace po = boost::program_options; +namespace { -int main(int argc, char **argv) { +void print_help(const char* prog) +{ + std::cout << "Usage: " << prog + << " [options]\n\n" + "Options:\n" + " -h, --help produce help message\n" + " -i, --input Input of synthesized controller.\n" + " -l, --learned Input of learned controller.\n" + " -c, --choice Output function name for choice.\n" + " -p, --pattern Output function name for pattern.\n" + " -a, --accuracy \n" + " -e, --exactness ...\n"; +} + +// A token counts as a value (not the start of a new option) unless it +// looks like "-x" or "--xxx" (as opposed to a negative number like "-1.5"). +bool is_option(std::string_view s) +{ + return s.size() > 1 && s[0] == '-' && !(std::isdigit(static_cast(s[1])) || s[1] == '.'); +} - po::options_description opts; +#if defined(__cpp_lib_to_chars) +double to_double(std::string_view s) +{ + double value; + auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); + if (ec != std::errc() || ptr != s.data() + s.size()) { + std::cerr << "Invalid number: " << s << std::endl; + std::exit(-1); + } + return value; +} +#else +// AppleClang's libc++ does not implement the floating-point std::from_chars +// overloads yet, so fall back to std::stod there. +double to_double(std::string_view s) +{ + std::string str(s); + size_t pos = 0; + double value = 0; + try { + value = std::stod(str, &pos); + } catch (...) { + pos = 0; + } + if (pos != str.size()) { + std::cerr << "Invalid number: " << s << std::endl; + std::exit(-1); + } + return value; +} +#endif + +} // namespace + +int main(int argc, char** argv) +{ std::string infile; std::string learned; std::string learned_name = "choice"; std::string pattern_name = "pattern"; double accuracy = 0; std::vector exactness; - opts.add_options() - ("help,h", "produce help message") - ("input,i", po::value(&infile), - "Input of synthesized controller.") - ("learned,l", po::value(&learned), - "Input of learned controller.") - ("choice,c", po::value(&learned_name), - "Output function name for choice.") - ("pattern,p", po::value(&pattern_name), - "Output function name for pattern.") - ("accuracy,a", po::value(&accuracy)) - ("exactness,e", po::value>(&exactness)->multitoken()) - ; - po::variables_map vm; - po::store(po::parse_command_line(argc, argv, opts), vm); - po::notify(vm); - - if (vm.count("help")) { - std::cout << opts << "\n"; - return 1; + std::vector args(argv + 1, argv + argc); + for (size_t i = 0; i < args.size(); ++i) { + std::string_view arg = args[i]; + auto next_value = [&]() -> std::string_view { + if (i + 1 >= args.size() || is_option(args[i + 1])) { + std::cerr << "Missing value for option " << arg << std::endl; + std::exit(-1); + } + return args[++i]; + }; + if (arg == "-h" || arg == "--help") { + print_help(argv[0]); + return 1; + } else if (arg == "-i" || arg == "--input") { + infile = next_value(); + } else if (arg == "-l" || arg == "--learned") { + learned = next_value(); + } else if (arg == "-c" || arg == "--choice") { + learned_name = next_value(); + } else if (arg == "-p" || arg == "--pattern") { + pattern_name = next_value(); + } else if (arg == "-a" || arg == "--accuracy") { + accuracy = to_double(next_value()); + } else if (arg == "-e" || arg == "--exactness") { + while (i + 1 < args.size() && !is_option(args[i + 1])) { + exactness.push_back(to_double(args[++i])); + } + } else { + std::cerr << "Unknown option: " << arg << std::endl; + print_help(argv[0]); + return -1; + } } std::cout << "#include \"sub_ctrl.h\"\n"; - if(!infile.empty()) - { + if (!infile.empty()) { std::ifstream instream(infile); - if(instream.fail()) - { + if (instream.fail()) { std::cerr << "Could not open infile for reading : " << infile << std::endl; - return -1; + return -1; } auto strategy = ZonotopStrategy::parse(instream); - std::cout << "// From \"" << infile << "\"" << std::endl; + std::cout << "// From \"" << infile << "\"" << std::endl; strategy.print_c(std::cout, pattern_name); - } - if(learned.empty()) - { + } + if (learned.empty()) { return 0; } std::ifstream lstream(learned); - if(lstream.fail()) - { + if (lstream.fail()) { std::cerr << "Could not open learned for reading : " << learned << std::endl; - return -1; + return -1; } - std::cout << "// From \"" << learned << "\"" << std::endl; + std::cout << "// From \"" << learned << "\"" << std::endl; std::cerr << "EXACTNESS "; - for(auto e : exactness) - std::cerr << e<< " , "; + for (auto e : exactness) + std::cerr << e << " , "; std::cerr << std::endl; auto learned_strategy = SimpleTree::parse(lstream, true, true, accuracy, exactness); - //strategy.filter(learned_strategy); + // strategy.filter(learned_strategy); learned_strategy.print_c(std::cout, learned_name); return 0; } - diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 620da41..010c7c4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,19 +1,17 @@ -find_package (Boost COMPONENTS unit_test_framework REQUIRED) -include_directories (${CMAKE_SOURCE_DIR}/src) +include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (utilities_test utilities_test.cpp) add_test(NAME utilities_test COMMAND utilities_test) - add_executable (SimpleTree_test SimpleTree_test.cpp) -target_link_libraries(SimpleTree_test PRIVATE SimpleTree Boost::unit_test_framework) +target_link_libraries(SimpleTree_test PRIVATE SimpleTree doctest::doctest_with_main) add_test(NAME SimpleTree_test COMMAND SimpleTree_test) add_executable (unordered_load unordered_load.cpp) -target_link_libraries(unordered_load PRIVATE strategy Boost::unit_test_framework) +target_link_libraries(unordered_load PRIVATE strategy doctest::doctest_with_main) add_test(NAME unordered_load COMMAND unordered_load) add_executable (inconsistent_lookup inconsistent_lookup.cpp) -target_link_libraries(inconsistent_lookup PRIVATE strategy Boost::unit_test_framework) +target_link_libraries(inconsistent_lookup PRIVATE strategy doctest::doctest_with_main) add_test(NAME inconsistent_lookup COMMAND inconsistent_lookup) set_tests_properties(inconsistent_lookup PROPERTIES ENVIRONMENT STRATEGY_DIR=${CMAKE_CURRENT_SOURCE_DIR}/strategies) diff --git a/test/SimpleTree_test.cpp b/test/SimpleTree_test.cpp index b5fce7b..bdd7b42 100644 --- a/test/SimpleTree_test.cpp +++ b/test/SimpleTree_test.cpp @@ -1,46 +1,44 @@ -#define BOOST_TEST_MODULE UnorderedLoad - #include "SimpleTree.h" #include "errors.h" #include "utilities.hpp" -#include +#include -BOOST_AUTO_TEST_CASE(SimpleTreeParseKey) +TEST_CASE("SimpleTree::parse_key") { const auto res_blank = parse_key("()"); - BOOST_CHECK(res_blank.empty()); + CHECK(res_blank.empty()); const auto res_one = parse_key("(1)"); - BOOST_REQUIRE_EQUAL(res_one.size(), 1); - BOOST_CHECK_EQUAL(res_one[0], 1); + REQUIRE(res_one.size() == 1); + CHECK(res_one[0] == 1); const auto res_two = parse_key("(2,1)"); - BOOST_REQUIRE_EQUAL(res_two.size(), 2); - BOOST_CHECK_EQUAL(res_two[0], 2); - BOOST_CHECK_EQUAL(res_two[1], 1); + REQUIRE(res_two.size() == 2); + CHECK(res_two[0] == 2); + CHECK(res_two[1] == 1); const auto res_three = parse_key("(3,2,1)"); - BOOST_REQUIRE_EQUAL(res_three.size(), 3); - BOOST_CHECK_EQUAL(res_three[0], 3); - BOOST_CHECK_EQUAL(res_three[1], 2); - BOOST_CHECK_EQUAL(res_three[2], 1); + REQUIRE(res_three.size() == 3); + CHECK(res_three[0] == 3); + CHECK(res_three[1] == 2); + CHECK(res_three[2] == 1); const auto res_float = parse_key("(3.141)"); - BOOST_REQUIRE_EQUAL(res_float.size(), 1); - BOOST_CHECK_EQUAL(res_float[0], 3.141); + REQUIRE(res_float.size() == 1); + CHECK(res_float[0] == 3.141); const auto res_floats = parse_key("(4.3,2.1)"); - BOOST_REQUIRE_EQUAL(res_floats.size(), 2); - BOOST_CHECK_EQUAL(res_floats[0], 4.3); - BOOST_CHECK_EQUAL(res_floats[1], 2.1); + REQUIRE(res_floats.size() == 2); + CHECK(res_floats[0] == 4.3); + CHECK(res_floats[1] == 2.1); // a few negative tests: - BOOST_CHECK_THROW(parse_key(""), base_error); - BOOST_CHECK_THROW(parse_key("1"), base_error); - BOOST_CHECK_THROW(parse_key(")"), base_error); - BOOST_CHECK_THROW(parse_key("("), base_error); - BOOST_CHECK_THROW(parse_key("(1"), base_error); - BOOST_CHECK_THROW(parse_key("(2,"), base_error); - BOOST_CHECK_THROW(parse_key("(2,1"), base_error); + CHECK_THROWS_AS(parse_key(""), base_error); + CHECK_THROWS_AS(parse_key("1"), base_error); + CHECK_THROWS_AS(parse_key(")"), base_error); + CHECK_THROWS_AS(parse_key("("), base_error); + CHECK_THROWS_AS(parse_key("(1"), base_error); + CHECK_THROWS_AS(parse_key("(2,"), base_error); + CHECK_THROWS_AS(parse_key("(2,1"), base_error); } diff --git a/test/inconsistent_lookup.cpp b/test/inconsistent_lookup.cpp index bd20e7a..f311314 100644 --- a/test/inconsistent_lookup.cpp +++ b/test/inconsistent_lookup.cpp @@ -15,43 +15,42 @@ * along with this program. If not, see . */ -#define BOOST_TEST_MODULE UnorderedLoad - #include "SimpleTree.h" -#include +#include + #include +#include -BOOST_AUTO_TEST_CASE(DirectoryTest) { BOOST_REQUIRE(getenv("STRATEGY_DIR")); } +TEST_SUITE_BEGIN("Unordered Load"); -BOOST_AUTO_TEST_CASE(Inconsistent1) -{ - std::string strategy = getenv("STRATEGY_DIR"); - strategy += "/inconsistent1.strategy"; - std::ifstream in(strategy); - auto tree = SimpleTree::parse(in, false, false); - double vars[] = {10}; - auto act18 = tree.value(vars, nullptr, 0); - auto act19 = tree.value(vars, nullptr, 1); - BOOST_REQUIRE_LT(act18, act19); -} +namespace fs = std::filesystem; -BOOST_AUTO_TEST_CASE(Inconsistent1Simplify) +TEST_CASE("Inconsistent1") { - std::string strategy = getenv("STRATEGY_DIR"); - strategy += "/inconsistent1.strategy"; - std::ifstream in(strategy); - auto tree = SimpleTree::parse(in, true, false); - double vars[] = {10}; - BOOST_REQUIRE_LT(tree.value(vars, nullptr, 0), tree.value(vars, nullptr, 1)); -} + const auto strategy_dir = getenv("STRATEGY_DIR"); + REQUIRE(strategy_dir != nullptr); -BOOST_AUTO_TEST_CASE(Inconsistent1SimplifySubsumption) -{ - std::string strategy = getenv("STRATEGY_DIR"); - strategy += "/inconsistent1.strategy"; - std::ifstream in(strategy); - auto tree = SimpleTree::parse(in, true, true); + const auto strategy_path = fs::path{strategy_dir} / "inconsistent1.strategy"; + auto in = std::ifstream{strategy_path}; double vars[] = {10}; - BOOST_REQUIRE_LT(tree.value(vars, nullptr, 0), tree.value(vars, nullptr, 1)); + SUBCASE("Plain") + { + auto tree = SimpleTree::parse(in, false, false); + auto act18 = tree.value(vars, nullptr, 0); + auto act19 = tree.value(vars, nullptr, 1); + REQUIRE(act18 < act19); + } + SUBCASE("Simplify") + { + auto tree = SimpleTree::parse(in, true, false); + REQUIRE(tree.value(vars, nullptr, 0) < tree.value(vars, nullptr, 1)); + } + SUBCASE("Simplify Subsumption") + { + auto tree = SimpleTree::parse(in, true, true); + REQUIRE(tree.value(vars, nullptr, 0) < tree.value(vars, nullptr, 1)); + } } + +TEST_SUITE_END(); diff --git a/test/unordered_load.cpp b/test/unordered_load.cpp index 22ec8f6..83bbcf6 100644 --- a/test/unordered_load.cpp +++ b/test/unordered_load.cpp @@ -15,11 +15,13 @@ * along with this program. If not, see . */ -#define BOOST_TEST_MODULE UnorderedLoad - #include "SimpleTree.h" -#include +#include + +#include + +TEST_SUITE_BEGIN("unordered_load"); const std::string simple_unordered_strategy = "{\"version\":1.0,\"type\":\"state->regressor\",\"representation\":\"map\"," @@ -320,36 +322,38 @@ const std::string unordered_strategy = "{\"version\":1.0,\"type\":\"state->regre " }" "}"; -BOOST_AUTO_TEST_CASE(SimpleUnorderedKeyLoad) +TEST_CASE("simple_unordered_strategy key load") { auto is = std::istringstream{simple_unordered_strategy}; auto strategy = SimpleTree::parse(is, false, false, 0); double disc[1] = {15.0}; - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 2.855407148131672); - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 1.352930166302546); + CHECK(strategy.value(disc, nullptr, 11) == 2.855407148131672); + CHECK(strategy.value(disc, nullptr, 12) == 1.352930166302546); disc[0] = 210; - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 1.009248500699941); - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 1.028772620412916); + CHECK(strategy.value(disc, nullptr, 11) == 1.009248500699941); + CHECK(strategy.value(disc, nullptr, 12) == 1.028772620412916); disc[0] = 30; - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 1.294636921229531); - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 3.091107032150207); + CHECK(strategy.value(disc, nullptr, 11) == 1.294636921229531); + CHECK(strategy.value(disc, nullptr, 12) == 3.091107032150207); } -BOOST_AUTO_TEST_CASE(UnorderedKeyLoad) +TEST_CASE("unordered_strategy key load") { auto is = std::stringstream{unordered_strategy}; auto strategy = SimpleTree::parse(is, false, false, 0); double disc[1] = {15.0}; - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 2.855407148131672); - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 1.352930166302546); + CHECK(strategy.value(disc, nullptr, 11) == 2.855407148131672); + CHECK(strategy.value(disc, nullptr, 12) == 1.352930166302546); disc[0] = 210; - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 1.009248500699941); - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 1.028772620412916); + CHECK(strategy.value(disc, nullptr, 11) == 1.009248500699941); + CHECK(strategy.value(disc, nullptr, 12) == 1.028772620412916); disc[0] = 30; - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 1.294636921229531); - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 3.091107032150207); + CHECK(strategy.value(disc, nullptr, 11) == 1.294636921229531); + CHECK(strategy.value(disc, nullptr, 12) == 3.091107032150207); } + +TEST_SUITE_END();