diff --git a/.github/workflows/build-nuget-package.yml b/.github/workflows/build-nuget-package.yml index f2c3cf84bd..ab620e4a62 100644 --- a/.github/workflows/build-nuget-package.yml +++ b/.github/workflows/build-nuget-package.yml @@ -185,7 +185,7 @@ jobs: - name: Dotnet pack working-directory: ${{runner.workspace}}/build/dotnet/Highs.Native - run: dotnet pack -c Release /p:Version=1.7.2 + run: dotnet pack -c Release /p:Version=1.8.0 - uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 5dfb9d9591..b621d680e7 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -8,9 +8,9 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: DoozyX/clang-format-lint-action@v0.14 + - uses: DoozyX/clang-format-lint-action@v0.18 with: source: 'app/ src/Highs.h ./src/lp_data ./src/mip ./src/model ./src/simplex ./src/presolve ./src/simplex ./src/util ./src/test' #./src/test ./interfaces' extensions: 'h,cpp,c' - clangFormatVersion: 14 + clangFormatVersion: 18 diff --git a/.github/workflows/sanitizers-cmake.yml b/.github/workflows/sanitizers-cmake.yml new file mode 100644 index 0000000000..6af8517c18 --- /dev/null +++ b/.github/workflows/sanitizers-cmake.yml @@ -0,0 +1,55 @@ +name: sanitizers-cmake +on: [] #push + +jobs: + sanitizer_release: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + # os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest] + sanitizer: [Address, Thread, Leak] + steps: + - uses: actions/checkout@v4 + + - name: Create Build Environment + run: cmake -E make_directory ${{runner.workspace}}/build + + - name: Configure CMake and Build + shell: bash + working-directory: ${{runner.workspace}}/build + run: | + cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDEBUG_MEMORY=${{ matrix.sanitizer }} + cmake --build . --parallel + + - name: Run + working-directory: ${{runner.workspace}}/build + shell: bash + run: ./bin/highs $GITHUB_WORKSPACE/check/instances/afiro.mps + + sanitizer_debug: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + # os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest] + sanitizer: [Address, Thread, Leak] + steps: + - uses: actions/checkout@v4 + + - name: Create Build Environment + run: cmake -E make_directory ${{runner.workspace}}/build + + - name: Configure CMake and Build + shell: bash + working-directory: ${{runner.workspace}}/build + run: | + cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=Debug -DDEBUG_MEMORY=${{ matrix.sanitizer }} + cmake --build . --parallel + + - name: Run + working-directory: ${{runner.workspace}}/build + shell: bash + run: ./bin/highs $GITHUB_WORKSPACE/check/instances/afiro.mps \ No newline at end of file diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers-meson.yml similarity index 100% rename from .github/workflows/sanitizers.yml rename to .github/workflows/sanitizers-meson.yml diff --git a/.github/workflows/test-nuget-macos.yml b/.github/workflows/test-nuget-macos.yml index 32724a3691..1cb9d04e4e 100644 --- a/.github/workflows/test-nuget-macos.yml +++ b/.github/workflows/test-nuget-macos.yml @@ -35,7 +35,7 @@ jobs: - name: Dotnet pack working-directory: ${{runner.workspace}}/build/dotnet/Highs.Native - run: dotnet pack -c Release /p:Version=1.7.2 + run: dotnet pack -c Release /p:Version=1.8.0 - name: Add local feed run: dotnet nuget add source ${{runner.workspace}}/nugets @@ -81,7 +81,7 @@ jobs: - name: Dotnet pack working-directory: ${{runner.workspace}}/build/dotnet/Highs.Native - run: dotnet pack -c Release /p:Version=1.7.2 + run: dotnet pack -c Release /p:Version=1.8.0 - name: Add local feed run: dotnet nuget add source ${{runner.workspace}}/nugets diff --git a/.github/workflows/test-nuget-package.yml b/.github/workflows/test-nuget-package.yml index 00fa60ca12..6b083d15d7 100644 --- a/.github/workflows/test-nuget-package.yml +++ b/.github/workflows/test-nuget-package.yml @@ -35,7 +35,7 @@ jobs: - name: Dotnet pack working-directory: ${{runner.workspace}}/build/dotnet/Highs.Native - run: dotnet pack -c Release /p:Version=1.7.2 + run: dotnet pack -c Release /p:Version=1.8.0 - name: Add local feed run: dotnet nuget add source ${{runner.workspace}}/nugets @@ -81,7 +81,7 @@ jobs: - name: Dotnet pack working-directory: ${{runner.workspace}}/build/dotnet/Highs.Native - run: dotnet pack -c Release /p:Version=1.7.2 + run: dotnet pack -c Release /p:Version=1.8.0 - name: Add local feed run: dotnet nuget add source ${{runner.workspace}}/nugets @@ -124,7 +124,7 @@ jobs: - name: Dotnet pack working-directory: ${{runner.workspace}}/build/dotnet/Highs.Native - run: dotnet pack -c Release /p:Version=1.7.2 + run: dotnet pack -c Release /p:Version=1.8.0 - name: Add local feed run: dotnet nuget add source ${{runner.workspace}}/nugets @@ -167,7 +167,7 @@ jobs: - name: Dotnet pack working-directory: ${{runner.workspace}}/build/dotnet/Highs.Native - run: dotnet pack -c Release /p:Version=1.7.2 + run: dotnet pack -c Release /p:Version=1.8.0 - name: Add local feed run: dotnet nuget add source ${{runner.workspace}}/nugets @@ -212,7 +212,7 @@ jobs: - name: Dotnet pack working-directory: ${{runner.workspace}}/build/dotnet/Highs.Native - run: dotnet pack -c Release /p:Version=1.7.2 + run: dotnet pack -c Release /p:Version=1.8.0 - name: Add local feed run: dotnet nuget add source -n name ${{runner.workspace}}\nugets @@ -228,5 +228,5 @@ jobs: dotnet new console rm Program.cs cp ${{runner.workspace}}\HiGHS\examples\call_highs_from_csharp.cs . - dotnet add package Highs.Native -v 1.7.2 -s ${{runner.workspace}}\nugets + dotnet add package Highs.Native -v 1.8.0 -s ${{runner.workspace}}\nugets dotnet run diff --git a/.github/workflows/test-nuget-ubuntu.yml b/.github/workflows/test-nuget-ubuntu.yml index 7ad24fe9e2..7c233abd36 100644 --- a/.github/workflows/test-nuget-ubuntu.yml +++ b/.github/workflows/test-nuget-ubuntu.yml @@ -31,7 +31,7 @@ jobs: - name: Dotnet pack working-directory: ${{runner.workspace}}/build/dotnet/Highs.Native - run: dotnet pack -c Release /p:Version=1.7.2 + run: dotnet pack -c Release /p:Version=1.8.0 - name: Add local feed run: dotnet nuget add source ${{runner.workspace}}/nugets @@ -74,7 +74,7 @@ jobs: - name: Dotnet pack working-directory: ${{runner.workspace}}/build/dotnet/Highs.Native - run: dotnet pack -c Release /p:Version=1.7.2 + run: dotnet pack -c Release /p:Version=1.8.0 - name: Add local feed run: dotnet nuget add source ${{runner.workspace}}/nugets diff --git a/.github/workflows/test-nuget-win.yml b/.github/workflows/test-nuget-win.yml index 965fce54d3..47bdf0ac60 100644 --- a/.github/workflows/test-nuget-win.yml +++ b/.github/workflows/test-nuget-win.yml @@ -33,7 +33,7 @@ jobs: - name: Dotnet pack working-directory: ${{runner.workspace}}/build/dotnet/Highs.Native - run: dotnet pack -c Release /p:Version=1.7.2 + run: dotnet pack -c Release /p:Version=1.8.0 - name: Add local feed run: dotnet nuget add source -n name ${{runner.workspace}}\nugets @@ -49,5 +49,5 @@ jobs: dotnet new console rm Program.cs cp ${{runner.workspace}}\HiGHS\examples\call_highs_from_csharp.cs . - dotnet add package Highs.Native -v 1.7.2 -s ${{runner.workspace}}\nugets + dotnet add package Highs.Native -v 1.8.0 -s ${{runner.workspace}}\nugets dotnet run diff --git a/CMakeLists.txt b/CMakeLists.txt index aa59302874..cfeea6ee9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,23 @@ if(EXISTS "${LOC_PATH}") endif() option(FAST_BUILD "Fast build: " ON) +find_program(GIT git) + +if((GIT) AND(EXISTS ${HIGHS_SOURCE_DIR}/.git)) + execute_process( + COMMAND ${GIT} status + WORKING_DIRECTORY ${HIGHS_SOURCE_DIR} OUTPUT_QUIET) + + execute_process( + COMMAND ${GIT} describe --always + WORKING_DIRECTORY ${HIGHS_SOURCE_DIR} + OUTPUT_VARIABLE GITHASH OUTPUT_STRIP_TRAILING_WHITESPACE) + # string(REGEX REPLACE "^.*-g" "" GITHASH ${GITHASH}) +else() + set(GITHASH "n/a") +endif() + +message(STATUS "Git hash: " ${GITHASH}) # By default only build the C++ library. option(BUILD_CXX "Build C++ library" ON) message(STATUS "Build C++ library: ${BUILD_CXX}") @@ -71,6 +88,12 @@ if (PYTHON_BUILD_SETUP) set(ZLIB OFF) endif() +# Address | Thread | Leak +# Linux atm +# Only Debug is theted atm +# See below for RelWithDeb info, todo test wip +set(DEBUG_MEMORY "Off" CACHE STRING "Sanitizers") + # emscripten option(EMSCRIPTEN_HTML "Emscripten HTML output" OFF) @@ -358,12 +381,6 @@ if(NOT FAST_BUILD OR CSHARP) endif(CMAKE_CSharp_COMPILER) endif() -# uncomment for memory debugging -# set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined") -# set (CMAKE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_LINKER_FLAGS_RELWITHDEBINFO} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined") -# set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined") -# set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined") - # if zlib is found, then we can enable reading zlib-compressed input if(ZLIB AND NOT TARGET ZLIB::ZLIB) find_package(ZLIB 1.2.3) @@ -375,18 +392,6 @@ set(CPACK_PACKAGE_VERSION_MINOR "${HIGHS_VERSION_MINOR}") set(CPACK_PACKAGE_VERSION_PATCH "${HIGHS_VERSION_PATCH}") set(CPACK_PACKAGE_VENDOR "University of Edinburgh") -find_program(GIT git) - -if((GIT) AND(EXISTS ${HIGHS_SOURCE_DIR}/.git)) - execute_process( - COMMAND ${GIT} describe --always --dirty - WORKING_DIRECTORY ${HIGHS_SOURCE_DIR} - OUTPUT_VARIABLE GITHASH OUTPUT_STRIP_TRAILING_WHITESPACE) - string(REGEX REPLACE "^.*-g" "" GITHASH ${GITHASH}) -else() - set(GITHASH "n/a") -endif() -message(STATUS "Git hash: " ${GITHASH}) # Deprecate # string(TIMESTAMP TODAY "%Y-%m-%d") @@ -394,6 +399,56 @@ message(STATUS "Git hash: " ${GITHASH}) configure_file(${HIGHS_SOURCE_DIR}/src/HConfig.h.in ${HIGHS_BINARY_DIR}/HConfig.h) +if (DEBUG_MEMORY STREQUAL "Address") + set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} \ + -fsanitize=address,undefined \ + -fno-omit-frame-pointer \ + -fsanitize-address-use-after-scope") + set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} \ + -fsanitize=address,undefined \ + -fno-omit-frame-pointer \ + -fsanitize-address-use-after-scope") + + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} \ + -fsanitize=address,undefined \ + -fno-omit-frame-pointer \ + -fno-optimize-sibling-calls ") + set (CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} \ + -fsanitize=address,undefined \ + -fno-omit-frame-pointer \ + -fno-optimize-sibling-calls ") + +elseif (DEBUG_MEMORY STREQUAL "Thread") + set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} \ + -fsanitize=thread,undefined \ + -fno-omit-frame-pointer") + set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} \ + -fsanitize=thread,undefined \ + -fno-omit-frame-pointer") + + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} \ + -fsanitize=thread,undefined \ + -fno-omit-frame-pointer ") + set (CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} \ + -fsanitize=thread,undefined \ + -fno-omit-frame-pointer ") + +elseif (DEBUG_MEMORY STREQUAL "Leak") + set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} \ + -fsanitize=leak,undefined \ + -fno-omit-frame-pointer") + set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} \ + -fsanitize=leak,undefined \ + -fno-omit-frame-pointer") + + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} \ + -fsanitize=leak,undefined \ + -fno-omit-frame-pointer ") + set (CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} \ + -fsanitize=leak,undefined \ + -fno-omit-frame-pointer ") +endif() + if(NOT FAST_BUILD) # For the moment keep above coverage part in case we are testing at CI. option(CI "CI extended tests" ON) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 225f329932..e45876de54 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ Contact [Julian](https://github.com/jajhall) (General issues and solvers), [Ivet ## Improve the documentation -The top level [documentation](https://ergo-code.github.io/HiGHS/) is created using [Docsy](https://www.docsy.dev/), with the files held on the [HiGHS repository](https://github.com/ERGO-Code/HiGHS/tree/docsy). If your change is small (like fixing typos, or one or two sentence corrections), the easiest way to do this is to fork the branch and create a pull request. (See *Contribute code to HiGHS* below for more on this.) If your change is larger, or touches multiple files, please raise an issue describing what you want to do. +The top level [documentation](https://ergo-code.github.io/HiGHS/) is created using [Docsy](https://www.docsy.dev/), with the files held on the [HiGHS repository](https://github.com/ERGO-Code/HiGHS/tree/master/docs). If your change is small (like fixing typos, or one or two sentence corrections), the easiest way to do this is to fork the branch and create a pull request. (See *Contribute code to HiGHS* below for more on this.) If your change is larger, or touches multiple files, please raise an issue describing what you want to do. ## Raise an issue diff --git a/FEATURES.md b/FEATURES.md index c587caf6ee..bf65e77459 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -1,22 +1,43 @@ ## Build changes -The python wrapper highspy is now available for aarch64 on manylinux -This allows highs to be run through Python on AWS arm64 +## Code changes + +Added `int64_t mip_total_lp_iterations` to `HighsCallbackDataOut` and modified accessor function + +`Highs::writeSolution` and `Highs::writeBasis` now being done via `HighsIO` logging, so can be redirected to logging callback. + +Introduced `const double kHighsUndefined` as value of undefined values in a user solution. It's equal to `kHighsInf` + +Added `Highs::setSolution(const HighsInt num_entries, const HighsInt* index, const double* value);` to allow a sparse primal solution to be defined. When a MIP is solved to do this, the value of (new) option `mip_max_start_nodes` is used for `mip_max_nodes` to avoid excessive cost + +Added options `write_presolved_model_to_file` and `write_presolved_model_file` so that presolved model can be written via a command line option + +Added `Highs::feasibilityRelaxation` to solve the problem of minimizing a (possibly weighted) sum of (allowable) infeasibilities in an LP/MIP. + +Added Python utility `examples/plot_highs_log.py` (due to @Thell) to visualise progress of the MIP solver. + +Added minimal documentation of solvers and how simplex variants can be run + +Methods receiving matrix data where only small values are explicit zeros (so removed internally) are now silent and return `HighsStatus::kOk` (since internal matrix is exact) + +Now multiplying by pre-computed reciprocals rather than performing divisions in loops in simplex solver: LP performance improvement ~2.5% + +Primal and dual residuals after IPM and cuPDLP-C are checked, and corrections applied to row solution and column duals + +`Highs::passModelName` added to allow name to be given to the incumbent model + +Memory leaks in cuPDLP-C fixed + +Bug fixed in MIP presolve + + + + + -Bug fix for fortran on macOS -## Code changes -The accessor function Highs_getCallbackDataOutItem in the C API means -that `pdlp_iteration_count` can be moved back to where it was inserted -into the `HighsCallbackDataOut` struct in v1.7.0, which broke the C -API. This fixes #1812 -Some duplicate code has been eliminated from the MIP solver, and -modifications made to eliminate compiler warnings -Declaration of the (deprecated) method `char* highsCompilationDate()` -has been corrected -Fixed bug when describing integrality status during the human-readable solution write diff --git a/MODULE.bazel b/MODULE.bazel index 9eff6f4a45..44a64ec712 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,6 +1,6 @@ module( name = "highs", - version = "1.7.2", + version = "1.8.0", ) bazel_dep( diff --git a/README.md b/README.md index 8ea121d8bd..2758c02fdd 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,7 @@ The nuget package Highs.Native is on https://www.nuget.org, at https://www.nuget It can be added to your C# project with `dotnet` ```shell -dotnet add package Highs.Native --version 1.7.2 +dotnet add package Highs.Native --version 1.8.0 ``` The nuget package contains runtime libraries for diff --git a/Version.txt b/Version.txt index ee0905004d..baa9e163b1 100644 --- a/Version.txt +++ b/Version.txt @@ -1,4 +1,4 @@ HIGHS_MAJOR=1 -HIGHS_MINOR=7 -HIGHS_PATCH=2 +HIGHS_MINOR=8 +HIGHS_PATCH=0 #PRE_RELEASE=YES diff --git a/app/RunHighs.cpp b/app/RunHighs.cpp index 183bb917c8..242f42bdd9 100644 --- a/app/RunHighs.cpp +++ b/app/RunHighs.cpp @@ -15,6 +15,9 @@ // #include "io/HighsIO.h" #include "lp_data/HighsRuntimeOptions.h" +// uncomment if we will be shutting down task executor from exe +// #include "parallel/HighsParallel.h" + void reportModelStatsOrError(const HighsLogOptions& log_options, const HighsStatus read_status, const HighsModel& model); @@ -62,6 +65,24 @@ int main(int argc, char** argv) { return (int)read_solution_status; } } + if (options.write_presolved_model_to_file) { + // Run presolve and write the presolved model to a file + HighsStatus status = highs.presolve(); + if (status == HighsStatus::kError) return int(status); + HighsPresolveStatus model_presolve_status = highs.getModelPresolveStatus(); + const bool ok_to_write = + model_presolve_status == HighsPresolveStatus::kNotReduced || + model_presolve_status == HighsPresolveStatus::kReduced || + model_presolve_status == HighsPresolveStatus::kReducedToEmpty || + model_presolve_status == HighsPresolveStatus::kTimeout; + if (!ok_to_write) { + highsLogUser(log_options, HighsLogType::kInfo, + "No presolved model to write to file\n"); + return int(status); + } + status = highs.writePresolvedModel(options.write_presolved_model_file); + return int(status); + } // Solve the model HighsStatus run_status = highs.run(); if (run_status == HighsStatus::kError) return int(run_status); @@ -78,6 +99,10 @@ int main(int argc, char** argv) { if (write_model_status == HighsStatus::kError) return (int)write_model_status; // todo: change to write model error } + + // Shut down task executor: optional and wip + // HighsTaskExecutor::shutdown(true); + return (int)run_status; } diff --git a/app/cxxopts.hpp b/app/cxxopts.hpp index 080a7c0391..350e2bcd96 100644 --- a/app/cxxopts.hpp +++ b/app/cxxopts.hpp @@ -25,8 +25,8 @@ THE SOFTWARE. #ifndef CXXOPTS_HPP_INCLUDED #define CXXOPTS_HPP_INCLUDED -#include #include +#include #include #include #include @@ -47,1613 +47,1072 @@ THE SOFTWARE. #define CXXOPTS__VERSION_MINOR 2 #define CXXOPTS__VERSION_PATCH 0 -namespace cxxopts -{ - static constexpr struct { - uint8_t major, minor, patch; - } version = { - CXXOPTS__VERSION_MAJOR, - CXXOPTS__VERSION_MINOR, - CXXOPTS__VERSION_PATCH - }; -} +namespace cxxopts { +static constexpr struct { + uint8_t major, minor, patch; +} version = {CXXOPTS__VERSION_MAJOR, CXXOPTS__VERSION_MINOR, + CXXOPTS__VERSION_PATCH}; +} // namespace cxxopts -//when we ask cxxopts to use Unicode, help strings are processed using ICU, -//which results in the correct lengths being computed for strings when they -//are formatted for the help output -//it is necessary to make sure that can be found by the -//compiler, and that icu-uc is linked in to the binary. +// when we ask cxxopts to use Unicode, help strings are processed using ICU, +// which results in the correct lengths being computed for strings when they +// are formatted for the help output +// it is necessary to make sure that can be found by the +// compiler, and that icu-uc is linked in to the binary. #ifdef CXXOPTS_USE_UNICODE #include -namespace cxxopts -{ - typedef icu::UnicodeString String; +namespace cxxopts { +typedef icu::UnicodeString String; - inline - String - toLocalString(std::string s) - { - return icu::UnicodeString::fromUTF8(std::move(s)); - } +inline String toLocalString(std::string s) { + return icu::UnicodeString::fromUTF8(std::move(s)); +} - class UnicodeStringIterator : public - std::iterator - { - public: +class UnicodeStringIterator + : public std::iterator { + public: + UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos) + : s(string), i(pos) {} - UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos) - : s(string) - , i(pos) - { - } + value_type operator*() const { return s->char32At(i); } - value_type - operator*() const - { - return s->char32At(i); - } + bool operator==(const UnicodeStringIterator& rhs) const { + return s == rhs.s && i == rhs.i; + } - bool - operator==(const UnicodeStringIterator& rhs) const - { - return s == rhs.s && i == rhs.i; - } + bool operator!=(const UnicodeStringIterator& rhs) const { + return !(*this == rhs); + } - bool - operator!=(const UnicodeStringIterator& rhs) const - { - return !(*this == rhs); - } + UnicodeStringIterator& operator++() { + ++i; + return *this; + } - UnicodeStringIterator& - operator++() - { - ++i; - return *this; - } + UnicodeStringIterator operator+(int32_t v) { + return UnicodeStringIterator(s, i + v); + } - UnicodeStringIterator - operator+(int32_t v) - { - return UnicodeStringIterator(s, i + v); - } + private: + const icu::UnicodeString* s; + int32_t i; +}; - private: - const icu::UnicodeString* s; - int32_t i; - }; +inline String& stringAppend(String& s, String a) { + return s.append(std::move(a)); +} - inline - String& - stringAppend(String&s, String a) - { - return s.append(std::move(a)); +inline String& stringAppend(String& s, int n, UChar32 c) { + for (int i = 0; i != n; ++i) { + s.append(c); } - inline - String& - stringAppend(String& s, int n, UChar32 c) - { - for (int i = 0; i != n; ++i) - { - s.append(c); - } + return s; +} - return s; +template +String& stringAppend(String& s, Iterator begin, Iterator end) { + while (begin != end) { + s.append(*begin); + ++begin; } - template - String& - stringAppend(String& s, Iterator begin, Iterator end) - { - while (begin != end) - { - s.append(*begin); - ++begin; - } + return s; +} - return s; - } +inline size_t stringLength(const String& s) { return s.length(); } - inline - size_t - stringLength(const String& s) - { - return s.length(); - } +inline std::string toUTF8String(const String& s) { + std::string result; + s.toUTF8String(result); - inline - std::string - toUTF8String(const String& s) - { - std::string result; - s.toUTF8String(result); + return result; +} - return result; - } +inline bool empty(const String& s) { return s.isEmpty(); } +} // namespace cxxopts - inline - bool - empty(const String& s) - { - return s.isEmpty(); - } +namespace std { +inline cxxopts::UnicodeStringIterator begin(const icu::UnicodeString& s) { + return cxxopts::UnicodeStringIterator(&s, 0); } -namespace std -{ - inline - cxxopts::UnicodeStringIterator - begin(const icu::UnicodeString& s) - { - return cxxopts::UnicodeStringIterator(&s, 0); - } - - inline - cxxopts::UnicodeStringIterator - end(const icu::UnicodeString& s) - { - return cxxopts::UnicodeStringIterator(&s, s.length()); - } +inline cxxopts::UnicodeStringIterator end(const icu::UnicodeString& s) { + return cxxopts::UnicodeStringIterator(&s, s.length()); } +} // namespace std -//ifdef CXXOPTS_USE_UNICODE +// ifdef CXXOPTS_USE_UNICODE #else -namespace cxxopts -{ - typedef std::string String; +namespace cxxopts { +typedef std::string String; - template - T - toLocalString(T&& t) - { - return std::forward(t); - } - - inline - size_t - stringLength(const String& s) - { - return s.length(); - } +template +T toLocalString(T&& t) { + return std::forward(t); +} - inline - String& - stringAppend(String&s, String a) - { - return s.append(std::move(a)); - } +inline size_t stringLength(const String& s) { return s.length(); } - inline - String& - stringAppend(String& s, size_t n, char c) - { - return s.append(n, c); - } +inline String& stringAppend(String& s, String a) { + return s.append(std::move(a)); +} - template - String& - stringAppend(String& s, Iterator begin, Iterator end) - { - return s.append(begin, end); - } +inline String& stringAppend(String& s, size_t n, char c) { + return s.append(n, c); +} - template - std::string - toUTF8String(T&& t) - { - return std::forward(t); - } +template +String& stringAppend(String& s, Iterator begin, Iterator end) { + return s.append(begin, end); +} - inline - bool - empty(const std::string& s) - { - return s.empty(); - } +template +std::string toUTF8String(T&& t) { + return std::forward(t); } -//ifdef CXXOPTS_USE_UNICODE +inline bool empty(const std::string& s) { return s.empty(); } +} // namespace cxxopts + +// ifdef CXXOPTS_USE_UNICODE #endif -namespace cxxopts -{ - namespace - { +namespace cxxopts { +namespace { #ifdef _WIN32 - const std::string LQUOTE("\'"); - const std::string RQUOTE("\'"); +const std::string LQUOTE("\'"); +const std::string RQUOTE("\'"); #else - const std::string LQUOTE("‘"); - const std::string RQUOTE("’"); +const std::string LQUOTE("‘"); +const std::string RQUOTE("’"); #endif - } - - class Value : public std::enable_shared_from_this - { - public: - - virtual ~Value() = default; - - virtual - std::shared_ptr - clone() const = 0; - - virtual void - parse(const std::string& text) const = 0; - - virtual void - parse() const = 0; - - virtual bool - has_default() const = 0; +} // namespace - virtual bool - is_container() const = 0; - - virtual bool - has_implicit() const = 0; +class Value : public std::enable_shared_from_this { + public: + virtual ~Value() = default; + + virtual std::shared_ptr clone() const = 0; + + virtual void parse(const std::string& text) const = 0; + + virtual void parse() const = 0; + + virtual bool has_default() const = 0; + + virtual bool is_container() const = 0; + + virtual bool has_implicit() const = 0; + + virtual std::string get_default_value() const = 0; + + virtual std::string get_implicit_value() const = 0; + + virtual std::shared_ptr default_value(const std::string& value) = 0; + + virtual std::shared_ptr implicit_value(const std::string& value) = 0; + + virtual bool is_boolean() const = 0; +}; + +class OptionException : public std::exception { + public: + OptionException(const std::string& message) : m_message(message) {} + + virtual const char* what() const noexcept { return m_message.c_str(); } + + private: + std::string m_message; +}; + +class OptionSpecException : public OptionException { + public: + OptionSpecException(const std::string& message) : OptionException(message) {} +}; + +class OptionParseException : public OptionException { + public: + OptionParseException(const std::string& message) : OptionException(message) {} +}; + +class option_exists_error : public OptionSpecException { + public: + option_exists_error(const std::string& option) + : OptionSpecException(u8"Option " + LQUOTE + option + RQUOTE + + u8" already exists") {} +}; + +class invalid_option_format_error : public OptionSpecException { + public: + invalid_option_format_error(const std::string& format) + : OptionSpecException(u8"Invalid option format " + LQUOTE + format + + RQUOTE) {} +}; + +class option_syntax_exception : public OptionParseException { + public: + option_syntax_exception(const std::string& text) + : OptionParseException(u8"Argument " + LQUOTE + text + RQUOTE + + u8" starts with a - but has incorrect syntax") {} +}; + +class option_not_exists_exception : public OptionParseException { + public: + option_not_exists_exception(const std::string& option) + : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + + u8" does not exist") {} +}; + +class missing_argument_exception : public OptionParseException { + public: + missing_argument_exception(const std::string& option) + : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + + u8" is missing an argument") {} +}; + +class option_requires_argument_exception : public OptionParseException { + public: + option_requires_argument_exception(const std::string& option) + : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + + u8" requires an argument") {} +}; + +class option_not_has_argument_exception : public OptionParseException { + public: + option_not_has_argument_exception(const std::string& option, + const std::string& arg) + : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + + u8" does not take an argument, but argument " + + LQUOTE + arg + RQUOTE + " given") {} +}; + +class option_not_present_exception : public OptionParseException { + public: + option_not_present_exception(const std::string& option) + : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + + u8" not present") {} +}; + +class argument_incorrect_type : public OptionParseException { + public: + argument_incorrect_type(const std::string& arg) + : OptionParseException(u8"Argument " + LQUOTE + arg + RQUOTE + + u8" failed to parse") {} +}; + +class option_required_exception : public OptionParseException { + public: + option_required_exception(const std::string& option) + : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + + u8" is required but not present") {} +}; + +namespace values { +namespace { +std::basic_regex integer_pattern("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); +std::basic_regex truthy_pattern("(t|T)(rue)?"); +std::basic_regex falsy_pattern("((f|F)(alse)?)?"); +} // namespace + +namespace detail { +template +struct SignedCheck; + +template +struct SignedCheck { + template + void operator()(bool negative, U u, const std::string& text) { + if (negative) { + if (u > static_cast(std::numeric_limits::min())) { + throw argument_incorrect_type(text); + } + } else { + if (u > static_cast(std::numeric_limits::max())) { + throw argument_incorrect_type(text); + } + } + } +}; - virtual std::string - get_default_value() const = 0; +template +struct SignedCheck { + template + void operator()(bool, U, const std::string&) {} +}; - virtual std::string - get_implicit_value() const = 0; +template +void check_signed_range(bool negative, U value, const std::string& text) { + SignedCheck::is_signed>()(negative, value, text); +} +} // namespace detail + +template +R checked_negate(T&& t, const std::string&, std::true_type) { + // if we got to here, then `t` is a positive number that fits into + // `R`. So to avoid MSVC C4146, we first cast it to `R`. + // See https://github.com/jarro2783/cxxopts/issues/62 for more details. + return -static_cast(t); +} - virtual std::shared_ptr - default_value(const std::string& value) = 0; +template +T checked_negate(T&&, const std::string& text, std::false_type) { + throw argument_incorrect_type(text); +} - virtual std::shared_ptr - implicit_value(const std::string& value) = 0; +template +void integer_parser(const std::string& text, T& value) { + std::smatch match; + std::regex_match(text, match, integer_pattern); - virtual bool - is_boolean() const = 0; - }; + if (match.length() == 0) { + throw argument_incorrect_type(text); + } - class OptionException : public std::exception - { - public: - OptionException(const std::string& message) - : m_message(message) - { - } + if (match.length(4) > 0) { + value = 0; + return; + } - virtual const char* - what() const noexcept - { - return m_message.c_str(); - } + using US = typename std::make_unsigned::type; - private: - std::string m_message; - }; + constexpr auto umax = std::numeric_limits::max(); + constexpr bool is_signed = std::numeric_limits::is_signed; + const bool negative = match.length(1) > 0; + const uint8_t base = match.length(2) > 0 ? 16 : 10; - class OptionSpecException : public OptionException - { - public: + auto value_match = match[3]; - OptionSpecException(const std::string& message) - : OptionException(message) - { - } - }; - - class OptionParseException : public OptionException - { - public: - OptionParseException(const std::string& message) - : OptionException(message) - { - } - }; - - class option_exists_error : public OptionSpecException - { - public: - option_exists_error(const std::string& option) - : OptionSpecException(u8"Option " + LQUOTE + option + RQUOTE + u8" already exists") - { - } - }; - - class invalid_option_format_error : public OptionSpecException - { - public: - invalid_option_format_error(const std::string& format) - : OptionSpecException(u8"Invalid option format " + LQUOTE + format + RQUOTE) - { - } - }; - - class option_syntax_exception : public OptionParseException { - public: - option_syntax_exception(const std::string& text) - : OptionParseException(u8"Argument " + LQUOTE + text + RQUOTE + - u8" starts with a - but has incorrect syntax") - { - } - }; - - class option_not_exists_exception : public OptionParseException - { - public: - option_not_exists_exception(const std::string& option) - : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + u8" does not exist") - { - } - }; - - class missing_argument_exception : public OptionParseException - { - public: - missing_argument_exception(const std::string& option) - : OptionParseException( - u8"Option " + LQUOTE + option + RQUOTE + u8" is missing an argument" - ) - { - } - }; - - class option_requires_argument_exception : public OptionParseException - { - public: - option_requires_argument_exception(const std::string& option) - : OptionParseException( - u8"Option " + LQUOTE + option + RQUOTE + u8" requires an argument" - ) - { - } - }; - - class option_not_has_argument_exception : public OptionParseException - { - public: - option_not_has_argument_exception - ( - const std::string& option, - const std::string& arg - ) - : OptionParseException( - u8"Option " + LQUOTE + option + RQUOTE + - u8" does not take an argument, but argument " + - LQUOTE + arg + RQUOTE + " given" - ) - { - } - }; - - class option_not_present_exception : public OptionParseException - { - public: - option_not_present_exception(const std::string& option) - : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + u8" not present") - { - } - }; - - class argument_incorrect_type : public OptionParseException - { - public: - argument_incorrect_type - ( - const std::string& arg - ) - : OptionParseException( - u8"Argument " + LQUOTE + arg + RQUOTE + u8" failed to parse" - ) - { - } - }; - - class option_required_exception : public OptionParseException - { - public: - option_required_exception(const std::string& option) - : OptionParseException( - u8"Option " + LQUOTE + option + RQUOTE + u8" is required but not present" - ) - { - } - }; - - namespace values - { - namespace - { - std::basic_regex integer_pattern - ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); - std::basic_regex truthy_pattern - ("(t|T)(rue)?"); - std::basic_regex falsy_pattern - ("((f|F)(alse)?)?"); - } + US result = 0; - namespace detail - { - template - struct SignedCheck; - - template - struct SignedCheck - { - template - void - operator()(bool negative, U u, const std::string& text) - { - if (negative) - { - if (u > static_cast(std::numeric_limits::min())) - { - throw argument_incorrect_type(text); - } - } - else - { - if (u > static_cast(std::numeric_limits::max())) - { - throw argument_incorrect_type(text); - } - } - } - }; - - template - struct SignedCheck - { - template - void - operator()(bool, U, const std::string&) {} - }; - - template - void - check_signed_range(bool negative, U value, const std::string& text) - { - SignedCheck::is_signed>()(negative, value, text); - } - } + for (auto iter = value_match.first; iter != value_match.second; ++iter) { + US digit = 0; - template - R - checked_negate(T&& t, const std::string&, std::true_type) - { - // if we got to here, then `t` is a positive number that fits into - // `R`. So to avoid MSVC C4146, we first cast it to `R`. - // See https://github.com/jarro2783/cxxopts/issues/62 for more details. - return -static_cast(t); + if (*iter >= '0' && *iter <= '9') { + digit = *iter - '0'; + } else if (base == 16 && *iter >= 'a' && *iter <= 'f') { + digit = *iter - 'a' + 10; + } else if (base == 16 && *iter >= 'A' && *iter <= 'F') { + digit = *iter - 'A' + 10; + } else { + throw argument_incorrect_type(text); } - template - T - checked_negate(T&&, const std::string& text, std::false_type) - { + if (umax - digit < result * base) { throw argument_incorrect_type(text); } - template - void - integer_parser(const std::string& text, T& value) - { - std::smatch match; - std::regex_match(text, match, integer_pattern); - - if (match.length() == 0) - { - throw argument_incorrect_type(text); - } - - if (match.length(4) > 0) - { - value = 0; - return; - } + result = result * base + digit; + } - using US = typename std::make_unsigned::type; + detail::check_signed_range(negative, result, text); - constexpr auto umax = std::numeric_limits::max(); - constexpr bool is_signed = std::numeric_limits::is_signed; - const bool negative = match.length(1) > 0; - const uint8_t base = match.length(2) > 0 ? 16 : 10; + if (negative) { + value = checked_negate(result, text, + std::integral_constant()); + } else { + value = result; + } +} - auto value_match = match[3]; +template +void stringstream_parser(const std::string& text, T& value) { + std::stringstream in(text); + in >> value; + if (!in) { + throw argument_incorrect_type(text); + } +} - US result = 0; +inline void parse_value(const std::string& text, uint8_t& value) { + integer_parser(text, value); +} - for (auto iter = value_match.first; iter != value_match.second; ++iter) - { - US digit = 0; +inline void parse_value(const std::string& text, int8_t& value) { + integer_parser(text, value); +} - if (*iter >= '0' && *iter <= '9') - { - digit = *iter - '0'; - } - else if (base == 16 && *iter >= 'a' && *iter <= 'f') - { - digit = *iter - 'a' + 10; - } - else if (base == 16 && *iter >= 'A' && *iter <= 'F') - { - digit = *iter - 'A' + 10; - } - else - { - throw argument_incorrect_type(text); - } +inline void parse_value(const std::string& text, uint16_t& value) { + integer_parser(text, value); +} - if (umax - digit < result * base) - { - throw argument_incorrect_type(text); - } +inline void parse_value(const std::string& text, int16_t& value) { + integer_parser(text, value); +} - result = result * base + digit; - } +inline void parse_value(const std::string& text, uint32_t& value) { + integer_parser(text, value); +} - detail::check_signed_range(negative, result, text); +inline void parse_value(const std::string& text, int32_t& value) { + integer_parser(text, value); +} - if (negative) - { - value = checked_negate(result, - text, - std::integral_constant()); - } - else - { - value = result; - } - } +inline void parse_value(const std::string& text, uint64_t& value) { + integer_parser(text, value); +} - template - void stringstream_parser(const std::string& text, T& value) - { - std::stringstream in(text); - in >> value; - if (!in) { - throw argument_incorrect_type(text); - } - } +inline void parse_value(const std::string& text, int64_t& value) { + integer_parser(text, value); +} - inline - void - parse_value(const std::string& text, uint8_t& value) - { - integer_parser(text, value); - } +inline void parse_value(const std::string& text, bool& value) { + std::smatch result; + std::regex_match(text, result, truthy_pattern); - inline - void - parse_value(const std::string& text, int8_t& value) - { - integer_parser(text, value); - } + if (!result.empty()) { + value = true; + return; + } - inline - void - parse_value(const std::string& text, uint16_t& value) - { - integer_parser(text, value); - } + std::regex_match(text, result, falsy_pattern); + if (!result.empty()) { + value = false; + return; + } - inline - void - parse_value(const std::string& text, int16_t& value) - { - integer_parser(text, value); - } + throw argument_incorrect_type(text); +} - inline - void - parse_value(const std::string& text, uint32_t& value) - { - integer_parser(text, value); - } +inline void parse_value(const std::string& text, std::string& value) { + value = text; +} - inline - void - parse_value(const std::string& text, int32_t& value) - { - integer_parser(text, value); - } +// The fallback parser. It uses the stringstream parser to parse all types +// that have not been overloaded explicitly. It has to be placed in the +// source code before all other more specialized templates. +template +void parse_value(const std::string& text, T& value) { + stringstream_parser(text, value); +} - inline - void - parse_value(const std::string& text, uint64_t& value) - { - integer_parser(text, value); - } +template +void parse_value(const std::string& text, std::vector& value) { + T v; + parse_value(text, v); + value.push_back(v); +} - inline - void - parse_value(const std::string& text, int64_t& value) - { - integer_parser(text, value); - } +#ifdef CXXOPTS_HAS_OPTIONAL +template +void parse_value(const std::string& text, std::optional& value) { + T result; + parse_value(text, result); + value = std::move(result); +} +#endif - inline - void - parse_value(const std::string& text, bool& value) - { - std::smatch result; - std::regex_match(text, result, truthy_pattern); - - if (!result.empty()) - { - value = true; - return; - } +template +struct type_is_container { + static constexpr bool value = false; +}; - std::regex_match(text, result, falsy_pattern); - if (!result.empty()) - { - value = false; - return; - } +template +struct type_is_container> { + static constexpr bool value = true; +}; - throw argument_incorrect_type(text); - } +template +class abstract_value : public Value { + using Self = abstract_value; - inline - void - parse_value(const std::string& text, std::string& value) - { - value = text; - } + public: + abstract_value() : m_result(std::make_shared()), m_store(m_result.get()) {} - // The fallback parser. It uses the stringstream parser to parse all types - // that have not been overloaded explicitly. It has to be placed in the - // source code before all other more specialized templates. - template - void - parse_value(const std::string& text, T& value) { - stringstream_parser(text, value); - } + abstract_value(T* t) : m_store(t) {} - template - void - parse_value(const std::string& text, std::vector& value) - { - T v; - parse_value(text, v); - value.push_back(v); - } + virtual ~abstract_value() = default; -#ifdef CXXOPTS_HAS_OPTIONAL - template - void - parse_value(const std::string& text, std::optional& value) - { - T result; - parse_value(text, result); - value = std::move(result); + abstract_value(const abstract_value& rhs) { + if (rhs.m_result) { + m_result = std::make_shared(); + m_store = m_result.get(); + } else { + m_store = rhs.m_store; } -#endif - - template - struct type_is_container - { - static constexpr bool value = false; - }; - - template - struct type_is_container> - { - static constexpr bool value = true; - }; - - template - class abstract_value : public Value - { - using Self = abstract_value; - - public: - abstract_value() - : m_result(std::make_shared()) - , m_store(m_result.get()) - { - } - - abstract_value(T* t) - : m_store(t) - { - } - virtual ~abstract_value() = default; + m_default = rhs.m_default; + m_implicit = rhs.m_implicit; + m_default_value = rhs.m_default_value; + m_implicit_value = rhs.m_implicit_value; + } - abstract_value(const abstract_value& rhs) - { - if (rhs.m_result) - { - m_result = std::make_shared(); - m_store = m_result.get(); - } - else - { - m_store = rhs.m_store; - } + void parse(const std::string& text) const { parse_value(text, *m_store); } - m_default = rhs.m_default; - m_implicit = rhs.m_implicit; - m_default_value = rhs.m_default_value; - m_implicit_value = rhs.m_implicit_value; - } + bool is_container() const { return type_is_container::value; } - void - parse(const std::string& text) const - { - parse_value(text, *m_store); - } + void parse() const { parse_value(m_default_value, *m_store); } - bool - is_container() const - { - return type_is_container::value; - } + bool has_default() const { return m_default; } - void - parse() const - { - parse_value(m_default_value, *m_store); - } + bool has_implicit() const { return m_implicit; } - bool - has_default() const - { - return m_default; - } + std::shared_ptr default_value(const std::string& value) { + m_default = true; + m_default_value = value; + return shared_from_this(); + } - bool - has_implicit() const - { - return m_implicit; - } + std::shared_ptr implicit_value(const std::string& value) { + m_implicit = true; + m_implicit_value = value; + return shared_from_this(); + } - std::shared_ptr - default_value(const std::string& value) - { - m_default = true; - m_default_value = value; - return shared_from_this(); - } + std::string get_default_value() const { return m_default_value; } - std::shared_ptr - implicit_value(const std::string& value) - { - m_implicit = true; - m_implicit_value = value; - return shared_from_this(); - } + std::string get_implicit_value() const { return m_implicit_value; } - std::string - get_default_value() const - { - return m_default_value; - } + bool is_boolean() const { return std::is_same::value; } - std::string - get_implicit_value() const - { - return m_implicit_value; - } + const T& get() const { + if (m_store == nullptr) { + return *m_result; + } else { + return *m_store; + } + } - bool - is_boolean() const - { - return std::is_same::value; - } + protected: + std::shared_ptr m_result; + T* m_store; - const T& - get() const - { - if (m_store == nullptr) - { - return *m_result; - } - else - { - return *m_store; - } - } + bool m_default = false; + bool m_implicit = false; - protected: - std::shared_ptr m_result; - T* m_store; + std::string m_default_value; + std::string m_implicit_value; +}; - bool m_default = false; - bool m_implicit = false; +template +class standard_value : public abstract_value { + public: + using abstract_value::abstract_value; - std::string m_default_value; - std::string m_implicit_value; - }; + std::shared_ptr clone() const { + return std::make_shared>(*this); + } +}; - template - class standard_value : public abstract_value - { - public: - using abstract_value::abstract_value; +template <> +class standard_value : public abstract_value { + public: + ~standard_value() = default; - std::shared_ptr - clone() const - { - return std::make_shared>(*this); - } - }; + standard_value() { set_default_and_implicit(); } - template <> - class standard_value : public abstract_value - { - public: - ~standard_value() = default; + standard_value(bool* b) : abstract_value(b) { set_default_and_implicit(); } - standard_value() - { - set_default_and_implicit(); - } + std::shared_ptr clone() const { + return std::make_shared>(*this); + } - standard_value(bool* b) - : abstract_value(b) - { - set_default_and_implicit(); - } + private: + void set_default_and_implicit() { + m_default = true; + m_default_value = "false"; + m_implicit = true; + m_implicit_value = "true"; + } +}; +} // namespace values - std::shared_ptr - clone() const - { - return std::make_shared>(*this); - } +template +std::shared_ptr value() { + return std::make_shared>(); +} - private: +template +std::shared_ptr value(T& t) { + return std::make_shared>(&t); +} - void - set_default_and_implicit() - { - m_default = true; - m_default_value = "false"; - m_implicit = true; - m_implicit_value = "true"; - } - }; +class OptionAdder; + +class OptionDetails { + public: + OptionDetails(const std::string& short_, const std::string& long_, + const String& desc, std::shared_ptr val) + : m_short(short_), + m_long(long_), + m_desc(desc), + m_value(val), + m_count(0) {} + + OptionDetails(const OptionDetails& rhs) + : m_desc(rhs.m_desc), m_count(rhs.m_count) { + m_value = rhs.m_value->clone(); } - template - std::shared_ptr - value() - { - return std::make_shared>(); + OptionDetails(OptionDetails&& rhs) = default; + + const String& description() const { return m_desc; } + + const Value& value() const { return *m_value; } + + std::shared_ptr make_storage() const { return m_value->clone(); } + + const std::string& short_name() const { return m_short; } + + const std::string& long_name() const { return m_long; } + + private: + std::string m_short; + std::string m_long; + String m_desc; + std::shared_ptr m_value; + int m_count; +}; + +struct HelpOptionDetails { + std::string s; + std::string l; + String desc; + bool has_default; + std::string default_value; + bool has_implicit; + std::string implicit_value; + std::string arg_help; + bool is_container; + bool is_boolean; +}; + +struct HelpGroupDetails { + std::string name; + std::string description; + std::vector options; +}; + +class OptionValue { + public: + void parse(std::shared_ptr details, + const std::string& text) { + ensure_value(details); + ++m_count; + m_value->parse(text); } - template - std::shared_ptr - value(T& t) - { - return std::make_shared>(&t); + void parse_default(std::shared_ptr details) { + ensure_value(details); + m_value->parse(); } - class OptionAdder; - - class OptionDetails - { - public: - OptionDetails - ( - const std::string& short_, - const std::string& long_, - const String& desc, - std::shared_ptr val - ) - : m_short(short_) - , m_long(long_) - , m_desc(desc) - , m_value(val) - , m_count(0) - { - } - - OptionDetails(const OptionDetails& rhs) - : m_desc(rhs.m_desc) - , m_count(rhs.m_count) - { - m_value = rhs.m_value->clone(); - } + size_t count() const { return m_count; } - OptionDetails(OptionDetails&& rhs) = default; - - const String& - description() const - { - return m_desc; + template + const T& as() const { + if (m_value == nullptr) { + throw std::domain_error("No value"); } - const Value& value() const { - return *m_value; - } +#ifdef CXXOPTS_NO_RTTI + return static_cast&>(*m_value).get(); +#else + return dynamic_cast&>(*m_value).get(); +#endif + } - std::shared_ptr - make_storage() const - { - return m_value->clone(); + private: + void ensure_value(std::shared_ptr details) { + if (m_value == nullptr) { + m_value = details->make_storage(); } + } - const std::string& - short_name() const - { - return m_short; - } + std::shared_ptr m_value; + size_t m_count = 0; +}; - const std::string& - long_name() const - { - return m_long; - } +class KeyValue { + public: + KeyValue(std::string key_, std::string value_) + : m_key(std::move(key_)), m_value(std::move(value_)) {} - private: - std::string m_short; - std::string m_long; - String m_desc; - std::shared_ptr m_value; - int m_count; - }; - - struct HelpOptionDetails - { - std::string s; - std::string l; - String desc; - bool has_default; - std::string default_value; - bool has_implicit; - std::string implicit_value; - std::string arg_help; - bool is_container; - bool is_boolean; - }; - - struct HelpGroupDetails - { - std::string name; - std::string description; - std::vector options; - }; - - class OptionValue - { - public: - void - parse - ( - std::shared_ptr details, - const std::string& text - ) - { - ensure_value(details); - ++m_count; - m_value->parse(text); - } + const std::string& key() const { return m_key; } - void - parse_default(std::shared_ptr details) - { - ensure_value(details); - m_value->parse(); - } + const std::string value() const { return m_value; } - size_t - count() const - { - return m_count; - } + template + T as() const { + T result; + values::parse_value(m_value, result); + return result; + } - template - const T& - as() const - { - if (m_value == nullptr) { - throw std::domain_error("No value"); - } + private: + std::string m_key; + std::string m_value; +}; -#ifdef CXXOPTS_NO_RTTI - return static_cast&>(*m_value).get(); -#else - return dynamic_cast&>(*m_value).get(); -#endif - } +class ParseResult { + public: + ParseResult( + const std::shared_ptr< + std::unordered_map>>, + std::vector, bool allow_unrecognised, int&, char**&); - private: - void - ensure_value(std::shared_ptr details) - { - if (m_value == nullptr) - { - m_value = details->make_storage(); - } + size_t count(const std::string& o) const { + auto iter = m_options->find(o); + if (iter == m_options->end()) { + return 0; } - std::shared_ptr m_value; - size_t m_count = 0; - }; - - class KeyValue - { - public: - KeyValue(std::string key_, std::string value_) - : m_key(std::move(key_)) - , m_value(std::move(value_)) - { - } + auto riter = m_results.find(iter->second); - const - std::string& - key() const - { - return m_key; - } + return riter->second.count(); + } - const std::string - value() const - { - return m_value; - } + const OptionValue& operator[](const std::string& option) const { + auto iter = m_options->find(option); - template - T - as() const - { - T result; - values::parse_value(m_value, result); - return result; + if (iter == m_options->end()) { + throw option_not_present_exception(option); } - private: - std::string m_key; - std::string m_value; - }; + auto riter = m_results.find(iter->second); - class ParseResult - { - public: + return riter->second; + } - ParseResult( - const std::shared_ptr< - std::unordered_map> - >, - std::vector, - bool allow_unrecognised, - int&, char**&); - - size_t - count(const std::string& o) const - { - auto iter = m_options->find(o); - if (iter == m_options->end()) - { - return 0; - } + const std::vector& arguments() const { return m_sequential; } - auto riter = m_results.find(iter->second); + private: + void parse(int& argc, char**& argv); - return riter->second.count(); - } + void add_to_option(const std::string& option, const std::string& arg); - const OptionValue& - operator[](const std::string& option) const - { - auto iter = m_options->find(option); + bool consume_positional(std::string a); - if (iter == m_options->end()) - { - throw option_not_present_exception(option); - } + void parse_option(std::shared_ptr value, + const std::string& name, const std::string& arg = ""); - auto riter = m_results.find(iter->second); + void parse_default(std::shared_ptr details); - return riter->second; - } + void checked_parse_arg(int argc, char* argv[], int& current, + std::shared_ptr value, + const std::string& name); - const std::vector& - arguments() const - { - return m_sequential; - } + const std::shared_ptr< + std::unordered_map>> + m_options; + std::vector m_positional; + std::vector::iterator m_next_positional; + std::unordered_set m_positional_set; + std::unordered_map, OptionValue> m_results; - private: + bool m_allow_unrecognised; - void - parse(int& argc, char**& argv); + std::vector m_sequential; +}; - void - add_to_option(const std::string& option, const std::string& arg); +class Options { + typedef std::unordered_map> + OptionMap; - bool - consume_positional(std::string a); + public: + Options(std::string program, std::string help_string = "") + : m_program(std::move(program)), + m_help_string(toLocalString(std::move(help_string))), + m_custom_help("[OPTION...]"), + m_positional_help("positional parameters"), + m_show_positional(false), + m_allow_unrecognised(false), + m_options(std::make_shared()), + m_next_positional(m_positional.end()) {} + + Options& positional_help(std::string help_text) { + m_positional_help = std::move(help_text); + return *this; + } - void - parse_option - ( - std::shared_ptr value, - const std::string& name, - const std::string& arg = "" - ); + Options& custom_help(std::string help_text) { + m_custom_help = std::move(help_text); + return *this; + } - void - parse_default(std::shared_ptr details); + Options& show_positional_help() { + m_show_positional = true; + return *this; + } - void - checked_parse_arg - ( - int argc, - char* argv[], - int& current, - std::shared_ptr value, - const std::string& name - ); + Options& allow_unrecognised_options() { + m_allow_unrecognised = true; + return *this; + } - const std::shared_ptr< - std::unordered_map> - > m_options; - std::vector m_positional; - std::vector::iterator m_next_positional; - std::unordered_set m_positional_set; - std::unordered_map, OptionValue> m_results; + ParseResult parse(int& argc, char**& argv); - bool m_allow_unrecognised; + OptionAdder add_options(std::string group = ""); - std::vector m_sequential; - }; + void add_option(const std::string& group, const std::string& s, + const std::string& l, std::string desc, + std::shared_ptr value, std::string arg_help); - class Options - { - typedef std::unordered_map> - OptionMap; - public: - - Options(std::string program, std::string help_string = "") - : m_program(std::move(program)) - , m_help_string(toLocalString(std::move(help_string))) - , m_custom_help("[OPTION...]") - , m_positional_help("positional parameters") - , m_show_positional(false) - , m_allow_unrecognised(false) - , m_options(std::make_shared()) - , m_next_positional(m_positional.end()) - { - } + // parse positional arguments into the given option + void parse_positional(std::string option); - Options& - positional_help(std::string help_text) - { - m_positional_help = std::move(help_text); - return *this; - } + void parse_positional(std::vector options); - Options& - custom_help(std::string help_text) - { - m_custom_help = std::move(help_text); - return *this; - } + void parse_positional(std::initializer_list options); - Options& - show_positional_help() - { - m_show_positional = true; - return *this; - } + template + void parse_positional(Iterator begin, Iterator end) { + parse_positional(std::vector{begin, end}); + } - Options& - allow_unrecognised_options() - { - m_allow_unrecognised = true; - return *this; - } + std::string help(const std::vector& groups = {""}) const; - ParseResult - parse(int& argc, char**& argv); - - OptionAdder - add_options(std::string group = ""); - - void - add_option - ( - const std::string& group, - const std::string& s, - const std::string& l, - std::string desc, - std::shared_ptr value, - std::string arg_help - ); - - //parse positional arguments into the given option - void - parse_positional(std::string option); - - void - parse_positional(std::vector options); - - void - parse_positional(std::initializer_list options); - - template - void - parse_positional(Iterator begin, Iterator end) { - parse_positional(std::vector{begin, end}); - } + const std::vector groups() const; - std::string - help(const std::vector& groups = {""}) const; + const HelpGroupDetails& group_help(const std::string& group) const; - const std::vector - groups() const; + private: + void add_one_option(const std::string& option, + std::shared_ptr details); - const HelpGroupDetails& - group_help(const std::string& group) const; + String help_one_group(const std::string& group) const; - private: + void generate_group_help(String& result, + const std::vector& groups) const; - void - add_one_option - ( - const std::string& option, - std::shared_ptr details - ); + void generate_all_groups_help(String& result) const; - String - help_one_group(const std::string& group) const; + std::string m_program; + String m_help_string; + std::string m_custom_help; + std::string m_positional_help; + bool m_show_positional; + bool m_allow_unrecognised; - void - generate_group_help - ( - String& result, - const std::vector& groups - ) const; + std::shared_ptr m_options; + std::vector m_positional; + std::vector::iterator m_next_positional; + std::unordered_set m_positional_set; - void - generate_all_groups_help(String& result) const; + // mapping from groups to help options + std::map m_help; +}; - std::string m_program; - String m_help_string; - std::string m_custom_help; - std::string m_positional_help; - bool m_show_positional; - bool m_allow_unrecognised; +class OptionAdder { + public: + OptionAdder(Options& options, std::string group) + : m_options(options), m_group(std::move(group)) {} - std::shared_ptr m_options; - std::vector m_positional; - std::vector::iterator m_next_positional; - std::unordered_set m_positional_set; + OptionAdder& operator()( + const std::string& opts, const std::string& desc, + std::shared_ptr value = ::cxxopts::value(), + std::string arg_help = ""); - //mapping from groups to help options - std::map m_help; - }; + private: + Options& m_options; + std::string m_group; +}; - class OptionAdder - { - public: +namespace { +constexpr int OPTION_LONGEST = 30; +constexpr int OPTION_DESC_GAP = 2; - OptionAdder(Options& options, std::string group) - : m_options(options), m_group(std::move(group)) - { - } +std::basic_regex option_matcher( + "--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); - OptionAdder& - operator() - ( - const std::string& opts, - const std::string& desc, - std::shared_ptr value - = ::cxxopts::value(), - std::string arg_help = "" - ); - - private: - Options& m_options; - std::string m_group; - }; - - namespace - { - constexpr int OPTION_LONGEST = 30; - constexpr int OPTION_DESC_GAP = 2; - - std::basic_regex option_matcher - ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); - - std::basic_regex option_specifier - ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?"); - - String - format_option - ( - const HelpOptionDetails& o - ) - { - auto& s = o.s; - auto& l = o.l; - - String result = " "; - - if (s.size() > 0) - { - result += "-" + toLocalString(s) + ","; - } - else - { - result += " "; - } +std::basic_regex option_specifier( + "(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?"); - if (l.size() > 0) - { - result += " --" + toLocalString(l); - } +String format_option(const HelpOptionDetails& o) { + auto& s = o.s; + auto& l = o.l; - auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; + String result = " "; - if (!o.is_boolean) - { - if (o.has_implicit) - { - result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; - } - else - { - result += " " + arg; - } - } + if (s.size() > 0) { + result += "-" + toLocalString(s) + ","; + } else { + result += " "; + } - return result; - } + if (l.size() > 0) { + result += " --" + toLocalString(l); + } - String - format_description - ( - const HelpOptionDetails& o, - size_t start, - size_t width - ) - { - auto desc = o.desc; - - if (o.has_default && (!o.is_boolean || o.default_value != "false")) - { - desc += toLocalString(" (default: " + o.default_value + ")"); - } + auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; - String result; + if (!o.is_boolean) { + if (o.has_implicit) { + result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; + } else { + result += " " + arg; + } + } - auto current = std::begin(desc); - auto startLine = current; - auto lastSpace = current; + return result; +} - auto size = size_t{}; +String format_description(const HelpOptionDetails& o, size_t start, + size_t width) { + auto desc = o.desc; - while (current != std::end(desc)) - { - if (*current == ' ') - { - lastSpace = current; - } + if (o.has_default && (!o.is_boolean || o.default_value != "false")) { + desc += toLocalString(" (default: " + o.default_value + ")"); + } - if (*current == '\n') - { - startLine = current + 1; - lastSpace = startLine; - } - else if (size > width) - { - if (lastSpace == startLine) - { - stringAppend(result, startLine, current + 1); - stringAppend(result, "\n"); - stringAppend(result, start, ' '); - startLine = current + 1; - lastSpace = startLine; - } - else - { - stringAppend(result, startLine, lastSpace); - stringAppend(result, "\n"); - stringAppend(result, start, ' '); - startLine = lastSpace + 1; - } - size = 0; - } - else - { - ++size; - } + String result; - ++current; + auto current = std::begin(desc); + auto startLine = current; + auto lastSpace = current; + + auto size = size_t{}; + + while (current != std::end(desc)) { + if (*current == ' ') { + lastSpace = current; + } + + if (*current == '\n') { + startLine = current + 1; + lastSpace = startLine; + } else if (size > width) { + if (lastSpace == startLine) { + stringAppend(result, startLine, current + 1); + stringAppend(result, "\n"); + stringAppend(result, start, ' '); + startLine = current + 1; + lastSpace = startLine; + } else { + stringAppend(result, startLine, lastSpace); + stringAppend(result, "\n"); + stringAppend(result, start, ' '); + startLine = lastSpace + 1; } - - //append whatever is left - stringAppend(result, startLine, current); - - return result; + size = 0; + } else { + ++size; } + + ++current; } -inline -ParseResult::ParseResult -( - const std::shared_ptr< - std::unordered_map> - > options, - std::vector positional, - bool allow_unrecognised, - int& argc, char**& argv -) -: m_options(options) -, m_positional(std::move(positional)) -, m_next_positional(m_positional.begin()) -, m_allow_unrecognised(allow_unrecognised) -{ + // append whatever is left + stringAppend(result, startLine, current); + + return result; +} +} // namespace + +inline ParseResult::ParseResult( + const std::shared_ptr< + std::unordered_map>> + options, + std::vector positional, bool allow_unrecognised, int& argc, + char**& argv) + : m_options(options), + m_positional(std::move(positional)), + m_next_positional(m_positional.begin()), + m_allow_unrecognised(allow_unrecognised) { parse(argc, argv); } -inline -OptionAdder -Options::add_options(std::string group) -{ +inline OptionAdder Options::add_options(std::string group) { return OptionAdder(*this, std::move(group)); } -inline -OptionAdder& -OptionAdder::operator() -( - const std::string& opts, - const std::string& desc, - std::shared_ptr value, - std::string arg_help -) -{ +inline OptionAdder& OptionAdder::operator()(const std::string& opts, + const std::string& desc, + std::shared_ptr value, + std::string arg_help) { std::match_results result; std::regex_match(opts.c_str(), result, option_specifier); - if (result.empty()) - { + if (result.empty()) { throw invalid_option_format_error(opts); } const auto& short_match = result[2]; const auto& long_match = result[3]; - if (!short_match.length() && !long_match.length()) - { + if (!short_match.length() && !long_match.length()) { throw invalid_option_format_error(opts); - } else if (long_match.length() == 1 && short_match.length()) - { + } else if (long_match.length() == 1 && short_match.length()) { throw invalid_option_format_error(opts); } - auto option_names = [] - ( - const std::sub_match& short_, - const std::sub_match& long_ - ) - { - if (long_.length() == 1) - { + auto option_names = [](const std::sub_match& short_, + const std::sub_match& long_) { + if (long_.length() == 1) { return std::make_tuple(long_.str(), short_.str()); - } - else - { + } else { return std::make_tuple(short_.str(), long_.str()); } }(short_match, long_match); - m_options.add_option - ( - m_group, - std::get<0>(option_names), - std::get<1>(option_names), - desc, - value, - std::move(arg_help) - ); + m_options.add_option(m_group, std::get<0>(option_names), + std::get<1>(option_names), desc, value, + std::move(arg_help)); return *this; } -inline -void -ParseResult::parse_default(std::shared_ptr details) -{ +inline void ParseResult::parse_default(std::shared_ptr details) { m_results[details].parse_default(details); } -inline -void -ParseResult::parse_option -( - std::shared_ptr value, - const std::string& /*name*/, - const std::string& arg -) -{ +inline void ParseResult::parse_option(std::shared_ptr value, + const std::string& /*name*/, + const std::string& arg) { auto& result = m_results[value]; result.parse(value, arg); m_sequential.emplace_back(value->long_name(), arg); } -inline -void -ParseResult::checked_parse_arg -( - int argc, - char* argv[], - int& current, - std::shared_ptr value, - const std::string& name -) -{ - if (current + 1 >= argc) - { - if (value->value().has_implicit()) - { +inline void ParseResult::checked_parse_arg(int argc, char* argv[], int& current, + std::shared_ptr value, + const std::string& name) { + if (current + 1 >= argc) { + if (value->value().has_implicit()) { parse_option(value, name, value->value().get_implicit_value()); - } - else - { + } else { throw missing_argument_exception(name); } - } - else - { - if (value->value().has_implicit()) - { + } else { + if (value->value().has_implicit()) { parse_option(value, name, value->value().get_implicit_value()); - } - else - { + } else { parse_option(value, name, argv[current + 1]); ++current; } } } -inline -void -ParseResult::add_to_option(const std::string& option, const std::string& arg) -{ +inline void ParseResult::add_to_option(const std::string& option, + const std::string& arg) { auto iter = m_options->find(option); - if (iter == m_options->end()) - { + if (iter == m_options->end()) { throw option_not_exists_exception(option); } parse_option(iter->second, option, arg); } -inline -bool -ParseResult::consume_positional(std::string a) -{ - while (m_next_positional != m_positional.end()) - { +inline bool ParseResult::consume_positional(std::string a) { + while (m_next_positional != m_positional.end()) { auto iter = m_options->find(*m_next_positional); - if (iter != m_options->end()) - { + if (iter != m_options->end()) { auto& result = m_results[iter->second]; - if (!iter->second->value().is_container()) - { - if (result.count() == 0) - { + if (!iter->second->value().is_container()) { + if (result.count() == 0) { add_to_option(*m_next_positional, a); ++m_next_positional; return true; - } - else - { + } else { ++m_next_positional; continue; } - } - else - { + } else { add_to_option(*m_next_positional, a); return true; } @@ -1664,52 +1123,36 @@ ParseResult::consume_positional(std::string a) return false; } -inline -void -Options::parse_positional(std::string option) -{ +inline void Options::parse_positional(std::string option) { parse_positional(std::vector{std::move(option)}); } -inline -void -Options::parse_positional(std::vector options) -{ +inline void Options::parse_positional(std::vector options) { m_positional = std::move(options); m_next_positional = m_positional.begin(); m_positional_set.insert(m_positional.begin(), m_positional.end()); } -inline -void -Options::parse_positional(std::initializer_list options) -{ +inline void Options::parse_positional( + std::initializer_list options) { parse_positional(std::vector(std::move(options))); } -inline -ParseResult -Options::parse(int& argc, char**& argv) -{ +inline ParseResult Options::parse(int& argc, char**& argv) { ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv); return result; } -inline -void -ParseResult::parse(int& argc, char**& argv) -{ +inline void ParseResult::parse(int& argc, char**& argv) { int current = 1; int nextKeep = 1; bool consume_remaining = false; - while (current != argc) - { - if (strcmp(argv[current], "--") == 0) - { + while (current != argc) { + if (strcmp(argv[current], "--") == 0) { consume_remaining = true; ++current; break; @@ -1718,137 +1161,107 @@ ParseResult::parse(int& argc, char**& argv) std::match_results result; std::regex_match(argv[current], result, option_matcher); - if (result.empty()) - { - //not a flag + if (result.empty()) { + // not a flag // but if it starts with a `-`, then it's an error if (argv[current][0] == '-') { throw option_syntax_exception(argv[current]); } - //if true is returned here then it was consumed, otherwise it is - //ignored - if (consume_positional(argv[current])) - { - } - else - { + // if true is returned here then it was consumed, otherwise it is + // ignored + if (consume_positional(argv[current])) { + } else { argv[nextKeep] = argv[current]; ++nextKeep; } - //if we return from here then it was parsed successfully, so continue - } - else - { - //short or long option? - if (result[4].length() != 0) - { + // if we return from here then it was parsed successfully, so continue + } else { + // short or long option? + if (result[4].length() != 0) { const std::string& s = result[4]; - for (std::size_t i = 0; i != s.size(); ++i) - { + for (std::size_t i = 0; i != s.size(); ++i) { std::string name(1, s[i]); auto iter = m_options->find(name); - if (iter == m_options->end()) - { - if (m_allow_unrecognised) - { + if (iter == m_options->end()) { + if (m_allow_unrecognised) { continue; - } - else - { - //error + } else { + // error throw option_not_exists_exception(name); } } auto value = iter->second; - if (i + 1 == s.size()) - { - //it must be the last argument + if (i + 1 == s.size()) { + // it must be the last argument checked_parse_arg(argc, argv, current, value, name); - } - else if (value->value().has_implicit()) - { + } else if (value->value().has_implicit()) { parse_option(value, name, value->value().get_implicit_value()); - } - else - { - //error + } else { + // error throw option_requires_argument_exception(name); } } - } - else if (result[1].length() != 0) - { + } else if (result[1].length() != 0) { const std::string& name = result[1]; auto iter = m_options->find(name); - if (iter == m_options->end()) - { - if (m_allow_unrecognised) - { + if (iter == m_options->end()) { + if (m_allow_unrecognised) { // keep unrecognised options in argument list, skip to next argument argv[nextKeep] = argv[current]; ++nextKeep; ++current; continue; - } - else - { - //error + } else { + // error throw option_not_exists_exception(name); } } auto opt = iter->second; - //equals provided for long option? - if (result[2].length() != 0) - { - //parse the option given + // equals provided for long option? + if (result[2].length() != 0) { + // parse the option given parse_option(opt, name, result[3]); - } - else - { - //parse the next argument + } else { + // parse the next argument checked_parse_arg(argc, argv, current, opt, name); } } - } ++current; } - for (auto& opt : *m_options) - { + for (auto& opt : *m_options) { auto& detail = opt.second; auto& value = detail->value(); auto& store = m_results[detail]; - if(!store.count() && value.has_default()){ + if (!store.count() && value.has_default()) { parse_default(detail); } } - if (consume_remaining) - { - while (current < argc) - { + if (consume_remaining) { + while (current < argc) { if (!consume_positional(argv[current])) { break; } ++current; } - //adjust argv for any that couldn't be swallowed + // adjust argv for any that couldn't be swallowed while (current != argc) { argv[nextKeep] = argv[current]; ++nextKeep; @@ -1857,70 +1270,46 @@ ParseResult::parse(int& argc, char**& argv) } argc = nextKeep; - } -inline -void -Options::add_option -( - const std::string& group, - const std::string& s, - const std::string& l, - std::string desc, - std::shared_ptr value, - std::string arg_help -) -{ +inline void Options::add_option(const std::string& group, const std::string& s, + const std::string& l, std::string desc, + std::shared_ptr value, + std::string arg_help) { auto stringDesc = toLocalString(std::move(desc)); auto option = std::make_shared(s, l, stringDesc, value); - if (s.size() > 0) - { + if (s.size() > 0) { add_one_option(s, option); } - if (l.size() > 0) - { + if (l.size() > 0) { add_one_option(l, option); } - //add the help details + // add the help details auto& options = m_help[group]; - options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, - value->has_default(), value->get_default_value(), - value->has_implicit(), value->get_implicit_value(), - std::move(arg_help), - value->is_container(), - value->is_boolean()}); + options.options.emplace_back(HelpOptionDetails{ + s, l, stringDesc, value->has_default(), value->get_default_value(), + value->has_implicit(), value->get_implicit_value(), std::move(arg_help), + value->is_container(), value->is_boolean()}); } -inline -void -Options::add_one_option -( - const std::string& option, - std::shared_ptr details -) -{ +inline void Options::add_one_option(const std::string& option, + std::shared_ptr details) { auto in = m_options->emplace(option, details); - if (!in.second) - { + if (!in.second) { throw option_exists_error(option); } } -inline -String -Options::help_one_group(const std::string& g) const -{ +inline String Options::help_one_group(const std::string& g) const { typedef std::vector> OptionHelp; auto group = m_help.find(g); - if (group == m_help.end()) - { + if (group == m_help.end()) { return ""; } @@ -1930,17 +1319,14 @@ Options::help_one_group(const std::string& g) const String result; - if (!g.empty()) - { + if (!g.empty()) { result += toLocalString(" " + g + " options:\n"); } - for (const auto& o : group->second.options) - { + for (const auto& o : group->second.options) { if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end() && - !m_show_positional) - { + !m_show_positional) { continue; } @@ -1951,32 +1337,26 @@ Options::help_one_group(const std::string& g) const longest = std::min(longest, static_cast(OPTION_LONGEST)); - //widest allowed description + // widest allowed description auto allowed = size_t{76} - longest - OPTION_DESC_GAP; auto fiter = format.begin(); - for (const auto& o : group->second.options) - { + for (const auto& o : group->second.options) { if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end() && - !m_show_positional) - { + !m_show_positional) { continue; } auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); result += fiter->first; - if (stringLength(fiter->first) > longest) - { + if (stringLength(fiter->first) > longest) { result += '\n'; result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' ')); - } - else - { - result += toLocalString(std::string(longest + OPTION_DESC_GAP - - stringLength(fiter->first), - ' ')); + } else { + result += toLocalString(std::string( + longest + OPTION_DESC_GAP - stringLength(fiter->first), ' ')); } result += d; result += '\n'; @@ -1987,50 +1367,35 @@ Options::help_one_group(const std::string& g) const return result; } -inline -void -Options::generate_group_help -( - String& result, - const std::vector& print_groups -) const -{ - for (size_t i = 0; i != print_groups.size(); ++i) - { +inline void Options::generate_group_help( + String& result, const std::vector& print_groups) const { + for (size_t i = 0; i != print_groups.size(); ++i) { const String& group_help_text = help_one_group(print_groups[i]); - if (empty(group_help_text)) - { + if (empty(group_help_text)) { continue; } result += group_help_text; - if (i < print_groups.size() - 1) - { + if (i < print_groups.size() - 1) { result += '\n'; } } } -inline -void -Options::generate_all_groups_help(String& result) const -{ +inline void Options::generate_all_groups_help(String& result) const { std::vector all_groups; all_groups.reserve(m_help.size()); - for (auto& group : m_help) - { + for (auto& group : m_help) { all_groups.push_back(group.first); } generate_group_help(result, all_groups); } -inline -std::string -Options::help(const std::vector& help_groups) const -{ - String result = m_help_string + "\nUsage:\n " + - toLocalString(m_program) + " " + toLocalString(m_custom_help); +inline std::string Options::help( + const std::vector& help_groups) const { + String result = m_help_string + "\nUsage:\n " + toLocalString(m_program) + + " " + toLocalString(m_custom_help); if (m_positional.size() > 0 && m_positional_help.size() > 0) { result += " " + toLocalString(m_positional_help); @@ -2038,44 +1403,32 @@ Options::help(const std::vector& help_groups) const result += "\n\n"; - if (help_groups.size() == 0) - { + if (help_groups.size() == 0) { generate_all_groups_help(result); - } - else - { + } else { generate_group_help(result, help_groups); } return toUTF8String(result); } -inline -const std::vector -Options::groups() const -{ +inline const std::vector Options::groups() const { std::vector g; std::transform( - m_help.begin(), - m_help.end(), - std::back_inserter(g), - [] (const std::map::value_type& pair) - { - return pair.first; - } - ); + m_help.begin(), m_help.end(), std::back_inserter(g), + [](const std::map::value_type& pair) { + return pair.first; + }); return g; } -inline -const HelpGroupDetails& -Options::group_help(const std::string& group) const -{ +inline const HelpGroupDetails& Options::group_help( + const std::string& group) const { return m_help.at(group); } -} +} // namespace cxxopts -#endif //CXXOPTS_HPP_INCLUDED +#endif // CXXOPTS_HPP_INCLUDED diff --git a/check/CMakeLists.txt b/check/CMakeLists.txt index 366a4c5924..f258f4c514 100644 --- a/check/CMakeLists.txt +++ b/check/CMakeLists.txt @@ -57,6 +57,7 @@ if (NOT FAST_BUILD OR ALL_TESTS) TestHighsSparseMatrix.cpp TestHSet.cpp TestICrash.cpp + TestIis.cpp TestIpm.cpp TestIpx.cpp TestLogging.cpp diff --git a/check/TestBasisSolves.cpp b/check/TestBasisSolves.cpp index b9418c814d..aeb878b1da 100644 --- a/check/TestBasisSolves.cpp +++ b/check/TestBasisSolves.cpp @@ -564,3 +564,50 @@ TEST_CASE("Basis-solves", "[highs_basis_solves]") { highs.run(); REQUIRE(highs.getInfo().simplex_iteration_count == 0); } + +TEST_CASE("Kappa", "[highs_basis_solves]") { + // chip optimal basis matrix is B=[1, 2; 1, 4] with + // + // ||B||_1=6; ||B^{-1}||_1=5/2 so kappa_1 = 15 + // + // ||B||_2=4.56; ||B^{-1}||_inf=2.355 so kappa_inf = 10.9 + // + // ||B||_inf=6; ||B^{-1}||_inf=5/2 so kappa_inf = 15 + // + double chip_kappa = 15; + + std::string model; + model = "chip"; + // model = "avgas"; + // model = "adlittle"; + std::string filename = + std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps"; + + Highs highs; + highs.setOptionValue("output_flag", dev_run); + + // Read the LP given by filename + highs.readModel(filename); + + double kappa; + REQUIRE(highs.getKappa(kappa) == HighsStatus::kError); + + highs.run(); + + REQUIRE(highs.getKappa(kappa) == HighsStatus::kOk); + if (dev_run) + printf("highs.getKappa for %s yields %g\n", model.c_str(), kappa); + + if (model == "chip") REQUIRE(std::fabs(kappa - chip_kappa) < 1e-4); + + highs.clearModel(); + + const bool test_mip = true; + if (test_mip) { + model = "flugpl"; + filename = std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps"; + highs.readModel(filename); + highs.run(); + REQUIRE(highs.getKappa(kappa) == HighsStatus::kError); + } +} diff --git a/check/TestCAPI.c b/check/TestCAPI.c index dff94ec653..e5848f67a1 100644 --- a/check/TestCAPI.c +++ b/check/TestCAPI.c @@ -1,8 +1,8 @@ -#include "interfaces/highs_c_api.h" - -#include "HCheckConfig.h" #include #include + +#include "HCheckConfig.h" +#include "interfaces/highs_c_api.h" // Force asserts to be checked always. #undef NDEBUG #include @@ -12,19 +12,24 @@ const HighsInt dev_run = 0; const double double_equal_tolerance = 1e-5; -void checkGetCallbackDataOutPointer(const HighsCallbackDataOut* data_out, const char* name, HighsInt valid) { +void checkGetCallbackDataOutPointer(const HighsCallbackDataOut* data_out, + const char* name, HighsInt valid) { const void* name_p = Highs_getCallbackDataOutItem(data_out, name); if (valid) { - if (!name_p) printf("checkGetCallbackDataOutItem fail for %s (valid = %d)\n", name, (int)valid); + if (!name_p) + printf("checkGetCallbackDataOutItem fail for %s (valid = %d)\n", name, + (int)valid); assert(name_p); } else { - if (name_p) printf("checkGetCallbackDataOutItem fail for %s (valid = %d)\n", - name, (int)valid); + if (name_p) + printf("checkGetCallbackDataOutItem fail for %s (valid = %d)\n", name, + (int)valid); assert(!name_p); } } - -void checkGetCallbackDataOutHighsInt(const HighsCallbackDataOut* data_out, const char* name, HighsInt value) { + +void checkGetCallbackDataOutHighsInt(const HighsCallbackDataOut* data_out, + const char* name, HighsInt value) { const void* name_p = Highs_getCallbackDataOutItem(data_out, name); if (!name_p) { printf("checkGetCallbackDataOutItem fail for %s\n", name); @@ -32,13 +37,17 @@ void checkGetCallbackDataOutHighsInt(const HighsCallbackDataOut* data_out, const } else { HighsInt check_value = *(HighsInt*)(name_p); HighsInt value_ok = check_value == value; - if (!value_ok) printf("checkGetCallbackDataOutItem fail for %s (%d = check_value != value = %d)\n", - name, (int)check_value, (int)value); + if (!value_ok) + printf( + "checkGetCallbackDataOutItem fail for %s (%d = check_value != value " + "= %d)\n", + name, (int)check_value, (int)value); assert(value_ok); } } - -void checkGetCallbackDataOutInt(const HighsCallbackDataOut* data_out, const char* name, int value) { + +void checkGetCallbackDataOutInt(const HighsCallbackDataOut* data_out, + const char* name, int value) { const void* name_p = Highs_getCallbackDataOutItem(data_out, name); if (!name_p) { printf("checkGetCallbackDataOutInt fail for %s\n", name); @@ -46,13 +55,17 @@ void checkGetCallbackDataOutInt(const HighsCallbackDataOut* data_out, const char } else { int check_value = *(int*)(name_p); int value_ok = check_value == value; - if (!value_ok) printf("checkGetCallbackDataOutInt fail for %s (%d = check_value != value = %d)\n", - name, check_value, value); + if (!value_ok) + printf( + "checkGetCallbackDataOutInt fail for %s (%d = check_value != value = " + "%d)\n", + name, check_value, value); assert(value_ok); } } - -void checkGetCallbackDataOutInt64(const HighsCallbackDataOut* data_out, const char* name, int64_t value) { + +void checkGetCallbackDataOutInt64(const HighsCallbackDataOut* data_out, + const char* name, int64_t value) { const void* name_p = Highs_getCallbackDataOutItem(data_out, name); if (!name_p) { printf("checkGetCallbackDataOutInt64 fail for %s\n", name); @@ -60,13 +73,17 @@ void checkGetCallbackDataOutInt64(const HighsCallbackDataOut* data_out, const ch } else { int64_t check_value = *(int*)(name_p); int value_ok = check_value == value; - if (!value_ok) printf("checkGetCallbackDataOutInt64 fail for %s (%d = check_value != value = %d)\n", - name, (int)check_value, (int)value); + if (!value_ok) + printf( + "checkGetCallbackDataOutInt64 fail for %s (%d = check_value != value " + "= %d)\n", + name, (int)check_value, (int)value); assert(value_ok); } } - -void checkGetCallbackDataOutDouble(const HighsCallbackDataOut* data_out, const char* name, double value) { + +void checkGetCallbackDataOutDouble(const HighsCallbackDataOut* data_out, + const char* name, double value) { const void* name_p = Highs_getCallbackDataOutItem(data_out, name); if (!name_p) { printf("checkGetCallbackDataOutDouble fail for %s\n", name); @@ -74,64 +91,67 @@ void checkGetCallbackDataOutDouble(const HighsCallbackDataOut* data_out, const c } else { double check_value = *(double*)(name_p); double value_ok = check_value == value; - if (!value_ok) printf("checkGetCallbackDataOutDouble fail for %s (%g = check_value != value = %g)\n", - name, check_value, value); + if (!value_ok) + printf( + "checkGetCallbackDataOutDouble fail for %s (%g = check_value != " + "value = %g)\n", + name, check_value, value); assert(value_ok); } } - + static void userCallback(const int callback_type, const char* message, - const HighsCallbackDataOut* data_out, - HighsCallbackDataIn* data_in, - void* user_callback_data) { + const HighsCallbackDataOut* data_out, + HighsCallbackDataIn* data_in, + void* user_callback_data) { // Extract the double value pointed to from void* user_callback_data - const double local_callback_data = user_callback_data == NULL ? -1 : *(double*)user_callback_data; + const double local_callback_data = + user_callback_data == NULL ? -1 : *(double*)user_callback_data; if (callback_type == kHighsCallbackLogging) { - if (dev_run) printf("userCallback(%11.4g): %s\n", local_callback_data, message); + if (dev_run) + printf("userCallback(%11.4g): %s\n", local_callback_data, message); } else if (callback_type == kHighsCallbackMipImprovingSolution) { // Test the accessor function for data_out // // Check that passing an valid name returns a non-null pointer, // and that the corresponding value is the same as obtained using // the struct - const void* objective_function_value_p = - Highs_getCallbackDataOutItem(data_out, kHighsCallbackDataOutObjectiveFunctionValueName); + const void* objective_function_value_p = Highs_getCallbackDataOutItem( + data_out, kHighsCallbackDataOutObjectiveFunctionValueName); assert(objective_function_value_p); double objective_function_value = *(double*)(objective_function_value_p); assert(objective_function_value == data_out->objective_function_value); - if (dev_run) printf("userCallback(%11.4g): improving solution with objective = %g\n", - local_callback_data, objective_function_value); + if (dev_run) + printf("userCallback(%11.4g): improving solution with objective = %g\n", + local_callback_data, objective_function_value); // Now test all more simply - checkGetCallbackDataOutInt(data_out, - kHighsCallbackDataOutLogTypeName, -1); - checkGetCallbackDataOutDouble(data_out, - kHighsCallbackDataOutRunningTimeName, - data_out->running_time); - checkGetCallbackDataOutHighsInt(data_out, - kHighsCallbackDataOutSimplexIterationCountName, - data_out->simplex_iteration_count); + checkGetCallbackDataOutInt(data_out, kHighsCallbackDataOutLogTypeName, -1); + checkGetCallbackDataOutDouble( + data_out, kHighsCallbackDataOutRunningTimeName, data_out->running_time); + checkGetCallbackDataOutHighsInt( + data_out, kHighsCallbackDataOutSimplexIterationCountName, + data_out->simplex_iteration_count); checkGetCallbackDataOutHighsInt(data_out, - kHighsCallbackDataOutIpmIterationCountName, - data_out->ipm_iteration_count); + kHighsCallbackDataOutIpmIterationCountName, + data_out->ipm_iteration_count); checkGetCallbackDataOutHighsInt(data_out, - kHighsCallbackDataOutPdlpIterationCountName, - data_out->pdlp_iteration_count); - checkGetCallbackDataOutDouble(data_out, - kHighsCallbackDataOutObjectiveFunctionValueName, - data_out->objective_function_value); + kHighsCallbackDataOutPdlpIterationCountName, + data_out->pdlp_iteration_count); + checkGetCallbackDataOutDouble( + data_out, kHighsCallbackDataOutObjectiveFunctionValueName, + data_out->objective_function_value); checkGetCallbackDataOutInt64(data_out, - kHighsCallbackDataOutMipNodeCountName, - data_out->mip_node_count); - checkGetCallbackDataOutDouble(data_out, - kHighsCallbackDataOutMipPrimalBoundName, - data_out->mip_primal_bound); + kHighsCallbackDataOutMipNodeCountName, + data_out->mip_node_count); checkGetCallbackDataOutDouble(data_out, - kHighsCallbackDataOutMipDualBoundName, - data_out->mip_dual_bound); + kHighsCallbackDataOutMipPrimalBoundName, + data_out->mip_primal_bound); checkGetCallbackDataOutDouble(data_out, - kHighsCallbackDataOutMipGapName, - data_out->mip_gap); + kHighsCallbackDataOutMipDualBoundName, + data_out->mip_dual_bound); + checkGetCallbackDataOutDouble(data_out, kHighsCallbackDataOutMipGapName, + data_out->mip_gap); // Cutpool data structure is not assigned, so num_col, num_cut and // num_nz are unassigned // checkGetCallbackDataOutHighsInt(data_out, @@ -147,68 +167,83 @@ static void userCallback(const int callback_type, const char* message, // Check that passing the name of an assigned vector returns // non-NULL, and that the corresponding value is the same as // obtained using the struct - const void* mip_solution_void_p = - Highs_getCallbackDataOutItem(data_out, - kHighsCallbackDataOutMipSolutionName); + const void* mip_solution_void_p = Highs_getCallbackDataOutItem( + data_out, kHighsCallbackDataOutMipSolutionName); assert(mip_solution_void_p); double mip_solution0 = *(double*)(mip_solution_void_p); assert(mip_solution0 == *(data_out->mip_solution)); - if (dev_run) printf("userCallback(%11.4g): improving solution with value[0] = %g\n", - local_callback_data, mip_solution0); + if (dev_run) + printf("userCallback(%11.4g): improving solution with value[0] = %g\n", + local_callback_data, mip_solution0); // Cutpool data structure is not assigned, so cannot check that // passing names of the unassigned vectors returns NULL - // assert(!Highs_getCallbackDataOutItem(data_out, kHighsCallbackDataOutCutpoolStartName)); - // assert(!Highs_getCallbackDataOutItem(data_out, kHighsCallbackDataOutCutpoolIndexName)); - // assert(!Highs_getCallbackDataOutItem(data_out, kHighsCallbackDataOutCutpoolValueName)); - // assert(!Highs_getCallbackDataOutItem(data_out, kHighsCallbackDataOutCutpoolLowerName)); - // assert(!Highs_getCallbackDataOutItem(data_out, kHighsCallbackDataOutCutpoolUpperName)); + // assert(!Highs_getCallbackDataOutItem(data_out, + // kHighsCallbackDataOutCutpoolStartName)); + // assert(!Highs_getCallbackDataOutItem(data_out, + // kHighsCallbackDataOutCutpoolIndexName)); + // assert(!Highs_getCallbackDataOutItem(data_out, + // kHighsCallbackDataOutCutpoolValueName)); + // assert(!Highs_getCallbackDataOutItem(data_out, + // kHighsCallbackDataOutCutpoolLowerName)); + // assert(!Highs_getCallbackDataOutItem(data_out, + // kHighsCallbackDataOutCutpoolUpperName)); } else if (callback_type == kHighsCallbackMipLogging) { - if (dev_run) printf("userCallback(%11.4g): MIP logging\n", local_callback_data); + if (dev_run) + printf("userCallback(%11.4g): MIP logging\n", local_callback_data); data_in->user_interrupt = 1; } else if (callback_type == kHighsCallbackMipInterrupt) { - if (dev_run) printf("userCallback(%11.4g): MIP interrupt\n", local_callback_data); + if (dev_run) + printf("userCallback(%11.4g): MIP interrupt\n", local_callback_data); data_in->user_interrupt = 1; } } -HighsInt highsIntArraysEqual(const HighsInt dim, const HighsInt* array0, const HighsInt* array1) { - for (HighsInt ix = 0; ix < dim; ix++) if (array0[ix] != array1[ix]) return 0; +HighsInt highsIntArraysEqual(const HighsInt dim, const HighsInt* array0, + const HighsInt* array1) { + for (HighsInt ix = 0; ix < dim; ix++) + if (array0[ix] != array1[ix]) return 0; return 1; } -HighsInt doubleArraysEqual(const double dim, const double* array0, const double* array1) { - for (HighsInt ix = 0; ix < dim; ix++) if (array0[ix] != array1[ix]) return 0; +HighsInt doubleArraysEqual(const double dim, const double* array0, + const double* array1) { + for (HighsInt ix = 0; ix < dim; ix++) + if (array0[ix] != array1[ix]) return 0; return 1; } -void assertDoubleValuesEqual(const char* name, const double is, const double should_be) { - const double dl = fabs(is-should_be); +void assertDoubleValuesEqual(const char* name, const double is, + const double should_be) { + const double dl = fabs(is - should_be); if (dl > double_equal_tolerance) { - printf("Value %s = %g differs from %g by %g but should be equal\n", name, is, should_be, dl); - assert(1==0); + printf("Value %s = %g differs from %g by %g but should be equal\n", name, + is, should_be, dl); + assert(1 == 0); } } -void assertIntValuesEqual(const char* name, const HighsInt is, const HighsInt should_be) { +void assertIntValuesEqual(const char* name, const HighsInt is, + const HighsInt should_be) { if (is != should_be) { - printf("Value %s = %"HIGHSINT_FORMAT" should be %"HIGHSINT_FORMAT"\n", name, is, should_be); - assert(1==0); + printf("Value %s = %" HIGHSINT_FORMAT " should be %" HIGHSINT_FORMAT "\n", + name, is, should_be); + assert(1 == 0); } } void assertLogical(const char* name, const HighsInt is) { if (is == 0) { - printf("Value %s = %"HIGHSINT_FORMAT" should not be 0\n", name, is); - assert(1==0); + printf("Value %s = %" HIGHSINT_FORMAT " should not be 0\n", name, is); + assert(1 == 0); } } void version_api() { if (dev_run) { printf("HiGHS version %s\n", Highs_version()); - printf("HiGHS version major %"HIGHSINT_FORMAT"\n", Highs_versionMajor()); - printf("HiGHS version minor %"HIGHSINT_FORMAT"\n", Highs_versionMinor()); - printf("HiGHS version patch %"HIGHSINT_FORMAT"\n", Highs_versionPatch()); + printf("HiGHS version major %" HIGHSINT_FORMAT "\n", Highs_versionMajor()); + printf("HiGHS version minor %" HIGHSINT_FORMAT "\n", Highs_versionMinor()); + printf("HiGHS version patch %" HIGHSINT_FORMAT "\n", Highs_versionPatch()); printf("HiGHS githash: %s\n", Highs_githash()); // Compilation date is deprecated. // printf("HiGHS compilation date %s\n", Highs_compilationDate()); @@ -299,31 +334,34 @@ void minimal_api_lp() { HighsInt model_status; - HighsInt return_status = Highs_lpCall(num_col, num_row, num_nz, a_format, - sense, offset, col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, - col_value, col_dual, row_value, row_dual, - col_basis_status, row_basis_status, - &model_status); + HighsInt return_status = + Highs_lpCall(num_col, num_row, num_nz, a_format, sense, offset, col_cost, + col_lower, col_upper, row_lower, row_upper, a_start, a_index, + a_value, col_value, col_dual, row_value, row_dual, + col_basis_status, row_basis_status, &model_status); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); if (dev_run) { - printf("Run status = %"HIGHSINT_FORMAT"; Model status = %"HIGHSINT_FORMAT"\n", return_status, model_status); - + printf("Run status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT + "\n", + return_status, model_status); + HighsInt i; if (model_status == kHighsModelStatusOptimal) { double objective_value = 0; // Report the column primal and dual values, and basis status for (i = 0; i < num_col; i++) { - printf("Col%"HIGHSINT_FORMAT" = %lf; dual = %lf; status = %"HIGHSINT_FORMAT"; \n", - i, col_value[i], col_dual[i], col_basis_status[i]); - objective_value += col_value[i]*col_cost[i]; + printf("Col%" HIGHSINT_FORMAT + " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "; \n", + i, col_value[i], col_dual[i], col_basis_status[i]); + objective_value += col_value[i] * col_cost[i]; } // Report the row primal and dual values, and basis status for (i = 0; i < num_row; i++) { - printf("Row%"HIGHSINT_FORMAT" = %lf; dual = %lf; status = %"HIGHSINT_FORMAT"; \n", - i, row_value[i], row_dual[i], row_basis_status[i]); + printf("Row%" HIGHSINT_FORMAT + " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "; \n", + i, row_value[i], row_dual[i], row_basis_status[i]); } printf("Optimal objective value = %g\n", objective_value); } @@ -358,14 +396,15 @@ void minimal_api_mip() { double col_upper[3] = {1.0e30, 1.0e30, 1.0}; // Define the row lower bounds and upper bounds double row_lower[2] = {-1.0e30, 12.0}; - double row_upper[2] = { 7.0, 12.0}; + double row_upper[2] = {7.0, 12.0}; // Define the constraint matrix column-wise HighsInt a_start[3] = {0, 2, 4}; HighsInt a_index[6] = {0, 1, 0, 1, 0, 1}; double a_value[6] = {1.0, 4.0, 1.0, 2.0, 1.0, 1.0}; // Give an illegal value to an entry in integrality - HighsInt integrality[3] = {kHighsVarTypeContinuous, kHighsVarTypeContinuous, -1}; + HighsInt integrality[3] = {kHighsVarTypeContinuous, kHighsVarTypeContinuous, + -1}; double* col_value = (double*)malloc(sizeof(double) * num_col); double* row_value = (double*)malloc(sizeof(double) * num_row); @@ -373,42 +412,40 @@ void minimal_api_mip() { HighsInt model_status; HighsInt return_status; - return_status = Highs_mipCall(num_col, num_row, num_nz, a_format, - sense, offset, col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, - integrality, - col_value, row_value, - &model_status); + return_status = Highs_mipCall( + num_col, num_row, num_nz, a_format, sense, offset, col_cost, col_lower, + col_upper, row_lower, row_upper, a_start, a_index, a_value, integrality, + col_value, row_value, &model_status); // Should return error, with model status not set - assert( return_status == kHighsStatusError ); - assert( model_status == kHighsModelStatusNotset ); + assert(return_status == kHighsStatusError); + assert(model_status == kHighsModelStatusNotset); // Correct integrality - integrality[num_col-1] = kHighsVarTypeInteger; - - return_status = Highs_mipCall(num_col, num_row, num_nz, a_format, - sense, offset, col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, - integrality, - col_value, row_value, - &model_status); + integrality[num_col - 1] = kHighsVarTypeInteger; + + return_status = Highs_mipCall( + num_col, num_row, num_nz, a_format, sense, offset, col_cost, col_lower, + col_upper, row_lower, row_upper, a_start, a_index, a_value, integrality, + col_value, row_value, &model_status); // Should return OK - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); if (dev_run) { - printf("Run status = %"HIGHSINT_FORMAT"; Model status = %"HIGHSINT_FORMAT"\n", return_status, model_status); - + printf("Run status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT + "\n", + return_status, model_status); + HighsInt i; if (model_status == kHighsModelStatusOptimal) { double objective_value = 0; // Report the column primal values for (i = 0; i < num_col; i++) { - printf("Col%"HIGHSINT_FORMAT" = %lf; \n", i, col_value[i]); - objective_value += col_value[i]*col_cost[i]; + printf("Col%" HIGHSINT_FORMAT " = %lf; \n", i, col_value[i]); + objective_value += col_value[i] * col_cost[i]; } // Report the row primal values for (i = 0; i < num_row; i++) { - printf("Row%"HIGHSINT_FORMAT" = %lf; \n", i, row_value[i]); + printf("Row%" HIGHSINT_FORMAT " = %lf; \n", i, row_value[i]); } printf("Optimal objective value = %g\n", objective_value); } @@ -416,7 +453,6 @@ void minimal_api_mip() { free(col_value); free(row_value); - } void minimal_api_qp() { @@ -445,20 +481,23 @@ void minimal_api_qp() { HighsInt q_start[3] = {0, 2, 3}; HighsInt q_index[4] = {0, 2, 1, 2}; double q_value[4] = {2.0, -1.0, 0.2, 2.0}; - + double* col_value = (double*)malloc(sizeof(double) * num_col); HighsInt model_status; - HighsInt return_status = Highs_qpCall(num_col, num_row, num_nz, q_num_nz, a_format, q_format, sense, offset, - col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, q_start, q_index, q_value, - col_value, NULL, NULL, NULL, NULL, NULL, &model_status); - assert( return_status == kHighsStatusOk ); - assertIntValuesEqual("Model status for QP qph", model_status, kHighsModelStatusOptimal); + HighsInt return_status = Highs_qpCall( + num_col, num_row, num_nz, q_num_nz, a_format, q_format, sense, offset, + col_cost, col_lower, col_upper, row_lower, row_upper, a_start, a_index, + a_value, q_start, q_index, q_value, col_value, NULL, NULL, NULL, NULL, + NULL, &model_status); + assert(return_status == kHighsStatusOk); + assertIntValuesEqual("Model status for QP qph", model_status, + kHighsModelStatusOptimal); double required_x[3] = {0.5, 5.0, 1.5}; if (dev_run) { for (HighsInt iCol = 0; iCol < num_col; iCol++) { printf("x%d1 = %g\n", (int)iCol, col_value[iCol]); - assertDoubleValuesEqual("Solution value for QP qph", col_value[iCol], required_x[iCol]); + assertDoubleValuesEqual("Solution value for QP qph", col_value[iCol], + required_x[iCol]); } } free(col_value); @@ -478,19 +517,17 @@ void minimal_api_illegal_lp() { double row_lower[1] = {-inf}; double row_upper[1] = {2}; HighsInt a_start[1] = {0}; - HighsInt a_index[2] = {0, -1}; // Illegal index + HighsInt a_index[2] = {0, -1}; // Illegal index double a_value[2] = {1.0, 1.0}; HighsInt model_status; - HighsInt return_status = Highs_lpCall(num_col, num_row, num_nz, a_format, - sense, offset, col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, - NULL, NULL, NULL, NULL, - NULL, NULL, - &model_status); + HighsInt return_status = + Highs_lpCall(num_col, num_row, num_nz, a_format, sense, offset, col_cost, + col_lower, col_upper, row_lower, row_upper, a_start, a_index, + a_value, NULL, NULL, NULL, NULL, NULL, NULL, &model_status); // Should return error, with model status not set - assert( return_status == kHighsStatusError ); - assert( model_status == kHighsModelStatusNotset ); + assert(return_status == kHighsStatusError); + assert(model_status == kHighsModelStatusNotset); } void full_api() { @@ -513,13 +550,13 @@ void full_api() { HighsInt a_index[4] = {0, 1, 0, 1}; double a_value[4] = {1.0, 2.0, 1.0, 3.0}; - assert( Highs_addCols(highs, 2, cc, cl, cu, 0, NULL, NULL, NULL) == 0); - assert( Highs_addRows(highs, 2, rl, ru, 4, a_start, a_index, a_value) == 0); + assert(Highs_addCols(highs, 2, cc, cl, cu, 0, NULL, NULL, NULL) == 0); + assert(Highs_addRows(highs, 2, rl, ru, 4, a_start, a_index, a_value) == 0); - assert( Highs_getNumCols(highs) == num_col); - assert( Highs_getNumRows(highs) == num_row); - assert( Highs_getNumNz(highs) == num_nz); - assert( Highs_getHessianNumNz(highs) == 0); + assert(Highs_getNumCols(highs) == num_col); + assert(Highs_getNumRows(highs) == num_row); + assert(Highs_getNumNz(highs) == num_nz); + assert(Highs_getHessianNumNz(highs) == 0); HighsInt ck_num_col; HighsInt ck_num_row; @@ -537,50 +574,48 @@ void full_api() { HighsInt ck_a_index[4]; double ck_a_value[4]; HighsInt return_status; - return_status = Highs_getModel(highs, a_format, 0, - &ck_num_col, &ck_num_row, &ck_num_nz, NULL, - &ck_sense, &ck_offset, - ck_cc, ck_cl, ck_cu, ck_rl, ck_ru, - ck_a_start, ck_a_index, ck_a_value, - NULL, NULL, NULL, NULL); - assert( return_status == kHighsStatusOk ); - - assert( ck_num_col == num_col ); - assert( ck_num_row == num_row ); - assert( ck_num_nz == num_nz ); - assert( ck_sense == sense ); - assert( ck_offset == offset ); - assert( doubleArraysEqual(num_col, ck_cc, cc) ); - assert( doubleArraysEqual(num_col, ck_cl, cl) ); - assert( doubleArraysEqual(num_col, ck_cu, cu) ); - assert( doubleArraysEqual(num_row, ck_rl, rl) ); - assert( doubleArraysEqual(num_row, ck_ru, ru) ); - assert( highsIntArraysEqual(num_col, ck_a_start, a_start) ); - assert( highsIntArraysEqual(num_nz, ck_a_index, a_index) ); - assert( doubleArraysEqual(num_nz, ck_a_value, a_value) ); + return_status = Highs_getModel( + highs, a_format, 0, &ck_num_col, &ck_num_row, &ck_num_nz, NULL, &ck_sense, + &ck_offset, ck_cc, ck_cl, ck_cu, ck_rl, ck_ru, ck_a_start, ck_a_index, + ck_a_value, NULL, NULL, NULL, NULL); + assert(return_status == kHighsStatusOk); + + assert(ck_num_col == num_col); + assert(ck_num_row == num_row); + assert(ck_num_nz == num_nz); + assert(ck_sense == sense); + assert(ck_offset == offset); + assert(doubleArraysEqual(num_col, ck_cc, cc)); + assert(doubleArraysEqual(num_col, ck_cl, cl)); + assert(doubleArraysEqual(num_col, ck_cu, cu)); + assert(doubleArraysEqual(num_row, ck_rl, rl)); + assert(doubleArraysEqual(num_row, ck_ru, ru)); + assert(highsIntArraysEqual(num_col, ck_a_start, a_start)); + assert(highsIntArraysEqual(num_nz, ck_a_index, a_index)); + assert(doubleArraysEqual(num_nz, ck_a_value, a_value)); return_status = Highs_run(highs); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); char* col_prefix = "Col"; char* row_prefix = "Row"; // Check index out of bounds return_status = Highs_passColName(highs, -1, col_prefix); - assert( return_status == kHighsStatusError ); + assert(return_status == kHighsStatusError); return_status = Highs_passColName(highs, num_col, col_prefix); - assert( return_status == kHighsStatusError ); + assert(return_status == kHighsStatusError); return_status = Highs_passRowName(highs, -1, row_prefix); - assert( return_status == kHighsStatusError ); + assert(return_status == kHighsStatusError); return_status = Highs_passRowName(highs, num_row, row_prefix); - assert( return_status == kHighsStatusError ); + assert(return_status == kHighsStatusError); // Define all column names to be the same for (HighsInt iCol = 0; iCol < num_col; iCol++) { return_status = Highs_passColName(highs, iCol, col_prefix); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); } return_status = Highs_writeModel(highs, ""); - assert( return_status == kHighsStatusError ); + assert(return_status == kHighsStatusError); // Define all column names to be different for (HighsInt iCol = 0; iCol < num_col; iCol++) { @@ -590,32 +625,32 @@ void full_api() { sprintf(name, "%s%" HIGHSINT_FORMAT "", col_prefix, iCol); const char* name_p = name; return_status = Highs_passColName(highs, iCol, name_p); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); } return_status = Highs_writeModel(highs, ""); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); // Check that the columns can be found by name HighsInt ck_iCol; for (HighsInt iCol = 0; iCol < num_col; iCol++) { char name[5]; return_status = Highs_getColName(highs, iCol, name); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); return_status = Highs_getColByName(highs, name, &ck_iCol); - assert( return_status == kHighsStatusOk ); - assert( ck_iCol == iCol ); + assert(return_status == kHighsStatusOk); + assert(ck_iCol == iCol); } return_status = Highs_getColByName(highs, "FRED", &ck_iCol); - assert( return_status == kHighsStatusError ); - + assert(return_status == kHighsStatusError); + // Define all row names to be the same for (HighsInt iRow = 0; iRow < num_row; iRow++) { return_status = Highs_passRowName(highs, iRow, row_prefix); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); } return_status = Highs_writeModel(highs, ""); - assert( return_status == kHighsStatusError ); - + assert(return_status == kHighsStatusError); + // Define all row names to be different for (HighsInt iRow = 0; iRow < num_row; iRow++) { const char suffix = iRow + '0'; @@ -624,38 +659,40 @@ void full_api() { sprintf(name, "%s%" HIGHSINT_FORMAT "", row_prefix, iRow); const char* name_p = name; return_status = Highs_passRowName(highs, iRow, name_p); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); } return_status = Highs_writeModel(highs, ""); - assert( return_status == kHighsStatusOk ); - + assert(return_status == kHighsStatusOk); + // Check that the rows can be found by name HighsInt ck_iRow; for (HighsInt iRow = 0; iRow < num_row; iRow++) { char name[5]; return_status = Highs_getRowName(highs, iRow, name); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); return_status = Highs_getRowByName(highs, name, &ck_iRow); - assert( return_status == kHighsStatusOk ); - assert( ck_iRow == iRow ); + assert(return_status == kHighsStatusOk); + assert(ck_iRow == iRow); } return_status = Highs_getRowByName(highs, "FRED", &ck_iRow); - assert( return_status == kHighsStatusError ); - + assert(return_status == kHighsStatusError); + for (HighsInt iCol = 0; iCol < num_col; iCol++) { char name[5]; char* name_p = name; return_status = Highs_getColName(highs, iCol, name_p); - assert( return_status == kHighsStatusOk ); - if (dev_run) printf("Column %" HIGHSINT_FORMAT " has name %s\n", iCol, name_p); + assert(return_status == kHighsStatusOk); + if (dev_run) + printf("Column %" HIGHSINT_FORMAT " has name %s\n", iCol, name_p); } - + for (HighsInt iRow = 0; iRow < num_row; iRow++) { char name[5]; char* name_p = name; return_status = Highs_getRowName(highs, iRow, name_p); - assert( return_status == kHighsStatusOk ); - if (dev_run) printf("Row %" HIGHSINT_FORMAT " has name %s\n", iRow, name_p); + assert(return_status == kHighsStatusOk); + if (dev_run) + printf("Row %" HIGHSINT_FORMAT " has name %s\n", iRow, name_p); } Highs_destroy(highs); @@ -670,114 +707,122 @@ void full_api_options() { const double kHighsInf = Highs_getInfinity(highs); HighsInt simplex_scale_strategy; HighsInt return_status; - return_status = Highs_getIntOptionValue(highs, "simplex_scale_strategy", &simplex_scale_strategy); - assert( return_status == kHighsStatusOk ); + return_status = Highs_getIntOptionValue(highs, "simplex_scale_strategy", + &simplex_scale_strategy); + assert(return_status == kHighsStatusOk); if (dev_run) - printf("simplex_scale_strategy = %"HIGHSINT_FORMAT": setting it to 3\n", simplex_scale_strategy); + printf("simplex_scale_strategy = %" HIGHSINT_FORMAT ": setting it to 3\n", + simplex_scale_strategy); simplex_scale_strategy = 3; - return_status = Highs_setIntOptionValue(highs, "simplex_scale_strategy", simplex_scale_strategy); + return_status = Highs_setIntOptionValue(highs, "simplex_scale_strategy", + simplex_scale_strategy); const HighsInt presolve_index = 0; char* name = NULL; return_status = Highs_getOptionName(highs, presolve_index, &name); - if (dev_run) printf("option %"HIGHSINT_FORMAT" has name %s\n", presolve_index, name); + if (dev_run) + printf("option %" HIGHSINT_FORMAT " has name %s\n", presolve_index, name); const char* presolve = "presolve"; - assert( *name == *presolve ); + assert(*name == *presolve); free(name); HighsInt check_simplex_scale_strategy; HighsInt min_simplex_scale_strategy; HighsInt max_simplex_scale_strategy; HighsInt default_simplex_scale_strategy; - return_status = Highs_getIntOptionValues(highs, "scale_strategy", NULL, NULL, NULL, NULL); - assert( return_status == kHighsStatusError ); - return_status = Highs_getDoubleOptionValues(highs, "simplex_scale_strategy", NULL, NULL, NULL, NULL); - assert( return_status == kHighsStatusError ); - return_status = Highs_getIntOptionValues(highs, "simplex_scale_strategy", - &check_simplex_scale_strategy, - &min_simplex_scale_strategy, - &max_simplex_scale_strategy, - &default_simplex_scale_strategy); - assert( return_status == kHighsStatusOk ); - assert( check_simplex_scale_strategy == simplex_scale_strategy ); - assert( min_simplex_scale_strategy == 0 ); - assert( max_simplex_scale_strategy == 5 ); - assert( default_simplex_scale_strategy == 1 ); - + return_status = + Highs_getIntOptionValues(highs, "scale_strategy", NULL, NULL, NULL, NULL); + assert(return_status == kHighsStatusError); + return_status = Highs_getDoubleOptionValues(highs, "simplex_scale_strategy", + NULL, NULL, NULL, NULL); + assert(return_status == kHighsStatusError); + return_status = Highs_getIntOptionValues( + highs, "simplex_scale_strategy", &check_simplex_scale_strategy, + &min_simplex_scale_strategy, &max_simplex_scale_strategy, + &default_simplex_scale_strategy); + assert(return_status == kHighsStatusOk); + assert(check_simplex_scale_strategy == simplex_scale_strategy); + assert(min_simplex_scale_strategy == 0); + assert(max_simplex_scale_strategy == 5); + assert(default_simplex_scale_strategy == 1); // There are some functions to check what type of option value you should // provide. HighsInt option_type; - return_status = Highs_getOptionType(highs, "simplex_scale_strategy", &option_type); - assert( return_status == kHighsStatusOk ); - assert( option_type == kHighsOptionTypeInt ); + return_status = + Highs_getOptionType(highs, "simplex_scale_strategy", &option_type); + assert(return_status == kHighsStatusOk); + assert(option_type == kHighsOptionTypeInt); return_status = Highs_getOptionType(highs, "bad_option", &option_type); - assert( return_status == kHighsStatusError ); + assert(return_status == kHighsStatusError); double primal_feasibility_tolerance; - return_status = Highs_getDoubleOptionValue(highs, "primal_feasibility_tolerance", &primal_feasibility_tolerance); - assert( return_status == kHighsStatusOk ); + return_status = Highs_getDoubleOptionValue( + highs, "primal_feasibility_tolerance", &primal_feasibility_tolerance); + assert(return_status == kHighsStatusOk); if (dev_run) - printf("primal_feasibility_tolerance = %g: setting it to 1e-6\n", primal_feasibility_tolerance); + printf("primal_feasibility_tolerance = %g: setting it to 1e-6\n", + primal_feasibility_tolerance); primal_feasibility_tolerance = 1e-6; - return_status = Highs_setDoubleOptionValue(highs, "primal_feasibility_tolerance", primal_feasibility_tolerance); - assert( return_status == kHighsStatusOk ); + return_status = Highs_setDoubleOptionValue( + highs, "primal_feasibility_tolerance", primal_feasibility_tolerance); + assert(return_status == kHighsStatusOk); double check_primal_feasibility_tolerance; - return_status = Highs_getDoubleOptionValues(highs, "primal_feasibility_tolerance", - &check_primal_feasibility_tolerance, NULL, NULL, NULL); - assert( return_status == kHighsStatusOk ); - assert( check_primal_feasibility_tolerance == primal_feasibility_tolerance ); + return_status = Highs_getDoubleOptionValues( + highs, "primal_feasibility_tolerance", + &check_primal_feasibility_tolerance, NULL, NULL, NULL); + assert(return_status == kHighsStatusOk); + assert(check_primal_feasibility_tolerance == primal_feasibility_tolerance); double default_primal_feasibility_tolerance; double min_primal_feasibility_tolerance; double max_primal_feasibility_tolerance; - return_status = Highs_getDoubleOptionValues(highs, "primal_feasibility_tolerance", - &check_primal_feasibility_tolerance, - &min_primal_feasibility_tolerance, - &max_primal_feasibility_tolerance, - &default_primal_feasibility_tolerance); - assert( min_primal_feasibility_tolerance == 1e-10 ); - assert( max_primal_feasibility_tolerance == kHighsInf ); - assert( default_primal_feasibility_tolerance == 1e-7 ); + return_status = Highs_getDoubleOptionValues( + highs, "primal_feasibility_tolerance", + &check_primal_feasibility_tolerance, &min_primal_feasibility_tolerance, + &max_primal_feasibility_tolerance, &default_primal_feasibility_tolerance); + assert(min_primal_feasibility_tolerance == 1e-10); + assert(max_primal_feasibility_tolerance == kHighsInf); + assert(default_primal_feasibility_tolerance == 1e-7); Highs_setStringOptionValue(highs, "presolve", "off"); return_status = Highs_getStringOptionValues(highs, "pre-solve", NULL, NULL); - assert( return_status == kHighsStatusError ); + assert(return_status == kHighsStatusError); // char check_presolve_value[kHighsMaximumStringLength]; char check_presolve_value[512]; - return_status = Highs_getStringOptionValues(highs, "presolve", check_presolve_value, NULL); - assert( return_status == kHighsStatusOk ); + return_status = Highs_getStringOptionValues(highs, "presolve", + check_presolve_value, NULL); + assert(return_status == kHighsStatusOk); // const HighsInt output_flag = 1; - // return_status = Highs_setBoolOptionValue(highs, "output_flag", output_flag); + // return_status = Highs_setBoolOptionValue(highs, "output_flag", + // output_flag); return_status = Highs_setBoolOptionValue(highs, "output_flag", 1); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); HighsInt check_output_flag, default_output_flag; return_status = Highs_getBoolOptionValues(highs, "output_flag", NULL, NULL); - assert( return_status == kHighsStatusOk ); - return_status = Highs_getBoolOptionValues(highs, "output_flag", - &check_output_flag, NULL); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); + return_status = + Highs_getBoolOptionValues(highs, "output_flag", &check_output_flag, NULL); + assert(return_status == kHighsStatusOk); // assert( check_output_flag == output_flag ); - assert( check_output_flag == 1 ); - return_status = Highs_getBoolOptionValues(highs, "output_flag", - &check_output_flag, - &default_output_flag); - assert( return_status == kHighsStatusOk ); + assert(check_output_flag == 1); + return_status = Highs_getBoolOptionValues( + highs, "output_flag", &check_output_flag, &default_output_flag); + assert(return_status == kHighsStatusOk); // assert( default_output_flag == output_flag ); - assert( default_output_flag == 1 ); - + assert(default_output_flag == 1); + HighsInt num_string_option = 0; char* option = NULL; HighsInt type; HighsInt num_options = Highs_getNumOptions(highs); char current_string_value[512]; - - if (dev_run) - printf("\nString options are:\n"); + + if (dev_run) printf("\nString options are:\n"); for (HighsInt index = 0; index < num_options; index++) { Highs_getOptionName(highs, index, &option); Highs_getOptionType(highs, option, &type); @@ -788,13 +833,12 @@ void full_api_options() { Highs_getStringOptionValues(highs, option, current_string_value, NULL); num_string_option++; if (dev_run) - printf("%"HIGHSINT_FORMAT": %-24s \"%s\"\n", - num_string_option, option, current_string_value); + printf("%" HIGHSINT_FORMAT ": %-24s \"%s\"\n", num_string_option, option, + current_string_value); free(option); } Highs_destroy(highs); - } void full_api_lp() { @@ -836,34 +880,35 @@ void full_api_lp() { HighsInt* row_basis_status = (HighsInt*)malloc(sizeof(HighsInt) * num_row); // Add two columns to the empty LP - assert( Highs_addCols(highs, num_col, col_cost, col_lower, col_upper, 0, NULL, NULL, NULL) == 0); + assert(Highs_addCols(highs, num_col, col_cost, col_lower, col_upper, 0, NULL, + NULL, NULL) == 0); // Add three rows to the 2-column LP - assert( Highs_addRows(highs, num_row, row_lower, row_upper, num_nz, arstart, arindex, arvalue) == 0); + assert(Highs_addRows(highs, num_row, row_lower, row_upper, num_nz, arstart, + arindex, arvalue) == 0); HighsInt sense; HighsInt return_status; return_status = Highs_getObjectiveSense(highs, &sense); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); if (dev_run) - printf("LP problem has objective sense = %"HIGHSINT_FORMAT"\n", sense); - assert( sense == kHighsObjSenseMinimize ); + printf("LP problem has objective sense = %" HIGHSINT_FORMAT "\n", sense); + assert(sense == kHighsObjSenseMinimize); sense *= -1; return_status = Highs_changeObjectiveSense(highs, sense); - assert( return_status == kHighsStatusOk ); - assert( sense == kHighsObjSenseMaximize ); + assert(return_status == kHighsStatusOk); + assert(sense == kHighsObjSenseMaximize); sense *= -1; return_status = Highs_changeObjectiveSense(highs, sense); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); return_status = Highs_getObjectiveSense(highs, &sense); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); if (dev_run) - printf("LP problem has old objective sense = %"HIGHSINT_FORMAT"\n", sense); - assert( sense == kHighsObjSenseMinimize ); - - + printf("LP problem has old objective sense = %" HIGHSINT_FORMAT "\n", + sense); + assert(sense == kHighsObjSenseMinimize); // fetch column data (just first column) { @@ -875,18 +920,21 @@ void full_api_lp() { double* get_upper = (double*)malloc(sizeof(double) * num_get_col); HighsInt get_num_nz = 0; - return_status = Highs_getColsByRange(highs, get_col, get_col, - &get_num_col, get_costs, get_lower, get_upper, &get_num_nz, - NULL, NULL, NULL); - assert( return_status == kHighsStatusOk ); + return_status = Highs_getColsByRange(highs, get_col, get_col, &get_num_col, + get_costs, get_lower, get_upper, + &get_num_nz, NULL, NULL, NULL); + assert(return_status == kHighsStatusOk); assertIntValuesEqual("getCols get_num_col", get_num_col, num_get_col); - assertDoubleValuesEqual("getCols get_costs", get_costs[0], col_cost[get_col]); - assertDoubleValuesEqual("getCols get_lower", get_lower[0], col_lower[get_col]); - assertDoubleValuesEqual("getCols get_upper", get_upper[0], col_upper[get_col]); + assertDoubleValuesEqual("getCols get_costs", get_costs[0], + col_cost[get_col]); + assertDoubleValuesEqual("getCols get_lower", get_lower[0], + col_lower[get_col]); + assertDoubleValuesEqual("getCols get_upper", get_upper[0], + col_upper[get_col]); assertIntValuesEqual("getCols get_num_nz", get_num_nz, 2); - // could also check coefficients by calling again... + // could also check coefficients by calling again... free(get_upper); free(get_lower); @@ -904,72 +952,77 @@ void full_api_lp() { assertIntValuesEqual("getNumRows", Highs_getNumRows(highs), num_row); - return_status = Highs_getRowsByRange(highs, get_row, get_row, - &get_num_row, get_lower, get_upper, &get_num_nz, - NULL, NULL, NULL); - assert( return_status == kHighsStatusOk ); + return_status = + Highs_getRowsByRange(highs, get_row, get_row, &get_num_row, get_lower, + get_upper, &get_num_nz, NULL, NULL, NULL); + assert(return_status == kHighsStatusOk); assertIntValuesEqual("getRows get_num_row", get_num_row, num_get_row); - assertDoubleValuesEqual("getRows get_lower", get_lower[0], row_lower[get_row]); - assertDoubleValuesEqual("getRows get_upper", get_upper[0], row_upper[get_row]); + assertDoubleValuesEqual("getRows get_lower", get_lower[0], + row_lower[get_row]); + assertDoubleValuesEqual("getRows get_upper", get_upper[0], + row_upper[get_row]); assertIntValuesEqual("getRows get_num_nz", get_num_nz, 2); - // could also check coefficients by calling again... + // could also check coefficients by calling again... free(get_upper); free(get_lower); } - - - - return_status = Highs_setBoolOptionValue(highs, "output_flag", 0); - assert( return_status == kHighsStatusOk ); - if (dev_run) - printf("Running quietly...\n"); + assert(return_status == kHighsStatusOk); + if (dev_run) printf("Running quietly...\n"); return_status = Highs_run(highs); - assert( return_status == kHighsStatusOk ); - if (dev_run) - printf("Running loudly...\n"); + assert(return_status == kHighsStatusOk); + if (dev_run) printf("Running loudly...\n"); // Get the model status HighsInt model_status = Highs_getModelStatus(highs); if (dev_run) - printf("Run status = %"HIGHSINT_FORMAT"; Model status = %"HIGHSINT_FORMAT"\n", return_status, model_status); + printf("Run status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT + "\n", + return_status, model_status); double objective_function_value; - return_status = Highs_getDoubleInfoValue(highs, "objective_function_value", &objective_function_value); - assert( return_status == kHighsStatusOk ); + return_status = Highs_getDoubleInfoValue(highs, "objective_function_value", + &objective_function_value); + assert(return_status == kHighsStatusOk); HighsInt simplex_iteration_count; - return_status = Highs_getIntInfoValue(highs, "simplex_iteration_count", &simplex_iteration_count); - assert( return_status == kHighsStatusOk ); + return_status = Highs_getIntInfoValue(highs, "simplex_iteration_count", + &simplex_iteration_count); + assert(return_status == kHighsStatusOk); HighsInt primal_solution_status; - return_status = Highs_getIntInfoValue(highs, "primal_solution_status", &primal_solution_status); - assert( return_status == kHighsStatusOk ); + return_status = Highs_getIntInfoValue(highs, "primal_solution_status", + &primal_solution_status); + assert(return_status == kHighsStatusOk); HighsInt dual_solution_status; - return_status = Highs_getIntInfoValue(highs, "dual_solution_status", &dual_solution_status); - assert( return_status == kHighsStatusOk ); + return_status = Highs_getIntInfoValue(highs, "dual_solution_status", + &dual_solution_status); + assert(return_status == kHighsStatusOk); if (dev_run) { - printf("Objective value = %g; Iteration count = %"HIGHSINT_FORMAT"\n", - objective_function_value, simplex_iteration_count); + printf("Objective value = %g; Iteration count = %" HIGHSINT_FORMAT "\n", + objective_function_value, simplex_iteration_count); if (model_status == kHighsModelStatusOptimal) { // Get the primal and dual solution - return_status = Highs_getSolution(highs, col_value, col_dual, row_value, row_dual); - assert( return_status == kHighsStatusOk ); + return_status = + Highs_getSolution(highs, col_value, col_dual, row_value, row_dual); + assert(return_status == kHighsStatusOk); // Get the basis return_status = Highs_getBasis(highs, col_basis_status, row_basis_status); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); // Report the column primal and dual values, and basis status for (HighsInt iCol = 0; iCol < num_col; iCol++) - printf("Col%"HIGHSINT_FORMAT" = %lf; dual = %lf; status = %"HIGHSINT_FORMAT"; \n", - iCol, col_value[iCol], col_dual[iCol], col_basis_status[iCol]); + printf("Col%" HIGHSINT_FORMAT + " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "; \n", + iCol, col_value[iCol], col_dual[iCol], col_basis_status[iCol]); // Report the row primal and dual values, and basis status for (HighsInt iRow = 0; iRow < num_row; iRow++) - printf("Row%"HIGHSINT_FORMAT" = %lf; dual = %lf; status = %"HIGHSINT_FORMAT"; \n", - iRow, row_value[iRow], row_dual[iRow], row_basis_status[iRow]); + printf("Row%" HIGHSINT_FORMAT + " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "; \n", + iRow, row_value[iRow], row_dual[iRow], row_basis_status[iRow]); } } free(col_value); @@ -990,17 +1043,18 @@ void full_api_lp() { double a_value[5] = {1.0, 2.0, 1.0, 2.0, 1.0}; highs = Highs_create(); if (!dev_run) Highs_setBoolOptionValue(highs, "output_flag", 0); - return_status = Highs_passLp(highs, num_col, num_row, num_nz, a_format, sense, offset, - col_cost, col_lower, col_upper, - row_lower, row_upper, - a_start, a_index, a_value); - assert( return_status == kHighsStatusOk ); + return_status = Highs_passLp(highs, num_col, num_row, num_nz, a_format, sense, + offset, col_cost, col_lower, col_upper, + row_lower, row_upper, a_start, a_index, a_value); + assert(return_status == kHighsStatusOk); return_status = Highs_run(highs); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); model_status = Highs_getModelStatus(highs); - assert( model_status == kHighsModelStatusOptimal ); + assert(model_status == kHighsModelStatusOptimal); if (dev_run) - printf("Run status = %"HIGHSINT_FORMAT"; Model status = %"HIGHSINT_FORMAT"\n", return_status, model_status); + printf("Run status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT + "\n", + return_status, model_status); Highs_destroy(highs); } @@ -1026,13 +1080,14 @@ void full_api_mip() { double col_upper[3] = {1.0e30, 1.0e30, 1.0}; // Define the row lower bounds and upper bounds double row_lower[2] = {-1.0e30, 12.0}; - double row_upper[2] = { 7.0, 12.0}; + double row_upper[2] = {7.0, 12.0}; // Define the constraint matrix column-wise HighsInt a_start[3] = {0, 2, 4}; HighsInt a_index[6] = {0, 1, 0, 1, 0, 1}; double a_value[6] = {1.0, 4.0, 1.0, 2.0, 1.0, 1.0}; - HighsInt integrality[3] = {kHighsVarTypeInteger, kHighsVarTypeInteger, kHighsVarTypeInteger}; + HighsInt integrality[3] = {kHighsVarTypeInteger, kHighsVarTypeInteger, + kHighsVarTypeInteger}; double* col_value = (double*)malloc(sizeof(double) * num_col); double* row_value = (double*)malloc(sizeof(double) * num_row); @@ -1042,10 +1097,10 @@ void full_api_mip() { void* highs = Highs_create(); if (!dev_run) Highs_setBoolOptionValue(highs, "output_flag", 0); - return_status = Highs_passMip(highs, num_col, num_row, num_nz, a_format, - sense, offset, col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, - integrality); + return_status = + Highs_passMip(highs, num_col, num_row, num_nz, a_format, sense, offset, + col_cost, col_lower, col_upper, row_lower, row_upper, + a_start, a_index, a_value, integrality); assert(return_status == kHighsStatusOk); Highs_setStringOptionValue(highs, "presolve", "off"); return_status = Highs_run(highs); @@ -1056,30 +1111,31 @@ void full_api_mip() { #ifdef HIGHSINT64 required_return_status = kHighsStatusOk; #endif - return_status = Highs_getIntInfoValue(highs, "mip_node_count", &mip_node_count_int); + return_status = + Highs_getIntInfoValue(highs, "mip_node_count", &mip_node_count_int); assert(return_status == required_return_status); int64_t mip_node_count; - return_status = Highs_getInt64InfoValue(highs, "mip_node_count", &mip_node_count); - assert( return_status == kHighsStatusOk ); - assert( mip_node_count == 1 ); + return_status = + Highs_getInt64InfoValue(highs, "mip_node_count", &mip_node_count); + assert(return_status == kHighsStatusOk); + assert(mip_node_count == 1); // Test Highs_getColIntegrality HighsInt col_integrality; return_status = Highs_getColIntegrality(highs, -1, &col_integrality); - assert( return_status == kHighsStatusError ); + assert(return_status == kHighsStatusError); return_status = Highs_getColIntegrality(highs, num_col, &col_integrality); - assert( return_status == kHighsStatusError ); + assert(return_status == kHighsStatusError); for (HighsInt iCol = 0; iCol < num_col; iCol++) { return_status = Highs_getColIntegrality(highs, iCol, &col_integrality); - assert( return_status == kHighsStatusOk ); - assert( col_integrality == 1 ); + assert(return_status == kHighsStatusOk); + assert(col_integrality == 1); } Highs_destroy(highs); free(col_value); free(row_value); - } void full_api_qp() { @@ -1099,12 +1155,12 @@ void full_api_qp() { HighsInt num_col = 0; return_status = Highs_addCol(highs, 1.0, -inf, inf, 0, NULL, NULL); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); num_col++; double offset = 0.25; return_status = Highs_changeObjectiveOffset(highs, offset); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); HighsInt q_dim = 1; HighsInt q_num_nz = 1; @@ -1115,23 +1171,26 @@ void full_api_qp() { q_start[0] = 0; q_index[0] = 0; q_value[0] = 2.0; - return_status = Highs_passHessian(highs, q_dim, q_num_nz, q_format, q_start, q_index, q_value); - assert( return_status == kHighsStatusOk ); + return_status = Highs_passHessian(highs, q_dim, q_num_nz, q_format, q_start, + q_index, q_value); + assert(return_status == kHighsStatusOk); if (dev_run) Highs_writeModel(highs, ""); return_status = Highs_run(highs); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); model_status = Highs_getModelStatus(highs); - assertIntValuesEqual("Model status for 1-d QP", model_status, kHighsModelStatusOptimal); + assertIntValuesEqual("Model status for 1-d QP", model_status, + kHighsModelStatusOptimal); required_objective_function_value = 0; required_x0 = -0.5; objective_function_value = Highs_getObjectiveValue(highs); - assertDoubleValuesEqual("Objective", objective_function_value, required_objective_function_value); + assertDoubleValuesEqual("Objective", objective_function_value, + required_objective_function_value); double* col_solution = (double*)malloc(sizeof(double) * num_col); return_status = Highs_getSolution(highs, col_solution, NULL, NULL, NULL); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); assertDoubleValuesEqual("x0", col_solution[0], required_x0); if (dev_run) Highs_writeSolutionPretty(highs, ""); @@ -1139,15 +1198,17 @@ void full_api_qp() { // // Add the variable return_status = Highs_addCol(highs, -1.0, -inf, inf, 0, NULL, NULL); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); num_col++; // Can solve the model before the Hessian has been replaced return_status = Highs_run(highs); - assert( return_status == kHighsStatusOk ); - assertIntValuesEqual("Run status for 2-d QP with OK Hessian", return_status, 0); + assert(return_status == kHighsStatusOk); + assertIntValuesEqual("Run status for 2-d QP with OK Hessian", return_status, + 0); model_status = Highs_getModelStatus(highs); - assertIntValuesEqual("Model status for this 2-d QP with OK Hessian", model_status, kHighsModelStatusUnbounded); + assertIntValuesEqual("Model status for this 2-d QP with OK Hessian", + model_status, kHighsModelStatusUnbounded); free(q_start); free(q_index); @@ -1165,26 +1226,29 @@ void full_api_qp() { q_start[1] = 1; q_index[1] = 1; q_value[1] = 2.0; - return_status = Highs_passHessian(highs, q_dim, q_num_nz, q_format, q_start, q_index, q_value); - assert( return_status == kHighsStatusOk ); + return_status = Highs_passHessian(highs, q_dim, q_num_nz, q_format, q_start, + q_index, q_value); + assert(return_status == kHighsStatusOk); if (dev_run) Highs_writeModel(highs, ""); return_status = Highs_run(highs); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); model_status = Highs_getModelStatus(highs); - assertIntValuesEqual("Model status for 2-d QP", model_status, kHighsModelStatusOptimal); - + assertIntValuesEqual("Model status for 2-d QP", model_status, + kHighsModelStatusOptimal); + required_objective_function_value = -0.25; required_x1 = 0.5; objective_function_value = Highs_getObjectiveValue(highs); - assertDoubleValuesEqual("Objective", objective_function_value, required_objective_function_value); + assertDoubleValuesEqual("Objective", objective_function_value, + required_objective_function_value); free(col_solution); col_solution = (double*)malloc(sizeof(double) * num_col); return_status = Highs_getSolution(highs, col_solution, NULL, NULL, NULL); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); assertDoubleValuesEqual("x0", col_solution[0], required_x0); assertDoubleValuesEqual("x1", col_solution[1], required_x1); @@ -1194,59 +1258,63 @@ void full_api_qp() { double check_offset; return_status = Highs_getObjectiveOffset(highs, &check_offset); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); assertDoubleValuesEqual("Offset", check_offset, offset); double dl_offset = -objective_function_value; offset += dl_offset; return_status = Highs_changeObjectiveOffset(highs, offset); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); required_objective_function_value += dl_offset; objective_function_value = Highs_getObjectiveValue(highs); - assertDoubleValuesEqual("Objective with new offset", objective_function_value, required_objective_function_value); + assertDoubleValuesEqual("Objective with new offset", objective_function_value, + required_objective_function_value); // Add the constraint 0.5 <= x0 + x1 HighsInt a_index[2] = {0, 1}; double a_value[2] = {1, 1}; return_status = Highs_addRow(highs, 0.5, inf, 2, a_index, a_value); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); if (dev_run) Highs_writeModel(highs, ""); return_status = Highs_run(highs); - assert( return_status == kHighsStatusOk ); - assertIntValuesEqual("Run status for 2-d QP with constraint", return_status, kHighsStatusOk); - + assert(return_status == kHighsStatusOk); + assertIntValuesEqual("Run status for 2-d QP with constraint", return_status, + kHighsStatusOk); + model_status = Highs_getModelStatus(highs); - assertIntValuesEqual("Model status for 2-d QP with constraint", model_status, kHighsModelStatusOptimal); + assertIntValuesEqual("Model status for 2-d QP with constraint", model_status, + kHighsModelStatusOptimal); required_objective_function_value = 0.125; required_x0 = -0.25; required_x1 = 0.75; objective_function_value = Highs_getObjectiveValue(highs); - assertDoubleValuesEqual("Objective", objective_function_value, required_objective_function_value); + assertDoubleValuesEqual("Objective", objective_function_value, + required_objective_function_value); return_status = Highs_getSolution(highs, col_solution, NULL, NULL, NULL); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); assertDoubleValuesEqual("x0", col_solution[0], required_x0); assertDoubleValuesEqual("x1", col_solution[1], required_x1); // Add bounds to make the QP infeasible return_status = Highs_changeColBounds(highs, 0, -inf, 0); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); return_status = Highs_changeColBounds(highs, 1, -inf, 0); - assert( return_status == kHighsStatusOk ); - + assert(return_status == kHighsStatusOk); + if (dev_run) Highs_writeModel(highs, ""); return_status = Highs_run(highs); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); assertIntValuesEqual("Run status for infeasible 2-d QP", return_status, 0); - + model_status = Highs_getModelStatus(highs); assertIntValuesEqual("Model status for infeasible 2-d QP", model_status, 8); - assert( model_status == kHighsModelStatusInfeasible ); + assert(model_status == kHighsModelStatusInfeasible); Highs_destroy(highs); @@ -1254,7 +1322,6 @@ void full_api_qp() { free(q_index); free(q_value); free(col_solution); - } void pass_presolve_get_lp() { @@ -1271,7 +1338,7 @@ void pass_presolve_get_lp() { const double kHighsInf = Highs_getInfinity(highs); HighsInt model_status; HighsInt return_status; - + Highs_setBoolOptionValue(highs, "output_flag", dev_run); HighsInt a_format = kHighsMatrixFormatColwise; HighsInt sense = kHighsObjSenseMinimize; @@ -1292,72 +1359,81 @@ void pass_presolve_get_lp() { HighsInt a_index[5] = {1, 2, 0, 1, 2}; double a_value[5] = {1.0, 2.0, 1.0, 2.0, 1.0}; - return_status = Highs_passLp(highs, num_col, num_row, num_nz, a_format, sense, offset, - col_cost, col_lower, col_upper, - row_lower, row_upper, - a_start, a_index, a_value); - assert( return_status == kHighsStatusOk ); + return_status = Highs_passLp(highs, num_col, num_row, num_nz, a_format, sense, + offset, col_cost, col_lower, col_upper, + row_lower, row_upper, a_start, a_index, a_value); + assert(return_status == kHighsStatusOk); return_status = Highs_presolve(highs); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); for (HighsInt k = 0; k < 2; k++) { // Loop twice: once for col-wise; once for row-wise HighsInt presolved_num_col = Highs_getPresolvedNumCol(highs); HighsInt presolved_num_row = Highs_getPresolvedNumRow(highs); HighsInt presolved_num_nz = Highs_getPresolvedNumNz(highs); - HighsInt presolved_a_format = k == 0 ? kHighsMatrixFormatColwise : kHighsMatrixFormatRowwise; + HighsInt presolved_a_format = + k == 0 ? kHighsMatrixFormatColwise : kHighsMatrixFormatRowwise; HighsInt presolved_sense; double presolved_offset; - double* presolved_col_cost = (double*)malloc(sizeof(double) * presolved_num_col); - double* presolved_col_lower = (double*)malloc(sizeof(double) * presolved_num_col); - double* presolved_col_upper = (double*)malloc(sizeof(double) * presolved_num_col); - double* presolved_row_lower = (double*)malloc(sizeof(double) * presolved_num_row); - double* presolved_row_upper = (double*)malloc(sizeof(double) * presolved_num_row); - HighsInt* presolved_a_start = (HighsInt*)malloc(sizeof(HighsInt) * (presolved_num_col+1)); - HighsInt* presolved_a_index = (HighsInt*)malloc(sizeof(HighsInt) * presolved_num_nz); - double* presolved_a_value = (double*)malloc(sizeof(double) * presolved_num_nz); - - return_status = Highs_getPresolvedLp(highs, presolved_a_format, - &presolved_num_col, &presolved_num_row, &presolved_num_nz, - &presolved_sense, &presolved_offset, - presolved_col_cost, presolved_col_lower, presolved_col_upper, - presolved_row_lower, presolved_row_upper, - presolved_a_start, presolved_a_index, presolved_a_value, NULL); - assert( return_status == kHighsStatusOk ); + double* presolved_col_cost = + (double*)malloc(sizeof(double) * presolved_num_col); + double* presolved_col_lower = + (double*)malloc(sizeof(double) * presolved_num_col); + double* presolved_col_upper = + (double*)malloc(sizeof(double) * presolved_num_col); + double* presolved_row_lower = + (double*)malloc(sizeof(double) * presolved_num_row); + double* presolved_row_upper = + (double*)malloc(sizeof(double) * presolved_num_row); + HighsInt* presolved_a_start = + (HighsInt*)malloc(sizeof(HighsInt) * (presolved_num_col + 1)); + HighsInt* presolved_a_index = + (HighsInt*)malloc(sizeof(HighsInt) * presolved_num_nz); + double* presolved_a_value = + (double*)malloc(sizeof(double) * presolved_num_nz); + + return_status = Highs_getPresolvedLp( + highs, presolved_a_format, &presolved_num_col, &presolved_num_row, + &presolved_num_nz, &presolved_sense, &presolved_offset, + presolved_col_cost, presolved_col_lower, presolved_col_upper, + presolved_row_lower, presolved_row_upper, presolved_a_start, + presolved_a_index, presolved_a_value, NULL); + assert(return_status == kHighsStatusOk); // Solve the presolved LP within a local version of HiGHS void* local_highs; local_highs = Highs_create(); Highs_setBoolOptionValue(local_highs, "output_flag", dev_run); Highs_setStringOptionValue(local_highs, "presolve", "off"); - return_status = Highs_passLp(local_highs, - presolved_num_col, presolved_num_row, presolved_num_nz, - presolved_a_format, presolved_sense, presolved_offset, - presolved_col_cost, presolved_col_lower, presolved_col_upper, - presolved_row_lower, presolved_row_upper, - presolved_a_start, presolved_a_index, presolved_a_value); - assert( return_status == kHighsStatusOk ); + return_status = Highs_passLp( + local_highs, presolved_num_col, presolved_num_row, presolved_num_nz, + presolved_a_format, presolved_sense, presolved_offset, + presolved_col_cost, presolved_col_lower, presolved_col_upper, + presolved_row_lower, presolved_row_upper, presolved_a_start, + presolved_a_index, presolved_a_value); + assert(return_status == kHighsStatusOk); return_status = Highs_run(local_highs); - + double* col_value = (double*)malloc(sizeof(double) * num_col); double* col_dual = (double*)malloc(sizeof(double) * num_col); double* row_dual = (double*)malloc(sizeof(double) * num_row); - return_status = Highs_getSolution(local_highs, col_value, col_dual, NULL, row_dual); - assert( return_status == kHighsStatusOk ); + return_status = + Highs_getSolution(local_highs, col_value, col_dual, NULL, row_dual); + assert(return_status == kHighsStatusOk); return_status = Highs_postsolve(highs, col_value, col_dual, row_dual); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); model_status = Highs_getModelStatus(highs); - assert( model_status == kHighsModelStatusOptimal ); - + assert(model_status == kHighsModelStatusOptimal); + // With just the primal solution, optimality cannot be determined return_status = Highs_postsolve(highs, col_value, NULL, NULL); - assert( return_status == kHighsStatusWarning ); + assert(return_status == kHighsStatusWarning); model_status = Highs_getModelStatus(highs); - assert( model_status == kHighsModelStatusUnknown ); + assert(model_status == kHighsModelStatusUnknown); free(presolved_col_cost); free(presolved_col_lower); @@ -1370,8 +1446,6 @@ void pass_presolve_get_lp() { free(col_value); free(col_dual); free(row_dual); - - } } @@ -1382,20 +1456,22 @@ void options() { HighsInt simplex_scale_strategy; HighsInt return_status; return_status = Highs_setIntOptionValue(highs, "simplex_scale_strategy", 0); - assert( return_status == kHighsStatusOk ); - return_status = Highs_getIntOptionValue(highs, "simplex_scale_strategy", &simplex_scale_strategy); - assert( return_status == kHighsStatusOk ); - assert( simplex_scale_strategy == 0 ); + assert(return_status == kHighsStatusOk); + return_status = Highs_getIntOptionValue(highs, "simplex_scale_strategy", + &simplex_scale_strategy); + assert(return_status == kHighsStatusOk); + assert(simplex_scale_strategy == 0); double primal_feasibility_tolerance; - return_status = Highs_setDoubleOptionValue(highs, "primal_feasibility_tolerance", 2.0); - assert( return_status == kHighsStatusOk ); - return_status = Highs_getDoubleOptionValue(highs, "primal_feasibility_tolerance", &primal_feasibility_tolerance); - assert( return_status == kHighsStatusOk ); - assert( primal_feasibility_tolerance == 2.0 ); + return_status = + Highs_setDoubleOptionValue(highs, "primal_feasibility_tolerance", 2.0); + assert(return_status == kHighsStatusOk); + return_status = Highs_getDoubleOptionValue( + highs, "primal_feasibility_tolerance", &primal_feasibility_tolerance); + assert(return_status == kHighsStatusOk); + assert(primal_feasibility_tolerance == 2.0); Highs_destroy(highs); - } void test_getColsByRange() { @@ -1403,32 +1479,33 @@ void test_getColsByRange() { if (!dev_run) Highs_setBoolOptionValue(highs, "output_flag", 0); HighsInt return_status; return_status = Highs_addCol(highs, -1.0, 0.0, 1.0, 0, NULL, NULL); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); return_status = Highs_addCol(highs, -1.0, 0.0, 1.0, 0, NULL, NULL); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); HighsInt a_index[2] = {0, 1}; double a_value[2] = {1.0, -1.0}; return_status = Highs_addRow(highs, 0.0, 0.0, 2, a_index, a_value); - assert( return_status == kHighsStatusOk ); + assert(return_status == kHighsStatusOk); HighsInt num_cols; HighsInt num_nz; HighsInt matrix_start[2] = {-1, -1}; - return_status = Highs_getColsByRange(highs, 0, 1, &num_cols, NULL, NULL, NULL, &num_nz, - matrix_start, NULL, NULL); - assert( return_status == kHighsStatusOk ); - assert( num_cols == 2 ); - assert( num_nz == 2 ); - assert( matrix_start[0] == 0 ); - assert( matrix_start[1] == 1 ); + return_status = Highs_getColsByRange(highs, 0, 1, &num_cols, NULL, NULL, NULL, + &num_nz, matrix_start, NULL, NULL); + assert(return_status == kHighsStatusOk); + assert(num_cols == 2); + assert(num_nz == 2); + assert(matrix_start[0] == 0); + assert(matrix_start[1] == 1); HighsInt matrix_index[2] = {-1, -1}; double matrix_value[2] = {0.0, 0.0}; - return_status = Highs_getColsByRange(highs, 0, 1, &num_cols, NULL, NULL, NULL, &num_nz, - matrix_start, matrix_index, matrix_value); - assert( return_status == kHighsStatusOk ); - assert( matrix_index[0] == 0 ); - assert( matrix_index[1] == 0 ); - assert( matrix_value[0] == 1.0 ); - assert( matrix_value[1] == -1.0 ); + return_status = + Highs_getColsByRange(highs, 0, 1, &num_cols, NULL, NULL, NULL, &num_nz, + matrix_start, matrix_index, matrix_value); + assert(return_status == kHighsStatusOk); + assert(matrix_index[0] == 0); + assert(matrix_index[1] == 0); + assert(matrix_value[0] == 1.0); + assert(matrix_value[1] == -1.0); Highs_destroy(highs); } @@ -1449,12 +1526,14 @@ void test_passHessian() { const double optimal_objective_value = 1; const double primal = 1; const double dual = 0; - assertIntValuesEqual("Status", Highs_getModelStatus(highs), kHighsModelStatusOptimal); // kOptimal + assertIntValuesEqual("Status", Highs_getModelStatus(highs), + kHighsModelStatusOptimal); // kOptimal double col_value[1] = {-123.0}; double col_dual[1] = {0.0}; Highs_getSolution(highs, col_value, col_dual, NULL, NULL); double objective_value = Highs_getObjectiveValue(highs); - assertDoubleValuesEqual("Objective", objective_value, optimal_objective_value); + assertDoubleValuesEqual("Objective", objective_value, + optimal_objective_value); assertDoubleValuesEqual("Primal", col_value[0], primal); assertDoubleValuesEqual("Dual", col_dual[0], dual); @@ -1462,7 +1541,6 @@ void test_passHessian() { } void test_ranging() { - void* highs = Highs_create(); if (!dev_run) Highs_setBoolOptionValue(highs, "output_flag", 0); // @@ -1485,7 +1563,7 @@ void test_ranging() { // Cost ranging // c0 2 -1 1 0 // c1 0 0 inf inf - // + // // Bound ranging // Columns // c0 1 -inf inf 1 @@ -1520,43 +1598,57 @@ void test_ranging() { double* row_bound_dn_objective = (double*)malloc(sizeof(double) * num_row); HighsInt* row_bound_dn_in_var = (HighsInt*)malloc(sizeof(HighsInt) * num_row); HighsInt* row_bound_dn_ou_var = (HighsInt*)malloc(sizeof(HighsInt) * num_row); - HighsInt status = - Highs_getRanging(highs, - // - col_cost_up_value, col_cost_up_objective, col_cost_up_in_var, col_cost_up_ou_var, - col_cost_dn_value, col_cost_dn_objective, col_cost_dn_in_var, col_cost_dn_ou_var, - col_bound_up_value, col_bound_up_objective, col_bound_up_in_var, col_bound_up_ou_var, - col_bound_dn_value, col_bound_dn_objective, col_bound_dn_in_var, col_bound_dn_ou_var, - row_bound_up_value, row_bound_up_objective, row_bound_up_in_var, row_bound_up_ou_var, - row_bound_dn_value, row_bound_dn_objective, row_bound_dn_in_var, row_bound_dn_ou_var); + HighsInt status = Highs_getRanging( + highs, + // + col_cost_up_value, col_cost_up_objective, col_cost_up_in_var, + col_cost_up_ou_var, col_cost_dn_value, col_cost_dn_objective, + col_cost_dn_in_var, col_cost_dn_ou_var, col_bound_up_value, + col_bound_up_objective, col_bound_up_in_var, col_bound_up_ou_var, + col_bound_dn_value, col_bound_dn_objective, col_bound_dn_in_var, + col_bound_dn_ou_var, row_bound_up_value, row_bound_up_objective, + row_bound_up_in_var, row_bound_up_ou_var, row_bound_dn_value, + row_bound_dn_objective, row_bound_dn_in_var, row_bound_dn_ou_var); assert(status == kHighsStatusOk); - assertDoubleValuesEqual("col_cost_dn_objective[0]", col_cost_dn_objective[0], 2); + assertDoubleValuesEqual("col_cost_dn_objective[0]", col_cost_dn_objective[0], + 2); assertDoubleValuesEqual("col_cost_dn_value[0]", col_cost_dn_value[0], -1); assertDoubleValuesEqual("col_cost_up_value[0]", col_cost_up_value[0], 1); - assertDoubleValuesEqual("col_cost_up_objective[0]", col_cost_up_objective[0], 0); - assertDoubleValuesEqual("col_cost_dn_objective[1]", col_cost_dn_objective[1], 0); + assertDoubleValuesEqual("col_cost_up_objective[0]", col_cost_up_objective[0], + 0); + assertDoubleValuesEqual("col_cost_dn_objective[1]", col_cost_dn_objective[1], + 0); assertDoubleValuesEqual("col_cost_dn_value[1]", col_cost_dn_value[1], 0); assertDoubleValuesEqual("col_cost_up_value[1]", col_cost_up_value[1], inf); - assertDoubleValuesEqual("col_cost_up_objective[1]", col_cost_up_objective[1], inf); + assertDoubleValuesEqual("col_cost_up_objective[1]", col_cost_up_objective[1], + inf); - assertDoubleValuesEqual("col_bound_dn_objective[0]", col_bound_dn_objective[0], 1); + assertDoubleValuesEqual("col_bound_dn_objective[0]", + col_bound_dn_objective[0], 1); assertDoubleValuesEqual("col_bound_dn_value[0]", col_bound_dn_value[0], -inf); assertDoubleValuesEqual("col_bound_up_value[0]", col_bound_up_value[0], inf); - assertDoubleValuesEqual("col_bound_up_objective[0]", col_bound_up_objective[0], 1); - assertDoubleValuesEqual("col_bound_dn_objective[1]", col_bound_dn_objective[1], 1); + assertDoubleValuesEqual("col_bound_up_objective[0]", + col_bound_up_objective[0], 1); + assertDoubleValuesEqual("col_bound_dn_objective[1]", + col_bound_dn_objective[1], 1); assertDoubleValuesEqual("col_bound_dn_value[1]", col_bound_dn_value[1], 1); assertDoubleValuesEqual("col_bound_up_value[1]", col_bound_up_value[1], inf); - assertDoubleValuesEqual("col_bound_up_objective[1]", col_bound_up_objective[1], 1); + assertDoubleValuesEqual("col_bound_up_objective[1]", + col_bound_up_objective[1], 1); - assertDoubleValuesEqual("row_bound_dn_objective[0]", row_bound_dn_objective[0], -inf); + assertDoubleValuesEqual("row_bound_dn_objective[0]", + row_bound_dn_objective[0], -inf); assertDoubleValuesEqual("row_bound_dn_value[0]", row_bound_dn_value[0], -inf); assertDoubleValuesEqual("row_bound_up_value[0]", row_bound_up_value[0], inf); - assertDoubleValuesEqual("row_bound_up_objective[0]", row_bound_up_objective[0], inf); - assertDoubleValuesEqual("row_bound_dn_objective[1]", row_bound_dn_objective[1], -inf); + assertDoubleValuesEqual("row_bound_up_objective[0]", + row_bound_up_objective[0], inf); + assertDoubleValuesEqual("row_bound_dn_objective[1]", + row_bound_dn_objective[1], -inf); assertDoubleValuesEqual("row_bound_dn_value[1]", row_bound_dn_value[1], -inf); assertDoubleValuesEqual("row_bound_up_value[1]", row_bound_up_value[1], inf); - assertDoubleValuesEqual("row_bound_up_objective[1]", row_bound_up_objective[1], inf); + assertDoubleValuesEqual("row_bound_up_objective[1]", + row_bound_up_objective[1], inf); free(col_cost_up_value); free(col_cost_up_objective); @@ -1586,6 +1678,56 @@ void test_ranging() { Highs_destroy(highs); } +void test_feasibilityRelaxation() { + void* highs; + highs = Highs_create(); + const double kHighsInf = Highs_getInfinity(highs); + Highs_setBoolOptionValue(highs, "output_flag", dev_run); + HighsInt return_status; + + HighsInt num_col = 2; + HighsInt num_row = 3; + HighsInt num_nz = 6; + HighsInt a_format = kHighsMatrixFormatColwise; + HighsInt sense = kHighsObjSenseMinimize; + double offset = 0; + double col_cost[2] = {1, -2}; + double col_lower[2] = {5, -kHighsInf}; + double col_upper[2] = {kHighsInf, kHighsInf}; + double row_lower[3] = {2, -kHighsInf, -kHighsInf}; + double row_upper[3] = {kHighsInf, 1, 20}; + HighsInt a_start[2] = {0, 3}; + HighsInt a_index[6] = {0, 1, 2, 0, 1, 2}; + double a_value[6] = {-1, -3, 20, 21, 2, 1}; + HighsInt integrality[2] = {kHighsVarTypeInteger, kHighsVarTypeInteger}; + + Highs_passMip(highs, num_col, num_row, num_nz, a_format, sense, offset, + col_cost, col_lower, col_upper, row_lower, row_upper, a_start, + a_index, a_value, integrality); + Highs_feasibilityRelaxation(highs, 1, 1, 1, NULL, NULL, NULL); + double objective_function_value; + Highs_getDoubleInfoValue(highs, "objective_function_value", + &objective_function_value); + double* col_value = (double*)malloc(sizeof(double) * num_col); + double* col_dual = (double*)malloc(sizeof(double) * num_col); + double* row_value = (double*)malloc(sizeof(double) * num_row); + double* row_dual = (double*)malloc(sizeof(double) * num_row); + return_status = + Highs_getSolution(highs, col_value, col_dual, row_value, row_dual); + assert(return_status == kHighsStatusOk); + assertDoubleValuesEqual("objective_function_value", objective_function_value, + 5); + assertDoubleValuesEqual("solution_value[0]", col_value[0], 1); + assertDoubleValuesEqual("solution_value[1]", col_value[1], 1); + + free(col_value); + free(col_dual); + free(row_value); + free(row_dual); + + Highs_destroy(highs); +} + void test_callback() { HighsInt num_col = 7; HighsInt num_row = 1; @@ -1602,40 +1744,42 @@ void test_callback() { HighsInt a_index[7] = {0, 1, 2, 3, 4, 5, 6}; double a_value[7] = {9, 6, 7, 9, 7, 9, 9}; HighsInt integrality[7] = {kHighsVarTypeInteger, kHighsVarTypeInteger, - kHighsVarTypeInteger, kHighsVarTypeInteger, - kHighsVarTypeInteger, kHighsVarTypeInteger, - kHighsVarTypeInteger}; + kHighsVarTypeInteger, kHighsVarTypeInteger, + kHighsVarTypeInteger, kHighsVarTypeInteger, + kHighsVarTypeInteger}; void* highs; highs = Highs_create(); Highs_setBoolOptionValue(highs, "output_flag", dev_run); Highs_passMip(highs, num_col, num_row, num_nz, a_format, sense, offset, - col_cost, col_lower, col_upper, - row_lower, row_upper, - a_start, a_index, a_value, - integrality); - + col_cost, col_lower, col_upper, row_lower, row_upper, a_start, + a_index, a_value, integrality); + Highs_setCallback(highs, userCallback, NULL); Highs_startCallback(highs, kHighsCallbackLogging); Highs_startCallback(highs, kHighsCallbackMipInterrupt); Highs_run(highs); double objective_function_value; - Highs_getDoubleInfoValue(highs, "objective_function_value", &objective_function_value); + Highs_getDoubleInfoValue(highs, "objective_function_value", + &objective_function_value); double inf = Highs_getInfinity(highs); - assertDoubleValuesEqual("objective_function_value", objective_function_value, inf); + assertDoubleValuesEqual("objective_function_value", objective_function_value, + inf); Highs_stopCallback(highs, kHighsCallbackMipInterrupt); Highs_run(highs); - Highs_getDoubleInfoValue(highs, "objective_function_value", &objective_function_value); - assertDoubleValuesEqual("objective_function_value", objective_function_value, 17); + Highs_getDoubleInfoValue(highs, "objective_function_value", + &objective_function_value); + assertDoubleValuesEqual("objective_function_value", objective_function_value, + 17); double user_callback_data = inf; void* p_user_callback_data = (void*)(&user_callback_data); - + Highs_setCallback(highs, userCallback, p_user_callback_data); Highs_clearSolver(highs); Highs_startCallback(highs, kHighsCallbackMipImprovingSolution); Highs_run(highs); - + Highs_destroy(highs); } @@ -1644,7 +1788,7 @@ void test_getModel() { highs = Highs_create(); Highs_setBoolOptionValue(highs, "output_flag", dev_run); const double inf = Highs_getInfinity(highs); - + HighsInt num_col = 2; HighsInt num_row = 2; HighsInt num_nz = 4; @@ -1659,8 +1803,9 @@ void test_getModel() { double a_value[4] = {0.3, 0.5, 0.7, 0.5}; HighsInt a_start[2] = {0, 2}; Highs_addVars(highs, num_col, col_lower, col_upper); - Highs_changeColsCostByRange(highs, 0, num_col-1, col_cost); - Highs_addRows(highs, num_row, row_lower, row_upper, num_nz, a_start, a_index, a_value); + Highs_changeColsCostByRange(highs, 0, num_col - 1, col_cost); + Highs_addRows(highs, num_row, row_lower, row_upper, num_nz, a_start, a_index, + a_value); Highs_changeObjectiveSense(highs, sense); Highs_run(highs); @@ -1671,20 +1816,19 @@ void test_getModel() { double ck_offset; // Get the model dimensions by passing array pointers as NULL - Highs_getLp(highs, kHighsMatrixFormatRowwise, - &ck_num_col, &ck_num_row, &ck_num_nz, - &ck_sense, &ck_offset, NULL, - NULL, NULL, NULL, - NULL, NULL, NULL, - NULL, NULL); - - assert( ck_num_col == num_col ); - assert( ck_num_row == num_row ); - assert( ck_num_nz == num_nz ); - // Motivated by #1712, ensure that the correct sense is returned when maximizing - assert( ck_sense == sense ); - - double* ck_col_cost = (double*)malloc(sizeof(double) * ck_num_col);; + Highs_getLp(highs, kHighsMatrixFormatRowwise, &ck_num_col, &ck_num_row, + &ck_num_nz, &ck_sense, &ck_offset, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL); + + assert(ck_num_col == num_col); + assert(ck_num_row == num_row); + assert(ck_num_nz == num_nz); + // Motivated by #1712, ensure that the correct sense is returned when + // maximizing + assert(ck_sense == sense); + + double* ck_col_cost = (double*)malloc(sizeof(double) * ck_num_col); + ; double* ck_col_lower = (double*)malloc(sizeof(double) * ck_num_col); double* ck_col_upper = (double*)malloc(sizeof(double) * ck_num_col); double* ck_row_lower = (double*)malloc(sizeof(double) * ck_num_row); @@ -1692,24 +1836,23 @@ void test_getModel() { HighsInt* ck_a_start = (HighsInt*)malloc(sizeof(HighsInt) * ck_num_col); HighsInt* ck_a_index = (HighsInt*)malloc(sizeof(HighsInt) * ck_num_nz); double* ck_a_value = (double*)malloc(sizeof(double) * num_nz); - + // Get the arrays - Highs_getLp(highs, kHighsMatrixFormatRowwise, - &ck_num_col, &ck_num_row, &ck_num_nz, - &ck_sense, &ck_offset, ck_col_cost, - ck_col_lower, ck_col_upper, ck_row_lower, - ck_row_upper, ck_a_start, ck_a_index, - ck_a_value, NULL); - - assert( doubleArraysEqual(num_col, ck_col_cost, col_cost) ); - assert( doubleArraysEqual(num_col, ck_col_lower, col_lower) ); - assert( doubleArraysEqual(num_col, ck_col_upper, col_upper) ); - assert( doubleArraysEqual(num_row, ck_row_lower, row_lower) ); - assert( doubleArraysEqual(num_row, ck_row_upper, row_upper) ); - assert( highsIntArraysEqual(num_col, ck_a_start, a_start) ); - assert( highsIntArraysEqual(num_nz, ck_a_index, a_index) ); - assert( doubleArraysEqual(num_nz, ck_a_value, a_value) ); + Highs_getLp(highs, kHighsMatrixFormatRowwise, &ck_num_col, &ck_num_row, + &ck_num_nz, &ck_sense, &ck_offset, ck_col_cost, ck_col_lower, + ck_col_upper, ck_row_lower, ck_row_upper, ck_a_start, ck_a_index, + ck_a_value, NULL); + + assert(doubleArraysEqual(num_col, ck_col_cost, col_cost)); + assert(doubleArraysEqual(num_col, ck_col_lower, col_lower)); + assert(doubleArraysEqual(num_col, ck_col_upper, col_upper)); + assert(doubleArraysEqual(num_row, ck_row_lower, row_lower)); + assert(doubleArraysEqual(num_row, ck_row_upper, row_upper)); + assert(highsIntArraysEqual(num_col, ck_a_start, a_start)); + assert(highsIntArraysEqual(num_nz, ck_a_index, a_index)); + assert(doubleArraysEqual(num_nz, ck_a_value, a_value)); + Highs_destroy(highs); } /* @@ -1731,7 +1874,7 @@ void test_setSolution() { HighsInt length = strlen(model_file0) + 1; char model_file[length]; strcpy(model_file, model_file0); - + if (dev_run) printf("\nSolving from scratch\n"); Highs_setBoolOptionValue(highs, "output_flag", dev_run); @@ -1751,9 +1894,9 @@ void test_setSolution() { HighsInt iteration_count1; Highs_getIntInfoValue(highs, "simplex_iteration_count", &iteration_count1); HighsInt logic = iteration_count0 > iteration_count1; - printf("Iteration counts are %d and %d\n", iteration_count0, iteration_count1); - assertLogical("Dual", logic); - + printf("Iteration counts are %d and %d\n", iteration_count0, +iteration_count1); assertLogical("Dual", logic); + Highs_destroy(highs); } */ @@ -1774,7 +1917,8 @@ int main() { test_getColsByRange(); test_passHessian(); test_ranging(); + test_feasibilityRelaxation(); test_getModel(); return 0; } - // test_setSolution(); +// test_setSolution(); diff --git a/check/TestCallbacks.cpp b/check/TestCallbacks.cpp index 171bf52721..d97bc0d271 100644 --- a/check/TestCallbacks.cpp +++ b/check/TestCallbacks.cpp @@ -121,9 +121,10 @@ HighsCallbackFunctionType userInterruptCallback = REQUIRE(local_callback_data == kUserCallbackNoData); } if (callback_type == kCallbackLogging) { - if (dev_run) - printf("userInterruptCallback(type %2d; data %2d): %s", - callback_type, local_callback_data, message.c_str()); + if (dev_run) printf("Callback: %s", message.c_str()); + // printf("userInterruptCallback(type %2d; data %2d): %s", + // callback_type, local_callback_data, + // message.c_str()); } else if (callback_type == kCallbackSimplexInterrupt) { if (dev_run) printf( @@ -190,12 +191,13 @@ std::functionmip_node_count, data_out->running_time, - data_out->mip_dual_bound, data_out->mip_primal_bound, - data_out->mip_gap, data_out->objective_function_value, - message.c_str()); + data_out->mip_node_count, data_out->mip_total_lp_iterations, + data_out->running_time, data_out->mip_dual_bound, + data_out->mip_primal_bound, data_out->mip_gap, + data_out->objective_function_value, message.c_str()); }; TEST_CASE("my-callback-logging", "[highs-callback]") { @@ -264,6 +266,21 @@ TEST_CASE("highs-callback-logging", "[highs-callback]") { highs.run(); } +TEST_CASE("highs-callback-solution-basis-logging", "[highs-callback]") { + std::string filename = std::string(HIGHS_DIR) + "/check/instances/avgas.mps"; + int user_callback_data = kUserCallbackData; + void* p_user_callback_data = + reinterpret_cast(static_cast(user_callback_data)); + Highs highs; + highs.setOptionValue("output_flag", dev_run); + highs.readModel(filename); + highs.run(); + highs.setCallback(userInterruptCallback, p_user_callback_data); + highs.startCallback(kCallbackLogging); + if (dev_run) highs.writeSolution("", kSolutionStylePretty); + if (dev_run) highs.writeBasis(""); +} + TEST_CASE("highs-callback-simplex-interrupt", "[highs-callback]") { std::string filename = std::string(HIGHS_DIR) + "/check/instances/adlittle.mps"; diff --git a/check/TestCheckSolution.cpp b/check/TestCheckSolution.cpp index c7ca67da2b..ff17e1bac6 100644 --- a/check/TestCheckSolution.cpp +++ b/check/TestCheckSolution.cpp @@ -229,6 +229,57 @@ TEST_CASE("check-set-mip-solution", "[highs_check_solution]") { highs.clear(); } + const bool test6 = other_tests; + if (test6) { + if (dev_run) + printf( + "\n***************************\nSolving from sparse integer " + "solution\n"); + HighsInt num_integer_variable = 0; + for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) + if (lp.integrality_[iCol] == HighsVarType::kInteger) + num_integer_variable++; + + highs.setOptionValue("output_flag", dev_run); + highs.readModel(model_file); + std::vector index; + std::vector value; + // Check that duplicate values are spotted + index.push_back(0); + value.push_back(0); + index.push_back(1); + value.push_back(1); + index.push_back(0); + value.push_back(2); + HighsInt num_entries = index.size(); + return_status = highs.setSolution(num_entries, index.data(), value.data()); + REQUIRE(return_status == HighsStatus::kWarning); + + index.clear(); + value.clear(); + std::vector is_set; + is_set.assign(lp.num_col_, false); + HighsInt num_to_set = 2; + assert(num_to_set > 0); + HighsRandom random; + for (HighsInt iSet = 0; iSet < num_to_set;) { + HighsInt iCol = random.integer(lp.num_col_); + if (lp.integrality_[iCol] != HighsVarType::kInteger) continue; + if (is_set[iCol]) continue; + is_set[iCol] = true; + index.push_back(iCol); + value.push_back(optimal_solution.col_value[iCol]); + iSet++; + } + num_entries = index.size(); + assert(num_entries == num_to_set); + return_status = highs.setSolution(num_entries, index.data(), value.data()); + REQUIRE(return_status == HighsStatus::kOk); + highs.run(); + REQUIRE(info.mip_node_count < scratch_num_nodes); + highs.clear(); + } + assert(other_tests); std::remove(solution_file.c_str()); } @@ -351,6 +402,7 @@ void runWriteReadCheckSolution(Highs& highs, const std::string model, HighsStatus return_status; std::string solution_file; HighsModelStatus status = HighsModelStatus::kNotset; + if (dev_run) printf("\nSolving model %s from scratch\n", model.c_str()); run_status = highs.run(); REQUIRE(run_status == HighsStatus::kOk); @@ -358,6 +410,9 @@ void runWriteReadCheckSolution(Highs& highs, const std::string model, REQUIRE(status == require_model_status); solution_file = model + ".sol"; + if (dev_run) + printf("Writing solution in style %d to %s\n", int(write_solution_style), + solution_file.c_str()); if (dev_run) return_status = highs.writeSolution("", write_solution_style); return_status = highs.writeSolution(solution_file, write_solution_style); REQUIRE(return_status == HighsStatus::kOk); @@ -367,6 +422,7 @@ void runWriteReadCheckSolution(Highs& highs, const std::string model, // primalDualInfeasible1Lp has no values in the solution file so, // after it's read, HiGHS::solution.value_valid is false + if (dev_run) printf("Reading solution from %s\n", solution_file.c_str()); return_status = highs.readSolution(solution_file); if (value_valid) { REQUIRE(return_status == HighsStatus::kOk); @@ -380,6 +436,13 @@ void runWriteReadCheckSolution(Highs& highs, const std::string model, } else { REQUIRE(return_status == HighsStatus::kError); } + if (dev_run) printf("Solving model from solution read from file\n"); + run_status = highs.run(); + REQUIRE(run_status == HighsStatus::kOk); + + status = highs.getModelStatus(); + REQUIRE(status == require_model_status); + std::remove(solution_file.c_str()); } @@ -389,7 +452,7 @@ void runSetLpSolution(const std::string model) { std::string model_file = std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps"; const HighsInfo& info = highs.getInfo(); - if (dev_run) printf("\nSolving from scratch\n"); + if (dev_run) printf("\nSolving %s from scratch\n", model.c_str()); highs.setOptionValue("output_flag", dev_run); if (dev_run) highs.setOptionValue("log_dev_level", 1); @@ -409,8 +472,8 @@ void runSetLpSolution(const std::string model) { REQUIRE(return_status == HighsStatus::kOk); highs.run(); - // Use a reduction in iteration count as a anity check that starting - // from the optimal solution has worked + // Use a reduction in iteration count as a sanity check that + // starting from the optimal solution has worked HighsInt simplex_iteration_count1 = info.simplex_iteration_count; if (dev_run) printf( @@ -419,4 +482,33 @@ void runSetLpSolution(const std::string model) { model.c_str(), (int)simplex_iteration_count0, (int)simplex_iteration_count1); REQUIRE(simplex_iteration_count1 < simplex_iteration_count0); + + // Now write a sparse solution, and read it in to hot start + HighsInt write_solution_style = kSolutionStyleSparse; + std::string solution_file = model + ".sol"; + if (dev_run) printf("Writing sparse solution to %s\n", solution_file.c_str()); + if (dev_run) return_status = highs.writeSolution(""); + return_status = highs.writeSolution(solution_file, write_solution_style); + REQUIRE(return_status == HighsStatus::kOk); + + highs.clear(); + highs.setOptionValue("output_flag", dev_run); + if (dev_run) highs.setOptionValue("log_dev_level", 1); + + highs.readModel(model_file); + if (dev_run) + printf("Reading sparse solution from %s\n", solution_file.c_str()); + return_status = highs.readSolution(solution_file); + REQUIRE(return_status == HighsStatus::kOk); + + if (dev_run) printf("Solving model from sparse solution read from file\n"); + HighsStatus run_status = highs.run(); + REQUIRE(run_status == HighsStatus::kOk); + + HighsModelStatus status = highs.getModelStatus(); + REQUIRE(status == HighsModelStatus::kOptimal); + + highs.clear(); + + std::remove(solution_file.c_str()); } diff --git a/check/TestFilereader.cpp b/check/TestFilereader.cpp index 14ad114b6e..915b73a25c 100644 --- a/check/TestFilereader.cpp +++ b/check/TestFilereader.cpp @@ -343,3 +343,17 @@ TEST_CASE("filereader-dD2e", "[highs_filereader]") { objective_value = highs.getInfo().objective_function_value; REQUIRE(objective_value == optimal_objective_value); } + +// TEST_CASE("filereader-comment", "[highs_filereader]") { +// // Check that comments - either whole line with * in first column, +// // or rest of line following */$ are handled correctly +// const double optimal_objective_value = -4; +// std::string model_file = +// std::string(HIGHS_DIR) + "/check/instances/comment.mps"; +// Highs highs; +// highs.setOptionValue("output_flag", dev_run); +// REQUIRE(highs.readModel(model_file) == HighsStatus::kOk); +// REQUIRE(highs.run() == HighsStatus::kOk); +// double objective_value = highs.getInfo().objective_function_value; +// REQUIRE(objective_value == optimal_objective_value); +// } diff --git a/check/TestIis.cpp b/check/TestIis.cpp new file mode 100644 index 0000000000..bc5c991b9b --- /dev/null +++ b/check/TestIis.cpp @@ -0,0 +1,465 @@ +#include +#include + +#include "HCheckConfig.h" +#include "Highs.h" +#include "catch.hpp" + +const bool dev_run = false; +const double inf = kHighsInf; + +void testIis(const std::string& model, const HighsIis& iis); + +void testMps(std::string& model, const HighsInt iis_strategy, + const HighsModelStatus require_model_status = + HighsModelStatus::kInfeasible); + +void testFeasibilityRelaxation( + std::string& model, const double lower_penalty, const double upper_penalty, + const double rhs_penalty, + const double require_feasibility_objective_function_value); + +TEST_CASE("lp-incompatible-bounds", "[iis]") { + // LP has row0 and col2 with inconsistent bounds. + // + // When prioritising rows, row0 and its constituent columns (1, 2) should be + // found + // + // When prioritising columns, col2 and its constituent rows (0, 1) should be + // found + HighsLp lp; + lp.num_col_ = 3; + lp.num_row_ = 2; + lp.col_cost_ = {0, 0, 0}; + lp.col_lower_ = {0, 0, 0}; + lp.col_upper_ = {1, 1, -1}; + lp.row_lower_ = {1, 0}; + lp.row_upper_ = {0, 1}; + lp.a_matrix_.format_ = MatrixFormat::kRowwise; + lp.a_matrix_.start_ = {0, 2, 4}; + lp.a_matrix_.index_ = {1, 2, 0, 2}; + lp.a_matrix_.value_ = {1, 1, 1, 1}; + Highs highs; + highs.setOptionValue("output_flag", dev_run); + highs.passModel(lp); + REQUIRE(highs.run() == HighsStatus::kOk); + REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible); + highs.setOptionValue("iis_strategy", kIisStrategyFromLpRowPriority); + HighsIis iis; + REQUIRE(highs.getIis(iis) == HighsStatus::kOk); + REQUIRE(iis.col_index_.size() == 0); + REQUIRE(iis.row_index_.size() == 1); + REQUIRE(iis.row_index_[0] == 0); + REQUIRE(iis.row_bound_[0] == kIisBoundStatusBoxed); + highs.setOptionValue("iis_strategy", kIisStrategyFromLpColPriority); + REQUIRE(highs.getIis(iis) == HighsStatus::kOk); + REQUIRE(iis.col_index_.size() == 1); + REQUIRE(iis.row_index_.size() == 0); + REQUIRE(iis.col_index_[0] == 2); + REQUIRE(iis.col_bound_[0] == kIisBoundStatusBoxed); +} + +TEST_CASE("lp-empty-infeasible-row", "[iis]") { + // Second row is empty, with bounds of [1, 2] + const HighsInt empty_row = 1; + HighsLp lp; + lp.num_col_ = 2; + lp.num_row_ = 3; + lp.col_cost_ = {0, 0}; + lp.col_lower_ = {0, 0}; + lp.col_upper_ = {inf, inf}; + lp.row_lower_ = {-inf, 1, -inf}; + lp.row_upper_ = {8, 2, 9}; + lp.a_matrix_.format_ = MatrixFormat::kRowwise; + lp.a_matrix_.start_ = {0, 2, 2, 4}; + lp.a_matrix_.index_ = {0, 1, 0, 1}; + lp.a_matrix_.value_ = {2, 1, 1, 3}; + Highs highs; + highs.setOptionValue("output_flag", dev_run); + highs.passModel(lp); + REQUIRE(highs.run() == HighsStatus::kOk); + REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible); + HighsIis iis; + REQUIRE(highs.getIis(iis) == HighsStatus::kOk); + REQUIRE(iis.col_index_.size() == 0); + REQUIRE(iis.row_index_.size() == 1); + REQUIRE(iis.row_index_[0] == empty_row); + REQUIRE(iis.row_bound_[0] == kIisBoundStatusLower); + REQUIRE(highs.changeRowBounds(empty_row, -2, -1) == HighsStatus::kOk); + REQUIRE(highs.run() == HighsStatus::kOk); + REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible); + REQUIRE(highs.getIis(iis) == HighsStatus::kOk); + REQUIRE(iis.col_index_.size() == 0); + REQUIRE(iis.row_index_.size() == 1); + REQUIRE(iis.row_index_[0] == empty_row); + REQUIRE(iis.row_bound_[0] == kIisBoundStatusUpper); +} + +TEST_CASE("lp-get-iis", "[iis]") { + HighsLp lp; + lp.num_col_ = 2; + lp.num_row_ = 3; + lp.col_cost_ = {0, 0}; + lp.col_lower_ = {0, 0}; + lp.col_upper_ = {inf, inf}; + lp.row_lower_ = {-inf, -inf, -inf}; + lp.row_upper_ = {8, 9, -2}; + lp.a_matrix_.format_ = MatrixFormat::kRowwise; + lp.a_matrix_.start_ = {0, 2, 4, 6}; + lp.a_matrix_.index_ = {0, 1, 0, 1, 0, 1}; + lp.a_matrix_.value_ = {2, 1, 1, 3, 1, 1}; + // lp.col_name_ = {"Col0", "Col1"}; + // lp.row_name_ = {"Row0", "Row1", "Row2"}; + Highs highs; + highs.setOptionValue("output_flag", dev_run); + highs.passModel(lp); + REQUIRE(highs.run() == HighsStatus::kOk); + REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible); + highs.setOptionValue("iis_strategy", kIisStrategyFromLpRowPriority); + HighsIis iis; + REQUIRE(highs.getIis(iis) == HighsStatus::kOk); + REQUIRE(iis.col_index_.size() == 2); + REQUIRE(iis.row_index_.size() == 1); + REQUIRE(iis.col_index_[0] == 0); + REQUIRE(iis.col_index_[1] == 1); + REQUIRE(iis.row_index_[0] == 2); +} + +TEST_CASE("lp-get-iis-woodinfe", "[iis]") { + std::string model = "woodinfe"; + testMps(model, kIisStrategyFromLpRowPriority); + // testMps(model, kIisStrategyFromRayRowPriority); +} + +TEST_CASE("lp-get-iis-galenet", "[iis]") { + // Dual ray corresponds to constraints + // + // r0: 0 <= c0 + c1 - c3 - c4 <=0 + // + // r1: 20 <= c2 + c3 + // + // r2: 30 <= c4 + // + // Where + // + // 0 <= c0 <= 10 + // + // 0 <= c1 <= 10 + // + // 0 <= c2 <= 2 + // + // 0 <= c3 <= 20 + // + // 0 <= c4 <= 30 + // + // This is infeasible since c4 >= 30 and c4 <= 30 fices c4 = 30, + // then c0 + c1 >= c3 + c4 >= 30 cannot be satisfied due to the + // upper bounds of 10 on these variables + // + // r1 can be removed and infeasibility is retained, but not r0 or r2 + // + // The upper bound on r0 can be removed + // + // The lower bounds on c0 and c1 can be removed, but not their upper + // bounds + // + // c2 can be removed, as it is empty once r1 is removed + // + // c3 can be removed, as the value of c4 is sufficient to make r0 + // infeasible + // + // The bounds on c4 can be removed, since it's the lower bound from + // r2 that makes r0 infeasible + // + // Hence only empty columns can be removed + std::string model = "galenet"; + testMps(model, kIisStrategyFromLpRowPriority); + // testMps(model, kIisStrategyFromRayRowPriority); +} + +TEST_CASE("lp-get-iis-avgas", "[iis]") { + std::string model = "avgas"; + // For the whole LP calculation the elasticity filter only + // identified feasibility, so the model status is not set + testMps(model, kIisStrategyFromLpRowPriority, HighsModelStatus::kNotset); + // For the ray calculation the model is solved, so its status is + // known + // testMps(model, kIisStrategyFromRayRowPriority, + // HighsModelStatus::kOptimal); +} + +TEST_CASE("lp-feasibility-relaxation", "[iis]") { + // Using infeasible LP from AMPL documentation + HighsLp lp; + lp.num_col_ = 2; + lp.num_row_ = 3; + lp.col_cost_ = {1, -2}; + lp.col_lower_ = {5, -inf}; + lp.col_upper_ = {inf, inf}; + lp.col_names_ = {"X", "Y"}; + lp.row_lower_ = {2, -inf, -inf}; + lp.row_upper_ = {inf, 1, 20}; + lp.row_names_ = {"R0", "R1", "R2"}; + lp.a_matrix_.start_ = {0, 3, 6}; + lp.a_matrix_.index_ = {0, 1, 2, 0, 1, 2}; + lp.a_matrix_.value_ = {-1, -3, 20, 21, 2, 1}; + lp.integrality_ = {HighsVarType::kInteger, HighsVarType::kInteger}; + Highs h; + h.setOptionValue("output_flag", dev_run); + const HighsSolution& solution = h.getSolution(); + h.passModel(lp); + // h.run(); + + const bool all_tests = false; + const bool test0 = false || all_tests; + const bool test1 = false || all_tests; + const bool test2 = false || all_tests; + const bool test3 = false || all_tests; + if (test0) { + // Vanilla feasibility relaxation + if (dev_run) + printf( + "==============================\n" + "Vanilla feasibility relaxation\n" + "==============================\n"); + REQUIRE(h.feasibilityRelaxation(1, 1, 1) == HighsStatus::kOk); + // Should get solution (1, 1) + h.writeSolution("", 1); + REQUIRE(solution.col_value[0] == 1); + REQUIRE(solution.col_value[1] == 1); + } + + if (test1) { + // Now we want to force all variable lower bounds to be respected + if (dev_run) + printf( + "========================\n" + "Respect all lower bounds\n" + "========================\n"); + h.feasibilityRelaxation(-1, 1, 1); + // Should get solution (5, 1) + h.writeSolution("", 1); + REQUIRE(solution.col_value[0] == 5); + REQUIRE(solution.col_value[1] == 1); + } + + if (test2) { + if (dev_run) + printf( + "===============================\n" + "Local penalties RHS {1, -1, 10}\n" + "===============================\n"); + // Now test local penalties + // + // constraint 0: normal weight + // + // constraint 1: cannot be violated + // + // constraint 2: rather not violate + // + std::vector local_rhs_penalty = {1, -1, 10}; + h.feasibilityRelaxation(1, 1, 0, nullptr, nullptr, + local_rhs_penalty.data()); + // Should get slacks (-3, 4, 0) + h.writeSolution("", 1); + REQUIRE(solution.row_value[0] == lp.row_lower_[0] - 3); + REQUIRE(solution.row_value[1] == lp.row_upper_[1] - 4); + REQUIRE(solution.row_value[2] == lp.row_upper_[2]); + } + + if (test3) { + if (dev_run) + printf( + "==============================\n" + "Local penalties RHS {10, 1, 1}\n" + "==============================\n"); + // + // constraint 0: rather not violate + // + // constraint 1: normal weight + // + // constraint 2: normal weight + // + std::vector local_rhs_penalty = {10, 1, 1}; + h.feasibilityRelaxation(1, 1, 0, nullptr, nullptr, + local_rhs_penalty.data()); + // Should get slacks (18, 2, -1) + REQUIRE(solution.row_value[0] == lp.row_lower_[0] + 18); + REQUIRE(solution.row_value[1] == lp.row_upper_[1] - 2); + REQUIRE(solution.row_value[2] == lp.row_upper_[2] + 1); + } + std::string model = "galenet"; + testFeasibilityRelaxation(model, 1, 1, 1, 28.0); + model = "woodinfe"; + testFeasibilityRelaxation(model, 1, 1, 1, 15.0); + model = "avgas"; + testFeasibilityRelaxation(model, 1, 1, 1, 0); +} + +void testIis(const std::string& model, const HighsIis& iis) { + HighsModelStatus model_status; + std::string model_file = + std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps"; + HighsInt num_iis_col = iis.col_index_.size(); + HighsInt num_iis_row = iis.row_index_.size(); + + Highs highs; + highs.setOptionValue("output_flag", false); + REQUIRE(highs.readModel(model_file) == HighsStatus::kOk); + const HighsLp& incumbent_lp = highs.getLp(); + HighsLp lp = highs.getLp(); + // Zero the objective + lp.col_cost_.assign(lp.num_col_, 0); + REQUIRE(highs.changeColsCost(0, lp.num_col_ - 1, lp.col_cost_.data()) == + HighsStatus::kOk); + + // Save the bounds + std::vector iis_col_lower; + std::vector iis_col_upper; + std::vector iis_row_lower; + std::vector iis_row_upper; + for (HighsInt iisCol = 0; iisCol < num_iis_col; iisCol++) { + HighsInt iCol = iis.col_index_[iisCol]; + iis_col_lower.push_back(lp.col_lower_[iCol]); + iis_col_upper.push_back(lp.col_upper_[iCol]); + } + for (HighsInt iisRow = 0; iisRow < num_iis_row; iisRow++) { + HighsInt iRow = iis.row_index_[iisRow]; + iis_row_lower.push_back(lp.row_lower_[iRow]); + iis_row_upper.push_back(lp.row_upper_[iRow]); + } + + // Free all the columns and rows + lp.col_lower_.assign(lp.num_col_, -inf); + lp.col_upper_.assign(lp.num_col_, inf); + lp.row_lower_.assign(lp.num_row_, -inf); + lp.row_upper_.assign(lp.num_row_, inf); + // Restore the bounds for the IIS columns and rows + for (HighsInt iisCol = 0; iisCol < num_iis_col; iisCol++) { + HighsInt iCol = iis.col_index_[iisCol]; + lp.col_lower_[iCol] = iis_col_lower[iisCol]; + lp.col_upper_[iCol] = iis_col_upper[iisCol]; + } + for (HighsInt iisRow = 0; iisRow < num_iis_row; iisRow++) { + HighsInt iRow = iis.row_index_[iisRow]; + lp.row_lower_[iRow] = iis_row_lower[iisRow]; + lp.row_upper_[iRow] = iis_row_upper[iisRow]; + } + + REQUIRE(highs.passModel(lp) == HighsStatus::kOk); + REQUIRE(highs.run() == HighsStatus::kOk); + REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible); + + for (HighsInt iisCol = 0; iisCol < num_iis_col; iisCol++) { + HighsInt iCol = iis.col_index_[iisCol]; + HighsInt iis_bound = iis.col_bound_[iisCol]; + const double lower = lp.col_lower_[iCol]; + const double upper = lp.col_upper_[iCol]; + double to_lower = lower; + double to_upper = upper; + REQUIRE(iis_bound != kIisBoundStatusDropped); + REQUIRE(iis_bound != kIisBoundStatusNull); + REQUIRE(iis_bound != kIisBoundStatusBoxed); + if (iis_bound == kIisBoundStatusLower) { + to_lower = -inf; + } else if (iis_bound == kIisBoundStatusUpper) { + to_upper = inf; + } else if (iis_bound == kIisBoundStatusFree) { + if (dev_run) + printf("IIS Col %2d (LP col %6d) status %s\n", int(iisCol), int(iCol), + iis.iisBoundStatusToString(iis_bound).c_str()); + continue; + } + REQUIRE(highs.changeColBounds(iCol, to_lower, to_upper) == + HighsStatus::kOk); + REQUIRE(highs.run() == HighsStatus::kOk); + model_status = highs.getModelStatus(); + if (dev_run) + printf( + "IIS Col %2d (LP col %6d) status %s: removal yields model status " + "%s\n", + int(iisCol), int(iCol), iis.iisBoundStatusToString(iis_bound).c_str(), + highs.modelStatusToString(model_status).c_str()); + REQUIRE(model_status == HighsModelStatus::kOptimal); + REQUIRE(highs.changeColBounds(iCol, lower, upper) == HighsStatus::kOk); + } + for (HighsInt iisRow = 0; iisRow < num_iis_row; iisRow++) { + HighsInt iRow = iis.row_index_[iisRow]; + HighsInt iis_bound = iis.row_bound_[iisRow]; + const double lower = lp.row_lower_[iRow]; + const double upper = lp.row_upper_[iRow]; + double to_lower = lower; + double to_upper = upper; + REQUIRE(iis_bound != kIisBoundStatusDropped); + REQUIRE(iis_bound != kIisBoundStatusNull); + REQUIRE(iis_bound != kIisBoundStatusFree); + REQUIRE(iis_bound != kIisBoundStatusBoxed); + if (iis_bound == kIisBoundStatusLower) { + to_lower = -inf; + } else if (iis_bound == kIisBoundStatusUpper) { + to_upper = inf; + } + REQUIRE(highs.changeRowBounds(iRow, to_lower, to_upper) == + HighsStatus::kOk); + REQUIRE(highs.run() == HighsStatus::kOk); + model_status = highs.getModelStatus(); + if (dev_run) + printf( + "IIS Row %2d (LP row %6d) status %s: removal yields model status " + "%s\n", + int(iisRow), int(iRow), iis.iisBoundStatusToString(iis_bound).c_str(), + highs.modelStatusToString(model_status).c_str()); + // if (model_status != HighsModelStatus::kOptimal) + // highs.writeSolution("", kSolutionStylePretty); + REQUIRE(model_status == HighsModelStatus::kOptimal); + REQUIRE(highs.changeRowBounds(iRow, lower, upper) == HighsStatus::kOk); + } +} + +void testMps(std::string& model, const HighsInt iis_strategy, + const HighsModelStatus require_model_status) { + std::string model_file = + std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps"; + Highs highs; + highs.setOptionValue("output_flag", dev_run); + + REQUIRE(highs.readModel(model_file) == HighsStatus::kOk); + // if (iis_strategy == kIisStrategyFromRayRowPriority || + // iis_strategy == kIisStrategyFromRayColPriority) { + // // For a ray strategy, solve the LP first + // REQUIRE(highs.run() == HighsStatus::kOk); + // } + highs.setOptionValue("iis_strategy", iis_strategy); + HighsIis iis; + REQUIRE(highs.getIis(iis) == HighsStatus::kOk); + HighsInt num_iis_col = iis.col_index_.size(); + HighsInt num_iis_row = iis.row_index_.size(); + HighsModelStatus model_status = highs.getModelStatus(); + REQUIRE(model_status == require_model_status); + if (model_status == HighsModelStatus::kInfeasible) { + REQUIRE(num_iis_col > 0); + REQUIRE(num_iis_row > 0); + if (dev_run) + printf("Model %s has IIS with %d columns and %d rows\n", model.c_str(), + int(num_iis_col), int(num_iis_row)); + testIis(model, iis); + } else { + REQUIRE(num_iis_col == 0); + REQUIRE(num_iis_row == 0); + } +} + +void testFeasibilityRelaxation( + std::string& model, const double lower_penalty, const double upper_penalty, + const double rhs_penalty, + const double require_feasibility_objective_function_value) { + std::string model_file = + std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps"; + Highs h; + h.setOptionValue("output_flag", dev_run); + h.readModel(model_file); + REQUIRE(h.feasibilityRelaxation(lower_penalty, upper_penalty, rhs_penalty) == + HighsStatus::kOk); + REQUIRE(h.getInfo().objective_function_value == + require_feasibility_objective_function_value); +} diff --git a/check/TestIpm.cpp b/check/TestIpm.cpp index 911d01a67a..cb2d1a3419 100644 --- a/check/TestIpm.cpp +++ b/check/TestIpm.cpp @@ -81,3 +81,56 @@ TEST_CASE("test-analytic-centre-box", "[highs_ipm]") { REQUIRE(solution_norm < 1e-6); if (dev_run) printf("Analytic centre solution norm is %g\n", solution_norm); } + +TEST_CASE("test-1966", "[highs_ipm]") { + Highs highs; + highs.setOptionValue("output_flag", dev_run); + const HighsInfo& info = highs.getInfo(); + HighsLp lp; + lp.num_col_ = 2; + lp.num_row_ = 2; + lp.col_cost_ = {2, -1}; + lp.col_lower_ = {0, 0}; + lp.col_upper_ = {kHighsInf, kHighsInf}; + lp.row_lower_ = {-kHighsInf, 2}; + lp.row_upper_ = {1, kHighsInf}; + lp.a_matrix_.start_ = {0, 2, 4}; + lp.a_matrix_.index_ = {0, 1, 0, 1}; + lp.a_matrix_.value_ = {1, -1, 1, -1}; + highs.passModel(lp); + highs.setOptionValue("solver", kIpmString); + highs.setOptionValue("presolve", kHighsOffString); + + if (dev_run) printf("\nWith default residual tolerances\n"); + highs.run(); + if (dev_run) { + highs.writeSolution("", kSolutionStylePretty); + printf("Num primal infeasibilities = %d\n", + int(info.num_primal_infeasibilities)); + printf("Max primal infeasibility = %g\n", info.max_primal_infeasibility); + printf("Sum primal infeasibilities = %g\n", + info.sum_primal_infeasibilities); + printf("Num dual infeasibilities = %d\n", + int(info.num_dual_infeasibilities)); + printf("Max dual infeasibility = %g\n", info.max_dual_infeasibility); + printf("Sum dual infeasibilities = %g\n", info.sum_dual_infeasibilities); + } + highs.clearSolver(); + + if (dev_run) printf("\nWith infinite residual tolerances\n"); + highs.setOptionValue("primal_residual_tolerance", 1e30); + highs.setOptionValue("dual_residual_tolerance", 1e30); + highs.run(); + if (dev_run) { + highs.writeSolution("", kSolutionStylePretty); + printf("Num primal infeasibilities = %d\n", + int(info.num_primal_infeasibilities)); + printf("Max primal infeasibility = %g\n", info.max_primal_infeasibility); + printf("Sum primal infeasibilities = %g\n", + info.sum_primal_infeasibilities); + printf("Num dual infeasibilities = %d\n", + int(info.num_dual_infeasibilities)); + printf("Max dual infeasibility = %g\n", info.max_dual_infeasibility); + printf("Sum dual infeasibilities = %g\n", info.sum_dual_infeasibilities); + } +} diff --git a/check/TestLpModification.cpp b/check/TestLpModification.cpp index b34cf18cae..ea55d16264 100644 --- a/check/TestLpModification.cpp +++ b/check/TestLpModification.cpp @@ -1937,3 +1937,20 @@ TEST_CASE("modify-empty-model", "[highs_data]") { REQUIRE(highs.changeColBounds(0, 1, 1) == HighsStatus::kError); REQUIRE(highs.changeRowBounds(0, 1, 1) == HighsStatus::kError); } + +TEST_CASE("zero-matrix-entries", "[highs_data]") { + Highs highs; + highs.setOptionValue("output_flag", dev_run); + HighsLp lp; + lp.num_col_ = 2; + lp.num_row_ = 2; + lp.col_cost_ = {0, 0}; + lp.col_lower_ = {0, 0}; + lp.col_upper_ = {1, 1}; + lp.row_lower_ = {-kHighsInf, -kHighsInf}; + lp.row_upper_ = {5, 8}; + lp.a_matrix_.start_ = {0, 2, 4}; + lp.a_matrix_.index_ = {0, 1, 0, 1}; + lp.a_matrix_.value_ = {1, 0, 0, 1}; + REQUIRE(highs.passModel(lp) == HighsStatus::kOk); +} diff --git a/check/TestLpSolvers.cpp b/check/TestLpSolvers.cpp index 5202a1dee5..572a478cf2 100644 --- a/check/TestLpSolvers.cpp +++ b/check/TestLpSolvers.cpp @@ -438,3 +438,35 @@ TEST_CASE("dual-objective-upper-bound", "[highs_lp_solver]") { if (dev_run) printf("\nOptimal objective value error = %g\n", error); REQUIRE(error < 1e-10); } + +TEST_CASE("blending-lp-ipm", "[highs_lp_solver]") { + Highs highs; + highs.setOptionValue("output_flag", dev_run); + HighsLp lp; + lp.num_col_ = 2; + lp.num_row_ = 2; + lp.col_cost_ = {-8, -10}; + lp.col_lower_ = {0, 0}; + lp.col_upper_ = {kHighsInf, kHighsInf}; + lp.row_lower_ = {-kHighsInf, -kHighsInf}; + lp.row_upper_ = {80, 120}; + lp.a_matrix_.start_ = {0, 2, 4}; + lp.a_matrix_.index_ = {0, 1, 0, 1}; + lp.a_matrix_.value_ = {1, 1, 2, 4}; + highs.passModel(lp); + highs.setOptionValue("solver", kIpmString); + highs.setOptionValue("presolve", kHighsOffString); + highs.run(); + HighsInfo info = highs.getInfo(); + if (dev_run) { + printf("Num primal infeasibilities = %d\n", + int(info.num_primal_infeasibilities)); + printf("Max primal infeasibility = %g\n", info.max_primal_infeasibility); + printf("Sum primal infeasibilities = %g\n", + info.sum_primal_infeasibilities); + printf("Num dual infeasibilities = %d\n", + int(info.num_dual_infeasibilities)); + printf("Max dual infeasibility = %g\n", info.max_dual_infeasibility); + printf("Sum dual infeasibilities = %g\n", info.sum_dual_infeasibilities); + } +} diff --git a/check/TestLpValidation.cpp b/check/TestLpValidation.cpp index bb0f71c1db..473fdeb681 100644 --- a/check/TestLpValidation.cpp +++ b/check/TestLpValidation.cpp @@ -94,7 +94,7 @@ TEST_CASE("LP-dimension-validation", "[highs_data]") { // Yields duplicate index, but values are still zero, so both are // discarded and a warning is returned lp.a_matrix_.index_[0] = 0; - REQUIRE(highs.passModel(lp) == HighsStatus::kWarning); + REQUIRE(highs.passModel(lp) == HighsStatus::kOk); if (dev_run) printf("Give nonzero a_matrix_.value_[0] and a_matrix_.value_[1]\n"); diff --git a/check/TestNames.cpp b/check/TestNames.cpp index bddecfdc4f..4aceee0ca6 100644 --- a/check/TestNames.cpp +++ b/check/TestNames.cpp @@ -118,3 +118,15 @@ TEST_CASE("highs-names", "[highs_names]") { std::remove(solution_file.c_str()); } + +TEST_CASE("highs-model-name", "[model_names]") { + Highs highs; + const HighsLp& lp = highs.getLp(); + + std::string name = lp.model_name_; + REQUIRE(name == ""); + + highs.passModelName("new_name"); + name = lp.model_name_; + REQUIRE(name == "new_name"); +} diff --git a/check/TestRays.cpp b/check/TestRays.cpp index 0b9f6ecfbf..5a42d19bc5 100644 --- a/check/TestRays.cpp +++ b/check/TestRays.cpp @@ -303,6 +303,8 @@ void testUnboundedMpsLp(const std::string model, // Test dual ray for unbounded LP model_file = std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps"; require_model_status = HighsModelStatus::kUnbounded; + // gas11 contains small nonzero matrix entries, so readModel yield + // HighsStatus::kWarning require_status = model == "gas11" ? HighsStatus::kWarning : HighsStatus::kOk; REQUIRE(highs.readModel(model_file) == require_status); REQUIRE(highs.changeObjectiveSense(sense) == HighsStatus::kOk); diff --git a/check/instances/comment.mps b/check/instances/comment.mps new file mode 100644 index 0000000000..96d7c80985 --- /dev/null +++ b/check/instances/comment.mps @@ -0,0 +1,23 @@ +* Optimal objective is -4 +NAME Comment +* Comment +ROWS + N COST $Comment + G R0 + G R1 *Comment + G R2 $Comment +COLUMNS + C0 R0 1.0 COST -1.0 $Comment + C0 R1 -1.0 $Comment + C0 R2 -1.0 $Comment + C1 R0 1.0 COST -2.0 *Comment + C1 R1 -1.0 *Comment + C1 R2 -1.0 *Comment +RHS + DEMANDS R0 1.0 R1 -2.0 $Comment + DEMANDS R2 -2.0 $Comment +RANGES + test R0 1 $Comment +BOUNDS + UP SERVINGS C0 1.0 $Comment +ENDATA diff --git a/check/matrix_multiplication.hpp b/check/matrix_multiplication.hpp index 5db3303f81..0d850b15e4 100644 --- a/check/matrix_multiplication.hpp +++ b/check/matrix_multiplication.hpp @@ -1,13 +1,13 @@ -#include +#include +#include #include -#include #include -#include +#include +#include #include -#include +#include #include -#include -#include +#include extern int N; extern double **a, **b, **c; @@ -20,7 +20,7 @@ inline void allocate_matrix() { a = static_cast(std::malloc(N * sizeof(double*))); b = static_cast(std::malloc(N * sizeof(double*))); c = static_cast(std::malloc(N * sizeof(double*))); - for(int i=0; i(std::malloc(N * sizeof(double))); b[i] = static_cast(std::malloc(N * sizeof(double))); c[i] = static_cast(std::malloc(N * sizeof(double))); @@ -28,7 +28,7 @@ inline void allocate_matrix() { } inline void deallocate_matrix() { - for(int i=0; i "parallel.md", + "Solvers" => "solvers.md", "Terminology" => "terminology.md", ], ) diff --git a/docs/src/executable.md b/docs/src/executable.md index d9f1777555..b82003127c 100644 --- a/docs/src/executable.md +++ b/docs/src/executable.md @@ -1,4 +1,4 @@ -# Executable +# [Executable](@id executable) For convenience, the executable is assumed to be `bin/highs`. diff --git a/docs/src/index.md b/docs/src/index.md index 59e799a4da..2e0e65a03b 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -39,7 +39,7 @@ Get started by following [Install HiGHS](@ref). ## Overview -The standalone [Executable](@ref) allows models to be solved from +The standalone [executable](@ref executable) allows models to be solved from [MPS](https://en.wikipedia.org/wiki/MPS_(format)) or (CPLEX) [LP](https://web.mit.edu/lpsolve/doc/CPLEX-format.htm) files, with full control of the HiGHS run-time options, and the solution can be written to files in human @@ -54,10 +54,12 @@ calls. These can be studied via the [C++ header file](https://github.com/ERGO-Co The C interface cannot make use of the C++ structures and enums, and its methods are documented [explicitly](@ref c-api). -## Solution algorithms +## Solvers -For LPs, HiGHS has implementations of both the revised simplex and interior -point methods. MIPs are solved by branch-and-cut, and QPs by active set. +For LPs, HiGHS has implementations of the revised simplex method, +interior point method, and PDLP first order method. MIPs are solved by +branch-and-cut, and QPs by active set. More information on the HiGHS +solvers is [available](@ref solvers). ## Citing HiGHS diff --git a/docs/src/interfaces/csharp.md b/docs/src/interfaces/csharp.md index dceed6b00d..5d492d9baf 100644 --- a/docs/src/interfaces/csharp.md +++ b/docs/src/interfaces/csharp.md @@ -17,7 +17,7 @@ The nuget package Highs.Native is on https://www.nuget.org, at https://www.nuget It can be added to your C# project with `dotnet` ```shell -dotnet add package Highs.Native --version 1.7.2 +dotnet add package Highs.Native --version 1.8.0 ``` The nuget package contains runtime libraries for diff --git a/docs/src/options/definitions.md b/docs/src/options/definitions.md index 2505c4d3c3..31a0eb128d 100644 --- a/docs/src/options/definitions.md +++ b/docs/src/options/definitions.md @@ -5,7 +5,7 @@ - Type: string - Default: "choose" -## solver +## [solver](@id option-solver) - Solver option: "simplex", "choose", "ipm" or "pdlp". If "simplex"/"ipm"/"pdlp" is chosen then, for a MIP (QP) the integrality constraint (quadratic term) will be ignored - Type: string - Default: "choose" @@ -109,7 +109,7 @@ - Range: {-2147483647, 2147483647} - Default: 0 -## simplex\_strategy +## [simplex\_strategy](@id option-simplex_strategy) - Strategy for simplex solver 0 => Choose; 1 => Dual (serial); 2 => Dual (PAMI); 3 => Dual (SIP); 4 => Primal - Type: integer - Range: {0, 4} diff --git a/docs/src/options/intro.md b/docs/src/options/intro.md index ff33cae234..81f0e64246 100644 --- a/docs/src/options/intro.md +++ b/docs/src/options/intro.md @@ -3,12 +3,12 @@ The options that control HiGHS are of four types: `boolean`, `integer`, `double` and `string`. Their values can be specified: - * via the command line when running the [Executable](@ref) + * via the command line when running the [Executable](@ref executable) * via method calls when running HiGHS in an application. ## Options file -When running the [Executable](@ref) via the command line, some options values +When running the [Executable](@ref executable) via the command line, some options values can be set explicitly in the command, and all options can be set by means of an options file. diff --git a/docs/src/parallel.md b/docs/src/parallel.md index 3a857629e3..016834ab98 100644 --- a/docs/src/parallel.md +++ b/docs/src/parallel.md @@ -20,7 +20,7 @@ has a variant allowing concurrent processing. This variant is used when the [parallel](@ref) option is set "on", by specifying `--parallel` when running the -[executable](@ref Executable) via +[executable](@ref executable) via the command line, or by setting it via a library call in an application. diff --git a/docs/src/solvers.md b/docs/src/solvers.md new file mode 100644 index 0000000000..6734ec694a --- /dev/null +++ b/docs/src/solvers.md @@ -0,0 +1,60 @@ +# [Solvers](@id solvers) + +## LP + +HiGHS has implementations of the three main solution techniques for +LP. HiGHS will choose the most appropriate technique for a given +problem, but this can be over-ridden by setting the option +[__solver__](@ref option-solver). + +#### Simplex + +HiGHS has efficient implementations of both the primal and dual +simplex methods, although the dual simplex solver is likely to be +faster and is more robust, so is used by default. The novel features +of the dual simplex solver are described in + +_Parallelizing the dual revised simplex method_, Q. Huangfu and +J. A. J. Hall, Mathematical Programming Computation, 10 (1), 119-142, +2018 [DOI: +10.1007/s12532-017-0130-5](https://link.springer.com/article/10.1007/s12532-017-0130-5). + +* Setting the option [__solver__](@ref option-solver) to "simplex" forces the simplex solver to be used +* The option [__simplex\_strategy__](@ref option-simplex_strategy) + determines whether the primal solver or one of the parallel solvers is + to be used. + +#### Interior point + +HiGHS has one interior point (IPM) solver based on the preconditioned conjugate gradient method, as discussed in + +_Implementation of an interior point method with basis +preconditioning_, Mathematical Programming Computation, 12, 603-635, 2020. [DOI: +10.1007/s12532-020-00181-8](https://link.springer.com/article/10.1007/s12532-020-00181-8). + +This solver is serial. An interior point solver based on direct factorization is being developed. + +Setting the option [__solver__](@ref option-solver) to "ipm" forces the IPM solver to be used + +#### Primal-dual hybrid gradient method + +HiGHS includes the [cuPDLP-C](https://github.com/COPT-Public/cuPDLP-C) +primal-dual hybrid gradient method for LP (PDLP). Currently this only +runs on CPU, so it is unlikely to be competitive with the HiGHS +interior point or simplex solvers. Enabling HiGHS to run PDLP on a GPU +is work in progress. + +Setting the option [__solver__](@ref option-solver) to "pdlp" forces the PDLP solver to be used + +## MIP + +The HiGHS MIP solver uses established branch-and-cut techniques + +## QP + +The HiGHS solver for convex QP problems uses an established primal +active set method. The new interior point solver will also be able to +solve convex QP problems. + + + diff --git a/docs/src/structures/classes/HighsHessian.md b/docs/src/structures/classes/HighsHessian.md new file mode 100644 index 0000000000..5e165ff8ee --- /dev/null +++ b/docs/src/structures/classes/HighsHessian.md @@ -0,0 +1,9 @@ +# HighsHessian + +A Hessian matrix is communicated via an instance of the HighsHessian class. + +- dim_: Scalar of type integer - Dimension of the Hessian +- format\_: Scalar of [HessianFormat](@ref) type - Format of the Hessian +- start\_: Vector of integer type - Start of each compressed column in the Hessian +- index\_: Vector of integer type - Indices of the nonzeros in the Hessian +- value\_: Vector of double type - Values of the nonzeros in the Hessian diff --git a/docs/src/structures/classes/HighsModel.md b/docs/src/structures/classes/HighsModel.md new file mode 100644 index 0000000000..21aa4c1e80 --- /dev/null +++ b/docs/src/structures/classes/HighsModel.md @@ -0,0 +1,7 @@ +# HighsModel + +A QP model is communicated via an instance of the HighsModel class + +- lp\_: Instance of [HighsLp](@ref) class - LP components of the model + +- hessian\_: Instance of [HighsHessian](@ref) class - Hessian matrix diff --git a/docs/src/structures/classes/HighsSparseMatrix.md b/docs/src/structures/classes/HighsSparseMatrix.md index fdda98936d..411c9bcc1e 100644 --- a/docs/src/structures/classes/HighsSparseMatrix.md +++ b/docs/src/structures/classes/HighsSparseMatrix.md @@ -2,9 +2,9 @@ The constraint matrix of an LP model is communicated via an instance of the HighsSparseMatrix class -- format\_: Scalar of MatrixFormat type - Format of the matrix +- format\_: Scalar of [MatrixFormat](@ref) type - Format of the matrix - num\_col\_ : Scalar of integer type - Number of columns in the matrix - num\_row\_: Scalar of integer type - Number of rows in the matrix -- start\_: Vector of integer type - Start of each compressed vector in the matrixs +- start\_: Vector of integer type - Start of each compressed vector in the matrix - index\_: Vector of integer type - Indices of the nonzeros in the matrix - value\_: Vector of double type - Values of the nonzeros in the matrix diff --git a/docs/src/structures/classes/index.md b/docs/src/structures/classes/index.md index b907914083..f2f5ed4326 100644 --- a/docs/src/structures/classes/index.md +++ b/docs/src/structures/classes/index.md @@ -4,6 +4,8 @@ The data members of fundamental classes in HiGHS are defined in this section. * [HighsSparseMatrix](@ref) * [HighsLp](@ref) + * [HighsHessian](@ref) + * [HighsModel](@ref) * [HighsSolution](@ref) * [HighsBasis](@ref) * [HighsInfo](@ref) diff --git a/docs/src/structures/enums.md b/docs/src/structures/enums.md index 7975e41fb0..d88306fd15 100644 --- a/docs/src/structures/enums.md +++ b/docs/src/structures/enums.md @@ -41,6 +41,13 @@ This defines the feasible values of a variable within a model: * `kSemiContinuous`: The variable must be zero or take continuous values between its bounds * `kSemiInteger`: The variable must be zero or take integer values between its bounds +## HessianFormat + +This defines the format of a [HighsHessian](@ref): + + * `kTriangular`: The lower triangular component of the Hessian is stored column-wise or, equivalently, the upper triangular component is stored row-wise + * `kSquare`: The whole Hessian ``Q`` is stored column-wise. This is for input only: internally the lower triangular component of ``(Q+Q^T)/2`` will be stored + ## SolutionStatus This defines the nature of any primal or dual solution information: diff --git a/docs/src/terminology.md b/docs/src/terminology.md index fba4e7849c..4faabb40bd 100644 --- a/docs/src/terminology.md +++ b/docs/src/terminology.md @@ -139,3 +139,4 @@ relative to the primal bound is a better measure. When the gap reaches zero then the MIP is solved to optimality. However, it is often preferable to stop the MIP solver when the relative gap is below a specified tolerance. + diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4427f4b487..91857d4098 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,7 +5,7 @@ endif() if(BUILD_CXX_EXAMPLE) file(GLOB CXX_SRCS "*.cpp") foreach(FILE_NAME IN LISTS CXX_SRCS) - add_cxx_test(${FILE_NAME}) + highs_cxx_test(${FILE_NAME}) endforeach() endif() @@ -21,6 +21,6 @@ if(BUILD_CXX_EXAMPLE) # set(C_SRCS "call_highs_from_c_minimal.c") foreach(FILE_NAME IN LISTS C_SRCS) message(${FILE_NAME}) - add_c_test(${FILE_NAME}) + highs_c_test(${FILE_NAME}) endforeach() endif() diff --git a/examples/call_highs_from_c.c b/examples/call_highs_from_c.c index 6b89169f03..fe0990dfc9 100644 --- a/examples/call_highs_from_c.c +++ b/examples/call_highs_from_c.c @@ -1,11 +1,12 @@ -#include "interfaces/highs_c_api.h" - -#include -#include #include #include +#include +#include + +#include "interfaces/highs_c_api.h" -// gcc call_highs_from_c.c -o highstest -I install_folder/include/ -L install_folder/lib/ -lhighs +// gcc call_highs_from_c.c -o highstest -I install_folder/include/ -L +// install_folder/lib/ -lhighs void minimal_api() { // This illustrates the use of Highs_lpCall, the simple C interface to @@ -96,75 +97,87 @@ void minimal_api() { HighsInt model_status; HighsInt run_status; - run_status = Highs_lpCall(num_col, num_row, num_nz, a_format, - sense, offset, col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, - col_value, col_dual, row_value, row_dual, - col_basis_status, row_basis_status, - &model_status); + run_status = + Highs_lpCall(num_col, num_row, num_nz, a_format, sense, offset, col_cost, + col_lower, col_upper, row_lower, row_upper, a_start, a_index, + a_value, col_value, col_dual, row_value, row_dual, + col_basis_status, row_basis_status, &model_status); // The run must be successful, and the model status optimal assert(run_status == kHighsStatusOk); assert(model_status == kHighsModelStatusOptimal); - printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT "\n", run_status, model_status); + printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT + "\n", + run_status, model_status); objective_value = offset; // Report the column primal and dual values, and basis status for (HighsInt i = 0; i < num_col; i++) { - printf("Col%" HIGHSINT_FORMAT " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", i, col_value[i], col_dual[i], col_basis_status[i]); - objective_value += col_value[i]*col_cost[i]; + printf("Col%" HIGHSINT_FORMAT + " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", + i, col_value[i], col_dual[i], col_basis_status[i]); + objective_value += col_value[i] * col_cost[i]; } // Report the row primal and dual values, and basis status for (HighsInt i = 0; i < num_row; i++) { - printf("Row%" HIGHSINT_FORMAT " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", i, row_value[i], row_dual[i], row_basis_status[i]); + printf("Row%" HIGHSINT_FORMAT + " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", + i, row_value[i], row_dual[i], row_basis_status[i]); } printf("Optimal objective value = %g\n", objective_value); // Switch the sense to maximization and solve the LP again sense = kHighsObjSenseMaximize; - run_status = Highs_lpCall(num_col, num_row, num_nz, a_format, - sense, offset, col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, - col_value, col_dual, row_value, row_dual, - col_basis_status, row_basis_status, - &model_status); + run_status = + Highs_lpCall(num_col, num_row, num_nz, a_format, sense, offset, col_cost, + col_lower, col_upper, row_lower, row_upper, a_start, a_index, + a_value, col_value, col_dual, row_value, row_dual, + col_basis_status, row_basis_status, &model_status); // The run must be successful, and the model status optimal assert(run_status == kHighsStatusOk); assert(model_status == kHighsModelStatusOptimal); - printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT "\n", run_status, model_status); + printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT + "\n", + run_status, model_status); // Compute the objective value objective_value = offset; - for (HighsInt i = 0; i < num_col; i++) objective_value += col_value[i]*col_cost[i]; + for (HighsInt i = 0; i < num_col; i++) + objective_value += col_value[i] * col_cost[i]; // Report the column primal and dual values, and basis status for (HighsInt i = 0; i < num_col; i++) { - printf("Col%" HIGHSINT_FORMAT " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", i, col_value[i], col_dual[i], col_basis_status[i]); + printf("Col%" HIGHSINT_FORMAT + " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", + i, col_value[i], col_dual[i], col_basis_status[i]); } // Report the row primal and dual values, and basis status for (HighsInt i = 0; i < num_row; i++) { - printf("Row%" HIGHSINT_FORMAT " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", i, row_value[i], row_dual[i], row_basis_status[i]); + printf("Row%" HIGHSINT_FORMAT + " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", + i, row_value[i], row_dual[i], row_basis_status[i]); } printf("Optimal objective value = %g\n", objective_value); - // + // // Indicate that the optimal solution for both columns must be // integer valued and solve the model as a MIP HighsInt integrality[2] = {1, 1}; - run_status = Highs_mipCall(num_col, num_row, num_nz, a_format, - sense, offset, col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, - integrality, - col_value, row_value, - &model_status); + run_status = Highs_mipCall(num_col, num_row, num_nz, a_format, sense, offset, + col_cost, col_lower, col_upper, row_lower, + row_upper, a_start, a_index, a_value, integrality, + col_value, row_value, &model_status); // The run must be successful, and the model status optimal assert(run_status == kHighsStatusOk); assert(model_status == kHighsModelStatusOptimal); - printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT "\n", run_status, model_status); + printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT + "\n", + run_status, model_status); // Compute the objective value objective_value = offset; - for (HighsInt i = 0; i < num_col; i++) objective_value += col_value[i]*col_cost[i]; + for (HighsInt i = 0; i < num_col; i++) + objective_value += col_value[i] * col_cost[i]; // Report the column primal values for (HighsInt i = 0; i < num_col; i++) { printf("Col%" HIGHSINT_FORMAT " = %lf\n", i, col_value[i]); @@ -189,7 +202,7 @@ void minimal_api_qp() { // minimize -x_2 + (1/2)(2x_1^2 - 2x_1x_3 + 0.2x_2^2 + 2x_3^2) // // subject to x_1 + x_2 + x_3 >= 1; x>=0 - + const HighsInt num_col = 3; const HighsInt num_row = 1; const HighsInt num_nz = 3; @@ -228,45 +241,50 @@ void minimal_api_qp() { HighsInt model_status; HighsInt run_status; - run_status = Highs_qpCall(num_col, num_row, num_nz, q_num_nz, a_format, q_format, - sense, offset, col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, - q_start, q_index, q_value, - col_value, col_dual, row_value, row_dual, - col_basis_status, row_basis_status, - &model_status); + run_status = Highs_qpCall( + num_col, num_row, num_nz, q_num_nz, a_format, q_format, sense, offset, + col_cost, col_lower, col_upper, row_lower, row_upper, a_start, a_index, + a_value, q_start, q_index, q_value, col_value, col_dual, row_value, + row_dual, col_basis_status, row_basis_status, &model_status); // The run must be successful, and the model status optimal assert(run_status == kHighsStatusOk); assert(model_status == kHighsModelStatusOptimal); - printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT "\n", run_status, model_status); + printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT + "\n", + run_status, model_status); // Compute the objective value objective_value = offset; - for (HighsInt i = 0; i < num_col; i++) objective_value += col_value[i]*col_cost[i]; + for (HighsInt i = 0; i < num_col; i++) + objective_value += col_value[i] * col_cost[i]; for (HighsInt i = 0; i < num_col; i++) { HighsInt from_el = q_start[i]; HighsInt to_el; - if (i+1 -#include #include #include +#include +#include + +#include "interfaces/highs_c_api.h" -// gcc call_highs_from_c.c -o highstest -I install_folder/include/ -L install_folder/lib/ -lhighs +// gcc call_highs_from_c.c -o highstest -I install_folder/include/ -L +// install_folder/lib/ -lhighs void minimal_api() { // This illustrates the use of Highs_lpCall, the simple C interface to @@ -96,75 +97,87 @@ void minimal_api() { HighsInt model_status; HighsInt run_status; - run_status = Highs_lpCall(num_col, num_row, num_nz, a_format, - sense, offset, col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, - col_value, col_dual, row_value, row_dual, - col_basis_status, row_basis_status, - &model_status); + run_status = + Highs_lpCall(num_col, num_row, num_nz, a_format, sense, offset, col_cost, + col_lower, col_upper, row_lower, row_upper, a_start, a_index, + a_value, col_value, col_dual, row_value, row_dual, + col_basis_status, row_basis_status, &model_status); // The run must be successful, and the model status optimal assert(run_status == kHighsStatusOk); assert(model_status == kHighsModelStatusOptimal); - printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT "\n", run_status, model_status); + printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT + "\n", + run_status, model_status); objective_value = offset; // Report the column primal and dual values, and basis status for (HighsInt i = 0; i < num_col; i++) { - printf("Col%" HIGHSINT_FORMAT " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", i, col_value[i], col_dual[i], col_basis_status[i]); - objective_value += col_value[i]*col_cost[i]; + printf("Col%" HIGHSINT_FORMAT + " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", + i, col_value[i], col_dual[i], col_basis_status[i]); + objective_value += col_value[i] * col_cost[i]; } // Report the row primal and dual values, and basis status for (HighsInt i = 0; i < num_row; i++) { - printf("Row%" HIGHSINT_FORMAT " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", i, row_value[i], row_dual[i], row_basis_status[i]); + printf("Row%" HIGHSINT_FORMAT + " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", + i, row_value[i], row_dual[i], row_basis_status[i]); } printf("Optimal objective value = %g\n", objective_value); // Switch the sense to maximization and solve the LP again sense = kHighsObjSenseMaximize; - run_status = Highs_lpCall(num_col, num_row, num_nz, a_format, - sense, offset, col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, - col_value, col_dual, row_value, row_dual, - col_basis_status, row_basis_status, - &model_status); + run_status = + Highs_lpCall(num_col, num_row, num_nz, a_format, sense, offset, col_cost, + col_lower, col_upper, row_lower, row_upper, a_start, a_index, + a_value, col_value, col_dual, row_value, row_dual, + col_basis_status, row_basis_status, &model_status); // The run must be successful, and the model status optimal assert(run_status == kHighsStatusOk); assert(model_status == kHighsModelStatusOptimal); - printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT "\n", run_status, model_status); + printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT + "\n", + run_status, model_status); // Compute the objective value objective_value = offset; - for (HighsInt i = 0; i < num_col; i++) objective_value += col_value[i]*col_cost[i]; + for (HighsInt i = 0; i < num_col; i++) + objective_value += col_value[i] * col_cost[i]; // Report the column primal and dual values, and basis status for (HighsInt i = 0; i < num_col; i++) { - printf("Col%" HIGHSINT_FORMAT " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", i, col_value[i], col_dual[i], col_basis_status[i]); + printf("Col%" HIGHSINT_FORMAT + " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", + i, col_value[i], col_dual[i], col_basis_status[i]); } // Report the row primal and dual values, and basis status for (HighsInt i = 0; i < num_row; i++) { - printf("Row%" HIGHSINT_FORMAT " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", i, row_value[i], row_dual[i], row_basis_status[i]); + printf("Row%" HIGHSINT_FORMAT + " = %lf; dual = %lf; status = %" HIGHSINT_FORMAT "\n", + i, row_value[i], row_dual[i], row_basis_status[i]); } printf("Optimal objective value = %g\n", objective_value); - // + // // Indicate that the optimal solution for both columns must be // integer valued and solve the model as a MIP HighsInt integrality[2] = {1, 1}; - run_status = Highs_mipCall(num_col, num_row, num_nz, a_format, - sense, offset, col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, - integrality, - col_value, row_value, - &model_status); + run_status = Highs_mipCall(num_col, num_row, num_nz, a_format, sense, offset, + col_cost, col_lower, col_upper, row_lower, + row_upper, a_start, a_index, a_value, integrality, + col_value, row_value, &model_status); // The run must be successful, and the model status optimal assert(run_status == kHighsStatusOk); assert(model_status == kHighsModelStatusOptimal); - printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT "\n", run_status, model_status); + printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT + "\n", + run_status, model_status); // Compute the objective value objective_value = offset; - for (HighsInt i = 0; i < num_col; i++) objective_value += col_value[i]*col_cost[i]; + for (HighsInt i = 0; i < num_col; i++) + objective_value += col_value[i] * col_cost[i]; // Report the column primal values for (HighsInt i = 0; i < num_col; i++) { printf("Col%" HIGHSINT_FORMAT " = %lf\n", i, col_value[i]); @@ -189,7 +202,7 @@ void minimal_api_qp() { // minimize -x_2 + (1/2)(2x_1^2 - 2x_1x_3 + 0.2x_2^2 + 2x_3^2) // // subject to x_1 + x_2 + x_3 >= 1; x>=0 - + const HighsInt num_col = 3; const HighsInt num_row = 1; const HighsInt num_nz = 3; @@ -228,45 +241,50 @@ void minimal_api_qp() { HighsInt model_status; HighsInt run_status; - run_status = Highs_qpCall(num_col, num_row, num_nz, q_num_nz, a_format, q_format, - sense, offset, col_cost, col_lower, col_upper, row_lower, row_upper, - a_start, a_index, a_value, - q_start, q_index, q_value, - col_value, col_dual, row_value, row_dual, - col_basis_status, row_basis_status, - &model_status); + run_status = Highs_qpCall( + num_col, num_row, num_nz, q_num_nz, a_format, q_format, sense, offset, + col_cost, col_lower, col_upper, row_lower, row_upper, a_start, a_index, + a_value, q_start, q_index, q_value, col_value, col_dual, row_value, + row_dual, col_basis_status, row_basis_status, &model_status); // The run must be successful, and the model status optimal assert(run_status == kHighsStatusOk); assert(model_status == kHighsModelStatusOptimal); - printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT "\n", run_status, model_status); + printf("\nRun status = %" HIGHSINT_FORMAT "; Model status = %" HIGHSINT_FORMAT + "\n", + run_status, model_status); // Compute the objective value objective_value = offset; - for (HighsInt i = 0; i < num_col; i++) objective_value += col_value[i]*col_cost[i]; + for (HighsInt i = 0; i < num_col; i++) + objective_value += col_value[i] * col_cost[i]; for (HighsInt i = 0; i < num_col; i++) { HighsInt from_el = q_start[i]; HighsInt to_el; - if (i+1 +#include "Highs.h" + using std::cout; using std::endl; @@ -94,7 +95,7 @@ int main() { // // Pass the model to HiGHS return_status = highs.passModel(model); - assert(return_status==HighsStatus::kOk); + assert(return_status == HighsStatus::kOk); // If a user passes a model with entries in // model.lp_.a_matrix_.value_ less than (the option) // small_matrix_value in magnitude, they will be ignored. A logging @@ -106,19 +107,21 @@ int main() { // // Solve the model return_status = highs.run(); - assert(return_status==HighsStatus::kOk); + assert(return_status == HighsStatus::kOk); // // Get the model status const HighsModelStatus& model_status = highs.getModelStatus(); - assert(model_status==HighsModelStatus::kOptimal); + assert(model_status == HighsModelStatus::kOptimal); cout << "Model status: " << highs.modelStatusToString(model_status) << endl; // // Get the solution information const HighsInfo& info = highs.getInfo(); cout << "Simplex iteration count: " << info.simplex_iteration_count << endl; cout << "Objective function value: " << info.objective_function_value << endl; - cout << "Primal solution status: " << highs.solutionStatusToString(info.primal_solution_status) << endl; - cout << "Dual solution status: " << highs.solutionStatusToString(info.dual_solution_status) << endl; + cout << "Primal solution status: " + << highs.solutionStatusToString(info.primal_solution_status) << endl; + cout << "Dual solution status: " + << highs.solutionStatusToString(info.dual_solution_status) << endl; cout << "Basis: " << highs.basisValidityToString(info.basis_validity) << endl; const bool has_values = info.primal_solution_status; const bool has_duals = info.dual_solution_status; @@ -129,39 +132,43 @@ int main() { const HighsBasis& basis = highs.getBasis(); // // Report the primal and solution values and basis - for (int col=0; col < lp.num_col_; col++) { + for (int col = 0; col < lp.num_col_; col++) { cout << "Column " << col; if (has_values) cout << "; value = " << solution.col_value[col]; if (has_duals) cout << "; dual = " << solution.col_dual[col]; - if (has_basis) cout << "; status: " << highs.basisStatusToString(basis.col_status[col]); + if (has_basis) + cout << "; status: " << highs.basisStatusToString(basis.col_status[col]); cout << endl; } - for (int row=0; row < lp.num_row_; row++) { + for (int row = 0; row < lp.num_row_; row++) { cout << "Row " << row; if (has_values) cout << "; value = " << solution.row_value[row]; if (has_duals) cout << "; dual = " << solution.row_dual[row]; - if (has_basis) cout << "; status: " << highs.basisStatusToString(basis.row_status[row]); + if (has_basis) + cout << "; status: " << highs.basisStatusToString(basis.row_status[row]); cout << endl; } // Now indicate that all the variables must take integer values model.lp_.integrality_.resize(lp.num_col_); - for (int col=0; col < lp.num_col_; col++) + for (int col = 0; col < lp.num_col_; col++) model.lp_.integrality_[col] = HighsVarType::kInteger; highs.passModel(model); // Solve the model return_status = highs.run(); - assert(return_status==HighsStatus::kOk); + assert(return_status == HighsStatus::kOk); // Report the primal solution values - for (int col=0; col < lp.num_col_; col++) { + for (int col = 0; col < lp.num_col_; col++) { cout << "Column " << col; - if (info.primal_solution_status) cout << "; value = " << solution.col_value[col]; + if (info.primal_solution_status) + cout << "; value = " << solution.col_value[col]; cout << endl; } - for (int row=0; row < lp.num_row_; row++) { + for (int row = 0; row < lp.num_row_; row++) { cout << "Row " << row; - if (info.primal_solution_status) cout << "; value = " << solution.row_value[row]; + if (info.primal_solution_status) + cout << "; value = " << solution.row_value[row]; cout << endl; } diff --git a/examples/call_highs_from_csharp.cs b/examples/call_highs_from_csharp.cs index 8fca3a77ca..0add20312a 100644 --- a/examples/call_highs_from_csharp.cs +++ b/examples/call_highs_from_csharp.cs @@ -6,17 +6,22 @@ class Program { static void Main(string[] args) { - double[] cc = {1, -2}; - double[] cl = {0, 0}; - double[] cu = {10, 10}; - double[] rl = {0, 0}; - double[] ru = {2, 1}; - int[] astart = {0, 2}; - int[] aindex = {0, 1, 0, 1}; - double[] avalue = {1, 2, 1, 3}; + // Illustrate the solution of a QP, after first solving just the LP + // + // minimize x_2 + (1/2)(2x_1^2 - 2x_1x_3 + 0.2x_2^2 + 2x_3^2) + // + // subject to x_1 + x_2 + x_3 >= 1; x>=0 + double[] cc = {0, 1, 0}; + double[] cl = {0, 0, 0}; + double[] cu = {1.0e30, 1.0e30, 1.0e30}; + double[] rl = {1}; + double[] ru = {1.0e30}; + int[] astart = {0, 3}; + int[] aindex = {0, 1, 2}; + double[] avalue = {1, 1, 1}; HighsObjectiveSense sense = HighsObjectiveSense.kMinimize; double offset = 0; - HighsMatrixFormat a_format = HighsMatrixFormat.kColwise; + HighsMatrixFormat a_format = HighsMatrixFormat.kRowwise; HighsModel model = new HighsModel(cc, cl, cu, rl, ru, astart, aindex, avalue, null, offset, a_format, sense); @@ -31,6 +36,9 @@ static void Main(string[] args) { Console.WriteLine("Status: " + status); Console.WriteLine("Modelstatus: " + modelStatus); + for (int i=0; i= 7, name = constrNames[0]) -h.addConstr(3*useTypeA + 4*useTypeB >= 12, name = constrNames[1]) -h.addConstr(2*useTypeA + useTypeB >= 6, name = constrNames[2]) +constrNames = ['Product1', 'Product2', 'Product3'] +cons = h.addConstrs(2*x['TypeA'] + 2*x['TypeB'] >= 7, + 3*x['TypeA'] + 4*x['TypeB'] >= 12, + 2*x['TypeA'] + x['TypeB'] >= 6, name = constrNames) h.setMinimize() @@ -47,31 +35,31 @@ h.solve() -for var in vars: +for name, var in x.items(): print('Use {0:.1f} of {1:s}: reduced cost {2:.6f}'.format(h.variableValue(var), h.variableName(var), h.variableDual(var))) -print('Use', h.variableValues(vars), 'of', h.variableNames(vars)) + +print('Use', h.variableValues(x.values()), 'of', h.variableNames(x.values())) print('Use', h.allVariableValues(), 'of', h.allVariableNames()) -for name in constrNames: - print(f"Constraint {name} has value {h.constrValue(name):{width}.{precision}} and dual {h.constrDual(name):{width}.{precision}}") - -print('Constraints have values', h.constrValues(constrNames), 'and duals', h.constrDuals(constrNames)) +for c in cons: + print(f"Constraint {c.name} has value {h.constrValue(c):{width}.{precision}} and dual {h.constrDual(c):{width}.{precision}}") +print('Constraints have values', h.constrValues(cons), 'and duals', h.constrDuals(cons)) print('Constraints have values', h.allConstrValues(), 'and duals', h.allConstrDuals()) -for var in vars: +for var in x.values(): print(f"Use {h.variableValue(var):{width}.{precision}} of {h.variableName(var)}") print(f"Optimal objective value is {h.getObjectiveValue():{width}.{precision}}") print() print('Solve as MIP') -for var in vars: +for var in x.values(): h.setInteger(var) h.solve() -for var in vars: +for var in x.values(): print(f"Use {h.variableValue(var):{width}.{precision}} of {h.variableName(var)}") print(f"Optimal objective value is {h.getObjectiveValue():{width}.{precision}}") @@ -79,14 +67,14 @@ print('Solve as LP with Gomory cut') # Make the variables continuous -for var in vars: +for var in x.values(): h.setContinuous(var) # Add Gomory cut -h.addConstr(useTypeA + useTypeB >= 4, name = "Gomory") +h.addConstr(x['TypeA'] + x['TypeB'] >= 4, name = "Gomory") h.solve() -for var in vars: +for var in x.values(): print(f"Use {h.variableValue(var):{width}.{precision}} of {h.variableName(var)}") print(f"Optimal objective value is {h.getObjectiveValue():{width}.{precision}}") diff --git a/examples/minimal.py b/examples/minimal.py index 943e231105..d4c55e4774 100644 --- a/examples/minimal.py +++ b/examples/minimal.py @@ -5,7 +5,7 @@ x1 = h.addVariable(lb = -h.inf) x2 = h.addVariable(lb = -h.inf) -h.addConstr(x2 - x1 >= 2) -h.addConstr(x1 + x2 >= 0) +h.addConstrs(x2 - x1 >= 2, + x1 + x2 >= 0) h.minimize(x2) diff --git a/examples/network_flow.py b/examples/network_flow.py new file mode 100644 index 0000000000..3b3ab95b96 --- /dev/null +++ b/examples/network_flow.py @@ -0,0 +1,37 @@ +# Example of a shortest path network flow in a graph +# Shows integration of highspy with networkx + +import highspy +import networkx as nx + +orig, dest = ('A', 'D') + +# create directed graph with edge weights (distances) +G = nx.DiGraph() +G.add_weighted_edges_from([('A', 'B', 2.0), ('B', 'C', 3.0), ('A', 'C', 1.5), ('B', 'D', 2.5), ('C', 'D', 1.0)]) + +h = highspy.Highs() +h.silent() + +x = h.addBinaries(G.edges, obj=nx.get_edge_attributes(G, 'weight')) + +# add flow conservation constraints +# { 1 if n = orig +# sum(out) - sum(in) = { -1 if n = dest +# { 0 otherwise +rhs = lambda n: 1 if n == orig else -1 if n == dest else 0 +flow = lambda E: h.qsum((x[e] for e in E)) + +h.addConstrs(flow(G.out_edges(n)) - flow(G.in_edges(n)) == rhs(n) for n in G.nodes) +h.minimize() + +# Print the solution +print('Shortest path from', orig, 'to', dest, 'is: ', end = '') +sol = h.vals(x) + +n = orig +while n != dest: + print(n, end=' ') + n = next(e[1] for e in G.out_edges(n) if sol[e] > 0.5) + +print(dest) diff --git a/examples/nqueens.py b/examples/nqueens.py new file mode 100644 index 0000000000..03e3286662 --- /dev/null +++ b/examples/nqueens.py @@ -0,0 +1,29 @@ +# This is an example of the N-Queens problem, which is a classic combinatorial problem. +# The problem is to place N queens on an N x N chessboard so that no two queens attack each other. +# +# We show how to model the problem as a MIP and solve it using highspy. +# Using numpy can simplify the construction of the constraints (i.e., diagonal). + +import highspy +import numpy as np + +N = 8 +h = highspy.Highs() +h.silent() + +x = h.addBinaries(N, N) + +h.addConstrs(x.sum(axis=0) == 1) # each row has exactly one queen +h.addConstrs(x.sum(axis=1) == 1) # each col has exactly one queen + +y = np.fliplr(x) +h.addConstrs(x.diagonal(k).sum() <= 1 for k in range(-N + 1, N)) # each diagonal has at most one queen +h.addConstrs(y.diagonal(k).sum() <= 1 for k in range(-N + 1, N)) # each 'reverse' diagonal has at most one queen + +h.solve() +sol = h.vals(x) + +print('Queens:') + +for i in range(N): + print(''.join('Q' if sol[i, j] > 0.5 else '*' for j in range(N))) \ No newline at end of file diff --git a/examples/plot_highs_log.py b/examples/plot_highs_log.py new file mode 100644 index 0000000000..6163c96039 --- /dev/null +++ b/examples/plot_highs_log.py @@ -0,0 +1,134 @@ +import re +import matplotlib.pyplot as plt +import numpy as np +import math as math + + +def parse_highs_log(log_file_path): + last_full_entry = [] + current_entry = [] + found_solution = False + + with open(log_file_path, "r") as f: + for line in f: + if "Running HiGHS" in line: + if found_solution: + last_full_entry = current_entry + current_entry = [line] + found_solution = False + else: + current_entry.append(line) + if "Writing the solution to" in line: + found_solution = True + + if not last_full_entry: + last_full_entry = current_entry + + if not last_full_entry: + return None, None, None, None, None, None + + time_values, best_bound_values, best_sol_values, in_queue_values, expl_values, gap_values = ( + [], + [], + [], + [], + [], + [], + ) + for line in last_full_entry: + match = re.search(r"\dk?\s+\d+\.\ds$", line) + + if not match: + continue + + tokens = line.split() + if len(tokens) == 13: + tokens = tokens[1:] + assert len(tokens) == 12, f"{line}" + + in_queue_values.append(float(tokens[1])) # InQueue + expl_values.append(float(tokens[3].replace("%", ""))) # Expl.% + best_bound_values.append(float(tokens[4].replace("inf", "nan"))) # Best Bound + best_sol_values.append(float(tokens[5].replace("inf", "nan"))) # Best Sol + gap_values.append( + float(tokens[6].replace("%", "").replace("inf", "nan").replace("Large", "nan")) + ) # Gap% + time_values.append(float(tokens[11].replace("s", ""))) # Time + + return time_values, best_bound_values, best_sol_values, in_queue_values, expl_values, gap_values + + +def plot_highs_log( + time_values, best_bound_values, best_sol_values, in_queue_values, expl_values, gap_values +): + fig, ax1 = plt.subplots(figsize=(10, 6)) + + best_bound_colour = "blue" + best_solution_colour = "green" + in_queue_colour = "red" + explored_colour = "purple" + gap_colour = "orange" + + # Plot Objective Bounds + ax1.plot(time_values, best_bound_values, label="Best Bound", color=best_bound_colour) + ax1.plot(time_values, best_sol_values, label="Best Solution", color=best_solution_colour) + ax1.set_xlabel("Time (seconds)") + ax1.set_ylabel("Objective Bounds", color=best_bound_colour, labelpad=15) + ax1.tick_params(axis="y", labelcolor=best_bound_colour) + + # Limit y-axis to the range between min and max of the non-NaN values + valid_gap_index = next(i for i, gap in enumerate(gap_values) if not np.isnan(gap)) + min_y = min(best_bound_values[valid_gap_index], best_sol_values[valid_gap_index]) + max_y = max(best_bound_values[valid_gap_index], best_sol_values[valid_gap_index]) + padding = (max_y - min_y) * 0.1 + ax1.set_ylim(min_y - padding, max_y + padding) + + # Add second y-axis for InQueue values + ax2 = ax1.twinx() + ax2.plot(time_values, in_queue_values, label="InQueue", color=in_queue_colour) +# ax2.set_ylabel("InQueue", color=in_queue_colour, loc="top", labelpad=12) + ax2.yaxis.label.set_rotation(0) + ax2.tick_params(axis="y", labelcolor=in_queue_colour) + + # Add third y-axis for Explored % values (scaled) + ax3 = ax1.twinx() + ax3.spines["right"].set_position(("outward", 50)) + ax3.plot(time_values, expl_values, label="Explored %", color=explored_colour) +# ax3.set_ylabel("Expl.%", color=explored_colour, loc="top", labelpad=10) + ax3.yaxis.label.set_rotation(0) + ax3.tick_params(axis="y", labelcolor=explored_colour) + + # Add fourth y-axis for Gap % values (scaled) + ax4 = ax1.twinx() + ax4.spines["right"].set_position(("outward", 90)) + ax4.plot(time_values, gap_values, label="Gap %", color=gap_colour, linestyle="--", linewidth=0.5) +# ax4.set_ylabel("Gap.%", color=gap_colour, loc="top", labelpad=22) + ax4.yaxis.label.set_rotation(0) + ax4.tick_params(axis="y", labelcolor=gap_colour) + + # Plot vertical hash lines where Best Solution changes + for i in range(1, len(best_sol_values)): + # Print if change detected and not NaN + if (best_sol_values[i] != best_sol_values[i - 1]) and not(math.isnan(best_sol_values[i])): + ax1.axvline(x=time_values[i], color="grey", linestyle="--", linewidth=0.5) + + # Shift plot area left to make room on the right for the three y-axis labels. + fig.subplots_adjust(left=0.08, right=0.85) + + # Set up legend + fig.legend(loc="lower center", ncols=5) + + # Show plot + plt.title("HiGHS MIP Log Analysis", pad=20) + plt.show() + + +#log_file_path = "/path/to/your/logfile.log" +log_file_path = "HiGHS.log" +time_values, best_bound_values, best_sol_values, in_queue_values, expl_values, gap_values = ( + parse_highs_log(log_file_path) +) + +plot_highs_log( + time_values, best_bound_values, best_sol_values, in_queue_values, expl_values, gap_values +) diff --git a/extern/catch.hpp b/extern/catch.hpp index db1fed3b98..b529505586 100644 --- a/extern/catch.hpp +++ b/extern/catch.hpp @@ -2,8 +2,8 @@ * Catch v2.13.8 * Generated: 2022-01-03 21:20:09.589503 * ---------------------------------------------------------- - * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. + * This file has been merged from multiple headers. Please don't edit it + * directly Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -12,55 +12,54 @@ #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED // start catch.hpp - #define CATCH_VERSION_MAJOR 2 #define CATCH_VERSION_MINOR 13 #define CATCH_VERSION_PATCH 8 #ifdef __clang__ -# pragma clang system_header +#pragma clang system_header #elif defined __GNUC__ -# pragma GCC system_header +#pragma GCC system_header #endif // start catch_suppress_warnings.h #ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(push) -# pragma warning(disable: 161 1682) -# else // __ICC -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wswitch-enum" -# pragma clang diagnostic ignored "-Wcovered-switch-default" -# endif +#ifdef __ICC // icpc defines the __clang__ macro +#pragma warning(push) +#pragma warning(disable : 161 1682) +#else // __ICC +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif #elif defined __GNUC__ - // Because REQUIREs trigger GCC's -Wparentheses, and because still - // supported version of g++ have only buggy support for _Pragmas, - // Wparentheses have to be suppressed globally. -# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details - -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wpadded" +// Because REQUIREs trigger GCC's -Wparentheses, and because still +// supported version of g++ have only buggy support for _Pragmas, +// Wparentheses have to be suppressed globally. +#pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wpadded" #endif // end catch_suppress_warnings.h #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) -# define CATCH_IMPL -# define CATCH_CONFIG_ALL_PARTS +#define CATCH_IMPL +#define CATCH_CONFIG_ALL_PARTS #endif // In the impl file, we want to have access to all parts of the headers // Can also be used to sanely support PCHs #if defined(CATCH_CONFIG_ALL_PARTS) -# define CATCH_CONFIG_EXTERNAL_INTERFACES -# if defined(CATCH_CONFIG_DISABLE_MATCHERS) -# undef CATCH_CONFIG_DISABLE_MATCHERS -# endif -# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) -# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER -# endif +#define CATCH_CONFIG_EXTERNAL_INTERFACES +#if defined(CATCH_CONFIG_DISABLE_MATCHERS) +#undef CATCH_CONFIG_DISABLE_MATCHERS +#endif +#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif #endif #if !defined(CATCH_CONFIG_IMPL_ONLY) @@ -69,34 +68,35 @@ // See e.g.: // https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ -# include -# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ - (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) -# define CATCH_PLATFORM_MAC -# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) -# define CATCH_PLATFORM_IPHONE -# endif +#include +#if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +#define CATCH_PLATFORM_MAC +#elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +#define CATCH_PLATFORM_IPHONE +#endif #elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX +#define CATCH_PLATFORM_LINUX -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) -# define CATCH_PLATFORM_WINDOWS +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || \ + defined(_MSC_VER) || defined(__MINGW32__) +#define CATCH_PLATFORM_WINDOWS #endif // end catch_platform.h #ifdef CATCH_IMPL -# ifndef CLARA_CONFIG_MAIN -# define CLARA_CONFIG_MAIN_NOT_DEFINED -# define CLARA_CONFIG_MAIN -# endif +#ifndef CLARA_CONFIG_MAIN +#define CLARA_CONFIG_MAIN_NOT_DEFINED +#define CLARA_CONFIG_MAIN +#endif #endif // start catch_user_interfaces.h namespace Catch { - unsigned int rngSeed(); +unsigned int rngSeed(); } // end catch_user_interfaces.h @@ -125,30 +125,33 @@ namespace Catch { #ifdef __cplusplus -# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -# define CATCH_CPP14_OR_GREATER -# endif +#if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +#define CATCH_CPP14_OR_GREATER +#endif -# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define CATCH_CPP17_OR_GREATER -# endif +#if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define CATCH_CPP17_OR_GREATER +#endif #endif // Only GCC compiler should be used in this block, so other compilers trying to // mask themselves as GCC should be ignored. -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && \ + !defined(__CUDACC__) && !defined(__LCC__) +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma("GCC diagnostic push") +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("GCC diagnostic pop") -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) +#define CATCH_INTERNAL_IGNORE_BUT_WARN(...) \ + (void)__builtin_constant_p(__VA_ARGS__) #endif #if defined(__clang__) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + _Pragma("clang diagnostic push") +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("clang diagnostic pop") // As of this writing, IBM XL's implementation of __builtin_constant_p has a bug // which results in calls to destructors being emitted for each temporary, @@ -161,62 +164,66 @@ namespace Catch { // ``` // // Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. -# if !defined(__ibmxl__) && !defined(__CUDACC__) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ -# endif +#if !defined(__ibmxl__) && !defined(__CUDACC__) +#define CATCH_INTERNAL_IGNORE_BUT_WARN(...) \ + (void)__builtin_constant_p( \ + __VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, \ + hicpp-vararg) */ +#endif -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +#define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +#define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wparentheses\"") -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) +#define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wunused-variable\"") -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) +#define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"") -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) +#define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wunused-template\"") -#endif // __clang__ +#endif // __clang__ //////////////////////////////////////////////////////////////////////////////// // Assume that non-Windows platforms support posix signals by default #if !defined(CATCH_PLATFORM_WINDOWS) - #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS #endif //////////////////////////////////////////////////////////////////////////////// // We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) - #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || \ + defined(__DJGPP__) +#define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS #endif #ifdef __OS400__ -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# define CATCH_CONFIG_COLOUR_NONE +#define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#define CATCH_CONFIG_COLOUR_NONE #endif //////////////////////////////////////////////////////////////////////////////// // Android somehow still does not support std::to_string #if defined(__ANDROID__) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE #endif //////////////////////////////////////////////////////////////////////////////// // Not all Windows environments support SEH properly #if defined(__MINGW32__) -# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH #endif //////////////////////////////////////////////////////////////////////////////// // PS4 #if defined(__ORBIS__) -# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE #endif //////////////////////////////////////////////////////////////////////////////// @@ -224,17 +231,20 @@ namespace Catch { #ifdef __CYGWIN__ // Required for some versions of Cygwin to declare gettimeofday -// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin -# define _BSD_SOURCE -// some versions of cygwin (most) do not support std::to_string. Use the libstd check. -// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 -# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ - && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) - -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +// see: +// http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +#define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd +// check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html +// line 2812-2813 +#if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) && \ + !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +#define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -# endif -#endif // __CYGWIN__ +#endif +#endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ @@ -242,49 +252,52 @@ namespace Catch { // Universal Windows platform does not support SEH // Or console colours (or console at all...) -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) -# define CATCH_CONFIG_COLOUR_NONE -# else -# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH -# endif +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +#define CATCH_CONFIG_COLOUR_NONE +#else +#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +#endif -# if !defined(__clang__) // Handle Clang masquerading for msvc +#if !defined(__clang__) // Handle Clang masquerading for msvc // MSVC traditional preprocessor needs some workaround for __VA_ARGS__ // _MSVC_TRADITIONAL == 0 means new conformant preprocessor // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif // MSVC_TRADITIONAL +#if !defined(_MSVC_TRADITIONAL) || \ + (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +#define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif // MSVC_TRADITIONAL -// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) -# endif // __clang__ +// Only do this if we're not using clang on Windows, which uses `diagnostic +// push` & `diagnostic pop` +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma(warning(push)) +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma(warning(pop)) +#endif // __clang__ -#endif // _MSC_VER +#endif // _MSC_VER #if defined(_REENTRANT) || defined(_MSC_VER) -// Enable async processing, as -pthread is specified or no additional linking is required -# define CATCH_INTERNAL_CONFIG_USE_ASYNC -#endif // _MSC_VER +// Enable async processing, as -pthread is specified or no additional linking is +// required +#define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// // Check if we are compiled with -fno-exceptions or equivalent #if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) -# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED #endif //////////////////////////////////////////////////////////////////////////////// // DJGPP #ifdef __DJGPP__ -# define CATCH_INTERNAL_CONFIG_NO_WCHAR -#endif // __DJGPP__ +#define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ //////////////////////////////////////////////////////////////////////////////// // Embarcadero C++Build #if defined(__BORLANDC__) - #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN #endif //////////////////////////////////////////////////////////////////////////////// @@ -294,8 +307,8 @@ namespace Catch { // handled by it. // Otherwise all supported compilers support COUNTER macro, // but user still might want to turn it off -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER +#if (!defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L) +#define CATCH_INTERNAL_CONFIG_COUNTER #endif //////////////////////////////////////////////////////////////////////////////// @@ -304,9 +317,9 @@ namespace Catch { // This means that it is detected as Windows, but does not provide // the same set of capabilities as real Windows does. #if defined(UNDER_RTSS) || defined(RTX64_BUILD) - #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH - #define CATCH_INTERNAL_CONFIG_NO_ASYNC - #define CATCH_CONFIG_COLOUR_NONE +#define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#define CATCH_INTERNAL_CONFIG_NO_ASYNC +#define CATCH_CONFIG_COLOUR_NONE #endif #if !defined(_GLIBCXX_USE_C99_MATH_TR1) @@ -315,139 +328,173 @@ namespace Catch { // Various stdlib support checks that require __has_include #if defined(__has_include) - // Check if string_view is available and usable - #if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW - #endif - - // Check if optional is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if byte is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # include - # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) - # define CATCH_INTERNAL_CONFIG_CPP17_BYTE - # endif - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if variant is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 - # include - # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # define CATCH_CONFIG_NO_CPP17_VARIANT - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__clang__) && (__clang_major__ < 8) - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // defined(__has_include) - -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) -# define CATCH_CONFIG_COUNTER +// Check if string_view is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif + +// Check if optional is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +#endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + +// Check if byte is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#include +#if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) +#define CATCH_INTERNAL_CONFIG_CPP17_BYTE +#endif +#endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + +// Check if variant is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#if defined(__clang__) && (__clang_major__ < 8) +// work around clang bug with libstdc++ +// https://bugs.llvm.org/show_bug.cgi?id=31852 fix should be in clang 8, +// workaround in libstdc++ 8.2 +#include +#if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +#define CATCH_CONFIG_NO_CPP17_VARIANT +#else +#define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +#endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && + // (_GLIBCXX_RELEASE < 9) +#else +#define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +#endif // defined(__clang__) && (__clang_major__ < 8) +#endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && \ + !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +#define CATCH_CONFIG_COUNTER #endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && \ + !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && \ + !defined(CATCH_CONFIG_WINDOWS_SEH) && \ + !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +#define CATCH_CONFIG_WINDOWS_SEH #endif -// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_CONFIG_POSIX_SIGNALS +// This is set by default, because we assume that unix compilers are +// posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && \ + !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && \ + !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && \ + !defined(CATCH_CONFIG_POSIX_SIGNALS) +#define CATCH_CONFIG_POSIX_SIGNALS #endif -// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. -#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) -# define CATCH_CONFIG_WCHAR +// This is set by default, because we assume that compilers with no wchar_t +// support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && \ + !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +#define CATCH_CONFIG_WCHAR #endif -#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) -# define CATCH_CONFIG_CPP11_TO_STRING +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && \ + !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && \ + !defined(CATCH_CONFIG_CPP11_TO_STRING) +#define CATCH_CONFIG_CPP11_TO_STRING #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) -# define CATCH_CONFIG_CPP17_OPTIONAL +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && \ + !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && \ + !defined(CATCH_CONFIG_CPP17_OPTIONAL) +#define CATCH_CONFIG_CPP17_OPTIONAL #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) -# define CATCH_CONFIG_CPP17_STRING_VIEW +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && \ + !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && \ + !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +#define CATCH_CONFIG_CPP17_STRING_VIEW #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) -# define CATCH_CONFIG_CPP17_VARIANT +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && \ + !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && \ + !defined(CATCH_CONFIG_CPP17_VARIANT) +#define CATCH_CONFIG_CPP17_VARIANT #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) -# define CATCH_CONFIG_CPP17_BYTE +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && \ + !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +#define CATCH_CONFIG_CPP17_BYTE #endif #if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) -# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#define CATCH_INTERNAL_CONFIG_NEW_CAPTURE #endif -#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) -# define CATCH_CONFIG_NEW_CAPTURE +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && \ + !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && \ + !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && \ + !defined(CATCH_CONFIG_NEW_CAPTURE) +#define CATCH_CONFIG_NEW_CAPTURE #endif -#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && \ + !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_CONFIG_DISABLE_EXCEPTIONS #endif -#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) -# define CATCH_CONFIG_POLYFILL_ISNAN +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && \ + !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && \ + !defined(CATCH_CONFIG_POLYFILL_ISNAN) +#define CATCH_CONFIG_POLYFILL_ISNAN #endif -#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) -# define CATCH_CONFIG_USE_ASYNC +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && \ + !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && \ + !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +#define CATCH_CONFIG_USE_ASYNC #endif -#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) -# define CATCH_CONFIG_ANDROID_LOGWRITE +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && \ + !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && \ + !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +#define CATCH_CONFIG_ANDROID_LOGWRITE #endif -#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) -# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && \ + !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && \ + !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +#define CATCH_CONFIG_GLOBAL_NEXTAFTER #endif // Even if we do not think the compiler has that warning, we still have // to provide a macro that can be used by the code. #if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION #endif #if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif // The goal of this macro is to avoid evaluation of the arguments, but // still have the compiler warn on problems inside... #if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#define CATCH_INTERNAL_IGNORE_BUT_WARN(...) #endif -#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#if defined(__APPLE__) && defined(__apple_build_version__) && \ + (__clang_major__ < 10) +#undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #elif defined(__clang__) && (__clang_major__ < 5) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) @@ -460,103 +507,109 @@ namespace Catch { #define CATCH_CATCH_ANON(type) catch (type) #endif -#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && \ + !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && \ + !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) #define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #endif // end catch_compiler_capabilities.h -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2(name, line) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE(name, line) \ + INTERNAL_CATCH_UNIQUE_NAME_LINE2(name, line) #ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#define INTERNAL_CATCH_UNIQUE_NAME(name) \ + INTERNAL_CATCH_UNIQUE_NAME_LINE(name, __COUNTER__) #else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#define INTERNAL_CATCH_UNIQUE_NAME(name) \ + INTERNAL_CATCH_UNIQUE_NAME_LINE(name, __LINE__) #endif +#include #include #include -#include -// We need a dummy global operator<< so we can bring it into Catch namespace later +// We need a dummy global operator<< so we can bring it into Catch namespace +// later struct Catch_global_namespace_dummy {}; std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); namespace Catch { - struct CaseSensitive { enum Choice { - Yes, - No - }; }; - - class NonCopyable { - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable && ) = delete; - NonCopyable& operator = ( NonCopyable const& ) = delete; - NonCopyable& operator = ( NonCopyable && ) = delete; +struct CaseSensitive { + enum Choice { Yes, No }; +}; - protected: - NonCopyable(); - virtual ~NonCopyable(); - }; +class NonCopyable { + NonCopyable(NonCopyable const&) = delete; + NonCopyable(NonCopyable&&) = delete; + NonCopyable& operator=(NonCopyable const&) = delete; + NonCopyable& operator=(NonCopyable&&) = delete; - struct SourceLineInfo { + protected: + NonCopyable(); + virtual ~NonCopyable(); +}; - SourceLineInfo() = delete; - SourceLineInfo( char const* _file, std::size_t _line ) noexcept - : file( _file ), - line( _line ) - {} +struct SourceLineInfo { + SourceLineInfo() = delete; + SourceLineInfo(char const* _file, std::size_t _line) noexcept + : file(_file), line(_line) {} - SourceLineInfo( SourceLineInfo const& other ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo( SourceLineInfo&& ) noexcept = default; - SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + SourceLineInfo(SourceLineInfo const& other) = default; + SourceLineInfo& operator=(SourceLineInfo const&) = default; + SourceLineInfo(SourceLineInfo&&) noexcept = default; + SourceLineInfo& operator=(SourceLineInfo&&) noexcept = default; - bool empty() const noexcept { return file[0] == '\0'; } - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; + bool empty() const noexcept { return file[0] == '\0'; } + bool operator==(SourceLineInfo const& other) const noexcept; + bool operator<(SourceLineInfo const& other) const noexcept; - char const* file; - std::size_t line; - }; + char const* file; + std::size_t line; +}; - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); +std::ostream& operator<<(std::ostream& os, SourceLineInfo const& info); - // Bring in operator<< from global namespace into Catch namespace - // This is necessary because the overload of operator<< above makes - // lookup stop at namespace Catch - using ::operator<<; +// Bring in operator<< from global namespace into Catch namespace +// This is necessary because the overload of operator<< above makes +// lookup stop at namespace Catch +using ::operator<<; - // Use this in variadic streaming macros to allow - // >> +StreamEndStop - // as well as - // >> stuff +StreamEndStop - struct StreamEndStop { - std::string operator+() const; - }; - template - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } +// Use this in variadic streaming macros to allow +// >> +StreamEndStop +// as well as +// >> stuff +StreamEndStop +struct StreamEndStop { + std::string operator+() const; +}; +template +T const& operator+(T const& value, StreamEndStop) { + return value; } +} // namespace Catch #define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + ::Catch::SourceLineInfo(__FILE__, static_cast(__LINE__)) // end catch_common.h namespace Catch { - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; +struct RegistrarForTagAliases { + RegistrarForTagAliases(char const* alias, char const* tag, + SourceLineInfo const& lineInfo); +}; -} // end namespace Catch +} // end namespace Catch -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#define CATCH_REGISTER_TAG_ALIAS(alias, spec) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( \ + AutoRegisterTagAlias)(alias, spec, CATCH_INTERNAL_LINEINFO); \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION // end catch_tag_alias_autoregistrar.h // start catch_test_registry.h @@ -567,143 +620,151 @@ namespace Catch { namespace Catch { - class TestSpec; +class TestSpec; - struct ITestInvoker { - virtual void invoke () const = 0; - virtual ~ITestInvoker(); - }; +struct ITestInvoker { + virtual void invoke() const = 0; + virtual ~ITestInvoker(); +}; - class TestCase; - struct IConfig; +class TestCase; +struct IConfig; - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; +struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( + IConfig const& config) const = 0; +}; - bool isThrowSafe( TestCase const& testCase, IConfig const& config ); - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); +bool isThrowSafe(TestCase const& testCase, IConfig const& config); +bool matchTest(TestCase const& testCase, TestSpec const& testSpec, + IConfig const& config); +std::vector filterTests(std::vector const& testCases, + TestSpec const& testSpec, + IConfig const& config); +std::vector const& getAllTestCasesSorted(IConfig const& config); -} +} // namespace Catch // end catch_interfaces_testcase.h // start catch_stringref.h +#include #include -#include #include -#include +#include namespace Catch { - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. - class StringRef { - public: - using size_type = std::size_t; - using const_iterator = const char*; +/// A non-owning string class (similar to the forthcoming std::string_view) +/// Note that, because a StringRef may be a substring of another string, +/// it may not be null terminated. +class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; - private: - static constexpr char const* const s_empty = ""; + private: + static constexpr char const* const s_empty = ""; - char const* m_start = s_empty; - size_type m_size = 0; + char const* m_start = s_empty; + size_type m_size = 0; - public: // construction - constexpr StringRef() noexcept = default; + public: // construction + constexpr StringRef() noexcept = default; - StringRef( char const* rawChars ) noexcept; + StringRef(char const* rawChars) noexcept; - constexpr StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} + constexpr StringRef(char const* rawChars, size_type size) noexcept + : m_start(rawChars), m_size(size) {} - StringRef( std::string const& stdString ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} + StringRef(std::string const& stdString) noexcept + : m_start(stdString.c_str()), m_size(stdString.size()) {} - explicit operator std::string() const { - return std::string(m_start, m_size); - } + explicit operator std::string() const { return std::string(m_start, m_size); } - public: // operators - auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != (StringRef const& other) const noexcept -> bool { - return !(*this == other); - } + public: // operators + auto operator==(StringRef const& other) const noexcept -> bool; + auto operator!=(StringRef const& other) const noexcept -> bool { + return !(*this == other); + } - auto operator[] ( size_type index ) const noexcept -> char { - assert(index < m_size); - return m_start[index]; - } + auto operator[](size_type index) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } - public: // named queries - constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } - constexpr auto size() const noexcept -> size_type { - return m_size; - } + public: // named queries + constexpr auto empty() const noexcept -> bool { return m_size == 0; } + constexpr auto size() const noexcept -> size_type { return m_size; } - // Returns the current start pointer. If the StringRef is not - // null-terminated, throws std::domain_exception - auto c_str() const -> char const*; + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; - public: // substrings and searches - // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, size()). - // If start > size(), then the substring is empty. - auto substr( size_type start, size_type length ) const noexcept -> StringRef; + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr(size_type start, size_type length) const noexcept -> StringRef; - // Returns the current start pointer. May not be null-terminated. - auto data() const noexcept -> char const*; + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; - constexpr auto isNullTerminated() const noexcept -> bool { - return m_start[m_size] == '\0'; - } + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } - public: // iterators - constexpr const_iterator begin() const { return m_start; } - constexpr const_iterator end() const { return m_start + m_size; } - }; + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } +}; - auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; - auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; +auto operator+=(std::string& lhs, StringRef const& sr) -> std::string&; +auto operator<<(std::ostream& os, StringRef const& sr) -> std::ostream&; - constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } -} // namespace Catch +constexpr auto operator"" _sr(char const* rawChars, + std::size_t size) noexcept -> StringRef { + return StringRef(rawChars, size); +} +} // namespace Catch -constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { - return Catch::StringRef( rawChars, size ); +constexpr auto operator"" _catch_sr( + char const* rawChars, std::size_t size) noexcept -> Catch::StringRef { + return Catch::StringRef(rawChars, size); } // end catch_stringref.h // start catch_preprocessor.hpp - #define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ -#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL1(...) \ + CATCH_RECURSION_LEVEL0( \ + CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) \ + CATCH_RECURSION_LEVEL1( \ + CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) \ + CATCH_RECURSION_LEVEL2( \ + CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) \ + CATCH_RECURSION_LEVEL3( \ + CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) \ + CATCH_RECURSION_LEVEL4( \ + CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) #ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ // MSVC needs more evaluations -#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#define CATCH_RECURSION_LEVEL6(...) \ + CATCH_RECURSION_LEVEL5( \ + CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) \ + CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) #else -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) #endif #define CATCH_REC_END(...) @@ -716,627 +777,1109 @@ constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) n #define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 #define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 #define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT -#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) -#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) - -#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) - -#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) - -// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, -// and passes userdata as the first parameter to each invocation, -// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) -#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER(CATCH_REC_NEXT0)(test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) \ + , f(x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1))(f, peek, \ + __VA_ARGS__) +#define CATCH_REC_LIST1(f, x, peek, ...) \ + , f(x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST0))(f, peek, \ + __VA_ARGS__) +#define CATCH_REC_LIST2(f, x, peek, ...) \ + f(x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1))(f, peek, __VA_ARGS__) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) \ + , f(userdata, x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD))( \ + f, userdata, peek, __VA_ARGS__) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) \ + , f(userdata, x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD))( \ + f, userdata, peek, __VA_ARGS__) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) \ + f(userdata, x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD))( \ + f, userdata, peek, __VA_ARGS__) + +// Applies the function macro `f` to each of the remaining parameters, inserts +// commas between the results, and passes userdata as the first parameter to +// each invocation, e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), +// f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) \ + CATCH_RECURSE( \ + CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) \ + CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) #define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) -#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO##__VA_ARGS__ #define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ #define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF #define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) \ + INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) #else -// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +// MSVC is adding extra space and needs another indirection to expand +// INTERNAL_CATCH_NOINTERNAL_CATCH_DEF #define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) #define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) \ + (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) #endif #define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ #define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) -#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) +#define INTERNAL_CATCH_REMOVE_PARENS(...) \ + INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) \ + decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) \ + INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) #else -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2( \ + INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) #endif -#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...) \ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST, __VA_ARGS__) #define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) -#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) -#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) -#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) -#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) -#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) -#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) -#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) -#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) -#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) -#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) - -#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N - -#define INTERNAL_CATCH_TYPE_GEN\ - template struct TypeList {};\ - template\ - constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ - template class...> struct TemplateTypeList{};\ - template class...Cs>\ - constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ - template\ - struct append;\ - template\ - struct rewrap;\ - template class, typename...>\ - struct create;\ - template class, typename>\ - struct convert;\ - \ - template \ - struct append { using type = T; };\ - template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ - struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ - template< template class L1, typename...E1, typename...Rest>\ - struct append, TypeList, Rest...> { using type = L1; };\ - \ - template< template class Container, template class List, typename...elems>\ - struct rewrap, List> { using type = TypeList>; };\ - template< template class Container, template class List, class...Elems, typename...Elements>\ - struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ - \ - template