diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eccca0e..beebc8b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,11 +50,9 @@ jobs: - name: Build and test run: | export CTEST_OUTPUT_ON_FAILURE=1 - mkdir build - cd build - cmake -DTESTING=yes -DSTATIC=$STATIC -DASAN=${{ matrix.sanitizers }} -DUBSAN=${{ matrix.sanitizers }} -DCMAKE_BUILD_TYPE=Release -DENABLE_DBM_NEW=${{ matrix.newdbm }} .. - cmake --build . - ctest + cmake -DTESTING=yes -DSTATIC=$STATIC -DASAN=${{ matrix.sanitizers }} -DUBSAN=${{ matrix.sanitizers }} -DCMAKE_BUILD_TYPE=Release -DENABLE_DBM_NEW=${{ matrix.newdbm }} -DCMAKE_PREFIX_PATH=$(pwd)/local -B build -S . + cmake --build build + (cd build/test ; ctest) build-win-native: runs-on: windows-latest steps: @@ -66,10 +64,9 @@ jobs: bash ./getlibs.sh - name: Build and test run: | - mkdir build + cmake -DCMAKE_BUILD_TYPE=Release -DTESTING=yes "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DCMAKE_PREFIX_PATH=$(pwd)/local -B build -S . + cmake --build build --config Release cd build - cmake -DCMAKE_BUILD_TYPE=Release -DTESTING=yes "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" .. - cmake --build . --config Release ctest --output-on-failure -C Release build-macos: runs-on: macos-latest @@ -84,11 +81,9 @@ jobs: - name: Build and test run: | export CTEST_OUTPUT_ON_FAILURE=1 - mkdir build - cd build - cmake -DTESTING=yes -DASAN=${{ matrix.sanitizers }} -DUBSAN=${{ matrix.sanitizers }} -DCMAKE_BUILD_TYPE=Release .. - cmake --build . - ctest + cmake -DTESTING=yes -DASAN=${{ matrix.sanitizers }} -DUBSAN=${{ matrix.sanitizers }} -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$(pwd)/local -B build -S . + cmake --build build + (cd build/test ; ctest) build-nix: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index c1f5af6..5b35e7c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /build +/build-* /cmake-build-* -/libs +/local compile_commands.json /.idea diff --git a/CMakeLists.txt b/CMakeLists.txt index b672238..2598922 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,10 +3,11 @@ project(UDBM VERSION 2.0.11 LANGUAGES CXX C) include(CMakePackageConfigHelpers) include(GNUInstallDirs) -option(TESTING OFF) -option(STATIC OFF) -option(UBSAN OFF) -option(ASAN OFF) +option(TESTING "Uint tests" OFF) +option(STATIC "Static linking" OFF) +option(SPP "Stack-Smashing Protector" OFF) +option(UBSAN "Undefined Behavior Sanitizer" OFF) +option(ASAN "Address Sanitizer" OFF) cmake_policy(SET CMP0048 NEW) # project() command manages VERSION variables set(CMAKE_CXX_STANDARD 17) @@ -15,30 +16,29 @@ set(UDBM_VERSION "${PACKAGE_VERSION}") set(ENABLE_STORE_MINGRAPH 1) CONFIGURE_FILE("src/config.h.cmake" "include/dbm/config.h") -if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - add_compile_options(-Wpedantic -Wall -Wextra) -elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - add_compile_options(/W4) -endif() - -set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${CMAKE_CURRENT_SOURCE_DIR}/libs") find_package(xxHash 0.8.0 CONFIG REQUIRED) find_package(UUtils 1.1.1 REQUIRED COMPONENTS base hash debug) -add_library(UDBM src/DBMAllocator.cpp src/dbm.c src/fed_dbm.cpp src/mingraph.c src/mingraph_read.c src/partition.cpp src/print.cpp src/gen.c src/mingraph_cache.cpp src/mingraph_relation.c src/pfed.cpp src/fed.cpp src/infimum.cpp src/mingraph_equal.c src/mingraph_write.c src/priced.cpp) -target_link_libraries(UDBM UUtils::base UUtils::udebug UUtils::hash) +if (SSP) + message(STATUS "Enabled Stack Smashing Protector") + add_compile_options(-fstack-protector) + add_link_options(-fstack-protector) +endif(SSP) -if (ASAN) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -fsanitize=address -fno-omit-frame-pointer") +if (UBSAN OR ASAN) + add_compile_options(-fno-omit-frame-pointer) + add_link_options(-fno-omit-frame-pointer) endif() if (UBSAN) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined") - set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined") -endif() - + message(STATUS "Enabled Undefined Behavior Sanitizer") + add_compile_options(-fsanitize=undefined) + add_link_options(-fsanitize=undefined) +endif(UBSAN) +if (ASAN) + message(STATUS "Enabled Address Sanitizer") + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) +endif(ASAN) if(STATIC) set(CMAKE_CXX_STANDARD_LIBRARIES "-static-libgcc -static-libstdc++ -lwsock32 -lws2_32 ${CMAKE_CXX_STANDARD_LIBRARIES}") @@ -46,31 +46,21 @@ if(STATIC) endif(STATIC) if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - add_compile_options(-Werror=vla) + add_compile_options(-Wpedantic -Wall -Wextra -Werror=vla -Wno-unused-parameter) +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/W4) endif() -target_include_directories(UDBM - PRIVATE - # where the library itself will look for its internal headers - ${CMAKE_CURRENT_SOURCE_DIR}/src - ${CMAKE_CURRENT_BINARY_DIR}/include - PUBLIC - # where top-level project will look for the library's public headers - $ - # where external projects will look for the library's public headers - $ -) - include_directories(${CMAKE_CURRENT_BINARY_DIR}/include) +add_subdirectory("src") + if(TESTING) enable_testing() add_subdirectory("test") endif(TESTING) write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/UDBMConfigVersion.cmake VERSION ${PACKAGE_VERSION} COMPATIBILITY SameMajorVersion) - - install(DIRECTORY include DESTINATION .) install(TARGETS UDBM EXPORT UDBMConfig LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(EXPORT UDBMConfig DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/UDBM/) diff --git a/README.md b/README.md index b8907ba..e140a27 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,62 @@ # UPPAAL DBM Library -UDBM is a DBM library developed for the UPPAAL project. +DBMs [[dill89, rokicki93, lpw:fct95, bengtsson02]](#-References) are efficient data structures to represent clock constraints in timed automata [[ad90]](#-References). +They are used in UPPAAL [[lpy97, by04, bdl04]](#-References) as the core data structure to represent time. +The library features all the common operations such as up (delay, or future), down (past), general updates, different extrapolation functions, etc. on DBMs and federations. +The library also supports subtractions. +The API is in C and C++. The C++ part uses active clocks and hides (to some extent) memory management. -DBM stands for Difference Bound Matrix and used to capture and reason about clock constraints described in Timed Automata models. + * Reliable
+ The DBM library has an extensive test suite with an extra alternative implementation of the algorithms. This implementation has also been tested on countless case studies. + * Performant
+ The DBM library is the fruit of many years of development of UPPAAL, bringing to the mainstream efficient algorithms to manipulate DBMs. The API is available in C and C++. + * Current
+ The DBM library is based on the latest internal development version of UPPAAL, containing the latest performance and language improvements. The general form of the constraints is the following: `x_i - x_j <=> c_ij`, where `x_i` and `x_j` are clocks (one of them can be "zero clock" which is always zero) and `<=` can be one of {`<`, `<=`, `=`, `>=`, `>`} and `c_ij` is an integer constant. For more details please see [wiki pages](https://github.com/UPPAALModelChecker/UDBM/wiki). -## Build -The following packages need to be installed: - * cmake - * gcc - * xxHash +## Build +The following packages need to be installed: + * [bash](https://www.gnu.org/software/bash/) + * [cmake](https://cmake.org/) + * [GCC](https://gcc.gnu.org/), [clang](https://clang.llvm.org/) or other C++17 compiler. + * [xxHash](https://github.com/Cyan4973/xxHash) * [UUtils](https://github.com/UPPAALModelChecker/UUtils) - * doctest (test only) - * boost (test only) + * [doctest](https://github.com/doctest/doctest) (just for unit tests) -Compile source: -```sh -cmake -B build/ -DCMAKE_BUILD_TYPE=Release -cmake --build build/ +The dependencies can be installed locally into `local` directory by running `getlibs.sh`: +```shell +./getlibs.sh ``` -You can also insall dependencies locally by running: +Compile source with **release** optimizations: +```shell +cmake -B build-release -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$PWD/local +cmake --build build-release ``` -./getlibs.sh + +Compile source with **release** optimizations and **unit tests**: +```shell +cmake -B build-release -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$PWD/local -DTESTING=ON +cmake --build build-release +(cd build-release/test ; ctest) +``` + +Compile source with **debug**, **sanitizers** and **unit tests**: +```shell +cmake -B build-debug -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=$PWD/local -DTESTING=ON -DSSP=ON -DUBSAN=ON -DASAN=ON +cmake --build build-debug +(cd build-debug/test ; ctest) ``` + +## References + +* [[dill89]](https://doi.org/10.1007/3-540-52148-8_17) David L. Dill. _Timing Assumptions and Verification of Finite-State Concurrent Systems._ LNCS 407\. Springer Berlin 1989, pp 197-212. +* [rokicki93] Tomas Gerhard Rokicki. _Representing and Modeling Digital Circuits._ Ph.D. thesis, Standford University 1993. +* [[lpw:fct95]](https://doi.org/10.1007/3-540-60249-6_41) Kim G. Larsen, Paul Pettersson, and Wang Yi. _Model-Checking for Real-Time Systems._ Fundamentals of Computation Theory 1995, LNCS 965 pages 62-88. +* [[bengtsson02]](http://uu.diva-portal.org/smash/record.jsf?pid=diva2:161779) Johan Bengtsson. _Clocks, DBM, and States in Timed Systems._ Ph.D. thesis, Uppsala University 2002. +* [[ad90]](https://doi.org/10.1007/BFb0032042) Rajeev Alur and David L. Dill. _Automata for Modeling Real-Time Systems._ International Colloquium on Algorithms, Languages, and Programming 1990, LNCS 443 pages 322-335. +* [[lpy97]](https://doi.org/10.1007/s100090050010) Kim G. Larsen, Paul Pettersson, and Wang Yi. _UPPAAL in a Nutshell._ International Journal on Software Tools for Technology Transfer , October 1997, number 1-2 pages 134-152. +* [[by04]](https://doi.org/10.1007/978-3-540-27755-2_3) Johan Bengtsson and Wang Yi. [_Timed Automata: Semantics, Algorithms and Tools._](https://homes.cs.aau.dk/~adavid/UDBM/materials/by04-bookchapter.pdf) Concurrency and Petri Nets 2004, LNCS 3098. +* [[bdl04]](https://www.it.uu.se/research/group/darts/papers/texts/new-tutorial.pdf) Gerd Behrmann, Alexandre David, and Kim G. Larsen. _A Tutorial on UPPAAL._ 4th International School on Formal Methods for the Design of Computer, Communication, and Software Systems (SFM-RT'04), LNCS 3185. diff --git a/getlibs.sh b/getlibs.sh index 3df778e..ec5cdc3 100755 --- a/getlibs.sh +++ b/getlibs.sh @@ -1,13 +1,15 @@ #!/usr/bin/env bash set -euxo pipefail -# Cursed line that should also work on macos -# https://stackoverflow.com/questions/59895/how-can-i-get-the-source-directory-of-a-bash-script-from-within-the-script-itsel SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -mkdir -p "$SOURCE_DIR/libs/sources"; -CMAKE_PREFIX_PATH="$SOURCE_DIR/libs/" -CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH" +PREFIX="$SOURCE_DIR/local" +SOURCES="$SOURCE_DIR/local/sources" +mkdir -p "$SOURCES" + +CMAKE_PREFIX_PATH="$PREFIX" + +CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH -DCMAKE_INSTALL_PREFIX=$PREFIX" if [ -z ${CMAKE_TOOLCHAIN_FILE+x} ]; then echo "Not using a custom toolchain"; else @@ -15,23 +17,36 @@ else CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=$CMAKE_TOOLCHAIN_FILE"; fi +# doctest for unit testing +cd "$SOURCES" +curl -L https://github.com/doctest/doctest/archive/refs/tags/v2.4.9.tar.gz -o doctest-2.4.9.tar.gz +tar -xf doctest-2.4.9.tar.gz +SOURCE_DIR="$SOURCES/doctest-2.4.9" +BUILD_DIR="$SOURCE_DIR/build" +mkdir -p "$BUILD_DIR" +cmake $CMAKE_ARGS -DDOCTEST_WITH_TESTS=OFF -B "$BUILD_DIR" "$SOURCE_DIR" +cmake --build "$BUILD_DIR" --config Release +cmake --install "$BUILD_DIR" --config Release -cd $SOURCE_DIR/libs/sources; -curl -L https://github.com/Cyan4973/xxHash/archive/refs/tags/v0.8.0.tar.gz -o v0.8.0.tar.gz -tar -xvf v0.8.0.tar.gz -mkdir -p "$SOURCE_DIR/libs/sources/xxHash-0.8.0/build" -cd "$SOURCE_DIR/libs/sources/xxHash-0.8.0/build" -cmake $CMAKE_ARGS -DCMAKE_INSTALL_PREFIX="$SOURCE_DIR/libs/xxHash" -DBUILD_SHARED_LIBS=OFF ../cmake_unofficial -cmake --build . --config Release -cmake --install . --config Release +# xxHash for fast high quality hashing +cd "$SOURCES" +curl -L https://github.com/Cyan4973/xxHash/archive/refs/tags/v0.8.0.tar.gz -o xxHash-0.8.0.tar.gz +tar -xf xxHash-0.8.0.tar.gz +SOURCE_DIR="$SOURCES/xxHash-0.8.0" +BUILD_DIR="$SOURCE_DIR/build" +mkdir -p "$BUILD_DIR" +cmake $CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF -B "$BUILD_DIR" "$SOURCE_DIR/cmake_unofficial" +cmake --build "$BUILD_DIR" --config Release +cmake --install "$BUILD_DIR" --config Release +# UUtils various low level Uppaal utilities #git clone https://github.com/UPPAALModelChecker/UUtils "$SOURCE_DIR/libs/sources/UUtils"; -cd $SOURCE_DIR/libs/sources; -curl -L https://github.com/UPPAALModelChecker/UUtils/archive/refs/tags/v1.1.1.tar.gz -o v1.1.1.tar.gz -tar -xvf v1.1.1.tar.gz -mkdir -p "$SOURCE_DIR/libs/sources/UUtils-1.1.1/build" -cd "$SOURCE_DIR/libs/sources/UUtils-1.1.1" -cd build -cmake $CMAKE_ARGS -DCMAKE_INSTALL_PREFIX="$SOURCE_DIR/libs/UUtils" .. -cmake --build . --config Release -cmake --install . --config Release +cd "$SOURCES" +curl -L https://github.com/UPPAALModelChecker/UUtils/archive/refs/tags/v1.1.1.tar.gz -o UUtils-1.1.1.tar.gz +tar -xf UUtils-1.1.1.tar.gz +SOURCE_DIR="$SOURCES/UUtils-1.1.1" +BUILD_DIR="$SOURCE_DIR/build" +mkdir -p "$BUILD_DIR" +cmake $CMAKE_ARGS -B "$BUILD_DIR" "$SOURCE_DIR" +cmake --build "$BUILD_DIR" --config Release +cmake --install "$BUILD_DIR" --config Release diff --git a/include/dbm/Valuation.h b/include/dbm/Valuation.h index 21a8512..a10327d 100644 --- a/include/dbm/Valuation.h +++ b/include/dbm/Valuation.h @@ -17,106 +17,77 @@ #define INCLUDE_DBM_VALUATION_H #include "dbm/ClockAccessor.h" -#include "base/pointer.h" #include #include #include +#include +#include namespace dbm { - /** Valuation: like a fixed vector of scalars S with - * some basic operations on it. - * We don't use array_t because we don't want to - * resize but we need basic memory alloc/dealloc. - */ + /** Valuation: similar to std::valarray, except it treats reference and dynamic values (clocks) specially. */ template - class Valuation : public base::pointer_t + class Valuation { private: - size_t dynamicSize; size_t staticSize; - // S* dynamic; + size_t dynamicSize; + std::vector values; + public: /** Constructor - * @param size: size of the S valuation vector, - * or dimension. + * @param size: size of the S valuation vector (number of dimensions). + * @param dyn: number of dynamic values. */ - Valuation(size_t size, size_t dyn = 0): - base::pointer_t(new S[size + dyn], dyn + size), dynamicSize(dyn), staticSize(size) - { - base::pointer_t::reset(); - } - /** Copy constructor - * @param original: vector to copy - */ - Valuation(const Valuation& original): - base::pointer_t(new S[original.staticSize + original.dynamicSize], - original.staticSize + original.dynamicSize), - dynamicSize(original.dynamicSize), staticSize(original.staticSize) - { - this->copyFrom(original); - } - - ~Valuation() { delete[] this->data; } + Valuation(size_t size, size_t dyn = 0): staticSize{size}, dynamicSize{dyn}, values(size + dyn, S{}) {} - /** - * Copy the elements of the original vector. - * It is dangerous not to have this operator. - * @pre original.size() == size(). + /** Add an amount to all the elements of this vector EXCEPT #0 (reference clock). + * @param value: amount to add. */ - Valuation& operator=(const Valuation& original) + Valuation& operator+=(S value) { - base::pointer_t::copyFrom(original); + if (value != 0 && !values.empty()) { + for (auto i = std::next(this->begin()); i != this->end(); ++i) + *i += value; + } return *this; } - - /** Add value to all the elements of this vector - * EXCEPT element 0 (reference clock). - * @param value: element to add. + /** Subtract an amount from all the elements EXCEPT #0. + * @param value: amount to subtract. */ - Valuation& operator+=(S value) + Valuation& operator-=(S value) { return *this += -value; } + + /** Reset all values (except #0) to specific value */ + void reset(S value = {}) { - if (value != 0) { - for (S* i = this->begin() + 1; i < this->end(); ++i) { - *i += value; - } - } - return *this; + if (!values.empty()) + std::fill(std::next(this->begin()), this->end(), value); } - void reset() { std::fill(this->begin(), this->end(), 0); } - S last() const + const S& last() const { - assert(base::pointer_t::size() > 0); - return (*this)[base::pointer_t::size() - 1]; + assert(!values.empty()); + return values.back(); } - S lastStatic() const + const S& lastStatic() const { assert(staticSize > 0); return (*this)[staticSize - 1]; } - /** Subtract value to all the elements of this vector. - * @param value: element to subtract. - */ - Valuation& operator-=(S value) { return *this += -value; } - /** @return the delay between this point and the * argument point. This has any sense iff * argument point = this point + some delay. */ S getDelayTo(const Valuation& arg) const { - if (base::pointer_t::size() <= 1) { - return 0; // Only ref clock. - } + if (values.size() <= 1) + return 0; // Only ref clock. S delay = arg[1] - (*this)[1]; // Get delay. #ifndef NDEBUG // Check consistency. - size_t n = base::pointer_t::size(); - for (size_t i = 1; i < n; ++i) { + for (size_t i = 1, n = values.size(); i < n; ++i) assert(arg[i] - (*this)[i] == delay); - } #endif return delay; } @@ -150,37 +121,53 @@ namespace dbm // Copy the dynamic parts in common std::copy(src.begin(), src.end(), this->begin()); for (size_t i = src.dynamicSize; i < dynamicSize; ++i) { - (*this)[staticSize + i] = 0; + (*this)[staticSize + i] = S{}; } } - bool extend(size_t n) + /** Extend the number dynamic values by the specified amount. */ + bool extend(size_t n, S value = {}) { - S* buf = new S[n + dynamicSize + staticSize]; - - std::copy(base::pointer_t::begin(), base::pointer_t::end(), buf); - delete[] base::pointer_t::data; - // dynamicSize += n; - std::fill_n(buf + staticSize + dynamicSize, n, 0); dynamicSize += n; - this->setData(buf, dynamicSize + staticSize); + values.resize(staticSize + dynamicSize, value); return true; } - }; - using IntValuation = Valuation; - using DoubleValuation = Valuation; + size_t size() const { return values.size(); } + auto begin() const { return values.cbegin(); } + auto end() const { return values.cend(); } + auto begin() { return values.begin(); } + auto end() { return values.end(); } - /** Overload += for automatic cast. */ - /* static inline - DoubleValuation& operator += (DoubleValuation& d, int32_t v) { - return d += (double)v; + const S* data() const { return values.data(); } + S* data() { return values.data(); } + + /** wrap and check */ + S& operator[](size_t at) + { + assert(at < values.size()); + return values[at]; } - static inline - DoubleValuation& operator += (DoubleValuation& d, double v) { - return d += v; - }*/ + /** wrap and check read-only */ + const S& operator[](size_t at) const + { + assert(at < values.size()); + return values[at]; + } + }; + + template + std::ostream& operator<<(std::ostream& os, const Valuation& val) + { + os << '(' << val.size() << ")["; + for (auto& v : val) + os << ' ' << v; + return os << " ]"; + } + + using IntValuation = Valuation; + using DoubleValuation = Valuation; } // namespace dbm #endif // INCLUDE_DBM_VALUATION_H diff --git a/include/dbm/fed.h b/include/dbm/fed.h index ac2d7aa..266a542 100644 --- a/include/dbm/fed.h +++ b/include/dbm/fed.h @@ -18,7 +18,6 @@ #include "dbm/config.h" #include "dbm/dbm.h" #include "dbm/mingraph.h" -#include "base/pointer.h" #include #include @@ -401,7 +400,6 @@ namespace dbm dbm_t& operator&=(const dbm_t&); dbm_t& operator&=(const raw_t*); dbm_t& operator&=(const constraint_t&); - dbm_t& operator&=(const base::pointer_t&); dbm_t& operator&=(const std::vector&); /// Compute the intersection with the axis of a given clock. @@ -425,7 +423,6 @@ namespace dbm bool constrain(const constraint_t& c); bool constrain(const constraint_t* c, size_t n); bool constrain(const cindex_t* table, const constraint_t* c, size_t n); - bool constrain(const cindex_t* table, const base::pointer_t&); bool constrain(const cindex_t* table, const std::vector&); /// @return false if there is no intersection with the argument @@ -996,7 +993,6 @@ namespace dbm fed_t& operator&=(const dbm_t&); fed_t& operator&=(const raw_t*); fed_t& operator&=(const constraint_t&); - fed_t& operator&=(const base::pointer_t&); fed_t& operator&=(const std::vector&); /// (Set) subtraction operator (-). @@ -1026,7 +1022,6 @@ namespace dbm bool constrain(const constraint_t& c); bool constrain(const constraint_t* c, size_t n); bool constrain(const cindex_t* table, const constraint_t* c, size_t n); - bool constrain(const cindex_t* table, const base::pointer_t&); bool constrain(const cindex_t* table, const std::vector&); /// @return false if there is no intersection with the argument @@ -1593,11 +1588,6 @@ namespace dbm fed_t operator&(const fed_t& a, const constraint_t& c); fed_t operator&(const constraint_t& c, const fed_t& a); - dbm_t operator&(const dbm_t& a, const base::pointer_t& c); - dbm_t operator&(const base::pointer_t& c, const dbm_t& a); - fed_t operator&(const fed_t& a, const base::pointer_t& c); - fed_t operator&(const base::pointer_t& c, const fed_t& a); - dbm_t operator&(const dbm_t& a, const std::vector& vec); dbm_t operator&(const std::vector& vec, const dbm_t& a); fed_t operator&(const fed_t& a, const std::vector& vec); diff --git a/include/dbm/inline_fed.h b/include/dbm/inline_fed.h index 1fdac73..d095213 100644 --- a/include/dbm/inline_fed.h +++ b/include/dbm/inline_fed.h @@ -1128,12 +1128,6 @@ namespace dbm return *this; } - inline dbm_t& dbm_t::operator&=(const base::pointer_t& c) - { - constrain(c.begin(), c.size()); - return *this; - } - inline dbm_t& dbm_t::operator&=(const std::vector& vec) { constrain(&vec[0], vec.size()); @@ -1167,11 +1161,6 @@ namespace dbm return !isEmpty() && ptr_constrain(table, cnstr, n); } - inline bool dbm_t::constrain(const cindex_t* table, const base::pointer_t& vec) - { - return !isEmpty() && ptr_constrain(table, vec.begin(), vec.size()); - } - inline bool dbm_t::constrain(const cindex_t* table, const std::vector& vec) { return !isEmpty() && constrain(table, &vec[0], vec.size()); @@ -1852,12 +1841,6 @@ namespace dbm return *this; } - inline fed_t& fed_t::operator&=(const base::pointer_t& c) - { - constrain(c.begin(), c.size()); - return *this; - } - inline fed_t& fed_t::operator&=(const std::vector& vec) { constrain(&vec[0], vec.size()); @@ -2014,11 +1997,6 @@ namespace dbm inline fed_t operator&(const fed_t& a, const constraint_t& c) { return fed_t(a) &= c; } inline fed_t operator&(const constraint_t& c, const fed_t& a) { return fed_t(a) &= c; } - inline dbm_t operator&(const dbm_t& a, const base::pointer_t& c) { return dbm_t(a) &= c; } - inline dbm_t operator&(const base::pointer_t& c, const dbm_t& a) { return dbm_t(a) &= c; } - inline fed_t operator&(const fed_t& a, const base::pointer_t& c) { return fed_t(a) &= c; } - inline fed_t operator&(const base::pointer_t& c, const fed_t& a) { return fed_t(a) &= c; } - inline dbm_t operator&(const dbm_t& a, const std::vector& vec) { return dbm_t(a) &= vec; } inline dbm_t operator&(const std::vector& vec, const dbm_t& a) { return dbm_t(a) &= vec; } inline fed_t operator&(const fed_t& a, const std::vector& vec) { return fed_t(a) &= vec; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..3a001d3 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,18 @@ +add_library(UDBM DBMAllocator.cpp dbm.c fed_dbm.cpp mingraph.c mingraph_read.c partition.cpp print.cpp gen.c + mingraph_cache.cpp mingraph_relation.c pfed.cpp fed.cpp infimum.cpp mingraph_equal.c mingraph_write.c + priced.cpp) +set_property(TARGET UDBM PROPERTY C_VISIBILITY_PRESET hidden) +set_property(TARGET UDBM PROPERTY VISIBILITY_INLINES_HIDDEN ON) +target_link_libraries(UDBM UUtils::base UUtils::udebug UUtils::hash) + +target_include_directories(UDBM + PRIVATE + # where the library itself will look for its internal headers + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/include + PUBLIC + # where top-level project will look for the library's public headers + $ + # where external projects will look for the library's public headers + $ +) diff --git a/src/DBMAllocator.h b/src/DBMAllocator.h index 3c93e53..bb0de0e 100644 --- a/src/DBMAllocator.h +++ b/src/DBMAllocator.h @@ -16,7 +16,6 @@ #include "dbm/config.h" #include "dbm/fed.h" -#include "base/array_t.h" #include #include diff --git a/src/dbm.c b/src/dbm.c index c29dbe8..03f2d95 100644 --- a/src/dbm.c +++ b/src/dbm.c @@ -26,7 +26,6 @@ #include "dbm/print.h" #include "base/bitstring.h" #include "base/doubles.h" -#include "base/intutils.h" #include "debug/macros.h" #include diff --git a/src/dbm.h b/src/dbm.h index ae093ca..6688a62 100644 --- a/src/dbm.h +++ b/src/dbm.h @@ -15,7 +15,6 @@ #define DBM_DBM_H #include "dbm/constraints.h" // bit_t -#include "base/inttypes.h" #ifdef __cplusplus #include diff --git a/src/fed.cpp b/src/fed.cpp index 295a379..abb7d29 100644 --- a/src/fed.cpp +++ b/src/fed.cpp @@ -20,8 +20,6 @@ #include "dbm/print.h" #include "base/bitstring.h" #include "base/doubles.h" -#include "base/stats.h" -#include "debug/macros.h" #include #include @@ -1744,19 +1742,6 @@ namespace dbm return !isEmpty(); } - bool fed_t::constrain(const cindex_t* table, const base::pointer_t& vec) - { - assert(isOK()); - for (iterator it = beginMutable(); !it.null();) { - if (it->ptr_constrain(table, vec.begin(), vec.size())) { - ++it; - } else { - it.removeEmpty(); - } - } - return !isEmpty(); - } - bool fed_t::constrain(const cindex_t* table, const std::vector& vec) { assert(isOK()); diff --git a/src/infimum.cpp b/src/infimum.cpp index 77fbbe1..a338357 100644 --- a/src/infimum.cpp +++ b/src/infimum.cpp @@ -18,17 +18,6 @@ #define constraintValue(i, j) (dbm_raw2bound(dbm[(i)*dim + (j)])) #define b(i) (-rates[i]) // b is the notation for supply/demand in the network flow literature -/* - * Shortcuts to the Node structure - */ -#define index(n) ((n)-nodes) -#define pred(i) index(nodes[i].pred) -#define depth(i) nodes[i].depth -#define thread_MACRO(i) index(nodes[i].thread) -#define inbound(i) nodes[i].inbound -#define flow(i) nodes[i].flow -#define potential(i) nodes[i].potential - /** * Tree structure: * Storing the information we need about nodes in the graph @@ -44,6 +33,22 @@ struct Node int32_t potential; /**< Node potential for spanning tree solution. */ }; +struct Tree : public std::vector +{ + using std::vector::vector; + /** Computes the index of the given tree node */ + uint32_t index_of(const Node* n) const + { + assert(data() <= n); + assert(n < data() + size()); + return static_cast(n - data()); + } + /** Computes the index of a predecessor of the given node index i. */ + [[nodiscard]] uint32_t pred_of(uint32_t i) const { return index_of((*this)[i].pred); } + /** Computes the next according to the preorder the given node index i. */ + [[nodiscard]] uint32_t thread_of(uint32_t i) const { return index_of((*this)[i].thread); } +}; + /* * Used to store the indices of the clock constraints * from the minimal constraint form. @@ -55,62 +60,62 @@ struct arc_t }; #ifndef NDEBUG -static void printSimpleNodeInfo(Node* nodes, uint32_t i) +static void printSimpleNodeInfo(const Tree& tree, const uint32_t i) { - std::cerr << "Index: " << i << ", pred: " << pred(i) << ", depth: " << depth(i) << ", thread: " << thread_MACRO(i) - << ", inbound: " << inbound(i) << ", flow: " << flow(i) << ", potential: " << potential(i); + std::cerr << "Index: " << i << ", pred: " << tree.pred_of(i) << ", depth: " << tree[i].depth + << ", thread: " << tree.thread_of(i) << ", inbound: " << tree[i].inbound << ", flow: " << tree[i].flow + << ", potential: " << tree[i].potential; } -static void printNodeInfo(Node* nodes, const int32_t* rates, uint32_t i) +static void printNodeInfo(const Tree& tree, const int32_t* rates, uint32_t i) { - printSimpleNodeInfo(nodes, i); + printSimpleNodeInfo(tree, i); std::cerr << ", s/d: " << b(i) << " "; std::cerr << std::endl; } -static void printAllNodeInfo(Node* nodes, const int32_t* rates, uint32_t dim) +static void printAllNodeInfo(const Tree& tree, const int32_t* rates) { std::cerr << "Nodes:" << std::endl; - for (uint32_t i = 0; i < dim; i++) { - printNodeInfo(nodes, rates, i); - } + for (uint32_t i = 0; i < tree.size(); ++i) + printNodeInfo(tree, rates, i); } /* -static void printSimpleAllNodeInfo(Node *nodes, uint32_t dim) +static void printSimpleAllNodeInfo(const Nodes& tree, const uint32_t dim) { std::cerr << "Nodes:" << std::endl; for (uint32_t i = 0; i < dim; i++) { - printSimpleNodeInfo(nodes, i); + printSimpleNodeInfo(tree, i); std::cerr << std::endl; } } */ -static bool checkTreeIntegrity(const raw_t* dbm, const int32_t* rates, Node* nodes, uint32_t dim) +static bool checkTreeIntegrity(const raw_t* dbm, const int32_t* rates, const Tree& tree, const uint32_t dim) { bool treeOK = true; - // Check that all nodes get their flow + // Check that all tree get their flow std::vector sum(dim); uint32_t total = 0; - uint32_t i; sum[0] = 0; - for (i = 1; i < dim; i++) { + for (auto i = 1u; i < dim; i++) { sum[0] -= b(i); total -= b(i); sum[i] = b(i); } - for (i = 1; i < dim; i++) { - if (inbound(i)) { - sum[i] += flow(i); - sum[pred(i)] -= flow(i); + for (auto i = 1u; i < dim; i++) { + const auto pred_i = tree.pred_of(i); + if (tree[i].inbound) { + sum[i] += tree[i].flow; + sum[pred_i] -= tree[i].flow; } else { - sum[pred(i)] += flow(i); - sum[i] -= flow(i); + sum[pred_i] += tree[i].flow; + sum[i] -= tree[i].flow; } } - for (i = 0; i < dim; i++) { + for (auto i = 0u; i < dim; i++) { if (sum[i] != 0) { treeOK = false; std::cerr << "sum[" << i << "] is corrupted: " << sum[i] << ", should be: " << (i == 0 ? total : b(i)) @@ -120,75 +125,76 @@ static bool checkTreeIntegrity(const raw_t* dbm, const int32_t* rates, Node* nod // Check that the reduced costs are zero int32_t reduced_cost; - for (i = 1; i < dim; i++) { - if (potential(i) == dbm_INFINITY && pred(i) == 0) { - } else { - if (inbound(i)) - reduced_cost = constraintValue(pred(i), i) - potential(pred(i)) + potential(i); + for (auto i = 1u; i < dim; i++) { + const auto pred_i = tree.pred_of(i); + if (tree[i].potential != dbm_INFINITY || pred_i != 0) { + if (tree[i].inbound) + reduced_cost = constraintValue(pred_i, i) - tree[pred_i].potential + tree[i].potential; else - reduced_cost = constraintValue(i, pred(i)) + potential(pred(i)) - potential(i); + reduced_cost = constraintValue(i, pred_i) + tree[pred_i].potential - tree[i].potential; if (reduced_cost != 0) { treeOK = false; - std::cerr << "Reduced cost not zero (" << reduced_cost << ") i (pot): " << i << " (" << potential(i) - << "), pred(i) (pot): " << pred(i) << " (" << potential(pred(i)) - << "), inbound: " << inbound(i) << std::endl; + std::cerr << "Reduced cost not zero (" << reduced_cost << ") i (pot): " << i << " (" + << tree[i].potential << "), pred(i) (pot): " << pred_i << " (" << tree[pred_i].potential + << "), inbound: " << tree[i].inbound << std::endl; } } } // Check depth integrity - for (i = 1; i < dim; i++) { - if ((depth(pred(i)) + 1) != depth(i)) { + for (auto i = 1u; i < dim; i++) { + const auto pred_i = tree.pred_of(i); + if ((tree[pred_i].depth + 1) != tree[i].depth) { treeOK = false; - std::cerr << "Depth of " << i << " is " << depth(i) << ", but depth of pred(i) " << pred(i) << " is " - << depth(pred(i)) << std::endl; + std::cerr << "Depth of " << i << " is " << tree[i].depth << ", but depth of pred(i) " << pred_i << " is " + << tree[pred_i].depth << std::endl; } } // Check thread integrity std::vector allVisited(bits2intsize(dim)); base_setOneBit(allVisited.data(), 0); - uint32_t j = thread_MACRO(0); + uint32_t j = tree.thread_of(0); - for (i = 1; i < dim; i++) { + for (auto i = 1u; i < dim; i++) { base_setOneBit(allVisited.data(), j); - j = thread_MACRO(j); + j = tree.thread_of(j); } if (j != 0) { treeOK = false; std::cerr << "Thread doesn't return to the root!! Ended with: " << j << std::endl; - printAllNodeInfo(nodes, rates, dim); + printAllNodeInfo(tree, rates); } // For each node the subtree should match the thread. - for (i = 1; i < dim; i++) { + for (auto i = 1u; i < dim; i++) { uint32_t size = bits2intsize(dim); std::vector thread(size, 0); std::vector subtree(size, 0); - j = thread_MACRO(i); + j = tree.thread_of(i); base_setOneBit(thread.data(), i); base_setOneBit(subtree.data(), i); // Getting the tread - while (depth(j) > depth(i)) { + while (tree[j].depth > tree[i].depth) { base_setOneBit(thread.data(), j); - j = thread_MACRO(j); + j = tree.thread_of(j); } // getting the subtree - for (j = 1; j < dim; j++) { + for (auto j = 1u; j < dim; ++j) { uint32_t k = j; while (k != 0) { if (k == i) { base_setOneBit(subtree.data(), j); } - k = pred(k); + k = tree.pred_of(k); } } - for (j = 0; j < size; j++) { + for (auto j = 0u; j < size; ++j) { if (thread[j] != subtree[j]) { treeOK = false; std::cerr << "Subtree of " << i << " doesn't match thread" << std::endl; } } - for (i = 0; i < dim; i++) { + for (auto i = 0u; i < dim; ++i) { if (!base_getOneBit(allVisited.data(), i)) { treeOK = false; std::cerr << "Node " << i << " not reach through thread!!" << std::endl; @@ -244,38 +250,37 @@ static void printAllRates(const int32_t *rates, uint32_t dim) * To guarantee strongly feasible spanning trees - and thus termination - we * also need outbound arc from transshipment nodes! * - * The initial solution includes computing the intial node potentials. + * The initial solution includes computing the initial node potentials. * */ -static void findInitialSpanningTreeSolution(const raw_t* dbm, uint32_t dim, const int32_t* rates, Node* nodes) +static void findInitialSpanningTreeSolution(const raw_t* dbm, const int32_t* rates, Tree& tree) { - Node* last = nodes + dim; - Node* node = nodes; - node->pred = NULL; + uint32_t dim = tree.size(); + Node* last = tree.data() + dim; + Node* node = &tree[0]; + node->pred = nullptr; node->depth = 0; - node->thread = nodes + 1; - node->inbound = 0; + node->thread = &tree[1]; + node->inbound = false; node->flow = -1; node->potential = 0; for (node++; node != last; node++) { - uint32_t i = index(node); - node->pred = nodes; + uint32_t i = tree.index_of(node); + node->pred = &tree[0]; node->depth = 1; - node->thread = nodes + ((i + 1) % dim); + node->thread = &tree[(i + 1) % dim]; node->flow = abs(rates[i]); - if (b(i) < 0) // -rate(i) < 0 - { + if (b(i) < 0) { // -rate(i) < 0 node->inbound = true; node->potential = -dbm_raw2bound(dbm[i]); - } else // -rate(i) >= 0 - { + } else { // -rate(i) >= 0 node->inbound = false; node->potential = dbm_raw2bound(dbm[i * dim]); } } - // printAllNodeInfo(nodes, rates, dim); + // printAllNodeInfo(tree, rates, dim); } /* @@ -311,7 +316,7 @@ static void updatePotentials(Node* leave, int32_t change) * * Node that this might be startNode itself. */ -static Node* findLastNodeBefore(Node* node, Node* exclude) +static Node* findLastNodeBefore(Node* node, const Node* exclude) { Node* i; do { @@ -326,7 +331,7 @@ static Node* findLastNodeBefore(Node* node, Node* exclude) * for which the preorder successor has a depth not higher than * \a depth. */ -static Node* findLastNodeBefore(Node* node, uint32_t depth) +static Node* findLastNodeBefore(Node* node, const uint32_t depth) { Node* i; do { @@ -353,7 +358,7 @@ static Node* findNthPredecessor(Node* node, int32_t n) /** * Returns true if and only if n is on the path to the root from m. */ -static inline bool isPredecessorOf(Node* n, Node* m) { return n == findNthPredecessor(m, m->depth - n->depth); } +static inline bool isPredecessorOf(const Node* n, Node* m) { return n == findNthPredecessor(m, m->depth - n->depth); } /** * Update all node information in the subtree not containing the root. @@ -419,8 +424,8 @@ static void updateNonRootSubtree(Node* rootNode, Node* nonRootNode, Node* leave, /* * Update the inbound, pred, and flow information * while tracing back the cycle. If (i,j) is still in the - * tree and nodes[i] had the information, then nodes[j] will - * get that information, as nodes[k] will hold the information + * tree and tree[i] had the information, then tree[j] will + * get that information, as tree[k] will hold the information * about enter. */ @@ -434,7 +439,7 @@ static void updateNonRootSubtree(Node* rootNode, Node* nonRootNode, Node* leave, newi = nonRootNode; do { i = newi; - assert(i != NULL); + assert(i != nullptr); // These are i's new values tmppred2 = tmppred1; tmpflow2 = tmpflow1; @@ -510,7 +515,7 @@ static void updateSpanningTree(Node* k, Node* l, Node* leave, Node* root, int32_ /* * ENTERING ARC FUNCTION REQUIREMENTS * - * Any entering arc function should return NULL to indicate that no + * Any entering arc function should return nullptr to indicate that no * eligible arc exists, thus, signifiying optimality of the current * solution. Otherwise an arc in [firstArc, lastArc) is returned. */ @@ -519,32 +524,29 @@ static void updateSpanningTree(Node* k, Node* l, Node* leave, Node* root, int32_ * Danzig's entering rule chooses the eligible arc with the * lowest cost. */ -static arc_t* enteringArcDanzig(arc_t* firstArc, arc_t* lastArc, Node* nodes, const raw_t* dbm, uint32_t dim) +static auto enteringArcDanzig(const std::vector& arcs, const Tree& tree, const raw_t* dbm) { - arc_t* best = NULL; + uint32_t dim = tree.size(); + auto best = arcs.end(); int32_t lowest_reduced_cost = 0; - - while (firstArc != lastArc) { + for (auto it = arcs.begin(); it != arcs.end(); ++it) { /* Check if reduced cost is negative, i.e. c_ij - pi(i) + - * pi(j) < 0 for any arcs in arcs - */ - int32_t reduced_cost = - constraintValue(firstArc->i, firstArc->j) - potential(firstArc->i) + potential(firstArc->j); + * pi(j) < 0 for any arcs in arcs */ + int32_t reduced_cost = constraintValue(it->i, it->j) - tree[it->i].potential + tree[it->j].potential; if (reduced_cost < lowest_reduced_cost) { lowest_reduced_cost = reduced_cost; - best = firstArc; + best = it; } - firstArc++; } - assert(lowest_reduced_cost < 0 || best == NULL); + assert(lowest_reduced_cost < 0 || best == std::end(arcs)); return best; } // static arc_t *enteringArcFirstEligible( // arc_t *firstArc, arc_t *lastArc, -// Node *nodes, const raw_t *dbm, uint32_t dim) +// Node *tree, const raw_t *dbm, uint32_t dim) // { // while (firstArc != lastArc) // { @@ -559,7 +561,7 @@ static arc_t* enteringArcDanzig(arc_t* firstArc, arc_t* lastArc, Node* nodes, co // } // firstArc++; // } -// return NULL; +// return nullptr; // } /** @@ -592,7 +594,7 @@ static Node* discoverCycleRoot(Node* k, Node* l) * * Actually, the node in the spanning tree representation that mentions the * the arc is returned. This is to compensate for the fact that it could be - * an artificial arc which is represented by NULL. + * an artificial arc which is represented by nullptr. */ static Node* findLeavingArc(Node* k, Node* l, Node* root) { @@ -600,7 +602,7 @@ static Node* findLeavingArc(Node* k, Node* l, Node* root) /* * Node with the lowest depth of the leaving arc. */ - Node* smallestFlowNode = NULL; + Node* smallestFlowNode = nullptr; /* * Move towards the common root to find the @@ -643,14 +645,16 @@ static Node* findLeavingArc(Node* k, Node* l, Node* root) * If it is not a transshipment node, we need to follow the * thread to update the potentials. We do it by computing */ -static void testAndRemoveArtificialArcs(const raw_t* dbm, const int32_t* rates, Node* nodes, uint32_t dim) +static void testAndRemoveArtificialArcs(const raw_t* dbm, const int32_t* rates, Tree& tree) { - for (uint32_t i = 1; i < dim; i++) { - if (potential(i) == dbm_INFINITY && pred(i) == 0 && flow(i) == 0) { - inbound(i) = true; + uint32_t dim = tree.size(); + for (uint32_t i = 1; i < dim; ++i) { + const auto pred_i = tree.pred_of(i); + if (tree[i].potential == dbm_INFINITY && pred_i == 0 && tree[i].flow == 0) { + tree[i].inbound = true; - Node* tmp = nodes[i].thread; - int32_t rateSum = b(i); + Node* tmp = tree[i].thread; + [[maybe_unused]] int32_t rateSum = b(i); int32_t minPotential = dbm_INFINITY + constraintValue(0, i); @@ -658,8 +662,8 @@ static void testAndRemoveArtificialArcs(const raw_t* dbm, const int32_t* rates, * Find the highest potential we can decrease i with and * maintain positive potentials. */ - while (tmp->depth > depth(i)) { - rateSum += b(index(tmp)); + while (tmp->depth > tree[i].depth) { + rateSum += b(tree.index_of(tmp)); if (tmp->potential < minPotential) minPotential = tmp->potential; tmp = tmp->thread; @@ -673,11 +677,11 @@ static void testAndRemoveArtificialArcs(const raw_t* dbm, const int32_t* rates, * Subtracting the same potetial maintains feasibility. */ - tmp = nodes + i; + tmp = &tree[i]; do { tmp->potential -= minPotential; tmp = tmp->thread; - } while (tmp->depth > depth(i)); + } while (tmp->depth > tree[i].depth); } } } @@ -702,29 +706,30 @@ static bool allPositive(const int32_t* first, const int32_t* last) * network flow problem. I.e. the cost of the offset and the costless * moves to the offset is not taken into account. */ -static void infimumNetSimplex(const raw_t* dbm, uint32_t dim, const int32_t* rates, Node* nodes) +static void infimumNetSimplex(const raw_t* dbm, const int32_t* rates, Tree& tree) { + uint32_t dim = tree.size(); /* Find and store the minimal set of arcs. The netsimplex * algorithm is polynomial in the number of arcs. */ std::vector bitMatrix(bits2intsize(dim * dim)); auto nbConstraints = dbm_analyzeForMinDBM(dbm, dim, bitMatrix.data()); - arc_t* arcs = (arc_t*)calloc(nbConstraints, sizeof(arc_t)); - arc_t* arc = arcs; + auto arcs = std::vector(nbConstraints); + auto arc_it = arcs.begin(); for (uint32_t i = 0; i < dim; i++) { for (uint32_t j = 0; j < dim; j++) { if (base_readOneBit(bitMatrix.data(), i * dim + j)) { - arc->i = i; - arc->j = j; - arc++; + arc_it->i = i; + arc_it->j = j; + ++arc_it; } } } - assert(arc - arcs == (int32_t)nbConstraints); + assert(std::distance(arcs.begin(), arc_it) == (int32_t)nbConstraints); - findInitialSpanningTreeSolution(dbm, dim, rates, nodes); + findInitialSpanningTreeSolution(dbm, rates, tree); - ASSERT(checkTreeIntegrity(dbm, rates, nodes, dim), printAllNodeInfo(nodes, rates, dim)); + ASSERT(checkTreeIntegrity(dbm, rates, tree, dim), printAllNodeInfo(tree, rates)); /* * Run the network simplex algorithm on the dual problem of the @@ -733,9 +738,10 @@ static void infimumNetSimplex(const raw_t* dbm, uint32_t dim, const int32_t* rat * of the node potential at the point of termination, matches * precisely the infimum point of the zone. */ - while ((arc = enteringArcDanzig(arcs, arcs + nbConstraints, nodes, dbm, dim))) { - Node* k = nodes + arc->i; - Node* l = nodes + arc->j; + auto arc = enteringArcDanzig(arcs, tree, dbm); + while (arc != std::end(arcs)) { + Node* k = &tree[arc->i]; + Node* l = &tree[arc->j]; /* Find common root in the cycle induced by introducing the * entering arc in the spanning tree. @@ -748,15 +754,15 @@ static void infimumNetSimplex(const raw_t* dbm, uint32_t dim, const int32_t* rat updateSpanningTree(k, l, leave, root, constraintValue(arc->i, arc->j)); - ASSERT(checkTreeIntegrity(dbm, rates, nodes, dim), printAllNodeInfo(nodes, rates, dim)); - // printAllNodeInfo(nodes, rates, dim); + ASSERT(checkTreeIntegrity(dbm, rates, tree, dim), printAllNodeInfo(tree, rates)); + // printAllNodeInfo(tree, rates, dim); + arc = enteringArcDanzig(arcs, tree, dbm); } /* * Get rid of artificial arcs. */ - testAndRemoveArtificialArcs(dbm, rates, nodes, dim); - free(arcs); + testAndRemoveArtificialArcs(dbm, rates, tree); } /* @@ -771,25 +777,23 @@ int32_t pdbm_infimum(const raw_t* dbm, uint32_t dim, uint32_t offsetCost, const return offsetCost; } - Node* nodes = (Node*)calloc(dim, sizeof(Node)); - infimumNetSimplex(dbm, dim, rates, nodes); + auto tree = Tree(dim); + infimumNetSimplex(dbm, rates, tree); int32_t solution = offsetCost; for (uint32_t i = 0; i < dim; i++) { - ASSERT(potential(i) >= 0, std::cerr << "Node: " << i << std::endl; printAllNodeInfo(nodes, rates, dim); + ASSERT(tree[i].potential >= 0, std::cerr << "Node: " << i << std::endl; printAllNodeInfo(tree, rates); printClockLowerBounds(dbm, dim)); /* * Check if best solution has positive flow on artificial arc * if so, return minus infinity as the solution is unbounded. */ - if (potential(i) == dbm_INFINITY && pred(i) == 0 && flow(i) > 0) { - free(nodes); + if (tree[i].potential == dbm_INFINITY && tree.pred_of(i) == 0 && tree[i].flow > 0) { return -dbm_INFINITY; } - solution += rates[i] * (potential(i) + constraintValue(0, i)); + solution += rates[i] * (tree[i].potential + constraintValue(0, i)); } - free(nodes); return solution; } @@ -797,31 +801,26 @@ void pdbm_infimum(const raw_t* dbm, uint32_t dim, uint32_t offsetCost, const int { if (allPositive(rates, rates + dim)) { valuation[0] = 0; - for (uint32_t i = 1; i < dim; i++) { + for (uint32_t i = 1; i < dim; ++i) valuation[i] = -constraintValue(0, i); - } } else { - Node* nodes = (Node*)calloc(dim, sizeof(Node)); - infimumNetSimplex(dbm, dim, rates, nodes); + auto tree = Tree(dim); + infimumNetSimplex(dbm, rates, tree); - /* - * Assign the potentials to the best solution. - */ + /* Assign the potentials to the best solution. */ valuation[0] = 0; for (uint32_t i = 1; i < dim; i++) { - ASSERT(potential(i) >= 0, std::cerr << "Node: " << i << std::endl; printAllNodeInfo(nodes, rates, dim); + ASSERT(tree[i].potential >= 0, std::cerr << "Node: " << i << std::endl; printAllNodeInfo(tree, rates); printSolution(valuation, rates, dim); printClockLowerBounds(dbm, dim)); /* * Check if best solution has positive flow on artificial arc * if so, infimum is not well defined as it is minus infinity * and we throw an exception. */ - if (potential(i) == dbm_INFINITY && pred(i) == 0 && flow(i) > 0) { + if (tree[i].potential == dbm_INFINITY && tree.pred_of(i) == 0 && tree[i].flow > 0) throw std::domain_error("Infimum is downward unbound, thus, no well-defined infimum valuation."); - } - valuation[i] = potential(i); + valuation[i] = tree[i].potential; } - free(nodes); } } diff --git a/src/mingraph_write.c b/src/mingraph_write.c index c125626..f77ed87 100644 --- a/src/mingraph_write.c +++ b/src/mingraph_write.c @@ -146,7 +146,7 @@ int32_t* dbm_writeToMinDBMWithOffset(const raw_t* dbm, cindex_t dim, bool minimi } else { /* choose the cheapest encoding */ - uint32_t* retVal = + int32_t* retVal = mingraph_encode(dbm, dim, bitMatrix, cnt, tryConstraints16 && (dbm_getMaxRange(dbm, dim) < dbm_LS_INF16), c_alloc, offset); free(bitMatrix); diff --git a/src/partition.cpp b/src/partition.cpp index bb381c6..05e41a7 100644 --- a/src/partition.cpp +++ b/src/partition.cpp @@ -13,7 +13,6 @@ #include "dbm/partition.h" -#include "base/intutils.h" #ifndef NDEBUG #include "dbm/print.h" #endif diff --git a/src/pfed.cpp b/src/pfed.cpp index f72e39f..ed5054b 100644 --- a/src/pfed.cpp +++ b/src/pfed.cpp @@ -145,17 +145,17 @@ namespace dbm bool pfed_t::contains(const IntValuation& valuation) const { - return find_if(begin(), end(), bind(pdbm_containsInt, _1, ptr->dim, valuation())) != end(); + return find_if(begin(), end(), bind(pdbm_containsInt, _1, ptr->dim, valuation.data())) != end(); } bool pfed_t::contains(const DoubleValuation& valuation) const { - return find_if(begin(), end(), bind(pdbm_containsDouble, _1, ptr->dim, valuation())) != end(); + return find_if(begin(), end(), bind(pdbm_containsDouble, _1, ptr->dim, valuation.data())) != end(); } bool pfed_t::containsWeakly(const IntValuation& valuation) const { - return find_if(begin(), end(), bind(pdbm_containsIntWeakly, _1, ptr->dim, valuation())) != end(); + return find_if(begin(), end(), bind(pdbm_containsIntWeakly, _1, ptr->dim, valuation.data())) != end(); } relation_t pfed_t::relation(const pfed_t& b) const @@ -357,7 +357,7 @@ namespace dbm * lambda x y . min(x, pdbm_getCostOfValuation(y, dim, val)) */ return accumulate(begin(), end(), INT_MAX, - bind(min, _1, bind(pdbm_getCostOfValuation, _2, ptr->dim, valuation()))); + bind(min, _1, bind(pdbm_getCostOfValuation, _2, ptr->dim, valuation.data()))); } void pfed_t::relax() { for_each(beginMutable(), endMutable(), bind(pdbm_relax, _1, ptr->dim)); } diff --git a/src/print.cpp b/src/print.cpp index b6b4198..e8808ed 100644 --- a/src/print.cpp +++ b/src/print.cpp @@ -17,9 +17,6 @@ #include "dbm/fed.h" #include "io/FileStreamBuffer.h" #include "base/bitstring.h" -#include "debug/macros.h" - -#include /* For easy reading */ #define DBM(I, J) dbm[(I)*dim + (J)] diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3493dec..c8f76bc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,8 +3,9 @@ set(libs UDBM UUtils::udebug UUtils::base UUtils::hash) if (TESTING) - file(GLOB test_sources "test_*.c" "test_*.cpp") + find_package(doctest 2.4.8 REQUIRED) + file(GLOB test_sources "test_*.c" "test_*.cpp") foreach(source ${test_sources}) get_filename_component(test_target ${source} NAME_WE) add_executable(${test_target} ${source}) @@ -23,7 +24,7 @@ if (TESTING) set_tests_properties(dbm_mingraph_1_10 PROPERTIES TIMEOUT 45) # 36s on Win64 add_test(NAME dbm_mingraph_1_10_1 COMMAND test_mingraph 1 10 1) # failing test on L64-debug set_tests_properties(dbm_mingraph_1_10_1 PROPERTIES TIMEOUT 45) # 39s on Win64 - add_test(NAME dbm_valuation_20 COMMAND test_valuation 20) + add_test(NAME dbm_valuation COMMAND test_valuation) add_test(NAME test_allocation COMMAND test_allocation) set_tests_properties(dbm_dbm_1_10 dbm_fed_1_7_7 PROPERTIES TIMEOUT 1200) diff --git a/test/test_dbm.c b/test/test_dbm.c index 54c0d2c..4fe7c7b 100644 --- a/test/test_dbm.c +++ b/test/test_dbm.c @@ -97,7 +97,7 @@ static uint32_t goodDBMs = 0; static void test_zero(uint32_t size) { ADBM(dbm); - uint32_t* pt = (uint32_t*)calloc(size, sizeof(uint32_t)); + int32_t* pt = (int32_t*)calloc(size, sizeof(int32_t)); uint32_t k; PRINTF("zero"); dbm_zero(dbm, size); @@ -484,7 +484,7 @@ static void test_constrain(uint32_t size) ADBM(dbm3); ADBM(dbm4); ADBM(dbm5); - uint32_t* pt = (uint32_t*)calloc(size, sizeof(uint32_t)); + int32_t* pt = (int32_t*)calloc(size, sizeof(int32_t)); uint32_t* touched1 = (uint32_t*)calloc(bits2intsize(size), sizeof(uint32_t)); uint32_t* touched2 = (uint32_t*)calloc(bits2intsize(size), sizeof(uint32_t)); uint32_t* touched3 = (uint32_t*)calloc(bits2intsize(size), sizeof(uint32_t)); @@ -604,7 +604,7 @@ static void test_constrain(uint32_t size) static void test_point(uint32_t size) { ADBM(dbm1); - uint32_t* pt = (uint32_t*)calloc(size, sizeof(uint32_t)); + int32_t* pt = (int32_t*)calloc(size, sizeof(int32_t)); uint32_t i, k; PRINTF("discrete point"); diff --git a/test/test_fed.cpp b/test/test_fed.cpp index 2f4c5b3..e52b7e6 100644 --- a/test/test_fed.cpp +++ b/test/test_fed.cpp @@ -27,6 +27,7 @@ #include // sort #include +#include #include using namespace std; @@ -49,6 +50,17 @@ using namespace dbm; // Skip too expensive tests #define CHEAP dim <= 100 && size <= 100 +static auto gen = std::mt19937{}; + +template +T rand_int(T mx) +{ + if (mx < 2) + return 0; + auto dist = std::uniform_int_distribution{0, mx - 1}; + return dist(gen); +} + /** Test that a discrete point is in a DBM list * @param pt: point * @param fed: federation of DBMs. @@ -109,7 +121,7 @@ static fed_t test_gen(cindex_t dim, size_t n) static const dbm_t& test_getDBM(const fed_t& fed) { fed_t::const_iterator iter(fed); - for (size_t size = rand() % fed.size(); size != 0; --size, ++iter) + for (size_t size = rand_int(fed.size()); size != 0; --size, ++iter) ; return *iter; } @@ -145,7 +157,7 @@ static void test_addDBMs(fed_t& fed, size_t nb) for (cindex_t dim = fed.getDimension(); nb; --nb) { const raw_t* inList = test_getDBM(fed)(); dbm_t dbm(dim); - if (rand() & 1) { + if (rand_int(2) & 1) { dbm_generateSuperset(dbm.getDBM(), inList, dim); } else { dbm_generateSubset(dbm.getDBM(), inList, dim); @@ -163,14 +175,14 @@ static fed_t test_genArg(size_t n, const fed_t& fed) { cindex_t dim = fed.getDimension(); fed_t result(dim); - n = rand() % (n + 1); + n = rand_int(n + 1); if (fed.isEmpty()) { return test_gen(dim, n); } for (; n; --n) { const dbm_t& inList = test_getDBM(fed); dbm_t dbm(dim); - switch (rand() % 4) { + switch (rand_int(4)) { case 0: // diff dbm_generate(dbm.getDBM(), dim, 1000); break; @@ -205,19 +217,19 @@ static size_t test_genConstraints(const fed_t& fed, size_t n, constraint_t* cons if (listSize && n && dim > 1) { const raw_t* dbm = test_getDBM(fed)(); // pick a DBM - uint32_t easy = rand() % (n + 1); // nb of easy constraints 0..n + uint32_t easy = rand_int(n + 1); // nb of easy constraints 0..n uint32_t i, j, k = 0; do { // generate non tightening constraints do { // pick a constraint - i = rand() % dim; - j = rand() % dim; + i = rand_int(dim); + j = rand_int(dim); } while (i == j); if (dbm[i * dim + j] != dbm_LS_INFINITY) { constraints[nb].i = i; constraints[nb].j = j; - constraints[nb].value = dbm[i * dim + j] + rand() % 4; /* loosen a bit */ + constraints[nb].value = dbm[i * dim + j] + rand_int(4); /* loosen a bit */ nb++; } @@ -227,8 +239,8 @@ static size_t test_genConstraints(const fed_t& fed, size_t n, constraint_t* cons while (nb < n) { // pick a constraint do { - i = rand() % dim; - j = rand() % dim; + i = rand_int(dim); + j = rand_int(dim); } while (i == j); constraints[nb].i = i; @@ -237,9 +249,9 @@ static size_t test_genConstraints(const fed_t& fed, size_t n, constraint_t* cons if (dbm[j * dim + i] == dbm_LS_INFINITY) { if (dbm[i * dim + j] == dbm_LS_INFINITY) { // anything will do - constraints[nb].value = 1 + rand() % 1000; + constraints[nb].value = 1 + rand_int(1000); } else if (i != 0 || dbm[i * dim + j] > dbm_LE_ZERO) { - constraints[nb].value = dbm[i * dim + j] - rand() % 10; + constraints[nb].value = dbm[i * dim + j] - rand_int(10); if (i == 0 && constraints[nb].value < dbm_LE_ZERO) { constraints[nb].value = dbm_LE_ZERO; } @@ -249,10 +261,10 @@ static size_t test_genConstraints(const fed_t& fed, size_t n, constraint_t* cons } else // has to take dbm[j,i] into account { if (dbm[i * dim + j] == dbm_LS_INFINITY) { - constraints[nb].value = 2 + rand() % 500 - dbm[j * dim + i]; + constraints[nb].value = 2 + rand_int(500) - dbm[j * dim + i]; } else { raw_t rawRange = dbm[i * dim + j] + dbm[j * dim + i]; - constraints[nb].value = rand() % rawRange + 1 - dbm[j * dim + i]; + constraints[nb].value = rand_int(rawRange) + 1 - dbm[j * dim + i]; } } ++nb; @@ -261,6 +273,20 @@ static size_t test_genConstraints(const fed_t& fed, size_t n, constraint_t* cons return nb; } +template +void shuffle(It first, It last) +{ + size_t size = std::distance(first, last); + if (size < 2) + return; + for (auto i = size_t{0}; i < size - 1; ++i) { + auto dist = std::uniform_int_distribution{i, size - 1}; + auto idx = dist(gen); + if (idx != i) + std::swap(*std::next(first, i), *std::next(first, idx)); + } +} + /** Mix a DBM list: misuse of quicksort. * We can swap indices randomly too, but * this is simpler (and quite cool). @@ -270,20 +296,13 @@ static size_t test_genConstraints(const fed_t& fed, size_t n, constraint_t* cons static void test_mix(fed_t& fed) { size_t size = fed.size(); + assert(size <= (1u << 16)); if (size > 0) { - std::vector oldlist(size); - std::vector newlist(size); - std::vector index(size); - size_t i; - assert(fed.write(oldlist.data()) == size); - for (i = 0; i < size; ++i) { - index[i] = i | (rand() << 16); - } - std::sort(index.data(), index.data() + size); - for (i = 0; i < size; ++i) { - newlist[i] = oldlist[index[i] & 0xffff]; - } - fed.read(newlist.data(), size); + std::vector list(size); + auto written = fed.write(list.data()); + assert(written == size); + shuffle(list.begin(), list.end()); + fed.read(list.data(), size); } assert(!fed.hasEmpty()); } @@ -639,7 +658,7 @@ static void test_freeClock(cindex_t dim, size_t size) fed_t fed1(test_gen(dim, size)); fed_t fed2 = fed1; uint32_t h = fed1.hash(); - cindex_t c = rand() % (dim - 1) + 1; // not ref clock + cindex_t c = rand_int(dim - 1) + 1; // not ref clock assert(fed2 <= fed1.freeClock(c)); assert(fed2.hash() == h); for (fed_t::iterator iter2 = fed2.beginMutable(); !iter2.null(); ++iter2) { @@ -674,8 +693,8 @@ static void test_updateValue(cindex_t dim, size_t size) fed_t fed2 = fed1; fed_t fed3 = fed1; uint32_t h = fed1.hash(); - cindex_t c = rand() % (dim - 1) + 1; - int32_t v = rand() % 100; + cindex_t c = rand_int(dim - 1) + 1; + int32_t v = rand_int(100); fed1.updateValue(c, v); fed2(c) = v; assert(fed1.hash() == fed2.hash()); @@ -720,8 +739,8 @@ static void test_updateClock(cindex_t dim, size_t size) fed_t fed2 = fed1; fed_t fed3 = fed1; uint32_t h = fed1.hash(); - cindex_t c1 = rand() % (dim - 1) + 1; - cindex_t c2 = rand() % (dim - 1) + 1; + cindex_t c1 = rand_int(dim - 1) + 1; + cindex_t c2 = rand_int(dim - 1) + 1; fed1.updateClock(c1, c2); fed2(c1) = fed2(c2); assert(fed3.hash() == h); @@ -765,8 +784,8 @@ static void test_updateIncrement(cindex_t dim, size_t size) fed_t fed2 = fed1; fed_t fed3 = fed1; uint32_t h = fed1.hash(); - cindex_t c = rand() % (dim - 1) + 1; - cindex_t v = rand() % 50; + cindex_t c = rand_int(dim - 1) + 1; + cindex_t v = rand_int(50); fed1.updateIncrement(c, v); fed2(c) += v; assert(fed3.hash() == h); @@ -810,9 +829,9 @@ static void test_update(cindex_t dim, size_t size) fed_t fed2 = fed1; fed_t fed3 = fed1; uint32_t h = fed1.hash(); - cindex_t c1 = rand() % (dim - 1) + 1; - cindex_t c2 = rand() % (dim - 1) + 1; - cindex_t v = rand() % 50; + cindex_t c1 = rand_int(dim - 1) + 1; + cindex_t c2 = rand_int(dim - 1) + 1; + cindex_t v = rand_int(50); fed1.update(c1, c2, v); fed2(c1) = fed2(c2) + v; assert(fed3.hash() == h); @@ -903,8 +922,8 @@ static void test_constrainEmpty(cindex_t dim, size_t size) << fed << endl); // now empty the federation do { - i = rand() % dim; - j = rand() % dim; + i = rand_int(dim); + j = rand_int(dim); } while (i == j); ASSERT(!fed.constrain(i, j, pt[i] - pt[j], dbm_STRICT), debug_cppPrintVector(cerr, pt.data(), dim) << endl @@ -1031,7 +1050,7 @@ static void test_freeDown(cindex_t dim, size_t size) fed_t fed1(test_gen(dim, size)); fed_t fed2 = fed1; uint32_t h = fed1.hash(); - cindex_t c = rand() % (dim - 1) + 1; // not ref clock + cindex_t c = rand_int(dim - 1) + 1; // not ref clock assert(fed2 <= fed1.freeDown(c)); assert(fed2.hash() == h); for (fed_t::iterator iter2 = fed2.beginMutable(); !iter2.null(); ++iter2) { @@ -1064,7 +1083,7 @@ static void test_freeUp(cindex_t dim, size_t size) fed_t fed1(test_gen(dim, size)); fed_t fed2 = fed1; uint32_t h = fed1.hash(); - cindex_t c = rand() % (dim - 1) + 1; // not ref clock + cindex_t c = rand_int(dim - 1) + 1; // not ref clock assert(fed2 <= fed1.freeUp(c)); assert(fed2.hash() == h); for (fed_t::iterator iter2 = fed2.beginMutable(); !iter2.null(); ++iter2) { @@ -1129,8 +1148,8 @@ static void test_equal(cindex_t dim, size_t size) assert(test_isPointIn(pt.data(), fed1, dim)); cindex_t i, j; do { - i = rand() % dim; - j = rand() % dim; + i = rand_int(dim); + j = rand_int(dim); } while (i == j); // will terminate if dim > 1 fed_t fed2 = fed1; fed2.constrain(i, j, pt[i] - pt[j], dbm_STRICT); @@ -1327,8 +1346,8 @@ int main(int argc, char* argv[]) start = atoi(argv[1]); end = atoi(argv[2]); size = atoi(argv[3]); - seed = argc > 4 ? atoi(argv[4]) : time(NULL); - srand(seed); + seed = argc > 4 ? atoi(argv[4]) : std::random_device{}(); + gen.seed(seed); if (size < 1) { cerr << "Minimum size=1 taken\n"; size = 1; @@ -1347,7 +1366,7 @@ int main(int argc, char* argv[]) cout << i << ": "; test(i, size); (cout << " \n").flush(); - if (rand() & 1) + if (rand_int(2) & 1) cleanUp(); } diff --git a/test/test_fed_dbm.cpp b/test/test_fed_dbm.cpp index a223599..8bffe68 100644 --- a/test/test_fed_dbm.cpp +++ b/test/test_fed_dbm.cpp @@ -23,14 +23,12 @@ #include "dbm/fed.h" #include "dbm/gen.h" #include "dbm/print.h" -#include "debug/macros.h" #include "debug/utils.h" #include using namespace std; using namespace dbm; -using namespace base; // Range for DBM generation #define MAXRANGE 10000 @@ -74,7 +72,7 @@ static void test(cindex_t dim) { NEW(dbm); NEW(dbm2); - constraint_t* cnstr = new constraint_t[dim * dim]; + auto cnstr = std::vector(dim * dim); std::vector lower(dim); std::vector upper(dim); IntValuation pt(dim); @@ -82,12 +80,13 @@ static void test(cindex_t dim) bool c1 = false, c2 = false, c4 = false; bool c5 = false, c6 = false, c7 = false; BEGIN; - dbm_t a; // default - dbm_t b(dim); // dim - dbm_t c(a), d(b); // copy + auto a = dbm_t{}; // default + auto b = dbm_t{dim}; // dim + auto c = dbm_t{a}; // copy + auto d = dbm_t{b}; // copy cindex_t i, j, k; bool ab_equal = false; - const raw_t* ptr; + const raw_t* ptr{nullptr}; PROGRESS(); // constructor post-conditions @@ -250,13 +249,13 @@ static void test(cindex_t dim) // constrain assert(a == dbm); - for (k = 7; k > 0 && !dbm_generatePoint(pt.begin(), dbm, dim); --k) + for (k = 7; k > 0 && !dbm_generatePoint(pt.data(), dbm, dim); --k) ; if (k > 0) { bool stop = rand() & 1; c5 = true; b = a; - assert(b.contains(pt.begin(), dim)); + assert(b.contains(pt.data(), dim)); for (i = 0, k = 0; i < dim; ++i) { for (j = 0; j < dim; ++j) { if (i != j) { @@ -274,15 +273,16 @@ static void test(cindex_t dim) if (stop && (rand() & 1)) break; } - b &= pointer_t(cnstr, k); + cnstr.resize(k); + b &= cnstr; assert(a == b); - assert(dbm_constrainN(dbm, dim, cnstr, k)); + assert(dbm_constrainN(dbm, dim, cnstr.data(), k)); assert(a == dbm); c = b; - b &= pointer_t(cnstr, k); + b &= cnstr; assert(b() == c()); c.nil(); - assert(a.contains(pt.begin(), dim)); + assert(a.contains(pt.data(), dim)); if (!stop && dim > 1 && k > 0) { c6 = true; assert(!a.isUnbounded()); @@ -476,7 +476,7 @@ static void test(cindex_t dim) } END; - delete[] cnstr; + cnstr.clear(); FREE(dbm2); FREE(dbm); // meant to check branches @@ -496,7 +496,7 @@ int main(int argc, char* argv[]) start = atoi(argv[1]); end = atoi(argv[2]); - seed = argc > 3 ? atoi(argv[3]) : time(NULL); + seed = argc > 3 ? atoi(argv[3]) : time(nullptr); srand(seed); if (start < 1) { cerr << "Minimum dimension=1 taken\n"; diff --git a/test/test_valuation.cpp b/test/test_valuation.cpp index 5573693..a9e7262 100644 --- a/test/test_valuation.cpp +++ b/test/test_valuation.cpp @@ -1,29 +1,21 @@ // -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; indent-tabs-mode: nil; -*- //////////////////////////////////////////////////////////////////// // -// Filename : testvaluation.cpp -// -// Basic tests of IntValuation and DoubleValuation +// Unit tests for IntValuation and DoubleValuation // // This file is a part of the UPPAAL toolkit. +// Copyright (c) 2011 - 2022, Aalborg University. // Copyright (c) 1995 - 2003, Uppsala University and Aalborg University. -// All right reserved. -// -// $Id: testvaluation.cpp,v 1.1 2005/04/22 15:20:09 adavid Exp $ +// All rights reserved. // /////////////////////////////////////////////////////////////////// -// Tests are always for debugging. - -#ifdef NDEBUG -#undef NDEBUG -#endif - #include "dbm/Valuation.h" -#include "debug/macros.h" +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include -#include + +#include using namespace std; using namespace dbm; @@ -34,47 +26,28 @@ static void test(int size) DoubleValuation dv(size); for (int i = 0; i < size; ++i) { - ASSERT(iv[i] == 0, cerr << iv[i] << endl); - ASSERT(dv[i] == 0.0, cerr << dv[i] << endl); + CHECK(iv[i] == 0); + CHECK(dv[i] == 0.0); } (iv += 3) -= 2; (dv += 3.1) -= 2; if (size > 0) { - assert(iv[0] == 0.0); - assert(dv[0] == 0.0); + CHECK(iv[0] == 0.0); + CHECK(dv[0] == 0.0); } for (int i = 1; i < size; ++i) { - ASSERT(iv[i] == 1, cerr << iv[i] << endl); - ASSERT(dv[i] == 1.1, cerr << dv[i] << endl); + CHECK(iv[i] == 1); + CHECK(dv[i] == 1.1); } cout << iv << endl << dv << endl; } -int main(int argc, char* argv[]) +TEST_CASE("Valuation") { - int n, seed; - - if (argc < 2) { - cerr << "Usage: " << argv[0] << " size [seed]\n"; - return 1; - } - - n = atoi(argv[1]); - seed = argc > 2 ? atoi(argv[2]) : time(NULL); - srand(seed); - - /* Print the seed for the random generator - * to be able to repeat a failed test. - */ - cout << "Test with seed=" << seed << endl; - - for (int i = 0; i <= n; ++i) + for (int i = 0; i <= 20; ++i) test(i); - - cout << "Passed\n"; - return 0; }