From: Patrick Jaap Date: Wed, 15 Sep 2021 08:50:45 +0000 (+0100) Subject: Import dune-common_2.8.0.orig.tar.gz X-Git-Tag: archive/raspbian/2.8.0-3+rpi1^2~7 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=4f50b8a7eb23bac8e59dba2265b04dfa7524c345;p=dune-common.git Import dune-common_2.8.0.orig.tar.gz [dgit import orig dune-common_2.8.0.orig.tar.gz] --- 4f50b8a7eb23bac8e59dba2265b04dfa7524c345 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82f8f72 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# ignore all build folders +/build*/ +# ignore backup files +*~ +# ignore Python files +*.pyc +# ignore files generated during python setup.py sdist +MANIFEST +_skbuild/ +dist diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..da1f537 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,64 @@ +--- +include: + - project: 'core/ci-config' + ref: master + file: 'config/common/master.yml' + - project: 'core/ci-config' + ref: master + file: 'jobs/common/master.yml' + +before_script: + - . /duneci/bin/duneci-init-job + +variables: + DUNECI_TEST_LABELS: quick + DUNE_TEST_EXPECTED_VC_IMPLEMENTATION: SSE2 + +debian:10 gcc-7-17--expensive: + # This image has Vc + image: registry.dune-project.org/docker/ci/debian:10 + script: duneci-standard-test + stage: test + # allow expensive tests + variables: + DUNECI_CXXFLAGS: -mavx + DUNECI_TEST_LABELS: "" + DUNECI_TOOLCHAIN: gcc-7-17 + DUNE_TEST_EXPECTED_VC_IMPLEMENTATION: AVX + # require AVX to properly test Vc + tags: [duneci, "iset:avx"] + # allowed to fail to e.g. do no hold up a merge when a runner supporting avx + # is unavailable + allow_failure: true + + +debian-11-gcc-9-17-python: + image: registry.dune-project.org/docker/ci/debian:11 + script: duneci-standard-test + stage: test + variables: + DUNECI_TOOLCHAIN: gcc-9-17 + # so we need some variables to build the dune-py module during execution of the first python test: + # we need to find the dune mdoule + DUNE_CONTROL_PATH: /duneci/modules:$CI_PROJECT_DIR + # the position for the dune-py module + DUNE_PY_DIR: /duneci/modules/dune-py + # during dune-py build this variable is used - do know a way to access + # the CMAKE_FLAGS used to build the modules... + DUNE_CMAKE_FLAGS: "CC=gcc-9 CXX=g++-9 -DCMAKE_CXX_FLAGS='-std=c++17 -O2 -g -Wall -fdiagnostics-color=always' -DDUNE_ENABLE_PYTHONBINDINGS=ON -DDUNE_MAX_TEST_CORES=4 -DBUILD_SHARED_LIBS=TRUE -DDUNE_PYTHON_INSTALL_LOCATION=none -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -DCMAKE_DISABLE_FIND_PACKAGE_LATEX=TRUE -DCMAKE_DISABLE_FIND_PACKAGE_Alberta=TRUE -DCMAKE_DISABLE_FIND_PACKAGE_Vc=TRUE -DCMAKE_DISABLE_DOCUMENTATION=TRUE" + # cmake flags we use for all dune moudle - important is that build shared libs is set (need some better way of doing this) + DUNECI_CMAKE_FLAGS: $DUNE_CMAKE_FLAGS + # finally set the python path to all modules + PYTHONPATH: $CI_PROJECT_DIR/build-cmake/python + tags: [duneci] + +system-test: + stage: downstream + variables: + CI_BUILD_REF_NAME: $CI_COMMIT_REF_NAME + DUNECI_TEST_LABELS: "" + trigger: + project: infrastructure/dune-nightly-test + branch: core + strategy: depend + allow_failure: true diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..70627af --- /dev/null +++ b/.mailmap @@ -0,0 +1,2 @@ +Benjamin Bykowski Convex Function <329364@wright.mathepool.rwth-aachen.de> + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f7e7042 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,335 @@ +# Release 2.8 + +- Set minimal required CMake version in cmake to >= 3.13. + +- Python bindings have been moved from `dune-python` to the respective + core modules. `dune-python` is now obsolete. To activate Python bindings the + CMake flag `DUNE_ENABLE_PYTHONBINDINGS` needs to be turned on (default is off). + Furthermore, flags for either shared library or position independent code + need to be used. + +- Add `instance` method to MPIHelper that does not expect arguments for access + to the singleton object after initialization. + +- Remove the cmake check for `HAVE_MPROTECT` and also do not define this variable in the + `config.h` file. It is defined only inside the header `debugallocator.hh`. + +- Remove deprecated type-traits `has_nan`, `is_indexable`, and + `is_range`, use the CamelCase versions instead. + +- Deprecate fallback implementations `Dune::Std::apply`, `Dune::Std::bool_constant`, and + `Dune::Std::make_array` in favor of std c++ implementations. + +- Deprecate type traits `Dune::Std::to_false_type`, `Dune::Std::to_true_type`. + `Dune::AlwaysFalse` and `Dune::AlwaysTrue` (from header `dune/common/typetraits.hh`) + now inherit from `std::true_type` and `std::false_type` and are therefore + exact replacements for these two type traits. + +- Deprecate fallback implementation `Dune::Std::conjunction`, `Dune::Std::disjunction`, + and `Dune::Std::negation`. Use std c++17 implementations. + +- Deprecate fallback implementations `Dune::Std::is_callable` and `Dune::Std::is_invocable`. + Use C++17 std implementation `std::is_invocable` instead. Be aware that + `Dune::Std::is_callable` and `std::is_invocable` are slightly different concepts, + since `std::is_invocable` also covers invocation of pointers to member functions + and pointers to data members. To additionally constrain for that case, + there is now `Dune::IsCallable` (in `dune/common/typetraits.hh`) + +- Added `Dune::IsCallable` (in `dune/common/typetraits.hh`) which is + an improved version of the deprecated `Dune::Std::is_callable` and allows + for checking if a type is a function object type, + i.e. has a ()-operator than can be invoked with the given argument types and + returns a specified return type. + +- Remove c++ feature tests in cmake for existing c++-17 standards. Add default + defines for `DUNE_HAVE_CXX_BOOL_CONSTANT`, `DUNE_HAVE_CXX_EXPERIMENTAL_BOOL_CONSTANT`, + `DUNE_HAVE_HEADER_EXPERIMENTAL_TYPE_TRAITS`, `DUNE_HAVE_CXX_APPLY`, + `DUNE_HAVE_CXX_EXPERIMENTAL_APPLY`, `HAVE_IS_INDEXABLE_SUPPORT` in `config.h` for one + more release. + +- Add backport of `FindPkgConfig.cmake` from cmake 3.19.4 since there was a bug in + an older find module leading to problems finding tbb in debian:10. + +- Update the FindTBB cmake module to search for the `TBBConfig.cmake` or the `tbb.pc` + file containing the configuration. Add the `AddTBBFlags.cmake` file containing + the macro `add_dune_tbb_flags` that must be called to use TBB. + +- Set minimal required MPI version to >= 3.0. + +- Previous versions of dune-common imported `std::shared_ptr` and `std::make_shared` + into the `Dune` namespace. dune-common-2.8 stops doing that. + +- The file `function.hh` is deprecated. It contained the two base classes + `Function` and `VirtualFunction`. In downstream codes, these should be + replaced by C++ function objects, `std::function` etc. + +- Python bindings have been moved from the `dune-python` module which is now + obsolete. To activate Python bindings the CMake flag + `DUNE_ENABLE_PYTHONBINDINGS` needs to be turned on (default is off). + Furthermore, flags for either shared library or position independent code + needs to be used. + +- Support for distributing DUNE modules as python packages has been added. + Package meta data is parsed in `packagemetadata.py` from the dune.module file. + A script `/bin/dunepackaging.py` was added to generate package files + (`setup.py`, `pyproject.toml`) that can also be used to upload packages to + the Python Package Index. For a brief description of what is required to add + this support to existing dune modules see + https://gitlab.dune-project.org/core/dune-common/-/merge_requests/900 + Note that this can also be used to generate a package for dune modules + that don't provide Python bindings. + +- Eigenvectors of symmetric 2x2 `FieldMatrix`es are now computed correctly + even when they have zero eigenvalues. + +- Eigenvectors and values are now also supported for matrices and + vectors with field_type being float. + +- The `ParameterTreeParser::readINITree` can now directly construct and + return a parameter tree by using the new overload without parameter tree + argument. + +- MPIHelper::instance can now be called without parameters if it was + already initialized. + +- MPITraits now support complex. + +- There is now a matrix wrapper transpose(M) that represents the + transpose of a matrix. + +## build-system + +- The name mangling for Fortran libraries like BLAS and LAPACK is now done + without a Fortran compiler. So a Fortran compiler is no longer a built + requirement. + +- `dune_list_filter` is deprecated and will be removed after Dune 2.8. Use + `list(FILTER ...)` introduced by CMake 3.6 instead. + +- `ToUniquePtr` is deprecated and will be removed after Dune 2.8. Use + `std::unique_ptr` or `std::shared_ptr` instead. + +- Remove the CMake options `DUNE_BUILD_BOTH_LIBS` and + `DUNE_USE_ONLY_STATIC_LIBS`. Use the default CMake way instead by + setting `BUILD_SHARED_LIBS` accordingly. Building both static + and shared libraries is no longer supported. + +- Remove the CMake function deprecated `inkscape_generate_png_from_svg`. + +- Remove the old and deprecated use of UseLATEX.cmake. + `dune_add_latex_document' is a redirection to `add_latex_document` + which internally uses `latexmk`. + +- Many of the CMake find modules habe been rewritten to use CMake's + imported targets. These targets are also used in the DUNE CMake + package configuration files, where they might appear in e.g. the + dune-module_LIBRARIES. If you do not use the DUNE CMake build system + the linker might complain about e.g. METIS::METIS not being + found. In that case your either need to use the CMake modules shipped with + DUNE or create these targets manually. + +## Deprecations and removals + +- Remove deprecated header `dune/common/std/memory.hh`; use `` + instead. + +- Deprecate header `dune/common/std/utility.hh`; use `` instead. + +- Deprecate header `dune/common/std/variant.hh`; use `` instead. + +- Remove incomplete CPack support that was never used to make an official + build or tarball. + +- Both macros `DUNE_DEPRECATED` and `DUNE_DEPRECATED_MSG(text)` are + deprecated and will be removed after Dune 2.8. Use C++14 attribute + `[[deprecated]]` but be aware that it is no drop-in replacement, + as it must be sometimes placed at different position in the code. + +- The macros `DUNE_UNUSED` is deprecated and will be removed after + Dune 2.8. Use C++17's attribute `[[maybe_unused]]` instead, but be + aware that it is no drop-in replacement, as it must be sometimes + placed at different position in the code. + The use of `DUNE_UNUSED_PARAMETER` is discouraged. + +- Dune::void_t has been deprecated and will be removed. Please use + std::void_t + +- Dune::lcd and Dune::gcd are deprecated and will be removed. Please + use std::lcd and std::gcd. + +- VariableSizeCommunicator::fixedsize has been renamed to FixedSize in + line with the communicator changes of dune-grid. The old method will + be removed in 2.9. + +# Release 2.7 + +- Added fallback implementation to C++20 feature: `std::identity`. + +- A helper class `TransformedRangeView` was added representing a + transformed version of a given range using an unary transformation + function. The transformation is done on the fly leaving the wrapped + range unchanged. + +- `dune-common` now provides an implementation of `std::variant` for all compilers + that support C++14. It is contained in the file `dune/common/std/variant.hh`, + in the namespace `Dune::Std::`. If your compiler does support C++17 the + implementation in `dune-common` is automatically disabled, and the official + implementation from the standard library is used instead. + +- By popular demand, dense vectors and matrices like `FieldVector` and `FieldMatrix` + now have additional operators. In particular, there are + - Vector = - Vector + - Matrix = - Matrix + While these two work for any vector or matrix class that inherits from `DenseVector` + or `DenseMatrix`, the following additional methods only work for `FieldVector`: + - Vector = Scalar * Vector + - Vector = Vector * Scalar + - Vector = Vector / Scalar + Correspondingly, the `FieldMatrix` class now has + - Matrix = Matrix + Matrix + - Matrix = Matrix - Matrix + - Matrix = Scalar * Matrix + - Matrix = Matrix * Scalar + - Matrix = Matrix / Scalar + - Matrix = Matrix * Matrix + Note that the operators + - Vector = Vector + Vector + - Vector = Vector - Vector + have been introduced earlier. + +- The matrix size functions `N()` and `M()` of `FieldMatrix` and `DiagonalMatrix` can now be used + in a `constexpr` context. + +- There is now (finally!) a method `power` in the file `math.hh` that computes + powers with an integer exponent, and is usable in compile-time expressions. + The use of the old power methods in `power.hh` is henceforth discouraged. + +- `FieldMatrix` and `FieldVector` are now [trivially copyable types] + if the underlying field type is trivially copyable. + + As a consequence the copy assignment operator of the `DenseVector` + class can no longer be used; just avoid going through + `DenseVector` and use the real vector type instead + (e.g. `FieldVector`). + + [trivially copyable types]: https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable + +## Deprecations and removals + +- The `VectorSize` helper has been deprecated. The `size()` method of + vectors should be called directly instead. + +- Drop support for Python 2. Only Python 3 works with Dune 2.7. + +- Support for older version than METIS 5.x and ParMETIS 4.x is deprecated and will be + removed after Dune 2.7. + +- Deprecated header `dune/common/parallel/collectivecommunication.hh` which will be + removed after Dune 2.7. Use dune/common/parallel/communication.hh instead! + +- Deprecated header `dune/common/parallel/mpicollectivecommunication.hh` which will be + removed after Dune 2.7. Use dune/common/parallel/mpicommunication.hh instead! + +## build-system + +- When run with an absolute build directory, `dunecontrol` now exposes the root build + directory to CMake in the variable `DUNE_BUILD_DIRECTORY_ROOT_PATH`. + + See core/dune-common!542 + +- The `dune_symlink_to_sources_files` CMake function now has a `DESTINATION` argument. + +- Dune no longer applies architecture flags detected by the Vc library + automatically. This applies to all targets that link to Vc explicitly (with + `add_dune_vc_flags()`) or implicitly (with `dune_enable_all_packages()`). + If you do want to make use of extended architecture features, set the + architecture explicitly in the compiler options, e.g. by specifying + ```sh + CMAKE_FLAGS="-DCMAKE_CXX_FLAGS=-march=native" + ``` + in your opts-file. Vc also sets compiler options to select a particular C++ + abi (`-fabi-version` and `-fabi-compat-version`), these continue to be + applied automatically. + + See core/dune-common!677 + +- `FindParMETIS.cmake` assumes METIS was found first using `FindMETIS.cmake` and does not + longer try to find METIS itself. + +- The `inkscape_generate_png_from_svg` CMake function is deprecated and will be removed + after 2.7. + +- LaTeX documents can now be built using `latexmk` with the help of UseLatexmk.cmake's + `add_latex_document`. `dune_add_latex_document` will use the new way of calling + LaTeX when the first argument is `SOURCE`. As a side effect, in-source builds are + supported, too. The old function call and UseLATEX.cmake are deprecated and will be + removed after 2.7. + + See core/dune-common!594 + +- The build system has learned some new tricks when creating or looking for the Python virtualenv: + When using an absolute build directory with `dunecontrol`, the virtualenv will now be placed + directly inside the root of the build directory hierarchy in the directory `dune-python-env`. + This should make it much easier to actually find the virtualenv and also avoids some corner + cases where the build system would create multiple virtualenvs that did not know about each + other. This behavior can be disabled by setting + `DUNE_PYTHON_EXTERNAL_VIRTUALENV_FOR_ABSOLUTE_BUILDDIR=0`. + If you need even more precise control about the location of the virtualenv, you can now also + directly set the CMake variable `DUNE_PYTHON_VIRTUALENV_PATH` to the directory in which to + create the virtualenv. + +# Release 2.6 + +**This release is dedicated to Elias Pipping (1986-2017).** + +- New class `IntegralRange` and free standing function + `range` added, providing a feature similar to Python's `range` function: + ``` + for (const auto &i : range(5,10)) + ``` + See core/dune-common!325 + +- `Dune::array` was deprecated, use `std::array` from instead. + Instead of `Dune::make_array`, use `Dune::Std::make_array` + from dune/common/std/make_array.hh + and instead of `Dune::fill_array` use `Dune::filledArray` + from dune/common/filledarray.hh.` + + See core/dune-common!359 + +- The `DUNE_VERSION...` macros are deprecated use the new macros + `DUNE_VERSION_GT`, `DUNE_VERSION_GTE`, `DUNE_VERSION_LTE`, and + `DUNE_VERSION_LT` instead. + + See core/dune-common!329 + +- Added some additional fallback implementation to C++17 features: + (e.g. `optional`, `conjunction`, `disjunction`) + +- `makeVirtualFunction`: + allows to easily convert any function object (e.g. lambda) to a `VirtualFunction` + + See core/dune-common!282 + +- Added infrastructure for explicit vectorization *(experimental)* + + We added experimental support for SIMD data types. We currently + provide infrastructure to use [Vc](https://github.com/VcDevel/Vc) + and some helper functions to transparently switch between scalar data + types and SIMD data types. + +- `FieldMatrix` now has experimental support for SIMD types from + [Vc](https://github.com/VcDevel/Vc) as field types. + + See core/dune-common!121 + +## build-system + +- Variables passed via `dunecontrol`'s command `--configure-opts=..` are now + added to the CMake flags. + +- Bash-style variables which are passed to `dunecontrol`'s command `configure-opts` + are no longer transformed to their equivalent CMake command. Pass + `-DCMAKE_C_COMPILER=gcc` instead of `CC=gcc`. + +- Added support for modules providing additional Python modules or bindings. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..499676d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.13) +project(dune-common LANGUAGES C CXX) + +# make sure our own modules are found +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules) + +# set the script dir for the macros. +set(DUNE_COMMON_SCRIPT_DIR ${PROJECT_SOURCE_DIR}/cmake/scripts) + +#include the dune macros +include(DuneMacros) + +# start a dune project with information from dune.module +dune_project() + +# add subdirectories to execute CMakeLists.txt there +add_subdirectory(bin) +add_subdirectory(cmake) +add_subdirectory(doc) +add_subdirectory(dune) +add_subdirectory(lib) +add_subdirectory(share) + +# if Python bindings are enabled, include necessary sub directories. +if(DUNE_ENABLE_PYTHONBINDINGS) + add_subdirectory(python) + dune_python_install_package(PATH "python") +endif() + +# finalize the dune project, e.g. generating config.h etc. +finalize_dune_project() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..575f5ec --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,71 @@ +Contributing to the Dune Core Modules +===================================== + +You've squashed an annoying bug or implemented a nifty new feature in DUNE? +And you're willing to share your improvements with the community? This page +explains how to get those changes to us and what to take care of. + +Take a look at the DUNE coding style +------------------------------------ + +Your work will enjoy much smoother sailing if you take a look at the [Coding +Style](https://dune-project.org/dev/codingstyle/) and try to stick to it with +your changes. We understand that everyone has their personal preferences and +that there is no such thing as the *right* coding style (in the end, it's a +matter of taste), but DUNE is a pretty large project, and a consistent way of +doing things really helps a lot when trying to find your way around a body of +code as big as DUNE. + +Make sure to install the Whitespace Hook before starting to work, because +our repositories enforce certain rules about whitespace and will not accept +commits that violate those rules. And a developer will be much more motivated +to merge your patch if doing so does not involve fixing a bunch of tab-based +indentations that you inadvertently added as part of your changes + +Use Git to your advantage +------------------------- + +We know, Git can be a bit daunting at first, but trust us, it's really worth +investing half an hour to learn the basics! Even though you don't have any +commit rights to the DUNE repositories, Git still allows you to create local +commits on your machine, avoiding the usual ugly business of creating backup +copies, copying around code in files, commenting and uncommenting variants etc. +And when you're done and send the changes to us, we can simply import those +commits into our repositories. That saves a lot of time and when your changes +can be applied in five minutes using two or three commands, chances are a +developer will much more easily find the time to do so. Git is really popular, +so there are tons of tutorials all over the web. Here are some pointers: + +* http://try.github.io/ is a very quick, hands-down introduction + to Git that allows you to try out Git directly in your browser. + Requires a GitHub account to continue at some point, though. +* http://git-scm.com/book is a very well-written and detailed resource + for all things Git. Chapter 2 is a great introduction to Git that also explains + a little bit how Git works, which really helps to reduce the number of + *WTF just happened?* moments. ;-) +* http://eagain.net/articles/git-for-computer-scientists/ is a short and + sweet explanation of what Git does at a fundamental level - just the thing for + scientists! ;-) +* http://git-scm.com/doc/ext is a collection of both introductory and + more in-depth Git resources. + +Whatever you do, make sure to set your Git identity so that the commits tell us who authored them! + +Getting the changes to us +------------------------- + +You should get your changes to us in the following way: +* Get an account for [our GitLab instance](http://gitlab.dune-project.org). +* Fork the core module that you want to contribute to, just + as you would do on GitHub. +* Push your changes to your fork on some branch. +* Open a merge request using the branch you pushed your changes + to as the source branch and the master of the core module repository + as the target branch. GitLab will usually ask you about opening + a merge request if you browse it right after pushing to some branch. +* Follow the discussion on the merge request to see what improvements + should be done to the branch before merging. + +If you have any questions or complaints about this workflow of +contributing to Dune, please rant on the +[dune-devel mailing list](mailto:dune-devel@lists.dune-project.org). diff --git a/COPYING b/COPYING new file mode 120000 index 0000000..f0c4298 --- /dev/null +++ b/COPYING @@ -0,0 +1 @@ +LICENSE.md \ No newline at end of file diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..bf49ef6 --- /dev/null +++ b/INSTALL @@ -0,0 +1,75 @@ +Installation Instructions +========================= + +For a full explanation of the DUNE installation process please read +the installation notes [0]. The following introduction is meant for +the impatient. + +Getting started +--------------- + +Suppose you have downloaded all DUNE modules of interest to your +computer and extracted then in one common directory. See [1] for a +list of available modules. + +To compile the modules Dune has to check several components of +your system and whether prerequisites within the modules are met. For +the ease of users we have designed a custom build system on top of CMake. +Run + + ./dune-common/bin/dunecontrol all + +to commence those tests and build all modules you have +downloaded. Don't worry about messages telling you that libraries are +missing: they are only needed for grid-self-checks we need for +developing. + +You can customize the build to your specific needs by using an options file +(see below) + + ./dune-common/bin/dunecontrol --opts=/path_to/file.opts + +If you did not tell dunecontrol to install with an options file you +need to run + + ./dune-common/bin/dunecontrol make install + +to install Dune (you may need root-permissions for the install +part depending on the prefix set) + +A more comprehensive introduction to the build system can be found in [0]. + +Passing options to the build process +------------------------------------ + +Using the dunecontrol script the following atomic commands can be +executed: + +- configure (runs the CMake configuration tests for each module) +- exec (executes a command in each module source directory) +- bexec (executes a command in each module build directory) +- make (builds each module) +- update (updates the Git or Subversion version) + +The composite command all simply runs configure and make for +each module. + +As it is often not convenient to specify the desired options after +the duncontroll call, one can pass the options via a file specified +by the --opts= option. Specify the options via the variable + + CMAKE_FLAGS= + +An example of an options file is + + # use a special compiler (g++ version 5.0), + # install to a custom directory, default is /usr/local/bin, + # disable the external library SuperLU, + # and use Ninja-build instead of make as the build-tool + CMAKE_FLAGS="-DCMAKE_CXX_COMPILER=g++-5 -DCMAKE_INSTALL_PREFIX='/tmp/HuHu' -DCMAKE_DISABLE_FIND_PACKAGE_SuperLU=true -GNinja" + +Links +----- + +0. https://www.dune-project.org/doc/installation +1. https://dune-project.org/releases/ diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..d67bc64 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,443 @@ +Copyright holders: +================== + +2015--2017 Marco Agnese +2015 Martin Alkämper +2003--2019 Peter Bastian +2004--2020 Markus Blatt +2013 Andreas Buhr +2020--2021 Samuel Burbulla +2011--2020 Ansgar Burchardt +2004--2005 Adrian Burri +2014 Benjamin Bykowski (may appear in the logs as "Convex Function") +2014 Marco Cecchetti +2018 Matthew Collins +2006--2021 Andreas Dedner +2019--2021 Nils-Arne Dreier +2003 Marc Droske +2003--2021 Christian Engwer +2004--2020 Jorrit Fahlke +2016 Thomas Fetzer +2008--2017 Bernd Flemisch +2013--2014 Christoph Gersbacher +2017--2020 Janick Gerstenberger +2015 Stefan Girke +2005--2021 Carsten Gräser +2015--2017 Felix Gruber +2010--2021 Christoph Grüninger +2006 Bernhard Haasdonk +2015--2018 Claus-Justus Heine +2015--2020 René Heß +2017--2019 Stephan Hilb +2017--2021 Lasse Hinrichsen +2012--2013 Olaf Ippisch +2020 Patrick Jaap +2020 Liam Keegan +2013--2021 Dominic Kempf +2009 Leonard Kern +2017--2018 Daniel Kienle +2013 Torbjörn Klatt +2003--2021 Robert Klöfkorn +2017--2021 Timo Koch +2005--2007 Sreejith Pulloor Kuttanikkad +2012--2016 Arne Morten Kvarving +2010--2014 Andreas Lauser +2016--2019 Tobias Leibner +2015 Lars Lubkoll +2012--2017 Tobias Malkmus +2007--2011 Sven Marnach +2010--2017 Rene Milk +2019--2020 Felix Müller +2011--2019 Steffen Müthing +2018 Lisa Julia Nebel +2003--2006 Thimo Neubauer +2011 Rebecca Neumann +2008--2018 Martin Nolte +2014 Andreas Nüßing +2004--2005 Mario Ohlberger +2019--2020 Santiago Ospina De Los Rios +2014 Steffen Persvold +2008--2017 Elias Pipping +2021 Joscha Podlesny +2011 Dan Popovic +2017--2021 Simon Praetorius +2009 Atgeirr Rasmussen +2017--2020 Lukas Renelt +2006--2014 Uli Sack +2003--2020 Oliver Sander +2006 Klaus Schneider +2004 Roland Schulz +2015 Nicolas Schwenck +2016 Linus Seelinger +2009--2014 BÃ¥rd Skaflestad +2019 Henrik Stolzmann +2012 Matthias Wohlmuth +2011--2016 Jonathan Youett + +This Licence does not cover the header files taken from the +[pybind11 project][pybind11] which are included here +(`dune/python/pybind11`) together with their own [licence file][pybind11Licence]. + +The DUNE library and headers are licensed under version 2 of the GNU +General Public License (see below), with a special exception for +linking and compiling against DUNE, the so-called "runtime exception." +The license is intended to be similar to the GNU Lesser General +Public License, which by itself isn't suitable for a template library. + +The exact wording of the exception reads as follows: + + As a special exception, you may use the DUNE source files as part + of a software library or application without restriction. + Specifically, if other files instantiate templates or use macros or + inline functions from one or more of the DUNE source files, or you + compile one or more of the DUNE source files and link them with + other files to produce an executable, this does not by itself cause + the resulting executable to be covered by the GNU General Public + License. This exception does not however invalidate any other + reasons why the executable file might be covered by the GNU General + Public License. + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + +[pybind11]: https://github.com/pybind/pybind11 +[pybind11Licence]: https://github.com/pybind/pybind11/blob/master/LICENSE diff --git a/README.md b/README.md new file mode 100644 index 0000000..40a93ea --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +DUNE-library +============ + +DUNE, the Distributed and Unified Numerics Environment is a modular toolbox +for solving partial differential equations with grid-based methods. + +The main intention is to create slim interfaces allowing an efficient use of +legacy and/or new libraries. Using C++ techniques DUNE allows one to use very +different implementation of the same concept (i.e. grid, solver, ...) under +a common interface with a very low overhead. + +DUNE was designed with flexibility in mind. It supports easy discretization +using methods, like Finite Elements, Finite Volume and also Finite +Differences. Through separation of data structures DUNE allows fast Linear +Algebra like provided in the ISTL module, or usage of external libraries +like blas. + +This package contains the basic DUNE common classes. + +Dependencies +------------ + +dune-common depends on the following software packages + +- pkg-config +- Compiler (C, C++): GNU >=7 or Clang >= 5 + + Other compilers might work too, they need to support C++17 to the extend the + ones above do. + +The following software is recommended but optional: + +- MPI (either OpenMPI, lam, or mpich suffice) + +For a full explanation of the DUNE installation process please read +the [installation notes][installation]. The following introduction is meant for +the impatient. + +License +------- + +The DUNE-library and headers are licensed under version 2 of the GNU +General Public License, with the so-called "runtime exception", as +follows: + +> As a special exception, you may use the DUNE source files as part +> of a software library or application without restriction. +> Specifically, if other files instantiate templates or use macros or +> inline functions from one or more of the DUNE source files, or you +> compile one or more of the DUNE source files and link them with +> other files to produce an executable, this does not by itself cause +> Athe resulting executable to be covered by the GNU General Public +> License. This exception does not however invalidate any other +> reasons why the executable file might be covered by the GNU General +> Public License. + +This licence clones the one of the libstc++ library. For further +implications of this library please see their [licence page][licence] + +See the file COPYING for full copying permissions. + +Installation +------------ + +Short installation instructions can be found in file INSTALL. For the +full instructions please see [here][installation]. + +Links +----- + +0. https://www.dune-project.org/doc/installation +1. https://dune-project.org/releases/ +2. https://dune-project.org/buildsystem/ +3. http://gcc.gnu.org/onlinedocs/libstdc++/faq.html#faq.license + +[installation]: https://www.dune-project.org/doc/installation +[licence]: http://gcc.gnu.org/onlinedocs/libstdc++/faq.html#faq.license \ No newline at end of file diff --git a/TODO b/TODO new file mode 100644 index 0000000..2303a56 --- /dev/null +++ b/TODO @@ -0,0 +1,2 @@ +Please see the Dune bugtracker at www.dune-project.org for things to do. + diff --git a/bin/CMakeLists.txt b/bin/CMakeLists.txt new file mode 100644 index 0000000..04ab0d0 --- /dev/null +++ b/bin/CMakeLists.txt @@ -0,0 +1,9 @@ +install(PROGRAMS + dune-ctest + duneproject + dunecontrol + dunepackaging.py + dune-git-whitespace-hook + rmgenerated.py + setup-dunepy.py + DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/bin/dune-ctest b/bin/dune-ctest new file mode 100755 index 0000000..3f9dfdb --- /dev/null +++ b/bin/dune-ctest @@ -0,0 +1,227 @@ +#! /usr/bin/env python3 +# +# Wrapper around CTest for DUNE +# +# CTest returns with an error status not only when tests failed, but also +# when tests were only skipped. This wrapper checks the log and returns +# successfully if no tests failed; skipped tests do not result in an error. +# This behaviour is needed in a continuous integration environment, when +# building binary packages or in other cases where the testsuite should be +# run automatically. +# +# Moreover, this script also converts the XML test report generated by CTest +# into a JUnit report file that can be consumed by a lot of reporting +# software. +# +# Author: Ansgar Burchardt +# Author: Steffen Müthing (for the JUnit part) + +import errno +import glob +import os.path +import shutil +import subprocess +import sys +import xml.etree.ElementTree as et +from pathlib import Path +import os +import re + + +class CTestParser: + + def findCTestOutput(self): + files = glob.glob("Testing/*/Test.xml") + if len(files) != 1: + fn = files.join(", ") + raise Exception("Found multiple CTest output files: {}".format(files.join(", "))) + return files[0] + + def printTest(self,test,output=None): + status = test.get("Status") + name = test.find("Name").text + fullName = test.find("FullName").text + if output is not None: + output = test.find("Results").find("Measurement").find("Value").text + + print("======================================================================") + print("Name: {}".format(name)) + print("FullName: {}".format(fullName)) + print("Status: {}".format(status.upper())) + if output: + print("Output:") + for line in output.splitlines(): + print(" ", line) + print() + + def __init__(self,junitpath=None): + self.inputpath = self.findCTestOutput() + if junitpath is None: + if "CI_PROJECT_DIR" in os.environ: + buildroot = Path(os.environ["CI_PROJECT_DIR"]) + # create a slug from the project name + name = os.environ["CI_PROJECT_NAME"].lower() + name = re.sub(r"[^-a-z0-9]","-",name); + junitbasename = "{}-".format(name) + else: + buildroot = Path.cwd() + junitbasename = "" + junitdir = buildroot / "junit" + junitdir.mkdir(parents=True,exist_ok=True) + self.junitpath = junitdir / "{}cmake.xml".format(junitbasename) + else: + self.junitpath = Path(junitpath) + junitdir = junitpath.resolve().parent + junitdir.mkdir(parents=True,exist_ok=True) + self.tests = 0 + self.passed = 0 + self.failures = 0 + self.skipped = 0 + self.errors = 0 + self.skipped = 0 + self.time = 0.0 + + def createJUnitSkeleton(self): + self.testsuites = et.Element("testsuites") + self.testsuite = et.SubElement(self.testsuites,"testsuite") + self.properties = et.SubElement(self.testsuite,"properties") + + def fillJUnitStatistics(self): + self.testsuite.set("name","cmake") + self.testsuite.set("tests",str(self.tests)) + self.testsuite.set("disabled","0") + self.testsuite.set("errors",str(self.errors)) + self.testsuite.set("failures",str(self.failures)) + self.testsuite.set("skipped",str(self.skipped)) + self.testsuite.set("time",str(self.time)) + + def processTest(self,test): + testcase = et.SubElement(self.testsuite,"testcase") + testcase.set("name",test.find("Name").text) + testcase.set("assertions","1") + testcase.set("classname","cmake") + time = test.find("./Results/NamedMeasurement[@name='Execution Time']/Value") + if time is not None: + self.time += float(time.text) + testcase.set("time",time.text) + self.tests += 1 + outcome = test.get("Status") + if outcome == "passed": + testcase.set("status","passed") + self.passed += 1 + elif outcome == "failed": + self.failures += 1 + testcase.set("status","failure") + failure = et.SubElement(testcase,"failure") + failure.set("message","program execution failed") + failure.text = test.find("./Results/Measurement/Value").text + self.printTest(test) + elif outcome == "notrun": + # This does not exit on older CMake versions, so work around that + try: + status = test.find("./Results/NamedMeasurement[@name='Completion Status']/Value").text + if status == "SKIP_RETURN_CODE=77": + self.skipped += 1 + et.SubElement(testcase,"skipped") + elif status == "Required Files Missing": + self.errors += 1 + error = et.SubElement(testcase,"error") + error.set("message","compilation failed") + error.set("type","compilation error") + self.printTest(test,output="Compilation error") + else: + error = et.SubElement(testcase,"error") + error.set("message","unknown error during test execution") + error.set("type","unknown") + error.text = test.find("./Results/Measurement/Value").text + self.errors += 1 + self.printTest(test) + except AttributeError: + output_tag = test.find("./Results/Measurement/Value") + if output_tag is not None: + msg = output_tag.text + if "skipped" in msg: + self.skipped += 1 + et.SubElement(testcase,"skipped") + elif "Unable to find required file" in msg: + self.errors += 1 + error = et.SubElement(testcase,"error") + error.set("message","compilation failed") + error.set("type","compilation error") + self.printTest(test,output="Compilation error") + else: + error = et.SubElement(testcase,"error") + error.set("message","unknown error during test execution") + error.set("type","unknown") + error.text = msg + self.errors += 1 + self.printTest(test) + else: + error = et.SubElement(testcase,"error") + error.set("message","unknown error during test execution") + error.set("type","unknown") + error.text = "no message" + self.errors += 1 + self.printTest(test) + + output_tag = test.find("./Results/Measurement/Value") + if output_tag is not None: + out = et.SubElement(testcase,"system-out") + out.text = output_tag.text + + def process(self): + + with open(self.inputpath, "r", encoding="utf-8") as fh: + tree = et.parse(fh) + + root = tree.getroot() + + self.createJUnitSkeleton() + + for test in root.findall(".//Testing/Test"): + self.processTest(test) + + self.fillJUnitStatistics() + + with self.junitpath.open("wb") as fh: + fh.write(et.tostring(self.testsuites,encoding="utf-8")) + print("JUnit report for CTest results written to {}".format(self.junitpath)) + + return self.errors + self.failures + + +def runCTest(argv=[]): + cmd = ["ctest", + "--output-on-failure", + "--dashboard", "ExperimentalTest", + "--no-compress-output", + ] + cmd.extend(argv) + subprocess.call(cmd) + +def checkDirectory(): + if not os.path.exists("CMakeCache.txt"): + raise Exception("ERROR: dune-ctest must be run in a cmake build directory") + +def removeCTestOutput(): + try: + shutil.rmtree("Testing") + except OSError as e: + if e.errno != errno.ENOENT: + raise + +def main(): + try: + checkDirectory() + removeCTestOutput() + runCTest(argv=sys.argv[1:]) + parser = CTestParser() + errors = parser.process() + status = 0 if errors == 0 else 1 + sys.exit(status) + except Exception as e: + print("Internal error: {}".format(e)) + sys.exit(127) + +if __name__ == "__main__": + main() diff --git a/bin/dune-git-whitespace-hook b/bin/dune-git-whitespace-hook new file mode 100755 index 0000000..a8bd344 --- /dev/null +++ b/bin/dune-git-whitespace-hook @@ -0,0 +1,169 @@ +#!/bin/sh +# dune-git-whitespace-hook +# DO NOT TOUCH THE PRECEDING LINE +# It is used by dunecontrol to enable automatic updates of the whitespace hook + +# DUNE pre-commit hook to enforce whitespace policy +# This hook prevents adding lines with trailing whitespace and or tab characters +# in line indentation for certain files (see the TRAILING_WHITESPACE_DEFAULT and +# TAB_IN_INDENT_DEFAULT variables below for the default sets of files that will +# be checked). +# You can tell the hook which files should be inspected by setting the Git +# configuration variables "hooks.whitespace.trailing" and "hooks.whitespace.tabinindent". +# Those variables should contain valid Perl regular expressions. The names of modified +# files will be matched against those regexes. + +# git-diff-index needs a valid commit to compare to +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + + +# By default, we disallow trailing whitespace for the following files, but the check for C/C++ and CMake sources +# happens in the tab-in-indent check to avoid confusing users with duplicate error messages +TRAILING_WHITESPACE_DEFAULT='^(dune\.module|README|README\.SVN|COPYING|INSTALL|TODO)$|^[^/]*(\.md|\.pc\.in)$|^doc/.*\.md$' + +# By default, we disallow tabs in indents and trailing whitespace in C/C++ and CMake source files +TAB_IN_INDENT_DEFAULT='(^|/)CMakeLists\.txt$|(\.cpp|\.hpp|\.cc|\.hh|\.c|\.h|\.cmake|\.sh|\.py)$' + +# Get user preferences +TRAILING_WHITESPACE_FILES=$(git config hooks.whitespace.trailing) + +# Set default regex for disallowing trailing whitespace if the user did not set anything. +# We need to check the return value of git-config to distinguish the case +# when the user set an empty value +if [ $? -ne 0 ]; +then + TRAILING_WHITESPACE_FILES="$TRAILING_WHITESPACE_DEFAULT" +fi + + +TAB_IN_INDENT_FILES=$(git config hooks.whitespace.tabinindent) + +# Set default regex for disallowing tabs if the user did not set anything. +# We need to check the return value of git-config to distinguish the case +# when the user set an empty value +if [ $? -ne 0 ]; +then + TAB_IN_INDENT_FILES="$TAB_IN_INDENT_DEFAULT" +fi + + +# Unfortunately, we have to mess directly with the repository config file, +# as git won't honor a custom config file specified by GIT_CONFIG + +# backup repository-local user setting for core.whitespace +USER_WHITESPACE=$(git config --local --get core.whitespace) +if [ $? -ne 0 ]; +then + USER_HAS_CUSTOM_WHITESPACE=0 +else + USER_HAS_CUSTOM_WHITESPACE=1 +fi + +# Figure out how to call xargs to make sure it won't invoke its argument with +# an empty argument list. BSD xargs will not do that by default, while GNU xargs +# needs -r to do the same. So we start by checking whether xargs does the right +# thing without options. Now there could be other obscure versions of xargs out +# there (on clusters etc.) that behave in yet another way, so we try with -r as +# well. If that fails, we throw a big error message at the user. + +# In the following line, xargs should not call false, so the return value should be 0. +echo "" | xargs false + +if [ $? -ne 0 ]; then + # Let's try with -r + echo "" | xargs -r false + if [ $? -ne 0 ]; then + # Houston, we have a problem + if [ -z "$DUNE_WHITESPACE_IGNORE_XARGS" ]; then + echo "You seem to be lacking a version of xargs that is compatible to either BSD or GNU!" 1>&2 + echo "Please file a bug report at http://dune-project.org about this issue with your exact operating system type and version!" 1>&2 + echo "You can still use this hook by setting the environment variable DUNE_WHITESPACE_IGNORE_XARGS to 1, but please be aware" 1>&2 + echo "that the hook might create false positives." 1>&2 + echo "==============================================================" 1>&2 + echo "Aborting the commit..." 1>&2 + exit 99 + else + SILENTXARGS=xargs + fi + else + SILENTXARGS="xargs -r" + fi +else + SILENTXARGS=xargs +fi + + +fail=0 +done=0 + +do_cleanup() +{ + + if [ $done -ne 1 ]; + then + echo "Error while executing whitespace checking pre-commit hook!" 1>&2 + echo "There might still be whitespace errors in your commit!" 1>&2 + fi + + if [ $USER_HAS_CUSTOM_WHITESPACE -eq 1 ]; + then + git config --replace-all core.whitespace "$USER_WHITESPACE" + else + git config --unset core.whitespace + fi + + # be nice and let the commit go through if something went wrong along the + # way and we did not record a failure + exit $fail +} + +trap do_cleanup EXIT + +# set custom value +git config --replace-all core.whitespace trailing-space + +if [ -z "$TRAILING_WHITESPACE_FILES" ]; +then + git diff-index --check --cached $against -- + result=$? +else + export TRAILING_WHITESPACE_FILES + git diff-index --cached --name-only $against \ + | perl -ne 'print if /$ENV{TRAILING_WHITESPACE_FILES}/' \ + | $SILENTXARGS git diff-index --check --cached $against -- + result=$? +fi + +if [ $result -ne 0 ]; +then + fail=1 +fi + +git config --replace-all core.whitespace trailing-space,tab-in-indent + +if [ -z "$TAB_IN_INDENT_FILES" ]; +then + git diff-index --check --cached $against -- + result=$? +else + export TAB_IN_INDENT_FILES + git diff-index --cached --name-only $against \ + | perl -ne 'print if /$ENV{TAB_IN_INDENT_FILES}/' \ + | $SILENTXARGS git diff-index --check --cached $against -- + result=$? +fi + +if [ $result -ne 0 ]; +then + fail=1 +fi + +done=1 + +# trap will call the cleanup code diff --git a/bin/dunecontrol b/bin/dunecontrol new file mode 100755 index 0000000..6b6254d --- /dev/null +++ b/bin/dunecontrol @@ -0,0 +1,1095 @@ +#!/usr/bin/env bash + +set -e + +############################################### +### +### check for environment variables +### +if test -z "$GREP"; then + GREP=grep +fi +if test -z "$SED"; then + SED=sed +fi + +if test -z "$MAKE"; then + MAKE=make +fi + +system_default_cmake="no" +if test -z "$CMAKE"; then + CMAKE=cmake + system_default_cmake="yes" +fi + +space=" " +tab=" " +BLANK="$space$tab" +nl=$'\n' + +############################################### +### +### read lib +### + +# quote parameter for passing it through the shell +# the result is meant to be used inside "...", i.e. the outer quotes are _not_ +# included +# +# you should have the identity +# eval "[ x\"\$param\" = x\"$(doublequote "$param")\" ]" +doublequote() +{ + local val="$1" + local result= + local token= + local special= + while [ -n "$val" ]; do + token=${val%%[\"\$\`\\]*} + val=${val#"$token"} + special=${val:0:1} + val=${val:1} + result=$result$token${special:+\\$special} + done + # if the value ends in \n, protect it by appending two double-quotes so it + # wont get stripped by the surrounding $(...) + case $result in + *"$nl") result=$result'""';; + esac + printf "%s" "$result" +} + +canonicalname(){ + if test $# -ne 1; then + echo Usage: canonicalname path >&2 + return 1 + fi + file="`eval echo $1`" # expand ~ + if test ! -e "$file"; then + echo $file: file not found >&2 + return 1 + fi + # if this is a symlink, then follow the symlink + if test -L "$file"; then + fdir="`dirname \"$file\"`" + flink="`readlink \"$file\"`" + if test -e "$flink"; then + # these are absolute links, or links in the CWD + canonicalname "$flink" + else + canonicalname "$fdir/$flink" + fi + else + # if this is a file, then remember the filename and + # canonicalize the directory name + if test -f "$file"; then + fdir="`dirname \"$file\"`" + fname="`basename \"$file\"`" + fdir="`canonicalname \"$fdir\"`" + echo "$fdir/$fname" + fi + # if this is a directory, then create an absolute + # directory name and we are done + if test -d "$file"; then + (cd "$file"; pwd) + fi + fi +} + +canonicalpath(){ + if test $# -ne 1; then + echo Usage: canonicalpath path >&2 + return 1 + fi + dirname "`canonicalname "$1"`" +} + +checkdebug () { + while test $# -gt 0; do + if test x$1 = x--debug; then + echo yes + return + fi + shift + done + echo no +} + +DEBUG=`checkdebug $@` +if test "x$DEBUG" = "xyes"; then + set -x + set -v +fi + + +onbuildfailure() { + echo "Terminating $(basename "$0") due to previous errors!" >&2 + exit 1 +} + +# +# for each module load the $CONTROL script part and run $command +# +# parameters: +# $1 list of modules +# $2-$* commands + parameters to execute +# +build_module() { + local module=$1 + shift + while test $# -gt 0; do + # get command + command=$1 + shift + + # only load other parameters + load_opts NONE + # get command options + CMD_FLAGS= + while test $# -gt 0 && test "$1" != ":"; do + COMMAND=$(echo $command | tr '[:lower:]' '[:upper:]') + # setup parameter list + CMD_FLAGS="$CMD_FLAGS \"$(doublequote "$1")\"" + shift + done + if test -z "$CMD_FLAGS"; then + load_opts $command + else + # disable usage of opts file + if test "x$DUNE_OPTS_FILE" != "x"; then + echo "WARNING: commandline parameters will overwrite setting in opts file \"$DUNE_OPTS_FILE\"" + fi + fi + + # skip command delimiter + if test "$1" = ":"; then shift; fi + + # actually run the commands (we already know that these are valid commands) + local runcommand=run_$command + + # build the modules + local path="$(eval "echo \$PATH_${module}")" + eval echo "--- calling $command for \$NAME_${module} ---" + trap onbuildfailure EXIT + if ! ( + set -e + cd "$path" + export module + export ABS_BUILDDIR=$(abs_builddir $module $BUILDDIR) + eval_control $runcommand "$path/$CONTROL" + ); then eval echo "--- Failed to build \$NAME_${module} ---"; exit 1; fi + trap onfailure EXIT + + eval echo "--- \$NAME_${module} done ---" + done +} + +# +# load command options from an opts file +# the name of the opts file is stored in the global variable $DUNE_OPTS_FILE +# +# parameters: +# $1 command +# +load_opts() { + local command=$1 + local COMMAND=$(echo $command | tr '[:lower:]' '[:upper:]') + CMD_FLAGS=$(eval echo \$${COMMAND}_FLAGS) + local CMD_FLAGS_FROM_FILE="" + if test "$command" = "NONE"; then + BUILDDIR=$DUNE_BUILDDIR + if test "x$DUNE_OPTS_FILE" != "x"; then + if test -z "$BUILDDIR"; then + # no builddir set yet, use build dir from opts file if set + # Note: if --use-buiddir is used BUILDDIR will be set already + OPTS_FILE_BUILDDIR="$(eval BUILDDIR=""; . $DUNE_OPTS_FILE; eval echo \$BUILDDIR)" + if test -n "$OPTS_FILE_BUILDDIR"; then + BUILDDIR="$OPTS_FILE_BUILDDIR" + fi + fi + if test "$system_default_cmake" = "yes"; then + # We use cmake for building, but CMAKE is not yet set. + # Check the opts file for it + OPTS_FILE_CMAKE="$(eval CMAKE=""; . $DUNE_OPTS_FILE; eval echo \$CMAKE)" + if test -n "$OPTS_FILE_CMAKE"; then + CMAKE="$OPTS_FILE_CMAKE" + fi + fi + fi + fi + if test "x$DUNE_OPTS_FILE" != "x"; then + if test "$command" = "configure"; then + CMAKE_FLAGS="$(. $DUNE_OPTS_FILE; eval echo \$CMAKE_FLAGS)" + CMAKE_MODULE_PATH="$(. $DUNE_OPTS_FILE; eval echo \$CMAKE_MODULE_PATH)" + fi + CMD_FLAGS_FROM_FILE="$(eval ${COMMAND}_FLAGS=""; . $DUNE_OPTS_FILE; eval echo \$${COMMAND}_FLAGS)" + fi + if test -n "$CMD_FLAGS_FROM_FILE"; then + echo "----- using default flags \$${COMMAND}_FLAGS from $DUNE_OPTS_FILE -----" + CMD_FLAGS=$CMD_FLAGS_FROM_FILE + elif test -n "$CMD_FLAGS"; then + echo "----- using default flags \$${COMMAND}_FLAGS from environment -----" + fi + + # if no build directory is set, use default "build-cmake" + if test -z "$BUILDDIR"; then + export BUILDDIR=build-cmake + fi +} + +abs_builddir() +{ + local m=$1 + local builddir=$2 + local name="$(eval echo \$NAME_$m)" + local path="$(eval echo \$PATH_$m)" + case $BUILDDIR in + /*) + echo $builddir/$name + ;; + *) + echo $path/$builddir + ;; + esac +} + +# Uses the current compiler to extract information about the +# multiarch triplets and sets the export variable MULTIARCH_LIBDIR +# according to it. +# If no compiler is specified then cc or gcc is used. +extract_multiarch(){ + set +e #error in the multiarch detection should not be fatal. + local my_cxx_compiler + if test "x$MULTIARCH_LIBDIR" != "x"; then + return + fi + load_opts "cmake" + if test "x$my_cxx_compiler" == "x"; then + load_opts "configure" + fi + my_cxx_compiler=`echo $CMD_FLAGS | $GREP CXX | $SED "s/.*CXX=[\"']\{0,1\}\([^$BLANK'\"]*\)[\"']\{0,1\}.*/\1/"` + if test "x$my_cxx_compiler" == "x"; then + $(which cc &>/dev/null) + if test $? -eq "0"; then + my_cxx_compiler=cc + else + my_cxx_compiler=gcc + fi + fi + multiarch=$($my_cxx_compiler --print-multiarch 2>/dev/null) + if test $? -gt 0; then + for i in "target=" "Target:"; do + multiarch=$($my_cxx_compiler -v 2>&1| $GREP "$i" | $SED "s/.*$i[$BLANK]*\([a-z0-9_-]*\)/\1/" | $SED "s/-[a-z]*-linux-gnu/-linux-gnu/") + if test -n "$multiarch"; then break; fi + done + fi + set -e # set to old value. + export MULTIARCH_LIBDIR="lib/$multiarch" +} + +export PREFIX_DIR="`canonicalpath "$0"`/.." + +# Read the modules find part +. "$PREFIX_DIR/lib/dunemodules.lib" + +############################################### + + +############################################### +### +### Commands +### + +# check all parameter +check_commands() { + while test $# -gt 0; do + # get command + command=$1 + shift + # skip command options + while test $# -gt 0 && test "$1" != ":"; do + shift + done + # skip command delimiter + if test "$1" = ":"; then shift; fi + # test the commands + if ! is_command $command; then + usage + echo "ERROR: unknown command \"$command\"" >&2 + exit 1 + fi + done +} + +# check whether the parameter is valid command or not +is_command() { +eval ' +case "$1" in + '`echo $COMMANDS | $SED -e 's/ / | /g'`') + return 0 + ;; + *) + return 1 + ;; +esac' +} + +# list of all dunecontrol commands +COMMANDS="printdeps vcsetup update cmake configure make all exec bexec status svn git" + +# list of dunecontrol commands for which the version check is skipped by default +COMMANDSTOSKIPVERSIONCHECK="update status svn git exec bexec" + +# help string for the commands +printdeps_HELP="print recursive dependencies of a module" +vcsetup_HELP="setup version control repository (Git etc.) or working copy (SVN)" +update_HELP="update all modules from their repositories" +cmake_HELP="run cmake for each module" +configure_HELP="${cmake_HELP}" +make_HELP="build each module" +all_HELP="\trun 'vcsetup', 'configure' and 'make' command for each module" +exec_HELP="execute an arbitrary command in each module source directory" +bexec_HELP="execute an arbitrary command in each module build directory" +status_HELP="show vc status for all modules" +svn_HELP="\trun svn command for each svn managed module" +git_HELP="\trun git command for each git managed module" + +# +# setup command proxies +# call will be forwarded to run_default_$command +# + +for command in $COMMANDS; do + eval "run_$command () { run_default_$command; }" +done + +# +# default implementations for commands... +# these can be overwritten in the $CONTROL files +# + +run_default_exec () { bash -c "eval $CMD_FLAGS"; } + +run_default_bexec () { + if test -d "$ABS_BUILDDIR"; then + bash -c "cd \"$ABS_BUILDDIR\" && eval $CMD_FLAGS"; + else + eval echo "Build directory \\\"$ABS_BUILDDIR\\\" not found, skipping bexec for \$NAME_${module}" + fi +} + +run_default_status () { + local verbose=0 + local update="" + local is_git="" + local is_svn="" + name="$(eval echo \$NAME_$module)" + + if test -e .git; then is_git=1; fi + if test -d .svn; then is_svn=1; fi + if test ! "$is_svn" -a ! "$is_git" ; then + echo "module $name not under known version control" + return + fi + + for i in $CMD_FLAGS; do + if eval test "x$i" = "x-v"; then verbose=1; fi + if eval test "x$i" = "x-vv"; then verbose=2; fi + if eval test "x$i" = "x-u"; then update="-u"; fi + done + # is out output connected to a tty? + if test -t 1; then + blue="\e[1m\e[34m" + green="\e[1m\e[32m" + red="\e[1m\e[31m" + reset="\e[0m\e[0m" + fi + + if test $verbose -eq 1; then + test "$is_svn" && svn status $update | $GREP -E "^M|^A|^D|^C|^U" + test "$is_git" && git status -uno + elif test $verbose -eq 2; then + test "$is_svn" && svn status $update + test "$is_git" && git status + fi + + + if test "$is_svn" ; then + changed=$(svn status | $GREP -E "^M|^A|^D" | wc -l) + collisions=$(svn status | $GREP -E "^C"| wc -l) + pending=$(svn status $update | $GREP -E "^...... \* " | wc -l) + fi + if test "$is_git" ; then + changed=$(git status --porcelain | $GREP -E "^ *M|^ *A|^ *D|^ *R|^ *C" | wc -l) + collisions=$(git status --porcelain | $GREP -E "^ *U"| wc -l) + pending=$(git status | $GREP -E "^\# Your branch is ahead |^\# Your branch is behind " | wc -l) + fi + color=$green + text="no changes" + if [ $changed -eq 0 ]; then + true + elif [ $changed -eq 1 ]; then + color=$blue; + text="1 change" + else + color=$blue; + text="$changed changes" + fi + if [ $pending -eq 0 ]; then + true + elif [ $pending -eq 1 ]; then + color=$blue; + text="$text, 1 update pending" + else + color=$blue; + text="$text, $pending updates pending" + fi + if [ $collisions -eq 0 ]; then + true + elif [ $collisions -eq 1 ]; then + color=$red + text="$text, 1 collision" + else + color=$red + text="$text, $count collisions" + fi + echo -e "$color[$text]$reset $name" +} + +run_default_vcsetup() { + # load user options + if [ -n "$CMD_FLAGS" ]; then + eval "$CMD_FLAGS" + fi + + # Check for both a file and a directory to cope with Git submodules + if [ -e .git ] ; then + + # Read Whitespace-Hook setting from dune.module file + local SETUPGITHOOK="$($GREP -i "^[$BLANK]*Whitespace-Hook:" dune.module | cut -d ':' -f2 | eval $PARSER_TRIM | tr '[:upper:]' '[:lower:]')" + + if [ "x$SETUPGITHOOK" = "xyes" ]; then + # we have to install the Git whitespace hook + + # There are several options as to what the current worktree might be backed by right now: + # - a plain old repository -> copy to .git/hooks + # - a submodule repository -> .git refers to the submodule repository inside the root $GIT_DIR + # - a git repo worktree -> .git refers to the worktree data inside the original $GIT_DIR and we need to find + # the "canonical" $GIT_DIR root + # - a submodule in a worktree -> That just gets checked out again into the worktree backing store, so no worktree + # dereferencing here + + # We try this first by trying git's built-in infrastructure, but if the user's git is too old, we fall back to + # manual parsing + + GITHOOKPATH="$(git rev-parse --git-common-dir)" + if [ $? -ne 0 -o "${GITHOOKPATH}" = "--git-common-dir" ] ; then + + # no worktree support from here on out + + GITHOOKPATH="$(git rev-parse --git-dir)" + + if [ $? -ne 0 -o "${GITHOOKPATH}" = "--git-dir" ] ; then + + # do the parsing manually + + if [ -f .git ] ; then + # submodule -> .git contains a pointer to the repository + GITHOOKPATH="$($SED 's/gitdir: //' < .git)" + else + # standard case, .git is the repository + GITHOOKPATH=.git + fi + fi + fi + GITHOOKPATH="${GITHOOKPATH}/hooks/pre-commit" + + if [ -n "$DISABLEWHITESPACEHOOK" ] ; then + # the user doesn't want the Git whitespace hook - deinstall it if necessary and warn the user + echo "WARNING: The current module wants to install the DUNE whitespace hook, but you have disabled the hook in your options!" + echo "WARNING: You will have to make sure that your commits don't introduce any trailing whitespace or indentation with tabs!" + echo "WARNING: Otherwise, your commits might be rejected when trying to push them to an official repository!" + + if [ -e "$GITHOOKPATH" ]; then + # there is a pre-commit hook, check whether it is our whitespace hook + local HOOKTAG="$(eval head -n 2 \"$GITHOOKPATH\" | tail -n 1)" + if [ "x$HOOKTAG" = "x# dune-git-whitespace-hook" ]; then + echo "--> Removing DUNE whitespace hook as requested by the user" + rm "$GITHOOKPATH" + fi + fi + else + # standard handling of Git whitespace hook + for f in dune-git-whitespace-hook git-whitespace-hook; do + f="${PREFIX_DIR}/bin/${f}" + if [ -e "${f}" ]; then + git_whitespace_hook="${f}" + break + fi + done + if [ -z "${git_whitespace_hook:-}" ]; then + echo "Did not find git-whitespace-hook." >&2 + exit 1 + fi + if [ ! -e "$GITHOOKPATH" ]; then + # there is no hook yet, we can safely install ours + echo "--> Installing Git pre-commit hook to enforce whitespace policy" + cp -p "${git_whitespace_hook}" "$GITHOOKPATH" + else + # there is already a hook, check whether it is our whitespace hook + local HOOKTAG="$(eval head -n 2 \"$GITHOOKPATH\" | tail -n 1)" + if [ "x$HOOKTAG" = "x# dune-git-whitespace-hook" ]; then + if [ "${git_whitespace_hook}" -nt "$GITHOOKPATH" ]; then + echo "--> Updating Git pre-commit hook with newer version" + cp -p "${git_whitespace_hook}" "$GITHOOKPATH" + fi + else + echo "WARNING: Existing pre-commit hook found!" + echo "WARNING: Skipping installation of DUNE whitespace hook!" + echo "WARNING: If you want to contribute patches to DUNE, you should make sure to call the whitespace hook" + echo "WARNING: (dune-common/bin/git-whitespace-hook) from you custom pre-commit hook, otherwise your commits" + echo "WARNING: might contain trailing whitespace and will not apply cleanly to the official repositories!" + fi + fi + fi + fi + + # Apply git configuration settings + if [ -f .vcsetup/config ]; then + echo -n "--> Setting Git configuration entries... " + cat .vcsetup/config | while read; do + # Filter out comments + local COMMENT="$(echo $REPLY | $GREP '^#')" + if [ ! "x$COMMENT" = "x$REPLY" ]; then + # parse line into an array first to catch obvious syntax errors + # like 'option value; rm -rf /' + eval local GIT_ARGS=($REPLY) + git config "${GIT_ARGS[@]}" + fi + done + echo "done" + fi + + # Apply user supplied configuration settings + if [ -n "$GIT_CONFIG_FILE" ]; then + if [ -f "$GIT_CONFIG_FILE" ]; then + echo -n "--> Setting custom Git configuration entries from '$GIT_CONFIG_FILE'... " + cat "$GIT_CONFIG_FILE" | while read; do + # Filter out comments + local COMMENT="$(echo $REPLY | $GREP '^#')" + if [ ! "x$COMMENT" = "x$REPLY" ]; then + # parse line into an array first to catch obvious syntax errors + # like 'option value; rm -rf /' + eval local GIT_ARGS=($REPLY) + git config "${GIT_ARGS[@]}" + fi + done + echo "done" + else + echo "WARNING: custom Git config file '$GIT_CONFIG_FILE' not found!" + fi + fi + + fi + + # Run custom setup scripts + if [ -e .git -o -d .svn ]; then + if [ -d .vcsetup/run.d ]; then + for SCRIPT in .vcsetup/run.d/* ; do + if [ -x "$SCRIPT" ]; then + echo "--> Running $SCRIPT" + "$SCRIPT" + fi + done + fi + fi +} + +run_default_update () { + if test -d .svn; then + svn update + elif test -e .git; then + if test -d .git && test -d ".git/svn" && test -n "`git svn find-rev HEAD`"; then + # If the current HEAD points to a SVN commit, update via git-svn + git svn rebase + else + # Update all remotes (if any) + git remote update + + # merge all changes fast-forward style if possible + if ! git merge --ff-only FETCH_HEAD 2> /dev/null; then + eval echo "\$NAME_${module} seems to be using git, and could not be" + echo "updated automatically. Please update it manually." + echo "(Usually, this is done via 'git svn rebase' for modules using" + echo "subversion or 'git merge' for modules which use git natively." + echo "Conflicts can be resolved using 'git mergetool'.)" + fi + fi + else + eval echo "WARNING: \$NAME_${module} is not under a known version control system." + echo " We support svn and git." + fi +} + +run_default_cmake () { + extract_multiarch + + # tell CMake about the build directory root when we are using an absolute build directory + if [[ ${BUILDDIR} = /* ]] ; then + CMAKE_PARAMS="$CMAKE_PARAMS -DDUNE_BUILD_DIRECTORY_ROOT_PATH='${BUILDDIR}'" + fi + + # add arguments given as configure-opts to CMAKE_params + CMAKE_PARAMS="$CMAKE_PARAMS $CMD_FLAGS" + + # get dependencies & suggestions + sort_modules $module + for m in $MODULES; do + path="$(eval "echo \$PATH_$m")" + + # add other module's build dir to path + if [ $module != $m ] ; then + name=$(eval "echo \$NAME_$m") + local m_ABS_BUILDDIR=$(abs_builddir $m $BUILDDIR) + + if test -d "$m_ABS_BUILDDIR"; then + CMAKE_PARAMS="$CMAKE_PARAMS \"-D""$name""_DIR=$m_ABS_BUILDDIR\"" + else + TMP_PARAMS="\"-D""$name""_DIR=$path\"" + for i in $MULTIARCH_LIBDIR lib lib64 lib32; do + if test -d "$path/$i/cmake/$name"; then + TMP_PARAMS="\"-D""$name""_DIR=$path/$i/cmake/$name\"" + break; + fi + done + CMAKE_PARAMS="$CMAKE_PARAMS $TMP_PARAMS" + fi + fi + done + # create build directory if requested + test -d "$ABS_BUILDDIR" || mkdir -p "$ABS_BUILDDIR" + SRCDIR="$PWD" + cd "$ABS_BUILDDIR" + + # Prevent using an empty module path + if test -n "$CMAKE_MODULE_PATH"; then + _MODULE_PATH="-DCMAKE_MODULE_PATH=\"$CMAKE_MODULE_PATH\"" + fi + echo "$CMAKE $_MODULE_PATH $CMAKE_PARAMS $CMAKE_FLAGS \"$SRCDIR\"" + eval $CMAKE "$_MODULE_PATH $CMAKE_PARAMS $CMAKE_FLAGS \"$SRCDIR\"" || exit 1 +} + +run_default_configure () { + # configure just forwards to cmake + run_default_cmake +} + +run_default_make () { + test ! -d "$ABS_BUILDDIR" || cd "$ABS_BUILDDIR" + PARAMS="$CMD_FLAGS" + echo "build directory: $BUILDDIR" + # prepend '--' to separate cmake and make parameters + if ! $(echo "$PARAMS" | grep -q -- '--'); then + PARAMS="-- $PARAMS" + fi + echo $CMAKE --build . "$PARAMS" + eval $CMAKE --build . "$PARAMS" +} + +run_default_all () { + for cmd in vcsetup cmake make; do + eval echo "--- calling $cmd for \$NAME_${module} ---" + load_opts $cmd + run_$cmd + done +} + +run_default_svn () { + if test -d .svn; then + PARAMS="$CMD_FLAGS" + eval svn "$PARAMS" + fi +} + +run_default_git () { + if test -e .git; then + PARAMS="$CMD_FLAGS" + eval git "$PARAMS" + fi +} + +############################################### +### +### main +### + +onfailure() { + echo "Execution of $(basename "$0") terminated due to errors!" >&2 + exit 1 +} + +usage () { + ( + echo "Usage: $(basename "$0") [OPTIONS] COMMANDS [COMMAND-OPTIONS]" + echo "" + echo " Execute COMMANDS for all Dune modules found. All entries in the" + echo " DUNE_CONTROL_PATH variable are scanned recursively for Dune modules." + echo " If DUNE_CONTROL_PATH is empty, the current directory is scanned." + echo " Dependencies are controlled by the $CONTROL files." + echo "" + echo "OPTIONS:" + echo " -h, --help show this help" + echo " --debug enable debug output of this script" + echo " --module=mod apply the actions on module mod" + echo " and all modules it depends on" + echo " --only=mod only apply the actions on module mod" + echo " and not the modules it depends on" + echo " --current only apply the actions on the current module," + echo " i.e. the one whose source tree we are standing in," + echo " and not the modules it depends on" + echo " --current-dep apply the actions on the current module," + echo " and all modules it depends on" + echo " --resume resume a previous run (only consider the modules" + echo " not built successfully on the previous run)" + echo " --skipfirst skip the first module (use with --resume)" + echo " --skipversioncheck do not perform version checks when looking for other Dune modules" + echo " --opts=FILE load default options from FILE" + echo " --builddir=NAME make out-of-source builds in a subdir NAME." + echo " This directory is created inside each module." + echo " If NAME is an absolute path, the build directory " + echo " is set to NAME/module-name for each module." + echo " --[COMMAND]-opts=opts set options for COMMAND" + echo " (this is mainly useful for the 'all' COMMAND)" + echo "COMMANDS:" + echo " Colon-separated list of commands. Available commands are:" + printf " \`help'\tguess what :-)\n" + printf " \`print'\tprint the list of modules sorted after their dependencies\n" + printf " \`info'\tsame as \`print\', but including whether it is a dependency or suggestion\n" + for i in $COMMANDS; do + printf " \`$i'\t$(eval echo \$${i}_HELP)\n" + done + printf " \`export'\trun eval \`dunecontrol export\` to save the list of\n" + printf " \t\tdune.module files to the DUNE_CONTROL_PATH variable\n" + echo + ) >&2 +} + +# create the module list +create_module_list() { + # try to get the resume file name from the options + if test -z "$RESUME_FILE" && test -n "$DUNE_OPTS_FILE"; then + export RESUME_FILE="$(eval . $DUNE_OPTS_FILE; eval echo \$RESUME_FILE)" + fi + + if test "$RESUME_FLAG" = "yes" ; then + if ! test -s "$RESUME_FILE" ; then + echo "Error: No previous run to resume. Please make sure that the RESUME_FILE" + echo " is the name of a writeable file (currently it is '$RESUME_FILE')" + exit 1 + fi + + export MODULES= + RESUME="`cat "$RESUME_FILE"`" + for a in $RESUME ; do + export NAME_`fix_variable_name $a`="$a" + fix_and_assign MODULE "$a" + export SEARCH_MODULES="$SEARCH_MODULES $MODULE" + export ONLY="$ONLY $MODULE" + done + fi + + find_modules_in_path + if test "x$ONLY" != x; then + export MODULES="$ONLY" + elif test "x$SEARCH_MODULES" != "x"; then + sort_modules $SEARCH_MODULES + else + sort_modules $MODULES + fi + + if test "x$REVERSE_FLAG" = "xyes"; then + export MODULES="$REVERSEMODULES" + fi + + if test "x$SKIPFIRST" = "xyes" ; then + export MODULES=`echo $MODULES " " | cut '--delimiter= ' --fields=2-` + fi + # warn about superseeded modules: + if test -n "$superseded_modules"; then + # sort moules list and make it unique. + superseded_modules=$(echo $superseded_modules | tr ' ' '\n'| sort -u) + echo >&2 + echo "The following local modules do supersede the corresponding installed ones:" >&2 + echo "$superseded_modules" >&2 + echo >&2 + fi +} + +# print the module list +print_module_list() { + DELIM=$1 + shift + while test -n "$2"; do + echo -n "$(eval echo \$NAME_$1)$DELIM" + shift + done + echo -n "$(eval echo \$NAME_$1)" +} + +trap onfailure EXIT + +# clear variables +export SEARCH_MODULES="" +export MODULES="" +export ONLY="" +export RESUME_FLAG=no +export REVERSE_FLAG=no +export SKIPFIRST=no + +# parse commandline parameters +while test $# -gt 0; do + # get option + command=$1 + option=$1 + + # get args + set +e + # stolen from configure... + # when no option is set, this returns an error code + arg=`expr "x$option" : 'x[^=]*=\(.*\)'` + set -e + + # switch + case "$option" in + --opts=*) + if test "x$arg" = "x"; then + usage + echo "ERROR: Parameter for --opts is missing" >&2 + echo >&2 + exit 1; + fi + DUNE_OPTS_FILE=`canonicalname $arg` + if ! test -r "$DUNE_OPTS_FILE"; then + usage + echo "ERROR: could not read opts file \"$DUNE_OPTS_FILE\"" >&2 + echo >&2 + exit 1; + fi + ;; + --*-opts=*) + optcmd=`expr "x$option=" : 'x--\([^-]*\)-opts=.*'` + if is_command $optcmd; then + COMMAND=`echo $optcmd | tr '[:lower:]' '[:upper:]'` + export ${COMMAND}_FLAGS="$arg" + else + usage + echo "ERROR: unknown option \"$option\"" >&2 + exit 1 + fi + ;; + -h|--help) + command=help + break + ;; + -p|--print) + command=print + break + ;; + --module=*) + if test "x$arg" = "x"; then + usage + echo "ERROR: Parameter for --module is missing" >&2 + echo >&2 + exit 1; + fi + for a in `echo $arg | tr ',' ' '`; do + export NAME_`fix_variable_name $a`="$a" + fix_and_assign MODULE "$a" + export SEARCH_MODULES="$SEARCH_MODULES $MODULE" + done + ;; + --only=*) + if test "x$arg" = "x"; then + usage + echo "ERROR: Parameter for --only is missing" >&2 + echo >&2 + exit 1; + fi + for a in `echo $arg | tr ',' ' '`; do + export NAME_`fix_variable_name $a`="$a" + fix_and_assign MODULE "$a" + export SEARCH_MODULES="$SEARCH_MODULES $MODULE" + export ONLY="$ONLY $MODULE" + done + ;; + --builddir=*) + export DUNE_BUILDDIR=$arg + ;; + --no-builddir) + export DUNE_BUILDDIR="" + ;; + --skipversioncheck) + export SKIPVERSIONCHECK=yes + ;; + --current) + while ! test -f $CONTROL; do + cd .. + if test "$OLDPWD" = "$PWD"; then + echo "You are not inside the source tree of a DUNE module." >&2 + exit -1 + fi + done; + parse_control $PWD/$CONTROL + fix_and_assign MODULE "$module" + export SEARCH_MODULES="$SEARCH_MODULES $MODULE" + export ONLY="$ONLY $MODULE" + ;; + --current-dep) + while ! test -f $CONTROL; do + cd .. + if test "$OLDPWD" = "$PWD"; then + echo "You are not inside the source tree of a DUNE module." >&2 + exit -1 + fi + done; + parse_control $PWD/$CONTROL + fix_and_assign MODULE "$module" + export SEARCH_MODULES="$SEARCH_MODULES $MODULE" + ;; + --resume) + export RESUME_FLAG="yes" + ;; + --reverse) + export REVERSE_FLAG="yes" + ;; + --skipfirst) + export SKIPFIRST=yes + ;; + --debug) true ;; # ignore this option, it is handled right at the beginning + --*) + usage + echo "ERROR: Unknown option \`$option'" >&2 + echo >&2 + exit 1 + ;; + *) + break + ;; + esac + + shift +done + +extract_multiarch + +# create PKG_CONFIG_PATH for installed dune modules +for i in $MULTIARCH_LIBDIR lib64 lib32 lib; do + if test -d "$PREFIX_DIR/$i/pkgconfig"; then + export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$PREFIX_DIR/$i/pkgconfig" + fi +done + +# we assume there should be a command... +if test "x$command" = "x"; then + usage + exit 1 +fi + +case "$command" in + print) + create_module_list + eval "print_module_list ' ' $MODULES" + echo >&2 + ;; + info) + create_module_list + echo $SORTEDMODULES_INFO + ;; + export) + create_module_list + DUNE_CONTROL_PATH="" + for mod in $MODULES; do + path="$(eval echo \$PATH_$mod)" + name=$(eval echo \$NAME_$mod) + if test -f "$path/dune.module"; then + export DUNE_CONTROL_PATH="$DUNE_CONTROL_PATH:$path/dune.module" + else + if test -f "$path/lib/dunecontrol/$name/dune.module"; then + export DUNE_CONTROL_PATH="$DUNE_CONTROL_PATH:$path/lib/dunecontrol/$name/dune.module" + else + echo "ERROR: while creating list of dune.module files" >&2 + echo " couldn't find dune.module file for $name in $path" >&2 + echo >&2 + exit 1 + fi + fi + done + echo export DUNE_CONTROL_PATH=$(echo $DUNE_CONTROL_PATH | $SED -e 's/^://') + ;; + printdeps) + find_modules_in_path + if test "x$SEARCH_MODULES" == "x"; then + echo "ERROR: printdeps requires an explicit --module=... parameter" >&2 + exit 1 + fi + mainmod=`echo $SEARCH_MODULES` + name=`eval echo \\${NAME_$mainmod}` + echo "dependencies for $name" + ### DEPENDENCIES + sort_modules $mainmod + for mod in $SORTEDMODULES_DEPS; do + eval echo "\" \$NAME_${mod} (required)\"" + done + for mod in $SORTEDMODULES_SUGS; do + eval echo "\" \$NAME_${mod} (suggested)\"" + done + ;; + unexport) + echo export DUNE_CONTROL_PATH="" + ;; + help) + usage + ;; + *) + set -e + # skip version check if command is in according list + if grep -q "$1" <<<"$COMMANDSTOSKIPVERSIONCHECK" ; then + export SKIPVERSIONCHECK=yes; + fi + check_commands "$@" + create_module_list + NAMES="" + BUILDMODULES="" + for mod in $MODULES; do + if test "$(eval echo \$INST_$mod)" != "yes"; then + NAMES="$NAMES$(eval echo \$NAME_$mod) " + BUILDMODULES="$BUILDMODULES$mod " + fi + done + echo "--- going to build $NAMES ---" + if test -n "$RESUME_FILE"; then + # write all modules to the resume file + for mod in $MODULES ; do + echo "$mod" + done > "$RESUME_FILE" + fi + + for mod in $BUILDMODULES; do + build_module "$mod" "$@" + + if test -n "$RESUME_FILE"; then + # remove the current module from the resume file + modules_togo=`cat "$RESUME_FILE"` + for mod_togo in $modules_togo ; do + if test "$mod_togo" != "$mod" ; then + echo "$mod_togo" + fi + done > "$RESUME_FILE" + fi + done + echo "--- done ---" + ;; +esac + +trap - EXIT diff --git a/bin/dunepackaging.py b/bin/dunepackaging.py new file mode 100755 index 0000000..4b24410 --- /dev/null +++ b/bin/dunepackaging.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python3 + +import sys, os, io, getopt, re, shutil +import importlib, subprocess +import email.utils +import pkg_resources +from datetime import date + +# make sure that 'metadata' is taken from the current `dune-common` folder +# and not some installed version which might be different from the one I'm +# packaging (by mistake). The path to `packagemetadata.py` needs to be +# added to the python path (to work here) and to the environment so that a +# later call to `python setup.py` also works. +here = os.path.dirname(os.path.abspath(__file__)) +mods = os.path.join(here, "..", "python", "dune") +sys.path.append(mods) +pythonpath = mods + ":" + os.environ.get('PYTHONPATH','.') +os.environ['PYTHONPATH'] = pythonpath +from packagemetadata import metaData + +def main(argv): + + repositories = ["gitlab", "testpypi", "pypi"] + def usage(): + return 'usage: dunepackaging.py [--upload <'+"|".join(repositories)+'> | -c | --clean | --version | --onlysdist | --bdist_conda]' + + try: + opts, args = getopt.getopt(argv, "hc", ["upload=", "clean", "version=", "onlysdist", "bdist_conda"]) + except getopt.GetoptError: + print(usage()) + sys.exit(2) + + upload = False + repository = "gitlab" + clean = False + version = None + onlysdist = False + bdistconda = False + for opt, arg in opts: + if opt == '-h': + print(usage()) + sys.exit(2) + elif opt in ("--upload"): + upload = True + if arg != '': + repository = arg + if repository not in repositories: + print("Specified repository must be one of: " + " ".join(repositories)) + sys.exit(2) + elif opt in ("-c", "--clean"): + clean = True + elif opt in ("--version"): + version = arg + elif opt in ("--onlysdist"): + onlysdist = True + elif opt in ("--bdist_conda"): + onlysdist = True + bdistconda = True + + # Remove generated files + def removeFiles(): + import glob + files = ['MANIFEST', 'dist', '_skbuild', '__pycache__'] + print("Remove generated files: " + ", ".join(files)) + remove = ['rm', '-rf'] + files + subprocess.call(remove) + # checkout setup.py and pyproject.toml + checkout = ['git', 'checkout', 'setup.py', 'pyproject.toml'] + subprocess.call(checkout) + + if clean: + removeFiles() + sys.exit(0) + + data, cmake_flags = metaData(version, dependencyCheck=False) + + if version is None: + version = data.version + + # Generate setup.py + print("Generate setup.py") + f = open("setup.py", "w") + if data.name == 'dune-common': + f.write("import os, sys\n") + f.write("here = os.path.dirname(os.path.abspath(__file__))\n") + f.write("mods = os.path.join(here, \"python\", \"dune\")\n") + f.write("sys.path.append(mods)\n\n") + f.write("try:\n") + f.write(" from dune.packagemetadata import metaData\n") + f.write("except ImportError:\n") + f.write(" from packagemetadata import metaData\n") + f.write("from skbuild import setup\n") + f.write("setup(**metaData('"+version+"')[1])\n") + f.close() + + # Generate pyproject.toml + print("Generate pyproject.toml") + f = open("pyproject.toml", "w") + requires = ["pip", "setuptools", "wheel", "scikit-build", "cmake", "ninja", "requests"] + requires += [r for r in data.asPythonRequirementString(data.depends + data.python_requires) if r not in requires] + f.write("[build-system]\n") + f.write("requires = "+requires.__str__()+"\n") + f.write("build-backend = 'setuptools.build_meta'\n") + f.close() + + # Create source distribution and upload to repository + python = sys.executable + if upload or onlysdist: + print("Remove dist") + remove = ['rm', '-rf', 'dist'] + subprocess.call(remove) + + # check if we have scikit-build + import pkg_resources + installed = {pkg.key for pkg in pkg_resources.working_set} + if not 'scikit-build' in installed: + print("Please install the pip package 'scikit-build' to build the source distribution.") + sys.exit(2) + + # append hash of current git commit to README + shutil.copy('README.md', 'tmp_README.md') + githash = ['git', 'rev-parse', 'HEAD'] + hash = subprocess.check_output(githash, encoding='UTF-8') + with open("README.md", "a") as f: + f.write("\n\ngit-" + hash) + + print("Create source distribution") + # make sure setup.py/pyproject.toml are tracked by git so that + # they get added to the package by scikit + gitadd = ['git', 'add', 'setup.py', 'pyproject.toml'] + subprocess.call(gitadd) + # run sdist + build = [python, 'setup.py', 'sdist'] + subprocess.call(build, stdout=subprocess.DEVNULL) + # undo the above git add + gitreset = ['git', 'reset', 'setup.py', 'pyproject.toml'] + subprocess.call(gitreset) + + # restore README.md + shutil.move('tmp_README.md', 'README.md') + + if not onlysdist: + # check if we have twine + import pkg_resources + installed = {pkg.key for pkg in pkg_resources.working_set} + if not 'twine' in installed: + print("Please install the pip package 'twine' to upload the source distribution.") + sys.exit(2) + + twine = [python, '-m', 'twine', 'upload'] + twine += ['--repository', repository] + twine += ['dist/*'] + subprocess.call(twine) + + removeFiles() + + # create conda package meta.yaml (experimental) + if bdistconda: + import hashlib + remove = ['rm', '-rf', 'dist/'+data.name] + subprocess.call(remove) + mkdir = ['mkdir', 'dist/'+data.name ] + subprocess.call(mkdir) + + print("Create bdist_conda (experimental)") + distfile = 'dist/'+data.name+'-'+version+'.tar.gz' + datahash = '' + with open(distfile, "rb") as include: + source = include.read() + datahash = hashlib.sha256( source ).hexdigest() + + print("Generate ",'dist/'+data.name+'/meta.yaml') + f = open('dist/'+data.name+'/meta.yaml', "w") + f.write('{% set name = "' + data.name + '" %}\n') + f.write('{% set version = "' + version + '" %}\n') + f.write('{% set hash = "' + datahash + '" %}\n\n') + f.write('package:\n') + f.write(' name: "{{ name|lower }}"\n') + f.write(' version: "{{ version }}"\n\n') + f.write('source:\n') + f.write(' path: ../{{ name }}-{{ version }}/\n') + f.write(' sha256: {{ hash }}\n\n') + f.write('build:\n') + f.write(' number: 1\n') + if 'TMPDIR' in os.environ: + f.write(' script_env:\n') + f.write(' - TMPDIR=' + os.environ['TMPDIR'] +'\n') + f.write(' script: "{{ PYTHON }} -m pip install . --no-deps --ignore-installed -vv "\n\n') + f.write('requirements:\n') + + requirements = ['pip', 'python', 'mkl', 'tbb', 'intel-openmp', + 'libgcc-ng', 'libstdcxx-ng', 'gmp', 'scikit-build', + 'mpi4py', 'matplotlib', 'numpy', 'scipy', 'ufl'] + + for dep in data.depends: + requirements += [dep[0]] + + f.write(' host:\n') + for dep in requirements: + f.write(' - ' + dep + '\n') + + f.write('\n') + f.write(' run:\n') + for dep in requirements: + f.write(' - ' + dep + '\n') + + f.write('\n') + f.write('test:\n') + f.write(' imports:\n') + f.write(' - ' + data.name.replace('-','.') + '\n\n') + f.write('about:\n') + f.write(' home: '+data.url+'\n') + f.write(' license: GPLv2 with linking exception.\n') + f.write(' license_family: GPL\n') + f.write(' summary: '+data.description+'\n') + f.close() + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/bin/duneproject b/bin/duneproject new file mode 100755 index 0000000..59f1f84 --- /dev/null +++ b/bin/duneproject @@ -0,0 +1,696 @@ +#!/usr/bin/env bash +# -*- indent-tabs-mode: nil; sh-basic-offset: 2; sh-indentation: 2 -*- +# vi: set et sw=2: + +# The sh-indentation in the emacs mode-line is needed for emacs <26, see +# https://debbugs.gnu.org/21751 + +# +# TODO: +# +# * Check module names entered as dependencies. + +set -e + +canonicalname(){ + if test $# -ne 1; then + echo Usage: canonicalname path >&2 + return 1 + fi + file="`eval echo $1`" # expand ~ + if test ! -e "$file"; then + echo $file: file not found >&2 + return 1 + fi + # if this is a symlink, then follow the symlink + if test -L "$file"; then + fdir="`dirname \"$file\"`" + flink="`readlink \"$file\"`" + if test -e "$flink"; then + # these are absolute links, or links in the CWD + canonicalname "$flink" + else + canonicalname "$fdir/$flink" + fi + else + # if this is a file, then remember the filename and + # canonicalize the directory name + if test -f "$file"; then + fdir="`dirname \"$file\"`" + fname="`basename \"$file\"`" + fdir="`canonicalname \"$fdir\"`" + echo "$fdir/$fname" + fi + # if this is a directory, then create an absolute + # directory name and we are done + if test -d "$file"; then + (cd "$file"; pwd) + fi + fi +} + +canonicalpath(){ + if test $# -ne 1; then + echo Usage: canonicalpath path >&2 + return 1 + fi + dirname "$(canonicalname "$1")" +} + +pkg_config_dependencies(){ + if test $# -ne 1; then + echo Usage: pkg_config_dependencies name >&2 + return 1 + fi + name="$1" + depends="`pkg-config --variable=DEPENDENCIES $name| sed -e 's/,/ /g'`" + for pkg in $depends; do + depends="$depends `pkg_config_dependencies $pkg`" + done + echo $depends +} + +# modulesexist DEPS MODULES +# +# DEPS is a space seperated list of modules the new module should depend on +# MODULES is a space seperated list of modules that are known to be present +# +# Each name in DEPS is checked to see wether it is present in MODULES. If +# not, is is checked whether it can be found with pkg-config. If still no, an +# error message is issued and modulesexist will return with an exit status +# indicating failure. +modulesexist(){ + local status dep found + status=0 + + for dep in $1; do + found=false + if [[ " $2 " == *" $dep "* ]]; then + found=true + fi + # If module not found in list, try pkg-config + if ! $found && pkg-config $dep &> /dev/null; then + found=true + fi + if ! $found; then + echo "ERROR:">&2 + echo "Module with name $dep was not found" >&2 + echo "Did you forget to specify its location" >&2 + echo "in the DUNE_CONTROL_PATH variable?">&2 + echo >&2 + status=1 + fi + done + + return $status +} + +make_unique(){ + if [ "$#" = "1" ]; then + # take first word + for exclude_word in $1; do + break; + done + make_unique $exclude_word "$1" 0 + else + local exclude_word="$1" + local words="$2" + local pos="$3" + local length=0 + local i=0 + local new_words="" + local cur=0 + for word in $words; do + if [ $i -le $pos ]; then + i=$((i+1)) + length=$((length+1)) + new_words="$new_words $word" + continue + fi + if [ "$word" != "$exclude_word" ]; then + new_words="$new_words $word" + if [ "$((length-1))" = "$pos" ]; then + next_word="$word" + fi + length=$((length+1)) + fi + done + if [ "$pos" -lt "$length" ]; then + # process next word + make_unique "$next_word" "$new_words" $((pos+1)) + else + export UNIQUE_WORDS="$new_words" + fi + fi +} + +echo +echo == Dune project/module generator == +echo +echo duneproject will assist you in the creation of a new Dune application. +echo During this process a new directory with the name of your project will be +echo created. This directory will hold all configuration and Makefiles and a +echo simple example application. +echo + +################## FIND AVAILABLE MODULES ################## + +. "$(canonicalpath $0)/../lib/dunemodules.lib" + +export PREFIX_DIR="`canonicalpath "$0"`/.." + +extract_multiarch_pkg_config_path + +# search for modules, both installed and src modules +find_modules_in_path + +# sort modules to remove duplicates +sort_modules $FOUND_MODULES +FOUND_MODULES=$MODULES + +# get the real module names +MODULES="" +for i in $FOUND_MODULES; do + mod=$(eval echo \$NAME_$i) + MODULES="$MODULES$mod " +done + +if [ "$MODULES" = "" ]; then + echo "ERROR:">&2 + echo " No dune modules were found!">&2 + echo " Did you forget to specify the places where ">&2 + echo " you installed your modules in the ">&2 + echo " DUNE_CONTROL_PATH environment variable">&2 + echo " and adjusted the PKG_CONFIG_PATH environment">&2 + echo " accordingly?" >&2 + exit 1; +fi + +################## READ CMDLINE OPTIONS ########## +PROJECT="$1" +DEPENDENCIES="$2" +VERSION="$3" +MAINTAINER="$4" + +################## READ OPTIONS ################## + +while [ "$DATACORRECT" != "y" -a "$DATACORRECT" != "Y" ]; do + + while [ -z $PROJECT ]; do + read -p "1) Name of your new Project? (e.g.: dune-grid): " PROJECT + if echo "$MODULES" | grep -q ^$PROJECT$; then + read -p " A module named $PROJECT already exists. Continue anyway? [y/N] " CONT + if test x$DELETE = xy -o x$DELETE = xY; then + PROJECT="" + fi + elif echo "$PROJECT" | grep -q "\."; then + echo "The Name contains a dot (.) which is not allowed." + PROJECT="" + fi + done + MODULE="$PROJECT" + + DEPOK=1 + + while [ "$DEPOK" != 0 ]; do + echo "2) Which modules should this module depend on?" + echo " The following modules have been found:" + echo " $MODULES" + # for i in $MODULES; do echo -n " $i"; done + # echo "" + while [ -z "$DEPENDENCIES" ]; do + read -p " Enter space-separated list: " DEPENDENCIES + done + set +e + modulesexist "$DEPENDENCIES" "$MODULES" + DEPOK=$? + set -e + if [ "$DEPOK" != 0 ]; then + DEPENDENCIES="" + fi + done + + while [ -z $VERSION ]; do + read -p "3) Project/Module version? " VERSION + done + while [ -z "$MAINTAINER" ]; do + read -p "4) Maintainer's email address? " MAINTAINER + done + + echo + echo "creating Project \"$PROJECT\", version $VERSION " + echo "which depends on \"$DEPENDENCIES\"" + echo "with maintainer \"$MAINTAINER\"" + read -p "Are these informations correct? [y/N] " DATACORRECT + + # reset data if necessary + if [ "$DATACORRECT" != "y" -a "$DATACORRECT" != "Y" ]; then + PROJECT="" + DEPENDENCIES="" + VERSION="" + MAINTAINER="" + fi + +done + + + +echo +echo "A sample code $MODULE.cc is generated in the \"$PROJECT\" directory." +echo "Look at the README and dune.module files there." +echo "Now you can run the dunecontrol script which will setup the new module." +echo "Sometimes you may have to tweak CMakeLists.txt a bit." + +if test -d $PROJECT; then + echo WARNING: + echo "A directory with the name $PROJECT already exists." + echo "Do you want to continue anyway?" + read -p "Type Y to overwrite the old directory, N to abort. [y/N] " DELETE + if test x$DELETE != xy -a x$DELETE != xY; then + echo Abort... + exit 1 + fi + rm -rf "$PROJECT" +fi +mkdir "$PROJECT" + +################## dune.module ################## +cat > "$PROJECT/dune.module" < "$PROJECT/README" <= 3.13 + +Getting started +--------------- + +If these preliminaries are met, you should run + + dunecontrol all + +which will find all installed dune modules as well as all dune modules +(not installed) which sources reside in a subdirectory of the current +directory. Note that if dune is not installed properly you will either +have to add the directory where the dunecontrol script resides (probably +./dune-common/bin) to your path or specify the relative path of the script. + +Most probably you'll have to provide additional information to dunecontrol +(e. g. compilers, configure options) and/or make options. + +The most convenient way is to use options files in this case. The files +define four variables: + +CMAKE_FLAGS flags passed to cmake (during configure) + +An example options file might look like this: + +#use this options to configure and make if no other options are given +CMAKE_FLAGS=" \\ +-DCMAKE_CXX_COMPILER=g++-5 \\ +-DCMAKE_CXX_FLAGS='-Wall -pedantic' \\ +-DCMAKE_INSTALL_PREFIX=/install/path" #Force g++-5 and set compiler flags + +If you save this information into example.opts you can pass the opts file to +dunecontrol via the --opts option, e. g. + + dunecontrol --opts=example.opts all + +More info +--------- + +See + + dunecontrol --help + +for further options. + + +The full build system is described in the dune-common/doc/buildsystem (Git version) or under share/doc/dune-common/buildsystem if you installed DUNE! +R_DELIM + +################## CMakeLists.txt ################## +echo "- $PROJECT/CMakeLists.txt" +cat> "$PROJECT/CMakeLists.txt" << M_DELIM +cmake_minimum_required(VERSION 3.13) +project($PROJECT CXX) + +if(NOT (dune-common_DIR OR dune-common_ROOT OR + "\${CMAKE_PREFIX_PATH}" MATCHES ".*dune-common.*")) + string(REPLACE \${PROJECT_NAME} dune-common dune-common_DIR + \${PROJECT_BINARY_DIR}) +endif() + +#find dune-common and set the module path +find_package(dune-common REQUIRED) +list(APPEND CMAKE_MODULE_PATH "\${PROJECT_SOURCE_DIR}/cmake/modules" + \${dune-common_MODULE_PATH}) + +#include the dune macros +include(DuneMacros) + +# start a dune project with information from dune.module +dune_project() + +dune_enable_all_packages() + +add_subdirectory(src) +add_subdirectory(dune) +add_subdirectory(doc) +add_subdirectory(cmake/modules) + +# finalize the dune project, e.g. generating config.h etc. +finalize_dune_project(GENERATE_CONFIG_H_CMAKE) +M_DELIM + +################## PROJECT.PC.IN ################## +echo "- $PROJECT/$MODULE.pc.in" +cat> "$PROJECT/$MODULE.pc.in" << CC_DELIM +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +CXX=@CXX@ +CC=@CC@ +DEPENDENCIES=@REQUIRES@ + +Name: @PACKAGE_NAME@ +Version: @VERSION@ +Description: $MODULE module +URL: http://dune-project.org/ +Requires: ${DEPENDENCIES} +Libs: -L\${libdir} +Cflags: -I\${includedir} +CC_DELIM +echo " Please remember to update your $PROJECT/$MODULE.pc.in," +echo " Description and URL are missing right now." + +################# config.h.cmake ##################### + +echo "- $PROJECT/config.h.cmake" +cat> "$PROJECT/config.h.cmake" < "$PROJECT/src/CMakeLists.txt" << M_DELIM +add_executable("${MODULE}" ${MODULE}.cc) +target_link_dune_default_libraries("${MODULE}") +M_DELIM + +################## PROJECT.CC ################## +echo "- $PROJECT/src/$MODULE.cc" +cat> "$PROJECT/src/$MODULE.cc" << CC_DELIM +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include // An initializer of MPI +#include // We use exceptions + +int main(int argc, char** argv) +{ + try{ + // Maybe initialize MPI + Dune::MPIHelper& helper = Dune::MPIHelper::instance(argc, argv); + std::cout << "Hello World! This is ${PROJECT}." << std::endl; + if(Dune::MPIHelper::isFake) + std::cout<< "This is a sequential program." << std::endl; + else + std::cout<<"I am rank "< $PROJECT/dune/CMakeLists.txt < $PROJECT/dune/$NAME/CMakeLists.txt < $PROJECT/dune/$NAME/$NAME.hh < "$PROJECT/doc/CMakeLists.txt" << CC_DELIM +add_subdirectory("doxygen") +CC_DELIM + +############################################################### +############### The doc/doxygen subdirectory ################## +############################################################### + +mkdir "$PROJECT/doc/doxygen" + +#################### basic Doxylocal ########################## + +echo "- $PROJECT/doc/doxygen/Doxylocal" +if [ "x`which doxygen`" == "x" ]; then + echo "Doxygen is not installed! Your documentation will not work without it." +fi +# Where to search and which files to use +cat> $PROJECT/doc/doxygen/Doxylocal << CC_DELIM +# This file contains local changes to the doxygen configuration +# please use '+=' to add files/directories to the lists + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT += @top_srcdir@/dune/ +# see e.g. dune-grid for the examples of mainpage and modules +# INPUT += @srcdir@/mainpage \\ +# @srcdir@/modules + +# The EXCLUDE tag can be used to specify files and/or directories that should +# be excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +# EXCLUDE += @top_srcdir@/dune/$NAME/test + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +# EXAMPLE_PATH += @top_srcdir@/src + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +# IMAGE_PATH += @top_srcdir@/dune/$NAME/pics +CC_DELIM + +################# doc/doxygen/CMakeLists.txt ##################### + +echo "- $PROJECT/doc/doxygen/CMakeLists.txt" +cat> "$PROJECT/doc/doxygen/CMakeLists.txt" << CC_DELIM +# shortcut for creating the Doxyfile.in and Doxyfile +add_doxygen_target() +CC_DELIM + +######################################################### +############### The cmake subdirectory ################## +######################################################### + +mkdir "$PROJECT/cmake" + +######################################################### +############### The cmake/modules subdirectory ########## +######################################################### + +mkdir "$PROJECT/cmake/modules" + +macroname="" +for i in $(echo $PROJECT| sed 's/-/ /g'); do + firstchar=$(echo $i | sed 's/\(.\).*/\1/') + macroname=$macroname$(echo $firstchar | tr '[a-z]' '[A-Z]')$(echo $i | sed 's/.\(.*\)/\1/') +done +macroname="$macroname""Macros.cmake" + +################# cmake/modules/CMakeLists.txt ##################### + +echo "- $PROJECT/cmake/modules/CMakeLists.txt" +cat> "$PROJECT/cmake/modules/CMakeLists.txt" < "$PROJECT/cmake/modules/$macroname" <0: + for m in args.modules: + base = os.path.join(generated_dir, m+'*') + for filename in glob.iglob( base ): + os.remove( filename ) + moduleFiles.update( [os.path.splitext(os.path.basename(filename))[0]] ) +else: + parser.print_help() + sys.exit(0) + +for line in fileinput.input( os.path.join(generated_dir, 'CMakeLists.txt'), inplace = True): + if not any( [m in line for m in moduleFiles] ): + print(line, end="") diff --git a/bin/setup-dunepy.py b/bin/setup-dunepy.py new file mode 100755 index 0000000..9c287ee --- /dev/null +++ b/bin/setup-dunepy.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +import getopt +import os +import shlex +import subprocess +import sys +import shutil +import logging + +logger = logging.getLogger(__name__) + +try: + from dune.common.module import build_dune_py_module, get_dune_py_dir, make_dune_py_module, select_modules, resolve_dependencies, resolve_order + from dune.common.locking import Lock, LOCK_EX +except ImportError: + import os + here = os.path.dirname(os.path.abspath(__file__)) + modsA = os.path.join(os.path.dirname(here), "python", "dune") + modsB = os.path.join(modsA,"common") + sys.path.append(modsB) + sys.path.append(modsA) + if os.path.exists(os.path.join(modsB, "module.py")): + from module import build_dune_py_module, get_dune_py_dir, make_dune_py_module, select_modules, resolve_dependencies, resolve_order + from locking import Lock, LOCK_EX + else: + raise + +def buffer_to_str(b): + return b if sys.version_info.major == 2 else b.decode('utf-8') + +def toBuildDir(builddir, moddir, module): + if os.path.isabs(builddir): + return os.path.join(builddir ,module) + else: + return os.path.join(moddir, builddir) + +def main(argv): + try: + opts, args = getopt.getopt(argv,"ho",["opts=","builddir=","module="]) + except getopt.GetoptError: + print('usage: setup-dunepy.py [-o config.opts | --opts=config.opts | --builddir] [--module=mod] [install]') + sys.exit(2) + + optsfile = None + builddir = None + masterModule = None + for opt, arg in opts: + if opt == '-h': + print('usage: setup-dunepy.py [-o config.opts | --opts=config.opts] [install]') + sys.exit(2) + elif opt in ("-o", "--opts"): + optsfile = arg + elif opt in ("--builddir",): + builddir = arg + elif opt in ("--module",): + masterModule = arg + if len(args) > 0: + execute = args[0] + else: + execute = "" + + # see if the standard Dune enviroment variable for the opts file is defined + if optsfile is None: + optsfile = os.environ.get('DUNE_OPTS_FILE', None) + + if optsfile is not None: + definitions = {} + command = ['bash', '-c', 'source ' + optsfile + ' && echo "$CMAKE_FLAGS"'] + proc = subprocess.Popen(command, stdout = subprocess.PIPE) + stdout, _ = proc.communicate() + cmake_args = shlex.split(buffer_to_str(stdout)) + if builddir is None: + # get the build dir (check for BUILDDIR, DUNE_BUILDDIR in opts file + # and then DUNE_BUILDDIR in environment variable + command = ['bash', '-c', 'source ' + optsfile + ' && echo "$BUILDDIR"'] + proc = subprocess.Popen(command, stdout = subprocess.PIPE) + stdout, _ = proc.communicate() + builddir = buffer_to_str(stdout).strip() + if not builddir: + command = ['bash', '-c', 'source ' + optsfile + ' && echo "$DUNE_BUILDDIR"'] + proc = subprocess.Popen(command, stdout = subprocess.PIPE) + stdout, _ = proc.communicate() + builddir = buffer_to_str(stdout).strip() + if not builddir: + builddir = os.environ.get('DUNE_BUILDDIR', 'build-cmake') + else: + cmake_args = None + if builddir is None: + builddir = os.environ.get('DUNE_BUILDDIR', 'build-cmake') + + # Generate list of all modules + duneModules = select_modules() + + # Generate list of dependencies for dune-py. If --module=mod is passed, + # use mod and all its dependencies only. Otherwise use all found modules + # as dependencies. + if masterModule is None: + deps = resolve_order(duneModules[0]) + else: + depsList = resolve_dependencies(duneModules[0], masterModule) + deps = {k:v for k,v in duneModules[0].items() if k in depsList} + deps = resolve_order(deps) + deps += [masterModule] + + if execute == "install": + for m in deps: + moddir = duneModules[1][m] + pythonModule = toBuildDir(builddir,moddir,m) + print("calling install_python for %s (%s)" % (m,pythonModule)) + try: + command = ['cmake', '--build', '.', '--target', 'install_python'] + proc = subprocess.Popen(command, cwd=pythonModule, stdout = subprocess.PIPE) + stdout, stderr = proc.communicate() + logger.debug(buffer_to_str(stdout)) + except FileNotFoundError: + print("Warning: build dir not found possibly module is installed then python bindings should be already available") + + dunepy = get_dune_py_dir() + dunepyBase = os.path.realpath( os.path.join(dunepy,"..") ) + if not os.path.exists(dunepyBase): + os.makedirs(dunepyBase) + with Lock(os.path.join(dunepyBase, 'lock-module.lock'), flags=LOCK_EX): + if os.path.exists(dunepy): + shutil.rmtree(dunepy) + os.makedirs(dunepy) + foundModule = make_dune_py_module(dunepy, deps) + output = build_dune_py_module(dunepy, cmake_args, None, builddir, deps, writetagfile=True) + + print("CMake output") + print(output) + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt new file mode 100644 index 0000000..2e05f44 --- /dev/null +++ b/cmake/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(modules) +add_subdirectory(scripts) diff --git a/cmake/modules/AddBLASLapackFlags.cmake b/cmake/modules/AddBLASLapackFlags.cmake new file mode 100644 index 0000000..bf9b466 --- /dev/null +++ b/cmake/modules/AddBLASLapackFlags.cmake @@ -0,0 +1,43 @@ +# Defines the functions to use BLAS/Lapack +# +# .. cmake_function:: add_dune_blas_lapack_flags +# +# .. cmake_param:: targets +# :positional: +# :single: +# :required: +# +# A list of targets to use BLAS/Lapack with. +# +include_guard(GLOBAL) + +set_package_properties("BLAS" PROPERTIES + DESCRIPTION "fast linear algebra routines") +set_package_properties("LAPACK" PROPERTIES + DESCRIPTION "fast linear algebra routines") + +# register HAVE_BLAS and HAVE_LAPACK for config.h +set(HAVE_BLAS ${BLAS_FOUND}) +set(HAVE_LAPACK ${LAPACK_FOUND}) + +# register Lapack library as dune package +if(HAVE_LAPACK) + dune_register_package_flags(LIBRARIES "${LAPACK_LIBRARIES}") + cmake_push_check_state() + set(CMAKE_REQUIRED_LIBRARIES ${LAPACK_LIBRARIES}) + check_function_exists("dsyev_" LAPACK_NEEDS_UNDERLINE) + cmake_pop_check_state() +elseif(HAVE_BLAS) + dune_register_package_flags(LIBRARIES "${BLAS_LIBRARIES}") +endif() + +# add function to link against the BLAS/Lapack library +function(add_dune_blas_lapack_flags _targets) + foreach(_target ${_targets}) + if(LAPACK_FOUND) + target_link_libraries(${_target} PUBLIC ${LAPACK_LIBRARIES}) + elseif(BLAS_FOUND) + target_link_libraries(${_target} PUBLIC ${BLAS_LIBRARIES}) + endif() + endforeach(_target) +endfunction(add_dune_blas_lapack_flags) diff --git a/cmake/modules/AddGMPFlags.cmake b/cmake/modules/AddGMPFlags.cmake new file mode 100644 index 0000000..e0cf7a0 --- /dev/null +++ b/cmake/modules/AddGMPFlags.cmake @@ -0,0 +1,33 @@ +# Defines the functions to use GMP +# +# .. cmake_function:: add_dune_gmp_flags +# +# .. cmake_param:: targets +# :positional: +# :single: +# :required: +# +# A list of targets to use GMP with. +# +include_guard(GLOBAL) + +# set HAVE_GMP for the config.h file +set(HAVE_GMP ${GMP_FOUND}) + +# register all GMP related flags +if(GMP_FOUND) + dune_register_package_flags( + LIBRARIES GMP::gmpxx + COMPILE_DEFINITIONS "ENABLE_GMP=1" + ) +endif() + +# add function to link against the GMP library +function(add_dune_gmp_flags _targets) + if(GMP_FOUND) + foreach(_target ${_targets}) + target_link_libraries(${_target} PUBLIC GMP::gmpxx) + target_compile_definitions(${_target} PUBLIC ENABLE_GMP=1) + endforeach(_target ${_targets}) + endif(GMP_FOUND) +endfunction(add_dune_gmp_flags) diff --git a/cmake/modules/AddMETISFlags.cmake b/cmake/modules/AddMETISFlags.cmake new file mode 100644 index 0000000..bd5c122 --- /dev/null +++ b/cmake/modules/AddMETISFlags.cmake @@ -0,0 +1,29 @@ +# Defines the functions to use METIS +# +# .. cmake_function:: add_dune_metis_flags +# +# .. cmake_param:: targets +# :positional: +# :single: +# :required: +# +# A list of targets to use METIS with. +# +include_guard(GLOBAL) + +# register HAVE_METIS for config.h +set(HAVE_METIS ${METIS_FOUND}) + +# register METIS library as dune package +if(METIS_FOUND) + dune_register_package_flags(LIBRARIES METIS::METIS) +endif() + +# Add function to link targets against METIS library +function(add_dune_metis_flags _targets) + if(METIS_FOUND) + foreach(_target ${_targets}) + target_link_libraries(${_target} PUBLIC METIS::METIS) + endforeach(_target) + endif() +endfunction(add_dune_metis_flags _targets) diff --git a/cmake/modules/AddMPIFlags.cmake b/cmake/modules/AddMPIFlags.cmake new file mode 100644 index 0000000..49f5190 --- /dev/null +++ b/cmake/modules/AddMPIFlags.cmake @@ -0,0 +1,39 @@ +# The DUNE way to compile MPI applications is to use the CXX +# compiler with MPI flags usually used for C. CXX bindings +# are deactivated to prevent ABI problems. +# +# .. cmake_function:: add_dune_mpi_flags +# +# .. cmake_param:: targets +# :single: +# :required: +# :positional: +# +# The target list to add the MPI flags to. +# +include_guard(GLOBAL) + +# text for feature summary +set_package_properties("MPI" PROPERTIES + DESCRIPTION "Message Passing Interface library" + PURPOSE "Parallel programming on multiple processors") + +if(MPI_C_FOUND) + set(HAVE_MPI ${MPI_C_FOUND}) + + dune_register_package_flags(COMPILE_DEFINITIONS "ENABLE_MPI=1" + LIBRARIES MPI::MPI_C) +endif() + +# adds MPI flags to the targets +function(add_dune_mpi_flags) + cmake_parse_arguments(ADD_MPI "SOURCE_ONLY;OBJECT" "" "" ${ARGN}) # ignored + set(targets ${ADD_MPI_UNPARSED_ARGUMENTS}) + + if(MPI_C_FOUND) + foreach(target ${targets}) + target_link_libraries(${target} PUBLIC MPI::MPI_C) + target_compile_definitions(${target} PUBLIC "ENABLE_MPI=1") + endforeach(target) + endif() +endfunction(add_dune_mpi_flags) diff --git a/cmake/modules/AddPTScotchFlags.cmake b/cmake/modules/AddPTScotchFlags.cmake new file mode 100644 index 0000000..4424127 --- /dev/null +++ b/cmake/modules/AddPTScotchFlags.cmake @@ -0,0 +1,36 @@ +# Defines the functions to use PTScotch +# +# .. cmake_function:: add_dune_ptscotch_flags +# +# .. cmake_param:: targets +# :positional: +# :single: +# :required: +# +# A list of targets to use PTScotch with. +# +include_guard(GLOBAL) + +# set HAVE_PTSCOTCH for config.h +set(HAVE_PTSCOTCH ${PTScotch_FOUND}) + +# register all PTScotch related flags +if(PTScotch_SCOTCH_FOUND) + dune_register_package_flags(LIBRARIES PTScotch::Scotch) +endif() +if(PTScotch_PTSCOTCH_FOUND) + dune_register_package_flags(LIBRARIES PTScotch::PTScotch) +endif() + +function(add_dune_ptscotch_flags _targets) + if(PTScotch_SCOTCH_FOUND) + foreach(_target ${_targets}) + target_link_libraries(${_target} PUBLIC PTScotch::Scotch) + endforeach(_target ${_targets}) + endif() + if(PTScotch_PTSCOTCH_FOUND) + foreach(_target ${_targets}) + target_link_libraries(${_target} PUBLIC PTScotch::PTScotch) + endforeach(_target ${_targets}) + endif() +endfunction(add_dune_ptscotch_flags) diff --git a/cmake/modules/AddParMETISFlags.cmake b/cmake/modules/AddParMETISFlags.cmake new file mode 100644 index 0000000..aae54a0 --- /dev/null +++ b/cmake/modules/AddParMETISFlags.cmake @@ -0,0 +1,29 @@ +# Defines the functions to use ParMETIS +# +# .. cmake_function:: add_dune_parmetis_flags +# +# .. cmake_param:: targets +# :positional: +# :single: +# :required: +# +# A list of targets to use ParMETIS with. +# +include_guard(GLOBAL) + +# set HAVE_PARMETIS for config.h +set(HAVE_PARMETIS ${ParMETIS_FOUND}) + +# register all ParMETIS related flags +if(ParMETIS_FOUND) + dune_register_package_flags(LIBRARIES ParMETIS::ParMETIS) +endif() + +# add function to link against the ParMETIS library +function(add_dune_parmetis_flags _targets) + if(ParMETIS_FOUND) + foreach(_target ${_targets}) + target_link_libraries(${_target} PUBLIC ParMETIS::ParMETIS) + endforeach(_target) + endif() +endfunction(add_dune_parmetis_flags) diff --git a/cmake/modules/AddQuadMathFlags.cmake b/cmake/modules/AddQuadMathFlags.cmake new file mode 100644 index 0000000..1a04d94 --- /dev/null +++ b/cmake/modules/AddQuadMathFlags.cmake @@ -0,0 +1,33 @@ +# Defines the functions to use QuadMath +# +# .. cmake_function:: add_dune_quadmath_flags +# +# .. cmake_param:: targets +# :positional: +# :single: +# :required: +# +# A list of targets to use QuadMath with. +# +include_guard(GLOBAL) + +# set HAVE_QUADMATH for config.h +set(HAVE_QUADMATH ${QuadMath_FOUND}) + +# register the QuadMath imported target +if(QuadMath_FOUND) + dune_register_package_flags( + LIBRARIES QuadMath::QuadMath + COMPILE_DEFINITIONS "ENABLE_QUADMATH=1" + ) +endif() + +# add function to link against QuadMath::QuadMath +function(add_dune_quadmath_flags _targets) + if(QuadMath_FOUND) + foreach(_target ${_targets}) + target_link_libraries(${_target} PUBLIC QuadMath::QuadMath) + target_compile_definitions(${_target} PUBLIC ENABLE_QUADMATH=1) + endforeach(_target ${_targets}) + endif() +endfunction(add_dune_quadmath_flags) diff --git a/cmake/modules/AddSuiteSparseFlags.cmake b/cmake/modules/AddSuiteSparseFlags.cmake new file mode 100644 index 0000000..f5df248 --- /dev/null +++ b/cmake/modules/AddSuiteSparseFlags.cmake @@ -0,0 +1,33 @@ +# Defines the functions to use SuiteSparse +# +# .. cmake_function:: add_dune_suitesparse_flags +# +# .. cmake_param:: targets +# :positional: +# :single: +# :required: +# +# A list of targets to use SuiteSparse with. +# +include_guard(GLOBAL) + +# set HAVE_SUITESPARSE for config.h +set(HAVE_SUITESPARSE ${SuiteSparse_FOUND}) +set(HAVE_UMFPACK ${SuiteSparse_UMFPACK_FOUND}) + +# register all SuiteSparse related flags +if(SuiteSparse_FOUND) + dune_register_package_flags( + COMPILE_DEFINITIONS "ENABLE_SUITESPARSE=1" + LIBRARIES SuiteSparse::SuiteSparse) +endif() + +# Provide function to set target properties for linking to SuiteSparse +function(add_dune_suitesparse_flags _targets) + if(SuiteSparse_FOUND) + foreach(_target ${_targets}) + target_link_libraries(${_target} PUBLIC SuiteSparse::SuiteSparse) + target_compile_definitions(${_target} PUBLIC ENABLE_SUITESPARSE=1) + endforeach(_target) + endif() +endfunction(add_dune_suitesparse_flags) diff --git a/cmake/modules/AddTBBFlags.cmake b/cmake/modules/AddTBBFlags.cmake new file mode 100644 index 0000000..95075c6 --- /dev/null +++ b/cmake/modules/AddTBBFlags.cmake @@ -0,0 +1,33 @@ +# Defines the functions to use TBB +# +# .. cmake_function:: add_dune_tbb_flags +# +# .. cmake_param:: targets +# :positional: +# :single: +# :required: +# +# A list of targets to use TBB with. +# +include_guard(GLOBAL) + +# set variable for config.h +set(HAVE_TBB ${TBB_FOUND}) + +# perform DUNE-specific setup tasks +if (TBB_FOUND) + dune_register_package_flags( + COMPILE_DEFINITIONS ENABLE_TBB=1 + LIBRARIES TBB::tbb + ) +endif() + +# function for adding TBB flags to a list of targets +function(add_dune_tbb_flags _targets) + if(TBB_FOUND) + foreach(_target ${_targets}) + target_link_libraries(${_target} PUBLIC TBB::tbb) + target_compile_definitions(${_target} PUBLIC ENABLE_TBB=1) + endforeach(_target) + endif() +endfunction(add_dune_tbb_flags) diff --git a/cmake/modules/AddThreadsFlags.cmake b/cmake/modules/AddThreadsFlags.cmake new file mode 100644 index 0000000..f3afbfd --- /dev/null +++ b/cmake/modules/AddThreadsFlags.cmake @@ -0,0 +1,13 @@ +include_guard(GLOBAL) + +# text for feature summary +set_package_properties("Threads" PROPERTIES + DESCRIPTION "Multi-threading library") + +# set HAVE_THREADS for config.h +set(HAVE_THREADS ${Threads_FOUND}) + +# register the Threads imported target globally +if(Threads_FOUND) + link_libraries(Threads::Threads) +endif() diff --git a/cmake/modules/AddVcFlags.cmake b/cmake/modules/AddVcFlags.cmake new file mode 100644 index 0000000..8c26aba --- /dev/null +++ b/cmake/modules/AddVcFlags.cmake @@ -0,0 +1,39 @@ +# Defines the functions to use Vc +# +# Vc is a library for high-level Vectorization support in C++ +# see https://github.com/VcDevel/Vc +# +# .. cmake_function:: add_dune_vc_flags +# +# .. cmake_param:: targets +# :positional: +# :single: +# :required: +# +# A list of targets to use VC with. +# +include_guard(GLOBAL) + +# text for feature summary +set_package_properties("Vc" PROPERTIES + DESCRIPTION "C++ Vectorization library" + URL "https://github.com/VcDevel/Vc" + PURPOSE "For use of SIMD instructions") + +function(add_dune_vc_flags _targets) + if(Vc_FOUND) + foreach(_target ${_targets}) + target_link_libraries(${_target} PUBLIC ${Vc_LIBRARIES}) + target_compile_options(${_target} PUBLIC ${Vc_COMPILE_FLAGS}) + target_compile_definitions(${_target} PUBLIC ENABLE_VC=1) + target_include_directories(${_target} SYSTEM PUBLIC ${Vc_INCLUDE_DIR}) + endforeach(_target ${_targets}) + endif(Vc_FOUND) +endfunction(add_dune_vc_flags) + +if(Vc_FOUND) + dune_register_package_flags(COMPILE_OPTIONS "${Vc_COMPILE_FLAGS};-DENABLE_VC=1" + LIBRARIES "${Vc_LIBRARIES}" + INCLUDE_DIRS "${Vc_INCLUDE_DIR}") +endif(Vc_FOUND) +set(HAVE_VC ${Vc_FOUND}) diff --git a/cmake/modules/CMakeBuiltinFunctionsDocumentation.cmake b/cmake/modules/CMakeBuiltinFunctionsDocumentation.cmake new file mode 100644 index 0000000..cd9c785 --- /dev/null +++ b/cmake/modules/CMakeBuiltinFunctionsDocumentation.cmake @@ -0,0 +1,83 @@ +# This modules contains only documentation for CMake builtins. +# This is necessary to have an complete API documentation. +# +# .. cmake_function:: add_subdirectory +# +# .. cmake_param:: dir +# :single: +# :positional: +# :required: +# +# The :code:`CMakeLists.txt` file from this subdirectory +# will be executed next. +# +# .. cmake_param:: EXCLUDE_FROM_ALL +# :option: +# +# Whether targets added in this subdirectory should be built +# during :code:`make all`. +# +# This is a cmake builtin command. +# For detailed information, check the cmake documentation: +# +# :: +# +# cmake --help-command add_subdirectory +# +# .. cmake_function:: install +# +# Define installation rules to customize the behaviour of :code:`make install`. +# +# This is a cmake builtin command. +# For detailed information, check the cmake documentation: +# +# :: +# +# cmake --help-command install +# +# .. cmake_function:: add_executable +# +# Adds an executable to the project. +# +# This is a cmake builtin command. +# For detailed information, check the cmake documentation: +# +# :: +# +# cmake --help-command add_executable +# +# .. cmake_variable:: CMAKE__COMPILER +# +# Set the compiler for the language LANG. +# LANG is in our case out of C, CXX. +# +# This is a cmake builtin variable. +# For detailed information, check the cmake documentation: +# +# :: +# +# cmake --help-variable CMAKE_\_COMPILER +# +# .. cmake_variable:: CMAKE__FLAGS +# +# Set the compile flags for the language LANG. +# LANG is in our case out of C, CXX. +# +# This is a cmake builtin variable. +# For detailed information, check the cmake documentation: +# +# :: +# +# cmake --help-variable CMAKE_\_FLAGS +# +# .. cmake_function:: find_package +# +# Look for an external package. +# +# This is a cmake builtin command. +# For detailed information, check the cmake documentation: +# +# :: +# +# cmake --help-command find_package +# diff --git a/cmake/modules/CMakeLists.txt b/cmake/modules/CMakeLists.txt new file mode 100644 index 0000000..533c302 --- /dev/null +++ b/cmake/modules/CMakeLists.txt @@ -0,0 +1,57 @@ +add_subdirectory(FindPkgConfig) +add_subdirectory(FindPython3) + +install(FILES + AddBLASLapackFlags.cmake + AddGMPFlags.cmake + AddMETISFlags.cmake + AddMPIFlags.cmake + AddParMETISFlags.cmake + AddPTScotchFlags.cmake + AddQuadMathFlags.cmake + AddTBBFlags.cmake + AddThreadsFlags.cmake + AddSuiteSparseFlags.cmake + AddVcFlags.cmake + CheckCXXFeatures.cmake + CMakeBuiltinFunctionsDocumentation.cmake + DuneAddPybind11Module.cmake + DuneCMakeCompat.cmake + DuneCommonMacros.cmake + DuneCxaDemangle.cmake + DuneDoc.cmake + DuneDoxygen.cmake + DuneEnableAllPackages.cmake + DuneExecuteProcess.cmake + DuneInstance.cmake + DuneMacros.cmake + DuneMPI.cmake + DunePathHelper.cmake + DunePkgConfig.cmake + DunePythonCommonMacros.cmake + DunePythonFindPackage.cmake + DunePythonInstallPackage.cmake + DunePythonMacros.cmake + DunePythonTestCommand.cmake + DunePythonVirtualenv.cmake + DuneSphinxDoc.cmake + DuneSphinxCMakeDoc.cmake + DuneStreams.cmake + DuneSymlinkOrCopy.cmake + DuneTestMacros.cmake + FindGMP.cmake + FindInkscape.cmake + FindLatexMk.cmake + FindMETIS.cmake + FindParMETIS.cmake + FindPTScotch.cmake + FindQuadMath.cmake + FindSphinx.cmake + FindSuiteSparse.cmake + FindTBB.cmake + Headercheck.cmake + latexmkrc.cmake + OverloadCompilerFlags.cmake + UseInkscape.cmake + UseLatexMk.cmake + DESTINATION ${DUNE_INSTALL_MODULEDIR}) diff --git a/cmake/modules/CheckCXXFeatures.cmake b/cmake/modules/CheckCXXFeatures.cmake new file mode 100644 index 0000000..2f17476 --- /dev/null +++ b/cmake/modules/CheckCXXFeatures.cmake @@ -0,0 +1,297 @@ +# .. cmake_module:: +# +# Module that checks for supported C++20, C++17 and non-standard features. +# +# The behaviour of this module can be modified by the following variable: +# +# :ref:`DISABLE_CXX_VERSION_CHECK` +# Disable checking for std=c++20 (c++23, ...) +# +# This module internally sets the following variables, which are then +# exported into the config.h of the current dune module. +# +# :code:`HAS_ATTRIBUTE_UNUSED` +# True if attribute unused is supported +# +# :code:`HAS_ATTRIBUTE_DEPRECATED` +# True if attribute deprecated is supported +# +# :code:`HAS_ATTRIBUTE_DEPRECATED_MSG` +# True if attribute deprecated("msg") is supported +# +# .. cmake_variable:: DISABLE_CXX_VERSION_CHECK +# +# You may set this variable to TRUE to disable checking for +# std=c++11 (c++14, c++1y). For more details, check :ref:`CheckCXXFeatures`. +# + + +include(CMakePushCheckState) +include(CheckCXXCompilerFlag) +include(CheckIncludeFileCXX) +include(CheckCXXSourceCompiles) +include(CheckCXXSymbolExists) + +# C++ standard versions that this test knows about +set(CXX_VERSIONS 20 17) + + +# Compile tests for the different standard revisions; these test both the compiler +# and the associated library to avoid problems like using a C++20 user-installed +# compiler together with a non C++20-compliant stdlib from the system compiler. + +# we need to escape semicolons in the tests to be able to stick them into a list +string(REPLACE ";" "\;" cxx_20_test + " + #include + + // `if constexpr` is a C++20 compiler feature + template + void f() + { if constexpr (T::anything) {} } + + int main() { + // std::is_bounded_array_v is a C++20 library feature + return std::is_bounded_array_v; + } + ") + +string(REPLACE ";" "\;" cxx_17_test + " + #include + + // nested namespaces are a C++17 compiler feature + namespace A::B { + using T = int; + } + + int main() { + // std::void_t is a C++17 library feature + return not std::is_same >{}; + } + ") + +# build a list out of the pre-escaped tests +set(CXX_VERSIONS_TEST "${cxx_20_test}" "${cxx_17_test}") + +# these are appended to "-std=c++" and tried in this order +# note the escaped semicolons; that's necessary to create a nested list +set(CXX_VERSIONS_FLAGS "20\;2a" "17\;1z") + +# by default, we enable C++17 for now, but not C++20 +# The user can override this choice by explicitly setting this variable +set(CXX_MAX_STANDARD 17 + CACHE STRING + "highest version of the C++ standard to enable. This version is also used if the version check is disabled") + + +function(dune_require_cxx_standard) + include(CMakeParseArguments) + + cmake_parse_arguments("" "" "MODULE;VERSION" "" ${ARGN}) + + if(_UNPARSED_ARGUMENTS) + message(WARNING "Unknown arguments in call to dune_require_cxx_standard(${ARGN})") + endif() + + if(${_VERSION} GREATER ${CXX_MAX_SUPPORTED_STANDARD}) + + if(NOT _MODULE) + set(_MODULE "This module") + endif() + + if(${_VERSION} GREATER ${CXX_MAX_STANDARD}) + message(FATAL_ERROR "\ +${_MODULE} requires compiler support for C++${_VERSION}, but the build system is currently \ +set up to not allow newer language standards than C++${CXX_MAX_STANDARD}. Try setting the \ +CMake variable CXX_MAX_STANDARD to at least ${_VERSION}." + ) + else() + if(${CXX_MAX_SUPPORTED_STANDARD} EQUAL 17) + set(CXX_STD_NAME 17) + else() + set(CXX_STD_NAME ${CXX_MAX_SUPPORTED_STANDARD}) + endif() + message(FATAL_ERROR "${_MODULE} requires support for C++${_VERSION}, but your compiler failed our compatibility test." + ) + endif() + endif() +endfunction() + + +# try to enable all of the C++ standards that we know about, in descending order +if(NOT DISABLE_CXX_VERSION_CHECK) + + foreach(version ${CXX_VERSIONS}) + + # skip versions that are newer than allowed + if(NOT(version GREATER CXX_MAX_STANDARD)) + + list(FIND CXX_VERSIONS ${version} version_index) + list(GET CXX_VERSIONS_FLAGS ${version_index} version_flags) + + # First try whether the compiler accepts one of the command line flags for this standard + foreach(flag ${version_flags}) + + set(cxx_std_flag_works "cxx_std_flag_${flag}") + check_cxx_compiler_flag("-std=c++${flag}" ${cxx_std_flag_works}) + + if(${cxx_std_flag_works}) + set(cxx_std_flag "-std=c++${flag}") + break() + endif() + + endforeach() + + # and if it did, run the compile test + if(cxx_std_flag) + + list(GET CXX_VERSIONS_TEST ${version_index} version_test) + set(test_compiler_output "compiler_supports_cxx${version}") + + cmake_push_check_state() + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${cxx_std_flag}") + check_cxx_source_compiles("${version_test}" ${test_compiler_output}) + cmake_pop_check_state() + + if(${test_compiler_output}) + set(CXX_MAX_SUPPORTED_STANDARD ${version}) + set(CMAKE_CXX_FLAGS "${cxx_std_flag} ${CMAKE_CXX_FLAGS}") + break() + else() + # Wipe the variable, as this version of the standard doesn't seem to work + unset(cxx_std_flag) + endif() + + endif() + endif() + endforeach() + + if(NOT DEFINED CXX_MAX_SUPPORTED_STANDARD) + # Let's just assume every compiler at least claims C++03 compliance by now + message(WARNING "\ +Unable to determine C++ standard support for your compiler, falling back to C++17. \ +If you know that your compiler supports a newer version of the standard, please set the CMake \ +variable DISABLE_CXX_VERSION_CHECK to true and the CMake variable CXX_MAX_SUPPORTED_STANDARD \ +to the highest version of the standard supported by your compiler (e.g. 20). If your compiler \ +needs custom flags to switch to that standard version, you have to manually add them to \ +CMAKE_CXX_FLAGS." + ) + set(CXX_MAX_SUPPORTED_STANDARD 17) + endif() +else() + # We did not check version but need to set maximum supported + # version for some checks. Therefore we set it to CXX_MAX_STANDARD. + set(CXX_MAX_SUPPORTED_STANDARD ${CXX_MAX_STANDARD}) +endif() + +# make sure we have at least C++17 +dune_require_cxx_standard(MODULE "DUNE" VERSION 17) + +# perform tests + +# __attribute__((unused)) +check_cxx_source_compiles(" + int main(void) + { + int __attribute__((unused)) foo; + return 0; + }; +" HAS_ATTRIBUTE_UNUSED +) + +# __attribute__((deprecated)) +check_cxx_source_compiles(" +#define DEP __attribute__((deprecated)) + class bar + { + bar() DEP; + }; + + class peng { } DEP; + + template + class t_bar + { + t_bar() DEP; + }; + + template + class t_peng { + t_peng() {}; + } DEP; + + void foo() DEP; + + void foo() {} + + int main(void) + { + return 0; + }; +" HAS_ATTRIBUTE_DEPRECATED +) + +# __attribute__((deprecated("msg"))) +check_cxx_source_compiles(" +#define DEP __attribute__((deprecated(\"message\"))) + class bar { + bar() DEP; + }; + + class peng { } DEP; + + template + class t_bar + { + t_bar() DEP; + }; + + template + class t_peng + { + t_peng() {}; + } DEP; + + void foo() DEP; + + void foo() {} + + int main(void) + { + return 0; + }; +" HAS_ATTRIBUTE_DEPRECATED_MSG +) + +# ****************************************************************************** +# +# Checks for standard library features +# +# While there are __cpp_lib_* feature test macros for all of these, those are +# unfortunately unreliable, as libc++ does not have feature test macros yet. +# +# In order to keep the tests short, they use check_cxx_symbol_exists(). That +# function can only test for macros and linkable symbols, however, so we wrap +# tested types into a call to std::move(). That should be safe, as std::move() +# does not require a complete type. +# +# ****************************************************************************** + +check_cxx_symbol_exists( + "std::experimental::make_array" + "experimental/array" + DUNE_HAVE_CXX_EXPERIMENTAL_MAKE_ARRAY + ) + +check_cxx_symbol_exists( + "std::move>" + "utility;experimental/type_traits" + DUNE_HAVE_CXX_EXPERIMENTAL_IS_DETECTED + ) + +check_cxx_symbol_exists( + "std::identity" + "functional" + DUNE_HAVE_CXX_STD_IDENTITY + ) diff --git a/cmake/modules/DuneAddPybind11Module.cmake b/cmake/modules/DuneAddPybind11Module.cmake new file mode 100644 index 0000000..fc7b17d --- /dev/null +++ b/cmake/modules/DuneAddPybind11Module.cmake @@ -0,0 +1,92 @@ +# This cmake module provides infrastructure for building modules using Pybind11 +# +# .. cmake_function:: dune_add_pybind11_module +# +# .. cmake_param:: NAME +# :required: +# :single: +# +# name of the Python module +# +# .. cmake_param:: SOURCES +# :multi: +# +# source files to build shared library +# +# If this parameter is omitted, .cc will be used if it exists. +# +# .. cmake_param:: EXCLUDE_FROM_ALL +# :option: +# +# exclude this module from the all target +# +# .. cmake_param:: COMPILE_DEFINITIONS +# :multi: +# :argname: def +# +# A set of compile definitions to add to the target. +# Only definitions beyond the application of :ref:`add_dune_all_flags` +# have to be stated. +# +# .. cmake_param:: CMAKE_GUARD +# :multi: +# :argname: condition +# +# A number of conditions that CMake should evaluate before adding this +# module. Use this feature instead of guarding the call to +# :code:`dune_add_pybind11_module` with an :code:`if` clause. +# +# The passed condition can be a complex expression like +# `( A OR B ) AND ( C OR D )`. Mind the spaces around the parentheses. +# +# Example: Write CMAKE_GUARD dune-foo_FOUND if you want your module to only +# build when the dune-foo module is present. +# +include_guard(GLOBAL) + +function(dune_add_pybind11_module) + include(CMakeParseArguments) + cmake_parse_arguments(PYBIND11_MODULE "EXCLUDE_FROM_ALL" "NAME" "SOURCES;COMPILE_DEFINITIONS;CMAKE_GUARD" ${ARGN}) + if(PYBIND11_MODULE_UNPARSED_ARGUMENTS) + message(WARNING "dune_add_pybind11_module: extra arguments provided (typos in named arguments?)") + endif() + + if(NOT PYBIND11_MODULE_NAME) + message(FATAL_ERROR "dune_add_pybind11_module: module name not specified") + endif() + + if(NOT PYBIND11_MODULE_SOURCES) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PYBIND11_MODULE_NAME}.cc) + set(PYBIND11_MODULE_SOURCES ${PYBIND11_MODULE_NAME}.cc) + else() + message(FATAL_ERROR "dune_add_pybind11_module: no source files specified") + endif() + endif() + + foreach(condition ${PYBIND11_MODULE_CMAKE_GUARD}) + separate_arguments(condition) + if(NOT (${condition})) + message(STATUS "not building ${PYBIND11_MODULE_NAME}, because condition ${condition} failed.") + return() + endif() + endforeach() + + add_library(${PYBIND11_MODULE_NAME} SHARED ${PYBIND11_MODULE_SOURCES}) + set_target_properties(${PYBIND11_MODULE_NAME} PROPERTIES PREFIX "") + + # force '.so' as library suffix on macOS due to a problem in Python + # https://stackoverflow.com/questions/2488016/how-to-make-python-load-dylib-on-osx + # and add -undefined dynamic_lookup flag to linker + # https://pybind11.readthedocs.io/en/stable/compiling.html#building-manually + if (APPLE) + set_target_properties(${PYBIND11_MODULE_NAME} PROPERTIES SUFFIX ".so") + target_link_options(${PYBIND11_MODULE_NAME} PRIVATE -undefined dynamic_lookup) + endif() + + target_compile_definitions(${PYBIND11_MODULE_NAME} PRIVATE ${PYBIND11_MODULE_COMPILE_DEFINITIONS}) + dune_target_enable_all_packages(${PYBIND11_MODULE_NAME}) + + if(PYBIND11_MODULE_EXCLUDE_FROM_ALL) + set_property(TARGET ${PYBIND11_MODULE_NAME} PROPERTY EXCLUDE_FROM_ALL 1) + endif() +endfunction() diff --git a/cmake/modules/DuneCMakeCompat.cmake b/cmake/modules/DuneCMakeCompat.cmake new file mode 100644 index 0000000..74440b7 --- /dev/null +++ b/cmake/modules/DuneCMakeCompat.cmake @@ -0,0 +1,96 @@ +# Module with backward compatibility implementation of newer cmake functionality +# +# .. cmake_module:: +# +# This module contains backward compatibility implementations of cmake +# functionality that is not available in all cmake versions we support. +# +# * :ref:`dune_list_filter(...) ` for ``list(FILTER +# ...)`` from cmake 3.7 +# +# +# .. cmake_function:: dune_list_filter +# +# .. cmake_brief:: +# +# Compatibility implementation of ``list(FILTER)`` +# +# .. cmake_param:: list +# :positional: +# :single: +# :required: +# +# Name of list variable used as both input and output. +# +# .. cmake_param:: +# :positional: +# :option: +# :required: +# +# Whether to include or to exclude the items matching the regular +# expression. +# +# .. cmake_param:: REGEX +# :single: +# :required: +# :argname: regular_expression +# +# The regular expression to match the items against. +# +# Match each item in the list against the regular expression. In +# ``INCLUDE`` mode the result contains all items that matched, in +# ``EXCLUDE`` mode it contains all items that did not match. Store the +# result back in the variable ``list`` in the scope of the caller. +# +# This is exactly the same as the ``list(FILTER ...)`` command available in +# cmake 3.7 and onward. +include_guard(GLOBAL) + +# list(FILTER...) was introduced in cmake 3.6, this is a compatibility +# implementation for earlier cmakes +function(dune_list_filter list mode REGEX regular_expression) + message(DEPRECATION "dune_list_filter is deprecated and will be removed after Dune 2.8. Use list(FILTER ...) from CMake 3.6") + + # validate arguments + if(NOT (("${mode}" STREQUAL "INCLUDE") OR ("${mode}" STREQUAL "EXCLUDE"))) + message(FATAL_ERROR "unsupported mode '${mode}', must be either INCLUDE or EXCLUDE") + endif() + if(NOT ("${REGEX}" STREQUAL "REGEX")) + message(FATAL_ERROR "dune_list_filter can only filter by regular expression") + endif() + if("${ARGC}" GREATER 4) + message(FATAL_ERROR "extra arguments given: <${ARGN}>") + endif() + + # cmake can't destinguish between empty lists and lists with one empty + # element. This is a problem when consecutively appending elements to a + # list: if the first elements we append are empty, we loose them. The + # "non-empty" token makes sure we start with a non-empty list and avoid this + # problem. + set(matched "non-empty") + set(unmatched "non-empty") + foreach(item IN LISTS "${list}") + # list(APPEND) does not quote the appended item (as of cmake 3.7.2), so do + # it manually + string(REPLACE [[;]] [[\;]] quoted_item "${item}") + if("${item}" MATCHES "${regular_expression}") + list(APPEND matched "${quoted_item}") + else() + list(APPEND unmatched "${quoted_item}") + endif() + endforeach(item) + + if("${mode}" STREQUAL "INCLUDE") + set(result "${matched}") + else() + set(result "${unmatched}") + endif() + + # remove the non-empty token from above. If the proper result would be a + # list of one empty element, we have no way of preserving that, it will turn + # into an empty list. + string(REGEX REPLACE "^non-empty;?" "" result "${result}") + + # export + set("${list}" "${result}" PARENT_SCOPE) +endfunction(dune_list_filter) diff --git a/cmake/modules/DuneCommonMacros.cmake b/cmake/modules/DuneCommonMacros.cmake new file mode 100644 index 0000000..4883c29 --- /dev/null +++ b/cmake/modules/DuneCommonMacros.cmake @@ -0,0 +1,51 @@ +# enforce C++-14 +dune_require_cxx_standard(MODULE "dune-common" VERSION 14) + +include(DuneStreams) +dune_set_minimal_debug_level() + +# search for lapack +find_package(LAPACK) +include(AddBLASLapackFlags) + +find_package(GMP) +include(AddGMPFlags) +find_package(QuadMath) +include(AddQuadMathFlags) + +# find program for image manipulation +find_package(Inkscape) +include(UseInkscape) + +# find the threading library +find_package(Threads) +include(AddThreadsFlags) + +# find the MPI library +find_package(MPI 3.0 COMPONENTS C) +include(AddMPIFlags) + +# find library for Threading Building Blocks +find_package(TBB) +include(AddTBBFlags) + +# find libraries for graph partitioning +find_package(PTScotch) +include(AddPTScotchFlags) +find_package(METIS) +include(AddMETISFlags) +find_package(ParMETIS 4.0) +include(AddParMETISFlags) + +# try to find the Vc library +set(MINIMUM_VC_VERSION) +if((CMAKE_CXX_COMPILER_ID STREQUAL Clang) AND + (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7)) + message("Raising minimum acceptable Vc version to 1.4.1 due to use of Clang 7 (or later), see https://gitlab.dune-project.org/core/dune-common/issues/132") + set(MINIMUM_VC_VERSION 1.4.1) +endif() +find_package(Vc ${MINIMUM_VC_VERSION} NO_MODULE) +include(AddVcFlags) + +# Run the python extension of the Dune cmake build system +include(DunePythonCommonMacros) diff --git a/cmake/modules/DuneCxaDemangle.cmake b/cmake/modules/DuneCxaDemangle.cmake new file mode 100644 index 0000000..ba577ae --- /dev/null +++ b/cmake/modules/DuneCxaDemangle.cmake @@ -0,0 +1,4 @@ +message(DEPRECATION + "The cmake file 'DuneCxaDemangle.cmake' is deprecated. " + "Include the 'dune/common/classname.hh' header if you want to ensure that " + "HAVE_CXA_DEMANGLE is defined") diff --git a/cmake/modules/DuneDoc.cmake b/cmake/modules/DuneDoc.cmake new file mode 100644 index 0000000..1a97a4c --- /dev/null +++ b/cmake/modules/DuneDoc.cmake @@ -0,0 +1,84 @@ +# +# Module that provides a custom target make doc at the top level +# directory and utility macros for creating install directives +# that make sure that the files to be installed are previously +# generated even if make doc was not called. +# +# All documentation (Latex, Doxygen) will be generated during +# make doc. +# It provides the following macros: +# +# .. cmake_function:: dune_add_latex_document +# +# .. cmake_brief:: +# +# wrapper around add_latex_document for compatibility reasons +# +# .. cmake_function:: create_doc_install +# +# .. cmake_brief:: +# +# creates a target for creating and installing a file +# to a given directory. +# +# .. cmake_param:: filename +# :single: +# :required: +# :positional: +# +# The name of the file to be installed. +# +# .. cmake_param:: targetdir +# :single: +# :required: +# :positional: +# +# The directory into which the beforementioned file will be installed. +# +# .. cmake_param:: dependency +# :single: +# :required: +# :positional: +# +# A target that gets called to create the file that will be installed. +# +# .. note:: +# +# This macro is needed, as we cannot add dependencies to the install +# target. See https://gitlab.kitware.com/cmake/cmake/issues/8438 +# and https://gitlab.dune-project.org/core/dune-common/issues/36 +# +include_guard(GLOBAL) + +include(UseLatexMk) + +if (LATEXMK_FOUND AND PDFLATEX_COMPILER) + set(LATEX_USABLE TRUE) +endif() + +add_custom_target(doc) + +# add the Sphinx-generated build system documentation +include(DuneSphinxCMakeDoc) +# Support building documentation with doxygen. +include(DuneDoxygen) + +macro(create_doc_install filename targetdir dependency) + if(LATEX_USABLE) + dune_module_path(MODULE dune-common RESULT scriptdir SCRIPT_DIR) + get_filename_component(targetfile ${filename} NAME) + set(install_command ${CMAKE_COMMAND} -D FILES=${filename} -D DIR=${CMAKE_INSTALL_PREFIX}/${targetdir} -P ${scriptdir}/InstallFile.cmake) + + # create a custom target for the installation + add_custom_target(install_${targetfile} ${install_command} + COMMENT "Installing ${filename} to ${targetdir}" + DEPENDS ${dependency}) + # When installing, call cmake install with the above install target and add the file to install_manifest.txt + install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" --build \"${CMAKE_BINARY_DIR}\" --target install_${targetfile} ) + LIST(APPEND CMAKE_INSTALL_MANIFEST_FILES ${CMAKE_INSTALL_PREFIX}/${targetdir}/${targetfile})") + endif() +endmacro(create_doc_install) + +macro(dune_add_latex_document) + add_latex_document(${ARGN}) +endmacro(dune_add_latex_document) diff --git a/cmake/modules/DuneDoxygen.cmake b/cmake/modules/DuneDoxygen.cmake new file mode 100644 index 0000000..e729e86 --- /dev/null +++ b/cmake/modules/DuneDoxygen.cmake @@ -0,0 +1,128 @@ +# Module for building documentation using doxygen. +# +# .. cmake_function:: add_doxygen_target +# +# .. cmake_param:: TARGET +# :single: +# +# The suffix to add to the target name, default to the module name. +# +# .. cmake_param:: DEPENDS +# :multi: +# +# A list of further dependencies of the doxygen documentation. +# Might include :code:`mainpage.txt`. +# +# .. cmake_param:: OUTPUT +# :single: +# +# Name of the output target, necessary if you don't generate html. +# +# This macro creates a target for building (:code:`doxygen_${ProjectName}`) and installing +# (:code:`doxygen_install_${ProjectName}`) the generated doxygen documentation. +# The documentation is built during the top-level :code:`make doc` call. We have added a dependency +# that makes sure it is built before running :code:`make install`. +# +include_guard(GLOBAL) + +find_package(Doxygen) +set_package_properties("Doxygen" PROPERTIES + DESCRIPTION "Class documentation generator" + URL "www.doxygen.org" + PURPOSE "To generate the class documentation from C++ sources") +include(CMakeParseArguments) + +# Set DOT_TRUE for the Doxyfile generation. +if (NOT DOXYGEN_DOT_FOUND) + set(DOT_TRUE '\#') +endif() + +add_custom_target(doxygen_install) + +# +# prepare_doxyfile() +# This functions adds the necessary routines for the generation of the +# Doxyfile[.in] files needed to doxygen. +macro(prepare_doxyfile) + message(STATUS "using ${DOXYSTYLE_FILE} to create doxystyle file") + message(STATUS "using C macro definitions from ${DOXYGENMACROS_FILE} for Doxygen") + + # check whether module has a Doxylocal file + find_file(_DOXYLOCAL Doxylocal PATHS ${CMAKE_CURRENT_SOURCE_DIR} NO_DEFAULT_PATH) + + if(_DOXYLOCAL) + set(make_doxyfile_command ${CMAKE_COMMAND} -D DOT_TRUE=${DOT_TRUE} -D DUNE_MOD_NAME=${ProjectName} -D DUNE_MOD_VERSION=${ProjectVersion} -D DOXYSTYLE=${DOXYSTYLE_FILE} -D DOXYGENMACROS=${DOXYGENMACROS_FILE} -D DOXYLOCAL=${CMAKE_CURRENT_SOURCE_DIR}/Doxylocal -D abs_top_srcdir=${CMAKE_SOURCE_DIR} -D srcdir=${CMAKE_CURRENT_SOURCE_DIR} -D top_srcdir=${CMAKE_SOURCE_DIR} -P ${scriptdir}/CreateDoxyFile.cmake) + add_custom_command(OUTPUT Doxyfile.in Doxyfile + COMMAND ${make_doxyfile_command} + COMMENT "Creating Doxyfile.in" + DEPENDS ${DOXYSTYLE_FILE} ${DOXYGENMACROS_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/Doxylocal) + else() + set(make_doxyfile_command ${CMAKE_COMMAND} -D DOT_TRUE=${DOT_TRUE} -D DUNE_MOD_NAME=${ProjectName} -D DUNE_MOD_VERSION=${DUNE_MOD_VERSION} -D DOXYSTYLE=${DOXYSTYLE_FILE} -D DOXYGENMACROS=${DOXYGENMACROS_FILE} -D abs_top_srcdir=${CMAKE_SOURCE_DIR} -D top_srcdir=${CMAKE_SOURCE_DIR} -P ${scriptdir}/CreateDoxyFile.cmake) + add_custom_command(OUTPUT Doxyfile.in Doxyfile + COMMAND ${make_doxyfile_command} + COMMENT "Creating Doxyfile.in" + DEPENDS ${DOXYSTYLE_FILE} ${DOXYGENMACROS_FILE}) + endif() + add_custom_target(doxyfile DEPENDS Doxyfile.in Doxyfile) +endmacro(prepare_doxyfile) + +macro(add_doxygen_target) + set(options ) + set(oneValueArgs TARGET OUTPUT) + set(multiValueArgs DEPENDS) + cmake_parse_arguments(DOXYGEN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + + # default target name is the module name + if(NOT DOXYGEN_TARGET) + set(DOXYGEN_TARGET ${ProjectName}) + endif() + + # default output is html + if(NOT DOXYGEN_OUTPUT) + set(DOXYGEN_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/html") + endif() + + dune_module_path(MODULE dune-common RESULT scriptdir SCRIPT_DIR) + if(PROJECT_NAME STREQUAL "dune-common") + set(DOXYSTYLE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Doxystyle) + set(DOXYGENMACROS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/doxygen-macros) + endif() + message(STATUS "Using scripts from ${scriptdir} for creating doxygen stuff.") + + if(DOXYGEN_FOUND) + prepare_doxyfile() + # custom command that executes doxygen + add_custom_command(OUTPUT ${DOXYGEN_OUTPUT} + COMMAND ${CMAKE_COMMAND} -D DOXYGEN_EXECUTABLE=${DOXYGEN_EXECUTABLE} -P ${scriptdir}/RunDoxygen.cmake + COMMENT "Building doxygen documentation. This may take a while" + DEPENDS Doxyfile.in ${DOXYGEN_DEPENDS}) + # Create a target for building the doxygen documentation of a module, + # that is run during make doc + add_custom_target(doxygen_${DOXYGEN_TARGET} + DEPENDS ${DOXYGEN_OUTPUT}) + add_dependencies(doc doxygen_${DOXYGEN_TARGET}) + + # Use a cmake call to install the doxygen documentation and create a + # target for it + include(GNUInstallDirs) + # When installing call cmake install with the above install target + install(CODE + "execute_process(COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target doxygen_${ProjectName} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + file(GLOB doxygenfiles + GLOB ${CMAKE_CURRENT_BINARY_DIR}/html/*.html + ${CMAKE_CURRENT_BINARY_DIR}/html/*.js + ${CMAKE_CURRENT_BINARY_DIR}/html/*.png + ${CMAKE_CURRENT_BINARY_DIR}/html/*.css + ${CMAKE_CURRENT_BINARY_DIR}/html/*.gif + ${CMAKE_CURRENT_BINARY_DIR}/*.tag + ) + set(doxygenfiles \"\${doxygenfiles}\") + foreach(_file \${doxygenfiles}) + get_filename_component(_basename \${_file} NAME) + LIST(APPEND CMAKE_INSTALL_MANIFEST_FILES ${CMAKE_INSTALL_FULL_DOCDIR}/doxygen/\${_basename}) + endforeach() + file(INSTALL \${doxygenfiles} DESTINATION ${CMAKE_INSTALL_FULL_DOCDIR}/doxygen) + message(STATUS \"Installed doxygen into ${CMAKE_INSTALL_FULL_DOCDIR}/doxygen\")") + endif() +endmacro(add_doxygen_target) diff --git a/cmake/modules/DuneEnableAllPackages.cmake b/cmake/modules/DuneEnableAllPackages.cmake new file mode 100644 index 0000000..0418334 --- /dev/null +++ b/cmake/modules/DuneEnableAllPackages.cmake @@ -0,0 +1,347 @@ +# Implementation of a simplified CMake build system. +# +# .. cmake_function:: dune_enable_all_packages +# +# .. cmake_brief:: +# +# Previously, the DUNE build system relied on the user to choose and add the compile and link flags +# necessary to build an executable. While this offers full control to the user, it +# is an error-prone procedure. +# +# Alternatively, users may use this function to simply add the compile flags for all +# found external modules to all executables in a DUNE module. Likewise, all found libraries are +# linked to all targets. +# +# .. cmake_param:: INCLUDE_DIRS +# :multi: +# +# A list of include directories, that should be added to all targets. +# In a standard Dune module, it is not necessary to specify anything. +# +# .. cmake_param:: COMPILE_DEFINITIONS +# :multi: +# +# A list of compile definitions, that should be added to all targets. +# In a standard Dune module, it is not necessary to specify anything. +# +# .. cmake_param:: COMPILE_OPTIONS +# :multi: +# +# A list of non-definition compile options, that should be added to all targets. +# In a standard Dune module, it is not necessary to specify anything. +# +# .. cmake_param:: MODULE_LIBRARIES +# :multi: +# +# If your module contains libraries as well as programs and if the programs should automatically +# link to those libraries, you *MUST* list these libraries in :code:`MODULE_LIBRARIES`. Those libraries will be +# automatically created by :ref:`dune_enable_all_packages` (which internally calls :ref:`dune_add_library`) and placed +# in the lib/ directory. The order of the libraries matters: if one library depends on another one, it must +# be listed after its dependency. This special handling of the libraries is due to the way CMake +# handle linking (in particular CMP022 and CMP038). You can later add source files to the library +# anywhere in the source tree by calling :ref:`dune_library_add_sources`. +# +# .. cmake_param:: VERBOSE +# :option: +# +# If this option is set, the set of compile flags, linked libraries and include directories +# that is in use for all targets in the module is printed upon configuration. +# +# .. cmake_param:: APPEND +# :option: +# +# If this option is set, the definitions, flags and directories specified in this function are +# appended to the global collection of flags instead of being prepended. Only use it, if you know +# what you are doing. +# +# Adds all flags and all libraries to all executables that are subsequently added in the directory +# from where this function is called and from all its subdirectories (recursively). +# If used, this function *MUST* be called in the top level CMakeLists.txt BEFORE adding any subdirectories! +# You can optionally add additional include dirs and compile definitions that will also be applied to +# all targets in the module. +# +# .. note:: +# If you want to use :code:`dune_enable_all_packages` with an older version of CMake and your DUNE module +# creates its own library, you have to manually create the library in the top-level CMakeLists.txt +# file using :ref:`dune_add_library` (with all sources listed within that call), use +# :ref:`dune_target_enable_all_packages` to add all packages to the library and finally list that library +# under :code:`LIBRARIES` in the call to :ref:`dune_register_package_flags`. See dune-pdelab for an example of +# how to do this correctly. +# +# While :ref:`dune_enable_all_packages` defines the user interface for this feature, developers might +# also be interested in the following related functions: +# +# * :ref:`dune_target_enable_all_packages` +# * :ref:`dune_register_package_flags` +# * :ref:`dune_library_add_sources` +# +# .. cmake_function:: dune_target_enable_all_packages +# +# .. cmake_param:: TARGETS +# :multi: +# +# A list of targets to add all flags etc. too. +# +# Adds all currently registered package flags (see :ref:`dune_register_package_flags`) to the given targets. +# This function is mainly intended to help write DUNE modules that want to use :ref:`dune_enable_all_packages` and +# define their own libraries, but need to be compatible with CMake < 3.1 +# +# .. cmake_function:: dune_register_package_flags +# +# .. cmake_param:: INCLUDE_DIRS +# :multi: +# +# The list of include directories needed by the external package. +# +# .. cmake_param:: COMPILE_DEFINITIONS +# :multi: +# +# The list of compile definitions needed by the external package. +# +# .. cmake_param:: COMPILE_OPTIONS +# :multi: +# +# The list of compile options needed by the external package. +# +# .. cmake_param:: LIBRARIES +# :multi: +# +# The list of libraries that the external package should link to. +# The order of the input is preserved in the output. +# +# .. cmake_param:: APPEND +# :option: +# +# If this option is set, the definitions, flags and directories specified in this function are +# appended to the global collection of flags instead of being prepended. Only use it, if you know +# what you are doing. +# +# To correctly implement the automatic handling of external libraries, the compile flags, include paths and link +# flags of all found packages must be registered with this function. This function is only necessary for people that +# want to write their own :code:`FindFooBar` CMake modules to link against additional libraries which are not supported by +# the DUNE core modules. Call this function at the end of every find module. If you are using an external FindFoo +# module which you cannot alter, call it after the call to :code:`find_package(foo)`. +# +# .. cmake_function:: dune_library_add_sources +# +# .. cmake_param:: module_library +# :single: +# :positional: +# +# The name of the module library target. +# +# .. cmake_param: SOURCES +# :multi: +# :required: +# +# The source files to add to the DUNE module library :code:`module_library`. +# That library must have been created by an earlier call to :ref:`dune_enable_all_packages` +# in the current DUNE module. +# +# Register sources for module exported library. +# +include_guard(GLOBAL) + +function(dune_register_package_flags) + include(CMakeParseArguments) + set(OPTIONS APPEND) + set(SINGLEARGS) + set(MULTIARGS COMPILE_DEFINITIONS COMPILE_OPTIONS INCLUDE_DIRS LIBRARIES) + cmake_parse_arguments(REGISTRY "${OPTIONS}" "${SINGLEARGS}" "${MULTIARGS}" ${ARGN}) + + if(REGISTRY_UNPARSED_ARGUMENTS) + message(WARNING "Unrecognized arguments for dune_register_package_flags!") + endif() + + if(REGISTRY_APPEND) + set_property(GLOBAL APPEND PROPERTY ALL_PKG_INCS "${REGISTRY_INCLUDE_DIRS}") + set_property(GLOBAL APPEND PROPERTY ALL_PKG_LIBS "${REGISTRY_LIBRARIES}") + set_property(GLOBAL APPEND PROPERTY ALL_PKG_DEFS "${REGISTRY_COMPILE_DEFINITIONS}") + set_property(GLOBAL APPEND PROPERTY ALL_PKG_OPTS "${REGISTRY_COMPILE_OPTIONS}") + else(REGISTRY_APPEND) + get_property(all_incs GLOBAL PROPERTY ALL_PKG_INCS) + get_property(all_libs GLOBAL PROPERTY ALL_PKG_LIBS) + get_property(all_defs GLOBAL PROPERTY ALL_PKG_DEFS) + get_property(all_opts GLOBAL PROPERTY ALL_PKG_OPTS) + set_property(GLOBAL PROPERTY ALL_PKG_INCS "${REGISTRY_INCLUDE_DIRS}" "${all_incs}") + set_property(GLOBAL PROPERTY ALL_PKG_LIBS "${REGISTRY_LIBRARIES}" "${all_libs}") + set_property(GLOBAL PROPERTY ALL_PKG_DEFS "${REGISTRY_COMPILE_DEFINITIONS}" "${all_defs}") + set_property(GLOBAL PROPERTY ALL_PKG_OPTS "${REGISTRY_COMPILE_OPTIONS}" "${all_opts}") + endif(REGISTRY_APPEND) +endfunction(dune_register_package_flags) + + +function(dune_enable_all_packages) + include(CMakeParseArguments) + set(OPTIONS APPEND VERBOSE) + set(SINGLEARGS) + set(MULTIARGS COMPILE_DEFINITIONS COMPILE_OPTIONS INCLUDE_DIRS MODULE_LIBRARIES) + cmake_parse_arguments(DUNE_ENABLE_ALL_PACKAGES "${OPTIONS}" "${SINGLEARGS}" "${MULTIARGS}" ${ARGN}) + + if(DUNE_ENABLE_ALL_PACKAGES_UNPARSED_ARGUMENTS) + message(WARNING "Unrecognized arguments for dune_enable_all_packages!") + endif() + + # handle additional include dirs specified in dune_enable_all_packages + if(DUNE_ENABLE_ALL_PACKAGES_INCLUDE_DIRS) + if(DUNE_ENABLE_ALL_PACKAGES_APPEND) + set_property(GLOBAL APPEND PROPERTY ALL_PKG_INCS "${DUNE_ENABLE_ALL_PACKAGES_INCLUDE_DIRS}") + else(DUNE_ENABLE_ALL_PACKAGES_APPEND) + get_property(all_incs GLOBAL PROPERTY ALL_PKG_INCS) + set_property(GLOBAL PROPERTY ALL_PKG_INCS "${DUNE_ENABLE_ALL_PACKAGES_INCLUDE_DIRS}" "${all_incs}") + endif(DUNE_ENABLE_ALL_PACKAGES_APPEND) + endif(DUNE_ENABLE_ALL_PACKAGES_INCLUDE_DIRS) + + # add include dirs to all targets in module + get_property(all_incs GLOBAL PROPERTY ALL_PKG_INCS) + include_directories(${all_incs}) + # verbose output of include dirs + if(DUNE_ENABLE_ALL_PACKAGES_VERBOSE) + message("Include directories for this project: ${all_incs}") + endif(DUNE_ENABLE_ALL_PACKAGES_VERBOSE) + + # handle additional compile definitions specified in dune_enable_all_packages + if(DUNE_ENABLE_ALL_PACKAGES_COMPILE_DEFINITIONS) + if(DUNE_ENABLE_ALL_PACKAGES_APPEND) + set_property(GLOBAL APPEND PROPERTY ALL_PKG_DEFS "${DUNE_ENABLE_ALL_PACKAGES_COMPILE_DEFINITIONS}") + else(DUNE_ENABLE_ALL_PACKAGES_APPEND) + get_property(all_defs GLOBAL PROPERTY ALL_PKG_DEFS) + set_property(GLOBAL PROPERTY ALL_PKG_DEFS "${DUNE_ENABLE_ALL_PACKAGES_COMPILE_DEFINITIONS}" "${all_defs}") + endif(DUNE_ENABLE_ALL_PACKAGES_APPEND) + endif(DUNE_ENABLE_ALL_PACKAGES_COMPILE_DEFINITIONS) + + # add compile definitions to all targets in module + get_property(all_defs GLOBAL PROPERTY ALL_PKG_DEFS) + # We have to do this in a loop because add_definitions() is kind of broken: even though it is supposed + # to be *the* function for adding compile definitions, it does not prepend "-D" (as opposed to + # target_compile_definitions(), which does). Well, whatever... + foreach(_definition ${all_defs}) + if(_definition) + add_definitions("-D${_definition}") + endif() + endforeach() + # verbose output of compile definitions + if(DUNE_ENABLE_ALL_PACKAGES_VERBOSE) + message("Compile definitions for this project: ${all_defs}") + endif(DUNE_ENABLE_ALL_PACKAGES_VERBOSE) + + # handle additional compile options specified in dune_enable_all_packages + if(DUNE_ENABLE_ALL_PACKAGES_COMPILE_OPTIONS) + if(DUNE_ENABLE_ALL_PACKAGES_APPEND) + set_property(GLOBAL APPEND PROPERTY ALL_PKG_OPTS "${DUNE_ENABLE_ALL_PACKAGES_COMPILE_OPTIONS}") + else(DUNE_ENABLE_ALL_PACKAGES_APPEND) + get_property(all_opts GLOBAL PROPERTY ALL_PKG_OPTS) + set_property(GLOBAL PROPERTY ALL_PKG_OPTS "${DUNE_ENABLE_ALL_PACKAGES_COMPILE_OPTIONS}" "${all_opts}") + endif(DUNE_ENABLE_ALL_PACKAGES_APPEND) + endif(DUNE_ENABLE_ALL_PACKAGES_COMPILE_OPTIONS) + + # add compile options to all targets in module + get_property(all_opts GLOBAL PROPERTY ALL_PKG_OPTS) + add_compile_options(${all_opts}) + # verbose output of compile definitions + if(DUNE_ENABLE_ALL_PACKAGES_VERBOSE) + message("Compile options for this project: ${all_opts}") + endif(DUNE_ENABLE_ALL_PACKAGES_VERBOSE) + + # handle libraries + # this is a little tricky because the libraries defined within the current module require special + # handling to avoid tripping over CMake policies CMP022 and CMP038 + + # first add all libraries of upstream Dune modules and of external dependencies + get_property(all_libs GLOBAL PROPERTY ALL_PKG_LIBS) + link_libraries(${DUNE_LIBS} ${all_libs}) + + # now we have to do a little dance: Newer versions of CMake complain if a target links to itself, + # so we have to create all targets for libraries inside the module before adding them to the set + # of default libraries to link to. That works because calling link_libraries does not affect targets + # which already exist. + # Moroever, CMake generates a warning when creating a library without any source files, and the linker + # does the same if we add an empty dummy file. We work around that problem by autogenerating a library-specific + # stub source file with two functions ${lib_name}_version() and ${lib_name}_version_string() and add that + # as an initial source file. + # After creating the library with dune_add_library(), we add it to all future targets with a call to + # link_libraries(). The user can then add the real source files by calling dune_library_add_sources() + # throughout the module. + + if(DUNE_ENABLE_ALL_PACKAGES_MODULE_LIBRARIES) + + # make sure the /lib directory exists - we need it to create the stub source file in there + file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/lib") + # figure out the location of the stub source template + dune_module_path(MODULE dune-common RESULT script_dir SCRIPT_DIR) + foreach(module_lib ${DUNE_ENABLE_ALL_PACKAGES_MODULE_LIBRARIES}) + # create the stub source file in the output directory (using a c++ compatible name)... + string(REGEX REPLACE "[^a-zA-Z0-9]" "_" module_lib_mangled ${module_lib}) + configure_file("${script_dir}/module_library.cc.in" "${PROJECT_BINARY_DIR}/lib/lib${module_lib}_stub.cc") + + # ...and create the library... + dune_add_library(${module_lib} SOURCES "${PROJECT_BINARY_DIR}/lib/lib${module_lib}_stub.cc") + # ...and add it to all future targets in the module + link_libraries(${module_lib}) + endforeach(module_lib ${DUNE_ENABLE_ALL_PACKAGES_MODULE_LIBRARIES}) + + # export the DUNE_ENABLE_ALL_PACKAGES_MODULE_LIBRARIES variable to the parent scope + # this is required to make dune_library_add_sources() work (see further down) + set( + DUNE_ENABLE_ALL_PACKAGES_MODULE_LIBRARIES + ${DUNE_ENABLE_ALL_PACKAGES_MODULE_LIBRARIES} + PARENT_SCOPE + ) + endif(DUNE_ENABLE_ALL_PACKAGES_MODULE_LIBRARIES) + + if(DUNE_ENABLE_ALL_PACKAGES_VERBOSE) + get_property(all_libs GLOBAL PROPERTY ALL_PKG_LIBS) + message("Libraries for this project: ${all_libs}") + endif(DUNE_ENABLE_ALL_PACKAGES_VERBOSE) + +endfunction(dune_enable_all_packages) + + +function(dune_target_enable_all_packages) + foreach(_target ${ARGN}) + + get_property(all_incs GLOBAL PROPERTY ALL_PKG_INCS) + target_include_directories(${_target} PUBLIC ${all_incs}) + + get_property(all_defs GLOBAL PROPERTY ALL_PKG_DEFS) + target_compile_definitions(${_target} PUBLIC ${all_defs}) + + get_property(all_opts GLOBAL PROPERTY ALL_PKG_OPTS) + target_compile_options(${_target} PUBLIC ${all_opts}) + + get_property(all_libs GLOBAL PROPERTY ALL_PKG_LIBS) + target_link_libraries(${_target} PUBLIC ${DUNE_LIBS} ${all_libs}) + + endforeach() +endfunction(dune_target_enable_all_packages) + + +function(dune_library_add_sources lib) + + if (NOT (DEFINED DUNE_ENABLE_ALL_PACKAGES_MODULE_LIBRARIES)) + message(FATAL_ERROR "You must call dune_enable_all_packages with the MODULE_LIBRARIES option before calling dune_library_add_sources") + endif() + + # This looks weird, but seems to be the most practical way to check for list membership, + # as list(FIND ...) does not work reliably in a macro... + if (NOT (";${DUNE_ENABLE_ALL_PACKAGES_MODULE_LIBRARIES};" MATCHES ";${lib};")) + message(FATAL_ERROR +"Attempt to add sources to library ${lib}, which has not been defined in dune_enable_all_packages. +List of libraries defined in dune_enable_all_packages: ${DUNE_ENABLE_ALL_PACKAGES_MODULE_LIBRARIES}") + endif() + + include(CMakeParseArguments) + cmake_parse_arguments(DUNE_LIBRARY_ADD_SOURCES "" "" "SOURCES" ${ARGN}) + + if(DUNE_LIBRARY_ADD_SOURCES_UNPARSED_ARGUMENTS) + message(WARNING "Unrecognized arguments for dune_library_add_sources!") + endif() + + foreach(source ${DUNE_LIBRARY_ADD_SOURCES_SOURCES}) + if(IS_ABSOLUTE ${source}) + target_sources(${lib} PRIVATE ${source}) + else() + target_sources(${lib} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/${source}) + endif() + endforeach() +endfunction() diff --git a/cmake/modules/DuneExecuteProcess.cmake b/cmake/modules/DuneExecuteProcess.cmake new file mode 100644 index 0000000..09bf1df --- /dev/null +++ b/cmake/modules/DuneExecuteProcess.cmake @@ -0,0 +1,59 @@ +# An error checking wrapper around the cmake command execute_process +# +# .. cmake_command:: dune_execute_process +# +# .. cmake_param:: ERROR_MESSAGE +# :single: +# +# Error message to show if command exited with non-zero exit code. +# This also implies abortion of the current cmake run with a fatal error. +# Note, that if this is omitted, no return code checking is done. +# +# A thin wrapper around the cmake command :code:`execute_process`, that +# exits on non-zero exit codes. All arguments are forwarded to the actual +# cmake command. +# +include_guard(GLOBAL) + +function(dune_execute_process) + include(CMakeParseArguments) + cmake_parse_arguments(EXECUTE "" "ERROR_MESSAGE;RESULT_VARIABLE;OUTPUT_VARIABLE;ERROR_VARIABLE" "" ${ARGN}) + + # Decide whether stdout and stderr have to be split + if(EXECUTE_OUTPUT_VARIABLE AND EXECUTE_ERROR_VARIABLE) + set(SPLIT_ERROR TRUE) + set(ERRLOGVAR errlog) + else() + set(SPLIT_ERROR FALSE) + set(ERRLOGVAR log) + endif() + + # Call the original cmake function + execute_process(${EXECUTE_UNPARSED_ARGUMENTS} + RESULT_VARIABLE retcode + OUTPUT_VARIABLE log + ERROR_VARIABLE ${ERRLOGVAR} + ) + + # Issue an error if requested! + if(EXECUTE_ERROR_MESSAGE) + if(NOT "${retcode}" STREQUAL "0") + cmake_parse_arguments(ERR "" "" "COMMAND" ${EXECUTE_UNPARSED_ARGUMENTS}) + if(SPLIT_ERROR) + set(log "stdout:\n${log}\n\nstderr:\b${errlog}") + endif() + message(FATAL_ERROR "${EXECUTE_ERROR_MESSAGE}\nRun command:${ERR_COMMAND}\nReturn code: ${retcode}\nDetailed log:\n${log}") + endif() + endif() + + # Propagate logs back to the calling scope + if(EXECUTE_RESULT_VARIABLE) + set(${EXECUTE_RESULT_VARIABLE} ${retcode} PARENT_SCOPE) + endif() + if(EXECUTE_OUTPUT_VARIABLE) + set(${EXECUTE_OUTPUT_VARIABLE} ${log} PARENT_SCOPE) + endif() + if(EXECUTE_ERROR_VARIABLE) + set(${EXECUTE_ERROR_VARIABLE} ${${ERROR_VARIABLE}} PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/modules/DuneInstance.cmake b/cmake/modules/DuneInstance.cmake new file mode 100644 index 0000000..3785bbe --- /dev/null +++ b/cmake/modules/DuneInstance.cmake @@ -0,0 +1,933 @@ +# Module to generate instantiations, typically for some template +# +# .. cmake_module:: +# +# This module can be used to generate explicit template instantiations. +# Suppose you have a template test function that you want to call for a +# number of template arguments. You want to explicitly instantiate the +# function for each set of template arguments, and put the instanciation +# into its own translation unit. (This can be beneficial in that it limits +# the amount of code that the optimizer sees at once, and thus it can +# reduce both memory and cpu requirements during compilation.) +# +# .. rubric:: Examples +# +# Let's say you are writing a test ``mytest.cc`` and need to call a +# template function for several types:: +# +# #include +# +# int main() { +# MyTestSuite suite; +# +# suite.test(); +# suite.test(); +# suite.test(); +# suite.test(); +# +# return suite.good() ? EXIT_SUCCESS : EXIT_FAILURE; +# } +# +# Let's further say that you want to explicitly instantiate each used +# ``MyTestSuite::test()`` instance in it's own translation unit. Then you +# need a series of files ``mytest_instance_bool.cc``, +# ``mytest_instance_char.cc``, etc, all with essentially the same content:: +# +# #include +# +# template void MyTestSuite::test<@TYPE@>(); +# +# where ``@TYPE@`` is replaced by ``bool``, ``char``, etc as appropriate. +# +# This is however not enough: all translation units need to know which +# instances of ``MyTestSuite::test()`` are instantiated explicitly so they +# do not instantiate them implicitly themselves (that would violate the +# one-definition rule). C++ only allows to declare individual instances as +# extern, not all of them collectively, so we need to put a list of all +# these instances into a header ``mytest.hh``:: +# +# #include +# +# extern template void MyTestSuite::test(); +# extern template void MyTestSuite::test(); +# extern template void MyTestSuite::test(); +# extern template void MyTestSuite::test(); +# +# We also need to include that header from each translation unit in the +# test, we can simply replace ``#include `` with ``#include +# ``. +# +# This is of course tedious and prone tp break if the list of tested types +# changes. To make this less fragile this module provides a series of +# commands: :ref:`dune_instance_begin() `, +# :ref:`dune_instance_add() `, and +# :ref:`dune_instance_end() `, which can be used to +# automatically generate the explicit instantiations for each type and the +# contents for the header and the body of main. +# +# This may look like this in ``CMakeLists.txt``:: +# +# dune_instance_begin(FILES mytest.cc mytest.hh) +# +# foreach(TYPE IN ITEMS bool char int double) +# dune_instance_add(ID "${TYPE}" FILES mytest_instance.cc) +# endforeach(TYPE IN ITEMS bool char int double) +# +# dune_instance_end() +# +# list(FILTER DUNE_INSTANCE_GENERATED INCLUDE REGEX [[\.cc$]]) +# dune_add_test(NAME mytest +# SOURCES ${DUNE_INSTANCE_GENERATED}) +# +# The call to :ref:`dune_instance_begin() ` reads +# ``mytest.cc.in`` and ``mytest.hh.in`` and splits them into embedded +# templates and other content. It will replace occurrances of ``@VAR@`` +# now in the other content and save the result for later. +# +# The call to :ref:`dune_instance_add() ` occurs in a +# loop. Each call will instanciate the embedded templates extracted +# earlier to replace occurance of ``@TYPE@`` by the value of the variable +# ``TYPE`` set in the for loop. Then files containing explicit +# instantiatons will be generated as ``mytest_instance_bool.cc``, +# ``mytest_instance_bool.cc``, etc, from a template file +# ``mytest_instance.cc.in``. The name of the generated files are the base +# file name from the template definition with the ``ID`` inserted before +# the extension. The name of the template file is the same base file name +# with ``.in`` appended. +# +# :ref:`dune_instance_end() ` is used to write +# ``mytest.cc`` and ``mytest.hh`` with the collected content from the +# embedded templates. The list of generated files will be available in the +# variable ``DUNE_INSTANCE_GENERATED``. +# +# The template files then look like this: +# +# ``mytest.cc.in``:: +# +# // @GENERATED_SOURCE@ +# +# #include +# +# #include +# +# int main() { +# MyTestSuite suite; +# +# #cmake @template@ +# suite.test<@TYPE@>(); +# #cmake @endtemplate@ +# +# return suite.good() ? EXIT_SUCCESS : EXIT_FAILURE; +# } +# +# ``mytest.hh.in``:: +# +# // @GENERATED_SOURCE@ +# +# #include +# +# #cmake @template@ +# extern template void MyTestSuite::test<@TYPE@>(); +# #cmake @endtemplate@ +# +# ``mytest_instance.cc.in``:: +# +# // @GENERATED_SOURCE@ +# +# #include +# +# #include +# +# template void MyTestSuite::test<@TYPE@>(); +# +# The ``@GENERATED_SOURCE@`` substitution is good practice, it tells a +# human reader that this file was generated and what the template file was, +# and it hints editors to go into read-only mode. +# +# .. rubric:: Embedded Templates +# +# The template files given in :ref:`dune_instance_begin() +# ` can contain embedded templates. These will be +# instantiated by :ref:`dune_instance_add() `, and all +# instantiations will be concatenated together and replace the original +# embedded template. +# +# The begin of an embedded template is marked by a line containing +# ``@template@`` or ``@template NAME@``. Leaving off the name is +# equivalent to an empty name. ``dune_instance_add(TEMPLATE NAME)`` will +# only instanciate embedded templates whose name matches and ignore all +# others. +# +# The end of an embedded template is marked by a line containing +# ``@endtemplate@`` or ``@endtemplate NAME@``. If a name is given, it must +# match the name of the embedded template it closes. If no name is given +# (or the name is empty), that check is omitted. +# +# There may be arbitrary characters on the same line before or after the +# begin and end markers. These are ignored, so you can use them for +# comments or to trick your editor into proper indentation. The one +# exception is that the line surrounding the marker may not contain any +# ``@`` characters to avoid ambiguities. +# +# .. rubric:: How Files And Strings Are Generated +# +# The generation is done using the cmake command ``configure_file(...)`` +# for template files and ``string(CONFIGURE ...)`` for template strings. +# These simply substitute the current variable values, so make sure to set +# up the variables to substitute before calling :ref:`dune_instance_add() +# ` or :ref:`dune_instance_begin() +# `. +# +# Refrain from using substitutions that begin with an underscore +# (i.e. ``@_my_local_var@``). The generation functions in this module use +# such names for their local variables and may hide the variable you are +# trying to substitude. +# +# When instantiating files we set up a few convenience variables before +# calling ``configure_file()`` that can be used in substitutions: +# ``@TEMPLATE@`` contains the name of the template file. ``@INSTANCE@`` +# contains the name of the file being generated, not including an implied +# ``${CMAKE_CURRENT_BINARY_DIR}``. Use ``@BINDIR_INSTANCE@`` if you do +# want the implied ``${CMAKE_CURRENT_BINARY_DIR}``. ``@GENERATED_SOURCE@`` +# contains a one-line message that this file was generated, including the +# name of the template file. +# +# .. rubric:: Main Interface +# +# These are the ones you normally use. +# +# * :ref:`dune_instance_begin() ` +# * :ref:`dune_instance_add() ` +# * :ref:`dune_instance_end() ` +# * :ref:`DUNE_INSTANCE_GENERATED ` +# +# .. rubric:: Utilities +# +# You would not use these directly under normal circumstances. +# +# * :ref:`dune_instance_parse_file_spec() ` +# * :ref:`dune_instance_from_id() ` +# * :ref:`dune_instance_generate_file() ` +# +# +# .. cmake_function:: dune_instance_begin +# +# .. cmake_brief:: +# +# Prepare for a list of instances. +# +# .. cmake_param:: FILES +# :multi: +# :argname: file_spec +# +# List of template files with embedded templates. +# +# Read the given template files, and extract embedded templates. Run the +# generator on the remaining file content with the variables currently in +# effect. +# +# .. note:: +# +# A matching :ref:`dune_instance_end() ` is required. +# Since information is communicated through variables in the callers +# scope, :ref:`dune_instance_begin() +# `/:ref:`dune_instance_end() ` +# blocks may not be nested inside the same scope. Since a function is a +# new scope, it may safely contain a :ref:`dune_instance_begin() +# `/:ref:`dune_instance_end() ` +# block, even if it is itself called from one. +# +# +# .. cmake_function:: dune_instance_add +# +# .. cmake_brief:: +# +# Instantiate a template with the currently set variable values. +# +# .. cmake_param:: FILES +# :multi: +# :argname: file_spec +# +# List of template file specifications. These are usually the names of +# template files with the ``.in`` extension removed. See the ID +# parameter for details. +# +# .. cmake_param:: ID +# :single: +# +# Used to build the names of generated files. Each file specification +# together with this id is given to :ref:`dune_instance_from_id() +# ` to determine the name of a template file and +# the name of an instance file. To get unique instance file names this +# ID should usually be a list of variable values joined together by +# ``_``. +# +# Specifically, each file specification may be of the form +# ``template_file_name:base_instance_file_name``, or it may be a single +# token not containing ``:``. In the latter case, if that token +# contains a trailing ``.in``, that is removed and the result is the base +# instance file name. The base instance file name has the ``.in`` +# appended again to form the template file name. +# +# The template file name is used as-is to generate files from. +# +# The ID is mangled by replacing any runs of non-alphanumeric characters +# with an underscore ``_``, and stripping any resulting underscore from +# the beginning and the end. The result is inserted before any +# extension into the base instance file name to form the instance file +# name. +# +# .. cmake_param:: TEMPLATE +# :single: +# +# Instantiate embedded templates by this name. Defaults to an empty +# name, matching embedded templates without name. +# +# Instantiate any embedded templates that match the given template name, +# substituting the current variables values. Then, generate files +# according the the file specifications in the template, doing +# substitutions as well. +# +# +# .. cmake_function:: dune_instance_end +# +# .. cmake_brief:: +# +# Close a block started by :ref:`dune_instance_begin() +# `, and write the files generated from the +# templates given there. +# +# Write the files generated from the template files given in +# :ref:`dune_instance_begin() `, including any content +# generated from embedded templates in :ref:`dune_instance_add() +# `. +# +# +# .. cmake_function:: dune_instance_parse_file_spec +# +# .. cmake_brief:: +# +# Parse a file specification into a template file name and an instance +# file name. +# +# .. cmake_param:: spec +# :positional: +# :single: +# :required: +# +# The file specification. +# +# .. cmake_param:: template_var +# :positional: +# :single: +# +# Name of the variable to store the template file name in. Can be empty +# to discard the template file name. +# +# .. cmake_param:: instance_var +# :positional: +# :single: +# +# Name of the variable to store the instance file name in. Can be empty +# to discard then instance file name. +# +# The file specification can be the name of a template file if it has +# ``.in`` at the end, or the name of an instance file if it doesn't. The +# name of the other file is obtained by appending or removing ``.in``, as +# applicable. Both file names can also be given explicitly in the form +# ``template_file_name:instance_file_name``. +# +# .. note:: +# +# This is the function use to parse the file specifications in +# :ref:`dune_instance_begin() `. It is also used +# as a helper in :ref:`dune_instance_from_id() ` +# to determine template file name and base instance file name. +# +# +# .. cmake_function:: dune_instance_from_id +# +# .. cmake_brief:: +# +# Determine a template file name and an instance file name from a file +# specification and a unique id. +# +# .. cmake_param:: file_spec +# :positional: +# :single: +# :required: +# +# The file specification. +# +# .. cmake_param:: id +# :positional: +# :single: +# :required: +# +# The id specification. This should uniquely identify an instance. +# +# .. cmake_param:: template_var +# :positional: +# :single: +# +# Name of the variable to store the template file name in. Can be empty +# to discard the template file name. +# +# .. cmake_param:: instance_var +# :positional: +# :single: +# +# Name of the variable to store the instance file name in. Can be empty +# to discard the instance file name. +# +# The file specification is handed to :ref:`dune_instance_parse_file_spec() +# ` to determine a template file name and a +# *base* instance file name. +# +# The ID is mangled by replacing any runs of non-alphanumeric characters +# with an underscore ``_``, and stripping any resulting underscore from the +# beginning and the end. The result is inserted before any extension into +# the base instance file name to form the instance file name. +# +# .. note:: +# +# This is the function use to parse the file specifications given in +# :ref:`dune_instance_add(FILES ...) `. +# +# +# .. cmake_function:: dune_instance_apply_bindir +# +# .. cmake_brief:: +# +# Modify a filename to be relative to ``CMAKE_CURRENT_BINARY_DIR``. +# +# .. cmake_param:: fname_var +# :positional: +# :single: +# :required: +# +# The name of the variable containing the file name. +# +# This is used to mimic the behaviour of ``configure_file()``. If the file +# name given is not absolute, it is modified by prepending +# ``${CMAKE_CURRENT_BINARY_DIR}``. +# +# +# .. cmake_function:: dune_instance_generate_file +# +# .. cmake_brief:: +# +# Convenience replacement for ``configure_file()``: enable standard +# substitutions, register files as generated, and flag the same file +# being generated twice. +# +# .. cmake_param:: TEMPLATE +# :positional: +# :single: +# :required: +# +# The name of the template file. +# +# .. cmake_param:: INSTANCE +# :positional: +# :single: +# :required: +# +# The name of the generated file. This is assumed relative to +# ``${CMAKE_CURRENT_BINARY_DIR}``. +# +# Make sure the variables ``TEMPLATE``, ``INSTANCE``, and +# ``BINDIR_INSTANCE`` are set to the parameter values and available for +# substitution. Also set the variable ``GENERATED_SOURCE`` to a one-line +# message that tells a human reader that this file is generated, and the +# name of the template file it was generated from. The message also +# includes hints for common editors telling them to switch to read-only +# mode. +# +# Then generate the file as if by ``configure_file()``. +# +# If the instance file has been registered as a generated source file +# before, this function generates a fatal error. This ensures that any +# accidential attempt to generate the same file twice is caught. As a +# special exception, if the generated content is the same as before, the +# error is silently skipped. +# +# +# .. cmake_variable:: DUNE_INSTANCE_GENERATED +# +# After :ref:`dune_instance_end() `, this holds the list +# of files that were generated. The list entries include an implied +# ``${CMAKE_CURRENT_BINARY_DIR}``, as appropriate. +# +# Do not rely on the value of this variable and do not modify it inside a +# :ref:`dune_instance_begin() +# `/:ref:`dune_instance_end() ` +# block. +include_guard(GLOBAL) + +# macro to print additional information to the cmake output file. +# Note: in cmake 3.15 this is available through the message(VERBOSE "...") function. +macro(message_verbose TEXT) + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.15") + message(VERBOSE "${TEXT}") + else() + file(APPEND ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "${TEXT}\n") + endif() +endmacro(message_verbose) + + +###################################################################### +# +# Coping with cmake list shortcomings +# + +# We use these commands internally to quote text before adding it to lists as +# an element, and to unquote elements again after extracting them. The quoted +# text is +# - free of ';' characters. This avoids problems when using list(APPEND), +# which does not quote ';' characters inside the appended element. It would +# also avoid problems with list(INSERT), which mangles any cmake quoting in +# the list it inserts to, but we don't actually use that command. +# - free of '\' characters. This avoids problems with a list element that +# ends in a '\' merging with the next element, because the '\' quotes the +# ';' that is used to seperate the elements +# - non-empty. This avoids the problem that cmake can't destinguish between +# an empty list and a list with one empty element. +function(dune_instance_quote_element VAR) + set(content "${${VAR}}") + string(REPLACE [[$]] [[$s]] content "${content}") + string(REPLACE [[;]] [[$:]] content "${content}") + string(REPLACE [[\]] [[$/]] content "${content}") + if(content STREQUAL "") + set(content [[$@]]) + endif() + set("${VAR}" "${content}" PARENT_SCOPE) +endfunction(dune_instance_quote_element) + +function(dune_instance_unquote_element VAR) + set(content "${${VAR}}") + string(REPLACE [[$@]] [[]] content "${content}") + string(REPLACE [[$/]] [[\]] content "${content}") + string(REPLACE [[$:]] [[;]] content "${content}") + string(REPLACE [[$s]] [[$]] content "${content}") + set("${VAR}" "${content}" PARENT_SCOPE) +endfunction(dune_instance_unquote_element) + +###################################################################### +# +# instance name and template name manipulation +# + +function(dune_instance_parse_file_spec spec template_var instance_var) + string(REPLACE ":" ";" spec_items "${spec}") + list(LENGTH spec_items len) + + # check arguments + if(len GREATER 2) + message(FATAL_ERROR "too many items in file specification: ${spec}") + endif(len GREATER 2) + if(len EQUAL 0) + message(FATAL_ERROR "empty file specification") + endif(len EQUAL 0) + + # use as-is + if(len EQUAL 2) + list(GET spec_items 0 instance) + list(GET spec_items 1 template) + endif(len EQUAL 2) + + # deduce + if(len EQUAL 1) + string(REGEX REPLACE ".in\\$" "" instance "${spec}") + set(template "${instance}.in") + endif(len EQUAL 1) + + #export + if(NOT ("${template_var}" STREQUAL "")) + set("${template_var}" "${template}" PARENT_SCOPE) + endif(NOT ("${template_var}" STREQUAL "")) + if(NOT ("${instance_var}" STREQUAL "")) + set("${instance_var}" "${instance}" PARENT_SCOPE) + endif(NOT ("${instance_var}" STREQUAL "")) +endfunction(dune_instance_parse_file_spec) + +# build output file name: parse the file_spec into a template name and a base +# instance name. Mangle the ID by replacing anything special with "_" and +# intersperse the result between basename and extension of the base instance +# name. Use the result as the instance name. +function(dune_instance_from_id file_spec id template_var instance_var) + dune_instance_parse_file_spec("${file_spec}" template base) + + # split into prefix and suffix + if(base MATCHES "\\.") + string(REGEX REPLACE "\\.[^.]*\$" "" prefix "${base}") + string(REGEX REPLACE "^.*(\\.[^.]*)\$" "\\1" suffix "${base}") + else(base MATCHES "\\.") + set(prefix "${base}") + set(suffix) + endif(base MATCHES "\\.") + + # mangle the id + string(REGEX REPLACE "[^a-zA-Z0-9]+" "_" mangled_id "${id}") + string(REGEX REPLACE "^_+" "" mangled_id "${mangled_id}") + string(REGEX REPLACE "_+\$" "" mangled_id "${mangled_id}") + if(mangled_id STREQUAL "") + message(FATAL_ERROR "\"${id}\" is empty after mangling") + endif(mangled_id STREQUAL "") + + #export + if(NOT ("${template_var}" STREQUAL "")) + set("${template_var}" "${template}" PARENT_SCOPE) + endif(NOT ("${template_var}" STREQUAL "")) + if(NOT ("${instance_var}" STREQUAL "")) + set("${instance_var}" "${prefix}_${mangled_id}${suffix}" PARENT_SCOPE) + endif(NOT ("${instance_var}" STREQUAL "")) +endfunction(dune_instance_from_id) + +# mimic the behaviour of configure_file(), placing relative paths in the +# current binary dir +function(dune_instance_apply_bindir fname_var) + if(NOT (IS_ABSOLUTE ${fname_var})) + set(${fname_var} "${CMAKE_CURRENT_BINARY_DIR}/${${fname_var}}" PARENT_SCOPE) + endif() +endfunction(dune_instance_apply_bindir) + + +###################################################################### +# +# File generation +# + +function(dune_instance_set_generated) + # prepare instance substitution variables + set(GENERATED_SOURCE + "generated from ${TEMPLATE} by cmake -*- buffer-read-only:t -*- vim: set readonly:" + PARENT_SCOPE) + + set(BINDIR_INSTANCE "${INSTANCE}") + dune_instance_apply_bindir(BINDIR_INSTANCE) + set(BINDIR_INSTANCE "${BINDIR_INSTANCE}" PARENT_SCOPE) +endfunction(dune_instance_set_generated) + +# Read a template file and split it into three lists +# - content_parts contains the parts before, between, and after templates +# - template_parts contains the content of each template +# - template_names contains the names of each template +# The elements in the lists are quoted using dune_instance_quote_element() to +# protect against problems with empty elements and against cmakes list() +# command butchering it's own quoting. +function(dune_instance_parse_embedded name content_parts template_parts template_names) + message_verbose("Parsing ${name} for embedded templates") + file(READ "${name}" content) + # ensure that the file content ends in a newline, which makes searching for + # template marker easier + string(APPEND content "\n") + + set(content_list "") + set(template_list "") + set(template_name_list "") + set(acc "") + set(lineno 0) + set(in_template FALSE) + while(NOT (content STREQUAL "")) + string(FIND "${content}" "\n" nextline) + math(EXPR nextline "${nextline} + 1") + + string(SUBSTRING "${content}" 0 "${nextline}" line) + string(SUBSTRING "${content}" "${nextline}" -1 content) + math(EXPR lineno "${lineno} + 1") + + if(line MATCHES "(.*)(@((end)?template)([ \t]+([-+._/0-9a-zA-Z]*))?@)(.*)") + set(prefix "${CMAKE_MATCH_1}") + set(sep "${CMAKE_MATCH_2}") + set(sep_keyword "${CMAKE_MATCH_3}") + set(sep_name "${CMAKE_MATCH_6}") + set(sep_suffix "${CMAKE_MATCH_7}") + + if(in_template) + if(NOT (sep_keyword STREQUAL "endtemplate")) + message(FATAL_ERROR "\ +${name}:${lineno}: '${sep}' nested inside... +${name}:${template_lineno}: ...'${template_sep}' here") + endif() + + if(NOT ((sep_name STREQUAL "") OR (sep_name STREQUAL template_name))) + message(FATAL_ERROR "\ +${name}:${template_lineno}: '${template_sep}' closed by nonmatching... +${name}:${lineno}: ...'${sep}' here") + endif() + + dune_instance_quote_element(acc) + list(APPEND template_list "${acc}") + dune_instance_quote_element(template_name) + list(APPEND template_name_list "${template_name}") + + set(in_template FALSE) + else() + if(NOT (sep_keyword STREQUAL "template")) + message(FATAL_ERROR "${name}:${lineno}: Lone '${sep}'") + endif() + + dune_instance_quote_element(acc) + list(APPEND content_list "${acc}") + set(template_sep "${sep}") + set(template_name "${sep_name}") + set(template_lineno "${lineno}") + + set(in_template TRUE) + endif() + set(acc "") + else() # line did not match seperator + string(APPEND acc "${line}") + endif() + endwhile() + + if(in_template) + message(FATAL_ERROR "${name}:${template_lineno}: Unclosed '${template_sep}'") + endif() + + dune_instance_quote_element(acc) + list(APPEND content_list "${acc}") + + set("${content_parts}" "${content_list}" PARENT_SCOPE) + set("${template_parts}" "${template_list}" PARENT_SCOPE) + set("${template_names}" "${template_name_list}" PARENT_SCOPE) +endfunction(dune_instance_parse_embedded) + +# Take the name of a list variable containing content parts other then +# embedded templates and instanciate each part. Put the result back into the +# same variable. List elements are quoted. +function(dune_instance_generate_parts _parts_list) + set(_acc "") + foreach(_part IN LISTS "${_parts_list}") + dune_instance_unquote_element(_part) + string(CONFIGURE "${_part}" _part) + dune_instance_quote_element(_part) + list(APPEND _acc "${_part}") + endforeach(_part) + set("${_parts_list}" "${_acc}" PARENT_SCOPE) +endfunction(dune_instance_generate_parts) + +function(dune_instance_generate_file TEMPLATE INSTANCE) + if(("${INSTANCE}" STREQUAL "") OR ("${TEMPLATE}" STREQUAL "")) + message(FATAL_ERROR "need both INSTANCE and TEMPLATE") + endif(("${INSTANCE}" STREQUAL "") OR ("${TEMPLATE}" STREQUAL "")) + + # prepare instance substitution variables + dune_instance_set_generated() + + # do the generation + message_verbose("Generating ${TEMPLATE} -> ${INSTANCE}") + file(READ "${TEMPLATE}" _content) + string(CONFIGURE "${_content}" _content) + + # make sure we did not generate this file before + get_property(_seen SOURCE "${BINDIR_INSTANCE}" PROPERTY DUNE_INSTANCE_GENERATED) + if(_seen) + file(READ "${BINDIR_INSTANCE}" _oldcontent) + if(NOT (_content STREQUAL _oldcontent)) + message(FATAL_ERROR "Attempt to generate ${INSTANCE} (from ${TEMPLATE}), " + "which has already been generated with different content") + endif() + # otherwise, the content matches, so nothing to do + else(_seen) + # _seen was false, but the file may still be around from a previous cmake + # run, only write if changed to avoid recompilations + dune_write_changed_file("${BINDIR_INSTANCE}" "${_content}") + set_property(SOURCE "${BINDIR_INSTANCE}" PROPERTY DUNE_INSTANCE_GENERATED TRUE) + set_property(DIRECTORY APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS "${TEMPLATE}") + endif(_seen) +endfunction(dune_instance_generate_file) + +# only write if the content changes, avoiding recompilations +function(dune_write_changed_file name content) + if(EXISTS "${name}") + file(READ "${name}" oldcontent) + if(content STREQUAL oldcontent) + return() + endif() + endif() + file(WRITE "${name}" "${content}") +endfunction(dune_write_changed_file) + +###################################################################### +# +# High-level interface commands +# + +function(dune_instance_begin) + cmake_parse_arguments(_arg + "" # options + "" # one_value_keywords + "FILES" # multi_value_keywords + ${ARGV} + ) + if(DEFINED _arg_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "unrecognized arguments: ${_arg_UNPARSED_ARGUMENTS}") + endif(DEFINED _arg_UNPARSED_ARGUMENTS) + + set(_all_content_parts "") + set(_all_template_parts "") + set(_all_template_names "") + foreach(_spec IN LISTS _arg_FILES) + dune_instance_parse_file_spec("${_spec}" TEMPLATE INSTANCE) + dune_instance_set_generated() + # reconfigure if the input template file changes + set_property(DIRECTORY APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS "${TEMPLATE}") + dune_instance_parse_embedded("${TEMPLATE}" + _file_content_parts _file_template_parts _file_template_names) + dune_instance_generate_parts(_file_content_parts) + + dune_instance_quote_element(_file_content_parts) + list(APPEND _all_content_parts "${_file_content_parts}") + + dune_instance_quote_element(_file_template_parts) + list(APPEND _all_template_parts "${_file_template_parts}") + + dune_instance_quote_element(_file_template_names) + list(APPEND _all_template_names "${_file_template_names}") + endforeach(_spec) + set(_DUNE_INSTANCE_GLOBAL_FILES "${_arg_FILES}" PARENT_SCOPE) + set(_DUNE_INSTANCE_CONTENT_PARTS "${_all_content_parts}" PARENT_SCOPE) + set(_DUNE_INSTANCE_TEMPLATE_PARTS "${_all_template_parts}" PARENT_SCOPE) + set(_DUNE_INSTANCE_TEMPLATE_NAMES "${_all_template_names}" PARENT_SCOPE) + + set(DUNE_INSTANCE_GENERATED "" PARENT_SCOPE) +endfunction(dune_instance_begin) + + +function(dune_instance_add) + cmake_parse_arguments(_arg + "" # options + "ID;TEMPLATE" # one_value_keywords + "FILES" # multi_value_keywords + ${ARGV} + ) + + if(DEFINED _arg_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "unrecognized arguments: ${_arg_UNPARSED_ARGUMENTS}") + endif(DEFINED _arg_UNPARSED_ARGUMENTS) + + # ensure _arg_TEMPLATE is set, even if it is the empty value + set(_arg_TEMPLATE "${_arg_TEMPLATE}") + + set(_template_used FALSE) + + ###################################################################### + # Instantiate global (list) templates + set(_all_content_parts "") + list(LENGTH _DUNE_INSTANCE_GLOBAL_FILES _file_count) + foreach(_file_index RANGE "${_file_count}") + # filter out the end of the range, this also works with empty ranges + if(_file_index EQUAL _file_count) + break() + endif() + list(GET _DUNE_INSTANCE_GLOBAL_FILES "${_file_index}" _spec) + dune_instance_parse_file_spec("${_spec}" TEMPLATE INSTANCE) + dune_instance_set_generated() + + list(GET _DUNE_INSTANCE_CONTENT_PARTS "${_file_index}" _content_parts) + dune_instance_unquote_element(_content_parts) + list(GET _DUNE_INSTANCE_TEMPLATE_PARTS "${_file_index}" _template_parts) + dune_instance_unquote_element(_template_parts) + list(GET _DUNE_INSTANCE_TEMPLATE_NAMES "${_file_index}" _template_names) + dune_instance_unquote_element(_template_names) + + set(_content_parts_result "") + list(LENGTH _template_parts _parts_count) + foreach(_part_index RANGE "${_parts_count}") + list(GET _content_parts "${_part_index}" _content_part) + # The list of template parts should be one shorter than the list of + # content parts + if(_part_index LESS _parts_count) + list(GET _template_names "${_part_index}" _template_name) + dune_instance_unquote_element(_template_name) + if(_template_name STREQUAL _arg_TEMPLATE) + set(_template_used TRUE) + + list(GET _template_parts "${_part_index}" _template_part) + dune_instance_unquote_element(_template_part) + string(CONFIGURE "${_template_part}" _result) + + dune_instance_unquote_element(_content_part) + string(APPEND _content_part "${_result}") + dune_instance_quote_element(_content_part) + endif() + endif() + list(APPEND _content_parts_result "${_content_part}") + endforeach(_part_index) + + dune_instance_quote_element(_content_parts_result) + list(APPEND _all_content_parts "${_content_parts_result}") + endforeach(_file_index) + set(_DUNE_INSTANCE_CONTENT_PARTS "${_all_content_parts}" PARENT_SCOPE) + + ###################################################################### + # instantiate per instance templates + foreach(_spec IN LISTS _arg_FILES) + set(_template_used TRUE) + dune_instance_from_id("${_spec}" "${_arg_ID}" _template_file _instance_file) + set(_bindir_instance_file "${_instance_file}") + dune_instance_apply_bindir(_bindir_instance_file) + dune_instance_generate_file("${_template_file}" "${_instance_file}") + list(FIND DUNE_INSTANCE_GENERATED "${_bindir_instance_file}" _found_pos) + if(_found_pos EQUAL -1) + list(APPEND DUNE_INSTANCE_GENERATED "${_bindir_instance_file}") + endif() + endforeach(_spec) + + # if we did not instantiate anything, that is probably an error + if(NOT _template_used) + message(FATAL_ERROR "No embedded template matched template '${_arg_TEMPLATE}', and no instance template files were given") + endif() + set(DUNE_INSTANCE_GENERATED "${DUNE_INSTANCE_GENERATED}" PARENT_SCOPE) +endfunction(dune_instance_add) + +function(dune_instance_end) + if(ARGC GREATER 0) + message(FATAL_ERROR "dune_instance_end() does not take any arguments") + endif() + + ###################################################################### + # Write global instances + list(LENGTH _DUNE_INSTANCE_GLOBAL_FILES _file_count) + foreach(_file_index RANGE "${_file_count}") + # filter out the end of the range, this also works with empty ranges + if(_file_index EQUAL _file_count) + break() + endif() + list(GET _DUNE_INSTANCE_GLOBAL_FILES "${_file_index}" _spec) + dune_instance_parse_file_spec("${_spec}" TEMPLATE INSTANCE) + set(BINDIR_INSTANCE "${INSTANCE}") + dune_instance_apply_bindir(BINDIR_INSTANCE) + + # make sure we did not generate this file before + get_property(_seen SOURCE "${BINDIR_INSTANCE}" PROPERTY DUNE_INSTANCE_GENERATED) + if("${_seen}") + message(FATAL_ERROR "Attempt to generate ${INSTANCE} (from ${TEMPLATE}), " + "which has already been generated") + endif("${_seen}") + + list(GET _DUNE_INSTANCE_CONTENT_PARTS "${_file_index}" _content_parts) + dune_instance_unquote_element(_content_parts) + + set(_content "") + foreach(_part IN LISTS _content_parts) + dune_instance_unquote_element(_part) + string(APPEND _content "${_part}") + endforeach(_part) + # remove the final newline that we appended when reading the template file + string(REGEX REPLACE "\n\$" "" _content "${_content}") + + message_verbose("Writing ${INSTANCE}") + # only write if the content changes, avoiding recompilations + dune_write_changed_file("${BINDIR_INSTANCE}" "${_content}") + + set_property(SOURCE "${BINDIR_INSTANCE}" PROPERTY DUNE_INSTANCE_GENERATED TRUE) + list(APPEND DUNE_INSTANCE_GENERATED "${BINDIR_INSTANCE}") + endforeach(_file_index) + + set(DUNE_INSTANCE_GENERATED "${DUNE_INSTANCE_GENERATED}" PARENT_SCOPE) +endfunction(dune_instance_end) diff --git a/cmake/modules/DuneMPI.cmake b/cmake/modules/DuneMPI.cmake new file mode 100644 index 0000000..bba94bf --- /dev/null +++ b/cmake/modules/DuneMPI.cmake @@ -0,0 +1,2 @@ +message(DEPRECATION "The cmake file 'DuneMPI.cmake' is deprecated. Include 'AddMPIFlags.cmake' instead.") +include(AddMPIFlags) diff --git a/cmake/modules/DuneMacros.cmake b/cmake/modules/DuneMacros.cmake new file mode 100644 index 0000000..dffaee2 --- /dev/null +++ b/cmake/modules/DuneMacros.cmake @@ -0,0 +1,1195 @@ +# Core DUNE module for CMake. +# +# Documentation of the public API defined in this module: +# +# .. cmake_function:: dune_project +# +# Initialize a Dune module. This function needs to be run from every +# top-level CMakeLists.txt file. It sets up the module, defines basic +# variables and manages depedencies. Don't forget to call +# :ref:`finalize_dune_project` afterwards. +# +# .. cmake_function:: finalize_dune_project +# +# Finalize a Dune module. This function needs to be run at the end of +# every top-level CMakeLists.txt file. Among other things it creates +# the cmake package configuration files. Modules can add additional +# entries to these files by setting the variable @${ProjectName}_INIT. +# +# .. cmake_function:: target_link_libraries +# +# .. cmake_brief:: +# +# Overwrite of CMake's :code:`target_link_libraries`. If no interface key +# word (like PUBLIC, INTERFACE, PRIVATE etc.) is given, PUBLIC is added. +# This is to fix problems with CMP0023. +# +# .. cmake_param:: basename +# +# .. cmake_function:: dune_add_library +# +# .. cmake_brief:: +# +# Add a library to a Dune module! +# +# .. cmake_param:: basename +# :single: +# :required: +# :positional: +# +# The basename for the library. On Unix this created :code:`lib.so` +# and :code:`lib.a` +# +# .. cmake_param:: NO_EXPORT +# :option: +# +# If omitted the library is exported for usage in other modules. +# +# .. cmake_param:: ADD_LIBS +# :multi: +# +# A list of libraries that should be incorporated into this library. +# +# .. cmake_param:: APPEND +# :option: +# +# Whether the library should be appended to the +# exported libraries. If there a DUNE module must +# make several libraries available, then first one +# must not use this option but the others have to +# use it. Otherwise only the last library will be +# exported as the others will be overwritten. +# +# .. cmake_param:: OBJECT +# :option: +# +# .. note:: +# This feature will very likely vanish in Dune 3.0 +# +# .. cmake_param:: SOURCES +# :multi: +# :required: +# +# The source files from which to build the library. +# +# .. cmake_param:: COMPILE_FLAGS +# :single: +# +# Any additional compile flags for building the library. +# +# .. cmake_function:: dune_target_link_libraries +# +# .. cmake_param:: BASENAME +# +# .. cmake_param:: LIBRARIES +# +# Link libraries to the static and shared version of +# library BASENAME +# +# +# .. cmake_function:: add_dune_all_flags +# +# .. cmake_param:: targets +# :single: +# :required: +# :positional: +# +# The targets to add the flags of all external libraries to. +# +# This function is superseded by :ref:`dune_target_enable_all_packages`. +# +# Documentation of internal macros in this module: +# +# dune_module_to_uppercase(upper_name module_name) +# +# Converts a module name given by module_name into an uppercase string +# upper_name where all dashes (-) are replaced by underscores (_) +# Example: dune-common -> DUNE_COMMON +# +# dune_module_information(MODULE_DIR [QUIET]) +# +# Parse ${MODULE_DIR}/dune.module and provide that information. +# If the second argument is QUIET no status message is printed. +# +# dune_create_dependency_tree() +# +# Creates the dependency tree of the module. +# +# dune_module_to_macro(_macro_name, _dune_module) +# +# Converts a module name given by _dune_module into a string _macro_name +# where all dashes (-) are removed and letters after a dash are capitalized +# Example: dune-grid-howto -> DuneGridHowto +# +# _macro_name: variable where the name will be stored. +# _dune_module: the name of the dune module. +# +# dune_regenerate_config_cmake() +# +# Creates a new config_collected.h.cmake file in ${CMAKE_CURRENT_BINARY_DIR} that +# consists of entries from ${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake +# and includes non-private entries from the config.h.cmake files +# of all dependent modules. +# Finally config.h is created from config_collected.h.cmake. +# +include_guard(GLOBAL) + +enable_language(C) # Enable C to skip CXX bindings for some tests. + +# By default use -pthread flag. This option is set at the beginning to enforce it for +# find_package(Threads) everywhere +set(THREADS_PREFER_PTHREAD_FLAG TRUE CACHE BOOL "Prefer -pthread compiler and linker flag") + +include(FeatureSummary) +include(DuneEnableAllPackages) +include(DuneTestMacros) +include(OverloadCompilerFlags) +include(DuneSymlinkOrCopy) +include(DunePathHelper) +include(DuneExecuteProcess) + +macro(target_link_libraries) + # do nothing if not at least the two arguments target and scope are passed + if(${ARGC} GREATER_EQUAL 2) + target_link_libraries_helper(${ARGN}) + endif() +endmacro(target_link_libraries) + +# helper for overwritten target_link_libraries to handle arguments more easily +macro(target_link_libraries_helper TARGET SCOPE) + if(${SCOPE} MATCHES "^(PRIVATE|INTERFACE|PUBLIC|LINK_PRIVATE|LINK_PUBLIC|LINK_INTERFACE_LIBRARIES)$") + _target_link_libraries(${TARGET} ${SCOPE} ${ARGN}) + else() + message(DEPRECATION "Calling target_link_libraries without the argument is deprecated.") + _target_link_libraries(${TARGET} PUBLIC ${SCOPE} ${ARGN}) + endif() +endmacro(target_link_libraries_helper) + +# Converts a module name given by _module into an uppercase string +# _upper where all dashes (-) are replaced by underscores (_) +# Example: dune-common -> DUNE_COMMON +macro(dune_module_to_uppercase _upper _module) + string(TOUPPER "${_module}" ${_upper}) + string(REPLACE "-" "_" ${_upper} "${${_upper}}") +endmacro(dune_module_to_uppercase _upper _module) + +macro(find_dune_package module) + include(CMakeParseArguments) + cmake_parse_arguments(DUNE_FIND "REQUIRED" "VERSION" "" ${ARGN}) + if(DUNE_FIND_REQUIRED) + set(required REQUIRED) + set_package_properties(${module} PROPERTIES TYPE REQUIRED) + set(_warning_level "FATAL_ERROR") + else() + unset(required) + set(_warning_level "WARNING") + set_package_properties(${module} PROPERTIES TYPE OPTIONAL) + endif() + if(DUNE_FIND_VERSION MATCHES "(>=|=|<=).*") + string(REGEX REPLACE "(>=|=|<=)(.*)" "\\1" DUNE_FIND_VERSION_OP ${DUNE_FIND_VERSION}) + string(REGEX REPLACE "(>=|=|<=)(.*)" "\\2" DUNE_FIND_VERSION_NUMBER ${DUNE_FIND_VERSION}) + string(STRIP ${DUNE_FIND_VERSION_NUMBER} DUNE_FIND_VERSION_NUMBER) + extract_major_minor_version("${DUNE_FIND_VERSION_NUMBER}" DUNE_FIND_VERSION) + set(DUNE_FIND_VERSION_STRING "${DUNE_FIND_VERSION_MAJOR}.${DUNE_FIND_VERSION_MINOR}.${DUNE_FIND_VERSION_REVISION}") + else() + set(DUNE_FIND_VERSION_STRING "0.0.0") + endif() + if(NOT ${module}_FOUND) + if(NOT (${module}_DIR OR ${module}_ROOT OR + "${CMAKE_PREFIX_PATH}" MATCHES ".*${module}.*")) + string(REPLACE ${ProjectName} ${module} ${module}_DIR + ${PROJECT_BINARY_DIR}) + endif() + find_package(${module} NO_CMAKE_PACKAGE_REGISTRY) + endif() + if(NOT ${module}_FOUND AND NOT CMAKE_DISABLE_FIND_PACKAGE_${module}) + message(STATUS "No full CMake package configuration support available." + " Falling back to pkg-config.") + # use pkg-config + find_package(PkgConfig) + if(NOT PKG_CONFIG_FOUND AND required) + message(FATAL_ERROR "Could not find module ${module}. We tried to use" + "pkg-config but could not find it. ") + endif() + pkg_check_modules (${module} ${required} ${module}${DUNE_FIND_VERSION}) + set(${module}_FAKE_CMAKE_PKGCONFIG TRUE) + endif() + if(${module}_FAKE_CMAKE_PKGCONFIG) + # compute the path to the libraries + if(${module}_LIBRARIES) + unset(_module_lib) + foreach(lib ${${module}_LIBRARIES}) + foreach(libdir ${${module}_LIBRARY_DIRS}) + if(EXISTS ${libdir}/lib${lib}.a) + set(_module_lib ${libdir}/lib${lib}.a) + set(_module_lib_static "STATIC") + endif() + if(EXISTS ${libdir}/lib${lib}.so) + set(_module_lib ${libdir}/lib${lib}.so) + set(_module_lib_static "") + endif() + if(_module_lib) + #import library + add_library(${lib} ${_module_lib_static} IMPORTED) + set_property(TARGET ${lib} APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG) + set_target_properties(${lib} PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_NOCONFIG "CXX" + IMPORTED_LOCATION_NOCONFIG "${_module_lib}") + break() + endif() + endforeach() + endforeach() + endif() + if(NOT ${module}_MODULE_PATH) + if(${module}_INCLUDE_DIRS) + list(GET ${module}_INCLUDE_DIRS 0 _dir) + if(EXISTS ${_dir}/../share/dune/cmake/modules) + set(${module}_MODULE_PATH ${_dir}/../share/dune/cmake/modules) + endif() + endif() + endif() + unset(${module}_FAKE_CMAKE_PKGCONFIG) + endif() + if(${module}_FOUND) + # parse other module's dune.module file to generate variables for config.h + unset(${module}_dune_module) + foreach(_dune_module_file + ${${module}_PREFIX}/dune.module + ${${module}_PREFIX}/lib/dunecontrol/${module}/dune.module + ${${module}_PREFIX}/lib64/dunecontrol/${module}/dune.module) + if(EXISTS ${_dune_module_file}) + get_filename_component(_dune_module_file_path ${_dune_module_file} PATH) + dune_module_information(${_dune_module_file_path})# QUIET) + set(${module}_dune_module 1) + set(DUNE_FIND_MOD_VERSION_STRING "${DUNE_VERSION_MAJOR}.${DUNE_VERSION_MINOR}.${DUNE_VERSION_REVISION}") + # check whether dependency mathes version requirement + unset(module_version_wrong) + if(DUNE_FIND_VERSION_OP MATCHES ">=") + if(NOT (DUNE_FIND_MOD_VERSION_STRING VERSION_EQUAL DUNE_FIND_VERSION_STRING OR + DUNE_FIND_MOD_VERSION_STRING VERSION_GREATER DUNE_FIND_VERSION_STRING)) + set(module_version_wrong 1) + endif() + elseif(DUNE_FIND_VERSION_OP MATCHES "<=") + if(NOT (DUNE_FIND_MOD_VERSION_STRING VERSION_EQUAL DUNE_FIND_VERSION_STRING OR + DUNE_FIND_MOD_VERSION_STRING VERSION_LESS DUNE_FIND_VERSION_STRING)) + set(module_version_wrong 1) + endif() + elseif(DUNE_FIND_VERSION_OP MATCHES "=" AND + NOT (DUNE_FIND_MOD_VERSION_STRING VERSION_EQUAL DUNE_FIND_VERSION_STRING)) + set(module_version_wrong 1) + endif() + endif() + endforeach() + if(NOT ${module}_dune_module) + message(${_warning_level} "Could not find dune.module file for module ${module} " + "in ${${module}_PREFIX}, ${${module}_PREFIX}/lib/dunecontrol/${module}/, " + "${${module}_PREFIX}/lib64/dunecontrol/${module}/dune.module") + set(${module}_FOUND OFF) + endif() + if(module_version_wrong) + message(${_warning_level} "Could not find requested version of module ${module}. " + "Requested version was ${DUNE_FIND_VERSION}, found version is ${DUNE_FIND_MOD_VERSION_STRING}") + set(${module}_FOUND OFF) + endif() + else(${module}_FOUND) + if(required) + message(FATAL_ERROR "Could not find required module ${module}.") + endif() + endif() + set(DUNE_${module}_FOUND ${${module}_FOUND}) +endmacro(find_dune_package module) + +macro(extract_line HEADER OUTPUT FILE_NAME) + set(REGEX "^${HEADER}[ ]*[^\\n]+") + file(STRINGS "${FILE_NAME}" OUTPUT1 REGEX "${REGEX}") + if(OUTPUT1) + set(REGEX "^[ ]*${HEADER}[ ]*(.+)[ ]*$") + string(REGEX REPLACE ${REGEX} "\\1" ${OUTPUT} "${OUTPUT1}") + else(OUTPUT1) + set(OUTPUT OUTPUT-NOTFOUND) + endif() +endmacro(extract_line) + +# +# split list of modules, potentially with version information +# into list of modules and list of versions +# +macro(split_module_version STRING MODULES VERSIONS) + set(REGEX "[a-zA-Z0-9-]+[ ]*(\\([ ]*([^ ]+)?[ ]*[^ ]+[ ]*\\))?") + string(REGEX MATCHALL "${REGEX}" matches "${STRING}") + set(${MODULES} "") + set(${VERSIONS} "") + foreach(i ${matches}) + string(REGEX REPLACE "^([a-zA-Z0-9-]+).*$" "\\1" mod ${i}) + string(REGEX MATCH "\\([ ]*(([^ ]+)?[ ]*[^ ]+)[ ]*\\)" have_version + ${i}) + if(have_version) + string(REGEX REPLACE "^\\([ ]*([^ ]*[ ]*[^ ]+)[ ]*\\)$" "\\1" + version ${have_version}) + else() + set(version " ") # Mark as no version requested. + # Having a space is mandatory as we will append it to a list + # and an empty string will not be treated as entry we append to it. + endif() + list(APPEND ${MODULES} ${mod}) + list(APPEND ${VERSIONS} ${version}) + endforeach() +endmacro(split_module_version) + +# +# Convert a string with spaces in a list which is a string with semicolon +# +function(convert_deps_to_list var) + string(REGEX REPLACE "([a-zA-Z0-9\\)]) ([a-zA-Z0-9])" "\\1;\\2" ${var} ${${var}}) + set(${var} ${${var}} PARENT_SCOPE) +endfunction(convert_deps_to_list var) + +# +# extracts major, minor, and revision from version string +# +function(extract_major_minor_version version_string varname) + string(REGEX REPLACE "([0-9]+).*" "\\1" ${varname}_MAJOR "${version_string}") + string(REGEX REPLACE "[0-9]+\\.([0-9]+).*" "\\1" ${varname}_MINOR "${version_string}") + string(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" ${varname}_REVISION "${version_string}") + set(${varname}_MAJOR "${${varname}_MAJOR}" PARENT_SCOPE) # make variable accessible in parent scope + + # remove false matches in version string and export to parent scop + string(REGEX MATCH "[^0-9]" NON_NUMBER_CHARACTER "${${varname}_MINOR}") + if(NON_NUMBER_CHARACTER) + set(${varname}_MINOR "0" PARENT_SCOPE) + else() + set(${varname}_MINOR ${${varname}_MINOR} PARENT_SCOPE) + endif() + string(REGEX MATCH "[^0-9]" NON_NUMBER_CHARACTER "${${varname}_REVISION}") + if(NON_NUMBER_CHARACTER) + set(${varname}_REVISION "0" PARENT_SCOPE) + else() + set(${varname}_REVISION ${${varname}_REVISION} PARENT_SCOPE) + endif() +endfunction(extract_major_minor_version version_string varname) + +# add dune-common version from dune.module to config.h +# optional second argument is verbosity +macro(dune_module_information MODULE_DIR) + # find version strings + extract_line("Version:" MODULE_LINE "${MODULE_DIR}/dune.module") + if(NOT MODULE_LINE MATCHES ".+") + message(FATAL_ERROR "${MODULE_DIR}/dune.module is missing a version.") + endif() + + string(REGEX REPLACE ".*Version:[ ]*([^ \n]+).*" "\\1" DUNE_MOD_VERSION "${MODULE_LINE}") + extract_major_minor_version("${DUNE_MOD_VERSION}" DUNE_VERSION) + + # find strings for module name, maintainer + # 1. Check for line starting with Module + extract_line("Module:" DUNE_MOD_NAME "${MODULE_DIR}/dune.module") + if(NOT DUNE_MOD_NAME) + message(FATAL_ERROR "${MODULE_DIR}/dune.module is missing a module name.") + endif() + + # 2. Check for line starting with Maintainer + extract_line("Maintainer:" DUNE_MAINTAINER "${MODULE_DIR}/dune.module") + if(NOT DUNE_MAINTAINER) + message(FATAL_ERROR "${MODULE_DIR}/dune.module is missing a maintainer.") + endif() + + # 3. Check for line starting with Depends + extract_line("Depends:" ${DUNE_MOD_NAME}_DEPENDS "${MODULE_DIR}/dune.module") + if(${DUNE_MOD_NAME}_DEPENDS) + split_module_version(${${DUNE_MOD_NAME}_DEPENDS} ${DUNE_MOD_NAME}_DEPENDS_MODULE ${DUNE_MOD_NAME}_DEPENDS_VERSION) + foreach(_mod ${${DUNE_MOD_NAME}_DEPENDS}) + set(${_mod}_REQUIRED REQUIRED) + endforeach() + convert_deps_to_list(${DUNE_MOD_NAME}_DEPENDS) + if(NOT ("${ARGV1}" STREQUAL QUIET)) + message(STATUS "Dependencies for ${DUNE_MOD_NAME}: ${${DUNE_MOD_NAME}_DEPENDS}") + endif() + endif() + + # 4. Check for line starting with Suggests + extract_line("Suggests:" ${DUNE_MOD_NAME}_SUGGESTS "${MODULE_DIR}/dune.module") + if(${DUNE_MOD_NAME}_SUGGESTS) + split_module_version(${${DUNE_MOD_NAME}_SUGGESTS} ${DUNE_MOD_NAME}_SUGGESTS_MODULE ${DUNE_MOD_NAME}_SUGGESTS_VERSION) + convert_deps_to_list(${DUNE_MOD_NAME}_SUGGESTS) + if(NOT ("${ARGV1}" STREQUAL QUIET)) + message(STATUS "Suggestions for ${DUNE_MOD_NAME}: ${${DUNE_MOD_NAME}_SUGGESTS}") + endif() + endif() + + dune_module_to_uppercase(DUNE_MOD_NAME_UPPERCASE ${DUNE_MOD_NAME}) + + # 5. Check for optional meta data + extract_line("Author:" ${DUNE_MOD_NAME_UPPERCASE}_AUTHOR "${MODULE_DIR}/dune.module") + extract_line("Description:" ${DUNE_MOD_NAME_UPPERCASE}_DESCRIPTION "${MODULE_DIR}/dune.module") + extract_line("URL:" ${DUNE_MOD_NAME_UPPERCASE}_URL "${MODULE_DIR}/dune.module") + extract_line("Python-Requires:" ${DUNE_MOD_NAME_UPPERCASE}_PYTHON_REQUIRES "${MODULE_DIR}/dune.module") + + # set module version + set(${DUNE_MOD_NAME_UPPERCASE}_VERSION "${DUNE_MOD_VERSION}") + set(${DUNE_MOD_NAME_UPPERCASE}_VERSION_MAJOR "${DUNE_VERSION_MAJOR}") + set(${DUNE_MOD_NAME_UPPERCASE}_VERSION_MINOR "${DUNE_VERSION_MINOR}") + set(${DUNE_MOD_NAME_UPPERCASE}_VERSION_REVISION "${DUNE_VERSION_REVISION}") +endmacro(dune_module_information) + +macro(dune_process_dependency_leafs modules versions is_required next_level_deps + next_level_sugs) + # modules, and versions are not real variables, make them one + set(mmodules ${modules}) + set(mversions ${versions}) + list(LENGTH mmodules mlength) + list(LENGTH mversions vlength) + if(NOT mlength EQUAL vlength) + message(STATUS "mmodules=${mmodules} modules=${modules}") + message(STATUS "mversions=${mversions} versions=${mversions}") + message(FATAL_ERROR "List of modules and versions do not have the same length!") + endif() + if(mlength GREATER 0) + math(EXPR length "${mlength}-1") + foreach(i RANGE 0 ${length}) + list(GET mmodules ${i} _mod) + list(GET mversions ${i} _ver) + find_dune_package(${_mod} ${is_required} VERSION "${_ver}") + set(${_mod}_SEARCHED ON) + if(NOT "${is_required}" STREQUAL "") + set(${_mod}_REQUIRED ON) + set(${next_level_deps} ${${_mod}_DEPENDS} ${${next_level_deps}}) + else(NOT "${is_required}" STREQUAL "") + set(${next_level_sugs} ${${_mod}_DEPENDS} ${${next_level_sugs}}) + endif() + set(${next_level_sugs} ${${_mod}_SUGGESTS} ${${next_level_sugs}}) + endforeach() + endif() + if(${next_level_sugs}) + list(REMOVE_DUPLICATES ${next_level_sugs}) + endif() + if(${next_level_deps}) + list(REMOVE_DUPLICATES ${next_level_deps}) + endif() +endmacro(dune_process_dependency_leafs) + +function(remove_processed_modules modules versions is_required) + list(LENGTH ${modules} mlength) + if(mlength GREATER 0) + math(EXPR length "${mlength}-1") + foreach(i RANGE ${length} 0 -1) + list(GET ${modules} ${i} _mod) + if(${_mod}_SEARCHED) + list(REMOVE_AT ${modules} ${i}) + list(REMOVE_AT ${versions} ${i}) + if(is_required AND NOT ${_mod}_REQUIRED AND NOT ${_mod}_FOUND) + message(FATAL_ERROR "Required module ${_mod} not found!") + endif() + endif() + endforeach() + endif() + set(${modules} ${${modules}} PARENT_SCOPE) + set(${versions} ${${versions}} PARENT_SCOPE) +endfunction(remove_processed_modules modules versions is_required) + +macro(dune_create_dependency_leafs depends depends_versions suggests suggests_versions) + set(deps "") + set(sugs "") + #Process dependencies + if(NOT "${depends}" STREQUAL "") + dune_process_dependency_leafs("${depends}" "${depends_versions}" REQUIRED deps sugs) + endif() + # Process suggestions + if(NOT "${suggests}" STREQUAL "") + dune_process_dependency_leafs("${suggests}" "${suggests_versions}" "" deps sugs) + endif() + split_module_version("${deps}" next_mod_depends next_depends_versions) + split_module_version("${sugs}" next_mod_suggests next_suggests_versions) + set(ALL_DEPENDENCIES ${ALL_DEPENDENCIES} ${next_mod_depends} ${next_mod_suggests}) + # Move to next level + if(next_mod_suggests OR next_mod_depends) + dune_create_dependency_leafs("${next_mod_depends}" "${next_depends_versions}" + "${next_mod_suggests}" "${next_suggests_versions}") + endif() +endmacro(dune_create_dependency_leafs) + +macro(dune_create_dependency_tree) + if(dune-common_MODULE_PATH) + list(REMOVE_ITEM CMAKE_MODULE_PATH "${dune-common_MODULE_PATH}") + endif() + list(FIND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules start) + set(ALL_DEPENDENCIES "") + if(${ProjectName}_DEPENDS_MODULE OR ${ProjectName}_SUGGESTS_MODULE) + set(ALL_DEPENDENCIES ${${ProjectName}_DEPENDS_MODULE} ${${ProjectName}_SUGGESTS_MODULE}) + dune_create_dependency_leafs("${${ProjectName}_DEPENDS_MODULE}" "${${ProjectName}_DEPENDS_VERSION}" + "${${ProjectName}_SUGGESTS_MODULE}" "${${ProjectName}_SUGGESTS_VERSION}") + endif() + set(_my_path "") + if(ALL_DEPENDENCIES) + # Reverse the order of the modules and remove duplicates + # At end of this clause we have have a list modules + # where for each entry all dependencies are before the + # module in the list. + set(NEW_ALL_DEPS "") + list(LENGTH ALL_DEPENDENCIES length) + if(length GREATER 0) + math(EXPR length "${length}-1") + list(GET ALL_DEPENDENCIES ${length} _mod) + set(${_mod}_cmake_path_processed 1) + set(_my_path ${${_mod}_MODULE_PATH}) + list(APPEND NEW_ALL_DEPS ${_mod}) + if(length GREATER 0) + math(EXPR length "${length}-1") + foreach(i RANGE ${length} 0 -1) + list(GET ALL_DEPENDENCIES ${i} _mod) + if(NOT ${_mod}_cmake_path_processed) + set(${_mod}_cmake_path_processed 1) + if(${_mod}_MODULE_PATH) + list(INSERT _my_path 0 ${${_mod}_MODULE_PATH}) + endif() + list(APPEND NEW_ALL_DEPS ${_mod}) + endif() + endforeach() + endif() + list(LENGTH CMAKE_MODULE_PATH length) + math(EXPR length "${length}-1") + if(start EQUAL -1) + list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules ${_my_path}) + else() + if(start EQUAL ${length}) + list(APPEND CMAKE_MODULE_PATH ${_my_path}) + else() + if(_my_path) + list(INSERT CMAKE_MODULE_PATH ${start} ${_my_path}) + endif() + endif() + endif() + endif() + set(ALL_DEPENDENCIES ${NEW_ALL_DEPS}) + endif() +endmacro(dune_create_dependency_tree) + +# Converts a module name given by _dune_module into a string _macro_name +# where all dashes (-) are removed and letters after a dash are capitalized +# Example: dune-grid-howto -> DuneGridHowto +macro(dune_module_to_macro _macro_name _dune_module) + set(${_macro_name} "") + set(_rest "${_dune_module}") + string(FIND "${_rest}" "-" _found) + while(_found GREATER -1) + string(REGEX REPLACE "([^-]*)-.*" "\\1" _first_part + "${_rest}") + string(REGEX REPLACE "[^-]*-(.*)" "\\1" _rest + "${_rest}") + string(SUBSTRING "${_first_part}" 0 1 _first_letter) + string(SUBSTRING "${_first_part}" 1 -1 _rest_first_part) + string(TOUPPER "${_first_letter}" _first_letter) + set(${_macro_name} "${${_macro_name}}${_first_letter}${_rest_first_part}") + string(FIND "${_rest}" "-" _found) + endwhile() + string(LENGTH "${_rest}" _length) + string(SUBSTRING "${_rest}" 0 1 _first_letter) + string(SUBSTRING "${_rest}" 1 -1 _rest) + string(TOUPPER "${_first_letter}" _first_letter) + set(${_macro_name} "${${_macro_name}}${_first_letter}${_rest}") +endmacro(dune_module_to_macro _macro_name _dune_module) + +macro(dune_process_dependency_macros) + foreach(_mod ${ALL_DEPENDENCIES} ${ProjectName}) + if(NOT ${_mod}_PROCESSED) + # module not processed yet + set(${_mod}_PROCESSED ${_mod}) + # Search for a cmake files containing tests and directives + # specific to this module + dune_module_to_macro(_cmake_mod_name "${_mod}") + set(_macro "${_cmake_mod_name}Macros") + set(_mod_cmake _mod_cmake-NOTFOUND) # Prevent false positives due to caching + message(STATUS "Searching for macro file '${_macro}' for module '${_mod}'.") + find_file(_mod_cmake + NAMES "${_macro}.cmake" + PATHS ${CMAKE_MODULE_PATH} + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH) + if(_mod_cmake) + message(STATUS "Performing tests specific to ${_mod} from file ${_mod_cmake}.") + include(${_mod_cmake}) + else() + message(STATUS "No module specific tests performed for module '${_mod}' because macro file '${_macro}.cmake' not found in ${CMAKE_MODULE_PATH}.") + endif() + dune_module_to_uppercase(_upper_case "${_mod}") + if(${_mod}_INCLUDE_DIRS) + message(STATUS "Setting ${_mod}_INCLUDE_DIRS=${${_mod}_INCLUDE_DIRS}") + include_directories("${${_mod}_INCLUDE_DIRS}") + endif() + if(${_mod}_LIBRARIES) + message(STATUS "Setting ${_mod}_LIBRARIES=${${_mod}_LIBRARIES}") + foreach(_lib ${${_mod}_LIBRARIES}) + list(INSERT DUNE_DEFAULT_LIBS 0 "${_lib}") + list(INSERT DUNE_LIBS 0 "${_lib}") + endforeach() + endif() + + # register dune module + dune_register_package_flags(INCLUDE_DIRS "${${_mod}_INCLUDE_DIRS}") + endif() + endforeach() +endmacro(dune_process_dependency_macros) + +# macro that should be called near the begin of the top level CMakeLists.txt. +# Namely it sets up the module, defines basic variables and manages +# depedencies. +# Don't forget to call finalize_dune_project afterwards. +macro(dune_project) + + # check if CXX flag overloading has been enabled (see OverloadCompilerFlags.cmake) + initialize_compiler_script() + + # extract information from dune.module + dune_module_information(${PROJECT_SOURCE_DIR}) + set(ProjectName "${DUNE_MOD_NAME}") + set(ProjectVersion "${DUNE_MOD_VERSION}") + set(ProjectVersionString "${DUNE_VERSION_MAJOR}.${DUNE_VERSION_MINOR}.${DUNE_VERSION_REVISION}") + set(ProjectVersionMajor "${DUNE_VERSION_MAJOR}") + set(ProjectVersionMinor "${DUNE_VERSION_MINOR}") + set(ProjectVersionRevision "${DUNE_VERSION_REVISION}") + set(ProjectMaintainerEmail "${DUNE_MAINTAINER}") + set(ProjectDescription "${${DUNE_MOD_NAME_UPPERCASE}_DESCRIPTION}") + set(ProjectAuthor "${${DUNE_MOD_NAME_UPPERCASE}_AUTHOR}") + set(ProjectUrl "${${DUNE_MOD_NAME_UPPERCASE}_URL}") + set(ProjectPythonRequires "${${DUNE_MOD_NAME_UPPERCASE}_PYTHON_REQUIRES}") + + # check whether this module has been explicitly disabled through the cmake flags. + # If so, stop the build. This is necessary because dunecontrol does not parse + # the given CMake flags for a disabled Dune modules. + if(CMAKE_DISABLE_FIND_PACKAGE_${ProjectName}) + message("Module ${ProjectName} has been explicitly disabled through the cmake flags. Skipping build.") + return() + endif() + + define_property(GLOBAL PROPERTY DUNE_MODULE_LIBRARIES + BRIEF_DOCS "List of libraries of the module. DO NOT EDIT!" + FULL_DOCS "List of libraries of the module. Used to generate CMake's package configuration files. DO NOT EDIT!") + dune_create_dependency_tree() + + # assert the project names matches + if(NOT (ProjectName STREQUAL PROJECT_NAME)) + message(FATAL_ERROR "Module name from dune.module does not match the name given in CMakeLists.txt.") + endif() + + # As default request position independent code if shared libraries are built + # This should allow DUNE modules to use CMake's object libraries. + # This can be overwritten for targets by setting the target property + # POSITION_INDEPENDENT_CODE to false/OFF + include(CMakeDependentOption) + cmake_dependent_option(CMAKE_POSITION_INDEPENDENT_CODE "Build position independent code" ON "NOT BUILD_SHARED_LIBS" ON) + + # check for C++ features, set compiler flags for C++14 or C++11 mode + include(CheckCXXFeatures) + + # set include path and link path for the current project. + include_directories("${PROJECT_BINARY_DIR}") + include_directories("${PROJECT_SOURCE_DIR}") + include_directories("${CMAKE_CURRENT_BINARY_DIR}") + include_directories("${CMAKE_CURRENT_SOURCE_DIR}") + add_definitions(-DHAVE_CONFIG_H) + + # Create custom target for building the documentation + # and provide macros for installing the docs and force + # building them before. + include(DuneDoc) + + # activate pkg-config + include(DunePkgConfig) + + # Process the macros provided by the dependencies and ourself + dune_process_dependency_macros() + + include(GNUInstallDirs) + # Set variable where the cmake modules will be installed. + # Thus the user can override it and for example install + # directly into the CMake installation. We use a cache variable + # that is overridden by a local variable of the same name if + # the user does not explicitely set a value for it. Thus the value + # will automatically change if the user changes CMAKE_INSTALL_DATAROOTDIR + # or CMAKE_INSTALL_PREFIX + if(NOT DUNE_INSTALL_MODULEDIR) + set(DUNE_INSTALL_MODULEDIR "" + CACHE PATH + "Installation directory for CMake modules. Default is \${CMAKE_INSTALL_DATAROOTDIR}/dune/cmake/modules when not set explicitely") + set(DUNE_INSTALL_MODULEDIR ${CMAKE_INSTALL_DATAROOTDIR}/dune/cmake/modules) + endif() + if(NOT DUNE_INSTALL_NONOBJECTLIBDIR) + set(DUNE_INSTALL_NONOBJECTLIBDIR "" + CACHE PATH + "Installation directory for libraries that are not architecture dependent. Default is lib when not set explicitely") + set(DUNE_INSTALL_NONOBJECTLIBDIR lib) + endif() + # set up make headercheck + include(Headercheck) + setup_headercheck() + +endmacro(dune_project) + +# create a new config.h file and overwrite the existing one +macro(dune_regenerate_config_cmake) + set(CONFIG_H_CMAKE_FILE "${PROJECT_BINARY_DIR}/config_collected.h.cmake") + if(EXISTS ${PROJECT_SOURCE_DIR}/config.h.cmake) + file(READ ${PROJECT_SOURCE_DIR}/config.h.cmake _file) + string(REGEX MATCH + "/[\\*/][ ]*begin[ ]+${ProjectName}.*\\/[/\\*][ ]*end[ ]*${ProjectName}[^\\*]*\\*/" + _myfile "${_file}") + endif() + # overwrite file with new content + file(WRITE ${CONFIG_H_CMAKE_FILE} "/* config.h. Generated from config_collected.h.cmake by CMake. + It was generated from config_collected.h.cmake which in turn is generated automatically + from the config.h.cmake files of modules this module depends on. */" + ) + + # define that we found this module + set(${ProjectName}_FOUND 1) + foreach(_dep ${ProjectName} ${ALL_DEPENDENCIES}) + dune_module_to_uppercase(upper ${_dep}) + set(HAVE_${upper} ${${_dep}_FOUND}) + file(APPEND ${CONFIG_H_CMAKE_FILE} + "\n\n/* Define to 1 if you have module ${_dep} available */ +#cmakedefine01 HAVE_${upper}\n") + endforeach() + + # add previous module specific section + foreach(_dep ${ALL_DEPENDENCIES}) + foreach(_mod_conf_file ${${_dep}_PREFIX}/config.h.cmake + ${${_dep}_PREFIX}/share/${_dep}/config.h.cmake) + if(EXISTS ${_mod_conf_file}) + file(READ "${_mod_conf_file}" _file) + string(REGEX REPLACE + ".*/\\*[ ]*begin[ ]+${_dep}[^\\*]*\\*/(.*)/[/\\*][ ]*end[ ]*${_dep}[^\\*]*\\*/" "\\1" + _tfile "${_file}") + # strip the private section + string(REGEX REPLACE "(.*)/[\\*][ ]*begin private.*/[\\*][ ]*end[ ]*private[^\\*]\\*/(.*)" "\\1\\2" _ttfile "${_tfile}") + + # extract the bottom section + string(REGEX MATCH "/[\\*][ ]*begin bottom.*/[\\*][ ]*end[ ]*bottom[^\\*]\\*/" _tbottom "${_ttfile}") + string(REGEX REPLACE ".*/\\*[ ]*begin[ ]+bottom[^\\*]*\\*/(.*)/[/\\*][ ]*end[ ]*bottom[^\\*]*\\*/" "\\1" ${_dep}_CONFIG_H_BOTTOM "${_tbottom}" ) + string(REGEX REPLACE "(.*)/[\\*][ ]*begin bottom.*/[\\*][ ]*end[ ]*bottom[^\\*]\\*/(.*)" "\\1\\2" _file "${_ttfile}") + + # append bottom section + if(${_dep}_CONFIG_H_BOTTOM) + set(CONFIG_H_BOTTOM "${CONFIG_H_BOTTOM} ${${_dep}_CONFIG_H_BOTTOM}") + endif() + + file(APPEND ${CONFIG_H_CMAKE_FILE} "${_file}") + endif() + endforeach() + endforeach() + # parse again dune.module file of current module to set PACKAGE_* variables + dune_module_information(${PROJECT_SOURCE_DIR} QUIET) + file(APPEND ${CONFIG_H_CMAKE_FILE} "\n${_myfile}") + # append CONFIG_H_BOTTOM section at the end if found + if(CONFIG_H_BOTTOM) + file(APPEND ${CONFIG_H_CMAKE_FILE} "${CONFIG_H_BOTTOM}") + endif() +endmacro(dune_regenerate_config_cmake) + +# macro that should be called at the end of the top level CMakeLists.txt. +# Namely it creates config.h and the cmake-config files, +# some install directives and exports the module. +macro(finalize_dune_project) + if(DUNE_SYMLINK_TO_SOURCE_TREE) + dune_symlink_to_source_tree() + endif() + + #configure all headerchecks + finalize_headercheck() + + #create cmake-config files for installation tree + include(CMakePackageConfigHelpers) + include(GNUInstallDirs) + set(DOXYSTYLE_DIR ${CMAKE_INSTALL_DATAROOTDIR}/dune-common/doc/doxygen/) + set(SCRIPT_DIR ${CMAKE_INSTALL_DATAROOTDIR}/dune/cmake/scripts) + # Set the location where the doc sources are installed. + # Needed by custom package configuration + # file section of dune-grid. + set(DUNE_MODULE_SRC_DOCDIR "\${${ProjectName}_PREFIX}/${CMAKE_INSTALL_DOCDIR}") + + if(NOT EXISTS ${PROJECT_SOURCE_DIR}/cmake/pkg/${ProjectName}-config.cmake.in) + # Generate a standard cmake package configuration file + file(WRITE ${PROJECT_BINARY_DIR}/CMakeFiles/${ProjectName}-config.cmake.in +"if(NOT ${ProjectName}_FOUND) +# Whether this module is installed or not +set(${ProjectName}_INSTALLED @MODULE_INSTALLED@) + +# Settings specific to the module +@${ProjectName}_INIT@ +# Package initialization +@PACKAGE_INIT@ + +#report other information +set_and_check(${ProjectName}_PREFIX \"\${PACKAGE_PREFIX_DIR}\") +set_and_check(${ProjectName}_INCLUDE_DIRS \"@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@\") +set(${ProjectName}_CXX_FLAGS \"${CMAKE_CXX_FLAGS}\") +set(${ProjectName}_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG}\") +set(${ProjectName}_CXX_FLAGS_MINSIZEREL \"${CMAKE_CXX_FLAGS_MINSIZEREL}\") +set(${ProjectName}_CXX_FLAGS_RELEASE \"${CMAKE_CXX_FLAGS_RELEASE}\") +set(${ProjectName}_CXX_FLAGS_RELWITHDEBINFO \"${CMAKE_CXX_FLAGS_RELWITHDEBINFO}\") +set(${ProjectName}_DEPENDS \"@${ProjectName}_DEPENDS@\") +set(${ProjectName}_SUGGESTS \"@${ProjectName}_SUGGESTS@\") +set(${ProjectName}_MODULE_PATH \"@PACKAGE_DUNE_INSTALL_MODULEDIR@\") +set(${ProjectName}_LIBRARIES \"@DUNE_MODULE_LIBRARIES@\") + +# Lines that are set by the CMake build system via the variable DUNE_CUSTOM_PKG_CONFIG_SECTION +${DUNE_CUSTOM_PKG_CONFIG_SECTION} + +#import the target +if(${ProjectName}_LIBRARIES) + get_filename_component(_dir \"\${CMAKE_CURRENT_LIST_FILE}\" PATH) + include(\"\${_dir}/${ProjectName}-targets.cmake\") +endif() +endif()") + set(CONFIG_SOURCE_FILE ${PROJECT_BINARY_DIR}/CMakeFiles/${ProjectName}-config.cmake.in) + else() + set(CONFIG_SOURCE_FILE ${PROJECT_SOURCE_DIR}/cmake/pkg/${ProjectName}-config.cmake.in) + endif() + get_property(DUNE_MODULE_LIBRARIES GLOBAL PROPERTY DUNE_MODULE_LIBRARIES) + + # compute under which libdir the package configuration files are to be installed. + # If the module installs an object library we use CMAKE_INSTALL_LIBDIR + # to capture the multiarch triplet of Debian/Ubuntu. + # Otherwise we fall back to DUNE_INSTALL_NONOBJECTLIB which is lib + # if not set otherwise. + get_property(DUNE_MODULE_LIBRARIES GLOBAL PROPERTY DUNE_MODULE_LIBRARIES) + if(DUNE_MODULE_LIBRARIES) + set(DUNE_INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR}) + else() + set(DUNE_INSTALL_LIBDIR ${DUNE_INSTALL_NONOBJECTLIBDIR}) + endif() + + # Set the location of the doc file source. Needed by custom package configuration + # file section of dune-grid. + set(DUNE_MODULE_SRC_DOCDIR "${PROJECT_SOURCE_DIR}/doc") + set(MODULE_INSTALLED ON) + + configure_package_config_file(${CONFIG_SOURCE_FILE} + ${PROJECT_BINARY_DIR}/cmake/pkg/${ProjectName}-config.cmake + INSTALL_DESTINATION ${DUNE_INSTALL_LIBDIR}/cmake/${ProjectName} + PATH_VARS CMAKE_INSTALL_DATAROOTDIR DUNE_INSTALL_MODULEDIR CMAKE_INSTALL_INCLUDEDIR + DOXYSTYLE_DIR SCRIPT_DIR) + + + #create cmake-config files for build tree + set(PACKAGE_CMAKE_INSTALL_INCLUDEDIR ${PROJECT_SOURCE_DIR}) + set(PACKAGE_CMAKE_INSTALL_DATAROOTDIR ${PROJECT_BINARY_DIR}) + set(PACKAGE_DOXYSTYLE_DIR ${PROJECT_SOURCE_DIR}/doc/doxygen) + set(PACKAGE_SCRIPT_DIR ${PROJECT_SOURCE_DIR}/cmake/scripts) + set(PACKAGE_DUNE_INSTALL_MODULEDIR ${PROJECT_SOURCE_DIR}/cmake/modules) + set(PACKAGE_PREFIX_DIR ${PROJECT_BINARY_DIR}) + set(PACKAGE_INIT "# Set prefix to source dir +set(PACKAGE_PREFIX_DIR ${PROJECT_SOURCE_DIR}) +macro(set_and_check _var _file) + set(\${_var} \"\${_file}\") + if(NOT EXISTS \"\${_file}\") + message(FATAL_ERROR \"File or directory \${_file} referenced by variable \${_var} does not exist !\") + endif() +endmacro()") + set(MODULE_INSTALLED OFF) + configure_file( + ${CONFIG_SOURCE_FILE} + ${PROJECT_BINARY_DIR}/${ProjectName}-config.cmake @ONLY) + + if(NOT EXISTS ${PROJECT_SOURCE_DIR}/${ProjectName}-config-version.cmake.in) + file(WRITE ${PROJECT_BINARY_DIR}/CMakeFiles/${ProjectName}-config-version.cmake.in +"set(PACKAGE_VERSION \"${ProjectVersionString}\") + +if(\"\${PACKAGE_FIND_VERSION_MAJOR}\" EQUAL \"${ProjectVersionMajor}\" AND + \"\${PACKAGE_FIND_VERSION_MINOR}\" EQUAL \"${ProjectVersionMinor}\") + set (PACKAGE_VERSION_COMPATIBLE 1) # compatible with newer + if (\"\${PACKAGE_FIND_VERSION}\" VERSION_EQUAL \"${ProjectVersionString}\") + set(PACKAGE_VERSION_EXACT 1) #exact match for this version + endif() +endif() +") + set(CONFIG_VERSION_FILE ${PROJECT_BINARY_DIR}/CMakeFiles/${ProjectName}-config-version.cmake.in) + else() + set(CONFIG_VERSION_FILE ${PROJECT_SOURCE_DIR}/${ProjectName}-config-version.cmake.in) + endif() + configure_file( + ${CONFIG_VERSION_FILE} + ${PROJECT_BINARY_DIR}/${ProjectName}-config-version.cmake @ONLY) + + # install dune.module file + install(FILES dune.module DESTINATION ${DUNE_INSTALL_NONOBJECTLIBDIR}/dunecontrol/${ProjectName}) + + # install cmake-config files + install(FILES ${PROJECT_BINARY_DIR}/cmake/pkg/${ProjectName}-config.cmake + ${PROJECT_BINARY_DIR}/${ProjectName}-config-version.cmake + DESTINATION ${DUNE_INSTALL_LIBDIR}/cmake/${ProjectName}) + + # install config.h + if(EXISTS ${PROJECT_SOURCE_DIR}/config.h.cmake) + install(FILES config.h.cmake DESTINATION share/${ProjectName}) + endif() + + # install pkg-config files + create_and_install_pkconfig(${DUNE_INSTALL_LIBDIR}) + + if("${ARGC}" EQUAL "1") + message(STATUS "Adding custom target for config.h generation") + dune_regenerate_config_cmake() + # add a target to generate config.h.cmake + if(NOT TARGET OUTPUT) + add_custom_target(OUTPUT config_collected.h.cmake + COMMAND dune_regenerate_config_cmake()) + endif() + # actually write the config.h file to disk + # using generated file + configure_file(${CMAKE_CURRENT_BINARY_DIR}/config_collected.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/config.h) + else() + message(STATUS "Not adding custom target for config.h generation") + # actually write the config.h file to disk + configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) + endif() + + if(PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME) + feature_summary(WHAT ALL) + endif() + + # check if CXX flag overloading has been enabled + # and write compiler script (see OverloadCompilerFlags.cmake) + finalize_compiler_script() +endmacro(finalize_dune_project) + +macro(target_link_dune_default_libraries _target) + foreach(_lib ${DUNE_DEFAULT_LIBS}) + target_link_libraries(${_target} PUBLIC ${_lib}) + endforeach() +endmacro(target_link_dune_default_libraries) + +function(dune_expand_object_libraries _SOURCES_var _ADD_LIBS_var _COMPILE_FLAGS_var) + set(_new_SOURCES "") + set(_new_ADD_LIBS "${${_ADD_LIBS_var}}") + set(_new_COMPILE_FLAGS "${${_COMPILE_FLAGS_var}}") + set(_regex "_DUNE_TARGET_OBJECTS:([a-zA-Z0-9_-]+)_") + foreach(_source ${${_SOURCES_var}}) + string(REGEX MATCH ${_regex} _matched "${_source}") + if(_matched) + string(REGEX REPLACE "${_regex}" "\\1" _basename "${_source}") + foreach(var _SOURCES _ADD_LIBS _COMPILE_FLAGS) + get_property(_prop GLOBAL PROPERTY DUNE_LIB_${_basename}${var}) + list(APPEND _new${var} "${_prop}") + endforeach() + else() + list(APPEND _new_SOURCES "${_source}") + endif() + endforeach() + + foreach(var _SOURCES _ADD_LIBS _COMPILE_FLAGS) + set(${${var}_var} "${_new${var}}" PARENT_SCOPE) + endforeach() +endfunction(dune_expand_object_libraries) + +# Creates shared and static libraries with the same basename. +# More docu can be found at the top of this file. +macro(dune_add_library basename) + include(CMakeParseArguments) + cmake_parse_arguments(DUNE_LIB "APPEND;NO_EXPORT;OBJECT" "COMPILE_FLAGS" + "ADD_LIBS;SOURCES" ${ARGN}) + list(APPEND DUNE_LIB_SOURCES ${DUNE_LIB_UNPARSED_ARGUMENTS}) + if(DUNE_LIB_OBJECT) + if(DUNE_LIB_${basename}_SOURCES) + message(FATAL_ERROR "There is already a library with the name ${basename}, " + "but only one is allowed!") + else() + foreach(source ${DUNE_LIB_SOURCES}) + list(APPEND full_path_sources ${CMAKE_CURRENT_SOURCE_DIR}/${source}) + endforeach() + # register sources, libs and flags for building the library later + define_property(GLOBAL PROPERTY DUNE_LIB_${basename}_SOURCES + BRIEF_DOCS "Convenience property with sources for library ${basename}. DO NOT EDIT!" + FULL_DOCS "Convenience property with sources for library ${basename}. DO NOT EDIT!") + set_property(GLOBAL PROPERTY DUNE_LIB_${basename}_SOURCES + "${full_path_sources}") + define_property(GLOBAL PROPERTY DUNE_LIB_${basename}_ADD_LIBS + BRIEF_DOCS "Convenience property with libraries for library ${basename}. DO NOT EDIT!" + FULL_DOCS "Convenience property with libraries for library ${basename}. DO NOT EDIT!") + set_property(GLOBAL PROPERTY DUNE_LIB_${basename}_ADD_LIBS + "${DUNE_LIB_ADD_LIBS}") + define_property(GLOBAL PROPERTY DUNE_LIB_${basename}_COMPILE_FLAGS + BRIEF_DOCS "Convenience property with compile flags for library ${basename}. DO NOT EDIT!" + FULL_DOCS "Convenience property with compile flags for library ${basename}. DO NOT EDIT!") + set_property(GLOBAL PROPERTY DUNE_LIB_${basename}_COMPILE_FLAGS + "${DUNE_LIB_COMPILE_FLAGS}") + endif() + else(DUNE_LIB_OBJECT) + dune_expand_object_libraries(DUNE_LIB_SOURCES DUNE_LIB_ADD_LIBS DUNE_LIB_COMPILE_FLAGS) + #create lib + add_library(${basename} ${DUNE_LIB_SOURCES}) + get_property(_prop GLOBAL PROPERTY DUNE_MODULE_LIBRARIES) + set_property(GLOBAL PROPERTY DUNE_MODULE_LIBRARIES ${_prop} ${basename}) + # link with specified libraries. + if(DUNE_LIB_ADD_LIBS) + target_link_libraries(${basename} PUBLIC "${DUNE_LIB_ADD_LIBS}") + endif() + if(DUNE_LIB_COMPILE_FLAGS) + set_property(${basename} APPEND_STRING COMPILE_FLAGS + "${DUNE_LIB_COMPILE_FLAGS}") + endif() + # Build library in ${PROJECT_BINARY_DIR}/lib + set_target_properties(${basename} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" + ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") + + if(NOT DUNE_LIB_NO_EXPORT) + # The following allows for adding multiple libs in the same + # directory or below with passing the APPEND keyword. + # If there are additional calls to dune_add_library in other + # modules then you have to use APPEND or otherwise only the + # last lib will get exported as a target. + if(NOT _MODULE_EXPORT_USED) + set(_MODULE_EXPORT_USED ON) + set(_append "") + else() + set(_append APPEND) + endif() + # Allow to explicitly pass APPEND + if(DUNE_LIB_APPEND) + set(_append APPEND) + endif() + + # install targets to use the libraries in other modules. + install(TARGETS ${basename} + EXPORT ${ProjectName}-targets DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(EXPORT ${ProjectName}-targets + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${ProjectName}) + + # export libraries for use in build tree + export(TARGETS ${basename} ${_append} + FILE ${PROJECT_BINARY_DIR}/${ProjectName}-targets.cmake) + endif() + endif() +endmacro(dune_add_library basename sources) + +macro(replace_properties_for_one) + get_property(properties ${option_command} ${_target} + PROPERTY ${REPLACE_PROPERTY}) + if(NOT properties) + # property not set. set it directly + foreach(i RANGE 0 ${hlength}) + math(EXPR idx "(2 * ${i}) + 1") + list(GET REPLACE_UNPARSED_ARGUMENTS ${idx} repl) + list(APPEND replacement ${repl}) + endforeach() + list(REMOVE_DUPLICATES replacement) + set_property(${option_command} ${_target} ${REPLACE_APPEND} + ${REPLACE_APPEND_STRING} PROPERTY ${REPLACE_PROPERTY} ${replacement}) + else() + foreach(prop ${properties}) + set(matched FALSE) + foreach(i RANGE 0 ${hlength}) + math(EXPR regexi "2 * ${i}") + math(EXPR repli "${regexi} +1") + list(GET REPLACE_UNPARSED_ARGUMENTS ${regexi} regex) + list(GET REPLACE_UNPARSED_ARGUMENTS ${repli} replacement) + string(REGEX MATCH ${regex} match ${prop}) + + if(match) + list(APPEND new_props ${replacement}) + set(matched TRUE) + endif() + endforeach() + + if(NOT matched) + list(APPEND new_props ${prop}) + endif() + endforeach() + list(REMOVE_DUPLICATES new_props) + set_property(${option_command} ${_target} + PROPERTY ${REPLACE_PROPERTY} ${new_props}) + endif() + get_property(properties ${option_command} ${_target} PROPERTY ${REPLACE_PROPERTY}) +endmacro(replace_properties_for_one) + +function(dune_target_link_libraries basename libraries) + target_link_libraries(${basename} PUBLIC ${libraries}) +endfunction(dune_target_link_libraries basename libraries) + +function(replace_properties) + include(CMakeParseArguments) + set(_first_opts "GLOBAL;DIRECTORY;TARGET;SOURCE;CACHE") + cmake_parse_arguments(REPLACE "GLOBAL" + "DIRECTORY;PROPERTY" "TARGET;SOURCE;TEST;CACHE" ${ARGN}) + + set(MY_DIRECTORY TRUE) + foreach(i ${_first_opts}) + if(REPLACE_${i}) + set(MY_DIRECTORY FALSE) + endif() + endforeach() + if(NOT MY_DIRECTORY) + list(FIND REPLACE_UNPARSED_ARGUMENTS DIRECTORY _found) + if(_found GREATER -1) + list(REMOVE_AT REPLACE_UNPARSED_ARGUMENTS ${_found}) + set(MY_DIRECTORY TRUE) + set(REPLACE_DIRECTORY "") + endif() + endif() + + # setup options + if(REPLACE_GLOBAL) + set(option_command GLOBAL) + elseif(MY_DIRECTORY) + set(option_command DIRECTORY) + elseif(REPLACE_DIRECTORY) + set(option_command DIRECTORY) + set(option_arg ${REPLACE_DIRECTORY}) + elseif(REPLACE_TARGET) + set(option_command TARGET) + set(option_arg ${REPLACE_TARGET}) + elseif(REPLACE_SOURCE) + set(option_command SOURCE) + set(option_arg ${REPLACE_SOURCE}) + elseif(REPLACE_TEST) + set(option_command TEST) + set(option_arg${REPLACE_TEST}) + elseif(REPLACE_CACHE) + set(option_command CACHE) + set(option_arg ${REPLACE_CACHE}) + endif() + + if(NOT (REPLACE_CACHE OR REPLACE_TEST OR REPLACE_SOURCE + OR REPLACE_TARGET OR REPLACE_DIRECTORY OR REPLACE_GLOBAL + OR MY_DIRECTORY)) + message(ERROR "One of GLOBAL, DIRECTORY, TARGET, SOURCE, TEST, or CACHE" + " has to be present") + endif() + + list(LENGTH REPLACE_UNPARSED_ARGUMENTS length) +# if(NOT (REPLACE_GLOBAL AND REPLACE_TARGET AND +# REPLACE_SOURCE AND REPLACE + math(EXPR mlength "${length} % 2 ") + math(EXPR hlength "${length} / 2 - 1") + + if(NOT ${mlength} EQUAL 0) + message(ERROR "You need to specify pairs consisting of a regular expression and a replacement string.") + endif() + + if(NOT length GREATER 0) + message(ERROR "You need to specify at least on pair consisting of a regular expression +and a replacement string. ${REPLACE_UNPARSED_ARGUMENTS}") + endif() + + foreach(_target ${option_arg}) + replace_properties_for_one() + endforeach() + + list(LENGTH option_arg _length) + if(_length EQUAL 0) + replace_properties_for_one() + endif() +endfunction(replace_properties) + +macro(add_dune_all_flags targets) + get_property(incs GLOBAL PROPERTY ALL_PKG_INCS) + get_property(defs GLOBAL PROPERTY ALL_PKG_DEFS) + get_property(libs GLOBAL PROPERTY ALL_PKG_LIBS) + get_property(opts GLOBAL PROPERTY ALL_PKG_OPTS) + foreach(target ${targets}) + set_property(TARGET ${target} APPEND PROPERTY INCLUDE_DIRECTORIES ${incs}) + set_property(TARGET ${target} APPEND PROPERTY COMPILE_DEFINITIONS ${defs}) + target_link_libraries(${target} PUBLIC ${DUNE_LIBS} ${libs}) + target_compile_options(${target} PUBLIC ${opts}) + endforeach() +endmacro(add_dune_all_flags targets) diff --git a/cmake/modules/DunePathHelper.cmake b/cmake/modules/DunePathHelper.cmake new file mode 100644 index 0000000..352cb07 --- /dev/null +++ b/cmake/modules/DunePathHelper.cmake @@ -0,0 +1,102 @@ +# Some helper functions for people developing the CMake build system +# to get quick and easy access to path variables of Dune modules. +# +# .. cmake_function:: dune_module_path +# +# .. cmake_param:: MODULE +# :single: +# :required: +# +# The name of the module. +# +# .. cmake_param:: RESULT +# :single: +# :required: +# +# The name of the variable to export the result. +# +# .. cmake_param:: CMAKE_MODULES +# :option: +# +# Set to return the path to cmake modules +# +# .. cmake_param:: BUILD_DIR +# :option: +# +# Set to return the path to the build directory +# +# .. cmake_param:: SOURCE_DIR +# :option: +# +# Set to return the include path of the module +# +# .. cmake_param:: SCRIPT_DIR +# :option: +# +# Set to return the CMake script dir +# +# +# Returns the specified path of the given module. This differs +# whether it is called from the actual module, or from a module +# requiring or suggesting this module. One and only one type of path +# may be requested. +# +include_guard(GLOBAL) + +function(dune_module_path) + # Parse Arguments + set(OPTION CMAKE_MODULES BUILD_DIR SOURCE_DIR SCRIPT_DIR) + set(SINGLE MODULE RESULT) + set(MULTI) + include(CMakeParseArguments) + cmake_parse_arguments(PATH "${OPTION}" "${SINGLE}" "${MULTI}" ${ARGN}) + if(PATH_UNPARSED_ARGUMENTS) + message(WARNING "Unparsed arguments in dune_module_path: This often indicates typos!") + endif() + + # Check whether one and only one path type was set. + set(OPTION_FOUND 0) + foreach(opt ${OPTION}) + if(${PATH_${opt}}) + if(OPTION_FOUND) + message(FATAL_ERROR "Cannot request two different paths from dune_module_path") + else() + set(OPTION_FOUND 1) + endif() + endif() + endforeach() + if(NOT OPTION_FOUND) + message(FATAL_ERROR "Cannot determine type of requested path!") + endif() + + # Set the requested paths for the cmake module path + if(PATH_CMAKE_MODULES) + set(IF_CURRENT_MOD ${PROJECT_SOURCE_DIR}/cmake/modules) + set(IF_NOT_CURRENT_MOD ${${PATH_MODULE}_MODULE_PATH}) + endif() + + # Set the requested paths for the cmake script path + if(PATH_SCRIPT_DIR) + set(IF_CURRENT_MOD ${PROJECT_SOURCE_DIR}/cmake/scripts) + set(IF_NOT_CURRENT_MOD ${${PATH_MODULE}_SCRIPT_DIR}) + endif() + + # Set the requested paths for the build directory + if(PATH_BUILD_DIR) + set(IF_CURRENT_MOD ${PROJECT_BINARY_DIR}) + set(IF_NOT_CURRENT_MOD ${${PATH_MODULE}_DIR}) + endif() + + # Set the requested paths for the include directory + if(PATH_SOURCE_DIR) + set(IF_CURRENT_MOD ${PROJECT_SOURCE_DIR}) + set(IF_NOT_CURRENT_MOD ${${PATH_MODULE}_PREFIX}) + endif() + + # Now set the path in the outer scope! + if(PROJECT_NAME STREQUAL ${PATH_MODULE}) + set(${PATH_RESULT} ${IF_CURRENT_MOD} PARENT_SCOPE) + else() + set(${PATH_RESULT} ${IF_NOT_CURRENT_MOD} PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/modules/DunePkgConfig.cmake b/cmake/modules/DunePkgConfig.cmake new file mode 100644 index 0000000..f809fac --- /dev/null +++ b/cmake/modules/DunePkgConfig.cmake @@ -0,0 +1,64 @@ +# searches for pkg-config, creates the +# file .pc from .pc.in, +# and adds installation directives. +# +include_guard(GLOBAL) + +find_package(PkgConfig) +# text for feature summary +set_package_properties("PkgConfig" PROPERTIES + DESCRIPTION "Unified interface for querying installed libraries" + PURPOSE "To find Dune module dependencies") + +function(create_and_install_pkconfig installlibdir) + # set some variables that are used in the pkg-config file + include(GNUInstallDirs) + + if(SKBUILD) + # we are using scikit-build to build a python wheel. The install prefix + # set by scikit is within a tmp directory (isolated build) and + # therefore not suitable for the prefix in the pc file. At least when + # installed into a virtual env the correct prefix path is two below the + # location of the pc file, i.e., + # location of pc files: dune-env/lib/pkgconfig + # location of dune.module files: dune-env/lib/dunecontrol + # and from the documentation + # installed module: ${path}/lib/dunecontrol/${name}/dune.module + # and there is a file ${path}/lib/pkgconfig/${name}.pc + set( prefix "\${pcfiledir}/../..") + else() + set( prefix ${CMAKE_INSTALL_PREFIX}) + endif() + + set(exec_prefix "\${prefix}") + set(libdir "\${exec_prefix}/${installlibdir}") + set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") + set(PACKAGE_NAME ${ProjectName}) + set(VERSION ${ProjectVersion}) + set(CC ${CMAKE_C_COMPILER}) + set(CXX "${CMAKE_CXX_COMPILER} ${CXX_STD11_FLAGS}") + + if(DUNE_DEPENDS) + foreach(_DUNE_DEPEND ${DUNE_DEPENDS}) + string(REGEX REPLACE "\\(" "" REQF1 ${_DUNE_DEPEND}) + string(REGEX REPLACE "\\)" "" LR ${REQF1}) + if(REQUIRES) + set(REQUIRES "${REQUIRES} ${LR}") + else() + set(REQUIRES ${LR}) + endif(REQUIRES) + endforeach(_DUNE_DEPEND ${DUNE_DEPENDS}) + endif(DUNE_DEPENDS) + + #create pkg-config file + configure_file( + ${PROJECT_SOURCE_DIR}/${ProjectName}.pc.in + ${PROJECT_BINARY_DIR}/${ProjectName}.pc + @ONLY + ) + + # install pkgconfig file + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${ProjectName}.pc + DESTINATION ${installlibdir}/pkgconfig) + +endfunction(create_and_install_pkconfig) diff --git a/cmake/modules/DunePythonCommonMacros.cmake b/cmake/modules/DunePythonCommonMacros.cmake new file mode 100644 index 0000000..e5ff306 --- /dev/null +++ b/cmake/modules/DunePythonCommonMacros.cmake @@ -0,0 +1,158 @@ +# The python extension of the Dune cmake build system +# +# .. cmake_module:: +# +# This module is the main entry point for the python extension of the Dune cmake +# build system. It handles the detection of the python installation, defines installation +# rules for python packages in Dune modules and provides virtual environments to +# run python code from cmake. +# +# If you want to use Dune modules that provide Python functionality, you should be aware +# of some facts: +# +# * CMake looks for your python interpreter during configure. If you want to have it +# work with a virtual environment, you should activate your virtualenv before configure. +# * Each module has an additional target :code:`make install_python`, that installs python packages +# defined in the Dune module. You can customize the install location with +# :ref:`DUNE_PYTHON_INSTALL_LOCATION`. This is also included in :code:`make install`. +# * There is additional functionality, that automatically sets up a virtual environment +# at configure time, you can read more at :ref:`DunePythonVirtualenv`. +# +# After the module :code:`DunePythonCommonMacros` is run (which happens automatically when +# configuring dune-common) the following python-related variables will be set and available +# for use in downstream modules: +# +# * All variables set by :code:`FindPythonInterp.cmake` and :code:`FindPythonLibs.cmake` +# * :code:`DUNE_PYTHON_SYSTEM_IS_VIRTUALENV`: True if the given system interpreter resides in +# virtual environment. +# +# For documentation on how to customize the build process, check the input variable +# reference for any variables prefixed with :code:`DUNE_PYTHON`. To learn how to write build +# system code for Dune modules shipping python, have a look at the command reference for +# commands prefixed :code:`dune_python`. +# +# .. cmake_variable:: DUNE_PYTHON_INSTALL_LOCATION +# +# This variable can be used to control where Dune should install python +# packages. Possible values are: +# +# * :code:`user`: installs into the users home directory through :code:`pip --user`. Note, that +# this is incompatible with using virtual environments (as per pip docs). +# * :code:`system`: into the standard paths of the interpreter which was found +# by cmake. +# * :code:`none`: Never install any python packages. +# +# The default value in use depends on the system interpreter to run in a virtual environment +# or not: If it does, :code:`system` is the default, if it does not :code:`none` is the default. +# This rather unintuitive default originates from the strong belief, that installing +# python packages into the system locations at :code:`/usr/...` should be discouraged. +# +# .. cmake_variable:: DUNE_PYTHON_VIRTUALENV_SETUP +# +# Set this variable to allow the Dune build system to set up a virtualenv at +# configure time. Such virtual environment is very useful, whenever python code +# is to be run at configure time, i.e. to implement code generation in Python or +# to use Python wrappers in testing. Some downstream modules will *require* you +# to set this variable. When setting this variable, you allow the Dune buildsystem +# to install packages through :code:`pip` into a virtualenv, that resides in a cmake +# build directory. For all the information on this virtualenv, see :ref:`DunePythonVirtualenv`. +# +# .. cmake_function:: dune_python_require_virtualenv_setup +# +# Call this function from a downstream module, if that module relies on the +# the presence of the configure time virtualenv described in :ref:`DunePythonVirtualenv`. +# +include_guard(GLOBAL) + +# unless the user has defined the variable, unversioned names (like python3) are found +# first, to match what users most probably use later on to call the executable +if(NOT DEFINED Python3_FIND_UNVERSIONED_NAMES) + set(Python3_FIND_UNVERSIONED_NAMES "FIRST") +endif() + +# include code from CMake 3.20 to back-port using unversioned Python first +if(${CMAKE_VERSION} VERSION_LESS "3.20") + list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_LIST_DIR}/FindPython3") +endif() + +# Include all the other parts of the python extension to avoid that users need +# to explicitly include parts of our build system. +include(DunePythonFindPackage) +include(DunePythonInstallPackage) +include(DunePythonTestCommand) + +# Find the Python Interpreter and libraries +find_package(Python3 COMPONENTS Interpreter Development) + + +# Determine whether the given interpreter is running inside a virtualenv +if(Python3_Interpreter_FOUND) + include(DuneExecuteProcess) + include(DunePathHelper) + dune_module_path(MODULE dune-common + RESULT scriptdir + SCRIPT_DIR) + + dune_execute_process(COMMAND "${Python3_EXECUTABLE}" "${scriptdir}/envdetect.py" + RESULT_VARIABLE DUNE_PYTHON_SYSTEM_IS_VIRTUALENV + ) +endif() + +# Determine where to install python packages +if(NOT DUNE_PYTHON_INSTALL_LOCATION) + if(DUNE_PYTHON_SYSTEM_IS_VIRTUALENV) + set(DUNE_PYTHON_INSTALL_LOCATION "system") + else() + set(DUNE_PYTHON_INSTALL_LOCATION "none") + endif() +endif() +if(NOT(("${DUNE_PYTHON_INSTALL_LOCATION}" STREQUAL "user") OR + ("${DUNE_PYTHON_INSTALL_LOCATION}" STREQUAL "system") OR + ("${DUNE_PYTHON_INSTALL_LOCATION}" STREQUAL "none"))) + message(FATAL_ERROR "DUNE_PYTHON_INSTALL_LOCATION must be user|system|none.") +endif() +if(("${DUNE_PYTHON_INSTALL_LOCATION}" STREQUAL "user") AND + DUNE_PYTHON_SYSTEM_IS_VIRTUALENV) + message(FATAL_ERROR "Specifying 'user' as install location is incomaptible with using virtual environments (as per pip docs)") +endif() + +# Check presence of python packages required by the buildsystem +dune_python_find_package(PACKAGE pip) + +# Add python related meta targets +add_custom_target(test_python) +add_custom_target(install_python) + +# Set the path to a Dune wheelhouse that is to be used during installation +# NB: Right now, the same logic is used to retrieve the location of the +# wheelhouse (which means that you have to use the same CMAKE_INSTALL_PREFIX +# when *using* installed modules, you used when *installing* them. +# TODO: Replace this with a better mechanism (like writing the location into +# dune-commons package config file) +set(DUNE_PYTHON_WHEELHOUSE ${CMAKE_INSTALL_PREFIX}/share/dune/wheelhouse) + +# Have make install do the same as make install_python +install(CODE "set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}) + set(DUNE_PYTHON_WHEELHOUSE ${DUNE_PYTHON_WHEELHOUSE}) + include(DuneExecuteProcess) + dune_execute_process(COMMAND \"${CMAKE_COMMAND}\" --build . --target install_python --config $) + ") + +# Implement a check for the presence of the virtualenv +function(dune_python_require_virtualenv_setup) + if(NOT DUNE_PYTHON_VIRTUALENV_SETUP) + message(FATAL_ERROR "\n + ${PROJECT_NAME} relies on a configure-time virtual environment being + set up by the Dune python build system. You have to set the CMake variable + DUNE_PYTHON_VIRTUALENV_SETUP to allow that.\n + ") + endif() +endfunction() + +# If requested, switch into DunePythonVirtualenv.cmake and setup the virtualenv. +if(DUNE_PYTHON_VIRTUALENV_SETUP) + include(DunePythonVirtualenv) +endif() + +# marcos used for the Python bindings +include(DunePythonMacros) diff --git a/cmake/modules/DunePythonFindPackage.cmake b/cmake/modules/DunePythonFindPackage.cmake new file mode 100644 index 0000000..c4262df --- /dev/null +++ b/cmake/modules/DunePythonFindPackage.cmake @@ -0,0 +1,116 @@ +# This module provides functions to check for the existence of python packages on the host system. +# +# .. cmake_function:: dune_python_find_package +# +# .. cmake_param:: PACKAGE +# :required: +# :single: +# +# The package name to look for. +# +# .. cmake_param: RESULT +# :single: +# +# The variable to store the result of the check in +# in the calling scope. Defaults to :code:`DUNE_PYTHON__FOUND` +# Note that the package name is case sensitive and will +# usually be lowercase. +# +# .. cmake_param:: REQUIRED +# :option: +# +# If set, the function will error out if the package is not +# found. +# +# .. cmake_param:: VERSION +# :single: +# +# The minimum version of the package that is required. +# +# .. cmake_param:: EXACT +# :option: +# +# Whether the given version requirement has to be matched exactly. +# +# .. cmake_param:: INTERPRETER +# :single: +# +# The python interpreter, whose paths are searched for the package. +# Defaults to :code:`${Python3_EXECUTABLE}`, might differ when dealing with +# the configure-time virtualenv set up with :ref:`DUNE_PYTHON_VIRTUALENV_SETUP`. +# +# Find a given python package on the system. +# +include_guard(GLOBAL) + +function(dune_python_find_package) + # Parse Arguments + set(OPTION REQUIRED EXACT) + set(SINGLE PACKAGE RESULT VERSION INTERPRETER) + set(MULTI) + include(CMakeParseArguments) + cmake_parse_arguments(PYPACKAGE "${OPTION}" "${SINGLE}" "${MULTI}" ${ARGN}) + if(PYCHECK_UNPARSED_ARGUMENTS) + message(WARNING "Unparsed arguments in dune_python_find_package: This often indicates typos!") + endif() + + # Do error checking on input and apply defaults + if(NOT PYPACKAGE_RESULT) + set(PYPACKAGE_RESULT DUNE_PYTHON_${PYPACKAGE_PACKAGE}_FOUND) + endif() + if(NOT PYPACKAGE_INTERPRETER) + set(PYPACKAGE_INTERPRETER "${Python3_EXECUTABLE}") + endif() + if(PYPACKAGE_EXACT AND NOT PYPACKAGE_VERSION) + message(FATAL_ERROR "dune_python_find_package: EXACT given, but no VERSION specified.") + endif() + + # Do the actual check + execute_process(COMMAND "${PYPACKAGE_INTERPRETER}" -c "import ${PYPACKAGE_PACKAGE}" + RESULT_VARIABLE PYPACKAGE_RETURN + ERROR_QUIET) + + # Perform additional checks + if(PYPACKAGE_RETURN STREQUAL "0") + include(DunePathHelper) + dune_module_path(MODULE dune-common + RESULT scriptdir + SCRIPT_DIR) + + # Check the found version of the given python package + # We cannot use find_package_handle_standard_args for that, as it is too + # closely tied to using find_package(), which we cannot use for variable package + # name... + execute_process(COMMAND "${PYPACKAGE_INTERPRETER}" "${scriptdir}/pyversion.py" "${PYPACKAGE_PACKAGE}" + RESULT_VARIABLE retcode + OUTPUT_VARIABLE VERSION_STRING + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + set(${PYPACKAGE_RESULT} TRUE) + if("${retcode}" STREQUAL "0") + if(("${VERSION_STRING}" VERSION_LESS "${PYPACKAGE_VERSION}") OR + (PYPACKAGE_EXACT AND NOT ("${VERSION_STRING}" VERSION_EQUAL "${PYPACKAGE_VERSION}"))) + set(${PYPACKAGE_RESULT} FALSE) + endif() + else() + set(VERSION_STRING "unknown version") + if(PYPACKAGE_VERSION) + set(${PYPACKAGE_RESULT} FALSE) + endif() + endif() + else() + set(${PYPACKAGE_RESULT} FALSE) + if(PYPACKAGE_REQUIRED) + message(FATAL_ERROR "The python package ${PYPACKAGE_PACKAGE} could not be found! (for interpreter ${PYPACKAGE_INTERPRETER})") + endif() + endif() + + # Set the result variable and print the result + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(${PYPACKAGE_PACKAGE}_${PYPACKAGE_INTERPRETER} + "Failed to find the python package ${PYPACKAGE_PACKAGE} with interpreter ${PYPACKAGE_INTERPRETER}." + ${PYPACKAGE_RESULT} + ) + set(${PYPACKAGE_RESULT} ${${PYPACKAGE_RESULT}} PARENT_SCOPE) +endfunction() diff --git a/cmake/modules/DunePythonInstallPackage.cmake b/cmake/modules/DunePythonInstallPackage.cmake new file mode 100644 index 0000000..94bf31d --- /dev/null +++ b/cmake/modules/DunePythonInstallPackage.cmake @@ -0,0 +1,152 @@ +# This cmake module provides infrastructure for cmake installation rules concerning python packages. +# +# .. cmake_function:: dune_python_install_package +# +# .. cmake_param:: PATH +# :required: +# :single: +# +# Relative path to the given python package source code. +# +# .. cmake_param:: ADDITIONAL_PIP_PARAMS +# :multi: +# :argname: param +# +# Parameters to add to any :code:`pip install` call (appended). +# +# This function installs the python package located at the given path. It +# +# * installs it to the location specified with :ref:`DUNE_PYTHON_INSTALL_LOCATION` during +# :code:`make install_python` and during :code:`make install`. +# * installs a wheel into the Dune wheelhouse during :code:`make install`. +# This is necessary for mixing installed and non-installed Dune modules. +# +# The package at the given location is expected to be a pip-installable package. +# +# .. cmake_variable:: DUNE_PYTHON_INSTALL_EDITABLE +# +# Set this variable to have all installations of python packages use +# :code:`pip --editable`. +# +# +# .. cmake_variable:: DUNE_PYTHON_ADDITIONAL_PIP_PARAMS +# +# Use this variable to set additional flags for pip in this build. This can e.g. +# be used to point pip to alternative package indices in restricted environments. +# +include_guard(GLOBAL) + +function(dune_python_install_package) + # Parse Arguments + set(OPTION) + set(SINGLE PATH) + set(MULTI ADDITIONAL_PIP_PARAMS) + include(CMakeParseArguments) + cmake_parse_arguments(PYINST "${OPTION}" "${SINGLE}" "${MULTI}" ${ARGN}) + if(PYINST_UNPARSED_ARGUMENTS) + message(WARNING "Unparsed arguments in dune_python_install_package: This often indicates typos!") + endif() + + set(PYINST_FULLPATH ${CMAKE_CURRENT_SOURCE_DIR}/${PYINST_PATH}) + if(EXISTS ${PYINST_FULLPATH}/setup.py.in) + configure_file(${PYINST_PATH}/setup.py.in ${PYINST_PATH}/setup.py) + set(PYINST_FULLPATH ${CMAKE_CURRENT_BINARY_DIR}/${PYINST_PATH}) + set(PYINST_PUREPYTHON FALSE) + elseif(EXISTS ${PYINST_FULLPATH}/setup.py) + set(PYINST_PUREPYTHON TRUE) + else() + message(FATAL_ERROR "dune_python_install_package: Requested installations, but neither setup.py nor setup.py.in found!") + endif() + + # Find out whether we should install in editable mode + set(INSTALL_EDITABLE ${DUNE_PYTHON_INSTALL_EDITABLE}) + + # Construct the wheel house installation option string + set(WHEEL_OPTION "") + if(IS_DIRECTORY ${DUNE_PYTHON_WHEELHOUSE}) + set(WHEEL_OPTION "--find-links=${DUNE_PYTHON_WHEELHOUSE}") + # + # The following line is a bummer! + # We cannot have editable packages once we start using global installations! + # This is related to the nightmare that is https://github.com/pypa/pip/issues/3 + # + set(INSTALL_EDITABLE FALSE) + endif() + + # Construct the editable option string + set(EDIT_OPTION "") + if(INSTALL_EDITABLE) + set(EDIT_OPTION "-e") + endif() + + # Construct the installation location option string + set(INSTALL_OPTION "") + if("${DUNE_PYTHON_INSTALL_LOCATION}" STREQUAL "user") + set(INSTALL_OPTION "--user") + endif() + + set(INSTALL_CMDLINE -m pip install + "${INSTALL_OPTION}" --upgrade "${WHEEL_OPTION}" "${EDIT_OPTION}" ${PYINST_ADDITIONAL_PIP_PARAMS} ${DUNE_PYTHON_ADDITIONAL_PIP_PARAMS} + "${PYINST_FULLPATH}") + + + # Leave this function if no installation rules are required + if("${DUNE_PYTHON_INSTALL_LOCATION}" STREQUAL "none" AND NOT DUNE_PYTHON_VIRTUALENV_SETUP) + return() + endif() + + # Check for the presence of the pip package + if(NOT DUNE_PYTHON_pip_FOUND) + message(FATAL_ERROR "dune_python_install_package: Requested installations, but pip was not found!") + endif() + + # + # If requested, install into the configure-time Dune virtualenv + # + + if(PYINST_PUREPYTHON AND DUNE_PYTHON_VIRTUALENV_SETUP) + message("-- Installing python package at ${CMAKE_CURRENT_SOURCE_DIR}/${PYINST_PATH} into the virtualenv...") + dune_execute_process(COMMAND "${DUNE_PYTHON_VIRTUALENV_EXECUTABLE}" "${INSTALL_CMDLINE}" + ERROR_MESSAGE "dune_python_install_package: Error installing into virtualenv!") + endif() + + # + # Now define rules for `make install_python`. + # + + # Leave this function if no installation rules are required + if("${DUNE_PYTHON_INSTALL_LOCATION}" STREQUAL "none") + return() + endif() + + dune_module_path(MODULE dune-common + RESULT scriptdir + SCRIPT_DIR) + + # Determine a target name for installing this package + string(REPLACE "/" "_" targetname "install_python_${CMAKE_CURRENT_SOURCE_DIR}_${PYINST_PATH}") + + # Add a custom target that globally installs this package if requested + add_custom_target(${targetname} + COMMAND ${Python3_EXECUTABLE} ${INSTALL_CMDLINE} + COMMENT "Installing the python package at ${PYINST_FULLPATH}" + ) + + add_dependencies(install_python ${targetname}) + + # Define rules for `make install` that install a wheel into a central wheelhouse + # + # NB: This is necessary, to allow mixing installed and non-installed modules + # with python packages. The wheelhouse will allow to install any missing + # python packages into a virtual environment. + # + + # Construct the wheel installation commandline + set(WHEEL_COMMAND ${Python3_EXECUTABLE} -m pip wheel -w ${DUNE_PYTHON_WHEELHOUSE} ${PYINST_ADDITIONAL_PIP_PARAMS} ${DUNE_PYTHON_ADDITIONAL_PIP_PARAMS} ${PYINST_FULLPATH}) + + # Add the installation rule + install(CODE "message(\"Installing wheel for python package at ${PYINST_FULLPATH}...\") + dune_execute_process(COMMAND ${WHEEL_COMMAND} + )" + ) +endfunction() diff --git a/cmake/modules/DunePythonMacros.cmake b/cmake/modules/DunePythonMacros.cmake new file mode 100644 index 0000000..2a1091b --- /dev/null +++ b/cmake/modules/DunePythonMacros.cmake @@ -0,0 +1,32 @@ +# this option enables the build of Python bindings for DUNE modules +option(DUNE_ENABLE_PYTHONBINDINGS "Enable Python bindings for DUNE" OFF) + +if( DUNE_ENABLE_PYTHONBINDINGS ) + if(NOT Python3_Interpreter_FOUND) + message(FATAL_ERROR "Python bindings require a Python 3 interpreter") + endif() + if(NOT Python3_INCLUDE_DIRS) + message(FATAL_ERROR "Found a Python interpreter but the Python bindings also requires the Python libraries (a package named like python-dev package or python3-devel)") + endif() + + include_directories("${Python3_INCLUDE_DIRS}") + + function(add_python_targets base) + include(DuneSymlinkOrCopy) + if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) + message(WARNING "Source and binary dir are the same, skipping symlink!") + else() + foreach(file ${ARGN}) + dune_symlink_to_source_files(FILES ${file}.py) + endforeach() + endif() + endfunction() + + include(DuneAddPybind11Module) + + # Add a custom command that triggers the configuration of dune-py + add_custom_command(TARGET install_python POST_BUILD + COMMAND ${Python3_EXECUTABLE} -m dune configure + ) + +endif() diff --git a/cmake/modules/DunePythonTestCommand.cmake b/cmake/modules/DunePythonTestCommand.cmake new file mode 100644 index 0000000..4a2ca79 --- /dev/null +++ b/cmake/modules/DunePythonTestCommand.cmake @@ -0,0 +1,108 @@ +# Wrap python testing commands into the CMake build system +# +# .. cmake_function:: dune_python_add_test +# +# .. cmake_param:: SCRIPT +# :multi: +# +# The script to execute using the python interpreter. It will be executed during :code:`make test_python` +# and during `ctest`. You are required to either pass SCRIPT or MODULE. +# +# .. note:: +# +# The script will be executed using :code:`${Python3_EXECUTABLE} SCRIPT`. If the INTERPRETER +# option is given, that interpreter is used instead. +# +# .. cmake_param:: MODULE +# :multi: +# +# The Python module to be executed. It will be executed during :code:`make test_python` +# and during `ctest`. You are required to either pass SCRIPT or MODULE. +# +# .. note:: +# +# The script will be executed using :code:`${Python3_EXECUTABLE} -m MODULE`. If the INTERPRETER +# option is given, that interpreter is used instead. +# +# .. cmake_param:: INTERPRETER +# :single: +# +# The Python interpreter to use for this test. It defaults to the one found by CMake. +# +# .. cmake_param:: WORKING_DIRECTORY +# :single: +# :argname: dir +# +# The working directory of the command. Defaults to +# the current build directory. +# +# .. cmake_param:: NAME +# :single: +# +# A name to identify this test in ctest. Names must be unique throughout +# the project. If omitted, defaults to mangling of the command. +# +# Integrates a python testing framework command into the Dune +# build system. Added commands are run, when the target +# :code:`test_python` is built and during :code:`ctest`. +# +include_guard(GLOBAL) + +function(dune_python_add_test) + # Parse Arguments + include(CMakeParseArguments) + set(OPTION) + set(SINGLE WORKING_DIRECTORY NAME INTERPRETER) + set(MULTI SCRIPT COMMAND LABELS MODULE) + + cmake_parse_arguments(PYTEST "${OPTION}" "${SINGLE}" "${MULTI}" ${ARGN}) + if(PYTEST_COMMAND) + message(FATAL_ERROR "dune_python_add_test: COMMAND argument should not be used, use SCRIPT instead providing only the Python script and not the Python interpreter") + endif() + if(PYTEST_UNPARSED_ARGUMENTS) + message(WARNING "Unparsed arguments in dune_python_add_test: This often indicates typos!") + endif() + + # Apply defaults + if(NOT PYTEST_INTERPRETER) + set(PYTEST_INTERPRETER ${Python3_EXECUTABLE}) + endif() + if(NOT PYTEST_WORKING_DIRECTORY) + set(PYTEST_WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + endif() + if((NOT PYTEST_MODULE) AND (NOT PYTEST_SCRIPT)) + message(FATAL_ERROR "dune_python_add_test: Either SCRIPT or MODULE need to be specified!") + endif() + if(PYTEST_MODULE AND PYTEST_SCRIPT) + message(FATAL_ERROR "dune_python_add_test: You can only specify either SCRIPT or MODULE, not both!") + endif() + if(PYTEST_MODULE) + set(PYTEST_SCRIPT -m ${PYTEST_MODULE}) + endif() + if(NOT PYTEST_NAME) + set(commandstr "") + foreach(comm ${PYTEST_SCRIPT}) + set(commandstr "${commandstr}_${comm}") + endforeach() + set(commandstr "${commandstr}_${PYTEST_WORKING_DIRECTORY}") + string(REPLACE "/" "_" PYTEST_NAME ${commandstr}) + endif() + + # Actually run the command + add_custom_target(target_${PYTEST_NAME} + COMMAND ${PYTEST_INTERPRETER} ${PYTEST_SCRIPT} + WORKING_DIRECTORY ${PYTEST_WORKING_DIRECTORY}) + + # Build this during make test_python + add_dependencies(test_python target_${PYTEST_NAME}) + + # make sure each label exists and its name is acceptable + dune_declare_test_label(LABELS ${PYTEST_LABELS}) + # Also build this during ctest + _add_test(NAME ${PYTEST_NAME} + COMMAND ${PYTEST_INTERPRETER} ${PYTEST_SCRIPT} + WORKING_DIRECTORY ${PYTEST_WORKING_DIRECTORY} + ) + # Set the labels on the test + set_tests_properties(${PYTEST_NAME} PROPERTIES LABELS "${PYTEST_LABELS}") +endfunction() diff --git a/cmake/modules/DunePythonVirtualenv.cmake b/cmake/modules/DunePythonVirtualenv.cmake new file mode 100644 index 0000000..1ba8e82 --- /dev/null +++ b/cmake/modules/DunePythonVirtualenv.cmake @@ -0,0 +1,234 @@ +# Manage the creation of a configure-time virtual environment +# +# .. cmake_module:: +# +# This module manages the creation of virtual python environment during +# configuration. Execution of this module must be explicitly enabled by +# setting the variable :ref:`DUNE_PYTHON_VIRTUALENV_SETUP`. Note that some +# downstream modules will require you to set this variable. The purpose +# of this virtual environment is to be able to run python code from cmake +# in situations such as python-based code generation, running postprocessing +# in python during testing etc. +# +# Although designed for internal use, this virtualenv can also be manually +# inspected. A symlink to the activation script is placed in the top level +# build directory of all Dune modules in the stack. To directly execute a +# command in the virtualenv, you can use the script :code:`run-in-dune-env `, +# which is also placed into every build directory. +# +# All packages installed with :ref:`dune_python_install_package` are automatically +# installed into the virtualenv. +# +# After execution of this module, the following are available for use in +# downstream modules: +# +# * :code:`DUNE_PYTHON_VIRTUALENV_PATH` The path of the virtual environment +# * :code:`DUNE_PYTHON_VIRTUALENV_EXECUTABLE` The python interpreter in the virtual environment +# +# By default, the created virtualenv resides in the first non-installed Dune module of +# the module stack (if no installation is performed: dune-common). Be aware +# that mixing installed and non-installed modules may result in a situation, +# where multiple such environments are created, although only one should. +# You can change this behavior by either specifying a fixed path for the virtualenv +# using :ref:`DUNE_PYTHON_VIRTUALENV_PATH` or by enabling +# :ref:`DUNE_PYTHON_EXTERNAL_VIRTUALENV_FOR_ABSOLUTE_BUILDDIR` if you are using an +# absolute build directory with dunecontrol. Note that this flag is enabled by default +# starting from Dune 2.7. +# +# .. cmake_variable:: DUNE_PYTHON_VIRTUALENV_PATH +# +# When the Dune build system has setup a virtualenv, this variable will contain its location. +# You can also set this variable to a fixed path when CMake, and the virtualenv will be placed +# at that location. +# +# .. cmake_variable:: DUNE_PYTHON_EXTERNAL_VIRTUALENV_FOR_ABSOLUTE_BUILDDIR +# +# Before Dune 2.7, the virtualenv was always placed inside the build directory of the first +# non-installed Dune module that the current module depends on. When using installed core modules +# or a multi-stage installation process, this can lead to situations where there are multiple +# virtualenvs, making it impossible to find all Python modules installed by upstream modules. +# In order to avoid this problem at least for builds using an absolute build directory (i.e., the +# :code:`--builddir` option of dunecontrol refers to an absolute path), the build system will +# place the virtualenv in a dedicated directory :code:`dune-python-env` inside that absolute +# build directory, where it will be found by all Dune modules. If you want to disable this +# behavior, set :code:`DUNE_PYTHON_EXTERNAL_VIRTUALENV_FOR_ABSOLUTE_BUILDDIR=0`. +# +# .. cmake_variable:: DUNE_PYTHON_ALLOW_GET_PIP +# +# The Dune build system will try to build a virtualenv with pip installed into it, +# but this can fail in some situations, in particular on Debian and Ubuntu distributions. +# In this case, you will se a warning message in the CMake output. If you are on Debian +# or Ubuntu, try installing the :code:`python3-venv` (for Python 3) and / or +# :code:`python-virtualenv` packages, delete your build directory and try configuring +# again. +# +# If that still does not help, set this variable to allow the Dune build system to download +# :code:`get-pip.py` from https://bootstrap.pypa.io/get-pip.py at configure time and execute +# it to install pip into the freshly set up virtual environment. While this should normally +# not be necessary anymore, see https://bugs.launchpad.net/debian/+source/python3.4/+bug/1290847 +# for more information about the underlying distribution bug. +# +include_guard(GLOBAL) + +# If the user has not specified an absolute, we look through the dependency tree of this module +# for a build directory that already contains a virtual environment. + +set(DUNE_PYTHON_VIRTUALENV_PATH "" CACHE PATH + "Location of Python virtualenv created by the Dune build system" + ) + +# pre-populate DUNE_PYTHON_EXTERNAL_VIRTUALENV_FOR_ABSOLUTE_BUILDDIR +set(DUNE_PYTHON_EXTERNAL_VIRTUALENV_FOR_ABSOLUTE_BUILDDIR ON CACHE BOOL + "Place Python virtualenv in top-level directory \"dune-python-env\" when using an absolute build directory" + ) + +if(DUNE_PYTHON_VIRTUALENV_PATH STREQUAL "") + foreach(mod ${ALL_DEPENDENCIES}) + if(IS_DIRECTORY ${${mod}_DIR}/dune-env) + set(DUNE_PYTHON_VIRTUALENV_PATH ${${mod}_DIR}/dune-env) + break() + endif() + endforeach() + + # if we haven't found it yet, check in the current build directory - this might be a reconfigure + if(DUNE_PYTHON_VIRTUALENV_PATH STREQUAL "") + if(IS_DIRECTORY ${CMAKE_BINARY_DIR}/dune-env) + set(DUNE_PYTHON_VIRTUALENV_PATH ${CMAKE_BINARY_DIR}/dune-env) + endif() + endif() +endif() + + +if(DUNE_PYTHON_VIRTUALENV_PATH STREQUAL "") + # We didn't find anything, so figure out the correct location for building the virtualenv + + if(DUNE_PYTHON_EXTERNAL_VIRTUALENV_FOR_ABSOLUTE_BUILDDIR AND DUNE_BUILD_DIRECTORY_ROOT_PATH) + # Use a dedicated directory not associated with any module + set(DUNE_PYTHON_VIRTUALENV_PATH "${DUNE_BUILD_DIRECTORY_ROOT_PATH}/dune-python-env") + else() + # Create the virtualenv inside our build directory + set(DUNE_PYTHON_VIRTUALENV_PATH ${CMAKE_BINARY_DIR}/dune-env) + endif() +endif() + +# If it does not yet exist, set it up! +if(NOT IS_DIRECTORY "${DUNE_PYTHON_VIRTUALENV_PATH}") + # Check for presence of the virtualenv/venv package + dune_python_find_package(PACKAGE virtualenv) + dune_python_find_package(PACKAGE venv) + if(NOT(DUNE_PYTHON_virtualenv_FOUND OR DUNE_PYTHON_venv_FOUND)) + message(FATAL_ERROR "One of the python packages virtualenv/venv is needed on the host system!") + endif() + + # Set some options depending on which virtualenv package is used + if(DUNE_PYTHON_venv_FOUND) + set(VIRTUALENV_PACKAGE_NAME venv) + set(NOPIP_OPTION --without-pip) + set(INTERPRETER_OPTION "") + endif() + if(DUNE_PYTHON_virtualenv_FOUND) + set(VIRTUALENV_PACKAGE_NAME virtualenv) + set(NOPIP_OPTION --no-pip) + set(INTERPRETER_OPTION -p "${Python3_EXECUTABLE}") + endif() + + if(("${VIRTUALENV_PACKAGE_NAME}" STREQUAL "venv") AND DUNE_PYTHON_SYSTEM_IS_VIRTUALENV) + message("-- WARNING: You are using a system interpreter which is a virtualenv and the venv package.") + message(" You might want to consider installing the virtualenv package if you experience inconveniences.") + endif() + + # Set up the env itself + message("-- Building a virtualenv in ${DUNE_PYTHON_VIRTUALENV_PATH}") + # First, try to build it with pip installed, but only if the user has not set DUNE_PYTHON_ALLOW_GET_PIP + if(NOT DUNE_PYTHON_ALLOW_GET_PIP) + dune_execute_process(COMMAND ${Python3_EXECUTABLE} + -m ${VIRTUALENV_PACKAGE_NAME} + ${INTERPRETER_OPTION} + "${DUNE_PYTHON_VIRTUALENV_PATH}" + RESULT_VARIABLE venv_install_result + ) + endif() + + if(NOT "${venv_install_result}" STREQUAL "0") + + if(NOT DUNE_PYTHON_ALLOW_GET_PIP) + # we attempted the default installation before, so issue a warning + message("-- WARNING: Failed to build a virtual env with pip installed, trying again without pip") + message("-- If you are using Debian or Ubuntu, consider installing python3-venv and / or python-virtualenv") + endif() + + # remove the remainder of a potential first attempt + file(REMOVE_RECURSE "${DUNE_PYTHON_VIRTUALENV_PATH}") + + # try to build the env without pip + dune_execute_process(COMMAND ${Python3_EXECUTABLE} + -m ${VIRTUALENV_PACKAGE_NAME} + ${INTERPRETER_OPTION} + ${NOPIP_OPTION} + "${DUNE_PYTHON_VIRTUALENV_PATH}" + ERROR_MESSAGE "Fatal error when setting up a virtualenv." + ) + endif() + +else() + message("-- Using existing virtualenv in ${DUNE_PYTHON_VIRTUALENV_PATH}") +endif() + +# Also store the virtual env interpreter directly +set(DUNE_PYTHON_VIRTUALENV_EXECUTABLE ${DUNE_PYTHON_VIRTUALENV_PATH}/bin/python) + +# Write a symlink for activation of the environment into all the +# build directories of the Dune stack +dune_execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${DUNE_PYTHON_VIRTUALENV_PATH}/bin/activate ${CMAKE_BINARY_DIR}/activate) + +# Also write a small wrapper script 'run-in-dune-env' into the build directory +# This is necessary to execute installed python scripts (the bin path of a virtualenv +# is *not* in the sys path, so a simple `python scriptname` does not work. +if(UNIX) + find_package(UnixCommands QUIET) + dune_module_path(MODULE dune-common + RESULT scriptdir + SCRIPT_DIR) + configure_file(${scriptdir}/run-in-dune-env.sh.in + ${CMAKE_BINARY_DIR}/run-in-dune-env + @ONLY) +else() + message(WARNING "Writing script 'run-in-dune-env' not implemented on your platform!") +endif() + +# The virtualenv might not contain pip due to the distribution bug described in +# https://bugs.launchpad.net/debian/+source/python3.4/+bug/1290847 +# We need to install pip, so if pip is missing, we offer to download and run the get-pip +# script. We ask users for permission to do so, or we allow them to set it up themselves. + +dune_python_find_package(PACKAGE pip + RESULT pippresent + INTERPRETER ${DUNE_PYTHON_VIRTUALENV_EXECUTABLE} + ) +if(NOT pippresent) + if(DUNE_PYTHON_ALLOW_GET_PIP) + # Fetch the get-pip.py script + message("-- Installing pip using https://bootstrap.pypa.io/get-pip.py...") + file(DOWNLOAD https://bootstrap.pypa.io/get-pip.py ${CMAKE_CURRENT_BINARY_DIR}/get-pip.py) + + # Verify that the script was successfully fetched + file(READ ${CMAKE_CURRENT_BINARY_DIR}/get-pip.py verify LIMIT 1) + if(NOT verify) + message(FATAL_ERROR " + Fetching get-pip.py failed. This often happens when CMake is built from source without SSL/TLS support. + Consider using a different cmake version or fall back to manually installing pip into the virtualenv. + ") + endif() + + # Execute the script + dune_execute_process(COMMAND ${DUNE_PYTHON_VIRTUALENV_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/get-pip.py + ERROR_MESSAGE "Fatal error when installing pip into the virtualenv." + ) + else() + message(FATAL_ERROR "dune-common set up a virtualenv, but needs pip to be installed into it. + You can either install it yourself manually activating the virtualenv with + the activate script in your build directory ${CMAKE_BINARY_DIR} or you set + the CMake variable DUNE_PYTHON_ALLOW_GET_PIP to allow Dune to use get-pip.py + from https://bootstrap.pypa.io/get-pip.py") + endif() +endif() diff --git a/cmake/modules/DuneSphinxCMakeDoc.cmake b/cmake/modules/DuneSphinxCMakeDoc.cmake new file mode 100644 index 0000000..18da843 --- /dev/null +++ b/cmake/modules/DuneSphinxCMakeDoc.cmake @@ -0,0 +1,184 @@ +# Module to generate CMake API documentation with Sphinx +# +# .. cmake_function:: dune_cmake_sphinx_doc +# +# .. cmake_brief:: +# +# Generate the documentation that you are just browsing!!! +# +# .. cmake_param:: BUILDTYPE +# :multi: +# +# Set the type of build that is requested. By default, "html" is chosen. +# The list of available build types: +# +# * `html` +# +# .. cmake_param:: SPHINX_CONF +# :single: +# :argname: conf +# +# A template for a conf file to be passed to :code:`sphinx-build`. +# The real configuration file will be generated through CMakes +# :code:`configure_file` mechanism. A reasonable default file is +# provided by dune-common. Only use this if you want to create +# custom documentation. +# +# .. cmake_param:: RST_SOURCES +# :multi: +# :argname: src +# +# A list of rst sources, that should be configured into the build tree +# (using :code:`configure_file`). If omitted, this defaults to +# :code:`index.rst` and :code:`contents.rst` with suitable content. +# Only use this if you want to create custom documentation. +# +# .. cmake_param:: MODULE_ONLY +# :option: +# +# Only document CMake functionality from the current Dune module. +# +# Generate a documentation for the CMake API. A set of cmake +# modules defined by the parameters and all functions and macros +# there in are automatically generated. The top level directory +# of the documentation is the current build directory (aka the +# directory that this function is called from) +# +# There are some assumptions on how the documentation in +# the CMake modules is written: +# +# * At the beginning of each CMake module there is a comment block that is written in restructured text. +# The first two characters of each line (the comment character +# and a blank) are ignored. Any resulting content of lines most form valid rst. +# * TODO document more +# +include_guard(GLOBAL) + +find_package(Sphinx) +# text for feature summary +set_package_properties("Sphinx" PROPERTIES + DESCRIPTION "Documentation generator" + URL "www.sphinx-doc.org" + PURPOSE "To generate the documentation from CMake and Python sources") + +function(dune_cmake_sphinx_doc) + # Only proceed if Sphinx was found on the system + if(NOT SPHINX_FOUND) + message("-- Skipping building CMake API documentation (Sphinx was not found!)") + return() + endif() + + # Only proceed if the python interpreter was found by cmake + if(NOT Python3_Interpreter_FOUND) + message("-- Skipping building CMake API documentation (Python interpreter was not found!)") + return() + endif() + + # Parse Arguments + set(OPTION MODULE_ONLY) + set(SINGLE SPHINX_CONF) + set(MULTI BUILDTYPE RST_SOURCES) + include(CMakeParseArguments) + cmake_parse_arguments(SPHINX_CMAKE "${OPTION}" "${SINGLE}" "${MULTI}" ${ARGN}) + if(SPHINX_CMAKE_UNPARSED_ARGUMENTS) + message(WARNING "Unparsed arguments in dune_cmake_sphinx_doc: This often indicates typos!") + endif() + + # Apply defaults + if(NOT SPHINX_CMAKE_BUILDTYPE) + set(SPHINX_CMAKE_BUILDTYPE html) + endif() + + # Extract the script directory from dune-common + dune_module_path(MODULE dune-common RESULT DUNE_SPHINX_EXT_PATH SCRIPT_DIR) + + # Find the configuration file template. + if(NOT SPHINX_CMAKE_SPHINX_CONF) + set(SPHINX_CMAKE_SPHINX_CONF ${DUNE_SPHINX_EXT_PATH}/conf.py.in) + endif() + + # Apply defaults to the rst sources that are not module dependent. + if(NOT SPHINX_CMAKE_RST_SOURCES) + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/contents.rst "") + set(SPHINX_CMAKE_RST_SOURCES ${DUNE_SPHINX_EXT_PATH}/index.rst.in ${CMAKE_CURRENT_BINARY_DIR}/contents.rst) + endif() + + # Write the conf.py, which sets up Sphinx into the build directory + configure_file(${SPHINX_CMAKE_SPHINX_CONF} ${CMAKE_CURRENT_BINARY_DIR}/conf.py) + + # Check whether we need to look through all dependencies + set(DOC_CMAKE_MODULES) + if(NOT SPHINX_CMAKE_MODULE_ONLY) + set(DOC_CMAKE_MODULES ${ALL_DEPENDENCIES}) + endif() + + # Now treat the module dependent rst sources. + set(CMAKE_DOC_DEPENDENCIES "") + set(${PROJECT_NAME}_PREFIX ${PROJECT_SOURCE_DIR}) + foreach(dep ${DOC_CMAKE_MODULES} ${PROJECT_NAME}) + # Look for a build system documentation exported by the module dep + set(RSTFILE "") + # check in the correct path for non-installed modules + if(EXISTS ${${dep}_PREFIX}/doc/buildsystem/${dep}.rst) + set(RSTFILE ${${dep}_PREFIX}/doc/buildsystem/${dep}.rst) + endif() + # now check for the correct path taking into account installed ones + if(EXISTS ${${dep}_PREFIX}/share/doc/${dep}/${dep}.rst) + set(RSTFILE ${${dep}_PREFIX}/share/doc/${dep}/${dep}.rst) + endif() + # Now process the file, if we have found one + if(RSTFILE) + # add it to index.rst then. + set(CMAKE_DOC_DEPENDENCIES "${CMAKE_DOC_DEPENDENCIES} ${dep}\n") + # ... and copy the rst file to the current build. + configure_file(${RSTFILE} ${CMAKE_CURRENT_BINARY_DIR}/${dep}.rst) + endif() + endforeach() + + # Write the non-module dependent rst source files from templates + foreach(rstin ${SPHINX_CMAKE_RST_SOURCES}) + get_filename_component(rst ${rstin} NAME_WE) + configure_file(${rstin} ${CMAKE_CURRENT_BINARY_DIR}/${rst}.rst) + endforeach() + + # Generate the list of modules by looking through the module paths + # of all dependencies for files matching *.cmake + set(SPHINX_DOC_MODULE_LIST) + set(${PROJECT_NAME}_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules) + foreach(dep ${DOC_CMAKE_MODULES} ${PROJECT_NAME}) + file(GLOB modules "${${dep}_MODULE_PATH}/*.cmake") + set(SPHINX_DOC_MODULE_LIST ${SPHINX_DOC_MODULE_LIST} ${modules}) + endforeach() + + # Initialize a variable that collects all dependencies of the documentation + set(DOC_DEPENDENCIES) + + # Generate the rst files for all cmake modules + foreach(module ${SPHINX_DOC_MODULE_LIST}) + get_filename_component(modname ${module} NAME) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/modules/${modname} + COMMAND ${Python3_EXECUTABLE} ${DUNE_SPHINX_EXT_PATH}/extract_cmake_data.py + --module=${module} + --builddir=${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${module} + COMMENT "Extracting CMake API documentation from ${modname}" + ) + set(DOC_DEPENDENCIES ${DOC_DEPENDENCIES} ${CMAKE_CURRENT_BINARY_DIR}/modules/${modname}) + endforeach() + + # Call Sphinx once for each requested build type + foreach(type ${SPHINX_CMAKE_BUILDTYPE}) + # Call the sphinx executable + add_custom_target(sphinx_${type} + COMMAND ${SPHINX_EXECUTABLE} + -b ${type} + -w ${PROJECT_BINARY_DIR}/SphinxError.log + -c ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/${type} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${DOC_DEPENDENCIES} + ) + add_dependencies(doc sphinx_${type}) + endforeach() +endfunction() diff --git a/cmake/modules/DuneSphinxDoc.cmake b/cmake/modules/DuneSphinxDoc.cmake new file mode 100644 index 0000000..b677b5e --- /dev/null +++ b/cmake/modules/DuneSphinxDoc.cmake @@ -0,0 +1,100 @@ +include_guard(GLOBAL) +find_package(Sphinx) +find_package(Python3 COMPONENTS Interpreter Development) + +function(dune_sphinx_doc) + # Only proceed if Sphinx was found on the system + if(NOT SPHINX_FOUND) + message("-- Skipping building Sphinx documentation (Sphinx was not found!)") + return() + endif() + + # Only proceed if the python interpreter was found by cmake + if(NOT Python3_Interpreter_FOUND) + message("-- Skipping building Sphinx documentation (Python interpreter was not found!)") + return() + endif() + + # Parse Arguments + include(CMakeParseArguments) + cmake_parse_arguments(SPHINX_DOC "" "CONF" "BUILDTYPE" ${ARGN}) + if(SPHINX_DOC_UNPARSED_ARGUMENTS) + message(WARNING "Unparsed arguments in dune_sphinx_doc") + endif() + + # copy conf.py into build directory + if(NOT SPHINX_DOC_CONF) + set(SPHINX_DOC_CONF conf.py) + endif() + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${SPHINX_DOC_CONF}.in) + configure_file(${SPHINX_DOC_CONF}.in ${CMAKE_CURRENT_BINARY_DIR}/conf.py) + elseif(EXISTS ${CMAKE_CURRENT_SOUREC_DIR}/${SPHINX_DOC_CONF}) + configure_file(${SPHINX_DOC_CONF} ${CMAKE_CURRENT_BINARY_DIR}/conf.py COPYONLY) + else() + message(SEND_ERROR "Sphinx configuration '${SPHINX_DOC_CONF}' not found.") + endif() + + # call Sphinx for each requested build type + if(NOT SPHINX_DOC_BUILDTYPE) + set(SPHINX_DOC_BUILDTYPE html) + endif() + foreach(type ${SPHINX_DOC_BUILDTYPE}) + add_custom_target(sphinx_doc_${type} + COMMAND ${SPHINX_EXECUTABLE} + -b ${type} + -w ${PROJECT_BINARY_DIR}/Sphinx-${type}.log + -c ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/${type} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/conf.py + ) + add_dependencies(sphinx_doc_${type} sphinx_files) + add_dependencies(doc sphinx_doc_${type}) + endforeach() +endfunction() + +function(add_sphinx_target base file) + find_program(JUPYTER jupyter) + get_filename_component(extension ${file} EXT) + set(SPHINXDIR ${PROJECT_BINARY_DIR}/doc/sphinx) + set(OUT ${SPHINXDIR}/${file}) + set(IN ${CMAKE_CURRENT_SOURCE_DIR}/${file}) + string(REGEX REPLACE "\\.[^.]*$" "" filebase ${file}) + set(TARGET ${base}.${file}) + add_custom_target(${TARGET} DEPENDS ${OUT}) + add_dependencies(sphinx_files ${TARGET}) + add_custom_command( + OUTPUT ${OUT} + DEPENDS ${IN} + COMMAND ${CMAKE_COMMAND} -E copy ${IN} ${OUT} + VERBATIM + ) + if ("${extension}" STREQUAL ".ipynb") + if (JUPYTER) + set(TARGET ${base}.${filebase}.rst) + set(OUTRST ${SPHINXDIR}/${filebase}.rst) + add_custom_target(${TARGET} DEPENDS ${OUTRST}) + add_dependencies(sphinx_files ${TARGET}) + add_custom_command( + OUTPUT ${OUTRST} + DEPENDS ${OUT} + COMMAND jupyter nbconvert --ExecutePreprocessor.timeout=-1 --execute --allow-errors --to="rst" ${OUT} --output ${filebase}.rst + COMMAND sed -i "s/raw:: latex/math::/g" ${OUTRST} + WORKING_DIRECTORY ${SPHINXDIR} + VERBATIM + ) + endif() + endif() +endfunction() + +function(add_sphinx_files base) + foreach(file ${ARGN}) + add_sphinx_target(${base} ${file}) + endforeach() +endfunction() +function(add_sphinx_targets base) + add_custom_target(sphinx_files) + add_sphinx_files(${base} ${ARGN}) + dune_sphinx_doc() +endfunction() diff --git a/cmake/modules/DuneStreams.cmake b/cmake/modules/DuneStreams.cmake new file mode 100644 index 0000000..90dd552 --- /dev/null +++ b/cmake/modules/DuneStreams.cmake @@ -0,0 +1,30 @@ +# This Module configures the DUNE debug streams. +# +# .. cmake_variable:: MINIMAL_DEBUG_LEVEL +# +# This variable configures the Dune debug streams. +# Standard debug streams with level below :code:`MINIMAL_DEBUG_LEVEL` will +# collapse to doing nothing if output is requested. Possible values are +# :code:`vverb`, :code:`verb`, :code:`info`, :code:`warn` and :code:`grave`. +# Defaults to :code:`warn`. +# +include_guard(GLOBAL) + +macro(dune_set_minimal_debug_level) +set(MINIMAL_DEBUG_LEVEL ON CACHE STRING "set the MINIMAL_DEBUG_LEVEL. Standard debug streams with level below MINIMAL_DEBUG_LEVEL will collapse to doing nothing if output is requested. (default=warn)") +set_property(CACHE MINIMAL_DEBUG_LEVEL PROPERTY STRINGS + "grave" "warn" "info" "verb" "vverb") +if(MINIMAL_DEBUG_LEVEL STREQUAL "grave") + set(DUNE_MINIMAL_DEBUG_LEVEL 5) +elseif(MINIMAL_DEBUG_LEVEL STREQUAL "info") + set(DUNE_MINIMAL_DEBUG_LEVEL 3) +elseif(MINIMAL_DEBUG_LEVEL STREQUAL "verb") + set(DUNE_MINIMAL_DEBUG_LEVEL 2) +elseif(MINIMAL_DEBUG_LEVEL STREQUAL "vverb") + set(DUNE_MINIMAL_DEBUG_LEVEL 1) +# default to warn +else() + set(DUNE_MINIMAL_DEBUG_LEVEL 4) +endif() +message(STATUS "Set Minimal Debug Level to ${DUNE_MINIMAL_DEBUG_LEVEL}") +endmacro(dune_set_minimal_debug_level) diff --git a/cmake/modules/DuneSymlinkOrCopy.cmake b/cmake/modules/DuneSymlinkOrCopy.cmake new file mode 100644 index 0000000..38caff4 --- /dev/null +++ b/cmake/modules/DuneSymlinkOrCopy.cmake @@ -0,0 +1,210 @@ +# This module provides convenience macros to provide files from the source tree in the build tree. +# +# It provides the following macros: +# +# dune_add_copy_command(filename) +# +# This macro adds a file-copy command. +# The file_name is the name of a file that exists +# in the source tree. This file will be copied +# to the build tree when executing this command. +# Notice that this does not create a top-level +# target. In order to do this you have to additionally +# call add_custom_target(...) with dependency +# on the file. +# +# dune_add_copy_target(target_name file_name) +# +# This macro adds a file-copy target under given target_name. +# The file_name is the name of a file that exists +# in the source tree. This file will be copied +# to the build tree. +# +# dune_add_copy_dependency(target file_name) +# +# This macro adds a copy-dependency to a target +# The file_name is the name of a file that exists +# in the source tree. This file will be copied +# to the build tree. +# +# +# .. cmake_function:: dune_add_copy_command +# +# .. cmake_param:: filename +# :positional: +# :single: +# :required: +# +# TODO DOC ME! +# +# .. cmake_function:: dune_add_copy_target +# +# .. cmake_param:: target_name +# :positional: +# :single: +# :required: +# +# .. cmake_param:: filename +# :positional: +# :single: +# :required: +# +# TODO DOC ME! +# +# .. cmake_function:: dune_add_copy_dependency +# +# .. cmake_param:: target +# :positional: +# :single: +# :required: +# +# .. cmake_param:: filename +# :positional: +# :single: +# :required: +# +# TODO DOC ME! +# +# .. cmake_function:: dune_symlink_to_source_tree +# +# .. cmake_param:: NAME +# :single: +# +# The name of the symlink, defaults to :code:`src_dir`. +# +# This function will place a symlink into every subdirectory +# of the build tree, that allows to jump to the corresponding +# source directory. Call this from your top-level :code:`CMakeLists.txt` +# to enable it for a given module. To enable it for all modules, +# set the variable :ref:`DUNE_SYMLINK_TO_SOURCE_TREE` instead. +# If used on Windows systems, a warning is issued. +# +# .. cmake_variable:: DUNE_SYMLINK_TO_SOURCE_TREE +# +# If this variable is set to TRUE, the functionality of +# :ref:`dune_symlink_to_source_tree` is enabled in all modules. +# This will place symlinks to the corresponding source directory +# in every subdirectory of the build directory. +# +# .. cmake_variable:: DUNE_SYMLINK_RELATIVE_LINKS +# +# If this variable is set to TRUE, the buildsystem will create relative +# links instead of absolute ones. +# +# .. cmake_function:: dune_symlink_to_source_files +# +# .. cmake_param:: FILES +# :multi: +# :required: +# +# The list of files to symlink +# +# .. cmake_param:: DESTINATION +# :multi: +# :required: +# +# Relative path of the target directory +# +# Create symlinks in the build tree that +# point to particular files in the source directory. This is usually +# used for grid and ini files and the like. On Windows systems, +# a warning is issued and copying is used as a fallback to +# symlinking. +# +include_guard(GLOBAL) + +macro(dune_add_copy_command file_name) + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${file_name}" + COMMAND ${CMAKE_COMMAND} + ARGS -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${file_name}" "${file_name}" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${file_name}" + ) +endmacro(dune_add_copy_command file_name) + +macro(dune_add_copy_target target_name file_name) + dune_add_copy_command(${file_name}) + add_custom_target("${target_name}" ALL DEPENDS "${file_name}") +endmacro(dune_add_copy_target target_name file_name) + +macro(dune_add_copy_dependency target file_name) + message(STATUS "Adding copy-to-build-dir dependency for ${file_name} to target ${target}") + dune_add_copy_target("${target}_copy_${file_name}" "${file_name}") + add_dependencies(${target} "${target}_copy_${file_name}") +endmacro(dune_add_copy_dependency) + +function(dune_symlink_to_source_tree) + # if source and binary dir are equal then the symlink will create serious problems + if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) + return() + endif() + + # parse arguments + include(CMakeParseArguments) + cmake_parse_arguments(ARG "" "NAME" "" ${ARGN}) + if(NOT ARG_NAME) + set(ARG_NAME "src_dir") + endif() + + # check for Windows to issue a warning + if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + if(NOT DEFINED DUNE_WINDOWS_SYMLINK_WARNING) + message(WARNING "Your module wanted to create symlinks, but you cannot do that on your platform.") + set(DUNE_WINDOWS_SYMLINK_WARNING) + endif() + else() + # get a list of all files in the current source directory and below. + file(GLOB_RECURSE files RELATIVE ${PROJECT_SOURCE_DIR} "*CMakeLists.txt") + + # iterate over all files, extract the directory name and write a symlink in the corresponding build directory + foreach(f ${files}) + get_filename_component(dir ${f} DIRECTORY) + set(_target "${PROJECT_SOURCE_DIR}/${dir}") + if(DUNE_SYMLINK_RELATIVE_LINKS) + file(RELATIVE_PATH _target "${PROJECT_BINARY_DIR}/${dir}" "${_target}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} "-E" "create_symlink" "${_target}" "${PROJECT_BINARY_DIR}/${dir}/${ARG_NAME}") + endforeach() + endif() +endfunction(dune_symlink_to_source_tree) + +function(dune_symlink_to_source_files) + + # if source and binary dir are equal then the symlink will create serious problems + if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) + return() + endif() + + # parse arguments + include(CMakeParseArguments) + cmake_parse_arguments(ARG "" "DESTINATION" "FILES" ${ARGN}) + if(ARG_UNPARSED_ARGUMENTS) + message(WARNING "You are using dune_symlink_to_source_files without named arguments (or have typos in your named arguments)!") + endif() + + # create symlinks for all given files + foreach(f ${ARG_FILES}) + # check whether there is an explicitly given destination + if(ARG_DESTINATION) + set(destination "${ARG_DESTINATION}/") + else() + set(destination "") + endif() + # check for Windows to issue a warning + if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + if(NOT DEFINED DUNE_WINDOWS_SYMLINK_WARNING) + message(WARNING "Your module wanted to create symlinks, but you cannot do that on your platform.") + set(DUNE_WINDOWS_SYMLINK_WARNING) + endif() + # create a copy + execute_process(COMMAND ${CMAKE_COMMAND} "-E" "copy" "${CMAKE_CURRENT_SOURCE_DIR}/${f}" "${CMAKE_CURRENT_BINARY_DIR}/${destination}${f}") + else() + # create symlink + set(_target "${CMAKE_CURRENT_SOURCE_DIR}/${f}") + if(DUNE_SYMLINK_RELATIVE_LINKS) + file(RELATIVE_PATH _target "${CMAKE_CURRENT_BINARY_DIR}/${destination}" "${_target}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} "-E" "create_symlink" "${_target}" "${CMAKE_CURRENT_BINARY_DIR}/${destination}${f}") + endif() + endforeach() +endfunction(dune_symlink_to_source_files) diff --git a/cmake/modules/DuneTestMacros.cmake b/cmake/modules/DuneTestMacros.cmake new file mode 100644 index 0000000..9f11ecc --- /dev/null +++ b/cmake/modules/DuneTestMacros.cmake @@ -0,0 +1,441 @@ +# Module that provides tools for testing the Dune way. +# +# Note that "the Dune way" of doing this has changed after +# the 2.4 release. See the build system documentation for details. +# +# .. cmake_function:: dune_declare_test_label +# +# .. cmake_brief:: +# +# Declare labels for :ref:`dune_add_test`. +# +# .. cmake_param:: LABELS +# :multi: +# +# The names of labels to declare. Label names must be nonempty and +# consist only of alphanumeric characters plus :code:`-` and :code:`_` +# to make sure it is easy to construct regular expressions from them for +# :code:`ctest -L ${label_regex}`. +# +# Labels need to be declared to ensure that the target +# :code:`build_${label}_tests` exists. They will normally be declared +# on-demand by :ref:`dune_add_test`. But sometimes it is useful to be able to +# run :code:`make build_${label}_tests` whether or not any tests with that +# label exists in a module. For these cases :ref:`dune_declare_test_label` can +# be called explicitly. +# +# The label :code:`quick` is always predeclared. +# +# .. cmake_function:: dune_add_test +# +# .. cmake_brief:: +# +# Adds a test to the Dune testing suite! +# +# .. cmake_param:: NAME +# :single: +# +# The name of the test that should be added. If an executable +# is also added (by specifying SOURCES), the executable is also +# named accordingly. If omitted, the name will be deduced from +# the (single) sources parameter or from the given target. Note +# that this requires you to take care, that you only use a target +# or source file for but one such test. +# +# .. cmake_param:: SOURCES +# :multi: +# +# The source files that this test depends on. These are the +# sources that will be passed to :ref:`add_executable`. +# +# You *must* specify either :code:`SOURCES` or :code:`TARGET`. +# +# .. cmake_param:: TARGET +# :single: +# +# An executable target which should be used for the test. Use +# this option over the :code:`SOURCES` parameter if you want to +# reuse already added targets. +# +# You *must* specify either :code:`SOURCES` or :code:`TARGET`. +# +# .. cmake_param:: COMPILE_DEFINITIONS +# :multi: +# :argname: def +# +# A set of compile definitions to add to the target. +# Only definitions beyond the application of :ref:`add_dune_all_flags` +# have to be stated. +# This is only used, if :code:`dune_add_test` adds the executable itself. +# +# .. cmake_param:: COMPILE_FLAGS +# :multi: +# :argname: flag +# +# A set of non-definition compile flags to add to the target. +# Only flags beyond the application of :ref:`add_dune_all_flags` +# have to be stated. +# This is only used, if :code:`dune_add_test` adds the executable itself. +# +# .. cmake_param:: LINK_LIBRARIES +# :multi: +# :argname: lib +# +# A list of libraries to link the target to. +# Only libraries beyond the application of :ref:`add_dune_all_flags` +# have to be stated. +# This is only used, if :code:`dune_add_test` adds the executable itself. +# +# .. cmake_param:: EXPECT_COMPILE_FAIL +# :option: +# +# If given, the test is expected to not compile successfully! +# +# .. cmake_param:: EXPECT_FAIL +# :option: +# +# If given, this test is expected to compile, but fail to run. +# +# .. cmake_param:: CMD_ARGS +# :multi: +# :argname: arg +# +# Command line arguments that should be passed to this test. +# +# .. cmake_param:: MPI_RANKS +# :multi: +# :argname: ranks +# +# The numbers of cores that this test should be executed with. +# Note that one test (in the ctest sense) is created for each number +# given here. Any number exceeding the user-specified processor maximum +# :ref:`DUNE_MAX_TEST_CORES` will be ignored. Tests with a +# processor number :code:`n` higher than one will have the suffix +# :code:`-mpi-n` appended to their name. You need to specify the +# TIMEOUT option when specifying the MPI_RANKS option. +# +# .. cmake_param:: CMAKE_GUARD +# :multi: +# :argname: condition +# +# A number of conditions that CMake should evaluate before adding this +# test. If one of the conditions fails, the test should be shown +# as skipped in the test summary. Use this feature instead of guarding +# the call to :code:`dune_add_test` with an :code:`if` clause. +# +# The passed condition can be a complex expression like +# `( A OR B ) AND ( C OR D )`. Mind the spaces around the parentheses. +# +# Example: Write CMAKE_GUARD dune-foo_FOUND if you want your test to only +# build and run when the dune-foo module is present. +# +# .. cmake_param:: COMMAND +# :multi: +# :argname: cmd +# +# You may specify the COMMAND option to give the exact command line to be +# executed when running the test. This defaults to the name of the executable +# added by dune_add_test for this test or the name of the executable of the given TARGET. +# Note that if you specify both CMD_ARGS +# and COMMAND, the given CMD_ARGS will be put behind your COMMAND. If you use +# this in combination with the MPI_RANKS parameter, the call to mpi will still be +# wrapped around the given commands. +# +# .. cmake_param:: COMPILE_ONLY +# :option: +# +# Set if the given test should only be compiled during :code:`make build_tests`, +# but not run during :code:`make test`. This is useful if you compile the same +# executable twice, but with different compile flags, where you want to assure that +# it compiles with both sets of flags, but you already know they will produce the +# same result. +# +# .. cmake_param:: TIMEOUT +# :single: +# +# If set, the test will time out after the given number of seconds. This supersedes +# any timeout setting in ctest (see `cmake --help-property TIMEOUT`). If you +# specify the MPI_RANKS option, you need to specify a TIMEOUT. +# +# .. cmake_param:: LABELS +# :multi: +# +# A list of labels to add to the test. This has two effects: it sets +# the LABELS property on the test so :code:`ctest -L ${label_regex}` can +# be used to run all tests with certain labels. It also adds any +# targets created as dependencies to a custom target, so you can build +# all tests with a particular label by doing :code:`make +# build_${label}_tests` without having to build all the other tests as +# well. +# +# The :code:`build_${label}_tests` targets are created on-demand the +# first time a test with that label is added. In some situations it can +# depend on the values of cmake cache variables whether a test is added, +# and then it can happen that the :code:`build_${target}_tests` target +# exists only sometimes. If your workflow relies on the existance of +# these targets, even if building them just returns successfully without +# doing anything, you can ensure they exist by calling +# :ref:`dune_declare_test_label` unconditionally. The label +# :code:`quick` is always predeclared in this way. +# +# The label names must be non-empty, and must only contain alphanumeric +# characters other than :code:`-` or :code:`_`. This restriction is in +# place to make it easy to construct regular expressions from the label +# names for :code:`ctest -L ${label_regex}`. +# +# This function defines the Dune way of adding a test to the testing suite. +# You may either add the executable yourself through :ref:`add_executable` +# and pass it to the :code:`TARGET` option, or you may rely on :ref:`dune_add_test` +# to do so. +# +# .. cmake_variable:: DUNE_REENABLE_ADD_TEST +# +# You may set this variable to True either through your opts file or in your module +# (before the call to :code:`include(DuneMacros)`) to suppress the error that is thrown if +# :code:`add_test` is used. You should only do that if you have proper reason to do so. +# +# .. cmake_variable:: DUNE_MAX_TEST_CORES +# +# You may set this variable to give an upperbound to the number of processors, that +# a single test may use. Defaults to 2, when MPI is found and to 1 otherwise. +# +# .. cmake_variable:: DUNE_BUILD_TESTS_ON_MAKE_ALL +# +# You may set this variable through your opts file or on a per module level (in the toplevel +# :code:`CMakeLists.txt` before :code:`include(DuneMacros)`) to have the Dune build system +# build all tests during `make all`. Note, that this may take quite some time for some modules. +# If not in use, you have to build tests through the target :code:`build_tests`. +# +include_guard(GLOBAL) + +# enable the testing suite on the CMake side. +enable_testing() +include(CTest) + +# Introduce a target that triggers the building of all tests +add_custom_target(build_tests) + +function(dune_declare_test_label) + include(CMakeParseArguments) + set(OPTIONS) + set(SINGLEARGS) + set(MULTIARGS LABELS) + cmake_parse_arguments(arg "${OPTIONS}" "${SINGLEARGS}" "${MULTIARGS}" ${ARGN}) + + if( (DEFINED arg_UNPARSED_ARGUMENTS) AND NOT ( arg_UNPARSED_ARGUMENTS STREQUAL "" ) ) + message(FATAL_ERROR "Unhandled extra arguments given to dune_declare_test_label(): " + "<${arg_UNPARSED_ARGUMENTS}>") + endif() + + foreach(label IN LISTS arg_LABELS) + # Make sure the label is not empty, and does not contain any funny + # characters, in particular regex characters + if(NOT (label MATCHES "[-_0-9a-zA-Z]+")) + message(FATAL_ERROR "Refusing to add label \"${label}\" since it is " + "empty or contains funny characters (characters other than " + "alphanumeric ones and \"-\" or \"_\"; the intent of this restriction " + "is to make construction of the argument to \"ctest -L\" easier") + endif() + set(target "build_${label}_tests") + if(NOT TARGET "${target}") + add_custom_target("${target}") + endif() + endforeach() +endfunction(dune_declare_test_label) + +# predefine "quick" test label so build_quick_tests can be built +# unconditionally +dune_declare_test_label(LABELS quick) + +# Set the default on the variable DUNE_MAX_TEST_CORES +if(NOT DUNE_MAX_TEST_CORES) + set(DUNE_MAX_TEST_CORES 2) +endif() + +function(dune_add_test) + include(CMakeParseArguments) + set(OPTIONS EXPECT_COMPILE_FAIL EXPECT_FAIL SKIP_ON_77 COMPILE_ONLY) + set(SINGLEARGS NAME TARGET TIMEOUT) + set(MULTIARGS SOURCES COMPILE_DEFINITIONS COMPILE_FLAGS LINK_LIBRARIES CMD_ARGS MPI_RANKS COMMAND CMAKE_GUARD LABELS) + cmake_parse_arguments(ADDTEST "${OPTIONS}" "${SINGLEARGS}" "${MULTIARGS}" ${ARGN}) + + # Check whether the parser produced any errors + if(ADDTEST_UNPARSED_ARGUMENTS) + message(WARNING "Unrecognized arguments ('${ADDTEST_UNPARSED_ARGUMENTS}') for dune_add_test!") + endif() + + # Check input for validity and apply defaults + if(NOT ADDTEST_SOURCES AND NOT ADDTEST_TARGET) + message(FATAL_ERROR "You need to specify either the SOURCES or the TARGET option for dune_add_test!") + endif() + if(ADDTEST_SOURCES AND ADDTEST_TARGET) + message(FATAL_ERROR "You cannot specify both SOURCES and TARGET for dune_add_test") + endif() + if(NOT ADDTEST_NAME) + # try deducing the test name from the executable name + if(ADDTEST_TARGET) + set(ADDTEST_NAME ${ADDTEST_TARGET}) + endif() + # try deducing the test name form the source name + if(ADDTEST_SOURCES) + # deducing a name is only possible with a single source argument + list(LENGTH ADDTEST_SOURCES len) + if(NOT len STREQUAL "1") + message(FATAL_ERROR "Cannot deduce test name from multiple sources!") + endif() + # strip file extension + get_filename_component(ADDTEST_NAME ${ADDTEST_SOURCES} NAME_WE) + endif() + endif() + if(NOT ADDTEST_COMMAND) + if(ADDTEST_TARGET) + set(ADDTEST_COMMAND ${ADDTEST_TARGET}) + else() + set(ADDTEST_COMMAND ${ADDTEST_NAME}) + endif() + endif() + if(ADDTEST_MPI_RANKS AND (NOT ADDTEST_TIMEOUT)) + message(FATAL_ERROR "dune_add_test: You need to specify the TIMEOUT parameter if using the MPI_RANKS parameter.") + endif() + if(NOT ADDTEST_MPI_RANKS) + set(ADDTEST_MPI_RANKS 1) + endif() + if(NOT ADDTEST_TIMEOUT) + set(ADDTEST_TIMEOUT 300) + endif() + foreach(num ${ADDTEST_MPI_RANKS}) + if(NOT "${num}" MATCHES "[1-9][0-9]*") + message(FATAL_ERROR "${num} was given to the MPI_RANKS arugment of dune_add_test, but it does not seem like a correct processor number") + endif() + endforeach() + if(ADDTEST_SKIP_ON_77) + message(WARNING "The SKIP_ON_77 option for dune_add_test is obsolete, it is now enabled by default.") + endif() + + # Discard all parallel tests if MPI was not found + if(NOT MPI_FOUND) + set(DUNE_MAX_TEST_CORES 1) + endif() + + # Find out whether this test should be a dummy + set(SHOULD_SKIP_TEST FALSE) + set(FAILED_CONDITION_PRINTING "") + foreach(condition ${ADDTEST_CMAKE_GUARD}) + separate_arguments(condition) + if(NOT (${condition})) + set(SHOULD_SKIP_TEST TRUE) + set(FAILED_CONDITION_PRINTING "${FAILED_CONDITION_PRINTING}std::cout << \" ${condition}\" << std::endl;\n") + endif() + endforeach() + + # If we do nothing, switch the sources for a dummy source + if(SHOULD_SKIP_TEST) + dune_module_path(MODULE dune-common RESULT scriptdir SCRIPT_DIR) + set(ADDTEST_TARGET) + set(dummymain ${CMAKE_CURRENT_BINARY_DIR}/main77_${ADDTEST_NAME}.cc) + configure_file(${scriptdir}/main77.cc.in ${dummymain}) + set(ADDTEST_SOURCES ${dummymain}) + endif() + + # Add the executable if it is not already present + if(ADDTEST_SOURCES) + add_executable(${ADDTEST_NAME} ${ADDTEST_SOURCES}) + # add all flags to the target! + add_dune_all_flags(${ADDTEST_NAME}) + # This is just a placeholder + target_compile_definitions(${ADDTEST_NAME} PUBLIC ${ADDTEST_COMPILE_DEFINITIONS}) + target_compile_options(${ADDTEST_NAME} PUBLIC ${ADDTEST_COMPILE_FLAGS}) + target_link_libraries(${ADDTEST_NAME} PUBLIC ${ADDTEST_LINK_LIBRARIES}) + set(ADDTEST_TARGET ${ADDTEST_NAME}) + endif() + + # Make sure to exclude the target from all, even when it is user-provided + if(DUNE_BUILD_TESTS_ON_MAKE_ALL AND (NOT ADDTEST_EXPECT_COMPILE_FAIL)) + set_property(TARGET ${ADDTEST_TARGET} PROPERTY EXCLUDE_FROM_ALL 0) + else() + set_property(TARGET ${ADDTEST_TARGET} PROPERTY EXCLUDE_FROM_ALL 1) + endif() + + # make sure each label exists and its name is acceptable + dune_declare_test_label(LABELS ${ADDTEST_LABELS}) + + # Have build_tests and build_${label}_tests depend on the given target in + # order to trigger the build correctly + if(NOT ADDTEST_EXPECT_COMPILE_FAIL) + add_dependencies(build_tests ${ADDTEST_TARGET}) + foreach(label IN LISTS ADDTEST_LABELS) + add_dependencies(build_${label}_tests ${ADDTEST_TARGET}) + endforeach() + endif() + + # Process the EXPECT_COMPILE_FAIL option + if(ADDTEST_EXPECT_COMPILE_FAIL) + set(ADDTEST_COMMAND "${CMAKE_COMMAND}") + set(ADDTEST_CMD_ARGS --build . --target ${ADDTEST_TARGET} --config "$") + endif() + + # Add one test for each specified processor number + foreach(procnum ${ADDTEST_MPI_RANKS}) + if((NOT "${procnum}" GREATER "${DUNE_MAX_TEST_CORES}") AND (NOT ADDTEST_COMPILE_ONLY)) + set(ACTUAL_NAME ${ADDTEST_NAME}) + set(ACTUAL_CMD_ARGS ${ADDTEST_CMD_ARGS}) + if(TARGET "${ADDTEST_COMMAND}") + # if the target name is specified as command, expand to full path using the TARGET_FILE generator expression + set(ACTUAL_TESTCOMMAND "$") + else() + set(ACTUAL_TESTCOMMAND "${ADDTEST_COMMAND}") + endif() + + # modify test name and command for parallel tests + if(NOT ${procnum} STREQUAL "1") + set(ACTUAL_NAME "${ACTUAL_NAME}-mpi-${procnum}") + set(ACTUAL_CMD_ARGS ${MPIEXEC_PREFLAGS} ${MPIEXEC_NUMPROC_FLAG} ${procnum} "${ACTUAL_TESTCOMMAND}" ${MPIEXEC_POSTFLAGS} ${ACTUAL_CMD_ARGS}) + set(ACTUAL_TESTCOMMAND "${MPIEXEC}") + endif() + + # if this is a skipped test because a guard was false, overwrite the command + if(SHOULD_SKIP_TEST) + set(ACTUAL_TESTCOMMAND "$") + set(ACTUAL_CMD_ARGS) + endif() + + # Now add the actual test + _add_test(NAME ${ACTUAL_NAME} + COMMAND "${ACTUAL_TESTCOMMAND}" ${ACTUAL_CMD_ARGS} + ) + + # Make the test depend on the existence of the target to trigger "Not Run" response + if(NOT ADDTEST_EXPECT_COMPILE_FAIL) + set_tests_properties(${ACTUAL_NAME} PROPERTIES REQUIRED_FILES $) + endif() + # Define the number of processors (ctest will coordinate this with the -j option) + set_tests_properties(${ACTUAL_NAME} PROPERTIES PROCESSORS ${procnum}) + # Apply the timeout (which was defaulted to 5 minutes if not specified) + set_tests_properties(${ACTUAL_NAME} PROPERTIES TIMEOUT ${ADDTEST_TIMEOUT}) + # Process the EXPECT_FAIL option + if(ADDTEST_EXPECT_COMPILE_FAIL OR ADDTEST_EXPECT_FAIL) + set_tests_properties(${ACTUAL_NAME} PROPERTIES WILL_FAIL true) + endif() + # When using ninja, we must call the build command from ${PROJECT_BINARY_DIR} + if(ADDTEST_EXPECT_COMPILE_FAIL) + set_tests_properties(${ACTUAL_NAME} PROPERTIES WORKING_DIRECTORY "${PROJECT_BINARY_DIR}") + endif() + # Skip the test if the return code is 77! + set_tests_properties(${ACTUAL_NAME} PROPERTIES SKIP_RETURN_CODE 77) + # Set the labels on the test + set_tests_properties(${ACTUAL_NAME} PROPERTIES LABELS "${ADDTEST_LABELS}") + endif() + endforeach() +endfunction() + +macro(add_directory_test_target) + message(FATAL_ERROR "The function add_directory_test_target has been removed alongside all testing magic in dune-common. Check dune_add_test for the new way!") +endmacro() + +macro(add_test) + if(NOT DUNE_REENABLE_ADD_TEST) + message(SEND_ERROR "Please use dune_add_test instead of add_test! If you need add_test in a downstream project, set the variable DUNE_REENABLE_ADD_TEST to True in that project to suppress this error.") + else() + _add_test(${ARGN}) + endif() +endmacro() diff --git a/cmake/modules/FindGMP.cmake b/cmake/modules/FindGMP.cmake new file mode 100644 index 0000000..d6e84af --- /dev/null +++ b/cmake/modules/FindGMP.cmake @@ -0,0 +1,96 @@ +#[=======================================================================[.rst: +FindGMP +------- + +Find the GNU MULTI-Precision Bignum (GMP) library +and the corresponding C++ bindings GMPxx. + +This module searches for both libraries and only considers the package +found if both can be located. It then defines separate targets for the C +and the C++ library. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported targets, if found: + +``GMP::gmp`` + Library target of the C library. +``GMP::gmpxx`` + Library target of the C++ library, which also links to the C library. + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``GMP_FOUND`` + True if the GMP library, the GMPxx headers and + the GMPxx library were found. + +Cache Variables +^^^^^^^^^^^^^^^ + +You may set the following variables to modify the behaviour of +this module: + +``GMP_INCLUDE_DIR`` + The directory containing ``gmp.h``. +``GMP_LIB`` + The path to the gmp library. +``GMPXX_INCLUDE_DIR`` + The directory containing ``gmpxx.h``. +``GMPXX_LIB`` + The path to the gmpxx library. + +#]=======================================================================] + +# Add a feature summary for this package +include(FeatureSummary) +set_package_properties(GMP PROPERTIES + DESCRIPTION "GNU multi-precision library" + URL "https://gmplib.org" +) + +# Try finding the package with pkg-config +find_package(PkgConfig QUIET) +pkg_check_modules(PKG QUIET gmp gmpxx) + +# Try to locate the libraries and their headers, using pkg-config hints +find_path(GMP_INCLUDE_DIR gmp.h HINTS ${PKG_gmp_INCLUDEDIR}) +find_library(GMP_LIB gmp HINTS ${PKG_gmp_LIBDIR}) + +find_path(GMPXX_INCLUDE_DIR gmpxx.h HINTS ${PKG_gmpxx_INCLUDEDIR}) +find_library(GMPXX_LIB gmpxx HINTS ${PKG_gmpxx_LIBDIR}) + +# Remove these variables from cache inspector +mark_as_advanced(GMP_INCLUDE_DIR GMP_LIB GMPXX_INCLUDE_DIR GMPXX_LIB) + +# Report if package was found +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GMP + DEFAULT_MSG + GMPXX_LIB GMPXX_INCLUDE_DIR GMP_INCLUDE_DIR GMP_LIB +) + +# Set targets +if(GMP_FOUND) + # C library + if(NOT TARGET GMP::gmp) + add_library(GMP::gmp UNKNOWN IMPORTED) + set_target_properties(GMP::gmp PROPERTIES + IMPORTED_LOCATION ${GMP_LIB} + INTERFACE_INCLUDE_DIRECTORIES ${GMP_INCLUDE_DIR} + ) + endif() + + # C++ library, which requires a link to the C library + if(NOT TARGET GMP::gmpxx) + add_library(GMP::gmpxx UNKNOWN IMPORTED) + set_target_properties(GMP::gmpxx PROPERTIES + IMPORTED_LOCATION ${GMPXX_LIB} + INTERFACE_INCLUDE_DIRECTORIES ${GMPXX_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES GMP::gmp + ) + endif() +endif() diff --git a/cmake/modules/FindInkscape.cmake b/cmake/modules/FindInkscape.cmake new file mode 100644 index 0000000..c0116c8 --- /dev/null +++ b/cmake/modules/FindInkscape.cmake @@ -0,0 +1,31 @@ +# .. cmake_module:: +# +# Module that checks for inkscape +# +# Sets the following variables +# +# :code:`INKSCAPE_FOUND` +# Whether inkscape was found +# +# :code:`INKSCAPE` +# Path to inkscape to generate .png's form .svg's +# + +find_program(INKSCAPE inkscape DOC "Path to inkscape to generate png files from svg files") +find_program(CONVERT convert DOC "Path to convert program") +if(INKSCAPE) + set(INKSCAPE_FOUND True) + # check for inkscape >= 1.0 + execute_process(COMMAND ${INKSCAPE} -z -e OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE INKSCAPE_RETURNED_ONE) + # if error (i.e. 1) was returned we have new inkscape version (>=1.0) + if(INKSCAPE_RETURNED_ONE) + set(INKSCAPE_NEW_VERSION True) + endif() + +endif(INKSCAPE) + +# text for feature summary +set_package_properties("Inkscape" PROPERTIES + DESCRIPTION "converts SVG images" + URL "www.inkscape.org" + PURPOSE "To generate the documentation with LaTeX") diff --git a/cmake/modules/FindLatexMk.cmake b/cmake/modules/FindLatexMk.cmake new file mode 100644 index 0000000..531e1e3 --- /dev/null +++ b/cmake/modules/FindLatexMk.cmake @@ -0,0 +1,75 @@ +# Find module for LatexMk +# +# This module honors the following input variables: +# LATEXMK_ROOT +# Directory to take the latexmk executable from +# LATEXMK_DIR +# Alternative variable instead of LATEXMK_ROOT +# +# The module checks for the presence of the LatexMk executable +# and sets the following variables: +# +# LATEXMK_FOUND +# Whether the latexmk executable was found on the system +# LATEXMK_EXECUTABLE +# The full path of the found latexmk executable +# LATEXMK_VERSION_STRING +# A well readable string of the latexmk version. +# LATEXMK_VERSION_MAJOR +# The major version of the latexmk executable +# LATEXMK_VERSION_MINOR +# The minor version of the latexmk executable +# +# Copyright (c) 2017, Dominic Kempf, Steffen Müthing +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# * Neither the name of the Universität Heidelberg nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# Find the actual program +find_program(LATEXMK_EXECUTABLE + latexmk + PATHS ${LATEXMK_ROOT} + ${LATEXMK_DIR} + ) + +# If found, figure out a version +if(LATEXMK_EXECUTABLE) + execute_process(COMMAND ${LATEXMK_EXECUTABLE} --version + OUTPUT_VARIABLE LATEXMK_VERSION_LINE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(REGEX MATCH "Version.*$" LATEXMK_VERSION_STRING "${LATEXMK_VERSION_LINE}") + string(REGEX REPLACE "([0-9]+)\\." "\\1" LATEXMK_VERSION_MINOR "${LATEXMK_VERSION_STRING}") + string(REGEX REPLACE "[0-9]+\\.([0-9a-z]+)" "\\1" LATEXMK_VERSION_MAJOR "${LATEXMK_VERSION_STRING}") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LatexMk + FOUND_VAR LATEXMK_FOUND + REQUIRED_VARS LATEXMK_EXECUTABLE + VERSION_VAR LATEXMK_VERSION_STRING) diff --git a/cmake/modules/FindMETIS.cmake b/cmake/modules/FindMETIS.cmake new file mode 100644 index 0000000..338b09e --- /dev/null +++ b/cmake/modules/FindMETIS.cmake @@ -0,0 +1,178 @@ +#[=======================================================================[.rst: +FindMETIS +--------- + +Find Serial Graph Partitioning library METIS +(see http://glaros.dtc.umn.edu/gkhome/metis/metis/overview) + +Imported targets +^^^^^^^^^^^^^^^^ + +This module defines the following :prop_tgt:`IMPORTED` target: + +``METIS::METIS`` + The libraries, flags, and includes to use for METIS, if found. + +Result Variables +^^^^^^^^^^^^^^^^ + +This module defines the following variables: + +``METIS_FOUND`` + The METIS library with all its dependencies is found + +Cache Variables +^^^^^^^^^^^^^^^ + +The following variables may be set to influence this module's behavior: + +``METIS_INCLUDE_DIR`` + Include directory of METIS + +``METIS_LIBRARY`` + Full path to the METIS library + +``METIS_API_VERSION`` + This variable specifies the METIS API version provided by the scotch-metis library. This + is required for Scotch >= 6.0.7 versions if it is not detected automatically. The + variable may be set to 3 to indicate that scotch implements the METIS API v3 (default + for older Scotch versions), or it can be set to 5 to indicate that v5 of the METIS API + is provided. This variable corresponds to the preprocessor flag `SCOTCH_METIS_VERSION` + that is used when compiling Scotch from source. +#]=======================================================================] + +# Text for feature summary +include(FeatureSummary) +set_package_properties("METIS" PROPERTIES + DESCRIPTION "Serial Graph Partitioning" +) + +# The METIS API version provided by the METIS or scotch-metis library +set(METIS_API_VERSION 0 CACHE STRING + "METIS API version provided by METIS or scotch-metis library") + +# Try to locate METIS header +find_path(METIS_INCLUDE_DIR metis.h + PATH_SUFFIXES metis) + +# Determine version of METIS installation +find_file(METIS_HEADER_FILE metis.h + PATHS ${METIS_INCLUDE_DIR} + NO_DEFAULT_PATH) +if(METIS_HEADER_FILE) + file(READ "${METIS_HEADER_FILE}" metisheader) + string(REGEX REPLACE ".*#define METIS_VER_MAJOR[ ]+([0-9]+).*" "\\1" + METIS_MAJOR_VERSION "${metisheader}") + string(REGEX REPLACE ".*#define METIS_VER_MINOR[ ]+([0-9]+).*" "\\1" + METIS_MINOR_VERSION "${metisheader}") + if(METIS_MAJOR_VERSION GREATER_EQUAL 0 AND METIS_MINOR_VERSION GREATER_EQUAL 0) + set(METIS_VERSION "${METIS_MAJOR_VERSION}.${METIS_MINOR_VERSION}") + + # Specify an api version to be used in config.h files or compile flags + if(NOT METIS_API_VERSION) + if(METIS_MAJOR_VERSION GREATER_EQUAL 3 AND METIS_MAJOR_VERSION LESS 5) + set(METIS_API_VERSION "3") + else() + set(METIS_API_VERSION "${METIS_MAJOR_VERSION}") + endif() + endif() + else() + unset(METIS_MAJOR_VERSION) + unset(METIS_MINOR_VERSION) + endif() + + # test whether header file is actually the scotch-metis header + string(FIND "${metisheader}" "SCOTCH_METIS_PREFIX" IS_SCOTCH_METIS_HEADER) + if(IS_SCOTCH_METIS_HEADER EQUAL "-1") + set(IS_SCOTCH_METIS_HEADER FALSE) + else() + set(IS_SCOTCH_METIS_HEADER TRUE) + endif() +endif() +unset(METIS_HEADER_FILE CACHE) + +# search for the METIS library or for the scotch-metis wrapper library +if(IS_SCOTCH_METIS_HEADER) + find_library(METIS_LIBRARY scotchmetis) +else() + find_library(METIS_LIBRARY metis) +endif() + +# We need to check whether we need to link m, copy the lazy solution +# from FindBLAS and FindLAPACK here. +if(METIS_LIBRARY AND NOT WIN32) + set(METIS_NEEDS_LIBM 1) +endif() + +mark_as_advanced(METIS_INCLUDE_DIR METIS_LIBRARY METIS_NEEDS_LIBM METIS_API_VERSION) + +# If scotch is requested, find package PTScotch and check version compatibility: +# Scotch provides METIS-3 interface only in version < 6.07, but provides an option to +# select the API-version in later Scotch releases +if(IS_SCOTCH_METIS_HEADER) + find_package(PTScotch) + set(HAVE_SCOTCH_METIS ${PTScotch_SCOTCH_FOUND}) + if (PTScotch_SCOTCH_FOUND AND NOT METIS_API_VERSION) + if(PTScotch_VERSION VERSION_LESS "6.0.7") + set(METIS_API_VERSION "3") + else() + # try to figure out the METIS_API_VERSION by checking for symbols in the library + include(CheckSymbolExists) + include(CMakePushCheckState) + find_package(Threads) + cmake_push_check_state() + set(CMAKE_REQUIRED_LIBRARIES ${METIS_LIBRARY} ${SCOTCH_LIBRARY} ${SCOTCHERR_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) + if(METIS_NEEDS_LIBM) + list(APPEND CMAKE_REQUIRED_LIBRARIES m) + endif() + set(CMAKE_REQUIRED_INCLUDES ${METIS_INCLUDE_DIR} ${SCOTCH_INCLUDE_DIR}) + + set(CMAKE_REQUIRED_DEFINITIONS "-DSCOTCH_METIS_VERSION=3") + check_symbol_exists("METIS_PartGraphVKway" "stdio.h;stdint.h;scotch.h;metis.h" IS_SCOTCH_METIS_API_V3) + if(IS_SCOTCH_METIS_API_V3) + set(METIS_API_VERSION "3") + else() + set(CMAKE_REQUIRED_DEFINITIONS "-DSCOTCH_METIS_VERSION=5") + check_symbol_exists("METIS_PartGraphKway" "stdio.h;stdint.h;scotch.h;metis.h" IS_SCOTCH_METIS_API_V5) + if(IS_SCOTCH_METIS_API_V5) + set(METIS_API_VERSION "5") + endif() + endif() + cmake_pop_check_state() + endif() + endif() +endif() + +# Behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args("METIS" + REQUIRED_VARS + METIS_LIBRARY METIS_INCLUDE_DIR METIS_API_VERSION + VERSION_VAR + METIS_VERSION +) + +# If both headers and library are found, create imported target +if(METIS_FOUND AND NOT TARGET METIS::METIS) + add_library(METIS::METIS UNKNOWN IMPORTED) + set_target_properties(METIS::METIS PROPERTIES + IMPORTED_LOCATION ${METIS_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${METIS_INCLUDE_DIR} + INTERFACE_COMPILE_DEFINITIONS METIS_API_VERSION=${METIS_API_VERSION} + ) + + # Link against libm if needed + if(METIS_NEEDS_LIBM) + set_property(TARGET METIS::METIS APPEND PROPERTY + INTERFACE_LINK_LIBRARIES m) + endif() + + # Link against Scotch library if option is enabled + if(IS_SCOTCH_METIS_HEADER AND PTScotch_FOUND) + set_property(TARGET METIS::METIS APPEND PROPERTY + INTERFACE_LINK_LIBRARIES PTScotch::Scotch) + set_property(TARGET METIS::METIS APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS + SCOTCH_METIS_VERSION=${METIS_API_VERSION}) + endif() +endif() diff --git a/cmake/modules/FindPTScotch.cmake b/cmake/modules/FindPTScotch.cmake new file mode 100644 index 0000000..e101065 --- /dev/null +++ b/cmake/modules/FindPTScotch.cmake @@ -0,0 +1,173 @@ +#[=======================================================================[.rst: +FindPTScotch +------------ + +Find library PTScotch, i.e. Software package and libraries for sequential +and parallel graph partitioning, static mapping and clustering, sequential +mesh and hypergraph partitioning, and sequential and parallel sparse matrix +block ordering + +Components +^^^^^^^^^^ + +The PTScotch module allows to search for the following components + +``SCOTCH`` + Sequential version of Scotch +``PTSCOTCH`` + Parallel version of Scotch. Requires MPI. + +Imported targets +^^^^^^^^^^^^^^^^ + +This module defines the following :prop_tgt:`IMPORTED` target: + +``PTScotch::Scotch`` + The sequential Scotch library to link against +``PTScotch::PTScotch`` + The parallel PTScotch library to link against + +Result Variables +^^^^^^^^^^^^^^^^ + +This module defines the following variables: + +``PTScotch_FOUND`` + The Scotch and/or PTScotch library with all its dependencies is found +``PTScotch_SCOTCH_FOUND`` + The sequential Scotch library is found +``PTScotch_PTSCOTCH_FOUND`` + The parallel PTScotch library is found +``PTScotch_VERSION`` + Version of Scotch that is found + +Cache Variables +^^^^^^^^^^^^^^^ + +The following variables may be set to influence this module's behavior: + +``PTSCOTCH_SUFFIX`` + Scotch might be compiled using different integer sizes (int32, int64, long). + When this is set the headers and libaries are search under the suffix + :code:`include/scotch-${PTSCOTCH_SUFFIX}`, and :code:`lib/scotch-${PTSCOTCH_SUFFIX}`, + respectively. + +``SCOTCH_INCLUDE_DIR`` + Include directory where the scotch.h is found. + +``PTSCOTCH_INCLUDE_DIR`` + Include directory where the ptscotch.h is found. + +``SCOTCH_LIBRARY`` and ``SCOTCHERR_LIBRARY`` + Full path to the scotch library + +``PTSCOTCH_LIBRARY`` and ``PTSCOTCHERR_LIBRARY`` + Full path to the ptscotch library + +#]=======================================================================] + +# text for feature summary +include(FeatureSummary) +set_package_properties("PTScotch" PROPERTIES + DESCRIPTION "Sequential and Parallel Graph Partitioning" +) + +# find dependency for PTScotch +include(CMakeFindDependencyMacro) +find_package(MPI QUIET) + +# search directory might have the PATH_SUFFIX scotch-SUFFIX +if(PTSCOTCH_SUFFIX) + set(PATH_SUFFIXES "scotch-${PTSCOTCH_SUFFIX}") +else() + set(PATH_SUFFIXES "scotch") +endif() + +# Try to find the include files +find_path(SCOTCH_INCLUDE_DIR scotch.h + PATH_SUFFIXES ${PATH_SUFFIXES}) + +find_path(PTSCOTCH_INCLUDE_DIR ptscotch.h + HINTS ${SCOTCH_INCLUDE_DIR} + PATH_SUFFIXES ${PATH_SUFFIXES}) + +# Try to find the (pt)scotch libraries +find_library(SCOTCH_LIBRARY scotch) +find_library(SCOTCHERR_LIBRARY scotcherr) +find_library(PTSCOTCH_LIBRARY ptscotch) +find_library(PTSCOTCHERR_LIBRARY ptscotcherr) + +mark_as_advanced(SCOTCH_INCLUDE_DIR SCOTCH_LIBRARY SCOTCHERR_LIBRARY + PTSCOTCH_INCLUDE_DIR PTSCOTCH_LIBRARY PTSCOTCHERR_LIBRARY) + +# check version of (PT)Scotch +find_file(SCOTCH_HEADER "scotch.h" + HINTS ${SCOTCH_INCLUDE_DIR} + NO_DEFAULT_PATH) +if(SCOTCH_HEADER) + file(READ "${SCOTCH_HEADER}" scotchheader) + string(REGEX REPLACE ".*#define SCOTCH_VERSION[ ]+([0-9]+).*" "\\1" + SCOTCH_MAJOR_VERSION "${scotchheader}") + string(REGEX REPLACE ".*#define SCOTCH_RELEASE[ ]+([0-9]+).*" "\\1" + SCOTCH_MINOR_VERSION "${scotchheader}") + string(REGEX REPLACE ".*#define SCOTCH_PATCHLEVEL[ ]+([0-9]+).*" "\\1" + SCOTCH_PREFIX_VERSION "${scotchheader}") + if(SCOTCH_MAJOR_VERSION GREATER_EQUAL 0) + set(PTScotch_VERSION "${SCOTCH_MAJOR_VERSION}") + endif() + if (SCOTCH_MINOR_VERSION GREATER_EQUAL 0) + set(PTScotch_VERSION "${PTScotch_VERSION}.${SCOTCH_MINOR_VERSION}") + endif() + if (SCOTCH_PREFIX_VERSION GREATER_EQUAL 0) + set(PTScotch_VERSION "${PTScotch_VERSION}.${SCOTCH_PREFIX_VERSION}") + endif() +endif() +unset(SCOTCH_HEADER CACHE) + +# set if (PT)Scotch components found +if (SCOTCH_INCLUDE_DIR AND SCOTCH_LIBRARY AND SCOTCHERR_LIBRARY) + set(PTScotch_SCOTCH_FOUND TRUE) +endif () + +if (PTSCOTCH_INCLUDE_DIR AND PTSCOTCH_LIBRARY AND PTSCOTCHERR_LIBRARY AND MPI_FOUND) + set(PTScotch_PTSCOTCH_FOUND TRUE) +endif () + +# dependencies between components +if (NOT PTScotch_SCOTCH_FOUND) + set(PTScotch_PTSCOTCH_FOUND FALSE) +endif () + +# behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args("PTScotch" + REQUIRED_VARS + SCOTCH_LIBRARY SCOTCHERR_LIBRARY SCOTCH_INCLUDE_DIR + VERSION_VAR + PTScotch_VERSION + HANDLE_COMPONENTS +) + +if(PTScotch_FOUND) + # Define an imported target for the sequential Scotch library + if(PTScotch_SCOTCH_FOUND AND NOT TARGET PTScotch::Scotch) + add_library(PTScotch::Scotch UNKNOWN IMPORTED) + set_target_properties(PTScotch::Scotch PROPERTIES + IMPORTED_LOCATION ${SCOTCH_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${SCOTCH_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES ${SCOTCHERR_LIBRARY} + ) + endif() + + # Define an imported target for the parallel PTScotch library + if(PTScotch_SCOTCH_FOUND AND PTScotch_PTSCOTCH_FOUND AND NOT TARGET PTScotch::PTScotch) + add_library(PTScotch::PTScotch UNKNOWN IMPORTED) + set_target_properties(PTScotch::PTScotch PROPERTIES + IMPORTED_LOCATION ${PTSCOTCH_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${PTSCOTCH_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES ${PTSCOTCHERR_LIBRARY} + ) + target_link_libraries(PTScotch::PTScotch + INTERFACE PTScotch::Scotch MPI::MPI_C) + endif() +endif() diff --git a/cmake/modules/FindParMETIS.cmake b/cmake/modules/FindParMETIS.cmake new file mode 100644 index 0000000..92422dd --- /dev/null +++ b/cmake/modules/FindParMETIS.cmake @@ -0,0 +1,123 @@ +#[=======================================================================[.rst: +FindParMETIS +------------ + +Find Parallel Graph Partitioning library ParMETIS +(see http://glaros.dtc.umn.edu/gkhome/metis/parmetis/overview) + +Imported targets +^^^^^^^^^^^^^^^^ + +This module defines the following :prop_tgt:`IMPORTED` target: + +``ParMETIS::ParMETIS`` + The libraries, flags, and includes to use for ParMETIS, if found. + +Result Variables +^^^^^^^^^^^^^^^^ + +This module defines the following variables: + +``ParMETIS_FOUND`` + The ParMETIS library with all its dependencies is found + +Cache Variables +^^^^^^^^^^^^^^^ + +The following variables may be set to influence this module's behavior: + +``PARMETIS_INCLUDE_DIR`` + Include directory where the parmetis.h is found. + +``PARMETIS_LIBRARY`` + Full path to the ParMETIS library + +#]=======================================================================] + +# text for feature summary +include(FeatureSummary) +set_package_properties("ParMETIS" PROPERTIES + DESCRIPTION "Parallel Graph Partitioning" +) + +find_path(PARMETIS_INCLUDE_DIR parmetis.h + PATH_SUFFIXES parmetis) + +# determine version of ParMETIS installation +find_file(PARMETIS_HEADER_FILE parmetis.h + PATHS ${PARMETIS_INCLUDE_DIR} + NO_DEFAULT_PATH) +if(PARMETIS_HEADER_FILE) + file(READ "${PARMETIS_HEADER_FILE}" parmetisheader) + string(REGEX REPLACE ".*#define PARMETIS_MAJOR_VERSION[ ]+([0-9]+).*" "\\1" + ParMETIS_MAJOR_VERSION "${parmetisheader}") + string(REGEX REPLACE ".*#define PARMETIS_MINOR_VERSION[ ]+([0-9]+).*" "\\1" + ParMETIS_MINOR_VERSION "${parmetisheader}") + if(ParMETIS_MAJOR_VERSION GREATER_EQUAL 0 AND ParMETIS_MINOR_VERSION GREATER_EQUAL 0) + set(ParMETIS_VERSION "${ParMETIS_MAJOR_VERSION}.${ParMETIS_MINOR_VERSION}") + endif() + + # test whether header file is actually the ptscotch-parmetis header + string(FIND "${parmetisheader}" "SCOTCH_METIS_PREFIX" IS_PTSCOTCH_PARMETIS_HEADER) + if(IS_PTSCOTCH_PARMETIS_HEADER EQUAL "-1") + set(IS_PTSCOTCH_PARMETIS_HEADER FALSE) + else() + set(IS_PTSCOTCH_PARMETIS_HEADER TRUE) + endif() +endif() +unset(PARMETIS_HEADER_FILE CACHE) + + +# search ParMETIS library +if(IS_PTSCOTCH_PARMETIS_HEADER) + find_library(PARMETIS_LIBRARY ptscotchparmetis) +else() + find_library(PARMETIS_LIBRARY parmetis) +endif() + +mark_as_advanced(PARMETIS_INCLUDE_DIR PARMETIS_LIBRARY) + +# minimal requires METIS version 5.0 for ParMETIS >= 4.0 +if(ParMETIS_VERSION VERSION_GREATER_EQUAL "4.0") + set(METIS_MIN_VERSION "5.0") +endif() + +# find package dependencies first +find_package(METIS ${METIS_MIN_VERSION}) +find_package(MPI COMPONENTS C) + +# set a list of required dependencies for ParMETIS +set(PARMETIS_DEPENDENCIES METIS_FOUND MPI_FOUND) + +# If ptscotch-parmetis is requested, find package PTScotch +if(IS_PTSCOTCH_PARMETIS_HEADER) + find_package(PTScotch) + set(HAVE_PTSCOTCH_PARMETIS ${PTScotch_PTSCOTCH_FOUND}) + list(APPEND PARMETIS_DEPENDENCIES PTScotch_PTSCOTCH_FOUND) +endif() + +# behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args("ParMETIS" + REQUIRED_VARS + PARMETIS_LIBRARY PARMETIS_INCLUDE_DIR ${PARMETIS_DEPENDENCIES} + VERSION_VAR + ParMETIS_VERSION +) + +# create imported target ParMETIS::ParMETIS +if(PARMETIS_FOUND AND NOT TARGET ParMETIS::ParMETIS) + add_library(ParMETIS::ParMETIS UNKNOWN IMPORTED) + set_target_properties(ParMETIS::ParMETIS PROPERTIES + IMPORTED_LOCATION ${PARMETIS_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${PARMETIS_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES "METIS::METIS;MPI::MPI_C" + INTERFACE_COMPILE_DEFINITIONS "MPICH_SKIP_MPICXX;OMPI_SKIP_MPICXX" + ) + + # link against PTScotch if needed + if(IS_PTSCOTCH_PARMETIS_HEADER AND PTScotch_PTSCOTCH_FOUND) + set_property(TARGET ParMETIS::ParMETIS APPEND PROPERTY + INTERFACE_LINK_LIBRARIES PTScotch::PTScotch) + endif() +endif() diff --git a/cmake/modules/FindPkgConfig/CMakeLists.txt b/cmake/modules/FindPkgConfig/CMakeLists.txt new file mode 100644 index 0000000..fc1b628 --- /dev/null +++ b/cmake/modules/FindPkgConfig/CMakeLists.txt @@ -0,0 +1,3 @@ +install(FILES + FindPkgConfig.cmake + DESTINATION ${DUNE_INSTALL_MODULEDIR}/FindPkgConfig) \ No newline at end of file diff --git a/cmake/modules/FindPkgConfig/FindPkgConfig.cmake b/cmake/modules/FindPkgConfig/FindPkgConfig.cmake new file mode 100644 index 0000000..dc97d60 --- /dev/null +++ b/cmake/modules/FindPkgConfig/FindPkgConfig.cmake @@ -0,0 +1,874 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# Note, this is a backport of the cmake/Modules/FindPkgConfig.cmake file from cmake version 3.19.4 + +#[========================================[.rst: +FindPkgConfig +------------- + +A ``pkg-config`` module for CMake. + +Finds the ``pkg-config`` executable and adds the :command:`pkg_get_variable`, +:command:`pkg_check_modules` and :command:`pkg_search_module` commands. The +following variables will also be set: + +``PKG_CONFIG_FOUND`` + if pkg-config executable was found +``PKG_CONFIG_EXECUTABLE`` + pathname of the pkg-config program +``PKG_CONFIG_VERSION_STRING`` + version of pkg-config (since CMake 2.8.8) + +#]========================================] + +cmake_policy(PUSH) +cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced +cmake_policy(SET CMP0057 NEW) # if IN_LIST + +### Common stuff #### +set(PKG_CONFIG_VERSION 1) + +# find pkg-config, use PKG_CONFIG if set +if((NOT PKG_CONFIG_EXECUTABLE) AND (NOT "$ENV{PKG_CONFIG}" STREQUAL "")) + set(PKG_CONFIG_EXECUTABLE "$ENV{PKG_CONFIG}" CACHE FILEPATH "pkg-config executable") +endif() + +set(PKG_CONFIG_NAMES "pkg-config") +if(CMAKE_HOST_WIN32) + list(PREPEND PKG_CONFIG_NAMES "pkg-config.bat") +endif() + +find_program(PKG_CONFIG_EXECUTABLE NAMES ${PKG_CONFIG_NAMES} DOC "pkg-config executable") +mark_as_advanced(PKG_CONFIG_EXECUTABLE) + +set(_PKG_CONFIG_FAILURE_MESSAGE "") +if (PKG_CONFIG_EXECUTABLE) + execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --version + OUTPUT_VARIABLE PKG_CONFIG_VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_VARIABLE _PKG_CONFIG_VERSION_ERROR ERROR_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _PKG_CONFIG_VERSION_RESULT + ) + + if (NOT _PKG_CONFIG_VERSION_RESULT EQUAL 0) + string(REPLACE "\n" "\n " _PKG_CONFIG_VERSION_ERROR " ${_PKG_CONFIG_VERSION_ERROR}") + string(APPEND _PKG_CONFIG_FAILURE_MESSAGE + "The command\n" + " \"${PKG_CONFIG_EXECUTABLE}\" --version\n" + " failed with output:\n${PKG_CONFIG_VERSION_STRING}\n" + " stderr: \n${_PKG_CONFIG_VERSION_ERROR}\n" + " result: \n${_PKG_CONFIG_VERSION_RESULT}" + ) + set(PKG_CONFIG_EXECUTABLE "") + unset(PKG_CONFIG_VERSION_STRING) + endif () + unset(_PKG_CONFIG_VERSION_RESULT) +endif () + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PkgConfig + REQUIRED_VARS PKG_CONFIG_EXECUTABLE + FAIL_MESSAGE "${_PKG_CONFIG_FAILURE_MESSAGE}" + VERSION_VAR PKG_CONFIG_VERSION_STRING) + +# This is needed because the module name is "PkgConfig" but the name of +# this variable has always been PKG_CONFIG_FOUND so this isn't automatically +# handled by FPHSA. +set(PKG_CONFIG_FOUND "${PKGCONFIG_FOUND}") + +# Unsets the given variables +macro(_pkgconfig_unset var) + set(${var} "" CACHE INTERNAL "") +endmacro() + +macro(_pkgconfig_set var value) + set(${var} ${value} CACHE INTERNAL "") +endmacro() + +# Invokes pkgconfig, cleans up the result and sets variables +macro(_pkgconfig_invoke _pkglist _prefix _varname _regexp) + set(_pkgconfig_invoke_result) + + execute_process( + COMMAND ${PKG_CONFIG_EXECUTABLE} ${ARGN} ${_pkglist} + OUTPUT_VARIABLE _pkgconfig_invoke_result + RESULT_VARIABLE _pkgconfig_failed + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if (_pkgconfig_failed) + set(_pkgconfig_${_varname} "") + _pkgconfig_unset(${_prefix}_${_varname}) + else() + string(REGEX REPLACE "[\r\n]" " " _pkgconfig_invoke_result "${_pkgconfig_invoke_result}") + + if (NOT ${_regexp} STREQUAL "") + string(REGEX REPLACE "${_regexp}" " " _pkgconfig_invoke_result "${_pkgconfig_invoke_result}") + endif() + + separate_arguments(_pkgconfig_invoke_result) + + #message(STATUS " ${_varname} ... ${_pkgconfig_invoke_result}") + set(_pkgconfig_${_varname} ${_pkgconfig_invoke_result}) + _pkgconfig_set(${_prefix}_${_varname} "${_pkgconfig_invoke_result}") + endif() +endmacro() + +# Internal version of pkg_get_variable; expects PKG_CONFIG_PATH to already be set +function (_pkg_get_variable result pkg variable) + _pkgconfig_invoke("${pkg}" "prefix" "result" "" "--variable=${variable}") + set("${result}" + "${prefix_result}" + PARENT_SCOPE) +endfunction () + +# Invokes pkgconfig two times; once without '--static' and once with +# '--static' +macro(_pkgconfig_invoke_dyn _pkglist _prefix _varname cleanup_regexp) + _pkgconfig_invoke("${_pkglist}" ${_prefix} ${_varname} "${cleanup_regexp}" ${ARGN}) + _pkgconfig_invoke("${_pkglist}" ${_prefix} STATIC_${_varname} "${cleanup_regexp}" --static ${ARGN}) +endmacro() + +# Splits given arguments into options and a package list +macro(_pkgconfig_parse_options _result _is_req _is_silent _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global) + set(${_is_req} 0) + set(${_is_silent} 0) + set(${_no_cmake_path} 0) + set(${_no_cmake_environment_path} 0) + set(${_imp_target} 0) + set(${_imp_target_global} 0) + if(DEFINED PKG_CONFIG_USE_CMAKE_PREFIX_PATH) + if(NOT PKG_CONFIG_USE_CMAKE_PREFIX_PATH) + set(${_no_cmake_path} 1) + set(${_no_cmake_environment_path} 1) + endif() + elseif(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.1) + set(${_no_cmake_path} 1) + set(${_no_cmake_environment_path} 1) + endif() + + foreach(_pkg ${ARGN}) + if (_pkg STREQUAL "REQUIRED") + set(${_is_req} 1) + endif () + if (_pkg STREQUAL "QUIET") + set(${_is_silent} 1) + endif () + if (_pkg STREQUAL "NO_CMAKE_PATH") + set(${_no_cmake_path} 1) + endif() + if (_pkg STREQUAL "NO_CMAKE_ENVIRONMENT_PATH") + set(${_no_cmake_environment_path} 1) + endif() + if (_pkg STREQUAL "IMPORTED_TARGET") + set(${_imp_target} 1) + endif() + if (_pkg STREQUAL "GLOBAL") + set(${_imp_target_global} 1) + endif() + endforeach() + + if (${_imp_target_global} AND NOT ${_imp_target}) + message(SEND_ERROR "the argument GLOBAL may only be used together with IMPORTED_TARGET") + endif() + + set(${_result} ${ARGN}) + list(REMOVE_ITEM ${_result} "REQUIRED") + list(REMOVE_ITEM ${_result} "QUIET") + list(REMOVE_ITEM ${_result} "NO_CMAKE_PATH") + list(REMOVE_ITEM ${_result} "NO_CMAKE_ENVIRONMENT_PATH") + list(REMOVE_ITEM ${_result} "IMPORTED_TARGET") + list(REMOVE_ITEM ${_result} "GLOBAL") +endmacro() + +# Add the content of a variable or an environment variable to a list of +# paths +# Usage: +# - _pkgconfig_add_extra_path(_extra_paths VAR) +# - _pkgconfig_add_extra_path(_extra_paths ENV VAR) +function(_pkgconfig_add_extra_path _extra_paths_var _var) + set(_is_env 0) + if(ARGC GREATER 2 AND _var STREQUAL "ENV") + set(_var ${ARGV2}) + set(_is_env 1) + endif() + if(NOT _is_env) + if(NOT "${${_var}}" STREQUAL "") + list(APPEND ${_extra_paths_var} ${${_var}}) + endif() + else() + if(NOT "$ENV{${_var}}" STREQUAL "") + file(TO_CMAKE_PATH "$ENV{${_var}}" _path) + list(APPEND ${_extra_paths_var} ${_path}) + unset(_path) + endif() + endif() + set(${_extra_paths_var} ${${_extra_paths_var}} PARENT_SCOPE) +endfunction() + +# scan the LDFLAGS returned by pkg-config for library directories and +# libraries, figure out the absolute paths of that libraries in the +# given directories +function(_pkg_find_libs _prefix _no_cmake_path _no_cmake_environment_path) + unset(_libs) + unset(_find_opts) + + # set the options that are used as long as the .pc file does not provide a library + # path to look into + if(_no_cmake_path) + list(APPEND _find_opts "NO_CMAKE_PATH") + endif() + if(_no_cmake_environment_path) + list(APPEND _find_opts "NO_CMAKE_ENVIRONMENT_PATH") + endif() + + unset(_search_paths) + unset(_next_is_framework) + foreach (flag IN LISTS ${_prefix}_LDFLAGS) + if (_next_is_framework) + list(APPEND _libs "-framework ${flag}") + unset(_next_is_framework) + continue() + endif () + if (flag MATCHES "^-L(.*)") + list(APPEND _search_paths ${CMAKE_MATCH_1}) + continue() + endif() + if (flag MATCHES "^-l(.*)") + set(_pkg_search "${CMAKE_MATCH_1}") + else() + if (flag STREQUAL "-framework") + set(_next_is_framework TRUE) + endif () + continue() + endif() + + if(_search_paths) + # Firstly search in -L paths + find_library(pkgcfg_lib_${_prefix}_${_pkg_search} + NAMES ${_pkg_search} + HINTS ${_search_paths} NO_DEFAULT_PATH) + endif() + find_library(pkgcfg_lib_${_prefix}_${_pkg_search} + NAMES ${_pkg_search} + ${_find_opts}) + mark_as_advanced(pkgcfg_lib_${_prefix}_${_pkg_search}) + if(pkgcfg_lib_${_prefix}_${_pkg_search}) + list(APPEND _libs "${pkgcfg_lib_${_prefix}_${_pkg_search}}") + else() + list(APPEND _libs ${_pkg_search}) + endif() + endforeach() + + set(${_prefix}_LINK_LIBRARIES "${_libs}" PARENT_SCOPE) +endfunction() + +# create an imported target from all the information returned by pkg-config +function(_pkg_create_imp_target _prefix _imp_target_global) + # only create the target if it is linkable, i.e. no executables + if (NOT TARGET PkgConfig::${_prefix} + AND ( ${_prefix}_INCLUDE_DIRS OR ${_prefix}_LINK_LIBRARIES OR ${_prefix}_LDFLAGS_OTHER OR ${_prefix}_CFLAGS_OTHER )) + if(${_imp_target_global}) + set(_global_opt "GLOBAL") + else() + unset(_global_opt) + endif() + add_library(PkgConfig::${_prefix} INTERFACE IMPORTED ${_global_opt}) + + if(${_prefix}_INCLUDE_DIRS) + set_property(TARGET PkgConfig::${_prefix} PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${${_prefix}_INCLUDE_DIRS}") + endif() + if(${_prefix}_LINK_LIBRARIES) + set_property(TARGET PkgConfig::${_prefix} PROPERTY + INTERFACE_LINK_LIBRARIES "${${_prefix}_LINK_LIBRARIES}") + endif() + if(${_prefix}_LDFLAGS_OTHER) + set_property(TARGET PkgConfig::${_prefix} PROPERTY + INTERFACE_LINK_OPTIONS "${${_prefix}_LDFLAGS_OTHER}") + endif() + if(${_prefix}_CFLAGS_OTHER) + set_property(TARGET PkgConfig::${_prefix} PROPERTY + INTERFACE_COMPILE_OPTIONS "${${_prefix}_CFLAGS_OTHER}") + endif() + endif() +endfunction() + +# recalculate the dynamic output +# this is a macro and not a function so the result of _pkg_find_libs is automatically propagated +macro(_pkg_recalculate _prefix _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global) + _pkg_find_libs(${_prefix} ${_no_cmake_path} ${_no_cmake_environment_path}) + if(${_imp_target}) + _pkg_create_imp_target(${_prefix} ${_imp_target_global}) + endif() +endmacro() + +### +macro(_pkg_set_path_internal) + set(_extra_paths) + + if(NOT _no_cmake_path) + _pkgconfig_add_extra_path(_extra_paths CMAKE_PREFIX_PATH) + _pkgconfig_add_extra_path(_extra_paths CMAKE_FRAMEWORK_PATH) + _pkgconfig_add_extra_path(_extra_paths CMAKE_APPBUNDLE_PATH) + endif() + + if(NOT _no_cmake_environment_path) + _pkgconfig_add_extra_path(_extra_paths ENV CMAKE_PREFIX_PATH) + _pkgconfig_add_extra_path(_extra_paths ENV CMAKE_FRAMEWORK_PATH) + _pkgconfig_add_extra_path(_extra_paths ENV CMAKE_APPBUNDLE_PATH) + endif() + + if(NOT _extra_paths STREQUAL "") + # Save the PKG_CONFIG_PATH environment variable, and add paths + # from the CMAKE_PREFIX_PATH variables + set(_pkgconfig_path_old "$ENV{PKG_CONFIG_PATH}") + set(_pkgconfig_path "${_pkgconfig_path_old}") + if(NOT _pkgconfig_path STREQUAL "") + file(TO_CMAKE_PATH "${_pkgconfig_path}" _pkgconfig_path) + endif() + + # Create a list of the possible pkgconfig subfolder (depending on + # the system + set(_lib_dirs) + if(NOT DEFINED CMAKE_SYSTEM_NAME + OR (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU)$" + AND NOT CMAKE_CROSSCOMPILING)) + if(EXISTS "/etc/debian_version") # is this a debian system ? + if(CMAKE_LIBRARY_ARCHITECTURE) + list(APPEND _lib_dirs "lib/${CMAKE_LIBRARY_ARCHITECTURE}/pkgconfig") + endif() + else() + # not debian, check the FIND_LIBRARY_USE_LIB32_PATHS and FIND_LIBRARY_USE_LIB64_PATHS properties + get_property(uselib32 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS) + if(uselib32 AND CMAKE_SIZEOF_VOID_P EQUAL 4) + list(APPEND _lib_dirs "lib32/pkgconfig") + endif() + get_property(uselib64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS) + if(uselib64 AND CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND _lib_dirs "lib64/pkgconfig") + endif() + get_property(uselibx32 GLOBAL PROPERTY FIND_LIBRARY_USE_LIBX32_PATHS) + if(uselibx32 AND CMAKE_INTERNAL_PLATFORM_ABI STREQUAL "ELF X32") + list(APPEND _lib_dirs "libx32/pkgconfig") + endif() + endif() + endif() + if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" AND NOT CMAKE_CROSSCOMPILING) + list(APPEND _lib_dirs "libdata/pkgconfig") + endif() + list(APPEND _lib_dirs "lib/pkgconfig") + list(APPEND _lib_dirs "share/pkgconfig") + + # Check if directories exist and eventually append them to the + # pkgconfig path list + foreach(_prefix_dir ${_extra_paths}) + foreach(_lib_dir ${_lib_dirs}) + if(EXISTS "${_prefix_dir}/${_lib_dir}") + list(APPEND _pkgconfig_path "${_prefix_dir}/${_lib_dir}") + list(REMOVE_DUPLICATES _pkgconfig_path) + endif() + endforeach() + endforeach() + + # Prepare and set the environment variable + if(NOT _pkgconfig_path STREQUAL "") + # remove empty values from the list + list(REMOVE_ITEM _pkgconfig_path "") + file(TO_NATIVE_PATH "${_pkgconfig_path}" _pkgconfig_path) + if(CMAKE_HOST_UNIX) + string(REPLACE ";" ":" _pkgconfig_path "${_pkgconfig_path}") + string(REPLACE "\\ " " " _pkgconfig_path "${_pkgconfig_path}") + endif() + set(ENV{PKG_CONFIG_PATH} "${_pkgconfig_path}") + endif() + + # Unset variables + unset(_lib_dirs) + unset(_pkgconfig_path) + endif() +endmacro() + +macro(_pkg_restore_path_internal) + if(NOT _extra_paths STREQUAL "") + # Restore the environment variable + set(ENV{PKG_CONFIG_PATH} "${_pkgconfig_path_old}") + endif() + + unset(_extra_paths) + unset(_pkgconfig_path_old) +endmacro() + +# pkg-config returns frameworks in --libs-only-other +# they need to be in ${_prefix}_LIBRARIES so "-framework a -framework b" does +# not incorrectly be combined to "-framework a b" +function(_pkgconfig_extract_frameworks _prefix) + set(ldflags "${${_prefix}_LDFLAGS_OTHER}") + list(FIND ldflags "-framework" FR_POS) + list(LENGTH ldflags LD_LENGTH) + + # reduce length by 1 as we need "-framework" and the next entry + math(EXPR LD_LENGTH "${LD_LENGTH} - 1") + while (FR_POS GREATER -1 AND LD_LENGTH GREATER FR_POS) + list(REMOVE_AT ldflags ${FR_POS}) + list(GET ldflags ${FR_POS} HEAD) + list(REMOVE_AT ldflags ${FR_POS}) + math(EXPR LD_LENGTH "${LD_LENGTH} - 2") + + list(APPEND LIBS "-framework ${HEAD}") + + list(FIND ldflags "-framework" FR_POS) + endwhile () + set(${_prefix}_LIBRARIES ${${_prefix}_LIBRARIES} ${LIBS} PARENT_SCOPE) + set(${_prefix}_LDFLAGS_OTHER "${ldflags}" PARENT_SCOPE) +endfunction() + +# pkg-config returns -isystem include directories in --cflags-only-other, +# depending on the version and if there is a space between -isystem and +# the actual path +function(_pkgconfig_extract_isystem _prefix) + set(cflags "${${_prefix}_CFLAGS_OTHER}") + set(outflags "") + set(incdirs "${${_prefix}_INCLUDE_DIRS}") + + set(next_is_isystem FALSE) + foreach (THING IN LISTS cflags) + # This may filter "-isystem -isystem". That would not work anyway, + # so let it happen. + if (THING STREQUAL "-isystem") + set(next_is_isystem TRUE) + continue() + endif () + if (next_is_isystem) + set(next_is_isystem FALSE) + list(APPEND incdirs "${THING}") + elseif (THING MATCHES "^-isystem") + string(SUBSTRING "${THING}" 8 -1 THING) + list(APPEND incdirs "${THING}") + else () + list(APPEND outflags "${THING}") + endif () + endforeach () + set(${_prefix}_CFLAGS_OTHER "${outflags}" PARENT_SCOPE) + set(${_prefix}_INCLUDE_DIRS "${incdirs}" PARENT_SCOPE) +endfunction() + +### +macro(_pkg_check_modules_internal _is_required _is_silent _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global _prefix) + _pkgconfig_unset(${_prefix}_FOUND) + _pkgconfig_unset(${_prefix}_VERSION) + _pkgconfig_unset(${_prefix}_PREFIX) + _pkgconfig_unset(${_prefix}_INCLUDEDIR) + _pkgconfig_unset(${_prefix}_LIBDIR) + _pkgconfig_unset(${_prefix}_MODULE_NAME) + _pkgconfig_unset(${_prefix}_LIBS) + _pkgconfig_unset(${_prefix}_LIBS_L) + _pkgconfig_unset(${_prefix}_LIBS_PATHS) + _pkgconfig_unset(${_prefix}_LIBS_OTHER) + _pkgconfig_unset(${_prefix}_CFLAGS) + _pkgconfig_unset(${_prefix}_CFLAGS_I) + _pkgconfig_unset(${_prefix}_CFLAGS_OTHER) + _pkgconfig_unset(${_prefix}_STATIC_LIBDIR) + _pkgconfig_unset(${_prefix}_STATIC_LIBS) + _pkgconfig_unset(${_prefix}_STATIC_LIBS_L) + _pkgconfig_unset(${_prefix}_STATIC_LIBS_PATHS) + _pkgconfig_unset(${_prefix}_STATIC_LIBS_OTHER) + _pkgconfig_unset(${_prefix}_STATIC_CFLAGS) + _pkgconfig_unset(${_prefix}_STATIC_CFLAGS_I) + _pkgconfig_unset(${_prefix}_STATIC_CFLAGS_OTHER) + + # create a better addressable variable of the modules and calculate its size + set(_pkg_check_modules_list ${ARGN}) + list(LENGTH _pkg_check_modules_list _pkg_check_modules_cnt) + + if(PKG_CONFIG_EXECUTABLE) + # give out status message telling checked module + if (NOT ${_is_silent}) + if (_pkg_check_modules_cnt EQUAL 1) + message(STATUS "Checking for module '${_pkg_check_modules_list}'") + else() + message(STATUS "Checking for modules '${_pkg_check_modules_list}'") + endif() + endif() + + set(_pkg_check_modules_packages) + set(_pkg_check_modules_failed) + + _pkg_set_path_internal() + + # iterate through module list and check whether they exist and match the required version + foreach (_pkg_check_modules_pkg ${_pkg_check_modules_list}) + set(_pkg_check_modules_exist_query) + + # check whether version is given + if (_pkg_check_modules_pkg MATCHES "(.*[^><])(=|[><]=?)(.*)") + set(_pkg_check_modules_pkg_name "${CMAKE_MATCH_1}") + set(_pkg_check_modules_pkg_op "${CMAKE_MATCH_2}") + set(_pkg_check_modules_pkg_ver "${CMAKE_MATCH_3}") + else() + set(_pkg_check_modules_pkg_name "${_pkg_check_modules_pkg}") + set(_pkg_check_modules_pkg_op) + set(_pkg_check_modules_pkg_ver) + endif() + + _pkgconfig_unset(${_prefix}_${_pkg_check_modules_pkg_name}_VERSION) + _pkgconfig_unset(${_prefix}_${_pkg_check_modules_pkg_name}_PREFIX) + _pkgconfig_unset(${_prefix}_${_pkg_check_modules_pkg_name}_INCLUDEDIR) + _pkgconfig_unset(${_prefix}_${_pkg_check_modules_pkg_name}_LIBDIR) + + list(APPEND _pkg_check_modules_packages "${_pkg_check_modules_pkg_name}") + + # create the final query which is of the format: + # * > + # * >= + # * = + # * <= + # * < + # * --exists + list(APPEND _pkg_check_modules_exist_query --print-errors --short-errors) + if (_pkg_check_modules_pkg_op) + list(APPEND _pkg_check_modules_exist_query "${_pkg_check_modules_pkg_name} ${_pkg_check_modules_pkg_op} ${_pkg_check_modules_pkg_ver}") + else() + list(APPEND _pkg_check_modules_exist_query --exists) + list(APPEND _pkg_check_modules_exist_query "${_pkg_check_modules_pkg_name}") + endif() + + # execute the query + execute_process( + COMMAND ${PKG_CONFIG_EXECUTABLE} ${_pkg_check_modules_exist_query} + RESULT_VARIABLE _pkgconfig_retval + ERROR_VARIABLE _pkgconfig_error + ERROR_STRIP_TRAILING_WHITESPACE) + + # evaluate result and tell failures + if (_pkgconfig_retval) + if(NOT ${_is_silent}) + message(STATUS " ${_pkgconfig_error}") + endif() + + set(_pkg_check_modules_failed 1) + endif() + endforeach() + + if(_pkg_check_modules_failed) + # fail when requested + if (${_is_required}) + message(FATAL_ERROR "A required package was not found") + endif () + else() + # when we are here, we checked whether requested modules + # exist. Now, go through them and set variables + + _pkgconfig_set(${_prefix}_FOUND 1) + list(LENGTH _pkg_check_modules_packages pkg_count) + + # iterate through all modules again and set individual variables + foreach (_pkg_check_modules_pkg ${_pkg_check_modules_packages}) + # handle case when there is only one package required + if (pkg_count EQUAL 1) + set(_pkg_check_prefix "${_prefix}") + else() + set(_pkg_check_prefix "${_prefix}_${_pkg_check_modules_pkg}") + endif() + + _pkgconfig_invoke(${_pkg_check_modules_pkg} "${_pkg_check_prefix}" VERSION "" --modversion ) + pkg_get_variable("${_pkg_check_prefix}_PREFIX" ${_pkg_check_modules_pkg} "prefix") + pkg_get_variable("${_pkg_check_prefix}_INCLUDEDIR" ${_pkg_check_modules_pkg} "includedir") + pkg_get_variable("${_pkg_check_prefix}_LIBDIR" ${_pkg_check_modules_pkg} "libdir") + foreach (variable IN ITEMS PREFIX INCLUDEDIR LIBDIR) + _pkgconfig_set("${_pkg_check_prefix}_${variable}" "${${_pkg_check_prefix}_${variable}}") + endforeach () + _pkgconfig_set("${_pkg_check_prefix}_MODULE_NAME" "${_pkg_check_modules_pkg}") + + if (NOT ${_is_silent}) + message(STATUS " Found ${_pkg_check_modules_pkg}, version ${_pkgconfig_VERSION}") + endif () + endforeach() + + # set variables which are combined for multiple modules + _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LIBRARIES "(^| )-l" --libs-only-l ) + _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LIBRARY_DIRS "(^| )-L" --libs-only-L ) + _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LDFLAGS "" --libs ) + _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LDFLAGS_OTHER "" --libs-only-other ) + + if (APPLE AND "-framework" IN_LIST ${_prefix}_LDFLAGS_OTHER) + _pkgconfig_extract_frameworks("${_prefix}") + endif() + + _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" INCLUDE_DIRS "(^| )(-I|-isystem ?)" --cflags-only-I ) + _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" CFLAGS "" --cflags ) + _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" CFLAGS_OTHER "" --cflags-only-other ) + + if (${_prefix}_CFLAGS_OTHER MATCHES "-isystem") + _pkgconfig_extract_isystem("${_prefix}") + endif () + + _pkg_recalculate("${_prefix}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global}) + endif() + + _pkg_restore_path_internal() + else() + if (${_is_required}) + message(SEND_ERROR "pkg-config tool not found") + endif () + endif() +endmacro() + + +#[========================================[.rst: +.. command:: pkg_check_modules + + Checks for all the given modules, setting a variety of result variables in + the calling scope. + + .. code-block:: cmake + + pkg_check_modules( + [REQUIRED] [QUIET] + [NO_CMAKE_PATH] + [NO_CMAKE_ENVIRONMENT_PATH] + [IMPORTED_TARGET [GLOBAL]] + [...]) + + When the ``REQUIRED`` argument is given, the command will fail with an error + if module(s) could not be found. + + When the ``QUIET`` argument is given, no status messages will be printed. + + By default, if :variable:`CMAKE_MINIMUM_REQUIRED_VERSION` is 3.1 or + later, or if :variable:`PKG_CONFIG_USE_CMAKE_PREFIX_PATH` is set to a + boolean ``True`` value, then the :variable:`CMAKE_PREFIX_PATH`, + :variable:`CMAKE_FRAMEWORK_PATH`, and :variable:`CMAKE_APPBUNDLE_PATH` cache + and environment variables will be added to the ``pkg-config`` search path. + The ``NO_CMAKE_PATH`` and ``NO_CMAKE_ENVIRONMENT_PATH`` arguments + disable this behavior for the cache variables and environment variables + respectively. + + The ``IMPORTED_TARGET`` argument will create an imported target named + ``PkgConfig::`` that can be passed directly as an argument to + :command:`target_link_libraries`. The ``GLOBAL`` argument will make the + imported target available in global scope. + + Each ```` can be either a bare module name or it can be a + module name with a version constraint (operators ``=``, ``<``, ``>``, + ``<=`` and ``>=`` are supported). The following are examples for a module + named ``foo`` with various constraints: + + - ``foo`` matches any version. + - ``foo<2`` only matches versions before 2. + - ``foo>=3.1`` matches any version from 3.1 or later. + - ``foo=1.2.3`` requires that foo must be exactly version 1.2.3. + + The following variables may be set upon return. Two sets of values exist: + One for the common case (`` = ``) and another for the + information ``pkg-config`` provides when called with the ``--static`` + option (`` = _STATIC``). + + ``_FOUND`` + set to 1 if module(s) exist + ``_LIBRARIES`` + only the libraries (without the '-l') + ``_LINK_LIBRARIES`` + the libraries and their absolute paths + ``_LIBRARY_DIRS`` + the paths of the libraries (without the '-L') + ``_LDFLAGS`` + all required linker flags + ``_LDFLAGS_OTHER`` + all other linker flags + ``_INCLUDE_DIRS`` + the '-I' preprocessor flags (without the '-I') + ``_CFLAGS`` + all required cflags + ``_CFLAGS_OTHER`` + the other compiler flags + + All but ``_FOUND`` may be a :ref:`;-list ` if the + associated variable returned from ``pkg-config`` has multiple values. + + There are some special variables whose prefix depends on the number of + ```` given. When there is only one ````, + ```` will simply be ````, but if two or more ```` + items are given, ```` will be ``_``. + + ``_VERSION`` + version of the module + ``_PREFIX`` + prefix directory of the module + ``_INCLUDEDIR`` + include directory of the module + ``_LIBDIR`` + lib directory of the module + + Examples: + + .. code-block:: cmake + + pkg_check_modules (GLIB2 glib-2.0) + + Looks for any version of glib2. If found, the output variable + ``GLIB2_VERSION`` will hold the actual version found. + + .. code-block:: cmake + + pkg_check_modules (GLIB2 glib-2.0>=2.10) + + Looks for at least version 2.10 of glib2. If found, the output variable + ``GLIB2_VERSION`` will hold the actual version found. + + .. code-block:: cmake + + pkg_check_modules (FOO glib-2.0>=2.10 gtk+-2.0) + + Looks for both glib2-2.0 (at least version 2.10) and any version of + gtk2+-2.0. Only if both are found will ``FOO`` be considered found. + The ``FOO_glib-2.0_VERSION`` and ``FOO_gtk+-2.0_VERSION`` variables will be + set to their respective found module versions. + + .. code-block:: cmake + + pkg_check_modules (XRENDER REQUIRED xrender) + + Requires any version of ``xrender``. Example output variables set by a + successful call:: + + XRENDER_LIBRARIES=Xrender;X11 + XRENDER_STATIC_LIBRARIES=Xrender;X11;pthread;Xau;Xdmcp +#]========================================] +macro(pkg_check_modules _prefix _module0) + _pkgconfig_parse_options(_pkg_modules _pkg_is_required _pkg_is_silent _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global "${_module0}" ${ARGN}) + # check cached value + if (NOT DEFINED __pkg_config_checked_${_prefix} OR __pkg_config_checked_${_prefix} LESS ${PKG_CONFIG_VERSION} OR NOT ${_prefix}_FOUND OR + (NOT "${ARGN}" STREQUAL "" AND NOT "${__pkg_config_arguments_${_prefix}}" STREQUAL "${_module0};${ARGN}") OR + ( "${ARGN}" STREQUAL "" AND NOT "${__pkg_config_arguments_${_prefix}}" STREQUAL "${_module0}")) + _pkg_check_modules_internal("${_pkg_is_required}" "${_pkg_is_silent}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global} "${_prefix}" ${_pkg_modules}) + + _pkgconfig_set(__pkg_config_checked_${_prefix} ${PKG_CONFIG_VERSION}) + if (${_prefix}_FOUND) + _pkgconfig_set(__pkg_config_arguments_${_prefix} "${_module0};${ARGN}") + endif() + else() + if (${_prefix}_FOUND) + _pkg_recalculate("${_prefix}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global}) + endif() + endif() +endmacro() + + +#[========================================[.rst: +.. command:: pkg_search_module + + The behavior of this command is the same as :command:`pkg_check_modules`, + except that rather than checking for all the specified modules, it searches + for just the first successful match. + + .. code-block:: cmake + + pkg_search_module( + [REQUIRED] [QUIET] + [NO_CMAKE_PATH] + [NO_CMAKE_ENVIRONMENT_PATH] + [IMPORTED_TARGET [GLOBAL]] + [...]) + + If a module is found, the ``_MODULE_NAME`` variable will contain the + name of the matching module. This variable can be used if you need to run + :command:`pkg_get_variable`. + + Example: + + .. code-block:: cmake + + pkg_search_module (BAR libxml-2.0 libxml2 libxml>=2) +#]========================================] +macro(pkg_search_module _prefix _module0) + _pkgconfig_parse_options(_pkg_modules_alt _pkg_is_required _pkg_is_silent _no_cmake_path _no_cmake_environment_path _imp_target _imp_target_global "${_module0}" ${ARGN}) + # check cached value + if (NOT DEFINED __pkg_config_checked_${_prefix} OR __pkg_config_checked_${_prefix} LESS ${PKG_CONFIG_VERSION} OR NOT ${_prefix}_FOUND) + set(_pkg_modules_found 0) + + if (NOT ${_pkg_is_silent}) + message(STATUS "Checking for one of the modules '${_pkg_modules_alt}'") + endif () + + # iterate through all modules and stop at the first working one. + foreach(_pkg_alt ${_pkg_modules_alt}) + if(NOT _pkg_modules_found) + _pkg_check_modules_internal(0 1 ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global} "${_prefix}" "${_pkg_alt}") + endif() + + if (${_prefix}_FOUND) + set(_pkg_modules_found 1) + break() + endif() + endforeach() + + if (NOT ${_prefix}_FOUND) + if(${_pkg_is_required}) + message(SEND_ERROR "None of the required '${_pkg_modules_alt}' found") + endif() + endif() + + _pkgconfig_set(__pkg_config_checked_${_prefix} ${PKG_CONFIG_VERSION}) + elseif (${_prefix}_FOUND) + _pkg_recalculate("${_prefix}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global}) + endif() +endmacro() + +#[========================================[.rst: +.. command:: pkg_get_variable + + Retrieves the value of a pkg-config variable ``varName`` and stores it in the + result variable ``resultVar`` in the calling scope. + + .. code-block:: cmake + + pkg_get_variable( ) + + If ``pkg-config`` returns multiple values for the specified variable, + ``resultVar`` will contain a :ref:`;-list `. + + For example: + + .. code-block:: cmake + + pkg_get_variable(GI_GIRDIR gobject-introspection-1.0 girdir) +#]========================================] +function (pkg_get_variable result pkg variable) + _pkg_set_path_internal() + _pkgconfig_invoke("${pkg}" "prefix" "result" "" "--variable=${variable}") + set("${result}" + "${prefix_result}" + PARENT_SCOPE) + _pkg_restore_path_internal() +endfunction () + + +#[========================================[.rst: +Variables Affecting Behavior +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. variable:: PKG_CONFIG_EXECUTABLE + + This can be set to the path of the pkg-config executable. If not provided, + it will be set by the module as a result of calling :command:`find_program` + internally. The ``PKG_CONFIG`` environment variable can be used as a hint. + +.. variable:: PKG_CONFIG_USE_CMAKE_PREFIX_PATH + + Specifies whether :command:`pkg_check_modules` and + :command:`pkg_search_module` should add the paths in the + :variable:`CMAKE_PREFIX_PATH`, :variable:`CMAKE_FRAMEWORK_PATH` and + :variable:`CMAKE_APPBUNDLE_PATH` cache and environment variables to the + ``pkg-config`` search path. + + If this variable is not set, this behavior is enabled by default if + :variable:`CMAKE_MINIMUM_REQUIRED_VERSION` is 3.1 or later, disabled + otherwise. +#]========================================] + + +### Local Variables: +### mode: cmake +### End: + +cmake_policy(POP) diff --git a/cmake/modules/FindPython3/CMakeLists.txt b/cmake/modules/FindPython3/CMakeLists.txt new file mode 100644 index 0000000..8031b37 --- /dev/null +++ b/cmake/modules/FindPython3/CMakeLists.txt @@ -0,0 +1,4 @@ +install(FILES + FindPython3.cmake + Support.cmake + DESTINATION ${DUNE_INSTALL_MODULEDIR}/FindPython3) diff --git a/cmake/modules/FindPython3/FindPython3.cmake b/cmake/modules/FindPython3/FindPython3.cmake new file mode 100644 index 0000000..ff3c43d --- /dev/null +++ b/cmake/modules/FindPython3/FindPython3.cmake @@ -0,0 +1,421 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindPython3 +----------- + +.. versionadded:: 3.12 + +Find Python 3 interpreter, compiler and development environment (include +directories and libraries). + +When a version is requested, it can be specified as a simple value or as a +range. For a detailed description of version range usage and capabilities, +refer to the :command:`find_package` command. + +The following components are supported: + +* ``Interpreter``: search for Python 3 interpreter +* ``Compiler``: search for Python 3 compiler. Only offered by IronPython. +* ``Development``: search for development artifacts (include directories and + libraries). This component includes two sub-components which can be specified + independently: + + * ``Development.Module``: search for artifacts for Python 3 module + developments. + * ``Development.Embed``: search for artifacts for Python 3 embedding + developments. + +* ``NumPy``: search for NumPy include directories. + +If no ``COMPONENTS`` are specified, ``Interpreter`` is assumed. + +If component ``Development`` is specified, it implies sub-components +``Development.Module`` and ``Development.Embed``. + +To ensure consistent versions between components ``Interpreter``, ``Compiler``, +``Development`` (or one of its sub-components) and ``NumPy``, specify all +components at the same time:: + + find_package (Python3 COMPONENTS Interpreter Development) + +This module looks only for version 3 of Python. This module can be used +concurrently with :module:`FindPython2` module to use both Python versions. + +The :module:`FindPython` module can be used if Python version does not matter +for you. + +.. note:: + + If components ``Interpreter`` and ``Development`` (or one of its + sub-components) are both specified, this module search only for interpreter + with same platform architecture as the one defined by ``CMake`` + configuration. This constraint does not apply if only ``Interpreter`` + component is specified. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module defines the following :ref:`Imported Targets ` +(when :prop_gbl:`CMAKE_ROLE` is ``PROJECT``): + +``Python3::Interpreter`` + Python 3 interpreter. Target defined if component ``Interpreter`` is found. +``Python3::Compiler`` + Python 3 compiler. Target defined if component ``Compiler`` is found. +``Python3::Module`` + Python 3 library for Python module. Target defined if component + ``Development.Module`` is found. +``Python3::Python`` + Python 3 library for Python embedding. Target defined if component + ``Development.Embed`` is found. +``Python3::NumPy`` + NumPy library for Python 3. Target defined if component ``NumPy`` is found. + +Result Variables +^^^^^^^^^^^^^^^^ + +This module will set the following variables in your project +(see :ref:`Standard Variable Names `): + +``Python3_FOUND`` + System has the Python 3 requested components. +``Python3_Interpreter_FOUND`` + System has the Python 3 interpreter. +``Python3_EXECUTABLE`` + Path to the Python 3 interpreter. +``Python3_INTERPRETER_ID`` + A short string unique to the interpreter. Possible values include: + * Python + * ActivePython + * Anaconda + * Canopy + * IronPython + * PyPy +``Python3_STDLIB`` + Standard platform independent installation directory. + + Information returned by + ``distutils.sysconfig.get_python_lib(plat_specific=False,standard_lib=True)`` + or else ``sysconfig.get_path('stdlib')``. +``Python3_STDARCH`` + Standard platform dependent installation directory. + + Information returned by + ``distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=True)`` + or else ``sysconfig.get_path('platstdlib')``. +``Python3_SITELIB`` + Third-party platform independent installation directory. + + Information returned by + ``distutils.sysconfig.get_python_lib(plat_specific=False,standard_lib=False)`` + or else ``sysconfig.get_path('purelib')``. +``Python3_SITEARCH`` + Third-party platform dependent installation directory. + + Information returned by + ``distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=False)`` + or else ``sysconfig.get_path('platlib')``. +``Python3_SOABI`` + Extension suffix for modules. + + Information returned by + ``distutils.sysconfig.get_config_var('SOABI')`` or computed from + ``distutils.sysconfig.get_config_var('EXT_SUFFIX')`` or + ``python3-config --extension-suffix``. If package ``distutils.sysconfig`` is + not available, ``sysconfig.get_config_var('SOABI')`` or + ``sysconfig.get_config_var('EXT_SUFFIX')`` are used. +``Python3_Compiler_FOUND`` + System has the Python 3 compiler. +``Python3_COMPILER`` + Path to the Python 3 compiler. Only offered by IronPython. +``Python3_COMPILER_ID`` + A short string unique to the compiler. Possible values include: + * IronPython +``Python3_DOTNET_LAUNCHER`` + The ``.Net`` interpreter. Only used by ``IronPython`` implementation. +``Python3_Development_FOUND`` + System has the Python 3 development artifacts. +``Python3_Development.Module_FOUND`` + System has the Python 3 development artifacts for Python module. +``Python3_Development.Embed_FOUND`` + System has the Python 3 development artifacts for Python embedding. +``Python3_INCLUDE_DIRS`` + The Python 3 include directories. +``Python3_LINK_OPTIONS`` + The Python 3 link options. Some configurations require specific link options + for a correct build and execution. +``Python3_LIBRARIES`` + The Python 3 libraries. +``Python3_LIBRARY_DIRS`` + The Python 3 library directories. +``Python3_RUNTIME_LIBRARY_DIRS`` + The Python 3 runtime library directories. +``Python3_VERSION`` + Python 3 version. +``Python3_VERSION_MAJOR`` + Python 3 major version. +``Python3_VERSION_MINOR`` + Python 3 minor version. +``Python3_VERSION_PATCH`` + Python 3 patch version. +``Python3_PyPy_VERSION`` + Python 3 PyPy version. +``Python3_NumPy_FOUND`` + System has the NumPy. +``Python3_NumPy_INCLUDE_DIRS`` + The NumPy include directories. +``Python3_NumPy_VERSION`` + The NumPy version. + +Hints +^^^^^ + +``Python3_ROOT_DIR`` + Define the root directory of a Python 3 installation. + +``Python3_USE_STATIC_LIBS`` + * If not defined, search for shared libraries and static libraries in that + order. + * If set to TRUE, search **only** for static libraries. + * If set to FALSE, search **only** for shared libraries. + +``Python3_FIND_ABI`` + This variable defines which ABIs, as defined in + `PEP 3149 `_, should be searched. + + .. note:: + + If ``Python3_FIND_ABI`` is not defined, any ABI will be searched. + + The ``Python3_FIND_ABI`` variable is a 3-tuple specifying, in that order, + ``pydebug`` (``d``), ``pymalloc`` (``m``) and ``unicode`` (``u``) flags. + Each element can be set to one of the following: + + * ``ON``: Corresponding flag is selected. + * ``OFF``: Corresponding flag is not selected. + * ``ANY``: The two possibilities (``ON`` and ``OFF``) will be searched. + + From this 3-tuple, various ABIs will be searched starting from the most + specialized to the most general. Moreover, ``debug`` versions will be + searched **after** ``non-debug`` ones. + + For example, if we have:: + + set (Python3_FIND_ABI "ON" "ANY" "ANY") + + The following flags combinations will be appended, in that order, to the + artifact names: ``dmu``, ``dm``, ``du``, and ``d``. + + And to search any possible ABIs:: + + set (Python3_FIND_ABI "ANY" "ANY" "ANY") + + The following combinations, in that order, will be used: ``mu``, ``m``, + ``u``, ````, ``dmu``, ``dm``, ``du`` and ``d``. + + .. note:: + + This hint is useful only on ``POSIX`` systems. So, on ``Windows`` systems, + when ``Python3_FIND_ABI`` is defined, ``Python`` distributions from + `python.org `_ will be found only if value for + each flag is ``OFF`` or ``ANY``. + +``Python3_FIND_STRATEGY`` + This variable defines how lookup will be done. + The ``Python3_FIND_STRATEGY`` variable can be set to one of the following: + + * ``VERSION``: Try to find the most recent version in all specified + locations. + This is the default if policy :policy:`CMP0094` is undefined or set to + ``OLD``. + * ``LOCATION``: Stops lookup as soon as a version satisfying version + constraints is founded. + This is the default if policy :policy:`CMP0094` is set to ``NEW``. + +``Python3_FIND_REGISTRY`` + On Windows the ``Python3_FIND_REGISTRY`` variable determine the order + of preference between registry and environment variables. + The ``Python3_FIND_REGISTRY`` variable can be set to one of the following: + + * ``FIRST``: Try to use registry before environment variables. + This is the default. + * ``LAST``: Try to use registry after environment variables. + * ``NEVER``: Never try to use registry. + +``Python3_FIND_FRAMEWORK`` + On macOS the ``Python3_FIND_FRAMEWORK`` variable determine the order of + preference between Apple-style and unix-style package components. + This variable can take same values as :variable:`CMAKE_FIND_FRAMEWORK` + variable. + + .. note:: + + Value ``ONLY`` is not supported so ``FIRST`` will be used instead. + + If ``Python3_FIND_FRAMEWORK`` is not defined, :variable:`CMAKE_FIND_FRAMEWORK` + variable will be used, if any. + +``Python3_FIND_VIRTUALENV`` + This variable defines the handling of virtual environments managed by + ``virtualenv`` or ``conda``. It is meaningful only when a virtual environment + is active (i.e. the ``activate`` script has been evaluated). In this case, it + takes precedence over ``Python3_FIND_REGISTRY`` and ``CMAKE_FIND_FRAMEWORK`` + variables. The ``Python3_FIND_VIRTUALENV`` variable can be set to one of the + following: + + * ``FIRST``: The virtual environment is used before any other standard + paths to look-up for the interpreter. This is the default. + * ``ONLY``: Only the virtual environment is used to look-up for the + interpreter. + * ``STANDARD``: The virtual environment is not used to look-up for the + interpreter but environment variable ``PATH`` is always considered. + In this case, variable ``Python3_FIND_REGISTRY`` (Windows) or + ``CMAKE_FIND_FRAMEWORK`` (macOS) can be set with value ``LAST`` or + ``NEVER`` to select preferably the interpreter from the virtual + environment. + + .. note:: + + If the component ``Development`` is requested, it is **strongly** + recommended to also include the component ``Interpreter`` to get expected + result. + +``Python3_FIND_IMPLEMENTATIONS`` + This variable defines, in an ordered list, the different implementations + which will be searched. The ``Python3_FIND_IMPLEMENTATIONS`` variable can + hold the following values: + + * ``CPython``: this is the standard implementation. Various products, like + ``Anaconda`` or ``ActivePython``, rely on this implementation. + * ``IronPython``: This implementation use the ``CSharp`` language for + ``.NET Framework`` on top of the `Dynamic Language Runtime` (``DLR``). + See `IronPython `_. + * ``PyPy``: This implementation use ``RPython`` language and + ``RPython translation toolchain`` to produce the python interpreter. + See `PyPy `_. + + The default value is: + + * Windows platform: ``CPython``, ``IronPython`` + * Other platforms: ``CPython`` + + .. note:: + + This hint has the lowest priority of all hints, so even if, for example, + you specify ``IronPython`` first and ``CPython`` in second, a python + product based on ``CPython`` can be selected because, for example with + ``Python3_FIND_STRATEGY=LOCATION``, each location will be search first for + ``IronPython`` and second for ``CPython``. + + .. note:: + + When ``IronPython`` is specified, on platforms other than ``Windows``, the + ``.Net`` interpreter (i.e. ``mono`` command) is expected to be available + through the ``PATH`` variable. + +``Python3_FIND_UNVERSIONED_NAMES`` + .. versionadded:: 3.20 + + This variable defines how the generic names will be searched. Currently, it + only applies to the generic names of the interpreter, namely, ``python3`` and + ``python``. + The ``Python3_FIND_UNVERSIONED_NAMES`` variable can be set to one of the + following values: + + * ``FIRST``: The generic names are searched before the more specialized ones + (such as ``python3.5`` for example). + * ``LAST``: The generic names are searched after the more specialized ones. + This is the default. + * ``NEVER``: The generic name are not searched at all. + +Artifacts Specification +^^^^^^^^^^^^^^^^^^^^^^^ + +To solve special cases, it is possible to specify directly the artifacts by +setting the following variables: + +``Python3_EXECUTABLE`` + The path to the interpreter. + +``Python3_COMPILER`` + The path to the compiler. + +``Python3_DOTNET_LAUNCHER`` + The ``.Net`` interpreter. Only used by ``IronPython`` implementation. + +``Python3_LIBRARY`` + The path to the library. It will be used to compute the + variables ``Python3_LIBRARIES``, ``Python3_LIBRARY_DIRS`` and + ``Python3_RUNTIME_LIBRARY_DIRS``. + +``Python3_INCLUDE_DIR`` + The path to the directory of the ``Python`` headers. It will be used to + compute the variable ``Python3_INCLUDE_DIRS``. + +``Python3_NumPy_INCLUDE_DIR`` + The path to the directory of the ``NumPy`` headers. It will be used to + compute the variable ``Python3_NumPy_INCLUDE_DIRS``. + +.. note:: + + All paths must be absolute. Any artifact specified with a relative path + will be ignored. + +.. note:: + + When an artifact is specified, all ``HINTS`` will be ignored and no search + will be performed for this artifact. + + If more than one artifact is specified, it is the user's responsibility to + ensure the consistency of the various artifacts. + +By default, this module supports multiple calls in different directories of a +project with different version/component requirements while providing correct +and consistent results for each call. To support this behavior, ``CMake`` cache +is not used in the traditional way which can be problematic for interactive +specification. So, to enable also interactive specification, module behavior +can be controlled with the following variable: + +``Python3_ARTIFACTS_INTERACTIVE`` + Selects the behavior of the module. This is a boolean variable: + + * If set to ``TRUE``: Create CMake cache entries for the above artifact + specification variables so that users can edit them interactively. + This disables support for multiple version/component requirements. + * If set to ``FALSE`` or undefined: Enable multiple version/component + requirements. + +Commands +^^^^^^^^ + +This module defines the command ``Python3_add_library`` (when +:prop_gbl:`CMAKE_ROLE` is ``PROJECT``), which has the same semantics as +:command:`add_library` and adds a dependency to target ``Python3::Python`` or, +when library type is ``MODULE``, to target ``Python3::Module`` and takes care +of Python module naming rules:: + + Python3_add_library ( [STATIC | SHARED | MODULE [WITH_SOABI]] + [ ...]) + +If the library type is not specified, ``MODULE`` is assumed. + +For ``MODULE`` library type, if option ``WITH_SOABI`` is specified, the +module suffix will include the ``Python3_SOABI`` value, if any. +#]=======================================================================] + + +set (_PYTHON_PREFIX Python3) + +set (_Python3_REQUIRED_VERSION_MAJOR 3) + +include (${CMAKE_CURRENT_LIST_DIR}/Support.cmake) + +if (COMMAND __Python3_add_library) + macro (Python3_add_library) + __Python3_add_library (Python3 ${ARGV}) + endmacro() +endif() + +unset (_PYTHON_PREFIX) diff --git a/cmake/modules/FindPython3/Support.cmake b/cmake/modules/FindPython3/Support.cmake new file mode 100644 index 0000000..d92ec06 --- /dev/null +++ b/cmake/modules/FindPython3/Support.cmake @@ -0,0 +1,3390 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# +# This file is a "template" file used by various FindPython modules. +# + +# +# Initial configuration +# + +cmake_policy(PUSH) +# numbers and boolean constants +cmake_policy (SET CMP0012 NEW) +# IN_LIST operator +cmake_policy (SET CMP0057 NEW) + +if (NOT DEFINED _PYTHON_PREFIX) + message (FATAL_ERROR "FindPython: INTERNAL ERROR") +endif() +if (NOT DEFINED _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + message (FATAL_ERROR "FindPython: INTERNAL ERROR") +endif() +if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "3") + set(_${_PYTHON_PREFIX}_VERSIONS 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) +elseif (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "2") + set(_${_PYTHON_PREFIX}_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0) +else() + message (FATAL_ERROR "FindPython: INTERNAL ERROR") +endif() + +get_property(_${_PYTHON_PREFIX}_CMAKE_ROLE GLOBAL PROPERTY CMAKE_ROLE) + +include (FindPackageHandleStandardArgs) + +# +# helper commands +# +macro (_PYTHON_DISPLAY_FAILURE _PYTHON_MSG) + if (${_PYTHON_PREFIX}_FIND_REQUIRED) + message (FATAL_ERROR "${_PYTHON_MSG}") + else() + if (NOT ${_PYTHON_PREFIX}_FIND_QUIETLY) + message(STATUS "${_PYTHON_MSG}") + endif () + endif() + + set (${_PYTHON_PREFIX}_FOUND FALSE) + string (TOUPPER "${_PYTHON_PREFIX}" _${_PYTHON_PREFIX}_UPPER_PREFIX) + set (${_PYTHON_UPPER_PREFIX}_FOUND FALSE) +endmacro() + + +function (_PYTHON_MARK_AS_INTERNAL) + foreach (var IN LISTS ARGV) + if (DEFINED CACHE{${var}}) + set_property (CACHE ${var} PROPERTY TYPE INTERNAL) + endif() + endforeach() +endfunction() + + +macro (_PYTHON_SELECT_LIBRARY_CONFIGURATIONS _PYTHON_BASENAME) + if(NOT DEFINED ${_PYTHON_BASENAME}_LIBRARY_RELEASE) + set(${_PYTHON_BASENAME}_LIBRARY_RELEASE "${_PYTHON_BASENAME}_LIBRARY_RELEASE-NOTFOUND") + endif() + if(NOT DEFINED ${_PYTHON_BASENAME}_LIBRARY_DEBUG) + set(${_PYTHON_BASENAME}_LIBRARY_DEBUG "${_PYTHON_BASENAME}_LIBRARY_DEBUG-NOTFOUND") + endif() + + get_property(_PYTHON_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if (${_PYTHON_BASENAME}_LIBRARY_DEBUG AND ${_PYTHON_BASENAME}_LIBRARY_RELEASE AND + NOT ${_PYTHON_BASENAME}_LIBRARY_DEBUG STREQUAL ${_PYTHON_BASENAME}_LIBRARY_RELEASE AND + (_PYTHON_isMultiConfig OR CMAKE_BUILD_TYPE)) + # if the generator is multi-config or if CMAKE_BUILD_TYPE is set for + # single-config generators, set optimized and debug libraries + set (${_PYTHON_BASENAME}_LIBRARIES "") + foreach (_PYTHON_libname IN LISTS ${_PYTHON_BASENAME}_LIBRARY_RELEASE) + list( APPEND ${_PYTHON_BASENAME}_LIBRARIES optimized "${_PYTHON_libname}") + endforeach() + foreach (_PYTHON_libname IN LISTS ${_PYTHON_BASENAME}_LIBRARY_DEBUG) + list( APPEND ${_PYTHON_BASENAME}_LIBRARIES debug "${_PYTHON_libname}") + endforeach() + elseif (${_PYTHON_BASENAME}_LIBRARY_RELEASE) + set (${_PYTHON_BASENAME}_LIBRARIES "${${_PYTHON_BASENAME}_LIBRARY_RELEASE}") + elseif (${_PYTHON_BASENAME}_LIBRARY_DEBUG) + set (${_PYTHON_BASENAME}_LIBRARIES "${${_PYTHON_BASENAME}_LIBRARY_DEBUG}") + else() + set (${_PYTHON_BASENAME}_LIBRARIES "${_PYTHON_BASENAME}_LIBRARY-NOTFOUND") + endif() +endmacro() + + +macro (_PYTHON_FIND_FRAMEWORKS) + if (CMAKE_HOST_APPLE OR APPLE) + file(TO_CMAKE_PATH "$ENV{CMAKE_FRAMEWORK_PATH}" _pff_CMAKE_FRAMEWORK_PATH) + set (_pff_frameworks ${CMAKE_FRAMEWORK_PATH} + ${_pff_CMAKE_FRAMEWORK_PATH} + ~/Library/Frameworks + /usr/local/Frameworks + ${CMAKE_SYSTEM_FRAMEWORK_PATH}) + if (_pff_frameworks) # Behavior change in CMake 3.14 + list (REMOVE_DUPLICATES _pff_frameworks) + endif () + foreach (_pff_implementation IN LISTS _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + unset (_${_PYTHON_PREFIX}_${_pff_implementation}_FRAMEWORKS) + if (_pff_implementation STREQUAL "CPython") + foreach (_pff_framework IN LISTS _pff_frameworks) + if (EXISTS ${_pff_framework}/Python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}.framework) + list (APPEND _${_PYTHON_PREFIX}_${_pff_implementation}_FRAMEWORKS ${_pff_framework}/Python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}.framework) + endif() + if (EXISTS ${_pff_framework}/Python.framework) + list (APPEND _${_PYTHON_PREFIX}_${_pff_implementation}_FRAMEWORKS ${_pff_framework}/Python.framework) + endif() + endforeach() + elseif (_pff_implementation STREQUAL "IronPython") + foreach (_pff_framework IN LISTS _pff_frameworks) + if (EXISTS ${_pff_framework}/IronPython.framework) + list (APPEND _${_PYTHON_PREFIX}_${_pff_implementation}_FRAMEWORKS ${_pff_framework}/IronPython.framework) + endif() + endforeach() + endif() + endforeach() + unset (_pff_implementation) + unset (_pff_frameworks) + unset (_pff_framework) + endif() +endmacro() + +function (_PYTHON_GET_FRAMEWORKS _PYTHON_PGF_FRAMEWORK_PATHS) + cmake_parse_arguments (PARSE_ARGV 1 _PGF "" "" "IMPLEMENTATIONS;VERSION") + + if (NOT _PGF_IMPLEMENTATIONS) + set (_PGF_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}) + endif() + + set (framework_paths) + + foreach (implementation IN LISTS _PGF_IMPLEMENTATIONS) + if (implementation STREQUAL "CPython") + foreach (version IN LISTS _PGF_VERSION) + foreach (framework IN LISTS _${_PYTHON_PREFIX}_${implementation}_FRAMEWORKS) + if (EXISTS "${framework}/Versions/${version}") + list (APPEND framework_paths "${framework}/Versions/${version}") + endif() + endforeach() + endforeach() + elseif (implementation STREQUAL "IronPython") + foreach (version IN LISTS _PGF_VERSION) + foreach (framework IN LISTS _${_PYTHON_PREFIX}_${implementation}_FRAMEWORKS) + # pick-up all available versions + file (GLOB versions LIST_DIRECTORIES true RELATIVE "${framework}/Versions/" + "${framework}/Versions/${version}*") + list (SORT versions ORDER DESCENDING) + list (TRANSFORM versions PREPEND "${framework}/Versions/") + list (APPEND framework_paths ${versions}) + endforeach() + endforeach() + endif() + endforeach() + + set (${_PYTHON_PGF_FRAMEWORK_PATHS} ${framework_paths} PARENT_SCOPE) +endfunction() + +function (_PYTHON_GET_REGISTRIES _PYTHON_PGR_REGISTRY_PATHS) + cmake_parse_arguments (PARSE_ARGV 1 _PGR "" "" "IMPLEMENTATIONS;VERSION") + + if (NOT _PGR_IMPLEMENTATIONS) + set (_PGR_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}) + endif() + + set (registries) + + foreach (implementation IN LISTS _PGR_IMPLEMENTATIONS) + if (implementation STREQUAL "CPython") + foreach (version IN LISTS _PGR_VERSION) + string (REPLACE "." "" version_no_dots ${version}) + list (APPEND registries + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${version}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${version}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]) + if (version VERSION_GREATER_EQUAL "3.5") + get_filename_component (arch "[HKEY_CURRENT_USER\\Software\\Python\\PythonCore\\${version};SysArchitecture]" NAME) + if (arch MATCHES "(${_${_PYTHON_PREFIX}_ARCH}|${_${_PYTHON_PREFIX}_ARCH2})bit") + list (APPEND registries + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${version}\\InstallPath]) + endif() + else() + list (APPEND registries + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${version}\\InstallPath]) + endif() + list (APPEND registries + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${version}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${version}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${version}\\InstallPath] + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]) + endforeach() + elseif (implementation STREQUAL "IronPython") + foreach (version IN LISTS _PGR_VERSION) + list (APPEND registries [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${version}\\InstallPath]) + endforeach() + endif() + endforeach() + + set (${_PYTHON_PGR_REGISTRY_PATHS} "${registries}" PARENT_SCOPE) +endfunction() + + +function (_PYTHON_GET_ABIFLAGS _PGABIFLAGS) + set (abiflags) + list (GET _${_PYTHON_PREFIX}_FIND_ABI 0 pydebug) + list (GET _${_PYTHON_PREFIX}_FIND_ABI 1 pymalloc) + list (GET _${_PYTHON_PREFIX}_FIND_ABI 2 unicode) + + if (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ANY") + set (abiflags "mu" "m" "u" "") + elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ON") + set (abiflags "mu" "u") + elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "OFF") + set (abiflags "m" "") + elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY") + set (abiflags "mu" "m") + elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ON") + set (abiflags "mu") + elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "OFF") + set (abiflags "m") + elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY") + set (abiflags "u" "") + elseif (pymalloc STREQUAL "OFF" AND unicode STREQUAL "ON") + set (abiflags "u") + endif() + + if (pydebug STREQUAL "ON") + if (abiflags) + list (TRANSFORM abiflags PREPEND "d") + else() + set (abiflags "d") + endif() + elseif (pydebug STREQUAL "ANY") + if (abiflags) + set (flags "${abiflags}") + list (TRANSFORM flags PREPEND "d") + list (APPEND abiflags "${flags}") + else() + set (abiflags "" "d") + endif() + endif() + + set (${_PGABIFLAGS} "${abiflags}" PARENT_SCOPE) +endfunction() + +function (_PYTHON_GET_PATH_SUFFIXES _PYTHON_PGPS_PATH_SUFFIXES) + cmake_parse_arguments (PARSE_ARGV 1 _PGPS "INTERPRETER;COMPILER;LIBRARY;INCLUDE" "" "IMPLEMENTATIONS;VERSION") + + if (NOT _PGPS_IMPLEMENTATIONS) + set (_PGPS_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}) + endif() + + if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS) + set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") + else() + set (abi "mu" "m" "u" "") + endif() + + set (path_suffixes) + + foreach (implementation IN LISTS _PGPS_IMPLEMENTATIONS) + if (implementation STREQUAL "CPython") + if (_PGPS_INTERPRETER) + list (APPEND path_suffixes bin Scripts) + else() + foreach (version IN LISTS _PGPS_VERSION) + if (_PGPS_LIBRARY) + if (CMAKE_LIBRARY_ARCHITECTURE) + list (APPEND path_suffixes lib/${CMAKE_LIBRARY_ARCHITECTURE}) + endif() + list (APPEND path_suffixes lib libs) + + if (CMAKE_LIBRARY_ARCHITECTURE) + set (suffixes "${abi}") + if (suffixes) + list (TRANSFORM suffixes PREPEND "lib/python${_PGPS_VERSION}/config-${_PGPS_VERSION}") + list (TRANSFORM suffixes APPEND "-${CMAKE_LIBRARY_ARCHITECTURE}") + else() + set (suffixes "lib/python${_PGPS_VERSION}/config-${_PGPS_VERSION}-${CMAKE_LIBRARY_ARCHITECTURE}") + endif() + list (APPEND path_suffixes ${suffixes}) + endif() + set (suffixes "${abi}") + if (suffixes) + list (TRANSFORM suffixes PREPEND "lib/python${_PGPS_VERSION}/config-${_PGPS_VERSION}") + else() + set (suffixes "lib/python${_PGPS_VERSION}/config-${_PGPS_VERSION}") + endif() + list (APPEND path_suffixes ${suffixes}) + elseif (_PGPS_INCLUDE) + set (suffixes "${abi}") + if (suffixes) + list (TRANSFORM suffixes PREPEND "include/python${_PGPS_VERSION}") + else() + set (suffixes "include/python${_PGPS_VERSION}") + endif() + list (APPEND path_suffixes ${suffixes} include) + endif() + endforeach() + endif() + elseif (implementation STREQUAL "IronPython") + if (_PGPS_INTERPRETER OR _PGPS_COMPILER) + foreach (version IN LISTS _PGPS_VERSION) + list (APPEND path_suffixes "share/ironpython${version}") + endforeach() + list (APPEND path_suffixes ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}) + endif() + elseif (implementation STREQUAL "PyPy") + if (_PGPS_INTERPRETER) + list (APPEND path_suffixes ${_${_PYTHON_PREFIX}_PYPY_EXECUTABLE_PATH_SUFFIXES}) + elseif (_PGPS_LIBRARY) + list (APPEND path_suffixes ${_${_PYTHON_PREFIX}_PYPY_LIBRARY_PATH_SUFFIXES}) + elseif (_PGPS_INCLUDE) + list (APPEND path_suffixes ${_${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES}) + endif() + endif() + endforeach() + if (path_suffixes) # Behavior change in CMake 3.14 + list (REMOVE_DUPLICATES path_suffixes) + endif () + + set (${_PYTHON_PGPS_PATH_SUFFIXES} ${path_suffixes} PARENT_SCOPE) +endfunction() + +function (_PYTHON_GET_NAMES _PYTHON_PGN_NAMES) + cmake_parse_arguments (PARSE_ARGV 1 _PGN "POSIX;INTERPRETER;COMPILER;CONFIG;LIBRARY;WIN32;DEBUG" "" "IMPLEMENTATIONS;VERSION") + + if (NOT _PGN_IMPLEMENTATIONS) + set (_PGN_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}) + endif() + + set (names) + + foreach (implementation IN LISTS _PGN_IMPLEMENTATIONS) + if (implementation STREQUAL "CPython") + if (_PGN_INTERPRETER AND _${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES STREQUAL "FIRST") + list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) + endif() + foreach (version IN LISTS _PGN_VERSION) + if (_PGN_WIN32) + string (REPLACE "." "" version_no_dots ${version}) + + set (name python${version_no_dots}) + if (_PGN_DEBUG) + string (APPEND name "_d") + endif() + + list (APPEND names "${name}") + endif() + + if (_PGN_POSIX) + if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS) + set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") + else() + if (_PGN_INTERPRETER OR _PGN_CONFIG) + set (abi "") + else() + set (abi "mu" "m" "u" "") + endif() + endif() + + if (abi) + if (_PGN_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) + set (abinames "${abi}") + list (TRANSFORM abinames PREPEND "${CMAKE_LIBRARY_ARCHITECTURE}-python${version}") + list (TRANSFORM abinames APPEND "-config") + list (APPEND names ${abinames}) + endif() + set (abinames "${abi}") + list (TRANSFORM abinames PREPEND "python${version}") + if (_PGN_CONFIG) + list (TRANSFORM abinames APPEND "-config") + endif() + list (APPEND names ${abinames}) + else() + unset (abinames) + if (_PGN_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) + set (abinames "${CMAKE_LIBRARY_ARCHITECTURE}-python${version}") + endif() + list (APPEND abinames "python${version}") + if (_PGN_CONFIG) + list (TRANSFORM abinames APPEND "-config") + endif() + list (APPEND names ${abinames}) + endif() + endif() + endforeach() + if (_PGN_INTERPRETER AND _${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES STREQUAL "LAST") + list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) + endif() + elseif (implementation STREQUAL "IronPython") + if (_PGN_INTERPRETER) + if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Do not use wrapper script on Linux because it is buggy: -c interpreter option cannot be used + foreach (version IN LISTS _PGN_VERSION) + list (APPEND names "ipy${version}") + endforeach() + endif() + list (APPEND names ${_${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES}) + elseif (_PGN_COMPILER) + list (APPEND names ${_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES}) + endif() + elseif (implementation STREQUAL "PyPy") + if (_PGN_INTERPRETER) + list (APPEND names ${_${_PYTHON_PREFIX}_PYPY_NAMES}) + elseif (_PGN_LIBRARY) + if (_PGN_WIN32) + foreach (version IN LISTS _PGN_VERSION) + string (REPLACE "." "" version_no_dots ${version}) + + set (name "python${version_no_dots}") + if (_PGN_DEBUG) + string (APPEND name "_d") + endif() + list (APPEND names "${name}") + endforeach() + endif() + list (APPEND names ${_${_PYTHON_PREFIX}_PYPY_LIB_NAMES}) + endif() + endif() + endforeach() + + set (${_PYTHON_PGN_NAMES} ${names} PARENT_SCOPE) +endfunction() + +function (_PYTHON_GET_CONFIG_VAR _PYTHON_PGCV_VALUE NAME) + unset (${_PYTHON_PGCV_VALUE} PARENT_SCOPE) + + if (NOT NAME MATCHES "^(PREFIX|ABIFLAGS|CONFIGDIR|INCLUDES|LIBS|SOABI)$") + return() + endif() + + if (_${_PYTHON_PREFIX}_CONFIG) + if (NAME STREQUAL "SOABI") + set (config_flag "--extension-suffix") + else() + set (config_flag "--${NAME}") + endif() + string (TOLOWER "${config_flag}" config_flag) + execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" ${config_flag} + RESULT_VARIABLE _result + OUTPUT_VARIABLE _values + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_result) + unset (_values) + else() + if (NAME STREQUAL "INCLUDES") + # do some clean-up + string (REGEX MATCHALL "(-I|-iwithsysroot)[ ]*[^ ]+" _values "${_values}") + string (REGEX REPLACE "(-I|-iwithsysroot)[ ]*" "" _values "${_values}") + if (_values) # Behavior change in CMake 3.14 + list (REMOVE_DUPLICATES _values) + endif () + elseif (NAME STREQUAL "SOABI") + # clean-up: remove prefix character and suffix + if (_values MATCHES "^(\\.${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$") + set(_values "") + else() + string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}") + endif() + endif() + endif() + endif() + + if (_${_PYTHON_PREFIX}_EXECUTABLE AND NOT CMAKE_CROSSCOMPILING) + if (NAME STREQUAL "PREFIX") + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n from distutils import sysconfig\n sys.stdout.write(';'.join([sysconfig.PREFIX,sysconfig.EXEC_PREFIX,sysconfig.BASE_EXEC_PREFIX]))\nexcept Exception:\n import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_config_var('base') or '', sysconfig.get_config_var('installed_base') or '']))" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _values + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_result) + unset (_values) + else() + if (_values) # Behavior change in CMake 3.14 + list (REMOVE_DUPLICATES _values) + endif () + endif() + elseif (NAME STREQUAL "INCLUDES") + if (WIN32) + set (_scheme "nt") + else() + set (_scheme "posix_prefix") + endif() + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys\ntry:\n from distutils import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_python_inc(plat_specific=True),sysconfig.get_python_inc(plat_specific=False)]))\nexcept Exception:\n import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_path('platinclude'),sysconfig.get_path('platinclude','${_scheme}'),sysconfig.get_path('include'),sysconfig.get_path('include','${_scheme}')]))" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _values + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_result) + unset (_values) + else() + if (_values) # Behavior change in CMake 3.14 + list (REMOVE_DUPLICATES _values) + endif () + endif() + elseif (NAME STREQUAL "SOABI") + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys\ntry:\n from distutils import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_config_var('SOABI') or '',sysconfig.get_config_var('EXT_SUFFIX') or '',sysconfig.get_config_var('SO') or '']))\nexcept Exception:\n import sysconfig;sys.stdout.write(';'.join([sysconfig.get_config_var('SOABI') or '',sysconfig.get_config_var('EXT_SUFFIX') or '',sysconfig.get_config_var('SO') or '']))" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _soabi + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_result) + unset (_values) + else() + foreach (_item IN LISTS _soabi) + if (_item) + set (_values "${_item}") + break() + endif() + endforeach() + if (_values) + # clean-up: remove prefix character and suffix + if (_values MATCHES "^(\\.${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$") + set(_values "") + else() + string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}") + endif() + endif() + endif() + else() + set (config_flag "${NAME}") + if (NAME STREQUAL "CONFIGDIR") + set (config_flag "LIBPL") + endif() + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys\ntry:\n from distutils import sysconfig\n sys.stdout.write(sysconfig.get_config_var('${config_flag}'))\nexcept Exception:\n import sysconfig\n sys.stdout.write(sysconfig.get_config_var('${config_flag}'))" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _values + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_result) + unset (_values) + endif() + endif() + endif() + + if (NAME STREQUAL "ABIFLAGS" OR NAME STREQUAL "SOABI") + set (${_PYTHON_PGCV_VALUE} "${_values}" PARENT_SCOPE) + return() + endif() + + if (NOT _values OR _values STREQUAL "None") + return() + endif() + + if (NAME STREQUAL "LIBS") + # do some clean-up + string (REGEX MATCHALL "-(l|framework)[ ]*[^ ]+" _values "${_values}") + # remove elements relative to python library itself + list (FILTER _values EXCLUDE REGEX "-lpython") + if (_values) # Behavior change in CMake 3.14 + list (REMOVE_DUPLICATES _values) + endif () + endif() + + if (WIN32 AND NAME MATCHES "^(PREFIX|CONFIGDIR|INCLUDES)$") + file (TO_CMAKE_PATH "${_values}" _values) + endif() + + set (${_PYTHON_PGCV_VALUE} "${_values}" PARENT_SCOPE) +endfunction() + +function (_PYTHON_GET_VERSION) + cmake_parse_arguments (PARSE_ARGV 0 _PGV "LIBRARY;INCLUDE" "PREFIX" "") + + unset (${_PGV_PREFIX}VERSION PARENT_SCOPE) + unset (${_PGV_PREFIX}VERSION_MAJOR PARENT_SCOPE) + unset (${_PGV_PREFIX}VERSION_MINOR PARENT_SCOPE) + unset (${_PGV_PREFIX}VERSION_PATCH PARENT_SCOPE) + unset (${_PGV_PREFIX}ABI PARENT_SCOPE) + + if (_PGV_LIBRARY) + # retrieve version and abi from library name + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) + get_filename_component (library_name "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" NAME) + # extract version from library name + if (library_name MATCHES "python([23])([0-9]+)") + set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE) + set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) + elseif (library_name MATCHES "python([23])\\.([0-9]+)([dmu]*)") + set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE) + set (${_PGV_PREFIX}ABI "${CMAKE_MATCH_3}" PARENT_SCOPE) + elseif (library_name MATCHES "pypy(3)?-c") + set (version "${CMAKE_MATCH_1}") + if (version EQUAL "3") + set (${_PGV_PREFIX}VERSION_MAJOR "3" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION "3" PARENT_SCOPE) + else() + set (${_PGV_PREFIX}VERSION_MAJOR "2" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION "2" PARENT_SCOPE) + endif() + set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) + endif() + endif() + else() + if (_${_PYTHON_PREFIX}_INCLUDE_DIR) + # retrieve version from header file + file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/patchlevel.h" version + REGEX "^#define[ \t]+PY_VERSION[ \t]+\"[^\"]+\"") + string (REGEX REPLACE "^#define[ \t]+PY_VERSION[ \t]+\"([^\"]+)\".*" "\\1" + version "${version}") + string (REGEX MATCHALL "[0-9]+" versions "${version}") + list (GET versions 0 version_major) + list (GET versions 1 version_minor) + list (GET versions 2 version_patch) + + set (${_PGV_PREFIX}VERSION "${version_major}.${version_minor}.${version_patch}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION_MAJOR ${version_major} PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION_MINOR ${version_minor} PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION_PATCH ${version_patch} PARENT_SCOPE) + + # compute ABI flags + if (version_major VERSION_GREATER "2") + file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/pyconfig.h" config REGEX "(Py_DEBUG|WITH_PYMALLOC|Py_UNICODE_SIZE|MS_WIN32)") + set (abi) + if (config MATCHES "#[ ]*define[ ]+MS_WIN32") + # ABI not used on Windows + set (abi "") + else() + if (NOT config) + # pyconfig.h can be a wrapper to a platform specific pyconfig.h + # In this case, try to identify ABI from include directory + if (_${_PYTHON_PREFIX}_INCLUDE_DIR MATCHES "python${version_major}\\.${version_minor}+([dmu]*)") + set (abi "${CMAKE_MATCH_1}") + else() + set (abi "") + endif() + else() + if (config MATCHES "#[ ]*define[ ]+Py_DEBUG[ ]+1") + string (APPEND abi "d") + endif() + if (config MATCHES "#[ ]*define[ ]+WITH_PYMALLOC[ ]+1") + string (APPEND abi "m") + endif() + if (config MATCHES "#[ ]*define[ ]+Py_UNICODE_SIZE[ ]+4") + string (APPEND abi "u") + endif() + endif() + set (${_PGV_PREFIX}ABI "${abi}" PARENT_SCOPE) + endif() + else() + # ABI not supported + set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) + endif() + endif() + endif() +endfunction() + +function (_PYTHON_GET_LAUNCHER _PYTHON_PGL_NAME) + cmake_parse_arguments (PARSE_ARGV 1 _PGL "INTERPRETER;COMPILER" "" "") + + unset ({_PYTHON_PGL_NAME} PARENT_SCOPE) + + if ((_PGL_INTERPRETER AND NOT _${_PYTHON_PREFIX}_EXECUTABLE) + OR (_PGL_COMPILER AND NOT _${_PYTHON_PREFIX}_COMPILER)) + return() + endif() + + if ("IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS + AND NOT SYSTEM_NAME MATCHES "Windows|Linux") + if (_PGL_INTERPRETER) + get_filename_component (name "${_${_PYTHON_PREFIX}_EXECUTABLE}" NAME) + get_filename_component (ext "${_${_PYTHON_PREFIX}_EXECUTABLE}" LAST_EXT) + if (name IN_LIST _${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES + AND ext STREQUAL ".exe") + set (${_PYTHON_PGL_NAME} "${${_PYTHON_PREFIX}_DOTNET_LAUNCHER}" PARENT_SCOPE) + endif() + else() + get_filename_component (name "${_${_PYTHON_PREFIX}_COMPILER}" NAME) + get_filename_component (ext "${_${_PYTHON_PREFIX}_COMPILER}" LAST_EXT) + if (name IN_LIST _${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES + AND ext STREQUAL ".exe") + set (${_PYTHON_PGL_NAME} "${${_PYTHON_PREFIX}_DOTNET_LAUNCHER}" PARENT_SCOPE) + endif() + endif() + endif() +endfunction() + + +function (_PYTHON_VALIDATE_INTERPRETER) + if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) + return() + endif() + + cmake_parse_arguments (PARSE_ARGV 0 _PVI "IN_RANGE;EXACT;CHECK_EXISTS" "VERSION" "") + + if (_PVI_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_EXECUTABLE}") + # interpreter does not exist anymore + set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot find the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + endif() + + _python_get_launcher (launcher INTERPRETER) + + # validate ABI compatibility + if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI) + execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; sys.stdout.write(sys.abiflags)" + RESULT_VARIABLE result + OUTPUT_VARIABLE abi + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (result) + # assume ABI is not supported + set (abi "") + endif() + if (NOT abi IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) + # incompatible ABI + set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong ABI for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + endif() + endif() + + if (_PVI_IN_RANGE OR _PVI_VERSION) + # retrieve full version + execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))" + RESULT_VARIABLE result + OUTPUT_VARIABLE version + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (result) + # interpreter is not usable + set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + endif() + + if (_PVI_VERSION) + # check against specified version + ## compute number of components for version + string (REGEX REPLACE "[^.]" "" dots "${_PVI_VERSION}") + ## add one dot because there is one dot less than there are components + string (LENGTH "${dots}." count) + if (count GREATER 3) + set (count 3) + endif() + set (version_regex "^[0-9]+") + if (count EQUAL 3) + string (APPEND version_regex "\\.[0-9]+\\.[0-9]+") + elseif (count EQUAL 2) + string (APPEND version_regex "\\.[0-9]+") + endif() + # extract needed range + string (REGEX MATCH "${version_regex}" version "${version}") + + if (_PVI_EXACT AND NOT version VERSION_EQUAL _PVI_VERSION) + # interpreter has wrong version + set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + else() + # check that version is OK + string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" major_version "${version}") + string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" expected_major_version "${_PVI_VERSION}") + if (NOT major_version VERSION_EQUAL expected_major_version + OR NOT version VERSION_GREATER_EQUAL _PVI_VERSION) + set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + endif() + endif() + endif() + + if (_PVI_IN_RANGE) + # check if version is in the requested range + find_package_check_version ("${version}" in_range HANDLE_VERSION_RANGE) + if (NOT in_range) + # interpreter has invalid version + set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + endif() + endif() + else() + get_filename_component (python_name "${_${_PYTHON_PREFIX}_EXECUTABLE}" NAME) + if (NOT python_name STREQUAL "python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}${CMAKE_EXECUTABLE_SUFFIX}") + # executable found do not have version in name + # ensure major version is OK + execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; sys.stdout.write(str(sys.version_info[0]))" + RESULT_VARIABLE result + OUTPUT_VARIABLE version + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (result OR NOT version EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + # interpreter not usable or has wrong major version + if (result) + set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + else() + set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong major version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + endif() + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + endif() + endif() + endif() + + if (CMAKE_SIZEOF_VOID_P AND ("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + AND NOT CMAKE_CROSSCOMPILING) + # In this case, interpreter must have same architecture as environment + execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys, struct; sys.stdout.write(str(struct.calcsize(\"P\")))" + RESULT_VARIABLE result + OUTPUT_VARIABLE size + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (result OR NOT size EQUAL CMAKE_SIZEOF_VOID_P) + # interpreter not usable or has wrong architecture + if (result) + set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + else() + set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong architecture for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE) + endif() + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + endif() + endif() +endfunction() + + +function (_PYTHON_VALIDATE_COMPILER) + if (NOT _${_PYTHON_PREFIX}_COMPILER) + return() + endif() + + cmake_parse_arguments (PARSE_ARGV 0 _PVC "IN_RANGE;EXACT;CHECK_EXISTS" "VERSION" "") + + if (_PVC_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_COMPILER}") + # Compiler does not exist anymore + set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Cannot find the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") + return() + endif() + + _python_get_launcher (launcher COMPILER) + + # retrieve python environment version from compiler + set (working_dir "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/PythonCompilerVersion.dir") + file (WRITE "${working_dir}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))\n") + execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_COMPILER}" + ${_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS} + /target:exe /embed "${working_dir}/version.py" + WORKING_DIRECTORY "${working_dir}" + OUTPUT_QUIET + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + get_filename_component (ir_dir "${_${_PYTHON_PREFIX}_COMPILER}" DIRECTORY) + execute_process (COMMAND "${CMAKE_COMMAND}" -E env "MONO_PATH=${ir_dir}" + ${${_PYTHON_PREFIX}_DOTNET_LAUNCHER} "${working_dir}/version.exe" + WORKING_DIRECTORY "${working_dir}" + RESULT_VARIABLE result + OUTPUT_VARIABLE version + ERROR_QUIET) + file (REMOVE_RECURSE "${working_dir}") + if (result) + # compiler is not usable + set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Cannot use the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") + return() + endif() + + if (_PVC_VERSION OR _PVC_IN_RANGE) + if (_PVC_VERSION) + # check against specified version + ## compute number of components for version + string (REGEX REPLACE "[^.]" "" dots "${_PVC_VERSION}") + ## add one dot because there is one dot less than there are components + string (LENGTH "${dots}." count) + if (count GREATER 3) + set (count 3) + endif() + set (version_regex "^[0-9]+") + if (count EQUAL 3) + string (APPEND version_regex "\\.[0-9]+\\.[0-9]+") + elseif (count EQUAL 2) + string (APPEND version_regex "\\.[0-9]+") + endif() + # extract needed range + string (REGEX MATCH "${version_regex}" version "${version}") + + if (_PVC_EXACT AND NOT version VERSION_EQUAL _PVC_VERSION) + # interpreter has wrong version + set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") + return() + else() + # check that version is OK + string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" major_version "${version}") + string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" expected_major_version "${_PVC_VERSION}") + if (NOT major_version VERSION_EQUAL expected_major_version + OR NOT version VERSION_GREATER_EQUAL _PVC_VERSION) + set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") + return() + endif() + endif() + endif() + + if (_PVC_IN_RANGE) + # check if version is in the requested range + find_package_check_version ("${version}" in_range HANDLE_VERSION_RANGE) + if (NOT in_range) + # interpreter has invalid version + set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") + return() + endif() + endif() + else() + string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" major_version "${version}") + if (NOT major_version EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + # Compiler has wrong major version + set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Wrong major version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") + return() + endif() + endif() +endfunction() + + +function (_PYTHON_VALIDATE_LIBRARY) + if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG) + return() + endif() + + cmake_parse_arguments (PARSE_ARGV 0 _PVL "IN_RANGE;EXACT;CHECK_EXISTS" "VERSION" "") + + if (_PVL_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") + # library does not exist anymore + set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") + if (WIN32) + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND") + endif() + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + return() + endif() + + # retrieve version and abi from library name + _python_get_version (LIBRARY PREFIX lib_) + + if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT lib_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) + # incompatible ABI + set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong ABI for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") + else() + if (_PVL_VERSION OR _PVL_IN_RANGE) + if (_PVL_VERSION) + # library have only major.minor information + string (REGEX MATCH "[0-9](\\.[0-9]+)?" version "${_PVL_VERSION}") + if ((_PVL_EXACT AND NOT lib_VERSION VERSION_EQUAL version) OR (lib_VERSION VERSION_LESS version)) + # library has wrong version + set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") + endif() + endif() + + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND _PVL_IN_RANGE) + # check if library version is in the requested range + find_package_check_version ("${lib_VERSION}" in_range HANDLE_VERSION_RANGE) + if (NOT in_range) + # library has wrong version + set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") + endif() + endif() + else() + if (NOT lib_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + # library has wrong major version + set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong major version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") + endif() + endif() + endif() + + if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + if (WIN32) + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND") + endif() + unset (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE CACHE) + unset (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG CACHE) + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + endif() +endfunction() + + +function (_PYTHON_VALIDATE_INCLUDE_DIR) + if (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) + return() + endif() + + cmake_parse_arguments (PARSE_ARGV 0 _PVID "IN_RANGE;EXACT;CHECK_EXISTS" "VERSION" "") + + if (_PVID_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}") + # include file does not exist anymore + set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + return() + endif() + + # retrieve version from header file + _python_get_version (INCLUDE PREFIX inc_) + + if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT inc_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) + # incompatible ABI + set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong ABI for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + else() + if (_PVID_VERSION OR _PVID_IN_RANGE) + if (_PVID_VERSION) + if ((_PVID_EXACT AND NOT inc_VERSION VERSION_EQUAL expected_version) OR (inc_VERSION VERSION_LESS expected_version)) + # include dir has wrong version + set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + endif() + endif() + + if (_${_PYTHON_PREFIX}_INCLUDE_DIR AND PVID_IN_RANGE) + # check if include dir is in the request range + find_package_check_version ("${inc_VERSION}" in_range HANDLE_VERSION_RANGE) + if (NOT in_range) + # include dir has wrong version + set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + endif() + endif() + else() + if (NOT inc_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + # include dir has wrong major version + set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong major version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE) + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + endif() + endif() + endif() +endfunction() + + +function (_PYTHON_FIND_RUNTIME_LIBRARY _PYTHON_LIB) + string (REPLACE "_RUNTIME" "" _PYTHON_LIB "${_PYTHON_LIB}") + # look at runtime part on systems supporting it + if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR + (CMAKE_SYSTEM_NAME MATCHES "MSYS|CYGWIN" + AND ${_PYTHON_LIB} MATCHES "${CMAKE_IMPORT_LIBRARY_SUFFIX}$")) + set (CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX}) + # MSYS has a special syntax for runtime libraries + if (CMAKE_SYSTEM_NAME MATCHES "MSYS") + list (APPEND CMAKE_FIND_LIBRARY_PREFIXES "msys-") + endif() + find_library (${ARGV}) + endif() +endfunction() + + +function (_PYTHON_SET_LIBRARY_DIRS _PYTHON_SLD_RESULT) + unset (_PYTHON_DIRS) + set (_PYTHON_LIBS ${ARGN}) + foreach (_PYTHON_LIB IN LISTS _PYTHON_LIBS) + if (${_PYTHON_LIB}) + get_filename_component (_PYTHON_DIR "${${_PYTHON_LIB}}" DIRECTORY) + list (APPEND _PYTHON_DIRS "${_PYTHON_DIR}") + endif() + endforeach() + if (_PYTHON_DIRS) # Behavior change in CMake 3.14 + list (REMOVE_DUPLICATES _PYTHON_DIRS) + endif () + set (${_PYTHON_SLD_RESULT} ${_PYTHON_DIRS} PARENT_SCOPE) +endfunction() + + +function (_PYTHON_SET_DEVELOPMENT_MODULE_FOUND module) + if ("Development.${module}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + string(TOUPPER "${module}" id) + set (module_found TRUE) + + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS + AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + set (module_found FALSE) + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS + AND NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) + set (module_found FALSE) + endif() + + set (${_PYTHON_PREFIX}_Development.${module}_FOUND ${module_found} PARENT_SCOPE) + endif() +endfunction() + + +if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + # range must include internal major version + if (${_PYTHON_PREFIX}_FIND_VERSION_MIN_MAJOR VERSION_GREATER _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR + OR ((${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" + AND ${_PYTHON_PREFIX}_FIND_VERSION_MAX VERSION_LESS _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + OR (${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" + AND ${_PYTHON_PREFIX}_FIND_VERSION_MAX VERSION_LESS_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR))) + _python_display_failure ("Could NOT find ${_PYTHON_PREFIX}: Wrong version range specified is \"${${_PYTHON_PREFIX}_FIND_VERSION_RANGE}\", but expected version range must include major version \"${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}\"") + + cmake_policy(POP) + return() + endif() +else() + if (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION_MAJOR + AND NOT ${_PYTHON_PREFIX}_FIND_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + # If major version is specified, it must be the same as internal major version + _python_display_failure ("Could NOT find ${_PYTHON_PREFIX}: Wrong major version specified is \"${${_PYTHON_PREFIX}_FIND_VERSION_MAJOR}\", but expected major version is \"${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}\"") + + cmake_policy(POP) + return() + endif() +endif() + + +# handle components +if (NOT ${_PYTHON_PREFIX}_FIND_COMPONENTS) + set (${_PYTHON_PREFIX}_FIND_COMPONENTS Interpreter) + set (${_PYTHON_PREFIX}_FIND_REQUIRED_Interpreter TRUE) +endif() +if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + list (APPEND ${_PYTHON_PREFIX}_FIND_COMPONENTS "Interpreter" "Development.Module") +endif() +if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + list (APPEND ${_PYTHON_PREFIX}_FIND_COMPONENTS "Development.Module" "Development.Embed") +endif() +if (${_PYTHON_PREFIX}_FIND_COMPONENTS) # Behavior change in CMake 3.14 + list (REMOVE_DUPLICATES ${_PYTHON_PREFIX}_FIND_COMPONENTS) +endif () +foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Interpreter Compiler Development Development.Module Development.Embed NumPy) + set (${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_FOUND FALSE) +endforeach() +if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development) + set (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.Module TRUE) + set (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.Embed TRUE) +endif() + +unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) +unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS) +unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS) +if ("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + if (CMAKE_SYSTEM_NAME MATCHES "^(Windows.*|CYGWIN|MSYS)$") + list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS "LIBRARY") + endif() + list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS "INCLUDE_DIR") +endif() +if ("Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS "LIBRARY" "INCLUDE_DIR") +endif() +set (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS ${_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS} ${_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS}) +if (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) # Behavior change in CMake 3.14 + list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) +endif () + +# Set versions to search +## default: search any version +set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${_${_PYTHON_PREFIX}_VERSIONS}) +unset (_${_PYTHON_PREFIX}_FIND_VERSION_EXACT) + +if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + unset (_${_PYTHON_PREFIX}_FIND_VERSIONS) + foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_VERSIONS) + if ((${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE" + AND _${_PYTHON_PREFIX}_VERSION VERSION_GREATER_EQUAL ${_PYTHON_PREFIX}_FIND_VERSION_MIN) + AND ((${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" + AND _${_PYTHON_PREFIX}_VERSION VERSION_LESS_EQUAL ${_PYTHON_PREFIX}_FIND_VERSION_MAX) + OR (${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" + AND _${_PYTHON_PREFIX}_VERSION VERSION_LESS ${_PYTHON_PREFIX}_FIND_VERSION_MAX))) + list (APPEND _${_PYTHON_PREFIX}_FIND_VERSIONS ${_${_PYTHON_PREFIX}_VERSION}) + endif() + endforeach() +else() + if (${_PYTHON_PREFIX}_FIND_VERSION_COUNT GREATER 1) + if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) + set (_${_PYTHON_PREFIX}_FIND_VERSION_EXACT "EXACT") + set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_FIND_VERSION_MAJOR}.${${_PYTHON_PREFIX}_FIND_VERSION_MINOR}) + else() + unset (_${_PYTHON_PREFIX}_FIND_VERSIONS) + # add all compatible versions + foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_VERSIONS) + if (_${_PYTHON_PREFIX}_VERSION VERSION_GREATER_EQUAL "${${_PYTHON_PREFIX}_FIND_VERSION_MAJOR}.${${_PYTHON_PREFIX}_FIND_VERSION_MINOR}") + list (APPEND _${_PYTHON_PREFIX}_FIND_VERSIONS ${_${_PYTHON_PREFIX}_VERSION}) + endif() + endforeach() + endif() + endif() +endif() + +# Set ABIs to search +## default: search any ABI +if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR VERSION_LESS "3") + # ABI not supported + unset (_${_PYTHON_PREFIX}_FIND_ABI) + set (_${_PYTHON_PREFIX}_ABIFLAGS "") +else() + unset (_${_PYTHON_PREFIX}_FIND_ABI) + unset (_${_PYTHON_PREFIX}_ABIFLAGS) + if (DEFINED ${_PYTHON_PREFIX}_FIND_ABI) + # normalization + string (TOUPPER "${${_PYTHON_PREFIX}_FIND_ABI}" _${_PYTHON_PREFIX}_FIND_ABI) + list (TRANSFORM _${_PYTHON_PREFIX}_FIND_ABI REPLACE "^(TRUE|Y(ES)?|1)$" "ON") + list (TRANSFORM _${_PYTHON_PREFIX}_FIND_ABI REPLACE "^(FALSE|N(O)?|0)$" "OFF") + if (NOT _${_PYTHON_PREFIX}_FIND_ABI MATCHES "^(ON|OFF|ANY);(ON|OFF|ANY);(ON|OFF|ANY)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_ABI}: invalid value for '${_PYTHON_PREFIX}_FIND_ABI'. Ignore it") + unset (_${_PYTHON_PREFIX}_FIND_ABI) + endif() + _python_get_abiflags (_${_PYTHON_PREFIX}_ABIFLAGS) + endif() +endif() +unset (${_PYTHON_PREFIX}_SOABI) + +# Define lookup strategy +if(POLICY CMP0094) + cmake_policy (GET CMP0094 _${_PYTHON_PREFIX}_LOOKUP_POLICY) +else() + set (_${_PYTHON_PREFIX}_LOOKUP_POLICY "OLD") +endif() +if (_${_PYTHON_PREFIX}_LOOKUP_POLICY STREQUAL "NEW") + set (_${_PYTHON_PREFIX}_FIND_STRATEGY "LOCATION") +else() + set (_${_PYTHON_PREFIX}_FIND_STRATEGY "VERSION") +endif() +if (DEFINED ${_PYTHON_PREFIX}_FIND_STRATEGY) + if (NOT ${_PYTHON_PREFIX}_FIND_STRATEGY MATCHES "^(VERSION|LOCATION)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_STRATEGY}: invalid value for '${_PYTHON_PREFIX}_FIND_STRATEGY'. 'VERSION' or 'LOCATION' expected.") + set (_${_PYTHON_PREFIX}_FIND_STRATEGY "VERSION") + else() + set (_${_PYTHON_PREFIX}_FIND_STRATEGY "${${_PYTHON_PREFIX}_FIND_STRATEGY}") + endif() +endif() + +# Python and Anaconda distributions: define which architectures can be used +if (CMAKE_SIZEOF_VOID_P) + # In this case, search only for 64bit or 32bit + math (EXPR _${_PYTHON_PREFIX}_ARCH "${CMAKE_SIZEOF_VOID_P} * 8") + set (_${_PYTHON_PREFIX}_ARCH2 ${_${_PYTHON_PREFIX}_ARCH}) +else() + # architecture unknown, search for both 64bit and 32bit + set (_${_PYTHON_PREFIX}_ARCH 64) + set (_${_PYTHON_PREFIX}_ARCH2 32) +endif() + +# IronPython support +unset (_${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES) +unset (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES) +unset (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS) +if (CMAKE_SIZEOF_VOID_P) + if (_${_PYTHON_PREFIX}_ARCH EQUAL "32") + set (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS "/platform:x86") + else() + set (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS "/platform:x64") + endif() +endif() +if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Do not use wrapper script on Linux because it is buggy: -c interpreter option cannot be used + list (APPEND _${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES "ipy${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}" "ipy64" "ipy32" "ipy") + list (APPEND _${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES "ipyc") +endif() +list (APPEND _${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES "ipy.exe") +list (APPEND _${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES "ipyc.exe") +set (_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES net45 net40 bin) + +# PyPy support +if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "3") + set (_${_PYTHON_PREFIX}_PYPY_NAMES pypy3) + set (_${_PYTHON_PREFIX}_PYPY_LIB_NAMES pypy3-c) + if (WIN32) + # special name for runtime part + list (APPEND _${_PYTHON_PREFIX}_PYPY_LIB_NAMES libpypy3-c) + endif() + set (_${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES lib/pypy3) +else() + set (_${_PYTHON_PREFIX}_PYPY_NAMES pypy) + set (_${_PYTHON_PREFIX}_PYPY_LIB_NAMES pypy-c) + if (WIN32) + # special name for runtime part + list (APPEND _${_PYTHON_PREFIX}_PYPY_LIB_NAMES libpypy-c) + endif() + set (_${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES lib/pypy) +endif() +set (_${_PYTHON_PREFIX}_PYPY_EXECUTABLE_PATH_SUFFIXES bin) +set (_${_PYTHON_PREFIX}_PYPY_LIBRARY_PATH_SUFFIXES lib libs bin) +list (APPEND _${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES include) + +# Python Implementations handling +unset (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) +if (DEFINED ${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + foreach (_${_PYTHON_PREFIX}_IMPLEMENTATION IN LISTS ${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + if (NOT _${_PYTHON_PREFIX}_IMPLEMENTATION MATCHES "^(CPython|IronPython|PyPy)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${_${_PYTHON_PREFIX}_IMPLEMENTATION}: invalid value for '${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS'. 'CPython', 'IronPython' or 'PyPy' expected. Value will be ignored.") + else() + list (APPEND _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_IMPLEMENTATION}) + endif() + endforeach() +else() + if (WIN32) + set (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS CPython IronPython) + else() + set (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS CPython) + endif() +endif() + +# compute list of names for header file +unset (_${_PYTHON_PREFIX}_INCLUDE_NAMES) +foreach (_${_PYTHON_PREFIX}_IMPLEMENTATION IN LISTS _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + if (_${_PYTHON_PREFIX}_IMPLEMENTATION STREQUAL "CPython") + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_NAMES "Python.h") + elseif (_${_PYTHON_PREFIX}_IMPLEMENTATION STREQUAL "PyPy") + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_NAMES "PyPy.h") + endif() +endforeach() + + +# Apple frameworks handling +_python_find_frameworks () + +set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK "FIRST") + +if (DEFINED ${_PYTHON_PREFIX}_FIND_FRAMEWORK) + if (NOT ${_PYTHON_PREFIX}_FIND_FRAMEWORK MATCHES "^(FIRST|LAST|NEVER)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_FRAMEWORK}: invalid value for '${_PYTHON_PREFIX}_FIND_FRAMEWORK'. 'FIRST', 'LAST' or 'NEVER' expected. 'FIRST' will be used instead.") + else() + set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK ${${_PYTHON_PREFIX}_FIND_FRAMEWORK}) + endif() +elseif (DEFINED CMAKE_FIND_FRAMEWORK) + if (CMAKE_FIND_FRAMEWORK STREQUAL "ONLY") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: CMAKE_FIND_FRAMEWORK: 'ONLY' value is not supported. 'FIRST' will be used instead.") + elseif (NOT CMAKE_FIND_FRAMEWORK MATCHES "^(FIRST|LAST|NEVER)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${CMAKE_FIND_FRAMEWORK}: invalid value for 'CMAKE_FIND_FRAMEWORK'. 'FIRST', 'LAST' or 'NEVER' expected. 'FIRST' will be used instead.") + else() + set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK}) + endif() +endif() + +# Save CMAKE_FIND_APPBUNDLE +if (DEFINED CMAKE_FIND_APPBUNDLE) + set (_${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE ${CMAKE_FIND_APPBUNDLE}) +else() + unset (_${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE) +endif() +# To avoid app bundle lookup +set (CMAKE_FIND_APPBUNDLE "NEVER") + +# Save CMAKE_FIND_FRAMEWORK +if (DEFINED CMAKE_FIND_FRAMEWORK) + set (_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK}) +else() + unset (_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK) +endif() +# To avoid framework lookup +set (CMAKE_FIND_FRAMEWORK "NEVER") + +# Windows Registry handling +if (DEFINED ${_PYTHON_PREFIX}_FIND_REGISTRY) + if (NOT ${_PYTHON_PREFIX}_FIND_REGISTRY MATCHES "^(FIRST|LAST|NEVER)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_REGISTRY}: invalid value for '${_PYTHON_PREFIX}_FIND_REGISTRY'. 'FIRST', 'LAST' or 'NEVER' expected. 'FIRST' will be used instead.") + set (_${_PYTHON_PREFIX}_FIND_REGISTRY "FIRST") + else() + set (_${_PYTHON_PREFIX}_FIND_REGISTRY ${${_PYTHON_PREFIX}_FIND_REGISTRY}) + endif() +else() + set (_${_PYTHON_PREFIX}_FIND_REGISTRY "FIRST") +endif() + +# virtual environments recognition +if (DEFINED ENV{VIRTUAL_ENV} OR DEFINED ENV{CONDA_PREFIX}) + if (DEFINED ${_PYTHON_PREFIX}_FIND_VIRTUALENV) + if (NOT ${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY|STANDARD)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_VIRTUALENV}: invalid value for '${_PYTHON_PREFIX}_FIND_VIRTUALENV'. 'FIRST', 'ONLY' or 'STANDARD' expected. 'FIRST' will be used instead.") + set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV "FIRST") + else() + set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV ${${_PYTHON_PREFIX}_FIND_VIRTUALENV}) + endif() + else() + set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV FIRST) + endif() +else() + set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STANDARD) +endif() + + +# Python naming handling +if (DEFINED ${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES) + if (NOT ${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES MATCHES "^(FIRST|LAST|NEVER)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES}: invalid value for '${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES'. 'FIRST', 'LAST' or 'NEVER' expected. 'LAST' will be used instead.") + set (_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES LAST) + else() + set (_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES ${${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES}) + endif() +else() + set (_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES LAST) +endif() + + +# Compute search signature +# This signature will be used to check validity of cached variables on new search +set (_${_PYTHON_PREFIX}_SIGNATURE "${${_PYTHON_PREFIX}_ROOT_DIR}:${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}:${_${_PYTHON_PREFIX}_FIND_STRATEGY}:${${_PYTHON_PREFIX}_FIND_VIRTUALENV}${_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES}") +if (NOT WIN32) + string (APPEND _${_PYTHON_PREFIX}_SIGNATURE ":${${_PYTHON_PREFIX}_USE_STATIC_LIBS}:") +endif() +if (CMAKE_HOST_APPLE) + string (APPEND _${_PYTHON_PREFIX}_SIGNATURE ":${_${_PYTHON_PREFIX}_FIND_FRAMEWORK}") +endif() +if (CMAKE_HOST_WIN32) + string (APPEND _${_PYTHON_PREFIX}_SIGNATURE ":${_${_PYTHON_PREFIX}_FIND_REGISTRY}") +endif() + +function (_PYTHON_CHECK_DEVELOPMENT_SIGNATURE module) + if ("Development.${module}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + string (TOUPPER "${module}" id) + set (signature "${_${_PYTHON_PREFIX}_SIGNATURE}:") + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + list (APPEND signature "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}:") + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + list (APPEND signature "${_${_PYTHON_PREFIX}_INCLUDE_DIR}:") + endif() + string (MD5 signature "${signature}") + if (signature STREQUAL _${_PYTHON_PREFIX}_DEVELOPMENT_${id}_SIGNATURE) + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) + _python_validate_library (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS) + elseif (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + _python_validate_library (IN_RANGE CHECK_EXISTS) + elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) + _python_validate_library (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS) + else() + _python_validate_library (CHECK_EXISTS) + endif() + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) + _python_validate_include_dir (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS) + elseif (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + _python_validate_include_dir (IN_RANGE CHECK_EXISTS) + elseif (${_PYTHON_PREFIX}_FIND_VERSION) + _python_validate_include_dir (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS) + else() + _python_validate_include_dir (CHECK_EXISTS) + endif() + endif() + else() + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + unset (_${_PYTHON_PREFIX}_LIBRARY_RELEASE CACHE) + unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG CACHE) + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE) + endif() + endif() + if (("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS + AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + OR ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS + AND NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)) + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + unset (_${_PYTHON_PREFIX}_DEVELOPMENT_${id}_SIGNATURE CACHE) + endif() + endif() +endfunction() + +function (_PYTHON_COMPUTE_DEVELOPMENT_SIGNATURE module) + string (TOUPPER "${module}" id) + if (${_PYTHON_PREFIX}_Development.${module}_FOUND) + set (signature "${_${_PYTHON_PREFIX}_SIGNATURE}:") + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + list (APPEND signature "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}:") + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + list (APPEND signature "${_${_PYTHON_PREFIX}_INCLUDE_DIR}:") + endif() + string (MD5 signature "${signature}") + set (_${_PYTHON_PREFIX}_DEVELOPMENT_${id}_SIGNATURE "${signature}" CACHE INTERNAL "") + else() + unset (_${_PYTHON_PREFIX}_DEVELOPMENT_${id}_SIGNATURE CACHE) + endif() +endfunction() + + +unset (_${_PYTHON_PREFIX}_REQUIRED_VARS) +unset (_${_PYTHON_PREFIX}_CACHED_VARS) +unset (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE) +unset (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE) +unset (_${_PYTHON_PREFIX}_Development_REASON_FAILURE) +unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE) + + +# preamble +## For IronPython on platforms other than Windows, search for the .Net interpreter +if ("IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS + AND NOT WIN32) + find_program (${_PYTHON_PREFIX}_DOTNET_LAUNCHER + NAMES "mono") +endif() + + +# first step, search for the interpreter +if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_EXECUTABLE + _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES) + if (${_PYTHON_PREFIX}_FIND_REQUIRED_Interpreter) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_EXECUTABLE) + endif() + + if (DEFINED ${_PYTHON_PREFIX}_EXECUTABLE + AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_EXECUTABLE}") + if (NOT ${_PYTHON_PREFIX}_EXECUTABLE STREQUAL _${_PYTHON_PREFIX}_EXECUTABLE) + # invalidate cache properties + unset (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES CACHE) + endif() + set (_${_PYTHON_PREFIX}_EXECUTABLE "${${_PYTHON_PREFIX}_EXECUTABLE}" CACHE INTERNAL "") + elseif (DEFINED _${_PYTHON_PREFIX}_EXECUTABLE) + # compute interpreter signature and check validity of definition + string (MD5 __${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_EXECUTABLE}") + if (__${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE STREQUAL _${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE) + # check version validity + if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) + _python_validate_interpreter (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS) + elseif (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + _python_validate_interpreter (IN_RANGE CHECK_EXISTS) + elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) + _python_validate_interpreter (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS) + else() + _python_validate_interpreter (CHECK_EXISTS) + endif() + else() + unset (_${_PYTHON_PREFIX}_EXECUTABLE CACHE) + endif() + if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) + unset (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE CACHE) + unset (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES CACHE) + endif() + endif() + + if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) + set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + + if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") + # build all executable names + _python_get_names (_${_PYTHON_PREFIX}_NAMES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} POSIX INTERPRETER) + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} INTERPRETER) + + # Framework Paths + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) + # Registry Paths + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) + + set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS ${_${_PYTHON_PREFIX}_FIND_VERSION_EXACT}) + if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS IN_RANGE) + elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) + list (APPEND VERSION ${${_PYTHON_PREFIX}_FIND_VERSION}) + endif() + + while (TRUE) + # Virtual environments handling + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + + _python_validate_interpreter (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") + break() + endif() + endif() + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + endif() + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + endif() + + # try using HINTS + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + # try using standard paths + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) + _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_DEFAULT_PATH) + _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + endif() + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_DEFAULT_PATH) + _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + endif() + + break() + endwhile() + else() + # look-up for various versions and locations + set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS EXACT) + if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS IN_RANGE) + endif() + + foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) + _python_get_names (_${_PYTHON_PREFIX}_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} POSIX INTERPRETER) + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} INTERPRETER) + + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) + + # Virtual environments handling + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") + continue() + endif() + endif() + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + + # try using HINTS + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + # try using standard paths. + # NAMES_PER_DIR is not defined on purpose to have a chance to find + # expected version. + # For example, typical systems have 'python' for version 2.* and 'python3' + # for version 3.*. So looking for names per dir will find, potentially, + # systematically 'python' (i.e. version 2) even if version 3 is searched. + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) + _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_DEFAULT_PATH) + endif() + + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_DEFAULT_PATH) + endif() + + _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + endforeach() + + if (NOT _${_PYTHON_PREFIX}_EXECUTABLE AND + NOT _${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") + # No specific version found. Retry with generic names and standard paths. + # NAMES_PER_DIR is not defined on purpose to have a chance to find + # expected version. + # For example, typical systems have 'python' for version 2.* and 'python3' + # for version 3.*. So looking for names per dir will find, potentially, + # systematically 'python' (i.e. version 2) even if version 3 is searched. + _python_get_names (_${_PYTHON_PREFIX}_NAMES POSIX INTERPRETER) + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES}) + _python_validate_interpreter () + endif() + endif() + endif() + + set (${_PYTHON_PREFIX}_EXECUTABLE "${_${_PYTHON_PREFIX}_EXECUTABLE}") + _python_get_launcher (_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER INTERPRETER) + + # retrieve exact version of executable found + if (_${_PYTHON_PREFIX}_EXECUTABLE) + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE ${_PYTHON_PREFIX}_VERSION + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (NOT _${_PYTHON_PREFIX}_RESULT) + set (_${_PYTHON_PREFIX}_EXECUTABLE_USABLE TRUE) + else() + # Interpreter is not usable + set (_${_PYTHON_PREFIX}_EXECUTABLE_USABLE FALSE) + unset (${_PYTHON_PREFIX}_VERSION) + set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot run the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + endif() + endif() + + if (_${_PYTHON_PREFIX}_EXECUTABLE AND _${_PYTHON_PREFIX}_EXECUTABLE_USABLE) + if (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES) + set (${_PYTHON_PREFIX}_Interpreter_FOUND TRUE) + + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 0 ${_PYTHON_PREFIX}_INTERPRETER_ID) + + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 1 ${_PYTHON_PREFIX}_VERSION_MAJOR) + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 2 ${_PYTHON_PREFIX}_VERSION_MINOR) + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 3 ${_PYTHON_PREFIX}_VERSION_PATCH) + + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 4 _${_PYTHON_PREFIX}_ARCH) + set (_${_PYTHON_PREFIX}_ARCH2 ${_${_PYTHON_PREFIX}_ARCH}) + + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 5 _${_PYTHON_PREFIX}_ABIFLAGS) + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 6 ${_PYTHON_PREFIX}_SOABI) + + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 7 ${_PYTHON_PREFIX}_STDLIB) + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 8 ${_PYTHON_PREFIX}_STDARCH) + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 9 ${_PYTHON_PREFIX}_SITELIB) + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 10 ${_PYTHON_PREFIX}_SITEARCH) + else() + string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${${_PYTHON_PREFIX}_VERSION}") + list (GET _${_PYTHON_PREFIX}_VERSIONS 0 ${_PYTHON_PREFIX}_VERSION_MAJOR) + list (GET _${_PYTHON_PREFIX}_VERSIONS 1 ${_PYTHON_PREFIX}_VERSION_MINOR) + list (GET _${_PYTHON_PREFIX}_VERSIONS 2 ${_PYTHON_PREFIX}_VERSION_PATCH) + + if (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + set (${_PYTHON_PREFIX}_Interpreter_FOUND TRUE) + + # Use interpreter version and ABI for future searches to ensure consistency + set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETR_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; sys.stdout.write(sys.abiflags)" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_ABIFLAGS + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + # assunme ABI is not supported + set (_${_PYTHON_PREFIX}_ABIFLAGS "") + endif() + endif() + + if (${_PYTHON_PREFIX}_Interpreter_FOUND) + unset (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE) + + # compute and save interpreter signature + string (MD5 __${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_EXECUTABLE}") + set (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${__${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}" CACHE INTERNAL "") + + if (NOT CMAKE_SIZEOF_VOID_P) + # determine interpreter architecture + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; sys.stdout.write(str(sys.maxsize > 2**32))" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE ${_PYTHON_PREFIX}_IS64BIT + ERROR_VARIABLE ${_PYTHON_PREFIX}_IS64BIT) + if (NOT _${_PYTHON_PREFIX}_RESULT) + if (${_PYTHON_PREFIX}_IS64BIT) + set (_${_PYTHON_PREFIX}_ARCH 64) + set (_${_PYTHON_PREFIX}_ARCH2 64) + else() + set (_${_PYTHON_PREFIX}_ARCH 32) + set (_${_PYTHON_PREFIX}_ARCH2 32) + endif() + endif() + endif() + + # retrieve interpreter identity + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -V + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE ${_PYTHON_PREFIX}_INTERPRETER_ID + ERROR_VARIABLE ${_PYTHON_PREFIX}_INTERPRETER_ID) + if (NOT _${_PYTHON_PREFIX}_RESULT) + if (${_PYTHON_PREFIX}_INTERPRETER_ID MATCHES "Anaconda") + set (${_PYTHON_PREFIX}_INTERPRETER_ID "Anaconda") + elseif (${_PYTHON_PREFIX}_INTERPRETER_ID MATCHES "Enthought") + set (${_PYTHON_PREFIX}_INTERPRETER_ID "Canopy") + elseif (${_PYTHON_PREFIX}_INTERPRETER_ID MATCHES "PyPy ([0-9.]+)") + set (${_PYTHON_PREFIX}_INTERPRETER_ID "PyPy") + set (${_PYTHON_PREFIX}_PyPy_VERSION "${CMAKE_MATCH_1}") + else() + string (REGEX REPLACE "^([^ ]+).*" "\\1" ${_PYTHON_PREFIX}_INTERPRETER_ID "${${_PYTHON_PREFIX}_INTERPRETER_ID}") + if (${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "Python") + # try to get a more precise ID + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; sys.stdout.write(sys.copyright)" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE ${_PYTHON_PREFIX}_COPYRIGHT + ERROR_QUIET) + if (${_PYTHON_PREFIX}_COPYRIGHT MATCHES "ActiveState") + set (${_PYTHON_PREFIX}_INTERPRETER_ID "ActivePython") + endif() + endif() + endif() + else() + set (${_PYTHON_PREFIX}_INTERPRETER_ID Python) + endif() + + # retrieve various package installation directories + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys\ntry:\n from distutils import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_python_lib(plat_specific=False,standard_lib=True),sysconfig.get_python_lib(plat_specific=True,standard_lib=True),sysconfig.get_python_lib(plat_specific=False,standard_lib=False),sysconfig.get_python_lib(plat_specific=True,standard_lib=False)]))\nexcept Exception:\n import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_path('stdlib'),sysconfig.get_path('platstdlib'),sysconfig.get_path('purelib'),sysconfig.get_path('platlib')]))" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_LIBPATHS + ERROR_QUIET) + if (NOT _${_PYTHON_PREFIX}_RESULT) + list (GET _${_PYTHON_PREFIX}_LIBPATHS 0 ${_PYTHON_PREFIX}_STDLIB) + list (GET _${_PYTHON_PREFIX}_LIBPATHS 1 ${_PYTHON_PREFIX}_STDARCH) + list (GET _${_PYTHON_PREFIX}_LIBPATHS 2 ${_PYTHON_PREFIX}_SITELIB) + list (GET _${_PYTHON_PREFIX}_LIBPATHS 3 ${_PYTHON_PREFIX}_SITEARCH) + else() + unset (${_PYTHON_PREFIX}_STDLIB) + unset (${_PYTHON_PREFIX}_STDARCH) + unset (${_PYTHON_PREFIX}_SITELIB) + unset (${_PYTHON_PREFIX}_SITEARCH) + endif() + + _python_get_config_var (${_PYTHON_PREFIX}_SOABI SOABI) + + # store properties in the cache to speed-up future searches + set (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES + "${${_PYTHON_PREFIX}_INTERPRETER_ID};${${_PYTHON_PREFIX}_VERSION_MAJOR};${${_PYTHON_PREFIX}_VERSION_MINOR};${${_PYTHON_PREFIX}_VERSION_PATCH};${_${_PYTHON_PREFIX}_ARCH};${_${_PYTHON_PREFIX}_ABIFLAGS};${${_PYTHON_PREFIX}_SOABI};${${_PYTHON_PREFIX}_STDLIB};${${_PYTHON_PREFIX}_STDARCH};${${_PYTHON_PREFIX}_SITELIB};${${_PYTHON_PREFIX}_SITEARCH}" CACHE INTERNAL "${_PYTHON_PREFIX} Properties") + else() + unset (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE CACHE) + unset (${_PYTHON_PREFIX}_INTERPRETER_ID) + endif() + endif() + endif() + + if (${_PYTHON_PREFIX}_ARTIFACTS_INTERACTIVE) + set (${_PYTHON_PREFIX}_EXECUTABLE "${_${_PYTHON_PREFIX}_EXECUTABLE}" CACHE FILEPATH "${_PYTHON_PREFIX} Interpreter") + endif() + + _python_mark_as_internal (_${_PYTHON_PREFIX}_EXECUTABLE + _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES + _${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE) +endif() + + +# second step, search for compiler (IronPython) +if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_COMPILER) + if (${_PYTHON_PREFIX}_FIND_REQUIRED_Compiler) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_COMPILER) + endif() + + if (NOT "IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + unset (_${_PYTHON_PREFIX}_COMPILER CACHE) + unset (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE CACHE) + elseif (DEFINED ${_PYTHON_PREFIX}_COMPILER + AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_COMPILER}") + set (_${_PYTHON_PREFIX}_COMPILER "${${_PYTHON_PREFIX}_COMPILER}" CACHE INTERNAL "") + elseif (DEFINED _${_PYTHON_PREFIX}_COMPILER) + # compute compiler signature and check validity of definition + string (MD5 __${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_COMPILER}") + if (__${_PYTHON_PREFIX}_COMPILER_SIGNATURE STREQUAL _${_PYTHON_PREFIX}_COMPILER_SIGNATURE) + # check version validity + if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) + _python_validate_compiler (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS) + elseif (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + _python_validate_compiler (IN_RANGE CHECK_EXISTS) + elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) + _python_validate_compiler (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS) + else() + _python_validate_compiler (CHECK_EXISTS) + endif() + else() + unset (_${_PYTHON_PREFIX}_COMPILER CACHE) + unset (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE CACHE) + endif() + endif() + + if ("IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS + AND NOT _${_PYTHON_PREFIX}_COMPILER) + # IronPython specific artifacts + # If IronPython interpreter is found, use its path + unset (_${_PYTHON_PREFIX}_IRON_ROOT) + if (${_PYTHON_PREFIX}_Interpreter_FOUND AND ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython") + get_filename_component (_${_PYTHON_PREFIX}_IRON_ROOT "${${_PYTHON_PREFIX}_EXECUTABLE}" DIRECTORY) + endif() + + if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") + _python_get_names (_${_PYTHON_PREFIX}_COMPILER_NAMES + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} + COMPILER) + + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} + COMPILER) + + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) + + set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS ${_${_PYTHON_PREFIX}_FIND_VERSION_EXACT}) + if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS IN_RANGE) + elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) + list (APPEND VERSION ${${_PYTHON_PREFIX}_FIND_VERSION}) + endif() + + while (TRUE) + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + + # try using HINTS + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + + # try using standard paths + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) + _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_DEFAULT_PATH) + _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_DEFAULT_PATH) + _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + + break() + endwhile() + else() + # try using root dir and registry + set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS EXACT) + if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS IN_RANGE) + endif() + + foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) + _python_get_names (_${_PYTHON_PREFIX}_COMPILER_NAMES + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} + COMPILER) + + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSION} + COMPILER) + + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_VERSION}) + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_VERSION}) + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + + # try using HINTS + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_DEFAULT_PATH) + _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_DEFAULT_PATH) + _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + endforeach() + + # no specific version found, re-try in standard paths + _python_get_names (_${_PYTHON_PREFIX}_COMPILER_NAMES + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} + COMPILER) + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} + COMPILER) + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) + _python_validate_compiler () + endif() + endif() + + set (${_PYTHON_PREFIX}_COMPILER "${_${_PYTHON_PREFIX}_COMPILER}") + + if (_${_PYTHON_PREFIX}_COMPILER) + # retrieve python environment version from compiler + _python_get_launcher (_${_PYTHON_PREFIX}_COMPILER_LAUNCHER COMPILER) + set (_${_PYTHON_PREFIX}_VERSION_DIR "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/PythonCompilerVersion.dir") + file (WRITE "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))\n") + execute_process (COMMAND ${_${_PYTHON_PREFIX}_COMPILER_LAUNCHER} "${_${_PYTHON_PREFIX}_COMPILER}" + ${_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS} + /target:exe /embed "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py" + WORKING_DIRECTORY "${_${_PYTHON_PREFIX}_VERSION_DIR}" + OUTPUT_QUIET + ERROR_QUIET) + get_filename_component (_${_PYTHON_PREFIX}_IR_DIR "${_${_PYTHON_PREFIX}_COMPILER}" DIRECTORY) + execute_process (COMMAND "${CMAKE_COMMAND}" -E env "MONO_PATH=${_${_PYTHON_PREFIX}_IR_DIR}" + ${${_PYTHON_PREFIX}_DOTNET_LAUNCHER} "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.exe" + WORKING_DIRECTORY "${_${_PYTHON_PREFIX}_VERSION_DIR}" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_VERSION + ERROR_QUIET) + if (NOT _${_PYTHON_PREFIX}_RESULT) + set (_${_PYTHON_PREFIX}_COMPILER_USABLE TRUE) + string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${_${_PYTHON_PREFIX}_VERSION}") + list (GET _${_PYTHON_PREFIX}_VERSIONS 0 _${_PYTHON_PREFIX}_VERSION_MAJOR) + list (GET _${_PYTHON_PREFIX}_VERSIONS 1 _${_PYTHON_PREFIX}_VERSION_MINOR) + list (GET _${_PYTHON_PREFIX}_VERSIONS 2 _${_PYTHON_PREFIX}_VERSION_PATCH) + + if (NOT ${_PYTHON_PREFIX}_Interpreter_FOUND) + # set public version information + set (${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_VERSION}) + set (${_PYTHON_PREFIX}_VERSION_MAJOR ${_${_PYTHON_PREFIX}_VERSION_MAJOR}) + set (${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_VERSION_MINOR}) + set (${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_VERSION_PATCH}) + endif() + else() + # compiler not usable + set (_${_PYTHON_PREFIX}_COMPILER_USABLE FALSE) + set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Cannot run the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") + endif() + file (REMOVE_RECURSE "${_${_PYTHON_PREFIX}_VERSION_DIR}") + endif() + + if (_${_PYTHON_PREFIX}_COMPILER AND _${_PYTHON_PREFIX}_COMPILER_USABLE) + if (${_PYTHON_PREFIX}_Interpreter_FOUND) + # Compiler must be compatible with interpreter + if ("${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}" VERSION_EQUAL "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}") + set (${_PYTHON_PREFIX}_Compiler_FOUND TRUE) + endif() + elseif (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + set (${_PYTHON_PREFIX}_Compiler_FOUND TRUE) + # Use compiler version for future searches to ensure consistency + set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) + endif() + endif() + + if (${_PYTHON_PREFIX}_Compiler_FOUND) + unset (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE) + + # compute and save compiler signature + string (MD5 __${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_COMPILER}") + set (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${__${_PYTHON_PREFIX}_COMPILER_SIGNATURE}" CACHE INTERNAL "") + + set (${_PYTHON_PREFIX}_COMPILER_ID IronPython) + else() + unset (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE CACHE) + unset (${_PYTHON_PREFIX}_COMPILER_ID) + endif() + + if (${_PYTHON_PREFIX}_ARTIFACTS_INTERACTIVE) + set (${_PYTHON_PREFIX}_COMPILER "${_${_PYTHON_PREFIX}_COMPILER}" CACHE FILEPATH "${_PYTHON_PREFIX} Compiler") + endif() + + _python_mark_as_internal (_${_PYTHON_PREFIX}_COMPILER + _${_PYTHON_PREFIX}_COMPILER_SIGNATURE) +endif() + +# third step, search for the development artifacts +if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.Module) + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_LIBRARIES) + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_INCLUDE_DIRS) + endif() +endif() +if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.Embed) + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_LIBRARIES) + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_INCLUDE_DIRS) + endif() +endif() +if (_${_PYTHON_PREFIX}_REQUIRED_VARS) # Behavior change in CMake 3.14 + list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_REQUIRED_VARS) +endif () +## Development environment is not compatible with IronPython interpreter +if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + AND ((${_PYTHON_PREFIX}_Interpreter_FOUND + AND NOT ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython") + OR NOT ${_PYTHON_PREFIX}_Interpreter_FOUND)) + if (${_PYTHON_PREFIX}_Interpreter_FOUND) + # reduce possible implementations to the interpreter one + if (${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "PyPy") + set (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS "PyPy") + else() + set (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS "CPython") + endif() + else() + list (REMOVE_ITEM _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS "IronPython") + endif() + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_LIBRARY_DEBUG + _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_INCLUDE_DIR) + endif() + + _python_check_development_signature (Module) + _python_check_development_signature (Embed) + + if (DEFINED ${_PYTHON_PREFIX}_LIBRARY + AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_LIBRARY}") + set (_${_PYTHON_PREFIX}_LIBRARY_RELEASE "${${_PYTHON_PREFIX}_LIBRARY}" CACHE INTERNAL "") + unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG CACHE) + unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE) + endif() + if (DEFINED ${_PYTHON_PREFIX}_INCLUDE_DIR + AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_INCLUDE_DIR}") + set (_${_PYTHON_PREFIX}_INCLUDE_DIR "${${_PYTHON_PREFIX}_INCLUDE_DIR}" CACHE INTERNAL "") + endif() + + # Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES + unset (_${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES) + if (DEFINED ${_PYTHON_PREFIX}_USE_STATIC_LIBS AND NOT WIN32) + set(_${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + if(${_PYTHON_PREFIX}_USE_STATIC_LIBS) + set (CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX}) + else() + list (REMOVE_ITEM CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX}) + endif() + endif() + + if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE OR NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) + # if python interpreter is found, use it to look-up for artifacts + # to ensure consistency between interpreter and development environments. + # If not, try to locate a compatible config tool + if ((NOT ${_PYTHON_PREFIX}_Interpreter_FOUND OR CMAKE_CROSSCOMPILING) + AND "CPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS) + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") + set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX) + endif() + + if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") + _python_get_names (_${_PYTHON_PREFIX}_CONFIG_NAMES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} POSIX CONFIG) + # Framework Paths + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_CONFIG + NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES bin + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + find_program (_${_PYTHON_PREFIX}_CONFIG + NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + PATH_SUFFIXES bin) + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_CONFIG + NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES bin + NO_DEFAULT_PATH) + endif() + + if (_${_PYTHON_PREFIX}_CONFIG) + execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --help + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE __${_PYTHON_PREFIX}_HELP + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + # assume config tool is not usable + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + endif() + endif() + + if (_${_PYTHON_PREFIX}_CONFIG) + execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --abiflags + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE __${_PYTHON_PREFIX}_ABIFLAGS + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + # assume ABI is not supported + set (__${_PYTHON_PREFIX}_ABIFLAGS "") + endif() + if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) + # Wrong ABI + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + endif() + endif() + + if (_${_PYTHON_PREFIX}_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) + # check that config tool match library architecture + execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + else() + string(FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT) + if (_${_PYTHON_PREFIX}_RESULT EQUAL -1) + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + endif() + endif() + endif() + else() + foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) + # try to use pythonX.Y-config tool + _python_get_names (_${_PYTHON_PREFIX}_CONFIG_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} POSIX CONFIG) + + # Framework Paths + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_CONFIG + NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES bin + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + find_program (_${_PYTHON_PREFIX}_CONFIG + NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + PATH_SUFFIXES bin) + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_CONFIG + NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES bin + NO_DEFAULT_PATH) + endif() + + unset (_${_PYTHON_PREFIX}_CONFIG_NAMES) + + if (_${_PYTHON_PREFIX}_CONFIG) + execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --help + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE __${_PYTHON_PREFIX}_HELP + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + # assume config tool is not usable + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + endif() + endif() + + if (NOT _${_PYTHON_PREFIX}_CONFIG) + continue() + endif() + + execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --abiflags + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE __${_PYTHON_PREFIX}_ABIFLAGS + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + # assume ABI is not supported + set (__${_PYTHON_PREFIX}_ABIFLAGS "") + endif() + if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) + # Wrong ABI + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + continue() + endif() + + if (_${_PYTHON_PREFIX}_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) + # check that config tool match library architecture + execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + continue() + endif() + string (FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT) + if (_${_PYTHON_PREFIX}_RESULT EQUAL -1) + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + continue() + endif() + endif() + + if (_${_PYTHON_PREFIX}_CONFIG) + break() + endif() + endforeach() + endif() + endif() + endif() + + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG) + # retrieve root install directory + _python_get_config_var (_${_PYTHON_PREFIX}_PREFIX PREFIX) + + # enforce current ABI + _python_get_config_var (_${_PYTHON_PREFIX}_ABIFLAGS ABIFLAGS) + + set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_PREFIX}") + + # retrieve library + ## compute some paths and artifact names + if (_${_PYTHON_PREFIX}_CONFIG) + string (REGEX REPLACE "^.+python([0-9.]+)[a-z]*-config" "\\1" _${_PYTHON_PREFIX}_VERSION "${_${_PYTHON_PREFIX}_CONFIG}") + else() + set (_${_PYTHON_PREFIX}_VERSION "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}") + endif() + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} LIBRARY) + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 POSIX LIBRARY) + + _python_get_config_var (_${_PYTHON_PREFIX}_CONFIGDIR CONFIGDIR) + list (APPEND _${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_CONFIGDIR}") + + list (APPEND _${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # Rely on HINTS and standard paths if interpreter or config tool failed to locate artifacts + if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + + unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS) + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") + set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX) + endif() + + if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") + # library names + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} WIN32 POSIX LIBRARY) + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} WIN32 DEBUG) + # Paths suffixes + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} LIBRARY) + + # Framework Paths + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_FIND_VERSIONS}) + # Registry Paths + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} ) + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # search in HINTS locations + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) + endif() + + # search in all default paths + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) + else() + foreach (_${_PYTHON_PREFIX}_LIB_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} WIN32 POSIX LIBRARY) + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} WIN32 DEBUG) + + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION}) + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION}) + + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} LIBRARY) + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # search in HINTS locations + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) + endif() + + # search in all default paths + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) + + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) + break() + endif() + endforeach() + endif() + endif() + endif() + + # finalize library version information + _python_get_version (LIBRARY PREFIX _${_PYTHON_PREFIX}_) + if (_${_PYTHON_PREFIX}_VERSION EQUAL "${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}") + # not able to extract full version from library name + if (${_PYTHON_PREFIX}_Interpreter_FOUND) + # update from interpreter + set (_${_PYTHON_PREFIX}_VERSION ${${_PYTHON_PREFIX}_VERSION}) + set (_${_PYTHON_PREFIX}_VERSION_MAJOR ${${_PYTHON_PREFIX}_VERSION_MAJOR}) + set (_${_PYTHON_PREFIX}_VERSION_MINOR ${${_PYTHON_PREFIX}_VERSION_MINOR}) + set (_${_PYTHON_PREFIX}_VERSION_PATCH ${${_PYTHON_PREFIX}_VERSION_PATCH}) + endif() + endif() + + set (${_PYTHON_PREFIX}_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") + + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") + set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") + endif() + + set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + + if (WIN32 AND _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + # search for debug library + # use release library location as a hint + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 DEBUG) + get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) + find_library (_${_PYTHON_PREFIX}_LIBRARY_DEBUG + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} + NAMES_PER_DIR + HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS} + NO_DEFAULT_PATH) + # second try including CMAKE variables to catch-up non conventional layouts + find_library (_${_PYTHON_PREFIX}_LIBRARY_DEBUG + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} + NAMES_PER_DIR + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # retrieve runtime libraries + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 POSIX LIBRARY) + get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) + get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) + _python_find_runtime_library (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS "${_${_PYTHON_PREFIX}_PATH}" + "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES bin) + endif() + if (_${_PYTHON_PREFIX}_LIBRARY_DEBUG) + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 DEBUG) + get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_DEBUG}" DIRECTORY) + get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) + _python_find_runtime_library (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} + NAMES_PER_DIR + HINTS "${_${_PYTHON_PREFIX}_PATH}" + "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES bin) + endif() + endif() + + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + while (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS + AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + # Don't search for include dir if no library was founded + break() + endif() + + if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG) + _python_get_config_var (_${_PYTHON_PREFIX}_INCLUDE_DIRS INCLUDES) + + find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR + NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES} + HINTS ${_${_PYTHON_PREFIX}_INCLUDE_DIRS} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # Rely on HINTS and standard paths if interpreter or config tool failed to locate artifacts + if (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) + unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS) + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") + set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX) + endif() + unset (_${_PYTHON_PREFIX}_INCLUDE_HINTS) + + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) + # Use the library's install prefix as a hint + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "^(.+/Frameworks/Python.framework/Versions/[0-9.]+)") + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") + elseif (_${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "^(.+)/lib(64|32)?/python[0-9.]+/config") + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") + elseif (DEFINED CMAKE_LIBRARY_ARCHITECTURE AND ${_${_PYTHON_PREFIX}_LIBRARY_RELEASE} MATCHES "^(.+)/lib/${CMAKE_LIBRARY_ARCHITECTURE}") + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") + else() + # assume library is in a directory under root + get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) + get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_PREFIX}" DIRECTORY) + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PREFIX}") + endif() + endif() + + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} INCLUDE) + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR + NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES} + HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR + NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES} + HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) + endif() + + find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR + NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES} + HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # search header file in standard locations + find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR + NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES}) + + break() + endwhile() + + set (${_PYTHON_PREFIX}_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}") + + if (_${_PYTHON_PREFIX}_INCLUDE_DIR AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}") + set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + endif() + + if (_${_PYTHON_PREFIX}_INCLUDE_DIR) + # retrieve version from header file + _python_get_version (INCLUDE PREFIX _${_PYTHON_PREFIX}_INC_) + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) + if ("${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}" + VERSION_EQUAL _${_PYTHON_PREFIX}_VERSION) + # update versioning + set (_${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_INC_VERSION}) + set (_${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_INC_VERSION_PATCH}) + endif() + else() + set (_${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_INC_VERSION}) + set (_${_PYTHON_PREFIX}_VERSION_MAJOR ${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}) + set (_${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}) + set (_${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_INC_VERSION_PATCH}) + endif() + endif() + endif() + + if (NOT ${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT ${_PYTHON_PREFIX}_Compiler_FOUND) + # set public version information + set (${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_VERSION}) + set (${_PYTHON_PREFIX}_VERSION_MAJOR ${_${_PYTHON_PREFIX}_VERSION_MAJOR}) + set (${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_VERSION_MINOR}) + set (${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_VERSION_PATCH}) + endif() + + # define public variables + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + set (${_PYTHON_PREFIX}_LIBRARY_DEBUG "${_${_PYTHON_PREFIX}_LIBRARY_DEBUG}") + _python_select_library_configurations (${_PYTHON_PREFIX}) + + set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") + set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG "${_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}") + + if (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE) + set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") + elseif (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) + set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}") + else() + set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${_PYTHON_PREFIX}_RUNTIME_LIBRARY-NOTFOUND") + endif() + + _python_set_library_dirs (${_PYTHON_PREFIX}_LIBRARY_DIRS + _${_PYTHON_PREFIX}_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_LIBRARY_DEBUG) + if (UNIX) + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$") + set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DIRS ${${_PYTHON_PREFIX}_LIBRARY_DIRS}) + endif() + else() + _python_set_library_dirs (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DIRS + _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) + endif() + endif() + + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE OR _${_PYTHON_PREFIX}_INCLUDE_DIR) + if (${_PYTHON_PREFIX}_Interpreter_FOUND OR ${_PYTHON_PREFIX}_Compiler_FOUND) + # development environment must be compatible with interpreter/compiler + if ("${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}" VERSION_EQUAL "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}" + AND "${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}" VERSION_EQUAL "${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}") + _python_set_development_module_found (Module) + _python_set_development_module_found (Embed) + endif() + elseif (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR + AND "${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}" VERSION_EQUAL "${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}") + _python_set_development_module_found (Module) + _python_set_development_module_found (Embed) + endif() + if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND + (NOT _${_PYTHON_PREFIX}_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS + OR NOT _${_PYTHON_PREFIX}_INC_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)) + set (${_PYTHON_PREFIX}_Development.Module_FOUND FALSE) + set (${_PYTHON_PREFIX}_Development.Embed_FOUND FALSE) + endif() + endif() + + if (( ${_PYTHON_PREFIX}_Development.Module_FOUND + AND ${_PYTHON_PREFIX}_Development.Embed_FOUND) + OR (NOT "Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND ${_PYTHON_PREFIX}_Development.Embed_FOUND) + OR (NOT "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND ${_PYTHON_PREFIX}_Development.Module_FOUND)) + unset (_${_PYTHON_PREFIX}_Development_REASON_FAILURE) + endif() + + if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND ${_PYTHON_PREFIX}_Development.Module_FOUND + AND ${_PYTHON_PREFIX}_Development.Embed_FOUND) + set (${_PYTHON_PREFIX}_Development_FOUND TRUE) + endif() + + if ((${_PYTHON_PREFIX}_Development.Module_FOUND + OR ${_PYTHON_PREFIX}_Development.Embed_FOUND) + AND EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/PyPy.h") + # retrieve PyPy version + file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/patchlevel.h" ${_PYTHON_PREFIX}_PyPy_VERSION + REGEX "^#define[ \t]+PYPY_VERSION[ \t]+\"[^\"]+\"") + string (REGEX REPLACE "^#define[ \t]+PYPY_VERSION[ \t]+\"([^\"]+)\".*" "\\1" + ${_PYTHON_PREFIX}_PyPy_VERSION "${${_PYTHON_PREFIX}_PyPy_VERSION}") + endif() + + unset(${_PYTHON_PREFIX}_LINK_OPTIONS) + if (${_PYTHON_PREFIX}_Development.Embed_FOUND AND APPLE + AND ${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$") + # rpath must be specified if python is part of a framework + unset(_${_PYTHON_PREFIX}_is_prefix) + foreach (_${_PYTHON_PREFIX}_implementation IN LISTS _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + foreach (_${_PYTHON_PREFIX}_framework IN LISTS _${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_implementation}_FRAMEWORKS) + if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "^${_${_PYTHON_PREFIX}_framework}") + get_filename_component (_${_PYTHON_PREFIX}_framework "${_${_PYTHON_PREFIX}_framework}" DIRECTORY) + set (${_PYTHON_PREFIX}_LINK_OPTIONS "LINKER:-rpath,${_${_PYTHON_PREFIX}_framework}") + break() + endif() + endforeach() + if (_${_PYTHON_PREFIX}_is_prefix) + break() + endif() + endforeach() + unset(_${_PYTHON_PREFIX}_implementation) + unset(_${_PYTHON_PREFIX}_framework) + unset(_${_PYTHON_PREFIX}_is_prefix) + endif() + + if (NOT DEFINED ${_PYTHON_PREFIX}_SOABI) + _python_get_config_var (${_PYTHON_PREFIX}_SOABI SOABI) + endif() + + _python_compute_development_signature (Module) + _python_compute_development_signature (Embed) + + # Restore the original find library ordering + if (DEFINED _${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES) + set (CMAKE_FIND_LIBRARY_SUFFIXES ${_${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES}) + endif() + + if (${_PYTHON_PREFIX}_ARTIFACTS_INTERACTIVE) + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + set (${_PYTHON_PREFIX}_LIBRARY "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" CACHE FILEPATH "${_PYTHON_PREFIX} Library") + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + set (${_PYTHON_PREFIX}_INCLUDE_DIR "${_${_PYTHON_PREFIX}_INCLUDE_DIR}" CACHE FILEPATH "${_PYTHON_PREFIX} Include Directory") + endif() + endif() + + _python_mark_as_internal (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_LIBRARY_DEBUG + _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG + _${_PYTHON_PREFIX}_INCLUDE_DIR + _${_PYTHON_PREFIX}_CONFIG + _${_PYTHON_PREFIX}_DEVELOPMENT_MODULE_SIGNATURE + _${_PYTHON_PREFIX}_DEVELOPMENT_EMBED_SIGNATURE) +endif() + +if (${_PYTHON_PREFIX}_FIND_REQUIRED_NumPy) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS) +endif() +if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Interpreter_FOUND) + list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) + + if (DEFINED ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR + AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") + set (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}" CACHE INTERNAL "") + elseif (DEFINED _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) + # compute numpy signature. Depends on interpreter and development signatures + string (MD5 __${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}:${_${_PYTHON_PREFIX}_DEVELOPMENT_MODULE_SIGNATURE}:${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") + if (NOT __${_PYTHON_PREFIX}_NUMPY_SIGNATURE STREQUAL _${_PYTHON_PREFIX}_NUMPY_SIGNATURE + OR NOT EXISTS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") + unset (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR CACHE) + unset (_${_PYTHON_PREFIX}_NUMPY_SIGNATURE CACHE) + endif() + endif() + + if (NOT _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) + execute_process(COMMAND ${${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys\ntry: import numpy; sys.stdout.write(numpy.get_include())\nexcept:pass\n" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_NumPy_PATH + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if (NOT _${_PYTHON_PREFIX}_RESULT) + find_path (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR + NAMES "numpy/arrayobject.h" "numpy/numpyconfig.h" + HINTS "${_${_PYTHON_PREFIX}_NumPy_PATH}" + NO_DEFAULT_PATH) + endif() + endif() + + set (${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") + + if(_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR AND NOT EXISTS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") + set (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE "Cannot find the directory \"${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}\"") + set_property (CACHE _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR-NOTFOUND") + endif() + + if (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) + execute_process (COMMAND ${${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys\ntry: import numpy; sys.stdout.write(numpy.__version__)\nexcept:pass\n" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_NumPy_VERSION) + if (NOT _${_PYTHON_PREFIX}_RESULT) + set (${_PYTHON_PREFIX}_NumPy_VERSION "${_${_PYTHON_PREFIX}_NumPy_VERSION}") + else() + unset (${_PYTHON_PREFIX}_NumPy_VERSION) + endif() + + # final step: set NumPy founded only if Development.Module component is founded as well + set(${_PYTHON_PREFIX}_NumPy_FOUND ${${_PYTHON_PREFIX}_Development.Module_FOUND}) + else() + set (${_PYTHON_PREFIX}_NumPy_FOUND FALSE) + endif() + + if (${_PYTHON_PREFIX}_NumPy_FOUND) + unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE) + + # compute and save numpy signature + string (MD5 __${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}:${_${_PYTHON_PREFIX}_DEVELOPMENT_MODULE_SIGNATURE}:${${_PYTHON_PREFIX}_NumPyINCLUDE_DIR}") + set (_${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${__${_PYTHON_PREFIX}_NUMPY_SIGNATURE}" CACHE INTERNAL "") + else() + unset (_${_PYTHON_PREFIX}_NUMPY_SIGNATURE CACHE) + endif() + + if (${_PYTHON_PREFIX}_ARTIFACTS_INTERACTIVE) + set (${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}" CACHE FILEPATH "${_PYTHON_PREFIX} NumPy Include Directory") + endif() + + _python_mark_as_internal (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR + _${_PYTHON_PREFIX}_NUMPY_SIGNATURE) +endif() + +# final validation +if (${_PYTHON_PREFIX}_VERSION_MAJOR AND + NOT ${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + _python_display_failure ("Could NOT find ${_PYTHON_PREFIX}: Found unsuitable major version \"${${_PYTHON_PREFIX}_VERSION_MAJOR}\", but required major version is exact version \"${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}\"") + + cmake_policy(POP) + return() +endif() + +unset (_${_PYTHON_PREFIX}_REASON_FAILURE) +foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Interpreter Compiler Development NumPy) + if (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE) + string (APPEND _${_PYTHON_PREFIX}_REASON_FAILURE "\n ${_${_PYTHON_PREFIX}_COMPONENT}: ${_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE}") + unset (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE) + endif() +endforeach() + +find_package_handle_standard_args (${_PYTHON_PREFIX} + REQUIRED_VARS ${_${_PYTHON_PREFIX}_REQUIRED_VARS} + VERSION_VAR ${_PYTHON_PREFIX}_VERSION + HANDLE_COMPONENTS) + +# Create imported targets and helper functions +if(_${_PYTHON_PREFIX}_CMAKE_ROLE STREQUAL "PROJECT") + if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND ${_PYTHON_PREFIX}_Interpreter_FOUND + AND NOT TARGET ${_PYTHON_PREFIX}::Interpreter) + add_executable (${_PYTHON_PREFIX}::Interpreter IMPORTED) + set_property (TARGET ${_PYTHON_PREFIX}::Interpreter + PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_EXECUTABLE}") + endif() + + if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND ${_PYTHON_PREFIX}_Compiler_FOUND + AND NOT TARGET ${_PYTHON_PREFIX}::Compiler) + add_executable (${_PYTHON_PREFIX}::Compiler IMPORTED) + set_property (TARGET ${_PYTHON_PREFIX}::Compiler + PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_COMPILER}") + endif() + + if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND ${_PYTHON_PREFIX}_Development.Module_FOUND) + OR ("Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND ${_PYTHON_PREFIX}_Development.Embed_FOUND)) + + macro (__PYTHON_IMPORT_LIBRARY __name) + if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" + OR ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE) + set (_${_PYTHON_PREFIX}_LIBRARY_TYPE SHARED) + else() + set (_${_PYTHON_PREFIX}_LIBRARY_TYPE STATIC) + endif() + + if (NOT TARGET ${__name}) + add_library (${__name} ${_${_PYTHON_PREFIX}_LIBRARY_TYPE} IMPORTED) + endif() + + set_property (TARGET ${__name} + PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIRS}") + + if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE) + # System manage shared libraries in two parts: import and runtime + if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_LIBRARY_DEBUG) + set_property (TARGET ${__name} PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" + IMPORTED_IMPLIB_RELEASE "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" + IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" + IMPORTED_IMPLIB_DEBUG "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}" + IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}") + else() + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_IMPLIB "${${_PYTHON_PREFIX}_LIBRARIES}" + IMPORTED_LOCATION "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") + endif() + else() + if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_LIBRARY_DEBUG) + set_property (TARGET ${__name} PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" + IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}") + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" + IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}") + else() + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}") + endif() + endif() + + if (_${_PYTHON_PREFIX}_LIBRARY_TYPE STREQUAL "STATIC") + # extend link information with dependent libraries + _python_get_config_var (_${_PYTHON_PREFIX}_LINK_LIBRARIES LIBS) + if (_${_PYTHON_PREFIX}_LINK_LIBRARIES) + set_property (TARGET ${__name} + PROPERTY INTERFACE_LINK_LIBRARIES ${_${_PYTHON_PREFIX}_LINK_LIBRARIES}) + endif() + endif() + + if (${_PYTHON_PREFIX}_LINK_OPTIONS + AND _${_PYTHON_PREFIX}_LIBRARY_TYPE STREQUAL "SHARED") + set_property (TARGET ${__name} PROPERTY INTERFACE_LINK_OPTIONS "${${_PYTHON_PREFIX}_LINK_OPTIONS}") + endif() + endmacro() + + if (${_PYTHON_PREFIX}_Development.Embed_FOUND) + __python_import_library (${_PYTHON_PREFIX}::Python) + endif() + + if (${_PYTHON_PREFIX}_Development.Module_FOUND) + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS) + # On Windows/CYGWIN/MSYS, Python::Module is the same as Python::Python + # but ALIAS cannot be used because the imported library is not GLOBAL. + __python_import_library (${_PYTHON_PREFIX}::Module) + else() + if (NOT TARGET ${_PYTHON_PREFIX}::Module) + add_library (${_PYTHON_PREFIX}::Module INTERFACE IMPORTED) + endif() + set_property (TARGET ${_PYTHON_PREFIX}::Module + PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIRS}") + + # When available, enforce shared library generation with undefined symbols + if (APPLE) + set_property (TARGET ${_PYTHON_PREFIX}::Module + PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-undefined,dynamic_lookup") + endif() + if (CMAKE_SYSTEM_NAME STREQUAL "SunOS") + set_property (TARGET ${_PYTHON_PREFIX}::Module + PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-z,nodefs") + endif() + if (CMAKE_SYSTEM_NAME STREQUAL "AIX") + set_property (TARGET ${_PYTHON_PREFIX}::Module + PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-b,erok") + endif() + endif() + endif() + + # + # PYTHON_ADD_LIBRARY ( [STATIC|SHARED|MODULE] src1 src2 ... srcN) + # It is used to build modules for python. + # + function (__${_PYTHON_PREFIX}_ADD_LIBRARY prefix name) + cmake_parse_arguments (PARSE_ARGV 2 PYTHON_ADD_LIBRARY "STATIC;SHARED;MODULE;WITH_SOABI" "" "") + + if (PYTHON_ADD_LIBRARY_STATIC) + set (type STATIC) + elseif (PYTHON_ADD_LIBRARY_SHARED) + set (type SHARED) + else() + set (type MODULE) + endif() + + if (type STREQUAL "MODULE" AND NOT TARGET ${prefix}::Module) + message (SEND_ERROR "${prefix}_ADD_LIBRARY: dependent target '${prefix}::Module' is not defined.\n Did you miss to request COMPONENT 'Development.Module'?") + return() + endif() + if (NOT type STREQUAL "MODULE" AND NOT TARGET ${prefix}::Python) + message (SEND_ERROR "${prefix}_ADD_LIBRARY: dependent target '${prefix}::Python' is not defined.\n Did you miss to request COMPONENT 'Development.Embed'?") + return() + endif() + + add_library (${name} ${type} ${PYTHON_ADD_LIBRARY_UNPARSED_ARGUMENTS}) + + get_property (type TARGET ${name} PROPERTY TYPE) + + if (type STREQUAL "MODULE_LIBRARY") + target_link_libraries (${name} PRIVATE ${prefix}::Module) + # customize library name to follow module name rules + set_property (TARGET ${name} PROPERTY PREFIX "") + if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set_property (TARGET ${name} PROPERTY SUFFIX ".pyd") + endif() + + if (PYTHON_ADD_LIBRARY_WITH_SOABI AND ${prefix}_SOABI) + get_property (suffix TARGET ${name} PROPERTY SUFFIX) + if (NOT suffix) + set (suffix "${CMAKE_SHARED_MODULE_SUFFIX}") + endif() + set_property (TARGET ${name} PROPERTY SUFFIX ".${${prefix}_SOABI}${suffix}") + endif() + else() + if (PYTHON_ADD_LIBRARY_WITH_SOABI) + message (AUTHOR_WARNING "Find${prefix}: Option `WITH_SOABI` is only supported for `MODULE` library type.") + endif() + target_link_libraries (${name} PRIVATE ${prefix}::Python) + endif() + endfunction() + endif() + + if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_NumPy_FOUND + AND NOT TARGET ${_PYTHON_PREFIX}::NumPy AND TARGET ${_PYTHON_PREFIX}::Module) + add_library (${_PYTHON_PREFIX}::NumPy INTERFACE IMPORTED) + set_property (TARGET ${_PYTHON_PREFIX}::NumPy + PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS}") + target_link_libraries (${_PYTHON_PREFIX}::NumPy INTERFACE ${_PYTHON_PREFIX}::Module) + endif() +endif() + +# final clean-up + +# Restore CMAKE_FIND_APPBUNDLE +if (DEFINED _${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE) + set (CMAKE_FIND_APPBUNDLE ${_${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE}) + unset (_${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE) +else() + unset (CMAKE_FIND_APPBUNDLE) +endif() +# Restore CMAKE_FIND_FRAMEWORK +if (DEFINED _${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK) + set (CMAKE_FIND_FRAMEWORK ${_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK}) + unset (_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK) +else() + unset (CMAKE_FIND_FRAMEWORK) +endif() + +cmake_policy(POP) diff --git a/cmake/modules/FindQuadMath.cmake b/cmake/modules/FindQuadMath.cmake new file mode 100644 index 0000000..a3fed45 --- /dev/null +++ b/cmake/modules/FindQuadMath.cmake @@ -0,0 +1,78 @@ +#[=======================================================================[.rst: +FindQuadMath +------------ + +Find the GCC Quad-Precision library + +This module checks if the used compiler has built-in support for QuadMath +by compiling a small source file. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported targets, if found: + +``QuadMath::QuadMath`` + Library to link against if QuadMath should be used. + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``QuadMath_FOUND`` + True if the Quad-Precision library was found. + +#]=======================================================================] + +# Add a feature summary for this package +include(FeatureSummary) +set_package_properties(QuadMath PROPERTIES + DESCRIPTION "GCC Quad-Precision Math Library" + URL "https://gcc.gnu.org/onlinedocs/libquadmath" +) + +# Check if QuadMath support is built into the compiler +include(CheckCXXSourceCompiles) +include(CMakePushCheckState) +cmake_push_check_state() +set(CMAKE_REQUIRED_LIBRARIES quadmath) +if(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) + set(CMAKE_REQUIRED_FLAGS "-fext-numeric-literals") +endif() +check_cxx_source_compiles(" +#include + +int main () +{ + __float128 r = 1.0q; + r = strtoflt128(\"1.2345678\", NULL); + return 0; +}" QuadMath_COMPILES) +cmake_pop_check_state() # Reset CMAKE_REQUIRED_XXX variables + +if(QuadMath_COMPILES) + # Use additional variable for better report message + set(QuadMath_VAR "(Supported by compiler)") +endif() + +# Report that package was found +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(QuadMath + DEFAULT_MSG + QuadMath_VAR QuadMath_COMPILES +) + +# add imported target for quadmath +if(QuadMath_FOUND AND NOT TARGET QuadMath::QuadMath) + # Compiler supports QuadMath: Add appropriate linker flag + add_library(QuadMath::QuadMath INTERFACE IMPORTED) + target_link_libraries(QuadMath::QuadMath INTERFACE quadmath) + + target_compile_definitions(QuadMath::QuadMath INTERFACE + _GLIBCXX_USE_FLOAT128 + ) + target_compile_options(QuadMath::QuadMath INTERFACE + $<$:-fext-numeric-literals> + ) +endif() diff --git a/cmake/modules/FindSphinx.cmake b/cmake/modules/FindSphinx.cmake new file mode 100644 index 0000000..718e20d --- /dev/null +++ b/cmake/modules/FindSphinx.cmake @@ -0,0 +1,41 @@ +# .. cmake_module:: +# +# Find Sphinx - the python documentation tool +# +# You may set the following variables to modify the +# behaviour of this module: +# +# :ref:`SPHINX_ROOT` +# the path to look for sphinx with the highest priority +# +# The following variables are set by this module: +# +# :code:`SPHINX_FOUND` +# whether Sphinx was found +# +# :code:`SPHINX_EXECUTABLE` +# the path to the sphinx-build executable +# +# .. cmake_variable:: SPHINX_ROOT +# +# You may set this variable to have :ref:`FindSphinx` look +# for the :code:`sphinx-build` executable in the given path +# before inspecting system paths. +# + +#TODO export version. + +find_program(SPHINX_EXECUTABLE + NAMES sphinx-build + PATHS ${SPHINX_ROOT} + NO_DEFAULT_PATH) + +find_program(SPHINX_EXECUTABLE + NAMES sphinx-build) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + "Sphinx" + DEFAULT_MSG + SPHINX_EXECUTABLE +) diff --git a/cmake/modules/FindSuiteSparse.cmake b/cmake/modules/FindSuiteSparse.cmake new file mode 100644 index 0000000..938177d --- /dev/null +++ b/cmake/modules/FindSuiteSparse.cmake @@ -0,0 +1,288 @@ +#[=======================================================================[.rst: +FindSuiteSparse +--------------- + +Find the SuiteSparse libraries like UMFPACK or SPQR. + +Use this module by invoking find_package with the form: + + find_package(SuiteSparse + [] [EXACT] # Minimum or EXACT version e.g. 5.1 + [REQUIRED] # Fail with error if SuiteSparse is not found + [COMPONENTS ...] # SuiteSparse libraries by their canonical name + # e.g. "UMFPACK" or "SPQR" + [OPTIONAL_COMPONENTS ...] + # Optional SuiteSparse libraries by their canonical name + ) # e.g. "UMFPACK" or "SPQR" + +Components +^^^^^^^^^^ + +The SuiteSparse module allows to search for the following components + +``CHOLMOD`` + Supernodal Cholesky factorization. +``CSparse`` and ``CXSparse`` + A Concise Sparse Matrix package. +``GraphBLAS`` + Graph algorithms and primitives using semiring algebra. (SuiteSparse >= 5.6) +``KLU`` and ``BTF`` + Sparse LU factorization, well-suited for circuit simulation. +``LDL`` + A sparse LDL' factorization and solve package. +``Mongoose`` + A graph partitioning library. (SuiteSparse >= 5.5) +``SPQR`` + Multifrontal QR factorization. +``UMFPACK`` + Multifrontal LU factorization. + +And ordering methods: ``AMD``, ``CAMD``, ``COLAMD``, and ``CCOLAMD``. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported targets, if found: + +``SuiteSparse::SuiteSparse`` + A meta library including all the requested optional or required components. +``SuiteSparse::`` + Library and include directories for the found ````. + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``SuiteSparse_FOUND`` + True if all the (required) components are found +``SuiteSparse__FOUND`` + True if a searched ```` is found + +Input and Cache Variables +^^^^^^^^^^^^^^^^^^^^^^^^^ + +You may set the following variables to modify the behaviour of +this module: + +``SuiteSparse_ROOT`` + The root directory of the SuiteSparse installation, containing + subdirectories :code:`include/` and :code:`lib/` including the + header files and libraries of SuiteSparse and its components, + respectively. +``SUITESPARSE_INCLUDE_DIR`` + The directory containing ``SuiteSparse_config.h``. +``SUITESPARSE_CONFIG_LIB`` + The path to the suitesparseconfig library. + +#]=======================================================================] + +# text for feature summary +include(FeatureSummary) +set_package_properties("SuiteSparse" PROPERTIES + DESCRIPTION "A suite of sparse matrix software" + URL "http://faculty.cse.tamu.edu/davis/suitesparse.html" +) + +# find package dependencies first +include(CMakeFindDependencyMacro) +find_package(LAPACK QUIET) +find_package(BLAS QUIET) + +# list of possible component names +set(SUITESPARSE_COMPONENTS + "AMD" "BTF" "CAMD" "CCOLAMD" "CHOLMOD" "COLAMD" + "CSparse" "CXSparse" "KLU" "LDL" "SPQR" "UMFPACK") + +# Define required and optional component dependencies +set(SUITESPARSE_CHOLDMOD_REQUIRED_DEPENDENCIES "AMD" "COLAMD" "CCOLAMD") +set(SUITESPARSE_CHOLDMOD_REQUIRES_BLAS TRUE) +set(SUITESPARSE_CHOLDMOD_REQUIRES_LAPACK TRUE) +set(SUITESPARSE_KLU_REQUIRED_DEPENDENCIES "AMD" "COLAMD" "BTF") +set(SUITESPARSE_KLU_OPTIONAL_DEPENDENCIES "CHOLMOD" "CAMD" "CCOLAMD") +set(SUITESPARSE_SPQR_REQUIRED_DEPENDENCIES "CHOLMOD" "AMD" "COLAMD") +set(SUITESPARSE_SPQR_REQUIRES_BLAS TRUE) +set(SUITESPARSE_SPQR_REQUIRES_LAPACK TRUE) +set(SUITESPARSE_UMFPACK_REQUIRED_DEPENDENCIES "AMD") +set(SUITESPARSE_UMFPACK_OPTIONAL_DEPENDENCIES "CHOLMOD" "CAMD" "CCOLAMD" "COLAMD") +set(SUITESPARSE_UMFPACK_REQUIRES_BLAS TRUE) + +# look for library suitesparseconfig +find_library(SUITESPARSE_CONFIG_LIB "suitesparseconfig" + PATH_SUFFIXES "SuiteSparse_config" +) +# look for header file SuiteSparse_config.h +find_path(SUITESPARSE_INCLUDE_DIR "SuiteSparse_config.h" + PATH_SUFFIXES "suitesparse" "include" "SuiteSparse_config" +) + +get_filename_component(SUITESPARSE_LIB_DIR ${SUITESPARSE_CONFIG_LIB} DIRECTORY) +mark_as_advanced(SUITESPARSE_INCLUDE_DIR SUITESPARSE_CONFIG_LIB) + +foreach(_component ${SUITESPARSE_COMPONENTS}) + string(TOLOWER ${_component} _componentLower) + + # look for library of the component + find_library(${_component}_LIBRARY "${_componentLower}" + HINTS ${SUITESPARSE_LIB_DIR} + PATH_SUFFIXES "${_component}/Lib" + ) + # look for header file of the component + find_path(${_component}_INCLUDE_DIR "${_componentLower}.h" + HINTS ${SUITESPARSE_INCLUDE_DIR} + PATH_SUFFIXES "suitesparse" "include" "${_component}/Include" + ) + + mark_as_advanced(${_component}_INCLUDE_DIR ${_component}_LIBRARY) +endforeach() + +# Look for the header files that have different header file names +find_path(SPQR_INCLUDE_DIR "SuiteSparseQR.hpp" + HINTS ${SUITESPARSE_INCLUDE_DIR} + PATH_SUFFIXES "suitesparse" "include" "SPQR/Include" +) +find_path(Mongoose_INCLUDE_DIR "Mongoose.hpp" + HINTS ${SUITESPARSE_INCLUDE_DIR} + PATH_SUFFIXES "suitesparse" "include" "Mongoose/Include" +) +find_path(GraphBLAS_INCLUDE_DIR "GraphBLAS.h" + HINTS ${SUITESPARSE_INCLUDE_DIR} + PATH_SUFFIXES "suitesparse" "include" "GraphBLAS/Include" +) + +# check version of SuiteSparse +find_file(SUITESPARSE_CONFIG_FILE "SuiteSparse_config.h" + HINTS ${SUITESPARSE_INCLUDE_DIR} + NO_DEFAULT_PATH) +if(SUITESPARSE_CONFIG_FILE) + file(READ "${SUITESPARSE_CONFIG_FILE}" suitesparseconfig) + string(REGEX REPLACE ".*#define SUITESPARSE_MAIN_VERSION[ ]+([0-9]+).*" "\\1" + SUITESPARSE_MAJOR_VERSION "${suitesparseconfig}") + string(REGEX REPLACE ".*#define SUITESPARSE_SUB_VERSION[ ]+([0-9]+).*" "\\1" + SUITESPARSE_MINOR_VERSION "${suitesparseconfig}") + string(REGEX REPLACE ".*#define SUITESPARSE_SUBSUB_VERSION[ ]+([0-9]+).*" "\\1" + SUITESPARSE_PREFIX_VERSION "${suitesparseconfig}") + if(SUITESPARSE_MAJOR_VERSION GREATER_EQUAL 0) + set(SuiteSparse_VERSION "${SUITESPARSE_MAJOR_VERSION}") + endif() + if (SUITESPARSE_MINOR_VERSION GREATER_EQUAL 0) + set(SuiteSparse_VERSION "${SuiteSparse_VERSION}.${SUITESPARSE_MINOR_VERSION}") + endif() + if (SUITESPARSE_PREFIX_VERSION GREATER_EQUAL 0) + set(SuiteSparse_VERSION "${SuiteSparse_VERSION}.${SUITESPARSE_PREFIX_VERSION}") + endif() +endif() +unset(SUITESPARSE_CONFIG_FILE CACHE) + + +# check wether everything was found +foreach(_component ${SUITESPARSE_COMPONENTS}) + if(${_component}_LIBRARY AND ${_component}_INCLUDE_DIR) + set(SuiteSparse_${_component}_FOUND TRUE) + else() + set(SuiteSparse_${_component}_FOUND FALSE) + endif() +endforeach(_component) + +# test for required dependencies +foreach(_component ${SUITESPARSE_COMPONENTS}) + foreach(_dependency ${SUITESPARSE_${_component}_REQUIRED_DEPENDENCIES}) + if(NOT SuiteSparse_${_dependency}_FOUND) + set(SuiteSparse_${_component}_FOUND FALSE) + endif() + endforeach(_dependency) +endforeach(_component) + +# SPQR requires SuiteSparse >= 4.3 +if(SPQR_LIBRARY) + if(SuiteSparse_VERSION VERSION_LESS "4.3") + set(SuiteSparse_SPQR_FOUND FALSE) + endif() +endif() + + +# behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args("SuiteSparse" + REQUIRED_VARS + SUITESPARSE_CONFIG_LIB SUITESPARSE_INCLUDE_DIR BLAS_FOUND + VERSION_VAR + SuiteSparse_VERSION + HANDLE_COMPONENTS +) + +# if both headers and library for all required components are found, +# then create imported targets for all components +if(SuiteSparse_FOUND) + if(NOT TARGET SuiteSparse::SuiteSparse_config) + add_library(SuiteSparse::SuiteSparse_config UNKNOWN IMPORTED) + set_target_properties(SuiteSparse::SuiteSparse_config PROPERTIES + IMPORTED_LOCATION ${SUITESPARSE_CONFIG_LIB} + INTERFACE_INCLUDE_DIRECTORIES ${SUITESPARSE_INCLUDE_DIR} + ) + endif() + + # Define component imported-targets + foreach(_component ${SUITESPARSE_COMPONENTS}) + if(SuiteSparse_${_component}_FOUND AND NOT TARGET SuiteSparse::${_component}) + add_library(SuiteSparse::${_component} UNKNOWN IMPORTED) + set_target_properties(SuiteSparse::${_component} PROPERTIES + IMPORTED_LOCATION ${${_component}_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${${_component}_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES SuiteSparse::SuiteSparse_config + ) + endif() + endforeach(_component) + + foreach(_component ${SUITESPARSE_COMPONENTS}) + # Link required dependencies + foreach(_dependency ${SUITESPARSE_${_component}_REQUIRED_DEPENDENCIES}) + target_link_libraries(SuiteSparse::${_component} + INTERFACE SuiteSparse::${_dependency}) + endforeach(_dependency) + + # Link found optional dependencies + foreach(_dependency ${SUITESPARSE_${_component}_OPTIONAL_DEPENDENCIES}) + if(SuiteSparse_${_dependency}_FOUND) + target_link_libraries(SuiteSparse::${_component} + INTERFACE SuiteSparse::${_dependency}) + endif() + endforeach(_dependency) + + # Link BLAS library + if(SUITESPARSE_${_component}_REQUIRES_BLAS) + if(TARGET BLAS::BLAS) + target_link_libraries(SuiteSparse::${_component} + INTERFACE BLAS::BLAS) + else() + target_link_libraries(SuiteSparse::${_component} + INTERFACE ${BLAS_LINKER_FLAGS} ${BLAS_LIBRARIES}) + endif() + endif() + + # Link LAPACK library + if(SUITESPARSE_${_component}_REQUIRES_LAPACK) + if(TARGET LAPACK::LAPACK) + target_link_libraries(SuiteSparse::${_component} + INTERFACE LAPACK::LAPACK) + else() + target_link_libraries(SuiteSparse::${_component} + INTERFACE ${LAPACK_LINKER_FLAGS} ${LAPACK_LIBRARIES}) + endif() + endif() + endforeach(_component) + + # Combine all requested components to an imported target + if(NOT TARGET SuiteSparse::SuiteSparse) + add_library(SuiteSparse::SuiteSparse INTERFACE IMPORTED) + target_link_libraries(SuiteSparse::SuiteSparse + INTERFACE SuiteSparse::SuiteSparse_config) + endif() + foreach(_component ${SuiteSparse_FIND_COMPONENTS}) + if(SuiteSparse_${_component}_FOUND) + set(HAVE_SUITESPARSE_${_component} TRUE) + target_link_libraries(SuiteSparse::SuiteSparse + INTERFACE SuiteSparse::${_component}) + endif() + endforeach(_component) +endif() diff --git a/cmake/modules/FindTBB.cmake b/cmake/modules/FindTBB.cmake new file mode 100644 index 0000000..b75bd08 --- /dev/null +++ b/cmake/modules/FindTBB.cmake @@ -0,0 +1,93 @@ +#[=======================================================================[.rst: +FindTBB +------- + +Finds the Threading Building Blocks (TBB) library. + +This is a fallback implementation in case the TBB library does not provide +itself a corresponding TBBConfig.cmake file. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported targets, if found: + +``TBB::tbb`` + Imported library to link against if TBB should be used. + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``TBB_FOUND`` + True if the TBB library was found. + +Finding the TBB library +^^^^^^^^^^^^^^^^^^^^^^^ + +Two strategies are implemented for finding the TBB library: + +1. Searching for the TBB cmake config file, typically named + ``TBBConfig.cmake``. In recent TBB versions, this file can be + created using a script provided by TBB itself. Simply set the + variable ``TBB_DIR`` to the directory containing the config file + in order to find TBB. + +2. Using pkg-config to configure TBB. Therefore it is necessary + to find the ``tbb.pc`` file. Several distributions provide this file + directly. In order to point pkg-config to the location of that file, + simply set the environmental variable ``PKG_CONFIG_PATH`` to include + the directory containing the .pc file, or add this path to the + ``CMAKE_PREFIX_PATH``. + +#]=======================================================================] + + +# text for feature summary +include(FeatureSummary) +set_package_properties("TBB" PROPERTIES + DESCRIPTION "Intel's Threading Building Blocks" +) + +# first, try to find TBBs cmake configuration +find_package(TBB ${TBB_FIND_VERSION} QUIET CONFIG) +if(TBB_FOUND AND TARGET TBB::tbb) + message(STATUS "Found TBB: using configuration from TBB_DIR=${TBB_DIR} (found version \"${TBB_VERSION}\")") + return() +endif() + +# Add a backport of cmakes FindPkgConfig module +if(${CMAKE_VERSION} VERSION_LESS "3.19.4") + list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_LIST_DIR}/FindPkgConfig") +endif() + +# second, try to find TBBs pkg-config file +find_package(PkgConfig) +if(PkgConfig_FOUND) + if(TBB_FIND_VERSION) + pkg_check_modules(PkgConfigTBB tbb>=${TBB_FIND_VERSION} QUIET IMPORTED_TARGET GLOBAL) + else() + pkg_check_modules(PkgConfigTBB tbb QUIET IMPORTED_TARGET GLOBAL) + endif() +endif() + +# check whether the static library was found +if(PkgConfigTBB_STATIC_FOUND) + set(_tbb PkgConfigTBB_STATIC) +else() + set(_tbb PkgConfigTBB) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args("TBB" + REQUIRED_VARS + ${_tbb}_LINK_LIBRARIES ${_tbb}_FOUND PkgConfig_FOUND + VERSION_VAR + ${_tbb}_VERSION + FAIL_MESSAGE "Could NOT find TBB (set TBB_DIR to path containing TBBConfig.cmake or set PKG_CONFIG_PATH to include the location of the tbb.pc file)" +) + +if(${_tbb}_FOUND AND NOT TARGET TBB::tbb) + add_library(TBB::tbb ALIAS PkgConfig::PkgConfigTBB) +endif() diff --git a/cmake/modules/Headercheck.cmake b/cmake/modules/Headercheck.cmake new file mode 100644 index 0000000..d8d0204 --- /dev/null +++ b/cmake/modules/Headercheck.cmake @@ -0,0 +1,96 @@ +# .. cmake_variable:: ENABLE_HEADERCHECK +# +# Set this variable to TRUE if you want to use the CMake +# reimplementation of the old autotools feaure :code:`make headercheck`. +# There has been a couple of issues with this implementation in +# the past, so it was deactivated by default. +# +include_guard(GLOBAL) + +# sets up a global property with the names of all header files +# in the module and a global target depending on all checks +macro(setup_headercheck) + #glob for headers + file(GLOB_RECURSE all_headers "*.hh") + # strip hidden files + string(REGEX REPLACE "[^;]*/\\.[^;/]*\\.hh;?" "" headers "${all_headers}") + set_property(GLOBAL PROPERTY headercheck_list ${headers}) + + #define headercheck target + dune_module_path(MODULE dune-common RESULT scriptdir SCRIPT_DIR) + if(NOT TARGET headercheck) + add_custom_target(headercheck ${CMAKE_COMMAND} + -DENABLE_HEADERCHECK=${ENABLE_HEADERCHECK} + -P ${scriptdir}/FinalizeHeadercheck.cmake + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + endif() +endmacro(setup_headercheck) + +# these macros are used to exclude headers from make headercheck +# call this from a CMakeLists.txt file with a list of headers in that directory +macro(exclude_from_headercheck) + #make this robust to argument being passed with or without "" + string(REGEX REPLACE "[\ \n]+([^\ ])" ";\\1" list ${ARGV0}) + set(list "${list};${ARGV}") + get_property(headerlist GLOBAL PROPERTY headercheck_list) + foreach(item ${list}) + list(REMOVE_ITEM headerlist "${CMAKE_CURRENT_SOURCE_DIR}/${item}") + endforeach() + set_property(GLOBAL PROPERTY headercheck_list ${headerlist}) +endmacro(exclude_from_headercheck) + +macro(exclude_dir_from_headercheck) + file(GLOB list RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.hh") + exclude_from_headercheck(${list}) +endmacro(exclude_dir_from_headercheck) + +macro(exclude_subdir_from_headercheck DIRNAME) + file(GLOB_RECURSE exlist "${CMAKE_CURRENT_SOURCE_DIR}/${DIRNAME}/*.hh") + get_property(headerlist GLOBAL PROPERTY headercheck_list) + foreach(item ${exlist}) + list(REMOVE_ITEM headerlist "${item}") + endforeach() + set_property(GLOBAL PROPERTY headercheck_list ${headerlist}) +endmacro(exclude_subdir_from_headercheck) + +macro(exclude_all_but_from_headercheck) + file(GLOB excllist RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.hh") + #make this robust to argument being passed with or without "" + string(REGEX REPLACE "[\ \n]+([^\ \n])" ";\\1" list ${ARGV0}) + set(list "${list};${ARGV}") + foreach(item ${list}) + list(REMOVE_ITEM excllist ${item}) + endforeach() + exclude_from_headercheck(${excllist}) +endmacro(exclude_all_but_from_headercheck) + +# configure all headerchecks +macro(finalize_headercheck) + if(ENABLE_HEADERCHECK) + get_property(headerlist GLOBAL PROPERTY headercheck_list) + foreach(header ${headerlist}) + #do some name conversion + string(REGEX REPLACE ".*/([^/]*)" "\\1" simple ${header}) + string(REPLACE ${PROJECT_SOURCE_DIR} "" rel ${header}) + string(REGEX REPLACE "(.*)/[^/]*" "\\1" relpath ${rel}) + string(REGEX REPLACE "/" "_" targname ${rel}) + + #generate the headercheck .cc file + file(WRITE ${CMAKE_BINARY_DIR}/headercheck/${rel}.cc "#ifdef HAVE_CONFIG_H\n#include\n#endif\n#include<${simple}>\n#include<${simple}>\nint main(){return 0;}") + + # add target for the check of current header, this is implemented as a library + # to prevent CMake from automatically trying to link the target, functionality + # of macro try_compile() is unfortunately not availbale due to it not being scriptable. + add_library(headercheck_${targname} STATIC EXCLUDE_FROM_ALL + ${CMAKE_BINARY_DIR}/headercheck/${rel}.cc) + add_dependencies(headercheck headercheck_${targname}) + + #add PKG_ALL_FLAGS and the directory where the header is located + set_property(TARGET headercheck_${targname} + APPEND_STRING PROPERTY COMPILE_FLAGS "-DHEADERCHECK -I${PROJECT_SOURCE_DIR}${relpath} -I${CMAKE_BINARY_DIR}") + set_property(TARGET headercheck_${targname} PROPERTY ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/headercheck/${relpath}") + add_dune_all_flags(headercheck_${targname}) + unset(headercheck_${targname}_LIB_DEPENDS CACHE) + endforeach(header ${headerlist}) + endif() +endmacro(finalize_headercheck) diff --git a/cmake/modules/OverloadCompilerFlags.cmake b/cmake/modules/OverloadCompilerFlags.cmake new file mode 100644 index 0000000..c2caf67 --- /dev/null +++ b/cmake/modules/OverloadCompilerFlags.cmake @@ -0,0 +1,143 @@ +# check whether the user wants to overload compile flags upon calling make +# +# Provides the following macros: +# +# initialize_compiler_script() : needs to be called before further flags are added to CMAKE_CXX_FLAGS +# finalize_compiler_script() : needs to be called at the end of the cmake macros, e.g. in finalize_dune_project +# +# Those two macro calls are hooked into dune_project/finalize_dune_project. +# +# .. cmake_variable:: ALLOW_CXXFLAGS_OVERWRITE +# +# Setting this option will allow you to overload preprocessor definitions from +# the command line, as it was possible naturally with the autotools build system. +# This feature only works with a :code:`Unix Makefiles` based generator. You can +# use it as: +# +# :code:`make CXXFLAGS="your flags" GRIDTYPE="grid type"` +# +# :code:`GRIDTYPE` can be anything defined in :code:`config.h` via the :ref:`dune_define_gridtype` macro from dune-grid. +# Furthermore any CPP variable of the form :code:`-DVAR=VALUE` can be overloaded on the command line. +# +# .. note:: +# If you don't know what this is or what it's good for, don't use it. +# +include_guard(GLOBAL) + +option(ALLOW_CXXFLAGS_OVERWRITE OFF) +option(ALLOW_CFLAGS_OVERWRITE OFF) + +set(CXX_COMPILER_SCRIPT "${CMAKE_BINARY_DIR}/CXX_compiler.sh" ) +set(C_COMPILER_SCRIPT "${CMAKE_BINARY_DIR}/C_compiler.sh" ) + +macro(find_extended_unix_commands) + include(FindUnixCommands) + set(FLAGSNAMES "ALLOW_CXXFLAGS_OVERWRITE and/or ALLOW_CFLAGS_OVERWRITE") + find_program (GREP_PROGRAM grep) + if(NOT GREP_PROGRAM) + message( SEND_ERROR "grep not found, please disable ${FLAGSNAMES}") + endif() + find_program (SED_PROGRAM sed) + if(NOT SED_PROGRAM) + message( SEND_ERROR "sed not found, please disable ${FLAGSNAMES}") + endif() + find_program (CUT_PROGRAM cut) + if(NOT CUT_PROGRAM) + message( SEND_ERROR "cut not found, please disable ${FLAGSNAMES}") + endif() + find_program (ENV_PROGRAM env) + if(NOT ENV_PROGRAM) + message( SEND_ERROR "env not found, please disable ${FLAGSNAMES}") + endif() + find_program (ECHO_PROGRAM echo) + if(NOT ECHO_PROGRAM) + message( SEND_ERROR "echo not found, please disable ${FLAGSNAMES}") + endif() + find_program (CHMOD_PROGRAM chmod) + if(NOT CHMOD_PROGRAM) + message( SEND_ERROR "chmod not found, please disable ${FLAGSNAMES}") + endif() + mark_as_advanced(GREP_PROGRAM) + mark_as_advanced(SED_PROGRAM) + mark_as_advanced(CUT_PROGRAM) + mark_as_advanced(ENV_PROGRAM) + mark_as_advanced(ECHO_PROGRAM) + mark_as_advanced(CHMOD_PROGRAM) +endmacro(find_extended_unix_commands) + +# init compiler script and store CXX flags +macro(initialize_compiler_script) + if(ALLOW_CXXFLAGS_OVERWRITE AND (${CMAKE_GENERATOR} MATCHES ".*Unix Makefiles.*")) + # check for unix commands necessary + find_extended_unix_commands() + # set CXXFLAGS as environment variable + set( DEFAULT_CXXFLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "default CXX flags") + set( CMAKE_CXX_FLAGS "" ) + set( DEFAULT_CXX_COMPILER ${CMAKE_CXX_COMPILER} ) + set( CXX_COMPILER_SCRIPT_FILE "#!${BASH}\nexec ${CMAKE_CXX_COMPILER} \"\$@\"") + file(WRITE ${CXX_COMPILER_SCRIPT} "${CXX_COMPILER_SCRIPT_FILE}") + execute_process(COMMAND ${CHMOD_PROGRAM} 755 ${CXX_COMPILER_SCRIPT}) + set(CMAKE_CXX_COMPILER ${CXX_COMPILER_SCRIPT}) + endif() + if(ALLOW_CFLAGS_OVERWRITE AND (${CMAKE_GENERATOR} MATCHES ".*Unix Makefiles.*")) + # check for unix commands necessary + find_extended_unix_commands() + # set CFLAGS as environment variable + set( DEFAULT_CFLAGS ${CMAKE_C_FLAGS} CACHE STRING "default C flags") + set( CMAKE_C_FLAGS "" ) + set( DEFAULT_C_COMPILER ${CMAKE_C_COMPILER} ) + set( C_COMPILER_SCRIPT_FILE "#!${BASH}\nexec ${CMAKE_C_COMPILER} \"\$@\"") + file(WRITE ${C_COMPILER_SCRIPT} "${C_COMPILER_SCRIPT_FILE}") + execute_process(COMMAND ${CHMOD_PROGRAM} 755 ${C_COMPILER_SCRIPT}) + set(CMAKE_C_COMPILER ${C_COMPILER_SCRIPT}) + endif() +endmacro() + +# finalize compiler script and write it +macro(finalize_compiler_script) + if(${CMAKE_GENERATOR} MATCHES ".*Unix Makefiles.*") + # check CXX compiler + if((ALLOW_CXXFLAGS_OVERWRITE)) + set(COMPILERS "CXX") + endif() + # check C compiler + if((ALLOW_CFLAGS_OVERWRITE)) + set(COMPILERS ${COMPILERS} "C") + endif() + + # for the found compilers for flag overloading generate compiler script + foreach(COMP ${COMPILERS}) + set( COMPILER_SCRIPT_FILE "#!${BASH}\nSED=${SED_PROGRAM}\nGREP=${GREP_PROGRAM}") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\nCUT=${CUT_PROGRAM}\nENV=${ENV_PROGRAM}\nECHO=${ECHO_PROGRAM}") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\n# store flags\nFLAGS=\"\$@\"") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\nMAKE_EXECUTABLE_NEW=0\n") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\nif [ \"\$${COMP}FLAGS\" == \"\" ]; then\n # default ${COMP} flags\n ${COMP}FLAGS=\"${DEFAULT_CXXFLAGS}\"\nfi\n") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\nif [ \"\$EXTRA_${COMP}FLAGS\" != \"\" ]; then\n # extra ${COMP} flags\n ${COMP}FLAGS=\"$${COMP}FLAGS $EXTRA_${COMP}FLAGS\"\nfi\n") + # only for CXX we need to scan config.h for GRIDTYPE + if( ${COMP} STREQUAL "CXX" ) + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\nGRIDS=\nCONFIG_H=${CMAKE_BINARY_DIR}/config.h") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\nif [ \"\$GRIDTYPE\" != \"\" ]; then") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\n GRIDS=`\$GREP \"defined USED_[A-Z_]*_GRIDTYPE\" \$CONFIG_H | \$SED 's/\\(.*defined USED\\_\\)\\(.*\\)\\(\\_GRIDTYPE*\\)/\\2/g'`\nfi\n") + endif() + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\nOLDFLAGS=\$FLAGS\nFLAGS=") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\nfor FLAG in \$OLDFLAGS; do") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\n NEWFLAG=\$FLAG\n VARNAME=`\$ECHO \$FLAG | \$GREP \"\\-D\" | \$SED 's/-D//g'`") + # only for CXX we have GRIDTYPE + if( ${COMP} STREQUAL "CXX" ) + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\n for GRID in \$GRIDS; do\n if [ \"\$VARNAME\" == \"\$GRID\" ]; then\n NEWFLAG=\"-D\$GRIDTYPE\"\n break\n fi\n done") + endif() + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\n VARNAME=`\$ECHO \$VARNAME | \$GREP \"=\" | \$CUT -d \"=\" -f 1`") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\n if [ \"\$VARNAME\" != \"\" ]; then\n VAR=`\$ENV | \$GREP \$VARNAME`\n if [ \"\$VAR\" != \"\" ]; then") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\n # add variable from environment to flags list\n NEWFLAG=\"-D\$VARNAME=\${!VARNAME}\"\n fi\n fi") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\n FLAGS=\"\$FLAGS \$NEWFLAG\"\ndone") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\n\$ECHO \"${DEFAULT_${COMP}_COMPILER} \$${COMP}FLAGS \$FLAGS\"") + set( COMPILER_SCRIPT_FILE "${COMPILER_SCRIPT_FILE}\nexec ${DEFAULT_${COMP}_COMPILER} \$${COMP}FLAGS \$FLAGS") + message("-- Generating ${COMP} compiler script for ${COMP}FLAGS overloading on command line") + if( ${COMP} STREQUAL "CXX" ) + file(WRITE ${CXX_COMPILER_SCRIPT} "${COMPILER_SCRIPT_FILE}") + else() + file(WRITE ${C_COMPILER_SCRIPT} "${COMPILER_SCRIPT_FILE}") + endif() + endforeach() + endif() +endmacro() diff --git a/cmake/modules/UseInkscape.cmake b/cmake/modules/UseInkscape.cmake new file mode 100644 index 0000000..5b5cd39 --- /dev/null +++ b/cmake/modules/UseInkscape.cmake @@ -0,0 +1,53 @@ +# Module that provides conversion routines using inkscape +# +# .. cmake_function:: inkscape_generate_png_from_svg +# +# .. cmake_param:: OUTPUT_DIR +# :single: +# +# The output directory for the generated png files. +# Defaults to the current build directory. +# +# .. cmake_param:: pngfiles +# :single: +# :positional: +# :required: +# +# The files that should be converted. +# +# .. cmake_param:: DPI +# :single: +# +# dpi value for the generated image (default: 90) +# +# TODO Switch to named arguments! +# +include_guard(GLOBAL) + +include(CMakeParseArguments) + +function(inkscape_generate_png_from_svg) + if(NOT INKSCAPE) + return() + endif() + cmake_parse_arguments(INKSCAPE "" "OUTPUT_DIR;DPI" "" ${ARGN}) + if(NOT INKSCAPE_OUTPUT_DIR) + set(INKSCAPE_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) + endif() + if(NOT INKSCAPE_DPI) + set(INKSCAPE_DPI 90) + endif() + + foreach(pic ${INKSCAPE_UNPARSED_ARGUMENTS}) + string(REGEX REPLACE "\\.[a-zA-Z]+" ".svg" input ${pic}) + if( INKSCAPE_NEW_VERSION ) + execute_process( + COMMAND ${INKSCAPE} --export-dpi=${INKSCAPE_DPI} --export-type=png --export-filename=${pic} ${CMAKE_CURRENT_SOURCE_DIR}/${input} + WORKING_DIRECTORY ${INKSCAPE_OUTPUT_DIR}) + else() + execute_process( + COMMAND ${INKSCAPE} -z --export-dpi=${INKSCAPE_DPI} -e ${pic} ${CMAKE_CURRENT_SOURCE_DIR}/${input} + WORKING_DIRECTORY ${INKSCAPE_OUTPUT_DIR}) + endif() + endforeach() +endfunction() diff --git a/cmake/modules/UseLatexMk.cmake b/cmake/modules/UseLatexMk.cmake new file mode 100644 index 0000000..70ca989 --- /dev/null +++ b/cmake/modules/UseLatexMk.cmake @@ -0,0 +1,255 @@ +# UseLatexMk.cmake is a CMake module to build Latex documents +# from CMake. +# +# add_latex_document(SOURCE texsource +# [TARGET target] +# [EXCLUDE_FROM_ALL] +# [REQUIRED] +# [FATHER_TARGET father1 [father2 ...]] +# [RCFILE rcfile1 [rcfile2 ...]] +# [INSTALL destination] +# [BUILD_ON_INSTALL] +# ) +# +# The arguments: +# SOURCE +# Required argument with a single tex source that defines the document to be built +# TARGET +# An optional target name, defaults to a suitable mangling of the given source and its path. +# An additional target with _clean appended will be added as well, which cleans the output +# and all auxiliary files. +# EXCLUDE_FROM_ALL +# Set this to avoid the target from being built by default. If the FATHER_TARGET +# parameter is set, this option is automatically set. +# REQUIRED +# Set this option to issue a fatal error if the document could not +# be built. By default it is only skipped. +# FATHER_TARGET +# A list of meta-targets that should trigger a rebuild of this target (like "make doc"). +# The targets are expected to exist already. Specifying any such targets will automatically add the +# above EXCLUDE_FROM_ALL option. +# RCFILE +# A list configuration file to customize the latexmk build process. These are read by latexmk +# *after* the automatically generated rc file in the indicated order. Note that latexmk rcfiles +# override any previous settings. +# You may also use CMake variables within @'s (like @CMAKE_CURRENT_BINARY_DIR@) and have +# them replaced with the matching CMake variables (see cmake's configure_file command). +# Note, that this is a powerful, but advanced feature. For details on what can be achieved +# see the latexmk manual. Note, that triggering non-PDF builds through latexmkrc files might +# cause problems with other features of UseLatexMk. +# INSTALL +# Set this option to an install directory to create an installation rule for this document. +# BUILD_ON_INSTALL +# Set this option, if you want to trigger a build of this document during installation. +# +# Furthermore, UseLatexMk defines a CMake target clean_latex which cleans the build tree from +# all PDF output and all auxiliary files. Note, that (at least for the Unix Makefiles generator) +# it is not possible to connect this process with the builtin clean target. +# +# Please note the following security restriction: +# +# UseLatexMk relies on latexmk separating input and output directory correctly. +# This includes using an absolute path for the output directory. On some TeX +# systems this requires the disabling of a security measure by setting `openout_any = a`. +# From the latexmk documentation: +# +# Commonly, the directory specified for output files is a subdirectory of the current working direc- +# tory. However, if you specify some other directory, e.g., "/tmp/foo" or "../output", be aware that +# this could cause problems, e.g., with makeindex or bibtex. This is because modern versions of +# these programs, by default, will refuse to work when they find that they are asked to write to a file +# in a directory that appears not to be the current working directory or one of its subdirectories. This +# is part of security measures by the whole TeX system that try to prevent malicious or errant TeX +# documents from incorrectly messing with a user’s files. If for $out_dir or $aux_dir you really do +# need to specify an absolute pathname (e.g., "/tmp/foo") or a path (e.g., "../output") that includes a +# higher-level directory, and you need to use makeindex or bibtex, then you need to disable the secu- +# rity measures (and assume any risks). One way of doing this is to temporarily set an operating +# system environment variable openout_any to "a" (as in "all"), to override the default "paranoid" +# setting. +# +# UseLatexMk.cmake allows to reenable the TeX security measure by setting LATEXMK_PARANOID to TRUE +# through cmake -D, but it is not guaranteed to work correctly in that case. +# +# For further informations, visit https://github.com/dokempf/UseLatexMk +# +# +# Copyright (c) 2017, Dominic Kempf, Steffen Müthing +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# * Neither the name of the Universität Heidelberg nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +include_guard(GLOBAL) + +# Find LATEX and LatexMk +find_package(LATEX) +find_package(LatexMk) + +# Find the latexmkrc template file shipped alongside UseLatexMk.cmake +find_file(LATEXMKRC_TEMPLATE + latexmkrc.cmake + HINTS ${CMAKE_MODULE_PATH} + ${CMAKE_CURRENT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/cmake + ${PROJECT_SOURCE_DIR}/cmake/modules + NO_CMAKE_FIND_ROOT_PATH + ) + +# Add the clean_latex target +if(TARGET clean_latex) + message(WARNING "clean_latex target already exists. UseLatexMk attaches clean rules to it!") +else() + add_custom_target(clean_latex) +endif() + +set(LATEXMK_SOURCES_BUILD_FROM) + +function(add_latex_document) + # Parse the input parameters to the function + set(OPTION REQUIRED EXCLUDE_FROM_ALL BUILD_ON_INSTALL) + set(SINGLE SOURCE TARGET INSTALL) + set(MULTI FATHER_TARGET RCFILE) + include(CMakeParseArguments) + cmake_parse_arguments(LMK "${OPTION}" "${SINGLE}" "${MULTI}" ${ARGN}) + + if(LMK_UNPARSED_ARGUMENTS) + message("add_latex_document: Unparsed arguments! This often indicates typos in named arguments.") + endif() + + # Apply default arguments and check for required arguments + if(NOT LMK_SOURCE) + message(FATAL_ERROR "No tex source specified for add_latex_document!") + endif() + if(NOT LMK_TARGET) + # Construct a nice target name from the source file + get_filename_component(LMK_TARGET ${LMK_SOURCE} ABSOLUTE) + file(RELATIVE_PATH LMK_TARGET ${PROJECT_SOURCE_DIR} ${LMK_TARGET}) + string(REPLACE "/" "_" LMK_TARGET ${LMK_TARGET}) + string(REPLACE "." "_" LMK_TARGET ${LMK_TARGET}) + endif() + if(LMK_FATHER_TARGET) + set(LMK_EXCLUDE_FROM_ALL TRUE) + endif() + if(LMK_BUILD_ON_INSTALL AND (NOT LMK_INSTALL)) + message(WARNING "Specified to build on installation, but not installing!") + endif() + + # Verify that each source is used exactly once + set(ABS_SOURCE ${LMK_SOURCE}) + if(NOT IS_ABSOLUTE ${ABS_SOURCE}) + get_filename_component(ABS_SOURCE ${ABS_SOURCE} ABSOLUTE) + endif() + list(FIND LATEXMK_SOURCES_BUILD_FROM ${ABS_SOURCE} ALREADY_BUILT) + if(NOT "${ALREADY_BUILT}" STREQUAL "-1") + message(FATAL_ERROR "UseLatexMk: You are building twice from the same source, which is unsupported!") + endif() + set(LATEXMK_SOURCES_BUILD_FROM ${LATEXMK_SOURCES_BUILD_FROM} ${ABS_SOURCE} PARENT_SCOPE) + + # Check the existence of the latexmk executable and skip/fail if not present + if(NOT (LATEXMK_FOUND AND PDFLATEX_COMPILER)) + if(LMK_REQUIRED) + message(FATAL_ERROR "Some Latex documents were required by the project, but LATEX or LatexMk were not found!") + else() + return() + endif() + endif() + + # Determine the output name + get_filename_component(output ${LMK_SOURCE} NAME_WE) + set(OUTPUT_PDF ${CMAKE_CURRENT_BINARY_DIR}/${output}.pdf) + + # Inspect the EXCLUDE_FROM_ALL option + if(LMK_EXCLUDE_FROM_ALL) + set(ALL_OPTION "") + else() + set(ALL_OPTION "ALL") + endif() + + # Generate a latexmkrc file for this project + if(NOT LATEXMKRC_TEMPLATE) + message("Fatal error: The latexmkrc template file could not be found. Consider adding its path to CMAKE_MODULE_PATH") + endif() + set(LATEXMKRC_FILE "${CMAKE_CURRENT_BINARY_DIR}/${LMK_TARGET}.latexmkrc") + configure_file(${LATEXMKRC_TEMPLATE} ${LATEXMKRC_FILE} @ONLY) + set(LATEXMKRC_OPTIONS -r ${LATEXMKRC_FILE}) + + # Process additional latexmkrc files + foreach(rcfile ${LMK_RCFILE}) + get_filename_component(rcfile_base ${rcfile} NAME) + set(LATEXMKRC_FILE "${CMAKE_CURRENT_BINARY_DIR}/${LMK_TARGET}_${rcfile_base}") + configure_file(${rcfile} ${LATEXMKRC_FILE} @ONLY) + set(LATEXMKRC_OPTIONS ${LATEXMKRC_OPTIONS} -r ${LATEXMKRC_FILE}) + endforeach() + + # Add the BYPRODUCTS parameter, if the CMake version supports it + set(BYPRODUCTS_PARAMETER "") + if (CMAKE_VERSION VERSION_GREATER "3.2") + set(BYPRODUCTS_PARAMETER BYPRODUCTS ${OUTPUT_PDF}) + endif() + + # Maybe allow latexmk the use of absolute paths + if(NOT LATEXMK_PARANOID) + set($ENV{openout_any} "a") + endif() + + # Call the latexmk executable + # NB: Using add_custom_target here results in the target always being outofdate. + # This offloads the dependency tracking from cmake to latexmk. This is an + # intentional decision of UseLatexMk to avoid listing dependencies of the tex source. + add_custom_target(${LMK_TARGET} + ${ALL_OPTION} + COMMAND ${LATEXMK_EXECUTABLE} ${LATEXMKRC_OPTIONS} ${LMK_SOURCE} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Building PDF from ${LMK_SOURCE}..." + ${BYPRODUCTS_PARAMETER} + ) + + # Add dependencies to father targets + foreach(father ${LMK_FATHER_TARGET}) + if(NOT TARGET ${father}) + message(FATAL_ERROR "The target given to add_latex_documents FATHER_TARGET parameter does not exist") + endif() + add_dependencies(${father} ${LMK_TARGET}) + endforeach() + + # Add installation rules + if(LMK_BUILD_ON_INSTALL) + install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} --build . --target ${LMK_TARGET} --config $)") + endif() + if(LMK_INSTALL) + install(FILES ${OUTPUT_PDF} + DESTINATION ${LMK_INSTALL} + OPTIONAL) + endif() + + # Add a clean up rule to the clean_latex target + add_custom_target(${LMK_TARGET}_clean + COMMAND ${LATEXMK_EXECUTABLE} -C ${LATEXMKRC_OPTIONS} ${LMK_SOURCE} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Cleaning build results from target ${LMK_TARGET}" + ) + add_dependencies(clean_latex ${LMK_TARGET}_clean) +endfunction() diff --git a/cmake/modules/latexmkrc.cmake b/cmake/modules/latexmkrc.cmake new file mode 100644 index 0000000..8721762 --- /dev/null +++ b/cmake/modules/latexmkrc.cmake @@ -0,0 +1,13 @@ +# .latexmkrc generated by CMake from UseLatexMk.cmake starts here + +$bibtex = "@BIBTEX_COMPILER@ %O %S"; +$dvips = "@DVIPS_CONVERTER@ %O -o %D %S"; +$latex = "@LATEX_COMPILER@ %O %S"; +$make = "@CMAKE_MAKE_COMMAND@"; +$makeindex = "@MAKEINDEX_COMPILER@ %O -o %D %S"; +$out_dir = "@CMAKE_CURRENT_BINARY_DIR@"; +$pdf_mode = 1; +$pdflatex = "@PDFLATEX_COMPILER@ -shell-escape -interaction=nonstopmode %O %S"; +$ps2pdf = "@PS2PDF_CONVERTER@ %O %S %D"; + +# .latexmkrc generated by CMake from UseLatexMk.cmake ends here diff --git a/cmake/pkg/dune-common-config.cmake.in b/cmake/pkg/dune-common-config.cmake.in new file mode 100644 index 0000000..4b06cdd --- /dev/null +++ b/cmake/pkg/dune-common-config.cmake.in @@ -0,0 +1,23 @@ +if(NOT @DUNE_MOD_NAME@_FOUND) +@PACKAGE_INIT@ + +#import the target +get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" PATH) +include("${_dir}/@DUNE_MOD_NAME@-targets.cmake") + +#report other information +set_and_check(@DUNE_MOD_NAME@_PREFIX "${PACKAGE_PREFIX_DIR}") +set_and_check(@DUNE_MOD_NAME@_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") +set(@DUNE_MOD_NAME@_CXX_FLAGS "@CMAKE_CXX_FLAGS@") +set(@DUNE_MOD_NAME@_CXX_FLAGS_DEBUG "@CMAKE_CXX_FLAGS_DEBUG@") +set(@DUNE_MOD_NAME@_CXX_FLAGS_MINSIZEREL "@CMAKE_CXX_FLAGS_MINSIZEREL@") +set(@DUNE_MOD_NAME@_CXX_FLAGS_RELEASE "@CMAKE_CXX_FLAGS_RELEASE@") +set(@DUNE_MOD_NAME@_CXX_FLAGS_RELWITHDEBINFO "@CMAKE_CXX_FLAGS_RELWITHDEBINFO@") +set(@DUNE_MOD_NAME@_LIBRARIES "dunecommon") +set_and_check(@DUNE_MOD_NAME@_SCRIPT_DIR "@PACKAGE_SCRIPT_DIR@") +set_and_check(DOXYSTYLE_FILE "@PACKAGE_DOXYSTYLE_DIR@/Doxystyle") +set_and_check(DOXYGENMACROS_FILE "@PACKAGE_DOXYSTYLE_DIR@/doxygen-macros") +set(@DUNE_MOD_NAME@_DEPENDS "@DUNE_DEPENDS@") +set(@DUNE_MOD_NAME@_SUGGESTS "@DUNE_SUGGESTS@") +set_and_check(@DUNE_MOD_NAME@_MODULE_PATH "@PACKAGE_DUNE_INSTALL_MODULEDIR@") +endif(NOT @DUNE_MOD_NAME@_FOUND) diff --git a/cmake/scripts/CMakeLists.txt b/cmake/scripts/CMakeLists.txt new file mode 100644 index 0000000..0d8587f --- /dev/null +++ b/cmake/scripts/CMakeLists.txt @@ -0,0 +1,21 @@ +# Install non-executable scripts +install(FILES + conf.py.in + CreateDoxyFile.cmake + envdetect.py + FinalizeHeadercheck.cmake + index.rst.in + InstallFile.cmake + main77.cc.in + module_library.cc.in + pyversion.py + RunDoxygen.cmake + sphinx_cmake_dune.py + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/dune/cmake/scripts) + +# Install executable programs +install(PROGRAMS + extract_cmake_data.py + run-in-dune-env.sh.in + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/dune/cmake/scripts +) diff --git a/cmake/scripts/CreateDoxyFile.cmake b/cmake/scripts/CreateDoxyFile.cmake new file mode 100644 index 0000000..2b70bb0 --- /dev/null +++ b/cmake/scripts/CreateDoxyFile.cmake @@ -0,0 +1,18 @@ +# For now we just support appending Doxyfile and Doxylocal +file(READ ${DOXYSTYLE} file_contents) +file(WRITE Doxyfile.in ${file_contents}) +# Write the list of predefined C preprocessor macros +file(READ ${DOXYGENMACROS} file_contents) +file(APPEND Doxyfile.in ${file_contents}) +if(DOXYLOCAL) + file(READ ${DOXYLOCAL} file_contents) +endif() +file(APPEND Doxyfile.in ${file_contents}) + +# configure_file does not work as it insists an existing input file, which in our +# needs to be generated first. +# Therefore we read the Doxyfile.in and replace the variables using string(CONFIGURE) +# and then write the file. +file(READ Doxyfile.in file_contents) +string(CONFIGURE ${file_contents} output) +file(WRITE Doxyfile ${output}) diff --git a/cmake/scripts/FinalizeHeadercheck.cmake b/cmake/scripts/FinalizeHeadercheck.cmake new file mode 100644 index 0000000..a6fdafc --- /dev/null +++ b/cmake/scripts/FinalizeHeadercheck.cmake @@ -0,0 +1,13 @@ +# this is script is called at the end of all header checks +if(ENABLE_HEADERCHECK) + message("Headerchecks finished! Rerun CMake if a new file has not been checked!") +else() + message("The headercheck feature is currently disabled. You can enable it by adding ENABLE_HEADERCHECK=1 to your cmake flags.") +endif() + +#message("Running make clean on headercheck targets...") +#this cleans the build directory from pollution through headerchecks but prevents caching... :/ +#file(GLOB_RECURSE list "./CMakeFiles/headercheck_*/cmake_clean.cmake") +#foreach(item ${list}) + # execute_process(COMMAND ${CMAKE_COMMAND} -P ${item}) +#endforeach() \ No newline at end of file diff --git a/cmake/scripts/InstallFile.cmake b/cmake/scripts/InstallFile.cmake new file mode 100644 index 0000000..5d8a9a9 --- /dev/null +++ b/cmake/scripts/InstallFile.cmake @@ -0,0 +1,6 @@ + +# Somehow variable list get destroyed when calling cmake (; is replaced with +# whitespace character. Undo this change +string(REGEX REPLACE "([a-zA-Z0-9]) ([/a-zA-Z0-9])" "\\1;\\2" files "${FILES}") +file(INSTALL ${files} DESTINATION ${DIR}) + diff --git a/cmake/scripts/RunDoxygen.cmake b/cmake/scripts/RunDoxygen.cmake new file mode 100644 index 0000000..1a1019e --- /dev/null +++ b/cmake/scripts/RunDoxygen.cmake @@ -0,0 +1,3 @@ +execute_process(COMMAND + ${DOXYGEN_EXECUTABLE} Doxyfile OUTPUT_FILE doxygen.log ERROR_FILE doxygen.log + TIMEOUT 3600) diff --git a/cmake/scripts/conf.py.in b/cmake/scripts/conf.py.in new file mode 100644 index 0000000..a58a393 --- /dev/null +++ b/cmake/scripts/conf.py.in @@ -0,0 +1,27 @@ +import sys + +sys.path.append('@DUNE_SPHINX_EXT_PATH@') + +extensions = ['sphinx_cmake_dune'] + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "classic" +html_theme_options = { + "rightsidebar": "true", + "relbarbgcolor": "#eeeeee", + "relbartextcolor": "#353B44", + "relbarlinkcolor": "#353B44", + "headbgcolor": "white", + "headtextcolor": "#353B44", + "linkcolor": "#337AB7", + "visitedlinkcolor": "#337AB7", + "textcolor": "#353B44", + "footerbgcolor": "white", + "footertextcolor": "#353B44", + "codebgcolor": "#eeeeee", +} + +html_sidebars = {'**': []} +html_title = "" diff --git a/cmake/scripts/envdetect.py b/cmake/scripts/envdetect.py new file mode 100644 index 0000000..9049edd --- /dev/null +++ b/cmake/scripts/envdetect.py @@ -0,0 +1,22 @@ +# A python script that determines whether the current interpreter is +# running inside a virtual environment. For discussion of the implemented +# methods, see http://stackoverflow.com/questions/1871549 +# +# Meant to be run from DunePythonCommonMacros.cmake. For that reason, it +# exits with either 1 or 0, where 1 indicates that the interpreter +# runs inside a virtualenv +# + +import sys + +# If sys.real_prefix exists, this is a virtualenv set up with the virtualenv package +real_prefix = hasattr(sys, 'real_prefix') +if real_prefix: + sys.exit(1) + +# If a virtualenv is set up with pyvenv, we check for equality of base_prefix and prefix +if hasattr(sys, 'base_prefix'): + sys.exit(sys.prefix != sys.base_prefix) + +# If none of the above conditions triggered, this is probably no virtualenv interpreter +sys.exit(0) \ No newline at end of file diff --git a/cmake/scripts/extract_cmake_data.py b/cmake/scripts/extract_cmake_data.py new file mode 100755 index 0000000..e15275f --- /dev/null +++ b/cmake/scripts/extract_cmake_data.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python + +""" This script will parse a cmake module and extract some + rst documentation from it. This might not be as elegant as + writing a Sphinx domain or using a custom extension with + cmake related directives, but it provides a straightforward + working way. + + This is used by dune-common to generate the build system documentation. + Users do not want to use this!!! +""" +from __future__ import print_function + +import argparse +import errno +import os +import re + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument('-b', '--builddir', help='The directory where to place the produced output', required=True) + parser.add_argument('-m', '--module', help='The module to parse', required=True) + return vars(parser.parse_args()) + +def write_line(f, line): + if len(line) > 2: + f.write(line[2:]) + else: + f.write('\n') + +def makedirs_if_not_exists(path): + # Python3's os.makedirs has exist_ok=True, but this is still Python2... + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + +def read_module(args=get_args()): + modname = os.path.splitext(os.path.basename(args['module']))[0] + modpath = os.path.join(args['builddir'], 'modules') + makedirs_if_not_exists(modpath) + modfile = os.path.join(modpath, modname + '.rst') + with open(args['module'], 'r') as i: +# mod = open(modfile, 'w') +# # Write the first block into the module rst file +# mod.write(".. _" + modname + ":\n\n") +# mod.write(modname + "\n") +# mod.write("="*len(modname) + "\n\n") + +# listHeader = False + o = None + + for l in i: + if not l.startswith('#'): + return + if l.startswith('# .. cmake_function'): + if o: + o.close() + cmdpath = os.path.join(args['builddir'], 'commands') + makedirs_if_not_exists(cmdpath) + try: + cmd = re.findall(r'# .. cmake_function:: (.*)', l)[0] + except IndexError as e: + print("CMake doc syntax error in {}: cannot parse function on line {}".format(args['module'], l)) + raise e + cmdfile = os.path.join(cmdpath, cmd + ".rst") +# if not listHeader: +# mod.write("\nThis module defines the following functions or macros:\n\n") +# listHeader = True +# mod.write("* :ref:`{}`\n".format(cmd)) + o = open(cmdfile, 'w') + o.write(".. _" + cmd + ":\n\n") + o.write(cmd + "\n") + o.write("="*len(cmd) + "\n\n") + write_line(o, l) + elif l.startswith('# .. cmake_variable'): + if o: + o.close() + varpath = os.path.join(args['builddir'], 'variables') + makedirs_if_not_exists(varpath) + try: + var = re.findall(r'# .. cmake_variable:: (.*)', l)[0] + except IndexError as e: + print("CMake doc syntax error in {}: cannot parse variable on line".format(args['module'], l)) + raise e + varfile = os.path.join(varpath, var + ".rst") + o = open(varfile, 'w') + o.write(".. _" + var + ":\n\n") + o.write(var + "\n") + o.write("="*len(var) + "\n\n") + write_line(o, l) + elif l.startswith('# .. cmake_module'): + if o: + o.close() + modpath = os.path.join(args['builddir'], 'modules') + makedirs_if_not_exists(modpath) + modfile = os.path.join(modpath, modname + ".rst") + o = open(modfile, 'w') + o.write(".. _" + modname + ":\n\n") + o.write(modname + "\n") + o.write("="*len(modname) + "\n\n") + write_line(o, l) + else: + if o: + write_line(o, l) + +# Parse the given arguments +read_module() diff --git a/cmake/scripts/index.rst.in b/cmake/scripts/index.rst.in new file mode 100644 index 0000000..3f18d5a --- /dev/null +++ b/cmake/scripts/index.rst.in @@ -0,0 +1,41 @@ +.. title:: @PROJECT_NAME@ CMake reference + +.. role:: cmake(code) + :language: cmake + +Introduction +============ +.. toctree:: + :maxdepth: 2 + +@CMAKE_DOC_DEPENDENCIES@ + +.. _variableref: + +Input Variable reference +======================== +.. toctree:: + :maxdepth: 1 + :glob: + + variables/* + +.. _commandref: + +Command reference +================= +.. toctree:: + :maxdepth: 1 + :glob: + + commands/* + +.. _moduleref: + +Module reference +================ +.. toctree:: + :maxdepth: 1 + :glob: + + modules/* diff --git a/cmake/scripts/main77.cc.in b/cmake/scripts/main77.cc.in new file mode 100644 index 0000000..9d0e425 --- /dev/null +++ b/cmake/scripts/main77.cc.in @@ -0,0 +1,8 @@ +#include + +int main() +{ + std::cout << "This test was skipped because it failed the following CMake Conditions:" << std::endl; + ${FAILED_CONDITION_PRINTING} + return 77; +} diff --git a/cmake/scripts/module_library.cc.in b/cmake/scripts/module_library.cc.in new file mode 100644 index 0000000..ad0ad96 --- /dev/null +++ b/cmake/scripts/module_library.cc.in @@ -0,0 +1,11 @@ +#include + +std::size_t ${module_lib_mangled}_version() +{ + return ${ProjectVersionMajor} * 10000 + ${ProjectVersionMinor} * 100 + ${ProjectVersionRevision}; +} + +std::string ${module_lib_mangled}_version_string() +{ + return "${ProjectVersion}"; +} diff --git a/cmake/scripts/pyversion.py b/cmake/scripts/pyversion.py new file mode 100644 index 0000000..1e5968e --- /dev/null +++ b/cmake/scripts/pyversion.py @@ -0,0 +1,31 @@ +# This python script tries to figure out the version of a given python +# package. This is only intended to be used from DunePythonFindPackage.cmake +# +# There is no unified way of specifying the version of a python package. This +# script implements some methods. For discussion on the implemented methods see +# http://stackoverflow.com/questions/20180543 +# + +import sys + +# Load the module passed as argument (this avoids the need for a template +# to be configured to put the package name inhere) +modstr = sys.argv[1] +module = __import__(modstr) + +# The most common mechanism is module.__version__ +if hasattr(module, '__version__'): + sys.stdout.write(module.__version__) + sys.exit(0) + +# Alternative implementation: through pip (pip itself implement pip.__version__, +# so we never get here, when checking the version of pip itself), only works if +# package name and distribution name are the same +import pkg_resources +for package in pkg_resources.working_set: + if package.project_name == modstr and package.has_version(): + sys.stdout.write(package.version) + sys.exit(0) + +# Give up on this one +sys.exit(1) diff --git a/cmake/scripts/run-in-dune-env.sh.in b/cmake/scripts/run-in-dune-env.sh.in new file mode 100755 index 0000000..bf8f0ec --- /dev/null +++ b/cmake/scripts/run-in-dune-env.sh.in @@ -0,0 +1,4 @@ +#!@BASH@ + +source @DUNE_PYTHON_VIRTUALENV_PATH@/bin/activate +"$@" diff --git a/cmake/scripts/sphinx_cmake_dune.py b/cmake/scripts/sphinx_cmake_dune.py new file mode 100644 index 0000000..16ab9c6 --- /dev/null +++ b/cmake/scripts/sphinx_cmake_dune.py @@ -0,0 +1,191 @@ +""" A cmake extension for Sphinx + +tailored for the Dune project. +This is used during `make doc` to build the +build system documentation. +""" + +from docutils import nodes +from docutils.parsers.rst import Directive +from itertools import chain + +class CMakeParamNode(nodes.Element): + pass + +class CMakeBriefNode(nodes.Element): + pass + +class CMakeFunction(Directive): + # We do require the name to be an argument + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + has_content = True + + def run(self): + env = self.state.document.settings.env + + # Parse the content of the directive recursively + node = nodes.Element() + node.document = self.state.document + self.state.nested_parse(self.content, self.content_offset, node) + + brief_nodes = [] + output_nodes = [] + positional_params = [] + required_params = {} + optional_params = {} + + for child in node: + if isinstance(child, CMakeParamNode): + if child["positional"]: + positional_params.append(child) + elif child["required"]: + required_params[child["name"]] = child + else: + optional_params[child["name"]] = child + elif isinstance(child, CMakeBriefNode): + par = nodes.paragraph() + self.state.nested_parse(child['content'], self.content_offset, par) + brief_nodes.append(par) + else: + output_nodes.append(child) + + def render_required(paramnode): + if paramnode["multi"]: + sl.append(" "*5 + paramnode['name'] + ' ' + paramnode['argname'] + '1 [' + paramnode['argname'] + '2 ...]\n') + if paramnode["single"]: + sl.append(" "*5 + paramnode['name'] + ' ' + paramnode['argname'] + '\n') + if paramnode["option"]: + sl.append(" "*5 + paramnode['name'] + '\n') + if paramnode["special"]: + sl.append(" "*5 + paramnode['argname'] + '\n') + + def render_optional(paramnode): + if paramnode["multi"]: + sl.append(' '*4 + '[' + paramnode['name'] + ' ' + paramnode['argname'] + '1 [' + paramnode['argname'] + '2 ...]' + ']\n') + if paramnode["single"]: + sl.append(" "*4 + '['+ paramnode['name'] + ' ' + paramnode['argname'] + ']\n') + if paramnode["option"]: + sl.append(" "*4 + '['+ paramnode['name'] + ']\n') + if paramnode["special"]: + sl.append(" "*4 + '['+ paramnode['argname'] + ']\n') + + # Build the content of the box + sl = [self.arguments[0] + '(\n'] + + for paramnode in positional_params: + if paramnode["required"]: + render_required(paramnode) + else: + render_optional(paramnode) + + for rp, paramnode in required_params.items(): + render_required(paramnode) + for op, paramnode in optional_params.items(): + render_optional(paramnode) + + sl.append(")\n") + lb = nodes.literal_block(''.join(sl), ''.join(sl)) + brief_nodes.append(lb) + + dl = nodes.definition_list() + for paramnode in chain(positional_params, required_params.values(), optional_params.values()): + dli = nodes.definition_list_item() + dl += dli + + dlit = nodes.term(text=paramnode["name"]) + dli += dlit + + dlic = nodes.definition() + dli += dlic + self.state.nested_parse(paramnode['content'], self.content_offset, dlic) + + # add the parameter list to the output + brief_nodes.append(dl) + + return brief_nodes + output_nodes + +class CMakeBrief(Directive): + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + has_content = True + + def run(self): + node = CMakeBriefNode() + node['content'] = self.content + return [node] + +class CMakeParam(Directive): + # We do require the name to be an argument + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {'argname' : lambda s: s, + 'multi': lambda s: True, + 'option': lambda s: True, + 'positional' : lambda s: True, + 'required': lambda s: True, + 'single': lambda s: True, + 'special': lambda s: True + } + has_content = True + + def run(self): + node = CMakeParamNode() + # set defaults: + node['name'] = self.arguments[0] + node['single'] = self.options.get('single', False) + node['multi'] = self.options.get('multi', False) + node['option'] = self.options.get('option', False) + node['special'] = self.options.get('special', False) + node['positional'] = self.options.get('positional', False) + node['required'] = self.options.get('required', False) + node['argname'] = self.options.get('argname', self.arguments[0].lower() if self.arguments[0].lower()[-1:] != 's' else self.arguments[0].lower()[:-1]) + node['content'] = self.content + if node['positional']: + node['argname'] = '' + return [node] + + +class CMakeVariable(Directive): + # We do require the name to be an argument + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {'argname' : lambda s: s, + 'multi': lambda s: True, + 'option': lambda s: True, + 'positional' : lambda s: True, + 'required': lambda s: True, + 'single': lambda s: True + } + has_content = True + + def run(self): + node = nodes.paragraph() + self.state.nested_parse(self.content, self.content_offset, node) + return [node] + +class CMakeModule(Directive): + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + has_content = True + + def run(self): + node = nodes.paragraph() + self.state.nested_parse(self.content, self.content_offset, node) + return [node] + +def setup(app): + app.add_node(CMakeBriefNode) + app.add_node(CMakeParamNode) + app.add_directive('cmake_module', CMakeModule) + app.add_directive('cmake_brief', CMakeBrief) + app.add_directive('cmake_function', CMakeFunction) + app.add_directive('cmake_param', CMakeParam) + app.add_directive('cmake_variable', CMakeVariable) + + return {'version': '0.1'} diff --git a/config.h.cmake b/config.h.cmake new file mode 100644 index 0000000..e942487 --- /dev/null +++ b/config.h.cmake @@ -0,0 +1,181 @@ +/* begin dune-common + put the definitions for config.h specific to + your project here. Everything above will be + overwritten +*/ + +/* begin private */ +/* Define to the version of dune-common */ +#define DUNE_COMMON_VERSION "${DUNE_COMMON_VERSION}" + +/* Define to the major version of dune-common */ +#define DUNE_COMMON_VERSION_MAJOR ${DUNE_COMMON_VERSION_MAJOR} + +/* Define to the minor version of dune-common */ +#define DUNE_COMMON_VERSION_MINOR ${DUNE_COMMON_VERSION_MINOR} + +/* Define to the revision of dune-common */ +#define DUNE_COMMON_VERSION_REVISION ${DUNE_COMMON_VERSION_REVISION} + +/* Standard debug streams with a level below will collapse to doing nothing */ +#define DUNE_MINIMAL_DEBUG_LEVEL ${DUNE_MINIMAL_DEBUG_LEVEL} + +/* does the compiler support __attribute__((deprecated))? */ +#cmakedefine HAS_ATTRIBUTE_DEPRECATED 1 + +/* does the compiler support __attribute__((deprecated("message"))? */ +#cmakedefine HAS_ATTRIBUTE_DEPRECATED_MSG 1 + +/* does the compiler support __attribute__((unused))? */ +#cmakedefine HAS_ATTRIBUTE_UNUSED 1 + +/* does the standard library provide experimental::make_array() ? */ +#cmakedefine DUNE_HAVE_CXX_EXPERIMENTAL_MAKE_ARRAY 1 + +/* does the standard library provide experimental::is_detected ? */ +#cmakedefine DUNE_HAVE_CXX_EXPERIMENTAL_IS_DETECTED 1 + +/* does the standard library provide identity ? */ +#cmakedefine DUNE_HAVE_CXX_STD_IDENTITY 1 + +/* Define if you have a BLAS library. */ +#cmakedefine HAVE_BLAS 1 + +/* Define if you have LAPACK library. */ +#cmakedefine HAVE_LAPACK 1 + +/* Define if you have the MPI library. */ +#cmakedefine HAVE_MPI ENABLE_MPI + +/* Deactivate cxx bindings for MPI */ +#if defined(HAVE_MPI) && HAVE_MPI +#define MPICH_SKIP_MPICXX 1 +#define OMPI_SKIP_MPICXX 1 +#define MPI_NO_CPPBIND 1 +#define MPIPP_H +#define _MPICC_H +#endif + +/* Define if you have the GNU GMP library. The value should be ENABLE_GMP + to facilitate activating and deactivating GMP using compile flags. */ +#cmakedefine HAVE_GMP ENABLE_GMP + +/* Define if you have the GCC Quad-Precision library. The value should be ENABLE_QUADMATH + to facilitate activating and deactivating QuadMath using compile flags. */ +#cmakedefine HAVE_QUADMATH ENABLE_QUADMATH + +/* Define if you have the Vc library. The value should be ENABLE_VC + to facilitate activating and deactivating Vc using compile flags. */ +#cmakedefine HAVE_VC ENABLE_VC + +/* Define to 1 if you have the Threading Building Blocks (TBB) library */ +#cmakedefine HAVE_TBB 1 + +/* begin private */ + +/* Name of package */ +#define PACKAGE "dune-common" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "@DUNE_MAINTAINER@" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "@DUNE_MOD_NAME@" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "@DUNE_MOD_NAME@ @DUNE_MOD_VERSION@" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "@DUNE_MOD_NAME@" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "@DUNE_MOD_URL@" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "@DUNE_MOD_VERSION@" + +/* Version number of package */ +#define VERSION "@DUNE_MOD_VERSION@" + +/* end private */ + + +/* old feature support macros which were tested until 2.7, kept around for one more release */ +/* As these are now always supported due to the new compiler requirements, they are directly */ +/* defined without an explicit test. */ +#define DUNE_HAVE_CXX_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1 +#define DUNE_HAVE_CXX_OPTIONAL 1 +#define DUNE_HAVE_CXX_VARIANT 1 +#define DUNE_SUPPORTS_CXX_THROW_IN_CONSTEXPR 1 +#define DUNE_HAVE_C_ALIGNED_ALLOC 1 +#define DUNE_HAVE_CXX_BOOL_CONSTANT 1 +#define DUNE_HAVE_CXX_EXPERIMENTAL_BOOL_CONSTANT 0 +#define DUNE_HAVE_HEADER_EXPERIMENTAL_TYPE_TRAITS 0 +#define DUNE_HAVE_CXX_APPLY 1 +#define DUNE_HAVE_CXX_EXPERIMENTAL_APPLY 0 +#define HAVE_IS_INDEXABLE_SUPPORT 1 + +/* Define to ENABLE_UMFPACK if the UMFPack library is available */ +#cmakedefine HAVE_UMFPACK ENABLE_SUITESPARSE + +/* Define to ENABLE_SUITESPARSE if the SuiteSparse library is available */ +#cmakedefine HAVE_SUITESPARSE ENABLE_SUITESPARSE + +/* Define to ENABLE_SUITESPARSE if the SuiteSparse's AMD library is available */ +#cmakedefine HAVE_SUITESPARSE_AMD ENABLE_SUITESPARSE + +/* Define to ENABLE_SUITESPARSE if the SuiteSparse's BTF library is available */ +#cmakedefine HAVE_SUITESPARSE_BTF ENABLE_SUITESPARSE + +/* Define to ENABLE_SUITESPARSE if the SuiteSparse's CAMD library is available */ +#cmakedefine HAVE_SUITESPARSE_CAMD ENABLE_SUITESPARSE + +/* Define to ENABLE_SUITESPARSE if the SuiteSparse's CCOLAMD library is available */ +#cmakedefine HAVE_SUITESPARSE_CCOLAMD ENABLE_SUITESPARSE + +/* Define to ENABLE_SUITESPARSE if the SuiteSparse's CHOLMOD library is available */ +#cmakedefine HAVE_SUITESPARSE_CHOLMOD ENABLE_SUITESPARSE + +/* Define to ENABLE_SUITESPARSE if the SuiteSparse's COLAMD library is available */ +#cmakedefine HAVE_SUITESPARSE_COLAMD ENABLE_SUITESPARSE + +/* Define to ENABLE_SUITESPARSE if the SuiteSparse's CXSPARSE library is available */ +#cmakedefine HAVE_SUITESPARSE_CXSPARSE ENABLE_SUITESPARSE + +/* Define to ENABLE_SUITESPARSE if the SuiteSparse's KLU library is available */ +#cmakedefine HAVE_SUITESPARSE_KLU ENABLE_SUITESPARSE + +/* Define to ENABLE_SUITESPARSE if the SuiteSparse's LDL library is available */ +#cmakedefine HAVE_SUITESPARSE_LDL ENABLE_SUITESPARSE + +/* Define to ENABLE_SUITESPARSE if the SuiteSparse's RBIO library is available */ +#cmakedefine HAVE_SUITESPARSE_RBIO ENABLE_SUITESPARSE + +/* Define to ENABLE_SUITESPARSE if the SuiteSparse's SPQR library is available + and if it's version is at least 4.3 */ +#cmakedefine HAVE_SUITESPARSE_SPQR ENABLE_SUITESPARSE + +/* Define to ENABLE_SUITESPARSE if the SuiteSparse's UMFPACK library is available */ +#cmakedefine HAVE_SUITESPARSE_UMFPACK ENABLE_SUITESPARSE + +/* Define to 1 if METIS is available */ +#cmakedefine HAVE_METIS 1 + +/* Define to 1 if the Scotch replacement for METIS is used. */ +#cmakedefine HAVE_SCOTCH_METIS 1 + +/* Define to 1 if you have the ParMETIS library. */ +#cmakedefine HAVE_PARMETIS 1 + +/* Define to 1 if the PTScotch replacement for ParMETIS is used. */ +#cmakedefine HAVE_PTSCOTCH_PARMETIS 1 + +/* Define to 1 if PT-Scotch is available */ +#cmakedefine HAVE_PTSCOTCH 1 + +/* Used to call lapack functions */ +#cmakedefine LAPACK_NEEDS_UNDERLINE + +/* end dune-common + Everything below here will be overwritten +*/ diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000..56c3aa4 --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory("doxygen") +add_subdirectory("buildsystem") +add_subdirectory("comm") +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dunecontrol.1 + DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) diff --git a/doc/buildsystem/CMakeLists.txt b/doc/buildsystem/CMakeLists.txt new file mode 100644 index 0000000..28a1ceb --- /dev/null +++ b/doc/buildsystem/CMakeLists.txt @@ -0,0 +1,5 @@ +# Install the buildsystem documentation defined in dune-common +install(FILES dune-common.rst DESTINATION ${CMAKE_INSTALL_DOCDIR}) + +# Also always build the CMake API documentation in dune-common +dune_cmake_sphinx_doc() diff --git a/doc/buildsystem/dune-common.rst b/doc/buildsystem/dune-common.rst new file mode 100644 index 0000000..076beb5 --- /dev/null +++ b/doc/buildsystem/dune-common.rst @@ -0,0 +1,390 @@ +=========== +dune-common +=========== + +.. _whatis: + +What is CMake anyway? +===================== + +CMake... + +- is an open source build system tool developed at KITware. +- offers a one-tool-solution to all building tasks, like configuring, building, linking, testing and packaging. +- is a build system generator: It supports a set of backends called *generators* +- is portable +- is controlled by ONE rather simple language + +You can install CMake through your favorite package manager or downloading source code from +`KITWare `_ +The minimum required version to build Dune with CMake is 3.13. + +.. _howtouse: + +How do I use Dune with CMake? +============================= + +The build process is controlled by the script :code:`dunecontrol`, located in :code:`dune-common/bin`. +There is a compatibility layer that will translate all the configure flags from your opts file into the corresponding +CMake flags. While this is a great tool to determine how to do the transition, in the long run you should switch to +a CMake-only approach. + +:code:`dunecontrol` will pickup the variable :code:`CMAKE_FLAGS` from your opts file and use it as command line options for +any call to CMake. There, you can define variables for the configure process with CMake's :code:`-D` option; just as +with the C pre-processor. + +The most important part of the configure flags is to tell the build system where to look for external libraries. +You can browse the :ref:`variableref` section of this documentation for a list of variables that are picked up +by the Dune CMake build system. + +.. _whatfiles: + +What files in a dune module belong to the CMake build system? +============================================================= + +Every directory in a project contains a file called :code:`CMakeLists.txt`, which is written in the CMake language. +You can think of these as a distributed configure script. Upon configure, the top-level :code:`CMakeLists.txt` is executed. +Whenever an :ref:`add_subdirectory` command is encountered, the :code:`CMakeLists.txt` file of that sub-directory is executed. +The top-level :code:`CMakeLists.txt` file is special, because it sets up the entire Dune module correctly. You should not delete the +auto-generated parts of it. + +Additionally, a Dune module can export some cmake modules. A cmake module is a file that contains one or +more build system macros meant for downstream use. If a module provides modules, they can be found in +the subfolder :code:`cmake/modules`. The module :code:`dune-foo/cmake/modules/DuneFooMacros.cmake` in a module +:code:`dune-foo` is special however: Its contents are always executed when configuring the module +:code:`dune-foo` or any other Dune module, that requires or suggests the module :code:`dune-foo`. +This is the perfect place to put your checks for external packages, see below. + +The file :code:`config.h.cmake` defines a template for the section of :code:`config.h`, that is generated by the module. + +.. _flags: + +How do I modify the flags and linked libraries of a given target? +================================================================= + +Again, there are multiple ways to do this. The Dune build system offers macros to make this task as +easy as possible. For each external module, there is a macro :code:`add_dune_*_flags`. Those macros should +cover most flags. Example usage: + +.. code-block:: cmake + + add_executable(foo foo.cc) + add_dune_umfpack_flags(foo) + add_dune_mpi_flags(foo) + +There is also the macro :ref:`add_dune_all_flags`, which uses the same flag registry mechanism as the simplified +build system in section :ref:`simplified`. + +If you want to fully control the configuration of the targets, you can do so. Build system entities such +as targets, directories and tests do have so called properties in CMake. You can access and modify those +properties via the commands :code:`get_property` and :code:`set_property`. You can for example use those +to modify a targets :code:`COMPILE_DEFINITIONS` or :code:`INCLUDE_DIRECTORIES` property: + +.. code-block:: cmake + + add_executable(foo foo.cc) + set_property(TARGET foo APPEND PROPERTY COMPILE_DEFINITIONS ) + set_property(TARGET foo APPEND PROPERTY INCLUDE_DIRECTORIES ) + +For a full list of properties, check the manual: + +.. code-block:: bash + + cmake --help-property-list + +Manually linking libraries can be done through the :code:`target_link_libraries` command instead of manually +tweaking properties. + +.. _external: + +How do I link against external libraries, that are not checked for by Dune? +=========================================================================== + +While there might be many solutions that make your application work, there is only one clean solution to this: You have +to provide a find module for the package. A find module is a CMake module that follows a specific naming scheme: For +an external package called :code:`SomePackage` it is called :code:`FindSomePackage.cmake`. Note that CMake +treats package names case sensitive. If CMake encounters a :code:`find_package(SomePackage)` line, it searches +its module include paths for this find module. A good read to get started writing a find module is +`this page `_ in the CMake wiki. + +Depending on how common your external package is, you may not even need to write the find module on your own. +You can have a look at the list of find modules shipped by CMake or simply search the +internet for the module name and profit from other open-source project's work. + +It is considered good style to also provide a macro :code:`add_dune_somepackage_flags`. + +.. _outofsource: + +What is an out-of-source build? +=============================== + +An out-of-source build does leave the version-controlled source tree untouched and puts all files that are +generated by the build process into a different directory -- the build directory. The build directory does mirror +your source tree's structure as seen in the following. Assume the following source directory structure: + +:: + + dune-foo/ + CMakeLists.txt + dune/ + foo/ + CMakeLists.txt + src/ + CMakeLists.txt + +The generated build directory will have the following structure, where the directory :code:`build-cmake` +is a subdirectory of the source directory: + +:: + + build-cmake/ + Makefile + dune/ + foo/ + Makefile + src/ + Makefile + +Using the :code:`Unix Makefiles` generator, your Makefiles are generated in the build tree, so that is where you +have to call :code:`make`. There are multiple advantages with this approach, such as a clear separation between +version controlled and generated files and you can have multiple out-of-source builds with different configurations +at the same time. + +Out-of-source builds are the default with CMake. In-source builds are strongly discouraged. + +By default, a subfolder :code:`build-cmake` is generated within each dune module and is used as a build directory. +You can customize this folder through the :code:`--builddir` option of :code:`dunecontrol`. Give an absolute path to +the :code:`--builddir` option, you will get something like this: + +:: + + build/ + dune-common/ + Makefile + dune-foo/ + Makefile + +So, instead of one build directory in every dune module, you will be able to collect all build directories in one +directory. This makes it much easier to have multiple build directories and to remove build directories. + +.. _simplified: + +What is the simplified build system and how do I use it? +======================================================== + +Dune offers a simplified build system, where all flags are added to all targets and all libraries are linked to all targets. You can enable the feature +by calling :ref:`dune_enable_all_packages` in the top-level :code:`CMakeLists.txt` file of your project, before you add any subdirectories. + +This will modify all targets in the directory of the :code:`CMakeLists.txt`, where you put this, and also in all +subdirectories. The compile flags for all found external packages are added to those targets and the target is +linked against all found external libraries. + +To use this while using custom external packages, you have to register your flags to the mechanism. +Also, some special care has to be given, if your module does build one or more library which targets within the module do link against. + +Carefully read the following documentation in those cases: + +* :ref:`dune_enable_all_packages` +* :ref:`dune_register_package_flags` +* :ref:`dune_library_add_sources` + +.. _compiler: + +How do I change my compiler and compiler flags? +=============================================== + +In general, there are multiple ways to do this: + +* Setting the CMake variables :ref:`CMAKE__COMPILER` (with :code:`LANG` being :code:`C` + or :code:`CXX`) from the opts file, e.g. via :code:`CMAKE_FLAGS="-DCMAKE_CXX_COMPILER=otherc++"`. +* Setting those variables within the project with the :code:`set` command +* Setting the environment variables :code:`CC`, :code:`CXX`, :code:`FC` etc. + +The first option is the recommended way. Whenever you change your compiler, you should delete all build +directories. For some CMake versions, there is a known CMake bug, that requires you to give an absolute path +to your compiler, but Dune will issue a warning, if you violate that. + +You can modify your default compiler flags by setting the variables +:ref:`CMAKE__FLAGS` in your opts file (again with :code:`LANG` being :code:`C` or +:code:`CXX`). + +.. _symlink: + +How should I handle ini and grid files in an out-of-source-build setup? +======================================================================= + +Such files are under version control, but they are needed in the build directory. +There are some CMake functions targeting this issue: + +* :ref:`dune_symlink_to_source_tree` +* :ref:`dune_symlink_to_source_files` +* :ref:`dune_add_copy_command` +* :ref:`dune_add_copy_dependency` +* :ref:`dune_add_copy_target` + +The simplest way to solve the problem is to set the variable :ref:`DUNE_SYMLINK_TO_SOURCE_TREE` to your opts file. +This will execute :ref:`dune_symlink_to_source_tree` in your top-level :code:`CMakeLists.txt`. This will add a symlink +:code:`src_dir` to all subdirectories of the build directory, which points to the corresponding directory of the source +tree. This will only work on platforms that support symlinking. + +.. _ides: + +How do I use CMake with IDEs? +============================= + +As already said, CMake is merely a build system generator with multiple backends (called a generator). Using IDEs requires +a different generator. Check :code:`cmake --help` for a list of generators. You can then add the :code:`-G` to the :code:`CMAKE_FLAGS` in your opts file. +Note that the generator name has to match character by character, including case and spaces. + +To configure highlighting of CMake errors in Emacs' compilation mode, include +the following in your :code:`~./emacs` (see the `Emacs bug +`_): + +.. code-block:: elisp + + (setq compilation-error-regexp-alist-alist + `((cmake "^CMake \\(?:Error\\|\\(Warning\\)\\) at \\(.*\\):\\([1-9][0-9]*\\) ([^)]+):$" + 2 3 nil (1)) + (cmake-info "^ \\(?: \\*\\)?\\(.*\\):\\([1-9][0-9]*\\) ([^)]+)$" + 2 3 nil 0) + . ,compilation-error-regexp-alist-alist)) + +Then customize the option :code:`compilation-error-regexp-alist` and add the +two predefined symbols :code:`cmake` and :code:`cmake-info` to the list. + +.. _cxxflags: + +I usually modify my CXXFLAGS upon calling make. How can I do this in CMake? +=========================================================================== + +This violates the CMake philosophy and there is no clean solution to achieve it. The CMake-ish solution would be +to have for each configuration one out-of-source build. We have nevertheless implemented a workaround. It can be enable +by setting the variable :ref:`ALLOW_CXXFLAGS_OVERWRITE` in your opts file. You can then type: + +.. code-block:: bash + + make CXXFLAGS="" + +Furthermore any C pre-processor variable of the form :code:`-DVAR=` can be overloaded on the command line +and the grid type can be set via :code:`GRIDTYPE=""`. + +Note this only works with generators that are based on Makefiles and several Unix tools like bash must be +available. + +.. _test: + +How do I run the test suite from CMake? +======================================= + +The built-in target to run the tests is called :code:`test` instead of Autotools' :code:`check`. +It is a mere wrapper around CMake's own testing tool CTest. You can check :code:`ctest --help` +for a lot of useful options, such as choosing the set of tests to be run by matching regular expressions or +showing the output of failed tests. + +The test programs are not built automatically. You need to build them manually +before running them using :code:`make build_tests`. + +The Dune test suite also defines tests that run in parallel. You may set an upper bound to the number +of cores in use for a single test by setting :ref:`DUNE_MAX_TEST_CORES`. + +.. _disable: + +Can I disable an external dependency? +===================================== + +To disable an external dependency :code:`Foo`, add + +:: + + -DCMAKE_DISABLE_FIND_PACKAGE_Foo=TRUE + +to your opts file. The name of the dependency is case sensitive but there is no canonical naming +scheme. See the output of configure to get the right name. + +Make sure to not use cached configure results by deleting the cache file or the build directory, cf. +:ref:`troubleshoot`. + +.. _parallel: + +How do I switch between parallel and sequential builds? +======================================================= + +Dune builds with CMake are parallel if and only if MPI is found. To have a sequential build despite an +installed MPI library, you have to explicitly disable the corresponding find module by setting + +:: + + -DCMAKE_DISABLE_FIND_PACKAGE_MPI=TRUE + +in the :code:`CMAKE_FLAGS` of your opts file, as described in section :ref:`disable`. + +.. _headercheck: + +Why is it not possible anymore to do make headercheck? +====================================================== + +The headercheck feature has been disabled by default. You can enable it by setting the CMake variable :ref:`ENABLE_HEADERCHECK` +through your opts file. This step has been necessary, because of the large amount of additional file the headercheck adds to the +build directory. A better implementation has not been found yet, because it simply does not fit the CMake philosophy. + +.. _packages: + +How do I create tarballs or packages? +===================================== + +To create source code packages, also known as tarballs, run `git archive` within your +module's Git repository. + +There is no default way to create binary packages like Deb or RPM packages. You can use +the Open Build Service for openSuse RPMs and related distributions. Or create packages according +to the distribution of your choice like the tools around dpkg-buildpackage and debuild +for Debian. + +CMake has a packaging tool CPack, but with CPack you are on your own. In the past, our +results based on CPack were not satisfying. + +.. _dune-python: + +How does the Dune build system handle Python? +============================================= + +dune-common contains a build system extension to handle many python-related aspects. You can +read more on this in the module description :ref:`DunePythonCommonMacros` and the pieces of +documentation mentioned inthere. + +.. _troubleshoot: + +How do I troubleshoot? +====================== + +CMake caches aggressively which makes it bad at recognizing changed configurations. +To trigger a fresh run of configure, you can delete the :code:`CMakeCache.txt` file from +the build directory and maybe save some compilation time afterward. + +Whenever you experience any problems, your first step should be to delete all build directories. Nice trick: + +:: + + dunecontrol exec "rm -rf build-cmake" + +This will remove all build directories from all DUNE modules. + +Later on you can get an error log from the file :code:`CMakeError.log` in the :code:`CMakeFiles` +subdirectory of your build directory. This is what you should send to the mailing list alongside the +description of your setup and efforts to help us help you. + +Where can I get help? +===================== + +The CMake manual is available on the command line: + +* :code:`cmake --help-command-list` +* :code:`cmake --help-command ` +* :code:`cmake --help-property-list` +* :code:`cmake --help-property ` +* :code:`cmake --help-module-list` +* :code:`cmake --help-module ` + +To get help on which variables are picked up by CMake, there is a CMake wiki page collecting them. +Of course, there is also Google, StackOverflow and the CMake Mailing list (archive). +For problems specific to DUNE's build system, ask on our mailing lists. diff --git a/doc/buildsystem/examples/Toolchain-Ubuntu-mingw32.cmake b/doc/buildsystem/examples/Toolchain-Ubuntu-mingw32.cmake new file mode 100644 index 0000000..5fed8a1 --- /dev/null +++ b/doc/buildsystem/examples/Toolchain-Ubuntu-mingw32.cmake @@ -0,0 +1,34 @@ +# Sample toolchain file for building for Windows from an Ubuntu Linux system. +# +# Typical usage: +# *) install cross compiler: `sudo apt-get install mingw-w64 g++-mingw-w64` +# *) cd build +# *) cmake -DCMAKE_TOOLCHAIN_FILE=~/Toolchain-Ubuntu-mingw32.cmake .. + +set(CMAKE_SYSTEM_NAME Windows) +set(TOOLCHAIN_PREFIX i686-w64-mingw32) + +# cross compilers to use for C and C++ +set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc-posix) +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++-posix) +set(CMAKE_Fortran_COMPILER ${TOOLCHAIN_PREFIX}-gfortran-posix) +set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) + +# enable to generate fully static binaries +# set(CMAKE_EXE_LINKER_FLAGS "-static -static-libgcc -static-libstdc++" CACHE STRING "executable linker flags") + +# target environment on the build host system +# set 1st to dir with the cross compiler's C/C++ headers/libs +set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) + +# modify default behavior of FIND_XXX() commands to +# search for headers/libs in the target environment and +# search for programs in the build host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# enable/disable some hardware specific feature +set(THREADS_PTHREAD_ARG "-pthread") +set(STDTHREAD_LINK_FLAGS "-pthread") +set(STDTHREAD_WORKS true) diff --git a/doc/buildsystem/examples/opts.mingw32 b/doc/buildsystem/examples/opts.mingw32 new file mode 100644 index 0000000..87b6db8 --- /dev/null +++ b/doc/buildsystem/examples/opts.mingw32 @@ -0,0 +1,2 @@ +BUILDDIR=build-mingw +CMAKE_FLAGS="-DCMAKE_TOOLCHAIN_FILE=$PATH_dune_common/Toolchain-Ubuntu-mingw32.cmake" diff --git a/doc/comm/CMakeLists.txt b/doc/comm/CMakeLists.txt new file mode 100644 index 0000000..92f1bfb --- /dev/null +++ b/doc/comm/CMakeLists.txt @@ -0,0 +1,16 @@ +add_executable(poosc08 "poosc08.cc") +target_link_libraries(poosc08 PUBLIC "dunecommon") + +add_executable(poosc08_test "poosc08_test.cc") +target_link_libraries(poosc08_test PUBLIC "dunecommon") + +add_executable(indexset "indexset.cc") +target_link_libraries(indexset PUBLIC "dunecommon") + +add_dune_mpi_flags("poosc08;poosc08_test;indexset") + +dune_add_latex_document( + SOURCE communication.tex + FATHER_TARGET doc + BUILD_ON_INSTALL + INSTALL ${CMAKE_INSTALL_DOCDIR}/comm) diff --git a/doc/comm/buildindexset.hh b/doc/comm/buildindexset.hh new file mode 100644 index 0000000..9abaa0a --- /dev/null +++ b/doc/comm/buildindexset.hh @@ -0,0 +1,54 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef BUILDINDEXSET_HH +#define BUILDINDEXSET_HH + + +#include +#include + +/** + * @brief Flag for marking the indices. + */ +enum Flag {owner, overlap}; + +// The type of local index we use +typedef Dune::ParallelLocalIndex LocalIndex; + +/** + * @brief Add indices to the example index set. + * @param indexSet The index set to build. + */ +template +void build(C& comm, Dune::ParallelIndexSet& indexSet) +{ + + + + + // The rank of our process + int rank=comm.rank(); + + // Indicate that we add or remove indices. + indexSet.beginResize(); + + if(rank==0) { + indexSet.add(0, LocalIndex(0,overlap,true)); + indexSet.add(2, LocalIndex(1,owner,true)); + indexSet.add(6, LocalIndex(2,owner,true)); + indexSet.add(3, LocalIndex(3,owner,true)); + indexSet.add(5, LocalIndex(4,owner,true)); + } + + if(rank==1) { + indexSet.add(0, LocalIndex(0,owner,true)); + indexSet.add(1, LocalIndex(1,owner,true)); + indexSet.add(7, LocalIndex(2,owner,true)); + indexSet.add(5, LocalIndex(3,overlap,true)); + indexSet.add(4, LocalIndex(4,owner,true)); + } + + // Modification is over + indexSet.endResize(); +} +#endif diff --git a/doc/comm/communication.bib b/doc/comm/communication.bib new file mode 100644 index 0000000..8cd508d --- /dev/null +++ b/doc/comm/communication.bib @@ -0,0 +1,79 @@ +@InProceedings{ISTL, + author = {Markus Blatt and Peter Bastian}, + title = {The Iterative Solver Template Library}, + booktitle = {Applied Parallel Computing. State of the Art in Scientific Computing}, + editor = {Bo K\r{a}gstr\"om and Erik Elmroth and Jack Dongarra and Jerzy Wa\'sniewski}, + year = 2007, + volume = 4699, + series = {Lecture Notes in Computer Science}, + publisher = {Springer}, + pages = {666--675} +} + +@Article{dune08-1, + author = {Peter Bastian and Markus Blatt and Andreas Dedner and Christian Engwer and Robert Kl\"ofkorn and Mario Ohlberger and Oliver Sander}, + title = { A generic grid interface for parallel and adaptive scientific computing. Part I: abstract framework}, + journal = {Computing}, + year = 2008, + volume = 82, + number = {2--3}, + pages = {103--119} +} +@Article{dune08-2, + author = {Peter Bastian and Markus Blatt and Andreas Dedner and Christian Engwer and Robert Kl\"ofkorn and Ralf Kornhuber and Mario Ohlberger and Oliver Sander}, + title = { A generic grid interface for parallel and adaptive scientific computing. Part II: implementation and test in DUNE}, + journal = {Computing}, + year = 2008, + volume = 82, + number = {2--3} , + pages = {121--138} +} +@Article{ISTLParallel, + author = {Markus Blatt and Peter Bastian}, + title = {On the Generic Parallelisation of Iterative Solvers for + the Finite Element Method}, + journal = {Int. J. Computational Science and + Engineering}, + volume = {4}, + number = {1}, + pages = {56--69}, + year = 2008 +} + +@Misc{DuneWeb, + author = {DUNE}, + howpublished = {\texttt{http://www.dune-project.org/}} +} +@Misc{boost_mpi, + author = {D. Gregor and M. Troyer}, + title = {{B}oost.{M}{P}{I}}, + howpublished = {\texttt{http://www.boost.org/}}, + year = 2006 +} + +@PhdThesis{gerlach02:janus, + author = {Jens Gerlach}, + title = {Domain Engineering and Generic Programming for Parallel Scientific Computing}, + school = {TU Berlin}, + year = {2002} +} + +@InProceedings{giloi95:_promot, + author = {W.K. Giloi and M. Kessler and A. Schramm}, + title = {PROMOTER: A High Level, Object-Parallel Programming Language}, + booktitle = {Proceedings of the International Conference on High Performance Computing}, + year = {1995}, + address = {New Dehli, India}, + month = {December} +} + +@inproceedings{nolte00:_taco, + author = {J\"{o}rg Nolte and Mitsuhisa Sato and Yutaka Ishikawa}, + title = {TACO -- Dynamic Distributed Collections with Templates and Topologies}, + booktitle = {Euro-Par '00: Proceedings from the 6th International Euro-Par Conference on Parallel Processing}, + year = {2000}, + isbn = {3-540-67956-1}, + pages = {1071--1080}, + publisher = {Springer-Verlag}, + address = {London, UK}, + } \ No newline at end of file diff --git a/doc/comm/communication.tex b/doc/comm/communication.tex new file mode 100644 index 0000000..ecd7d1f --- /dev/null +++ b/doc/comm/communication.tex @@ -0,0 +1,562 @@ +\documentclass[11pt]{article} +\usepackage{multicol} +\usepackage{ifthen} +\usepackage{amsthm} +\usepackage{amsmath} +\usepackage{amsfonts} +\usepackage{color} +\usepackage{graphicx} +\usepackage{hyperref} +\usepackage{psfrag} +\usepackage{subfigure} +\usepackage[dvips]{epsfig} +\usepackage[dvips]{graphicx} +\usepackage[a4paper,body={148mm,240mm,nohead}]{geometry} +\usepackage[ansinew]{inputenc} +\usepackage{tikz} +\usepackage{listings} +\lstset{language=C++, basicstyle=\ttfamily, + stringstyle=\ttfamily, commentstyle=\it, extendedchars=true} + +\newtheorem{theorem}{Theorem}[section] +\newtheorem{lemma}[theorem]{Lemma} + +\theoremstyle{definition} +\newtheorem{definition}[theorem]{Definition} +\newtheorem{class}[theorem]{Class} +\newtheorem{algorithm}[theorem]{Algorithm} +\theoremstyle{remark} +\newtheorem{remark}[theorem]{Remark} + +\newcommand{\C}{\mathbb{C}} +\newcommand{\R}{\mathbb{R}} +\newcommand{\N}{\mathbb{N}} +\newcommand{\Z}{\mathbb{Z}} +\newcommand{\Q}{\mathbb{Q}} +\newcommand{\K}{\mathbb{K}} +\newcommand{\loc}{\mbox{loc}} + +\title{Communication within the Iterative Solver Template Library (ISTL)\thanks{Part of the + Distributed and Unified Numerics Environment (DUNE) which is + available from the site + \texttt{http://www.dune-project.org/}}} + +\author{% +Markus Blatt\\ +Interdisziplinäres Zentrum für Wissenschaftliches Rechnen,\\ +Universität Heidelberg, Im Neuenheimer Feld 368, D-69120 Heidelberg, \\ +email: \texttt{Markus.Blatt@iwr.uni-heidelberg.de}} + +\date{April 27, 2005} + +\begin{document} + +\maketitle + +\begin{abstract} + This document describes usage and interface of the classes meant for + setting up the communication within a parallel program using + ISTL. As most of the communication in distributed program occur in + the same pattern it is often more efficient (and of course more easy + for the programmer) to build the communication pattern once in the + program and then use multiple times (e.~g. at each iteration step + of an iterative solver). +\end{abstract} + +\begin{multicols}{2} +{\small\tableofcontents} +\end{multicols} + + +\section{Introduction} +\label{sec:introduction} + +When using the data parallel programming model a set of processes +works collectively on the same set of finite data objects. These might +be elements of a finite element grid or vector entries in a linear algebra +computation. Each process works on different partitions of the global +data. Only for this partition it computes updated values. + +In large scale parallel codes it is advisable to store the data +partition in a local data structure directly in the local memory of +the process. Due to data dependencies the process needs to access data +in the partition of other processes, too. This can either be done by +communicating these values on demand between the processes whenever +they are accessed. This results in data structures that are aware of +the data distribution. Or by augmenting the partition of the process such +that it additionally includes the data values that the other values +depend on. Note that now the partitioning is not disjoint any more but +overlapping. Of course the values other processes compute for need to +be updated using communication at so called synchronisation points of +the algorithm + +In the latter case the data structures do not need to know anything +about the data distribution. +This demands more effort from the parallel algorithm designer to make +sure that the data used for computations is valid, i.e. contains an +updated value if another process computes the data for it. Still it allows +for fewer synchronisation points in the algorithms as even in collective +operations all input data may already be updated from other processes +due to a previous operation. Between the necessary synchronisation +points one can take advantage of the fast local memory +access. + +Consider representing a random access container $x$ on a set of +processes ${\cal P}=\{0, \ldots, P-1\}$. It is represented by individual +pieces $x^p$, where $x^p$ is the piece stored on +process $p$ of the $P$ processes participating in the +calculation. Although the global representation of the container is +not available on any process, a process $p$ needs to know how the +entries of its local piece $x^p$ correspond to the entries of the +global container $x$, which would be used in a sequential program. + +\section{Communication Software Components} +\label{sec:comm-softw-comp} + +From an abstract point of view a random access container $x: I +\rightarrow K$ provides a +mapping from an index set $I \subset \N_0$ onto a set of objects +$K$. Note that we do not require $I$ to be consecutive. The piece +$x_p$ of the container $x$ stored on process $p$ is a mapping $x_p:I_p +\rightarrow K$, where $I_p \subset I$. Due to efficiency the entries +of $x_p$ should be stored consecutively in memory. +This means that for the local computation the data must be addressable +by a consecutive index starting from $0$. + +When using adaptive +discretisation methods there might be the need to reorder the indices +after adding and/or deleting some of the discretisation +points. Therefore this index does not need to be persistent +and can easily be changed. We will call this index {\em\index{local index}local index}. + +For the communication phases of our algorithms these locally stored +entries must also be addressable by a global identifier. It is used to +store the received values at and to retrieve the values to be sent from the +correct local position in the consecutive memory chunk. To ease the +addition and removal of discretisation points this global identifier has +to be persistent but does not need to be consecutive. We +will call this global identifier {\em\index{global index}global index}. + +\subsection{ParallelIndexSet} + Let $I \subset \N_0$ be an arbitrary, not necessarily consecutive, + index set identifying all discretisation points of the computation. + Furthermore, let + $$({I}_p)_{p\in {\cal P}}, \quad + \bigcup\limits_{p \in {\cal P}} {I}_p = I$$ be an overlapping decomposition of the global index set + $I$ into the sets of indices ${I}_p$ corresponding to the + global indices of the values stored locally in the chunk of process $p$. + + Then the class + \begin{lstlisting}{} + template class ParallelIndexSet; + \end{lstlisting} + realises the one to one mapping + $$ + \gamma_p\::\: {I}_p \longrightarrow {I}^{\loc}_p := [0, n_p) + $$ + of the globally unique index onto the local index. + + The template parameter \lstinline!TG! is the type of the global + index and + \lstinline!TL! is the type of the local index. The only prerequisite + of \lstinline!TG! is that objects of this type are comparable using + the less-than-operator \lstinline! class ParallelLocalIndex; +\end{lstlisting} +as the type for the local index of class \lstinline!ParallelIndexSet!. +Here the template parameter \lstinline!TA! is the type of the +attributes used, e.g. an enumeration \lstinline!Flags! defined by +\begin{lstlisting} + enum Flags {owner, ghost}; +\end{lstlisting} +where +\lstinline!owner! marks the indices $k \in I_p$ owned by process +$p$ and \lstinline!ghost! the indices $k\not\in I_p$ owned +by other processes. + +As an example let us look at an array distributed between two +processes. In Figure \ref{fig:redistarray} one can see the array +$a$ as it appears in a sequential program. Below there are two +different distributions of that array. The local views $s_0$ and +$s_1$ are the parts process $0$ and $1$ store in the case that $a$ is +divided into two +blocks. The local views $t_0$ and $t_1$ are the parts of $a$ that +process $0$ and $1$ store in the case that $a$ is divided into 4 +blocks and process +$0$ stores the first and third block and process $1$ the second and +fourth block. The decompositions have an overlap of one and the indices have +the attributes \lstinline!owner! and \lstinline!ghost! visualised by +white and shaded cells, respectively. +The index sets $I_s$ and $I_t$ corresponding to the decompositions $s_p$ +and $t_p$, $p \in \{0,1\}$, are shown in Figure \ref{fig:redistindex} as sets of triples +$(g,l,a)$. Here $g$ is the global index, $l$ is the local index and +$a$ is the attribute (either o for \lstinline!owner! or {g} +for \lstinline!ghost!). +\begin{figure} + \centering + \begin{tikzpicture} + \draw (0,3.3) ellipse (2.2cm and 1.5cm) node [align=center,yshift=0.2cm] {$I_s$\\(0,0,o) (1,1,o)\\(2,2,o) (3,3,o) (4,4,o)\\(5,5,o) (6,6,g)}; + \draw (0,0) ellipse (2.2cm and 1.5cm) node [align=center,yshift=0.2cm] {$I_t$\\(0,0,o) (1,1,o) (2,2,o)\\(3,3,g) (5,4,g) (6,5,o)\\(7,6,o) (8,7,o) (9,8,g)}; + \draw (2.5,-2.7) -- ++(0,7.5); + \draw (5,3.3) ellipse (2.2cm and 1.5cm) node [align=center,yshift=0.2cm] {$I_s$\\(5,0,g) (6,1,o)\\(7,2,o) (8,3,o) (9,4,o)\\(10,5,o) (11,6,o)}; + \draw (5,0) ellipse (2.2cm and 1.5cm) node [align=center,yshift=0.2cm] {$I_t$\\(2,0,g) (3,1,o) (4,2,o)\\(5,3,o) (6,4,g) (8,5,g)\\(9,6,o) (10,7,o) (11,8,o)}; + \node at (0,-2.2) {Processor 0}; + \node at (5,-2.2) {Processor 1}; + \end{tikzpicture} + \caption{Index sets for array redistribution} + \label{fig:redistindex} +\end{figure} +\begin{figure*} + \tikzset{ + box/.style={ + draw, + shape=rectangle, + minimum width=1.5em, + minimum height=1.5em, + anchor=base, + inner sep=0pt, + }, + overlap/.style={fill=black!25!white}, + } + \newcommand{\interior}[1]{% + \tikz[baseline={(0,0)}]\node[box]{#1};% + } + \newcommand{\overlap}[1]{% + \tikz[baseline={(0,0)}]\node[box,overlap]{#1};% + } + \def\mc{\multicolumn}% + \newcommand{\leader}[1]{\makebox[2em][r]{#1 }}% + \renewcommand{\arraystretch}{2}% + \centering + \begin{tabular}{r|l} + \mc2c{global array} \\ + \mc2c{\leader{a:}% + \foreach\i in {0,...,11} {\interior{\i}}} \\ + \mc2c{local views} \\ + \leader{$s_0$:}% + \foreach\i in {0,...,5} {\interior{\i}}% + \foreach\i in {6} {\overlap {\i}} & + \leader{$s_1$:}% + \foreach\i in {5} {\overlap {\i}}% + \foreach\i in {6,...,11} {\interior{\i}} \\ + \leader{$t_0$:}% + \foreach\i in {0,1,2} {\interior{\i}}% + \foreach\i in {3,5} {\overlap {\i}}% + \foreach\i in {6,7,8} {\interior{\i}}% + \foreach\i in {9} {\overlap {\i}} & + \leader{$t_1$:}% + \foreach\i in {2} {\overlap {\i}}% + \foreach\i in {3,4,5} {\interior{\i}}% + \foreach\i in {6,8} {\overlap {\i}}% + \foreach\i in {9,10,11} {\interior{\i}} \\ + \mc1c{Processor 0} & \mc1{|c}{Processor 1} \\ + \end{tabular} + \caption{Redistributed array} + \label{fig:redistarray} +\end{figure*} + +The following code snippet demonstrates how to set up the index set +$I_s$ on process $0$: +\lstinputlisting[linerange={53-57,59-61,67-67}]{poosc08_test.cc} +\subsection{Remote Indices} +\label{sec:remote-indices} + +To set up communication between the processes every process needs to +know which indices are also known to other processes and which +attributes are attached to them on the remote side. +There are scenarios where data is exchanged between different +index sets, e.g. if the data is agglomerated on lesser processes or +redistributed. Therefore communication is allowed to occur between different +decompositions of the same index set. + + +Let $I \subset \N$ be the global index set and +$$ +(I^s_p)_{p\in{\cal P}},\quad \bigcup_{p\in{\cal P}} I^s_p = I,\quad +\text{ and } \quad +(I^t_p)_{p\in{\cal P}}, \quad\bigcup_{p\in{\cal P}} I^t_p = I +$$ be two overlapping +decompositions of the same index set $I$. Then an instance of class +\lstinline!RemoteIndices! on process $p \in {\cal P}$ +stores the sets of triples +\begin{equation} + \label{eq:ri_s_set} + \begin{split} + r_{p \rightarrow q}^{s} = \{ (g,(l,a),b) \,|\, g \in I^s_q \wedge g \in I_p^t, +l=\gamma_p^s(g), a = \alpha_p^s(l), b = +\alpha_q^t(\gamma_q^t(g))\} +\end{split} +\end{equation} +and +\begin{equation} + \label{eq:ri_t_set} + \begin{split} + r_{p \rightarrow q}^{t} = \{ (g,(l,a),b) \,|\, g \in I^s_q \wedge g \in I_p^t, + l=\gamma_p^t(g), a = \alpha_p^t(l), b = + \alpha_p^s(\gamma_p^s(g))\}\,, + \end{split} +\end{equation} +for all $q\in{\cal P}$. +Here $\alpha^s_p$ and $\alpha^t_p$ denote the mapping of local +indices on process $p$ onto attributes for the index set $I^s_p$ and +$I^t_p$ as realised by \lstinline!ParallelLocalIndex!. +Note that the sets $r_{p \rightarrow q}^{s}$ and $r_{p \rightarrow + q}^{t}$ will only be nonempty if the processes $p$ and $q$ manage +overlapping index sets. + +For our example in Figure \ref{fig:redistarray} and Figure +\ref{fig:redistindex} the interface between $I_s$ and $I_t$ on process +$0$ is: +\begin{align*} + r_{0\rightarrow 0}^{s} = \{&(0,(0,o),o), (1,(1,o),o), (2,(2,o),o), + (3,(3,o),g), (5,(5,o),g), (6,(6,g),o)\}\\ + r_{0\rightarrow 0}^{t} = \{&(0,(0,o),o), (1,(1,o),o), (2,(2,o),o), + (3,(3,g),o), (5,(4,g),o), (6,(5,o),g)\}\\ + r_{0\rightarrow 1}^{s} = \{&(2(2,o),g), (3,(3,o),o), (4,(4,o),o), + (5,(5,o),o), (6,(6,g),g)\}\\ + r_{0\rightarrow 1}^{t} = \{&(5,(4,g),g), (6,(5,o),o), (7,(6,o),o), + (8,(7,o),o), (9,(8,g),o)\} +\end{align*} +This information can either be calculated automatically by +communicating all indices in a ring or set up by hand if the user has +this information available. Assuming that \lstinline!sis! is the index set +$I_s$ and \lstinline!tis! the index set $I_t$ set up as described in +the previous subsection and \lstinline!comm! is an MPI communicator +then the simple call +\lstinputlisting[linerange={83-84}]{poosc08_test.cc} +on all processes automatically calculates this information and +stores it in \lstinline!riRedist!. For a +parallel calculation on the local views $s_0$ and $s_1$ calling +\lstinputlisting[linerange={86-87}]{poosc08_test.cc} +on all processes builds the necessary information in \lstinline!riS!. + +\subsection{Communication Interface} +\label{sec:comm-interf} + +With the information provided by class \lstinline!RemoteIndices! the +user can set up arbitrary communication interfaces. These interfaces +are realised in \lstinline!template class Interface!, +where the template parameter \lstinline!T! is the custom type of the +\lstinline!ParallelIndexSet! representing the index sets. +Using the attributes attached to the indices by +\lstinline!ParallelLocalIndex! the user can select subsets of the +indices for exchanging data, e.g. send data from indices marked +as \lstinline!owner! to indices marked as \lstinline!ghost!. + +Basically the interface on process $p$ manages two sets for each +process $q$ it shares common indices with: + +$$ +i_{p\rightarrow q}^{s} = \{ l | (g,(l,a),b) \in r_{p\rightarrow q}^{s} | +a \in A_s \wedge b \in A_t\} +$$ +and +$$ +i_{p\rightarrow q}^{t} = \{ l | (g, (l,a), b) \in r_{p\rightarrow q}^{t} | +a \in A_t \wedge b \in A_s\}\,, +$$ +where $A_s$ and $A_t$ are the attributes marking the indices where the +source and target of the communication will be, respectively. + +In our example these sets on process $0$ will be stored for +communication if $A_s=\{o\}$ and $A_t=\{o, g\}$: +\begin{align*} + i_{0\rightarrow 0}^{s} = \{0, 1, 3, 5\}\quad & \quad + i_{0\rightarrow 0}^{t} = \{0, 1, 3, 4\}\\ + i_{0\rightarrow 1}^{s} = \{2, 3, 4, 5\}\quad & \quad + i_{0\rightarrow 1}^{t} = \{5, 6, 7, 8\}\,. +\end{align*} + +The following code snippet would build the interface above in +\lstinline!infRedist! as well as the interface \lstinline!infS! +to communicate between +indices marked as \lstinline!owner! and \lstinline!ghost! on the local +array views $s_0$ and $s_1$: +\lstinputlisting[linerange={89-97}]{poosc08_test.cc} + +\subsection{Communicator} +\label{sec:communicator} + +Using the classes from the previous sections all information about the +communication is available and we are set to communicate data values +of arbitrary +container types. The only prerequisite for the container type is that +its values are addressable via \lstinline!operator[](size_t index)!. +This should be safe to assume. + +An important feature of our communicators is that we are not only able to +send one data item per index, but also different numbers of data +elements (of the same type) for each index. This is +supported in a generic way by the traits class +\lstinline!template struct CommPolicy! +describing the container type \lstinline!V!. The +\lstinline!typedef IndexedType! is the atomic type to be communicated and +\lstinline!typedef IndexedTypeFlag! is either \lstinline!SizeOne! if +there is only one data item per index or \lstinline!VariableSize! if the +number of data items per index is variable. + +The default implementation works for all +array-like containers which provide only one data item per index. For all +other containers the user has to provide its own custom +specialisation. +%For the vector classes of ISTL (up to two block levels) +%those specialisations are already implemented. + +The class \lstinline!template class BufferedCommunicator! +performs the +actual communication. The template parameter \lstinline!T! describes +the type of the parallel index set. +It uses the information about the communication interface provided by +an object of class \lstinline!Interface! to set up communication +buffers for a container containing a specific data type. It is also +responsible for gathering the data before and scattering the data +after the communication step. The strict separation of the interface +description from the actual buffering and communication allows for +reusing the interface information with various different container and +data types. + +Before the communication can start one has to call the +\lstinline!build! method with the data source and target containers as +well as the communication interface as arguments. Assuming +\lstinline!s! and \lstinline!t! as arrays $s_i$ and $t_i$, +respectively, then +\lstinputlisting[linerange=103-106]{poosc08_test.cc} +demonstrates how to set up the communicator \lstinline!bCommRedist! for the array +redistribution and \lstinline!bComm! for a parallel calculation on the +local views $s_i$. The +\lstinline!build! function +calculates the size of the messages to send to other processes and +allocates buffers for the send and receive actions. The +representatives \lstinline!s! and \lstinline!t! are +needed to get the number of data values at each index in the case of +variable numbers of data items per index. Note that, due to the generic +programming techniques used, the compiler knows if the number of data +points is constant for each index and will apply a specialised +algorithm for calculating the message size without querying neither +\lstinline!s! nor \lstinline!t!. Clean up of allocated +resources is done either by calling the method \lstinline!free()! or +automatically in the destructor. + +The actual communication takes place if one of the methods +\lstinline!forward! +and \lstinline!backward! is called. In our case in +\lstinline!bCommRedist! the \lstinline!forward! method +sends data from the local views $s_i$ to the local views $t_i$ +according to the interface information and the \lstinline!backward! +method in the opposite direction. + +The following code snippet first redistributes the local views $s_i$ +of the global array to the local views $t_i$ and +performs some calculation on this representation. Afterwards the +result is communicated backwards. +\lstinputlisting[linerange=110-113]{poosc08_test.cc} + +Note that both methods have a different template parameter, either +\lstinline!CopyData! or \lstinline!AddData!. These are policies for +gathering and scattering the data items. The former just copies +the data from and to the location. The latter copies from the source +location but adds the received data items to the target +entries. Assuming our data is stored in simple C-arrays +\lstinline!AddData! could be implemented like this: + +\lstinputlisting[linerange=16-27]{poosc08_test.cc} + +Note that arbitrary +manipulations can be applied to the communicated data in both methods. + +For containers with multiple data items associated with one index +the methods \lstinline!gather! and \lstinline!scatter! must have an additional +integer argument specifying the sub-index. + +\section{Collective Communication} +\label{sec:collective-communication} + +While communicating entries of array-like structures is a prominent +task in scientific computing codes one must not neglect +collective communication operations, like gathering and scattering data + from and to all processes, respectively, or waiting for other processes. An +abstraction for these operations is crucial for decoupling the +communication from the parallel programming paradigm used. + +Therefore we designed +\lstinline!template class CollectiveCommunication! which provides +information of the underlying parallel programming paradigm as well as +the collective communication operations as known from MPI. See Table +\ref{tab:col-comm} for a list of all functions. + +\begin{table*}%[b] + \centering + \begin{tabular}{p{.5\textwidth}|p{.4\textwidth}} + Function&Description\\\hline\hline + \lstinline!int rank()!&Get the rank of the process\\ + \lstinline!int size()!&Get the number of processes\\ + \lstinline!template T sum (T& in)!& Compute global + sum\\ + \lstinline!template T prod (T& in)!&Compute global + product\\ + \lstinline!template T min (T& in)!&Compute global minimum\\ + \lstinline!template T max (T& in)!&Compute global + maximum\\ + \lstinline!void barrier()!& Wait for all processes.\\ + \lstinline!template int broadcast (T* inout, int len, int root)! +& Broadcast an array from root to all other processes\\ +\lstinline!template int gather (T* in, T* out, int len, int root)!& +Gather arrays at a root process\\ +\lstinline!template int allreduce(Type* in, Type* out, int len)!& +Combine values from all processes on all processes. Combine function +is given with \lstinline!BinaryFunction! + \end{tabular} + \caption{Collective Communication Functions} + \label{tab:col-comm} +\end{table*} + +Currently there is a default implementation for sequential programs +as well as a specialisation working with MPI. This approach allows for +running parallel programs sequentially without any parallel overhead +simply by choosing the sequential specialisation at compile time. +Note that the interface is far more convenient to use than the C++ +interface of MPI. The latter is a simple wrapper around the C +implementation without taking advantage of the power of generic programming. + + +The collective communication classes were developed before the release +of Boost.MPI \cite{boost_mpi}. In contrast to Boost.MPI it was never +meant as a full generic implementation of all MPI functions. Instead it +is restricted to the most basic subset of collective operations needed +to implement finite element methods and iterative solver using the +previously described components. This lean interface should make it +possible to easily port this approach to +thread based parallelisation as well as other parallelisation +paradigms. This would allow code to easily switch between different paradigms + + +\bibliographystyle{plainnat} +\bibliography{communication} +\end{document} diff --git a/doc/comm/figures/darray.eps b/doc/comm/figures/darray.eps new file mode 100644 index 0000000..5c167b8 --- /dev/null +++ b/doc/comm/figures/darray.eps @@ -0,0 +1,230 @@ +%!PS-Adobe-2.0 EPSF-2.0 +%%Title: ../eps/darray.eps +%%Creator: fig2dev Version 3.2 Patchlevel 1 +%%CreationDate: Thu Mar 4 15:25:17 1999 +%%For: peter@speedo (Peter Bastian) +%%Orientation: Portrait +%%BoundingBox: 0 0 345 260 +%%Pages: 0 +%%BeginSetup +%%EndSetup +%%Magnification: 1.0000 +%%EndComments +/$F2psDict 200 dict def +$F2psDict begin +$F2psDict /mtrx matrix put +/col-1 {0 setgray} bind def +/col0 {0.000 0.000 0.000 srgb} bind def +/col1 {0.000 0.000 1.000 srgb} bind def +/col2 {0.000 1.000 0.000 srgb} bind def +/col3 {0.000 1.000 1.000 srgb} bind def +/col4 {1.000 0.000 0.000 srgb} bind def +/col5 {1.000 0.000 1.000 srgb} bind def +/col6 {1.000 1.000 0.000 srgb} bind def +/col7 {1.000 1.000 1.000 srgb} bind def +/col8 {0.000 0.000 0.560 srgb} bind def +/col9 {0.000 0.000 0.690 srgb} bind def +/col10 {0.000 0.000 0.820 srgb} bind def +/col11 {0.530 0.810 1.000 srgb} bind def +/col12 {0.000 0.560 0.000 srgb} bind def +/col13 {0.000 0.690 0.000 srgb} bind def +/col14 {0.000 0.820 0.000 srgb} bind def +/col15 {0.000 0.560 0.560 srgb} bind def +/col16 {0.000 0.690 0.690 srgb} bind def +/col17 {0.000 0.820 0.820 srgb} bind def +/col18 {0.560 0.000 0.000 srgb} bind def +/col19 {0.690 0.000 0.000 srgb} bind def +/col20 {0.820 0.000 0.000 srgb} bind def +/col21 {0.560 0.000 0.560 srgb} bind def +/col22 {0.690 0.000 0.690 srgb} bind def +/col23 {0.820 0.000 0.820 srgb} bind def +/col24 {0.500 0.190 0.000 srgb} bind def +/col25 {0.630 0.250 0.000 srgb} bind def +/col26 {0.750 0.380 0.000 srgb} bind def +/col27 {1.000 0.500 0.500 srgb} bind def +/col28 {1.000 0.630 0.630 srgb} bind def +/col29 {1.000 0.750 0.750 srgb} bind def +/col30 {1.000 0.880 0.880 srgb} bind def +/col31 {1.000 0.840 0.000 srgb} bind def + +end +save +-130.0 296.0 translate +1 -1 scale + +/cp {closepath} bind def +/ef {eofill} bind def +/gr {grestore} bind def +/gs {gsave} bind def +/sa {save} bind def +/rs {restore} bind def +/l {lineto} bind def +/m {moveto} bind def +/rm {rmoveto} bind def +/n {newpath} bind def +/s {stroke} bind def +/sh {show} bind def +/slc {setlinecap} bind def +/slj {setlinejoin} bind def +/slw {setlinewidth} bind def +/srgb {setrgbcolor} bind def +/rot {rotate} bind def +/sc {scale} bind def +/sd {setdash} bind def +/ff {findfont} bind def +/sf {setfont} bind def +/scf {scalefont} bind def +/sw {stringwidth} bind def +/tr {translate} bind def +/tnt {dup dup currentrgbcolor + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} + bind def +/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul + 4 -2 roll mul srgb} bind def +/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def +/$F2psEnd {$F2psEnteredState restore end} def +%%EndProlog + +$F2psBegin +10 setmiterlimit +n -1000 5689 m -1000 -1000 l 8527 -1000 l 8527 5689 l cp clip + 0.06299 0.06299 sc +% Polyline +7.500 slw +n 5265 3105 m 7515 3105 l 7515 3780 l 5265 3780 l cp gs col0 s gr +% Polyline +n 5670 3105 m 5670 3780 l gs col0 s gr +% Polyline +n 6120 3105 m 6120 3780 l gs col0 s gr +% Polyline +n 6570 3105 m 6570 3780 l gs col0 s gr +% Polyline +n 7020 3105 m 7020 3780 l gs col0 s gr +/Times-Roman ff 180.00 scf sf +5400 4050 m +gs 1 -1 sc (0) col0 sh gr +/Times-Roman ff 180.00 scf sf +6300 4050 m +gs 1 -1 sc (2) col0 sh gr +/Times-Roman ff 180.00 scf sf +6750 4050 m +gs 1 -1 sc (3) col0 sh gr +/Times-Roman ff 180.00 scf sf +7245 4050 m +gs 1 -1 sc (4) col0 sh gr +/Times-Roman ff 180.00 scf sf +5850 4050 m +gs 1 -1 sc (1) col0 sh gr +% Polyline +n 2340 3105 m 4590 3105 l 4590 3780 l 2340 3780 l cp gs col0 s gr +% Polyline +n 2745 3105 m 2745 3780 l gs col0 s gr +% Polyline +n 3195 3105 m 3195 3780 l gs col0 s gr +% Polyline +n 3645 3105 m 3645 3780 l gs col0 s gr +% Polyline +n 4095 3105 m 4095 3780 l gs col0 s gr +/Times-Roman ff 180.00 scf sf +2475 4050 m +gs 1 -1 sc (0) col0 sh gr +/Times-Roman ff 180.00 scf sf +3375 4050 m +gs 1 -1 sc (2) col0 sh gr +/Times-Roman ff 180.00 scf sf +3825 4050 m +gs 1 -1 sc (3) col0 sh gr +/Times-Roman ff 180.00 scf sf +4320 4050 m +gs 1 -1 sc (4) col0 sh gr +/Times-Roman ff 180.00 scf sf +2925 4050 m +gs 1 -1 sc (1) col0 sh gr +/Times-Roman ff 180.00 scf sf +2970 4320 m +gs 1 -1 sc (local indices) col0 sh gr +/Times-Roman ff 180.00 scf sf +2475 4635 m +gs 1 -1 sc (local array in processor 0) col0 sh gr +% Polyline +n 3150 1215 m 6750 1215 l 6750 1890 l 3150 1890 l cp gs col0 s gr +% Polyline +n 4950 1215 m 4950 1890 l gs col0 s gr +% Polyline +n 4050 1215 m 4050 1890 l gs col0 s gr +% Polyline +n 3600 1215 m 3600 1890 l gs col0 s gr +% Polyline +n 4500 1215 m 4500 1890 l gs col0 s gr +% Polyline +n 5850 1215 m 5850 1890 l gs col0 s gr +% Polyline +n 5400 1215 m 5400 1890 l gs col0 s gr +% Polyline +n 6300 1215 m 6300 1890 l gs col0 s gr +% Polyline +n 2520 3105 m 3375 1890 l gs col0 s gr +% Polyline +n 2970 3105 m 4230 1890 l gs col0 s gr +% Polyline +n 3375 3105 m 6030 1890 l gs col0 s gr +% Polyline +n 3825 3105 m 4725 1890 l gs col0 s gr +% Polyline +n 5490 3105 m 3465 1890 l gs col0 s gr +% Polyline +n 5850 3105 m 3870 1890 l gs col0 s gr +% Polyline +n 6345 3105 m 6525 1890 l gs col0 s gr +% Polyline +n 6795 3105 m 5625 1890 l gs col0 s gr +% Polyline +n 7290 3105 m 5175 1890 l gs col0 s gr +% Polyline +n 4320 3105 m 5535 1890 l gs col0 s gr +/Times-Roman ff 180.00 scf sf +3285 1035 m +gs 1 -1 sc (0) col0 sh gr +/Times-Roman ff 180.00 scf sf +3735 1035 m +gs 1 -1 sc (1) col0 sh gr +/Times-Roman ff 180.00 scf sf +4230 1035 m +gs 1 -1 sc (2) col0 sh gr +/Times-Roman ff 180.00 scf sf +4680 1035 m +gs 1 -1 sc (3) col0 sh gr +/Times-Roman ff 180.00 scf sf +5085 1035 m +gs 1 -1 sc (4) col0 sh gr +/Times-Roman ff 180.00 scf sf +5535 1035 m +gs 1 -1 sc (5) col0 sh gr +/Times-Roman ff 180.00 scf sf +5985 1035 m +gs 1 -1 sc (6) col0 sh gr +/Times-Roman ff 180.00 scf sf +6435 1035 m +gs 1 -1 sc (7) col0 sh gr +/Times-Roman ff 180.00 scf sf +5940 4320 m +gs 1 -1 sc (local indices) col0 sh gr +/Times-Roman ff 180.00 scf sf +5490 4635 m +gs 1 -1 sc (local array in processor 1) col0 sh gr +/Times-Roman ff 180.00 scf sf +3825 720 m +gs 1 -1 sc (global array with global indices) col0 sh gr +/Times-Italic ff 180.00 scf sf +2880 1665 m +gs 1 -1 sc (a:) col0 sh gr +/Times-Italic ff 180.00 scf sf +2070 3555 m +gs 1 -1 sc (a0:) col0 sh gr +/Times-Italic ff 180.00 scf sf +4995 3555 m +gs 1 -1 sc (a1:) col0 sh gr +$F2psEnd +rs diff --git a/doc/comm/figures/distindex.eps b/doc/comm/figures/distindex.eps new file mode 100644 index 0000000..1cf2092 --- /dev/null +++ b/doc/comm/figures/distindex.eps @@ -0,0 +1,4736 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: inkscape 0.44.1 +%%Pages: 1 +%%Orientation: Portrait +%%BoundingBox: -5 414 276 601 +%%HiResBoundingBox: -5.9999911 414 276 600.4 +%%DocumentMedia: plain 596 842 0 () () +%%EndComments +%%Page: 1 1 +0 842 translate +0.8 -0.8 scale +gsave [1 0 0 1 0 0] concat +gsave +0 0 0 setrgbcolor +newpath +11.34082 347.36218 moveto +10.730139 348.41037 10.276689 349.44715 9.9804688 350.47253 curveto +9.6842421 351.49793 9.5361303 352.53699 9.5361328 353.58972 curveto +9.5361303 354.64246 9.6842421 355.68608 9.9804688 356.72058 curveto +10.281247 357.75053 10.734697 358.78731 11.34082 359.83093 curveto +10.24707 359.83093 lineto +9.563474 358.75997 9.0507792 357.70723 8.7089844 356.67273 curveto +8.3717434 355.63823 8.2031238 354.61056 8.203125 353.58972 curveto +8.2031238 352.57345 8.3717434 351.55034 8.7089844 350.52039 curveto +9.0462219 349.49045 9.5589167 348.43771 10.24707 347.36218 curveto +11.34082 347.36218 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +15.155273 356.82312 moveto +19.974609 356.82312 lineto +19.974609 357.98523 lineto +13.494141 357.98523 lineto +13.494141 356.82312 lineto +14.018228 356.2808 14.731443 355.55392 15.633789 354.64246 curveto +16.540686 353.72644 17.110347 353.13628 17.342773 352.87195 curveto +17.784825 352.37521 18.092442 351.95594 18.265625 351.61414 curveto +18.443353 351.26779 18.53222 350.92827 18.532227 350.59558 curveto +18.53222 350.05327 18.340814 349.61121 17.958008 349.26941 curveto +17.579747 348.92762 17.085282 348.75672 16.474609 348.75671 curveto +16.041663 348.75672 15.583656 348.83192 15.100586 348.9823 curveto +14.622068 349.1327 14.109373 349.36056 13.5625 349.66589 curveto +13.5625 348.27136 lineto +14.118488 348.04806 14.638019 347.87945 15.121094 347.7655 curveto +15.604164 347.65158 16.04622 347.59461 16.447266 347.5946 curveto +17.504552 347.59461 18.34765 347.85894 18.976562 348.38757 curveto +19.605462 348.91623 19.919914 349.62261 19.919922 350.50671 curveto +19.919914 350.92599 19.840162 351.32475 19.680664 351.703 curveto +19.525709 352.07671 19.240879 352.51876 18.826172 353.02917 curveto +18.712233 353.16134 18.349929 353.54415 17.739258 354.17761 curveto +17.128576 354.80652 16.267249 355.68836 15.155273 356.82312 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +23.023438 356.2489 moveto +24.46582 356.2489 lineto +24.46582 357.42468 lineto +23.344727 359.61218 lineto +22.462891 359.61218 lineto +23.023438 357.42468 lineto +23.023438 356.2489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +28.526367 356.82312 moveto +33.345703 356.82312 lineto +33.345703 357.98523 lineto +26.865234 357.98523 lineto +26.865234 356.82312 lineto +27.389321 356.2808 28.102537 355.55392 29.004883 354.64246 curveto +29.91178 353.72644 30.481441 353.13628 30.713867 352.87195 curveto +31.155919 352.37521 31.463536 351.95594 31.636719 351.61414 curveto +31.814447 351.26779 31.903314 350.92827 31.90332 350.59558 curveto +31.903314 350.05327 31.711908 349.61121 31.329102 349.26941 curveto +30.950841 348.92762 30.456376 348.75672 29.845703 348.75671 curveto +29.412757 348.75672 28.954749 348.83192 28.47168 348.9823 curveto +27.993162 349.1327 27.480467 349.36056 26.933594 349.66589 curveto +26.933594 348.27136 lineto +27.489582 348.04806 28.009112 347.87945 28.492188 347.7655 curveto +28.975257 347.65158 29.417314 347.59461 29.818359 347.5946 curveto +30.875646 347.59461 31.718744 347.85894 32.347656 348.38757 curveto +32.976555 348.91623 33.291008 349.62261 33.291016 350.50671 curveto +33.291008 350.92599 33.211256 351.32475 33.051758 351.703 curveto +32.896803 352.07671 32.611972 352.51876 32.197266 353.02917 curveto +32.083327 353.16134 31.721023 353.54415 31.110352 354.17761 curveto +30.49967 354.80652 29.638343 355.68836 28.526367 356.82312 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +36.394531 356.2489 moveto +37.836914 356.2489 lineto +37.836914 357.42468 lineto +36.71582 359.61218 lineto +35.833984 359.61218 lineto +36.394531 357.42468 lineto +36.394531 356.2489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +43.49707 351.21082 moveto +42.822588 351.21082 42.289385 351.47514 41.897461 352.00378 curveto +41.505532 352.52788 41.309568 353.24793 41.30957 354.16394 curveto +41.309568 355.07996 41.503253 355.80229 41.890625 356.33093 curveto +42.282549 356.85502 42.81803 357.11707 43.49707 357.11707 curveto +44.166987 357.11707 44.697911 356.85274 45.089844 356.3241 curveto +45.481765 355.79545 45.677728 355.0754 45.677734 354.16394 curveto +45.677728 353.25704 45.481765 352.53927 45.089844 352.01062 curveto +44.697911 351.47742 44.166987 351.21082 43.49707 351.21082 curveto +43.49707 350.14441 moveto +44.590815 350.14442 45.449864 350.49989 46.074219 351.21082 curveto +46.69856 351.92176 47.010734 352.90613 47.010742 354.16394 curveto +47.010734 355.4172 46.69856 356.40157 46.074219 357.11707 curveto +45.449864 357.828 44.590815 358.18347 43.49707 358.18347 curveto +42.39876 358.18347 41.537433 357.828 40.913086 357.11707 curveto +40.293293 356.40157 39.983398 355.4172 39.983398 354.16394 curveto +39.983398 352.90613 40.293293 351.92176 40.913086 351.21082 curveto +41.537433 350.49989 42.39876 350.14442 43.49707 350.14441 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +48.890625 347.36218 moveto +49.984375 347.36218 lineto +50.667966 348.43771 51.178382 349.49045 51.515625 350.52039 curveto +51.857418 351.55034 52.028316 352.57345 52.02832 353.58972 curveto +52.028316 354.61056 51.857418 355.63823 51.515625 356.67273 curveto +51.178382 357.70723 50.667966 358.75997 49.984375 359.83093 curveto +48.890625 359.83093 lineto +49.496743 358.78731 49.947914 357.75053 50.244141 356.72058 curveto +50.544919 355.68608 50.69531 354.64246 50.695312 353.58972 curveto +50.69531 352.53699 50.544919 351.49793 50.244141 350.47253 curveto +49.947914 349.44715 49.496743 348.41037 48.890625 347.36218 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +32.34082 327.36218 moveto +31.730139 328.41037 31.276689 329.44715 30.980469 330.47253 curveto +30.684242 331.49793 30.53613 332.53699 30.536133 333.58972 curveto +30.53613 334.64246 30.684242 335.68608 30.980469 336.72058 curveto +31.281247 337.75053 31.734697 338.78731 32.34082 339.83093 curveto +31.24707 339.83093 lineto +30.563474 338.75997 30.050779 337.70723 29.708984 336.67273 curveto +29.371743 335.63823 29.203124 334.61056 29.203125 333.58972 curveto +29.203124 332.57345 29.371743 331.55034 29.708984 330.52039 curveto +30.046222 329.49045 30.558917 328.43771 31.24707 327.36218 curveto +32.34082 327.36218 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +37.918945 328.68835 moveto +37.208004 328.68836 36.672523 329.03927 36.3125 329.74109 curveto +35.957029 330.43836 35.779295 331.48882 35.779297 332.89246 curveto +35.779295 334.29155 35.957029 335.342 36.3125 336.04382 curveto +36.672523 336.74109 37.208004 337.08972 37.918945 337.08972 curveto +38.634435 337.08972 39.169916 336.74109 39.525391 336.04382 curveto +39.88541 335.342 40.065423 334.29155 40.06543 332.89246 curveto +40.065423 331.48882 39.88541 330.43836 39.525391 329.74109 curveto +39.169916 329.03927 38.634435 328.68836 37.918945 328.68835 curveto +37.918945 327.5946 moveto +39.06282 327.59461 39.93554 328.04806 40.537109 328.95496 curveto +41.143221 329.85731 41.446281 331.16981 41.446289 332.89246 curveto +41.446281 334.61056 41.143221 335.92306 40.537109 336.82996 curveto +39.93554 337.7323 39.06282 338.18347 37.918945 338.18347 curveto +36.775062 338.18347 35.900063 337.7323 35.293945 336.82996 curveto +34.692382 335.92306 34.391601 334.61056 34.391602 332.89246 curveto +34.391601 331.16981 34.692382 329.85731 35.293945 328.95496 curveto +35.900063 328.04806 36.775062 327.59461 37.918945 327.5946 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +44.023438 336.2489 moveto +45.46582 336.2489 lineto +45.46582 337.42468 lineto +44.344727 339.61218 lineto +43.462891 339.61218 lineto +44.023438 337.42468 lineto +44.023438 336.2489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +51.290039 328.68835 moveto +50.579098 328.68836 50.043617 329.03927 49.683594 329.74109 curveto +49.328123 330.43836 49.150388 331.48882 49.150391 332.89246 curveto +49.150388 334.29155 49.328123 335.342 49.683594 336.04382 curveto +50.043617 336.74109 50.579098 337.08972 51.290039 337.08972 curveto +52.005529 337.08972 52.54101 336.74109 52.896484 336.04382 curveto +53.256504 335.342 53.436517 334.29155 53.436523 332.89246 curveto +53.436517 331.48882 53.256504 330.43836 52.896484 329.74109 curveto +52.54101 329.03927 52.005529 328.68836 51.290039 328.68835 curveto +51.290039 327.5946 moveto +52.433914 327.59461 53.306634 328.04806 53.908203 328.95496 curveto +54.514315 329.85731 54.817375 331.16981 54.817383 332.89246 curveto +54.817375 334.61056 54.514315 335.92306 53.908203 336.82996 curveto +53.306634 337.7323 52.433914 338.18347 51.290039 338.18347 curveto +50.146156 338.18347 49.271156 337.7323 48.665039 336.82996 curveto +48.063475 335.92306 47.762694 334.61056 47.762695 332.89246 curveto +47.762694 331.16981 48.063475 329.85731 48.665039 328.95496 curveto +49.271156 328.04806 50.146156 327.59461 51.290039 327.5946 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +57.394531 336.2489 moveto +58.836914 336.2489 lineto +58.836914 337.42468 lineto +57.71582 339.61218 lineto +56.833984 339.61218 lineto +57.394531 337.42468 lineto +57.394531 336.2489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +64.49707 331.21082 moveto +63.822588 331.21082 63.289385 331.47514 62.897461 332.00378 curveto +62.505532 332.52788 62.309568 333.24793 62.30957 334.16394 curveto +62.309568 335.07996 62.503253 335.80229 62.890625 336.33093 curveto +63.282549 336.85502 63.81803 337.11707 64.49707 337.11707 curveto +65.166987 337.11707 65.697911 336.85274 66.089844 336.3241 curveto +66.481765 335.79545 66.677728 335.0754 66.677734 334.16394 curveto +66.677728 333.25704 66.481765 332.53927 66.089844 332.01062 curveto +65.697911 331.47742 65.166987 331.21082 64.49707 331.21082 curveto +64.49707 330.14441 moveto +65.590815 330.14442 66.449864 330.49989 67.074219 331.21082 curveto +67.69856 331.92176 68.010734 332.90613 68.010742 334.16394 curveto +68.010734 335.4172 67.69856 336.40157 67.074219 337.11707 curveto +66.449864 337.828 65.590815 338.18347 64.49707 338.18347 curveto +63.39876 338.18347 62.537433 337.828 61.913086 337.11707 curveto +61.293293 336.40157 60.983398 335.4172 60.983398 334.16394 curveto +60.983398 332.90613 61.293293 331.92176 61.913086 331.21082 curveto +62.537433 330.49989 63.39876 330.14442 64.49707 330.14441 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +69.890625 327.36218 moveto +70.984375 327.36218 lineto +71.667966 328.43771 72.178382 329.49045 72.515625 330.52039 curveto +72.857418 331.55034 73.028316 332.57345 73.02832 333.58972 curveto +73.028316 334.61056 72.857418 335.63823 72.515625 336.67273 curveto +72.178382 337.70723 71.667966 338.75997 70.984375 339.83093 curveto +69.890625 339.83093 lineto +70.496743 338.78731 70.947914 337.75053 71.244141 336.72058 curveto +71.544919 335.68608 71.69531 334.64246 71.695312 333.58972 curveto +71.69531 332.53699 71.544919 331.49793 71.244141 330.47253 curveto +70.947914 329.44715 70.496743 328.41037 69.890625 327.36218 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +77.683594 328.36218 moveto +77.072913 329.41037 76.619463 330.44715 76.323242 331.47253 curveto +76.027016 332.49793 75.878904 333.53699 75.878906 334.58972 curveto +75.878904 335.64246 76.027016 336.68608 76.323242 337.72058 curveto +76.62402 338.75053 77.07747 339.78731 77.683594 340.83093 curveto +76.589844 340.83093 lineto +75.906247 339.75997 75.393553 338.70723 75.051758 337.67273 curveto +74.714517 336.63823 74.545897 335.61056 74.545898 334.58972 curveto +74.545897 333.57345 74.714517 332.55034 75.051758 331.52039 curveto +75.388995 330.49045 75.90169 329.43771 76.589844 328.36218 curveto +77.683594 328.36218 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +80.547852 337.82312 moveto +82.803711 337.82312 lineto +82.803711 330.03699 lineto +80.349609 330.52917 lineto +80.349609 329.27136 lineto +82.790039 328.77917 lineto +84.170898 328.77917 lineto +84.170898 337.82312 lineto +86.426758 337.82312 lineto +86.426758 338.98523 lineto +80.547852 338.98523 lineto +80.547852 337.82312 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +89.366211 337.2489 moveto +90.808594 337.2489 lineto +90.808594 338.42468 lineto +89.6875 340.61218 lineto +88.805664 340.61218 lineto +89.366211 338.42468 lineto +89.366211 337.2489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +93.918945 337.82312 moveto +96.174805 337.82312 lineto +96.174805 330.03699 lineto +93.720703 330.52917 lineto +93.720703 329.27136 lineto +96.161133 328.77917 lineto +97.541992 328.77917 lineto +97.541992 337.82312 lineto +99.797852 337.82312 lineto +99.797852 338.98523 lineto +93.918945 338.98523 lineto +93.918945 337.82312 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +102.7373 337.2489 moveto +104.17969 337.2489 lineto +104.17969 338.42468 lineto +103.05859 340.61218 lineto +102.17676 340.61218 lineto +102.7373 338.42468 lineto +102.7373 337.2489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +109.83984 332.21082 moveto +109.16536 332.21082 108.63216 332.47514 108.24023 333.00378 curveto +107.8483 333.52788 107.65234 334.24793 107.65234 335.16394 curveto +107.65234 336.07996 107.84603 336.80229 108.2334 337.33093 curveto +108.62532 337.85502 109.1608 338.11707 109.83984 338.11707 curveto +110.50976 338.11707 111.04068 337.85274 111.43262 337.3241 curveto +111.82454 336.79545 112.0205 336.0754 112.02051 335.16394 curveto +112.0205 334.25704 111.82454 333.53927 111.43262 333.01062 curveto +111.04068 332.47742 110.50976 332.21082 109.83984 332.21082 curveto +109.83984 331.14441 moveto +110.93359 331.14442 111.79264 331.49989 112.41699 332.21082 curveto +113.04133 332.92176 113.35351 333.90613 113.35352 335.16394 curveto +113.35351 336.4172 113.04133 337.40157 112.41699 338.11707 curveto +111.79264 338.828 110.93359 339.18347 109.83984 339.18347 curveto +108.74153 339.18347 107.88021 338.828 107.25586 338.11707 curveto +106.63607 337.40157 106.32617 336.4172 106.32617 335.16394 curveto +106.32617 333.90613 106.63607 332.92176 107.25586 332.21082 curveto +107.88021 331.49989 108.74153 331.14442 109.83984 331.14441 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +115.2334 328.36218 moveto +116.32715 328.36218 lineto +117.01074 329.43771 117.52116 330.49045 117.8584 331.52039 curveto +118.20019 332.55034 118.37109 333.57345 118.37109 334.58972 curveto +118.37109 335.61056 118.20019 336.63823 117.8584 337.67273 curveto +117.52116 338.70723 117.01074 339.75997 116.32715 340.83093 curveto +115.2334 340.83093 lineto +115.83952 339.78731 116.29069 338.75053 116.58691 337.72058 curveto +116.88769 336.68608 117.03808 335.64246 117.03809 334.58972 curveto +117.03808 333.53699 116.88769 332.49793 116.58691 331.47253 curveto +116.29069 330.44715 115.83952 329.41037 115.2334 328.36218 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +56.84082 347.36218 moveto +56.230139 348.41037 55.776689 349.44715 55.480469 350.47253 curveto +55.184242 351.49793 55.03613 352.53699 55.036133 353.58972 curveto +55.03613 354.64246 55.184242 355.68608 55.480469 356.72058 curveto +55.781247 357.75053 56.234697 358.78731 56.84082 359.83093 curveto +55.74707 359.83093 lineto +55.063474 358.75997 54.550779 357.70723 54.208984 356.67273 curveto +53.871743 355.63823 53.703124 354.61056 53.703125 353.58972 curveto +53.703124 352.57345 53.871743 351.55034 54.208984 350.52039 curveto +54.546222 349.49045 55.058917 348.43771 55.74707 347.36218 curveto +56.84082 347.36218 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +63.649414 352.4823 moveto +64.310215 352.62358 64.825188 352.91753 65.194336 353.36414 curveto +65.568026 353.81075 65.754875 354.36219 65.754883 355.01843 curveto +65.754875 356.0256 65.408521 356.80489 64.71582 357.35632 curveto +64.023106 357.90776 63.038732 358.18347 61.762695 358.18347 curveto +61.334307 358.18347 60.89225 358.14018 60.436523 358.05359 curveto +59.98535 357.97156 59.518228 357.84623 59.035156 357.67761 curveto +59.035156 356.3446 lineto +59.417967 356.56791 59.837238 356.73653 60.292969 356.85046 curveto +60.748695 356.9644 61.224932 357.02136 61.72168 357.02136 curveto +62.58756 357.02136 63.246088 356.85047 63.697266 356.50867 curveto +64.152989 356.16687 64.380853 355.67013 64.380859 355.01843 curveto +64.380853 354.41687 64.168939 353.94747 63.745117 353.61023 curveto +63.325841 353.26844 62.74023 353.09754 61.988281 353.09753 curveto +60.798828 353.09753 lineto +60.798828 351.96277 lineto +62.042969 351.96277 lineto +62.722 351.96277 63.241531 351.82833 63.601562 351.55945 curveto +63.961583 351.28602 64.141595 350.89409 64.141602 350.38367 curveto +64.141595 349.85959 63.954747 349.45855 63.581055 349.18054 curveto +63.211909 348.898 62.680985 348.75672 61.988281 348.75671 curveto +61.610022 348.75672 61.204424 348.79774 60.771484 348.87976 curveto +60.338539 348.9618 59.862303 349.08941 59.342773 349.26257 curveto +59.342773 348.0321 lineto +59.86686 347.88628 60.356768 347.77691 60.8125 347.70398 curveto +61.272783 347.63107 61.705725 347.59461 62.111328 347.5946 curveto +63.1595 347.59461 63.988926 347.83387 64.599609 348.31238 curveto +65.210279 348.78635 65.515617 349.42892 65.515625 350.24011 curveto +65.515617 350.80522 65.353834 351.28374 65.030273 351.67566 curveto +64.706699 352.06303 64.246413 352.33191 63.649414 352.4823 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +68.523438 356.2489 moveto +69.96582 356.2489 lineto +69.96582 357.42468 lineto +68.844727 359.61218 lineto +67.962891 359.61218 lineto +68.523438 357.42468 lineto +68.523438 356.2489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +77.020508 352.4823 moveto +77.681309 352.62358 78.196282 352.91753 78.56543 353.36414 curveto +78.93912 353.81075 79.125969 354.36219 79.125977 355.01843 curveto +79.125969 356.0256 78.779615 356.80489 78.086914 357.35632 curveto +77.3942 357.90776 76.409826 358.18347 75.133789 358.18347 curveto +74.7054 358.18347 74.263343 358.14018 73.807617 358.05359 curveto +73.356443 357.97156 72.889321 357.84623 72.40625 357.67761 curveto +72.40625 356.3446 lineto +72.789061 356.56791 73.208331 356.73653 73.664062 356.85046 curveto +74.119789 356.9644 74.596025 357.02136 75.092773 357.02136 curveto +75.958654 357.02136 76.617182 356.85047 77.068359 356.50867 curveto +77.524082 356.16687 77.751947 355.67013 77.751953 355.01843 curveto +77.751947 354.41687 77.540033 353.94747 77.116211 353.61023 curveto +76.696935 353.26844 76.111323 353.09754 75.359375 353.09753 curveto +74.169922 353.09753 lineto +74.169922 351.96277 lineto +75.414062 351.96277 lineto +76.093094 351.96277 76.612625 351.82833 76.972656 351.55945 curveto +77.332676 351.28602 77.512689 350.89409 77.512695 350.38367 curveto +77.512689 349.85959 77.32584 349.45855 76.952148 349.18054 curveto +76.583003 348.898 76.052079 348.75672 75.359375 348.75671 curveto +74.981116 348.75672 74.575518 348.79774 74.142578 348.87976 curveto +73.709633 348.9618 73.233397 349.08941 72.713867 349.26257 curveto +72.713867 348.0321 lineto +73.237954 347.88628 73.727862 347.77691 74.183594 347.70398 curveto +74.643877 347.63107 75.076819 347.59461 75.482422 347.5946 curveto +76.530594 347.59461 77.36002 347.83387 77.970703 348.31238 curveto +78.581373 348.78635 78.886711 349.42892 78.886719 350.24011 curveto +78.886711 350.80522 78.724928 351.28374 78.401367 351.67566 curveto +78.077793 352.06303 77.617507 352.33191 77.020508 352.4823 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +81.894531 356.2489 moveto +83.336914 356.2489 lineto +83.336914 357.42468 lineto +82.21582 359.61218 lineto +81.333984 359.61218 lineto +81.894531 357.42468 lineto +81.894531 356.2489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +88.99707 351.21082 moveto +88.322588 351.21082 87.789385 351.47514 87.397461 352.00378 curveto +87.005532 352.52788 86.809568 353.24793 86.80957 354.16394 curveto +86.809568 355.07996 87.003253 355.80229 87.390625 356.33093 curveto +87.782549 356.85502 88.31803 357.11707 88.99707 357.11707 curveto +89.666987 357.11707 90.197911 356.85274 90.589844 356.3241 curveto +90.981765 355.79545 91.177728 355.0754 91.177734 354.16394 curveto +91.177728 353.25704 90.981765 352.53927 90.589844 352.01062 curveto +90.197911 351.47742 89.666987 351.21082 88.99707 351.21082 curveto +88.99707 350.14441 moveto +90.090815 350.14442 90.949864 350.49989 91.574219 351.21082 curveto +92.19856 351.92176 92.510734 352.90613 92.510742 354.16394 curveto +92.510734 355.4172 92.19856 356.40157 91.574219 357.11707 curveto +90.949864 357.828 90.090815 358.18347 88.99707 358.18347 curveto +87.89876 358.18347 87.037433 357.828 86.413086 357.11707 curveto +85.793293 356.40157 85.483398 355.4172 85.483398 354.16394 curveto +85.483398 352.90613 85.793293 351.92176 86.413086 351.21082 curveto +87.037433 350.49989 87.89876 350.14442 88.99707 350.14441 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +94.390625 347.36218 moveto +95.484375 347.36218 lineto +96.167966 348.43771 96.678382 349.49045 97.015625 350.52039 curveto +97.357418 351.55034 97.528316 352.57345 97.52832 353.58972 curveto +97.528316 354.61056 97.357418 355.63823 97.015625 356.67273 curveto +96.678382 357.70723 96.167966 358.75997 95.484375 359.83093 curveto +94.390625 359.83093 lineto +94.996743 358.78731 95.447914 357.75053 95.744141 356.72058 curveto +96.044919 355.68608 96.19531 354.64246 96.195312 353.58972 curveto +96.19531 352.53699 96.044919 351.49793 95.744141 350.47253 curveto +95.447914 349.44715 94.996743 348.41037 94.390625 347.36218 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +102.34082 348.36218 moveto +101.73014 349.41037 101.27669 350.44715 100.98047 351.47253 curveto +100.68424 352.49793 100.53613 353.53699 100.53613 354.58972 curveto +100.53613 355.64246 100.68424 356.68608 100.98047 357.72058 curveto +101.28125 358.75053 101.7347 359.78731 102.34082 360.83093 curveto +101.24707 360.83093 lineto +100.56347 359.75997 100.05078 358.70723 99.708984 357.67273 curveto +99.371743 356.63823 99.203124 355.61056 99.203125 354.58972 curveto +99.203124 353.57345 99.371743 352.55034 99.708984 351.52039 curveto +100.04622 350.49045 100.55892 349.43771 101.24707 348.36218 curveto +102.34082 348.36218 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +108.75977 349.9823 moveto +105.27344 355.43054 lineto +108.75977 355.43054 lineto +108.75977 349.9823 lineto +108.39746 348.77917 moveto +110.13379 348.77917 lineto +110.13379 355.43054 lineto +111.58984 355.43054 lineto +111.58984 356.57898 lineto +110.13379 356.57898 lineto +110.13379 358.98523 lineto +108.75977 358.98523 lineto +108.75977 356.57898 lineto +104.15234 356.57898 lineto +104.15234 355.24597 lineto +108.39746 348.77917 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +114.02344 357.2489 moveto +115.46582 357.2489 lineto +115.46582 358.42468 lineto +114.34473 360.61218 lineto +113.46289 360.61218 lineto +114.02344 358.42468 lineto +114.02344 357.2489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +122.13086 349.9823 moveto +118.64453 355.43054 lineto +122.13086 355.43054 lineto +122.13086 349.9823 lineto +121.76855 348.77917 moveto +123.50488 348.77917 lineto +123.50488 355.43054 lineto +124.96094 355.43054 lineto +124.96094 356.57898 lineto +123.50488 356.57898 lineto +123.50488 358.98523 lineto +122.13086 358.98523 lineto +122.13086 356.57898 lineto +117.52344 356.57898 lineto +117.52344 355.24597 lineto +121.76855 348.77917 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +127.39453 357.2489 moveto +128.83691 357.2489 lineto +128.83691 358.42468 lineto +127.71582 360.61218 lineto +126.83398 360.61218 lineto +127.39453 358.42468 lineto +127.39453 357.2489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +134.49707 352.21082 moveto +133.82259 352.21082 133.28938 352.47514 132.89746 353.00378 curveto +132.50553 353.52788 132.30957 354.24793 132.30957 355.16394 curveto +132.30957 356.07996 132.50325 356.80229 132.89062 357.33093 curveto +133.28255 357.85502 133.81803 358.11707 134.49707 358.11707 curveto +135.16699 358.11707 135.69791 357.85274 136.08984 357.3241 curveto +136.48176 356.79545 136.67773 356.0754 136.67773 355.16394 curveto +136.67773 354.25704 136.48176 353.53927 136.08984 353.01062 curveto +135.69791 352.47742 135.16699 352.21082 134.49707 352.21082 curveto +134.49707 351.14441 moveto +135.59081 351.14442 136.44986 351.49989 137.07422 352.21082 curveto +137.69856 352.92176 138.01073 353.90613 138.01074 355.16394 curveto +138.01073 356.4172 137.69856 357.40157 137.07422 358.11707 curveto +136.44986 358.828 135.59081 359.18347 134.49707 359.18347 curveto +133.39876 359.18347 132.53743 358.828 131.91309 358.11707 curveto +131.29329 357.40157 130.9834 356.4172 130.9834 355.16394 curveto +130.9834 353.90613 131.29329 352.92176 131.91309 352.21082 curveto +132.53743 351.49989 133.39876 351.14442 134.49707 351.14441 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +139.89062 348.36218 moveto +140.98438 348.36218 lineto +141.66797 349.43771 142.17838 350.49045 142.51562 351.52039 curveto +142.85742 352.55034 143.02832 353.57345 143.02832 354.58972 curveto +143.02832 355.61056 142.85742 356.63823 142.51562 357.67273 curveto +142.17838 358.70723 141.66797 359.75997 140.98438 360.83093 curveto +139.89062 360.83093 lineto +140.49674 359.78731 140.94791 358.75053 141.24414 357.72058 curveto +141.54492 356.68608 141.69531 355.64246 141.69531 354.58972 curveto +141.69531 353.53699 141.54492 352.49793 141.24414 351.47253 curveto +140.94791 350.44715 140.49674 349.41037 139.89062 348.36218 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +27.34082 371.86218 moveto +26.730139 372.91037 26.276689 373.94715 25.980469 374.97253 curveto +25.684242 375.99793 25.53613 377.03699 25.536133 378.08972 curveto +25.53613 379.14246 25.684242 380.18608 25.980469 381.22058 curveto +26.281247 382.25053 26.734697 383.28731 27.34082 384.33093 curveto +26.24707 384.33093 lineto +25.563474 383.25997 25.050779 382.20723 24.708984 381.17273 curveto +24.371743 380.13823 24.203124 379.11056 24.203125 378.08972 curveto +24.203124 377.07345 24.371743 376.05034 24.708984 375.02039 curveto +25.046222 373.99045 25.558917 372.93771 26.24707 371.86218 curveto +27.34082 371.86218 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +29.979492 372.27917 moveto +35.400391 372.27917 lineto +35.400391 373.44128 lineto +31.244141 373.44128 lineto +31.244141 375.94324 lineto +31.444658 375.87488 31.645179 375.82475 31.845703 375.79285 curveto +32.04622 375.7564 32.246741 375.73817 32.447266 375.73816 curveto +33.586583 375.73817 34.488926 376.05034 35.154297 376.67468 curveto +35.819654 377.29904 36.152336 378.14441 36.152344 379.21082 curveto +36.152336 380.30912 35.81054 381.16362 35.126953 381.77429 curveto +34.443353 382.38041 33.479487 382.68347 32.235352 382.68347 curveto +31.806963 382.68347 31.369463 382.64701 30.922852 382.5741 curveto +30.480792 382.50118 30.022785 382.39181 29.548828 382.24597 curveto +29.548828 380.85828 lineto +29.958983 381.08159 30.382811 381.24793 30.820312 381.3573 curveto +31.25781 381.46668 31.720374 381.52136 32.208008 381.52136 curveto +32.996415 381.52136 33.620763 381.31401 34.081055 380.89929 curveto +34.541335 380.48458 34.771478 379.92176 34.771484 379.21082 curveto +34.771478 378.49988 34.541335 377.93706 34.081055 377.52234 curveto +33.620763 377.10763 32.996415 376.90027 32.208008 376.90027 curveto +31.838864 376.90027 31.469724 376.94129 31.100586 377.02332 curveto +30.736 377.10535 30.362303 377.23296 29.979492 377.40613 curveto +29.979492 372.27917 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +39.023438 380.7489 moveto +40.46582 380.7489 lineto +40.46582 381.92468 lineto +39.344727 384.11218 lineto +38.462891 384.11218 lineto +39.023438 381.92468 lineto +39.023438 380.7489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +43.350586 372.27917 moveto +48.771484 372.27917 lineto +48.771484 373.44128 lineto +44.615234 373.44128 lineto +44.615234 375.94324 lineto +44.815752 375.87488 45.016273 375.82475 45.216797 375.79285 curveto +45.417314 375.7564 45.617835 375.73817 45.818359 375.73816 curveto +46.957677 375.73817 47.86002 376.05034 48.525391 376.67468 curveto +49.190748 377.29904 49.52343 378.14441 49.523438 379.21082 curveto +49.52343 380.30912 49.181633 381.16362 48.498047 381.77429 curveto +47.814447 382.38041 46.850581 382.68347 45.606445 382.68347 curveto +45.178057 382.68347 44.740557 382.64701 44.293945 382.5741 curveto +43.851886 382.50118 43.393879 382.39181 42.919922 382.24597 curveto +42.919922 380.85828 lineto +43.330077 381.08159 43.753904 381.24793 44.191406 381.3573 curveto +44.628903 381.46668 45.091468 381.52136 45.579102 381.52136 curveto +46.367508 381.52136 46.991857 381.31401 47.452148 380.89929 curveto +47.912429 380.48458 48.142572 379.92176 48.142578 379.21082 curveto +48.142572 378.49988 47.912429 377.93706 47.452148 377.52234 curveto +46.991857 377.10763 46.367508 376.90027 45.579102 376.90027 curveto +45.209958 376.90027 44.840817 376.94129 44.47168 377.02332 curveto +44.107094 377.10535 43.733397 377.23296 43.350586 377.40613 curveto +43.350586 372.27917 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +52.394531 380.7489 moveto +53.836914 380.7489 lineto +53.836914 381.92468 lineto +52.71582 384.11218 lineto +51.833984 384.11218 lineto +52.394531 381.92468 lineto +52.394531 380.7489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +59.49707 375.71082 moveto +58.822588 375.71082 58.289385 375.97514 57.897461 376.50378 curveto +57.505532 377.02788 57.309568 377.74793 57.30957 378.66394 curveto +57.309568 379.57996 57.503253 380.30229 57.890625 380.83093 curveto +58.282549 381.35502 58.81803 381.61707 59.49707 381.61707 curveto +60.166987 381.61707 60.697911 381.35274 61.089844 380.8241 curveto +61.481765 380.29545 61.677728 379.5754 61.677734 378.66394 curveto +61.677728 377.75704 61.481765 377.03927 61.089844 376.51062 curveto +60.697911 375.97742 60.166987 375.71082 59.49707 375.71082 curveto +59.49707 374.64441 moveto +60.590815 374.64442 61.449864 374.99989 62.074219 375.71082 curveto +62.69856 376.42176 63.010734 377.40613 63.010742 378.66394 curveto +63.010734 379.9172 62.69856 380.90157 62.074219 381.61707 curveto +61.449864 382.328 60.590815 382.68347 59.49707 382.68347 curveto +58.39876 382.68347 57.537433 382.328 56.913086 381.61707 curveto +56.293293 380.90157 55.983398 379.9172 55.983398 378.66394 curveto +55.983398 377.40613 56.293293 376.42176 56.913086 375.71082 curveto +57.537433 374.99989 58.39876 374.64442 59.49707 374.64441 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +64.890625 371.86218 moveto +65.984375 371.86218 lineto +66.667966 372.93771 67.178382 373.99045 67.515625 375.02039 curveto +67.857418 376.05034 68.028316 377.07345 68.02832 378.08972 curveto +68.028316 379.11056 67.857418 380.13823 67.515625 381.17273 curveto +67.178382 382.20723 66.667966 383.25997 65.984375 384.33093 curveto +64.890625 384.33093 lineto +65.496743 383.28731 65.947914 382.25053 66.244141 381.22058 curveto +66.544919 380.18608 66.69531 379.14246 66.695312 378.08972 curveto +66.69531 377.03699 66.544919 375.99793 66.244141 374.97253 curveto +65.947914 373.94715 65.496743 372.91037 64.890625 371.86218 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +75.84082 370.86218 moveto +75.230139 371.91037 74.776689 372.94715 74.480469 373.97253 curveto +74.184242 374.99793 74.03613 376.03699 74.036133 377.08972 curveto +74.03613 378.14246 74.184242 379.18608 74.480469 380.22058 curveto +74.781247 381.25053 75.234697 382.28731 75.84082 383.33093 curveto +74.74707 383.33093 lineto +74.063474 382.25997 73.550779 381.20723 73.208984 380.17273 curveto +72.871743 379.13823 72.703124 378.11056 72.703125 377.08972 curveto +72.703124 376.07345 72.871743 375.05034 73.208984 374.02039 curveto +73.546222 372.99045 74.058917 371.93771 74.74707 370.86218 curveto +75.84082 370.86218 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +81.589844 375.83191 moveto +80.970048 375.83191 80.477861 376.04383 80.113281 376.46765 curveto +79.753252 376.89148 79.57324 377.47254 79.573242 378.21082 curveto +79.57324 378.94454 79.753252 379.5256 80.113281 379.95398 curveto +80.477861 380.37781 80.970048 380.58972 81.589844 380.58972 curveto +82.20963 380.58972 82.699539 380.37781 83.05957 379.95398 curveto +83.424147 379.5256 83.606439 378.94454 83.606445 378.21082 curveto +83.606439 377.47254 83.424147 376.89148 83.05957 376.46765 curveto +82.699539 376.04383 82.20963 375.83191 81.589844 375.83191 curveto +84.331055 371.50476 moveto +84.331055 372.76257 lineto +83.984694 372.59852 83.633782 372.47319 83.27832 372.3866 curveto +82.927403 372.30002 82.57877 372.25672 82.232422 372.25671 curveto +81.320959 372.25672 80.623694 372.56434 80.140625 373.17957 curveto +79.662107 373.79481 79.388669 374.72449 79.320312 375.96863 curveto +79.58919 375.57215 79.926429 375.26909 80.332031 375.05945 curveto +80.737626 374.84526 81.184241 374.73817 81.671875 374.73816 curveto +82.69726 374.73817 83.506178 375.05034 84.098633 375.67468 curveto +84.69563 376.29448 84.994133 377.13986 84.994141 378.21082 curveto +84.994133 379.25899 84.684237 380.09981 84.064453 380.73328 curveto +83.444655 381.36674 82.619786 381.68347 81.589844 381.68347 curveto +80.409502 381.68347 79.507159 381.2323 78.882812 380.32996 curveto +78.258462 379.42306 77.946288 378.11056 77.946289 376.39246 curveto +77.946288 374.77918 78.3291 373.49403 79.094727 372.53699 curveto +79.860349 371.57541 80.888017 371.09461 82.177734 371.0946 curveto +82.524083 371.09461 82.872715 371.12879 83.223633 371.19714 curveto +83.579095 371.26551 83.948235 371.36805 84.331055 371.50476 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +87.523438 379.7489 moveto +88.96582 379.7489 lineto +88.96582 380.92468 lineto +87.844727 383.11218 lineto +86.962891 383.11218 lineto +87.523438 380.92468 lineto +87.523438 379.7489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +94.960938 375.83191 moveto +94.341142 375.83191 93.848955 376.04383 93.484375 376.46765 curveto +93.124346 376.89148 92.944333 377.47254 92.944336 378.21082 curveto +92.944333 378.94454 93.124346 379.5256 93.484375 379.95398 curveto +93.848955 380.37781 94.341142 380.58972 94.960938 380.58972 curveto +95.580724 380.58972 96.070632 380.37781 96.430664 379.95398 curveto +96.795241 379.5256 96.977532 378.94454 96.977539 378.21082 curveto +96.977532 377.47254 96.795241 376.89148 96.430664 376.46765 curveto +96.070632 376.04383 95.580724 375.83191 94.960938 375.83191 curveto +97.702148 371.50476 moveto +97.702148 372.76257 lineto +97.355787 372.59852 97.004876 372.47319 96.649414 372.3866 curveto +96.298497 372.30002 95.949864 372.25672 95.603516 372.25671 curveto +94.692053 372.25672 93.994788 372.56434 93.511719 373.17957 curveto +93.0332 373.79481 92.759763 374.72449 92.691406 375.96863 curveto +92.960284 375.57215 93.297523 375.26909 93.703125 375.05945 curveto +94.10872 374.84526 94.555334 374.73817 95.042969 374.73816 curveto +96.068354 374.73817 96.877272 375.05034 97.469727 375.67468 curveto +98.066724 376.29448 98.365226 377.13986 98.365234 378.21082 curveto +98.365226 379.25899 98.055331 380.09981 97.435547 380.73328 curveto +96.815749 381.36674 95.99088 381.68347 94.960938 381.68347 curveto +93.780596 381.68347 92.878253 381.2323 92.253906 380.32996 curveto +91.629556 379.42306 91.317382 378.11056 91.317383 376.39246 curveto +91.317382 374.77918 91.700194 373.49403 92.46582 372.53699 curveto +93.231442 371.57541 94.259111 371.09461 95.548828 371.0946 curveto +95.895177 371.09461 96.243809 371.12879 96.594727 371.19714 curveto +96.950189 371.26551 97.319329 371.36805 97.702148 371.50476 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +100.89453 379.7489 moveto +102.33691 379.7489 lineto +102.33691 380.92468 lineto +101.21582 383.11218 lineto +100.33398 383.11218 lineto +100.89453 380.92468 lineto +100.89453 379.7489 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +110.06836 377.56824 moveto +110.06835 376.65678 109.87923 375.9504 109.50098 375.4491 curveto +109.12727 374.9478 108.60091 374.69715 107.92188 374.69714 curveto +107.24739 374.69715 106.72103 374.9478 106.34277 375.4491 curveto +105.96907 375.9504 105.78222 376.65678 105.78223 377.56824 curveto +105.78222 378.47514 105.96907 379.17924 106.34277 379.68054 curveto +106.72103 380.18185 107.24739 380.4325 107.92188 380.4325 curveto +108.60091 380.4325 109.12727 380.18185 109.50098 379.68054 curveto +109.87923 379.17924 110.06835 378.47514 110.06836 377.56824 curveto +111.32617 380.53503 moveto +111.32616 381.83842 111.03678 382.80684 110.45801 383.44031 curveto +109.87923 384.07833 108.99283 384.39734 107.79883 384.39734 curveto +107.35677 384.39734 106.93978 384.36316 106.54785 384.2948 curveto +106.15592 384.23099 105.77539 384.13073 105.40625 383.99402 curveto +105.40625 382.77039 lineto +105.77539 382.97091 106.13997 383.11902 106.5 383.21472 curveto +106.86002 383.31042 107.22688 383.35827 107.60059 383.35828 curveto +108.42545 383.35827 109.04296 383.1418 109.45312 382.70886 curveto +109.86328 382.28048 110.06835 381.63106 110.06836 380.76062 curveto +110.06836 380.13855 lineto +109.80859 380.58972 109.47591 380.92696 109.07031 381.15027 curveto +108.66471 381.37358 108.17936 381.48523 107.61426 381.48523 curveto +106.67545 381.48523 105.91894 381.12748 105.34473 380.41199 curveto +104.77051 379.69649 104.4834 378.74858 104.4834 377.56824 curveto +104.4834 376.38335 104.77051 375.43315 105.34473 374.71765 curveto +105.91894 374.00216 106.67545 373.64442 107.61426 373.64441 curveto +108.17936 373.64442 108.66471 373.75607 109.07031 373.97937 curveto +109.47591 374.20268 109.80859 374.53992 110.06836 374.99109 curveto +110.06836 373.82898 lineto +111.32617 373.82898 lineto +111.32617 380.53503 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +113.71875 370.86218 moveto +114.8125 370.86218 lineto +115.49609 371.93771 116.00651 372.99045 116.34375 374.02039 curveto +116.68554 375.05034 116.85644 376.07345 116.85645 377.08972 curveto +116.85644 378.11056 116.68554 379.13823 116.34375 380.17273 curveto +116.00651 381.20723 115.49609 382.25997 114.8125 383.33093 curveto +113.71875 383.33093 lineto +114.32487 382.28731 114.77604 381.25053 115.07227 380.22058 curveto +115.37304 379.18608 115.52343 378.14246 115.52344 377.08972 curveto +115.52343 376.03699 115.37304 374.99793 115.07227 373.97253 curveto +114.77604 372.94715 114.32487 371.91037 113.71875 370.86218 curveto +fill +grestore +gsave [1.04082 0 0 0.999696 -220.2817 5.106745] concat +0 0 0 setrgbcolor +[] 0 setdash +1.5 setlinewidth +0 setlinejoin +0 setlinecap +newpath +349.5 351.36218 moveto +349.5 378.68618 319.484 400.86218 282.5 400.86218 curveto +245.516 400.86218 215.5 378.68618 215.5 351.36218 curveto +215.5 324.03818 245.516 301.86218 282.5 301.86218 curveto +319.484 301.86218 349.5 324.03818 349.5 351.36218 curveto +closepath +stroke +grestore +gsave +0 0 0 setrgbcolor +newpath +66.874023 311.95789 moveto +68.254883 311.95789 lineto +68.254883 322.16394 lineto +66.874023 322.16394 lineto +66.874023 311.95789 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +75.829102 314.73328 moveto +75.829102 315.92273 lineto +75.473627 315.74044 75.104487 315.60373 74.72168 315.51257 curveto +74.338862 315.42143 73.942378 315.37586 73.532227 315.37585 curveto +72.907874 315.37586 72.438474 315.47156 72.124023 315.66296 curveto +71.814125 315.85438 71.659178 316.14149 71.65918 316.52429 curveto +71.659178 316.81596 71.770831 317.04611 71.994141 317.21472 curveto +72.217445 317.37879 72.666338 317.53602 73.34082 317.6864 curveto +73.771484 317.7821 lineto +74.664709 317.97351 75.298171 318.24467 75.671875 318.59558 curveto +76.050124 318.94194 76.239251 319.42729 76.239258 320.05164 curveto +76.239251 320.76257 75.956699 321.3254 75.391602 321.74011 curveto +74.831049 322.15483 74.058589 322.36218 73.074219 322.36218 curveto +72.664059 322.36218 72.235674 322.32117 71.789062 322.23914 curveto +71.347003 322.16166 70.879882 322.04317 70.387695 321.88367 curveto +70.387695 320.58484 lineto +70.852538 320.82638 71.310545 321.00867 71.761719 321.13171 curveto +72.212888 321.2502 72.659502 321.30945 73.101562 321.30945 curveto +73.694006 321.30945 74.149735 321.20919 74.46875 321.00867 curveto +74.787755 320.80359 74.94726 320.51648 74.947266 320.14734 curveto +74.94726 319.80554 74.831049 319.5435 74.598633 319.36121 curveto +74.370763 319.17892 73.867183 319.00346 73.087891 318.83484 curveto +72.650391 318.7323 lineto +71.871092 318.56824 71.308267 318.31759 70.961914 317.98035 curveto +70.615559 317.63855 70.442382 317.17143 70.442383 316.57898 curveto +70.442382 315.85893 70.69759 315.30294 71.208008 314.91101 curveto +71.718422 314.51909 72.443031 314.32313 73.381836 314.32312 curveto +73.846675 314.32313 74.284175 314.35731 74.694336 314.42566 curveto +75.104487 314.49403 75.482742 314.59657 75.829102 314.73328 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +6.3125 439.27167 moveto +5.7018192 440.31985 5.2483691 441.35664 4.9521484 442.38202 curveto +4.6559218 443.40742 4.50781 444.44648 4.5078125 445.49921 curveto +4.50781 446.55194 4.6559218 447.59556 4.9521484 448.63007 curveto +5.2529264 449.66001 5.7063765 450.6968 6.3125 451.74042 curveto +5.21875 451.74042 lineto +4.5351537 450.66945 4.0224589 449.61672 3.6806641 448.58221 curveto +3.3434231 447.54771 3.1748035 446.52004 3.1748047 445.49921 curveto +3.1748035 444.48294 3.3434231 443.45982 3.6806641 442.42987 curveto +4.0179016 441.39993 4.5305964 440.3472 5.21875 439.27167 curveto +6.3125 439.27167 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +11.890625 440.59784 moveto +11.179684 440.59785 10.644203 440.94876 10.28418 441.65057 curveto +9.9287084 442.34785 9.7509743 443.3983 9.7509766 444.80194 curveto +9.7509743 446.20103 9.9287084 447.25149 10.28418 447.95331 curveto +10.644203 448.65057 11.179684 448.99921 11.890625 448.99921 curveto +12.606115 448.99921 13.141596 448.65057 13.49707 447.95331 curveto +13.85709 447.25149 14.037103 446.20103 14.037109 444.80194 curveto +14.037103 443.3983 13.85709 442.34785 13.49707 441.65057 curveto +13.141596 440.94876 12.606115 440.59785 11.890625 440.59784 curveto +11.890625 439.50409 moveto +13.0345 439.5041 13.90722 439.95755 14.508789 440.86444 curveto +15.114901 441.76679 15.417961 443.07929 15.417969 444.80194 curveto +15.417961 446.52004 15.114901 447.83254 14.508789 448.73944 curveto +13.90722 449.64178 13.0345 450.09296 11.890625 450.09296 curveto +10.746741 450.09296 9.8717424 449.64178 9.265625 448.73944 curveto +8.6640613 447.83254 8.3632803 446.52004 8.3632812 444.80194 curveto +8.3632803 443.07929 8.6640613 441.76679 9.265625 440.86444 curveto +9.8717424 439.95755 10.746741 439.5041 11.890625 439.50409 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +17.995117 448.15839 moveto +19.4375 448.15839 lineto +19.4375 449.33417 lineto +18.316406 451.52167 lineto +17.43457 451.52167 lineto +17.995117 449.33417 lineto +17.995117 448.15839 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +25.261719 440.59784 moveto +24.550778 440.59785 24.015296 440.94876 23.655273 441.65057 curveto +23.299802 442.34785 23.122068 443.3983 23.12207 444.80194 curveto +23.122068 446.20103 23.299802 447.25149 23.655273 447.95331 curveto +24.015296 448.65057 24.550778 448.99921 25.261719 448.99921 curveto +25.977208 448.99921 26.51269 448.65057 26.868164 447.95331 curveto +27.228184 447.25149 27.408197 446.20103 27.408203 444.80194 curveto +27.408197 443.3983 27.228184 442.34785 26.868164 441.65057 curveto +26.51269 440.94876 25.977208 440.59785 25.261719 440.59784 curveto +25.261719 439.50409 moveto +26.405593 439.5041 27.278314 439.95755 27.879883 440.86444 curveto +28.485995 441.76679 28.789055 443.07929 28.789062 444.80194 curveto +28.789055 446.52004 28.485995 447.83254 27.879883 448.73944 curveto +27.278314 449.64178 26.405593 450.09296 25.261719 450.09296 curveto +24.117835 450.09296 23.242836 449.64178 22.636719 448.73944 curveto +22.035155 447.83254 21.734374 446.52004 21.734375 444.80194 curveto +21.734374 443.07929 22.035155 441.76679 22.636719 440.86444 curveto +23.242836 439.95755 24.117835 439.5041 25.261719 439.50409 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +31.366211 448.15839 moveto +32.808594 448.15839 lineto +32.808594 449.33417 lineto +31.6875 451.52167 lineto +30.805664 451.52167 lineto +31.366211 449.33417 lineto +31.366211 448.15839 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +38.46875 443.1203 moveto +37.794267 443.12031 37.261065 443.38463 36.869141 443.91327 curveto +36.477211 444.43736 36.281248 445.15741 36.28125 446.07343 curveto +36.281248 446.98944 36.474933 447.71177 36.862305 448.24042 curveto +37.254229 448.76451 37.78971 449.02655 38.46875 449.02655 curveto +39.138667 449.02655 39.669591 448.76223 40.061523 448.23358 curveto +40.453444 447.70494 40.649408 446.98489 40.649414 446.07343 curveto +40.649408 445.16653 40.453444 444.44876 40.061523 443.9201 curveto +39.669591 443.38691 39.138667 443.12031 38.46875 443.1203 curveto +38.46875 442.05389 moveto +39.562495 442.0539 40.421543 442.40937 41.045898 443.1203 curveto +41.67024 443.83124 41.982414 444.81562 41.982422 446.07343 curveto +41.982414 447.32668 41.67024 448.31106 41.045898 449.02655 curveto +40.421543 449.73749 39.562495 450.09296 38.46875 450.09296 curveto +37.37044 450.09296 36.509112 449.73749 35.884766 449.02655 curveto +35.264973 448.31106 34.955077 447.32668 34.955078 446.07343 curveto +34.955077 444.81562 35.264973 443.83124 35.884766 443.1203 curveto +36.509112 442.40937 37.37044 442.0539 38.46875 442.05389 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +43.862305 439.27167 moveto +44.956055 439.27167 lineto +45.639646 440.3472 46.150062 441.39993 46.487305 442.42987 curveto +46.829097 443.45982 46.999996 444.48294 47 445.49921 curveto +46.999996 446.52004 46.829097 447.54771 46.487305 448.58221 curveto +46.150062 449.61672 45.639646 450.66945 44.956055 451.74042 curveto +43.862305 451.74042 lineto +44.468423 450.6968 44.919594 449.66001 45.21582 448.63007 curveto +45.516599 447.59556 45.666989 446.55194 45.666992 445.49921 curveto +45.666989 444.44648 45.516599 443.40742 45.21582 442.38202 curveto +44.919594 441.35664 44.468423 440.31985 43.862305 439.27167 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +51.951172 439.27167 moveto +51.340491 440.31985 50.887041 441.35664 50.59082 442.38202 curveto +50.294594 443.40742 50.146482 444.44648 50.146484 445.49921 curveto +50.146482 446.55194 50.294594 447.59556 50.59082 448.63007 curveto +50.891598 449.66001 51.345048 450.6968 51.951172 451.74042 curveto +50.857422 451.74042 lineto +50.173826 450.66945 49.661131 449.61672 49.319336 448.58221 curveto +48.982095 447.54771 48.813475 446.52004 48.813477 445.49921 curveto +48.813475 444.48294 48.982095 443.45982 49.319336 442.42987 curveto +49.656573 441.39993 50.169268 440.3472 50.857422 439.27167 curveto +51.951172 439.27167 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +54.81543 448.7326 moveto +57.071289 448.7326 lineto +57.071289 440.94647 lineto +54.617188 441.43866 lineto +54.617188 440.18085 lineto +57.057617 439.68866 lineto +58.438477 439.68866 lineto +58.438477 448.7326 lineto +60.694336 448.7326 lineto +60.694336 449.89471 lineto +54.81543 449.89471 lineto +54.81543 448.7326 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +63.633789 448.15839 moveto +65.076172 448.15839 lineto +65.076172 449.33417 lineto +63.955078 451.52167 lineto +63.073242 451.52167 lineto +63.633789 449.33417 lineto +63.633789 448.15839 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +68.186523 448.7326 moveto +70.442383 448.7326 lineto +70.442383 440.94647 lineto +67.988281 441.43866 lineto +67.988281 440.18085 lineto +70.428711 439.68866 lineto +71.80957 439.68866 lineto +71.80957 448.7326 lineto +74.06543 448.7326 lineto +74.06543 449.89471 lineto +68.186523 449.89471 lineto +68.186523 448.7326 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +77.004883 448.15839 moveto +78.447266 448.15839 lineto +78.447266 449.33417 lineto +77.326172 451.52167 lineto +76.444336 451.52167 lineto +77.004883 449.33417 lineto +77.004883 448.15839 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +84.107422 443.1203 moveto +83.432939 443.12031 82.899737 443.38463 82.507812 443.91327 curveto +82.115883 444.43736 81.91992 445.15741 81.919922 446.07343 curveto +81.91992 446.98944 82.113604 447.71177 82.500977 448.24042 curveto +82.892901 448.76451 83.428382 449.02655 84.107422 449.02655 curveto +84.777339 449.02655 85.308263 448.76223 85.700195 448.23358 curveto +86.092116 447.70494 86.288079 446.98489 86.288086 446.07343 curveto +86.288079 445.16653 86.092116 444.44876 85.700195 443.9201 curveto +85.308263 443.38691 84.777339 443.12031 84.107422 443.1203 curveto +84.107422 442.05389 moveto +85.201166 442.0539 86.060215 442.40937 86.68457 443.1203 curveto +87.308912 443.83124 87.621086 444.81562 87.621094 446.07343 curveto +87.621086 447.32668 87.308912 448.31106 86.68457 449.02655 curveto +86.060215 449.73749 85.201166 450.09296 84.107422 450.09296 curveto +83.009111 450.09296 82.147784 449.73749 81.523438 449.02655 curveto +80.903645 448.31106 80.593749 447.32668 80.59375 446.07343 curveto +80.593749 444.81562 80.903645 443.83124 81.523438 443.1203 curveto +82.147784 442.40937 83.009111 442.0539 84.107422 442.05389 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +89.500977 439.27167 moveto +90.594727 439.27167 lineto +91.278317 440.3472 91.788734 441.39993 92.125977 442.42987 curveto +92.467769 443.45982 92.638668 444.48294 92.638672 445.49921 curveto +92.638668 446.52004 92.467769 447.54771 92.125977 448.58221 curveto +91.788734 449.61672 91.278317 450.66945 90.594727 451.74042 curveto +89.500977 451.74042 lineto +90.107095 450.6968 90.558266 449.66001 90.854492 448.63007 curveto +91.155271 447.59556 91.305661 446.55194 91.305664 445.49921 curveto +91.305661 444.44648 91.155271 443.40742 90.854492 442.38202 curveto +90.558266 441.35664 90.107095 440.31985 89.500977 439.27167 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +51.951172 458.80682 moveto +51.340491 459.85501 50.887041 460.89179 50.59082 461.91718 curveto +50.294594 462.94257 50.146482 463.98163 50.146484 465.03436 curveto +50.146482 466.0871 50.294594 467.13072 50.59082 468.16522 curveto +50.891598 469.19517 51.345048 470.23195 51.951172 471.27557 curveto +50.857422 471.27557 lineto +50.173826 470.20461 49.661131 469.15188 49.319336 468.11737 curveto +48.982095 467.08287 48.813475 466.0552 48.813477 465.03436 curveto +48.813475 464.01809 48.982095 462.99498 49.319336 461.96503 curveto +49.656573 460.93509 50.169268 459.88235 50.857422 458.80682 curveto +51.951172 458.80682 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +54.589844 459.22382 moveto +60.010742 459.22382 lineto +60.010742 460.38593 lineto +55.854492 460.38593 lineto +55.854492 462.88788 lineto +56.05501 462.81953 56.255531 462.7694 56.456055 462.73749 curveto +56.656572 462.70104 56.857093 462.68281 57.057617 462.6828 curveto +58.196935 462.68281 59.099278 462.99498 59.764648 463.61932 curveto +60.430006 464.24368 60.762688 465.08905 60.762695 466.15546 curveto +60.762688 467.25377 60.420891 468.10826 59.737305 468.71893 curveto +59.053705 469.32505 58.089839 469.62811 56.845703 469.62811 curveto +56.417314 469.62811 55.979815 469.59165 55.533203 469.51874 curveto +55.091144 469.44582 54.633136 469.33645 54.15918 469.19061 curveto +54.15918 467.80292 lineto +54.569334 468.02623 54.993162 468.19257 55.430664 468.30194 curveto +55.868161 468.41132 56.330726 468.466 56.818359 468.466 curveto +57.606766 468.466 58.231115 468.25865 58.691406 467.84393 curveto +59.151687 467.42922 59.38183 466.8664 59.381836 466.15546 curveto +59.38183 465.44452 59.151687 464.8817 58.691406 464.46698 curveto +58.231115 464.05227 57.606766 463.84492 56.818359 463.84491 curveto +56.449215 463.84492 56.080075 463.88593 55.710938 463.96796 curveto +55.346352 464.04999 54.972654 464.1776 54.589844 464.35077 curveto +54.589844 459.22382 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +63.633789 467.69354 moveto +65.076172 467.69354 lineto +65.076172 468.86932 lineto +63.955078 471.05682 lineto +63.073242 471.05682 lineto +63.633789 468.86932 lineto +63.633789 467.69354 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +71.741211 460.42694 moveto +68.254883 465.87518 lineto +71.741211 465.87518 lineto +71.741211 460.42694 lineto +71.378906 459.22382 moveto +73.115234 459.22382 lineto +73.115234 465.87518 lineto +74.571289 465.87518 lineto +74.571289 467.02362 lineto +73.115234 467.02362 lineto +73.115234 469.42987 lineto +71.741211 469.42987 lineto +71.741211 467.02362 lineto +67.133789 467.02362 lineto +67.133789 465.69061 lineto +71.378906 459.22382 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +77.004883 467.69354 moveto +78.447266 467.69354 lineto +78.447266 468.86932 lineto +77.326172 471.05682 lineto +76.444336 471.05682 lineto +77.004883 468.86932 lineto +77.004883 467.69354 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +86.178711 465.51288 moveto +86.178705 464.60142 85.989577 463.89505 85.611328 463.39374 curveto +85.237625 462.89244 84.711258 462.64179 84.032227 462.64178 curveto +83.357744 462.64179 82.831377 462.89244 82.453125 463.39374 curveto +82.079425 463.89505 81.892576 464.60142 81.892578 465.51288 curveto +81.892576 466.41978 82.079425 467.12388 82.453125 467.62518 curveto +82.831377 468.12649 83.357744 468.37714 84.032227 468.37714 curveto +84.711258 468.37714 85.237625 468.12649 85.611328 467.62518 curveto +85.989577 467.12388 86.178705 466.41978 86.178711 465.51288 curveto +87.436523 468.47968 moveto +87.436516 469.78306 87.147128 470.75148 86.568359 471.38495 curveto +85.989577 472.02297 85.103185 472.34198 83.90918 472.34198 curveto +83.467119 472.34198 83.050127 472.3078 82.658203 472.23944 curveto +82.266274 472.17564 81.88574 472.07538 81.516602 471.93866 curveto +81.516602 470.71503 lineto +81.88574 470.91555 82.250323 471.06366 82.610352 471.15936 curveto +82.970374 471.25506 83.337236 471.30292 83.710938 471.30292 curveto +84.535803 471.30292 85.153315 471.08644 85.563477 470.6535 curveto +85.973627 470.22512 86.178705 469.5757 86.178711 468.70526 curveto +86.178711 468.08319 lineto +85.918939 468.53436 85.586257 468.8716 85.180664 469.09491 curveto +84.77506 469.31822 84.289709 469.42987 83.724609 469.42987 curveto +82.785804 469.42987 82.029295 469.07212 81.455078 468.35663 curveto +80.880858 467.64114 80.593749 466.69322 80.59375 465.51288 curveto +80.593749 464.32799 80.880858 463.37779 81.455078 462.66229 curveto +82.029295 461.94681 82.785804 461.58906 83.724609 461.58905 curveto +84.289709 461.58906 84.77506 461.70071 85.180664 461.92401 curveto +85.586257 462.14733 85.918939 462.48457 86.178711 462.93573 curveto +86.178711 461.77362 lineto +87.436523 461.77362 lineto +87.436523 468.47968 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +89.829102 458.80682 moveto +90.922852 458.80682 lineto +91.606442 459.88235 92.116859 460.93509 92.454102 461.96503 curveto +92.795894 462.99498 92.966793 464.01809 92.966797 465.03436 curveto +92.966793 466.0552 92.795894 467.08287 92.454102 468.11737 curveto +92.116859 469.15188 91.606442 470.20461 90.922852 471.27557 curveto +89.829102 471.27557 lineto +90.43522 470.23195 90.886391 469.19517 91.182617 468.16522 curveto +91.483396 467.13072 91.633786 466.0871 91.633789 465.03436 curveto +91.633786 463.98163 91.483396 462.94257 91.182617 461.91718 curveto +90.886391 460.89179 90.43522 459.85501 89.829102 458.80682 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +97.405273 439.73846 moveto +96.794593 440.78665 96.341143 441.82343 96.044922 442.84882 curveto +95.748695 443.87421 95.600583 444.91327 95.600586 445.966 curveto +95.600583 447.01874 95.748695 448.06236 96.044922 449.09686 curveto +96.3457 450.12681 96.79915 451.16359 97.405273 452.20721 curveto +96.311523 452.20721 lineto +95.627927 451.13625 95.115232 450.08352 94.773438 449.04901 curveto +94.436197 448.01451 94.267577 446.98684 94.267578 445.966 curveto +94.267577 444.94973 94.436197 443.92662 94.773438 442.89667 curveto +95.110675 441.86673 95.62337 440.81399 96.311523 439.73846 curveto +97.405273 439.73846 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +101.21973 449.1994 moveto +106.03906 449.1994 lineto +106.03906 450.36151 lineto +99.558594 450.36151 lineto +99.558594 449.1994 lineto +100.08268 448.65709 100.7959 447.9302 101.69824 447.01874 curveto +102.60514 446.10273 103.1748 445.51256 103.40723 445.24823 curveto +103.84928 444.75149 104.1569 444.33222 104.33008 443.99042 curveto +104.50781 443.64407 104.59667 443.30455 104.59668 442.97186 curveto +104.59667 442.42955 104.40527 441.9875 104.02246 441.64569 curveto +103.6442 441.3039 103.14973 441.133 102.53906 441.133 curveto +102.10612 441.133 101.64811 441.2082 101.16504 441.35858 curveto +100.68652 441.50898 100.17383 441.73685 99.626953 442.04218 curveto +99.626953 440.64764 lineto +100.18294 440.42435 100.70247 440.25573 101.18555 440.14178 curveto +101.66862 440.02786 102.11067 439.9709 102.51172 439.97089 curveto +103.56901 439.9709 104.4121 440.23522 105.04102 440.76385 curveto +105.66991 441.29251 105.98437 441.99889 105.98438 442.883 curveto +105.98437 443.30227 105.90462 443.70104 105.74512 444.07928 curveto +105.59016 444.45299 105.30533 444.89505 104.89062 445.40546 curveto +104.77669 445.53762 104.41438 445.92043 103.80371 446.55389 curveto +103.19303 447.1828 102.3317 448.06464 101.21973 449.1994 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +109.08789 448.62518 moveto +110.53027 448.62518 lineto +110.53027 449.80096 lineto +109.40918 451.98846 lineto +108.52734 451.98846 lineto +109.08789 449.80096 lineto +109.08789 448.62518 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +114.59082 449.1994 moveto +119.41016 449.1994 lineto +119.41016 450.36151 lineto +112.92969 450.36151 lineto +112.92969 449.1994 lineto +113.45377 448.65709 114.16699 447.9302 115.06934 447.01874 curveto +115.97623 446.10273 116.54589 445.51256 116.77832 445.24823 curveto +117.22037 444.75149 117.52799 444.33222 117.70117 443.99042 curveto +117.8789 443.64407 117.96777 443.30455 117.96777 442.97186 curveto +117.96777 442.42955 117.77636 441.9875 117.39355 441.64569 curveto +117.01529 441.3039 116.52083 441.133 115.91016 441.133 curveto +115.47721 441.133 115.0192 441.2082 114.53613 441.35858 curveto +114.05762 441.50898 113.54492 441.73685 112.99805 442.04218 curveto +112.99805 440.64764 lineto +113.55403 440.42435 114.07357 440.25573 114.55664 440.14178 curveto +115.03971 440.02786 115.48177 439.9709 115.88281 439.97089 curveto +116.9401 439.9709 117.7832 440.23522 118.41211 440.76385 curveto +119.04101 441.29251 119.35546 441.99889 119.35547 442.883 curveto +119.35546 443.30227 119.27571 443.70104 119.11621 444.07928 curveto +118.96126 444.45299 118.67643 444.89505 118.26172 445.40546 curveto +118.14778 445.53762 117.78548 445.92043 117.1748 446.55389 curveto +116.56412 447.1828 115.7028 448.06464 114.59082 449.1994 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +122.45898 448.62518 moveto +123.90137 448.62518 lineto +123.90137 449.80096 lineto +122.78027 451.98846 lineto +121.89844 451.98846 lineto +122.45898 449.80096 lineto +122.45898 448.62518 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +129.56152 443.5871 moveto +128.88704 443.5871 128.35384 443.85143 127.96191 444.38007 curveto +127.56998 444.90416 127.37402 445.62421 127.37402 446.54022 curveto +127.37402 447.45624 127.56771 448.17857 127.95508 448.70721 curveto +128.347 449.2313 128.88248 449.49335 129.56152 449.49335 curveto +130.23144 449.49335 130.76236 449.22903 131.1543 448.70038 curveto +131.54622 448.17173 131.74218 447.45168 131.74219 446.54022 curveto +131.74218 445.63333 131.54622 444.91555 131.1543 444.3869 curveto +130.76236 443.85371 130.23144 443.5871 129.56152 443.5871 curveto +129.56152 442.52069 moveto +130.65527 442.5207 131.51432 442.87617 132.13867 443.5871 curveto +132.76301 444.29804 133.07519 445.28241 133.0752 446.54022 curveto +133.07519 447.79348 132.76301 448.77785 132.13867 449.49335 curveto +131.51432 450.20428 130.65527 450.55975 129.56152 450.55975 curveto +128.46321 450.55975 127.60189 450.20428 126.97754 449.49335 curveto +126.35775 448.77785 126.04785 447.79348 126.04785 446.54022 curveto +126.04785 445.28241 126.35775 444.29804 126.97754 443.5871 curveto +127.60189 442.87617 128.46321 442.5207 129.56152 442.52069 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +134.95508 439.73846 moveto +136.04883 439.73846 lineto +136.73242 440.81399 137.24284 441.86673 137.58008 442.89667 curveto +137.92187 443.92662 138.09277 444.94973 138.09277 445.966 curveto +138.09277 446.98684 137.92187 448.01451 137.58008 449.04901 curveto +137.24284 450.08352 136.73242 451.13625 136.04883 452.20721 curveto +134.95508 452.20721 lineto +135.5612 451.16359 136.01237 450.12681 136.30859 449.09686 curveto +136.60937 448.06236 136.75976 447.01874 136.75977 445.966 curveto +136.75976 444.91327 136.60937 443.87421 136.30859 442.84882 curveto +136.01237 441.82343 135.5612 440.78665 134.95508 439.73846 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +5.1376953 458.27362 moveto +4.5270145 459.32181 4.0735644 460.35859 3.7773438 461.38397 curveto +3.4811171 462.40937 3.3330053 463.44843 3.3330078 464.50116 curveto +3.3330053 465.5539 3.4811171 466.59752 3.7773438 467.63202 curveto +4.0781217 468.66197 4.5315718 469.69875 5.1376953 470.74237 curveto +4.0439453 470.74237 lineto +3.360349 469.67141 2.8476542 468.61867 2.5058594 467.58417 curveto +2.1686184 466.54966 1.9999988 465.522 2 464.50116 curveto +1.9999988 463.48489 2.1686184 462.46178 2.5058594 461.43182 curveto +2.8430969 460.40188 3.3557917 459.34915 4.0439453 458.27362 curveto +5.1376953 458.27362 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +11.946289 463.39374 moveto +12.60709 463.53502 13.122063 463.82896 13.491211 464.27557 curveto +13.864901 464.72219 14.05175 465.27362 14.051758 465.92987 curveto +14.05175 466.93703 13.705396 467.71633 13.012695 468.26776 curveto +12.319981 468.81919 11.335607 469.09491 10.05957 469.09491 curveto +9.6311815 469.09491 9.1891247 469.05162 8.7333984 468.96503 curveto +8.2822245 468.883 7.8151026 468.75767 7.3320312 468.58905 curveto +7.3320312 467.25604 lineto +7.7148423 467.47935 8.1341127 467.64797 8.5898438 467.7619 curveto +9.0455701 467.87584 9.5218066 467.9328 10.018555 467.9328 curveto +10.884435 467.9328 11.542963 467.7619 11.994141 467.4201 curveto +12.449864 467.07831 12.677728 466.58157 12.677734 465.92987 curveto +12.677728 465.32831 12.465814 464.85891 12.041992 464.52167 curveto +11.622716 464.17988 11.037105 464.00898 10.285156 464.00897 curveto +9.0957031 464.00897 lineto +9.0957031 462.87421 lineto +10.339844 462.87421 lineto +11.018875 462.87421 11.538406 462.73977 11.898438 462.47089 curveto +12.258458 462.19746 12.43847 461.80553 12.438477 461.2951 curveto +12.43847 460.77102 12.251622 460.36998 11.87793 460.09198 curveto +11.508784 459.80944 10.97786 459.66816 10.285156 459.66815 curveto +9.9068974 459.66816 9.5012988 459.70918 9.0683594 459.7912 curveto +8.6354143 459.87324 8.1591778 460.00084 7.6396484 460.17401 curveto +7.6396484 458.94354 lineto +8.1637351 458.79772 8.6536434 458.68834 9.109375 458.61542 curveto +9.5696582 458.54251 10.0026 458.50605 10.408203 458.50604 curveto +11.456375 458.50605 12.285801 458.74531 12.896484 459.22382 curveto +13.507154 459.69778 13.812492 460.34036 13.8125 461.15155 curveto +13.812492 461.71666 13.650709 462.19518 13.327148 462.5871 curveto +13.003574 462.97447 12.543288 463.24335 11.946289 463.39374 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +16.820312 467.16034 moveto +18.262695 467.16034 lineto +18.262695 468.33612 lineto +17.141602 470.52362 lineto +16.259766 470.52362 lineto +16.820312 468.33612 lineto +16.820312 467.16034 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +25.317383 463.39374 moveto +25.978184 463.53502 26.493157 463.82896 26.862305 464.27557 curveto +27.235995 464.72219 27.422844 465.27362 27.422852 465.92987 curveto +27.422844 466.93703 27.07649 467.71633 26.383789 468.26776 curveto +25.691075 468.81919 24.706701 469.09491 23.430664 469.09491 curveto +23.002275 469.09491 22.560218 469.05162 22.104492 468.96503 curveto +21.653318 468.883 21.186196 468.75767 20.703125 468.58905 curveto +20.703125 467.25604 lineto +21.085936 467.47935 21.505206 467.64797 21.960938 467.7619 curveto +22.416664 467.87584 22.8929 467.9328 23.389648 467.9328 curveto +24.255529 467.9328 24.914057 467.7619 25.365234 467.4201 curveto +25.820957 467.07831 26.048822 466.58157 26.048828 465.92987 curveto +26.048822 465.32831 25.836908 464.85891 25.413086 464.52167 curveto +24.99381 464.17988 24.408198 464.00898 23.65625 464.00897 curveto +22.466797 464.00897 lineto +22.466797 462.87421 lineto +23.710938 462.87421 lineto +24.389969 462.87421 24.9095 462.73977 25.269531 462.47089 curveto +25.629551 462.19746 25.809564 461.80553 25.80957 461.2951 curveto +25.809564 460.77102 25.622715 460.36998 25.249023 460.09198 curveto +24.879878 459.80944 24.348954 459.66816 23.65625 459.66815 curveto +23.277991 459.66816 22.872393 459.70918 22.439453 459.7912 curveto +22.006508 459.87324 21.530272 460.00084 21.010742 460.17401 curveto +21.010742 458.94354 lineto +21.534829 458.79772 22.024737 458.68834 22.480469 458.61542 curveto +22.940752 458.54251 23.373694 458.50605 23.779297 458.50604 curveto +24.827469 458.50605 25.656895 458.74531 26.267578 459.22382 curveto +26.878248 459.69778 27.183586 460.34036 27.183594 461.15155 curveto +27.183586 461.71666 27.021803 462.19518 26.698242 462.5871 curveto +26.374668 462.97447 25.914382 463.24335 25.317383 463.39374 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +30.191406 467.16034 moveto +31.633789 467.16034 lineto +31.633789 468.33612 lineto +30.512695 470.52362 lineto +29.630859 470.52362 lineto +30.191406 468.33612 lineto +30.191406 467.16034 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +39.365234 464.97968 moveto +39.365228 464.06822 39.176101 463.36184 38.797852 462.86053 curveto +38.424148 462.35924 37.897782 462.10859 37.21875 462.10858 curveto +36.544267 462.10859 36.017901 462.35924 35.639648 462.86053 curveto +35.265948 463.36184 35.079099 464.06822 35.079102 464.97968 curveto +35.079099 465.88658 35.265948 466.59068 35.639648 467.09198 curveto +36.017901 467.59328 36.544267 467.84393 37.21875 467.84393 curveto +37.897782 467.84393 38.424148 467.59328 38.797852 467.09198 curveto +39.176101 466.59068 39.365228 465.88658 39.365234 464.97968 curveto +40.623047 467.94647 moveto +40.623039 469.24986 40.333652 470.21828 39.754883 470.85175 curveto +39.176101 471.48976 38.289708 471.80877 37.095703 471.80878 curveto +36.653642 471.80877 36.23665 471.77459 35.844727 471.70624 curveto +35.452797 471.64243 35.072264 471.54217 34.703125 471.40546 curveto +34.703125 470.18182 lineto +35.072264 470.38234 35.436847 470.53045 35.796875 470.62616 curveto +36.156898 470.72186 36.52376 470.76971 36.897461 470.76971 curveto +37.722326 470.76971 38.339838 470.55324 38.75 470.1203 curveto +39.16015 469.69191 39.365228 469.0425 39.365234 468.17206 curveto +39.365234 467.54999 lineto +39.105463 468.00116 38.772781 468.3384 38.367188 468.56171 curveto +37.961584 468.78501 37.476233 468.89667 36.911133 468.89667 curveto +35.972328 468.89667 35.215818 468.53892 34.641602 467.82343 curveto +34.067382 467.10793 33.780273 466.16002 33.780273 464.97968 curveto +33.780273 463.79478 34.067382 462.84459 34.641602 462.12909 curveto +35.215818 461.4136 35.972328 461.05586 36.911133 461.05585 curveto +37.476233 461.05586 37.961584 461.16751 38.367188 461.39081 curveto +38.772781 461.61412 39.105463 461.95136 39.365234 462.40253 curveto +39.365234 461.24042 lineto +40.623047 461.24042 lineto +40.623047 467.94647 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +43.015625 458.27362 moveto +44.109375 458.27362 lineto +44.792966 459.34915 45.303382 460.40188 45.640625 461.43182 curveto +45.982418 462.46178 46.153316 463.48489 46.15332 464.50116 curveto +46.153316 465.522 45.982418 466.54966 45.640625 467.58417 curveto +45.303382 468.61867 44.792966 469.67141 44.109375 470.74237 curveto +43.015625 470.74237 lineto +43.621743 469.69875 44.072914 468.66197 44.369141 467.63202 curveto +44.669919 466.59752 44.82031 465.5539 44.820312 464.50116 curveto +44.82031 463.44843 44.669919 462.40937 44.369141 461.38397 curveto +44.072914 460.35859 43.621743 459.32181 43.015625 458.27362 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +98.233398 458.80682 moveto +97.622718 459.85501 97.169268 460.89179 96.873047 461.91718 curveto +96.57682 462.94257 96.428708 463.98163 96.428711 465.03436 curveto +96.428708 466.0871 96.57682 467.13072 96.873047 468.16522 curveto +97.173825 469.19517 97.627275 470.23195 98.233398 471.27557 curveto +97.139648 471.27557 lineto +96.456052 470.20461 95.943357 469.15188 95.601562 468.11737 curveto +95.264322 467.08287 95.095702 466.0552 95.095703 465.03436 curveto +95.095702 464.01809 95.264322 462.99498 95.601562 461.96503 curveto +95.9388 460.93509 96.451495 459.88235 97.139648 458.80682 curveto +98.233398 458.80682 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +103.98242 463.77655 moveto +103.36263 463.77656 102.87044 463.98847 102.50586 464.41229 curveto +102.14583 464.83613 101.96582 465.41718 101.96582 466.15546 curveto +101.96582 466.88918 102.14583 467.47024 102.50586 467.89862 curveto +102.87044 468.32245 103.36263 468.53436 103.98242 468.53436 curveto +104.60221 468.53436 105.09212 468.32245 105.45215 467.89862 curveto +105.81673 467.47024 105.99902 466.88918 105.99902 466.15546 curveto +105.99902 465.41718 105.81673 464.83613 105.45215 464.41229 curveto +105.09212 463.98847 104.60221 463.77656 103.98242 463.77655 curveto +106.72363 459.4494 moveto +106.72363 460.70721 lineto +106.37727 460.54316 106.02636 460.41784 105.6709 460.33124 curveto +105.31998 460.24466 104.97135 460.20136 104.625 460.20135 curveto +103.71354 460.20136 103.01627 460.50898 102.5332 461.12421 curveto +102.05468 461.73945 101.78125 462.66914 101.71289 463.91327 curveto +101.98177 463.51679 102.31901 463.21373 102.72461 463.00409 curveto +103.1302 462.7899 103.57682 462.68281 104.06445 462.6828 curveto +105.08984 462.68281 105.89876 462.99498 106.49121 463.61932 curveto +107.08821 464.23912 107.38671 465.0845 107.38672 466.15546 curveto +107.38671 467.20364 107.07682 468.04446 106.45703 468.67792 curveto +105.83723 469.31138 105.01236 469.62811 103.98242 469.62811 curveto +102.80208 469.62811 101.89974 469.17694 101.27539 468.2746 curveto +100.65104 467.3677 100.33887 466.0552 100.33887 464.3371 curveto +100.33887 462.72382 100.72168 461.43867 101.4873 460.48163 curveto +102.25293 459.52005 103.2806 459.03926 104.57031 459.03925 curveto +104.91666 459.03926 105.26529 459.07344 105.61621 459.14178 curveto +105.97167 459.21015 106.34081 459.31269 106.72363 459.4494 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +109.91602 467.69354 moveto +111.3584 467.69354 lineto +111.3584 468.86932 lineto +110.2373 471.05682 lineto +109.35547 471.05682 lineto +109.91602 468.86932 lineto +109.91602 467.69354 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +114.24316 459.22382 moveto +119.66406 459.22382 lineto +119.66406 460.38593 lineto +115.50781 460.38593 lineto +115.50781 462.88788 lineto +115.70833 462.81953 115.90885 462.7694 116.10938 462.73749 curveto +116.30989 462.70104 116.51041 462.68281 116.71094 462.6828 curveto +117.85026 462.68281 118.7526 462.99498 119.41797 463.61932 curveto +120.08333 464.24368 120.41601 465.08905 120.41602 466.15546 curveto +120.41601 467.25377 120.07421 468.10826 119.39062 468.71893 curveto +118.70703 469.32505 117.74316 469.62811 116.49902 469.62811 curveto +116.07063 469.62811 115.63314 469.59165 115.18652 469.51874 curveto +114.74446 469.44582 114.28646 469.33645 113.8125 469.19061 curveto +113.8125 467.80292 lineto +114.22265 468.02623 114.64648 468.19257 115.08398 468.30194 curveto +115.52148 468.41132 115.98405 468.466 116.47168 468.466 curveto +117.26009 468.466 117.88443 468.25865 118.34473 467.84393 curveto +118.80501 467.42922 119.03515 466.8664 119.03516 466.15546 curveto +119.03515 465.44452 118.80501 464.8817 118.34473 464.46698 curveto +117.88443 464.05227 117.26009 463.84492 116.47168 463.84491 curveto +116.10254 463.84492 115.7334 463.88593 115.36426 463.96796 curveto +114.99967 464.04999 114.62597 464.1776 114.24316 464.35077 curveto +114.24316 459.22382 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +123.28711 467.69354 moveto +124.72949 467.69354 lineto +124.72949 468.86932 lineto +123.6084 471.05682 lineto +122.72656 471.05682 lineto +123.28711 468.86932 lineto +123.28711 467.69354 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +130.38965 462.65546 moveto +129.71517 462.65546 129.18196 462.91979 128.79004 463.44843 curveto +128.39811 463.97252 128.20215 464.69257 128.20215 465.60858 curveto +128.20215 466.5246 128.39583 467.24693 128.7832 467.77557 curveto +129.17513 468.29966 129.71061 468.56171 130.38965 468.56171 curveto +131.05957 468.56171 131.59049 468.29738 131.98242 467.76874 curveto +132.37434 467.24009 132.57031 466.52004 132.57031 465.60858 curveto +132.57031 464.70169 132.37434 463.98391 131.98242 463.45526 curveto +131.59049 462.92206 131.05957 462.65546 130.38965 462.65546 curveto +130.38965 461.58905 moveto +131.48339 461.58906 132.34244 461.94453 132.9668 462.65546 curveto +133.59114 463.3664 133.90331 464.35077 133.90332 465.60858 curveto +133.90331 466.86184 133.59114 467.84621 132.9668 468.56171 curveto +132.34244 469.27264 131.48339 469.62811 130.38965 469.62811 curveto +129.29134 469.62811 128.43001 469.27264 127.80566 468.56171 curveto +127.18587 467.84621 126.87598 466.86184 126.87598 465.60858 curveto +126.87598 464.35077 127.18587 463.3664 127.80566 462.65546 curveto +128.43001 461.94453 129.29134 461.58906 130.38965 461.58905 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +135.7832 458.80682 moveto +136.87695 458.80682 lineto +137.56054 459.88235 138.07096 460.93509 138.4082 461.96503 curveto +138.75 462.99498 138.92089 464.01809 138.9209 465.03436 curveto +138.92089 466.0552 138.75 467.08287 138.4082 468.11737 curveto +138.07096 469.15188 137.56054 470.20461 136.87695 471.27557 curveto +135.7832 471.27557 lineto +136.38932 470.23195 136.84049 469.19517 137.13672 468.16522 curveto +137.4375 467.13072 137.58789 466.0871 137.58789 465.03436 curveto +137.58789 463.98163 137.4375 462.94257 137.13672 461.91718 curveto +136.84049 460.89179 136.38932 459.85501 135.7832 458.80682 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +4.1689453 477.84198 moveto +3.5582645 478.89017 3.1048144 479.92695 2.8085938 480.95233 curveto +2.5123671 481.97773 2.3642553 483.01679 2.3642578 484.06952 curveto +2.3642553 485.12226 2.5123671 486.16588 2.8085938 487.20038 curveto +3.1093717 488.23033 3.5628218 489.26711 4.1689453 490.31073 curveto +3.0751953 490.31073 lineto +2.391599 489.23977 1.8789042 488.18703 1.5371094 487.15253 curveto +1.1998684 486.11802 1.0312488 485.09036 1.03125 484.06952 curveto +1.0312488 483.05325 1.1998684 482.03014 1.5371094 481.00018 curveto +1.8743469 479.97024 2.3870417 478.91751 3.0751953 477.84198 curveto +4.1689453 477.84198 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +6.4453125 478.25897 moveto +13.007812 478.25897 lineto +13.007812 478.84686 lineto +9.3027344 488.46503 lineto +7.8603516 488.46503 lineto +11.34668 479.42108 lineto +6.4453125 479.42108 lineto +6.4453125 478.25897 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +15.851562 486.7287 moveto +17.293945 486.7287 lineto +17.293945 487.90448 lineto +16.172852 490.09198 lineto +15.291016 490.09198 lineto +15.851562 487.90448 lineto +15.851562 486.7287 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +23.289062 482.81171 moveto +22.669267 482.81171 22.17708 483.02363 21.8125 483.44745 curveto +21.452471 483.87128 21.272458 484.45234 21.272461 485.19061 curveto +21.272458 485.92434 21.452471 486.50539 21.8125 486.93378 curveto +22.17708 487.35761 22.669267 487.56952 23.289062 487.56952 curveto +23.908849 487.56952 24.398757 487.35761 24.758789 486.93378 curveto +25.123366 486.50539 25.305657 485.92434 25.305664 485.19061 curveto +25.305657 484.45234 25.123366 483.87128 24.758789 483.44745 curveto +24.398757 483.02363 23.908849 482.81171 23.289062 482.81171 curveto +26.030273 478.48456 moveto +26.030273 479.74237 lineto +25.683912 479.57832 25.333001 479.45299 24.977539 479.36639 curveto +24.626622 479.27981 24.277989 479.23652 23.931641 479.23651 curveto +23.020178 479.23652 22.322913 479.54414 21.839844 480.15936 curveto +21.361325 480.7746 21.087888 481.70429 21.019531 482.94843 curveto +21.288409 482.55195 21.625648 482.24889 22.03125 482.03925 curveto +22.436845 481.82506 22.883459 481.71796 23.371094 481.71796 curveto +24.396479 481.71796 25.205397 482.03014 25.797852 482.65448 curveto +26.394849 483.27428 26.693351 484.11965 26.693359 485.19061 curveto +26.693351 486.23879 26.383456 487.07961 25.763672 487.71307 curveto +25.143874 488.34654 24.319005 488.66327 23.289062 488.66327 curveto +22.108721 488.66327 21.206378 488.2121 20.582031 487.30975 curveto +19.957681 486.40285 19.645507 485.09036 19.645508 483.37225 curveto +19.645507 481.75898 20.028319 480.47382 20.793945 479.51678 curveto +21.559567 478.55521 22.587236 478.07441 23.876953 478.0744 curveto +24.223302 478.07441 24.571934 478.10859 24.922852 478.17694 curveto +25.278314 478.24531 25.647454 478.34785 26.030273 478.48456 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +29.222656 486.7287 moveto +30.665039 486.7287 lineto +30.665039 487.90448 lineto +29.543945 490.09198 lineto +28.662109 490.09198 lineto +29.222656 487.90448 lineto +29.222656 486.7287 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +36.325195 481.69061 moveto +35.650713 481.69062 35.11751 481.95494 34.725586 482.48358 curveto +34.333657 483.00768 34.137693 483.72773 34.137695 484.64374 curveto +34.137693 485.55976 34.331378 486.28209 34.71875 486.81073 curveto +35.110674 487.33482 35.646155 487.59686 36.325195 487.59686 curveto +36.995112 487.59686 37.526036 487.33254 37.917969 486.80389 curveto +38.30989 486.27525 38.505853 485.5552 38.505859 484.64374 curveto +38.505853 483.73684 38.30989 483.01907 37.917969 482.49042 curveto +37.526036 481.95722 36.995112 481.69062 36.325195 481.69061 curveto +36.325195 480.62421 moveto +37.41894 480.62421 38.277989 480.97968 38.902344 481.69061 curveto +39.526685 482.40156 39.838859 483.38593 39.838867 484.64374 curveto +39.838859 485.897 39.526685 486.88137 38.902344 487.59686 curveto +38.277989 488.3078 37.41894 488.66327 36.325195 488.66327 curveto +35.226885 488.66327 34.365558 488.3078 33.741211 487.59686 curveto +33.121418 486.88137 32.811523 485.897 32.811523 484.64374 curveto +32.811523 483.38593 33.121418 482.40156 33.741211 481.69061 curveto +34.365558 480.97968 35.226885 480.62421 36.325195 480.62421 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +41.71875 477.84198 moveto +42.8125 477.84198 lineto +43.496091 478.91751 44.006507 479.97024 44.34375 481.00018 curveto +44.685543 482.03014 44.856441 483.05325 44.856445 484.06952 curveto +44.856441 485.09036 44.685543 486.11802 44.34375 487.15253 curveto +44.006507 488.18703 43.496091 489.23977 42.8125 490.31073 curveto +41.71875 490.31073 lineto +42.324868 489.26711 42.776039 488.23033 43.072266 487.20038 curveto +43.373044 486.16588 43.523435 485.12226 43.523438 484.06952 curveto +43.523435 483.01679 43.373044 481.97773 43.072266 480.95233 curveto +42.776039 479.92695 42.324868 478.89017 41.71875 477.84198 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +51.951172 478.34198 moveto +51.340491 479.39017 50.887041 480.42695 50.59082 481.45233 curveto +50.294594 482.47773 50.146482 483.51679 50.146484 484.56952 curveto +50.146482 485.62226 50.294594 486.66588 50.59082 487.70038 curveto +50.891598 488.73033 51.345048 489.76711 51.951172 490.81073 curveto +50.857422 490.81073 lineto +50.173826 489.73977 49.661131 488.68703 49.319336 487.65253 curveto +48.982095 486.61802 48.813475 485.59036 48.813477 484.56952 curveto +48.813475 483.55325 48.982095 482.53014 49.319336 481.50018 curveto +49.656573 480.47024 50.169268 479.41751 50.857422 478.34198 curveto +51.951172 478.34198 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +57.529297 484.11835 moveto +56.873043 484.11835 56.355791 484.29381 55.977539 484.64471 curveto +55.603839 484.99563 55.41699 485.4787 55.416992 486.09393 curveto +55.41699 486.70917 55.603839 487.19224 55.977539 487.54315 curveto +56.355791 487.89406 56.873043 488.06952 57.529297 488.06952 curveto +58.185542 488.06952 58.702794 487.89406 59.081055 487.54315 curveto +59.459304 487.18768 59.648431 486.70461 59.648438 486.09393 curveto +59.648431 485.4787 59.459304 484.99563 59.081055 484.64471 curveto +58.707351 484.29381 58.190099 484.11835 57.529297 484.11835 curveto +56.148438 483.53046 moveto +55.555987 483.38463 55.093422 483.10891 54.760742 482.70331 curveto +54.432616 482.29772 54.268553 481.80325 54.268555 481.21991 curveto +54.268553 480.40416 54.557941 479.75931 55.136719 479.28534 curveto +55.720049 478.81139 56.517575 478.57441 57.529297 478.5744 curveto +58.545567 478.57441 59.343093 478.81139 59.921875 479.28534 curveto +60.500644 479.75931 60.790031 480.40416 60.790039 481.21991 curveto +60.790031 481.80325 60.62369 482.29772 60.291016 482.70331 curveto +59.962884 483.10891 59.504876 483.38463 58.916992 483.53046 curveto +59.58235 483.68541 60.099602 483.98847 60.46875 484.43964 curveto +60.84244 484.89081 61.029289 485.44224 61.029297 486.09393 curveto +61.029289 487.08287 60.726229 487.84166 60.120117 488.3703 curveto +59.518548 488.89895 58.654942 489.16327 57.529297 489.16327 curveto +56.403643 489.16327 55.537758 488.89895 54.931641 488.3703 curveto +54.330077 487.84166 54.029296 487.08287 54.029297 486.09393 curveto +54.029296 485.44224 54.216145 484.89081 54.589844 484.43964 curveto +54.96354 483.98847 55.483071 483.68541 56.148438 483.53046 curveto +55.642578 481.34979 moveto +55.642576 481.87845 55.806638 482.29088 56.134766 482.5871 curveto +56.467445 482.88333 56.932288 483.03144 57.529297 483.03143 curveto +58.12174 483.03144 58.584304 482.88333 58.916992 482.5871 curveto +59.254226 482.29088 59.422845 481.87845 59.422852 481.34979 curveto +59.422845 480.82115 59.254226 480.40872 58.916992 480.11249 curveto +58.584304 479.81627 58.12174 479.66816 57.529297 479.66815 curveto +56.932288 479.66816 56.467445 479.81627 56.134766 480.11249 curveto +55.806638 480.40872 55.642576 480.82115 55.642578 481.34979 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +63.633789 487.2287 moveto +65.076172 487.2287 lineto +65.076172 488.40448 lineto +63.955078 490.59198 lineto +63.073242 490.59198 lineto +63.633789 488.40448 lineto +63.633789 487.2287 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +67.598633 478.75897 moveto +74.161133 478.75897 lineto +74.161133 479.34686 lineto +70.456055 488.96503 lineto +69.013672 488.96503 lineto +72.5 479.92108 lineto +67.598633 479.92108 lineto +67.598633 478.75897 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +77.004883 487.2287 moveto +78.447266 487.2287 lineto +78.447266 488.40448 lineto +77.326172 490.59198 lineto +76.444336 490.59198 lineto +77.004883 488.40448 lineto +77.004883 487.2287 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +84.107422 482.19061 moveto +83.432939 482.19062 82.899737 482.45494 82.507812 482.98358 curveto +82.115883 483.50768 81.91992 484.22773 81.919922 485.14374 curveto +81.91992 486.05976 82.113604 486.78209 82.500977 487.31073 curveto +82.892901 487.83482 83.428382 488.09686 84.107422 488.09686 curveto +84.777339 488.09686 85.308263 487.83254 85.700195 487.30389 curveto +86.092116 486.77525 86.288079 486.0552 86.288086 485.14374 curveto +86.288079 484.23684 86.092116 483.51907 85.700195 482.99042 curveto +85.308263 482.45722 84.777339 482.19062 84.107422 482.19061 curveto +84.107422 481.12421 moveto +85.201166 481.12421 86.060215 481.47968 86.68457 482.19061 curveto +87.308912 482.90156 87.621086 483.88593 87.621094 485.14374 curveto +87.621086 486.397 87.308912 487.38137 86.68457 488.09686 curveto +86.060215 488.8078 85.201166 489.16327 84.107422 489.16327 curveto +83.009111 489.16327 82.147784 488.8078 81.523438 488.09686 curveto +80.903645 487.38137 80.593749 486.397 80.59375 485.14374 curveto +80.593749 483.88593 80.903645 482.90156 81.523438 482.19061 curveto +82.147784 481.47968 83.009111 481.12421 84.107422 481.12421 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +89.500977 478.34198 moveto +90.594727 478.34198 lineto +91.278317 479.41751 91.788734 480.47024 92.125977 481.50018 curveto +92.467769 482.53014 92.638668 483.55325 92.638672 484.56952 curveto +92.638668 485.59036 92.467769 486.61802 92.125977 487.65253 curveto +91.788734 488.68703 91.278317 489.73977 90.594727 490.81073 curveto +89.500977 490.81073 lineto +90.107095 489.76711 90.558266 488.73033 90.854492 487.70038 curveto +91.155271 486.66588 91.305661 485.62226 91.305664 484.56952 curveto +91.305661 483.51679 91.155271 482.47773 90.854492 481.45233 curveto +90.558266 480.42695 90.107095 479.39017 89.500977 478.34198 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +96.905273 479.30878 moveto +96.294593 480.35696 95.841143 481.39375 95.544922 482.41913 curveto +95.248695 483.44453 95.100583 484.48359 95.100586 485.53632 curveto +95.100583 486.58905 95.248695 487.63267 95.544922 488.66718 curveto +95.8457 489.69712 96.29915 490.73391 96.905273 491.77753 curveto +95.811523 491.77753 lineto +95.127927 490.70656 94.615232 489.65383 94.273438 488.61932 curveto +93.936197 487.58482 93.767577 486.55715 93.767578 485.53632 curveto +93.767577 484.52005 93.936197 483.49693 94.273438 482.46698 curveto +94.610675 481.43704 95.12337 480.38431 95.811523 479.30878 curveto +96.905273 479.30878 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +99.571289 489.71991 moveto +99.571289 488.4621 lineto +99.917641 488.62616 100.26855 488.75149 100.62402 488.83807 curveto +100.97949 488.92466 101.32812 488.96796 101.66992 488.96796 curveto +102.58138 488.96796 103.27636 488.66262 103.75488 488.05194 curveto +104.23795 487.43671 104.51367 486.50474 104.58203 485.25604 curveto +104.3177 485.64797 103.98274 485.94875 103.57715 486.15839 curveto +103.17154 486.36803 102.72265 486.47284 102.23047 486.47284 curveto +101.20963 486.47284 100.40071 486.16523 99.803711 485.54999 curveto +99.211262 484.9302 98.915038 484.08482 98.915039 483.01385 curveto +98.915038 481.96569 99.224934 481.12487 99.844727 480.49139 curveto +100.46452 479.85794 101.28938 479.54121 102.31934 479.5412 curveto +103.49967 479.54121 104.39973 479.99466 105.01953 480.90155 curveto +105.64387 481.8039 105.95605 483.1164 105.95605 484.83905 curveto +105.95605 486.44778 105.57323 487.73293 104.80762 488.69452 curveto +104.04654 489.65155 103.02115 490.13007 101.73145 490.13007 curveto +101.38509 490.13007 101.03418 490.09589 100.67871 490.02753 curveto +100.32324 489.95917 99.9541 489.85663 99.571289 489.71991 curveto +102.31934 485.39276 moveto +102.93912 485.39277 103.42903 485.18085 103.78906 484.75702 curveto +104.15364 484.3332 104.33593 483.75214 104.33594 483.01385 curveto +104.33593 482.28014 104.15364 481.70136 103.78906 481.27753 curveto +103.42903 480.84915 102.93912 480.63496 102.31934 480.63495 curveto +101.69954 480.63496 101.20735 480.84915 100.84277 481.27753 curveto +100.48274 481.70136 100.30273 482.28014 100.30273 483.01385 curveto +100.30273 483.75214 100.48274 484.3332 100.84277 484.75702 curveto +101.20735 485.18085 101.69954 485.39277 102.31934 485.39276 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +108.58789 488.1955 moveto +110.03027 488.1955 lineto +110.03027 489.37128 lineto +108.90918 491.55878 lineto +108.02734 491.55878 lineto +108.58789 489.37128 lineto +108.58789 488.1955 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +115.85449 485.08514 moveto +115.19824 485.08515 114.68099 485.2606 114.30273 485.61151 curveto +113.92903 485.96243 113.74219 486.4455 113.74219 487.06073 curveto +113.74219 487.67597 113.92903 488.15904 114.30273 488.50995 curveto +114.68099 488.86086 115.19824 489.03632 115.85449 489.03632 curveto +116.51074 489.03632 117.02799 488.86086 117.40625 488.50995 curveto +117.7845 488.15448 117.97363 487.67141 117.97363 487.06073 curveto +117.97363 486.4455 117.7845 485.96243 117.40625 485.61151 curveto +117.03255 485.2606 116.51529 485.08515 115.85449 485.08514 curveto +114.47363 484.49725 moveto +113.88118 484.35143 113.41862 484.07571 113.08594 483.6701 curveto +112.75781 483.26451 112.59375 482.77005 112.59375 482.18671 curveto +112.59375 481.37096 112.88314 480.7261 113.46191 480.25214 curveto +114.04524 479.77819 114.84277 479.54121 115.85449 479.5412 curveto +116.87076 479.54121 117.66829 479.77819 118.24707 480.25214 curveto +118.82584 480.7261 119.11523 481.37096 119.11523 482.18671 curveto +119.11523 482.77005 118.94889 483.26451 118.61621 483.6701 curveto +118.28808 484.07571 117.83007 484.35143 117.24219 484.49725 curveto +117.90755 484.65221 118.4248 484.95527 118.79395 485.40643 curveto +119.16764 485.85761 119.35448 486.40904 119.35449 487.06073 curveto +119.35448 488.04966 119.05142 488.80845 118.44531 489.3371 curveto +117.84374 489.86574 116.98014 490.13007 115.85449 490.13007 curveto +114.72884 490.13007 113.86295 489.86574 113.25684 489.3371 curveto +112.65527 488.80845 112.35449 488.04966 112.35449 487.06073 curveto +112.35449 486.40904 112.54134 485.85761 112.91504 485.40643 curveto +113.28874 484.95527 113.80827 484.65221 114.47363 484.49725 curveto +113.96777 482.31659 moveto +113.96777 482.84524 114.13183 483.25768 114.45996 483.55389 curveto +114.79264 483.85012 115.25748 483.99824 115.85449 483.99823 curveto +116.44694 483.99824 116.9095 483.85012 117.24219 483.55389 curveto +117.57942 483.25768 117.74804 482.84524 117.74805 482.31659 curveto +117.74804 481.78795 117.57942 481.37552 117.24219 481.07928 curveto +116.9095 480.78307 116.44694 480.63496 115.85449 480.63495 curveto +115.25748 480.63496 114.79264 480.78307 114.45996 481.07928 curveto +114.13183 481.37552 113.96777 481.78795 113.96777 482.31659 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +121.95898 488.1955 moveto +123.40137 488.1955 lineto +123.40137 489.37128 lineto +122.28027 491.55878 lineto +121.39844 491.55878 lineto +121.95898 489.37128 lineto +121.95898 488.1955 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +131.13281 486.01483 moveto +131.13281 485.10338 130.94368 484.397 130.56543 483.89569 curveto +130.19173 483.3944 129.66536 483.14374 128.98633 483.14374 curveto +128.31185 483.14374 127.78548 483.3944 127.40723 483.89569 curveto +127.03353 484.397 126.84668 485.10338 126.84668 486.01483 curveto +126.84668 486.92174 127.03353 487.62584 127.40723 488.12714 curveto +127.78548 488.62844 128.31185 488.87909 128.98633 488.87909 curveto +129.66536 488.87909 130.19173 488.62844 130.56543 488.12714 curveto +130.94368 487.62584 131.13281 486.92174 131.13281 486.01483 curveto +132.39062 488.98163 moveto +132.39062 490.28501 132.10123 491.25344 131.52246 491.8869 curveto +130.94368 492.52492 130.05729 492.84393 128.86328 492.84393 curveto +128.42122 492.84393 128.00423 492.80975 127.6123 492.74139 curveto +127.22038 492.67759 126.83984 492.57733 126.4707 492.44061 curveto +126.4707 491.21698 lineto +126.83984 491.4175 127.20442 491.56561 127.56445 491.66132 curveto +127.92448 491.75702 128.29134 491.80487 128.66504 491.80487 curveto +129.4899 491.80487 130.10742 491.5884 130.51758 491.15546 curveto +130.92773 490.72707 131.13281 490.07766 131.13281 489.20721 curveto +131.13281 488.58514 lineto +130.87304 489.03632 130.54036 489.37356 130.13477 489.59686 curveto +129.72916 489.82017 129.24381 489.93182 128.67871 489.93182 curveto +127.73991 489.93182 126.9834 489.57408 126.40918 488.85858 curveto +125.83496 488.14309 125.54785 487.19517 125.54785 486.01483 curveto +125.54785 484.82994 125.83496 483.87975 126.40918 483.16425 curveto +126.9834 482.44876 127.73991 482.09101 128.67871 482.091 curveto +129.24381 482.09101 129.72916 482.20266 130.13477 482.42596 curveto +130.54036 482.64928 130.87304 482.98652 131.13281 483.43768 curveto +131.13281 482.27557 lineto +132.39062 482.27557 lineto +132.39062 488.98163 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +134.7832 479.30878 moveto +135.87695 479.30878 lineto +136.56054 480.38431 137.07096 481.43704 137.4082 482.46698 curveto +137.75 483.49693 137.92089 484.52005 137.9209 485.53632 curveto +137.92089 486.55715 137.75 487.58482 137.4082 488.61932 curveto +137.07096 489.65383 136.56054 490.70656 135.87695 491.77753 curveto +134.7832 491.77753 lineto +135.38932 490.73391 135.84049 489.69712 136.13672 488.66718 curveto +136.4375 487.63267 136.58789 486.58905 136.58789 485.53632 curveto +136.58789 484.48359 136.4375 483.44453 136.13672 482.41913 curveto +135.84049 481.39375 135.38932 480.35696 134.7832 479.30878 curveto +fill +grestore +gsave [1.209899 0 0 0.857509 -38.19343 49.51799] concat +0 0 0 setrgbcolor +[] 0 setdash +1.5 setlinewidth +0 setlinejoin +0 setlinecap +newpath +154.5 482.61218 moveto +154.5 515.59418 125.716 542.36218 90.25 542.36218 curveto +54.784 542.36218 26 515.59418 26 482.61218 curveto +26 449.63018 54.784 422.86218 90.25 422.86218 curveto +125.716 422.86218 154.5 449.63018 154.5 482.61218 curveto +closepath +stroke +grestore +gsave +0 0 0 setrgbcolor +newpath +67.874023 420.15613 moveto +69.254883 420.15613 lineto +69.254883 430.36218 lineto +67.874023 430.36218 lineto +67.874023 420.15613 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +73.192383 420.5321 moveto +73.192383 422.70593 lineto +75.783203 422.70593 lineto +75.783203 423.68347 lineto +73.192383 423.68347 lineto +73.192383 427.83972 lineto +73.19238 428.46407 73.27669 428.86511 73.445312 429.04285 curveto +73.618487 429.22058 73.967119 429.30945 74.491211 429.30945 curveto +75.783203 429.30945 lineto +75.783203 430.36218 lineto +74.491211 430.36218 lineto +73.520505 430.36218 72.850584 430.18217 72.481445 429.82214 curveto +72.112303 429.45756 71.927733 428.79675 71.927734 427.83972 curveto +71.927734 423.68347 lineto +71.004883 423.68347 lineto +71.004883 422.70593 lineto +71.927734 422.70593 lineto +71.927734 420.5321 lineto +73.192383 420.5321 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +59.254883 524.79089 moveto +59.254883 528.62585 lineto +60.991211 528.62585 lineto +61.633784 528.62586 62.130528 528.45952 62.481445 528.12683 curveto +62.83235 527.79415 63.007806 527.3202 63.007812 526.70496 curveto +63.007806 526.09429 62.83235 525.62261 62.481445 525.28992 curveto +62.130528 524.95724 61.633784 524.7909 60.991211 524.79089 curveto +59.254883 524.79089 lineto +57.874023 523.65613 moveto +60.991211 523.65613 lineto +62.135086 523.65614 62.998691 523.9159 63.582031 524.43542 curveto +64.169914 524.95041 64.463859 525.70692 64.463867 526.70496 curveto +64.463859 527.71212 64.169914 528.47319 63.582031 528.98816 curveto +62.998691 529.50314 62.135086 529.76062 60.991211 529.76062 curveto +59.254883 529.76062 lineto +59.254883 533.86218 lineto +57.874023 533.86218 lineto +57.874023 523.65613 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +69.399414 524.56531 moveto +68.688473 524.56532 68.152992 524.91623 67.792969 525.61804 curveto +67.437498 526.31532 67.259763 527.36577 67.259766 528.76941 curveto +67.259763 530.1685 67.437498 531.21896 67.792969 531.92078 curveto +68.152992 532.61804 68.688473 532.96668 69.399414 532.96667 curveto +70.114904 532.96668 70.650385 532.61804 71.005859 531.92078 curveto +71.365879 531.21896 71.545892 530.1685 71.545898 528.76941 curveto +71.545892 527.36577 71.365879 526.31532 71.005859 525.61804 curveto +70.650385 524.91623 70.114904 524.56532 69.399414 524.56531 curveto +69.399414 523.47156 moveto +70.543289 523.47157 71.416009 523.92502 72.017578 524.83191 curveto +72.62369 525.73426 72.92675 527.04676 72.926758 528.76941 curveto +72.92675 530.48751 72.62369 531.80001 72.017578 532.70691 curveto +71.416009 533.60925 70.543289 534.06042 69.399414 534.06042 curveto +68.255531 534.06042 67.380531 533.60925 66.774414 532.70691 curveto +66.17285 531.80001 65.872069 530.48751 65.87207 528.76941 curveto +65.872069 527.04676 66.17285 525.73426 66.774414 524.83191 curveto +67.380531 523.92502 68.255531 523.47157 69.399414 523.47156 curveto +fill +grestore +gsave [1.063088 0 0 0.999533 -49.82231 5.914079] concat +0 0 0 setrgbcolor +[] 0 setdash +1.5 setlinewidth +0 setlinejoin +0 setlinecap +newpath +349.5 351.36218 moveto +349.5 378.68618 319.484 400.86218 282.5 400.86218 curveto +245.516 400.86218 215.5 378.68618 215.5 351.36218 curveto +215.5 324.03818 245.516 301.86218 282.5 301.86218 curveto +319.484 301.86218 349.5 324.03818 349.5 351.36218 curveto +closepath +stroke +grestore +gsave +0 0 0 setrgbcolor +newpath +205.85547 329.61218 moveto +205.24479 330.66037 204.79134 331.69715 204.49512 332.72253 curveto +204.19889 333.74793 204.05078 334.78699 204.05078 335.83972 curveto +204.05078 336.89246 204.19889 337.93608 204.49512 338.97058 curveto +204.7959 340.00053 205.24935 341.03731 205.85547 342.08093 curveto +204.76172 342.08093 lineto +204.07812 341.00997 203.56543 339.95723 203.22363 338.92273 curveto +202.88639 337.88823 202.71777 336.86056 202.71777 335.83972 curveto +202.71777 334.82345 202.88639 333.80034 203.22363 332.77039 curveto +203.56087 331.74045 204.07357 330.68771 204.76172 329.61218 curveto +205.85547 329.61218 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +208.49414 330.02917 moveto +213.91504 330.02917 lineto +213.91504 331.19128 lineto +209.75879 331.19128 lineto +209.75879 333.69324 lineto +209.95931 333.62488 210.15983 333.57475 210.36035 333.54285 curveto +210.56087 333.5064 210.76139 333.48817 210.96191 333.48816 curveto +212.10123 333.48817 213.00357 333.80034 213.66895 334.42468 curveto +214.3343 335.04904 214.66698 335.89441 214.66699 336.96082 curveto +214.66698 338.05912 214.32519 338.91362 213.6416 339.52429 curveto +212.958 340.13041 211.99414 340.43347 210.75 340.43347 curveto +210.32161 340.43347 209.88411 340.39701 209.4375 340.3241 curveto +208.99544 340.25118 208.53743 340.14181 208.06348 339.99597 curveto +208.06348 338.60828 lineto +208.47363 338.83159 208.89746 338.99793 209.33496 339.1073 curveto +209.77246 339.21668 210.23502 339.27136 210.72266 339.27136 curveto +211.51106 339.27136 212.13541 339.06401 212.5957 338.64929 curveto +213.05598 338.23458 213.28613 337.67176 213.28613 336.96082 curveto +213.28613 336.24988 213.05598 335.68706 212.5957 335.27234 curveto +212.13541 334.85763 211.51106 334.65027 210.72266 334.65027 curveto +210.35351 334.65027 209.98437 334.69129 209.61523 334.77332 curveto +209.25065 334.85535 208.87695 334.98296 208.49414 335.15613 curveto +208.49414 330.02917 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +217.53809 338.4989 moveto +218.98047 338.4989 lineto +218.98047 339.67468 lineto +217.85938 341.86218 lineto +216.97754 341.86218 lineto +217.53809 339.67468 lineto +217.53809 338.4989 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +224.80469 330.93835 moveto +224.09375 330.93836 223.55827 331.28927 223.19824 331.99109 curveto +222.84277 332.68836 222.66504 333.73882 222.66504 335.14246 curveto +222.66504 336.54155 222.84277 337.592 223.19824 338.29382 curveto +223.55827 338.99109 224.09375 339.33972 224.80469 339.33972 curveto +225.52018 339.33972 226.05566 338.99109 226.41113 338.29382 curveto +226.77115 337.592 226.95117 336.54155 226.95117 335.14246 curveto +226.95117 333.73882 226.77115 332.68836 226.41113 331.99109 curveto +226.05566 331.28927 225.52018 330.93836 224.80469 330.93835 curveto +224.80469 329.8446 moveto +225.94856 329.84461 226.82128 330.29806 227.42285 331.20496 curveto +228.02896 332.10731 228.33202 333.41981 228.33203 335.14246 curveto +228.33202 336.86056 228.02896 338.17306 227.42285 339.07996 curveto +226.82128 339.9823 225.94856 340.43347 224.80469 340.43347 curveto +223.6608 340.43347 222.7858 339.9823 222.17969 339.07996 curveto +221.57812 338.17306 221.27734 336.86056 221.27734 335.14246 curveto +221.27734 333.41981 221.57812 332.10731 222.17969 331.20496 curveto +222.7858 330.29806 223.6608 329.84461 224.80469 329.8446 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +230.90918 338.4989 moveto +232.35156 338.4989 lineto +232.35156 339.67468 lineto +231.23047 341.86218 lineto +230.34863 341.86218 lineto +230.90918 339.67468 lineto +230.90918 338.4989 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +240.08301 336.31824 moveto +240.083 335.40678 239.89387 334.7004 239.51562 334.1991 curveto +239.14192 333.6978 238.61556 333.44715 237.93652 333.44714 curveto +237.26204 333.44715 236.73567 333.6978 236.35742 334.1991 curveto +235.98372 334.7004 235.79687 335.40678 235.79688 336.31824 curveto +235.79687 337.22514 235.98372 337.92924 236.35742 338.43054 curveto +236.73567 338.93185 237.26204 339.1825 237.93652 339.1825 curveto +238.61556 339.1825 239.14192 338.93185 239.51562 338.43054 curveto +239.89387 337.92924 240.083 337.22514 240.08301 336.31824 curveto +241.34082 339.28503 moveto +241.34081 340.58842 241.05142 341.55684 240.47266 342.19031 curveto +239.89387 342.82833 239.00748 343.14734 237.81348 343.14734 curveto +237.37142 343.14734 236.95442 343.11316 236.5625 343.0448 curveto +236.17057 342.98099 235.79004 342.88073 235.4209 342.74402 curveto +235.4209 341.52039 lineto +235.79004 341.72091 236.15462 341.86902 236.51465 341.96472 curveto +236.87467 342.06042 237.24153 342.10827 237.61523 342.10828 curveto +238.4401 342.10827 239.05761 341.8918 239.46777 341.45886 curveto +239.87792 341.03048 240.083 340.38106 240.08301 339.51062 curveto +240.08301 338.88855 lineto +239.82324 339.33972 239.49055 339.67696 239.08496 339.90027 curveto +238.67936 340.12358 238.19401 340.23523 237.62891 340.23523 curveto +236.6901 340.23523 235.93359 339.87748 235.35938 339.16199 curveto +234.78516 338.44649 234.49805 337.49858 234.49805 336.31824 curveto +234.49805 335.13335 234.78516 334.18315 235.35938 333.46765 curveto +235.93359 332.75216 236.6901 332.39442 237.62891 332.39441 curveto +238.19401 332.39442 238.67936 332.50607 239.08496 332.72937 curveto +239.49055 332.95268 239.82324 333.28992 240.08301 333.74109 curveto +240.08301 332.57898 lineto +241.34082 332.57898 lineto +241.34082 339.28503 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +243.7334 329.61218 moveto +244.82715 329.61218 lineto +245.51074 330.68771 246.02116 331.74045 246.3584 332.77039 curveto +246.70019 333.80034 246.87109 334.82345 246.87109 335.83972 curveto +246.87109 336.86056 246.70019 337.88823 246.3584 338.92273 curveto +246.02116 339.95723 245.51074 341.00997 244.82715 342.08093 curveto +243.7334 342.08093 lineto +244.33952 341.03731 244.79069 340.00053 245.08691 338.97058 curveto +245.38769 337.93608 245.53808 336.89246 245.53809 335.83972 curveto +245.53808 334.78699 245.38769 333.74793 245.08691 332.72253 curveto +244.79069 331.69715 244.33952 330.66037 243.7334 329.61218 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +253.34082 328.61218 moveto +252.73014 329.66037 252.27669 330.69715 251.98047 331.72253 curveto +251.68424 332.74793 251.53613 333.78699 251.53613 334.83972 curveto +251.53613 335.89246 251.68424 336.93608 251.98047 337.97058 curveto +252.28125 339.00053 252.7347 340.03731 253.34082 341.08093 curveto +252.24707 341.08093 lineto +251.56347 340.00997 251.05078 338.95723 250.70898 337.92273 curveto +250.37174 336.88823 250.20312 335.86056 250.20312 334.83972 curveto +250.20312 333.82345 250.37174 332.80034 250.70898 331.77039 curveto +251.04622 330.74045 251.55892 329.68771 252.24707 328.61218 curveto +253.34082 328.61218 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +259.08984 333.58191 moveto +258.47005 333.58191 257.97786 333.79383 257.61328 334.21765 curveto +257.25325 334.64148 257.07324 335.22254 257.07324 335.96082 curveto +257.07324 336.69454 257.25325 337.2756 257.61328 337.70398 curveto +257.97786 338.12781 258.47005 338.33972 259.08984 338.33972 curveto +259.70963 338.33972 260.19954 338.12781 260.55957 337.70398 curveto +260.92415 337.2756 261.10644 336.69454 261.10645 335.96082 curveto +261.10644 335.22254 260.92415 334.64148 260.55957 334.21765 curveto +260.19954 333.79383 259.70963 333.58191 259.08984 333.58191 curveto +261.83105 329.25476 moveto +261.83105 330.51257 lineto +261.48469 330.34852 261.13378 330.22319 260.77832 330.1366 curveto +260.4274 330.05002 260.07877 330.00672 259.73242 330.00671 curveto +258.82096 330.00672 258.12369 330.31434 257.64062 330.92957 curveto +257.16211 331.54481 256.88867 332.47449 256.82031 333.71863 curveto +257.08919 333.32215 257.42643 333.01909 257.83203 332.80945 curveto +258.23763 332.59526 258.68424 332.48817 259.17188 332.48816 curveto +260.19726 332.48817 261.00618 332.80034 261.59863 333.42468 curveto +262.19563 334.04448 262.49413 334.88986 262.49414 335.96082 curveto +262.49413 337.00899 262.18424 337.84981 261.56445 338.48328 curveto +260.94465 339.11674 260.11979 339.43347 259.08984 339.43347 curveto +257.9095 339.43347 257.00716 338.9823 256.38281 338.07996 curveto +255.75846 337.17306 255.44629 335.86056 255.44629 334.14246 curveto +255.44629 332.52918 255.8291 331.24403 256.59473 330.28699 curveto +257.36035 329.32541 258.38802 328.84461 259.67773 328.8446 curveto +260.02408 328.84461 260.37272 328.87879 260.72363 328.94714 curveto +261.07909 329.01551 261.44824 329.11805 261.83105 329.25476 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +265.02344 337.4989 moveto +266.46582 337.4989 lineto +266.46582 338.67468 lineto +265.34473 340.86218 lineto +264.46289 340.86218 lineto +265.02344 338.67468 lineto +265.02344 337.4989 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +269.57617 338.07312 moveto +271.83203 338.07312 lineto +271.83203 330.28699 lineto +269.37793 330.77917 lineto +269.37793 329.52136 lineto +271.81836 329.02917 lineto +273.19922 329.02917 lineto +273.19922 338.07312 lineto +275.45508 338.07312 lineto +275.45508 339.23523 lineto +269.57617 339.23523 lineto +269.57617 338.07312 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +278.39453 337.4989 moveto +279.83691 337.4989 lineto +279.83691 338.67468 lineto +278.71582 340.86218 lineto +277.83398 340.86218 lineto +278.39453 338.67468 lineto +278.39453 337.4989 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +285.49707 332.46082 moveto +284.82259 332.46082 284.28938 332.72514 283.89746 333.25378 curveto +283.50553 333.77788 283.30957 334.49793 283.30957 335.41394 curveto +283.30957 336.32996 283.50325 337.05229 283.89062 337.58093 curveto +284.28255 338.10502 284.81803 338.36707 285.49707 338.36707 curveto +286.16699 338.36707 286.69791 338.10274 287.08984 337.5741 curveto +287.48176 337.04545 287.67773 336.3254 287.67773 335.41394 curveto +287.67773 334.50704 287.48176 333.78927 287.08984 333.26062 curveto +286.69791 332.72742 286.16699 332.46082 285.49707 332.46082 curveto +285.49707 331.39441 moveto +286.59081 331.39442 287.44986 331.74989 288.07422 332.46082 curveto +288.69856 333.17176 289.01073 334.15613 289.01074 335.41394 curveto +289.01073 336.6672 288.69856 337.65157 288.07422 338.36707 curveto +287.44986 339.078 286.59081 339.43347 285.49707 339.43347 curveto +284.39876 339.43347 283.53743 339.078 282.91309 338.36707 curveto +282.29329 337.65157 281.9834 336.6672 281.9834 335.41394 curveto +281.9834 334.15613 282.29329 333.17176 282.91309 332.46082 curveto +283.53743 331.74989 284.39876 331.39442 285.49707 331.39441 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +290.89062 328.61218 moveto +291.98438 328.61218 lineto +292.66797 329.68771 293.17838 330.74045 293.51562 331.77039 curveto +293.85742 332.80034 294.02832 333.82345 294.02832 334.83972 curveto +294.02832 335.86056 293.85742 336.88823 293.51562 337.92273 curveto +293.17838 338.95723 292.66797 340.00997 291.98438 341.08093 curveto +290.89062 341.08093 lineto +291.49674 340.03731 291.94791 339.00053 292.24414 337.97058 curveto +292.54492 336.93608 292.69531 335.89246 292.69531 334.83972 curveto +292.69531 333.78699 292.54492 332.74793 292.24414 331.72253 curveto +291.94791 330.69715 291.49674 329.66037 290.89062 328.61218 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +188.1377 350.98914 moveto +187.52701 352.03732 187.07356 353.07411 186.77734 354.09949 curveto +186.48112 355.12488 186.33301 356.16395 186.33301 357.21667 curveto +186.33301 358.26941 186.48112 359.31303 186.77734 360.34753 curveto +187.07812 361.37748 187.53157 362.41427 188.1377 363.45789 curveto +187.04395 363.45789 lineto +186.36035 362.38692 185.84765 361.33419 185.50586 360.29968 curveto +185.16862 359.26518 185 358.23751 185 357.21667 curveto +185 356.2004 185.16862 355.17729 185.50586 354.14734 curveto +185.8431 353.1174 186.35579 352.06467 187.04395 350.98914 curveto +188.1377 350.98914 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +190.41406 351.40613 moveto +196.97656 351.40613 lineto +196.97656 351.99402 lineto +193.27148 361.61218 lineto +191.8291 361.61218 lineto +195.31543 352.56824 lineto +190.41406 352.56824 lineto +190.41406 351.40613 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +199.82031 359.87585 moveto +201.2627 359.87585 lineto +201.2627 361.05164 lineto +200.1416 363.23914 lineto +199.25977 363.23914 lineto +199.82031 361.05164 lineto +199.82031 359.87585 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +205.32324 360.45007 moveto +210.14258 360.45007 lineto +210.14258 361.61218 lineto +203.66211 361.61218 lineto +203.66211 360.45007 lineto +204.1862 359.90776 204.89941 359.18087 205.80176 358.26941 curveto +206.70865 357.3534 207.27832 356.76323 207.51074 356.4989 curveto +207.95279 356.00216 208.26041 355.58289 208.43359 355.24109 curveto +208.61132 354.89474 208.70019 354.55522 208.7002 354.22253 curveto +208.70019 353.68022 208.50878 353.23817 208.12598 352.89636 curveto +207.74772 352.55457 207.25325 352.38368 206.64258 352.38367 curveto +206.20963 352.38368 205.75162 352.45887 205.26855 352.60925 curveto +204.79004 352.75965 204.27734 352.98752 203.73047 353.29285 curveto +203.73047 351.89832 lineto +204.28646 351.67502 204.80599 351.5064 205.28906 351.39246 curveto +205.77213 351.27853 206.21419 351.22157 206.61523 351.22156 curveto +207.67252 351.22157 208.51562 351.48589 209.14453 352.01453 curveto +209.77343 352.54318 210.08788 353.24956 210.08789 354.13367 curveto +210.08788 354.55294 210.00813 354.95171 209.84863 355.32996 curveto +209.69368 355.70366 209.40885 356.14572 208.99414 356.65613 curveto +208.8802 356.78829 208.5179 357.17111 207.90723 357.80457 curveto +207.29654 358.43347 206.43522 359.31531 205.32324 360.45007 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +213.19141 359.87585 moveto +214.63379 359.87585 lineto +214.63379 361.05164 lineto +213.5127 363.23914 lineto +212.63086 363.23914 lineto +213.19141 361.05164 lineto +213.19141 359.87585 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +220.29395 354.83777 moveto +219.61946 354.83778 219.08626 355.1021 218.69434 355.63074 curveto +218.30241 356.15483 218.10644 356.87488 218.10645 357.79089 curveto +218.10644 358.70691 218.30013 359.42924 218.6875 359.95789 curveto +219.07942 360.48198 219.61491 360.74402 220.29395 360.74402 curveto +220.96386 360.74402 221.49479 360.4797 221.88672 359.95105 curveto +222.27864 359.42241 222.4746 358.70235 222.47461 357.79089 curveto +222.4746 356.884 222.27864 356.16622 221.88672 355.63757 curveto +221.49479 355.10438 220.96386 354.83778 220.29395 354.83777 curveto +220.29395 353.77136 moveto +221.38769 353.77137 222.24674 354.12684 222.87109 354.83777 curveto +223.49544 355.54871 223.80761 356.53309 223.80762 357.79089 curveto +223.80761 359.04415 223.49544 360.02853 222.87109 360.74402 curveto +222.24674 361.45496 221.38769 361.81042 220.29395 361.81042 curveto +219.19563 361.81042 218.33431 361.45496 217.70996 360.74402 curveto +217.09017 360.02853 216.78027 359.04415 216.78027 357.79089 curveto +216.78027 356.53309 217.09017 355.54871 217.70996 354.83777 curveto +218.33431 354.12684 219.19563 353.77137 220.29395 353.77136 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +225.6875 350.98914 moveto +226.78125 350.98914 lineto +227.46484 352.06467 227.97526 353.1174 228.3125 354.14734 curveto +228.65429 355.17729 228.82519 356.2004 228.8252 357.21667 curveto +228.82519 358.23751 228.65429 359.26518 228.3125 360.29968 curveto +227.97526 361.33419 227.46484 362.38692 226.78125 363.45789 curveto +225.6875 363.45789 lineto +226.29362 362.41427 226.74479 361.37748 227.04102 360.34753 curveto +227.34179 359.31303 227.49218 358.26941 227.49219 357.21667 curveto +227.49218 356.16395 227.34179 355.12488 227.04102 354.09949 curveto +226.74479 353.07411 226.29362 352.03732 225.6875 350.98914 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +232.84082 350.48914 moveto +232.23014 351.53732 231.77669 352.57411 231.48047 353.59949 curveto +231.18424 354.62488 231.03613 355.66395 231.03613 356.71667 curveto +231.03613 357.76941 231.18424 358.81303 231.48047 359.84753 curveto +231.78125 360.87748 232.2347 361.91427 232.84082 362.95789 curveto +231.74707 362.95789 lineto +231.06347 361.88692 230.55078 360.83419 230.20898 359.79968 curveto +229.87174 358.76518 229.70312 357.73751 229.70312 356.71667 curveto +229.70312 355.7004 229.87174 354.67729 230.20898 353.64734 curveto +230.54622 352.6174 231.05892 351.56467 231.74707 350.48914 curveto +232.84082 350.48914 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +238.41895 356.2655 moveto +237.76269 356.26551 237.24544 356.44096 236.86719 356.79187 curveto +236.49349 357.14279 236.30664 357.62586 236.30664 358.24109 curveto +236.30664 358.85633 236.49349 359.3394 236.86719 359.69031 curveto +237.24544 360.04122 237.76269 360.21668 238.41895 360.21667 curveto +239.07519 360.21668 239.59244 360.04122 239.9707 359.69031 curveto +240.34895 359.33484 240.53808 358.85177 240.53809 358.24109 curveto +240.53808 357.62586 240.34895 357.14279 239.9707 356.79187 curveto +239.597 356.44096 239.07975 356.26551 238.41895 356.2655 curveto +237.03809 355.67761 moveto +236.44564 355.53178 235.98307 355.25607 235.65039 354.85046 curveto +235.32226 354.44487 235.1582 353.95041 235.1582 353.36707 curveto +235.1582 352.55132 235.44759 351.90646 236.02637 351.4325 curveto +236.6097 350.95855 237.40722 350.72157 238.41895 350.72156 curveto +239.43522 350.72157 240.23274 350.95855 240.81152 351.4325 curveto +241.39029 351.90646 241.67968 352.55132 241.67969 353.36707 curveto +241.67968 353.95041 241.51334 354.44487 241.18066 354.85046 curveto +240.85253 355.25607 240.39452 355.53178 239.80664 355.67761 curveto +240.472 355.83257 240.98925 356.13563 241.3584 356.58679 curveto +241.73209 357.03797 241.91894 357.5894 241.91895 358.24109 curveto +241.91894 359.23002 241.61588 359.98881 241.00977 360.51746 curveto +240.4082 361.0461 239.54459 361.31042 238.41895 361.31042 curveto +237.29329 361.31042 236.42741 361.0461 235.82129 360.51746 curveto +235.21973 359.98881 234.91894 359.23002 234.91895 358.24109 curveto +234.91894 357.5894 235.10579 357.03797 235.47949 356.58679 curveto +235.85319 356.13563 236.37272 355.83257 237.03809 355.67761 curveto +236.53223 353.49695 moveto +236.53222 354.0256 236.69629 354.43804 237.02441 354.73425 curveto +237.35709 355.03048 237.82194 355.17859 238.41895 355.17859 curveto +239.01139 355.17859 239.47395 355.03048 239.80664 354.73425 curveto +240.14387 354.43804 240.31249 354.0256 240.3125 353.49695 curveto +240.31249 352.96831 240.14387 352.55588 239.80664 352.25964 curveto +239.47395 351.96343 239.01139 351.81532 238.41895 351.81531 curveto +237.82194 351.81532 237.35709 351.96343 237.02441 352.25964 curveto +236.69629 352.55588 236.53222 352.96831 236.53223 353.49695 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +244.52344 359.37585 moveto +245.96582 359.37585 lineto +245.96582 360.55164 lineto +244.84473 362.73914 lineto +243.96289 362.73914 lineto +244.52344 360.55164 lineto +244.52344 359.37585 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +253.02051 355.60925 moveto +253.68131 355.75053 254.19628 356.04448 254.56543 356.49109 curveto +254.93912 356.93771 255.12597 357.48914 255.12598 358.14539 curveto +255.12597 359.15255 254.77961 359.93185 254.08691 360.48328 curveto +253.3942 361.03471 252.40983 361.31042 251.13379 361.31042 curveto +250.7054 361.31042 250.26334 361.26713 249.80762 361.18054 curveto +249.35644 361.09851 248.88932 360.97319 248.40625 360.80457 curveto +248.40625 359.47156 lineto +248.78906 359.69487 249.20833 359.86349 249.66406 359.97742 curveto +250.11979 360.09135 250.59603 360.14832 251.09277 360.14832 curveto +251.95865 360.14832 252.61718 359.97742 253.06836 359.63562 curveto +253.52408 359.29383 253.75195 358.79708 253.75195 358.14539 curveto +253.75195 357.54383 253.54003 357.07443 253.11621 356.73718 curveto +252.69693 356.39539 252.11132 356.22449 251.35938 356.22449 curveto +250.16992 356.22449 lineto +250.16992 355.08972 lineto +251.41406 355.08972 lineto +252.09309 355.08973 252.61262 354.95529 252.97266 354.6864 curveto +253.33268 354.41297 253.51269 354.02104 253.5127 353.51062 curveto +253.51269 352.98654 253.32584 352.5855 252.95215 352.3075 curveto +252.583 352.02495 252.05208 351.88368 251.35938 351.88367 curveto +250.98112 351.88368 250.57552 351.92469 250.14258 352.00671 curveto +249.70963 352.08875 249.2334 352.21636 248.71387 352.38953 curveto +248.71387 351.15906 lineto +249.23795 351.01323 249.72786 350.90386 250.18359 350.83093 curveto +250.64388 350.75803 251.07682 350.72157 251.48242 350.72156 curveto +252.53059 350.72157 253.36002 350.96083 253.9707 351.43933 curveto +254.58137 351.9133 254.88671 352.55588 254.88672 353.36707 curveto +254.88671 353.93218 254.72493 354.41069 254.40137 354.80261 curveto +254.07779 355.18999 253.61751 355.45887 253.02051 355.60925 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +257.89453 359.37585 moveto +259.33691 359.37585 lineto +259.33691 360.55164 lineto +258.21582 362.73914 lineto +257.33398 362.73914 lineto +257.89453 360.55164 lineto +257.89453 359.37585 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +264.99707 354.33777 moveto +264.32259 354.33778 263.78938 354.6021 263.39746 355.13074 curveto +263.00553 355.65483 262.80957 356.37488 262.80957 357.29089 curveto +262.80957 358.20691 263.00325 358.92924 263.39062 359.45789 curveto +263.78255 359.98198 264.31803 360.24402 264.99707 360.24402 curveto +265.66699 360.24402 266.19791 359.9797 266.58984 359.45105 curveto +266.98176 358.92241 267.17773 358.20235 267.17773 357.29089 curveto +267.17773 356.384 266.98176 355.66622 266.58984 355.13757 curveto +266.19791 354.60438 265.66699 354.33778 264.99707 354.33777 curveto +264.99707 353.27136 moveto +266.09081 353.27137 266.94986 353.62684 267.57422 354.33777 curveto +268.19856 355.04871 268.51073 356.03309 268.51074 357.29089 curveto +268.51073 358.54415 268.19856 359.52853 267.57422 360.24402 curveto +266.94986 360.95496 266.09081 361.31042 264.99707 361.31042 curveto +263.89876 361.31042 263.03743 360.95496 262.41309 360.24402 curveto +261.79329 359.52853 261.4834 358.54415 261.4834 357.29089 curveto +261.4834 356.03309 261.79329 355.04871 262.41309 354.33777 curveto +263.03743 353.62684 263.89876 353.27137 264.99707 353.27136 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +270.39062 350.48914 moveto +271.48438 350.48914 lineto +272.16797 351.56467 272.67838 352.6174 273.01562 353.64734 curveto +273.35742 354.67729 273.52832 355.7004 273.52832 356.71667 curveto +273.52832 357.73751 273.35742 358.76518 273.01562 359.79968 curveto +272.67838 360.83419 272.16797 361.88692 271.48438 362.95789 curveto +270.39062 362.95789 lineto +270.99674 361.91427 271.44791 360.87748 271.74414 359.84753 curveto +272.04492 358.81303 272.19531 357.76941 272.19531 356.71667 curveto +272.19531 355.66395 272.04492 354.62488 271.74414 353.59949 curveto +271.44791 352.57411 270.99674 351.53732 270.39062 350.48914 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +278.84082 350.64343 moveto +278.23014 351.69162 277.77669 352.7284 277.48047 353.75378 curveto +277.18424 354.77918 277.03613 355.81824 277.03613 356.87097 curveto +277.03613 357.92371 277.18424 358.96733 277.48047 360.00183 curveto +277.78125 361.03178 278.2347 362.06856 278.84082 363.11218 curveto +277.74707 363.11218 lineto +277.06347 362.04122 276.55078 360.98848 276.20898 359.95398 curveto +275.87174 358.91948 275.70312 357.89181 275.70312 356.87097 curveto +275.70312 355.8547 275.87174 354.83159 276.20898 353.80164 curveto +276.54622 352.7717 277.05892 351.71896 277.74707 350.64343 curveto +278.84082 350.64343 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +281.50684 361.05457 moveto +281.50684 359.79675 lineto +281.85319 359.96082 282.2041 360.08614 282.55957 360.17273 curveto +282.91504 360.25932 283.26367 360.30261 283.60547 360.30261 curveto +284.51692 360.30261 285.21191 359.99728 285.69043 359.3866 curveto +286.1735 358.77136 286.44921 357.8394 286.51758 356.5907 curveto +286.25325 356.98263 285.91829 357.28341 285.5127 357.49304 curveto +285.10709 357.70268 284.6582 357.8075 284.16602 357.8075 curveto +283.14518 357.8075 282.33626 357.49988 281.73926 356.88464 curveto +281.14681 356.26486 280.85059 355.41948 280.85059 354.34851 curveto +280.85059 353.30034 281.16048 352.45952 281.78027 351.82605 curveto +282.40006 351.1926 283.22493 350.87586 284.25488 350.87585 curveto +285.43522 350.87586 286.33528 351.32931 286.95508 352.23621 curveto +287.57942 353.13856 287.89159 354.45106 287.8916 356.17371 curveto +287.89159 357.78243 287.50878 359.06759 286.74316 360.02917 curveto +285.98209 360.98621 284.9567 361.46472 283.66699 361.46472 curveto +283.32063 361.46472 282.96972 361.43054 282.61426 361.36218 curveto +282.25879 361.29382 281.88965 361.19128 281.50684 361.05457 curveto +284.25488 356.72742 moveto +284.87467 356.72742 285.36458 356.51551 285.72461 356.09167 curveto +286.08919 355.66785 286.27148 355.0868 286.27148 354.34851 curveto +286.27148 353.61479 286.08919 353.03602 285.72461 352.61218 curveto +285.36458 352.18381 284.87467 351.96961 284.25488 351.9696 curveto +283.63509 351.96961 283.1429 352.18381 282.77832 352.61218 curveto +282.41829 353.03602 282.23828 353.61479 282.23828 354.34851 curveto +282.23828 355.0868 282.41829 355.66785 282.77832 356.09167 curveto +283.1429 356.51551 283.63509 356.72742 284.25488 356.72742 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +290.52344 359.53015 moveto +291.96582 359.53015 lineto +291.96582 360.70593 lineto +290.84473 362.89343 lineto +289.96289 362.89343 lineto +290.52344 360.70593 lineto +290.52344 359.53015 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +298.63086 352.26355 moveto +295.14453 357.71179 lineto +298.63086 357.71179 lineto +298.63086 352.26355 lineto +298.26855 351.06042 moveto +300.00488 351.06042 lineto +300.00488 357.71179 lineto +301.46094 357.71179 lineto +301.46094 358.86023 lineto +300.00488 358.86023 lineto +300.00488 361.26648 lineto +298.63086 361.26648 lineto +298.63086 358.86023 lineto +294.02344 358.86023 lineto +294.02344 357.52722 lineto +298.26855 351.06042 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +303.89453 359.53015 moveto +305.33691 359.53015 lineto +305.33691 360.70593 lineto +304.21582 362.89343 lineto +303.33398 362.89343 lineto +303.89453 360.70593 lineto +303.89453 359.53015 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +310.99707 354.49207 moveto +310.32259 354.49207 309.78938 354.75639 309.39746 355.28503 curveto +309.00553 355.80913 308.80957 356.52918 308.80957 357.44519 curveto +308.80957 358.36121 309.00325 359.08354 309.39062 359.61218 curveto +309.78255 360.13627 310.31803 360.39832 310.99707 360.39832 curveto +311.66699 360.39832 312.19791 360.13399 312.58984 359.60535 curveto +312.98176 359.0767 313.17773 358.35665 313.17773 357.44519 curveto +313.17773 356.53829 312.98176 355.82052 312.58984 355.29187 curveto +312.19791 354.75867 311.66699 354.49207 310.99707 354.49207 curveto +310.99707 353.42566 moveto +312.09081 353.42567 312.94986 353.78114 313.57422 354.49207 curveto +314.19856 355.20301 314.51073 356.18738 314.51074 357.44519 curveto +314.51073 358.69845 314.19856 359.68282 313.57422 360.39832 curveto +312.94986 361.10925 312.09081 361.46472 310.99707 361.46472 curveto +309.89876 361.46472 309.03743 361.10925 308.41309 360.39832 curveto +307.79329 359.68282 307.4834 358.69845 307.4834 357.44519 curveto +307.4834 356.18738 307.79329 355.20301 308.41309 354.49207 curveto +309.03743 353.78114 309.89876 353.42567 310.99707 353.42566 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +316.39062 350.64343 moveto +317.48438 350.64343 lineto +318.16797 351.71896 318.67838 352.7717 319.01562 353.80164 curveto +319.35742 354.83159 319.52832 355.8547 319.52832 356.87097 curveto +319.52832 357.89181 319.35742 358.91948 319.01562 359.95398 curveto +318.67838 360.98848 318.16797 362.04122 317.48438 363.11218 curveto +316.39062 363.11218 lineto +316.99674 362.06856 317.44791 361.03178 317.74414 360.00183 curveto +318.04492 358.96733 318.19531 357.92371 318.19531 356.87097 curveto +318.19531 355.81824 318.04492 354.77918 317.74414 353.75378 curveto +317.44791 352.7284 316.99674 351.69162 316.39062 350.64343 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +197.1377 371.14343 moveto +196.52701 372.19162 196.07356 373.2284 195.77734 374.25378 curveto +195.48112 375.27918 195.33301 376.31824 195.33301 377.37097 curveto +195.33301 378.42371 195.48112 379.46733 195.77734 380.50183 curveto +196.07812 381.53178 196.53157 382.56856 197.1377 383.61218 curveto +196.04395 383.61218 lineto +195.36035 382.54122 194.84765 381.48848 194.50586 380.45398 curveto +194.16862 379.41948 194 378.39181 194 377.37097 curveto +194 376.3547 194.16862 375.33159 194.50586 374.30164 curveto +194.8431 373.2717 195.35579 372.21896 196.04395 371.14343 curveto +197.1377 371.14343 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +200.00195 380.60437 moveto +202.25781 380.60437 lineto +202.25781 372.81824 lineto +199.80371 373.31042 lineto +199.80371 372.05261 lineto +202.24414 371.56042 lineto +203.625 371.56042 lineto +203.625 380.60437 lineto +205.88086 380.60437 lineto +205.88086 381.76648 lineto +200.00195 381.76648 lineto +200.00195 380.60437 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +211.62988 372.4696 moveto +210.91894 372.46961 210.38346 372.82052 210.02344 373.52234 curveto +209.66797 374.21961 209.49023 375.27007 209.49023 376.67371 curveto +209.49023 378.0728 209.66797 379.12325 210.02344 379.82507 curveto +210.38346 380.52234 210.91894 380.87097 211.62988 380.87097 curveto +212.34537 380.87097 212.88085 380.52234 213.23633 379.82507 curveto +213.59635 379.12325 213.77636 378.0728 213.77637 376.67371 curveto +213.77636 375.27007 213.59635 374.21961 213.23633 373.52234 curveto +212.88085 372.82052 212.34537 372.46961 211.62988 372.4696 curveto +211.62988 371.37585 moveto +212.77376 371.37586 213.64648 371.82931 214.24805 372.73621 curveto +214.85416 373.63856 215.15722 374.95106 215.15723 376.67371 curveto +215.15722 378.39181 214.85416 379.70431 214.24805 380.61121 curveto +213.64648 381.51355 212.77376 381.96472 211.62988 381.96472 curveto +210.486 381.96472 209.611 381.51355 209.00488 380.61121 curveto +208.40332 379.70431 208.10254 378.39181 208.10254 376.67371 curveto +208.10254 374.95106 208.40332 373.63856 209.00488 372.73621 curveto +209.611 371.82931 210.486 371.37586 211.62988 371.37585 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +217.73438 380.03015 moveto +219.17676 380.03015 lineto +219.17676 381.20593 lineto +218.05566 383.39343 lineto +217.17383 383.39343 lineto +217.73438 381.20593 lineto +217.73438 380.03015 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +222.06152 371.56042 moveto +227.48242 371.56042 lineto +227.48242 372.72253 lineto +223.32617 372.72253 lineto +223.32617 375.22449 lineto +223.52669 375.15613 223.72721 375.106 223.92773 375.0741 curveto +224.12825 375.03765 224.32877 375.01942 224.5293 375.01941 curveto +225.66861 375.01942 226.57096 375.33159 227.23633 375.95593 curveto +227.90169 376.58029 228.23437 377.42566 228.23438 378.49207 curveto +228.23437 379.59037 227.89257 380.44487 227.20898 381.05554 curveto +226.52538 381.66166 225.56152 381.96472 224.31738 381.96472 curveto +223.88899 381.96472 223.45149 381.92826 223.00488 381.85535 curveto +222.56282 381.78243 222.10482 381.67306 221.63086 381.52722 curveto +221.63086 380.13953 lineto +222.04101 380.36284 222.46484 380.52918 222.90234 380.63855 curveto +223.33984 380.74793 223.80241 380.80261 224.29004 380.80261 curveto +225.07845 380.80261 225.70279 380.59526 226.16309 380.18054 curveto +226.62337 379.76583 226.85351 379.20301 226.85352 378.49207 curveto +226.85351 377.78113 226.62337 377.21831 226.16309 376.80359 curveto +225.70279 376.38888 225.07845 376.18152 224.29004 376.18152 curveto +223.9209 376.18152 223.55175 376.22254 223.18262 376.30457 curveto +222.81803 376.3866 222.44433 376.51421 222.06152 376.68738 curveto +222.06152 371.56042 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +231.10547 380.03015 moveto +232.54785 380.03015 lineto +232.54785 381.20593 lineto +231.42676 383.39343 lineto +230.54492 383.39343 lineto +231.10547 381.20593 lineto +231.10547 380.03015 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +238.20801 374.99207 moveto +237.53353 374.99207 237.00032 375.25639 236.6084 375.78503 curveto +236.21647 376.30913 236.02051 377.02918 236.02051 377.94519 curveto +236.02051 378.86121 236.21419 379.58354 236.60156 380.11218 curveto +236.99349 380.63627 237.52897 380.89832 238.20801 380.89832 curveto +238.87792 380.89832 239.40885 380.63399 239.80078 380.10535 curveto +240.1927 379.5767 240.38867 378.85665 240.38867 377.94519 curveto +240.38867 377.03829 240.1927 376.32052 239.80078 375.79187 curveto +239.40885 375.25867 238.87792 374.99207 238.20801 374.99207 curveto +238.20801 373.92566 moveto +239.30175 373.92567 240.1608 374.28114 240.78516 374.99207 curveto +241.4095 375.70301 241.72167 376.68738 241.72168 377.94519 curveto +241.72167 379.19845 241.4095 380.18282 240.78516 380.89832 curveto +240.1608 381.60925 239.30175 381.96472 238.20801 381.96472 curveto +237.1097 381.96472 236.24837 381.60925 235.62402 380.89832 curveto +235.00423 380.18282 234.69434 379.19845 234.69434 377.94519 curveto +234.69434 376.68738 235.00423 375.70301 235.62402 374.99207 curveto +236.24837 374.28114 237.1097 373.92567 238.20801 373.92566 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +243.60156 371.14343 moveto +244.69531 371.14343 lineto +245.3789 372.21896 245.88932 373.2717 246.22656 374.30164 curveto +246.56836 375.33159 246.73925 376.3547 246.73926 377.37097 curveto +246.73925 378.39181 246.56836 379.41948 246.22656 380.45398 curveto +245.88932 381.48848 245.3789 382.54122 244.69531 383.61218 curveto +243.60156 383.61218 lineto +244.20768 382.56856 244.65885 381.53178 244.95508 380.50183 curveto +245.25586 379.46733 245.40625 378.42371 245.40625 377.37097 curveto +245.40625 376.31824 245.25586 375.27918 244.95508 374.25378 curveto +244.65885 373.2284 244.20768 372.19162 243.60156 371.14343 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +252.1377 371.98914 moveto +251.52701 373.03732 251.07356 374.07411 250.77734 375.09949 curveto +250.48112 376.12488 250.33301 377.16395 250.33301 378.21667 curveto +250.33301 379.26941 250.48112 380.31303 250.77734 381.34753 curveto +251.07812 382.37748 251.53157 383.41427 252.1377 384.45789 curveto +251.04395 384.45789 lineto +250.36035 383.38692 249.84765 382.33419 249.50586 381.29968 curveto +249.16862 380.26518 249 379.23751 249 378.21667 curveto +249 377.2004 249.16862 376.17729 249.50586 375.14734 curveto +249.8431 374.1174 250.35579 373.06467 251.04395 371.98914 curveto +252.1377 371.98914 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +255.00195 381.45007 moveto +257.25781 381.45007 lineto +257.25781 373.66394 lineto +254.80371 374.15613 lineto +254.80371 372.89832 lineto +257.24414 372.40613 lineto +258.625 372.40613 lineto +258.625 381.45007 lineto +260.88086 381.45007 lineto +260.88086 382.61218 lineto +255.00195 382.61218 lineto +255.00195 381.45007 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +263.91602 381.45007 moveto +266.17188 381.45007 lineto +266.17188 373.66394 lineto +263.71777 374.15613 lineto +263.71777 372.89832 lineto +266.1582 372.40613 lineto +267.53906 372.40613 lineto +267.53906 381.45007 lineto +269.79492 381.45007 lineto +269.79492 382.61218 lineto +263.91602 382.61218 lineto +263.91602 381.45007 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +272.73438 380.87585 moveto +274.17676 380.87585 lineto +274.17676 382.05164 lineto +273.05566 384.23914 lineto +272.17383 384.23914 lineto +272.73438 382.05164 lineto +272.73438 380.87585 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +280.17188 376.95886 moveto +279.55208 376.95887 279.05989 377.17078 278.69531 377.5946 curveto +278.33528 378.01844 278.15527 378.59949 278.15527 379.33777 curveto +278.15527 380.0715 278.33528 380.65255 278.69531 381.08093 curveto +279.05989 381.50476 279.55208 381.71668 280.17188 381.71667 curveto +280.79166 381.71668 281.28157 381.50476 281.6416 381.08093 curveto +282.00618 380.65255 282.18847 380.0715 282.18848 379.33777 curveto +282.18847 378.59949 282.00618 378.01844 281.6416 377.5946 curveto +281.28157 377.17078 280.79166 376.95887 280.17188 376.95886 curveto +282.91309 372.63171 moveto +282.91309 373.88953 lineto +282.56672 373.72547 282.21581 373.60015 281.86035 373.51355 curveto +281.50943 373.42697 281.1608 373.38368 280.81445 373.38367 curveto +279.90299 373.38368 279.20573 373.69129 278.72266 374.30652 curveto +278.24414 374.92176 277.9707 375.85145 277.90234 377.09558 curveto +278.17122 376.6991 278.50846 376.39604 278.91406 376.1864 curveto +279.31966 375.97222 279.76627 375.86512 280.25391 375.86511 curveto +281.27929 375.86512 282.08821 376.17729 282.68066 376.80164 curveto +283.27766 377.42143 283.57616 378.26681 283.57617 379.33777 curveto +283.57616 380.38595 283.26627 381.22677 282.64648 381.86023 curveto +282.02669 382.49369 281.20182 382.81042 280.17188 382.81042 curveto +278.99153 382.81042 278.08919 382.35925 277.46484 381.45691 curveto +276.84049 380.55001 276.52832 379.23751 276.52832 377.51941 curveto +276.52832 375.90613 276.91113 374.62098 277.67676 373.66394 curveto +278.44238 372.70236 279.47005 372.22157 280.75977 372.22156 curveto +281.10611 372.22157 281.45475 372.25575 281.80566 372.3241 curveto +282.16113 372.39247 282.53027 372.49501 282.91309 372.63171 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +286.10547 380.87585 moveto +287.54785 380.87585 lineto +287.54785 382.05164 lineto +286.42676 384.23914 lineto +285.54492 384.23914 lineto +286.10547 382.05164 lineto +286.10547 380.87585 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +293.20801 375.83777 moveto +292.53353 375.83778 292.00032 376.1021 291.6084 376.63074 curveto +291.21647 377.15483 291.02051 377.87488 291.02051 378.79089 curveto +291.02051 379.70691 291.21419 380.42924 291.60156 380.95789 curveto +291.99349 381.48198 292.52897 381.74402 293.20801 381.74402 curveto +293.87792 381.74402 294.40885 381.4797 294.80078 380.95105 curveto +295.1927 380.42241 295.38867 379.70235 295.38867 378.79089 curveto +295.38867 377.884 295.1927 377.16622 294.80078 376.63757 curveto +294.40885 376.10438 293.87792 375.83778 293.20801 375.83777 curveto +293.20801 374.77136 moveto +294.30175 374.77137 295.1608 375.12684 295.78516 375.83777 curveto +296.4095 376.54871 296.72167 377.53309 296.72168 378.79089 curveto +296.72167 380.04415 296.4095 381.02853 295.78516 381.74402 curveto +295.1608 382.45496 294.30175 382.81042 293.20801 382.81042 curveto +292.1097 382.81042 291.24837 382.45496 290.62402 381.74402 curveto +290.00423 381.02853 289.69434 380.04415 289.69434 378.79089 curveto +289.69434 377.53309 290.00423 376.54871 290.62402 375.83777 curveto +291.24837 375.12684 292.1097 374.77137 293.20801 374.77136 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +298.60156 371.98914 moveto +299.69531 371.98914 lineto +300.3789 373.06467 300.88932 374.1174 301.22656 375.14734 curveto +301.56836 376.17729 301.73925 377.2004 301.73926 378.21667 curveto +301.73925 379.23751 301.56836 380.26518 301.22656 381.29968 curveto +300.88932 382.33419 300.3789 383.38692 299.69531 384.45789 curveto +298.60156 384.45789 lineto +299.20768 383.41427 299.65885 382.37748 299.95508 381.34753 curveto +300.25586 380.31303 300.40625 379.26941 300.40625 378.21667 curveto +300.40625 377.16395 300.25586 376.12488 299.95508 375.09949 curveto +299.65885 374.07411 299.20768 373.03732 298.60156 371.98914 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +238.37402 312.40613 moveto +239.75488 312.40613 lineto +239.75488 322.61218 lineto +238.37402 322.61218 lineto +238.37402 312.40613 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +247.3291 315.18152 moveto +247.3291 316.37097 lineto +246.97363 316.18869 246.60449 316.05197 246.22168 315.96082 curveto +245.83886 315.86968 245.44238 315.8241 245.03223 315.8241 curveto +244.40787 315.8241 243.93847 315.91981 243.62402 316.11121 curveto +243.31413 316.30262 243.15918 316.58973 243.15918 316.97253 curveto +243.15918 317.26421 243.27083 317.49435 243.49414 317.66296 curveto +243.71745 317.82703 244.16634 317.98426 244.84082 318.13464 curveto +245.27148 318.23035 lineto +246.16471 318.42176 246.79817 318.69292 247.17188 319.04382 curveto +247.55012 319.39018 247.73925 319.87553 247.73926 320.49988 curveto +247.73925 321.21082 247.4567 321.77364 246.8916 322.18835 curveto +246.33105 322.60307 245.55859 322.81042 244.57422 322.81042 curveto +244.16406 322.81042 243.73567 322.76941 243.28906 322.68738 curveto +242.847 322.6099 242.37988 322.49141 241.8877 322.33191 curveto +241.8877 321.03308 lineto +242.35254 321.27462 242.81055 321.45691 243.26172 321.57996 curveto +243.71289 321.69845 244.1595 321.75769 244.60156 321.75769 curveto +245.19401 321.75769 245.64974 321.65743 245.96875 321.45691 curveto +246.28776 321.25183 246.44726 320.96472 246.44727 320.59558 curveto +246.44726 320.25379 246.33105 319.99174 246.09863 319.80945 curveto +245.87076 319.62716 245.36718 319.4517 244.58789 319.28308 curveto +244.15039 319.18054 lineto +243.37109 319.01648 242.80827 318.76583 242.46191 318.42859 curveto +242.11556 318.0868 241.94238 317.61967 241.94238 317.02722 curveto +241.94238 316.30718 242.19759 315.75119 242.70801 315.35925 curveto +243.21842 314.96733 243.94303 314.77137 244.88184 314.77136 curveto +245.34668 314.77137 245.78418 314.80555 246.19434 314.8739 curveto +246.60449 314.94227 246.98274 315.04481 247.3291 315.18152 curveto +fill +grestore +gsave [1 0 0 1 3.250285 1] concat +gsave +0 0 0 setrgbcolor +newpath +180.84082 436.23914 moveto +180.23014 437.28732 179.77669 438.32411 179.48047 439.34949 curveto +179.18424 440.37488 179.03613 441.41395 179.03613 442.46667 curveto +179.03613 443.51941 179.18424 444.56303 179.48047 445.59753 curveto +179.78125 446.62748 180.2347 447.66427 180.84082 448.70789 curveto +179.74707 448.70789 lineto +179.06347 447.63692 178.55078 446.58419 178.20898 445.54968 curveto +177.87174 444.51518 177.70312 443.48751 177.70312 442.46667 curveto +177.70312 441.4504 177.87174 440.42729 178.20898 439.39734 curveto +178.54622 438.3674 179.05892 437.31467 179.74707 436.23914 curveto +180.84082 436.23914 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +184.65527 445.70007 moveto +189.47461 445.70007 lineto +189.47461 446.86218 lineto +182.99414 446.86218 lineto +182.99414 445.70007 lineto +183.51823 445.15776 184.23144 444.43087 185.13379 443.51941 curveto +186.04069 442.6034 186.61035 442.01323 186.84277 441.7489 curveto +187.28483 441.25216 187.59244 440.83289 187.76562 440.49109 curveto +187.94335 440.14474 188.03222 439.80522 188.03223 439.47253 curveto +188.03222 438.93022 187.84081 438.48817 187.45801 438.14636 curveto +187.07975 437.80457 186.58528 437.63368 185.97461 437.63367 curveto +185.54166 437.63368 185.08366 437.70887 184.60059 437.85925 curveto +184.12207 438.00965 183.60937 438.23752 183.0625 438.54285 curveto +183.0625 437.14832 lineto +183.61849 436.92502 184.13802 436.7564 184.62109 436.64246 curveto +185.10416 436.52853 185.54622 436.47157 185.94727 436.47156 curveto +187.00455 436.47157 187.84765 436.73589 188.47656 437.26453 curveto +189.10546 437.79318 189.41991 438.49956 189.41992 439.38367 curveto +189.41991 439.80294 189.34016 440.20171 189.18066 440.57996 curveto +189.02571 440.95366 188.74088 441.39572 188.32617 441.90613 curveto +188.21223 442.03829 187.84993 442.42111 187.23926 443.05457 curveto +186.62858 443.68347 185.76725 444.56531 184.65527 445.70007 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +192.52344 445.12585 moveto +193.96582 445.12585 lineto +193.96582 446.30164 lineto +192.84473 448.48914 lineto +191.96289 448.48914 lineto +192.52344 446.30164 lineto +192.52344 445.12585 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +199.79004 437.56531 moveto +199.0791 437.56532 198.54362 437.91623 198.18359 438.61804 curveto +197.82812 439.31532 197.65039 440.36577 197.65039 441.76941 curveto +197.65039 443.1685 197.82812 444.21896 198.18359 444.92078 curveto +198.54362 445.61804 199.0791 445.96668 199.79004 445.96667 curveto +200.50553 445.96668 201.04101 445.61804 201.39648 444.92078 curveto +201.7565 444.21896 201.93652 443.1685 201.93652 441.76941 curveto +201.93652 440.36577 201.7565 439.31532 201.39648 438.61804 curveto +201.04101 437.91623 200.50553 437.56532 199.79004 437.56531 curveto +199.79004 436.47156 moveto +200.93391 436.47157 201.80663 436.92502 202.4082 437.83191 curveto +203.01432 438.73426 203.31737 440.04676 203.31738 441.76941 curveto +203.31737 443.48751 203.01432 444.80001 202.4082 445.70691 curveto +201.80663 446.60925 200.93391 447.06042 199.79004 447.06042 curveto +198.64616 447.06042 197.77116 446.60925 197.16504 445.70691 curveto +196.56348 444.80001 196.26269 443.48751 196.2627 441.76941 curveto +196.26269 440.04676 196.56348 438.73426 197.16504 437.83191 curveto +197.77116 436.92502 198.64616 436.47157 199.79004 436.47156 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +205.89453 445.12585 moveto +207.33691 445.12585 lineto +207.33691 446.30164 lineto +206.21582 448.48914 lineto +205.33398 448.48914 lineto +205.89453 446.30164 lineto +205.89453 445.12585 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +215.06836 442.94519 moveto +215.06835 442.03374 214.87923 441.32736 214.50098 440.82605 curveto +214.12727 440.32475 213.60091 440.0741 212.92188 440.0741 curveto +212.24739 440.0741 211.72103 440.32475 211.34277 440.82605 curveto +210.96907 441.32736 210.78222 442.03374 210.78223 442.94519 curveto +210.78222 443.85209 210.96907 444.5562 211.34277 445.0575 curveto +211.72103 445.5588 212.24739 445.80945 212.92188 445.80945 curveto +213.60091 445.80945 214.12727 445.5588 214.50098 445.0575 curveto +214.87923 444.5562 215.06835 443.85209 215.06836 442.94519 curveto +216.32617 445.91199 moveto +216.32616 447.21537 216.03678 448.1838 215.45801 448.81726 curveto +214.87923 449.45528 213.99283 449.77429 212.79883 449.77429 curveto +212.35677 449.77429 211.93978 449.74011 211.54785 449.67175 curveto +211.15592 449.60795 210.77539 449.50769 210.40625 449.37097 curveto +210.40625 448.14734 lineto +210.77539 448.34786 211.13997 448.49597 211.5 448.59167 curveto +211.86002 448.68738 212.22688 448.73523 212.60059 448.73523 curveto +213.42545 448.73523 214.04296 448.51876 214.45312 448.08582 curveto +214.86328 447.65743 215.06835 447.00802 215.06836 446.13757 curveto +215.06836 445.5155 lineto +214.80859 445.96668 214.47591 446.30391 214.07031 446.52722 curveto +213.66471 446.75053 213.17936 446.86218 212.61426 446.86218 curveto +211.67545 446.86218 210.91894 446.50444 210.34473 445.78894 curveto +209.77051 445.07345 209.4834 444.12553 209.4834 442.94519 curveto +209.4834 441.7603 209.77051 440.81011 210.34473 440.0946 curveto +210.91894 439.37912 211.67545 439.02137 212.61426 439.02136 curveto +213.17936 439.02137 213.66471 439.13302 214.07031 439.35632 curveto +214.47591 439.57964 214.80859 439.91688 215.06836 440.36804 curveto +215.06836 439.20593 lineto +216.32617 439.20593 lineto +216.32617 445.91199 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +218.71875 436.23914 moveto +219.8125 436.23914 lineto +220.49609 437.31467 221.00651 438.3674 221.34375 439.39734 curveto +221.68554 440.42729 221.85644 441.4504 221.85645 442.46667 curveto +221.85644 443.48751 221.68554 444.51518 221.34375 445.54968 curveto +221.00651 446.58419 220.49609 447.63692 219.8125 448.70789 curveto +218.71875 448.70789 lineto +219.32487 447.66427 219.77604 446.62748 220.07227 445.59753 curveto +220.37304 444.56303 220.52343 443.51941 220.52344 442.46667 curveto +220.52343 441.41395 220.37304 440.37488 220.07227 439.34949 curveto +219.77604 438.32411 219.32487 437.28732 218.71875 436.23914 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +228.53711 436.77234 moveto +227.92643 437.82053 227.47298 438.85731 227.17676 439.88269 curveto +226.88053 440.90809 226.73242 441.94715 226.73242 442.99988 curveto +226.73242 444.05262 226.88053 445.09623 227.17676 446.13074 curveto +227.47754 447.16069 227.93099 448.19747 228.53711 449.24109 curveto +227.44336 449.24109 lineto +226.75976 448.17012 226.24707 447.11739 225.90527 446.08289 curveto +225.56803 445.04838 225.39941 444.02071 225.39941 442.99988 curveto +225.39941 441.98361 225.56803 440.9605 225.90527 439.93054 curveto +226.24251 438.9006 226.75521 437.84787 227.44336 436.77234 curveto +228.53711 436.77234 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +235.3457 441.89246 moveto +236.0065 442.03374 236.52148 442.32768 236.89062 442.77429 curveto +237.26432 443.22091 237.45116 443.77234 237.45117 444.42859 curveto +237.45116 445.43575 237.10481 446.21505 236.41211 446.76648 curveto +235.71939 447.31791 234.73502 447.59363 233.45898 447.59363 curveto +233.0306 447.59363 232.58854 447.55033 232.13281 447.46375 curveto +231.68164 447.38171 231.21452 447.25639 230.73145 447.08777 curveto +230.73145 445.75476 lineto +231.11426 445.97807 231.53353 446.14669 231.98926 446.26062 curveto +232.44498 446.37455 232.92122 446.43152 233.41797 446.43152 curveto +234.28385 446.43152 234.94238 446.26062 235.39355 445.91882 curveto +235.84928 445.57703 236.07714 445.08028 236.07715 444.42859 curveto +236.07714 443.82703 235.86523 443.35763 235.44141 443.02039 curveto +235.02213 442.67859 234.43652 442.5077 233.68457 442.50769 curveto +232.49512 442.50769 lineto +232.49512 441.37292 lineto +233.73926 441.37292 lineto +234.41829 441.37293 234.93782 441.23849 235.29785 440.9696 curveto +235.65787 440.69617 235.83788 440.30425 235.83789 439.79382 curveto +235.83788 439.26974 235.65104 438.8687 235.27734 438.5907 curveto +234.9082 438.30816 234.37727 438.16688 233.68457 438.16687 curveto +233.30631 438.16688 232.90071 438.20789 232.46777 438.28992 curveto +232.03483 438.37196 231.55859 438.49956 231.03906 438.67273 curveto +231.03906 437.44226 lineto +231.56315 437.29644 232.05306 437.18706 232.50879 437.11414 curveto +232.96907 437.04123 233.40201 437.00477 233.80762 437.00476 curveto +234.85579 437.00477 235.68522 437.24403 236.2959 437.72253 curveto +236.90657 438.1965 237.21191 438.83908 237.21191 439.65027 curveto +237.21191 440.21538 237.05012 440.6939 236.72656 441.08582 curveto +236.40299 441.47319 235.9427 441.74207 235.3457 441.89246 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +240.21973 445.65906 moveto +241.66211 445.65906 lineto +241.66211 446.83484 lineto +240.54102 449.02234 lineto +239.65918 449.02234 lineto +240.21973 446.83484 lineto +240.21973 445.65906 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +244.77246 446.23328 moveto +247.02832 446.23328 lineto +247.02832 438.44714 lineto +244.57422 438.93933 lineto +244.57422 437.68152 lineto +247.01465 437.18933 lineto +248.39551 437.18933 lineto +248.39551 446.23328 lineto +250.65137 446.23328 lineto +250.65137 447.39539 lineto +244.77246 447.39539 lineto +244.77246 446.23328 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +253.59082 445.65906 moveto +255.0332 445.65906 lineto +255.0332 446.83484 lineto +253.91211 449.02234 lineto +253.03027 449.02234 lineto +253.59082 446.83484 lineto +253.59082 445.65906 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +260.69336 440.62097 moveto +260.01888 440.62098 259.48567 440.8853 259.09375 441.41394 curveto +258.70182 441.93803 258.50586 442.65809 258.50586 443.5741 curveto +258.50586 444.49012 258.69954 445.21245 259.08691 445.74109 curveto +259.47884 446.26518 260.01432 446.52722 260.69336 446.52722 curveto +261.36328 446.52722 261.8942 446.2629 262.28613 445.73425 curveto +262.67805 445.20561 262.87402 444.48556 262.87402 443.5741 curveto +262.87402 442.6672 262.67805 441.94943 262.28613 441.42078 curveto +261.8942 440.88758 261.36328 440.62098 260.69336 440.62097 curveto +260.69336 439.55457 moveto +261.7871 439.55457 262.64615 439.91004 263.27051 440.62097 curveto +263.89485 441.33192 264.20702 442.31629 264.20703 443.5741 curveto +264.20702 444.82735 263.89485 445.81173 263.27051 446.52722 curveto +262.64615 447.23816 261.7871 447.59363 260.69336 447.59363 curveto +259.59505 447.59363 258.73372 447.23816 258.10938 446.52722 curveto +257.48958 445.81173 257.17969 444.82735 257.17969 443.5741 curveto +257.17969 442.31629 257.48958 441.33192 258.10938 440.62097 curveto +258.73372 439.91004 259.59505 439.55457 260.69336 439.55457 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +266.08691 436.77234 moveto +267.18066 436.77234 lineto +267.86425 437.84787 268.37467 438.9006 268.71191 439.93054 curveto +269.05371 440.9605 269.22461 441.98361 269.22461 442.99988 curveto +269.22461 444.02071 269.05371 445.04838 268.71191 446.08289 curveto +268.37467 447.11739 267.86425 448.17012 267.18066 449.24109 curveto +266.08691 449.24109 lineto +266.69303 448.19747 267.1442 447.16069 267.44043 446.13074 curveto +267.74121 445.09623 267.8916 444.05262 267.8916 442.99988 curveto +267.8916 441.94715 267.74121 440.90809 267.44043 439.88269 curveto +267.1442 438.85731 266.69303 437.82053 266.08691 436.77234 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +275.5498 436.77234 moveto +274.93912 437.82053 274.48567 438.85731 274.18945 439.88269 curveto +273.89323 440.90809 273.74511 441.94715 273.74512 442.99988 curveto +273.74511 444.05262 273.89323 445.09623 274.18945 446.13074 curveto +274.49023 447.16069 274.94368 448.19747 275.5498 449.24109 curveto +274.45605 449.24109 lineto +273.77246 448.17012 273.25976 447.11739 272.91797 446.08289 curveto +272.58073 445.04838 272.41211 444.02071 272.41211 442.99988 curveto +272.41211 441.98361 272.58073 440.9605 272.91797 439.93054 curveto +273.25521 438.9006 273.7679 437.84787 274.45605 436.77234 curveto +275.5498 436.77234 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +281.96875 438.39246 moveto +278.48242 443.8407 lineto +281.96875 443.8407 lineto +281.96875 438.39246 lineto +281.60645 437.18933 moveto +283.34277 437.18933 lineto +283.34277 443.8407 lineto +284.79883 443.8407 lineto +284.79883 444.98914 lineto +283.34277 444.98914 lineto +283.34277 447.39539 lineto +281.96875 447.39539 lineto +281.96875 444.98914 lineto +277.36133 444.98914 lineto +277.36133 443.65613 lineto +281.60645 437.18933 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +287.23242 445.65906 moveto +288.6748 445.65906 lineto +288.6748 446.83484 lineto +287.55371 449.02234 lineto +286.67188 449.02234 lineto +287.23242 446.83484 lineto +287.23242 445.65906 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +292.73535 446.23328 moveto +297.55469 446.23328 lineto +297.55469 447.39539 lineto +291.07422 447.39539 lineto +291.07422 446.23328 lineto +291.59831 445.69096 292.31152 444.96407 293.21387 444.05261 curveto +294.12076 443.1366 294.69043 442.54643 294.92285 442.2821 curveto +295.3649 441.78537 295.67252 441.36609 295.8457 441.02429 curveto +296.02343 440.67794 296.1123 440.33843 296.1123 440.00574 curveto +296.1123 439.46343 295.92089 439.02137 295.53809 438.67957 curveto +295.15983 438.33778 294.66536 438.16688 294.05469 438.16687 curveto +293.62174 438.16688 293.16373 438.24207 292.68066 438.39246 curveto +292.20215 438.54286 291.68945 438.77072 291.14258 439.07605 curveto +291.14258 437.68152 lineto +291.69857 437.45822 292.2181 437.2896 292.70117 437.17566 curveto +293.18424 437.06174 293.6263 437.00477 294.02734 437.00476 curveto +295.08463 437.00477 295.92773 437.26909 296.55664 437.79773 curveto +297.18554 438.32638 297.49999 439.03276 297.5 439.91687 curveto +297.49999 440.33615 297.42024 440.73491 297.26074 441.11316 curveto +297.10579 441.48686 296.82096 441.92892 296.40625 442.43933 curveto +296.29231 442.5715 295.93001 442.95431 295.31934 443.58777 curveto +294.70865 444.21668 293.84733 445.09851 292.73535 446.23328 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +300.60352 445.65906 moveto +302.0459 445.65906 lineto +302.0459 446.83484 lineto +300.9248 449.02234 lineto +300.04297 449.02234 lineto +300.60352 446.83484 lineto +300.60352 445.65906 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +307.70605 440.62097 moveto +307.03157 440.62098 306.49837 440.8853 306.10645 441.41394 curveto +305.71452 441.93803 305.51855 442.65809 305.51855 443.5741 curveto +305.51855 444.49012 305.71224 445.21245 306.09961 445.74109 curveto +306.49153 446.26518 307.02701 446.52722 307.70605 446.52722 curveto +308.37597 446.52722 308.9069 446.2629 309.29883 445.73425 curveto +309.69075 445.20561 309.88671 444.48556 309.88672 443.5741 curveto +309.88671 442.6672 309.69075 441.94943 309.29883 441.42078 curveto +308.9069 440.88758 308.37597 440.62098 307.70605 440.62097 curveto +307.70605 439.55457 moveto +308.7998 439.55457 309.65885 439.91004 310.2832 440.62097 curveto +310.90754 441.33192 311.21972 442.31629 311.21973 443.5741 curveto +311.21972 444.82735 310.90754 445.81173 310.2832 446.52722 curveto +309.65885 447.23816 308.7998 447.59363 307.70605 447.59363 curveto +306.60774 447.59363 305.74642 447.23816 305.12207 446.52722 curveto +304.50228 445.81173 304.19238 444.82735 304.19238 443.5741 curveto +304.19238 442.31629 304.50228 441.33192 305.12207 440.62097 curveto +305.74642 439.91004 306.60774 439.55457 307.70605 439.55457 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +313.09961 436.77234 moveto +314.19336 436.77234 lineto +314.87695 437.84787 315.38737 438.9006 315.72461 439.93054 curveto +316.0664 440.9605 316.2373 441.98361 316.2373 442.99988 curveto +316.2373 444.02071 316.0664 445.04838 315.72461 446.08289 curveto +315.38737 447.11739 314.87695 448.17012 314.19336 449.24109 curveto +313.09961 449.24109 lineto +313.70573 448.19747 314.1569 447.16069 314.45312 446.13074 curveto +314.7539 445.09623 314.90429 444.05262 314.9043 442.99988 curveto +314.90429 441.94715 314.7539 440.90809 314.45312 439.88269 curveto +314.1569 438.85731 313.70573 437.82053 313.09961 436.77234 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +181.16895 456.3075 moveto +180.55826 457.35568 180.10481 458.39246 179.80859 459.41785 curveto +179.51237 460.44324 179.36426 461.48231 179.36426 462.53503 curveto +179.36426 463.58777 179.51237 464.63139 179.80859 465.66589 curveto +180.10937 466.69584 180.56282 467.73262 181.16895 468.77625 curveto +180.0752 468.77625 lineto +179.3916 467.70528 178.8789 466.65255 178.53711 465.61804 curveto +178.19987 464.58354 178.03125 463.55587 178.03125 462.53503 curveto +178.03125 461.51876 178.19987 460.49565 178.53711 459.4657 curveto +178.87435 458.43576 179.38704 457.38303 180.0752 456.3075 curveto +181.16895 456.3075 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +183.80762 456.72449 moveto +189.22852 456.72449 lineto +189.22852 457.8866 lineto +185.07227 457.8866 lineto +185.07227 460.38855 lineto +185.27278 460.3202 185.4733 460.27007 185.67383 460.23816 curveto +185.87435 460.20171 186.07487 460.18348 186.27539 460.18347 curveto +187.41471 460.18348 188.31705 460.49565 188.98242 461.12 curveto +189.64778 461.74435 189.98046 462.58973 189.98047 463.65613 curveto +189.98046 464.75444 189.63866 465.60893 188.95508 466.2196 curveto +188.27148 466.82572 187.30761 467.12878 186.06348 467.12878 curveto +185.63509 467.12878 185.19759 467.09233 184.75098 467.01941 curveto +184.30892 466.94649 183.85091 466.83712 183.37695 466.69128 curveto +183.37695 465.30359 lineto +183.78711 465.5269 184.21094 465.69324 184.64844 465.80261 curveto +185.08593 465.91199 185.5485 465.96668 186.03613 465.96667 curveto +186.82454 465.96668 187.44889 465.75932 187.90918 465.3446 curveto +188.36946 464.92989 188.5996 464.36707 188.59961 463.65613 curveto +188.5996 462.94519 188.36946 462.38237 187.90918 461.96765 curveto +187.44889 461.55294 186.82454 461.34559 186.03613 461.34558 curveto +185.66699 461.34559 185.29785 461.3866 184.92871 461.46863 curveto +184.56413 461.55066 184.19043 461.67827 183.80762 461.85144 curveto +183.80762 456.72449 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +192.85156 465.19421 moveto +194.29395 465.19421 lineto +194.29395 466.37 lineto +193.17285 468.5575 lineto +192.29102 468.5575 lineto +192.85156 466.37 lineto +192.85156 465.19421 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +201.34863 461.42761 moveto +202.00943 461.56889 202.52441 461.86284 202.89355 462.30945 curveto +203.26725 462.75607 203.45409 463.3075 203.4541 463.96375 curveto +203.45409 464.97091 203.10774 465.7502 202.41504 466.30164 curveto +201.72232 466.85307 200.73795 467.12878 199.46191 467.12878 curveto +199.03353 467.12878 198.59147 467.08549 198.13574 466.9989 curveto +197.68457 466.91687 197.21745 466.79154 196.73438 466.62292 curveto +196.73438 465.28992 lineto +197.11719 465.51323 197.53646 465.68185 197.99219 465.79578 curveto +198.44791 465.90971 198.92415 465.96668 199.4209 465.96667 curveto +200.28678 465.96668 200.94531 465.79578 201.39648 465.45398 curveto +201.85221 465.11218 202.08007 464.61544 202.08008 463.96375 curveto +202.08007 463.36219 201.86816 462.89279 201.44434 462.55554 curveto +201.02506 462.21375 200.43945 462.04285 199.6875 462.04285 curveto +198.49805 462.04285 lineto +198.49805 460.90808 lineto +199.74219 460.90808 lineto +200.42122 460.90809 200.94075 460.77365 201.30078 460.50476 curveto +201.6608 460.23133 201.84081 459.8394 201.84082 459.32898 curveto +201.84081 458.8049 201.65397 458.40386 201.28027 458.12585 curveto +200.91113 457.84331 200.3802 457.70204 199.6875 457.70203 curveto +199.30924 457.70204 198.90364 457.74305 198.4707 457.82507 curveto +198.03776 457.90711 197.56152 458.03472 197.04199 458.20789 curveto +197.04199 456.97742 lineto +197.56608 456.83159 198.05599 456.72222 198.51172 456.64929 curveto +198.972 456.57639 199.40494 456.53993 199.81055 456.53992 curveto +200.85872 456.53993 201.68815 456.77918 202.29883 457.25769 curveto +202.9095 457.73166 203.21484 458.37424 203.21484 459.18542 curveto +203.21484 459.75054 203.05305 460.22905 202.72949 460.62097 curveto +202.40592 461.00835 201.94563 461.27723 201.34863 461.42761 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +206.22266 465.19421 moveto +207.66504 465.19421 lineto +207.66504 466.37 lineto +206.54395 468.5575 lineto +205.66211 468.5575 lineto +206.22266 466.37 lineto +206.22266 465.19421 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +213.3252 460.15613 moveto +212.65071 460.15613 212.11751 460.42046 211.72559 460.9491 curveto +211.33366 461.47319 211.13769 462.19324 211.1377 463.10925 curveto +211.13769 464.02527 211.33138 464.7476 211.71875 465.27625 curveto +212.11067 465.80033 212.64616 466.06238 213.3252 466.06238 curveto +213.99511 466.06238 214.52604 465.79806 214.91797 465.26941 curveto +215.30989 464.74077 215.50585 464.02071 215.50586 463.10925 curveto +215.50585 462.20236 215.30989 461.48458 214.91797 460.95593 curveto +214.52604 460.42274 213.99511 460.15613 213.3252 460.15613 curveto +213.3252 459.08972 moveto +214.41894 459.08973 215.27799 459.4452 215.90234 460.15613 curveto +216.52669 460.86707 216.83886 461.85145 216.83887 463.10925 curveto +216.83886 464.36251 216.52669 465.34688 215.90234 466.06238 curveto +215.27799 466.77332 214.41894 467.12878 213.3252 467.12878 curveto +212.22688 467.12878 211.36556 466.77332 210.74121 466.06238 curveto +210.12142 465.34688 209.81152 464.36251 209.81152 463.10925 curveto +209.81152 461.85145 210.12142 460.86707 210.74121 460.15613 curveto +211.36556 459.4452 212.22688 459.08973 213.3252 459.08972 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +218.71875 456.3075 moveto +219.8125 456.3075 lineto +220.49609 457.38303 221.00651 458.43576 221.34375 459.4657 curveto +221.68554 460.49565 221.85644 461.51876 221.85645 462.53503 curveto +221.85644 463.55587 221.68554 464.58354 221.34375 465.61804 curveto +221.00651 466.65255 220.49609 467.70528 219.8125 468.77625 curveto +218.71875 468.77625 lineto +219.32487 467.73262 219.77604 466.69584 220.07227 465.66589 curveto +220.37304 464.63139 220.52343 463.58777 220.52344 462.53503 curveto +220.52343 461.48231 220.37304 460.44324 220.07227 459.41785 curveto +219.77604 458.39246 219.32487 457.35568 218.71875 456.3075 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +228.20898 455.77429 moveto +227.5983 456.82248 227.14485 457.85926 226.84863 458.88464 curveto +226.55241 459.91004 226.40429 460.9491 226.4043 462.00183 curveto +226.40429 463.05457 226.55241 464.09819 226.84863 465.13269 curveto +227.14941 466.16264 227.60286 467.19942 228.20898 468.24304 curveto +227.11523 468.24304 lineto +226.43164 467.17208 225.91894 466.11934 225.57715 465.08484 curveto +225.23991 464.05034 225.07129 463.02267 225.07129 462.00183 curveto +225.07129 460.98556 225.23991 459.96245 225.57715 458.9325 curveto +225.91439 457.90256 226.42708 456.84982 227.11523 455.77429 curveto +228.20898 455.77429 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +233.95801 460.74402 moveto +233.33821 460.74402 232.84603 460.95594 232.48145 461.37976 curveto +232.12142 461.80359 231.9414 462.38465 231.94141 463.12292 curveto +231.9414 463.85665 232.12142 464.43771 232.48145 464.86609 curveto +232.84603 465.28992 233.33821 465.50183 233.95801 465.50183 curveto +234.57779 465.50183 235.0677 465.28992 235.42773 464.86609 curveto +235.79231 464.43771 235.9746 463.85665 235.97461 463.12292 curveto +235.9746 462.38465 235.79231 461.80359 235.42773 461.37976 curveto +235.0677 460.95594 234.57779 460.74402 233.95801 460.74402 curveto +236.69922 456.41687 moveto +236.69922 457.67468 lineto +236.35286 457.51063 236.00195 457.3853 235.64648 457.29871 curveto +235.29557 457.21213 234.94693 457.16883 234.60059 457.16882 curveto +233.68912 457.16883 232.99186 457.47645 232.50879 458.09167 curveto +232.03027 458.70692 231.75683 459.6366 231.68848 460.88074 curveto +231.95735 460.48426 232.29459 460.1812 232.7002 459.97156 curveto +233.10579 459.75737 233.5524 459.65028 234.04004 459.65027 curveto +235.06542 459.65028 235.87434 459.96245 236.4668 460.58679 curveto +237.06379 461.20659 237.3623 462.05197 237.3623 463.12292 curveto +237.3623 464.1711 237.0524 465.01192 236.43262 465.64539 curveto +235.81282 466.27885 234.98795 466.59558 233.95801 466.59558 curveto +232.77767 466.59558 231.87532 466.14441 231.25098 465.24207 curveto +230.62663 464.33517 230.31445 463.02267 230.31445 461.30457 curveto +230.31445 459.69129 230.69726 458.40614 231.46289 457.4491 curveto +232.22851 456.48752 233.25618 456.00672 234.5459 456.00671 curveto +234.89225 456.00672 235.24088 456.0409 235.5918 456.10925 curveto +235.94726 456.17762 236.3164 456.28016 236.69922 456.41687 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +239.8916 464.66101 moveto +241.33398 464.66101 lineto +241.33398 465.83679 lineto +240.21289 468.02429 lineto +239.33105 468.02429 lineto +239.8916 465.83679 lineto +239.8916 464.66101 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +247.99902 457.39441 moveto +244.5127 462.84265 lineto +247.99902 462.84265 lineto +247.99902 457.39441 lineto +247.63672 456.19128 moveto +249.37305 456.19128 lineto +249.37305 462.84265 lineto +250.8291 462.84265 lineto +250.8291 463.99109 lineto +249.37305 463.99109 lineto +249.37305 466.39734 lineto +247.99902 466.39734 lineto +247.99902 463.99109 lineto +243.3916 463.99109 lineto +243.3916 462.65808 lineto +247.63672 456.19128 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +253.2627 464.66101 moveto +254.70508 464.66101 lineto +254.70508 465.83679 lineto +253.58398 468.02429 lineto +252.70215 468.02429 lineto +253.2627 465.83679 lineto +253.2627 464.66101 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +262.43652 462.48035 moveto +262.43652 461.56889 262.24739 460.86251 261.86914 460.36121 curveto +261.49544 459.85991 260.96907 459.60926 260.29004 459.60925 curveto +259.61556 459.60926 259.08919 459.85991 258.71094 460.36121 curveto +258.33724 460.86251 258.15039 461.56889 258.15039 462.48035 curveto +258.15039 463.38725 258.33724 464.09135 258.71094 464.59265 curveto +259.08919 465.09395 259.61556 465.34461 260.29004 465.3446 curveto +260.96907 465.34461 261.49544 465.09395 261.86914 464.59265 curveto +262.24739 464.09135 262.43652 463.38725 262.43652 462.48035 curveto +263.69434 465.44714 moveto +263.69433 466.75053 263.40494 467.71895 262.82617 468.35242 curveto +262.24739 468.99044 261.361 469.30945 260.16699 469.30945 curveto +259.72493 469.30945 259.30794 469.27527 258.91602 469.20691 curveto +258.52409 469.1431 258.14355 469.04284 257.77441 468.90613 curveto +257.77441 467.6825 lineto +258.14355 467.88301 258.50814 468.03113 258.86816 468.12683 curveto +259.22819 468.22253 259.59505 468.27038 259.96875 468.27039 curveto +260.79362 468.27038 261.41113 468.05391 261.82129 467.62097 curveto +262.23144 467.19259 262.43652 466.54317 262.43652 465.67273 curveto +262.43652 465.05066 lineto +262.17675 465.50183 261.84407 465.83907 261.43848 466.06238 curveto +261.03287 466.28569 260.54752 466.39734 259.98242 466.39734 curveto +259.04362 466.39734 258.28711 466.03959 257.71289 465.3241 curveto +257.13867 464.6086 256.85156 463.66069 256.85156 462.48035 curveto +256.85156 461.29546 257.13867 460.34526 257.71289 459.62976 curveto +258.28711 458.91427 259.04362 458.55653 259.98242 458.55652 curveto +260.54752 458.55653 261.03287 458.66818 261.43848 458.89148 curveto +261.84407 459.11479 262.17675 459.45203 262.43652 459.9032 curveto +262.43652 458.74109 lineto +263.69434 458.74109 lineto +263.69434 465.44714 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +266.08691 455.77429 moveto +267.18066 455.77429 lineto +267.86425 456.84982 268.37467 457.90256 268.71191 458.9325 curveto +269.05371 459.96245 269.22461 460.98556 269.22461 462.00183 curveto +269.22461 463.02267 269.05371 464.05034 268.71191 465.08484 curveto +268.37467 466.11934 267.86425 467.17208 267.18066 468.24304 curveto +266.08691 468.24304 lineto +266.69303 467.19942 267.1442 466.16264 267.44043 465.13269 curveto +267.74121 464.09819 267.8916 463.05457 267.8916 462.00183 curveto +267.8916 460.9491 267.74121 459.91004 267.44043 458.88464 curveto +267.1442 457.85926 266.69303 456.82248 266.08691 455.77429 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +275.57715 455.77429 moveto +274.96647 456.82248 274.51302 457.85926 274.2168 458.88464 curveto +273.92057 459.91004 273.77246 460.9491 273.77246 462.00183 curveto +273.77246 463.05457 273.92057 464.09819 274.2168 465.13269 curveto +274.51757 466.16264 274.97102 467.19942 275.57715 468.24304 curveto +274.4834 468.24304 lineto +273.7998 467.17208 273.28711 466.11934 272.94531 465.08484 curveto +272.60807 464.05034 272.43945 463.02267 272.43945 462.00183 curveto +272.43945 460.98556 272.60807 459.96245 272.94531 458.9325 curveto +273.28255 457.90256 273.79524 456.84982 274.4834 455.77429 curveto +275.57715 455.77429 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +281.15527 461.55066 moveto +280.49902 461.55066 279.98177 461.72612 279.60352 462.07703 curveto +279.22982 462.42794 279.04297 462.91101 279.04297 463.52625 curveto +279.04297 464.14148 279.22982 464.62455 279.60352 464.97546 curveto +279.98177 465.32638 280.49902 465.50183 281.15527 465.50183 curveto +281.81152 465.50183 282.32877 465.32638 282.70703 464.97546 curveto +283.08528 464.62 283.27441 464.13692 283.27441 463.52625 curveto +283.27441 462.91101 283.08528 462.42794 282.70703 462.07703 curveto +282.33333 461.72612 281.81608 461.55066 281.15527 461.55066 curveto +279.77441 460.96277 moveto +279.18196 460.81694 278.7194 460.54122 278.38672 460.13562 curveto +278.05859 459.73003 277.89453 459.23556 277.89453 458.65222 curveto +277.89453 457.83648 278.18392 457.19162 278.7627 456.71765 curveto +279.34603 456.2437 280.14355 456.00672 281.15527 456.00671 curveto +282.17154 456.00672 282.96907 456.2437 283.54785 456.71765 curveto +284.12662 457.19162 284.41601 457.83648 284.41602 458.65222 curveto +284.41601 459.23556 284.24967 459.73003 283.91699 460.13562 curveto +283.58886 460.54122 283.13085 460.81694 282.54297 460.96277 curveto +283.20833 461.11772 283.72558 461.42078 284.09473 461.87195 curveto +284.46842 462.32312 284.65527 462.87456 284.65527 463.52625 curveto +284.65527 464.51518 284.35221 465.27397 283.74609 465.80261 curveto +283.14452 466.33126 282.28092 466.59558 281.15527 466.59558 curveto +280.02962 466.59558 279.16373 466.33126 278.55762 465.80261 curveto +277.95605 465.27397 277.65527 464.51518 277.65527 463.52625 curveto +277.65527 462.87456 277.84212 462.32312 278.21582 461.87195 curveto +278.58952 461.42078 279.10905 461.11772 279.77441 460.96277 curveto +279.26855 458.7821 moveto +279.26855 459.31076 279.43261 459.72319 279.76074 460.01941 curveto +280.09342 460.31564 280.55826 460.46375 281.15527 460.46375 curveto +281.74772 460.46375 282.21028 460.31564 282.54297 460.01941 curveto +282.8802 459.72319 283.04882 459.31076 283.04883 458.7821 curveto +283.04882 458.25347 282.8802 457.84103 282.54297 457.5448 curveto +282.21028 457.24858 281.74772 457.10047 281.15527 457.10046 curveto +280.55826 457.10047 280.09342 457.24858 279.76074 457.5448 curveto +279.43261 457.84103 279.26855 458.25347 279.26855 458.7821 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +287.25977 464.66101 moveto +288.70215 464.66101 lineto +288.70215 465.83679 lineto +287.58105 468.02429 lineto +286.69922 468.02429 lineto +287.25977 465.83679 lineto +287.25977 464.66101 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +291.58691 456.19128 moveto +297.00781 456.19128 lineto +297.00781 457.35339 lineto +292.85156 457.35339 lineto +292.85156 459.85535 lineto +293.05208 459.78699 293.2526 459.73686 293.45312 459.70496 curveto +293.65364 459.6685 293.85416 459.65028 294.05469 459.65027 curveto +295.19401 459.65028 296.09635 459.96245 296.76172 460.58679 curveto +297.42708 461.21115 297.75976 462.05652 297.75977 463.12292 curveto +297.75976 464.22123 297.41796 465.07573 296.73438 465.6864 curveto +296.05078 466.29252 295.08691 466.59558 293.84277 466.59558 curveto +293.41438 466.59558 292.97689 466.55912 292.53027 466.48621 curveto +292.08821 466.41329 291.63021 466.30391 291.15625 466.15808 curveto +291.15625 464.77039 lineto +291.5664 464.99369 291.99023 465.16004 292.42773 465.26941 curveto +292.86523 465.37879 293.3278 465.43347 293.81543 465.43347 curveto +294.60384 465.43347 295.22818 465.22612 295.68848 464.8114 curveto +296.14876 464.39669 296.3789 463.83386 296.37891 463.12292 curveto +296.3789 462.41199 296.14876 461.84917 295.68848 461.43445 curveto +295.22818 461.01974 294.60384 460.81238 293.81543 460.81238 curveto +293.44629 460.81238 293.07715 460.8534 292.70801 460.93542 curveto +292.34342 461.01746 291.96972 461.14507 291.58691 461.31824 curveto +291.58691 456.19128 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +300.63086 464.66101 moveto +302.07324 464.66101 lineto +302.07324 465.83679 lineto +300.95215 468.02429 lineto +300.07031 468.02429 lineto +300.63086 465.83679 lineto +300.63086 464.66101 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +309.80469 462.48035 moveto +309.80468 461.56889 309.61555 460.86251 309.2373 460.36121 curveto +308.8636 459.85991 308.33723 459.60926 307.6582 459.60925 curveto +306.98372 459.60926 306.45735 459.85991 306.0791 460.36121 curveto +305.7054 460.86251 305.51855 461.56889 305.51855 462.48035 curveto +305.51855 463.38725 305.7054 464.09135 306.0791 464.59265 curveto +306.45735 465.09395 306.98372 465.34461 307.6582 465.3446 curveto +308.33723 465.34461 308.8636 465.09395 309.2373 464.59265 curveto +309.61555 464.09135 309.80468 463.38725 309.80469 462.48035 curveto +311.0625 465.44714 moveto +311.06249 466.75053 310.7731 467.71895 310.19434 468.35242 curveto +309.61555 468.99044 308.72916 469.30945 307.53516 469.30945 curveto +307.0931 469.30945 306.6761 469.27527 306.28418 469.20691 curveto +305.89225 469.1431 305.51172 469.04284 305.14258 468.90613 curveto +305.14258 467.6825 lineto +305.51172 467.88301 305.8763 468.03113 306.23633 468.12683 curveto +306.59635 468.22253 306.96321 468.27038 307.33691 468.27039 curveto +308.16178 468.27038 308.77929 468.05391 309.18945 467.62097 curveto +309.5996 467.19259 309.80468 466.54317 309.80469 465.67273 curveto +309.80469 465.05066 lineto +309.54492 465.50183 309.21223 465.83907 308.80664 466.06238 curveto +308.40104 466.28569 307.91569 466.39734 307.35059 466.39734 curveto +306.41178 466.39734 305.65527 466.03959 305.08105 465.3241 curveto +304.50683 464.6086 304.21973 463.66069 304.21973 462.48035 curveto +304.21973 461.29546 304.50683 460.34526 305.08105 459.62976 curveto +305.65527 458.91427 306.41178 458.55653 307.35059 458.55652 curveto +307.91569 458.55653 308.40104 458.66818 308.80664 458.89148 curveto +309.21223 459.11479 309.54492 459.45203 309.80469 459.9032 curveto +309.80469 458.74109 lineto +311.0625 458.74109 lineto +311.0625 465.44714 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +313.45508 455.77429 moveto +314.54883 455.77429 lineto +315.23242 456.84982 315.74284 457.90256 316.08008 458.9325 curveto +316.42187 459.96245 316.59277 460.98556 316.59277 462.00183 curveto +316.59277 463.02267 316.42187 464.05034 316.08008 465.08484 curveto +315.74284 466.11934 315.23242 467.17208 314.54883 468.24304 curveto +313.45508 468.24304 lineto +314.0612 467.19942 314.51237 466.16264 314.80859 465.13269 curveto +315.10937 464.09819 315.25976 463.05457 315.25977 462.00183 curveto +315.25976 460.9491 315.10937 459.91004 314.80859 458.88464 curveto +314.51237 457.85926 314.0612 456.82248 313.45508 455.77429 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +174.3125 475.84265 moveto +173.70182 476.89084 173.24837 477.92762 172.95215 478.953 curveto +172.65592 479.9784 172.50781 481.01746 172.50781 482.07019 curveto +172.50781 483.12293 172.65592 484.16655 172.95215 485.20105 curveto +173.25293 486.231 173.70638 487.26778 174.3125 488.3114 curveto +173.21875 488.3114 lineto +172.53515 487.24044 172.02246 486.1877 171.68066 485.1532 curveto +171.34342 484.1187 171.1748 483.09103 171.1748 482.07019 curveto +171.1748 481.05392 171.34342 480.03081 171.68066 479.00085 curveto +172.0179 477.97092 172.5306 476.91818 173.21875 475.84265 curveto +174.3125 475.84265 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +176.97852 486.25378 moveto +176.97852 484.99597 lineto +177.32487 485.16004 177.67578 485.28536 178.03125 485.37195 curveto +178.38672 485.45854 178.73535 485.50183 179.07715 485.50183 curveto +179.9886 485.50183 180.68359 485.19649 181.16211 484.58582 curveto +181.64518 483.97058 181.92089 483.03862 181.98926 481.78992 curveto +181.72493 482.18185 181.38997 482.48263 180.98438 482.69226 curveto +180.57877 482.9019 180.12988 483.00672 179.6377 483.00671 curveto +178.61686 483.00672 177.80794 482.6991 177.21094 482.08386 curveto +176.61849 481.46408 176.32226 480.6187 176.32227 479.54773 curveto +176.32226 478.49956 176.63216 477.65874 177.25195 477.02527 curveto +177.87174 476.39182 178.69661 476.07508 179.72656 476.07507 curveto +180.9069 476.07508 181.80696 476.52853 182.42676 477.43542 curveto +183.0511 478.33778 183.36327 479.65028 183.36328 481.37292 curveto +183.36327 482.98165 182.98046 484.26681 182.21484 485.22839 curveto +181.45377 486.18543 180.42838 486.66394 179.13867 486.66394 curveto +178.79231 486.66394 178.4414 486.62976 178.08594 486.5614 curveto +177.73047 486.49304 177.36133 486.3905 176.97852 486.25378 curveto +179.72656 481.92664 moveto +180.34635 481.92664 180.83626 481.71473 181.19629 481.29089 curveto +181.56087 480.86707 181.74316 480.28602 181.74316 479.54773 curveto +181.74316 478.81401 181.56087 478.23524 181.19629 477.8114 curveto +180.83626 477.38303 180.34635 477.16883 179.72656 477.16882 curveto +179.10677 477.16883 178.61458 477.38303 178.25 477.8114 curveto +177.88997 478.23524 177.70996 478.81401 177.70996 479.54773 curveto +177.70996 480.28602 177.88997 480.86707 178.25 481.29089 curveto +178.61458 481.71473 179.10677 481.92664 179.72656 481.92664 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +185.99512 484.72937 moveto +187.4375 484.72937 lineto +187.4375 485.90515 lineto +186.31641 488.09265 lineto +185.43457 488.09265 lineto +185.99512 485.90515 lineto +185.99512 484.72937 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +193.43262 480.81238 moveto +192.81282 480.81238 192.32063 481.0243 191.95605 481.44812 curveto +191.59603 481.87195 191.41601 482.45301 191.41602 483.19128 curveto +191.41601 483.92501 191.59603 484.50606 191.95605 484.93445 curveto +192.32063 485.35828 192.81282 485.57019 193.43262 485.57019 curveto +194.0524 485.57019 194.54231 485.35828 194.90234 484.93445 curveto +195.26692 484.50606 195.44921 483.92501 195.44922 483.19128 curveto +195.44921 482.45301 195.26692 481.87195 194.90234 481.44812 curveto +194.54231 481.0243 194.0524 480.81238 193.43262 480.81238 curveto +196.17383 476.48523 moveto +196.17383 477.74304 lineto +195.82747 477.57899 195.47656 477.45366 195.12109 477.36707 curveto +194.77018 477.28049 194.42154 477.23719 194.0752 477.23718 curveto +193.16373 477.23719 192.46647 477.54481 191.9834 478.16003 curveto +191.50488 478.77528 191.23144 479.70496 191.16309 480.9491 curveto +191.43196 480.55262 191.7692 480.24956 192.1748 480.03992 curveto +192.5804 479.82573 193.02701 479.71863 193.51465 479.71863 curveto +194.54003 479.71863 195.34895 480.03081 195.94141 480.65515 curveto +196.5384 481.27495 196.83691 482.12032 196.83691 483.19128 curveto +196.83691 484.23946 196.52701 485.08028 195.90723 485.71375 curveto +195.28743 486.34721 194.46256 486.66394 193.43262 486.66394 curveto +192.25228 486.66394 191.34993 486.21277 190.72559 485.31042 curveto +190.10124 484.40353 189.78906 483.09103 189.78906 481.37292 curveto +189.78906 479.75965 190.17187 478.4745 190.9375 477.51746 curveto +191.70312 476.55588 192.73079 476.07508 194.02051 476.07507 curveto +194.36686 476.07508 194.71549 476.10926 195.06641 476.17761 curveto +195.42187 476.24598 195.79101 476.34852 196.17383 476.48523 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +199.36621 484.72937 moveto +200.80859 484.72937 lineto +200.80859 485.90515 lineto +199.6875 488.09265 lineto +198.80566 488.09265 lineto +199.36621 485.90515 lineto +199.36621 484.72937 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +206.46875 479.69128 moveto +205.79427 479.69129 205.26106 479.95561 204.86914 480.48425 curveto +204.47721 481.00835 204.28125 481.7284 204.28125 482.64441 curveto +204.28125 483.56043 204.47493 484.28276 204.8623 484.8114 curveto +205.25423 485.33549 205.78971 485.59754 206.46875 485.59753 curveto +207.13867 485.59754 207.66959 485.33321 208.06152 484.80457 curveto +208.45344 484.27592 208.64941 483.55587 208.64941 482.64441 curveto +208.64941 481.73751 208.45344 481.01974 208.06152 480.49109 curveto +207.66959 479.95789 207.13867 479.69129 206.46875 479.69128 curveto +206.46875 478.62488 moveto +207.56249 478.62489 208.42154 478.98035 209.0459 479.69128 curveto +209.67024 480.40223 209.98241 481.3866 209.98242 482.64441 curveto +209.98241 483.89767 209.67024 484.88204 209.0459 485.59753 curveto +208.42154 486.30847 207.56249 486.66394 206.46875 486.66394 curveto +205.37044 486.66394 204.50911 486.30847 203.88477 485.59753 curveto +203.26497 484.88204 202.95508 483.89767 202.95508 482.64441 curveto +202.95508 481.3866 203.26497 480.40223 203.88477 479.69128 curveto +204.50911 478.98035 205.37044 478.62489 206.46875 478.62488 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +211.8623 475.84265 moveto +212.95605 475.84265 lineto +213.63965 476.91818 214.15006 477.97092 214.4873 479.00085 curveto +214.8291 480.03081 215 481.05392 215 482.07019 curveto +215 483.09103 214.8291 484.1187 214.4873 485.1532 curveto +214.15006 486.1877 213.63965 487.24044 212.95605 488.3114 curveto +211.8623 488.3114 lineto +212.46842 487.26778 212.91959 486.231 213.21582 485.20105 curveto +213.5166 484.16655 213.66699 483.12293 213.66699 482.07019 curveto +213.66699 481.01746 213.5166 479.9784 213.21582 478.953 curveto +212.91959 477.92762 212.46842 476.89084 211.8623 475.84265 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +222.12305 476.34265 moveto +221.51237 477.39084 221.05892 478.42762 220.7627 479.453 curveto +220.46647 480.4784 220.31836 481.51746 220.31836 482.57019 curveto +220.31836 483.62293 220.46647 484.66655 220.7627 485.70105 curveto +221.06347 486.731 221.51692 487.76778 222.12305 488.8114 curveto +221.0293 488.8114 lineto +220.3457 487.74044 219.83301 486.6877 219.49121 485.6532 curveto +219.15397 484.6187 218.98535 483.59103 218.98535 482.57019 curveto +218.98535 481.55392 219.15397 480.53081 219.49121 479.50085 curveto +219.82845 478.47092 220.34114 477.41818 221.0293 476.34265 curveto +222.12305 476.34265 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +224.9873 485.80359 moveto +227.24316 485.80359 lineto +227.24316 478.01746 lineto +224.78906 478.50964 lineto +224.78906 477.25183 lineto +227.22949 476.75964 lineto +228.61035 476.75964 lineto +228.61035 485.80359 lineto +230.86621 485.80359 lineto +230.86621 486.9657 lineto +224.9873 486.9657 lineto +224.9873 485.80359 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +236.61523 477.66882 moveto +235.90429 477.66883 235.36881 478.01974 235.00879 478.72156 curveto +234.65332 479.41883 234.47558 480.46929 234.47559 481.87292 curveto +234.47558 483.27202 234.65332 484.32247 235.00879 485.02429 curveto +235.36881 485.72156 235.90429 486.07019 236.61523 486.07019 curveto +237.33072 486.07019 237.86621 485.72156 238.22168 485.02429 curveto +238.5817 484.32247 238.76171 483.27202 238.76172 481.87292 curveto +238.76171 480.46929 238.5817 479.41883 238.22168 478.72156 curveto +237.86621 478.01974 237.33072 477.66883 236.61523 477.66882 curveto +236.61523 476.57507 moveto +237.75911 476.57508 238.63183 477.02853 239.2334 477.93542 curveto +239.83951 478.83778 240.14257 480.15028 240.14258 481.87292 curveto +240.14257 483.59103 239.83951 484.90353 239.2334 485.81042 curveto +238.63183 486.71277 237.75911 487.16394 236.61523 487.16394 curveto +235.47135 487.16394 234.59635 486.71277 233.99023 485.81042 curveto +233.38867 484.90353 233.08789 483.59103 233.08789 481.87292 curveto +233.08789 480.15028 233.38867 478.83778 233.99023 477.93542 curveto +234.59635 477.02853 235.47135 476.57508 236.61523 476.57507 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +242.71973 485.22937 moveto +244.16211 485.22937 lineto +244.16211 486.40515 lineto +243.04102 488.59265 lineto +242.15918 488.59265 lineto +242.71973 486.40515 lineto +242.71973 485.22937 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +246.68457 476.75964 moveto +253.24707 476.75964 lineto +253.24707 477.34753 lineto +249.54199 486.9657 lineto +248.09961 486.9657 lineto +251.58594 477.92175 lineto +246.68457 477.92175 lineto +246.68457 476.75964 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +256.09082 485.22937 moveto +257.5332 485.22937 lineto +257.5332 486.40515 lineto +256.41211 488.59265 lineto +255.53027 488.59265 lineto +256.09082 486.40515 lineto +256.09082 485.22937 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +263.19336 480.19128 moveto +262.51888 480.19129 261.98567 480.45561 261.59375 480.98425 curveto +261.20182 481.50835 261.00586 482.2284 261.00586 483.14441 curveto +261.00586 484.06043 261.19954 484.78276 261.58691 485.3114 curveto +261.97884 485.83549 262.51432 486.09754 263.19336 486.09753 curveto +263.86328 486.09754 264.3942 485.83321 264.78613 485.30457 curveto +265.17805 484.77592 265.37402 484.05587 265.37402 483.14441 curveto +265.37402 482.23751 265.17805 481.51974 264.78613 480.99109 curveto +264.3942 480.45789 263.86328 480.19129 263.19336 480.19128 curveto +263.19336 479.12488 moveto +264.2871 479.12489 265.14615 479.48035 265.77051 480.19128 curveto +266.39485 480.90223 266.70702 481.8866 266.70703 483.14441 curveto +266.70702 484.39767 266.39485 485.38204 265.77051 486.09753 curveto +265.14615 486.80847 264.2871 487.16394 263.19336 487.16394 curveto +262.09505 487.16394 261.23372 486.80847 260.60938 486.09753 curveto +259.98958 485.38204 259.67969 484.39767 259.67969 483.14441 curveto +259.67969 481.8866 259.98958 480.90223 260.60938 480.19128 curveto +261.23372 479.48035 262.09505 479.12489 263.19336 479.12488 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +268.58691 476.34265 moveto +269.68066 476.34265 lineto +270.36425 477.41818 270.87467 478.47092 271.21191 479.50085 curveto +271.55371 480.53081 271.72461 481.55392 271.72461 482.57019 curveto +271.72461 483.59103 271.55371 484.6187 271.21191 485.6532 curveto +270.87467 486.6877 270.36425 487.74044 269.68066 488.8114 curveto +268.58691 488.8114 lineto +269.19303 487.76778 269.6442 486.731 269.94043 485.70105 curveto +270.24121 484.66655 270.3916 483.62293 270.3916 482.57019 curveto +270.3916 481.51746 270.24121 480.4784 269.94043 479.453 curveto +269.6442 478.42762 269.19303 477.39084 268.58691 476.34265 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +276.49121 476.34265 moveto +275.88053 477.39084 275.42708 478.42762 275.13086 479.453 curveto +274.83463 480.4784 274.68652 481.51746 274.68652 482.57019 curveto +274.68652 483.62293 274.83463 484.66655 275.13086 485.70105 curveto +275.43164 486.731 275.88509 487.76778 276.49121 488.8114 curveto +275.39746 488.8114 lineto +274.71386 487.74044 274.20117 486.6877 273.85938 485.6532 curveto +273.52213 484.6187 273.35351 483.59103 273.35352 482.57019 curveto +273.35351 481.55392 273.52213 480.53081 273.85938 479.50085 curveto +274.19661 478.47092 274.70931 477.41818 275.39746 476.34265 curveto +276.49121 476.34265 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +279.35547 485.80359 moveto +281.61133 485.80359 lineto +281.61133 478.01746 lineto +279.15723 478.50964 lineto +279.15723 477.25183 lineto +281.59766 476.75964 lineto +282.97852 476.75964 lineto +282.97852 485.80359 lineto +285.23438 485.80359 lineto +285.23438 486.9657 lineto +279.35547 486.9657 lineto +279.35547 485.80359 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +288.26953 485.80359 moveto +290.52539 485.80359 lineto +290.52539 478.01746 lineto +288.07129 478.50964 lineto +288.07129 477.25183 lineto +290.51172 476.75964 lineto +291.89258 476.75964 lineto +291.89258 485.80359 lineto +294.14844 485.80359 lineto +294.14844 486.9657 lineto +288.26953 486.9657 lineto +288.26953 485.80359 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +297.08789 485.22937 moveto +298.53027 485.22937 lineto +298.53027 486.40515 lineto +297.40918 488.59265 lineto +296.52734 488.59265 lineto +297.08789 486.40515 lineto +297.08789 485.22937 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +304.35449 482.11902 moveto +303.69824 482.11902 303.18099 482.29448 302.80273 482.64539 curveto +302.42903 482.9963 302.24219 483.47937 302.24219 484.0946 curveto +302.24219 484.70984 302.42903 485.19291 302.80273 485.54382 curveto +303.18099 485.89474 303.69824 486.07019 304.35449 486.07019 curveto +305.01074 486.07019 305.52799 485.89474 305.90625 485.54382 curveto +306.2845 485.18836 306.47363 484.70528 306.47363 484.0946 curveto +306.47363 483.47937 306.2845 482.9963 305.90625 482.64539 curveto +305.53255 482.29448 305.01529 482.11902 304.35449 482.11902 curveto +302.97363 481.53113 moveto +302.38118 481.3853 301.91862 481.10958 301.58594 480.70398 curveto +301.25781 480.29839 301.09375 479.80392 301.09375 479.22058 curveto +301.09375 478.40483 301.38314 477.75998 301.96191 477.28601 curveto +302.54524 476.81206 303.34277 476.57508 304.35449 476.57507 curveto +305.37076 476.57508 306.16829 476.81206 306.74707 477.28601 curveto +307.32584 477.75998 307.61523 478.40483 307.61523 479.22058 curveto +307.61523 479.80392 307.44889 480.29839 307.11621 480.70398 curveto +306.78808 481.10958 306.33007 481.3853 305.74219 481.53113 curveto +306.40755 481.68608 306.9248 481.98914 307.29395 482.44031 curveto +307.66764 482.89148 307.85448 483.44292 307.85449 484.0946 curveto +307.85448 485.08354 307.55142 485.84233 306.94531 486.37097 curveto +306.34374 486.89962 305.48014 487.16394 304.35449 487.16394 curveto +303.22884 487.16394 302.36295 486.89962 301.75684 486.37097 curveto +301.15527 485.84233 300.85449 485.08354 300.85449 484.0946 curveto +300.85449 483.44292 301.04134 482.89148 301.41504 482.44031 curveto +301.78874 481.98914 302.30827 481.68608 302.97363 481.53113 curveto +302.46777 479.35046 moveto +302.46777 479.87912 302.63183 480.29155 302.95996 480.58777 curveto +303.29264 480.884 303.75748 481.03211 304.35449 481.0321 curveto +304.94694 481.03211 305.4095 480.884 305.74219 480.58777 curveto +306.07942 480.29155 306.24804 479.87912 306.24805 479.35046 curveto +306.24804 478.82183 306.07942 478.40939 305.74219 478.11316 curveto +305.4095 477.81694 304.94694 477.66883 304.35449 477.66882 curveto +303.75748 477.66883 303.29264 477.81694 302.95996 478.11316 curveto +302.63183 478.40939 302.46777 478.82183 302.46777 479.35046 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +310.45898 485.22937 moveto +311.90137 485.22937 lineto +311.90137 486.40515 lineto +310.78027 488.59265 lineto +309.89844 488.59265 lineto +310.45898 486.40515 lineto +310.45898 485.22937 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +317.56152 480.19128 moveto +316.88704 480.19129 316.35384 480.45561 315.96191 480.98425 curveto +315.56998 481.50835 315.37402 482.2284 315.37402 483.14441 curveto +315.37402 484.06043 315.56771 484.78276 315.95508 485.3114 curveto +316.347 485.83549 316.88248 486.09754 317.56152 486.09753 curveto +318.23144 486.09754 318.76236 485.83321 319.1543 485.30457 curveto +319.54622 484.77592 319.74218 484.05587 319.74219 483.14441 curveto +319.74218 482.23751 319.54622 481.51974 319.1543 480.99109 curveto +318.76236 480.45789 318.23144 480.19129 317.56152 480.19128 curveto +317.56152 479.12488 moveto +318.65527 479.12489 319.51432 479.48035 320.13867 480.19128 curveto +320.76301 480.90223 321.07519 481.8866 321.0752 483.14441 curveto +321.07519 484.39767 320.76301 485.38204 320.13867 486.09753 curveto +319.51432 486.80847 318.65527 487.16394 317.56152 487.16394 curveto +316.46321 487.16394 315.60189 486.80847 314.97754 486.09753 curveto +314.35775 485.38204 314.04785 484.39767 314.04785 483.14441 curveto +314.04785 481.8866 314.35775 480.90223 314.97754 480.19128 curveto +315.60189 479.48035 316.46321 479.12489 317.56152 479.12488 curveto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +322.95508 476.34265 moveto +324.04883 476.34265 lineto +324.73242 477.41818 325.24284 478.47092 325.58008 479.50085 curveto +325.92187 480.53081 326.09277 481.55392 326.09277 482.57019 curveto +326.09277 483.59103 325.92187 484.6187 325.58008 485.6532 curveto +325.24284 486.6877 324.73242 487.74044 324.04883 488.8114 curveto +322.95508 488.8114 lineto +323.5612 487.76778 324.01237 486.731 324.30859 485.70105 curveto +324.60937 484.66655 324.75976 483.62293 324.75977 482.57019 curveto +324.75976 481.51746 324.60937 480.4784 324.30859 479.453 curveto +324.01237 478.42762 323.5612 477.39084 322.95508 476.34265 curveto +fill +grestore +gsave [1.436848 0 0 0.806571 118.9493 72.56034] concat +0 0 0 setrgbcolor +[] 0 setdash +1.5 setlinewidth +0 setlinejoin +0 setlinecap +newpath +154.5 482.61218 moveto +154.5 515.59418 125.716 542.36218 90.25 542.36218 curveto +54.784 542.36218 26 515.59418 26 482.61218 curveto +26 449.63018 54.784 422.86218 90.25 422.86218 curveto +125.716 422.86218 154.5 449.63018 154.5 482.61218 curveto +closepath +stroke +grestore +gsave +0 0 0 setrgbcolor +newpath +244.37402 420.15613 moveto +245.75488 420.15613 lineto +245.75488 430.36218 lineto +244.37402 430.36218 lineto +244.37402 420.15613 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +249.69238 420.5321 moveto +249.69238 422.70593 lineto +252.2832 422.70593 lineto +252.2832 423.68347 lineto +249.69238 423.68347 lineto +249.69238 427.83972 lineto +249.69238 428.46407 249.77669 428.86511 249.94531 429.04285 curveto +250.11849 429.22058 250.46712 429.30945 250.99121 429.30945 curveto +252.2832 429.30945 lineto +252.2832 430.36218 lineto +250.99121 430.36218 lineto +250.0205 430.36218 249.35058 430.18217 248.98145 429.82214 curveto +248.6123 429.45756 248.42773 428.79675 248.42773 427.83972 curveto +248.42773 423.68347 lineto +247.50488 423.68347 lineto +247.50488 422.70593 lineto +248.42773 422.70593 lineto +248.42773 420.5321 lineto +249.69238 420.5321 lineto +fill +grestore +grestore +gsave +0 0 0 setrgbcolor +newpath +250.25488 525.79089 moveto +250.25488 529.62585 lineto +251.99121 529.62585 lineto +252.63378 529.62586 253.13053 529.45952 253.48145 529.12683 curveto +253.83235 528.79415 254.00781 528.3202 254.00781 527.70496 curveto +254.00781 527.09429 253.83235 526.62261 253.48145 526.28992 curveto +253.13053 525.95724 252.63378 525.7909 251.99121 525.79089 curveto +250.25488 525.79089 lineto +248.87402 524.65613 moveto +251.99121 524.65613 lineto +253.13509 524.65614 253.99869 524.9159 254.58203 525.43542 curveto +255.16991 525.95041 255.46386 526.70692 255.46387 527.70496 curveto +255.46386 528.71212 255.16991 529.47319 254.58203 529.98816 curveto +253.99869 530.50314 253.13509 530.76062 251.99121 530.76062 curveto +250.25488 530.76062 lineto +250.25488 534.86218 lineto +248.87402 534.86218 lineto +248.87402 524.65613 lineto +fill +grestore +gsave +0 0 0 setrgbcolor +newpath +257.68555 533.70007 moveto +259.94141 533.70007 lineto +259.94141 525.91394 lineto +257.4873 526.40613 lineto +257.4873 525.14832 lineto +259.92773 524.65613 lineto +261.30859 524.65613 lineto +261.30859 533.70007 lineto +263.56445 533.70007 lineto +263.56445 534.86218 lineto +257.68555 534.86218 lineto +257.68555 533.70007 lineto +fill +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +155 302.36218 moveto +154.5 534.36218 lineto +stroke +grestore +showpage +%%EOF diff --git a/doc/comm/indexset.cc b/doc/comm/indexset.cc new file mode 100644 index 0000000..4de2d25 --- /dev/null +++ b/doc/comm/indexset.cc @@ -0,0 +1,51 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#include + +#include +#include +#include +#include +#include "buildindexset.hh" +#include "reverse.hh" + +int main(int argc, char **argv) +{ + // This is a parallel programm so we need to + // initialize mpi first. + Dune::MPIHelper& helper = Dune::MPIHelper::instance(argc, argv); + + // The rank of our process + int rank = helper.rank(); + + // The type used as the global index + typedef int GlobalIndex; + + // The index set we use to identify the local indices with the globally + // unique ones + typedef Dune::ParallelIndexSet ParallelIndexSet; + + // The index set + ParallelIndexSet indexSet; + + build(helper, indexSet); + + // Print the index set + std::cout< +#include // We use exceptions +#include // An initializer of MPI +#include +#include + +enum Flags { owner, ghost }; + +struct Bla +{ + + /** @brief The local index. */ + size_t localIndex_; + + /** @brief An attribute for the index. */ + char attribute_; + + /** @brief True if the index is also known to other processors. */ + bool public_; + + /** + * @brief The state of the index. + * + * Has to be one of LocalIndexState! + * @see LocalIndexState. + */ + char state_; +}; + + +template +void buildBlockedIndexSet(T1& indexset, int N, const T2& comm) +{ + int rank=comm.rank(); + int size=comm.size(); + int localsize=N/size; + int bigger=N%size; + int start, end; + if(rank0) + indexset.add(gindex-1,LocalIndex(index++,ghost)); + + for(int i=start; i > IndexSet; + IndexSet blockedSet; + buildBlockedIndexSet(blockedSet, n, helper.getCommunication()); + } + return 0; + } + catch (Dune::Exception &e) { + std::cerr << "Dune reported error: " << e << std::endl; + } + catch (...) { + std::cerr << "Unknown exception thrown!" << std::endl; + } +} diff --git a/doc/comm/poosc08_test.cc b/doc/comm/poosc08_test.cc new file mode 100644 index 0000000..2fe7eae --- /dev/null +++ b/doc/comm/poosc08_test.cc @@ -0,0 +1,155 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include // We use exceptions +#include // An initializer of MPI +#include +#include +#include +#include +#include +#include + +enum Flags { owner, ghost }; + +template +struct AddData { + typedef typename T::value_type IndexedType; + + static const IndexedType& gather(const T& v, int i){ + return v[i]; + } + + static void scatter(T& v, const IndexedType& item, int i){ + v[i]+=item; + } +}; + +template +struct CopyData { + typedef typename T::value_type IndexedType; + + static const IndexedType& gather(const T& v, int i){ + return v[i]; + } + + static void scatter(T& v, const IndexedType& item, int i){ + v[i]=item; + } +}; + + +template +void doCalculations(T&){} + +#if HAVE_MPI +void test() +{ + int rank; + MPI_Comm comm=(MPI_COMM_WORLD); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + using namespace Dune; + // shortcut for index set type + typedef ParallelLocalIndex LocalIndex; + typedef ParallelIndexSet PIndexSet; + PIndexSet sis; + sis.beginResize(); + if(rank==0) { + + sis.add(11, LocalIndex(0, ghost)); + for(int i=1; i<=6; i++) + sis.add(i-1, LocalIndex(i, owner, i<=1||i>5)); + sis.add(6, LocalIndex(7, ghost)); + }else{ + sis.add(5, LocalIndex(0, ghost)); + for(int i=1; i<=6; i++) + sis.add(5+i, LocalIndex(i, owner, i<=1||i>5)); + sis.add(0,LocalIndex(7, ghost)); + } + sis.endResize(); + + PIndexSet tis; + tis.beginResize(); + int l=0; + for(int i=0; i<2; ++i) + for(int j=0; j<5; ++j) { + int g=rank*3-1+i*6+j; + if(g<0||g>11) + continue; + Flags flag=(j>0&&j<4) ? owner : ghost; + tis.add(g, LocalIndex(l++, flag)); + } + tis.endResize(); + std::cout< riRedist(sis, tis, comm); + riRedist.rebuild(); + + std::vector v; + RemoteIndices riS(sis,sis, comm, v, true); + riS.rebuild(); + + std::cout<,EnumItem,Flags> ghostFlags; + EnumItem ownerFlags; + Combine, EnumItem > allFlags; + + Interface infRedist; + Interface infS; + + infRedist.build(riRedist, ownerFlags, allFlags); + infS.build(riS, ownerFlags, ghostFlags); + + std::cout<<"inf "< Container; + Container s(sis.size(),3), t(tis.size()); + + s[sis.size()-1]=-1; + + BufferedCommunicator bComm; + BufferedCommunicator bCommRedist; + bComm.build(s, s, infS); + //bCommRedist.build(s, t, infRedist); + for(std::size_t i=0; i >(s,s); + + for(std::size_t i=0; i >(s,t); + // calculate on the redistributed array + doCalculations(t); + bCommRedist.backward >(s,t); +} +#endif // HAVE_MPI + +int main(int argc, char** argv) +{ + try{ + using namespace Dune; +#if HAVE_MPI + //Maybe initialize Mpi + MPIHelper& helper = MPIHelper::instance(argc, argv); + std::cout << "Hello World! This is poosc08. rank=" < +void reverseLocalIndex(Dune::ParallelIndexSet& indexSet) +{ + // reverse the local indices + typedef typename Dune::ParallelIndexSet::iterator iterator; + + iterator end = indexSet.end(); + size_t maxLocal = 0; + + // find the maximal local index + for(iterator index = indexSet.begin(); index != end; ++index) { + // Get the local index + LocalIndex& local = index->local(); + maxLocal = std::max(maxLocal, local.local()); + } + + for(iterator index = indexSet.begin(); index != end; ++index) { + // Get the local index + LocalIndex& local = index->local(); + local = maxLocal--; + } + +} +#endif diff --git a/doc/doxygen/CMakeLists.txt b/doc/doxygen/CMakeLists.txt new file mode 100644 index 0000000..e1ca781 --- /dev/null +++ b/doc/doxygen/CMakeLists.txt @@ -0,0 +1,10 @@ +# create Doxyfile.in and Doxyfile +add_doxygen_target() + +install( + FILES + Doxystyle + doxygen-macros + DESTINATION + ${CMAKE_INSTALL_DATAROOTDIR}/dune-common/doc/doxygen + ) diff --git a/doc/doxygen/Doxylocal b/doc/doxygen/Doxylocal new file mode 100644 index 0000000..e88d780 --- /dev/null +++ b/doc/doxygen/Doxylocal @@ -0,0 +1,8 @@ +# Where to search and which files to use +INPUT += @srcdir@/mainpage.txt \ + @srcdir@/modules.txt \ + @top_srcdir@/dune/common/modules.txt \ + @top_srcdir@/dune/common +EXCLUDE += @top_srcdir@/dune/common/test \ + @top_srcdir@/dune/common/debugallocator.cc + diff --git a/doc/doxygen/Doxystyle b/doc/doxygen/Doxystyle new file mode 100644 index 0000000..d8a7ab2 --- /dev/null +++ b/doc/doxygen/Doxystyle @@ -0,0 +1,206 @@ +#----------- Doxystyle ----------- + +################################################################################## +# Project Details: + +PROJECT_NAME = @DUNE_MOD_NAME@ +PROJECT_NUMBER = @DUNE_MOD_VERSION@ + +################################################################################## +# What to parse + +RECURSIVE = YES +FILE_PATTERNS = *.hh \ + *.cc +INPUT = +EXCLUDE = + +EXCLUDE_PATTERNS = */test/* + +EXCLUDE_SYMBOLS = Impl detail Imp Internal + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = NO # don't warn about missing stl-headers etc. + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = @abs_top_srcdir@ + +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_LOGFILE = doxyerr.log + +################################################################################# +# Styling + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = YES + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 3 +HTML_OUTPUT = html +SEARCHENGINE = YES + +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES + +GENERATE_HTML = YES +GENERATE_DOCSET = NO +GENERATE_HTMLHELP = NO +GENERATE_CHI = NO +GENERATE_QHP = NO +GENERATE_TREEVIEW = NO + +GENERATE_LATEX = NO +GENERATE_RTF = NO +GENERATE_MAN = NO +GENERATE_XML = NO +GENERATE_AUTOGEN_DEF = NO +GENERATE_PERLMOD = NO +GENERATE_TAGFILE = @DUNE_MOD_NAME@.tag +GENERATE_LEGEND = NO + +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES + + +@DOT_TRUE@HAVE_DOT = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = NO +GROUP_GRAPHS = YES +INCLUDE_GRAPH = NO +INCLUDED_BY_GRAPH = NO +GRAPHICAL_HIERARCHY = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = NO +DOT_CLEANUP = NO + +##################################################################### +# Header Footer and Stylesheet in use is controlled by the Makefile # +# (christi 16. Jan 2006) # +##################################################################### + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +########################## DOXYGEN DOXYSTYLE +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = YES +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = NO +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = diff --git a/doc/doxygen/doxygen-macros b/doc/doxygen/doxygen-macros new file mode 100644 index 0000000..ea9f105 --- /dev/null +++ b/doc/doxygen/doxygen-macros @@ -0,0 +1,19 @@ +# This file contains the list of predefined preprocessor macros required for running +# Doxygen. It should be included automatically by the build system after +# Doxystyle. +# +# The reason to have this separate is for building the website, where we +# would otherwise have to maintain these definitions a second time. + +PREDEFINED = DOXYGEN \ + HAVE_MPI:=1 \ + _DEBUG_ALLOCATOR_H:=1 \ + "DUNE_DEPRECATED:=/** \deprecated */" \ + "DUNE_DEPRECATED_MSG(A):=/** \deprecated A */" \ + "DUNE_INLINE_VARIABLE:= " \ + __cpp_inline_variables:=201606 \ + __cpp_constexpr:=201603 \ + __cpp_variable_templates:=201304 \ + + +# marker - here to allow the last line continuation diff --git a/doc/doxygen/mainpage.txt b/doc/doxygen/mainpage.txt new file mode 100644 index 0000000..53e0bf0 --- /dev/null +++ b/doc/doxygen/mainpage.txt @@ -0,0 +1,17 @@ +/** \mainpage dune-common Automatic Documentation + +\section intro Introduction + +Welcome to the %Dune documentation pages. This documentation has been +generated using Doxygen, a free source code documentation system for +documenting C/C++ code. + +\section mods Modules + +The best way to start is from the page \subpage modules which gives +you access to the documentation by category. + +*/ + +/** \page modules Modules +*/ diff --git a/doc/doxygen/modules.txt b/doc/doxygen/modules.txt new file mode 100644 index 0000000..1f21548 --- /dev/null +++ b/doc/doxygen/modules.txt @@ -0,0 +1,78 @@ +/** + @defgroup Common Common + @brief foundation classes +*/ + +/** + @defgroup Allocators Allocators + @brief Implementations of the STL allocator concept + @ingroup Common +*/ + +/** + @defgroup Utilities Utilities + @brief Collection of helper classes, type traits, etc. + @ingroup Common +*/ + +/** + @defgroup Path Filesystem Paths + @brief Utilities for filesystem path management + @ingroup Utilities +*/ + +/** + @defgroup RangeUtilities Range Utilities + @brief Utilities for reduction like operations on ranges + + All these reduction operations work for appropriate ranges and scalar values + + @ingroup Utilities +*/ + +/** + @defgroup StringUtilities String Utilities + @brief Utility functions for std::string + @ingroup Utilities +*/ + +/** + @defgroup TupleUtilities Tuple Utilities + @brief Utility classes which can be used with std::tuple + @ingroup Utilities +*/ + +/** + @defgroup TypeUtilities Type Utilities + @brief Type traits, overload helpers, and other utilities for type computations + @ingroup Utilities +*/ + +/** + @defgroup HybridUtilities Hybrid Utilities + @brief Hybrid utility functions that work on homogeneous as well as heterogeneous containers + @ingroup Utilities +*/ + +/** + @defgroup CxxUtilities C++ utilities and backports + @brief Standard library features backported from newer C++ versions or technical specifications and DUNE-specific utilities. + @ingroup Utilities +*/ + +/** + @defgroup CxxConcepts C++ concepts + @brief Concepts built on top of C++14. + @ingroup Utilities +*/ + +/** + @defgroup Numbers Numbers + @brief Class implementing different number representations and helper functions + @ingroup Common +*/ + +/** + @defgroup FloatCmp FloatCmp + @ingroup Numbers +*/ diff --git a/doc/dunecontrol.1 b/doc/dunecontrol.1 new file mode 100644 index 0000000..234703e --- /dev/null +++ b/doc/dunecontrol.1 @@ -0,0 +1,173 @@ +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH DUNECONTROL 1 "November 8, 2016" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +dunecontrol \- Control program for the Dune build system +.SH SYNOPSIS +.B dunecontrol +[\fIOPTIONS\fP] \fICOMMANDS\fP [\fICOMMAND-OPTIONS\fP] +.SH DESCRIPTION +.B dunecontrol +is the control program for the build system of the Dune libraries. + +The Dune libraries form a set of modules. Each can be built independently using CMake. +Additionally, though, there are dependencies between modules, +which are expected to form a directed acyclic graph. These dependencies are set in a +file called +.B dune.module +contained in the main directory of each Dune module. + +The +.B dunecontrol +program helps to build sets of inter-dependent modules. It allows to construct +the entire dependency graph and obtain information about it. Then it allows to run various build-related +commands for all modules. These are executed in the order mandated by the dependency graph. + +.SH COMMANDS +Colon-separated list of commands. Available commands are: +.HP +.B help +.IP +Show a help message and exit +.HP +.B print +.IP +Print the list of modules sorted according to their dependency relations +.HP +.B info +.IP +Same as `print', but including whether it is a dependency or suggestion +.HP +.B printdeps +.IP +Print recursive dependencies of a module +.HP +.B vcsetup +.IP +Setup version control repository (Git etc.) or working copy (SVN) +.HP +.B update +.IP +Update all modules from the repository from their respective version control systems +.HP +.B configure +.IP +Run cmake for each module +.HP +.B make +.IP +Run make for each module +.HP +.B all +.IP +Run the 'configure' and 'make' commands for each module +.HP +.B exec +.IP +Execute an arbitrary command in each module source directory +.HP +.B bexec +.IP +Execute an arbitrary command in each module build directory +.HP +.B status +.IP +Show version control status for each module +.HP +.B svn +.IP +Run svn command for each svn-managed module +.HP +.B git +.IP +Run git command for each git-managed module +.HP +.B export +.IP +Run eval `dunecontrol export` to save the list of dune.module files to the DUNE_CONTROL_PATH variable +.SH OPTIONS +.HP +\fB\-h\fP, \fB\-\-help\fP +.IP +Show this help +.HP +\fB--debug\fP +.IP +Run with debugging output enabled +.HP +\fB--module=\fP\fImod\fP +.IP +Apply the actions on module +.I mod +and all modules it depends on +.HP +\fB--only=\fP\fImod\fP +.IP +Only apply the actions on module +.I mod +, but not on the modules it depends on +.HP +\fB--current\fP +.IP +Only apply the actions on the current module, the one whose source tree we are in +.HP +\fB--current-dep\fP +.IP +Apply the actions on the current module, and all modules it depends on +.HP +\fB--resume\fP +.IP +Resume a previous run (only consider the modules not built successfully on the previous run) +.HP +\fB--skipfirst\fP +.IP +Skip the first module (use with --resume) +.HP +\fB--skipversioncheck\fP +.IP +When looking for Dune modules, do not check whether they have the required versions +.HP +\fB--opts=\fP\fIfile\fP +.IP +Load default options from \fIfile\fP +.HP +\fB--builddir=\fP\fIname\fP +.IP +Make out-of-source builds in a subdir \fIname\fP. This directory is created inside each module. +.HP +\fB--[COMMAND]-opts=\fP\fIopts\fP +.IP +Set options for COMMAND (this is mainly useful for the 'all' COMMAND) + + +.SH ENVIRONMENT VARIABLES +.B dunecontrol +looks for Dune modules in all directories given in the +.B DUNE_CONTROL_PATH +variable, and additionally recursively in all subdirectories of those directories. +The default for the case that DUNE_CONTROL_PATH is empty is the current directory, +plus a system-wide installation in /usr. + +.SH AUTHOR +Dune was written by the Dune team (https://www.dune-project.org/community/people). +.PP +This manual page was written by Oliver Sander. + +.SH COPYRIGHT +Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without any warranty. diff --git a/dune-common.pc.in b/dune-common.pc.in new file mode 100644 index 0000000..d78094b --- /dev/null +++ b/dune-common.pc.in @@ -0,0 +1,15 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +CXX=@CXX@ +CC=@CC@ +DEPENDENCIES=@REQUIRES@ + +Name: @PACKAGE_NAME@ +Version: @VERSION@ +Description: @DESCRIPTION@ +URL: @URL@ +Requires: ${DEPENDENCIES} +Libs: -L${libdir} -ldunecommon +Cflags: -I${includedir} diff --git a/dune.module b/dune.module new file mode 100644 index 0000000..d8dcca2 --- /dev/null +++ b/dune.module @@ -0,0 +1,8 @@ +Module: dune-common +Version: 2.8.0 +Author: The Dune Core developers +Maintainer: dune-devel@lists.dune-project.org +Description: Basis infrastructure for all Dune modules +URL: https://gitlab.dune-project.org/core/dune-common +Python-Requires: portalocker numpy wheel mpi4py +Whitespace-Hook: Yes diff --git a/dune/CMakeLists.txt b/dune/CMakeLists.txt new file mode 100644 index 0000000..57eebc3 --- /dev/null +++ b/dune/CMakeLists.txt @@ -0,0 +1,8 @@ +add_subdirectory(common) + +# if Python bindings are enabled, include necessary sub directories. +if(DUNE_ENABLE_PYTHONBINDINGS) + add_subdirectory(python) +else() + exclude_subdir_from_headercheck(python) +endif() diff --git a/dune/common/CMakeLists.txt b/dune/common/CMakeLists.txt new file mode 100644 index 0000000..81b98db --- /dev/null +++ b/dune/common/CMakeLists.txt @@ -0,0 +1,118 @@ +add_subdirectory("parallel") +add_subdirectory("simd") +add_subdirectory("std") +add_subdirectory("test") + +#build the library dunecommon + +dune_add_library("dunecommon" + debugalign.cc + debugallocator.cc + exceptions.cc + fmatrixev.cc + ios_state.cc + parametertree.cc + parametertreeparser.cc + path.cc + simd/test.cc + stdstreams.cc + stdthread.cc) + +add_dune_blas_lapack_flags(dunecommon) +add_dune_tbb_flags(dunecommon) + +#install headers +install(FILES + alignedallocator.hh + arraylist.hh + assertandreturn.hh + bartonnackmanifcheck.hh + bigunsignedint.hh + binaryfunctions.hh + bitsetvector.hh + boundschecking.hh + classname.hh + concept.hh + conditional.hh + debugalign.hh + debugallocator.hh + debugstream.hh + deprecated.hh + densematrix.hh + densevector.hh + diagonalmatrix.hh + documentation.hh + dotproduct.hh + dynmatrix.hh + dynmatrixev.hh + dynvector.hh + enumset.hh + exceptions.hh + filledarray.hh + float_cmp.cc + float_cmp.hh + fmatrix.hh + fmatrixev.hh + ftraits.hh + function.hh + fvector.hh + gcd.hh + genericiterator.hh + gmpfield.hh + hash.hh + hybridutilities.hh + indent.hh + indices.hh + interfaces.hh + ios_state.hh + iteratorfacades.hh + iteratorrange.hh + keywords.hh + lcm.hh + lru.hh + mallocallocator.hh + math.hh + matvectraits.hh + overloadset.hh + parameterizedobject.hh + parametertree.hh + parametertreeparser.hh + path.hh + poolallocator.hh + power.hh + precision.hh + propertymap.hh + promotiontraits.hh + proxymemberaccess.hh + quadmath.hh + rangeutilities.hh + reservedvector.hh + scalarvectorview.hh + scalarmatrixview.hh + shared_ptr.hh + simd.hh + singleton.hh + sllist.hh + stdstreams.hh + stdthread.hh + streamoperators.hh + stringutility.hh + to_unique_ptr.hh + timer.hh + transpose.hh + tupleutility.hh + tuplevector.hh + typelist.hh + typetraits.hh + typeutilities.hh + unused.hh + vc.hh + version.hh + visibility.hh +DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/common) + +# Install some test headers, because they get used by tests in other modules +# We do this here as test will not be considered for make install +install(FILES test/iteratortest.hh + test/checkmatrixinterface.hh + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/common/test) diff --git a/dune/common/alignedallocator.hh b/dune/common/alignedallocator.hh new file mode 100644 index 0000000..ee1f55a --- /dev/null +++ b/dune/common/alignedallocator.hh @@ -0,0 +1,60 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_ALIGNED_ALLOCATOR_HH +#define DUNE_ALIGNED_ALLOCATOR_HH + +#include "mallocallocator.hh" +#include + + +namespace Dune +{ + + /** + @ingroup Allocators + @brief Allocators which guarantee alignment of the memory + + @tparam T type of the object one wants to allocate + @tparam Alignment explicitly specify the alignment, by default it is std::alignment_of::value + */ + template + class AlignedAllocator : public MallocAllocator { + + /* + * Check whether an explicit alignment was + * restricted or fall back to the default alignment of T. + */ + static constexpr int fixAlignment(int align) + { + return (Alignment==-1) ? std::alignment_of::value : Alignment; + } + + public: + using pointer = typename MallocAllocator::pointer; + using size_type = typename MallocAllocator::size_type; + template struct rebind { + typedef AlignedAllocator other; + }; + + static constexpr int alignment = fixAlignment(sizeof(void*)); + + //! allocate n objects of type T + pointer allocate(size_type n, [[maybe_unused]] const void* hint = 0) + { + if (n > this->max_size()) + throw std::bad_alloc(); + + /* + * Everybody else gets the standard treatment. + */ + pointer ret = static_cast(std::aligned_alloc(alignment, n * sizeof(T))); + if (!ret) + throw std::bad_alloc(); + + return ret; + } + }; + +} + +#endif // DUNE_ALIGNED_ALLOCATOR_HH diff --git a/dune/common/arraylist.hh b/dune/common/arraylist.hh new file mode 100644 index 0000000..7788da4 --- /dev/null +++ b/dune/common/arraylist.hh @@ -0,0 +1,732 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_COMMON_ARRAYLIST_HH +#define DUNE_COMMON_ARRAYLIST_HH + +#include +#include +#include +#include +#include "iteratorfacades.hh" + +namespace Dune +{ + // forward declaration + template + class ArrayListIterator; + + template + class ConstArrayListIterator; + + /** + * @file + * \brief Implements a random-access container that can efficiently change size (similar to std::deque) + * + * This file implements the class ArrayList which behaves like + * dynamically growing array together with + * the class ArrayListIterator which is random access iterator as needed + * by the stl for sorting and other algorithms. + * @author Markus Blatt + */ + /** + * @addtogroup Common + * + * @{ + */ + + /** + * @brief A dynamically growing random access list. + * + * Internally the data is organised in a list of arrays of fixed size. + * Whenever the capacity of the array list is not sufficient a new + * std::array is allocated. In contrast to + * std::vector this approach prevents data copying. On the outside + * we provide the same interface as the stl random access containers. + * + * While the concept sounds quite similar to std::deque there are slight + * but crucial differences: + * - In contrast to std:deque the actual implementation (a list of arrays) + * is known. While + * for std::deque there are at least two possible implementations + * (dynamic array or using a double linked list. + * - In contrast to std:deque there is not insert which invalidates iterators + * but our push_back method leaves all iterators valid. + * - Additional functionality lets one delete entries before and at an + * iterator while moving the iterator to the next valid position. + */ + template > + class ArrayList + { + public: + /** + * @brief The member type that is stored. + * + * Has to be assignable and has to have an empty constructor. + */ + typedef T MemberType; + + /** + * @brief Value type for stl compliance. + */ + typedef T value_type; + + /** + * @brief The type of a reference to the type we store. + */ + typedef T& reference; + + /** + * @brief The type of a const reference to the type we store. + */ + typedef const T& const_reference; + + /** + * @brief The type of a pointer to the type we store. + */ + typedef T* pointer; + + /** + * @brief The type of a const pointer to the type we store. + */ + typedef const T* const_pointer; + + enum + { + /** + * @brief The number of elements in one chunk of the list. + * This has to be at least one. The default is 100. + */ + chunkSize_ = (N > 0) ? N : 1 + }; + + /** + * @brief A random access iterator. + */ + typedef ArrayListIterator iterator; + + /** + * @brief A constant random access iterator. + */ + typedef ConstArrayListIterator const_iterator; + + /** + * @brief The size type. + */ + typedef std::size_t size_type; + + /** + * @brief The difference type. + */ + typedef std::ptrdiff_t difference_type; + + /** + * @brief Get an iterator that is positioned at the first element. + * @return The iterator. + */ + iterator begin(); + + /** + * @brief Get a random access iterator that is positioned at the + * first element. + * @return The iterator. + */ + const_iterator begin() const; + + /** + * @brief Get a random access iterator positioned after the last + * element + */ + iterator end(); + + /** + * @brief Get a random access iterator positioned after the last + * element + */ + const_iterator end() const; + + /** + * @brief Append an entry to the list. + * @param entry The new entry. + */ + inline void push_back(const_reference entry); + + /** + * @brief Get the element at specific position. + * @param i The index of the position. + * @return The element at that position. + */ + inline reference operator[](size_type i); + + /** + * @brief Get the element at specific position. + * @param i The index of the position. + * @return The element at that position. + */ + inline const_reference operator[](size_type i) const; + + /** + * @brief Get the number of elements in the list. + * @return The number of elements. + */ + inline size_type size() const; + + /** + * @brief Purge the list. + * + * If there are empty chunks at the front all nonempty + * chunks will be moved towards the front and the capacity + * increases. + */ + inline void purge(); + + /** + * @brief Delete all entries from the list. + */ + inline void clear(); + /** + * @brief Constructs an Array list with one chunk. + */ + ArrayList(); + + private: + + /** + * @brief The allocators for the smart pointer. + */ + using SmartPointerAllocator = typename std::allocator_traits::template rebind_alloc< std::shared_ptr< std::array > >; + + /** + * @brief The allocator for the fixed array. + */ + using ArrayAllocator = typename std::allocator_traits::template rebind_alloc< std::array >; + + /** + * @brief The iterator needs access to the private variables. + */ + friend class ArrayListIterator; + friend class ConstArrayListIterator; + + /** @brief the data chunks of our list. */ + std::vector >, + SmartPointerAllocator> chunks_; + /** @brief The current data capacity. + * This is the capacity that the list could have theoretically + * with this number of chunks. That is chunks * chunkSize. + * In practice some of the chunks at the beginning might be empty + * (i.e. null pointers in the first start_/chunkSize chunks) + * because of previous calls to eraseToHere. + * start_+size_<=capacity_ holds. + */ + size_type capacity_; + /** @brief The current number of elements in our data structure. */ + size_type size_; + /** @brief The index of the first entry. */ + size_type start_; + /** + * @brief Get the element at specific position. + * + * Index 0 always refers to the first entry in the list + * whether it is erased or not! + * @param i The index of the position. + * @return The element at that position. + */ + inline reference elementAt(size_type i); + + /** + * @brief Get the element at specific position. + * + * Index 0 always refers to the first entry in the list + * whether it is erased or not! + * @param i The index of the position. + * @return The element at that position. + */ + inline const_reference elementAt(size_type i) const; + }; + + + /** + * @brief A random access iterator for the Dune::ArrayList class. + */ + template + class ArrayListIterator : public RandomAccessIteratorFacade, + typename A::value_type, + typename A::value_type &, + typename A::difference_type> + { + + friend class ArrayList; + friend class ConstArrayListIterator; + public: + /** + * @brief The member type. + */ + typedef typename A::value_type MemberType; + + typedef typename A::difference_type difference_type; + + typedef typename A::size_type size_type; + + using reference = typename A::value_type &; + + using const_reference = typename A::value_type const&; + + enum + { + /** + * @brief The number of elements in one chunk of the list. + * + * This has to be at least one. The default is 100. + */ + chunkSize_ = (N > 0) ? N : 1 + }; + + + /** + * @brief Comares two iterators. + * @return True if the iterators are for the same list and + * at the position. + */ + inline bool equals(const ArrayListIterator& other) const; + + /** + * @brief Comares two iterators. + * @return True if the iterators are for the same list and + * at the position. + */ + inline bool equals(const ConstArrayListIterator& other) const; + + /** + * @brief Increment the iterator. + */ + inline void increment(); + + /** + * @brief decrement the iterator. + */ + inline void decrement(); + + /** + * @brief Get the value of the list at an arbitrary position. + * @return The value at that position. + */ + inline reference elementAt(size_type i) const; + + /** + * @brief Access the element at the current position. + * @return The element at the current position. + */ + inline reference dereference() const; + + /** + * @brief Erase all entries before the current position + * and the one at the current position. + * + * Afterwards the iterator will be positioned at the next + * unerased entry or the end if the list is empty. + * This does not invalidate any iterators positioned after + * the current position but those positioned at previous ones. + * @return An iterator to the first position after the deleted + * ones or to the end if the list is empty. + */ + inline void eraseToHere(); + + /** \todo Please doc me! */ + inline size_type position(){return position_;} + + /** \todo Please doc me! */ + inline void advance(difference_type n); + + /** \todo Please doc me! */ + inline difference_type distanceTo(const ArrayListIterator& other) const; + + /** \todo Please doc me! */ + inline ArrayListIterator& operator=(const ArrayListIterator& other); + + //! Standard constructor + inline ArrayListIterator() : position_(0), list_(nullptr) + {} + + private: + /** + * @brief Constructor. + * @param list The list we are an iterator for. + * @param position The initial position of the iterator. + */ + inline ArrayListIterator(ArrayList& arrayList, size_type position); + + /** + * @brief The current position. + */ + size_type position_; + /** + * @brief The list we are an iterator for. + */ + ArrayList* list_; + }; + + /** + * @brief A constant random access iterator for the Dune::ArrayList class. + */ + template + class ConstArrayListIterator + : public RandomAccessIteratorFacade, + const typename A::value_type, + typename A::value_type const&, + typename A::difference_type> + { + + friend class ArrayList; + friend class ArrayListIterator; + + public: + /** + * @brief The member type. + */ + typedef typename A::value_type MemberType; + + typedef typename A::difference_type difference_type; + + typedef typename A::size_type size_type; + + using reference = typename A::value_type &; + + using const_reference = typename A::value_type const&; + + enum + { + /** + * @brief The number of elements in one chunk of the list. + * + * This has to be at least one. The default is 100. + */ + chunkSize_ = (N > 0) ? N : 1 + }; + + /** + * @brief Comares to iterators. + * @return true if the iterators are for the same list and + * at the position. + */ + inline bool equals(const ConstArrayListIterator& other) const; + + /** + * @brief Increment the iterator. + */ + inline void increment(); + + /** + * @brief decrement the iterator. + */ + inline void decrement(); + + /** \todo Please doc me! */ + inline void advance(difference_type n); + + /** \todo Please doc me! */ + inline difference_type distanceTo(const ConstArrayListIterator& other) const; + + /** + * @brief Get the value of the list at an arbitrary position. + * @return The value at that position. + */ + inline const_reference elementAt(size_type i) const; + + /** + * @brief Access the element at the current position. + * @return The element at the current position. + */ + inline const_reference dereference() const; + + inline const ConstArrayListIterator& operator=(const ConstArrayListIterator& other); + + inline ConstArrayListIterator() : position_(0), list_(nullptr) + {} + + inline ConstArrayListIterator(const ArrayListIterator& other); + + private: + + /** + * @brief Constructor. + * @param list The list we are an iterator for. + * @param position The initial position of the iterator. + */ + inline ConstArrayListIterator(const ArrayList& arrayList, size_type position); + + /** + * @brief The current position. + */ + size_type position_; + /** + * @brief The list we are an iterator for. + */ + const ArrayList* list_; + }; + + + template + ArrayList::ArrayList() + : capacity_(0), size_(0), start_(0) + { + chunks_.reserve(100); + } + + template + void ArrayList::clear(){ + capacity_=0; + size_=0; + start_=0; + chunks_.clear(); + } + + template + size_t ArrayList::size() const + { + return size_; + } + + template + void ArrayList::push_back(const_reference entry) + { + size_t index=start_+size_; + if(index==capacity_) + { + chunks_.push_back(std::make_shared >()); + capacity_ += chunkSize_; + } + elementAt(index)=entry; + ++size_; + } + + template + typename ArrayList::reference ArrayList::operator[](size_type i) + { + return elementAt(start_+i); + } + + + template + typename ArrayList::const_reference ArrayList::operator[](size_type i) const + { + return elementAt(start_+i); + } + + template + typename ArrayList::reference ArrayList::elementAt(size_type i) + { + return chunks_[i/chunkSize_]->operator[](i%chunkSize_); + } + + + template + typename ArrayList::const_reference ArrayList::elementAt(size_type i) const + { + return chunks_[i/chunkSize_]->operator[](i%chunkSize_); + } + + template + ArrayListIterator ArrayList::begin() + { + return ArrayListIterator(*this, start_); + } + + template + ConstArrayListIterator ArrayList::begin() const + { + return ConstArrayListIterator(*this, start_); + } + + template + ArrayListIterator ArrayList::end() + { + return ArrayListIterator(*this, start_+size_); + } + + template + ConstArrayListIterator ArrayList::end() const + { + return ConstArrayListIterator(*this, start_+size_); + } + + template + void ArrayList::purge() + { + // Distance to copy to the left. + size_t distance = start_/chunkSize_; + if(distance>0) { + // Number of chunks with entries in it; + size_t chunks = ((start_%chunkSize_ + size_)/chunkSize_ ); + + // Copy chunks to the left. + std::copy(chunks_.begin()+distance, + chunks_.begin()+(distance+chunks), chunks_.begin()); + + // Calculate new parameters + start_ = start_ % chunkSize_; + //capacity += distance * chunkSize_; + } + } + + template + void ArrayListIterator::advance(difference_type i) + { + position_+=i; + } + + template + void ConstArrayListIterator::advance(difference_type i) + { + position_+=i; + } + + + template + bool ArrayListIterator::equals(const ArrayListIterator& other) const + { + // Makes only sense if we reference a common list + assert(list_==(other.list_)); + return position_==other.position_ ; + } + + + template + bool ArrayListIterator::equals(const ConstArrayListIterator& other) const + { + // Makes only sense if we reference a common list + assert(list_==(other.list_)); + return position_==other.position_ ; + } + + + template + bool ConstArrayListIterator::equals(const ConstArrayListIterator& other) const + { + // Makes only sense if we reference a common list + assert(list_==(other.list_)); + return position_==other.position_ ; + } + + template + void ArrayListIterator::increment() + { + ++position_; + } + + template + void ConstArrayListIterator::increment() + { + ++position_; + } + + template + void ArrayListIterator::decrement() + { + --position_; + } + + template + void ConstArrayListIterator::decrement() + { + --position_; + } + + template + typename ArrayListIterator::reference ArrayListIterator::elementAt(size_type i) const + { + return list_->elementAt(i+position_); + } + + template + typename ConstArrayListIterator::const_reference ConstArrayListIterator::elementAt(size_type i) const + { + return list_->elementAt(i+position_); + } + + template + typename ArrayListIterator::reference ArrayListIterator::dereference() const + { + return list_->elementAt(position_); + } + + template + typename ConstArrayListIterator::const_reference ConstArrayListIterator::dereference() const + { + return list_->elementAt(position_); + } + + template + typename ArrayListIterator::difference_type ArrayListIterator::distanceTo(const ArrayListIterator& other) const + { + // Makes only sense if we reference a common list + assert(list_==(other.list_)); + return other.position_ - position_; + } + + template + typename ConstArrayListIterator::difference_type ConstArrayListIterator::distanceTo(const ConstArrayListIterator& other) const + { + // Makes only sense if we reference a common list + assert(list_==(other.list_)); + return other.position_ - position_; + } + + template + ArrayListIterator& ArrayListIterator::operator=(const ArrayListIterator& other) + { + position_=other.position_; + list_=other.list_; + return *this; + } + + template + const ConstArrayListIterator& ConstArrayListIterator::operator=(const ConstArrayListIterator& other) + { + position_=other.position_; + list_=other.list_; + return *this; + } + + template + void ArrayListIterator::eraseToHere() + { + list_->size_ -= ++position_ - list_->start_; + // chunk number of the new position. + size_t posChunkStart = position_ / chunkSize_; + // number of chunks to deallocate + size_t chunks = (position_ - list_->start_ + list_->start_ % chunkSize_) + / chunkSize_; + list_->start_ = position_; + + // Deallocate memory not needed any more. + for(size_t chunk=0; chunkchunks_[posChunkStart].reset(); + } + + // Capacity stays the same as the chunks before us + // are still there. They null pointers. + assert(list_->start_+list_->size_<=list_->capacity_); + } + + template + ArrayListIterator::ArrayListIterator(ArrayList& arrayList, size_type position) + : position_(position), list_(&arrayList) + {} + + + template + ConstArrayListIterator::ConstArrayListIterator(const ArrayList& arrayList, + size_type position) + : position_(position), list_(&arrayList) + {} + + template + ConstArrayListIterator::ConstArrayListIterator(const ArrayListIterator& other) + : position_(other.position_), list_(other.list_) + {} + + + /** @} */ +} +#endif diff --git a/dune/common/assertandreturn.hh b/dune/common/assertandreturn.hh new file mode 100644 index 0000000..b424911 --- /dev/null +++ b/dune/common/assertandreturn.hh @@ -0,0 +1,25 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_ASSERTANDRETURN_HH +#define DUNE_COMMON_ASSERTANDRETURN_HH + +#include + +//! Asserts a condition and return on success in constexpr context. +/** + * The macro DUNE_ASSERT_AND_RETURN can be used as expression in the return + * statement of a constexpr function to have assert() and constexpr at the + * same time. It first uses assert for the condition given by the first argument + * and then returns the value of the second argument. + * + * \ingroup CxxUtilities + */ +#ifdef NDEBUG + #define DUNE_ASSERT_AND_RETURN(C,X) X +#else + #define DUNE_ASSERT_AND_RETURN(C,X) (!(C) ? throw [&](){assert(!#C);return 0;}() : 0), X +#endif + + + +#endif // DUNE_COMMON_ASSERTANDRETURN_HH diff --git a/dune/common/bartonnackmanifcheck.hh b/dune/common/bartonnackmanifcheck.hh new file mode 100644 index 0000000..3a60d3b --- /dev/null +++ b/dune/common/bartonnackmanifcheck.hh @@ -0,0 +1,64 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +/** @file + @author Robert Kloefkorn + @brief Provides check for implementation of interface methods when using + static polymorphism, i.e. the Barton-Nackman trick. This is purely for + debugging purposes. To check the correct implementation of interface methods + (and pick up possible infinite loops) NDEBUG must be undefined and + DUNE_INTERFACECHECK has to be defined. + + Use by invoking CHECK_INTERFACE_IMPLEMENTATION(asImp().methodToCheck()) + and for + template methods double (CHECK_INTERFACE_IMPLEMENTATION((asImp().template methodToCheck ())). + If either NDEBUG is defined or + DUNE_INTERFACECHECK is undefined the CHECK_INTERFACE_IMPLEMENTATION macro is empty. + + Note: adding the interface check to a method will cause the implementation of the + method to be called twice, so before use make sure + that this will not cause problems e.g. if internal counters are updated. + **/ + +//- Dune includes +#include + +#ifdef CHECK_INTERFACE_IMPLEMENTATION +#undef CHECK_INTERFACE_IMPLEMENTATION +#endif +#ifdef CHECK_AND_CALL_INTERFACE_IMPLEMENTATION +#undef CHECK_AND_CALL_INTERFACE_IMPLEMENTATION +#endif + +#if defined NDEBUG || !defined DUNE_INTERFACECHECK +#define CHECK_INTERFACE_IMPLEMENTATION(dummy) +#else +#define CHECK_INTERFACE_IMPLEMENTATION(__interface_method_to_call__) \ + {\ + static bool call = false; \ + if( call == true ) \ + DUNE_THROW(NotImplemented,"Interface method not implemented!");\ + call = true; \ + try { \ + (__interface_method_to_call__); \ + call = false; \ + } \ + catch ( ... ) \ + { \ + call = false; \ + throw; \ + } \ + } +#endif + +/** The macro CHECK_AND_CALL_INTERFACE_IMPLEMENTATION throws an exception, + if the interface method ist not implemented and just calls the method + otherwise. If NDEBUG is defined no + checking is done and the method is just called. + */ +#if defined NDEBUG || !defined DUNE_INTERFACECHECK +#define CHECK_AND_CALL_INTERFACE_IMPLEMENTATION(__interface_method_to_call__) \ + (__interface_method_to_call__) +#else +#define CHECK_AND_CALL_INTERFACE_IMPLEMENTATION(__interface_method_to_call__) \ + CHECK_INTERFACE_IMPLEMENTATION(__interface_method_to_call__) +#endif diff --git a/dune/common/bigunsignedint.hh b/dune/common/bigunsignedint.hh new file mode 100644 index 0000000..d85e0e4 --- /dev/null +++ b/dune/common/bigunsignedint.hh @@ -0,0 +1,691 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_BIGUNSIGNEDINT_HH +#define DUNE_BIGUNSIGNEDINT_HH + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @file + * @brief Portable very large unsigned integers + * @author Peter Bastian + */ + +namespace Dune +{ +#if HAVE_MPI + template + struct MPITraits; +#endif + + /** @addtogroup Numbers + * + * @{ + */ + + namespace Impl { + + // numeric_limits_helper provides std::numeric_limits access to the internals + // of bigunsignedint. Previously, the correct specialization of std::numeric_limits + // was a friend of bigunsignedint, but that creates problems on recent versions + // of clang with the alternative libc++ library, because that library declares the + // base template of std::numeric_limits as a class and clang subsequently complains + // if the friend declaration uses 'struct'. Unfortunately, libstdc++ uses a struct, + // making it impossible to keep clang happy for both standard libraries. + // So we move the access helper functionality into a custom struct and simply let + // the numeric_limits specialization inherit from the helper. + + template + struct numeric_limits_helper + { + + protected: + + static std::uint16_t& digit(T& big_unsigned_int, std::size_t i) + { + return big_unsigned_int.digit[i]; + } + + }; + + } + + /** + * @brief Portable very large unsigned integers + * + * Implements (arbitrarily) large unsigned integers to be used as global + * ids in some grid managers. Size is a template parameter. + * + * \tparam k Number of bits of the integer type + */ + + template + class bigunsignedint { + public: + + // unsigned short is 16 bits wide, n is the number of digits needed + enum { bits=std::numeric_limits::digits, n=k/bits+(k%bits!=0), + hexdigits=4, bitmask=0xFFFF, compbitmask=0xFFFF0000, + overflowmask=0x1 }; + + //! Construct uninitialized + bigunsignedint (); + + //! Construct from signed int + template + bigunsignedint (Signed x, typename std::enable_if::value && std::is_signed::value>::type* = 0); + + //! Construct from unsigned int + bigunsignedint (std::uintmax_t x); + + //! Print number in hex notation + void print (std::ostream& s) const ; + + //! add + bigunsignedint operator+ (const bigunsignedint& x) const; + bigunsignedint& operator+= (const bigunsignedint& x); + + //! subtract + bigunsignedint operator- (const bigunsignedint& x) const; + bigunsignedint& operator-= (const bigunsignedint& x); + + //! multiply + bigunsignedint operator* (const bigunsignedint& x) const; + bigunsignedint& operator*= (const bigunsignedint& x); + + //! prefix increment + bigunsignedint& operator++ (); + + //! divide + //! \warning This function is very slow and its usage should be + //! prevented if possible + bigunsignedint operator/ (const bigunsignedint& x) const; + bigunsignedint& operator/= (const bigunsignedint& x); + + //! modulo + //! \warning This function is very slow and its usage should be + //! prevented if possible + bigunsignedint operator% (const bigunsignedint& x) const; + bigunsignedint& operator%= (const bigunsignedint& x); + + //! bitwise and + bigunsignedint operator& (const bigunsignedint& x) const; + bigunsignedint& operator&= (const bigunsignedint& x); + + //! bitwise exor + bigunsignedint operator^ (const bigunsignedint& x) const; + bigunsignedint& operator^= (const bigunsignedint& x); + + //! bitwise or + bigunsignedint operator| (const bigunsignedint& x) const; + bigunsignedint& operator|= (const bigunsignedint& x); + + //! bitwise complement + bigunsignedint operator~ () const; + + + //! left shift + bigunsignedint operator<< (int i) const; + + //! right shift + bigunsignedint operator>> (int i) const; + + + //! less than + bool operator< (const bigunsignedint& x) const; + + //! less than or equal + bool operator<= (const bigunsignedint& x) const; + + //! greater than + bool operator> (const bigunsignedint& x) const; + + //! greater or equal + bool operator>= (const bigunsignedint& x) const; + + //! equal + bool operator== (const bigunsignedint& x) const; + + //! not equal + bool operator!= (const bigunsignedint& x) const; + + + //! export to other types + // operator unsigned int () const; + std::uint_least32_t touint() const; + /** + * @brief Convert to a double. + * + * @warning Subject to rounding errors! + */ + double todouble() const; + + friend class bigunsignedint; + friend struct Impl::numeric_limits_helper< bigunsignedint >; + + inline friend std::size_t hash_value(const bigunsignedint& arg) + { + return hash_range(arg.digit,arg.digit + arg.n); + } + + private: + std::uint16_t digit[n]; +#if HAVE_MPI + friend struct MPITraits >; +#endif + inline void assign(std::uintmax_t x); + + + } ; + + // Constructors + template + bigunsignedint::bigunsignedint () + { + assign(0u); + } + + template + template + bigunsignedint::bigunsignedint (Signed y, typename std::enable_if::value && std::is_signed::value>::type*) + { + if (y < 0) + DUNE_THROW(Dune::Exception, "Trying to construct a Dune::bigunsignedint from a negative integer: " << y); + assign(y); + } + + template + bigunsignedint::bigunsignedint (std::uintmax_t x) + { + assign(x); + } + template + void bigunsignedint::assign(std::uintmax_t x) + { + static const int no=std::min(static_cast(n), + static_cast(std::numeric_limits::digits/bits)); + + for(int i=0; i>bits; + } + for (unsigned int i=no; i + inline std::uint_least32_t bigunsignedint::touint () const + { + return (digit[1]< + inline double bigunsignedint::todouble() const + { + int firstInZeroRange=n; + for(int i=n-1; i>=0; --i) + if(digit[i]!=0) + break; + else + --firstInZeroRange; + int representableDigits=std::numeric_limits::digits/bits; + int lastInRepresentableRange=0; + if(representableDigits=lastInRepresentableRange; --i) + val =val*(1< + inline void bigunsignedint::print (std::ostream& s) const + { + bool leading=false; + + // print from left to right + for (int i=n-1; i>=0; i--) + for (int d=hexdigits-1; d>=0; d--) + { + // extract one hex digit + int current = (digit[i]>>(d*4))&0xF; + if (current!=0) + { + // s.setf(std::ios::noshowbase); + s << std::hex << current; + leading = false; + } + else if (!leading) s << std::hex << current; + } + if (leading) s << "0"; + s << std::dec; + } + + template + inline std::ostream& operator<< (std::ostream& s, const bigunsignedint& x) + { + x.print(s); + return s; + } + + #define DUNE_BINOP(OP) \ + template \ + inline bigunsignedint bigunsignedint::operator OP (const bigunsignedint &x) const \ + { \ + auto temp = *this; \ + temp OP##= x; \ + return temp; \ + } + + DUNE_BINOP(+) + DUNE_BINOP(-) + DUNE_BINOP(*) + DUNE_BINOP(/) + DUNE_BINOP(%) + DUNE_BINOP(&) + DUNE_BINOP(^) + DUNE_BINOP(|) + + #undef DUNE_BINOP + + template + inline bigunsignedint& bigunsignedint::operator+= (const bigunsignedint& x) + { + std::uint_fast32_t overflow=0; + + for (unsigned int i=0; i(digit[i]) + static_cast(x.digit[i]) + overflow; + digit[i] = sum&bitmask; + overflow = (sum>>bits)&overflowmask; + } + return *this; + } + + template + inline bigunsignedint& bigunsignedint::operator-= (const bigunsignedint& x) + { + std::int_fast32_t overflow=0; + + for (unsigned int i=0; i(digit[i]) - static_cast(x.digit[i]) - overflow; + if (diff>=0) + { + digit[i] = static_cast(diff); + overflow = 0; + } + else + { + digit[i] = static_cast(diff+bitmask+1); + overflow = 1; + } + } + return *this; + } + + template + inline bigunsignedint& bigunsignedint::operator*= (const bigunsignedint& x) + { + bigunsignedint<2*k> finalproduct(0); + + for (unsigned int m=0; m singleproduct(0); + std::uint_fast32_t overflow(0); + for (unsigned int i=0; i(digit[i])*static_cast(x.digit[m])+overflow; + singleproduct.digit[i+m] = static_cast(digitproduct&bitmask); + overflow = (digitproduct>>bits)&bitmask; + } + finalproduct = finalproduct+singleproduct; + } + + for (unsigned int i=0; i + inline bigunsignedint& bigunsignedint::operator++ () + { + std::uint_fast32_t overflow=1; + + for (unsigned int i=0; i(digit[i]) + overflow; + digit[i] = sum&bitmask; + overflow = (sum>>bits)&overflowmask; + } + return *this; + } + + template + inline bigunsignedint& bigunsignedint::operator/= (const bigunsignedint& x) + { + if(x==0) + DUNE_THROW(Dune::MathError, "division by zero!"); + + // better slow than nothing + bigunsignedint result(0); + + while (*this>=x) + { + ++result; + *this -= x; + } + + *this = result; + return *this; + } + + template + inline bigunsignedint& bigunsignedint::operator%= (const bigunsignedint& x) + { + // better slow than nothing + while (*this>=x) + { + *this -= x; + } + + return *this; + } + + template + inline bigunsignedint& bigunsignedint::operator&= (const bigunsignedint& x) + { + for (unsigned int i=0; i + inline bigunsignedint& bigunsignedint::operator^= (const bigunsignedint& x) + { + for (unsigned int i=0; i + inline bigunsignedint& bigunsignedint::operator|= (const bigunsignedint& x) + { + for (unsigned int i=0; i + inline bigunsignedint bigunsignedint::operator~ () const + { + bigunsignedint result; + for (unsigned int i=0; i + inline bigunsignedint bigunsignedint::operator<< (int shift) const + { + bigunsignedint result(0); + + // multiples of bits + int j=shift/bits; + for (int i=n-1-j; i>=0; i--) + result.digit[i+j] = digit[i]; + + // remainder + j=shift%bits; + for (int i=n-1; i>=0; i--) + { + unsigned int temp = result.digit[i]; + temp = temp<(temp&bitmask); + temp = temp>>bits; + if (i+1<(int)n) + result.digit[i+1] = result.digit[i+1]|temp; + } + + return result; + } + + template + inline bigunsignedint bigunsignedint::operator>> (int shift) const + { + bigunsignedint result(0); + + // multiples of bits + int j=shift/bits; + for (unsigned int i=0; i((temp&compbitmask)>>bits); + if (i>0) + result.digit[i-1] = result.digit[i-1] | (temp&bitmask); + } + + return result; + } + + template + inline bool bigunsignedint::operator!= (const bigunsignedint& x) const + { + for (unsigned int i=0; i + inline bool bigunsignedint::operator== (const bigunsignedint& x) const + { + return !((*this)!=x); + } + + template + inline bool bigunsignedint::operator< (const bigunsignedint& x) const + { + for (int i=n-1; i>=0; i--) + if (digit[i]x.digit[i]) return false; + return false; + } + + template + inline bool bigunsignedint::operator<= (const bigunsignedint& x) const + { + for (int i=n-1; i>=0; i--) + if (digit[i]x.digit[i]) return false; + return true; + } + + template + inline bool bigunsignedint::operator> (const bigunsignedint& x) const + { + return !((*this)<=x); + } + + template + inline bool bigunsignedint::operator>= (const bigunsignedint& x) const + { + return !((*this) + inline bigunsignedint operator+ (const bigunsignedint& x, std::uintmax_t y) + { + bigunsignedint temp(y); + return x+temp; + } + + template + inline bigunsignedint operator- (const bigunsignedint& x, std::uintmax_t y) + { + bigunsignedint temp(y); + return x-temp; + } + + template + inline bigunsignedint operator* (const bigunsignedint& x, std::uintmax_t y) + { + bigunsignedint temp(y); + return x*temp; + } + + template + inline bigunsignedint operator/ (const bigunsignedint& x, std::uintmax_t y) + { + bigunsignedint temp(y); + return x/temp; + } + + template + inline bigunsignedint operator% (const bigunsignedint& x, std::uintmax_t y) + { + bigunsignedint temp(y); + return x%temp; + } + + template + inline bigunsignedint operator+ (std::uintmax_t x, const bigunsignedint& y) + { + bigunsignedint temp(x); + return temp+y; + } + + template + inline bigunsignedint operator- (std::uintmax_t x, const bigunsignedint& y) + { + bigunsignedint temp(x); + return temp-y; + } + + template + inline bigunsignedint operator* (std::uintmax_t x, const bigunsignedint& y) + { + bigunsignedint temp(x); + return temp*y; + } + + template + inline bigunsignedint operator/ (std::uintmax_t x, const bigunsignedint& y) + { + bigunsignedint temp(x); + return temp/y; + } + + template + inline bigunsignedint operator% (std::uintmax_t x, const bigunsignedint& y) + { + bigunsignedint temp(x); + return temp%y; + } + + + /** @} */ +} + +namespace std +{ + template + struct numeric_limits > + : private Dune::Impl::numeric_limits_helper > // for access to internal state of bigunsignedint + { + public: + static const bool is_specialized = true; + + static Dune::bigunsignedint min() + { + return static_cast >(0); + } + + static Dune::bigunsignedint max() + { + Dune::bigunsignedint max_; + for(std::size_t i=0; i < Dune::bigunsignedint::n; ++i) + // access internal state via the helper base class + Dune::Impl::numeric_limits_helper >:: + digit(max_,i)=std::numeric_limits::max(); + return max_; + } + + + static const int digits = Dune::bigunsignedint::bits * + Dune::bigunsignedint::n; + static const bool is_signed = false; + static const bool is_integer = true; + static const bool is_exact = true; + static const int radix = 2; + + static Dune::bigunsignedint epsilon() + { + return static_cast >(0); + } + + static Dune::bigunsignedint round_error() + { + return static_cast >(0); + } + + static const int min_exponent = 0; + static const int min_exponent10 = 0; + static const int max_exponent = 0; + static const int max_exponent10 = 0; + + static const bool has_infinity = false; + static const bool has_quiet_NaN = false; + static const bool has_signaling_NaN = false; + + static const float_denorm_style has_denorm = denorm_absent; + static const bool has_denorm_loss = false; + + static Dune::bigunsignedint infinity() noexcept + { + return static_cast >(0); + } + + static Dune::bigunsignedint quiet_NaN() noexcept + { + return static_cast >(0); + } + + static Dune::bigunsignedint signaling_NaN() noexcept + { + return static_cast >(0); + } + + static Dune::bigunsignedint denorm_min() noexcept + { + return static_cast >(0); + } + + static const bool is_iec559 = false; + static const bool is_bounded = true; + static const bool is_modulo = true; + + static const bool traps = false; + static const bool tinyness_before = false; + static const float_round_style round_style = round_toward_zero; + + }; + +} + +DUNE_DEFINE_HASH(DUNE_HASH_TEMPLATE_ARGS(int k),DUNE_HASH_TYPE(Dune::bigunsignedint)) + +#endif diff --git a/dune/common/binaryfunctions.hh b/dune/common/binaryfunctions.hh new file mode 100644 index 0000000..6f0b7b9 --- /dev/null +++ b/dune/common/binaryfunctions.hh @@ -0,0 +1,47 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_BINARYFUNCTIONS_HH +#define DUNE_BINARYFUNCTIONS_HH + +/** \file + * \brief helper classes to provide unique types for standard functions + */ + +#include + +namespace Dune +{ + template + struct Min + { + using first_argument_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; + + using second_argument_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; + + using result_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; + + Type operator()(const Type& t1, const Type& t2) const + { + using std::min; + return min(t1,t2); + } + }; + + template + struct Max + { + using first_argument_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; + + using second_argument_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; + + using result_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; + + Type operator()(const Type& t1, const Type& t2) const + { + using std::max; + return max(t1,t2); + } + }; +} + +#endif diff --git a/dune/common/bitsetvector.hh b/dune/common/bitsetvector.hh new file mode 100644 index 0000000..1b4c0f3 --- /dev/null +++ b/dune/common/bitsetvector.hh @@ -0,0 +1,653 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_BLOCK_BITFIELD_HH +#define DUNE_BLOCK_BITFIELD_HH + +/** \file + \brief Efficient implementation of a dynamic array of static arrays of booleans + */ + +#include +#include +#include +#include + +#include +#include +#include + +namespace Dune { + + template class BitSetVector; + template class BitSetVectorReference; + + /** + \brief A proxy class that acts as a const reference to a single + bitset in a BitSetVector. + + It contains a conversion to std::bitset and most of the + interface of const std::bitset. + + \warning As this is only a proxy class, you can not get the + address of the bitset. + */ + template + class BitSetVectorConstReference + { + protected: + + typedef Dune::BitSetVector BitSetVector; + friend class Dune::BitSetVector; + + BitSetVectorConstReference(const BitSetVector& blockBitField_, int block_number_) : + blockBitField(blockBitField_), + block_number(block_number_) + { + DUNE_ASSERT_BOUNDS(blockBitField_.size() > static_cast(block_number_)); + } + + //! hide assignment operator + BitSetVectorConstReference& operator=(const BitSetVectorConstReference & b); + + public: + + typedef std::bitset bitset; + + // bitset interface typedefs + typedef typename std::vector::const_reference reference; + typedef typename std::vector::const_reference const_reference; + typedef size_t size_type; + + //! Returns a copy of *this shifted left by n bits. + bitset operator<<(size_type n) const + { + bitset b = *this; + b <<= n; + return b; + } + + //! Returns a copy of *this shifted right by n bits. + bitset operator>>(size_type n) const + { + bitset b = *this; + b >>= n; + return b; + } + + //! Returns a copy of *this with all of its bits flipped. + bitset operator~() const + { + bitset b = *this; + b.flip(); + return b; + } + + //! Returns block_size. + size_type size() const + { + return block_size; + } + + //! Returns the number of bits that are set. + size_type count() const + { + size_type n = 0; + for(size_type i=0; i + bool equals(const BS & bs) const + { + bool eq = true; + for(int i=0; i; + }; + + /** + \brief A proxy class that acts as a mutable reference to a + single bitset in a BitSetVector. + + It contains an assignment operator from std::bitset. It + inherits the const std::bitset interface provided by + BitSetVectorConstReference and adds most of the non-const + methods of std::bitset. + + \warning As this is only a proxy class, you can not get the + address of the bitset. + */ + template + class BitSetVectorReference : public BitSetVectorConstReference + { + protected: + + typedef Dune::BitSetVector BitSetVector; + friend class Dune::BitSetVector; + + typedef Dune::BitSetVectorConstReference BitSetVectorConstReference; + + BitSetVectorReference(BitSetVector& blockBitField_, int block_number_) : + BitSetVectorConstReference(blockBitField_, block_number_), + blockBitField(blockBitField_) + {} + + public: + typedef std::bitset bitset; + + //! bitset interface typedefs + //! \{ + //! A proxy class that acts as a reference to a single bit. + typedef typename std::vector::reference reference; + //! A proxy class that acts as a const reference to a single bit. + typedef typename std::vector::const_reference const_reference; + //! \} + + //! size_type typedef (an unsigned integral type) + typedef size_t size_type; + + //! Assignment from bool, sets each bit in the bitset to b + BitSetVectorReference& operator=(bool b) + { + for(int i=0; i>=(size_type n) + { + for (size_type i=0; iblock_number,i); + } + }; + + // implementation of helper - I put it into the template to avoid having + // to compile it in a dedicated compilation unit + template + bool BitSetVectorReference::xor_helper(bool a, bool b) + { + return a ^ b; + } + + /** + typetraits for BitSetVectorReference + */ + template + struct const_reference< BitSetVectorReference > + { + typedef BitSetVectorConstReference type; + }; + + template + struct const_reference< BitSetVectorConstReference > + { + typedef BitSetVectorConstReference type; + }; + + template + struct mutable_reference< BitSetVectorReference > + { + typedef BitSetVectorReference type; + }; + + template + struct mutable_reference< BitSetVectorConstReference > + { + typedef BitSetVectorReference type; + }; + + /** + \brief A dynamic %array of blocks of booleans + */ + template > + class BitSetVector : private std::vector + { + /** \brief The implementation class: an unblocked bitfield */ + typedef std::vector BlocklessBaseClass; + + public: + //! container interface typedefs + //! \{ + + /** \brief Type of the values stored by the container */ + typedef std::bitset value_type; + + /** \brief Reference to a small block of bits */ + typedef BitSetVectorReference reference; + + /** \brief Const reference to a small block of bits */ + typedef BitSetVectorConstReference const_reference; + + /** \brief Pointer to a small block of bits */ + typedef BitSetVectorReference* pointer; + + /** \brief Const pointer to a small block of bits */ + typedef BitSetVectorConstReference* const_pointer; + + /** \brief size type */ + typedef typename std::vector::size_type size_type; + + /** \brief The type of the allocator */ + typedef Allocator allocator_type; + //! \} + + //! iterators + //! \{ + typedef Dune::GenericIterator, value_type, reference, std::ptrdiff_t, ForwardIteratorFacade> iterator; + typedef Dune::GenericIterator, const value_type, const_reference, std::ptrdiff_t, ForwardIteratorFacade> const_iterator; + //! \} + + //! Returns a iterator pointing to the beginning of the vector. + iterator begin(){ + return iterator(*this, 0); + } + + //! Returns a const_iterator pointing to the beginning of the vector. + const_iterator begin() const { + return const_iterator(*this, 0); + } + + //! Returns an iterator pointing to the end of the vector. + iterator end(){ + return iterator(*this, size()); + } + + //! Returns a const_iterator pointing to the end of the vector. + const_iterator end() const { + return const_iterator(*this, size()); + } + + //! Default constructor + BitSetVector() : + BlocklessBaseClass() + {} + + //! Construction from an unblocked bitfield + BitSetVector(const BlocklessBaseClass& blocklessBitField) : + BlocklessBaseClass(blocklessBitField) + { + if (blocklessBitField.size()%block_size != 0) + DUNE_THROW(RangeError, "Vector size is not a multiple of the block size!"); + } + + /** Constructor with a given length + \param n Number of blocks + */ + explicit BitSetVector(int n) : + BlocklessBaseClass(n*block_size) + {} + + //! Constructor which initializes the field with true or false + BitSetVector(int n, bool v) : + BlocklessBaseClass(n*block_size,v) + {} + + //! Erases all of the elements. + void clear() + { + BlocklessBaseClass::clear(); + } + + //! Resize field + void resize(int n, bool v = bool()) + { + BlocklessBaseClass::resize(n*block_size, v); + } + + /** \brief Return the number of blocks */ + size_type size() const + { + return BlocklessBaseClass::size()/block_size; + } + + //! Sets all entries to true + void setAll() { + this->assign(BlocklessBaseClass::size(), true); + } + + //! Sets all entries to false + void unsetAll() { + this->assign(BlocklessBaseClass::size(), false); + } + + /** \brief Return reference to i-th block */ + reference operator[](int i) + { + return reference(*this, i); + } + + /** \brief Return const reference to i-th block */ + const_reference operator[](int i) const + { + return const_reference(*this, i); + } + + /** \brief Return reference to last block */ + reference back() + { + return reference(*this, size()-1); + } + + /** \brief Return const reference to last block */ + const_reference back() const + { + return const_reference(*this, size()-1); + } + + //! Returns the number of bits that are set. + size_type count() const + { + return std::count(BlocklessBaseClass::begin(), BlocklessBaseClass::end(), true); + } + + //! Returns the number of set bits, while each block is masked with 1<::reference getBit(size_type i, size_type j) { + DUNE_ASSERT_BOUNDS(j < block_size); + DUNE_ASSERT_BOUNDS(i < size()); + return BlocklessBaseClass::operator[](i*block_size+j); + } + + typename std::vector::const_reference getBit(size_type i, size_type j) const { + DUNE_ASSERT_BOUNDS(j < block_size); + DUNE_ASSERT_BOUNDS(i < size()); + return BlocklessBaseClass::operator[](i*block_size+j); + } + + friend class BitSetVectorReference; + friend class BitSetVectorConstReference; + }; + +} // namespace Dune + +#endif diff --git a/dune/common/boundschecking.hh b/dune/common/boundschecking.hh new file mode 100644 index 0000000..355e304 --- /dev/null +++ b/dune/common/boundschecking.hh @@ -0,0 +1,41 @@ +#ifndef DUNE_BOUNDSCHECKING_HH +#define DUNE_BOUNDSCHECKING_HH + +#include + +/** + * \file + * \brief Macro for wrapping boundary checks + */ + +/** + * @addtogroup Common + * + * @{ + */ + +#ifndef DUNE_ASSERT_BOUNDS +#if defined(DUNE_CHECK_BOUNDS) || defined(DOXYGEN) + +/** + * \brief If `DUNE_CHECK_BOUNDS` is defined: check if condition + * \a cond holds; otherwise, do nothing. + * + * Meant to be used for conditions that assure writes and reads + * do not occur outside of memory limits or pre-defined patterns + * and related conditions. + */ +#define DUNE_ASSERT_BOUNDS(cond) \ + do { \ + if (!(cond)) \ + DUNE_THROW(Dune::RangeError, "Index out of bounds."); \ + } while (false) + +#else +#define DUNE_ASSERT_BOUNDS(cond) +#endif +#endif + +/* @} */ + +#endif // DUNE_BOUNDSCHECKING_HH diff --git a/dune/common/classname.hh b/dune/common/classname.hh new file mode 100644 index 0000000..f24e330 --- /dev/null +++ b/dune/common/classname.hh @@ -0,0 +1,77 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_CLASSNAME_HH +#define DUNE_CLASSNAME_HH + +/** \file + * \brief A free function to provide the demangled class name + * of a given object or type as a string + */ + +#include +#include +#include +#include +#include + +#if __has_include() && !DISABLE_CXA_DEMANGLE +#define HAVE_CXA_DEMANGLE 1 +#include +#endif // #if HAVE_CXA_DEMANGLE + +namespace Dune { + + namespace Impl { + + inline std::string demangle(std::string name) + { +#if HAVE_CXA_DEMANGLE + int status; + std::unique_ptr + demangled(abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), + std::free); + if( demangled ) + name = demangled.get(); +#endif // #if HAVE_CXA_DEMANGLE + return name; + } + } + + /** \brief Provide the demangled class name of a type T as a string */ + /* + * \ingroup CxxUtilities + */ + template + std::string className () + { + typedef typename std::remove_reference::type TR; + std::string className = Impl::demangle( typeid( TR ).name() ); + if (std::is_const::value) + className += " const"; + if (std::is_volatile::value) + className += " volatile"; + if (std::is_lvalue_reference::value) + className += "&"; + else if (std::is_rvalue_reference::value) + className += "&&"; + return className; + } + + /** \brief Provide the demangled class name of a given object as a string */ + /* + * \ingroup CxxUtilities + */ + template + std::string className ( T&& v) + { + typedef typename std::remove_reference::type TR; + std::string className = Impl::demangle( typeid(v).name() ); + if (std::is_const::value) + className += " const"; + if (std::is_volatile::value) + className += " volatile"; + return className; + } +} // namespace Dune + +#endif // DUNE_CLASSNAME_HH diff --git a/dune/common/concept.hh b/dune/common/concept.hh new file mode 100644 index 0000000..c129425 --- /dev/null +++ b/dune/common/concept.hh @@ -0,0 +1,331 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_CONCEPT_HH +#define DUNE_COMMON_CONCEPT_HH + +#include +#include +#include + +#include +#include +#include +#include + +/** + * \file + * + * \brief Infrastructure for concepts. + */ + +namespace Dune { + +/** + * \brief Namespace for concepts + * + * This namespace contains helper functions for + * concept definitions and the concept definitions + * themselves. + * + * \ingroup CxxConcepts + */ +namespace Concept { + + + +/** + * \brief Base class for refined concepts. + * + * If a new concept should refine one or more existing concepts, + * this can be achieved by deriving the new concept from + * Refines where C1, ..., CN are the concepts + * to be refined. If you want to refine several concepts + * they should all be put in a single Refines<...> base + * class. + * + * \tparam BaseConcepts The list of concepts to be refined. + * + * \ingroup CxxConcepts + */ +template +struct Refines +{ + typedef TypeList BaseConceptList; +}; + + +#ifndef DOXYGEN + +namespace Impl { + + // ############################################################################# + // # All functions following here are implementation details + // # for the models() function below. + // ############################################################################# + + // Forward declaration + template + constexpr bool models(); + + + + // Here is the implementation of the concept checking. + // The first two overloads do the magic for checking + // if the requirements of a concept are satisfied. + // The rest is just for checking base concepts in case + // of refinement. + + // This overload is present if type substitution for + // C::require(T...) is successful, i.e., if the T... + // matches the requirement of C. In this case this + // overload is selected because PriorityTag<1> + // is a better match for PrioriryTag<42> than + // PriorityTag<0> in the default overload. + template().require(std::declval()...), 0) =0> + constexpr std::true_type matchesRequirement(PriorityTag<1>) + { return {}; } + + // If the above overload is ruled out by SFINAE because + // the T... does not match the requirements of C, then + // this default overload drops in. + template + constexpr std::false_type matchesRequirement(PriorityTag<0>) + { return {}; } + + + + // An empty list C of concepts is always matched by T... + template + constexpr bool modelsConceptList(TypeList<>) + { return true; } + + // A nonempty list C0,..,CN of concepts is modeled + // by T... if it models the concept C0 + // and all concepts in the list C1,..,CN. + template + constexpr bool modelsConceptList(TypeList) + { return models() and modelsConceptList(TypeList()); } + + + + // If C is an unrefined concept, then T... models C + // if it matches the requirement of C. + template + constexpr bool modelsConcept(PriorityTag<0>) + { return matchesRequirement(PriorityTag<42>()); } + + // If C is a refined concept, then T... models C + // if it matches the requirement of C and of + // all base concepts. + // + // This overload is used if C::BaseConceptList exists + // due to its higher priority. + template + constexpr bool modelsConcept(PriorityTag<1>) + { return matchesRequirement(PriorityTag<42>()) and modelsConceptList(typename C::BaseConceptList()); } + + // This is the full concept check. It's defined here in the + // implementation namespace with 'constexpr bool' return type + // because we need a forward declaration in order to use it + // internally above. + // + // The actual interface function can then call this one and + // return the result as std::integral_constant which + // does not allow for a forward declaration because the return + // type is deduced. + template + constexpr bool models() + { + return modelsConcept(PriorityTag<42>()); + } + +} // namespace Dune::Concept::Impl + +#endif // DOXYGEN + +} // namespace Dune::Concept + + + +/** + * \brief Check if concept is modeled by given types + * + * This will check if the given concept is modeled by the given + * list of types. This is true if the list of types models all + * the base concepts that are refined by the given concept + * and if it satisfies all additional requirements of the latter. + * + * Notice that a concept may be defined for a list of interacting types. + * The function will check if the given list of types matches the requirements + * on the whole list. It does not check if each individual type in the list + * satisfies the concept. + * + * This concept check mechanism is inspired by the concept checking + * facility in Eric Nieblers range-v3. For more information please + * refer to the libraries project page https://github.com/ericniebler/range-v3 + * or this blog entry: http://ericniebler.com/2013/11/23/concept-checking-in-c11. + * In fact the interface provided here is almost exactly the same as in range-v3. + * However the implementation differs, because range-v3 uses its own meta-programming + * library whereas our implementation is more straight forward. + * + * The result is returned as std::integral_constant which + * allows to nicely use this method with Hybrid::ifElse. + * + * \tparam C The concept to check + * \tparam T The list of type to check against the concept + * + * \ingroup CxxConcepts + */ +template +constexpr auto models() +{ + return Std::bool_constant()>(); +} + + + +namespace Concept { + +#ifndef DOXYGEN + +namespace Impl { + + // ############################################################################# + // # All functions following here are implementation details for the + // # for the tupleEntriesModel() function below. + // ############################################################################# + + template + struct TupleEntriesModelHelper + { + template + struct AccumulateFunctor + { + using type = typename std::integral_constant()>; + }; + using Result = typename ReduceTuple::type; + }; + +} // namespace Dune::Concept::Impl + +#endif // DOXYGEN + + +// ############################################################################# +// # The method tupleEntriesModel() does the actual check if the types in a tuple +// # model a concept using the implementation details above. +// ############################################################################# + +template +constexpr auto tupleEntriesModel() + -> typename Impl::TupleEntriesModelHelper::Result +{ + return {}; +} + +// ############################################################################# +// # The following require*() functions are just helpers that allow to +// # propagate a failed check as substitution failure. This is useful +// # inside of a concept definition. +// ############################################################################# + +// Helper function for use in concept definitions. +// If the passed value b is not true, the concept will to be satisfied. +template::type = 0> +constexpr bool requireTrue() +{ + return true; +} + +// Helper function for use in concept definitions. +template(), int>::type = 0> +constexpr bool requireConcept() +{ + return true; +} + +// Helper function for use in concept definitions. +// This allows to avoid using decltype +template(), int>::type = 0> +constexpr bool requireConcept(T&&... /*t*/) +{ + return true; +} + +// Helper function for use in concept definitions. +// This checks if the concept given as first type is modelled by all types in the tuple passed as argument +template(), int>::type = 0> +constexpr bool requireConceptForTupleEntries() +{ + return true; +} + +// Helper function for use in concept definitions. +// If the first passed type is not convertible to the second, the concept will not be satisfied. +template::value, int>::type = 0> +constexpr bool requireConvertible() +{ + return true; +} + +// Helper function for use in concept definitions. +// If passed argument is not convertible to the first passed type, the concept will not be satisfied. +template::value, int>::type = 0> +constexpr bool requireConvertible(const From&) +{ + return true; +} + +// Helper function for use in concept definitions. +// This will always evaluate to true. If just allow +// to turn a type into an expression. The failure happens +// already during substitution for the type argument. +template +constexpr bool requireType() +{ + return true; +} + +// Helper function for use in concept definitions. +// If first passed type is not a base class of second type, the concept will not be satisfied. +template::value, int>::type = 0> +constexpr bool requireBaseOf() +{ + return true; +} + +// Helper function for use in concept definitions. +// If first passed type is not a base class of first arguments type, the concept will not be satisfied. +template::value, int>::type = 0> +constexpr bool requireBaseOf(const Derived&) +{ + return true; +} + +// Helper function for use in concept definitions. +// If the passed types are not the same, the concept will not be satisfied. +template::value, int>::type = 0> +constexpr bool requireSameType() +{ + return true; +} + + + +} // namespace Dune::Concept + + /** @} */ + +} // namespace Dune + + + + +#endif // DUNE_COMMON_CONCEPT_HH diff --git a/dune/common/conditional.hh b/dune/common/conditional.hh new file mode 100644 index 0000000..e4590f6 --- /dev/null +++ b/dune/common/conditional.hh @@ -0,0 +1,33 @@ +#ifndef DUNE_COMMON_CONDITIONAL_HH +#define DUNE_COMMON_CONDITIONAL_HH + +namespace Dune +{ + + /** \brief conditional evaluate + + sometimes call immediate if, evaluates to + + \code + if (b) + return v1; + else + return v2; + \endcode + + In contrast to if-then-else the cond function can also be + evaluated for vector valued SIMD data types, see simd.hh. + + \param b boolean value + \param v1 value of b==true + \param v2 value of b==false + */ + template + const T1 cond(bool b, const T1 & v1, const T2 & v2) + { + return (b ? v1 : v2); + } + +} // end namespace Dune + +#endif // DUNE_COMMON_CONDITIONAL_HH diff --git a/dune/common/debugalign.cc b/dune/common/debugalign.cc new file mode 100644 index 0000000..bd9affb --- /dev/null +++ b/dune/common/debugalign.cc @@ -0,0 +1,45 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace Dune { + + //! default alignment violation handler + /** + * Prints it's arguments on `stderr` and aborts. + */ + static void defaultViolatedAlignment(const char *className, + std::size_t expectedAlignment, + const void *address) + { + std::cerr << "Error: Detected invalid alignment for type " << className + << ": Address " << address << " not aligned to 0x" << std::hex + << expectedAlignment << std::endl; + std::abort(); + } + + ViolatedAlignmentHandler &violatedAlignmentHandler() + { + static ViolatedAlignmentHandler handler = defaultViolatedAlignment; + return handler; + } + + void violatedAlignment(const char *className, std::size_t expectedAlignment, + const void *address) + { + const auto &handler = violatedAlignmentHandler(); + if(handler) + handler(className, expectedAlignment, address); + } + +} // namespace Dune diff --git a/dune/common/debugalign.hh b/dune/common/debugalign.hh new file mode 100644 index 0000000..46c2315 --- /dev/null +++ b/dune/common/debugalign.hh @@ -0,0 +1,541 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_DEBUGALIGN_HH +#define DUNE_DEBUGALIGN_HH + +#include +#include +#include +#include +#include +#include +#include // abs +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace Dune { + + //! type of the handler called by `violatedAlignment()` + using ViolatedAlignmentHandler = + std::function; + + //! access the handler called by `violatedAlignment()` + /** + * This may be used to obtain the handler for the purpose of calling, or for + * saving it somewhere to restore it later. It may also be used to set the + * handler simply by assigning a new handler. Setting the handler races + * with other accesses. + */ + ViolatedAlignmentHandler &violatedAlignmentHandler(); + + //! called when an alignment violation is detected + /** + * \p className Name of the class whose alignment was violated + * \p expectedAlignment The (over-)alignment that the class expected + * \p address The address the class actually found itself at. + * + * The main purpose of the function is to serve as a convenient breakpoint + * for debugging -- which is why we put it in an external compilation unit + * so it isn't inlined. + */ + void violatedAlignment(const char *className, std::size_t expectedAlignment, + const void *address); + + //! check whether an address conforms to the given alignment + inline bool isAligned(const void *p, std::size_t align) + { + // a more portable way to do this would be to abuse std::align(), but that + // isn't supported by g++-4.9 yet + return std::uintptr_t(p) % align == 0; + } + + //! CRTP base mixin class to check alignment + template + class alignas(align) AlignedBase + { + void checkAlignment() const + { + auto pimpl = static_cast(this); + if(!isAligned(pimpl, align)) + violatedAlignment(className().c_str(), align, pimpl); + } + public: + AlignedBase() { checkAlignment(); } + AlignedBase(const AlignedBase &) { checkAlignment(); } + AlignedBase(AlignedBase &&) { checkAlignment(); } + ~AlignedBase() { checkAlignment(); } + + AlignedBase& operator=(const AlignedBase &) = default; + AlignedBase& operator=(AlignedBase &&) = default; + }; + + //! an alignment large enough to trigger alignment errors + static constexpr auto debugAlignment = 2*alignof(std::max_align_t); + + namespace AlignedNumberImpl { + + template + class AlignedNumber; + + } // namespace AlignedNumberImpl + + using AlignedNumberImpl::AlignedNumber; + + //! align a value to a certain alignment + template + AlignedNumber aligned(T value) { return { std::move(value) }; } + + // The purpose of this namespace is to move the `` function overloads + // out of namespace `Dune`. This avoids problems where people called + // e.g. `sqrt(1.0)` inside the `Dune` namespace, without first doing `using + // std::sqrt;`. Without any `Dune::sqrt()`, such a use will find + // `::sqrt()`, but with `Dune::sqrt()` it will find only `Dune::sqrt()`, + // which does not have an overload for `double`. + namespace AlignedNumberImpl { + + //! aligned wrappers for arithmetic types + template + class AlignedNumber + : public AlignedBase > + { + T value_; + + public: + AlignedNumber() = default; + AlignedNumber(T value) : value_(std::move(value)) {} + template= uAlign) && + std::is_convertible::value> > + AlignedNumber(const AlignedNumber &o) : value_(U(o)) {} + + // accessors + template::value> > + explicit operator U() const { return value_; } + + const T &value() const { return value_; } + T &value() { return value_; } + + // I/O + template + friend std::basic_istream& + operator>>(std::basic_istream& str, AlignedNumber &u) + { + return str >> u.value_; + } + + template + friend std::basic_ostream& + operator<<(std::basic_ostream& str, + const AlignedNumber &u) + { + return str << u.value_; + } + + // The trick with `template >` is + // needed because at least g++-4.9 seems to evaluates a default argument + // in `template >` as soon as possible and will + // error out if `expr(T)` is invalid. E.g. for `expr(T)` = + // `decltype(--std::declval())`, instantiating `AlignedNumber` + // will result in an unrecoverable error (`--` cannot be applied to a + // `bool`). + + // Increment, decrement + template())> > + AlignedNumber &operator++() { ++value_; return *this; } + + template())> > + AlignedNumber &operator--() { --value_; return *this; } + + template()++)> > + decltype(auto) operator++(int) { return aligned(value_++); } + + template()--)> > + decltype(auto) operator--(int) { return aligned(value_--); } + + // unary operators + template())> > + decltype(auto) operator+() const { return aligned(+value_); } + + template())> > + decltype(auto) operator-() const { return aligned(-value_); } + + /* + * silence warnings from GCC about using `~` on a bool + * (when instantiated for T=bool) + */ +#if __GNUC__ >= 7 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wbool-operation" +#endif + template())> > + decltype(auto) operator~() const { return aligned(~value_); } +#if __GNUC__ >= 7 +# pragma GCC diagnostic pop +#endif + + template())> > + decltype(auto) operator!() const { return aligned(!value_); } + + // assignment operators +#define DUNE_ASSIGN_OP(OP) \ + template() OP std::declval()) ) \ + > > \ + AlignedNumber &operator OP(const AlignedNumber &u) \ + { \ + value_ OP U(u); \ + return *this; \ + } \ + \ + template() OP \ + std::declval())> > \ + AlignedNumber &operator OP(const U &u) \ + { \ + value_ OP u; \ + return *this; \ + } \ + \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_ASSIGN_OP(+=); + DUNE_ASSIGN_OP(-=); + + DUNE_ASSIGN_OP(*=); + DUNE_ASSIGN_OP(/=); + DUNE_ASSIGN_OP(%=); + + DUNE_ASSIGN_OP(^=); + DUNE_ASSIGN_OP(&=); + DUNE_ASSIGN_OP(|=); + + DUNE_ASSIGN_OP(<<=); + DUNE_ASSIGN_OP(>>=); + +#undef DUNE_ASSIGN_OP + }; + + // binary operators +#define DUNE_BINARY_OP(OP) \ + template() \ + OP std::declval())> > \ + decltype(auto) \ + operator OP(const AlignedNumber &t, \ + const AlignedNumber &u) \ + { \ + /* can't use std::max(); not constexpr */ \ + return aligned<(tAlign > uAlign ? tAlign : uAlign)>(T(t) OP U(u)); \ + } \ + \ + template() \ + OP std::declval())> > \ + decltype(auto) \ + operator OP(const T &t, const AlignedNumber &u) \ + { \ + return aligned(t OP U(u)); \ + } \ + \ + template() \ + OP std::declval())> > \ + decltype(auto) \ + operator OP(const AlignedNumber &t, const U &u) \ + { \ + return aligned(T(t) OP u); \ + } \ + \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_BINARY_OP(+); + DUNE_BINARY_OP(-); + + DUNE_BINARY_OP(*); + DUNE_BINARY_OP(/); + DUNE_BINARY_OP(%); + + DUNE_BINARY_OP(^); + DUNE_BINARY_OP(&); + DUNE_BINARY_OP(|); + + DUNE_BINARY_OP(<<); + DUNE_BINARY_OP(>>); + + DUNE_BINARY_OP(==); + DUNE_BINARY_OP(!=); + DUNE_BINARY_OP(<); + DUNE_BINARY_OP(>); + DUNE_BINARY_OP(<=); + DUNE_BINARY_OP(>=); + + DUNE_BINARY_OP(&&); + DUNE_BINARY_OP(||); + +#undef DUNE_BINARY_OP + + ////////////////////////////////////////////////////////////////////// + // + // Overloads for the functions provided by the standard library + // +#define DUNE_UNARY_FUNC(name) \ + template \ + decltype(auto) name(const AlignedNumber &u) \ + { \ + using std::name; \ + return aligned(name(T(u))); \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + // + // functions + // + + // note: only unary functions are provided at the moment. Getting all the + // overloads right for functions with more than one argument is tricky. + // All functions appear in the list below in the order they are + // listed in in the standard, but the unimplemented ones are commented + // out. + + // note: abs is provided by both (for integer) and (for + // floating point). This overload works for both. + DUNE_UNARY_FUNC(abs); + DUNE_UNARY_FUNC(acos); + DUNE_UNARY_FUNC(acosh); + DUNE_UNARY_FUNC(asin); + DUNE_UNARY_FUNC(asinh); + DUNE_UNARY_FUNC(atan); + // atan2 + DUNE_UNARY_FUNC(atanh); + DUNE_UNARY_FUNC(cbrt); + DUNE_UNARY_FUNC(ceil); + // copysign + DUNE_UNARY_FUNC(cos); + DUNE_UNARY_FUNC(cosh); + DUNE_UNARY_FUNC(erf); + DUNE_UNARY_FUNC(erfc); + DUNE_UNARY_FUNC(exp); + DUNE_UNARY_FUNC(exp2); + DUNE_UNARY_FUNC(expm1); + DUNE_UNARY_FUNC(fabs); + // fdim + DUNE_UNARY_FUNC(floor); + // fma + // fmax + // fmin + // fmod + // frexp + // hypos + DUNE_UNARY_FUNC(ilogb); + // ldexp + DUNE_UNARY_FUNC(lgamma); + DUNE_UNARY_FUNC(llrint); + DUNE_UNARY_FUNC(llround); + DUNE_UNARY_FUNC(log); + DUNE_UNARY_FUNC(log10); + DUNE_UNARY_FUNC(log1p); + DUNE_UNARY_FUNC(log2); + DUNE_UNARY_FUNC(logb); + DUNE_UNARY_FUNC(lrint); + DUNE_UNARY_FUNC(lround); + // modf + DUNE_UNARY_FUNC(nearbyint); + // nextafter + // nexttoward + // pow + // remainder + // remquo + DUNE_UNARY_FUNC(rint); + DUNE_UNARY_FUNC(round); + // scalbln + // scalbn + DUNE_UNARY_FUNC(sin); + DUNE_UNARY_FUNC(sinh); + DUNE_UNARY_FUNC(sqrt); + DUNE_UNARY_FUNC(tan); + DUNE_UNARY_FUNC(tanh); + DUNE_UNARY_FUNC(tgamma); + DUNE_UNARY_FUNC(trunc); + + DUNE_UNARY_FUNC(isfinite); + DUNE_UNARY_FUNC(isinf); + DUNE_UNARY_FUNC(isnan); + DUNE_UNARY_FUNC(isnormal); + DUNE_UNARY_FUNC(signbit); + + // isgreater + // isgreaterequal + // isless + // islessequal + // islessgreater + // isunordered + + // + // functions + // + + // not all functions are implemented, and unlike for , no + // comprehensive list is provided + DUNE_UNARY_FUNC(real); + +#undef DUNE_UNARY_FUNC + + // We need to overload min() and max() since they require types to be + // LessThanComparable, which requires `a in the standard (still open + // as of 2018-07-06), which strives to require both "implicitly" and + // "contextually" convertible -- plus a few other things. + // + // We do not want our debug type to automatically decay to the underlying + // type, so we do not want to make the conversion non-explicit. So the + // only option left is to overload min() and max(). + + template + auto max(const AlignedNumber &a, + const AlignedNumber &b) + { + using std::max; + return aligned(max(T(a), T(b))); + } + + template + auto max(const T &a, const AlignedNumber &b) + { + using std::max; + return aligned(max(a, T(b))); + } + + template + auto max(const AlignedNumber &a, const T &b) + { + using std::max; + return aligned(max(T(a), b)); + } + + template + auto min(const AlignedNumber &a, + const AlignedNumber &b) + { + using std::min; + return aligned(min(T(a), T(b))); + } + + template + auto min(const T &a, const AlignedNumber &b) + { + using std::min; + return aligned(min(a, T(b))); + } + + template + auto min(const AlignedNumber &a, const T &b) + { + using std::min; + return aligned(min(T(a), b)); + } + + } // namespace AlignedNumberImpl + + // SIMD-like functions from "conditional.hh" + template + AlignedNumber + cond(const AlignedNumber &b, + const AlignedNumber &v1, const AlignedNumber &v2) + { + return b ? v1 : v2; + } + + // SIMD-like functions from "rangeutilities.hh" + template + T max_value(const AlignedNumber& val) + { + return T(val); + } + + template + T min_value(const AlignedNumber& val) + { + return T(val); + } + + template + bool any_true(const AlignedNumber& val) + { + return bool(val); + } + + template + bool all_true(const AlignedNumber& val) + { + return bool(val); + } + + // SIMD-like functionality from "simd/interface.hh" + namespace Simd { + namespace Overloads { + + template + struct ScalarType > { using type = T; }; + + template + struct RebindType > { + using type = AlignedNumber; + }; + + template + struct LaneCount > : index_constant<1> {}; + + template + T& lane(ADLTag<5>, std::size_t l, AlignedNumber &v) + { + assert(l == 0); + return v.value(); + } + + template + T lane(ADLTag<5>, std::size_t l, const AlignedNumber &v) + { + assert(l == 0); + return v.value(); + } + + template + const AlignedNumber & + cond(ADLTag<5>, AlignedNumber mask, + const AlignedNumber &ifTrue, + const AlignedNumber &ifFalse) + { + return mask ? ifTrue : ifFalse; + } + + template + bool anyTrue(ADLTag<5>, const AlignedNumber &mask) + { + return bool(mask); + } + + } // namespace Overloads + + } // namespace Simd + +} // namespace Dune + +#endif // DUNE_DEBUGALIGN_HH diff --git a/dune/common/debugallocator.cc b/dune/common/debugallocator.cc new file mode 100644 index 0000000..386b35d --- /dev/null +++ b/dune/common/debugallocator.cc @@ -0,0 +1,35 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "debugallocator.hh" + +#if HAVE_MPROTECT + +#include +#include +#include + +namespace Dune +{ + namespace DebugMemory + { + // system constant for page size + const std::ptrdiff_t page_size = sysconf(_SC_PAGESIZE); + + // implement member functions + void AllocationManager::allocation_error(const char* msg) + { + std::cerr << "Abort - Memory Corruption: " << msg << std::endl; + std::abort(); + } + + // global instance of AllocationManager + AllocationManager alloc_man; + + } // end namespace DebugMemory +} // end namespace Dune + +#endif // HAVE_MPROTECT diff --git a/dune/common/debugallocator.hh b/dune/common/debugallocator.hh new file mode 100644 index 0000000..6acc2d2 --- /dev/null +++ b/dune/common/debugallocator.hh @@ -0,0 +1,351 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_DEBUG_ALLOCATOR_HH +#define DUNE_DEBUG_ALLOCATOR_HH + +#if __has_include() + +#include +#define HAVE_SYS_MMAN_H 1 +#define HAVE_MPROTECT 1 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mallocallocator.hh" + +namespace Dune +{ + +#ifndef DOXYGEN // hide implementation details from doxygen + namespace DebugMemory + { + + extern const std::ptrdiff_t page_size; + + struct AllocationManager + { + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef void* pointer; + + protected: + static void allocation_error(const char* msg); + + struct AllocationInfo; + friend struct AllocationInfo; + +#define ALLOCATION_ASSERT(A) { if (!(A)) \ + { allocation_error("Assertion " # A " failed");\ + }\ +}; + + struct AllocationInfo + { + AllocationInfo(const std::type_info & t) : type(&t) {} + const std::type_info * type; + + pointer page_ptr; + pointer ptr; + size_type pages; + size_type capacity; + size_type size; + bool not_free; + }; + + typedef MallocAllocator Alloc; + typedef std::vector AllocationList; + AllocationList allocation_list; + + private: + void memprotect([[maybe_unused]] void* from, + [[maybe_unused]] difference_type len, + [[maybe_unused]] int prot) + { +#if HAVE_SYS_MMAN_H && HAVE_MPROTECT + int result = mprotect(from, len, prot); + if (result == -1) + { + + std::cerr << "ERROR: (" << result << ": " << strerror(result) << ")" << std::endl; + std::cerr << " Failed to "; + if (prot == PROT_NONE) + std::cerr << "protect "; + else + std::cerr << "unprotect "; + std::cerr << "memory range: " + << from << ", " + << static_cast( + static_cast(from) + len) + << std::endl; + abort(); + } +#else + std::cerr << "WARNING: memory protection not available" << std::endl; +#endif + } + + public: + + ~AllocationManager () + { + AllocationList::iterator it; + bool error = false; + for (it=allocation_list.begin(); it!=allocation_list.end(); it++) + { + if (it->not_free) + { + std::cerr << "ERROR: found memory chunk still in use: " << + it->capacity << " bytes at " << it->ptr << std::endl; + error = true; + } + munmap(it->page_ptr, it->pages * page_size); + } + if (error) + allocation_error("lost allocations"); + } + + template + T* allocate(size_type n) + { + // setup chunk info + AllocationInfo ai(typeid(T)); + ai.size = n; + ai.capacity = n * sizeof(T); + ai.pages = (ai.capacity) / page_size + 2; + ai.not_free = true; + size_type overlap = ai.capacity % page_size; + ai.page_ptr = mmap(NULL, ai.pages * page_size, + PROT_READ | PROT_WRITE, +#ifdef __APPLE__ + MAP_ANON | MAP_PRIVATE, +#else + MAP_ANONYMOUS | MAP_PRIVATE, +#endif + -1, 0); + if (MAP_FAILED == ai.page_ptr) + { + throw std::bad_alloc(); + } + ai.ptr = static_cast(ai.page_ptr) + page_size - overlap; + // write protect memory behind the actual data + memprotect(static_cast(ai.page_ptr) + (ai.pages-1) * page_size, + page_size, + PROT_NONE); + // remember the chunk + allocation_list.push_back(ai); + // return the ptr + return static_cast(ai.ptr); + } + + template + void deallocate(T* ptr, size_type n = 0) noexcept + { + // compute page address + void* page_ptr = + static_cast( + (char*)(ptr) - ((std::uintptr_t)(ptr) % page_size)); + // search list + AllocationList::iterator it; + unsigned int i = 0; + for (it=allocation_list.begin(); it!=allocation_list.end(); it++, i++) + { + if (it->page_ptr == page_ptr) + { + // std::cout << "found memory_block in allocation " << i << std::endl; + // sanity checks + if (n != 0) + ALLOCATION_ASSERT(n == it->size); + ALLOCATION_ASSERT(ptr == it->ptr); + ALLOCATION_ASSERT(true == it->not_free); + ALLOCATION_ASSERT(typeid(T) == *(it->type)); + // free memory + it->not_free = false; +#if DEBUG_ALLOCATOR_KEEP + // write protect old memory + memprotect(it->page_ptr, + (it->pages) * page_size, + PROT_NONE); +#else + // unprotect old memory + memprotect(it->page_ptr, + (it->pages) * page_size, + PROT_READ | PROT_WRITE); + munmap(it->page_ptr, it->pages * page_size); + // remove chunk info + allocation_list.erase(it); +#endif + return; + } + } + allocation_error("memory block not found"); + } + }; +#undef ALLOCATION_ASSERT + + extern AllocationManager alloc_man; + } // end namespace DebugMemory +#endif // DOXYGEN + + template + class DebugAllocator; + + // specialize for void + template <> + class DebugAllocator { + public: + typedef void* pointer; + typedef const void* const_pointer; + // reference to void members are impossible. + typedef void value_type; + template struct rebind { + typedef DebugAllocator other; + }; + }; + + // actual implementation + /** + @ingroup Allocators + @brief Allocators implementation which performs different kind of memory checks + + We check: + - access past the end + - only free memory which was allocated with this allocator + - list allocated memory chunks still in use upon destruction of the allocator + + When defining DEBUG_ALLOCATOR_KEEP to 1, we also check + - double free + - access after free + + When defining DEBUG_NEW_DELETE >= 1, we + - overload new/delte + - use the Debug memory management for new/delete + - DEBUG_NEW_DELETE > 2 gives extensive debug output + */ + template + class DebugAllocator { + public: + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T value_type; + template struct rebind { + typedef DebugAllocator other; + }; + + //! create a new DebugAllocator + DebugAllocator() noexcept {} + //! copy construct from an other DebugAllocator, possibly for a different result type + template + DebugAllocator(const DebugAllocator&) noexcept {} + //! cleanup this allocator + ~DebugAllocator() noexcept {} + + pointer address(reference x) const + { + return &x; + } + const_pointer address(const_reference x) const + { + return &x; + } + + //! allocate n objects of type T + pointer allocate(size_type n, + [[maybe_unused]] DebugAllocator::const_pointer hint = 0) + { + return DebugMemory::alloc_man.allocate(n); + } + + //! deallocate n objects of type T at address p + void deallocate(pointer p, size_type n) + { + DebugMemory::alloc_man.deallocate(p,n); + } + + //! max size for allocate + size_type max_size() const noexcept + { + return size_type(-1) / sizeof(T); + } + + //! copy-construct an object of type T (i.e. make a placement new on p) + void construct(pointer p, const T& val) + { + ::new((void*)p)T(val); + } + + //! construct an object of type T from variadic parameters + template + void construct(pointer p, Args&&... args) + { + ::new((void *)p)T(std::forward(args) ...); + } + + //! destroy an object of type T (i.e. call the destructor) + void destroy(pointer p) + { + p->~T(); + } + }; + + //! check whether allocators are equivalent + template + constexpr bool + operator==(const DebugAllocator &, const DebugAllocator &) + { + return true; + } + + //! check whether allocators are not equivalent + template + constexpr bool + operator!=(const DebugAllocator &, const DebugAllocator &) + { + return false; + } +} + +#ifdef DEBUG_NEW_DELETE +void * operator new(size_t size) +{ + // try to allocate size bytes + void *p = Dune::DebugMemory::alloc_man.allocate(size); +#if DEBUG_NEW_DELETE > 2 + std::cout << "NEW " << size + << " -> " << p + << std::endl; +#endif + return p; +} + +void operator delete(void * p) noexcept +{ +#if DEBUG_NEW_DELETE > 2 + std::cout << "FREE " << p << std::endl; +#endif + Dune::DebugMemory::alloc_man.deallocate(static_cast(p)); +} + +void operator delete(void * p, size_t size) noexcept +{ +#if DEBUG_NEW_DELETE > 2 + std::cout << "FREE " << p << std::endl; +#endif + Dune::DebugMemory::alloc_man.deallocate(static_cast(p), size); +} + +#endif // DEBUG_NEW_DELETE + +#endif // __has_include() + +#endif // DUNE_DEBUG_ALLOCATOR_HH diff --git a/dune/common/debugstream.hh b/dune/common/debugstream.hh new file mode 100644 index 0000000..57b0e47 --- /dev/null +++ b/dune/common/debugstream.hh @@ -0,0 +1,435 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_DEBUGSTREAM_HH +#define DUNE_DEBUGSTREAM_HH + +/** \file + * \brief Defines several output streams for messages of different importance + */ + +#include +#include + +#include + +namespace Dune { + + /*! \defgroup DebugOut Debug output + \ingroup Common + + The debug output is implemented by instances of DebugStream which + provides the following features: + + - output-syntax in the standard ostream-notation + - output can be totally deactivated depending on template parameters + - streams with active output can be deactivated during runtime + - redirecting to std::ostream or other DebugStream s during runtime + - stack oriented state + + The Dune-components should use the streams explained in \ref StdStreams + for output so that applications may redirect the output globally. + + Changes in runtime are provided by three sets of methods: + + - push()/pop() sets new activation flag or restore old setting + - attach()/detach() redirects output to a different std::ostream or restore old stream + - tie()/untie() redirects output through another DebugStream. If the state of the master stream changes (activation or output-stream) it is changed in the tied stream as well + + The first methods implement a full stack whereas tie() is a bit + different: though a tied stream may be (de)activated via + push()/pop() you cannot attach() or detach() an output. You'll need + to change the master stream instead. + + \section DebugAppl Applications + + Applications using the Dune-library should create an independent set + of DebugStreams so that the debug levels can be changed separately. + Example: + + \code + static const Dune::DebugLevel APPL_MINLEVEL = 3; + + Dune::DebugStream<1, APPL_MINLEVEL> myverbose; + Dune::DebugStream<2, APPL_MINLEVEL> myinfo; + Dune::DebugStream<3, APPL_MINLEVEL> mywarn; + \endcode + + This code creates three streams of which only the last one really + creates output. The output-routines of the other streams vanish in + optimized executables. + + You can use the common_bits-Template to switch to a policy using bitflags: + + \code + enum { APPL_CORE = 1, APPL_IO = 2, APPL_GRAPHICS = 4}; + + static const Dune::DebugLevel APPL_DEBUG_MASK = APPL_CORE | APPL_GRAPHICS; + static const Dune::DebugLevel APPL_ACTIVE_MASK = 0xff; + + Dune::DebugStream coreout; + Dune::DebugStream ioout; + Dune::DebugStream graphout; + \endcode + + Applications that wish to redirect the \ref StdStreams through their + private streams may use the tie()-mechanism: + + \code + // initialize streams like above + + Dune::dwarn.tie(coreout); + + // ... Dune-output to dwarn will be directed through coreout ... + + Dune::dwarn.untie(); + \endcode + + Keep in mind to untie() a stream before the tied stream is destructed. + + An alternative is to attach() an output stream defined by the application: + + \code + std::ofstream mylog("application.log"); + + Dune::dwarn.attach(mylog); + \endcode + */ + /** + \addtogroup DebugOut + \{ + */ + /*! \file + + This file implements the class DebugStream to support output in a + variety of debug levels. Additionally, template parameters control + if the output operation is really performed so that unused debug + levels can be deactivated + + */ + + + /*! \brief Type for debug levels. + + Only positive values allowed + */ + typedef unsigned int DebugLevel; + + /*! + + \brief Greater or equal template test. + + value is false if current is below the threshold, true otherwise + + This is the default struct to control the activation policy of + DebugStream and deactivates output below the threshold + */ + template + struct greater_or_equal { + static const bool value = (current >= threshold); + }; + + + /*! \brief activate if current and mask have common bits switched on. + + This template implements an alternative strategy to activate or + deactivate a DebugStream. Keep in mind to number your streams as + powers of two if using this template + */ + template + struct common_bits { + enum {value = ((current & mask)!=0) }; + }; + + + //! \brief standard exception for the debugstream + class DebugStreamError : public IOError {}; + + class StreamWrap { + public: + StreamWrap(std::ostream& _out) : out(_out) { } + std::ostream& out; + StreamWrap *next; + }; + + //! \brief Intermediate class to implement tie-operation of DebugStream + class DebugStreamState { + // !!! should be protected somehow but that won't be easy + public: + //! \brief current output stream and link to possibly pushed old output streams + StreamWrap* current; + + //! \brief flag to switch output during runtime + bool _active; + + //! \brief are we tied to another DebugStream? + bool _tied; + + //! \brief how many streams are tied to this state + unsigned int _tied_streams; + }; + + /*! + \brief Generic class to implement debug output streams + + The main function of a DebugStream is to provide output in a + standard ostream fashion that is fully deactivated if the level of + the stream does not meet the current requirements. More information in \ref DebugOut + + \param thislevel this level + \param dlevel level needed for any output to happen + \param alevel level needed to switch activation flag on + \param activator template describing the activation policy + + \todo Fix visibility of internal data + */ + template class activator = greater_or_equal> + class DebugStream : public DebugStreamState { + public: + /*! \brief Create a DebugStream and set initial output stream + + during runtime another stream can be attach()ed, however the + initial stream may not be detach()ed. + */ + DebugStream(std::ostream& out = std::cerr) { + // start a new list of streams + current = new StreamWrap(out); + current->next = 0; + + // check if we are above the default activation level + _active = activator::value; + + // we're not tied to another DebugStream + _tied = false; + + // no child streams yet + _tied_streams = 0; + } + + /*! \brief Create a DebugStream and directly tie to another DebugStream + + The fallback is used if a DebugStream constructed via this method + is untie()ed later. Otherwise the stream would be broken afterwards. + */ + DebugStream (DebugStreamState& master, + std::ostream& fallback = std::cerr) + { + // start a new list of streams + current = new StreamWrap(fallback); + current->next = 0; + + // check if we are above the default activation level + _active = activator::value; + _tied_streams = 0; + + // tie to the provided stream + _tied = true; + tiedstate = &master; + tiedstate->_tied_streams++; + } + + /*! \brief Destroy stream. + + If other streams still tie() to this stream the destructor + will call std::terminate() because you can hardly recover + from this problem and the child streams would certainly break on the + next output. + */ + ~DebugStream() + { + // untie + if (_tied) + tiedstate->_tied_streams--; + else { + // check if somebody still ties to us... + if (_tied_streams != 0) + { + std::cerr << "DebugStream destructor is called while other streams are still tied to it. Terminating!" << std::endl; + std::terminate(); + } + } + + // remove ostream-stack + while (current != 0) { + StreamWrap *s = current; + current = current->next; + delete s; + } + } + + //! \brief Generic types are passed on to current output stream + template + DebugStream& operator<<(const T data) { + // remove the following code if stream wasn't compiled active + if (activator::value) { + if (! _tied) { + if (_active) + current->out << data; + } else { + if (_active && tiedstate->_active) + tiedstate->current->out << data; + } + } + + return *this; + } + + /*! \brief explicit specialization so that enums can be printed + + Operators for built-in types follow special + rules (§11.2.3) so that enums won't fit into the generic + method above. With an existing operator<< for int however + the enum will be automatically casted. + */ + DebugStream& operator<<(const int data) { + // remove the following code if stream wasn't compiled active + if (activator::value) { + if (! _tied) { + if (_active) + current->out << data; + } else { + if (_active && tiedstate->_active) + tiedstate->current->out << data; + } + } + + return *this; + } + + //! \brief pass on manipulators to underlying output stream + DebugStream& operator<<(std::ostream& (*f)(std::ostream&)) { + if (activator::value) { + if (! _tied) { + if (_active) + f(current->out); + } else { + if (_active && tiedstate->_active) + f(tiedstate->current->out); + } + } + + return *this; + } + + //! \brief pass on flush to underlying output stream + DebugStream& flush() { + if (activator::value) { + if (! _tied) { + if (_active) + current->out.flush(); + } else { + if (_active && tiedstate->_active) + tiedstate->current->out.flush(); + } + } + + return *this; + } + + //! \brief set activation flag and store old value + void push(bool b) { + // are we at all active? + if (activator::value) { + _actstack.push(_active); + _active = b; + } else { + // stay off + _actstack.push(false); + } + } + + /*! \brief restore previously set activation flag + * \throws DebugStreamError + */ + void pop() { + if (_actstack.empty()) + DUNE_THROW(DebugStreamError, "No previous activation setting!"); + + _active = _actstack.top(); + _actstack.pop(); + } + + /*! \brief reports if this stream will produce output + + a DebugStream that is deactivated because of its level will always + return false, otherwise the state of the internal activation is + returned + */ + bool active() const { + return activator::value && _active; + } + + /*! \brief set output to a different stream. + + Old stream data is stored + */ + void attach(std::ostream& stream) { + if (_tied) + DUNE_THROW(DebugStreamError, "Cannot attach to a tied stream!"); + + StreamWrap* newcurr = new StreamWrap(stream); + newcurr->next = current; + current = newcurr; + } + + /*! \brief detach current output stream and restore to previous stream + * \throws DebugStreamError + */ + void detach() { + if (current->next == 0) + DUNE_THROW(DebugStreamError, "Cannot detach initial stream!"); + if (_tied) + DUNE_THROW(DebugStreamError, "Cannot detach a tied stream!"); + + StreamWrap* old = current; + current = current->next; + delete old; + } + + /*! \brief Tie a stream to this one. + * \throws DebugStreamError + */ + void tie(DebugStreamState& to) { + if (to._tied) + DUNE_THROW(DebugStreamError, "Cannot tie to an already tied stream!"); + if (_tied) + DUNE_THROW(DebugStreamError, "Stream already tied: untie first!"); + + _tied = true; + tiedstate = &to; + + // tell master class + tiedstate->_tied_streams++; + } + + /*! \brief Untie stream + * \throws DebugStreamError + */ + void untie() { + if(! _tied) + DUNE_THROW(DebugStreamError, "Cannot untie, stream is not tied!"); + + tiedstate->_tied_streams--; + _tied = false; + tiedstate = 0; + } + + private: + //! \brief pointer to data of stream we're tied to + DebugStreamState* tiedstate; + + /*! \brief Activation state history. + + store old activation settings so that the outside code doesn't + need to remember */ + std::stack _actstack; + }; + + /** /} */ +} + + +#endif diff --git a/dune/common/densematrix.hh b/dune/common/densematrix.hh new file mode 100644 index 0000000..81d3c67 --- /dev/null +++ b/dune/common/densematrix.hh @@ -0,0 +1,1262 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_DENSEMATRIX_HH +#define DUNE_DENSEMATRIX_HH + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Dune +{ + + template class DenseMatrix; + + template + struct FieldTraits< DenseMatrix > + { + typedef const typename FieldTraits< typename DenseMatVecTraits::value_type >::field_type field_type; + typedef const typename FieldTraits< typename DenseMatVecTraits::value_type >::real_type real_type; + }; + + /** + work around a problem of FieldMatrix/FieldVector, + there is no unique way to obtain the size of a class + + \deprecated VectorSize is deprecated; please call the 'size()' method directly instead. + This will be removed after Dune 2.8. + */ + template class FieldMatrix; + template class FieldVector; + namespace { + template + struct [[deprecated("VectorSize is deprecated; please call the 'size()' method directly instead")]] VectorSize + { + static typename V::size_type size(const V & v) { return v.size(); } + }; + + DUNE_NO_DEPRECATED_BEGIN + template + struct [[deprecated("VectorSize is deprecated; please call the 'size()' method directly instead")]] VectorSize< const FieldVector > + { + typedef FieldVector V; + static typename V::size_type size([[maybe_unused]] const V & v) + { + return N; + } + }; + DUNE_NO_DEPRECATED_END + } + + /** + @addtogroup DenseMatVec + @{ + */ + + /*! \file + + \brief Implements a matrix constructed from a given type + representing a field and a compile-time given number of rows and columns. + */ + + + + /** + \brief you have to specialize this structure for any type that should be assignable to a DenseMatrix + \tparam DenseMatrix Some type implementing the dense matrix interface + \tparam RHS Right hand side type + */ + template< class DenseMatrix, class RHS > + struct DenseMatrixAssigner; + +#ifndef DOXYGEN + namespace Impl + { + + template< class DenseMatrix, class RHS, class = void > + class DenseMatrixAssigner + {}; + + template< class DenseMatrix, class RHS > + class DenseMatrixAssigner< DenseMatrix, RHS, std::enable_if_t< Dune::IsNumber< RHS >::value > > + { + public: + static void apply ( DenseMatrix &denseMatrix, const RHS &rhs ) + { + typedef typename DenseMatrix::field_type field_type; + std::fill( denseMatrix.begin(), denseMatrix.end(), static_cast< field_type >( rhs ) ); + } + }; + + template< class DenseMatrix, class RHS > + class DenseMatrixAssigner< DenseMatrix, RHS, std::enable_if_t< !std::is_same< typename RHS::const_iterator, void >::value + && std::is_convertible< typename RHS::const_iterator::value_type, typename DenseMatrix::iterator::value_type >::value > > + { + public: + static void apply ( DenseMatrix &denseMatrix, const RHS &rhs ) + { + DUNE_ASSERT_BOUNDS(rhs.N() == denseMatrix.N()); + DUNE_ASSERT_BOUNDS(rhs.M() == denseMatrix.M()); + typename DenseMatrix::iterator tIt = std::begin(denseMatrix); + typename RHS::const_iterator sIt = std::begin(rhs); + for(; sIt != std::end(rhs); ++tIt, ++sIt) + std::copy(std::begin(*sIt), std::end(*sIt), std::begin(*tIt)); + } + }; + + } // namespace Impl + + + + template< class DenseMatrix, class RHS > + struct DenseMatrixAssigner + : public Impl::DenseMatrixAssigner< DenseMatrix, RHS > + {}; + + + namespace Impl + { + + template< class DenseMatrix, class RHS > + std::true_type hasDenseMatrixAssigner ( DenseMatrix &, const RHS &, decltype( Dune::DenseMatrixAssigner< DenseMatrix, RHS >::apply( std::declval< DenseMatrix & >(), std::declval< const RHS & >() ) ) * = nullptr ); + + std::false_type hasDenseMatrixAssigner ( ... ); + + } // namespace Impl + + template< class DenseMatrix, class RHS > + struct HasDenseMatrixAssigner + : public decltype( Impl::hasDenseMatrixAssigner( std::declval< DenseMatrix & >(), std::declval< const RHS & >() ) ) + {}; + +#endif // #ifndef DOXYGEN + + + + /** @brief Error thrown if operations of a FieldMatrix fail. */ + class FMatrixError : public MathError {}; + + /** + @brief A dense n x m matrix. + + Matrices represent linear maps from a vector space V to a vector space W. + This class represents such a linear map by storing a two-dimensional + %array of numbers of a given field type K. The number of rows and + columns is given at compile time. + + \tparam MAT type of the matrix implementation + */ + template + class DenseMatrix + { + typedef DenseMatVecTraits Traits; + + // Curiously recurring template pattern + constexpr MAT & asImp() { return static_cast(*this); } + constexpr const MAT & asImp() const { return static_cast(*this); } + + template + friend class DenseMatrix; + + public: + //===== type definitions and constants + + //! type of derived matrix class + typedef typename Traits::derived_type derived_type; + + //! export the type representing the field + typedef typename Traits::value_type value_type; + + //! export the type representing the field + typedef typename Traits::value_type field_type; + + //! export the type representing the components + typedef typename Traits::value_type block_type; + + //! The type used for the index access and size operation + typedef typename Traits::size_type size_type; + + //! The type used to represent a row (must fulfill the Dune::DenseVector interface) + typedef typename Traits::row_type row_type; + + //! The type used to represent a reference to a row (usually row_type &) + typedef typename Traits::row_reference row_reference; + + //! The type used to represent a reference to a constant row (usually const row_type &) + typedef typename Traits::const_row_reference const_row_reference; + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain. This is 1. + blocklevel = 1 + }; + + private: + //! \brief if value_type is a simd vector, then this is a simd vector of + //! the same length that can be used for indices. + using simd_index_type = Simd::Rebind; + + public: + //===== access to components + + //! random access + row_reference operator[] ( size_type i ) + { + return asImp().mat_access(i); + } + + const_row_reference operator[] ( size_type i ) const + { + return asImp().mat_access(i); + } + + //! size method (number of rows) + size_type size() const + { + return rows(); + } + + //===== iterator interface to rows of the matrix + //! Iterator class for sequential access + typedef DenseIterator Iterator; + //! typedef for stl compliant access + typedef Iterator iterator; + //! rename the iterators for easier access + typedef Iterator RowIterator; + //! rename the iterators for easier access + typedef typename std::remove_reference::type::Iterator ColIterator; + + //! begin iterator + Iterator begin () + { + return Iterator(*this,0); + } + + //! end iterator + Iterator end () + { + return Iterator(*this,rows()); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the vector, i.e. at the last entry. + Iterator beforeEnd () + { + return Iterator(*this,rows()-1); + } + + //! @returns an iterator that is positioned before + //! the first entry of the vector. + Iterator beforeBegin () + { + return Iterator(*this,-1); + } + + //! Iterator class for sequential access + typedef DenseIterator ConstIterator; + //! typedef for stl compliant access + typedef ConstIterator const_iterator; + //! rename the iterators for easier access + typedef ConstIterator ConstRowIterator; + //! rename the iterators for easier access + typedef typename std::remove_reference::type::ConstIterator ConstColIterator; + + //! begin iterator + ConstIterator begin () const + { + return ConstIterator(*this,0); + } + + //! end iterator + ConstIterator end () const + { + return ConstIterator(*this,rows()); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the vector. i.e. at the last element + ConstIterator beforeEnd () const + { + return ConstIterator(*this,rows()-1); + } + + //! @returns an iterator that is positioned before + //! the first entry of the vector. + ConstIterator beforeBegin () const + { + return ConstIterator(*this,-1); + } + + //===== assignment + + template< class RHS, class = std::enable_if_t< HasDenseMatrixAssigner< MAT, RHS >::value > > + derived_type &operator= ( const RHS &rhs ) + { + DenseMatrixAssigner< MAT, RHS >::apply( asImp(), rhs ); + return asImp(); + } + + //===== vector space arithmetic + + //! vector space addition + template + derived_type &operator+= (const DenseMatrix& x) + { + DUNE_ASSERT_BOUNDS(rows() == x.rows()); + for (size_type i=0; i + derived_type &operator-= (const DenseMatrix& x) + { + DUNE_ASSERT_BOUNDS(rows() == x.rows()); + for (size_type i=0; i + derived_type &axpy (const field_type &a, const DenseMatrix &x ) + { + DUNE_ASSERT_BOUNDS(rows() == x.rows()); + for( size_type i = 0; i < rows(); ++i ) + (*this)[ i ].axpy( a, x[ i ] ); + return asImp(); + } + + //! Binary matrix comparison + template + bool operator== (const DenseMatrix& x) const + { + DUNE_ASSERT_BOUNDS(rows() == x.rows()); + for (size_type i=0; i + bool operator!= (const DenseMatrix& x) const + { + return !operator==(x); + } + + + //===== linear maps + + //! y = A x + template + void mv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS((void*)(&x) != (void*)(&y)); + DUNE_ASSERT_BOUNDS(xx.N() == M()); + DUNE_ASSERT_BOUNDS(yy.N() == N()); + + using y_field_type = typename FieldTraits::field_type; + for (size_type i=0; i + void mtv ( const X &x, Y &y ) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS((void*)(&x) != (void*)(&y)); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + + using y_field_type = typename FieldTraits::field_type; + for(size_type i = 0; i < cols(); ++i) + { + yy[i] = y_field_type(0); + for(size_type j = 0; j < rows(); ++j) + yy[i] += (*this)[j][i] * xx[j]; + } + } + + //! y += A x + template + void umv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == M()); + DUNE_ASSERT_BOUNDS(yy.N() == N()); + for (size_type i=0; i + void umtv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + for(size_type i = 0; i + void umhv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + for (size_type i=0; i + void mmv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == M()); + DUNE_ASSERT_BOUNDS(yy.N() == N()); + for (size_type i=0; i + void mmtv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + for (size_type i=0; i + void mmhv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + for (size_type i=0; i + void usmv (const typename FieldTraits::field_type & alpha, + const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == M()); + DUNE_ASSERT_BOUNDS(yy.N() == N()); + for (size_type i=0; i + void usmtv (const typename FieldTraits::field_type & alpha, + const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + for (size_type i=0; i + void usmhv (const typename FieldTraits::field_type & alpha, + const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + for (size_type i=0; i::real_type frobenius_norm () const + { + typename FieldTraits::real_type sum=(0.0); + for (size_type i=0; i::real_type frobenius_norm2 () const + { + typename FieldTraits::real_type sum=(0.0); + for (size_type i=0; i::value, int>::type = 0> + typename FieldTraits::real_type infinity_norm() const { + using real_type = typename FieldTraits::real_type; + using std::max; + + real_type norm = 0; + for (auto const &x : *this) { + real_type const a = x.one_norm(); + norm = max(a, norm); + } + return norm; + } + + //! simplified infinity norm (uses Manhattan norm for complex values) + template ::value, int>::type = 0> + typename FieldTraits::real_type infinity_norm_real() const { + using real_type = typename FieldTraits::real_type; + using std::max; + + real_type norm = 0; + for (auto const &x : *this) { + real_type const a = x.one_norm_real(); + norm = max(a, norm); + } + return norm; + } + + //! infinity norm (row sum norm, how to generalize for blocks?) + template ::value, int>::type = 0> + typename FieldTraits::real_type infinity_norm() const { + using real_type = typename FieldTraits::real_type; + using std::max; + + real_type norm = 0; + real_type isNaN = 1; + for (auto const &x : *this) { + real_type const a = x.one_norm(); + norm = max(a, norm); + isNaN += a; + } + return norm * (isNaN / isNaN); + } + + //! simplified infinity norm (uses Manhattan norm for complex values) + template ::value, int>::type = 0> + typename FieldTraits::real_type infinity_norm_real() const { + using real_type = typename FieldTraits::real_type; + using std::max; + + real_type norm = 0; + real_type isNaN = 1; + for (auto const &x : *this) { + real_type const a = x.one_norm_real(); + norm = max(a, norm); + isNaN += a; + } + return norm * (isNaN / isNaN); + } + + //===== solve + + /** \brief Solve system A x = b + * + * \exception FMatrixError if the matrix is singular + */ + template + void solve (V1& x, const V2& b, bool doPivoting = true) const; + + /** \brief Compute inverse + * + * \exception FMatrixError if the matrix is singular + */ + void invert(bool doPivoting = true); + + //! calculates the determinant of this matrix + field_type determinant (bool doPivoting = true) const; + + //! Multiplies M from the left to this matrix + template + MAT& leftmultiply (const DenseMatrix& M) + { + DUNE_ASSERT_BOUNDS(M.rows() == M.cols()); + DUNE_ASSERT_BOUNDS(M.rows() == rows()); + AutonomousValue C(asImp()); + + for (size_type i=0; i + MAT& rightmultiply (const DenseMatrix& M) + { + DUNE_ASSERT_BOUNDS(M.rows() == M.cols()); + DUNE_ASSERT_BOUNDS(M.cols() == cols()); + AutonomousValue C(asImp()); + + for (size_type i=0; i + DenseMatrix leftmultiplyany (const FieldMatrix& M) const + { + FieldMatrix C; + + for (size_type i=0; i + FieldMatrix rightmultiplyany (const FieldMatrix& M) const + { + FieldMatrix C; + + for (size_type i=0; i= 0 && i < rows()); + DUNE_ASSERT_BOUNDS(j >= 0 && j < cols()); + return true; + } + + protected: + +#ifndef DOXYGEN + struct ElimPivot + { + ElimPivot(std::vector & pivot); + + void swap(std::size_t i, simd_index_type j); + + template + void operator()(const T&, int, int) + {} + + std::vector & pivot_; + }; + + template + struct Elim + { + Elim(V& rhs); + + void swap(std::size_t i, simd_index_type j); + + void operator()(const typename V::field_type& factor, int k, int i); + + V* rhs_; + }; + + struct ElimDet + { + ElimDet(field_type& sign) : sign_(sign) + { sign_ = 1; } + + void swap(std::size_t i, simd_index_type j) + { + sign_ *= + Simd::cond(simd_index_type(i) == j, field_type(1), field_type(-1)); + } + + void operator()(const field_type&, int, int) + {} + + field_type& sign_; + }; +#endif // DOXYGEN + + //! do an LU-Decomposition on matrix A + /** + * \param A The matrix to decompose, and to store the + * result in. + * \param func Functor used for swapping lanes and to conduct + * the elimination. Depending on the functor, \c + * luDecomposition() can be used for solving, for + * inverting, or to compute the determinant. + * \param nonsingularLanes SimdMask of lanes that are nonsingular. + * \param throwEarly Whether to throw an \c FMatrixError immediately + * as soon as one lane is discovered to be + * singular. If \c false, do not throw, instead + * continue until finished or all lanes are + * singular, and exit via return in both cases. + * \param doPivoting Enable pivoting. + * + * There are two modes of operation: + *
    + *
  • Terminate as soon as one lane is discovered to be singular. Early + * termination is done by throwing an \c FMatrixError. On entry, \c + * Simd::allTrue(nonsingularLanes) and \c throwEarly==true should hold. + * After early termination, the contents of \c A should be considered + * bogus, and \c nonsingularLanes has the lane(s) that triggered the + * early termination unset. There may be more singular lanes than the + * one reported in \c nonsingularLanes, which just havent been + * discovered yet; so the value of \c nonsingularLanes is mostly + * useful for diagnostics.
  • + *
  • Terminate only when all lanes are discovered to be singular. Use + * this when you want to apply special postprocessing in singular + * lines (e.g. setting the determinant of singular lanes to 0 in \c + * determinant()). On entry, \c nonsingularLanes may have any value + * and \c throwEarly==false should hold. The function will not throw + * an exception if some lanes are discovered to be singular, instead + * it will continue running until all lanes are singular or until + * finished, and terminate only via normal return. On exit, \c + * nonsingularLanes contains the map of lanes that are valid in \c + * A.
  • + *
+ */ + template + static void luDecomposition(DenseMatrix& A, Func func, + Mask &nonsingularLanes, bool throwEarly, bool doPivoting); + }; + +#ifndef DOXYGEN + template + DenseMatrix::ElimPivot::ElimPivot(std::vector & pivot) + : pivot_(pivot) + { + typedef typename std::vector::size_type size_type; + for(size_type i=0; i < pivot_.size(); ++i) pivot_[i]=i; + } + + template + void DenseMatrix::ElimPivot::swap(std::size_t i, simd_index_type j) + { + pivot_[i] = + Simd::cond(Simd::Scalar(i) == j, pivot_[i], j); + } + + template + template + DenseMatrix::Elim::Elim(V& rhs) + : rhs_(&rhs) + {} + + template + template + void DenseMatrix::Elim::swap(std::size_t i, simd_index_type j) + { + using std::swap; + + // see the comment in luDecomposition() + for(std::size_t l = 0; l < Simd::lanes(j); ++l) + swap(Simd::lane(l, (*rhs_)[ i ]), + Simd::lane(l, (*rhs_)[Simd::lane(l, j)])); + } + + template + template + void DenseMatrix:: + Elim::operator()(const typename V::field_type& factor, int k, int i) + { + (*rhs_)[k] -= factor*(*rhs_)[i]; + } + + template + template + inline void DenseMatrix:: + luDecomposition(DenseMatrix& A, Func func, Mask &nonsingularLanes, + bool throwEarly, bool doPivoting) + { + using std::max; + using std::swap; + + typedef typename FieldTraits::real_type real_type; + + // LU decomposition of A in A + for (size_type i=0; i pivmax; + pivmax = Simd::cond(mask, abs, pivmax); + imax = Simd::cond(mask, simd_index_type(k), imax); + } + // swap rows + for (size_type j=0; j + template + inline void DenseMatrix::solve(V1& x, const V2& b, bool doPivoting) const + { + using real_type = typename FieldTraits::real_type; + // never mind those ifs, because they get optimized away + if (rows()!=cols()) + DUNE_THROW(FMatrixError, "Can't solve for a " << rows() << "x" << cols() << " matrix!"); + + if (rows()==1) { + +#ifdef DUNE_FMatrix_WITH_CHECKING + if (Simd::anyTrue(fvmeta::absreal((*this)[0][0]) + < FMatrixPrecision<>::absolute_limit())) + DUNE_THROW(FMatrixError,"matrix is singular"); +#endif + x[0] = b[0]/(*this)[0][0]; + + } + else if (rows()==2) { + + field_type detinv = (*this)[0][0]*(*this)[1][1]-(*this)[0][1]*(*this)[1][0]; +#ifdef DUNE_FMatrix_WITH_CHECKING + if (Simd::anyTrue(fvmeta::absreal(detinv) + < FMatrixPrecision<>::absolute_limit())) + DUNE_THROW(FMatrixError,"matrix is singular"); +#endif + detinv = real_type(1.0)/detinv; + + x[0] = detinv*((*this)[1][1]*b[0]-(*this)[0][1]*b[1]); + x[1] = detinv*((*this)[0][0]*b[1]-(*this)[1][0]*b[0]); + + } + else if (rows()==3) { + + field_type d = determinant(doPivoting); +#ifdef DUNE_FMatrix_WITH_CHECKING + if (Simd::anyTrue(fvmeta::absreal(d) + < FMatrixPrecision<>::absolute_limit())) + DUNE_THROW(FMatrixError,"matrix is singular"); +#endif + + x[0] = (b[0]*(*this)[1][1]*(*this)[2][2] - b[0]*(*this)[2][1]*(*this)[1][2] + - b[1] *(*this)[0][1]*(*this)[2][2] + b[1]*(*this)[2][1]*(*this)[0][2] + + b[2] *(*this)[0][1]*(*this)[1][2] - b[2]*(*this)[1][1]*(*this)[0][2]) / d; + + x[1] = ((*this)[0][0]*b[1]*(*this)[2][2] - (*this)[0][0]*b[2]*(*this)[1][2] + - (*this)[1][0] *b[0]*(*this)[2][2] + (*this)[1][0]*b[2]*(*this)[0][2] + + (*this)[2][0] *b[0]*(*this)[1][2] - (*this)[2][0]*b[1]*(*this)[0][2]) / d; + + x[2] = ((*this)[0][0]*(*this)[1][1]*b[2] - (*this)[0][0]*(*this)[2][1]*b[1] + - (*this)[1][0] *(*this)[0][1]*b[2] + (*this)[1][0]*(*this)[2][1]*b[0] + + (*this)[2][0] *(*this)[0][1]*b[1] - (*this)[2][0]*(*this)[1][1]*b[0]) / d; + + } + else { + + V1& rhs = x; // use x to store rhs + rhs = b; // copy data + Elim elim(rhs); + AutonomousValue A(asImp()); + Simd::Mask::real_type> + nonsingularLanes(true); + + AutonomousValue::luDecomposition(A, elim, nonsingularLanes, true, doPivoting); + + // backsolve + for(int i=rows()-1; i>=0; i--) { + for (size_type j=i+1; j + inline void DenseMatrix::invert(bool doPivoting) + { + using real_type = typename FieldTraits::real_type; + using std::swap; + + // never mind those ifs, because they get optimized away + if (rows()!=cols()) + DUNE_THROW(FMatrixError, "Can't invert a " << rows() << "x" << cols() << " matrix!"); + + if (rows()==1) { + +#ifdef DUNE_FMatrix_WITH_CHECKING + if (Simd::anyTrue(fvmeta::absreal((*this)[0][0]) + < FMatrixPrecision<>::absolute_limit())) + DUNE_THROW(FMatrixError,"matrix is singular"); +#endif + (*this)[0][0] = real_type( 1 ) / (*this)[0][0]; + + } + else if (rows()==2) { + + field_type detinv = (*this)[0][0]*(*this)[1][1]-(*this)[0][1]*(*this)[1][0]; +#ifdef DUNE_FMatrix_WITH_CHECKING + if (Simd::anyTrue(fvmeta::absreal(detinv) + < FMatrixPrecision<>::absolute_limit())) + DUNE_THROW(FMatrixError,"matrix is singular"); +#endif + detinv = real_type( 1 ) / detinv; + + field_type temp=(*this)[0][0]; + (*this)[0][0] = (*this)[1][1]*detinv; + (*this)[0][1] = -(*this)[0][1]*detinv; + (*this)[1][0] = -(*this)[1][0]*detinv; + (*this)[1][1] = temp*detinv; + + } + else if (rows()==3) + { + using K = field_type; + // code generated by maple + K t4 = (*this)[0][0] * (*this)[1][1]; + K t6 = (*this)[0][0] * (*this)[1][2]; + K t8 = (*this)[0][1] * (*this)[1][0]; + K t10 = (*this)[0][2] * (*this)[1][0]; + K t12 = (*this)[0][1] * (*this)[2][0]; + K t14 = (*this)[0][2] * (*this)[2][0]; + + K det = (t4*(*this)[2][2]-t6*(*this)[2][1]-t8*(*this)[2][2]+ + t10*(*this)[2][1]+t12*(*this)[1][2]-t14*(*this)[1][1]); + K t17 = K(1.0)/det; + + K matrix01 = (*this)[0][1]; + K matrix00 = (*this)[0][0]; + K matrix10 = (*this)[1][0]; + K matrix11 = (*this)[1][1]; + + (*this)[0][0] = ((*this)[1][1] * (*this)[2][2] - (*this)[1][2] * (*this)[2][1])*t17; + (*this)[0][1] = -((*this)[0][1] * (*this)[2][2] - (*this)[0][2] * (*this)[2][1])*t17; + (*this)[0][2] = (matrix01 * (*this)[1][2] - (*this)[0][2] * (*this)[1][1])*t17; + (*this)[1][0] = -((*this)[1][0] * (*this)[2][2] - (*this)[1][2] * (*this)[2][0])*t17; + (*this)[1][1] = (matrix00 * (*this)[2][2] - t14) * t17; + (*this)[1][2] = -(t6-t10) * t17; + (*this)[2][0] = (matrix10 * (*this)[2][1] - matrix11 * (*this)[2][0]) * t17; + (*this)[2][1] = -(matrix00 * (*this)[2][1] - t12) * t17; + (*this)[2][2] = (t4-t8) * t17; + } + else { + using std::swap; + + AutonomousValue A(asImp()); + std::vector pivot(rows()); + Simd::Mask::real_type> + nonsingularLanes(true); + AutonomousValue::luDecomposition(A, ElimPivot(pivot), nonsingularLanes, true, doPivoting); + auto& L=A; + auto& U=A; + + // initialize inverse + *this=field_type(); + + for(size_type i=0; i0;) { + --i; + for (size_type k=0; k0; ) { + --i; + for(std::size_t l = 0; l < Simd::lanes((*this)[0][0]); ++l) + { + std::size_t pi = Simd::lane(l, pivot[i]); + if(i!=pi) + for(size_type j=0; j + inline typename DenseMatrix::field_type + DenseMatrix::determinant(bool doPivoting) const + { + // never mind those ifs, because they get optimized away + if (rows()!=cols()) + DUNE_THROW(FMatrixError, "There is no determinant for a " << rows() << "x" << cols() << " matrix!"); + + if (rows()==1) + return (*this)[0][0]; + + if (rows()==2) + return (*this)[0][0]*(*this)[1][1] - (*this)[0][1]*(*this)[1][0]; + + if (rows()==3) { + // code generated by maple + field_type t4 = (*this)[0][0] * (*this)[1][1]; + field_type t6 = (*this)[0][0] * (*this)[1][2]; + field_type t8 = (*this)[0][1] * (*this)[1][0]; + field_type t10 = (*this)[0][2] * (*this)[1][0]; + field_type t12 = (*this)[0][1] * (*this)[2][0]; + field_type t14 = (*this)[0][2] * (*this)[2][0]; + + return (t4*(*this)[2][2]-t6*(*this)[2][1]-t8*(*this)[2][2]+ + t10*(*this)[2][1]+t12*(*this)[1][2]-t14*(*this)[1][1]); + + } + + AutonomousValue A(asImp()); + field_type det; + Simd::Mask::real_type> + nonsingularLanes(true); + + AutonomousValue::luDecomposition(A, ElimDet(det), nonsingularLanes, false, doPivoting); + det = Simd::cond(nonsingularLanes, det, field_type(0)); + + for (size_type i = 0; i < rows(); ++i) + det *= A[i][i]; + return det; + } + +#endif // DOXYGEN + + namespace DenseMatrixHelp { + + //! calculates ret = matrix * x + template + static inline void multAssign(const DenseMatrix &matrix, const DenseVector & x, DenseVector & ret) + { + DUNE_ASSERT_BOUNDS(x.size() == matrix.cols()); + DUNE_ASSERT_BOUNDS(ret.size() == matrix.rows()); + typedef typename DenseMatrix::size_type size_type; + + for(size_type i=0; i + static inline void multAssignTransposed( const FieldMatrix &matrix, const FieldVector & x, FieldVector & ret) + { + typedef typename FieldMatrix::size_type size_type; + + for(size_type i=0; i + static inline FieldVector mult(const FieldMatrix &matrix, const FieldVector & x) + { + FieldVector ret; + multAssign(matrix,x,ret); + return ret; + } + + //! calculates ret = matrix^T * x + template + static inline FieldVector multTransposed(const FieldMatrix &matrix, const FieldVector & x) + { + FieldVector ret; + multAssignTransposed( matrix, x, ret ); + return ret; + } +#endif + + } // end namespace DenseMatrixHelp + + /** \brief Sends the matrix to an output stream */ + template + std::ostream& operator<< (std::ostream& s, const DenseMatrix& a) + { + for (typename DenseMatrix::size_type i=0; i +#include +#include + +#include "genericiterator.hh" +#include "ftraits.hh" +#include "matvectraits.hh" +#include "promotiontraits.hh" +#include "dotproduct.hh" +#include "boundschecking.hh" + +namespace Dune { + + // forward declaration of template + template class DenseVector; + + template + struct FieldTraits< DenseVector > + { + typedef typename FieldTraits< typename DenseMatVecTraits::value_type >::field_type field_type; + typedef typename FieldTraits< typename DenseMatVecTraits::value_type >::real_type real_type; + }; + + /** @defgroup DenseMatVec Dense Matrix and Vector Template Library + @ingroup Common + @{ + */ + + /*! \file + * \brief Implements the dense vector interface, with an exchangeable storage class + */ + + namespace fvmeta + { + /** + \private + \memberof Dune::DenseVector + */ + template + inline typename FieldTraits::real_type absreal (const K& k) + { + using std::abs; + return abs(k); + } + + /** + \private + \memberof Dune::DenseVector + */ + template + inline typename FieldTraits::real_type absreal (const std::complex& c) + { + using std::abs; + return abs(c.real()) + abs(c.imag()); + } + + /** + \private + \memberof Dune::DenseVector + */ + template + inline typename FieldTraits::real_type abs2 (const K& k) + { + return k*k; + } + + /** + \private + \memberof Dune::DenseVector + */ + template + inline typename FieldTraits::real_type abs2 (const std::complex& c) + { + return c.real()*c.real() + c.imag()*c.imag(); + } + + /** + \private + \memberof Dune::DenseVector + */ + template::is_integer> + struct Sqrt + { + static inline typename FieldTraits::real_type sqrt (const K& k) + { + using std::sqrt; + return sqrt(k); + } + }; + + /** + \private + \memberof Dune::DenseVector + */ + template + struct Sqrt + { + static inline typename FieldTraits::real_type sqrt (const K& k) + { + using std::sqrt; + return typename FieldTraits::real_type(sqrt(double(k))); + } + }; + + /** + \private + \memberof Dune::DenseVector + */ + template + inline typename FieldTraits::real_type sqrt (const K& k) + { + return Sqrt::sqrt(k); + } + + } + + /*! \brief Generic iterator class for dense vector and matrix implementations + + provides sequential access to DenseVector, FieldVector and FieldMatrix + */ + template + class DenseIterator : + public Dune::RandomAccessIteratorFacade,T, R, std::ptrdiff_t> + { + friend class DenseIterator::type, typename std::remove_const::type, typename mutable_reference::type >; + friend class DenseIterator::type, const typename std::remove_const::type, typename const_reference::type >; + + typedef DenseIterator::type, typename std::remove_const::type, typename mutable_reference::type > MutableIterator; + typedef DenseIterator::type, const typename std::remove_const::type, typename const_reference::type > ConstIterator; + public: + + /** + * @brief The type of the difference between two positions. + */ + typedef std::ptrdiff_t DifferenceType; + + /** + * @brief The type to index the underlying container. + */ + typedef typename C::size_type SizeType; + + // Constructors needed by the base iterators. + DenseIterator() + : container_(0), position_() + {} + + DenseIterator(C& cont, SizeType pos) + : container_(&cont), position_(pos) + {} + + DenseIterator(const MutableIterator & other) + : container_(other.container_), position_(other.position_) + {} + + DenseIterator(const ConstIterator & other) + : container_(other.container_), position_(other.position_) + {} + + // Methods needed by the forward iterator + bool equals(const MutableIterator &other) const + { + return position_ == other.position_ && container_ == other.container_; + } + + + bool equals(const ConstIterator & other) const + { + return position_ == other.position_ && container_ == other.container_; + } + + R dereference() const { + return container_->operator[](position_); + } + + void increment(){ + ++position_; + } + + // Additional function needed by BidirectionalIterator + void decrement(){ + --position_; + } + + // Additional function needed by RandomAccessIterator + R elementAt(DifferenceType i) const { + return container_->operator[](position_+i); + } + + void advance(DifferenceType n){ + position_=position_+n; + } + + DifferenceType distanceTo(DenseIterator::type,const typename std::remove_const::type> other) const + { + assert(other.container_==container_); + return static_cast< DifferenceType >( other.position_ ) - static_cast< DifferenceType >( position_ ); + } + + DifferenceType distanceTo(DenseIterator::type, typename std::remove_const::type> other) const + { + assert(other.container_==container_); + return static_cast< DifferenceType >( other.position_ ) - static_cast< DifferenceType >( position_ ); + } + + //! return index + SizeType index () const + { + return this->position_; + } + + private: + C *container_; + SizeType position_; + }; + + /** \brief Interface for a class of dense vectors over a given field. + * + * \tparam V implementation class of the vector + */ + template + class DenseVector + { + typedef DenseMatVecTraits Traits; + // typedef typename Traits::value_type K; + + // Curiously recurring template pattern + V & asImp() { return static_cast(*this); } + const V & asImp() const { return static_cast(*this); } + + protected: + // construction allowed to derived classes only + constexpr DenseVector() = default; + // copying only allowed by derived classes + DenseVector(const DenseVector&) = default; + + public: + //===== type definitions and constants + + //! type of derived vector class + typedef typename Traits::derived_type derived_type; + + //! export the type representing the field + typedef typename Traits::value_type value_type; + + //! export the type representing the field + typedef typename FieldTraits< value_type >::field_type field_type; + + //! export the type representing the components + typedef typename Traits::value_type block_type; + + //! The type used for the index access and size operation + typedef typename Traits::size_type size_type; + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain + blocklevel = 1 + }; + + //===== assignment from scalar + //! Assignment operator for scalar + inline derived_type& operator= (const value_type& k) + { + for (size_type i=0; i::value_type>::value, int> = 0> + derived_type& operator= (const DenseVector& other) + { + assert(other.size() == size()); + for (size_type i=0; i Iterator; + //! typedef for stl compliant access + typedef Iterator iterator; + + //! begin iterator + Iterator begin () + { + return Iterator(*this,0); + } + + //! end iterator + Iterator end () + { + return Iterator(*this,size()); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the vector, i.e. at the last entry. + Iterator beforeEnd () + { + return Iterator(*this,size()-1); + } + + //! @returns an iterator that is positioned before + //! the first entry of the vector. + Iterator beforeBegin () + { + return Iterator(*this,-1); + } + + //! return iterator to given element or end() + Iterator find (size_type i) + { + return Iterator(*this,std::min(i,size())); + } + + //! ConstIterator class for sequential access + typedef DenseIterator ConstIterator; + //! typedef for stl compliant access + typedef ConstIterator const_iterator; + + //! begin ConstIterator + ConstIterator begin () const + { + return ConstIterator(*this,0); + } + + //! end ConstIterator + ConstIterator end () const + { + return ConstIterator(*this,size()); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the vector. i.e. at the last element + ConstIterator beforeEnd () const + { + return ConstIterator(*this,size()-1); + } + + //! @returns an iterator that is positioned before + //! the first entry of the vector. + ConstIterator beforeBegin () const + { + return ConstIterator(*this,-1); + } + + //! return iterator to given element or end() + ConstIterator find (size_type i) const + { + return ConstIterator(*this,std::min(i,size())); + } + + //===== vector space arithmetic + + //! vector space addition + template + derived_type& operator+= (const DenseVector& x) + { + DUNE_ASSERT_BOUNDS(x.size() == size()); + for (size_type i=0; i + derived_type& operator-= (const DenseVector& x) + { + DUNE_ASSERT_BOUNDS(x.size() == size()); + for (size_type i=0; i + derived_type operator+ (const DenseVector& b) const + { + derived_type z = asImp(); + return (z+=b); + } + + //! Binary vector subtraction + template + derived_type operator- (const DenseVector& b) const + { + derived_type z = asImp(); + return (z-=b); + } + + //! Vector negation + derived_type operator- () const + { + V result; + using idx_type = typename decltype(result)::size_type; + + for (idx_type i = 0; i < size(); ++i) + result[i] = -asImp()[i]; + + return result; + } + + //! \brief vector space add scalar to all comps + /** + we use enable_if to avoid an ambiguity, if the + function parameter can be converted to value_type implicitly. + (see FS#1457) + + The function is only enabled, if the parameter is directly + convertible to value_type. + */ + template + typename std::enable_if< + std::is_convertible::value, + derived_type + >::type& + operator+= (const ValueType& kk) + { + const value_type& k = kk; + for (size_type i=0; i + typename std::enable_if< + std::is_convertible::value, + derived_type + >::type& + operator-= (const ValueType& kk) + { + const value_type& k = kk; + for (size_type i=0; i + typename std::enable_if< + std::is_convertible::value, + derived_type + >::type& + operator*= (const FieldType& kk) + { + const field_type& k = kk; + for (size_type i=0; i + typename std::enable_if< + std::is_convertible::value, + derived_type + >::type& + operator/= (const FieldType& kk) + { + const field_type& k = kk; + for (size_type i=0; i + bool operator== (const DenseVector& x) const + { + DUNE_ASSERT_BOUNDS(x.size() == size()); + for (size_type i=0; i + bool operator!= (const DenseVector& x) const + { + return !operator==(x); + } + + + //! vector space axpy operation ( *this += a x ) + template + derived_type& axpy (const field_type& a, const DenseVector& x) + { + DUNE_ASSERT_BOUNDS(x.size() == size()); + for (size_type i=0; i + typename PromotionTraits::field_type>::PromotedType operator* (const DenseVector& x) const { + typedef typename PromotionTraits::field_type>::PromotedType PromotedType; + PromotedType result(0); + assert(x.size() == size()); + for (size_type i=0; i + typename PromotionTraits::field_type>::PromotedType dot(const DenseVector& x) const { + typedef typename PromotionTraits::field_type>::PromotedType PromotedType; + PromotedType result(0); + assert(x.size() == size()); + for (size_type i=0; i::real_type one_norm() const { + using std::abs; + typename FieldTraits::real_type result( 0 ); + for (size_type i=0; i::real_type one_norm_real () const + { + typename FieldTraits::real_type result( 0 ); + for (size_type i=0; i::real_type two_norm () const + { + typename FieldTraits::real_type result( 0 ); + for (size_type i=0; i::real_type two_norm2 () const + { + typename FieldTraits::real_type result( 0 ); + for (size_type i=0; i::value, int>::type = 0> + typename FieldTraits::real_type infinity_norm() const { + using real_type = typename FieldTraits::real_type; + using std::abs; + using std::max; + + real_type norm = 0; + for (auto const &x : *this) { + real_type const a = abs(x); + norm = max(a, norm); + } + return norm; + } + + //! simplified infinity norm (uses Manhattan norm for complex values) + template ::value, int>::type = 0> + typename FieldTraits::real_type infinity_norm_real() const { + using real_type = typename FieldTraits::real_type; + using std::max; + + real_type norm = 0; + for (auto const &x : *this) { + real_type const a = fvmeta::absreal(x); + norm = max(a, norm); + } + return norm; + } + + //! infinity norm (maximum of absolute values of entries) + template ::value, int>::type = 0> + typename FieldTraits::real_type infinity_norm() const { + using real_type = typename FieldTraits::real_type; + using std::abs; + using std::max; + + real_type norm = 0; + real_type isNaN = 1; + for (auto const &x : *this) { + real_type const a = abs(x); + norm = max(a, norm); + isNaN += a; + } + return norm * (isNaN / isNaN); + } + + //! simplified infinity norm (uses Manhattan norm for complex values) + template ::value, int>::type = 0> + typename FieldTraits::real_type infinity_norm_real() const { + using real_type = typename FieldTraits::real_type; + using std::max; + + real_type norm = 0; + real_type isNaN = 1; + for (auto const &x : *this) { + real_type const a = fvmeta::absreal(x); + norm = max(a, norm); + isNaN += a; + } + return norm * (isNaN / isNaN); + } + + //===== sizes + + //! number of blocks in the vector (are of size 1 here) + size_type N () const + { + return size(); + } + + //! dimension of the vector space + size_type dim () const + { + return size(); + } + + }; + + /** \brief Write a DenseVector to an output stream + * \relates DenseVector + * + * \param[in] s std :: ostream to write to + * \param[in] v DenseVector to write + * + * \returns the output stream (s) + */ + template + std::ostream& operator<< (std::ostream& s, const DenseVector& v) + { + for (typename DenseVector::size_type i=0; i0) ? " " : "") << v[i]; + return s; + } + + /** @} end documentation */ + +} // end namespace + +#endif // DUNE_DENSEVECTOR_HH diff --git a/dune/common/deprecated.hh b/dune/common/deprecated.hh new file mode 100644 index 0000000..6d367e6 --- /dev/null +++ b/dune/common/deprecated.hh @@ -0,0 +1,87 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_DEPRECATED_HH +#define DUNE_DEPRECATED_HH + +/** \file + * \brief Definition of the DUNE_DEPRECATED macro for the case that config.h + * is not available + */ + +//! @addtogroup CxxUtilities +//! @{ +#if defined(DOXYGEN) || !defined(HAS_ATTRIBUTE_DEPRECATED) +//! Mark some entity as deprecated +/** + * \deprecated Use C++14's \code[[deprecated]]\endcode instead. It will be + * removed after Dune 2.8. Be aware that it must be sometimes placed at + * different position in the code. + */ +#define DUNE_DEPRECATED +#else // defined(HAS_ATTRIBUTE_DEPRECATED) +#define DUNE_DEPRECATED __attribute__((deprecated)) +#endif + +#if defined(DOXYGEN) || !defined(HAS_ATTRIBUTE_DEPRECATED_MSG) +//! Mark some entity as deprecated +/** + * \deprecated Use C++14's \code[[deprecated(msg)]]\endcode instead. It + * will be removed after Dune 2.8. Be aware that it must be sometimes + * placed at different position in the code. + */ +#define DUNE_DEPRECATED_MSG(text) DUNE_DEPRECATED +#else // defined(HAS_ATTRIBUTE_DEPRECATED_MSG) +#define DUNE_DEPRECATED_MSG(text) __attribute__((deprecated(# text))) +#endif + +#ifdef DOXYGEN +/** + * \brief Ignore deprecation warnings (start) + * + * This macro can be used together with `DUNE_NO_DEPRECATED_END` to mark a + * block in which deprecation warnings are ignored. This can be useful for + * implementations of deprecated methods that call other deprecated methods + * or for testing deprecated methods in the testsuite. + * + * \code + DUNE_NO_DEPRECATED_BEGIN + some_deprecated_function(); + another_deprecated_function(); + DUNE_NO_DEPRECATED_END + * \endcode + * + * \warning This macro must always be used together with `DUNE_NO_DEPRECATED_END` + */ +#define DUNE_NO_DEPRECATED_BEGIN ... +/** + * \brief Ignore deprecation warnings (end) + * + * \warning This macro must always be used together with `DUNE_NO_DEPRECATED_BEGIN` + */ +#define DUNE_NO_DEPRECATED_END ... +#else +# if defined __clang__ +# define DUNE_NO_DEPRECATED_BEGIN \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +# define DUNE_NO_DEPRECATED_END _Pragma("clang diagnostic pop") +# elif defined __INTEL_COMPILER +# define DUNE_NO_DEPRECATED_BEGIN \ + _Pragma("warning push") \ + _Pragma("warning(disable:1478)") \ + _Pragma("warning(disable:1786)") +# define DUNE_NO_DEPRECATED_END _Pragma("warning pop") +# elif defined __GNUC__ +# define DUNE_NO_DEPRECATED_BEGIN \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +# define DUNE_NO_DEPRECATED_END _Pragma("GCC diagnostic pop") +# else +# define DUNE_NO_DEPRECATED_BEGIN /* Noop. */ +# define DUNE_NO_DEPRECATED_END /* Noop. */ +# endif +#endif + +//! @} + +#endif diff --git a/dune/common/diagonalmatrix.hh b/dune/common/diagonalmatrix.hh new file mode 100644 index 0000000..4886938 --- /dev/null +++ b/dune/common/diagonalmatrix.hh @@ -0,0 +1,1098 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_DIAGONAL_MATRIX_HH +#define DUNE_DIAGONAL_MATRIX_HH + +/*! \file + \brief This file implements a quadratic diagonal matrix of fixed size. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +namespace Dune { + + template< class K, int n > class DiagonalRowVectorConst; + template< class K, int n > class DiagonalRowVector; + template< class DiagonalMatrixType > class DiagonalMatrixWrapper; + template< class C, class T, class R> class ContainerWrapperIterator; + + /** + @addtogroup DenseMatVec + @{ + */ + + /** + *@brief A diagonal matrix of static size. + * + * This is meant to be a replacement of FieldMatrix for the case that + * it is a diagonal matrix. + * + * \tparam K Type used for scalars + * \tparam n Matrix size + */ + template + class DiagonalMatrix + { + typedef DiagonalMatrixWrapper< DiagonalMatrix > WrapperType; + + public: + //===== type definitions and constants + + //! export the type representing the field + typedef K value_type; + typedef value_type field_type; + + //! export the type representing the components + typedef K block_type; + + //! The type used for the index access and size operations. + typedef std::size_t size_type; + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain. This is 1. + blocklevel = 1 + }; + + //! Each row is implemented by a field vector + typedef DiagonalRowVector row_type; + typedef row_type reference; + typedef row_type row_reference; + typedef DiagonalRowVectorConst const_row_type; + typedef const_row_type const_reference; + typedef const_row_type const_row_reference; + + //! export size + enum { + //! The number of rows + rows = n, + //! The number of columns + cols = n + }; + + //==== size + + static constexpr size_type size () + { + return rows; + } + + //===== constructors + + //! Default constructor + constexpr DiagonalMatrix() = default; + + //! Constructor initializing the whole matrix with a scalar + DiagonalMatrix (const K& k) + : diag_(k) + {} + + //! Constructor initializing the diagonal with a vector + DiagonalMatrix (const FieldVector& diag) + : diag_(diag) + {} + + /** \brief Construct diagonal matrix from an initializer list + * + * The elements of the list are copied into the diagonal elements of the matrix. + * If the initializer list is shorter than the matrix diagonal (which has n elements), + * the remaining matrix diagonal elements are left uninitialized. If the initializer + * list is longer, than only the first n elements will be copied into the matrix + * diagonal. + */ + DiagonalMatrix (std::initializer_list const &l) + { + std::copy_n(l.begin(), std::min(static_cast(rows), + l.size()), + diag_.begin()); + } + + /** \brief Assignment from a scalar */ + DiagonalMatrix& operator= (const K& k) + { + diag_ = k; + return *this; + } + + /** \brief Check if matrix is the same object as the other matrix */ + bool identical(const DiagonalMatrix& other) const + { + return (this==&other); + } + + //===== iterator interface to rows of the matrix + //! Iterator class for sequential access + typedef ContainerWrapperIterator Iterator; + //! typedef for stl compliant access + typedef Iterator iterator; + //! rename the iterators for easier access + typedef Iterator RowIterator; + //! rename the iterators for easier access + typedef typename row_type::Iterator ColIterator; + + //! begin iterator + Iterator begin () + { + return Iterator(WrapperType(this),0); + } + + //! end iterator + Iterator end () + { + return Iterator(WrapperType(this),n); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the rows, i.e. at the last row. + Iterator beforeEnd () + { + return Iterator(WrapperType(this),n-1); + } + + //! @returns an iterator that is positioned before + //! the first row of the matrix. + Iterator beforeBegin () + { + return Iterator(WrapperType(this),-1); + } + + + //! Iterator class for sequential access + typedef ContainerWrapperIterator ConstIterator; + //! typedef for stl compliant access + typedef ConstIterator const_iterator; + //! rename the iterators for easier access + typedef ConstIterator ConstRowIterator; + //! rename the iterators for easier access + typedef typename const_row_type::ConstIterator ConstColIterator; + + //! begin iterator + ConstIterator begin () const + { + return ConstIterator(WrapperType(this),0); + } + + //! end iterator + ConstIterator end () const + { + return ConstIterator(WrapperType(this),n); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the rows. i.e. at the last row. + ConstIterator beforeEnd() const + { + return ConstIterator(WrapperType(this),n-1); + } + + //! @returns an iterator that is positioned before + //! the first row of the matrix. + ConstIterator beforeBegin () const + { + return ConstIterator(WrapperType(this),-1); + } + + + + //===== vector space arithmetic + + //! vector space addition + DiagonalMatrix& operator+= (const DiagonalMatrix& y) + { + diag_ += y.diag_; + return *this; + } + + //! vector space subtraction + DiagonalMatrix& operator-= (const DiagonalMatrix& y) + { + diag_ -= y.diag_; + return *this; + } + + //! vector space multiplication with scalar + DiagonalMatrix& operator+= (const K& k) + { + diag_ += k; + return *this; + } + + //! vector space division by scalar + DiagonalMatrix& operator-= (const K& k) + { + diag_ -= k; + return *this; + } + + //! vector space multiplication with scalar + DiagonalMatrix& operator*= (const K& k) + { + diag_ *= k; + return *this; + } + + //! vector space division by scalar + DiagonalMatrix& operator/= (const K& k) + { + diag_ /= k; + return *this; + } + + //===== comparison ops + + //! comparison operator + bool operator==(const DiagonalMatrix& other) const + { + return diag_==other.diagonal(); + } + + //! incomparison operator + bool operator!=(const DiagonalMatrix& other) const + { + return diag_!=other.diagonal(); + } + + + //===== linear maps + + //! y = A x + template + void mv (const X& x, Y& y) const + { +#ifdef DUNE_FMatrix_WITH_CHECKING + if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); +#endif + for (size_type i=0; i + void mtv (const X& x, Y& y) const + { + mv(x, y); + } + + //! y += A x + template + void umv (const X& x, Y& y) const + { +#ifdef DUNE_FMatrix_WITH_CHECKING + if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); +#endif + for (size_type i=0; i + void umtv (const X& x, Y& y) const + { +#ifdef DUNE_FMatrix_WITH_CHECKING + if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); +#endif + for (size_type i=0; i + void umhv (const X& x, Y& y) const + { +#ifdef DUNE_FMatrix_WITH_CHECKING + if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); +#endif + for (size_type i=0; i + void mmv (const X& x, Y& y) const + { +#ifdef DUNE_FMatrix_WITH_CHECKING + if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); +#endif + for (size_type i=0; i + void mmtv (const X& x, Y& y) const + { +#ifdef DUNE_FMatrix_WITH_CHECKING + if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); +#endif + for (size_type i=0; i + void mmhv (const X& x, Y& y) const + { +#ifdef DUNE_FMatrix_WITH_CHECKING + if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); +#endif + for (size_type i=0; i + void usmv (const typename FieldTraits::field_type & alpha, + const X& x, Y& y) const + { +#ifdef DUNE_FMatrix_WITH_CHECKING + if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); +#endif + for (size_type i=0; i + void usmtv (const typename FieldTraits::field_type & alpha, + const X& x, Y& y) const + { +#ifdef DUNE_FMatrix_WITH_CHECKING + if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); +#endif + for (size_type i=0; i + void usmhv (const typename FieldTraits::field_type & alpha, + const X& x, Y& y) const + { +#ifdef DUNE_FMatrix_WITH_CHECKING + if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); +#endif + for (size_type i=0; i + void solve (V& x, const V& b) const + { + for (int i=0; i::real_type; + for (int i=0; i= 0 && i < n); + DUNE_ASSERT_BOUNDS(j >= 0 && j < n); + return i==j; + } + + + + //! Sends the matrix to an output stream + friend std::ostream& operator<< (std::ostream& s, const DiagonalMatrix& a) + { + for (size_type i=0; i(&diag_[i]), i); + } + + //! Return const_reference object as row replacement + const_reference operator[](size_type i) const + { + return const_reference(const_cast(&diag_[i]), i); + } + + //! Get const reference to diagonal entry + const K& diagonal(size_type i) const + { + return diag_[i]; + } + + //! Get reference to diagonal entry + K& diagonal(size_type i) + { + return diag_[i]; + } + + //! Get const reference to diagonal vector + const FieldVector& diagonal() const + { + return diag_; + } + + //! Get reference to diagonal vector + FieldVector& diagonal() + { + return diag_; + } + + private: + + // the data, a FieldVector storing the diagonal + FieldVector diag_; + }; + + template< class K, int n > + struct FieldTraits< DiagonalMatrix > + { + typedef typename FieldTraits::field_type field_type; + typedef typename FieldTraits::real_type real_type; + }; + + +#ifndef DOXYGEN // hide specialization + /** \brief Special type for 1x1 matrices + */ + template< class K > + class DiagonalMatrix : public FieldMatrix + { + typedef FieldMatrix Base; + public: + //! The type used for index access and size operations + typedef typename Base::size_type size_type; + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain. + //! This is always one for this type. + blocklevel = 1 + }; + + typedef typename Base::row_type row_type; + + typedef typename Base::row_reference row_reference; + typedef typename Base::const_row_reference const_row_reference; + + //! export size + enum { + //! \brief The number of rows. + //! This is always one for this type. + rows = 1, + //! \brief The number of columns. + //! This is always one for this type. + cols = 1 + }; + + + //! Default Constructor + constexpr DiagonalMatrix() = default; + + //! Constructor initializing the whole matrix with a scalar + DiagonalMatrix(const K& scalar) + { + (*this)[0][0] = scalar; + } + + //! Get const reference to diagonal entry + const K& diagonal(size_type) const + { + return (*this)[0][0]; + } + + //! Get reference to diagonal entry + K& diagonal(size_type) + { + return (*this)[0][0]; + } + + //! Get const reference to diagonal vector + const FieldVector& diagonal() const + { + return (*this)[0]; + } + + //! Get reference to diagonal vector + FieldVector& diagonal() + { + return (*this)[0]; + } + + }; +#endif + + + template + class DiagonalMatrixWrapper + { + typedef typename DiagonalMatrixType::reference reference; + typedef typename DiagonalMatrixType::const_reference const_reference; + typedef typename DiagonalMatrixType::field_type K; + typedef DiagonalRowVector row_type; + typedef std::size_t size_type; + typedef DiagonalMatrixWrapper< DiagonalMatrixType> MyType; + + friend class ContainerWrapperIterator; + friend class ContainerWrapperIterator; + + public: + + DiagonalMatrixWrapper() : + mat_(0) + {} + + DiagonalMatrixWrapper(const DiagonalMatrixType* mat) : + mat_(const_cast(mat)) + {} + + size_type realIndex(int i) const + { + return i; + } + + row_type* pointer(int i) const + { + row_ = row_type(&(mat_->diagonal(i)), i); + return &row_; + } + + bool identical(const DiagonalMatrixWrapper& other) const + { + return mat_==other.mat_; + } + + private: + + mutable DiagonalMatrixType* mat_; + mutable row_type row_; + }; + + /** \brief + * + */ + template< class K, int n > + class DiagonalRowVectorConst + { + template + friend class DiagonalMatrixWrapper; + friend class ContainerWrapperIterator, const K, const K&>; + + public: + // remember size of vector + enum { dimension = n }; + + // standard constructor and everything is sufficient ... + + //===== type definitions and constants + + //! export the type representing the field + typedef K field_type; + + //! export the type representing the components + typedef K block_type; + + //! The type used for the index access and size operation + typedef std::size_t size_type; + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain + blocklevel = 1 + }; + + //! export size + enum { + //! The size of this vector. + size = n + }; + + //! Constructor making uninitialized vector + DiagonalRowVectorConst() : + p_(0), + row_(0) + {} + + //! Constructor making vector with identical coordinates + explicit DiagonalRowVectorConst (K* p, int col) : + p_(p), + row_(col) + {} + + //===== access to components + + //! same for read only access + const K& operator[] ([[maybe_unused]] size_type i) const + { + DUNE_ASSERT_BOUNDS(i == row_); + return *p_; + } + + // check if row is identical to other row (not only identical values) + // since this is a proxy class we need to check equality of the stored pointer + bool identical(const DiagonalRowVectorConst& other) const + { + return ((p_ == other.p_)and (row_ == other.row_)); + } + + //! ConstIterator class for sequential access + typedef ContainerWrapperIterator, const K, const K&> ConstIterator; + //! typedef for stl compliant access + typedef ConstIterator const_iterator; + + //! begin ConstIterator + ConstIterator begin () const + { + return ConstIterator(*this,0); + } + + //! end ConstIterator + ConstIterator end () const + { + return ConstIterator(*this,1); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the rows. i.e. at the row. + ConstIterator beforeEnd() const + { + return ConstIterator(*this,0); + } + + //! @returns an iterator that is positioned before + //! the first row of the matrix. + ConstIterator beforeBegin () const + { + return ConstIterator(*this,-1); + } + + //! Binary vector comparison + bool operator== (const DiagonalRowVectorConst& y) const + { + return ((p_==y.p_)and (row_==y.row_)); + } + + //===== sizes + + //! number of blocks in the vector (are of size 1 here) + size_type N () const + { + return n; + } + + //! dimension of the vector space + size_type dim () const + { + return n; + } + + //! index of this row in surrounding matrix + size_type rowIndex() const + { + return row_; + } + + //! the diagonal value + const K& diagonal() const + { + return *p_; + } + + protected: + + size_type realIndex([[maybe_unused]] int i) const + { + return rowIndex(); + } + + K* pointer([[maybe_unused]] size_type i) const + { + return const_cast(p_); + } + + DiagonalRowVectorConst* operator&() + { + return this; + } + + // the data, very simply a pointer to the diagonal value and the row number + K* p_; + size_type row_; + }; + + template< class K, int n > + class DiagonalRowVector : public DiagonalRowVectorConst + { + template + friend class DiagonalMatrixWrapper; + friend class ContainerWrapperIterator, K, K&>; + + public: + // standard constructor and everything is sufficient ... + + //===== type definitions and constants + + //! export the type representing the field + typedef K field_type; + + //! export the type representing the components + typedef K block_type; + + //! The type used for the index access and size operation + typedef std::size_t size_type; + + //! Constructor making uninitialized vector + DiagonalRowVector() : DiagonalRowVectorConst() + {} + + //! Constructor making vector with identical coordinates + explicit DiagonalRowVector (K* p, int col) : DiagonalRowVectorConst(p, col) + {} + + //===== assignment from scalar + //! Assignment operator for scalar + DiagonalRowVector& operator= (const K& k) + { + *p_ = k; + return *this; + } + + //===== access to components + + //! random access + K& operator[] ([[maybe_unused]] size_type i) + { + DUNE_ASSERT_BOUNDS(i == row_); + return *p_; + } + + //! Iterator class for sequential access + typedef ContainerWrapperIterator, K, K&> Iterator; + //! typedef for stl compliant access + typedef Iterator iterator; + + //! begin iterator + Iterator begin () + { + return Iterator(*this, 0); + } + + //! end iterator + Iterator end () + { + return Iterator(*this, 1); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the rows, i.e. at the last row. + Iterator beforeEnd () + { + return Iterator(*this, 0); + } + + //! @returns an iterator that is positioned before + //! the first row of the matrix. + Iterator beforeBegin () + { + return Iterator(*this, -1); + } + + //! ConstIterator class for sequential access + typedef ContainerWrapperIterator, const K, const K&> ConstIterator; + //! typedef for stl compliant access + typedef ConstIterator const_iterator; + + using DiagonalRowVectorConst::identical; + using DiagonalRowVectorConst::operator[]; + using DiagonalRowVectorConst::operator==; + using DiagonalRowVectorConst::begin; + using DiagonalRowVectorConst::end; + using DiagonalRowVectorConst::beforeEnd; + using DiagonalRowVectorConst::beforeBegin; + using DiagonalRowVectorConst::N; + using DiagonalRowVectorConst::dim; + using DiagonalRowVectorConst::rowIndex; + using DiagonalRowVectorConst::diagonal; + + protected: + + DiagonalRowVector* operator&() + { + return this; + } + + private: + + using DiagonalRowVectorConst::p_; + using DiagonalRowVectorConst::row_; + }; + + + // implement type traits + template + struct const_reference< DiagonalRowVector > + { + typedef DiagonalRowVectorConst type; + }; + + template + struct const_reference< DiagonalRowVectorConst > + { + typedef DiagonalRowVectorConst type; + }; + + template + struct mutable_reference< DiagonalRowVector > + { + typedef DiagonalRowVector type; + }; + + template + struct mutable_reference< DiagonalRowVectorConst > + { + typedef DiagonalRowVector type; + }; + + + + /** \brief Iterator class for sparse vector-like containers + * + * This class provides an iterator for sparse vector like containers. + * It contains a ContainerWrapper that must provide the translation + * from the position in the underlying container to the index + * in the sparse container. + * + * The ContainerWrapper must be default and copy-constructable. + * Furthermore it must provide the methods: + * + * bool identical(other) - check if this is identical to other (same container, not only equal) + * T* pointer(position) - get pointer to data at position in underlying container + * size_t realIndex(position) - get index in sparse container for position in underlying container + * + * Notice that the iterator stores a ContainerWrapper. + * This allows one to use proxy classes as underlying container + * and as returned reference type. + * + * \tparam CW The container wrapper class + * \tparam T The contained type + * \tparam R The reference type returned by dereference + */ + template + class ContainerWrapperIterator : public BidirectionalIteratorFacade,T, R, int> + { + typedef typename std::remove_const::type NonConstCW; + + friend class ContainerWrapperIterator::type, typename mutable_reference::type>; + friend class ContainerWrapperIterator::type, typename const_reference::type>; + + typedef ContainerWrapperIterator::type, typename mutable_reference::type> MyType; + typedef ContainerWrapperIterator::type, typename const_reference::type> MyConstType; + + public: + + // Constructors needed by the facade iterators. + ContainerWrapperIterator() : + containerWrapper_(), + position_(0) + {} + + ContainerWrapperIterator(CW containerWrapper, int position) : + containerWrapper_(containerWrapper), + position_(position) + {} + + template + ContainerWrapperIterator(OtherContainerWrapperIteratorType& other) : + containerWrapper_(other.containerWrapper_), + position_(other.position_) + {} + + ContainerWrapperIterator(const MyType& other) : + containerWrapper_(other.containerWrapper_), + position_(other.position_) + {} + + ContainerWrapperIterator(const MyConstType& other) : + containerWrapper_(other.containerWrapper_), + position_(other.position_) + {} + + template + ContainerWrapperIterator& operator=(OtherContainerWrapperIteratorType& other) + { + containerWrapper_ = other.containerWrapper_; + position_ = other.position_; + return *this; + } + + // This operator is needed since we can not get the address of the + // temporary object returned by dereference + T* operator->() const + { + return containerWrapper_.pointer(position_); + } + + // Methods needed by the forward iterator + bool equals(const MyType& other) const + { + return position_ == other.position_ && containerWrapper_.identical(other.containerWrapper_); + } + + bool equals(const MyConstType& other) const + { + return position_ == other.position_ && containerWrapper_.identical(other.containerWrapper_); + } + + R dereference() const + { + return *containerWrapper_.pointer(position_); + } + + void increment() + { + ++position_; + } + + // Additional function needed by BidirectionalIterator + void decrement() + { + --position_; + } + + // Additional function needed by RandomAccessIterator + R elementAt(int i) const + { + return *containerWrapper_.pointer(position_+i); + } + + void advance(int n) + { + position_=position_+n; + } + + template + std::ptrdiff_t distanceTo(OtherContainerWrapperIteratorType& other) const + { + assert(containerWrapper_.identical(other)); + return other.position_ - position_; + } + + std::ptrdiff_t index() const + { + return containerWrapper_.realIndex(position_); + } + + private: + NonConstCW containerWrapper_; + size_t position_; + }; + + template + struct DenseMatrixAssigner> { + static void apply(DenseMatrix& denseMatrix, + DiagonalMatrix const& rhs) { + DUNE_ASSERT_BOUNDS(denseMatrix.M() == N); + DUNE_ASSERT_BOUNDS(denseMatrix.N() == N); + denseMatrix = field(0); + for (int i = 0; i < N; ++i) + denseMatrix[i][i] = rhs.diagonal()[i]; + } + }; + /* @} */ +} // end namespace +#endif diff --git a/dune/common/documentation.hh b/dune/common/documentation.hh new file mode 100644 index 0000000..934edfc --- /dev/null +++ b/dune/common/documentation.hh @@ -0,0 +1,58 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_COMMON_DOCUMENTATION_HH +#define DUNE_COMMON_DOCUMENTATION_HH + +/** \file + \brief Documentation related stuff + */ + +namespace Dune { + + /** + * \brief Dummy struct used for documentation purposes + * + * This struct can be used for documenting interfaces. One example would + * be: + * \code + * // Traits class that determines some property for some other type T + * template + * class SomeTraits { + * static_assert(Dune::AlwaysFalse::value, + * "Sorry, SomeTraits must be specialized for all types"); + * public: + * // The type of some property of T + * typedef ImplementationDefined type; + * }; + * #ifndef DOXYGEN + * template<> + * struct SomeTraits + * typedef ... type; + * }; + * // ... + * #endif // DOXYGEN + * \endcode + * + * \sa implementationDefined + * \ingroup Common + */ + struct ImplementationDefined {}; + + /** + * \brief Dummy integral value used for documentation purposes + * + * \var Dune::implementationDefined + * \code + * #include + * \endcode + * + * \sa ImplementationDefined + * \ingroup Common + */ + enum { implementationDefined }; + +} + + +#endif // DUNE_COMMON_DOCUMENTATION_HH diff --git a/dune/common/dotproduct.hh b/dune/common/dotproduct.hh new file mode 100644 index 0000000..731acad --- /dev/null +++ b/dune/common/dotproduct.hh @@ -0,0 +1,96 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_DOTPRODUCT_HH +#define DUNE_DOTPRODUCT_HH + +#include "ftraits.hh" +#include "typetraits.hh" + +namespace Dune { + /** + * @file + * @brief Provides the functions dot(a,b) := \f$a^H \cdot b \f$ and dotT(a,b) := \f$a^T \cdot b \f$ + * + * The provided dot products dot,dotT are used to compute (indefinite) dot products for fundamental types as well as DUNE vector types, such as DenseVector, FieldVector, ISTLVector. + * Note that the definition of dot(a,b) conjugates the first argument. This agrees with the behaviour of Matlab and Petsc, but not with BLAS. + * @author Jö Fahlke, Matthias Wohlmuth + */ + + /** @addtogroup Common + * + * @{ + */ + + template + struct IsVector : std::false_type {}; + + template + struct IsVector > + : std::true_type {}; + + /** @brief computes the dot product for fundamental data types according to Petsc's VectDot function: dot(a,b) := std::conj(a)*b + * + * @see http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecDot.html#VecDot + * @param a + * @param b + * @return conj(a)*b + */ + template + auto + dot(const A & a, const B & b) -> typename std::enable_if::value && !std::is_same::field_type,typename FieldTraits
::real_type> ::value, decltype(conj(a)*b)>::type + { + return conj(a)*b; + } + + /** + * @brief computes the dot product for fundamental data types according to Petsc's VectDot function: dot(a,b) := std::conj(a)*b + * + * Specialization for real first arguments which replaces conj(a) by a. + * @see http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecTDot.html#VecTDot + * @param a + * @param b + * @return a*b (which is the same as conj(a)*b in this case) + */ + // fundamental type with A being a real type + template + auto + dot(const A & a, const B & b) -> typename std::enable_if::value && std::is_same::field_type,typename FieldTraits::real_type>::value, decltype(a*b)>::type + { + return a*b; + } + + /** + * @brief computes the dot product for various dune vector types according to Petsc's VectDot function: dot(a,b) := std::conj(a)*b + * + * Specialization for real first arguments which replaces conj(a) by a. + * @see http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecTDot.html#VecTDot + * @param a + * @param b + * @return dot(a,b) + */ + template + auto + dot(const A & a, const B & b) -> typename std::enable_if::value, decltype(a.dot(b))>::type + { + return a.dot(b); + } + /** @} */ + + /** + * @brief Computes an indefinite vector dot product for fundamental data types according to Petsc's VectTDot function: dotT(a,b) := a*b + * @see http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecTDot.html#VecTDot + * @param a + * @param b + * @return a*b + */ + template + auto + dotT(const A & a, const B & b) -> decltype(a*b) + { + return a*b; + } + + /** @} */ +} // end namespace DUNE + +#endif // DUNE_DOTPRODUCT_HH diff --git a/dune/common/dynmatrix.hh b/dune/common/dynmatrix.hh new file mode 100644 index 0000000..ba78118 --- /dev/null +++ b/dune/common/dynmatrix.hh @@ -0,0 +1,149 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_DYNMATRIX_HH +#define DUNE_DYNMATRIX_HH + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace Dune +{ + + /** + @addtogroup DenseMatVec + @{ + */ + + /*! \file + * \brief This file implements a dense matrix with dynamic numbers of rows and columns. + */ + + template< class K > class DynamicMatrix; + + template< class K > + struct DenseMatVecTraits< DynamicMatrix > + { + typedef DynamicMatrix derived_type; + + typedef DynamicVector row_type; + + typedef row_type &row_reference; + typedef const row_type &const_row_reference; + + typedef std::vector container_type; + typedef K value_type; + typedef typename container_type::size_type size_type; + }; + + template< class K > + struct FieldTraits< DynamicMatrix > + { + typedef typename FieldTraits::field_type field_type; + typedef typename FieldTraits::real_type real_type; + }; + + /** \brief Construct a matrix with a dynamic size. + * + * \tparam K is the field type (use float, double, complex, etc) + */ + template + class DynamicMatrix : public DenseMatrix< DynamicMatrix > + { + std::vector< DynamicVector > _data; + typedef DenseMatrix< DynamicMatrix > Base; + public: + typedef typename Base::size_type size_type; + typedef typename Base::value_type value_type; + typedef typename Base::row_type row_type; + + //===== constructors + //! \brief Default constructor + DynamicMatrix () {} + + //! \brief Constructor initializing the whole matrix with a scalar + DynamicMatrix (size_type r, size_type c, value_type v = value_type() ) : + _data(r, row_type(c, v) ) + {} + + /** \brief Constructor initializing the matrix from a list of vector + */ + DynamicMatrix (std::initializer_list> const &ll) + : _data(ll) + {} + + + template ::value && HasDenseMatrixAssigner::value>> + DynamicMatrix(T const& rhs) + { + *this = rhs; + } + + //==== resize related methods + /** + * \brief resize matrix to r × c + * + * Resize the matrix to r × c, using v + * as the value of all entries. + * + * \warning All previous entries are lost, even when the matrix + * was not actually resized. + * + * \param r number of rows + * \param c number of columns + * \param v value of matrix entries + */ + void resize (size_type r, size_type c, value_type v = value_type() ) + { + _data.resize(0); + _data.resize(r, row_type(c, v) ); + } + + //===== assignment + // General assignment with resizing + template ::value>> + DynamicMatrix& operator=(T const& rhs) { + _data.resize(rhs.N()); + std::fill(_data.begin(), _data.end(), row_type(rhs.M(), K(0))); + Base::operator=(rhs); + return *this; + } + + // Specialisation: scalar assignment (no resizing) + template ::value>> + DynamicMatrix& operator=(T scalar) { + std::fill(_data.begin(), _data.end(), scalar); + return *this; + } + + // make this thing a matrix + size_type mat_rows() const { return _data.size(); } + size_type mat_cols() const { + assert(this->rows()); + return _data.front().size(); + } + row_type & mat_access(size_type i) { + DUNE_ASSERT_BOUNDS(i < _data.size()); + return _data[i]; + } + const row_type & mat_access(size_type i) const { + DUNE_ASSERT_BOUNDS(i < _data.size()); + return _data[i]; + } + }; + + /** @} end documentation */ + +} // end namespace + +#endif diff --git a/dune/common/dynmatrixev.hh b/dune/common/dynmatrixev.hh new file mode 100644 index 0000000..2cf1074 --- /dev/null +++ b/dune/common/dynmatrixev.hh @@ -0,0 +1,106 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_DYNMATRIXEIGENVALUES_HH +#define DUNE_DYNMATRIXEIGENVALUES_HH + +#include +#include + +#include "dynmatrix.hh" +#include "fmatrixev.hh" + +/*! + \file + \brief utility functions to compute eigenvalues for + dense matrices. + \addtogroup DenseMatVec + @{ + */ + +namespace Dune { + + namespace DynamicMatrixHelp { + +#if HAVE_LAPACK + using Dune::FMatrixHelp::eigenValuesNonsymLapackCall; +#endif + + /** \brief calculates the eigenvalues of a symmetric field matrix + \param[in] matrix matrix eigenvalues are calculated for + \param[out] eigenValues FieldVector that contains eigenvalues in + ascending order + \param[out] eigenVectors (optional) list of right eigenvectors + + \note LAPACK::dgeev is used to calculate the eigen values + */ + template + static void eigenValuesNonSym(const DynamicMatrix& matrix, + DynamicVector& eigenValues, + std::vector>* eigenVectors = nullptr + ) + { + +#if HAVE_LAPACK + { + const long int N = matrix.rows(); + const char jobvl = 'n'; + const char jobvr = eigenVectors ? 'v' : 'n'; + + + // matrix to put into dgeev + auto matrixVector = std::make_unique(N*N); + + // copy matrix + int row = 0; + for(int i=0; i(N); + auto eigenI = std::make_unique(N); + + const long int lwork = eigenVectors ? 4*N : 3*N; + auto work = std::make_unique(lwork); + auto vr = eigenVectors ? std::make_unique(N*N) : std::unique_ptr{}; + + // return value information + long int info = 0; + + // call LAPACK routine (see fmatrixev_ext.cc) + eigenValuesNonsymLapackCall(&jobvl, &jobvr, &N, matrixVector.get(), &N, + eigenR.get(), eigenI.get(), nullptr, &N, vr.get(), &N, work.get(), + &lwork, &info); + + if( info != 0 ) + { + std::cerr << "For matrix " << matrix << " eigenvalue calculation failed! " << std::endl; + DUNE_THROW(InvalidStateException,"eigenValues: Eigenvalue calculation failed!"); + } + + eigenValues.resize(N); + for (int i=0; i(eigenR[i], eigenI[i]); + + if (eigenVectors) { + eigenVectors->resize(N); + for (int i = 0; i < N; ++i) { + auto& v = (*eigenVectors)[i]; + v.resize(N); + std::copy(vr.get() + N*i, vr.get() + N*(i+1), &v[0]); + } + } + } +#else // #if HAVE_LAPACK + DUNE_THROW(NotImplemented,"LAPACK not found!"); +#endif + } + } + +} +/** @} */ +#endif diff --git a/dune/common/dynvector.hh b/dune/common/dynvector.hh new file mode 100644 index 0000000..3d9c091 --- /dev/null +++ b/dune/common/dynvector.hh @@ -0,0 +1,202 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_DYNVECTOR_HH +#define DUNE_DYNVECTOR_HH + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "boundschecking.hh" +#include "exceptions.hh" +#include "genericiterator.hh" + +#include +#include "densevector.hh" + +namespace Dune { + + /** @addtogroup DenseMatVec + @{ + */ + + /*! \file + * \brief This file implements a dense vector with a dynamic size. + */ + + template< class K, class Allocator > class DynamicVector; + template< class K, class Allocator > + struct DenseMatVecTraits< DynamicVector< K, Allocator > > + { + typedef DynamicVector< K, Allocator > derived_type; + typedef std::vector< K, Allocator > container_type; + typedef K value_type; + typedef typename container_type::size_type size_type; + }; + + template< class K, class Allocator > + struct FieldTraits< DynamicVector< K, Allocator > > + { + typedef typename FieldTraits< K >::field_type field_type; + typedef typename FieldTraits< K >::real_type real_type; + }; + + /** \brief Construct a vector with a dynamic size. + * + * \tparam K is the field type (use float, double, complex, etc) + * \tparam Allocator type of allocator object used to define the storage allocation model, + * default Allocator = std::allocator< K >. + */ + template< class K, class Allocator = std::allocator< K > > + class DynamicVector : public DenseVector< DynamicVector< K, Allocator > > + { + std::vector< K, Allocator > _data; + + typedef DenseVector< DynamicVector< K, Allocator > > Base; + public: + typedef typename Base::size_type size_type; + typedef typename Base::value_type value_type; + + typedef std::vector< K, Allocator > container_type; + + typedef Allocator allocator_type; + + //! Constructor making uninitialized vector + explicit DynamicVector(const allocator_type &a = allocator_type() ) : + _data( a ) + {} + + explicit DynamicVector(size_type n, const allocator_type &a = allocator_type() ) : + _data( n, value_type(), a ) + {} + + //! Constructor making vector with identical coordinates + DynamicVector( size_type n, value_type c, const allocator_type &a = allocator_type() ) : + _data( n, c, a ) + {} + + /** \brief Construct from a std::initializer_list */ + DynamicVector (std::initializer_list const &l) : + _data(l) + {} + + //! Constructor making vector with identical coordinates + DynamicVector(const DynamicVector & x) : + Base(), _data(x._data) + {} + + //! Move constructor + DynamicVector(DynamicVector && x) : + _data(std::move(x._data)) + {} + + template< class T > + DynamicVector(const DynamicVector< T, Allocator > & x) : + _data(x.begin(), x.end(), x.get_allocator()) + {} + + //! Copy constructor from another DenseVector + template< class X > + DynamicVector(const DenseVector< X > & x, const allocator_type &a = allocator_type() ) : + _data(a) + { + const size_type n = x.size(); + _data.reserve(n); + for( size_type i =0; i + inline std::istream &operator>> ( std::istream &in, + DynamicVector< K, Allocator > &v ) + { + DynamicVector< K, Allocator > w(v); + for( typename DynamicVector< K, Allocator >::size_type i = 0; i < w.size(); ++i ) + in >> w[ i ]; + if(in) + v = std::move(w); + return in; + } + + /** @} end documentation */ + +} // end namespace + +#endif diff --git a/dune/common/enumset.hh b/dune/common/enumset.hh new file mode 100644 index 0000000..fc1afef --- /dev/null +++ b/dune/common/enumset.hh @@ -0,0 +1,172 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_ENUMSET_HH +#define DUNE_ENUMSET_HH + +#include + +namespace Dune +{ + /** + * @file + * @brief Classes for building sets out of enumeration values. + * @author Markus Blatt + */ + /** @addtogroup Common + * + * @{ + */ + + /** + * @brief An empty set. + */ + template + class EmptySet + { + public: + /** + * @brief The POD type the set holds. + */ + typedef TA Type; + /** + * @brief Always returns false. + */ + static bool contains(const Type& attribute); + }; + + /** + * @brief A set containing everything. + */ + template + class AllSet + { + public: + /** + * @brief The POD type the set holds. + */ + typedef TA Type; + /** + * @brief Always returns true. + */ + static bool contains(const Type& attribute); + }; + + /** + * @brief A set consisting only of one item. + */ + template + class EnumItem + { + public: + /** + * @brief The type the set holds. + */ + typedef TA Type; + + /** + * @brief Tests whether an item is in the set. + * @return True if item==Type. + */ + static bool contains(const Type& attribute); + }; + + /** + * @brief A set representing a range including the borders. + */ + template + class EnumRange //: public PODSet,T> + { + public: + /** + * @brief The type the set holds. + */ + typedef TA Type; + static bool contains(const Type& item); + }; + + /** + * @brief The negation of a set. + * An item is contained in the set if and only if it is not + * contained in the negated set. + */ + template + class NegateSet + { + public: + typedef typename S::Type Type; + + static bool contains(const Type& item) + { + return !S::contains(item); + } + }; + + /** + * @brief A set combining two other sets. + */ + template + class Combine + { + public: + static bool contains(const TA& item); + }; + + template + inline bool EmptySet::contains([[maybe_unused]] const Type& attribute) + { + return false; + } + + template + inline bool AllSet::contains([[maybe_unused]] const Type& attribute) + { + return true; + } + + template + inline bool EnumItem::contains(const Type& item) + { + return item==i; + } + + template + inline std::ostream& operator<<(std::ostream& os, const EnumItem&) + { + return os< + inline bool EnumRange::contains(const Type& item) + { + return from<=item && item<=to; + } + + template + inline std::ostream& operator<<(std::ostream& os, const EnumRange&) + { + return os<<"["< + inline bool Combine::contains(const TA& item) + { + return TI1::contains(item) || + TI2::contains(item); + } + + template + inline Combine combine([[maybe_unused]] const TI1& set1, + [[maybe_unused]] const TI2& set2) + { + return Combine(); + } + + template + inline std::ostream& operator<<(std::ostream& os, const Combine&) + { + return os << TI1()<<" "< + +namespace Dune { + /* + static member of Dune::Exception + */ + ExceptionHook * Exception::_hook = 0; + + /* + Implementation of Dune::Exception + */ + Exception::Exception () + { + // call the hook if necessary + if (_hook != 0) _hook->operator()(); + } + + void Exception::registerHook (ExceptionHook * hook) + { + _hook = hook; + } + + void Exception::clearHook () + { + _hook = 0; + } + + void Exception::message(const std::string & msg) + { + _message = msg; + } + + const char* Exception::what() const noexcept + { + return _message.data(); + } + +} diff --git a/dune/common/exceptions.hh b/dune/common/exceptions.hh new file mode 100644 index 0000000..fe59e60 --- /dev/null +++ b/dune/common/exceptions.hh @@ -0,0 +1,289 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_EXCEPTIONS_HH +#define DUNE_EXCEPTIONS_HH + +#include +#include +#include + +namespace Dune { + + /*! \defgroup Exceptions Exception handling + \ingroup Common + \{ + + The Dune-exceptions are designed to allow a simple derivation of subclasses + and to accept a text written in the '<<' syntax. + + Example of usage: + + \code + #include + + ... + + class FileNotFoundError : public Dune::IOError {}; + + ... + + void fileopen (std::string name) { + std::ifstream file; + + file.open(name.c_str()); + + if (file == 0) + DUNE_THROW(FileNotFoundError, "File " << name << " not found!"); + + ... + + file.close(); + } + + ... + + int main () { + try { + ... + } catch (Dune::IOError &e) { + std::cerr << "I/O error: " << e << std::endl; + return 1; + } catch (Dune::Exception &e) { + std::cerr << "Generic Dune error: " << e << std::endl; + return 2; + } + } + \endcode + + \see exceptions.hh for detailed info + + */ + + /*! \file + \brief A few common exception classes + + This file defines a common framework for generating exception + subclasses and to throw them in a simple manner + + */ + + /* forward declarations */ + class Exception; + struct ExceptionHook; + + /*! \class Exception + \brief Base class for Dune-Exceptions + + all Dune exceptions are derived from this class via trivial subclassing: + + \code + class MyException : public Dune::Exception {}; + \endcode + + You should not \c throw a Dune::Exception directly but use the macro + DUNE_THROW() instead which fills the message-buffer of the exception + in a standard way and features a way to pass the result in the + operator<<-style + + \see DUNE_THROW, IOError, MathError + + */ + class Exception + : public std::exception + { + public: + Exception (); + void message(const std::string &msg); //!< store string in internal message buffer + const char* what() const noexcept override; //!< output internal message buffer + static void registerHook (ExceptionHook * hook); //!< add a functor which is called before a Dune::Exception is emitted (see Dune::ExceptionHook) \see Dune::ExceptionHook + static void clearHook (); //!< remove all hooks + private: + std::string _message; + static ExceptionHook * _hook; + }; + + /*! \brief Base class to add a hook to the Dune::Exception + + The user can add a functor which should be called before a Dune::Exception is emitted. + + + Example: attach a debugger to the process, if an exception is thrown + \code + struct ExceptionHookDebugger : public Dune::ExceptionHook + { + char * process_; + char * debugger_; + ExceptionHookDebugger (int argc, char ** argv, std::string debugger) + { + process_ = strdup(argv[0]); + debugger_ = strdup(debugger.c_str()); + } + virtual void operator () () + { + pid_t pid = getpid(); + pid_t cpid; + cpid = fork(); + if (cpid == 0) // child + { + char * argv[4]; + argv[0] = debugger_; + argv[1] = process_; + argv[2] = new char[12]; + snprintf(argv[2], 12, "%i", int(pid)); + argv[3] = 0; + // execute debugger + std::cout << process_ << "\n"; + std::cout << argv[0] << " " + << argv[1] << " " + << argv[2] << std::endl; + execv(argv[0], argv); + } + else // parent + { + // send application to sleep + kill(pid, SIGSTOP); + } + } + }; + \endcode + + This hook is registered via a static method of Dune::Exception: + \code + int main(int argc, char** argv) { + Dune::MPIHelper & mpihelper = Dune::MPIHelper::instance(argc,argv); + ExceptionHookDebugger debugger(argc, argv, "/usr/bin/ddd"); + Dune::Exception::registerHook(& debugger); + try + { + ... + } + catch (std::string & s) { + std::cout << mpihelper.rank() << ": ERROR: " << s << std::endl; + } + catch (Dune::Exception & e) { + std::cout << mpihelper.rank() << ": DUNE ERROR: " << e.what() << std::endl; + } + } + \endcode + + */ + struct ExceptionHook + { + virtual ~ExceptionHook() {} + virtual void operator () () = 0; + }; + + inline std::ostream& operator<<(std::ostream &stream, const Exception &e) + { + return stream << e.what(); + } + +#ifndef DOXYGEN + // the "format" the exception-type gets printed. __FILE__ and + // __LINE__ are standard C-defines, the GNU cpp-infofile claims that + // C99 defines __func__ as well. __FUNCTION__ is a GNU-extension +#define THROWSPEC(E) # E << " [" << __func__ << ":" << __FILE__ << ":" << __LINE__ << "]: " +#endif // DOXYGEN + + /*! Macro to throw an exception + + \code + #include + \endcode + + \param E exception class derived from Dune::Exception + \param m reason for this exception in ostream-notation + + Example: + + \code + if (filehandle == 0) + DUNE_THROW(FileError, "Could not open " << filename << " for reading!"); + \endcode + + DUNE_THROW automatically adds information about the exception thrown + to the text. + + \note + you can add a hook to be called before a Dune::Exception is emitted, + e.g. to add additional information to the exception, + or to invoke a debugger during parallel debugging. (see Dune::ExceptionHook) + + */ + // this is the magic: use the usual do { ... } while (0) trick, create + // the full message via a string stream and throw the created object +#define DUNE_THROW(E, m) do { E th__ex; std::ostringstream th__out; \ + th__out << THROWSPEC(E) << m; th__ex.message(th__out.str()); throw th__ex; \ +} while (0) + + /*! \brief Default exception class for I/O errors + + This is a superclass for any errors dealing with file/socket I/O problems + like + + - file not found + - could not write file + - could not connect to remote socket + */ + class IOError : public Exception {}; + + /*! \brief Default exception class for mathematical errors + + This is the superclass for all errors which are caused by + mathematical problems like + + - matrix not invertible + - not convergent + */ + class MathError : public Exception {}; + + /*! \brief Default exception class for range errors + + This is the superclass for all errors which are caused because + the user tries to access data that was not allocated before. + These can be problems like + + - accessing array entries behind the last entry + - adding the fourth non zero entry in a sparse matrix + with only three non zero entries per row + + */ + class RangeError : public Exception {}; + + /*! \brief Default exception for dummy implementations + + This exception can be used for functions/methods + + - that have to be implemented but should never be called + - that are missing + */ + class NotImplemented : public Exception {}; + + /*! \brief Default exception class for OS errors + + This class is thrown when a system-call is used and returns an + error. + + */ + class SystemError : public Exception {}; + + /*! \brief Default exception if memory allocation fails + + */ + class OutOfMemoryError : public SystemError {}; + + /*! \brief Default exception if a function was called while + the object is not in a valid state for that function. + */ + class InvalidStateException : public Exception {}; + + /*! \brief Default exception if an error in the parallel + communication of the program occurred + \ingroup ParallelCommunication + */ + class ParallelError : public Exception {}; + +} // end namespace + +#endif diff --git a/dune/common/filledarray.hh b/dune/common/filledarray.hh new file mode 100644 index 0000000..5d0559b --- /dev/null +++ b/dune/common/filledarray.hh @@ -0,0 +1,44 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_COMMON_FILLED_ARRAY_HH +#define DUNE_COMMON_FILLED_ARRAY_HH + +/** \file + \brief Utility to generate an array with a certain value + */ + +#include +#include + +namespace Dune +{ + /** @addtogroup Common + + @{ + */ + + //! Return an array filled with the provided value. + /** + * \note This function is `constexpr` only in C++17, or, more precisely, + * when `std::array::begin()` and `std::array::end()` are `constexpr`. + * + * \tparam n Size of the returned array. + * \tparam T Value type of the returned array. This is usually deduced + * from `t`. + */ + template + constexpr std::array filledArray(const T& t) + { + std::array arr{}; + // this is constexpr in c++17, `arr.fill(t)` is not + for(auto &el : arr) + el = t; + return arr; + } + + /** @} */ + +} // end namespace Dune + +#endif // DUNE_COMMON_FILLED_ARRAY_HH diff --git a/dune/common/float_cmp.cc b/dune/common/float_cmp.cc new file mode 100644 index 0000000..1dc18a8 --- /dev/null +++ b/dune/common/float_cmp.cc @@ -0,0 +1,506 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#include "float_cmp.hh" + +#include +#include +#include +#include +#include + +namespace Dune { + + + namespace FloatCmp { + // traits + //! Mapping of value type to epsilon type + /** + * @ingroup FloatCmp + * @tparam T The value type + */ + template struct EpsilonType { + //! The epsilon type corresponding to value type T + typedef T Type; + }; + //! Specialization of EpsilonType for std::vector + /** + * @ingroup FloatCmp + * @tparam T The value_type of the std::vector + * @tparam A The Allocator of the std::vector + */ + template + struct EpsilonType > { + //! The epsilon type corresponding to value type std::vector + typedef typename EpsilonType::Type Type; + }; + //! Specialization of EpsilonType for Dune::FieldVector + /** + * @ingroup FloatCmp + * @tparam T The field_type of the Dune::FieldVector + * @tparam n The size of the Dune::FieldVector + */ + template + struct EpsilonType > { + //! The epsilon type corresponding to value type Dune::FieldVector + typedef typename EpsilonType::Type Type; + }; + + // default epsilon + template + struct DefaultEpsilon { + static typename EpsilonType::Type value() + { return std::numeric_limits::Type>::epsilon()*8.; } + }; + template + struct DefaultEpsilon { + static typename EpsilonType::Type value() + { return std::numeric_limits::Type>::epsilon()*8.; } + }; + template + struct DefaultEpsilon { + static typename EpsilonType::Type value() + { return std::max(std::numeric_limits::Type>::epsilon(), 1e-6); } + }; + + namespace Impl { + // basic comparison + template + struct eq_t; + template + struct eq_t { + static bool eq(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + using std::abs; + return abs(first - second) <= epsilon*std::max(abs(first), abs(second)); + } + }; + template + struct eq_t { + static bool eq(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + using std::abs; + return abs(first - second) <= epsilon*std::min(abs(first), abs(second)); + } + }; + template + struct eq_t { + static bool eq(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + using std::abs; + return abs(first-second) <= epsilon; + } + }; + template + struct eq_t_std_vec { + typedef std::vector V; + static bool eq(const V &first, + const V &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()) { + auto size = first.size(); + if(size != second.size()) return false; + for(unsigned int i = 0; i < size; ++i) + if(!eq_t::eq(first[i], second[i], epsilon)) + return false; + return true; + } + }; + template< class T> + struct eq_t, relativeWeak> : eq_t_std_vec {}; + template< class T> + struct eq_t, relativeStrong> : eq_t_std_vec {}; + template< class T> + struct eq_t, absolute> : eq_t_std_vec {}; + + template + struct eq_t_fvec { + typedef Dune::FieldVector V; + static bool eq(const V &first, + const V &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()) { + for(int i = 0; i < n; ++i) + if(!eq_t::eq(first[i], second[i], epsilon)) + return false; + return true; + } + }; + template< class T, int n > + struct eq_t< Dune::FieldVector, relativeWeak> : eq_t_fvec {}; + template< class T, int n > + struct eq_t< Dune::FieldVector, relativeStrong> : eq_t_fvec {}; + template< class T, int n > + struct eq_t< Dune::FieldVector, absolute> : eq_t_fvec {}; + } // namespace Impl + + // operations in functional style + template + bool eq(const T &first, + const T &second, + typename EpsilonType::Type epsilon) + { + return Impl::eq_t::eq(first, second, epsilon); + } + template + bool ne(const T &first, + const T &second, + typename EpsilonType::Type epsilon) + { + return !eq(first, second, epsilon); + } + template + bool gt(const T &first, + const T &second, + typename EpsilonType::Type epsilon) + { + return first > second && ne(first, second, epsilon); + } + template + bool lt(const T &first, + const T &second, + typename EpsilonType::Type epsilon) + { + return first < second && ne(first, second, epsilon); + } + template + bool ge(const T &first, + const T &second, + typename EpsilonType::Type epsilon) + { + return first > second || eq(first, second, epsilon); + } + template + bool le(const T &first, + const T &second, + typename EpsilonType::Type epsilon) + { + return first < second || eq(first, second, epsilon); + } + + // default template arguments + template + bool eq(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + return eq(first, second, epsilon); + } + template + bool ne(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + return ne(first, second, epsilon); + } + template + bool gt(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + return gt(first, second, epsilon); + } + template + bool lt(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + return lt(first, second, epsilon); + } + template + bool ge(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + return ge(first, second, epsilon); + } + template + bool le(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + return le(first, second, epsilon); + } + + // rounding operations + namespace Impl { + template + struct round_t; + template + struct round_t { + static I + round(const T &val, + typename EpsilonType::Type epsilon = (DefaultEpsilon::value())) { + // first get an approximation + I lower = I(val); + I upper; + if(eq(T(lower), val, epsilon)) return lower; + if(T(lower) > val) { upper = lower; lower--; } + else upper = lower+1; + if(le(val - T(lower), T(upper) - val, epsilon)) + return lower; + else return upper; + } + }; + template + struct round_t { + static I + round(const T &val, + typename EpsilonType::Type epsilon = (DefaultEpsilon::value())) { + // first get an approximation + I lower = I(val); + I upper; + if(eq(T(lower), val, epsilon)) return lower; + if(T(lower) > val) { upper = lower; lower--; } + else upper = lower+1; + if(lt(val - T(lower), T(upper) - val, epsilon)) + return lower; + else return upper; + } + }; + template + struct round_t { + static I + round(const T &val, + typename EpsilonType::Type epsilon = (DefaultEpsilon::value())) { + if(val > T(0)) + return round_t::round(val, epsilon); + else return round_t::round(val, epsilon); + } + }; + template + struct round_t { + static I + round(const T &val, + typename EpsilonType::Type epsilon = (DefaultEpsilon::value())) { + if(val > T(0)) + return round_t::round(val, epsilon); + else return round_t::round(val, epsilon); + } + }; + template + struct round_t, std::vector, cstyle, rstyle> { + static std::vector + round(const T &val, + typename EpsilonType::Type epsilon = (DefaultEpsilon::value())) { + unsigned int size = val.size(); + std::vector res(size); + for(unsigned int i = 0; i < size; ++i) + res[i] = round_t::round(val[i], epsilon); + return res; + } + }; + template + struct round_t, Dune::FieldVector, cstyle, rstyle> { + static Dune::FieldVector + round(const T &val, + typename EpsilonType::Type epsilon = (DefaultEpsilon::value())) { + Dune::FieldVector res; + for(int i = 0; i < n; ++i) + res[i] = round_t::round(val[i], epsilon); + return res; + } + }; + } // end namespace Impl + template + I round(const T &val, typename EpsilonType::Type epsilon /*= DefaultEpsilon::value()*/) + { + return Impl::round_t::round(val, epsilon); + } + template + I round(const T &val, typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + return round(val, epsilon); + } + template + I round(const T &val, typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + return round(val, epsilon); + } + template + I round(const T &val, typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + return round(val, epsilon); + } + + // truncation + namespace Impl { + template + struct trunc_t; + template + struct trunc_t { + static I + trunc(const T &val, + typename EpsilonType::Type epsilon = (DefaultEpsilon::value())) { + // this should be optimized away unless needed + if(!std::numeric_limits::is_signed) + // make sure this works for all useful cases even if I is an unsigned type + if(eq(val, T(0), epsilon)) return I(0); + // first get an approximation + I lower = I(val); // now |val-lower| < 1 + // make sure we're really lower in case the cast truncated to an unexpected direction + if(T(lower) > val) lower--; // now val-lower < 1 + // check whether lower + 1 is approximately val + if(eq(T(lower+1), val, epsilon)) + return lower+1; + else return lower; + } + }; + template + struct trunc_t { + static I + trunc(const T &val, + typename EpsilonType::Type epsilon = (DefaultEpsilon::value())) { + I upper = trunc_t::trunc(val, epsilon); + if(ne(T(upper), val, epsilon)) ++upper; + return upper; + } + }; + template + struct trunc_t { + static I + trunc(const T &val, + typename EpsilonType::Type epsilon = (DefaultEpsilon::value())) { + if(val > T(0)) return trunc_t::trunc(val, epsilon); + else return trunc_t::trunc(val, epsilon); + } + }; + template + struct trunc_t { + static I + trunc(const T &val, + typename EpsilonType::Type epsilon = (DefaultEpsilon::value())) { + if(val > T(0)) return trunc_t::trunc(val, epsilon); + else return trunc_t::trunc(val, epsilon); + } + }; + template + struct trunc_t, std::vector, cstyle, rstyle> { + static std::vector + trunc(const std::vector &val, + typename EpsilonType::Type epsilon = (DefaultEpsilon::value())) { + unsigned int size = val.size(); + std::vector res(size); + for(unsigned int i = 0; i < size; ++i) + res[i] = trunc_t::trunc(val[i], epsilon); + return res; + } + }; + template + struct trunc_t, Dune::FieldVector, cstyle, rstyle> { + static Dune::FieldVector + trunc(const Dune::FieldVector &val, + typename EpsilonType::Type epsilon = (DefaultEpsilon::value())) { + Dune::FieldVector res; + for(int i = 0; i < n; ++i) + res[i] = trunc_t::trunc(val[i], epsilon); + return res; + } + }; + } // namespace Impl + template + I trunc(const T &val, typename EpsilonType::Type epsilon /*= DefaultEpsilon::value()*/) + { + return Impl::trunc_t::trunc(val, epsilon); + } + template + I trunc(const T &val, typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + return trunc(val, epsilon); + } + template + I trunc(const T &val, typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + return trunc(val, epsilon); + } + template + I trunc(const T &val, typename EpsilonType::Type epsilon = DefaultEpsilon::value()) + { + return trunc(val, epsilon); + } + } //namespace Dune + + // oo interface + template + FloatCmpOps:: + FloatCmpOps(EpsilonType epsilon) : epsilon_(epsilon) {} + + + template + typename FloatCmpOps::EpsilonType + FloatCmpOps::epsilon() const + { + return epsilon_; + } + + template + void + FloatCmpOps::epsilon(EpsilonType epsilon__) + { + epsilon_ = epsilon__; + } + + + template + bool FloatCmpOps:: + eq(const ValueType &first, const ValueType &second) const + { + return Dune::FloatCmp::eq(first, second, epsilon_); + } + + template + bool FloatCmpOps:: + ne(const ValueType &first, const ValueType &second) const + { + return Dune::FloatCmp::ne(first, second, epsilon_); + } + + template + bool FloatCmpOps:: + gt(const ValueType &first, const ValueType &second) const + { + return Dune::FloatCmp::gt(first, second, epsilon_); + } + + template + bool FloatCmpOps:: + lt(const ValueType &first, const ValueType &second) const + { + return Dune::FloatCmp::lt(first, second, epsilon_); + } + + template + bool FloatCmpOps:: + ge(const ValueType &first, const ValueType &second) const + { + return Dune::FloatCmp::ge(first, second, epsilon_); + } + + template + bool FloatCmpOps:: + le(const ValueType &first, const ValueType &second) const + { + return Dune::FloatCmp::le(first, second, epsilon_); + } + + + template + template + I FloatCmpOps:: + round(const ValueType &val) const + { + return Dune::FloatCmp::round(val, epsilon_); + } + + template + template + I FloatCmpOps:: + trunc(const ValueType &val) const + { + return Dune::FloatCmp::trunc(val, epsilon_); + } + +} //namespace Dune diff --git a/dune/common/float_cmp.hh b/dune/common/float_cmp.hh new file mode 100644 index 0000000..3ac9067 --- /dev/null +++ b/dune/common/float_cmp.hh @@ -0,0 +1,385 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_FLOAT_CMP_HH +#define DUNE_COMMON_FLOAT_CMP_HH + +/** \file + * \brief Various ways to compare floating-point numbers + */ + +/** + @addtogroup FloatCmp + + @section How_to_compare How to compare floats + + When comparing floating point numbers for equality, one often faces the + problem that floating point operations are not always exact. For example on + i386 the expression + @code + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 == 2.0 + @endcode + evaluates to + @code + 1.99999999999999977796 == 2.00000000000000000000 + @endcode + which is false. One solution is to compare approximately, using an epsilon + which says how much deviation to accept. + + The most straightforward way of comparing is using an @em absolute epsilon. + This means comparison for equality is replaced by + @code + abs(first-second) <= epsilon + @endcode + This has a severe disadvantage: if you have an epsilon like 1e-10 but first + and second are of the magnitude 1e-15 everything will compare equal which is + certainly not what you want. This can be overcome by selecting an + appropriate epsilon. Nevertheless this method of comparing is not + recommended in general, and we will present a more robus method in the + next paragraph. + + There is another way of comparing approximately, using a @em relative + epsilon which is then scaled with first: + @code + abs(first-second) <= epsilon * abs(first) + @endcode + Of cource the comparison should be symmetric in first and second so we + cannot arbitrarily select either first or second to scale epsilon. The are + two symmetric variants, @em relative_weak + @code + abs(first-second) <= epsilon * max(abs(first), abs(second)) + @endcode + and @em relative_strong + @code + abs(first-second) <= epsilon * min(abs(first), abs(second)) + @endcode + Both variants are good, but in practice the relative_weak variant is + preferred. This is also the default variant. + + \note Although using a relative epsilon is better than using an absolute + epsilon, using a relative epsilon leads to problems if either first or + second equals 0. In principle the relative method can be combined + with an absolute method using an epsilon near the minimum + representable positive value, but this is not implemented here. + + There is a completely different way of comparing floats. Instead of giving + an epsilon, the programmer states how many representable value are allowed + between first and second. See the "Comparing using integers" section in + http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm + for more about that. + + @section Interface Interface + + To do the comparison, you can use the free functions @link + Dune::FloatCmp::eq eq()@endlink, @link Dune::FloatCmp::ne ne()@endlink, + @link Dune::FloatCmp::gt gt()@endlink, @link Dune::FloatCmp::lt + lt()@endlink, @link Dune::FloatCmp::ge ge()@endlink and @link + Dune::FloatCmp::le le()@endlink from the namespace Dune::FloatCmp. They + take the values to compare and optionally an epsilon, which defaults to 8 + times the machine epsilon (the difference between 1.0 and the smallest + representable value > 1.0) for relative comparisons, or simply 1e-6 for + absolute comparisons. The compare style can be given as an optional second + template parameter and defaults to relative_weak. + + You can also use the class Dune::FloatCmpOps which has @link + Dune::FloatCmpOps::eq eq()@endlink, @link Dune::FloatCmpOps::ne + ne()@endlink, @link Dune::FloatCmpOps::gt gt()@endlink, @link + Dune::FloatCmpOps::lt lt()@endlink, @link Dune::FloatCmpOps::ge ge()@endlink + and @link Dune::FloatCmpOps::le le()@endlink as member functions. In this + case the class encapsulates the epsilon and the comparison style (again the + defaults from the previous paragraph apply). This may be more convenient if + you write your own class utilizing floating point comparisons, and you want + the user of you class to specify epsilon and compare style. + */ + +//! Dune namespace +namespace Dune { + //! FloatCmp namespace + //! @ingroup FloatCmp + namespace FloatCmp { + // basic constants + //! How to compare + //! @ingroup FloatCmp + enum CmpStyle { + //! |a-b|/|a| <= epsilon || |a-b|/|b| <= epsilon + relativeWeak, + //! |a-b|/|a| <= epsilon && |a-b|/|b| <= epsilon + relativeStrong, + //! |a-b| <= epsilon + absolute, + //! the global default compare style (relative_weak) + defaultCmpStyle = relativeWeak + }; + //! How to round or truncate + //! @ingroup FloatCmp + enum RoundingStyle { + //! always round toward 0 + towardZero, + //! always round away from 0 + towardInf, + //! round toward \f$-\infty\f$ + downward, + //! round toward \f$+\infty\f$ + upward, + //! the global default rounding style (toward_zero) + defaultRoundingStyle = towardZero + }; + + template struct EpsilonType; + + //! mapping from a value type and a compare style to a default epsilon + /** + * @ingroup FloatCmp + * @tparam T The value type to map from + * @tparam style The compare style to map from + */ + template + struct DefaultEpsilon { + //! Returns the default epsilon for the given value type and compare style + static typename EpsilonType::Type value(); + }; + + // operations in functional style + + //! @addtogroup FloatCmp + //! @{ + + //! test for equality using epsilon + /** + * @tparam T Type of the values to compare + * @tparam style How to compare. This defaults to defaultCmpStyle. + * @param first left operand of equals operation + * @param second right operand of equals operation + * @param epsilon The epsilon to use in the comparison + */ + template + bool eq(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()); + //! test for inequality using epsilon + /** + * @tparam T Type of the values to compare + * @tparam style How to compare. This defaults to defaultCmpStyle. + * @param first left operand of not-equal operation + * @param second right operand of not-equal operation + * @param epsilon The epsilon to use in the comparison + * @return !eq(first, second, epsilon) + */ + template + bool ne(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()); + //! test if first greater than second + /** + * @tparam T Type of the values to compare + * @tparam style How to compare. This defaults to defaultCmpStyle. + * @param first left operand of greater-than operation + * @param second right operand of greater-than operation + * @param epsilon The epsilon to use in the comparison + * @return ne(first, second, epsilon) && first > second + * + * this is like first > second but the region that compares equal with an + * epsilon is excluded + */ + template + bool gt(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()); + //! test if first lesser than second + /** + * @tparam T Type of the values to compare + * @tparam style How to compare. This defaults to defaultCmpStyle. + * @param first left operand of less-than operation + * @param second right operand of less-than operation + * @param epsilon The epsilon to use in the comparison + * @return ne(first, second, epsilon) && first < second + * + * this is like first < second, but the region that compares equal with an + * epsilon is excluded + */ + template + bool lt(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()); + //! test if first greater or equal second + /** + * @tparam T Type of the values to compare + * @tparam style How to compare. This defaults to defaultCmpStyle. + * @param first left operand of greater-or-equals operation + * @param second right operand of greater-or-equals operation + * @param epsilon The epsilon to use in the comparison + * @return eq(first, second, epsilon) || first > second + * + * this is like first > second, but the region that compares equal with an + * epsilon is also included + */ + template + bool ge(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()); + //! test if first lesser or equal second + /** + * @tparam T Type of the values to compare + * @tparam style How to compare. This defaults to defaultCmpStyle. + * @param first left operand of less-or-equals operation + * @param second right operand of less-or-equals operation + * @param epsilon The epsilon to use in the comparison + * @return eq(first, second) || first < second + * + * this is like first < second, but the region that compares equal with an + * epsilon is also included + */ + template + bool le(const T &first, + const T &second, + typename EpsilonType::Type epsilon = DefaultEpsilon::value()); + + // rounding operations + //! round using epsilon + /** + * @tparam I The integral type to round to + * @tparam T Type of the value to round + * @tparam cstyle How to compare. This defaults to defaultCmpStyle. + * @tparam rstyle How to round. This defaults to defaultRoundingStyle. + * @param val The value to round + * @param epsilon The epsilon to use in comparisons + * @return The rounded value + * + * Round according to rstyle. If val is already near the mean of two + * adjacent integers in terms of epsilon, the result will be the rounded + * mean. + */ + template + I round(const T &val, typename EpsilonType::Type epsilon = DefaultEpsilon::value()); + // truncation + //! truncate using epsilon + /** + * @tparam I The integral type to truncate to + * @tparam T Type of the value to truncate + * @tparam cstyle How to compare. This defaults to defaultCmpStyle. + * @tparam rstyle How to truncate. This defaults to defaultRoundingStyle. + * @param val The value to truncate + * @param epsilon The epsilon to use in comparisons + * @return The truncated value + * + * Truncate according to rstyle. If val is already near an integer in + * terms of epsilon, the result will be that integer instead of the real + * truncated value. + */ + template + I trunc(const T &val, typename EpsilonType::Type epsilon = DefaultEpsilon::value()); + + //! @} + // group FloatCmp + } //namespace FloatCmp + + + // oo interface + //! Class encapsulating a default epsilon + /** + * @ingroup FloatCmp + * @tparam T Type of the values to compare + * @tparam cstyle_ How to compare + * @tparam rstyle_ How to round + */ + template + class FloatCmpOps { + typedef FloatCmp::CmpStyle CmpStyle; + typedef FloatCmp::RoundingStyle RoundingStyle; + + public: + // record template parameters + //! How comparisons are done + static const CmpStyle cstyle = cstyle_; + //! How rounding is done + static const RoundingStyle rstyle = rstyle_; + //! Type of the values to compare + typedef T ValueType; + //! Type of the epsilon. + /** + * May be different from the value type, for example for complex + */ + typedef typename FloatCmp::EpsilonType::Type EpsilonType; + + private: + EpsilonType epsilon_; + + typedef FloatCmp::DefaultEpsilon DefaultEpsilon; + + public: + //! construct an operations object + /** + * @param epsilon Use the specified epsilon for comparing + */ + FloatCmpOps(EpsilonType epsilon = DefaultEpsilon::value()); + + //! return the current epsilon + EpsilonType epsilon() const; + //! set new epsilon + void epsilon(EpsilonType epsilon__); + + //! test for equality using epsilon + bool eq(const ValueType &first, const ValueType &second) const; + //! test for inequality using epsilon + /** + * this is exactly !eq(first, second) + */ + bool ne(const ValueType &first, const ValueType &second) const; + //! test if first greater than second + /** + * this is exactly ne(first, second) && first > second, i.e. greater but + * the region that compares equal with an epsilon is excluded + */ + bool gt(const ValueType &first, const ValueType &second) const; + //! test if first lesser than second + /** + * this is exactly ne(first, second) && first < second, i.e. lesser but + * the region that compares equal with an epsilon is excluded + */ + bool lt(const ValueType &first, const ValueType &second) const; + //! test if first greater or equal second + /** + * this is exactly eq(first, second) || first > second, i.e. greater but + * the region that compares equal with an epsilon is also included + */ + bool ge(const ValueType &first, const ValueType &second) const; + //! test if first lesser or equal second + /** + * this is exactly eq(first, second) || first > second, i.e. lesser but + * the region that compares equal with an epsilon is also included + */ + bool le(const ValueType &first, const ValueType &second) const; + + //! round using epsilon + /** + * @tparam I The integral type to round to + * + * @param val The value to round + * + * Round according to rstyle. If val is already near the mean of two + * adjacent integers in terms of epsilon, the result will be the rounded + * mean. + */ + template + I round(const ValueType &val) const; + + //! truncate using epsilon + /** + * @tparam I The integral type to truncate to + * + * @param val The value to truncate + * + * Truncate according to rstyle. If val is already near an integer in + * terms of epsilon, the result will be that integer instead of the real + * truncated value. + */ + template + I trunc(const ValueType &val) const; + + }; + +} //namespace Dune + +#include "float_cmp.cc" + +#endif //DUNE_COMMON_FLOAT_CMP_HH diff --git a/dune/common/fmatrix.hh b/dune/common/fmatrix.hh new file mode 100644 index 0000000..c014a45 --- /dev/null +++ b/dune/common/fmatrix.hh @@ -0,0 +1,707 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_FMATRIX_HH +#define DUNE_FMATRIX_HH + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Dune +{ + + /** + @addtogroup DenseMatVec + @{ + */ + + /*! \file + + \brief Implements a matrix constructed from a given type + representing a field and compile-time given number of rows and columns. + */ + + template< class K, int ROWS, int COLS = ROWS > class FieldMatrix; + + template< class K, int ROWS, int COLS > + struct DenseMatVecTraits< FieldMatrix > + { + typedef FieldMatrix derived_type; + + // each row is implemented by a field vector + typedef FieldVector row_type; + + typedef row_type &row_reference; + typedef const row_type &const_row_reference; + + typedef std::array container_type; + typedef K value_type; + typedef typename container_type::size_type size_type; + }; + + template< class K, int ROWS, int COLS > + struct FieldTraits< FieldMatrix > + { + typedef typename FieldTraits::field_type field_type; + typedef typename FieldTraits::real_type real_type; + }; + + /** + @brief A dense n x m matrix. + + Matrices represent linear maps from a vector space V to a vector space W. + This class represents such a linear map by storing a two-dimensional + %array of numbers of a given field type K. The number of rows and + columns is given at compile time. + */ + template + class FieldMatrix : public DenseMatrix< FieldMatrix > + { + std::array< FieldVector, ROWS > _data; + typedef DenseMatrix< FieldMatrix > Base; + public: + + //! export size + enum { + //! The number of rows. + rows = ROWS, + //! The number of columns. + cols = COLS + }; + + typedef typename Base::size_type size_type; + typedef typename Base::row_type row_type; + + typedef typename Base::row_reference row_reference; + typedef typename Base::const_row_reference const_row_reference; + + //===== constructors + /** \brief Default constructor + */ + constexpr FieldMatrix() = default; + + /** \brief Constructor initializing the matrix from a list of vector + */ + FieldMatrix(std::initializer_list > const &l) { + assert(l.size() == rows); // Actually, this is not needed any more! + std::copy_n(l.begin(), std::min(static_cast(ROWS), + l.size()), + _data.begin()); + } + + template ::value>> + FieldMatrix(T const& rhs) + { + *this = rhs; + } + + using Base::operator=; + + //! copy assignment operator + FieldMatrix& operator=(const FieldMatrix&) = default; + + //! copy assignment from FieldMatrix over a different field + template + FieldMatrix& operator=(const FieldMatrix& x) + { + _data = x._data; + return *this; + } + + //! no copy assignment from FieldMatrix of different size + template + FieldMatrix& operator=(FieldMatrix const&) = delete; + + //! vector space addition -- two-argument version + template + friend auto operator+ ( const FieldMatrix& matrixA, + const FieldMatrix& matrixB) + { + FieldMatrix::PromotedType,ROWS,COLS> result; + + for (size_type i = 0; i < ROWS; ++i) + for (size_type j = 0; j < COLS; ++j) + result[i][j] = matrixA[i][j] + matrixB[i][j]; + + return result; + } + + //! vector space subtraction -- two-argument version + template + friend auto operator- ( const FieldMatrix& matrixA, + const FieldMatrix& matrixB) + { + FieldMatrix::PromotedType,ROWS,COLS> result; + + for (size_type i = 0; i < ROWS; ++i) + for (size_type j = 0; j < COLS; ++j) + result[i][j] = matrixA[i][j] - matrixB[i][j]; + + return result; + } + + //! vector space multiplication with scalar + template ::value, int> = 0> + friend auto operator* ( const FieldMatrix& matrix, Scalar scalar) + { + FieldMatrix::PromotedType,ROWS,COLS> result; + + for (size_type i = 0; i < ROWS; ++i) + for (size_type j = 0; j < COLS; ++j) + result[i][j] = matrix[i][j] * scalar; + + return result; + } + + //! vector space multiplication with scalar + template ::value, int> = 0> + friend auto operator* ( Scalar scalar, const FieldMatrix& matrix) + { + FieldMatrix::PromotedType,ROWS,COLS> result; + + for (size_type i = 0; i < ROWS; ++i) + for (size_type j = 0; j < COLS; ++j) + result[i][j] = scalar * matrix[i][j]; + + return result; + } + + //! vector space division by scalar + template ::value, int> = 0> + friend auto operator/ ( const FieldMatrix& matrix, Scalar scalar) + { + FieldMatrix::PromotedType,ROWS,COLS> result; + + for (size_type i = 0; i < ROWS; ++i) + for (size_type j = 0; j < COLS; ++j) + result[i][j] = matrix[i][j] / scalar; + + return result; + } + + /** \brief Matrix-matrix multiplication + */ + template + friend auto operator* ( const FieldMatrix& matrixA, + const FieldMatrix& matrixB) + { + FieldMatrix::PromotedType,ROWS,otherCols> result; + + for (size_type i = 0; i < matrixA.mat_rows(); ++i) + for (size_type j = 0; j < matrixB.mat_cols(); ++j) + { + result[i][j] = 0; + for (size_type k = 0; k < matrixA.mat_cols(); ++k) + result[i][j] += matrixA[i][k] * matrixB[k][j]; + } + + return result; + } + + //! Multiplies M from the left to this matrix, this matrix is not modified + template + FieldMatrix leftmultiplyany (const FieldMatrix& M) const + { + FieldMatrix C; + + for (size_type i=0; i + FieldMatrix& rightmultiply (const FieldMatrix& M) + { + static_assert(r == c, "Cannot rightmultiply with non-square matrix"); + static_assert(r == cols, "Size mismatch"); + FieldMatrix C(*this); + + for (size_type i=0; i + FieldMatrix rightmultiplyany (const FieldMatrix& M) const + { + FieldMatrix C; + + for (size_type i=0; i + class FieldMatrix : public DenseMatrix< FieldMatrix > + { + FieldVector _data; + typedef DenseMatrix< FieldMatrix > Base; + public: + // standard constructor and everything is sufficient ... + + //===== type definitions and constants + + //! The type used for index access and size operations + typedef typename Base::size_type size_type; + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain. + //! This is always one for this type. + blocklevel = 1 + }; + + typedef typename Base::row_type row_type; + + typedef typename Base::row_reference row_reference; + typedef typename Base::const_row_reference const_row_reference; + + //! export size + enum { + //! \brief The number of rows. + //! This is always one for this type. + rows = 1, + //! \brief The number of columns. + //! This is always one for this type. + cols = 1 + }; + + //===== constructors + /** \brief Default constructor + */ + constexpr FieldMatrix() = default; + + /** \brief Constructor initializing the matrix from a list of vector + */ + FieldMatrix(std::initializer_list> const &l) + { + std::copy_n(l.begin(), std::min(static_cast< std::size_t >( 1 ), l.size()), &_data); + } + + template ::value>> + FieldMatrix(T const& rhs) + { + *this = rhs; + } + + using Base::operator=; + + //! vector space addition -- two-argument version + template + friend auto operator+ ( const FieldMatrix& matrixA, + const FieldMatrix& matrixB) + { + return FieldMatrix::PromotedType,1,1>{matrixA[0][0] + matrixB[0][0]}; + } + + //! Binary addition when treating FieldMatrix like K + template ::value, int> = 0> + friend auto operator+ ( const FieldMatrix& matrix, + const Scalar& scalar) + { + return FieldMatrix::PromotedType,1,1>{matrix[0][0] + scalar}; + } + + //! Binary addition when treating FieldMatrix like K + template ::value, int> = 0> + friend auto operator+ ( const Scalar& scalar, + const FieldMatrix& matrix) + { + return FieldMatrix::PromotedType,1,1>{scalar + matrix[0][0]}; + } + + //! vector space subtraction -- two-argument version + template + friend auto operator- ( const FieldMatrix& matrixA, + const FieldMatrix& matrixB) + { + return FieldMatrix::PromotedType,1,1>{matrixA[0][0] - matrixB[0][0]}; + } + + //! Binary subtraction when treating FieldMatrix like K + template ::value, int> = 0> + friend auto operator- ( const FieldMatrix& matrix, + const Scalar& scalar) + { + return FieldMatrix::PromotedType,1,1>{matrix[0][0] - scalar}; + } + + //! Binary subtraction when treating FieldMatrix like K + template ::value, int> = 0> + friend auto operator- ( const Scalar& scalar, + const FieldMatrix& matrix) + { + return FieldMatrix::PromotedType,1,1>{scalar - matrix[0][0]}; + } + + //! vector space multiplication with scalar + template ::value, int> = 0> + friend auto operator* ( const FieldMatrix& matrix, Scalar scalar) + { + return FieldMatrix::PromotedType,1,1> {matrix[0][0] * scalar}; + } + + //! vector space multiplication with scalar + template ::value, int> = 0> + friend auto operator* ( Scalar scalar, const FieldMatrix& matrix) + { + return FieldMatrix::PromotedType,1,1> {scalar * matrix[0][0]}; + } + + //! vector space division by scalar + template ::value, int> = 0> + friend auto operator/ ( const FieldMatrix& matrix, Scalar scalar) + { + return FieldMatrix::PromotedType,1,1> {matrix[0][0] / scalar}; + } + + //===== solve + + /** \brief Matrix-matrix multiplication + */ + template + friend auto operator* ( const FieldMatrix& matrixA, + const FieldMatrix& matrixB) + { + FieldMatrix::PromotedType,1,otherCols> result; + + for (size_type j = 0; j < matrixB.mat_cols(); ++j) + result[0][j] = matrixA[0][0] * matrixB[0][j]; + + return result; + } + + //! Multiplies M from the left to this matrix, this matrix is not modified + template + FieldMatrix leftmultiplyany (const FieldMatrix& M) const + { + FieldMatrix C; + for (size_type j=0; j + FieldMatrix rightmultiplyany (const FieldMatrix& M) const + { + FieldMatrix C; + + for (size_type j=0; j + std::ostream& operator<< (std::ostream& s, const FieldMatrix& a) + { + s << a[0][0]; + return s; + } + +#endif // DOXYGEN + + namespace FMatrixHelp { + + //! invert scalar without changing the original matrix + template + static inline K invertMatrix (const FieldMatrix &matrix, FieldMatrix &inverse) + { + using real_type = typename FieldTraits::real_type; + inverse[0][0] = real_type(1.0)/matrix[0][0]; + return matrix[0][0]; + } + + //! invert scalar without changing the original matrix + template + static inline K invertMatrix_retTransposed (const FieldMatrix &matrix, FieldMatrix &inverse) + { + return invertMatrix(matrix,inverse); + } + + + //! invert 2x2 Matrix without changing the original matrix + template + static inline K invertMatrix (const FieldMatrix &matrix, FieldMatrix &inverse) + { + using real_type = typename FieldTraits::real_type; + // code generated by maple + K det = (matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0]); + K det_1 = real_type(1.0)/det; + inverse[0][0] = matrix[1][1] * det_1; + inverse[0][1] = - matrix[0][1] * det_1; + inverse[1][0] = - matrix[1][0] * det_1; + inverse[1][1] = matrix[0][0] * det_1; + return det; + } + + //! invert 2x2 Matrix without changing the original matrix + //! return transposed matrix + template + static inline K invertMatrix_retTransposed (const FieldMatrix &matrix, FieldMatrix &inverse) + { + using real_type = typename FieldTraits::real_type; + // code generated by maple + K det = (matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0]); + K det_1 = real_type(1.0)/det; + inverse[0][0] = matrix[1][1] * det_1; + inverse[1][0] = - matrix[0][1] * det_1; + inverse[0][1] = - matrix[1][0] * det_1; + inverse[1][1] = matrix[0][0] * det_1; + return det; + } + + //! invert 3x3 Matrix without changing the original matrix + template + static inline K invertMatrix (const FieldMatrix &matrix, FieldMatrix &inverse) + { + using real_type = typename FieldTraits::real_type; + // code generated by maple + K t4 = matrix[0][0] * matrix[1][1]; + K t6 = matrix[0][0] * matrix[1][2]; + K t8 = matrix[0][1] * matrix[1][0]; + K t10 = matrix[0][2] * matrix[1][0]; + K t12 = matrix[0][1] * matrix[2][0]; + K t14 = matrix[0][2] * matrix[2][0]; + + K det = (t4*matrix[2][2]-t6*matrix[2][1]-t8*matrix[2][2]+ + t10*matrix[2][1]+t12*matrix[1][2]-t14*matrix[1][1]); + K t17 = real_type(1.0)/det; + + inverse[0][0] = (matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1])*t17; + inverse[0][1] = -(matrix[0][1] * matrix[2][2] - matrix[0][2] * matrix[2][1])*t17; + inverse[0][2] = (matrix[0][1] * matrix[1][2] - matrix[0][2] * matrix[1][1])*t17; + inverse[1][0] = -(matrix[1][0] * matrix[2][2] - matrix[1][2] * matrix[2][0])*t17; + inverse[1][1] = (matrix[0][0] * matrix[2][2] - t14) * t17; + inverse[1][2] = -(t6-t10) * t17; + inverse[2][0] = (matrix[1][0] * matrix[2][1] - matrix[1][1] * matrix[2][0]) * t17; + inverse[2][1] = -(matrix[0][0] * matrix[2][1] - t12) * t17; + inverse[2][2] = (t4-t8) * t17; + + return det; + } + + //! invert 3x3 Matrix without changing the original matrix + template + static inline K invertMatrix_retTransposed (const FieldMatrix &matrix, FieldMatrix &inverse) + { + using real_type = typename FieldTraits::real_type; + // code generated by maple + K t4 = matrix[0][0] * matrix[1][1]; + K t6 = matrix[0][0] * matrix[1][2]; + K t8 = matrix[0][1] * matrix[1][0]; + K t10 = matrix[0][2] * matrix[1][0]; + K t12 = matrix[0][1] * matrix[2][0]; + K t14 = matrix[0][2] * matrix[2][0]; + + K det = (t4*matrix[2][2]-t6*matrix[2][1]-t8*matrix[2][2]+ + t10*matrix[2][1]+t12*matrix[1][2]-t14*matrix[1][1]); + K t17 = real_type(1.0)/det; + + inverse[0][0] = (matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1])*t17; + inverse[1][0] = -(matrix[0][1] * matrix[2][2] - matrix[0][2] * matrix[2][1])*t17; + inverse[2][0] = (matrix[0][1] * matrix[1][2] - matrix[0][2] * matrix[1][1])*t17; + inverse[0][1] = -(matrix[1][0] * matrix[2][2] - matrix[1][2] * matrix[2][0])*t17; + inverse[1][1] = (matrix[0][0] * matrix[2][2] - t14) * t17; + inverse[2][1] = -(t6-t10) * t17; + inverse[0][2] = (matrix[1][0] * matrix[2][1] - matrix[1][1] * matrix[2][0]) * t17; + inverse[1][2] = -(matrix[0][0] * matrix[2][1] - t12) * t17; + inverse[2][2] = (t4-t8) * t17; + + return det; + } + + //! calculates ret = A * B + template< class K, int m, int n, int p > + static inline void multMatrix ( const FieldMatrix< K, m, n > &A, + const FieldMatrix< K, n, p > &B, + FieldMatrix< K, m, p > &ret ) + { + typedef typename FieldMatrix< K, m, p > :: size_type size_type; + + for( size_type i = 0; i < m; ++i ) + { + for( size_type j = 0; j < p; ++j ) + { + ret[ i ][ j ] = K( 0 ); + for( size_type k = 0; k < n; ++k ) + ret[ i ][ j ] += A[ i ][ k ] * B[ k ][ j ]; + } + } + } + + //! calculates ret= A_t*A + template + static inline void multTransposedMatrix(const FieldMatrix &matrix, FieldMatrix& ret) + { + typedef typename FieldMatrix::size_type size_type; + + for(size_type i=0; i + static inline void multAssignTransposed( const FieldMatrix &matrix, const FieldVector & x, FieldVector & ret) + { + typedef typename FieldMatrix::size_type size_type; + + for(size_type i=0; i + static inline FieldVector mult(const FieldMatrix &matrix, const FieldVector & x) + { + FieldVector ret; + multAssign(matrix,x,ret); + return ret; + } + + //! calculates ret = matrix^T * x + template + static inline FieldVector multTransposed(const FieldMatrix &matrix, const FieldVector & x) + { + FieldVector ret; + multAssignTransposed( matrix, x, ret ); + return ret; + } + + } // end namespace FMatrixHelp + + /** @} end documentation */ + +} // end namespace + +#include "fmatrixev.hh" +#endif diff --git a/dune/common/fmatrixev.cc b/dune/common/fmatrixev.cc new file mode 100644 index 0000000..86f28c7 --- /dev/null +++ b/dune/common/fmatrixev.cc @@ -0,0 +1,264 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_FMATRIXEIGENVALUES_CC +#define DUNE_FMATRIXEIGENVALUES_CC + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include + +#if HAVE_LAPACK + +#ifdef LAPACK_NEEDS_UNDERLINE + #define LAPACK_MANGLE(name,NAME) name##_ +#else + #define LAPACK_MANGLE(name,NAME) name +#endif + +#define FC_FUNC LAPACK_MANGLE + +// symmetric matrices +#define DSYEV_FORTRAN FC_FUNC (dsyev, DSYEV) +#define SSYEV_FORTRAN FC_FUNC (ssyev, SSYEV) + +// nonsymmetric matrices +#define DGEEV_FORTRAN FC_FUNC (dgeev, DGEEV) +#define SGEEV_FORTRAN FC_FUNC (sgeev, SGEEV) + +// dsyev declaration (in liblapack) +extern "C" { + + /* + * + ** purpose + ** ======= + ** + ** xsyev computes all eigenvalues and, optionally, eigenvectors of a + ** BASE DATA TYPE symmetric matrix a. + ** + ** arguments + ** ========= + ** + ** jobz (input) char + ** = 'n': compute eigenvalues only; + ** = 'v': compute eigenvalues and eigenvectors. + ** + ** uplo (input) char + ** = 'u': upper triangle of a is stored; + ** = 'l': lower triangle of a is stored. + ** + ** n (input) long int + ** the order of the matrix a. n >= 0. + ** + ** a (input/output) BASE DATA TYPE array, dimension (lda, n) + ** on entry, the symmetric matrix a. if uplo = 'u', the + ** leading n-by-n upper triangular part of a contains the + ** upper triangular part of the matrix a. if uplo = 'l', + ** the leading n-by-n lower triangular part of a contains + ** the lower triangular part of the matrix a. + ** on exit, if jobz = 'v', then if info = 0, a contains the + ** orthonormal eigenvectors of the matrix a. + ** if jobz = 'n', then on exit the lower triangle (if uplo='l') + ** or the upper triangle (if uplo='u') of a, including the + ** diagonal, is destroyed. + ** + ** lda (input) long int + ** the leading dimension of the array a. lda >= max(1,n). + ** + ** w (output) BASE DATA TYPE array, dimension (n) + ** if info = 0, the eigenvalues in ascending order. + ** + ** work (workspace/output) DOUBLE PRECISION array, dimension (MAX(1,LWORK)) + ** On exit, if INFO = 0, WORK(1) returns the optimal LWORK. + ** + ** lwork (input) INTEGER + ** The length of the array WORK. LWORK >= max(1,3*N-1). + ** For optimal efficiency, LWORK >= (NB+2)*N, + ** where NB is the blocksize for DSYTRD returned by ILAENV. + ** + ** If LWORK = -1, then a workspace query is assumed; the routine + ** only calculates the optimal size of the WORK array, returns + ** this value as the first entry of the WORK array, and no error + ** message related to LWORK is issued by XERBLA. + ** + ** + ** info (output) long int + ** = 0: successful exit + ** < 0: if info = -i, the i-th argument had an illegal value + ** > 0: if info = i, the algorithm failed to converge; i + ** off-diagonal elements of an intermediate tridiagonal + ** form did not converge to zero. + ** + **/ + extern void DSYEV_FORTRAN(const char* jobz, const char* uplo, const long + int* n, double* a, const long int* lda, double* w, + double* work, const long int* lwork, long int* info); + extern void SSYEV_FORTRAN(const char* jobz, const char* uplo, const long + int* n, float* a, const long int* lda, float* w, + float* work, const long int* lwork, long int* info); + + /* + * + ** purpose + ** ======= + ** + ** xgeev computes for an N-by-N BASE DATA TYPE nonsymmetric matrix A, the + ** eigenvalues and, optionally, the left and/or right eigenvectors. + ** + ** The right eigenvector v(j) of A satisfies + ** A * v(j) = lambda(j) * v(j) + ** where lambda(j) is its eigenvalue. + ** The left eigenvector u(j) of A satisfies + ** u(j)**T * A = lambda(j) * u(j)**T + ** where u(j)**T denotes the transpose of u(j). + ** + ** The computed eigenvectors are normalized to have Euclidean norm + ** equal to 1 and largest component real. + ** + ** arguments + ** ========= + ** + ** jobvl (input) char + ** = 'n': left eigenvectors of a are not computed; + ** = 'v': left eigenvectors of a are computed. + ** + ** jobvr (input) char + ** = 'n': right eigenvectors of a are not computed; + ** = 'v': right eigenvectors of a are computed. + ** + ** n (input) long int + ** the order of the matrix v. v >= 0. + ** + ** a (input/output) BASE DATA TYPE array, dimension (lda,n) + ** on entry, the n-by-n matrix a. + ** on exit, a has been overwritten. + ** + ** lda (input) long int + ** the leading dimension of the array a. lda >= max(1,n). + ** + ** wr (output) BASE DATA TYPE array, dimension (n) + ** wi (output) BASE DATA TYPE array, dimension (n) + ** wr and wi contain the real and imaginary parts, + ** respectively, of the computed eigenvalues. complex + ** conjugate pairs of eigenvalues appear consecutively + ** with the eigenvalue having the positive imaginary part + ** first. + ** + ** vl (output) COMPLEX DATA TYPE array, dimension (ldvl,n) + ** if jobvl = 'v', the left eigenvectors u(j) are stored one + ** after another in the columns of vl, in the same order + ** as their eigenvalues. + ** if jobvl = 'n', vl is not referenced. + ** if the j-th eigenvalue is real, then u(j) = vl(:,j), + ** the j-th column of vl. + ** if the j-th and (j+1)-st eigenvalues form a complex + ** conjugate pair, then u(j) = vl(:,j) + i*vl(:,j+1) and + ** u(j+1) = vl(:,j) - i*vl(:,j+1). + ** + ** ldvl (input) long int + ** the leading dimension of the array vl. ldvl >= 1; if + ** jobvl = 'v', ldvl >= n. + ** + ** vr (output) COMPLEX DATA TYPE array, dimension (ldvr,n) + ** if jobvr = 'v', the right eigenvectors v(j) are stored one + ** after another in the columns of vr, in the same order + ** as their eigenvalues. + ** if jobvr = 'n', vr is not referenced. + ** if the j-th eigenvalue is real, then v(j) = vr(:,j), + ** the j-th column of vr. + ** if the j-th and (j+1)-st eigenvalues form a complex + ** conjugate pair, then v(j) = vr(:,j) + i*vr(:,j+1) and + ** v(j+1) = vr(:,j) - i*vr(:,j+1). + ** + ** ldvr (input) long int + ** the leading dimension of the array vr. ldvr >= 1; if + ** jobvr = 'v', ldvr >= n. + ** + ** work (workspace/output) BASE DATA TYPE array, dimension (max(1,lwork)) + ** on exit, if info = 0, work(1) returns the optimal lwork. + ** + ** lwork (input) long int + ** the dimension of the array work. lwork >= max(1,3*n), and + ** if jobvl = 'v' or jobvr = 'v', lwork >= 4*n. for good + ** performance, lwork must generally be larger. + ** + ** if lwork = -1, then a workspace query is assumed; the routine + ** only calculates the optimal size of the work array, returns + ** this value as the first entry of the work array, and no error + ** message related to lwork is issued by xerbla. + ** + ** info (output) long int + ** = 0: successful exit + ** < 0: if info = -i, the i-th argument had an illegal value. + ** > 0: if info = i, the qr algorithm failed to compute all the + ** eigenvalues, and no eigenvectors have been computed; + ** elements i+1:n of wr and wi contain eigenvalues which + ** have converged. + ** + **/ + + extern void DGEEV_FORTRAN(const char* jobvl, const char* jobvr, const long + int* n, double* a, const long int* lda, double* wr, double* wi, double* vl, + const long int* ldvl, double* vr, const long int* ldvr, double* work, + const long int* lwork, long int* info); + extern void SGEEV_FORTRAN(const char* jobvl, const char* jobvr, const long + int* n, float* a, const long int* lda, float* wr, float* wi, float* vl, + const long int* ldvl, float* vr, const long int* ldvr, float* work, + const long int* lwork, long int* info); + +} // end extern C + +namespace Dune { + + namespace FMatrixHelp { + + void eigenValuesLapackCall( + const char* jobz, const char* uplo, const long + int* n, double* a, const long int* lda, double* w, + double* work, const long int* lwork, long int* info) + { + // call LAPACK dsyev + DSYEV_FORTRAN(jobz, uplo, n, a, lda, w, work, lwork, info); + } + + void eigenValuesLapackCall( + const char* jobz, const char* uplo, const long + int* n, float* a, const long int* lda, float* w, + float* work, const long int* lwork, long int* info) + { + // call LAPACK dsyev + SSYEV_FORTRAN(jobz, uplo, n, a, lda, w, work, lwork, info); + } + + void eigenValuesNonsymLapackCall( + const char* jobvl, const char* jobvr, const long + int* n, double* a, const long int* lda, double* wr, double* wi, double* vl, + const long int* ldvl, double* vr, const long int* ldvr, double* work, + const long int* lwork, long int* info) + { + // call LAPACK dgeev + DGEEV_FORTRAN(jobvl, jobvr, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, + work, lwork, info); + } + + void eigenValuesNonsymLapackCall( + const char* jobvl, const char* jobvr, const long + int* n, float* a, const long int* lda, float* wr, float* wi, float* vl, + const long int* ldvl, float* vr, const long int* ldvr, float* work, + const long int* lwork, long int* info) + { + // call LAPACK dgeev + SGEEV_FORTRAN(jobvl, jobvr, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, + work, lwork, info); + } + } // end namespace FMatrixHelp +} // end namespace Dune + +#endif // #if HAVE_LAPACK +#endif // #ifndef DUNE_FMATRIXEIGENVALUES_CC diff --git a/dune/common/fmatrixev.hh b/dune/common/fmatrixev.hh new file mode 100644 index 0000000..b0bc21b --- /dev/null +++ b/dune/common/fmatrixev.hh @@ -0,0 +1,638 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_FMATRIXEIGENVALUES_HH +#define DUNE_FMATRIXEIGENVALUES_HH + +/** \file + * \brief Eigenvalue computations for the FieldMatrix class + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Dune { + + /** + @addtogroup DenseMatVec + @{ + */ + + namespace FMatrixHelp { + +#if HAVE_LAPACK + // defined in fmatrixev.cc + extern void eigenValuesLapackCall( + const char* jobz, const char* uplo, const long + int* n, double* a, const long int* lda, double* w, + double* work, const long int* lwork, long int* info); + + extern void eigenValuesNonsymLapackCall( + const char* jobvl, const char* jobvr, const long + int* n, double* a, const long int* lda, double* wr, double* wi, double* vl, + const long int* ldvl, double* vr, const long int* ldvr, double* work, + const long int* lwork, long int* info); + + extern void eigenValuesLapackCall( + const char* jobz, const char* uplo, const long + int* n, float* a, const long int* lda, float* w, + float* work, const long int* lwork, long int* info); + + extern void eigenValuesNonsymLapackCall( + const char* jobvl, const char* jobvr, const long + int* n, float* a, const long int* lda, float* wr, float* wi, float* vl, + const long int* ldvl, float* vr, const long int* ldvr, float* work, + const long int* lwork, long int* info); + +#endif + + namespace Impl { + //internal tag to activate/disable code for eigenvector calculation at compile time + enum Jobs { OnlyEigenvalues=0, EigenvaluesEigenvectors=1 }; + + //internal dummy used if only eigenvalues are to be calculated + template + using EVDummy = FieldMatrix; + + //compute the cross-product of two vectors + template + inline FieldVector crossProduct(const FieldVector& vec0, const FieldVector& vec1) { + return {vec0[1]*vec1[2] - vec0[2]*vec1[1], vec0[2]*vec1[0] - vec0[0]*vec1[2], vec0[0]*vec1[1] - vec0[1]*vec1[0]}; + } + + template + static void eigenValues2dImpl(const FieldMatrix& matrix, + FieldVector& eigenvalues) + { + using std::sqrt; + const K p = 0.5 * (matrix[0][0] + matrix [1][1]); + const K p2 = p - matrix[1][1]; + K q = p2 * p2 + matrix[1][0] * matrix[0][1]; + if( q < 0 && q > -1e-14 ) q = 0; + if (q < 0) + { + std::cout << matrix << std::endl; + // Complex eigenvalues are either caused by non-symmetric matrices or by round-off errors + DUNE_THROW(MathError, "Complex eigenvalue detected (which this implementation cannot handle)."); + } + + // get square root + q = sqrt(q); + + // store eigenvalues in ascending order + eigenvalues[0] = p - q; + eigenvalues[1] = p + q; + } + + /* + This implementation was adapted from the pseudo-code (Python?) implementation found on + http://en.wikipedia.org/wiki/Eigenvalue_algorithm (retrieved late August 2014). + Wikipedia claims to have taken it from + Smith, Oliver K. (April 1961), Eigenvalues of a symmetric 3 × 3 matrix., + Communications of the ACM 4 (4): 168, doi:10.1145/355578.366316 + */ + template + static K eigenValues3dImpl(const FieldMatrix& matrix, + FieldVector& eigenvalues) + { + using std::sqrt; + using std::acos; + using real_type = typename FieldTraits::real_type; + const K pi = MathematicalConstants::pi(); + K p1 = matrix[0][1]*matrix[0][1] + matrix[0][2]*matrix[0][2] + matrix[1][2]*matrix[1][2]; + + if (p1 <= std::numeric_limits::epsilon()) { + // A is diagonal. + eigenvalues[0] = matrix[0][0]; + eigenvalues[1] = matrix[1][1]; + eigenvalues[2] = matrix[2][2]; + std::sort(eigenvalues.begin(), eigenvalues.end()); + + return 0.0; + } + else + { + // q = trace(A)/3 + K q = 0; + for (int i=0; i<3; i++) + q += matrix[i][i] / 3.0; + + K p2 = (matrix[0][0] - q)*(matrix[0][0] - q) + (matrix[1][1] - q)*(matrix[1][1] - q) + (matrix[2][2] - q)*(matrix[2][2] - q) + 2.0 * p1; + K p = sqrt(p2 / 6); + // B = (1 / p) * (A - q * I); // I is the identity matrix + FieldMatrix B; + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + B[i][j] = (real_type(1.0)/p) * (matrix[i][j] - q*(i==j)); + + K r = B.determinant() / 2.0; + + /*In exact arithmetic for a symmetric matrix -1 <= r <= 1 + but computation error can leave it slightly outside this range. + acos(z) function requires |z| <= 1, but will fail silently + and return NaN if the input is larger than 1 in magnitude. + Thus r is clamped to [-1,1].*/ + r = std::min(std::max(r, -1.0), 1.0); + K phi = acos(r) / 3.0; + + // the eigenvalues satisfy eig[2] <= eig[1] <= eig[0] + eigenvalues[2] = q + 2 * p * cos(phi); + eigenvalues[0] = q + 2 * p * cos(phi + (2*pi/3)); + eigenvalues[1] = 3 * q - eigenvalues[0] - eigenvalues[2]; // since trace(matrix) = eig1 + eig2 + eig3 + + return r; + } + } + + //see https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf + //Robustly compute a right-handed orthonormal set {u, v, evec0}. + template + void orthoComp(const FieldVector& evec0, FieldVector& u, FieldVector& v) { + if(std::abs(evec0[0]) > std::abs(evec0[1])) { + //The component of maximum absolute value is either evec0[0] or evec0[2]. + FieldVector temp = {evec0[0], evec0[2]}; + auto L = 1.0 / temp.two_norm(); + u = L * FieldVector({-evec0[2], 0.0, evec0[0]}); + } + else { + //The component of maximum absolute value is either evec0[1] or evec0[2]. + FieldVector temp = {evec0[1], evec0[2]}; + auto L = 1.0 / temp.two_norm(); + u = L * FieldVector({0.0, evec0[2], -evec0[1]}); + } + v = crossProduct(evec0, u); + } + + //see https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf + template + void eig0(const FieldMatrix& matrix, K eval0, FieldVector& evec0) { + /* Compute a unit-length eigenvector for eigenvalue[i0]. The + matrix is rank 2, so two of the rows are linearly independent. + For a robust computation of the eigenvector, select the two + rows whose cross product has largest length of all pairs of + rows. */ + using Vector = FieldVector; + Vector row0 = {matrix[0][0]-eval0, matrix[0][1], matrix[0][2]}; + Vector row1 = {matrix[1][0], matrix[1][1]-eval0, matrix[1][2]}; + Vector row2 = {matrix[2][0], matrix[2][1], matrix[2][2]-eval0}; + + Vector r0xr1 = crossProduct(row0, row1); + Vector r0xr2 = crossProduct(row0, row2); + Vector r1xr2 = crossProduct(row1, row2); + auto d0 = r0xr1.two_norm(); + auto d1 = r0xr2.two_norm(); + auto d2 = r1xr2.two_norm(); + + auto dmax = d0 ; + int imax = 0; + if(d1>dmax) { + dmax = d1; + imax = 1; + } + if(d2>dmax) + imax = 2; + + if(imax == 0) + evec0 = r0xr1 / d0; + else if(imax == 1) + evec0 = r0xr2 / d1; + else + evec0 = r1xr2 / d2; + } + + //see https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf + template + void eig1(const FieldMatrix& matrix, const FieldVector& evec0, FieldVector& evec1, K eval1) { + using Vector = FieldVector; + + //Robustly compute a right-handed orthonormal set {u, v, evec0}. + Vector u,v; + orthoComp(evec0, u, v); + + /* Let e be eval1 and let E be a corresponding eigenvector which + is a solution to the linear system (A - e*I)*E = 0. The matrix + (A - e*I) is 3x3, not invertible (so infinitely many + solutions), and has rank 2 when eval1 and eval are different. + It has rank 1 when eval1 and eval2 are equal. Numerically, it + is difficult to compute robustly the rank of a matrix. Instead, + the 3x3 linear system is reduced to a 2x2 system as follows. + Define the 3x2 matrix J = [u,v] whose columns are the u and v + computed previously. Define the 2x1 vector X = J*E. The 2x2 + system is 0 = M * X = (J^T * (A - e*I) * J) * X where J^T is + the transpose of J and M = J^T * (A - e*I) * J is a 2x2 matrix. + The system may be written as + +- -++- -+ +- -+ + | U^T*A*U - e U^T*A*V || x0 | = e * | x0 | + | V^T*A*U V^T*A*V - e || x1 | | x1 | + +- -++ -+ +- -+ + where X has row entries x0 and x1. */ + + Vector Au, Av; + matrix.mv(u, Au); + matrix.mv(v, Av); + + auto m00 = u.dot(Au) - eval1; + auto m01 = u.dot(Av); + auto m11 = v.dot(Av) - eval1; + + /* For robustness, choose the largest-length row of M to compute + the eigenvector. The 2-tuple of coefficients of U and V in the + assignments to eigenvector[1] lies on a circle, and U and V are + unit length and perpendicular, so eigenvector[1] is unit length + (within numerical tolerance). */ + auto absM00 = std::abs(m00); + auto absM01 = std::abs(m01); + auto absM11 = std::abs(m11); + if(absM00 >= absM11) { + auto maxAbsComp = std::max(absM00, absM01); + if(maxAbsComp > 0.0) { + if(absM00 >= absM01) { + m01 /= m00; + m00 = 1.0 / std::sqrt(1.0 + m01*m01); + m01 *= m00; + } + else { + m00 /= m01; + m01 = 1.0 / std::sqrt(1.0 + m00*m00); + m00 *= m01; + } + evec1 = m01*u - m00*v; + } + else + evec1 = u; + } + else { + auto maxAbsComp = std::max(absM11, absM01); + if(maxAbsComp > 0.0) { + if(absM11 >= absM01) { + m01 /= m11; + m11 = 1.0 / std::sqrt(1.0 + m01*m01); + m01 *= m11; + } + else { + m11 /= m01; + m01 = 1.0 / std::sqrt(1.0 + m11*m11); + m11 *= m01; + } + evec1 = m11*u - m01*v; + } + else + evec1 = u; + } + } + + // 1d specialization + template + static void eigenValuesVectorsImpl(const FieldMatrix& matrix, + FieldVector& eigenValues, + FieldMatrix& eigenVectors) + { + eigenValues[0] = matrix[0][0]; + if constexpr(Tag==EigenvaluesEigenvectors) + eigenVectors[0] = {1.0}; + } + + + // 2d specialization + template + static void eigenValuesVectorsImpl(const FieldMatrix& matrix, + FieldVector& eigenValues, + FieldMatrix& eigenVectors) + { + // Compute eigen values + Impl::eigenValues2dImpl(matrix, eigenValues); + + // Compute eigenvectors by exploiting the Cayley–Hamilton theorem. + // If λ_1, λ_2 are the eigenvalues, then (A - λ_1I )(A - λ_2I ) = (A - λ_2I )(A - λ_1I ) = 0, + // so the columns of (A - λ_2I ) are annihilated by (A - λ_1I ) and vice versa. + // Assuming neither matrix is zero, the columns of each must include eigenvectors + // for the other eigenvalue. (If either matrix is zero, then A is a multiple of the + // identity and any non-zero vector is an eigenvector.) + // From: https://en.wikipedia.org/wiki/Eigenvalue_algorithm#2x2_matrices + if constexpr(Tag==EigenvaluesEigenvectors) { + + // Special casing for multiples of the identity + FieldMatrix temp = matrix; + temp[0][0] -= eigenValues[0]; + temp[1][1] -= eigenValues[0]; + if(temp.infinity_norm() <= 1e-14) { + eigenVectors[0] = {1.0, 0.0}; + eigenVectors[1] = {0.0, 1.0}; + } + else { + // The columns of A - λ_2I are eigenvectors for λ_1, or zero. + // Take the column with the larger norm to avoid zero columns. + FieldVector ev0 = {matrix[0][0]-eigenValues[1], matrix[1][0]}; + FieldVector ev1 = {matrix[0][1], matrix[1][1]-eigenValues[1]}; + eigenVectors[0] = (ev0.two_norm2() >= ev1.two_norm2()) ? ev0/ev0.two_norm() : ev1/ev1.two_norm(); + + // The columns of A - λ_1I are eigenvectors for λ_2, or zero. + // Take the column with the larger norm to avoid zero columns. + ev0 = {matrix[0][0]-eigenValues[0], matrix[1][0]}; + ev1 = {matrix[0][1], matrix[1][1]-eigenValues[0]}; + eigenVectors[1] = (ev0.two_norm2() >= ev1.two_norm2()) ? ev0/ev0.two_norm() : ev1/ev1.two_norm(); + } + } + } + + // 3d specialization + template + static void eigenValuesVectorsImpl(const FieldMatrix& matrix, + FieldVector& eigenValues, + FieldMatrix& eigenVectors) + { + using Vector = FieldVector; + using Matrix = FieldMatrix; + + //compute eigenvalues + /* Precondition the matrix by factoring out the maximum absolute + value of the components. This guards against floating-point + overflow when computing the eigenvalues.*/ + using std::isnormal; + K maxAbsElement = (isnormal(matrix.infinity_norm())) ? matrix.infinity_norm() : K(1.0); + Matrix scaledMatrix = matrix / maxAbsElement; + K r = Impl::eigenValues3dImpl(scaledMatrix, eigenValues); + + if constexpr(Tag==EigenvaluesEigenvectors) { + K offDiagNorm = Vector{scaledMatrix[0][1],scaledMatrix[0][2],scaledMatrix[1][2]}.two_norm2(); + if (offDiagNorm <= std::numeric_limits::epsilon()) + { + eigenValues = {scaledMatrix[0][0], scaledMatrix[1][1], scaledMatrix[2][2]}; + eigenVectors = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}}; + + // Use bubble sort to jointly sort eigenvalues and eigenvectors + // such that eigenvalues are ascending + if (eigenValues[0] > eigenValues[1]) + { + std::swap(eigenValues[0], eigenValues[1]); + std::swap(eigenVectors[0], eigenVectors[1]); + } + if (eigenValues[1] > eigenValues[2]) + { + std::swap(eigenValues[1], eigenValues[2]); + std::swap(eigenVectors[1], eigenVectors[2]); + } + if (eigenValues[0] > eigenValues[1]) + { + std::swap(eigenValues[0], eigenValues[1]); + std::swap(eigenVectors[0], eigenVectors[1]); + } + } + else { + /*Compute the eigenvectors so that the set + [evec[0], evec[1], evec[2]] is right handed and + orthonormal. */ + + Matrix evec(0.0); + Vector eval(eigenValues); + if(r >= 0) { + Impl::eig0(scaledMatrix, eval[2], evec[2]); + Impl::eig1(scaledMatrix, evec[2], evec[1], eval[1]); + evec[0] = Impl::crossProduct(evec[1], evec[2]); + } + else { + Impl::eig0(scaledMatrix, eval[0], evec[0]); + Impl::eig1(scaledMatrix, evec[0], evec[1], eval[1]); + evec[2] = Impl::crossProduct(evec[0], evec[1]); + } + //sort eval/evec-pairs in ascending order + using EVPair = std::pair; + std::vector pairs; + for(std::size_t i=0; i<=2; ++i) + pairs.push_back(EVPair(eval[i], evec[i])); + auto comp = [](EVPair x, EVPair y){ return x.first < y.first; }; + std::sort(pairs.begin(), pairs.end(), comp); + for(std::size_t i=0; i<=2; ++i){ + eigenValues[i] = pairs[i].first; + eigenVectors[i] = pairs[i].second; + } + } + } + //The preconditioning scaled the matrix, which scales the eigenvalues. Revert the scaling. + eigenValues *= maxAbsElement; + } + + // forwarding to LAPACK with corresponding tag + template + static void eigenValuesVectorsLapackImpl(const FieldMatrix& matrix, + FieldVector& eigenValues, + FieldMatrix& eigenVectors) + { + { +#if HAVE_LAPACK + /*Lapack uses a proprietary tag to determine whether both eigenvalues and + -vectors ('v') or only eigenvalues ('n') should be calculated */ + const char jobz = "nv"[Tag]; + + const long int N = dim ; + const char uplo = 'u'; // use upper triangular matrix + + // length of matrix vector, LWORK >= max(1,3*N-1) + const long int lwork = 3*N -1 ; + + constexpr bool isKLapackType = std::is_same_v || std::is_same_v; + using LapackNumType = std::conditional_t; + + // matrix to put into dsyev + LapackNumType matrixVector[dim * dim]; + + // copy matrix + int row = 0; + for(int i=0; i + static void eigenValuesVectorsImpl(const FieldMatrix& matrix, + FieldVector& eigenValues, + FieldMatrix& eigenVectors) + { + eigenValuesVectorsLapackImpl(matrix,eigenValues,eigenVectors); + } + } //namespace Impl + + /** \brief calculates the eigenvalues of a symmetric field matrix + \param[in] matrix matrix eigenvalues are calculated for + \param[out] eigenValues FieldVector that contains eigenvalues in + ascending order + + \note specializations for dim=1,2,3 exist, for dim>3 LAPACK::dsyev is used + */ + template + static void eigenValues(const FieldMatrix& matrix, + FieldVector& eigenValues) + { + Impl::EVDummy dummy; + Impl::eigenValuesVectorsImpl(matrix, eigenValues, dummy); + } + + /** \brief calculates the eigenvalues and eigenvectors of a symmetric field matrix + \param[in] matrix matrix eigenvalues are calculated for + \param[out] eigenValues FieldVector that contains eigenvalues in + ascending order + \param[out] eigenVectors FieldMatrix that contains the eigenvectors + + \note specializations for dim=1,2,3 exist, for dim>3 LAPACK::dsyev is used + */ + template + static void eigenValuesVectors(const FieldMatrix& matrix, + FieldVector& eigenValues, + FieldMatrix& eigenVectors) + { + Impl::eigenValuesVectorsImpl(matrix, eigenValues, eigenVectors); + } + + /** \brief calculates the eigenvalues of a symmetric field matrix + \param[in] matrix matrix eigenvalues are calculated for + \param[out] eigenValues FieldVector that contains eigenvalues in + ascending order + + \note LAPACK::dsyev is used to calculate the eigenvalues + */ + template + static void eigenValuesLapack(const FieldMatrix& matrix, + FieldVector& eigenValues) + { + Impl::EVDummy dummy; + Impl::eigenValuesVectorsLapackImpl(matrix, eigenValues, dummy); + } + + /** \brief calculates the eigenvalues and -vectors of a symmetric field matrix + \param[in] matrix matrix eigenvalues are calculated for + \param[out] eigenValues FieldVector that contains eigenvalues in + ascending order + \param[out] eigenVectors FieldMatrix that contains the eigenvectors + + \note LAPACK::dsyev is used to calculate the eigenvalues and -vectors + */ + template + static void eigenValuesVectorsLapack(const FieldMatrix& matrix, + FieldVector& eigenValues, + FieldMatrix& eigenVectors) + { + Impl::eigenValuesVectorsLapackImpl(matrix, eigenValues, eigenVectors); + } + + /** \brief calculates the eigenvalues of a non-symmetric field matrix + \param[in] matrix matrix eigenvalues are calculated for + \param[out] eigenValues FieldVector that contains eigenvalues in + ascending order + + \note LAPACK::dgeev is used to calculate the eigenvalues + */ + template + static void eigenValuesNonSym(const FieldMatrix& matrix, + FieldVector& eigenValues) + { +#if HAVE_LAPACK + { + const long int N = dim ; + const char jobvl = 'n'; + const char jobvr = 'n'; + + constexpr bool isKLapackType = std::is_same_v || std::is_same_v; + using LapackNumType = std::conditional_t; + + // matrix to put into dgeev + LapackNumType matrixVector[dim * dim]; + + // copy matrix + int row = 0; + for(int i=0; i +#include + +namespace Dune { + + /** + @addtogroup DenseMatVec + \brief Type traits to retrieve the field and the real type of classes + + Type traits to retrieve the field and the real type of classes + e.g. that of FieldVector or FieldMatrix + */ + template + struct FieldTraits + { + //! export the type representing the field + typedef T field_type; + //! export the type representing the real type of the field + typedef T real_type; + }; + + template + struct FieldTraits + { + typedef typename FieldTraits::field_type field_type; + typedef typename FieldTraits::real_type real_type; + }; + + template + struct FieldTraits< std::complex > + { + typedef std::complex field_type; + typedef T real_type; + }; + + template + struct FieldTraits< T[N] > + { + typedef typename FieldTraits::field_type field_type; + typedef typename FieldTraits::real_type real_type; + }; + + template + struct FieldTraits< std::vector > + { + typedef typename FieldTraits::field_type field_type; + typedef typename FieldTraits::real_type real_type; + }; + +} // end namespace Dune + +#endif // DUNE_FTRAITS_HH diff --git a/dune/common/function.hh b/dune/common/function.hh new file mode 100644 index 0000000..496bc76 --- /dev/null +++ b/dune/common/function.hh @@ -0,0 +1,160 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_FUNCTION_HH_SILENCE_DEPRECATION +#warning This file is deprecated after Dune 2.7! Use C++ function objects and std::function stuff instead! +#else // !DUNE_FUNCTION_HH_SILENCE_DEPRECATION +#undef DUNE_FUNCTION_HH_SILENCE_DEPRECATION +#endif // !DUNE_FUNCTION_HH_SILENCE_DEPRECATION + +#ifndef DUNE_FUNCTION_HH +#define DUNE_FUNCTION_HH + +#include + +#include +#include "typetraits.hh" + +namespace Dune { + + /** @addtogroup Common + @{ + */ + + /*! \file + \brief Simple base class templates for functions. + */ + + /** + * \brief Base class template for function classes + * + * \tparam Domain Type of input variable. This could be some 'const T' or 'const T&'. + * + * \tparam Range Type of output variable. This should be some non-const 'T&' to allow to return results. + */ + template + class + [[deprecated("Dune::Function is deprecated after Dune 2.7. Use C++ " + "function objects instead!")]] + Function + { + typedef typename std::remove_cv::type >::type RawDomainType; + typedef typename std::remove_cv::type >::type RawRangeType; + + public: + + //! Raw type of input variable with removed reference and constness + typedef RawRangeType RangeType; + + //! Raw type of output variable with removed reference and constness + typedef RawDomainType DomainType; + + //! Traits class containing raw types + struct Traits + { + typedef RawDomainType DomainType; + typedef RawRangeType RangeType; + }; + + /** + * \brief Function evaluation. + * + * \param x Argument for function evaluation. + * \param y Result of function evaluation. + */ + void evaluate(const typename Traits::DomainType& x, typename Traits::RangeType& y) const; + }; // end of Function class + + + + DUNE_NO_DEPRECATED_BEGIN + /** + * \brief Virtual base class template for function classes. + * + * \see makeVirtualFunction for a helper to convert lambda functions to + * `VirtualFunction` objects. + * + * \tparam DomainType The type of the input variable is 'const DomainType &' + * + * \tparam RangeType The type of the output variable is 'RangeType &' + */ + template + class + [[deprecated("Dune::VirtualFunction is deprecated after Dune 2.7. Use C++ " + "function objects and std::function instead!")]] + VirtualFunction : public Function + { + public: + typedef typename Function::Traits Traits; + + virtual ~VirtualFunction() {} + /** + * \brief Function evaluation. + * + * \param x Argument for function evaluation. + * \param y Result of function evaluation. + */ + virtual void evaluate(const typename Traits::DomainType& x, typename Traits::RangeType& y) const = 0; + }; // end of VirtualFunction class + DUNE_NO_DEPRECATED_END + + namespace Impl { + + DUNE_NO_DEPRECATED_BEGIN + template + class LambdaVirtualFunction final + : public VirtualFunction + { + public: + LambdaVirtualFunction(F&& f) + : f_(std::move(f)) + {} + + LambdaVirtualFunction(const F& f) + : f_(f) + {} + + void evaluate(const Domain& x, Range& y) const override + { + y = f_(x); + } + + private: + const F f_; + }; + DUNE_NO_DEPRECATED_END + + } /* namespace Impl */ + + /** + * \brief make `VirtualFunction` out of a function object + * + * This helper function wraps a function object into a class implementing + * the `VirtualFunction` interface. It allows for easy use of lambda + * expressions in places that expect a `VirtualFunction`: + \code + void doSomething(const VirtualFunction& f); + + auto f = makeVirtualFunction( + [](double x) { return x*x; }); + doSomething(f); + \endcode + * + * \returns object of a class derived from `VirtualFunction` + * + * \tparam Domain domain of the function + * \tparam Range range of the function + */ + template + [[deprecated("Dune::LambdaVirtualFunction is deprecated after Dune 2.7. " + "Use std::function instead!")]] + Impl::LambdaVirtualFunction< Domain, Range, std::decay_t > + makeVirtualFunction(F&& f) + { + return {std::forward(f)}; + } + + /** @} end documentation */ + +} // end namespace + +#endif diff --git a/dune/common/fvector.hh b/dune/common/fvector.hh new file mode 100644 index 0000000..ca889d7 --- /dev/null +++ b/dune/common/fvector.hh @@ -0,0 +1,630 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_FVECTOR_HH +#define DUNE_FVECTOR_HH + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "typetraits.hh" +#include "exceptions.hh" + +#include "ftraits.hh" +#include "densevector.hh" +#include "boundschecking.hh" + +#include +#include + +namespace Dune { + + /** @addtogroup DenseMatVec + @{ + */ + + /*! \file + * \brief Implements a vector constructed from a given type + representing a field and a compile-time given size. + */ + + template< class K, int SIZE > class FieldVector; + template< class K, int SIZE > + struct DenseMatVecTraits< FieldVector > + { + typedef FieldVector derived_type; + typedef std::array container_type; + typedef K value_type; + typedef typename container_type::size_type size_type; + }; + + template< class K, int SIZE > + struct FieldTraits< FieldVector > + { + typedef typename FieldTraits::field_type field_type; + typedef typename FieldTraits::real_type real_type; + }; + + /** + * @brief TMP to check the size of a DenseVectors statically, if possible. + * + * If the implementation type of C is a FieldVector, we statically check + * whether its dimension is SIZE. + * @tparam C The implementation of the other DenseVector + * @tparam SIZE The size we need assume. + */ + template + struct IsFieldVectorSizeCorrect + { + enum { + /** + *@param True if C is not of type FieldVector or its dimension + * is not equal SIZE. + */ + value = true + }; + }; + + template + struct IsFieldVectorSizeCorrect,SIZE> + { + enum {value = true}; + }; + + template + struct IsFieldVectorSizeCorrect,SIZE> + { + enum {value = false}; + }; + + + /** \brief vector space out of a tensor product of fields. + * + * \tparam K the field type (use float, double, complex, etc) + * \tparam SIZE number of components. + */ + template< class K, int SIZE > + class FieldVector : + public DenseVector< FieldVector > + { + std::array _data; + typedef DenseVector< FieldVector > Base; + public: + //! export size + enum { + //! The size of this vector. + dimension = SIZE + }; + + typedef typename Base::size_type size_type; + typedef typename Base::value_type value_type; + + /** \brief The type used for references to the vector entry */ + typedef value_type& reference; + + /** \brief The type used for const references to the vector entry */ + typedef const value_type& const_reference; + + //! Constructor making default-initialized vector + constexpr FieldVector() + : _data{{}} + {} + + //! Constructor making vector with identical coordinates + explicit FieldVector (const K& t) + { + std::fill(_data.begin(),_data.end(),t); + } + +#if __GNUC__ == 5 && !defined(__clang__) + // `... = default;` causes an internal compiler error on GCC 5.4 (Ubuntu 16.04) + //! copy constructor + FieldVector(const FieldVector& x) : _data(x._data) {} +#else + //! Copy constructor + FieldVector (const FieldVector&) = default; +#endif + + /** \brief Construct from a std::initializer_list */ + FieldVector (std::initializer_list const &l) + { + assert(l.size() == dimension);// Actually, this is not needed any more! + std::copy_n(l.begin(), std::min(static_cast(dimension), + l.size()), + _data.begin()); + } + + //! copy assignment operator + FieldVector& operator= (const FieldVector&) = default; + + template + FieldVector& operator= (const FieldVector& x) + { + std::copy_n(x.begin(), SIZE, _data.begin()); + return *this; + } + + template + FieldVector& operator=(const FieldVector&) = delete; + + /** + * \brief Copy constructor from a second vector of possibly different type + * + * If the DenseVector type of the this constructor's argument + * is implemented by a FieldVector, it is statically checked + * if it has the correct size. If this is not the case + * the constructor is removed from the overload set using SFINAE. + * + * \param[in] x A DenseVector with correct size. + * \param[in] dummy A void* dummy argument needed by SFINAE. + */ + template + FieldVector (const DenseVector & x, + [[maybe_unused]] typename std::enable_if::value>::type* dummy=0) + { + // do a run-time size check, for the case that x is not a FieldVector + assert(x.size() == SIZE); // Actually this is not needed any more! + std::copy_n(x.begin(), std::min(static_cast(SIZE),x.size()), _data.begin()); + } + + //! Constructor making vector with identical coordinates + template + explicit FieldVector (const FieldVector & x) + { + std::copy_n(x.begin(), SIZE, _data.begin()); + } + + template + explicit FieldVector(const FieldVector&) = delete; + + using Base::operator=; + + // make this thing a vector + static constexpr size_type size () { return SIZE; } + + K & operator[](size_type i) { + DUNE_ASSERT_BOUNDS(i < SIZE); + return _data[i]; + } + const K & operator[](size_type i) const { + DUNE_ASSERT_BOUNDS(i < SIZE); + return _data[i]; + } + + //! return pointer to underlying array + K* data() noexcept + { + return _data.data(); + } + + //! return pointer to underlying array + const K* data() const noexcept + { + return _data.data(); + } + + //! vector space multiplication with scalar + template ::value, int> = 0> + friend auto operator* ( const FieldVector& vector, Scalar scalar) + { + FieldVector::PromotedType,SIZE> result; + + for (size_type i = 0; i < vector.size(); ++i) + result[i] = vector[i] * scalar; + + return result; + } + + //! vector space multiplication with scalar + template ::value, int> = 0> + friend auto operator* ( Scalar scalar, const FieldVector& vector) + { + FieldVector::PromotedType,SIZE> result; + + for (size_type i = 0; i < vector.size(); ++i) + result[i] = scalar * vector[i]; + + return result; + } + + //! vector space division by scalar + template ::value, int> = 0> + friend auto operator/ ( const FieldVector& vector, Scalar scalar) + { + FieldVector::PromotedType,SIZE> result; + + for (size_type i = 0; i < vector.size(); ++i) + result[i] = vector[i] / scalar; + + return result; + } + + }; + + /** \brief Read a FieldVector from an input stream + * \relates FieldVector + * + * \note This operator is STL compliant, i.e., the content of v is only + * changed if the read operation is successful. + * + * \param[in] in std :: istream to read from + * \param[out] v FieldVector to be read + * + * \returns the input stream (in) + */ + template + inline std::istream &operator>> ( std::istream &in, + FieldVector &v ) + { + FieldVector w; + for( typename FieldVector::size_type i = 0; i < SIZE; ++i ) + in >> w[ i ]; + if(in) + v = w; + return in; + } + +#ifndef DOXYGEN + template< class K > + struct DenseMatVecTraits< FieldVector > + { + typedef FieldVector derived_type; + typedef K container_type; + typedef K value_type; + typedef size_t size_type; + }; + + /** \brief Vectors containing only one component + */ + template + class FieldVector : + public DenseVector< FieldVector > + { + K _data; + typedef DenseVector< FieldVector > Base; + public: + //! export size + enum { + //! The size of this vector. + dimension = 1 + }; + + typedef typename Base::size_type size_type; + + /** \brief The type used for references to the vector entry */ + typedef K& reference; + + /** \brief The type used for const references to the vector entry */ + typedef const K& const_reference; + + //===== construction + + /** \brief Default constructor */ + constexpr FieldVector () + : _data() + {} + + /** \brief Constructor with a given scalar */ + template::value && + ! std::is_base_of::field_type>, K + >::value + >::type + > + FieldVector (const T& k) : _data(k) {} + + //! Constructor from static vector of different type + template::value_type>::value, int> = 0> + FieldVector (const DenseVector & x) + { + static_assert(((bool)IsFieldVectorSizeCorrect::value), "FieldVectors do not match in dimension!"); + assert(x.size() == 1); + _data = x[0]; + } + + //! copy constructor + FieldVector(const FieldVector&) = default; + + //! copy assignment operator + FieldVector& operator=(const FieldVector&) = default; + + template + FieldVector& operator= (const FieldVector& other) + { + _data = other[0]; + return *this; + } + + template + FieldVector& operator=(const FieldVector&) = delete; + + /** \brief Construct from a std::initializer_list */ + FieldVector (std::initializer_list const &l) + { + assert(l.size() == 1); + _data = *l.begin(); + } + + //! Assignment operator for scalar + template::value && + ! std::is_base_of::field_type>, K + >::value + >::type + > + inline FieldVector& operator= (const T& k) + { + _data = k; + return *this; + } + + //===== forward methods to container + static constexpr size_type size () { return 1; } + + K & operator[]([[maybe_unused]] size_type i) + { + DUNE_ASSERT_BOUNDS(i == 0); + return _data; + } + const K & operator[]([[maybe_unused]] size_type i) const + { + DUNE_ASSERT_BOUNDS(i == 0); + return _data; + } + + //! return pointer to underlying array + K* data() noexcept + { + return &_data; + } + + //! return pointer to underlying array + const K* data() const noexcept + { + return &_data; + } + + //===== conversion operator + + /** \brief Conversion operator */ + operator K& () { return _data; } + + /** \brief Const conversion operator */ + operator const K& () const { return _data; } + }; + + /* ----- FV / FV ----- */ + /* mostly not necessary as these operations are already covered via the cast operator */ + + //! Binary compare, when using FieldVector like K + template + inline bool operator> (const FieldVector& a, const FieldVector& b) + { + return a[0]>b[0]; + } + + //! Binary compare, when using FieldVector like K + template + inline bool operator>= (const FieldVector& a, const FieldVector& b) + { + return a[0]>=b[0]; + } + + //! Binary compare, when using FieldVector like K + template + inline bool operator< (const FieldVector& a, const FieldVector& b) + { + return a[0] like K + template + inline bool operator<= (const FieldVector& a, const FieldVector& b) + { + return a[0]<=b[0]; + } + + /* ----- FV / scalar ----- */ + + //! Binary addition, when using FieldVector like K + template + inline FieldVector operator+ (const FieldVector& a, const K b) + { + return a[0]+b; + } + + //! Binary subtraction, when using FieldVector like K + template + inline FieldVector operator- (const FieldVector& a, const K b) + { + return a[0]-b; + } + + //! Binary multiplication, when using FieldVector like K + template + inline FieldVector operator* (const FieldVector& a, const K b) + { + return a[0]*b; + } + + //! Binary division, when using FieldVector like K + template + inline FieldVector operator/ (const FieldVector& a, const K b) + { + return a[0]/b; + } + + //! Binary compare, when using FieldVector like K + template + inline bool operator> (const FieldVector& a, const K b) + { + return a[0]>b; + } + + //! Binary compare, when using FieldVector like K + template + inline bool operator>= (const FieldVector& a, const K b) + { + return a[0]>=b; + } + + //! Binary compare, when using FieldVector like K + template + inline bool operator< (const FieldVector& a, const K b) + { + return a[0] like K + template + inline bool operator<= (const FieldVector& a, const K b) + { + return a[0]<=b; + } + + //! Binary compare, when using FieldVector like K + template + inline bool operator== (const FieldVector& a, const K b) + { + return a[0]==b; + } + + //! Binary compare, when using FieldVector like K + template + inline bool operator!= (const FieldVector& a, const K b) + { + return a[0]!=b; + } + + /* ----- scalar / FV ------ */ + + //! Binary addition, when using FieldVector like K + template + inline FieldVector operator+ (const K a, const FieldVector& b) + { + return a+b[0]; + } + + //! Binary subtraction, when using FieldVector like K + template + inline FieldVector operator- (const K a, const FieldVector& b) + { + return a-b[0]; + } + + //! Binary multiplication, when using FieldVector like K + template + inline FieldVector operator* (const K a, const FieldVector& b) + { + return a*b[0]; + } + + //! Binary division, when using FieldVector like K + template + inline FieldVector operator/ (const K a, const FieldVector& b) + { + return a/b[0]; + } + + //! Binary compare, when using FieldVector like K + template + inline bool operator> (const K a, const FieldVector& b) + { + return a>b[0]; + } + + //! Binary compare, when using FieldVector like K + template + inline bool operator>= (const K a, const FieldVector& b) + { + return a>=b[0]; + } + + //! Binary compare, when using FieldVector like K + template + inline bool operator< (const K a, const FieldVector& b) + { + return a like K + template + inline bool operator<= (const K a, const FieldVector& b) + { + return a<=b[0]; + } + + //! Binary compare, when using FieldVector like K + template + inline bool operator== (const K a, const FieldVector& b) + { + return a==b[0]; + } + + //! Binary compare, when using FieldVector like K + template + inline bool operator!= (const K a, const FieldVector& b) + { + return a!=b[0]; + } +#endif + + /* Overloads for common classification functions */ + namespace MathOverloads { + + // ! Returns whether all entries are finite + template + auto isFinite(const FieldVector &b, PriorityTag<2>, ADLTag) { + bool out = true; + for(int i=0; i + bool isInf(const FieldVector &b, PriorityTag<2>, ADLTag) { + bool out = false; + for(int i=0; i::value>> + bool isNaN(const FieldVector &b, PriorityTag<2>, ADLTag) { + bool out = false; + for(int i=0; i::value>> + bool isUnordered(const FieldVector &b, const FieldVector &c, + PriorityTag<2>, ADLTag) { + return Dune::isUnordered(b[0],c[0]); + } + } //MathOverloads + + /** @} end documentation */ + +} // end namespace + +#endif diff --git a/dune/common/gcd.hh b/dune/common/gcd.hh new file mode 100644 index 0000000..ae13dbc --- /dev/null +++ b/dune/common/gcd.hh @@ -0,0 +1,27 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_GCD_HH +#define DUNE_GCD_HH +#warning "This header is deprecated and will be removed after Dune release 2.8. Use std::gcd instead" + +#include + +namespace Dune +{ + /** + * @brief Calculator of the greatest common divisor. + */ + template + struct [[deprecated("Will be removed after Dune 2.8. Use std::gcd from instead!")]] Gcd + { + /** + * @brief The greatest common divisior of a and b. */ + constexpr static long value = std::gcd(a,b); + }; + + /** + * @} + */ +} + +#endif diff --git a/dune/common/genericiterator.hh b/dune/common/genericiterator.hh new file mode 100644 index 0000000..865b8ec --- /dev/null +++ b/dune/common/genericiterator.hh @@ -0,0 +1,278 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_GENERICITERATOR_HH +#define DUNE_GENERICITERATOR_HH + +#include +#include + +namespace Dune { + + /*! \defgroup GenericIterator GenericIterator + \ingroup IteratorFacades + + \brief Generic Iterator class for writing stl conformant iterators + for any container class with operator[] + + Using this template class you can create an iterator and a const_iterator + for any container class. + + Imagine you have SimpleContainer and would like to have an iterator. + All you have to do is provide operator[], begin() and end() + (for const and for non-const). + + \code + template + class SimpleContainer{ + public: + typedef GenericIterator,T> iterator; + + typedef GenericIterator,const T> const_iterator; + + SimpleContainer(){ + for(int i=0; i < 100; i++) + values_[i]=i; + } + + iterator begin(){ + return iterator(*this, 0); + } + + const_iterator begin() const{ + return const_iterator(*this, 0); + } + + iterator end(){ + return iterator(*this, 100); + } + + const_iterator end() const{ + return const_iterator(*this, 100); + } + + T& operator[](int i){ + return values_[i]; + } + + const T& operator[](int i) const{ + return values_[i]; + } + private: + T values_[100]; + }; + \endcode + + See dune/common/test/iteratorfacestest.hh for details or + Dune::QuadratureDefault in dune/quadrature/quadrature.hh + for a real example. + */ + + /** + * @file + * @brief Implements a generic iterator class for writing stl conformant iterators. + * + * Using this generic iterator writing iterators for containers + * that implement operator[] is only a matter of seconds. + */ + + /** + \brief Get the 'const' version of a reference to a mutable object + + Given a reference R=T& const_reference::type gives you the typedef for const T& + */ + template + struct const_reference + { + typedef const R type; + }; + + template + struct const_reference + { + typedef const R type; + }; + + template + struct const_reference + { + typedef const R& type; + }; + + template + struct const_reference + { + typedef const R& type; + }; + + /** + \brief get the 'mutable' version of a reference to a const object + + given a const reference R=const T& mutable_reference::type gives you the typedef for T& + */ + template + struct mutable_reference + { + typedef R type; + }; + + template + struct mutable_reference + { + typedef R type; + }; + + template + struct mutable_reference + { + typedef R& type; + }; + + template + struct mutable_reference + { + typedef R& type; + }; + + /** @addtogroup GenericIterator + * + * @{ + */ + + /** + * @brief Generic class for stl-conforming iterators for container classes with operator[]. + * + * If template parameter C has a const qualifier we are a const iterator, otherwise we + * are a mutable iterator. + */ + template class IteratorFacade=RandomAccessIteratorFacade> + class GenericIterator : + public IteratorFacade,T,R,D> + { + friend class GenericIterator::type, typename std::remove_const::type, typename mutable_reference::type, D, IteratorFacade>; + friend class GenericIterator::type, const typename std::remove_const::type, typename const_reference::type, D, IteratorFacade>; + + typedef GenericIterator::type, typename std::remove_const::type, typename mutable_reference::type, D, IteratorFacade> MutableIterator; + typedef GenericIterator::type, const typename std::remove_const::type, typename const_reference::type, D, IteratorFacade> ConstIterator; + + public: + + /** + * @brief The type of container we are an iterator for. + * + * The container type must provide an operator[] method. + * + * If C has a const qualifier we are a const iterator, otherwise we + * are a mutable iterator. + */ + typedef C Container; + + /** + * @brief The value type of the iterator. + * + * This is the return type when dereferencing the iterator. + */ + typedef T Value; + + /** + * @brief The type of the difference between two positions. + */ + typedef D DifferenceType; + + /** + * @brief The type of the reference to the values accessed. + */ + typedef R Reference; + + // Constructors needed by the base iterators + GenericIterator() : container_(0), position_(0) + {} + + /** + * @brief Constructor + * @param cont Reference to the container we are an iterator for + * @param pos The position the iterator will be positioned to + * (e.g. 0 for an iterator returned by Container::begin() or + * the size of the container for an iterator returned by Container::end() + */ + GenericIterator(Container& cont, DifferenceType pos) + : container_(&cont), position_(pos) + {} + + /** + * @brief Copy constructor + * + * This is somehow hard to understand, therefore play with the cases: + * 1. if we are mutable this is the only valid copy constructor, as the argument is a mutable iterator + * 2. if we are a const iterator the argument is a mutable iterator => This is the needed conversion to initialize a const iterator from a mutable one. + */ + GenericIterator(const MutableIterator& other) : container_(other.container_), position_(other.position_) + {} + + /** + * @brief Copy constructor + * + * @warning Calling this method results in a compiler error, if this is a mutable iterator. + * + * This is somehow hard to understand, therefore play with the cases: + * 1. if we are mutable the arguments is a const iterator and therefore calling this method is mistake in the user's code and results in a (probably not understandable) compiler error + * 2. If we are a const iterator this is the default copy constructor as the argument is a const iterator too. + */ + GenericIterator(const ConstIterator& other) : container_(other.container_), position_(other.position_) + {} + + // Methods needed by the forward iterator + bool equals(const MutableIterator & other) const + { + return position_ == other.position_ && container_ == other.container_; + } + + bool equals(const ConstIterator & other) const + { + return position_ == other.position_ && container_ == other.container_; + } + + Reference dereference() const { + return container_->operator[](position_); + } + + void increment(){ + ++position_; + } + + // Additional function needed by BidirectionalIterator + void decrement(){ + --position_; + } + + // Additional function needed by RandomAccessIterator + Reference elementAt(DifferenceType i) const { + return container_->operator[](position_+i); + } + + void advance(DifferenceType n){ + position_=position_+n; + } + + DifferenceType distanceTo(const MutableIterator& other) const + { + assert(other.container_==container_); + return other.position_ - position_; + } + + DifferenceType distanceTo(const ConstIterator& other) const + { + assert(other.container_==container_); + return other.position_ - position_; + } + + private: + Container *container_; + DifferenceType position_; + }; + + /** @} */ + +} // end namespace Dune + +#endif diff --git a/dune/common/gmpfield.hh b/dune/common/gmpfield.hh new file mode 100644 index 0000000..503da90 --- /dev/null +++ b/dune/common/gmpfield.hh @@ -0,0 +1,104 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_GMPFIELD_HH +#define DUNE_GMPFIELD_HH + +/** \file + * \brief Wrapper for the GNU multiprecision (GMP) library + */ + +#include +#include +#include + +#if HAVE_GMP || DOXYGEN + +#include + +#include +#include + +namespace Dune +{ + + /** + * \ingroup Numbers + * \brief Number class for high precision floating point number using the GMP library mpf_class implementation + */ + template< unsigned int precision > + class GMPField + : public mpf_class + { + typedef mpf_class Base; + + public: + /** default constructor, initialize to zero */ + GMPField () + : Base(0,precision) + {} + + /** \brief initialize from a string + \note this is the only reliable way to initialize with higher precision values + */ + GMPField ( const char* str ) + : Base(str,precision) + {} + + /** \brief initialize from a string + \note this is the only reliable way to initialize with higher precision values + */ + GMPField ( const std::string& str ) + : Base(str,precision) + {} + + /** \brief initialize from a compatible scalar type + */ + template< class T, + typename EnableIf = typename std::enable_if< + std::is_convertible::value>::type + > + GMPField ( const T &v ) + : Base( v,precision ) + {} + + // type conversion operators + operator double () const + { + return this->get_d(); + } + + }; + + template + struct IsNumber> + : public std::integral_constant { + }; + + template< unsigned int precision1, unsigned int precision2 > + struct PromotionTraits, GMPField> + { + typedef GMPField<(precision1 > precision2 ? precision1 : precision2)> PromotedType; + }; + + template< unsigned int precision > + struct PromotionTraits,GMPField> + { + typedef GMPField PromotedType; + }; + + template< unsigned int precision, class T > + struct PromotionTraits, T> + { + typedef GMPField PromotedType; + }; + + template< class T, unsigned int precision > + struct PromotionTraits> + { + typedef GMPField PromotedType; + }; +} + +#endif // HAVE_GMP + +#endif // #ifndef DUNE_GMPFIELD_HH diff --git a/dune/common/hash.hh b/dune/common/hash.hh new file mode 100644 index 0000000..7d35b55 --- /dev/null +++ b/dune/common/hash.hh @@ -0,0 +1,349 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_HASH_HH +#define DUNE_COMMON_HASH_HH + +#include + +#include + +/** + * \file + * \brief Support for calculating hash values of objects. + * + * This file provides the functor Dune::hash to calculate hash values and + * some infrastructure to simplify extending Dune::hash for user-defined types, + * independent of the actual underlying implementation. + * + */ + + + +// ******************************************************************************** +// Doxygen documentation +// ******************************************************************************** + +#ifdef DOXYGEN + +namespace Dune { + + //! Functor for hashing objects of type T. + /** + * The interface outlined below is compatible with std::hash, std::tr1::hash and + * boost::hash, so it is possible to use Dune::hash in associative containers from + * those libraries. + */ + template + struct hash + { + + //! Calculates the hash of t. + std::size_t operator()(const T& t) const + { + return hash(t); + } + + }; + +} + +//! Defines the required struct specialization to make type hashable via `Dune::hash`. +/** + * In order to calculate the hash, operator() of the generated specialization will + * return the result of an unqualified call to the global function `hash_value(const type&)`. + * As the call is not qualified, the function will be found using argument-dependent lookup, + * allowing implementors to conveniently place it inside the class body. + * + * Consider the following type: + * + * \code + * namespace ns { + * template + * class Foo + * { + * ... + * }; + * } + * \endcode + * + * In order to add support for `Dune::hash`, you need to extend the definition like this: + * + * \code + * namespace ns { + * template + * class Foo + * { + * ... + * // The keyword "friend" turns this into a global function that is a friend of Foo. + * inline friend std::size_t hash_value(const Foo& arg) + * { + * return ...; + * } + * }; + * } + * + * // Define hash struct specialization + * DUNE_DEFINE_HASH(DUNE_HASH_TEMPLATE_ARGS(typename A, int i),DUNE_HASH_TYPE(Foo)) + * \endcode + * + * \warning + * As the specialization has to be placed in the original namespace of the + * `hash` struct (e.g. `std`), this macro *must* be called from the global namespace! + * + * \param template_args The template arguments required by the hash struct specialization, + * wrapped in a call to DUNE_HASH_TEMPLATE_ARGS. If this is a complete + * specialization, call DUNE_HASH_TEMPLATE_ARGS without arguments. + * \param type The exact type of the specialization, wrapped in a call to DUNE_HASH_TYPE. + */ +#define DUNE_DEFINE_HASH(template_args,type) + + +//! Wrapper macro for the template arguments in DUNE_DEFINE_HASH. +/** + * This macro should always be used as a wrapper for the template arguments when calling DUNE_DEFINE_HASH. + * It works around some preprocessor limitations when the template arguments contain commas or the list + * is completely empty. + */ +#define DUNE_HASH_TEMPLATE_ARGS(...) + +//! Wrapper macro for the type to be hashed in DUNE_DEFINE_HASH. +/** + * This macro should always be used as a wrapper for the type of the specialization when calling + * DUNE_DEFINE_HASH. + * It works around some preprocessor limitations when the type contains commas. + */ +#define DUNE_HASH_TYPE(...) + +#else // DOXYGEN - hide all the ugly implementation + + + +// ******************************************************************************** +// C++11 support +// ******************************************************************************** + +// import std::hash into Dune namespace +namespace Dune { + + using std::hash; + +} + +// Macro for defining a std::hash specialization for type. +// This should not be called directly. Call DUNE_DEFINE_HASH +// instead. +#define DUNE_DEFINE_STD_HASH(template_args,type) \ + namespace std { \ + \ + template \ + struct hash \ + { \ + \ + typedef type argument_type; \ + typedef std::size_t result_type; \ + \ + std::size_t operator()(const type& arg) const \ + { \ + return hash_value(arg); \ + } \ + }; \ + \ + template \ + struct hash \ + { \ + \ + typedef type argument_type; \ + typedef std::size_t result_type; \ + \ + std::size_t operator()(const type& arg) const \ + { \ + return hash_value(arg); \ + } \ + }; \ + \ + } \ + +// Wrapper macro for template arguments. +// This is required because the template arguments can contain commas, +// which will create a macro argument list of unknown length. That in itself +// would not be a problem, but DUNE_DEFINE_HASH has to be called with two argument +// lists of unknown length. So this macro wraps its arguments with parentheses, +// turning it into a single argument. The result is used as the parameter list of +// an expansion macro in the calls to the implementation-specific macros +// for C++11 and TR1. Noto that technically, this trick is only legal for C++11, +// but pretty much every compiler supports variadic macros in C++03 mode, as they +// are part of C99. +#define DUNE_HASH_TEMPLATE_ARGS(...) (__VA_ARGS__) + +// Wrapper macro for type to be hashed. +// See above for rationale. +#define DUNE_HASH_TYPE(...) (__VA_ARGS__) + +// Expansion macro for the parenthesed argument lists created by +// DUNE_HASH_TEMPLATE_ARGS and DUNE_HASH_TYPE. +#define DUNE_HASH_EXPAND_VA_ARGS(...) __VA_ARGS__ + +// Define specializations for all discovered hash implementations. +#define DUNE_DEFINE_HASH(template_args,type) \ + DUNE_DEFINE_STD_HASH(DUNE_HASH_EXPAND_VA_ARGS template_args, DUNE_HASH_EXPAND_VA_ARGS type) \ + + +#endif // DOXYGEN + + + +// ******************************************************************************** +// Some utility functions for combining hashes of member variables. +// ******************************************************************************** + +namespace Dune { + + // The following functions are an implementation of the proposed hash extensions for + // the C++ standard by Peter Dimov + // (cf. http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf, issue 6.18). + // They are also contained in the boost::functional::hash library by Daniel James, but + // that implementation uses boost::hash internally, while we want to use Dune::hash. They + // are also considered for inclusion in TR2 (then based on std::hash, of course). + +#ifndef DOXYGEN + + // helper struct for providing different hash combining algorithms dependent on + // the size of size_t. + // hash_combiner has to be specialized for the size (in bytes) of std::size_t. + // Specialized versions should provide a method + // + // template + // void operator()(typeof_size_t& seed, const T& arg) const; + // + // that will be called by the interface function hash_combine() described further below. + // The redundant template parameter typeof_size_t is needed to avoid warnings for the + // unused 64-bit specialization on 32-bit systems. + // + // There is no default implementation! + template + struct hash_combiner; + + + // hash combining for 64-bit platforms. + template<> + struct hash_combiner<8> + { + + template + void operator()(typeof_size_t& seed, const T& arg) const + { + static_assert(sizeof(typeof_size_t)==8, "hash_combiner::operator() instantiated with nonmatching type and size"); + + // The following algorithm for combining two 64-bit hash values is inspired by a similar + // function in CityHash (http://cityhash.googlecode.com/svn-history/r2/trunk/src/city.h), + // which is in turn based on ideas from the MurmurHash library. The basic idea is easy to + // grasp, though: New information is XORed into the existing hash multiple times at different + // places (using shift operations), and the resulting pattern is spread over the complete + // range of available bits via multiplication with a "magic" constant. The constants used + // below (47 and 0x9ddfea08eb382d69ULL) are taken from the CityHash implementation. + // + // We opted not to use the mixing algorithm proposed in the C++ working group defect list at + // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf, p. 57f. because it + // has very bad hash distribution properties if you apply it to lists of very small numbers, + // an application that is frequent in PDELab's ordering framework. + + Dune::hash hasher; + const typeof_size_t kMul = 0x9ddfea08eb382d69ULL; + typeof_size_t h = hasher(arg); + typeof_size_t a = (seed ^ h) * kMul; + a ^= (a >> 47); + typeof_size_t b = (h ^ a) * kMul; + b ^= (b >> 47); + b *= kMul; + seed = b; + } + + }; + + + // hash combining for 32-bit platforms. + template<> + struct hash_combiner<4> + { + + template + void operator()(typeof_size_t& seed, const T& arg) const + { + static_assert(sizeof(typeof_size_t)==4, "hash_combiner::operator() instantiated with nonmatching type and size"); + + // The default algorithm above requires a 64-bit std::size_t. The following algorithm is a + // 32-bit compatible fallback, again inspired by CityHash and MurmurHash + // (http://cityhash.googlecode.com/svn-history/r2/trunk/src/city.cc). + // It uses 32-bit constants and relies on rotation instead of multiplication to spread the + // mixed bits as that is apparently more efficient on IA-32. The constants used below are again + // taken from CityHash, in particular from the file referenced above. + + Dune::hash hasher; + const typeof_size_t c1 = 0xcc9e2d51; + const typeof_size_t c2 = 0x1b873593; + const typeof_size_t c3 = 0xe6546b64; + typeof_size_t h = hasher(arg); + typeof_size_t a = seed * c1; + a = (a >> 17) | (a << (32 - 17)); + a *= c2; + h ^= a; + h = (h >> 19) | (h << (32 - 19)); + seed = h * 5 + c3; + } + + }; + +#endif // DOXYGEN + + //! Calculates the hash value of arg and combines it in-place with seed. + /** + * + * \param seed The hash value that will be combined with the hash of arg. + * \param arg The object for which to calculate a hash value and combine it with seed. + */ + template + inline void hash_combine(std::size_t& seed, const T& arg) + { + hash_combiner()(seed,arg); + } + + //! Hashes all elements in the range [first,last) and returns the combined hash. + /** + * + * \param first Iterator pointing to the first object to hash. + * \param last Iterator pointing one past the last object to hash. + + * \returns The result of hashing all objects in the range and combining them + * using hash_combine() in sequential fashion, starting with seed 0. + */ + template + inline std::size_t hash_range(It first, It last) + { + std::size_t seed = 0; + for (; first != last; ++first) + { + hash_combine(seed,*first); + } + return seed; + } + + //! Hashes all elements in the range [first,last) and combines the hashes in-place with seed. + /** + * + * \param seed Start value that will be combined with the hash values of all objects in + * the range using hash_combine() in sequential fashion. + * \param first Iterator pointing to the first ojbect to hash. + * \param last Iterator pointing one past the last object to hash. + */ + template + inline void hash_range(std::size_t& seed, It first, It last) + { + for (; first != last; ++first) + { + hash_combine(seed,*first); + } + } + +} // end namespace Dune + +#endif // DUNE_COMMON_HASH_HH diff --git a/dune/common/hybridutilities.hh b/dune/common/hybridutilities.hh new file mode 100644 index 0000000..facdd9f --- /dev/null +++ b/dune/common/hybridutilities.hh @@ -0,0 +1,495 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_HYBRIDUTILITIES_HH +#define DUNE_COMMON_HYBRIDUTILITIES_HH + +#include +#include + +#include +#include +#include +#include +#include +#include + + + +namespace Dune { +namespace Hybrid { + +namespace Impl { + + // Try if tuple_size is implemented for class + template + constexpr auto size(const Dune::FieldVector&, const PriorityTag<5>&) + -> decltype(std::integral_constant()) + { + return {}; + } + + // Try if tuple_size is implemented for class + template + constexpr auto size(const T&, const PriorityTag<3>&) + -> decltype(std::integral_constant::value>()) + { + return {}; + } + + // Try if there's a static constexpr size() + template + constexpr auto size(const T&, const PriorityTag<1>&) + -> decltype(std::integral_constant()) + { + return {}; + } + + // As a last resort try if there's a static constexpr size() + template + constexpr auto size(const T& t, const PriorityTag<0>&) + { + return t.size(); + } + +} // namespace Impl + + + +/** + * \brief Size query + * + * \ingroup HybridUtilities + * + * \tparam T Type of container whose size is queried + * + * \param t Container whose size is queried + * + * \return Size of t + * + * If the size of t is known at compile type the size is + * returned as std::integral_constant. + * Otherwise the result of t.size() is returned. + * + * Supported types for deriving the size at compile time are: + * * instances of std::integer_sequence + * * all types std::tuple_size is implemented for + * * all typed that have a static method ::size() + * * instances of Dune::FieldVector + */ +template +constexpr auto size(const T& t) +{ + return Impl::size(t, PriorityTag<42>()); +} + + + +namespace Impl { + + template>::value, int> = 0> + constexpr decltype(auto) elementAt(Container&& c, Index&&, PriorityTag<2>) + { + return std::get::value>(c); + } + + template + constexpr decltype(auto) elementAt(std::integer_sequence c, Index, PriorityTag<1>) + { + return Dune::integerSequenceEntry(c, std::integral_constant()); + } + + template + constexpr decltype(auto) elementAt(Container&& c, Index&& i, PriorityTag<0>) + { + return c[i]; + } + +} // namespace Impl + + + +/** + * \brief Get element at given position from container + * + * \ingroup HybridUtilities + * + * \tparam Container Type of given container + * \tparam Index Type of index + * + * \param c Given container + * \param i Index of element to obtain + * + * \return The element at position i, i.e. c[i] + * + * If this returns the i-th entry of c. It supports the following + * containers + * * Containers providing dynamic access via operator[] + * * Heterogeneous containers providing access via operator[](integral_constant<...>) + * * std::tuple<...> + * * std::integer_sequence + */ +template +constexpr decltype(auto) elementAt(Container&& c, Index&& i) +{ + return Impl::elementAt(std::forward(c), std::forward(i), PriorityTag<42>()); +} + + + +namespace Impl { + + template::value and IsIntegralConstant::value, int> = 0> + constexpr auto integralRange(const Begin& /*begin*/, const End& /*end*/, const PriorityTag<1>&) + { + static_assert(Begin::value <= End::value, "You cannot create an integralRange where end(); + } + + // This should be constexpr but gcc-4.9 does not support + // the relaxed constexpr requirements. Hence for being + // constexpr the function body can only contain a return + // statement and no assertion before this. + template + constexpr auto integralRange(const Begin& begin, const End& end, const PriorityTag<0>&) + { + return DUNE_ASSERT_AND_RETURN(begin<=end, Dune::IntegralRange(begin, end)); + } + +} // namespace Impl + + + +/** + * \brief Create an integral range + * + * \ingroup HybridUtilities + * + * \tparam Begin Type of begin entry of the range + * \tparam End Type of end entry of the range + * + * \param begin First entry of the range + * \param end One past the last entry of the range + * + * \returns An object encoding the given range + * + * If Begin and End are both instances of type + * std::integral_constant, the returned range + * encodes begin and end statically. + */ +template +constexpr auto integralRange(const Begin& begin, const End& end) +{ + return Impl::integralRange(begin, end, PriorityTag<42>()); +} + +/** + * \brief Create an integral range starting from 0 + * + * \ingroup HybridUtilities + * + * \tparam End Type of end entry of the range + * + * \param end One past the last entry of the range + * + * \returns An object encoding the given range + * + * This is a short cut for integralRange(_0, end). + */ +template +constexpr auto integralRange(const End& end) +{ + return Impl::integralRange(Dune::Indices::_0, end, PriorityTag<42>()); +} + + + +namespace Impl { + + template + constexpr void evaluateFoldExpression(std::initializer_list&&) + {} + + template + constexpr void forEachIndex(Range&& range, F&& f, std::integer_sequence) + { + evaluateFoldExpression({(f(Hybrid::elementAt(range, std::integral_constant())), 0)...}); + } + + template + constexpr void forEach(std::integer_sequence /*range*/, F&& f, PriorityTag<2>) + { + evaluateFoldExpression({(f(std::integral_constant()), 0)...}); + } + + + template()))>::value, int> = 0> + constexpr void forEach(Range&& range, F&& f, PriorityTag<1>) + { + auto size = Hybrid::size(range); + auto indices = std::make_index_sequence(); + (forEachIndex)(std::forward(range), std::forward(f), indices); + } + + template + constexpr void forEach(Range&& range, F&& f, PriorityTag<0>) + { + for(auto&& e : range) + f(e); + } + +} // namespace Impl + + + +/** + * \brief Range based for loop + * + * \ingroup HybridUtilities + * + * \tparam Range Type of given range + * \tparam F Type of given predicate + * + * \param range The range to loop over + * \param f A predicate that will be called with each entry of the range + * + * This supports looping over the following ranges + * * ranges obtained from integralRange() + * * all ranges that provide Hybrid::size() and Hybrid::elementAt() + * + * This especially included instances of std::integer_sequence, + * std::tuple, Dune::TupleVector, and Dune::MultiTypeBlockVector. + */ +template +constexpr void forEach(Range&& range, F&& f) +{ + Impl::forEach(std::forward(range), std::forward(f), PriorityTag<42>()); +} + + + +/** + * \brief Accumulate values + * + * \ingroup HybridUtilities + * + * \tparam Range Type of given range + * \tparam T Type of accumulated value + * \tparam F Type of binary accumulation operator + * + * \param range The range of values to accumulate + * \param value Initial value for accumulation + * \param f Binary operator for accumulation + * + * This supports looping over the same ranges as Hybrid::forEach + */ +template +constexpr T accumulate(Range&& range, T value, F&& f) +{ + forEach(std::forward(range), [&](auto&& entry) { + value = f(value, entry); + }); + return value; +} + + + +namespace Impl { + + struct Id { + template + constexpr T operator()(T&& x) const { + return std::forward(x); + } + }; + + template + constexpr decltype(auto) ifElse(std::true_type, IfFunc&& ifFunc, ElseFunc&& /*elseFunc*/) + { + return ifFunc(Id{}); + } + + template + constexpr decltype(auto) ifElse(std::false_type, IfFunc&& /*ifFunc*/, ElseFunc&& elseFunc) + { + return elseFunc(Id{}); + } + + template + decltype(auto) ifElse(const bool& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc) + { + if (condition) + return ifFunc(Id{}); + else + return elseFunc(Id{}); + } + +} // namespace Impl + + + +/** + * \brief A conditional expression + * + * \ingroup HybridUtilities + * + * This will call either ifFunc or elseFunc depending + * on the condition. In any case a single argument + * will be passed to the called function. This will always + * be the identity function. Passing an expression through + * this function will lead to lazy evaluation. This way both + * 'branches' can contain expressions that are only valid + * within this branch if the condition is a std::integral_constant. + * + * In order to do this, the passed functors must have a single + * argument of type auto. + * + * Due to the lazy evaluation mechanism and support for + * std::integral_constant this allows to emulate + * a static if statement. + */ +template +decltype(auto) ifElse(const Condition& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc) +{ + return Impl::ifElse(condition, std::forward(ifFunc), std::forward(elseFunc)); +} + +/** + * \brief A conditional expression + * + * \ingroup HybridUtilities + * + * This provides an ifElse conditional with empty else clause. + */ +template +void ifElse(const Condition& condition, IfFunc&& ifFunc) +{ + ifElse(condition, std::forward(ifFunc), [](auto&&) {}); +} + + + +namespace Impl { + + template + constexpr auto equals(const T1& /*t1*/, const T2& /*t2*/, PriorityTag<1>) -> decltype(T1::value, T2::value, std::integral_constant()) + { return {}; } + + template + constexpr auto equals(const T1& t1, const T2& t2, PriorityTag<0>) + { + return t1==t2; + } + +} // namespace Impl + + + +/** + * \brief Equality comparison + * + * \ingroup HybridUtilities + * + * If both types have a static member value, the result of comparing + * these is returned as std::integral_constant. Otherwise + * the result of a runtime comparison of t1 and t2 is directly returned. + */ +template +constexpr auto equals(T1&& t1, T2&& t2) +{ + return Impl::equals(std::forward(t1), std::forward(t2), PriorityTag<1>()); +} + + + +namespace Impl { + + template + constexpr Result switchCases(std::integer_sequence, const Value& /*value*/, Branches&& /*branches*/, ElseBranch&& elseBranch) + { + return elseBranch(); + } + + template + constexpr Result switchCases(std::integer_sequence, const Value& value, Branches&& branches, ElseBranch&& elseBranch) + { + return ifElse( + Hybrid::equals(std::integral_constant(), value), + [&](auto id) -> decltype(auto) { + return id(branches)(std::integral_constant()); + }, [&](auto id) -> decltype(auto) { + return Impl::switchCases(id(std::integer_sequence()), value, branches, elseBranch); + }); + } + +} // namespace Impl + + + +/** + * \brief Switch statement + * + * \ingroup HybridUtilities + * + * \tparam Cases Type of case range + * \tparam Value Type of value to check against the cases + * \tparam Branches Type of branch function + * \tparam ElseBranch Type of branch function + * + * \param cases A range of cases to check for + * \param value The value to check against the cases + * \param branches A callback that will be executed with matching entry from case list + * \param elseBranch A callback that will be executed if no other entry matches + * + * Value is checked against all entries of the given range. + * If one matches, then branches is executed with the matching + * value as single argument. If the range is an std::integer_sequence, + * the value is passed as std::integral_constant. + * If non of the entries matches, then elseBranch is executed + * without any argument. + * + * Notice that this short circuits, e.g., if one case matches, + * the others are no longer evaluated. + * + * The return value will be deduced from the else branch. + */ +template +constexpr decltype(auto) switchCases(const Cases& cases, const Value& value, Branches&& branches, ElseBranch&& elseBranch) +{ + return Impl::switchCases(cases, value, std::forward(branches), std::forward(elseBranch)); +} + +/** + * \brief Switch statement + * + * \ingroup HybridUtilities + * + * \tparam Cases Type of case range + * \tparam Value Type of value to check against the cases + * \tparam Branches Type of branch function + * + * \param cases A range of cases to check for + * \param value The value to check against the cases + * \param branches A callback that will be executed with matching entry from case list + * + * Value is checked against all entries of the given range. + * If one matches, then branches is executed with the matching + * value as single argument. If the range is an std::integer_sequence, + * the value is passed as std::integral_constant. + * If non of the entries matches, then elseBranch is executed + * without any argument. + */ +template +constexpr void switchCases(const Cases& cases, const Value& value, Branches&& branches) +{ + Impl::switchCases(cases, value, std::forward(branches), []() {}); +} + + +} // namespace Hybrid +} // namespace Dune + + +#endif // #ifndef DUNE_COMMON_HYBRIDUTILITIES_HH diff --git a/dune/common/indent.hh b/dune/common/indent.hh new file mode 100644 index 0000000..3db8dab --- /dev/null +++ b/dune/common/indent.hh @@ -0,0 +1,115 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_INDENT_HH +#define DUNE_COMMON_INDENT_HH + +#include +#include + +namespace Dune { + /** @addtogroup Common + * + * @{ + */ + /** + * @file + * @brief Utility class for handling nested indentation in output. + * @author Jö Fahlke + */ + //! Utility class for handling nested indentation in output. + /** + * An indentation object hast a string basic_indent and an indentation + * level. When it is put into a std::ostream using << it will print its + * basic_indent as many times as its indentation level. By default the + * basic_indent will be two spaces and the indentation level will be 0. + * + * An Indent object may also have a reference to a parent Indent object. If + * it has, that object it put into the stream with the << operator before + * the indentation of this object is put into the stream. This effectively + * chains Indent objects together. + * + * You can use the ++ operator to raise and the -- operator to lower the + * indentation by one level. + * + * You can use the + operator with a numeric second argument morelevel to + * create a copy of the Indent object with the indentation level increased + * morelevel times. This is mainly useful to pass indent+1 to a function, + * where indent is an indentation object. + * + * You can use the + operator with a string second argument newindent to + * create a new Indent object with this object as parent, a basic_indent of + * newindent, and an indentation level of one. This is mainly useful to + * pass indent+"> " to a function, where "> " is a possibly different + * indentation string then the one used by indent indentation object. + * + * \note The idea is for functions receive indentation objects as call by + * value parameters. This way, the indentation object of the caller + * will not be modified by the function and the function can simply + * return at anytime without having to clean up. + */ + class Indent + { + const Indent* parent; + std::string basic_indent; + unsigned level; + + public: + //! setup without parent + /** + * \note Initial indentation level is 0 by default for this constructor. + */ + inline Indent(const std::string& basic_indent_ = " ", unsigned level_ = 0) + : parent(0), basic_indent(basic_indent_), level(level_) + { } + + //! setup without parent and basic_indentation of two spaces + inline Indent(unsigned level_) + : parent(0), basic_indent(" "), level(level_) + { } + + //! setup with parent + /** + * \note Initial indentation level is 1 by default for this constructor. + */ + inline Indent(const Indent* parent_, + const std::string& basic_indent_ = " ", unsigned level_ = 1) + : parent(parent_), basic_indent(basic_indent_), level(level_) + { } + + //! setup with parent + inline Indent(const Indent* parent_, unsigned level_) + : parent(parent_), basic_indent(" "), level(level_) + { } + + //! create new indentation object with this one as parent + inline Indent operator+(const std::string& newindent) const { + return Indent(this, newindent); + } + //! create a copy of this indentation object with raised level + inline Indent operator+(unsigned morelevel) const { + return Indent(parent, basic_indent, level+morelevel); + } + //! raise indentation level + inline Indent& operator++() { ++level; return *this; } + //! lower indentation level + inline Indent& operator--() { if ( level > 0 ) --level; return *this; } + + //! write indentation to a stream + friend inline std::ostream& operator<<(std::ostream& s, + const Indent& indent); + }; + + //! write indentation to a stream + inline std::ostream& operator<<(std::ostream& s, const Indent& indent) { + if(indent.parent) + s << *indent.parent; + for(unsigned i = 0; i < indent.level; ++i) + s << indent.basic_indent; + return s; + } + + /** }@ group Common */ + +} // namespace Dune + +#endif // DUNE_COMMON_INDENT_HH diff --git a/dune/common/indices.hh b/dune/common/indices.hh new file mode 100644 index 0000000..8581937 --- /dev/null +++ b/dune/common/indices.hh @@ -0,0 +1,130 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_COMMON_INDICES_HH +#define DUNE_COMMON_INDICES_HH + +#include +#include +#include + +#include + +namespace Dune +{ + /** \addtogroup Common + * \{ + */ + + /** \brief An index constant with value i + * + * An index constant is a simple type alias for an integral_constant. + * Its main advantages are clarity (it is easier to see that code uses it + * as an index) and the fact that the integral type is fixed, reducing verbosity + * and avoiding the problem of maybe trying to overload / specialize using a different + * integral type. + */ + template + using index_constant = std::integral_constant; + + + + /** \brief Namespace with predefined compile time indices for the range [0,19] + * + * The predefined index objects in this namespace are `constexpr`, which allows them to + * be used in situations where a compile time constant is needed, e.g. for a template + * parameter. Apart from that, `constexpr` implies internal linkage, which helps to avoid + * ODR problems. + * + * The constants implicitly convert to their contained value, so you can for example write + * + * \code{.cc} + * std::array a; + * // the above line is equivalent to + * std::array b; + * \endcode + * + */ + namespace Indices + { + //! Compile time index with value 0. + DUNE_INLINE_VARIABLE constexpr index_constant< 0> _0 = {}; + + //! Compile time index with value 1. + DUNE_INLINE_VARIABLE constexpr index_constant< 1> _1 = {}; + + //! Compile time index with value 2. + DUNE_INLINE_VARIABLE constexpr index_constant< 2> _2 = {}; + + //! Compile time index with value 3. + DUNE_INLINE_VARIABLE constexpr index_constant< 3> _3 = {}; + + //! Compile time index with value 4. + DUNE_INLINE_VARIABLE constexpr index_constant< 4> _4 = {}; + + //! Compile time index with value 5. + DUNE_INLINE_VARIABLE constexpr index_constant< 5> _5 = {}; + + //! Compile time index with value 6. + DUNE_INLINE_VARIABLE constexpr index_constant< 6> _6 = {}; + + //! Compile time index with value 7. + DUNE_INLINE_VARIABLE constexpr index_constant< 7> _7 = {}; + + //! Compile time index with value 8. + DUNE_INLINE_VARIABLE constexpr index_constant< 8> _8 = {}; + + //! Compile time index with value 9. + DUNE_INLINE_VARIABLE constexpr index_constant< 9> _9 = {}; + + //! Compile time index with value 10. + DUNE_INLINE_VARIABLE constexpr index_constant<10> _10 = {}; + + //! Compile time index with value 11. + DUNE_INLINE_VARIABLE constexpr index_constant<11> _11 = {}; + + //! Compile time index with value 12. + DUNE_INLINE_VARIABLE constexpr index_constant<12> _12 = {}; + + //! Compile time index with value 13. + DUNE_INLINE_VARIABLE constexpr index_constant<13> _13 = {}; + + //! Compile time index with value 14. + DUNE_INLINE_VARIABLE constexpr index_constant<14> _14 = {}; + + //! Compile time index with value 15. + DUNE_INLINE_VARIABLE constexpr index_constant<15> _15 = {}; + + //! Compile time index with value 16. + DUNE_INLINE_VARIABLE constexpr index_constant<16> _16 = {}; + + //! Compile time index with value 17. + DUNE_INLINE_VARIABLE constexpr index_constant<17> _17 = {}; + + //! Compile time index with value 18. + DUNE_INLINE_VARIABLE constexpr index_constant<18> _18 = {}; + + //! Compile time index with value 19. + DUNE_INLINE_VARIABLE constexpr index_constant<19> _19 = {}; + + } // namespace Indices + + /** + * \brief Unpack an std::integer_sequence to std::integral_constant... + * + * This forward all entries of the given std::integer_sequence + * as individual std::integral_constant arguments to the given callback. + * + * \param f Callback which has to accept unpacked values + * \param sequence Packed std::integer_sequence of values + * \returns Result of calling f with unpacked integers. + */ + template + decltype(auto) unpackIntegerSequence(F&& f, std::integer_sequence sequence) + { + return f(std::integral_constant()...); + } + +} //namespace Dune + +#endif // DUNE_COMMON_INDICES_HH diff --git a/dune/common/interfaces.hh b/dune/common/interfaces.hh new file mode 100644 index 0000000..57f4b59 --- /dev/null +++ b/dune/common/interfaces.hh @@ -0,0 +1,30 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_INTERFACES_HH +#define DUNE_INTERFACES_HH + +/** @file + @author Robert Kloefkorn + @brief Provides interfaces for detection of specific behavior + */ + +namespace Dune { + + //! An interface class for cloneable objects + struct Cloneable { + + /** \brief Clones the object + * clone needs to be redefined by an implementation class, with the + * return type covariantly adapted. Remember to + * delete the resulting pointer. + */ + virtual Cloneable* clone() const = 0; + + /** \brief Destructor */ + virtual ~Cloneable() + {} + + }; + +} // end namespace Dune +#endif diff --git a/dune/common/ios_state.cc b/dune/common/ios_state.cc new file mode 100644 index 0000000..6c576d4 --- /dev/null +++ b/dune/common/ios_state.cc @@ -0,0 +1,34 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +namespace Dune { + + ////////////////////////////////////////////////////////////////////// + // + // class ios_base_all_saver + // + + ios_base_all_saver::ios_base_all_saver(state_type& ios_) + : ios(ios_), oldflags(ios.flags()), oldprec(ios.precision()), + oldwidth(ios.width()) + {} + + ios_base_all_saver::~ios_base_all_saver() + { + restore(); + } + + void ios_base_all_saver::restore() + { + ios.flags(oldflags); + ios.precision(oldprec); + ios.width(oldwidth); + } + +} diff --git a/dune/common/ios_state.hh b/dune/common/ios_state.hh new file mode 100644 index 0000000..736dbdf --- /dev/null +++ b/dune/common/ios_state.hh @@ -0,0 +1,75 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_IOS_STATE_HH +#define DUNE_COMMON_IOS_STATE_HH + +#include + +namespace Dune { + /** @addtogroup Common + * + * @{ + */ + /** + * @file + * @brief Utility class for storing and resetting stream attributes. + * @author Markus Blatt + */ + /** + * @brief Utility class for storing and resetting stream attributes. + * + * The constructor saves the attributes currently set in the ios_base + * object and the destructor restores these attributes again. The + * attributes can also be restores at any time by calling the method + * restore(). + * + * The saved attributes are the format flags, precision, and width. + * + * @note The interface of this class is meant to be drop-in compatible the + * the class of the same name from . + */ + class ios_base_all_saver + { + public: + /** @brief Export type of object we save the state for */ + typedef std::ios_base state_type; + + /** + * @brief Constructor that stores the currently used flags. + * @param ios_ The ios_base object whose flags are to be saved and + * restored. Any stream object should work here. + * + * @note A reference to the ios_base object is store in this object. Thus + * the ios_base object must remain valid until the destructor of + * this object has been called. + */ + ios_base_all_saver(state_type& ios_); + + /** + * @brief Destructor that restores the flags stored by the constructor. + */ + ~ios_base_all_saver(); + + /** + * @brief Restore flags now + * + * The flags will also be restored at destruction time even if this method + * was used. + */ + void restore(); + + private: + /** @brief the ios object to restore the flags to. */ + state_type& ios; + /** @brief The flags used when the constructor was called. */ + state_type::fmtflags oldflags; + /** @brief The precision in use when the constructor was called. */ + std::streamsize oldprec; + /** @brief The width in use when the constructor was called. */ + std::streamsize oldwidth; + }; + + /** }@ */ +} + +#endif // DUNE_COMMON_IOS_STATE_HH diff --git a/dune/common/iteratorfacades.hh b/dune/common/iteratorfacades.hh new file mode 100644 index 0000000..d4e80bb --- /dev/null +++ b/dune/common/iteratorfacades.hh @@ -0,0 +1,736 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_ITERATORFACADES_HH +#define DUNE_ITERATORFACADES_HH + +#include +#include + +#include "typetraits.hh" + +namespace Dune +{ + /*! \defgroup IteratorFacades Iterator facades + \ingroup Common + + \brief Iterator facades for writing stl conformant iterators. + + With using these facades writing iterators for arbitrary containers becomes much less + cumbersome as only few functions have to be implemented. All other functions needed by + the stl are provided by the facades using the Barton-Nackman trick (also known as + curiously recurring template pattern). + + The following example illustrates how a random access iterator might be written: + + \code + #include + + ... + + template + class TestIterator : public Dune::BidirectionalIteratorFacade,T, T&, int> + { + friend class TestIterator::type, typename std::remove_const::type >; + friend class TestIterator::type, const typename std::remove_const::type >; + + public: + + // Constructors needed by the facade iterators. + TestIterator(): container_(0), position_(0) + { } + + TestIterator(C& cont, int pos) + : container_(&cont), position_(pos) + {} + + TestIterator(const TestIterator::type, typename std::remove_const::type >& other) + : container_(other.container_), position_(other.position_) + {} + + + TestIterator(const TestIterator::type, const typename std::remove_const::type >& other) + : container_(other.container_), position_(other.position_) + {} + + // Methods needed by the forward iterator + bool equals(const TestIterator::type,typename std::remove_const::type>& other) const + { + return position_ == other.position_ && container_ == other.container_; + } + + + bool equals(const TestIterator::type,const typename std::remove_const::type>& other) const + { + return position_ == other.position_ && container_ == other.container_; + } + + T& dereference() const + { + return container_->values_[position_]; + } + + void increment() + { + ++position_; + } + + // Additional function needed by BidirectionalIterator + void decrement() + { + --position_; + } + + // Additional function needed by RandomAccessIterator + T& elementAt(int i)const + { + return container_->operator[](position_+i); + } + + void advance(int n) + { + position_=position_+n; + } + + std::ptrdiff_t distanceTo(TestIterator::type,const typename std::remove_const::type> other) const + { + assert(other.container_==container_); + return other.position_ - position_; + } + + std::ptrdiff_t distanceTo(TestIterator::type, typename std::remove_const::type> other) const + { + assert(other.container_==container_); + return other.position_ - position_; + } + private: + C *container_; + size_t position_; + }; + + \endcode + See dune/common/test/iteratorbase.hh for details. + */ + + + /** + * @file + * @brief This file implements iterator facade classes for writing stl conformant iterators. + * + * With using these facades writing iterators for arbitrary containers becomes much less + * cumbersome as only few functions have to be implemented. All other functions needed by + * the stl are provided by the facades using the Barton-Nackman trick (also known as + * curiously recurring template pattern. + */ + + /** @addtogroup IteratorFacades + * + * @{ + */ + /** + * @brief Base class for stl conformant forward iterators. + * + * \tparam T The derived class + * \tparam V The value type + * \tparam R The reference type + * \tparam D The type for differences between two iterators + */ + template + class ForwardIteratorFacade + { + + public: + /* type aliases required by C++ for iterators */ + using iterator_category = std::forward_iterator_tag; + using value_type = typename std::remove_const::type; + using difference_type = D; + using pointer = V*; + using reference = R; + + /** + * @brief The type of derived iterator. + * + * The iterator has to define following + * functions have to be present: + * + * \code + * + * // Access the value referred to. + * Reference dereference() const; + * + * // Compare for equality with iterator j + * bool equals(j); + * + * // position the iterator at the next element. + * void increment() + * + * // check for equality with other iterator + * bool equals(other) + * \endcode + * + * For an elaborate explanation see the + * STL Documentation! + */ + typedef T DerivedType; + + /** + * @brief The type of value accessed through the iterator. + */ + typedef V Value; + + /** + * @brief The pointer to the Value. + */ + typedef V* Pointer; + + /** + * @brief The type of the difference between two positions. + */ + typedef D DifferenceType; + + /** + * @brief The type of the reference to the values accessed. + */ + typedef R Reference; + + /** @brief Dereferencing operator. */ + Reference operator*() const + { + return static_cast(this)->dereference(); + } + + Pointer operator->() const + { + return &(static_cast(this)->dereference()); + } + + /** @brief Preincrement operator. */ + DerivedType& operator++() + { + static_cast(this)->increment(); + return *static_cast(this); + } + + /** @brief Postincrement operator. */ + DerivedType operator++(int) + { + DerivedType tmp(static_cast(*this)); + this->operator++(); + return tmp; + } + }; + + /** + * @brief Checks for equality. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable::type is + * not defined. + * + */ + template + inline typename EnableIfInterOperable::type + operator==(const ForwardIteratorFacade& lhs, + const ForwardIteratorFacade& rhs) + { + if(std::is_convertible::value) + return static_cast(lhs).equals(static_cast(rhs)); + else + return static_cast(rhs).equals(static_cast(lhs)); + } + + /** + * @brief Checks for inequality. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable::type is + * not defined. + * + */ + template + inline typename EnableIfInterOperable::type + operator!=(const ForwardIteratorFacade& lhs, + const ForwardIteratorFacade& rhs) + { + if(std::is_convertible::value) + return !static_cast(lhs).equals(static_cast(rhs)); + else + return !static_cast(rhs).equals(static_cast(lhs)); + } + + /** + * @brief Facade class for stl conformant bidirectional iterators. + * + */ + template + class BidirectionalIteratorFacade + { + + public: + /* type aliases required by C++ for iterators */ + using iterator_category = std::bidirectional_iterator_tag; + using value_type = typename std::remove_const::type; + using difference_type = D; + using pointer = V*; + using reference = R; + + /** + * @brief The type of derived iterator. + * + * The iterator has to define following + * functions have to be present: + * + * \code + * + * // Access the value referred to. + * Reference dereference() const; + * + * // Compare for equality with j + * bool equals(j); + * + * // position the iterator at the next element. + * void increment() + * + * // position the iterator at the previous element. + * void decrement() + * + * \endcode + * + * For an elaborate explanation see the + * STL Documentation + */ + typedef T DerivedType; + + /** + * @brief The type of value accessed through the iterator. + */ + typedef V Value; + + /** + * @brief The pointer to the Value. + */ + typedef V* Pointer; + + /** + * @brief The type of the difference between two positions. + */ + typedef D DifferenceType; + + /** + * @brief The type of the reference to the values accessed. + */ + typedef R Reference; + + /** @brief Dereferencing operator. */ + Reference operator*() const + { + return static_cast(this)->dereference(); + } + + Pointer operator->() const + { + return &(static_cast(this)->dereference()); + } + + /** @brief Preincrement operator. */ + DerivedType& operator++() + { + static_cast(this)->increment(); + return *static_cast(this); + } + + /** @brief Postincrement operator. */ + DerivedType operator++(int) + { + DerivedType tmp(static_cast(*this)); + this->operator++(); + return tmp; + } + + + /** @brief Preincrement operator. */ + DerivedType& operator--() + { + static_cast(this)->decrement(); + return *static_cast(this); + } + + /** @brief Postincrement operator. */ + DerivedType operator--(int) + { + DerivedType tmp(static_cast(*this)); + this->operator--(); + return tmp; + } + }; + + /** + * @brief Checks for equality. + * + * This operation is only defined if T2 is convertible to T1, otherwise it + * is removed from the overload set since the enable_if for the return type + * yield an invalid type expression. + */ + template + inline typename std::enable_if::value,bool>::type + operator==(const BidirectionalIteratorFacade& lhs, + const BidirectionalIteratorFacade& rhs) + { + return static_cast(lhs).equals(static_cast(rhs)); + } + + /** + * @brief Checks for equality. + * + * This operation is only defined if either T1 is convertible to T2, and T2 + * is not convetible to T1. Otherwise the operator is removed from the + * overload set since the enable_if for the return type yield an invalid + * type expression. + */ + template + inline + typename std::enable_if::value && !std::is_convertible::value, + bool>::type + operator==(const BidirectionalIteratorFacade& lhs, + const BidirectionalIteratorFacade& rhs) + { + return static_cast(rhs).equals(static_cast(lhs)); + } + + /** + * @brief Checks for inequality. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable::type is + * not defined. + * + */ + template + inline typename EnableIfInterOperable::type + operator!=(const BidirectionalIteratorFacade& lhs, + const BidirectionalIteratorFacade& rhs) + { + return !(lhs == rhs); + } + + /** + * @brief Base class for stl conformant forward iterators. + * + */ + template + class RandomAccessIteratorFacade + { + + public: + /* type aliases required by C++ for iterators */ + using iterator_category = std::random_access_iterator_tag; + using value_type = typename std::remove_const::type; + using difference_type = D; + using pointer = V*; + using reference = R; + + /** + * @brief The type of derived iterator. + * + * The iterator has to define following + * functions have to be present: + * + * \code + * + * // Access the value referred to. + * Reference dereference() const; + * // Access the value at some other location + * Reference elementAt(n) const; + * + * // Compare for equality with j + * bool equals(j); + * + * // position the iterator at the next element. + * void increment() + * + * // position the iterator at the previous element. + * void decrement() + * + * // advance the iterator by a number of positions- + * void advance(DifferenceType n); + * // calculate the distance to another iterator. + * // One should incorporate an assertion whether + * // the same containers are referenced + * DifferenceType distanceTo(j) const; + * \endcode + * + * For an elaborate explanation see the + * STL Documentation + */ + typedef T DerivedType; + + /** + * @brief The type of value accessed through the iterator. + */ + typedef V Value; + + /** + * @brief The pointer to the Value. + */ + typedef V* Pointer; + + /** + * @brief The type of the difference between two positions. + */ + typedef D DifferenceType; + + /** + * @brief The type of the reference to the values accessed. + */ + typedef R Reference; + + /** @brief Dereferencing operator. */ + Reference operator*() const + { + return static_cast(this)->dereference(); + } + + Pointer operator->() const + { + return &(static_cast(this)->dereference()); + } + + /** + * @brief Get the element n positions from the current one. + * @param n The distance to the element. + * @return The element at that distance. + */ + Reference operator[](DifferenceType n) const + { + return static_cast(this)->elementAt(n); + } + + /** @brief Preincrement operator. */ + DerivedType& operator++() + { + static_cast(this)->increment(); + return *static_cast(this); + } + + /** @brief Postincrement operator. */ + DerivedType operator++(int) + { + DerivedType tmp(static_cast(*this)); + this->operator++(); + return tmp; + } + + DerivedType& operator+=(DifferenceType n) + { + static_cast(this)->advance(n); + return *static_cast(this); + } + + DerivedType operator+(DifferenceType n) const + { + DerivedType tmp(static_cast(*this)); + tmp.advance(n); + return tmp; + } + + + /** @brief Predecrement operator. */ + DerivedType& operator--() + { + static_cast(this)->decrement(); + return *static_cast(this); + } + + /** @brief Postdecrement operator. */ + DerivedType operator--(int) + { + DerivedType tmp(static_cast(*this)); + this->operator--(); + return tmp; + } + + DerivedType& operator-=(DifferenceType n) + { + static_cast(this)->advance(-n); + return *static_cast(this); + } + + DerivedType operator-(DifferenceType n) const + { + DerivedType tmp(static_cast(*this)); + tmp.advance(-n); + return tmp; + } + + + }; + + /** + * @brief Checks for equality. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable::type is + * not defined. + * + */ + template + inline typename EnableIfInterOperable::type + operator==(const RandomAccessIteratorFacade& lhs, + const RandomAccessIteratorFacade& rhs) + { + if(std::is_convertible::value) + return static_cast(lhs).equals(static_cast(rhs)); + else + return static_cast(rhs).equals(static_cast(lhs)); + } + + /** + * @brief Checks for inequality. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable::type is + * not defined. + * + */ + template + inline typename EnableIfInterOperable::type + operator!=(const RandomAccessIteratorFacade& lhs, + const RandomAccessIteratorFacade& rhs) + { + if(std::is_convertible::value) + return !static_cast(lhs).equals(static_cast(rhs)); + else + return !static_cast(rhs).equals(static_cast(lhs)); + } + + /** + * @brief Comparison operator. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable::type is + * not defined. + * + */ + template + inline typename EnableIfInterOperable::type + operator<(const RandomAccessIteratorFacade& lhs, + const RandomAccessIteratorFacade& rhs) + { + if(std::is_convertible::value) + return static_cast(lhs).distanceTo(static_cast(rhs))>0; + else + return static_cast(rhs).distanceTo(static_cast(lhs))<0; + } + + + /** + * @brief Comparison operator. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable::type is + * not defined. + * + */ + template + inline typename EnableIfInterOperable::type + operator<=(const RandomAccessIteratorFacade& lhs, + const RandomAccessIteratorFacade& rhs) + { + if(std::is_convertible::value) + return static_cast(lhs).distanceTo(static_cast(rhs))>=0; + else + return static_cast(rhs).distanceTo(static_cast(lhs))<=0; + } + + + /** + * @brief Comparison operator. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable::type is + * not defined. + * + */ + template + inline typename EnableIfInterOperable::type + operator>(const RandomAccessIteratorFacade& lhs, + const RandomAccessIteratorFacade& rhs) + { + if(std::is_convertible::value) + return static_cast(lhs).distanceTo(static_cast(rhs))<0; + else + return static_cast(rhs).distanceTo(static_cast(lhs))>0; + } + + /** + * @brief Comparison operator. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable::type is + * not defined. + * + */ + template + inline typename EnableIfInterOperable::type + operator>=(const RandomAccessIteratorFacade& lhs, + const RandomAccessIteratorFacade& rhs) + { + if(std::is_convertible::value) + return static_cast(lhs).distanceTo(static_cast(rhs))<=0; + else + return static_cast(rhs).distanceTo(static_cast(lhs))>=0; + } + + /** + * @brief Calculates the difference between two pointers. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable::type is + * not defined. + * + */ + template + inline typename EnableIfInterOperable::type + operator-(const RandomAccessIteratorFacade& lhs, + const RandomAccessIteratorFacade& rhs) + { + if(std::is_convertible::value) + return -static_cast(lhs).distanceTo(static_cast(rhs)); + else + return static_cast(rhs).distanceTo(static_cast(lhs)); + } + + /** @} */ +} +#endif diff --git a/dune/common/iteratorrange.hh b/dune/common/iteratorrange.hh new file mode 100644 index 0000000..5211e55 --- /dev/null +++ b/dune/common/iteratorrange.hh @@ -0,0 +1,64 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_ITERATORRANGE_HH +#define DUNE_COMMON_ITERATORRANGE_HH + +namespace Dune { + + //! Simple range between a begin and an end iterator. + /** + * IteratorRange is mainly useful as a lightweight adaptor + * class when adding support for range-based for loops to + * existing containers that lack a standard begin(), end() + * pair of member functions. + * + * \tparam Iterator The type of iterator + * \ingroup CxxUtilities + */ + template + class IteratorRange + { + + public: + + //! The iterator belonging to this range. + typedef Iterator iterator; + + //! The iterator belonging to this range. + /** + * This typedef is here mainly for compatibility reasons. + */ + typedef Iterator const_iterator; + + //! Constructs an iterator range on [begin,end). + IteratorRange(const Iterator& begin, const Iterator& end) + : _begin(begin) + , _end(end) + {} + + //! Default constructor, relies on iterators being default-constructible. + IteratorRange() + {} + + //! Returns an iterator pointing to the begin of the range. + iterator begin() const + { + return _begin; + } + + //! Returns an iterator pointing past the end of the range. + iterator end() const + { + return _end; + } + + private: + + Iterator _begin; + Iterator _end; + + }; + +} + +#endif // DUNE_COMMON_ITERATORRANGE_HH diff --git a/dune/common/keywords.hh b/dune/common/keywords.hh new file mode 100644 index 0000000..d6a9604 --- /dev/null +++ b/dune/common/keywords.hh @@ -0,0 +1,38 @@ +#ifndef DUNE_COMMON_KEYWORDS_HH +#define DUNE_COMMON_KEYWORDS_HH + +/** \file + * \brief Definitions of several macros that conditionally make C++ syntax + * available. + * + * This header contains several macros that enable C++ features depending on your + * compiler. Most of these features are optional and provide additional functionality + * like making code constexpr. + * + * \ingroup CxxUtilities + */ + + +#if __cpp_inline_variables >= 201606 +#define DUNE_INLINE_VARIABLE inline +#else +//! Preprocessor macro used for marking variables inline on supported compilers. +/** + * \ingroup CxxUtilities + */ +#define DUNE_INLINE_VARIABLE +#endif + + +#if __cpp_constexpr >= 201304 +#define DUNE_GENERALIZED_CONSTEXPR constexpr +#else +//! Preprocessor macro used for marking code as constexpr under the relaxed rules of C++14 if supported by the compiler. +/** + * \ingroup CxxUtilities + */ +#define DUNE_GENERALIZED_CONSTEXPR +#endif + + +#endif // DUNE_COMMON_KEYWORDS_HH diff --git a/dune/common/lcm.hh b/dune/common/lcm.hh new file mode 100644 index 0000000..4308fe2 --- /dev/null +++ b/dune/common/lcm.hh @@ -0,0 +1,46 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_LCM_HH +#define DUNE_LCM_HH + +#warning "This header is deprecated and will be removed after release 2.8. Use std::lcm instead." + +/** \file + * \brief Statically compute the least common multiple of two integers + */ + +#include + +namespace Dune +{ + + /** + * @addtogroup Common + * @{ + */ + /** + * @file + * This file provides template constructs for calculation the + * least common multiple. + */ + + /** + * @brief Calculate the least common multiple of two numbers + */ + template + struct [[deprecated("Will be removed after Dune 2.8. Use std::lcm instead.")]] Lcm + { + static void conceptCheck() + { + static_assert(0 +#include +#include +#include + +#include + +/** @file + @author Christian Engwer + @brief LRU Cache Container, using an STL like interface + */ + +namespace Dune { + + namespace { + + /* + hide the default traits in an empty namespace + */ + template > + struct _lru_default_traits + { + typedef Key key_type; + typedef Alloc allocator; + typedef std::list< std::pair > list_type; + typedef typename list_type::iterator iterator; + typedef typename std::less cmp; + typedef std::map< key_type, iterator, cmp, + typename std::allocator_traits::template rebind_alloc > > map_type; + }; + + } // end empty namespace + + /** + @brief LRU Cache Container + + Implementation of an LRU (least recently used) cache + container. This implementation follows the approach presented in + http://aim.adc.rmit.edu.au/phd/sgreuter/papers/graphite2003.pdf + */ + template > + class lru + { + typedef typename Traits::list_type list_type; + typedef typename Traits::map_type map_type; + typedef typename Traits::allocator allocator; + typedef typename map_type::iterator map_iterator; + typedef typename map_type::const_iterator const_map_iterator; + + public: + typedef typename Traits::key_type key_type; + typedef typename allocator::value_type value_type; + using pointer = typename allocator::value_type*; + using const_pointer = typename allocator::value_type const*; + using const_reference = typename allocator::value_type const&; + using reference = typename allocator::value_type&; + typedef typename allocator::size_type size_type; + typedef typename list_type::iterator iterator; + typedef typename list_type::const_iterator const_iterator; + + /** + * Returns a read/write reference to the data of the most + * recently used entry. + */ + reference front() + { + return _data.front().second; + } + + /** + * Returns a read-only (constant) reference to the data of the + * most recently used entry. + */ + const_reference front() const + { + return _data.front().second; + } + + /** + * Returns a read/write reference to the data of the least + * recently used entry. + */ + reference back() + { + return _data.back().second; + } + + /** + * Returns a read-only (constant) reference to the data of the + * least recently used entry. + */ + const_reference back ([[maybe_unused]] int i) const + { + return _data.back().second; + } + + + /** + * @brief Removes the first element. + */ + void pop_front() + { + key_type k = _data.front().first; + _data.pop_front(); + _index.erase(k); + } + /** + * @brief Removes the last element. + */ + void pop_back() + { + key_type k = _data.back().first; + _data.pop_back(); + _index.erase(k); + } + + /** + * @brief Finds the element whose key is k. + * + * @return iterator + */ + iterator find (const key_type & key) + { + const map_iterator it = _index.find(key); + if (it == _index.end()) return _data.end(); + return it->second; + } + + /** + * @brief Finds the element whose key is k. + * + * @return const_iterator + */ + const_iterator find (const key_type & key) const + { + const map_iterator it = _index.find(key); + if (it == _index.end()) return _data.end(); + return it->second; + } + + /** + * @brief Insert a value into the container + * + * Stores value under key and marks it as most recent. If this key + * is already present, the associated data is replaced. + * + * @param key associated with data + * @param data to store + * + * @return reference of stored data + */ + reference insert (const key_type & key, const_reference data) + { + std::pair x(key, data); + /* insert item as mru */ + iterator it = _data.insert(_data.begin(), x); + /* store index */ + _index.insert(std::make_pair(key,it)); + + return it->second; + } + + /** + * @copydoc touch + */ + reference insert (const key_type & key) + { + return touch (key); + } + + /** + * @brief mark data associated with key as most recent + * + * @return reference of stored data + */ + reference touch (const key_type & key) + { + /* query _index for iterator */ + map_iterator it = _index.find(key); + if (it == _index.end()) + DUNE_THROW(Dune::RangeError, + "Failed to touch key " << key << ", it is not in the lru container"); + /* update _data + move it to the front + */ + _data.splice(_data.begin(), _data, it->second); + return it->second->second; + } + + /** + * @brief Retrieve number of entries in the container + */ + size_type size() const + { + return _data.size(); + } + + /** + * @brief ensure a maximum size of the container + * + * If new_size is smaller than size the oldest elements are + * dropped. Otherwise nothing happens. + */ + void resize(size_type new_size) + { + assert(new_size <= size()); + + while (new_size < size()) + pop_back(); + } + + /** + * + */ + void clear() + { + _data.clear(); + _index.clear(); + } + + private: + list_type _data; + map_type _index; + + }; + +} // namespace Dune + +#endif // DUNE_COMMON_LRU_HH diff --git a/dune/common/mallocallocator.hh b/dune/common/mallocallocator.hh new file mode 100644 index 0000000..fa48d5b --- /dev/null +++ b/dune/common/mallocallocator.hh @@ -0,0 +1,114 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_MALLOC_ALLOCATOR_HH +#define DUNE_MALLOC_ALLOCATOR_HH + +#include +#include +#include +#include + +/** + * @file + * @brief Allocators that use malloc/free. + */ +namespace Dune +{ + /** + @ingroup Allocators + @brief Allocators implementation which simply calls malloc/free + */ + template + class MallocAllocator { + public: + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T value_type; + template struct rebind { + typedef MallocAllocator other; + }; + + //! create a new MallocAllocator + MallocAllocator() noexcept {} + //! copy construct from an other MallocAllocator, possibly for a different result type + template + MallocAllocator(const MallocAllocator&) noexcept {} + //! cleanup this allocator + ~MallocAllocator() noexcept {} + + pointer address(reference x) const + { + return &x; + } + const_pointer address(const_reference x) const + { + return &x; + } + + //! allocate n objects of type T + pointer allocate(size_type n, + [[maybe_unused]] const void* hint = 0) + { + if (n > this->max_size()) + throw std::bad_alloc(); + + pointer ret = static_cast(std::malloc(n * sizeof(T))); + if (!ret) + throw std::bad_alloc(); + return ret; + } + + //! deallocate n objects of type T at address p + void deallocate(pointer p, [[maybe_unused]] size_type n) + { + std::free(p); + } + + //! max size for allocate + size_type max_size() const noexcept + { + return size_type(-1) / sizeof(T); + } + + //! copy-construct an object of type T (i.e. make a placement new on p) + void construct(pointer p, const T& val) + { + ::new((void*)p)T(val); + } + + //! construct an object of type T from variadic parameters + template + void construct(pointer p, Args&&... args) + { + ::new((void *)p)T(std::forward(args) ...); + } + + //! destroy an object of type T (i.e. call the destructor) + void destroy(pointer p) + { + p->~T(); + } + }; + + //! check whether allocators are equivalent + template + constexpr bool + operator==(const MallocAllocator &, const MallocAllocator &) + { + return true; + } + + //! check whether allocators are not equivalent + template + constexpr bool + operator!=(const MallocAllocator &, const MallocAllocator &) + { + return false; + } +} + +#endif // DUNE_MALLOC_ALLOCATOR_HH diff --git a/dune/common/math.hh b/dune/common/math.hh new file mode 100644 index 0000000..c3f3dc8 --- /dev/null +++ b/dune/common/math.hh @@ -0,0 +1,364 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_MATH_HH +#define DUNE_MATH_HH + +/** \file + * \brief Some useful basic math stuff + */ + +#include +#include +#include +#include + +#include + +namespace Dune +{ + + /** + \brief Standard implementation of MathematicalConstants. + + This implementation will work with all built-in floating point + types. It provides + + * e as exp(1.0) + * pi as acos(-1.0) + + */ + template< class T > + struct StandardMathematicalConstants + { + /** + * \brief Euler's number + */ + static const T e () + { + using std::exp; + static const T e = exp( T( 1 ) ); + return e; + } + + /** + * \brief Archimedes' constant + */ + static const T pi () + { + using std::acos; + static const T pi = acos( T( -1 ) ); + return pi; + } + }; + + + /** + \brief Provides commonly used mathematical constants. + + a struct that is specialized for types repesenting real or complex + numbers. It provides commonly used mathematical constants with the + required accuary for the specified type. + */ + template< class Field > + struct MathematicalConstants + : public StandardMathematicalConstants + {}; + + + /** \brief Power method for integer exponents + * + * \note Make sure that Mantissa is a non-integer type when using negative exponents! + */ + template + constexpr Mantissa power(Mantissa m, Exponent p) + { + static_assert(std::numeric_limits::is_integer, "Exponent must be an integer type!"); + + auto result = Mantissa(1); + auto absp = (p<0) ? -p : p; // This is simply abs, but std::abs is not constexpr + for (Exponent i = Exponent(0); i + struct Factorial + { + //! factorial stores m! + enum { factorial = m * Factorial::factorial }; + }; + + //! end of recursion of factorial via specialization + template <> + struct Factorial<0> + { + // 0! = 1 + enum { factorial = 1 }; + }; + + + //! calculate the factorial of n as a constexpr + // T has to be an integral type + template + constexpr inline static T factorial(const T& n) noexcept + { + static_assert(std::numeric_limits::is_integer, "`factorial(n)` has to be called with an integer type."); + T fac = 1; + for(T k = 0; k < n; ++k) + fac *= k+1; + return fac; + } + + //! calculate the factorial of n as a constexpr + template + constexpr inline static auto factorial (std::integral_constant) noexcept + { + return std::integral_constant{}; + } + + + //! calculate the binomial coefficient n over k as a constexpr + // T has to be an integral type + template + constexpr inline static T binomial (const T& n, const T& k) noexcept + { + static_assert(std::numeric_limits::is_integer, "`binomial(n, k)` has to be called with an integer type."); + + if( k < 0 || k > n ) + return 0; + + if (2*k > n) + return binomial(n, n-k); + + T bin = 1; + for(auto i = n-k; i < n; ++i) + bin *= i+1; + return bin / factorial(k); + } + + //! calculate the binomial coefficient n over k as a constexpr + template + constexpr inline static auto binomial (std::integral_constant, std::integral_constant) noexcept + { + return std::integral_constant{}; + } + + template + constexpr inline static auto binomial (std::integral_constant, std::integral_constant) noexcept + { + return std::integral_constant= 0 ? 1 : 0)>{}; + } + + + //! compute conjugate complex of x + // conjugate complex does nothing for non-complex types + template + inline K conjugateComplex (const K& x) + { + return x; + } + +#ifndef DOXYGEN + // specialization for complex + template + inline std::complex conjugateComplex (const std::complex& c) + { + return std::complex(c.real(),-c.imag()); + } +#endif + + //! Return the sign of the value + template + int sign(const T& val) + { + return (val < 0 ? -1 : 1); + } + + + namespace Impl { + // Returns whether a given type behaves like std::complex<>, i.e. whether + // real() and imag() are defined + template + struct isComplexLike { + private: + template + static auto test(U* u) -> decltype(u->real(), u->imag(), std::true_type()); + + template + static auto test(...) -> decltype(std::false_type()); + + public: + static const bool value = decltype(test(0))::value; + }; + } // namespace Impl + + //! namespace for customization of math functions with Dune-Semantics + /** + You can add overloads for the Dune-semantics of math-functions in this + namespace. These overloads will be used by functors like `Dune::isNaN` + to implement these functions, and will be preferred over functions found + by ADL, or the corresponding functions from the standard (whether they + are found by ADL or in the namespace `std`. + + PriorityTag + =========== + + There are two predefined priorities: + + <1> provides a default implementation, only applicable if the + camelCase-Version of the function (e.g. `isNaN`) can be found via ADL + for an argument of type `T`. (Otherwise the overload should not + participate in overload resolution.) + + <0> provides a default implementation that forwards the call to the + lower-case version of the function (e.g. `isnan`), found via ADL and + the namespace `std`. + + Any higher priority up to 10 can be used by other overloads. + */ + namespace MathOverloads { + + //! Tag to make sure the functions in this namespace can be found by ADL. + struct ADLTag {}; + +#define DUNE_COMMON_MATH_ISFUNCTION(function, stdfunction) \ + template \ + auto function(const T &t, PriorityTag<1>, ADLTag) \ + -> decltype(function(t)) { \ + return function(t); \ + } \ + template \ + auto function(const T &t, PriorityTag<0>, ADLTag) { \ + using std::stdfunction; \ + return stdfunction(t); \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_COMMON_MATH_ISFUNCTION(isNaN,isnan); + DUNE_COMMON_MATH_ISFUNCTION(isInf,isinf); + DUNE_COMMON_MATH_ISFUNCTION(isFinite,isfinite); +#undef DUNE_COMMON_MATH_ISFUNCTION + + template + auto isUnordered(const T &t1, const T &t2, PriorityTag<1>, ADLTag) + -> decltype(isUnordered(t1, t2)) { + return isUnordered(t1, t2); + } + + template + auto isUnordered(const T &t1, const T &t2, PriorityTag<0>, ADLTag) { + using std::isunordered; + return isunordered(t1, t2); + } + } + + namespace MathImpl { + + // NOTE: it is important that these functors have names different from the + // names of the functions they are forwarding to. Otherwise the + // unqualified call would find the functor type, not a function, and ADL + // would never be attempted. +#define DUNE_COMMON_MATH_ISFUNCTION_FUNCTOR(function) \ + struct function##Impl { \ + template \ + constexpr auto operator()(const T &t) const { \ + return function(t, PriorityTag<10>{}, MathOverloads::ADLTag{}); \ + } \ + }; \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_COMMON_MATH_ISFUNCTION_FUNCTOR(isNaN); + DUNE_COMMON_MATH_ISFUNCTION_FUNCTOR(isInf); + DUNE_COMMON_MATH_ISFUNCTION_FUNCTOR(isFinite); +#undef DUNE_COMMON_MATH_ISFUNCTION_FUNCTOR + + struct isUnorderedImpl { + template + constexpr auto operator()(const T &t1, const T &t2) const { + return isUnordered(t1, t2, PriorityTag<10>{}, MathOverloads::ADLTag{}); + } + }; + + } //MathImpl + + + namespace Impl { + /* This helper has a math functor as a static constexpr member. Doing + this as a static member of a template struct means we can do this + without violating the ODR or putting the definition into a seperate + compilation unit, while still still ensuring the functor is the same + lvalue across all compilation units. + */ + template + struct MathDummy + { + static constexpr T value{}; + }; + + template + constexpr T MathDummy::value; + + } //namespace Impl + + namespace { + /* Provide the math functors directly in the `Dune` namespace. + + This actually declares a different name in each translation unit, but + they all resolve to the same lvalue. + */ + + //! check wether the argument is NaN + /** + * Dune-Semantic: for multi-valued types (complex, vectors), check whether + * *any* value is NaN. + */ + constexpr auto const &isNaN = Impl::MathDummy::value; + + //! check wether the argument is infinite or NaN + /** + * Dune-Semantic: for multi-valued types (complex, vectors), check whether + * *any* value is infinite or NaN. + */ + constexpr auto const &isInf = Impl::MathDummy::value; + + //! check wether the argument is finite and non-NaN + /** + * Dune-Semantic: for multi-valued types (complex, vectors), check whether + * *all* values are finite and non-NaN. + */ + constexpr auto const &isFinite = Impl::MathDummy::value; + + //! check wether the arguments are ordered + /** + * Dune-Semantic: for multi-valued types (complex, vectors), there is + * never an ordering, so at the moment these types are not supported as + * arguments. + */ + constexpr auto const &isUnordered = Impl::MathDummy::value; + } + + namespace MathOverloads { + /*Overloads for complex types*/ + template::value> > + auto isNaN(const T &t, PriorityTag<2>, ADLTag) { + return Dune::isNaN(real(t)) || Dune::isNaN(imag(t)); + } + + template::value> > + auto isInf(const T &t, PriorityTag<2>, ADLTag) { + return Dune::isInf(real(t)) || Dune::isInf(imag(t)); + } + + template::value> > + auto isFinite(const T &t, PriorityTag<2>, ADLTag) { + return Dune::isFinite(real(t)) && Dune::isFinite(imag(t)); + } + } //MathOverloads +} + +#endif // #ifndef DUNE_MATH_HH diff --git a/dune/common/matvectraits.hh b/dune/common/matvectraits.hh new file mode 100644 index 0000000..1c79157 --- /dev/null +++ b/dune/common/matvectraits.hh @@ -0,0 +1,33 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_MATVECTRAITS_HH +#define DUNE_MATVECTRAITS_HH + +/** \file + * \brief Documentation of the traits classes you need to write for each implementation of DenseVector or DenseMatrix + */ + +namespace Dune { + + /** + @addtogroup DenseMatVec + \brief Type Traits to retrieve types associated with an implementation of Dune::DenseVector or Dune::DenseMatrix + + you have to specialize this class for every implementation of DenseVector or DenseMatrix. + + \code + //! export the type of the derived class (e.g. FieldVector) + typedef ... derived_type; + //! export the type of the stored values + typedef ... value_type; + //! export the type representing the size information + typedef ... size_type; + \endcode + + */ + template + struct DenseMatVecTraits {}; + +} // end namespace Dune + +#endif // DUNE_FTRAITS_HH diff --git a/dune/common/modules.txt b/dune/common/modules.txt new file mode 100644 index 0000000..4d6c6b0 --- /dev/null +++ b/dune/common/modules.txt @@ -0,0 +1,9 @@ +/* This file determines the order how things appear in the doxygen + documentation within the dune-common module. It works like this: + + @defgroup commands appear only in this file here which is + parsed before the other files (because it is mentioned first + in the Doxyfile). + + Only @addtogroup is used in the code documentation. +*/ diff --git a/dune/common/overloadset.hh b/dune/common/overloadset.hh new file mode 100644 index 0000000..1cef44b --- /dev/null +++ b/dune/common/overloadset.hh @@ -0,0 +1,157 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_OVERLOADSET_HH +#define DUNE_COMMON_OVERLOADSET_HH + +#include +#include +#include + +namespace Dune { + +namespace Impl { + + template + class OverloadSet + : public F... + { + + public: + + template + OverloadSet(FF&&... ff) + : F(std::forward(ff))... + {} + + using F::operator()...; + + }; + +} // end namespace Impl + + + +/** + * \brief Create an overload set + * + * \tparam F List of function object types + * \param f List of function objects + * + * This returns an object that contains all + * operator() implementations of the passed + * functions. All those are available when + * calling operator() of the returned object. + * + * The returned object derives from + * those implementations such that it contains + * all operator() implementations in its + * overload set. When calling operator() + * this will select the best overload. + * If multiple overload are equally good this + * will lead to ambiguity. + * + * Notice that the passed function objects are + * stored by value and must be copy-constructible. + * + * \ingroup CxxUtilities + */ +template +auto overload(F&&... f) +{ + return Impl::OverloadSet...>(std::forward(f)...); +} + + + +namespace Impl { + + template + class OrderedOverloadSet: public OrderedOverloadSet, F0 + { + using Base = OrderedOverloadSet; + public: + + template + OrderedOverloadSet(FF0&& f0, FF&&... ff) : + Base(std::forward(ff)...), + F0(std::forward(f0)) + {} + + // Forward to operator() of F0 if it can be called with the given arguments. + template::value, int> = 0> + decltype(auto) operator()(Args&&... args) + { + return F0::operator()(std::forward(args)...); + } + + // Forward to operator() of base class if F0 cannot be called with the given + // arguments. In this case the base class will successively try operator() + // of all F... . + template::value, int> = 0> + decltype(auto) operator()(Args&&... args) + { + return Base::operator()(std::forward(args)...); + } + + }; + + template + class OrderedOverloadSet: public F0 + { + public: + + template + OrderedOverloadSet(FF0&& f0) : + F0(std::forward(f0)) + {} + + // Forward to operator() of F0. If it cannot be called with + // the given arguments a static assertion will fail. + template + decltype(auto) operator()(Args&&... args) + { + static_assert(IsCallable::value, + "No matching overload found in OrderedOverloadSet"); + return F0::operator()(std::forward(args)...); + } + }; + +} // end namespace Impl + + + +/** + * \brief Create an ordered overload set + * + * \tparam F List of function object types + * \param f List of function objects + * + * This returns an object that contains all + * operator() implementations of the passed + * functions. All those are available when + * calling operator() of the returned object. + * + * In contrast to overload() these overloads + * are ordered in the sense that the first + * matching overload for the given arguments + * is selected and later ones are ignored. + * Hence such a call is never ambiguous. + * + * Notice that the passed function objects are + * stored by value and must be copy-constructible. + * + * \ingroup CxxUtilities + */ +template +auto orderedOverload(F&&... f) +{ + return Impl::OrderedOverloadSet...>(std::forward(f)...); +} + + + +} // end namespace Dune + +#endif // DUNE_COMMON_OVERLOADSET_HH diff --git a/dune/common/parallel/CMakeLists.txt b/dune/common/parallel/CMakeLists.txt new file mode 100644 index 0000000..1244770 --- /dev/null +++ b/dune/common/parallel/CMakeLists.txt @@ -0,0 +1,26 @@ +add_subdirectory(test) +add_subdirectory(benchmark) + +#install headers +install(FILES + collectivecommunication.hh + communication.hh + communicator.hh + indexset.hh + indicessyncer.hh + interface.hh + localindex.hh + mpicollectivecommunication.hh + mpicommunication.hh + mpiguard.hh + future.hh + mpifuture.hh + mpidata.hh + mpipack.hh + mpihelper.hh + mpitraits.hh + plocalindex.hh + remoteindices.hh + selection.hh + variablesizecommunicator.hh + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/common/parallel) diff --git a/dune/common/parallel/benchmark/CMakeLists.txt b/dune/common/parallel/benchmark/CMakeLists.txt new file mode 100644 index 0000000..a50296b --- /dev/null +++ b/dune/common/parallel/benchmark/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(mpi_collective_benchmark EXCLUDE_FROM_ALL mpi_collective_benchmark.cc) +dune_target_link_libraries(mpi_collective_benchmark PUBLIC "dunecommon") +add_dune_mpi_flags(mpi_collective_benchmark) + +configure_file(options.ini options.ini COPYONLY) diff --git a/dune/common/parallel/benchmark/mpi_collective_benchmark.cc b/dune/common/parallel/benchmark/mpi_collective_benchmark.cc new file mode 100644 index 0000000..480c21e --- /dev/null +++ b/dune/common/parallel/benchmark/mpi_collective_benchmark.cc @@ -0,0 +1,316 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +/** + * @brief Benchmark for measure the possible overlap of computation + * and communication at MPI collective communications. + * + * This benchmark is inspired by the sandia micro benchmark: + * W. Lawry, C. Wilson, A. Maccabe, R. Brightwell. COMB: A Portable Benchmark + * Suite for Assessing MPI Overlap. In Proceedings of the IEEE International + * Conference on Cluster Computing (CLUSTER 2002), p. 472, 2002. + * http://www.cs.sandia.gov/smb/overhead.html + * + * The following communication times are measured: + * Blocking: Blocking call. E.g. MPI_Allreduce + * Nonblocking_wait (NB_Wait): Nonblocking (e.g. MPI_Iallreduce) call + * directly followed by MPI_Wait. + * Nonblocking_sleep (NB_Sleep): Nonblocking call followed by a busy wait + * until the work time has passed. Then + * MPI_Wait. + * Nonblocking_active (NB_active): Nonblocking call followed by a basy wait + * where in every iteration MPI_Test is + * called until the work time has passed. + * The MPI_wait. + * + * The overhead is computed as the time for the Nonblocking call plus + * the time for MPI_Wait. The iteration time is the time for the whole + * communication. The available part of the communication + * time(avail(%)) is computed as 1-(overhead/base_t), where base_t is + * the time for calling the method with wait time = 0. The overhead is + * determined by increasing the work time successive until it is the + * dominant factor in the iteration time. Then the overhead is + * computed as iter_t-work_t. + * + * Usage: mpirun ./mpi_collective_benchmark [options] + * + * options: + * -method: default: allreduce. + * possible methods: allreduce, barrier, + * broadcast, gather, allgather, scatter + * -iterations: default: 10000. Number of iterations for + * measure the time for one communication + * -allMethods: defaukt:0. If 1 iterates over all available methods + * -startSize: default: n, where n is the size of MPI_COMM_WORLD. runs + * the benchmark for different communicator sizes, starting with + * startSize. After every run the size is doubled. Finally one run is + * made for the whole communicator. + * -verbose: default: 0. If 1 prints intermediate information while determining + * the overhead. + * -threshold: default: 2. The threshold when the work time is the dominant + * factor in the iteration time. (Similar to the threshold in the + * sandia benchmark) + * -nohdr: default: 0. Suppress output of the header. + * + * options can be set either in the options.ini file or can be pass at + * the command-line (-key value). + * + * To get a good 'available' value for the NB_sleep communication, some + * MPI implementation need to spawn an extra thread. With MPICH you + * can activate this by setting the environment variable + * MPI_ASYNC_PROGRESS to 1, with IntelMPI the variable is called + * I_MPI_ASYNC_PROGRESS. + * (https://software.intel.com/en-us/mpi-developer-reference-linux-asynchronous-progress-control) + */ + + +#include + +#include +#include +#include + +#include +#include +#include +#include + +Dune::ParameterTree options; +std::vector all_methods = {"allreduce", + "barrier", + "broadcast", + "gather", + "allgather", + "scatter"}; + +template +void communicate(CC& cc){ + auto method = options.get("method", "allreduce"); + std::vector data(1, 42); + if(method == "allreduce"){ + cc.template allreduce>(data); + return; + } + if(method == "barrier"){ + cc.barrier(); + return; + } + if(method == "broadcast"){ + cc.broadcast(data.data(), data.size(), 0); + return; + } + if(method == "gather"){ + std::vector recv_data(cc.size(), 0); + cc.gather(data.data(), recv_data.data(), 1, 0); + return; + } + if(method == "allgather"){ + std::vector recv_data(cc.size(), 0); + cc.allgather(data.data(), 1, recv_data.data()); + return; + } + if(method == "scatter"){ + std::vector send_data(cc.size(), 42); + cc.scatter(send_data.data(), data.data(), 1, 0); + return; + } + DUNE_THROW(Dune::Exception, "Unknown method"); +} + +template +Dune::Future startCommunication(CC& cc){ + auto method = options.get("method", "allreduce"); + if(method == "allreduce"){ + return cc.template iallreduce>(42); + } + if(method == "barrier"){ + return cc.ibarrier(); + } + if(method == "broadcast"){ + return cc.ibroadcast(42, 0); + } + if(method == "gather"){ + return cc.igather(42, std::vector(cc.size()), 0); + } + if(method == "allgather"){ + return cc.iallgather(42, std::vector(cc.size())); + } + if(method == "scatter"){ + return cc.iscatter(std::vector(cc.size(), 42), 0, 0); + } + DUNE_THROW(Dune::Exception, "Unknown method"); +} + +template +double runBlocking(CC& cc){ + std::vector answer(1, 42); + int iterations = options.get("iterations", 1000); + Dune::Timer watch; + for(int i = 0; i < iterations; i++){ + cc.barrier(); + watch.start(); + communicate(cc); + watch.stop(); + } + return cc.sum(watch.elapsed())/iterations/cc.size(); +} + +template +double runNonblockingWait(CC& cc){ + std::vector answer(1, 42); + Dune::Timer watch; + int iterations = options.get("iterations", 1000); + for(int i = 0; i < iterations; i++){ + cc.barrier(); + watch.start(); + auto f = startCommunication(cc); + f.wait(); + watch.stop(); + } + return cc.sum(watch.elapsed())/iterations/cc.size(); +} + +std::tuple runNonblockingSleep(decltype(Dune::MPIHelper::getCommunication())& cc, std::chrono::duration wait_time){ + std::vector answer(1, 42); + Dune::Timer watch, watch_work; + int iterations = options.get("iterations", 1000); + for(int i = 0; i < iterations; i++){ + cc.barrier(); + watch.start(); + auto f = startCommunication(cc); + watch_work.start(); + auto start_time = std::chrono::high_resolution_clock::now(); + while(std::chrono::high_resolution_clock::now()-start_time < wait_time); + watch_work.stop(); + f.wait(); + watch.stop(); + } + return std::tuple(cc.sum(watch.stop())/iterations/cc.size(), + cc.sum(watch_work.stop())/iterations/cc.size()); +} + +std::tuple runNonblockingActive(decltype(Dune::MPIHelper::getCommunication())& cc, std::chrono::duration wait_time){ + std::vector answer(1, 42); + int iterations = options.get("iterations", 1000); + Dune::Timer watch, watch_work; + for(int i = 0; i < iterations; i++){ + cc.barrier(); + watch.start(); + auto f = startCommunication(cc); + watch_work.start(); + auto start_time = std::chrono::high_resolution_clock::now(); + while(std::chrono::high_resolution_clock::now()-start_time < wait_time) + f.ready(); + watch_work.stop(); + f.wait(); + watch.stop(); + } + // return the time spend in communication methods + return std::tuple(cc.sum(watch.stop())/iterations/cc.size(), + cc.sum(watch_work.stop())/iterations/cc.size()); +} + +/* Increases the work until it is the dominant factor in the iteration + time. Returns the base time and how much of it is available for + computations(%). It is computed with the formula 1-(overhead/base_t). + */ +std::tuple determineOverlap(std::function(std::chrono::duration)> fun) +{ + double base_t = 0; + std::tie(base_t, std::ignore) = fun(std::chrono::duration(0)); + if(options.get("verbose", 0)) + std::cout << std::endl << std::endl << std::setw(12) << "base_t:" << base_t << std::endl; + double iter_t = 0; + double work_t = 0; + int i = 1; + double iter_t_threshold = options.get("threshold", 2.0); + for(double work = 0.25*base_t; iter_t < iter_t_threshold*base_t; work *= 2, i++){ + std::tie(iter_t, work_t) = fun(std::chrono::duration(work)); + if(options.get("verbose", 0)) + std::cout << i << std::setw(12) << " iter_t:" << std::setw(12) << iter_t + << std::setw(12) << " work_t:" << std::setw(12) << work_t << std::endl; + } + double overhead = iter_t-work_t; + double avail = 1.0-overhead/base_t; + if(options.get("verbose", 0)) + std::cout << std::setw(12) << " ovhd:" << std::setw(12) << overhead + << std::setw(12) << " available:" << std::setw(12) << avail << std::endl; + return std::tuple(base_t, avail); +} + +void printHeader(){ + if(options.get("nohdr", 0) == 0){ + std::cout << "Method: " << options.get("method", "allreduce") << std::endl; + std::cout << std::scientific; + std::cout << std::setw(10) << "commsize" + << std::setw(12) << "iterations" + << std::setw(16) << "Blocking" + << std::setw(16) << "NB_wait" + << std::setw(16) << "NB_sleep" + << std::setw(12) << "avail(%)" + << std::setw(16) << "NB_active" + << std::setw(12) << "avail(%)" + << std::endl; + } +} + +void run(int s){ + auto comm_world = Dune::MPIHelper::getCommunication(); + Dune::MPIHelper::MPICommunicator comm; + #if HAVE_MPI + MPI_Comm_split(comm_world, comm_world.rank() < s, comm_world.rank(), &comm); + #endif + if(comm_world.rank() < s){ + Dune::Communication cc(comm); + std::cout << std::setw(10) << cc.size() + << std::setw(12) << options.get("iterations", 1000) << std::flush; + + double blocking_t = runBlocking(cc); + std::cout << std::setw(16) << blocking_t << std::flush; + + double nb_wait_t = runNonblockingWait(cc); + std::cout << std::setw(16) << nb_wait_t << std::flush; + + using namespace std::placeholders; + auto nb_sleep = std::bind(runNonblockingSleep, std::ref(cc), _1); + double nb_sleep_t, nb_sleep_avail; + std::tie(nb_sleep_t, nb_sleep_avail) = determineOverlap(nb_sleep); + std::cout << std::setw(16) << nb_sleep_t + << std::setw(12) << std::fixed << std::setprecision(2) << 100*nb_sleep_avail + << std::scientific << std::setprecision(6) << std::flush; + + auto nb_active = std::bind(runNonblockingActive, cc, _1); + double nb_active_t, nb_active_avail; + std::tie(nb_active_t, nb_active_avail) = determineOverlap(nb_active); + std::cout << std::setw(16) << nb_active_t + << std::setw(12) << std::fixed << std::setprecision(2) << 100*nb_active_avail + << std::scientific << std::setprecision(6) << std::endl; + } +} + +int main(int argc, char** argv){ + Dune::MPIHelper& mpihelper = Dune::MPIHelper::instance(argc, argv); + + // disable output on almost all ranks + if(mpihelper.rank() != 0) + std::cout.setstate(std::ios_base::failbit); + // parse options + Dune::ParameterTreeParser::readINITree("options.ini", options); + Dune::ParameterTreeParser::readOptions(argc, argv, options); + + std::vector methods = {options.get("method", "allreduce")}; + if(options.get("allMethods", 0) == 1) + methods = std::vector(all_methods); + for(std::string method : methods){ + options["method"] = method; + std::cout << std::left << std::scientific; + printHeader(); + int s = options.get("startSize", mpihelper.size()); + while(s < mpihelper.size()){ + run(s); + s *= 2; + } + run(mpihelper.size()); + } + return 0; +} diff --git a/dune/common/parallel/benchmark/options.ini b/dune/common/parallel/benchmark/options.ini new file mode 100644 index 0000000..9b96b69 --- /dev/null +++ b/dune/common/parallel/benchmark/options.ini @@ -0,0 +1,5 @@ +iterations = 10000 +method = "allreduce" +allMethods = 0 +threshold = 2.0 +# startSize = 1 \ No newline at end of file diff --git a/dune/common/parallel/collectivecommunication.hh b/dune/common/parallel/collectivecommunication.hh new file mode 100644 index 0000000..f80beb5 --- /dev/null +++ b/dune/common/parallel/collectivecommunication.hh @@ -0,0 +1,3 @@ +// Will be removed after the 2.7 release +#warning "Deprecated header, use #include instead!" +#include diff --git a/dune/common/parallel/communication.hh b/dune/common/parallel/communication.hh new file mode 100644 index 0000000..5f542ae --- /dev/null +++ b/dune/common/parallel/communication.hh @@ -0,0 +1,538 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_PARALLEL_COMMUNICATION_HH +#define DUNE_COMMON_PARALLEL_COMMUNICATION_HH +/*! + \file + \brief Implements an utility class that provides + collective communication methods for sequential programs. + + \ingroup ParallelCommunication + */ +#include +#include +#include +#include + +#include +#include +#include + +/*! \defgroup ParallelCommunication Parallel Communication + \ingroup Common + + \brief Abstractions for parallel computing + + Dune offers an abstraction to the basic methods of parallel + communication. It allows one to switch parallel features on and off, + without changing the code. This is done using either Communication + or MPICommunication. + + */ + +/*! + \file + \brief An abstraction to the basic methods of parallel communication, + following the message-passing paradigm. + \ingroup ParallelCommunication + */ + +namespace Dune +{ + + /* define some type that definitely differs from MPI_Comm */ + struct No_Comm {}; + + /*! @brief Comparison operator for MPI compatibility + + Always returns true. + */ + inline bool operator==(const No_Comm&, const No_Comm&) + { + return true; + } + + /*! @brief Comparison operator for MPI compatibility + + Always returns false. + */ + inline bool operator!=(const No_Comm&, const No_Comm&) + { + return false; + } + + /*! @brief Collective communication interface and sequential default implementation + + Communication offers an abstraction to the basic methods + of parallel communication, following the message-passing + paradigm. It allows one to switch parallel features on and off, without + changing the code. Currently only MPI and sequential code are + supported. + + A Communication object is returned by all grids (also + the sequential ones) in order to allow code to be written in + a transparent way for sequential and parallel grids. + + This class provides a default implementation for sequential grids. + The number of processes involved is 1, any sum, maximum, etc. returns + just its input argument and so on. + + In specializations one can implement the real thing using appropriate + communication functions, e.g. there exists an implementation using + the Message Passing %Interface (MPI), see Dune::Communication. + + Moreover, the communication subsystem used by an implementation + is not visible in the interface, i.e. Dune grid implementations + are not restricted to MPI. + + \tparam Communicator The communicator type used by your message-passing implementation. + For MPI this will be MPI_Comm. For sequential codes there is the dummy communicator No_Comm. + It is assumed that if you want to specialize the Communication class for a + message-passing system other than MPI, that message-passing system will have something + equivalent to MPI communicators. + + \ingroup ParallelCommunication + */ + template + class Communication + { + public: + //! Construct default object + Communication() + {} + + /** \brief Constructor with a given communicator + * + * As this is implementation for the sequential setting, the communicator is a dummy and simply discarded. + */ + Communication (const Communicator&) + {} + + //! Return rank, is between 0 and size()-1 + int rank () const + { + return 0; + } + + //! cast to the underlying Fake MPI communicator + operator No_Comm() const + { + return {}; + } + + //! Number of processes in set, is greater than 0 + int size () const + { + return 1; + } + + /** @brief Sends the data to the dest_rank + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template + int send([[maybe_unused]] const T& data, + [[maybe_unused]] int dest_rank, + [[maybe_unused]] int tag) + { + DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); + } + + /** @brief Sends the data to the dest_rank nonblocking + @returns Future containing the send buffer, completes when data is send + */ + template + PseudoFuture isend([[maybe_unused]] const T&& data, + [[maybe_unused]] int dest_rank, + [[maybe_unused]] int tag) + { + DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); + } + + /** @brief Receives the data from the source_rank + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template + T recv([[maybe_unused]] T&& data, + [[maybe_unused]] int source_rank, + [[maybe_unused]] int tag, + [[maybe_unused]] void* status = 0) + { + DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); + } + + /** @brief Receives the data from the source_rank nonblocking + @returns Future containing the received data when complete + */ + template + PseudoFuture irecv([[maybe_unused]] T&& data, + [[maybe_unused]] int source_rank, + [[maybe_unused]] int tag) + { + DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); + } + + template + T rrecv([[maybe_unused]] T&& data, + [[maybe_unused]] int source_rank, + [[maybe_unused]] int tag, + [[maybe_unused]] void* status = 0) const + { + DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); + } + /** @brief Compute the sum of the argument over all processes and + return the result in every process. Assumes that T has an operator+ + */ + template + T sum (const T& in) const + { + return in; + } + + /** @brief Compute the sum over all processes for each component of an array and return the result + in every process. Assumes that T has an operator+ + + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template + int sum ([[maybe_unused]] T* inout, [[maybe_unused]] int len) const + { + return 0; + } + + /** @brief Compute the product of the argument over all processes and + return the result in every process. Assumes that T has an operator* + */ + template + T prod (const T& in) const + { + return in; + } + + /** @brief Compute the product over all processes + for each component of an array and return the result + in every process. Assumes that T has an operator* + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template + int prod ([[maybe_unused]] T* inout, [[maybe_unused]] int len) const + { + return 0; + } + + /** @brief Compute the minimum of the argument over all processes and + return the result in every process. Assumes that T has an operator< + */ + template + T min (const T& in) const + { + return in; + } + + /** @brief Compute the minimum over all processes + for each component of an array and return the result + in every process. Assumes that T has an operator< + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template + int min ([[maybe_unused]] T* inout, [[maybe_unused]] int len) const + { + return 0; + } + + /** @brief Compute the maximum of the argument over all processes and + return the result in every process. Assumes that T has an operator< + */ + template + T max (const T& in) const + { + return in; + } + + /** @brief Compute the maximum over all processes + for each component of an array and return the result + in every process. Assumes that T has an operator< + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template + int max ([[maybe_unused]] T* inout, [[maybe_unused]] int len) const + { + return 0; + } + + /** @brief Wait until all processes have arrived at this point in the program. + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + int barrier () const + { + return 0; + } + + /** @brief Nonblocking barrier + @returns Future which is complete when all processes have reached the barrier + */ + PseudoFuture ibarrier () const + { + return {true}; // return a valid future + } + + /** @brief Distribute an array from the process with rank root to all other processes + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template + int broadcast ([[maybe_unused]] T* inout, + [[maybe_unused]] int len, + [[maybe_unused]] int root) const + { + return 0; + } + + /** @brief Distribute an array from the process with rank root to all other processes nonblocking + @returns Future containing the distributed data + */ + template + PseudoFuture ibroadcast(T&& data, int root) const{ + return {std::forward(data)}; + } + + + /** @brief Gather arrays on root task. + * + * Each process sends its in array of length len to the root process + * (including the root itself). In the root process these arrays are stored in rank + * order in the out array which must have size len * number of processes. + * @param[in] in The send buffer with the data to send. + * @param[out] out The buffer to store the received data in. Might have length zero on non-root + * tasks. + * @param[in] len The number of elements to send on each task. + * @param[in] root The root task that gathers the data. + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template + int gather (const T* in, T* out, int len, [[maybe_unused]] int root) const // note out must have same size as in + { + for (int i=0; i containing the gathered data + */ + template> + PseudoFuture igather(TIN&& data_in, TOUT&& data_out, int root){ + *(data_out.begin()) = std::forward(data_in); + return {std::forward(data_out)}; + } + + + /** @brief Gather arrays of variable size on root task. + * + * Each process sends its in array of length sendDataLen to the root process + * (including the root itself). In the root process these arrays are stored in rank + * order in the out array. + * @param[in] in The send buffer with the data to be sent + * @param[in] sendDataLen The number of elements to send on each task + * @param[out] out The buffer to store the received data in. May have length zero on non-root + * tasks. + * @param[in] recvDataLen An array with size equal to the number of processes containing the number + * of elements to receive from process i at position i, i.e. the number that + * is passed as sendDataLen argument to this function in process i. + * May have length zero on non-root tasks. + * @param[out] displ An array with size equal to the number of processes. Data received from + * process i will be written starting at out+displ[i] on the root process. + * May have length zero on non-root tasks. + * @param[in] root The root task that gathers the data. + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template + int gatherv (const T* in, + int sendDataLen, + T* out, + [[maybe_unused]] int* recvDataLen, + int* displ, + [[maybe_unused]] int root) const + { + for (int i=*displ; i + int scatter (const T* sendData, T* recvData, int len, [[maybe_unused]] int root) const // note out must have same size as in + { + for (int i=0; i containing scattered data; + */ + template + PseudoFuture iscatter(TIN&& data_in, TOUT&& data_out, int root){ + data_out = *(std::forward(data_in).begin()); + return {std::forward(data_out)}; + } + + /** @brief Scatter arrays of variable length from a root to all other tasks. + * + * The root process sends the elements with index from send+displ[k] to send+displ[k]-1 in + * its array to task k, which stores it at index 0 to recvDataLen-1. + * @param[in] sendData The array to scatter. May have length zero on non-root + * tasks. + * @param[in] sendDataLen An array with size equal to the number of processes containing the number + * of elements to scatter to process i at position i, i.e. the number that + * is passed as recvDataLen argument to this function in process i. + * @param[in] displ An array with size equal to the number of processes. Data scattered to + * process i will be read starting at send+displ[i] on root the process. + * @param[out] recvData The buffer to store the received data in. Upon completion of the + * method each task will have the same data stored there as the one in + * send buffer of the root task before. + * @param[in] recvDataLen The number of elements in the recvData buffer. + * @param[in] root The root task that gathers the data. + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template + int scatterv (const T* sendData,int* sendDataLen, int* displ, T* recvData, + [[maybe_unused]] int recvDataLen, [[maybe_unused]] int root) const + { + for (int i=*displ; i<*sendDataLen; i++) + recvData[i] = sendData[i]; + return 0; + } + + /** + * @brief Gathers data from all tasks and distribute it to all. + * + * The block of data sent from the jth process is received by every + * process and placed in the jth block of the buffer recvbuf. + * + * @param[in] sbuf The buffer with the data to send. Has to be the same for + * each task. + * @param[in] count The number of elements to send by any process. + * @param[out] rbuf The receive buffer for the data. Has to be of size + * notasks*count, with notasks being the number of tasks in the communicator. + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template + int allgather(const T* sbuf, int count, T* rbuf) const + { + for(const T* end=sbuf+count; sbuf < end; ++sbuf, ++rbuf) + *rbuf=*sbuf; + return 0; + } + + /** + * @brief Gathers data from all tasks and distribute it to all nonblocking. + @returns Future containing the distributed data + */ + template + PseudoFuture iallgather(TIN&& data_in, TOUT&& data_out){ + return {std::forward(data_out)}; + } + + /** + * @brief Gathers data of variable length from all tasks and distribute it to all. + * + * The block of data sent from the jth process is received by every + * process and placed in the jth block of the buffer out. + * + * @param[in] in The send buffer with the data to send. + * @param[in] sendDataLen The number of elements to send on each task. + * @param[out] out The buffer to store the received data in. + * @param[in] recvDataLen An array with size equal to the number of processes containing the number + * of elements to receive from process i at position i, i.e. the number that + * is passed as sendDataLen argument to this function in process i. + * @param[in] displ An array with size equal to the number of processes. Data received from + * process i will be written starting at out+displ[i]. + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template + int allgatherv (const T* in, int sendDataLen, T* out, [[maybe_unused]] int* recvDataLen, int* displ) const + { + for (int i=*displ; i + int allreduce([[maybe_unused]] Type* inout, [[maybe_unused]] int len) const + { + return 0; + } + + /** + * @brief Compute something over all processes nonblocking + @return Future containing the computed something + */ + template + PseudoFuture iallreduce(TIN&& data_in, TOUT&& data_out){ + data_out = std::forward(data_in); + return {std::forward(data_out)}; + } + + /** + * @brief Compute something over all processes nonblocking and in-place + @return Future containing the computed something + */ + template + PseudoFuture iallreduce(T&& data){ + return {std::forward(data)}; + } + + + /** + * @brief Compute something over all processes + * for each component of an array and return the result + * in every process. + * + * The template parameter BinaryFunction is the type of + * the binary function to use for the computation + * + * @param in The array to compute on. + * @param out The array to store the results in. + * @param len The number of components in the array + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template + int allreduce(const Type* in, Type* out, int len) const + { + std::copy(in, in+len, out); + return 0; + } + + }; + + template + using CollectiveCommunication + // Will be deprecated after the 2.7 release + //[[deprecated("CollectiveCommunication is deprecated. Use Communication instead.")]] + = Communication; +} + +#endif diff --git a/dune/common/parallel/communicator.hh b/dune/common/parallel/communicator.hh new file mode 100644 index 0000000..1e97f3e --- /dev/null +++ b/dune/common/parallel/communicator.hh @@ -0,0 +1,1544 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMUNICATOR +#define DUNE_COMMUNICATOR + +#if HAVE_MPI + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace Dune +{ + /** @defgroup Common_Parallel Parallel Computing based on Indexsets + * @ingroup ParallelCommunication + * @brief Provides classes for syncing distributed indexed + * data structures. + * + * In a parallel representation a container \f$x\f$, + * e.g. a plain C-array, cannot be stored with all entries on each process + * because of limited memory and efficiency reasons. Therefore + * it is represented by individual + * pieces \f$x_p\f$, \f$p=0, \ldots, P-1\f$, where \f$x_p\f$ is the piece stored on + * process \f$p\f$ of the \f$P\f$ processes participating in the calculation. + * Although the global representation of the container is not + * available on any process, a process \f$p\f$ needs to know how the entries + * of it's local piece \f$x_p\f$ correspond to the entries of the global + * container \f$x\f$, which would be used in a sequential program. In this + * module we present classes describing the mapping of the local pieces + * to the global + * view and the communication interfaces. + * + * @section IndexSet Parallel Index Sets + * + * Form an abstract point of view a random access container \f$x: I + * \rightarrow K\f$ provides a + * mapping from an index set \f$I \subset N_0\f$ onto a set of objects + * \f$K\f$. Note that we do not require \f$I\f$ to be consecutive. The piece + * \f$x_p\f$ of the container \f$x\f$ stored on process \f$p\f$ is a mapping \f$x_p:I_p + * \rightarrow K\f$, where \f$I_p \subset I\f$. Due to efficiency the entries + * of \f$x_p\f$ should be stored in consecutive memory. + * + * This means that for the local computation the data must be addressable + * by a consecutive index starting from \f$0\f$. When using adaptive + * discretisation methods there might be the need to reorder the indices + * after adding and/or deleting some of the discretisation + * points. Therefore this index does not have to be persistent. Further + * on we will call this index local index. + * + * For the communication phases of our algorithms these locally stored + * entries must also be addressable by a global identifier to be able to + * store the received values tagged with the global identifiers at the + * correct local index in the consecutive local memory chunk. To ease the + * addition and removal of discretisation points this global identifier has + * to be persistent. Further on we will call this global identifier + * global index. + * + * Classes to build the mapping are ParallelIndexSet and ParallelLocalIndex. + * As these just provide a mapping from the global index to the local index, + * the wrapper class GlobalLookupIndexSet facilitates the reverse lookup. + * + * @section remote Remote Index Information + * + * To setup communication between the processes every process needs to + * know what indices are also known to other processes and what + * attributes are attached to them on the remote side. This information is + * calculated and encapsulated in class RemoteIndices. + * + * @section comm Communication + * + * Based on the information about the distributed index sets, data + * independent interfaces between different sets of the index sets + * can be setup using the class Interface. For the actual communication + * data dependent communicators can be setup using BufferedCommunicator, + * DatatypeCommunicator VariableSizeCommunicator based on the interface + * information. In contrast to the former + * the latter is independent of the class Interface can work on a map + * from process number to a pair of index lists describing which local indices + * are send and received from that processs, respectively. + */ + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Provides utility classes for syncing distributed data via + * MPI communication. + * @author Markus Blatt + */ + + /** + * @brief Flag for marking indexed data structures where data at + * each index is of the same size. + * @see VariableSize + */ + struct SizeOne + {}; + + /** + * @brief Flag for marking indexed data structures where the data at each index may + * be a variable multiple of another type. + * @see SizeOne + */ + struct VariableSize + {}; + + + /** + * @brief Default policy used for communicating an indexed type. + * + * This + */ + template + struct CommPolicy + { + /** + * @brief The type the policy is for. + * + * It has to provide the mode + * \code Type::IndexedType operator[](int i);\endcode + * for + * the access of the value at index i and a typedef IndexedType. + * It is assumed + * that only one entry is at each index (as in scalar + * vector. + */ + typedef V Type; + + /** + * @brief The type we get at each index with operator[]. + * + * The default is the value_type typedef of the container. + */ + typedef typename V::value_type IndexedType; + + /** + * @brief Whether the indexed type has variable size or there + * is always one value at each index. + */ + typedef SizeOne IndexedTypeFlag; + + /** + * @brief Get the address of entry at an index. + * + * The default implementation uses operator[] to + * get the address. + * @param v An existing representation of the type that has more elements than index. + * @param index The index of the entry. + */ + static const void* getAddress(const V& v, int index); + + /** + * @brief Get the number of primitve elements at that index. + * + * The default always returns 1. + */ + static int getSize(const V&, int index); + }; + + template class FieldVector; + + template class VariableBlockVector; + + template + struct CommPolicy, A> > + { + typedef VariableBlockVector, A> Type; + + typedef typename Type::B IndexedType; + + typedef VariableSize IndexedTypeFlag; + + static const void* getAddress(const Type& v, int i); + + static int getSize(const Type& v, int i); + }; + + /** + * @brief Error thrown if there was a problem with the communication. + */ + class CommunicationError : public IOError + {}; + + /** + * @brief GatherScatter default implementation that just copies data. + */ + template + struct CopyGatherScatter + { + typedef typename CommPolicy::IndexedType IndexedType; + + static const IndexedType& gather(const T& vec, std::size_t i); + + static void scatter(T& vec, const IndexedType& v, std::size_t i); + + }; + + /** + * @brief An utility class for communicating distributed data structures via MPI datatypes. + * + * This communicator creates special MPI datatypes that address the non contiguous elements + * to be send and received. The idea was to prevent the copying to an additional buffer and + * the mpi implementation decide whether to allocate buffers or use buffers offered by the + * interconnection network. + * + * Unfortunately the implementation of MPI datatypes seems to be poor. Therefore for most MPI + * implementations using a BufferedCommunicator will be more efficient. + */ + template + class DatatypeCommunicator : public InterfaceBuilder + { + public: + + /** + * @brief Type of the index set. + */ + typedef T ParallelIndexSet; + + /** + * @brief Type of the underlying remote indices class. + */ + typedef Dune::RemoteIndices RemoteIndices; + + /** + * @brief The type of the global index. + */ + typedef typename RemoteIndices::GlobalIndex GlobalIndex; + + /** + * @brief The type of the attribute. + */ + typedef typename RemoteIndices::Attribute Attribute; + + /** + * @brief The type of the local index. + */ + typedef typename RemoteIndices::LocalIndex LocalIndex; + + /** + * @brief Creates a new DatatypeCommunicator. + */ + DatatypeCommunicator(); + + /** + * @brief Destructor. + */ + ~DatatypeCommunicator(); + + /** + * @brief Builds the interface between the index sets. + * + * Has to be called before the actual communication by forward or backward + * can be called. Nonpublic indices will be ignored! + * + * + * The types T1 and T2 are classes representing a set of + * enumeration values of type DatatypeCommunicator::Attribute. + * They have to provide + * a (static) method + * \code + * bool contains(Attribute flag) const; + * \endcode + * for checking whether the set contains a specific flag. + * This functionality is for example provided the classes + * EnumItem, EnumRange and Combine. + * + * @param remoteIndices The indices present on remote processes. + * @param sourceFlags The set of attributes which mark indices we send to other + * processes. + * @param sendData The indexed data structure whose data will be send. + * @param destFlags The set of attributes which mark the indices we might + * receive values from. + * @param receiveData The indexed data structure for which we receive data. + */ + template + void build(const RemoteIndices& remoteIndices, const T1& sourceFlags, V& sendData, const T2& destFlags, V& receiveData); + + /** + * @brief Sends the primitive values from the source to the destination. + */ + void forward(); + + /** + * @brief Sends the primitive values from the destination to the source. + */ + void backward(); + + /** + * @brief Deallocates the MPI requests and data types. + */ + void free(); + private: + enum { + /** + * @brief Tag for the MPI communication. + */ + commTag_ = 234 + }; + + /** + * @brief The indices also known at other processes. + */ + const RemoteIndices* remoteIndices_; + + typedef std::map > + MessageTypeMap; + + /** + * @brief The datatypes built according to the communication interface. + */ + MessageTypeMap messageTypes; + + /** + * @brief The pointer to the data whose entries we communicate. + */ + void* data_; + + MPI_Request* requests_[2]; + + /** + * @brief True if the request and data types were created. + */ + bool created_; + + /** + * @brief Creates the MPI_Requests for the forward communication. + */ + template + void createRequests(V& sendData, V& receiveData); + + /** + * @brief Creates the data types needed for the unbuffered receive. + */ + template + void createDataTypes(const T1& source, const T2& destination, V& data); + + /** + * @brief Initiates the sending and receive. + */ + void sendRecv(MPI_Request* req); + + /** + * @brief Information used for setting up the MPI Datatypes. + */ + struct IndexedTypeInformation + { + /** + * @brief Allocate space for setting up the MPI datatype. + * + * @param i The number of values the datatype will have. + */ + void build(int i) + { + length = new int[i]; + displ = new MPI_Aint[i]; + size = i; + } + + /** + * @brief Free the allocated space. + */ + void free() + { + delete[] length; + delete[] displ; + } + /** @brief The number of values at each index. */ + int* length; + /** @brief The displacement at each index. */ + MPI_Aint* displ; + /** + * @brief The number of elements we send. + * In case of variable sizes this will differ from + * size. + */ + int elements; + /** + * @param The number of indices in the data type. + */ + int size; + }; + + /** + * @brief Functor for the InterfaceBuilder. + * + * It will record the information needed to build the MPI_Datatypes. + */ + template + struct MPIDatatypeInformation + { + /** + * @brief Constructor. + * @param data The data we construct an MPI data type for. + */ + MPIDatatypeInformation(const V& data) : data_(data) + {} + + /** + * @brief Reserver space for the information about the datatype. + * @param proc The rank of the process this information is for. + * @param size The number of indices the datatype will contain. + */ + void reserve(int proc, int size) + { + information_[proc].build(size); + } + /** + * @brief Add a new index to the datatype. + * @param proc The rank of the process this index is send to + * or received from. + * @param local The index to add. + */ + void add(int proc, int local) + { + IndexedTypeInformation& info=information_[proc]; + assert((info.elements)(CommPolicy::getAddress(data_, local)), + info.displ+info.elements); + info.length[info.elements]=CommPolicy::getSize(data_, local); + info.elements++; + } + + /** + * @brief The information about the datatypes to send to or + * receive from each process. + */ + std::map information_; + /** + * @brief A representative of the indexed data we send. + */ + const V& data_; + + }; + + }; + + /** + * @brief A communicator that uses buffers to gather and scatter + * the data to be send or received. + * + * Before the data is sent it is copied to a consecutive buffer and + * then that buffer is sent. + * The data is received in another buffer and then copied to the actual + * position. + */ + class BufferedCommunicator + { + + public: + /** + * @brief Constructor. + */ + BufferedCommunicator(); + + /** + * @brief Build the buffers and information for the communication process. + * + * + * @param interface The interface that defines what indices are to be communicated. + */ + template + typename std::enable_if::IndexedTypeFlag>::value, void>::type + build(const Interface& interface); + + /** + * @brief Build the buffers and information for the communication process. + * + * @param source The source in a forward send. The values will be copied from here to the send buffers. + * @param target The target in a forward send. The received values will be copied to here. + * @param interface The interface that defines what indices are to be communicated. + */ + template + void build(const Data& source, const Data& target, const Interface& interface); + + /** + * @brief Send from source to target. + * + * The template parameter GatherScatter (e.g. CopyGatherScatter) has to have a static method + * \code + * // Gather the data at index index of data + * static const typename CommPolicy::IndexedType>& gather(Data& data, int index); + * + * // Scatter the value at a index of data + * static void scatter(Data& data, typename CommPolicy::IndexedType> value, + * int index); + * \endcode + * in the case where CommPolicy::IndexedTypeFlag is SizeOne + * and + * + * \code + * static const typename CommPolicy::IndexedType> gather(Data& data, int index, int subindex); + * + * static void scatter(Data& data, typename CommPolicy::IndexedType> value, + * int index, int subindex); + * \endcode + * in the case where CommPolicy::IndexedTypeFlag is VariableSize. Here subindex is the + * subindex of the block at index. + * @warning The source and target data have to have the same layout as the ones given + * to the build function in case of variable size values at the indices. + * @param source The values will be copied from here to the send buffers. + * @param dest The received values will be copied to here. + */ + template + void forward(const Data& source, Data& dest); + + /** + * @brief Communicate in the reverse direction, i.e. send from target to source. + * + * The template parameter GatherScatter (e.g. CopyGatherScatter) has to have a static method + * \code + * // Gather the data at index index of data + * static const typename CommPolicy::IndexedType>& gather(Data& data, int index); + * + * // Scatter the value at a index of data + * static void scatter(Data& data, typename CommPolicy::IndexedType> value, + * int index); + * \endcode + * in the case where CommPolicy::IndexedTypeFlag is SizeOne + * and + * + * \code + * static const typename CommPolicy::IndexedType> gather(Data& data, int index, int subindex); + * + * static void scatter(Data& data, typename CommPolicy::IndexedType> value, + * int index, int subindex); + * \endcode + * in the case where CommPolicy::IndexedTypeFlag is VariableSize. Here subindex is the + * subindex of the block at index. + * @warning The source and target data have to have the same layout as the ones given + * to the build function in case of variable size values at the indices. + * @param dest The values will be copied from here to the send buffers. + * @param source The received values will be copied to here. + */ + template + void backward(Data& source, const Data& dest); + + /** + * @brief Forward send where target and source are the same. + * + * The template parameter GatherScatter has to have a static method + * \code + * // Gather the data at index index of data + * static const typename CommPolicy::IndexedType>& gather(Data& data, int index); + * + * // Scatter the value at a index of data + * static void scatter(Data& data, typename CommPolicy::IndexedType> value, + * int index); + * \endcode + * in the case where CommPolicy::IndexedTypeFlag is SizeOne + * and + * + * \code + * static const typename CommPolicy::IndexedType> gather(Data& data, int index, int subindex); + * + * static void scatter(Data& data, typename CommPolicy::IndexedType> value, + * int index, int subindex); + * \endcode + * in the case where CommPolicy::IndexedTypeFlag is VariableSize. Here subindex is the + * subindex of the block at index. + * @param data Source and target of the communication. + */ + template + void forward(Data& data); + + /** + * @brief Backward send where target and source are the same. + * + * The template parameter GatherScatter has to have a static method + * \code + * // Gather the data at index index of data + * static const typename CommPolicy::IndexedType>& gather(Data& data, int index); + * + * // Scatter the value at a index of data + * static void scatter(Data& data, typename CommPolicy::IndexedType> value, + * int index); + * \endcode + * in the case where CommPolicy::IndexedTypeFlag is SizeOne + * and + * + * \code + * static const typename CommPolicy::IndexedType> gather(Data& data, int index, int subindex); + * + * static void scatter(Data& data, typename CommPolicy::IndexedType> value, + * int index, int subindex); + * \endcode + * in the case where CommPolicy::IndexedTypeFlag is VariableSize. Here subindex is the + * subindex of the block at index. + * @param data Source and target of the communication. + */ + template + void backward(Data& data); + + /** + * @brief Free the allocated memory (i.e. buffers and message information. + */ + void free(); + + /** + * @brief Destructor. + */ + ~BufferedCommunicator(); + + private: + + /** + * @brief The type of the map that maps interface information to processors. + */ + typedef std::map > + InterfaceMap; + + + /** + * @brief Functors for message size caculation + */ + template + struct MessageSizeCalculator + {}; + + /** + * @brief Functor for message size caculation for datatypes + * where at each index is only one value. + */ + template + struct MessageSizeCalculator + { + /** + * @brief Calculate the number of values in message + * @param info The information about the interface corresponding + * to the message. + * @return The number of values in th message. + */ + inline int operator()(const InterfaceInformation& info) const; + /** + * @brief Calculate the number of values in message + * + * @param info The information about the interface corresponding + * to the message. + * @param data ignored. + * @return The number of values in th message. + */ + inline int operator()(const Data& data, const InterfaceInformation& info) const; + }; + + /** + * @brief Functor for message size caculation for datatypes + * where at each index can be a variable number of values. + */ + template + struct MessageSizeCalculator + { + /** + * @brief Calculate the number of values in message + * + * @param info The information about the interface corresponding + * to the message. + * @param data A representative of the data we send. + * @return The number of values in th message. + */ + inline int operator()(const Data& data, const InterfaceInformation& info) const; + }; + + /** + * @brief Functors for message data gathering. + */ + template + struct MessageGatherer + {}; + + /** + * @brief Functor for message data gathering for datatypes + * where at each index is only one value. + */ + template + struct MessageGatherer + { + /** @brief The type of the values we send. */ + typedef typename CommPolicy::IndexedType Type; + + /** + * @brief The type of the functor that does the actual copying + * during the data Scattering. + */ + typedef GatherScatter Gatherer; + + enum { + /** + * @brief The communication mode + * + * True if this was a forward communication. + */ + forward=send + }; + + /** + * @brief Copies the values to send into the buffer. + * @param interface The interface used in the send. + * @param data The data from which we copy the values. + * @param buffer The send buffer to copy to. + * @param bufferSize The size of the buffer in bytes. For checks. + */ + inline void operator()(const InterfaceMap& interface, const Data& data, Type* buffer, size_t bufferSize) const; + }; + + /** + * @brief Functor for message data scattering for datatypes + * where at each index can be a variable size of values + */ + template + struct MessageGatherer + { + /** @brief The type of the values we send. */ + typedef typename CommPolicy::IndexedType Type; + + /** + * @brief The type of the functor that does the actual copying + * during the data Scattering. + */ + typedef GatherScatter Gatherer; + + enum { + /** + * @brief The communication mode + * + * True if this was a forward communication. + */ + forward=send + }; + + /** + * @brief Copies the values to send into the buffer. + * @param interface The interface used in the send. + * @param data The data from which we copy the values. + * @param buffer The send buffer to copy to. + * @param bufferSize The size of the buffer in bytes. For checks. + */ + inline void operator()(const InterfaceMap& interface, const Data& data, Type* buffer, size_t bufferSize) const; + }; + + /** + * @brief Functors for message data scattering. + */ + template + struct MessageScatterer + {}; + + /** + * @brief Functor for message data gathering for datatypes + * where at each index is only one value. + */ + template + struct MessageScatterer + { + /** @brief The type of the values we send. */ + typedef typename CommPolicy::IndexedType Type; + + /** + * @brief The type of the functor that does the actual copying + * during the data Scattering. + */ + typedef GatherScatter Scatterer; + + enum { + /** + * @brief The communication mode + * + * True if this was a forward communication. + */ + forward=send + }; + + /** + * @brief Copy the message data from the receive buffer to the data. + * @param interface The interface used in the send. + * @param data The data to which we copy the values. + * @param buffer The receive buffer to copy from. + * @param proc The rank of the process the message is from. + */ + inline void operator()(const InterfaceMap& interface, Data& data, Type* buffer, const int& proc) const; + }; + /** + * @brief Functor for message data scattering for datatypes + * where at each index can be a variable size of values + */ + template + struct MessageScatterer + { + /** @brief The type of the values we send. */ + typedef typename CommPolicy::IndexedType Type; + + /** + * @brief The type of the functor that does the actual copying + * during the data Scattering. + */ + typedef GatherScatter Scatterer; + + enum { + /** + * @brief The communication mode + * + * True if this was a forward communication. + */ + forward=send + }; + + /** + * @brief Copy the message data from the receive buffer to the data. + * @param interface The interface used in the send. + * @param data The data to which we copy the values. + * @param buffer The receive buffer to copy from. + * @param proc The rank of the process the message is from. + */ + inline void operator()(const InterfaceMap& interface, Data& data, Type* buffer, const int& proc) const; + }; + + /** + * @brief Information about a message to send. + */ + struct MessageInformation + { + /** @brief Constructor. */ + MessageInformation() + : start_(0), size_(0) + {} + + /** + * @brief Constructor. + * @param start The start of the message in the global buffer. + * Not in bytes but in number of values from the beginning of + * the buffer + * @param size The size of the message in bytes. + */ + MessageInformation(size_t start, size_t size) + : start_(start), size_(size) + {} + /** + * @brief Start of the message in the buffer counted in number of value. + */ + size_t start_; + /** + * @brief Number of bytes in the message. + */ + size_t size_; + }; + + /** + * @brief Type of the map of information about the messages to send. + * + * The key is the process number to communicate with and the value is + * the pair of information about sending and receiving messages. + */ + typedef std::map > + InformationMap; + /** + * @brief Gathered information about the messages to send. + */ + InformationMap messageInformation_; + /** + * @brief Communication buffers. + */ + char* buffers_[2]; + /** + * @brief The size of the communication buffers + */ + size_t bufferSize_[2]; + + enum { + /** + * @brief The tag we use for communication. + */ + commTag_ + }; + + /** + * @brief The interface we currently work with. + */ + std::map > interfaces_; + + MPI_Comm communicator_; + + /** + * @brief Send and receive Data. + */ + template + void sendRecv(const Data& source, Data& target); + + }; + +#ifndef DOXYGEN + + template + inline const void* CommPolicy::getAddress(const V& v, int index) + { + return &(v[index]); + } + + template + inline int CommPolicy::getSize([[maybe_unused]] const V& v, [[maybe_unused]] int index) + { + return 1; + } + + template + inline const void* CommPolicy, A> >::getAddress(const Type& v, int index) + { + return &(v[index][0]); + } + + template + inline int CommPolicy, A> >::getSize(const Type& v, int index) + { + return v[index].getsize(); + } + + template + inline const typename CopyGatherScatter::IndexedType& CopyGatherScatter::gather(const T & vec, std::size_t i) + { + return vec[i]; + } + + template + inline void CopyGatherScatter::scatter(T& vec, const IndexedType& v, std::size_t i) + { + vec[i]=v; + } + + template + DatatypeCommunicator::DatatypeCommunicator() + : remoteIndices_(0), created_(false) + { + requests_[0]=0; + requests_[1]=0; + } + + + + template + DatatypeCommunicator::~DatatypeCommunicator() + { + free(); + } + + template + template + inline void DatatypeCommunicator::build(const RemoteIndices& remoteIndices, + const T1& source, V& sendData, + const T2& destination, V& receiveData) + { + remoteIndices_ = &remoteIndices; + free(); + createDataTypes(source,destination, receiveData); + createDataTypes(source,destination, sendData); + createRequests(sendData, receiveData); + createRequests(receiveData, sendData); + created_=true; + } + + template + void DatatypeCommunicator::free() + { + if(created_) { + delete[] requests_[0]; + delete[] requests_[1]; + typedef MessageTypeMap::iterator iterator; + typedef MessageTypeMap::const_iterator const_iterator; + + const const_iterator end=messageTypes.end(); + + for(iterator process = messageTypes.begin(); process != end; ++process) { + MPI_Datatype *type = &(process->second.first); + int finalized=0; + MPI_Finalized(&finalized); + if(*type!=MPI_DATATYPE_NULL && !finalized) + MPI_Type_free(type); + type = &(process->second.second); + if(*type!=MPI_DATATYPE_NULL && !finalized) + MPI_Type_free(type); + } + messageTypes.clear(); + created_=false; + } + + } + + template + template + void DatatypeCommunicator::createDataTypes(const T1& sourceFlags, const T2& destFlags, V& data) + { + + MPIDatatypeInformation dataInfo(data); + this->template buildInterface,send>(*remoteIndices_,sourceFlags, destFlags, dataInfo); + + typedef typename RemoteIndices::RemoteIndexMap::const_iterator const_iterator; + const const_iterator end=this->remoteIndices_->end(); + + // Allocate MPI_Datatypes and deallocate memory for the type construction. + for(const_iterator process=this->remoteIndices_->begin(); process != end; ++process) { + IndexedTypeInformation& info=dataInfo.information_[process->first]; + // Shift the displacement + MPI_Aint base; + MPI_Get_address(const_cast(CommPolicy::getAddress(data, 0)), &base); + + for(int i=0; i< info.elements; i++) { + info.displ[i]-=base; + } + + // Create data type + MPI_Datatype* type = &( send ? messageTypes[process->first].first : messageTypes[process->first].second); + MPI_Type_create_hindexed(info.elements, info.length, info.displ, + MPITraits::IndexedType>::getType(), type); + MPI_Type_commit(type); + // Deallocate memory + info.free(); + } + } + + template + template + void DatatypeCommunicator::createRequests(V& sendData, V& receiveData) + { + typedef std::map >::const_iterator MapIterator; + int rank; + static int index = createForward ? 1 : 0; + int noMessages = messageTypes.size(); + // allocate request handles + requests_[index] = new MPI_Request[2*noMessages]; + const MapIterator end = messageTypes.end(); + int request=0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + // Set up the requests for receiving first + for(MapIterator process = messageTypes.begin(); process != end; + ++process, ++request) { + MPI_Datatype type = createForward ? process->second.second : process->second.first; + void* address = const_cast(CommPolicy::getAddress(receiveData,0)); + MPI_Recv_init(address, 1, type, process->first, commTag_, this->remoteIndices_->communicator(), requests_[index]+request); + } + + // And now the send requests + + for(MapIterator process = messageTypes.begin(); process != end; + ++process, ++request) { + MPI_Datatype type = createForward ? process->second.first : process->second.second; + void* address = const_cast(CommPolicy::getAddress(sendData, 0)); + MPI_Ssend_init(address, 1, type, process->first, commTag_, this->remoteIndices_->communicator(), requests_[index]+request); + } + } + + template + void DatatypeCommunicator::forward() + { + sendRecv(requests_[1]); + } + + template + void DatatypeCommunicator::backward() + { + sendRecv(requests_[0]); + } + + template + void DatatypeCommunicator::sendRecv(MPI_Request* requests) + { + int noMessages = messageTypes.size(); + // Start the receive calls first + MPI_Startall(noMessages, requests); + // Now the send calls + MPI_Startall(noMessages, requests+noMessages); + + // Wait for completion of the communication send first then receive + MPI_Status* status=new MPI_Status[2*noMessages]; + for(int i=0; i<2*noMessages; i++) + status[i].MPI_ERROR=MPI_SUCCESS; + + int send = MPI_Waitall(noMessages, requests+noMessages, status+noMessages); + int receive = MPI_Waitall(noMessages, requests, status); + + // Error checks + int success=1, globalSuccess=0; + if(send==MPI_ERR_IN_STATUS) { + int rank; + MPI_Comm_rank(this->remoteIndices_->communicator(), &rank); + std::cerr<remoteIndices_->communicator(), &rank); + std::cerr<remoteIndices_->communicator()); + + delete[] status; + + if(!globalSuccess) + DUNE_THROW(CommunicationError, "A communication error occurred!"); + + } + + inline BufferedCommunicator::BufferedCommunicator() + { + buffers_[0]=0; + buffers_[1]=0; + bufferSize_[0]=0; + bufferSize_[1]=0; + } + + template + typename std::enable_if::IndexedTypeFlag>::value, void>::type + BufferedCommunicator::build(const Interface& interface) + { + interfaces_=interface.interfaces(); + communicator_=interface.communicator(); + typedef typename std::map > + ::const_iterator const_iterator; + typedef typename CommPolicy::IndexedTypeFlag Flag; + const const_iterator end = interfaces_.end(); + int lrank; + MPI_Comm_rank(communicator_, &lrank); + + bufferSize_[0]=0; + bufferSize_[1]=0; + + for(const_iterator interfacePair = interfaces_.begin(); + interfacePair != end; ++interfacePair) { + int noSend = MessageSizeCalculator() (interfacePair->second.first); + int noRecv = MessageSizeCalculator() (interfacePair->second.second); + if (noSend + noRecv > 0) + messageInformation_.insert(std::make_pair(interfacePair->first, + std::make_pair(MessageInformation(bufferSize_[0], + noSend*sizeof(typename CommPolicy::IndexedType)), + MessageInformation(bufferSize_[1], + noRecv*sizeof(typename CommPolicy::IndexedType))))); + bufferSize_[0] += noSend; + bufferSize_[1] += noRecv; + } + + // allocate the buffers + bufferSize_[0] *= sizeof(typename CommPolicy::IndexedType); + bufferSize_[1] *= sizeof(typename CommPolicy::IndexedType); + + buffers_[0] = new char[bufferSize_[0]]; + buffers_[1] = new char[bufferSize_[1]]; + } + + template + void BufferedCommunicator::build(const Data& source, const Data& dest, const Interface& interface) + { + + interfaces_=interface.interfaces(); + communicator_=interface.communicator(); + typedef typename std::map > + ::const_iterator const_iterator; + typedef typename CommPolicy::IndexedTypeFlag Flag; + const const_iterator end = interfaces_.end(); + + bufferSize_[0]=0; + bufferSize_[1]=0; + + for(const_iterator interfacePair = interfaces_.begin(); + interfacePair != end; ++interfacePair) { + int noSend = MessageSizeCalculator() (source, interfacePair->second.first); + int noRecv = MessageSizeCalculator() (dest, interfacePair->second.second); + if (noSend + noRecv > 0) + messageInformation_.insert(std::make_pair(interfacePair->first, + std::make_pair(MessageInformation(bufferSize_[0], + noSend*sizeof(typename CommPolicy::IndexedType)), + MessageInformation(bufferSize_[1], + noRecv*sizeof(typename CommPolicy::IndexedType))))); + bufferSize_[0] += noSend; + bufferSize_[1] += noRecv; + } + + bufferSize_[0] *= sizeof(typename CommPolicy::IndexedType); + bufferSize_[1] *= sizeof(typename CommPolicy::IndexedType); + // allocate the buffers + buffers_[0] = new char[bufferSize_[0]]; + buffers_[1] = new char[bufferSize_[1]]; + } + + inline void BufferedCommunicator::free() + { + messageInformation_.clear(); + if(buffers_[0]) + delete[] buffers_[0]; + + if(buffers_[1]) + delete[] buffers_[1]; + buffers_[0]=buffers_[1]=0; + } + + inline BufferedCommunicator::~BufferedCommunicator() + { + free(); + } + + template + inline int BufferedCommunicator::MessageSizeCalculator::operator() + (const InterfaceInformation& info) const + { + return info.size(); + } + + + template + inline int BufferedCommunicator::MessageSizeCalculator::operator() + (const Data&, const InterfaceInformation& info) const + { + return operator()(info); + } + + + template + inline int BufferedCommunicator::MessageSizeCalculator::operator() + (const Data& data, const InterfaceInformation& info) const + { + int entries=0; + + for(size_t i=0; i < info.size(); i++) + entries += CommPolicy::getSize(data,info[i]); + + return entries; + } + + + template + inline void BufferedCommunicator::MessageGatherer::operator()(const InterfaceMap& interfaces,const Data& data, Type* buffer, [[maybe_unused]] size_t bufferSize) const + { + typedef typename InterfaceMap::const_iterator + const_iterator; + + int rank; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + const const_iterator end = interfaces.end(); + size_t index=0; + + for(const_iterator interfacePair = interfaces.begin(); + interfacePair != end; ++interfacePair) { + int size = forward ? interfacePair->second.first.size() : + interfacePair->second.second.size(); + + for(int i=0; i < size; i++) { + int local = forward ? interfacePair->second.first[i] : + interfacePair->second.second[i]; + for(std::size_t j=0; j < CommPolicy::getSize(data, local); j++, index++) { + +#ifdef DUNE_ISTL_WITH_CHECKING + assert(bufferSize>=(index+1)*sizeof(typename CommPolicy::IndexedType)); +#endif + buffer[index]=GatherScatter::gather(data, local, j); + } + + } + } + + } + + + template + inline void BufferedCommunicator::MessageGatherer::operator()( + const InterfaceMap& interfaces, const Data& data, Type* buffer, [[maybe_unused]] size_t bufferSize) const + { + typedef typename InterfaceMap::const_iterator + const_iterator; + const const_iterator end = interfaces.end(); + size_t index = 0; + + int rank; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + for(const_iterator interfacePair = interfaces.begin(); + interfacePair != end; ++interfacePair) { + size_t size = FORWARD ? interfacePair->second.first.size() : + interfacePair->second.second.size(); + + for(size_t i=0; i < size; i++) { + +#ifdef DUNE_ISTL_WITH_CHECKING + assert(bufferSize>=(index+1)*sizeof(typename CommPolicy::IndexedType)); +#endif + + buffer[index++] = GatherScatter::gather(data, FORWARD ? interfacePair->second.first[i] : + interfacePair->second.second[i]); + } + } + + } + + + template + inline void BufferedCommunicator::MessageScatterer::operator()(const InterfaceMap& interfaces, Data& data, Type* buffer, const int& proc) const + { + typedef typename InterfaceMap::value_type::second_type::first_type Information; + const typename InterfaceMap::const_iterator infoPair = interfaces.find(proc); + + assert(infoPair!=interfaces.end()); + + const Information& info = FORWARD ? infoPair->second.second : + infoPair->second.first; + + for(size_t i=0, index=0; i < info.size(); i++) { + for(size_t j=0; j < CommPolicy::getSize(data, info[i]); j++) + GatherScatter::scatter(data, buffer[index++], info[i], j); + } + } + + + template + inline void BufferedCommunicator::MessageScatterer::operator()(const InterfaceMap& interfaces, Data& data, Type* buffer, const int& proc) const + { + typedef typename InterfaceMap::value_type::second_type::first_type Information; + const typename InterfaceMap::const_iterator infoPair = interfaces.find(proc); + + assert(infoPair!=interfaces.end()); + + const Information& info = FORWARD ? infoPair->second.second : + infoPair->second.first; + + for(size_t i=0; i < info.size(); i++) { + GatherScatter::scatter(data, buffer[i], info[i]); + } + } + + + template + void BufferedCommunicator::forward(Data& data) + { + this->template sendRecv(data, data); + } + + + template + void BufferedCommunicator::backward(Data& data) + { + this->template sendRecv(data, data); + } + + + template + void BufferedCommunicator::forward(const Data& source, Data& dest) + { + this->template sendRecv(source, dest); + } + + + template + void BufferedCommunicator::backward(Data& source, const Data& dest) + { + this->template sendRecv(dest, source); + } + + + template + void BufferedCommunicator::sendRecv(const Data& source, Data& dest) + { + int rank, lrank; + + MPI_Comm_rank(MPI_COMM_WORLD,&rank); + MPI_Comm_rank(MPI_COMM_WORLD,&lrank); + + typedef typename CommPolicy::IndexedType Type; + Type *sendBuffer, *recvBuffer; + size_t sendBufferSize; +#ifndef NDEBUG + size_t recvBufferSize; +#endif + + if(FORWARD) { + sendBuffer = reinterpret_cast(buffers_[0]); + sendBufferSize = bufferSize_[0]; + recvBuffer = reinterpret_cast(buffers_[1]); +#ifndef NDEBUG + recvBufferSize = bufferSize_[1]; +#endif + }else{ + sendBuffer = reinterpret_cast(buffers_[1]); + sendBufferSize = bufferSize_[1]; + recvBuffer = reinterpret_cast(buffers_[0]); +#ifndef NDEBUG + recvBufferSize = bufferSize_[0]; +#endif + } + typedef typename CommPolicy::IndexedTypeFlag Flag; + + MessageGatherer() (interfaces_, source, sendBuffer, sendBufferSize); + + MPI_Request* sendRequests = new MPI_Request[messageInformation_.size()]; + MPI_Request* recvRequests = new MPI_Request[messageInformation_.size()]; + /* Number of recvRequests that are not MPI_REQUEST_NULL */ + size_t numberOfRealRecvRequests = 0; + + // Setup receive first + typedef typename InformationMap::const_iterator const_iterator; + + const const_iterator end = messageInformation_.end(); + size_t i=0; + int* processMap = new int[messageInformation_.size()]; + + for(const_iterator info = messageInformation_.begin(); info != end; ++info, ++i) { + processMap[i]=info->first; + if(FORWARD) { + assert(info->second.second.start_*sizeof(typename CommPolicy::IndexedType)+info->second.second.size_ <= recvBufferSize ); + Dune::dvverb<second.second.size_<<" from "<first<second.second.size_) { + MPI_Irecv(recvBuffer+info->second.second.start_, info->second.second.size_, + MPI_BYTE, info->first, commTag_, communicator_, + recvRequests+i); + numberOfRealRecvRequests += 1; + } else { + // Nothing to receive -> set request to inactive + recvRequests[i]=MPI_REQUEST_NULL; + } + }else{ + assert(info->second.first.start_*sizeof(typename CommPolicy::IndexedType)+info->second.first.size_ <= recvBufferSize ); + Dune::dvverb<second.first.size_<<" to "<first<second.first.size_) { + MPI_Irecv(recvBuffer+info->second.first.start_, info->second.first.size_, + MPI_BYTE, info->first, commTag_, communicator_, + recvRequests+i); + numberOfRealRecvRequests += 1; + } else { + // Nothing to receive -> set request to inactive + recvRequests[i]=MPI_REQUEST_NULL; + } + } + } + + // now the send requests + i=0; + for(const_iterator info = messageInformation_.begin(); info != end; ++info, ++i) + if(FORWARD) { + assert(info->second.second.start_*sizeof(typename CommPolicy::IndexedType)+info->second.second.size_ <= recvBufferSize ); + Dune::dvverb<second.first.size_<<" to "<first<second.first.start_*sizeof(typename CommPolicy::IndexedType)+info->second.first.size_ <= sendBufferSize ); + if(info->second.first.size_) + MPI_Issend(sendBuffer+info->second.first.start_, info->second.first.size_, + MPI_BYTE, info->first, commTag_, communicator_, + sendRequests+i); + else + // Nothing to send -> set request to inactive + sendRequests[i]=MPI_REQUEST_NULL; + }else{ + assert(info->second.second.start_*sizeof(typename CommPolicy::IndexedType)+info->second.second.size_ <= sendBufferSize ); + Dune::dvverb<second.second.size_<<" to "<first<second.second.size_) + MPI_Issend(sendBuffer+info->second.second.start_, info->second.second.size_, + MPI_BYTE, info->first, commTag_, communicator_, + sendRequests+i); + else + // Nothing to send -> set request to inactive + sendRequests[i]=MPI_REQUEST_NULL; + } + + // Wait for completion of receive and immediately start scatter + i=0; + //int success = 1; + int finished = MPI_UNDEFINED; + MPI_Status status; //[messageInformation_.size()]; + //MPI_Waitall(messageInformation_.size(), recvRequests, status); + + for(i=0; i< numberOfRealRecvRequests; i++) { + status.MPI_ERROR=MPI_SUCCESS; + MPI_Waitany(messageInformation_.size(), recvRequests, &finished, &status); + assert(finished != MPI_UNDEFINED); + + if(status.MPI_ERROR==MPI_SUCCESS) { + int& proc = processMap[finished]; + typename InformationMap::const_iterator infoIter = messageInformation_.find(proc); + assert(infoIter != messageInformation_.end()); + + MessageInformation info = (FORWARD) ? infoIter->second.second : infoIter->second.first; + assert(info.start_+info.size_ <= recvBufferSize); + + MessageScatterer() (interfaces_, dest, recvBuffer+info.start_, proc); + }else{ + std::cerr<communicator()); + + if(!globalSuccess) + DUNE_THROW(CommunicationError, "A communication error occurred!"); + */ + delete[] processMap; + delete[] sendRequests; + delete[] recvRequests; + + } + +#endif // DOXYGEN + + /** @} */ +} + +#endif // HAVE_MPI + +#endif diff --git a/dune/common/parallel/future.hh b/dune/common/parallel/future.hh new file mode 100644 index 0000000..4587011 --- /dev/null +++ b/dune/common/parallel/future.hh @@ -0,0 +1,187 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_PARALLEL_FUTURE_HH +#define DUNE_COMMON_PARALLEL_FUTURE_HH + +#include +#include + +namespace Dune{ + + /*! \brief This exception is thrown when `ready()`, `wait()` or `get()` is + called on an invalid future. A future is valid until `get()` is called and + if it is not default-constructed and it was not moved from. + */ + class InvalidFutureException : public InvalidStateException + {}; + + // forward declaration + template + class PseudoFuture; + + /*! \brief Type-erasure for future-like objects. A future-like object is a + object satisfying the interface of FutureBase. + */ + template + class Future{ + // Future interface: + class FutureBase{ + public: + virtual ~FutureBase() = default; + virtual void wait() = 0; + virtual bool ready() const = 0; + virtual bool valid() const = 0; + virtual T get() = 0; + }; + + // model class + template + class FutureModel + : public FutureBase + { + F _future; + public: + FutureModel(F&& f) + : _future(std::forward(f)) + {} + + virtual void wait() override + { + _future.wait(); + } + + virtual bool ready() const override + { + return _future.ready(); + } + + virtual bool valid() const override + { + return _future.valid(); + } + + virtual T get() override{ + return (T)_future.get(); + } + }; + + std::unique_ptr _future; + public: + template + Future(F&& f) + : _future(std::make_unique>(std::forward(f))) + {} + + template::value && !std::is_same::value>> + Future(U&& data) + : _future(std::make_unique>>(PseudoFuture(std::forward(data)))) + {} + + Future() = default; + + /*! \brief wait until the future is ready + \throws InvalidFutureException + */ + void wait(){ + _future->wait(); + } + + /*! \brief Waits until the future is ready and returns the resulting value + \returns The contained value + \throws InvalidFutureException + */ + T get() { + return _future->get(); + } + + /*! \brief + \returns true is the future is ready, otherwise false + \throws InvalidFutureException + */ + bool ready() const { + return _future->ready(); + } + + /*! \brief Checks whether the future is valid. I.e. `get()' was not called + on that future and when it was not default-constructed and not moved + from. + \returns true is the future is valid, otherwise false + */ + bool valid() const { + if(_future) + return _future->valid(); + return false; + } + }; + + /*! \brief A wrapper-class for a object which is ready immediately. + */ + template + class PseudoFuture{ + bool valid_; + T data_; + public: + PseudoFuture() : + valid_(false) + {} + + template + PseudoFuture(U&& u) : + valid_(true), + data_(std::forward(u)) + {} + + void wait() { + if(!valid_) + DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); + } + + bool ready() const { + if(!valid_) + DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); + return true; + } + + T get() { + if(!valid_) + DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); + valid_ = false; + return std::forward(data_); + } + + bool valid() const { + return valid_; + } + }; + + template<> + class PseudoFuture{ + bool valid_; + public: + PseudoFuture(bool valid = false) : + valid_(valid) + {} + + void wait(){ + if(!valid_) + DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); + } + bool ready() const{ + if(!valid_) + DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); + return true; + } + + void get(){ + if(!valid_) + DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); + valid_ = false; + } + + bool valid() const{ + return valid_; + } + }; +} + +#endif diff --git a/dune/common/parallel/indexset.hh b/dune/common/parallel/indexset.hh new file mode 100644 index 0000000..a729b62 --- /dev/null +++ b/dune/common/parallel/indexset.hh @@ -0,0 +1,1158 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_INDEXSET_HH +#define DUNE_INDEXSET_HH + +#include +#include +#include +#include + +#include "localindex.hh" + +#include // for uint32_t + +namespace Dune +{ + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Provides a map between global and local indices. + * @author Markus Blatt + */ + // forward declarations + + template + class IndexPair; + + /** + * @brief Print an index pair. + * @param os The outputstream to print to. + * @param pair The index pair to print. + */ + template + std::ostream& operator<<(std::ostream& os, const IndexPair& pair); + + template + bool operator==(const IndexPair&, const IndexPair&); + + template + bool operator!=(const IndexPair&, const IndexPair&); + + template + bool operator<(const IndexPair&, const IndexPair&); + + template + bool operator>(const IndexPair&, const IndexPair&); + + template + bool operator<=(const IndexPair&, const IndexPair&); + + template + bool operator >=(const IndexPair&, const IndexPair&); + + template + bool operator==(const IndexPair&, const TG&); + + template + bool operator!=(const IndexPair&, const TG&); + + template + bool operator<(const IndexPair&, const TG&); + + template + bool operator>(const IndexPair&, const TG&); + + template + bool operator<=(const IndexPair&, const TG&); + + template + bool operator >=(const IndexPair&, const TG&); + + template + struct MPITraits; + + /** + * @brief A pair consisting of a global and local index. + */ + template + class IndexPair + { + friend std::ostream& operator<<<>(std::ostream&, const IndexPair&); + friend bool operator==<>(const IndexPair&, const IndexPair&); + friend bool operator!=<>(const IndexPair&, const IndexPair&); + friend bool operator< <>(const IndexPair&, const IndexPair&); + friend bool operator><>(const IndexPair&, const IndexPair&); + friend bool operator<=<>(const IndexPair&, const IndexPair&); + friend bool operator>=<>(const IndexPair&, const IndexPair&); + friend bool operator==<>(const IndexPair&, const TG &); + friend bool operator!=<>(const IndexPair&, const TG &); + friend bool operator< <>(const IndexPair&, const TG &); + friend bool operator> <>(const IndexPair&, const TG &); + friend bool operator<=<>(const IndexPair&, const TG &); + friend bool operator>=<>(const IndexPair&, const TG &); + friend struct MPITraits >; + + public: + /** + * @brief the type of the global index. + * + * This type has to provide at least a operator< for sorting. + */ + typedef TG GlobalIndex; + + /** + * @brief the type of the local index. + * + * This class to provide the following functions: + * \code + * LocalIndex operator=(int); + * operator int() const; + * LocalIndexState state() const; + * void setState(LocalIndexState); + * \endcode + */ + typedef TL LocalIndex; + + /** + * @brief Constructs a new Pair. + * + * @param global The global index. + * @param local The local index. + */ + IndexPair(const GlobalIndex& global, const LocalIndex& local); + + /** + * @brief Construct a new Pair. + */ + IndexPair(); + /** + * @brief Constructs a new Pair. + * + * The local index will be 0. + * @param global The global index. + */ + IndexPair(const GlobalIndex& global); + + /** + * @brief Get the global index. + * + * @return The global index. + */ + inline const GlobalIndex& global() const; + + /** + * @brief Get the local index. + * + * @return The local index. + */ + inline LocalIndex& local(); + + /** + * @brief Get the local index. + * + * @return The local index. + */ + inline const LocalIndex& local() const; + + /** + * @brief Set the local index. + * + * @param index The index to set it to. + */ + inline void setLocal(int index); + private: + /** @brief The global index. */ + GlobalIndex global_; + /** @brief The local index. */ + LocalIndex local_; + }; + + /** + * @brief The states the index set can be in. + * @see ParallelIndexSet::state_ + */ + enum ParallelIndexSetState + { + /** + * @brief The default mode. + * Indicates that the index set is ready to be used. + */ + GROUND, + /** + * @brief Indicates that the index set is currently being resized. + */ + RESIZE + /** + * @brief Indicates that all previously deleted indices are now deleted. + * + CLEAN, + ** + * @brief Indicates that the index set is currently being reordered. + * + REORDER + */ + }; + + /** + * @brief Exception indicating that the index set is not in the expected state. + */ + class InvalidIndexSetState : public InvalidStateException {}; + + // Forward declaration + template class GlobalLookupIndexSet; + + /** + * @brief Manager class for the mapping between local indices and globally unique indices. + * + * The mapping is between a globally unique id and local index. The local index is consecutive + * and non persistent while the global id might not be consecutive but definitely is persistent. + */ + template + class ParallelIndexSet + { + friend class GlobalLookupIndexSet >; + + public: + /** + * @brief the type of the global index. + * This type has to provide at least a operator< for sorting. + */ + typedef TG GlobalIndex; + + /** + * @brief The type of the local index, e.g. ParallelLocalIndex. + * + * This class to provide the following functions: + * \code + * LocalIndex operator=(int); + * operator int() const; + * LocalIndexState state() const; + * void setState(LocalIndexState); + * \endcode + */ + typedef TL LocalIndex; + + /** + * @brief The type of the pair stored. + */ + typedef Dune::IndexPair IndexPair; + + enum { + /** + * @brief The size of the individual arrays in the underlying ArrayList. + * + * The default value is 100. + * @see ArrayList::size + */ + arraySize= (N>0) ? N : 1 + }; + + /** @brief The iterator over the pairs. */ + class iterator : + public ArrayList::iterator + { + typedef typename ArrayList::iterator + Father; + friend class ParallelIndexSet; + public: + iterator(ParallelIndexSet& indexSet, const Father& father) + : Father(father), indexSet_(&indexSet) + {} + + private: + /** + * @brief Mark the index as deleted. + * + * The deleted flag will be set in the local index. + * The index will be removed in the endResize method of the + * index set. + * + * @exception InvalidIndexSetState only when NDEBUG is not defined + */ + inline void markAsDeleted() const + { +#ifndef NDEBUG + if(indexSet_->state_ != RESIZE) + DUNE_THROW(InvalidIndexSetState, "Indices can only be removed " + <<"while in RESIZE state!"); +#endif + Father::operator*().local().setState(DELETED); + } + + /** @brief The index set we are an iterator of. */ + ParallelIndexSet* indexSet_; + + }; + + + + /** @brief The constant iterator over the pairs. */ + typedef typename + ArrayList::const_iterator + const_iterator; + + /** + * @brief Constructor. + */ + ParallelIndexSet(); + + /** + * @brief Get the state the index set is in. + * @return The state of the index set. + */ + inline const ParallelIndexSetState& state() + { + return state_; + } + + /** + * @brief Indicate that the index set is to be resized. + * @exception InvalidState If index set was not in + * ParallelIndexSetState::GROUND mode. + */ + void beginResize(); + + /** + * @brief Add an new index to the set. + * + * The local index is created by the default constructor. + * @param global The globally unique id of the index. + * @exception InvalidState If index set is not in + * ParallelIndexSetState::RESIZE mode. + */ + inline void add(const GlobalIndex& global); + + /** + * @brief Add an new index to the set. + * + * @param global The globally unique id of the index. + * @param local The local index. + * @exception InvalidState If index set is not in + * ParallelIndexSetState::RESIZE mode. + */ + inline void add(const GlobalIndex& global, const LocalIndex& local); + + /** + * @brief Mark an index as deleted. + * + * The index will be deleted during endResize(). + * @param position An iterator at the position we want to delete. + * @exception InvalidState If index set is not in ParallelIndexSetState::RESIZE mode. + */ + inline void markAsDeleted(const iterator& position); + + /** + * @brief Indicate that the resizing finishes. + * + * @warning Invalidates all pointers stored to the elements of this index set. + * The local indices will be ordered + * according to the global indices: + * Let \f$(g_i,l_i)_{i=0}^N \f$ be the set of all indices then \f$l_i < l_j\f$ + * if and + * only if \f$g_i < g_j\f$ for arbitrary \f$i \neq j\f$. + * @exception InvalidState If index set was not in + * ParallelIndexSetState::RESIZE mode. + */ + void endResize(); + + /** + * @brief Find the index pair with a specific global id. + * + * This starts a binary search for the entry and therefore has complexity + * log(N). + * @param global The globally unique id of the pair. + * @return The pair of indices for the id. + * @warning If the global index is not in the set a wrong or even a + * null reference might be returned. To be save use the throwing alternative at. + */ + inline IndexPair& + operator[](const GlobalIndex& global); + + /** + * @brief Find the index pair with a specific global id. + * + * This starts a binary search for the entry and therefore has complexity + * log(N). + * @param global The globally unique id of the pair. + * @return The pair of indices for the id. + * @exception RangeError Thrown if the global id is not known. + */ + inline IndexPair& + at(const GlobalIndex& global); + + /** + * @brief Find the index pair with a specific global id. + * + * This starts a binary search for the entry and therefore has complexity + * log(N). + * @param global The globally unique id of the pair. + * @return The pair of indices for the id. + * @exception RangeError Thrown if the global id is not known. + */ + inline bool + exists (const GlobalIndex& global) const; + + /** + * @brief Find the index pair with a specific global id. + * + * This starts a binary search for the entry and therefore has complexity + * log(N). + * @param global The globally unique id of the pair. + * @return The pair of indices for the id. + * @warning If the global index is not in the set a wrong or even a + * null reference might be returned. To be save use the throwing alternative at. + */ + inline const IndexPair& + operator[](const GlobalIndex& global) const; + + /** + * @brief Find the index pair with a specific global id. + * + * This starts a binary search for the entry and therefore has complexity + * log(N). + * @param global The globally unique id of the pair. + * @return The pair of indices for the id. + * @exception RangeError Thrown if the global id is not known. + */ + inline const IndexPair& + at(const GlobalIndex& global) const; + + /** + * @brief Get an iterator over the indices positioned at the first index. + * @return Iterator over the local indices. + */ + inline iterator begin(); + + /** + * @brief Get an iterator over the indices positioned after the last index. + * @return Iterator over the local indices. + */ + inline iterator end(); + + /** + * @brief Get an iterator over the indices positioned at the first index. + * @return Iterator over the local indices. + */ + inline const_iterator begin() const; + + /** + * @brief Get an iterator over the indices positioned after the last index. + * @return Iterator over the local indices. + */ + inline const_iterator end() const; + + /** + * @brief Renumbers the local index numbers. + * + * After this function returns the indices are + * consecutively numbered beginning from 0. Let + * $(g_i,l_i)$, $(g_j,l_j)$ be two arbitrary index + * pairs with $g_i localIndices_; + /** @brief The new indices for the RESIZE state. */ + ArrayList newIndices_; + /** @brief The state of the index set. */ + ParallelIndexSetState state_; + /** @brief Number to keep track of the number of resizes. */ + int seqNo_; + /** @brief Whether entries were deleted in resize mode. */ + bool deletedEntries_; + /** + * @brief Merges the _localIndices and newIndices arrays and creates a new + * localIndices array. + */ + inline void merge(); + }; + + + /** + * @brief Print an index set. + * @param os The outputstream to print to. + * @param indexSet The index set to print. + */ + template + std::ostream& operator<<(std::ostream& os, const ParallelIndexSet& indexSet); + + /** + * @brief Decorates an index set with the possibility to find a global index + * that is mapped to a specific local. + * + */ + template + class GlobalLookupIndexSet + { + public: + /** + * @brief The type of the index set. + */ + typedef I ParallelIndexSet; + + /** + * @brief The type of the local index. + */ + typedef typename ParallelIndexSet::LocalIndex LocalIndex; + + /** + * @brief The type of the global index. + */ + typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; + + /** + * @brief The iterator over the index pairs. + */ + typedef typename ParallelIndexSet::const_iterator const_iterator; + + typedef Dune::IndexPair IndexPair; + + /** + * @brief Constructor. + * @param indexset The index set we want to be able to lookup the corresponding + * global index of a local index. + * @param size The number of indices present, i.e. one more than the maximum local index. + */ + GlobalLookupIndexSet(const ParallelIndexSet& indexset, std::size_t size); + + /** + * @brief Constructor. + * @param indexset The index set we want to be able to lookup the corresponding + * global index of a local index. + */ + GlobalLookupIndexSet(const ParallelIndexSet& indexset); + + /** + * @brief Destructor. + */ + ~GlobalLookupIndexSet(); + + /** + * @brief Find the index pair with a specific global id. + * + * This starts a binary search for the entry and therefore has complexity + * log(N). This method is forwarded to the underlying index set. + * @param global The globally unique id of the pair. + * @return The pair of indices for the id. + * @exception RangeError Thrown if the global id is not known. + */ + inline const IndexPair& + operator[](const GlobalIndex& global) const; + + /** + * @brief Get the index pair corresponding to a local index. + */ + inline const IndexPair* + pair(const std::size_t& local) const; + + /** + * @brief Get an iterator over the indices positioned at the first index. + * @return Iterator over the local indices. + */ + inline const_iterator begin() const; + + /** + * @brief Get an iterator over the indices positioned after the last index. + * @return Iterator over the local indices. + */ + inline const_iterator end() const; + + /** + * @brief Get the internal sequence number. + * + * Is initially 0 is incremented for each resize. + * @return The sequence number. + */ + inline int seqNo() const; + + /** + * @brief Get the total number (public and nonpublic) indices. + * @return The total number (public and nonpublic) indices. + */ + inline size_t size() const; + private: + /** + * @brief The index set we lookup in. + */ + const ParallelIndexSet& indexSet_; + + /** + * @brief The number of indices. + */ + std::size_t size_; + + /** + * @brief Array with the positions of the corresponding index pair of the index set. + */ + std::vector indices_; + + }; + + + template + struct LocalIndexComparator + { + static bool compare([[maybe_unused]] const T& t1, [[maybe_unused]] const T& t2) + { + return false; + } + }; + + template + struct IndexSetSortFunctor + { + bool operator()(const IndexPair& i1, const IndexPair& i2) + { + return i1.global()::compare(i1.local(), + i2.local())); + } + }; + + + + template + inline std::ostream& operator<<(std::ostream& os, const IndexPair& pair) + { + os<<"{global="< + inline std::ostream& operator<<(std::ostream& os, const ParallelIndexSet& indexSet) + { + typedef typename ParallelIndexSet::const_iterator Iterator; + Iterator end = indexSet.end(); + os<<"{"; + for(Iterator index = indexSet.begin(); index != end; ++index) + os<<*index<<" "; + os<<"}"; + return os; + + } + + template + inline bool operator==(const IndexPair& a, const IndexPair& b) + { + return a.global_==b.global_; + } + + template + inline bool operator!=(const IndexPair& a, const IndexPair& b) + { + return a.global_!=b.global_; + } + + template + inline bool operator<(const IndexPair& a, const IndexPair& b) + { + return a.global_ + inline bool operator>(const IndexPair& a, const IndexPair& b) + { + return a.global_>b.global_; + } + + template + inline bool operator<=(const IndexPair& a, const IndexPair& b) + { + return a.global_<=b.global_; + } + + template + inline bool operator >=(const IndexPair& a, const IndexPair& b) + { + return a.global_>=b.global_; + } + + template + inline bool operator==(const IndexPair& a, const TG& b) + { + return a.global_==b; + } + + template + inline bool operator!=(const IndexPair& a, const TG& b) + { + return a.global_!=b; + } + + template + inline bool operator<(const IndexPair& a, const TG& b) + { + return a.global_ + inline bool operator>(const IndexPair& a, const TG& b) + { + return a.global_>b; + } + + template + inline bool operator<=(const IndexPair& a, const TG& b) + { + return a.global_<=b; + } + + template + inline bool operator >=(const IndexPair& a, const TG& b) + { + return a.global_>=b; + } + +#ifndef DOXYGEN + + template + IndexPair::IndexPair(const TG& global, const TL& local) + : global_(global), local_(local){} + + template + IndexPair::IndexPair(const TG& global) + : global_(global), local_(){} + + template + IndexPair::IndexPair() + : global_(), local_(){} + + template + inline const TG& IndexPair::global() const { + return global_; + } + + template + inline TL& IndexPair::local() { + return local_; + } + + template + inline const TL& IndexPair::local() const { + return local_; + } + + template + inline void IndexPair::setLocal(int local){ + local_=local; + } + + template + ParallelIndexSet::ParallelIndexSet() + : state_(GROUND), seqNo_(0) + {} + + template + void ParallelIndexSet::beginResize() + { + + // Checks in unproductive code +#ifndef NDEBUG + if(state_!=GROUND) + DUNE_THROW(InvalidIndexSetState, + "IndexSet has to be in GROUND state, when " + << "beginResize() is called!"); +#endif + + state_ = RESIZE; + deletedEntries_ = false; + } + + template + inline void ParallelIndexSet::add(const GlobalIndex& global) + { + // Checks in unproductive code +#ifndef NDEBUG + if(state_ != RESIZE) + DUNE_THROW(InvalidIndexSetState, "Indices can only be added " + <<"while in RESIZE state!"); +#endif + newIndices_.push_back(IndexPair(global)); + } + + template + inline void ParallelIndexSet::add(const TG& global, const TL& local) + { + // Checks in unproductive code +#ifndef NDEBUG + if(state_ != RESIZE) + DUNE_THROW(InvalidIndexSetState, "Indices can only be added " + <<"while in RESIZE state!"); +#endif + newIndices_.push_back(IndexPair(global,local)); + } + + template + inline void ParallelIndexSet::markAsDeleted(const iterator& global) + { + // Checks in unproductive code +#ifndef NDEBUG + if(state_ != RESIZE) + DUNE_THROW(InvalidIndexSetState, "Indices can only be removed " + <<"while in RESIZE state!"); +#endif + deletedEntries_ = true; + + global.markAsDeleted(); + } + + template + void ParallelIndexSet::endResize() { + // Checks in unproductive code +#ifndef NDEBUG + if(state_ != RESIZE) + DUNE_THROW(InvalidIndexSetState, "endResize called while not " + <<"in RESIZE state!"); +#endif + + std::sort(newIndices_.begin(), newIndices_.end(), IndexSetSortFunctor()); + merge(); + seqNo_++; + state_ = GROUND; + } + + + template + inline void ParallelIndexSet::merge(){ + if(localIndices_.size()==0) + { + localIndices_=newIndices_; + newIndices_.clear(); + } + else if(newIndices_.size()>0 || deletedEntries_) + { + ArrayList tempPairs; + + auto old = localIndices_.begin(); + auto added = newIndices_.begin(); + const auto endold = localIndices_.end(); + const auto endadded = newIndices_.end(); + + while(old != endold && added!= endadded) + { + if(old->local().state()==DELETED) { + old.eraseToHere(); + } + else + { + if(old->global() < added->global() || + (old->global() == added->global() + && LocalIndexComparator::compare(old->local(),added->local()))) + { + tempPairs.push_back(*old); + old.eraseToHere(); + continue; + }else + { + tempPairs.push_back(*added); + added.eraseToHere(); + } + } + } + + while(old != endold) + { + if(old->local().state()!=DELETED) { + tempPairs.push_back(*old); + } + old.eraseToHere(); + } + + while(added!= endadded) + { + tempPairs.push_back(*added); + added.eraseToHere(); + } + localIndices_ = tempPairs; + } + } + + + template + inline const IndexPair& + ParallelIndexSet::at(const TG& global) const + { + // perform a binary search + int low=0, high=localIndices_.size()-1, probe=-1; + + while(low + inline const IndexPair& + ParallelIndexSet::operator[](const TG& global) const + { + // perform a binary search + int low=0, high=localIndices_.size()-1, probe=-1; + + while(low + inline IndexPair& ParallelIndexSet::at(const TG& global) + { + // perform a binary search + int low=0, high=localIndices_.size()-1, probe=-1; + + while(low= global) + high = probe; + else + low = probe+1; + } + + if(probe==-1) + DUNE_THROW(RangeError, "No entries!"); + + if( localIndices_[low].global() != global) + DUNE_THROW(RangeError, "Could not find entry of "< + inline bool ParallelIndexSet::exists (const TG& global) const + { + // perform a binary search + int low=0, high=localIndices_.size()-1, probe=-1; + + while(low= global) + high = probe; + else + low = probe+1; + } + + if(probe==-1) + return false; + + if( localIndices_[low].global() != global) + return false; + return true; + } + + template + inline IndexPair& ParallelIndexSet::operator[](const TG& global) + { + // perform a binary search + int low=0, high=localIndices_.size()-1, probe=-1; + + while(low= global) + high = probe; + else + low = probe+1; + } + + return localIndices_[low]; + } + template + inline typename ParallelIndexSet::iterator + ParallelIndexSet::begin() + { + return iterator(*this, localIndices_.begin()); + } + + + template + inline typename ParallelIndexSet::iterator + ParallelIndexSet::end() + { + return iterator(*this,localIndices_.end()); + } + + template + inline typename ParallelIndexSet::const_iterator + ParallelIndexSet::begin() const + { + return localIndices_.begin(); + } + + + template + inline typename ParallelIndexSet::const_iterator + ParallelIndexSet::end() const + { + return localIndices_.end(); + } + + template + void ParallelIndexSet::renumberLocal(){ +#ifndef NDEBUG + if(state_==RESIZE) + DUNE_THROW(InvalidIndexSetState, "IndexSet has to be in " + <<"GROUND state for renumberLocal()"); +#endif + + const auto end_ = end(); + uint32_t index=0; + + for(auto pair=begin(); pair!=end_; index++, ++pair) + pair->local()=index; + } + + template + inline int ParallelIndexSet::seqNo() const + { + return seqNo_; + } + + template + inline size_t ParallelIndexSet::size() const + { + return localIndices_.size(); + } + + template + GlobalLookupIndexSet::GlobalLookupIndexSet(const I& indexset, + std::size_t size) + : indexSet_(indexset), size_(size), + indices_(size_, static_cast(0)) + { + const_iterator end_ = indexSet_.end(); + + for(const_iterator pair = indexSet_.begin(); pair!=end_; ++pair) { + assert(pair->local()local()] = &(*pair); + } + } + + template + GlobalLookupIndexSet::GlobalLookupIndexSet(const I& indexset) + : indexSet_(indexset), size_(0) + { + const_iterator end_ = indexSet_.end(); + for(const_iterator pair = indexSet_.begin(); pair!=end_; ++pair) + size_=std::max(size_,static_cast(pair->local())); + + indices_.resize(++size_, 0); + + for(const_iterator pair = indexSet_.begin(); pair!=end_; ++pair) + indices_[pair->local()] = &(*pair); + } + + template + GlobalLookupIndexSet::~GlobalLookupIndexSet() + {} + + template + inline const IndexPair* + GlobalLookupIndexSet::pair(const std::size_t& local) const + { + return indices_[local]; + } + + template + inline const IndexPair& + GlobalLookupIndexSet::operator[](const GlobalIndex& global) const + { + return indexSet_[global]; + } + + template + typename I::const_iterator GlobalLookupIndexSet::begin() const + { + return indexSet_.begin(); + } + + template + typename I::const_iterator GlobalLookupIndexSet::end() const + { + return indexSet_.end(); + } + + template + inline size_t GlobalLookupIndexSet::size() const + { + return size_; + } + + template + inline int GlobalLookupIndexSet::seqNo() const + { + return indexSet_.seqNo(); + } + + template + bool operator==(const ParallelIndexSet& idxset, + const ParallelIndexSet& idxset1) + { + if(idxset.size()!=idxset1.size()) + return false; + typedef typename ParallelIndexSet::const_iterator Iter; + typedef typename ParallelIndexSet::const_iterator Iter1; + Iter iter=idxset.begin(); + for(Iter1 iter1=idxset1.begin(); iter1 != idxset1.end(); ++iter, ++iter1) { + if(iter1->global()!=iter->global()) + return false; + typedef typename ParallelIndexSet::LocalIndex PI; + const PI& pi=iter->local(), pi1=iter1->local(); + + if(pi!=pi1) + return false; + } + return true; + } + + template + bool operator!=(const ParallelIndexSet& idxset, + const ParallelIndexSet& idxset1) + { + return !(idxset==idxset1); + } + + +#endif // DOXYGEN + +} +#endif diff --git a/dune/common/parallel/indicessyncer.hh b/dune/common/parallel/indicessyncer.hh new file mode 100644 index 0000000..d132652 --- /dev/null +++ b/dune/common/parallel/indicessyncer.hh @@ -0,0 +1,1175 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_INDICESSYNCER_HH +#define DUNE_INDICESSYNCER_HH + +#include "indexset.hh" +#include "remoteindices.hh" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_MPI +namespace Dune +{ + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Class for adding missing indices of a distributed index set in a local + * communication. + * @author Markus Blatt + */ + + /** + * @brief Class for recomputing missing indices of a distributed index set. + * + * Missing local and remote indices will be added. + */ + template + class IndicesSyncer + { + public: + + /** @brief The type of the index set. */ + typedef T ParallelIndexSet; + + /** @brief The type of the index pair */ + typedef typename ParallelIndexSet::IndexPair IndexPair; + + /** @brief Type of the global index used in the index set. */ + typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; + + /** @brief Type of the attribute used in the index set. */ + typedef typename ParallelIndexSet::LocalIndex::Attribute Attribute; + + /** + * @brief Type of the remote indices. + */ + typedef Dune::RemoteIndices RemoteIndices; + + /** + * @brief Constructor. + * + * The source as well as the target index set of the remote + * indices have to be the same as the provided index set. + * @param indexSet The index set with the information + * of the locally present indices. + * @param remoteIndices The remoteIndices. + */ + IndicesSyncer(ParallelIndexSet& indexSet, + RemoteIndices& remoteIndices); + + /** + * @brief Sync the index set. + * + * Computes the missing indices in the local and the remote index list and adds them. + * No global communication is necessary! + * All indices added to the index will become the local index + * std::numeric_limits::max() + * + */ + void sync(); + + /** + * @brief Synce the index set and assign local numbers to new indices + * + * Computes the missing indices in the local and the remote index list and adds them. + * No global communication is necessary! + * @param numberer Functor providing the local indices for the added global indices. + * has to provide a function size_t operator()(const TG& global) that provides the + * local index to a global one. It will be called for ascending global indices. + * + */ + template + void sync(T1& numberer); + + private: + + /** @brief The set of locally present indices.*/ + ParallelIndexSet& indexSet_; + + /** @brief The remote indices. */ + RemoteIndices& remoteIndices_; + + /** @brief The send buffers for the neighbour processes. */ + char** sendBuffers_; + + /** @brief The receive buffer. */ + char* receiveBuffer_; + + /** @brief The size of the send buffers. */ + std::size_t* sendBufferSizes_; + + /** @brief The size of the receive buffer in bytes. */ + int receiveBufferSize_; // int because of MPI + + /** + * @brief Information about the messages to send to a neighbouring process. + */ + struct MessageInformation + { + MessageInformation() + : publish(), pairs() + {} + /** @brief The number of indices we publish for the other process. */ + int publish; + /** + * @brief The number of pairs (attribute and process number) + * we publish to the neighbour process. + */ + int pairs; + }; + + /** + * @brief Default numberer for sync(). + */ + class DefaultNumberer + { + public: + /** + * @brief Provide the lcoal index, always + * std::numeric_limits::max() + * @param global The global index (ignored). + */ + std::size_t operator()([[maybe_unused]] const GlobalIndex& global) + { + return std::numeric_limits::max(); + } + }; + + /** @brief The mpi datatype for the MessageInformation */ + MPI_Datatype datatype_; + + /** @brief Our rank. */ + int rank_; + + /** + * @brief List type for temporarily storing the global indices of the + * remote indices. + */ + typedef SLList, typename RemoteIndices::Allocator> GlobalIndexList; + + /** @brief The modifying iterator for the global index list. */ + typedef typename GlobalIndexList::ModifyIterator GlobalIndexModifier; + + /** + * @brief The type of the iterator of GlobalIndexList + */ + typedef typename SLList::iterator + GlobalIndexIterator; + + /** @brief Type of the map of ranks onto GlobalIndexLists. */ + typedef std::map GlobalIndicesMap; + + /** + * @brief Map of global index lists onto process ranks. + * + * As the pointers in the remote index lists become invalid due to + * resorting the index set entries one has store the corresponding + * global index for each remote index. Thus the pointers can be adjusted + * properly as a last step. + */ + GlobalIndicesMap globalMap_; + + /** + * @brief The type of the single linked list of bools. + */ + typedef SLList BoolList; + + /** + * @brief The mutable iterator of the single linked bool list. + */ + typedef typename BoolList::iterator BoolIterator; + + /** @brief The type of the modifying iterator for the list of bools. */ + typedef typename BoolList::ModifyIterator BoolListModifier; + + /** @brief The type of the map of bool lists. */ + typedef std::map BoolMap; + + /** + * @brief Map of lists of bool indicating whether the remote index was present before + * call of sync. + */ + BoolMap oldMap_; + + /** @brief Information about the messages we send. */ + std::map infoSend_; + + /** @brief The type of the remote index list. */ + typedef typename RemoteIndices::RemoteIndexList RemoteIndexList; + + /** @brief The tyoe of the modifying iterator of the remote index list. */ + typedef typename RemoteIndexList::ModifyIterator RemoteIndexModifier; + + /** @brief The type of the remote inde. */ + typedef Dune::RemoteIndex RemoteIndex; + + /** @brief The iterator of the remote index list. */ + typedef typename RemoteIndexList::iterator RemoteIndexIterator; + + /** @brief The const iterator of the remote index list. */ + typedef typename RemoteIndexList::const_iterator ConstRemoteIndexIterator; + + /** @brief Type of the tuple of iterators needed for the adding of indices. */ + typedef std::tuple IteratorTuple; + + /** + * @brief A tuple of iterators. + * + * Insertion into a single linked list is only possible at the position after the one of the iterator. + * Therefore for each linked list two iterators are needed: One position before the actual entry + * (for insertion) and one positioned at the actual position (for searching). + */ + class Iterators + { + friend class IndicesSyncer; + public: + /** + * @brief Constructor. + * + * Initializes all iterator to first entry and the one before the first entry, respectively. + * @param remoteIndices The list of the remote indices. + * @param globalIndices The list of the coresponding global indices. This is needed because the + * the pointers to the local index will become invalid due to the merging of the index sets. + * @param booleans Whether the remote index was there before the sync process started. + */ + Iterators(RemoteIndexList& remoteIndices, GlobalIndexList& globalIndices, + BoolList& booleans); + + /** + * @brief Default constructor. + */ + Iterators(); + + /** + * @brief Increment all iteraors. + */ + Iterators& operator++(); + + /** + * @brief Insert a new remote index to the underlying remote index list. + * @param index The remote index. + * @param global The global index corresponding to the remote index. + */ + void insert(const RemoteIndex& index, + const std::pair& global); + + /** + * @brief Get the remote index at current position. + * @return The current remote index. + */ + RemoteIndex& remoteIndex() const; + + /** + * @brief Get the global index of the remote index at current position. + * @return The current global index. + */ + std::pair& globalIndexPair() const; + + Attribute& attribute() const; + + /** + * @brief Was this entry already in the remote index list before the sync process? + * @return True if the current index wasalready in the remote index list + * before the sync process. + */ + bool isOld() const; + + /** + * @brief Reset all the underlying iterators. + * + * Position them to first list entry and the entry before the first entry respectively. + * @param remoteIndices The list of the remote indices. + * @param globalIndices The list of the coresponding global indices. This is needed because the + * the pointers to the local index will become invalid due to the merging of the index sets. + * @param booleans Whether the remote index was there before the sync process started. + */ + void reset(RemoteIndexList& remoteIndices, GlobalIndexList& globalIndices, + BoolList& booleans); + + /** + * @brief Are we not at the end of the list? + * @return True if the iterators are not positioned at the end of the list + * and the tail of the list respectively. + */ + bool isNotAtEnd() const; + + /** + * @brief Are we at the end of the list? + * @return True if the iterators are positioned at the end of the list + * and the tail of the list respectively. + */ + bool isAtEnd() const; + + private: + /** + * @brief The iterator tuple. + * + * The tuple consists of one iterator over a single linked list of remote indices + * initially positioned before the first entry, one over a sll of global indices + * , one over a all of bool values both postioned at the same entry. The another three + * iterators of the same type positioned at the first entry. Last an iterator over the + * sll of remote indices positioned at the end. + */ + IteratorTuple iterators_; + }; + + /** @brief Type of the map from ranks to iterator tuples. */ + typedef std::map IteratorsMap; + + /** + * @brief The iterator tuples mapped on the neighbours. + * + * The key of the map is the rank of the neighbour. + * The first entry in the tuple is an iterator over the remote indices + * initially positioned before the first entry. The second entry is an + * iterator over the corresponding global indices also initially positioned + * before the first entry. The third entry an iterator over remote indices + * initially positioned at the beginning. The last entry is the iterator over + * the remote indices positioned at the end. + */ + IteratorsMap iteratorsMap_; + + /** @brief Calculates the message sizes to send. */ + void calculateMessageSizes(); + + /** + * @brief Pack and send the message for another process. + * @param destination The rank of the process we send to. + * @param buffer The allocated buffer to use. + * @param bufferSize The size of the buffer. + * @param req The MPI_Request to setup the nonblocking send. + */ + void packAndSend(int destination, char* buffer, std::size_t bufferSize, MPI_Request& req); + + /** + * @brief Recv and unpack the message from another process and add the indices. + * @param numberer Functor providing local indices for added global indices. + */ + template + void recvAndUnpack(T1& numberer); + + /** + * @brief Register the MPI datatype for the MessageInformation. + */ + void registerMessageDatatype(); + + /** + * @brief Insert an entry into the remote index list if not yet present. + */ + void insertIntoRemoteIndexList(int process, + const std::pair& global, + char attribute); + + /** + * @brief Reset the iterator tuples of all neighbouring processes. + */ + void resetIteratorsMap(); + + /** + * @brief Check whether the iterator tuples of all neighbouring processes + * are reset. + */ + bool checkReset(); + + /** + * @brief Check whether the iterator tuple is reset. + * + * @param iterators The iterator tuple to check. + * @param rlist The SLList of the remote indices. + * @param gList The SLList of the global indices. + * @param bList The SLList of the bool values. + */ + bool checkReset(const Iterators& iterators, RemoteIndexList& rlist, GlobalIndexList& gList, + BoolList& bList); + }; + + template + bool operator<(const IndexPair >& i1, + const std::pair& i2) + { + return i1.global() < i2.first || + (i1.global() == i2.first && i1.local().attribute() + bool operator<(const std::pair& i1, + const IndexPair >& i2) + { + return i1.first < i2.global() || + (i1.first == i2.global() && i1.second + bool operator==(const IndexPair >& i1, + const std::pair& i2) + { + return (i1.global() == i2.first && i1.local().attribute()==i2.second); + } + + template + bool operator!=(const IndexPair >& i1, + const std::pair& i2) + { + return (i1.global() != i2.first || i1.local().attribute()!=i2.second); + } + + template + bool operator==(const std::pair& i2, + const IndexPair >& i1) + { + return (i1.global() == i2.first && i1.local().attribute()==i2.second); + } + + template + bool operator!=(const std::pair& i2, + const IndexPair >& i1) + { + return (i1.global() != i2.first || i1.local().attribute()!=i2.second); + } + + /** + * @brief Stores the corresponding global indices of the remote index information. + * + * Whenever a ParallelIndexSet is resized all RemoteIndices that use it will be invalided + * as the pointers to the index set are invalid after calling ParallelIndexSet::Resize() + * One can rebuild them by storing the global indices in a map with this function and later + * repairing the pointers by calling repairLocalIndexPointers. + * + * @warning The RemoteIndices class has to be build with the same index set for both the + * sending and receiving side + * @param globalMap Map to store the corresponding global indices in. + * @param remoteIndices The remote index information we need to store the corresponding global + * indices of. + * @param indexSet The index set that is for both the sending and receiving side of the remote + * index information. + */ + template + void storeGlobalIndicesOfRemoteIndices(std::map,A> >& globalMap, + const RemoteIndices& remoteIndices) + { + for(auto remote = remoteIndices.begin(), end =remoteIndices.end(); remote != end; ++remote) { + typedef typename RemoteIndices::RemoteIndexList RemoteIndexList; + typedef SLList,A> GlobalIndexList; + GlobalIndexList& global = globalMap[remote->first]; + RemoteIndexList& rList = *(remote->second.first); + + for(auto index = rList.begin(), riEnd = rList.end(); + index != riEnd; ++index) { + global.push_back(std::make_pair(index->localIndexPair().global(), + index->localIndexPair().local().attribute())); + } + } + } + + /** + * @brief Repair the pointers to the local indices in the remote indices. + * + * @param globalMap The map of the process number to the list of global indices + * corresponding to the remote index list of the process. + * @param remoteIndices The known remote indices. + * @param indexSet The set of local indices of the current process. + */ + template + inline void repairLocalIndexPointers(std::map,A> >& globalMap, + RemoteIndices& remoteIndices, + const T& indexSet) + { + assert(globalMap.size()==static_cast(remoteIndices.neighbours())); + // Repair pointers to index set in remote indices. + auto global = globalMap.begin(); + auto end = remoteIndices.remoteIndices_.end(); + + for(auto remote = remoteIndices.remoteIndices_.begin(); remote != end; ++remote, ++global) { + assert(remote->first==global->first); + assert(remote->second.first->size() == global->second.size()); + + auto riEnd = remote->second.first->end(); + auto rIndex = remote->second.first->begin(); + auto gIndex = global->second.begin(); + auto index = indexSet.begin(); + + assert(rIndex==riEnd || gIndex != global->second.end()); + while(rIndex != riEnd) { + // Search for the index in the set. + assert(gIndex != global->second.end()); + + while(!(index->global() == gIndex->first + && index->local().attribute() == gIndex->second)) { + ++index; + // this is only needed for ALU, where there may exist + // more entries with the same global index in the remote index set + // than in the index set + if (index->global() > gIndex->first) { + index=indexSet.begin(); + } + } + + assert(index != indexSet.end() && *index == *gIndex); + + rIndex->localIndex_ = &(*index); + ++index; + ++rIndex; + ++gIndex; + } + } + remoteIndices.sourceSeqNo_ = remoteIndices.source_->seqNo(); + remoteIndices.destSeqNo_ = remoteIndices.target_->seqNo(); + } + + template + IndicesSyncer::IndicesSyncer(ParallelIndexSet& indexSet, + RemoteIndices& remoteIndices) + : indexSet_(indexSet), remoteIndices_(remoteIndices) + { + // index sets must match. + assert(remoteIndices.source_ == remoteIndices.target_); + assert(remoteIndices.source_ == &indexSet); + MPI_Comm_rank(remoteIndices_.communicator(), &rank_); + } + + template + IndicesSyncer::Iterators::Iterators(RemoteIndexList& remoteIndices, + GlobalIndexList& globalIndices, + BoolList& booleans) + : iterators_(remoteIndices.beginModify(), globalIndices.beginModify(), + booleans.beginModify(), remoteIndices.end()) + { } + + template + IndicesSyncer::Iterators::Iterators() + : iterators_() + {} + + template + inline typename IndicesSyncer::Iterators& IndicesSyncer::Iterators::operator++() + { + ++(std::get<0>(iterators_)); + ++(std::get<1>(iterators_)); + ++(std::get<2>(iterators_)); + return *this; + } + + template + inline void IndicesSyncer::Iterators::insert(const RemoteIndex & index, + const std::pair& global) + { + std::get<0>(iterators_).insert(index); + std::get<1>(iterators_).insert(global); + std::get<2>(iterators_).insert(false); + } + + template + inline typename IndicesSyncer::RemoteIndex& + IndicesSyncer::Iterators::remoteIndex() const + { + return *(std::get<0>(iterators_)); + } + + template + inline std::pair::GlobalIndex,typename IndicesSyncer::Attribute>& + IndicesSyncer::Iterators::globalIndexPair() const + { + return *(std::get<1>(iterators_)); + } + + template + inline bool IndicesSyncer::Iterators::isOld() const + { + return *(std::get<2>(iterators_)); + } + + template + inline void IndicesSyncer::Iterators::reset(RemoteIndexList& remoteIndices, + GlobalIndexList& globalIndices, + BoolList& booleans) + { + std::get<0>(iterators_) = remoteIndices.beginModify(); + std::get<1>(iterators_) = globalIndices.beginModify(); + std::get<2>(iterators_) = booleans.beginModify(); + } + + template + inline bool IndicesSyncer::Iterators::isNotAtEnd() const + { + return std::get<0>(iterators_) != std::get<3>(iterators_); + } + + template + inline bool IndicesSyncer::Iterators::isAtEnd() const + { + return std::get<0>(iterators_) == std::get<3>(iterators_); + } + + template + void IndicesSyncer::registerMessageDatatype() + { + MPI_Datatype type[2] = {MPI_INT, MPI_INT}; + int blocklength[2] = {1,1}; + MPI_Aint displacement[2]; + MPI_Aint base; + + // Compute displacement + MessageInformation message; + + MPI_Get_address( &(message.publish), displacement); + MPI_Get_address( &(message.pairs), displacement+1); + + // Make the displacement relative + MPI_Get_address(&message, &base); + displacement[0] -= base; + displacement[1] -= base; + + MPI_Type_create_struct( 2, blocklength, displacement, type, &datatype_); + MPI_Type_commit(&datatype_); + } + + template + void IndicesSyncer::calculateMessageSizes() + { + auto iEnd = indexSet_.end(); + auto collIter = remoteIndices_.template iterator(); + + for(auto index = indexSet_.begin(); index != iEnd; ++index) { + collIter.advance(index->global(), index->local().attribute()); + if(collIter.empty()) + break; + int knownRemote=0; + auto end = collIter.end(); + + // Count the remote indices we know. + for(auto valid = collIter.begin(); valid != end; ++valid) { + ++knownRemote; + } + + if(knownRemote>0) { + Dune::dverb<global()<< " for processes "; + + // Update MessageInformation + for(auto valid = collIter.begin(); valid != end; ++valid) { + ++(infoSend_[valid.process()].publish); + (infoSend_[valid.process()].pairs) += knownRemote; + Dune::dverb<first==remote->first) { + // We want to send message information to that process + message = const_cast(&(messageIter->second)); + ++messageIter; + }else + // We do not want to send information but the other process might. + message = &dummy; + + sendBufferSizes_[neighbour]=0; + int tsize; + // The number of indices published + MPI_Pack_size(1, MPI_INT,remoteIndices_.communicator(), &tsize); + sendBufferSizes_[neighbour] += tsize; + + for(int i=0; i < message->publish; ++i) { + // The global index + MPI_Pack_size(1, MPITraits::getType(), remoteIndices_.communicator(), &tsize); + sendBufferSizes_[neighbour] += tsize; + // The attribute in the local index + MPI_Pack_size(1, MPI_CHAR, remoteIndices_.communicator(), &tsize); + sendBufferSizes_[neighbour] += tsize; + // The number of corresponding remote indices + MPI_Pack_size(1, MPI_INT, remoteIndices_.communicator(), &tsize); + sendBufferSizes_[neighbour] += tsize; + } + for(int i=0; i < message->pairs; ++i) { + // The process of the remote index + MPI_Pack_size(1, MPI_INT, remoteIndices_.communicator(), &tsize); + sendBufferSizes_[neighbour] += tsize; + // The attribute of the remote index + MPI_Pack_size(1, MPI_CHAR, remoteIndices_.communicator(), &tsize); + sendBufferSizes_[neighbour] += tsize; + } + + Dune::dverb< + void IndicesSyncer::packAndSend(int destination, char* buffer, std::size_t bufferSize, MPI_Request& request) + { + auto iEnd = indexSet_.end(); + int bpos = 0; + int published = 0; + int pairs = 0; + + assert(checkReset()); + + // Pack the number of indices we publish + MPI_Pack(&(infoSend_[destination].publish), 1, MPI_INT, buffer, bufferSize, &bpos, + remoteIndices_.communicator()); + + for(auto index = indexSet_.begin(); index != iEnd; ++index) { + // Search for corresponding remote indices in all iterator tuples + auto iteratorsEnd = iteratorsMap_.end(); + + // advance all iterators to a position with global index >= index->global() + for(auto iterators = iteratorsMap_.begin(); iteratorsEnd != iterators; ++iterators) { + while(iterators->second.isNotAtEnd() && + iterators->second.globalIndexPair().first < index->global()) + ++(iterators->second); + assert(!iterators->second.isNotAtEnd() || iterators->second.globalIndexPair().first >= index->global()); + } + + // Add all remote indices positioned at global which were already present before calling sync + // to the message. + // Count how many remote indices we will send + int indices = 0; + bool knownRemote = false; // Is the remote process supposed to know this index? + + for(auto iterators = iteratorsMap_.begin(); iteratorsEnd != iterators; ++iterators) + { + std::pair p; + if (iterators->second.isNotAtEnd()) + { + p = iterators->second.globalIndexPair(); + } + + if(iterators->second.isNotAtEnd() && iterators->second.isOld() + && iterators->second.globalIndexPair().first == index->global()) { + indices++; + if(destination == iterators->first) + knownRemote = true; + } + } + + if(!knownRemote) + // We do not need to send any indices + continue; + + Dune::dverb<global()<<" to "<(&(index->global())), 1, MPITraits::getType(), buffer, bufferSize, &bpos, + remoteIndices_.communicator()); + + char attr = index->local().attribute(); + MPI_Pack(&attr, 1, MPI_CHAR, buffer, bufferSize, &bpos, + remoteIndices_.communicator()); + + // Pack the number of remote indices we send. + MPI_Pack(&indices, 1, MPI_INT, buffer, bufferSize, &bpos, + remoteIndices_.communicator()); + + // Pack the information about the remote indices + for(auto iterators = iteratorsMap_.begin(); iteratorsEnd != iterators; ++iterators) + if(iterators->second.isNotAtEnd() && iterators->second.isOld() + && iterators->second.globalIndexPair().first == index->global()) { + int process = iterators->first; + + ++pairs; + assert(pairs <= infoSend_[destination].pairs); + MPI_Pack(&process, 1, MPI_INT, buffer, bufferSize, &bpos, + remoteIndices_.communicator()); + char attr2 = iterators->second.remoteIndex().attribute(); + + MPI_Pack(&attr2, 1, MPI_CHAR, buffer, bufferSize, &bpos, + remoteIndices_.communicator()); + --indices; + } + assert(indices==0); + ++published; + Dune::dvverb<<" (publish="< + inline void IndicesSyncer::insertIntoRemoteIndexList(int process, + const std::pair& globalPair, + char attribute) + { + Dune::dverb<<"Inserting from "<second; + + // Search for the remote index + while(iterators.isNotAtEnd() && iterators.globalIndexPair() < globalPair) { + // Increment all iterators + ++iterators; + + } + + if(iterators.isAtEnd() || iterators.globalIndexPair() != globalPair) { + // The entry is not yet known + // Insert in the list and do not change the first iterator. + iterators.insert(RemoteIndex(Attribute(attribute)),globalPair); + return; + } + + // Global indices match + bool indexIsThere=false; + for(Iterators tmpIterators = iterators; + !tmpIterators.isAtEnd() && tmpIterators.globalIndexPair() == globalPair; + ++tmpIterators) + //entry already exists with the same attribute + if(tmpIterators.globalIndexPair().second == attribute) { + indexIsThere=true; + break; + } + + if(!indexIsThere) + // The entry is not yet known + // Insert in the list and do not change the first iterator. + iterators.insert(RemoteIndex(Attribute(attribute)),globalPair); + } + + template + template + void IndicesSyncer::recvAndUnpack(T1& numberer) + { + const ParallelIndexSet& constIndexSet = indexSet_; + auto iEnd = constIndexSet.end(); + auto index = constIndexSet.begin(); + int bpos = 0; + int publish; + + assert(checkReset()); + + MPI_Status status; + + // We have to determine the message size and source before the receive + MPI_Probe(MPI_ANY_SOURCE, 345, remoteIndices_.communicator(), &status); + + int source=status.MPI_SOURCE; + int count; + MPI_Get_count(&status, MPI_PACKED, &count); + + Dune::dvverb<receiveBufferSize_) { + receiveBufferSize_=count; + delete[] receiveBuffer_; + receiveBuffer_ = new char[receiveBufferSize_]; + } + + MPI_Recv(receiveBuffer_, count, MPI_PACKED, source, 345, remoteIndices_.communicator(), &status); + + // How many global entries were published? + MPI_Unpack(receiveBuffer_, count, &bpos, &publish, 1, MPI_INT, remoteIndices_.communicator()); + + // Now unpack the remote indices and add them. + while(publish>0) { + + // Unpack information about the local index on the source process + GlobalIndex global; // global index of the current entry + char sourceAttribute; // Attribute on the source process + int pairs; + + MPI_Unpack(receiveBuffer_, count, &bpos, &global, 1, MPITraits::getType(), + remoteIndices_.communicator()); + MPI_Unpack(receiveBuffer_, count, &bpos, &sourceAttribute, 1, MPI_CHAR, + remoteIndices_.communicator()); + MPI_Unpack(receiveBuffer_, count, &bpos, &pairs, 1, MPI_INT, + remoteIndices_.communicator()); + + // Insert the entry on the remote process to our + // remote index list + SLList > sourceAttributeList; + sourceAttributeList.push_back(std::make_pair(source,Attribute(sourceAttribute))); +#ifndef NDEBUG + bool foundSelf = false; +#endif + Attribute myAttribute=Attribute(); + + // Unpack the remote indices + for(; pairs>0; --pairs) { + // Unpack the process id that knows the index + int process; + char attribute; + MPI_Unpack(receiveBuffer_, count, &bpos, &process, 1, MPI_INT, + remoteIndices_.communicator()); + // Unpack the attribute + MPI_Unpack(receiveBuffer_, count, &bpos, &attribute, 1, MPI_CHAR, + remoteIndices_.communicator()); + + if(process==rank_) { +#ifndef NDEBUG + foundSelf=true; +#endif + myAttribute=Attribute(attribute); + // Now we know the local attribute of the global index + //Only add the index if it is unknown. + // Do we know that global index already? + auto pos = std::lower_bound(index, iEnd, IndexPair(global)); + + if(pos == iEnd || pos->global() != global) { + // no entry with this global index + indexSet_.add(global, + ParallelLocalIndex(numberer(global), + myAttribute, true)); + Dune::dvverb << "Adding "<global()==global; ++pos) + if(pos->local().attribute() == myAttribute) { + Dune::dvverb<<"found "<(numberer(global), + myAttribute, true)); + Dune::dvverb << "Adding "< >::const_iterator Iter; + for(Iter i=sourceAttributeList.begin(), end=sourceAttributeList.end(); + i!=end; ++i) + insertIntoRemoteIndexList(i->first, std::make_pair(global, myAttribute), + i->second); + --publish; + } + + resetIteratorsMap(); + } + + template + void IndicesSyncer::resetIteratorsMap(){ + + // Reset iterators in all tuples. + const auto remoteEnd = remoteIndices_.remoteIndices_.end(); + auto iterators = iteratorsMap_.begin(); + auto global = globalMap_.begin(); + auto added = oldMap_.begin(); + + for(auto remote = remoteIndices_.remoteIndices_.begin(); + remote != remoteEnd; ++remote, ++global, ++added, ++iterators) { + iterators->second.reset(*(remote->second.first), global->second, added->second); + } + } + + template + bool IndicesSyncer::checkReset(const Iterators& iterators, RemoteIndexList& rList, GlobalIndexList& gList, + BoolList& bList){ + + if(std::get<0>(iterators.iterators_) != rList.begin()) + return false; + if(std::get<1>(iterators.iterators_) != gList.begin()) + return false; + if(std::get<2>(iterators.iterators_) != bList.begin()) + return false; + return true; + } + + + template + bool IndicesSyncer::checkReset(){ + + // Reset iterators in all tuples. + const auto remoteEnd = remoteIndices_.remoteIndices_.end(); + auto iterators = iteratorsMap_.begin(); + auto global = globalMap_.begin(); + auto added = oldMap_.begin(); + bool ret = true; + + for(auto remote = remoteIndices_.remoteIndices_.begin(); + remote != remoteEnd; ++remote, ++global, ++added, ++iterators) { + if(!checkReset(iterators->second, *(remote->second.first), global->second, + added->second)) + ret=false; + } + return ret; + } +} + +#endif +#endif diff --git a/dune/common/parallel/interface.hh b/dune/common/parallel/interface.hh new file mode 100644 index 0000000..d43d082 --- /dev/null +++ b/dune/common/parallel/interface.hh @@ -0,0 +1,528 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_INTERFACE_HH +#define DUNE_INTERFACE_HH + +#if HAVE_MPI + +#include "remoteindices.hh" +#include + +namespace Dune +{ + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Provides classes for building the communication + * interface between remote indices. + * @author Markus Blatt + */ + + /** @} */ + /** + * @brief Base class of all classes representing a communication + * interface. + * + * It provides an generic utility method for building the interface + * for a set of remote indices. + */ + class InterfaceBuilder + { + public: + class RemoteIndicesStateError : public InvalidStateException + {}; + + virtual ~InterfaceBuilder() + {} + + protected: + /** + * @brief Not for public use. + */ + InterfaceBuilder() + {} + + /** + * @brief Builds the interface between remote processes. + * + * + * The types T1 and T2 are classes representing a set of + * enumeration values of type InterfaceBuilder::Attribute. They have to provide + * a (static) method + * \code + * bool contains(Attribute flag) const; + * \endcode + * for checking whether the set contains a specific flag. + * This functionality is for example provided the classes + * EnumItem, EnumRange and Combine. + * + * If the template parameter send is true the sending side of + * the interface will be built, otherwise the information for + * receiving will be built. + * + * + * If the template parameter send is true we create interface for sending + * in a forward communication. + * + * @param remoteIndices The indices known to remote processes. + * @param sourceFlags The set of flags marking source indices. + * @param destFlags The setof flags markig destination indices. + * @param functor A functor for callbacks. It should provide the + * following methods: + * \code + * // Reserve memory for the interface to processor proc. The interface + * // has to hold size entries + * void reserve(int proc, int size); + * + * // Add an entry to the interface + * // We will send/receive size entries at index local to process proc + * void add(int proc, int local); + * \endcode + */ + template + void buildInterface (const R& remoteIndices, + const T1& sourceFlags, const T2& destFlags, + Op& functor) const; + }; + + /** + * @brief Information describing an interface. + * + * This class is used for temporary gathering information + * about the interface needed for actually building it. It + * is used be class Interface as functor for InterfaceBuilder::build. + */ + class InterfaceInformation + { + + public: + + /** + * @brief Get the number of entries in the interface. + */ + size_t size() const + { + return size_; + } + /** + * @brief Get the local index for an entry. + * @param i The index of the entry. + */ + std::size_t& operator[](size_t i) + { + assert(i > InformationMap; + + /** + * @brief Builds the interface. + * + * The types T1 and T2 are classes representing a set of + * enumeration values of type Interface::Attribute. They have to provide + * a (static) method + * \code + * bool contains(Attribute flag) const; + * \endcode + * for checking whether the set contains a specific flag. + * This functionality is for example provided the classes + * EnumItem, EnumRange and Combine. + * @param remoteIndices The indices known to remote processes. + * @param sourceFlags The set of flags marking indices we send from. + * @param destFlags The set of flags marking indices we receive for. + */ + template + void build(const R& remoteIndices, const T1& sourceFlags, + const T2& destFlags); + + /** + * @brief Frees memory allocated during the build. + */ + void free(); + + /** + * @brief Get the MPI Communicator. + */ + MPI_Comm communicator() const; + + /** + * @brief Get information about the interfaces. + * + * @return Map of the interfaces. + * The key of the map is the process number and the value + * is the information pair (first the send and then the receive + * information). + */ + const InformationMap& interfaces() const; + + Interface(MPI_Comm comm) + : communicator_(comm), interfaces_() + {} + + Interface() + : communicator_(MPI_COMM_NULL), interfaces_() + {} + + /** + * @brief Print the interface to std::out for debugging. + */ + void print() const; + + bool operator!=(const Interface& o) const + { + return ! operator==(o); + } + + bool operator==(const Interface& o) const + { + if(communicator_!=o.communicator_) + return false; + if(interfaces_.size()!=o.interfaces_.size()) + return false; + typedef InformationMap::const_iterator MIter; + + for(MIter m=interfaces_.begin(), om=o.interfaces_.begin(); + m!=interfaces_.end(); ++m, ++om) + { + if(om->first!=m->first) + return false; + if(om->second.first!=om->second.first) + return false; + if(om->second.second!=om->second.second) + return false; + } + return true; + } + + /** + * @brief Destructor. + */ + virtual ~Interface(); + + void strip(); + protected: + + /** + * @brief Get information about the interfaces. + * + * @return Map of the interfaces. + * The key of the map is the process number and the value + * is the information pair (first the send and then the receive + * information). + */ + InformationMap& interfaces(); + + /** @brief The MPI communicator we use. */ + MPI_Comm communicator_; + + private: + /** + * @brief Information about the interfaces. + * + * The key of the map is the process number and the value + * is the information pair (first the send and then the receive + * information). + */ + InformationMap interfaces_; + + template + class InformationBuilder + { + public: + InformationBuilder(InformationMap& interfaces) + : interfaces_(interfaces) + {} + + void reserve(int proc, int size) + { + if(send) + interfaces_[proc].first.reserve(size); + else + interfaces_[proc].second.reserve(size); + } + void add(int proc, std::size_t local) + { + if(send) { + interfaces_[proc].first.add(local); + }else{ + interfaces_[proc].second.add(local); + } + } + + private: + InformationMap& interfaces_; + }; + }; + + template + void InterfaceBuilder::buildInterface(const R& remoteIndices, const T1& sourceFlags, const T2& destFlags, Op& interfaceInformation) const + { + + if(!remoteIndices.isSynced()) + DUNE_THROW(RemoteIndicesStateError,"RemoteIndices is not in sync with the index set. Call RemoteIndices::rebuild first!"); + // Allocate the memory for the data type construction. + typedef R RemoteIndices; + typedef typename RemoteIndices::RemoteIndexMap::const_iterator const_iterator; + + const const_iterator end=remoteIndices.end(); + + int rank; + + MPI_Comm_rank(remoteIndices.communicator(), &rank); + + // Allocate memory for the type construction. + for(const_iterator process=remoteIndices.begin(); process != end; ++process) { + // Messure the number of indices send to the remote process first + int size=0; + typedef typename RemoteIndices::RemoteIndexList::const_iterator RemoteIterator; + const RemoteIterator remoteEnd = send ? process->second.first->end() : + process->second.second->end(); + RemoteIterator remote = send ? process->second.first->begin() : process->second.second->begin(); + + while(remote!=remoteEnd) { + if( send ? destFlags.contains(remote->attribute()) : + sourceFlags.contains(remote->attribute())) { + + // do we send the index? + if( send ? sourceFlags.contains(remote->localIndexPair().local().attribute()) : + destFlags.contains(remote->localIndexPair().local().attribute())) + ++size; + } + ++remote; + } + interfaceInformation.reserve(process->first, size); + } + + // compare the local and remote indices and set up the types + + for(const_iterator process=remoteIndices.begin(); process != end; ++process) { + typedef typename RemoteIndices::RemoteIndexList::const_iterator RemoteIterator; + const RemoteIterator remoteEnd = send ? process->second.first->end() : + process->second.second->end(); + RemoteIterator remote = send ? process->second.first->begin() : process->second.second->begin(); + + while(remote!=remoteEnd) { + if( send ? destFlags.contains(remote->attribute()) : + sourceFlags.contains(remote->attribute())) { + // do we send the index? + if( send ? sourceFlags.contains(remote->localIndexPair().local().attribute()) : + destFlags.contains(remote->localIndexPair().local().attribute())) + interfaceInformation.add(process->first,remote->localIndexPair().local().local()); + } + ++remote; + } + } + } + + inline MPI_Comm Interface::communicator() const + { + return communicator_; + + } + + + inline const std::map >& Interface::interfaces() const + { + return interfaces_; + } + + inline std::map >& Interface::interfaces() + { + return interfaces_; + } + + inline void Interface::print() const + { + typedef InformationMap::const_iterator const_iterator; + const const_iterator end=interfaces_.end(); + int rank; + MPI_Comm_rank(communicator(), &rank); + + for(const_iterator infoPair=interfaces_.begin(); infoPair!=end; ++infoPair) { + { + std::cout<first<<": "; + const InterfaceInformation& info(infoPair->second.first); + for(size_t i=0; i < info.size(); i++) + std::cout<first<<": "; + const InterfaceInformation& info(infoPair->second.second); + for(size_t i=0; i < info.size(); i++) + std::cout< + inline void Interface::build(const R& remoteIndices, const T1& sourceFlags, + const T2& destFlags) + { + communicator_=remoteIndices.communicator(); + + assert(interfaces_.empty()); + + // Build the send interface + InformationBuilder sendInformation(interfaces_); + this->template buildInterface,true>(remoteIndices, sourceFlags, + destFlags, sendInformation); + + // Build the receive interface + InformationBuilder recvInformation(interfaces_); + this->template buildInterface,false>(remoteIndices,sourceFlags, + destFlags, recvInformation); + strip(); + } + inline void Interface::strip() + { + typedef InformationMap::iterator const_iterator; + for(const_iterator interfacePair = interfaces_.begin(); interfacePair != interfaces_.end();) + if(interfacePair->second.first.size()==0 && interfacePair->second.second.size()==0) { + interfacePair->second.first.free(); + interfacePair->second.second.free(); + const_iterator toerase=interfacePair++; + interfaces_.erase(toerase); + }else + ++interfacePair; + } + + inline void Interface::free() + { + typedef InformationMap::iterator iterator; + typedef InformationMap::const_iterator const_iterator; + const const_iterator end = interfaces_.end(); + for(iterator interfacePair = interfaces_.begin(); interfacePair != end; ++interfacePair) { + interfacePair->second.first.free(); + interfacePair->second.second.free(); + } + interfaces_.clear(); + } + + inline Interface::~Interface() + { + free(); + } + /** @} */ + + inline std::ostream& operator<<(std::ostream& os, const Interface& interface) + { + typedef Interface::InformationMap InfoMap; + typedef InfoMap::const_iterator Iter; + for(Iter i=interface.interfaces().begin(), end = interface.interfaces().end(); + i!=end; ++i) + { + os<first<<": [ source=["; + for(std::size_t j=0; j < i->second.first.size(); ++j) + os<second.first[j]<<" "; + os<<"] size="<second.first.size()<<", target=["; + for(std::size_t j=0; j < i->second.second.size(); ++j) + os<second.second[j]<<" "; + os<<"] size="<second.second.size()<<"\n"; + } + return os; + } +} +#endif // HAVE_MPI + +#endif diff --git a/dune/common/parallel/localindex.hh b/dune/common/parallel/localindex.hh new file mode 100644 index 0000000..761543b --- /dev/null +++ b/dune/common/parallel/localindex.hh @@ -0,0 +1,120 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_COMMON_LOCALINDEX_HH +#define DUNE_COMMON_LOCALINDEX_HH + +#include + +namespace Dune +{ + + + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Provides classes for use as the local index in ParallelIndexSet. + * @author Markus Blatt + */ + /** + * @brief The states avaiable for the local indices. + * @see LocalIndex::state() + */ + enum LocalIndexState {VALID, DELETED}; + + + /** + * @brief An index present on the local process. + */ + class LocalIndex + { + public: + /** + * @brief Constructor. + * known to other processes. + */ + LocalIndex() : + localIndex_(0), state_(VALID){} + + + /** + * @brief Constructor. + * @param index The value of the index. + */ + LocalIndex(std::size_t index) : + localIndex_(index), state_(VALID){} + /** + * @brief get the local index. + * @return The local index. + */ + inline const std::size_t& local() const; + + /** + * @brief Convert to the local index represented by an int. + */ + inline operator std::size_t() const; + + /** + * @brief Assign a new local index. + * + * @param index The new local index. + */ + inline LocalIndex& operator=(std::size_t index); + + /** + * @brief Get the state. + * @return The state. + */ + inline LocalIndexState state() const; + + /** + * @brief Set the state. + * @param state The state to set. + */ + inline void setState(LocalIndexState state); + + private: + /** @brief The local index. */ + std::size_t localIndex_; + + /** + * @brief The state of the index. + * + * Has to be one of LocalIndexState! + * @see LocalIndexState. + */ + char state_; + + }; + + + + inline const std::size_t& LocalIndex::local() const { + return localIndex_; + } + + inline LocalIndex::operator std::size_t() const { + return localIndex_; + } + + inline LocalIndex& LocalIndex::operator=(std::size_t index){ + localIndex_ = index; + return *this; + } + + inline LocalIndexState LocalIndex::state() const { + return static_cast(state_); + } + + inline void LocalIndex::setState(LocalIndexState state){ + state_ = static_cast(state); + } + + /** @} */ + +} // namespace Dune + +#endif diff --git a/dune/common/parallel/mpicollectivecommunication.hh b/dune/common/parallel/mpicollectivecommunication.hh new file mode 100644 index 0000000..b086d87 --- /dev/null +++ b/dune/common/parallel/mpicollectivecommunication.hh @@ -0,0 +1,3 @@ +// Will be removed after the 2.7 release +#warning "Deprecated header, use #include instead!" +#include diff --git a/dune/common/parallel/mpicommunication.hh b/dune/common/parallel/mpicommunication.hh new file mode 100644 index 0000000..8e2e181 --- /dev/null +++ b/dune/common/parallel/mpicommunication.hh @@ -0,0 +1,468 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_PARALLEL_MPICOMMUNICATION_HH +#define DUNE_COMMON_PARALLEL_MPICOMMUNICATION_HH + +/*! + \file + \brief Implements an utility class that provides + MPI's collective communication methods. + + \ingroup ParallelCommunication + */ + +#if HAVE_MPI + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace Dune +{ + + //======================================================= + // use singleton pattern and template specialization to + // generate MPI operations + //======================================================= + + template + class Generic_MPI_Op + { + + public: + static MPI_Op get () + { + if (!op) + { + op = std::make_unique(); + // The following line leaks an MPI operation object, because the corresponding + //`MPI_Op_free` is never called. It is never called because there is no easy + // way to call it at the right moment: right before the call to MPI_Finalize. + // See https://gitlab.dune-project.org/core/dune-istl/issues/80 + MPI_Op_create((void (*)(void*, void*, int*, MPI_Datatype*))&operation,true,op.get()); + } + return *op; + } + private: + static void operation (Type *in, Type *inout, int *len, MPI_Datatype*) + { + BinaryFunction func; + + for (int i=0; i< *len; ++i, ++in, ++inout) { + Type temp; + temp = func(*in, *inout); + *inout = temp; + } + } + Generic_MPI_Op () {} + Generic_MPI_Op (const Generic_MPI_Op& ) {} + static std::unique_ptr op; + }; + + + template + std::unique_ptr Generic_MPI_Op::op; + +#define ComposeMPIOp(func,op) \ + template \ + class Generic_MPI_Op, std::enable_if_t::is_intrinsic> >{ \ + public: \ + static MPI_Op get(){ \ + return op; \ + } \ + private: \ + Generic_MPI_Op () {} \ + Generic_MPI_Op (const Generic_MPI_Op & ) {} \ + } + + + ComposeMPIOp(std::plus, MPI_SUM); + ComposeMPIOp(std::multiplies, MPI_PROD); + ComposeMPIOp(Min, MPI_MIN); + ComposeMPIOp(Max, MPI_MAX); + +#undef ComposeMPIOp + + + //======================================================= + // use singleton pattern and template specialization to + // generate MPI operations + //======================================================= + + /*! \brief Specialization of Communication for MPI + \ingroup ParallelCommunication + */ + template<> + class Communication + { + public: + //! Instantiation using a MPI communicator + Communication (const MPI_Comm& c = MPI_COMM_WORLD) + : communicator(c) + { + if(communicator!=MPI_COMM_NULL) { + int initialized = 0; + MPI_Initialized(&initialized); + if (!initialized) + DUNE_THROW(ParallelError,"You must call MPIHelper::instance(argc,argv) in your main() function before using the MPI Communication!"); + MPI_Comm_rank(communicator,&me); + MPI_Comm_size(communicator,&procs); + }else{ + procs=0; + me=-1; + } + } + + //! @copydoc Communication::rank + int rank () const + { + return me; + } + + //! @copydoc Communication::size + int size () const + { + return procs; + } + + //! @copydoc Communication::send + template + int send(const T& data, int dest_rank, int tag) const + { + auto mpi_data = getMPIData(data); + return MPI_Send(mpi_data.ptr(), mpi_data.size(), mpi_data.type(), + dest_rank, tag, communicator); + } + + //! @copydoc Communication::isend + template + MPIFuture isend(const T&& data, int dest_rank, int tag) const + { + MPIFuture future(std::forward(data)); + auto mpidata = future.get_mpidata(); + MPI_Isend(mpidata.ptr(), mpidata.size(), mpidata.type(), + dest_rank, tag, communicator, &future.req_); + return future; + } + + //! @copydoc Communication::recv + template + T recv(T&& data, int source_rank, int tag, MPI_Status* status = MPI_STATUS_IGNORE) const + { + T lvalue_data(std::forward(data)); + auto mpi_data = getMPIData(lvalue_data); + MPI_Recv(mpi_data.ptr(), mpi_data.size(), mpi_data.type(), + source_rank, tag, communicator, status); + return lvalue_data; + } + + //! @copydoc Communication::irecv + template + MPIFuture irecv(T&& data, int source_rank, int tag) const + { + MPIFuture future(std::forward(data)); + auto mpidata = future.get_mpidata(); + MPI_Irecv(mpidata.ptr(), mpidata.size(), mpidata.type(), + source_rank, tag, communicator, &future.req_); + return future; + } + + template + T rrecv(T&& data, int source_rank, int tag, MPI_Status* status = MPI_STATUS_IGNORE) const + { + MPI_Status _status; + MPI_Message _message; + T lvalue_data(std::forward(data)); + auto mpi_data = getMPIData(lvalue_data); + static_assert(!mpi_data.static_size, "rrecv work only for non-static-sized types."); + if(status == MPI_STATUS_IGNORE) + status = &_status; + MPI_Mprobe(source_rank, tag, communicator, &_message, status); + int size; + MPI_Get_count(status, mpi_data.type(), &size); + mpi_data.resize(size); + MPI_Mrecv(mpi_data.ptr(), mpi_data.size(), mpi_data.type(), &_message, status); + return lvalue_data; + } + + //! @copydoc Communication::sum + template + T sum (const T& in) const + { + T out; + allreduce >(&in,&out,1); + return out; + } + + //! @copydoc Communication::sum + template + int sum (T* inout, int len) const + { + return allreduce >(inout,len); + } + + //! @copydoc Communication::prod + template + T prod (const T& in) const + { + T out; + allreduce >(&in,&out,1); + return out; + } + + //! @copydoc Communication::prod + template + int prod (T* inout, int len) const + { + return allreduce >(inout,len); + } + + //! @copydoc Communication::min + template + T min (const T& in) const + { + T out; + allreduce >(&in,&out,1); + return out; + } + + //! @copydoc Communication::min + template + int min (T* inout, int len) const + { + return allreduce >(inout,len); + } + + + //! @copydoc Communication::max + template + T max (const T& in) const + { + T out; + allreduce >(&in,&out,1); + return out; + } + + //! @copydoc Communication::max + template + int max (T* inout, int len) const + { + return allreduce >(inout,len); + } + + //! @copydoc Communication::barrier + int barrier () const + { + return MPI_Barrier(communicator); + } + + //! @copydoc Communication::ibarrier + MPIFuture ibarrier () const + { + MPIFuture future(true); // make a valid MPIFuture + MPI_Ibarrier(communicator, &future.req_); + return future; + } + + + //! @copydoc Communication::broadcast + template + int broadcast (T* inout, int len, int root) const + { + return MPI_Bcast(inout,len,MPITraits::getType(),root,communicator); + } + + //! @copydoc Communication::ibroadcast + template + MPIFuture ibroadcast(T&& data, int root) const{ + MPIFuture future(std::forward(data)); + auto mpidata = future.get_mpidata(); + MPI_Ibcast(mpidata.ptr(), + mpidata.size(), + mpidata.type(), + root, + communicator, + &future.req_); + return future; + } + + //! @copydoc Communication::gather() + //! @note out must have space for P*len elements + template + int gather (const T* in, T* out, int len, int root) const + { + return MPI_Gather(const_cast(in),len,MPITraits::getType(), + out,len,MPITraits::getType(), + root,communicator); + } + + //! @copydoc Communication::igather + template> + MPIFuture igather(TIN&& data_in, TOUT&& data_out, int root) const{ + MPIFuture future(std::forward(data_out), std::forward(data_in)); + auto mpidata_in = future.get_send_mpidata(); + auto mpidata_out = future.get_mpidata(); + assert(root != me || mpidata_in.size()*procs <= mpidata_out.size()); + int outlen = (me==root) * mpidata_in.size(); + MPI_Igather(mpidata_in.ptr(), mpidata_in.size(), mpidata_in.type(), + mpidata_out.ptr(), outlen, mpidata_out.type(), + root, communicator, &future.req_); + return future; + } + + //! @copydoc Communication::gatherv() + template + int gatherv (const T* in, int sendDataLen, T* out, int* recvDataLen, int* displ, int root) const + { + return MPI_Gatherv(const_cast(in),sendDataLen,MPITraits::getType(), + out,recvDataLen,displ,MPITraits::getType(), + root,communicator); + } + + //! @copydoc Communication::scatter() + //! @note out must have space for P*len elements + template + int scatter (const T* sendData, T* recvData, int len, int root) const + { + return MPI_Scatter(const_cast(sendData),len,MPITraits::getType(), + recvData,len,MPITraits::getType(), + root,communicator); + } + + //! @copydoc Communication::iscatter + template + MPIFuture iscatter(TIN&& data_in, TOUT&& data_out, int root) const + { + MPIFuture future(std::forward(data_out), std::forward(data_in)); + auto mpidata_in = future.get_send_mpidata(); + auto mpidata_out = future.get_mpidata(); + int inlen = (me==root) * mpidata_in.size()/procs; + MPI_Iscatter(mpidata_in.ptr(), inlen, mpidata_in.type(), + mpidata_out.ptr(), mpidata_out.size(), mpidata_out.type(), + root, communicator, &future.req_); + return future; + } + + //! @copydoc Communication::scatterv() + template + int scatterv (const T* sendData, int* sendDataLen, int* displ, T* recvData, int recvDataLen, int root) const + { + return MPI_Scatterv(const_cast(sendData),sendDataLen,displ,MPITraits::getType(), + recvData,recvDataLen,MPITraits::getType(), + root,communicator); + } + + + operator MPI_Comm () const + { + return communicator; + } + + //! @copydoc Communication::allgather() + template + int allgather(const T* sbuf, int count, T1* rbuf) const + { + return MPI_Allgather(const_cast(sbuf), count, MPITraits::getType(), + rbuf, count, MPITraits::getType(), + communicator); + } + + //! @copydoc Communication::iallgather + template + MPIFuture iallgather(TIN&& data_in, TOUT&& data_out) const + { + MPIFuture future(std::forward(data_out), std::forward(data_in)); + auto mpidata_in = future.get_send_mpidata(); + auto mpidata_out = future.get_mpidata(); + assert(mpidata_in.size()*procs <= mpidata_out.size()); + int outlen = mpidata_in.size(); + MPI_Iallgather(mpidata_in.ptr(), mpidata_in.size(), mpidata_in.type(), + mpidata_out.ptr(), outlen, mpidata_out.type(), + communicator, &future.req_); + return future; + } + + //! @copydoc Communication::allgatherv() + template + int allgatherv (const T* in, int sendDataLen, T* out, int* recvDataLen, int* displ) const + { + return MPI_Allgatherv(const_cast(in),sendDataLen,MPITraits::getType(), + out,recvDataLen,displ,MPITraits::getType(), + communicator); + } + + //! @copydoc Communication::allreduce(Type* inout,int len) const + template + int allreduce(Type* inout, int len) const + { + Type* out = new Type[len]; + int ret = allreduce(inout,out,len); + std::copy(out, out+len, inout); + delete[] out; + return ret; + } + + template + Type allreduce(Type&& in) const{ + Type lvalue_data = std::forward(in); + auto data = getMPIData(lvalue_data); + MPI_Allreduce(MPI_IN_PLACE, data.ptr(), data.size(), data.type(), + (Generic_MPI_Op::get()), + communicator); + return lvalue_data; + } + + //! @copydoc Communication::iallreduce + template + MPIFuture iallreduce(TIN&& data_in, TOUT&& data_out) const { + MPIFuture future(std::forward(data_out), std::forward(data_in)); + auto mpidata_in = future.get_send_mpidata(); + auto mpidata_out = future.get_mpidata(); + assert(mpidata_out.size() == mpidata_in.size()); + assert(mpidata_out.type() == mpidata_in.type()); + MPI_Iallreduce(mpidata_in.ptr(), mpidata_out.ptr(), + mpidata_out.size(), mpidata_out.type(), + (Generic_MPI_Op::get()), + communicator, &future.req_); + return future; + } + + //! @copydoc Communication::iallreduce + template + MPIFuture iallreduce(T&& data) const{ + MPIFuture future(std::forward(data)); + auto mpidata = future.get_mpidata(); + MPI_Iallreduce(MPI_IN_PLACE, mpidata.ptr(), + mpidata.size(), mpidata.type(), + (Generic_MPI_Op::get()), + communicator, &future.req_); + return future; + } + + //! @copydoc Communication::allreduce(Type* in,Type* out,int len) const + template + int allreduce(const Type* in, Type* out, int len) const + { + return MPI_Allreduce(const_cast(in), out, len, MPITraits::getType(), + (Generic_MPI_Op::get()),communicator); + } + + private: + MPI_Comm communicator; + int me; + int procs; + }; +} // namespace dune + +#endif // HAVE_MPI + +#endif diff --git a/dune/common/parallel/mpidata.hh b/dune/common/parallel/mpidata.hh new file mode 100644 index 0000000..bc31d8d --- /dev/null +++ b/dune/common/parallel/mpidata.hh @@ -0,0 +1,138 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_COMMON_PARALLEL_MPIDATA_HH +#define DUNE_COMMON_PARALLEL_MPIDATA_HH + +#include +#include + +#if HAVE_MPI + +#include +#include +#include + +/** @addtogroup ParallelCommunication + * + * @{ + */ +/** + * @file + * + * @brief Interface class to translate objects to a MPI_Datatype, void* + * and size used for MPI calls. + * + * Furthermore it can be used to resize the object + * if possible. This makes it possible to receive a message with variable + * size. See `Communication::rrecv`. + * + * To 'register' a new dynamic type for MPI communication specialize `MPIData` or + * overload `getMPIData`. + * + */ + +namespace Dune{ + + template + struct MPIData; + + template + auto getMPIData(T& t){ + return MPIData(t); + } + + // Default implementation for static datatypes + template + struct MPIData + { + friend auto getMPIData(T&); + protected: + T& data_; + + MPIData(T& t) + : data_(t) + {} + + public: + void* ptr() const { + return (void*)&data_; + } + + // indicates whether the datatype can be resized + static constexpr bool static_size = true; + + int size() const{ + return 1; + } + + MPI_Datatype type() const { + return MPITraits>::getType(); + } + }; + + // dummy implementation for void + template<> + struct MPIData{ + protected: + MPIData() {} + + public: + void* ptr(){ + return nullptr; + } + int size(){ + return 0; + } + void get(){} + MPI_Datatype type() const{ + return MPI_INT; + } + }; + + // specializations: + // std::vector of static sized elements or std::string + template + struct MPIData().data()), + decltype(std::declval().size()), + typename std::decay_t::value_type>>>{ + private: + template + using hasResizeOp = decltype(std::declval().resize(0)); + + protected: + friend auto getMPIData(T&); + MPIData(T& t) + : data_(t) + {} + public: + static constexpr bool static_size = std::is_const::value || !Std::is_detected_v; + void* ptr() { + return (void*) data_.data(); + } + int size() { + return data_.size(); + } + MPI_Datatype type() const{ + return MPITraits::value_type>::getType(); + } + + template + auto /*void*/ resize(int size) + -> std::enable_if_t::value || !Std::is_detected_v> + { + data_.resize(size); + } + + protected: + T& data_; + }; + +} + +/** + * @} + */ + +#endif +#endif diff --git a/dune/common/parallel/mpifuture.hh b/dune/common/parallel/mpifuture.hh new file mode 100644 index 0000000..14d6ba9 --- /dev/null +++ b/dune/common/parallel/mpifuture.hh @@ -0,0 +1,174 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_PARALLEL_MPIFUTURE_HH +#define DUNE_COMMON_PARALLEL_MPIFUTURE_HH + +#include + +#include +#include +#include + +#if HAVE_MPI +namespace Dune{ + + namespace impl{ + template + struct Buffer{ + Buffer(bool valid){ + if(valid) + value = std::make_unique(); + } + template + Buffer(V&& t) + : value(std::make_unique(std::forward(t))) + {} + std::unique_ptr value; + T get(){ + T tmp = std::move(*value); + value.reset(); + return tmp; + } + operator bool () const { + return (bool)value; + } + T& operator *() const{ + return *value; + } + }; + + template + struct Buffer{ + Buffer(bool valid = false) + { + if(valid) + value = T(); + } + template + Buffer(V&& t) + : value(std::forward(t)) + {} + std::optional> value; + T& get(){ + T& tmp = *value; + value.reset(); + return tmp; + } + operator bool () const{ + return (bool)value; + } + T& operator *() const{ + return *value; + } + }; + + template<> + struct Buffer{ + bool valid_; + Buffer(bool valid = false) + : valid_(valid) + {} + operator bool () const{ + return valid_; + } + void get(){} + }; + } + + /*! \brief Provides a future-like object for MPI communication. It contains + the object that will be received and might contain also a sending object, + which must be hold (keep alive) until the communication has been completed. + */ + template + class MPIFuture{ + mutable MPI_Request req_; + mutable MPI_Status status_; + impl::Buffer data_; + impl::Buffer send_data_; + friend class Communication; + public: + MPIFuture(bool valid = false) + : req_(MPI_REQUEST_NULL) + , data_(valid) + {} + + // Hide this constructor if R or S is void + template + MPIFuture(V&& recv_data, U&& send_data, typename std::enable_if_t::value && !std::is_void::value>* = 0) : + req_(MPI_REQUEST_NULL) + , data_(std::forward(recv_data)) + , send_data_(std::forward(send_data)) + {} + + // hide this constructor if R is void + template + MPIFuture(V&& recv_data, typename std::enable_if_t::value>* = 0) + : req_(MPI_REQUEST_NULL) + , data_(std::forward(recv_data)) + {} + + ~MPIFuture() { + if(req_ != MPI_REQUEST_NULL){ + try{ // might fail when it is a collective communication + MPI_Cancel(&req_); + MPI_Request_free(&req_); + }catch(...){ + } + } + } + + MPIFuture(MPIFuture&& f) + : req_(MPI_REQUEST_NULL) + , data_(std::move(f.data_)) + , send_data_(std::move(f.send_data_)) + { + std::swap(req_, f.req_); + std::swap(status_, f.status_); + } + + MPIFuture& operator=(MPIFuture&& f){ + std::swap(req_, f.req_); + std::swap(status_, f.status_); + std::swap(data_, f.data_); + std::swap(send_data_, f.send_data_); + return *this; + } + + bool valid() const{ + return (bool)data_; + } + + void wait(){ + if(!valid()) + DUNE_THROW(InvalidFutureException, "The MPIFuture is not valid!"); + MPI_Wait(&req_, &status_); + } + + bool ready() const{ + int flag = -1; + MPI_Test(&req_, &flag, &status_); + return flag; + } + + R get() { + wait(); + return data_.get(); + } + + S get_send_data(){ + wait(); + return send_data_.get(); + } + + auto get_mpidata(){ + return getMPIData(*data_); + } + + auto get_send_mpidata(){ + return getMPIData(*send_data_); + } + }; + +} +#endif +#endif diff --git a/dune/common/parallel/mpiguard.hh b/dune/common/parallel/mpiguard.hh new file mode 100644 index 0000000..5bd4a4c --- /dev/null +++ b/dune/common/parallel/mpiguard.hh @@ -0,0 +1,233 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +/** + * @file + * @brief Implements a MPIGuard which detects an error on a remote process + * @author Christian Engwer + * @ingroup ParallelCommunication + */ + +#ifndef DUNE_COMMON_MPIGUARD_HH +#define DUNE_COMMON_MPIGUARD_HH + +#include "mpihelper.hh" +#include "communication.hh" +#include "mpicommunication.hh" +#include + +namespace Dune +{ + +#ifndef DOXYGEN + + /* + Interface class for the communication needed by MPIGuard + */ + struct GuardCommunicator + { + // cleanup + virtual ~GuardCommunicator() {}; + // all the communication methods we need + virtual int rank() = 0; + virtual int size() = 0; + virtual int sum(int i) = 0; + // create a new GuardCommunicator pointer + template + static GuardCommunicator * create(const CollectiveCommunication & c); +#if HAVE_MPI + inline + static GuardCommunicator * create(const MPI_Comm & c); +#endif + }; + + namespace { + /* + templated implementation of different communication classes + */ + // the default class will always fail, due to the missing implementation of "sum" + template + struct GenericGuardCommunicator + : public GuardCommunicator + {}; + // specialization for Communication + template + struct GenericGuardCommunicator< Communication > + : public GuardCommunicator + { + const Communication comm; + GenericGuardCommunicator(const Communication & c) : + comm(c) {} + int rank() override { return comm.rank(); }; + int size() override { return comm.size(); }; + int sum(int i) override { return comm.sum(i); } + }; + +#if HAVE_MPI + // specialization for MPI_Comm + template <> + struct GenericGuardCommunicator + : public GenericGuardCommunicator< Communication > + { + GenericGuardCommunicator(const MPI_Comm & c) : + GenericGuardCommunicator< Communication >( + Communication(c)) {} + }; +#endif + } // anonymous namespace + + template + GuardCommunicator * GuardCommunicator::create(const CollectiveCommunication & comm) + { + return new GenericGuardCommunicator< CollectiveCommunication >(comm); + } + +#if HAVE_MPI + GuardCommunicator * GuardCommunicator::create(const MPI_Comm & comm) + { + return new GenericGuardCommunicator< CollectiveCommunication >(comm); + } +#endif + +#endif + + /*! @brief This exception is thrown if the MPIGuard detects an error on a remote process + @ingroup ParallelCommunication + */ + class MPIGuardError : public ParallelError {}; + + /*! @brief detects a thrown exception and communicates to all other processes + @ingroup ParallelCommunication + + @code + { + MPIGuard guard(...); + + do_something(); + + // tell the guard that you successfully passed a critical operation + guard.finalize(); + // reactivate the guard for the next critical operation + guard.reactivate(); + + int result = do_something_else(); + + // tell the guard the result of your operation + guard.finalize(result == success); + } + @endcode + + You create a MPIGuard object. If an exception is risen on a + process the MPIGuard detects the exception, because the finalize + method was not called. When reaching the finalize call all + other processes are informed that an error occurred and the + MPIGuard throws an exception of type MPIGuardError. + + @note You can initialize the MPIGuard from different types of communication objects: + - MPIHelper + - Communication + - MPI_Comm + */ + class MPIGuard + { + GuardCommunicator * comm_; + bool active_; + + // we don't want to copy this class + MPIGuard (const MPIGuard &); + + public: + /*! @brief create an MPIGuard operating on the Communicator of the global Dune::MPIHelper + + @param active should the MPIGuard be active upon creation? + */ + MPIGuard (bool active=true) : + comm_(GuardCommunicator::create( + MPIHelper::getCommunication())), + active_(active) + {} + + /*! @brief create an MPIGuard operating on the Communicator of a special Dune::MPIHelper m + + @param m a reference to an MPIHelper + @param active should the MPIGuard be active upon creation? + */ + MPIGuard (MPIHelper & m, bool active=true) : + comm_(GuardCommunicator::create( + m.getCommunication())), + active_(active) + {} + + /*! @brief create an MPIGuard operating on an arbitrary communicator. + + Supported types for the communication object are: + - MPIHelper + - Communication + - MPI_Comm + + @param comm reference to a communication object + @param active should the MPIGuard be active upon creation? + */ + template + MPIGuard (const C & comm, bool active=true) : + comm_(GuardCommunicator::create(comm)), + active_(active) + {} + +#if HAVE_MPI + MPIGuard (const MPI_Comm & comm, bool active=true) : + comm_(GuardCommunicator::create(comm)), + active_(active) + {} +#endif + + /*! @brief destroy the guard and check for undetected exceptions + */ + ~MPIGuard() + { + if (active_) + { + active_ = false; + finalize(false); + } + delete comm_; + } + + /*! @brief reactivate the guard. + + If the guard is still active finalize(true) is called first. + */ + void reactivate() { + if (active_ == true) + finalize(); + active_ = true; + } + + /*! @brief stop the guard. + + If no success parameter is passed, the guard assumes that + everything worked as planned. All errors are communicated + and an exception of type MPIGuardError is thrown if an error + (or exception) occurred on any of the processors in the + communicator. + + @param success inform the guard about possible errors + */ + void finalize(bool success = true) + { + int result = success ? 0 : 1; + bool was_active = active_; + active_ = false; + result = comm_->sum(result); + if (result>0 && was_active) + { + DUNE_THROW(MPIGuardError, "Terminating process " + << comm_->rank() << " due to " + << result << " remote error(s)"); + } + } + }; + +} + +#endif // DUNE_COMMON_MPIGUARD_HH diff --git a/dune/common/parallel/mpihelper.hh b/dune/common/parallel/mpihelper.hh new file mode 100644 index 0000000..742fdd1 --- /dev/null +++ b/dune/common/parallel/mpihelper.hh @@ -0,0 +1,327 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_MPIHELPER +#define DUNE_MPIHELPER + +#if HAVE_MPI +#include +#include +#endif + +#include + +#include +#if HAVE_MPI +#include +#include +#endif +#include + +namespace Dune +{ + /** + * @file + * @brief Helpers for dealing with MPI. + * + * @ingroup ParallelCommunication + * + * Basically there are two helpers available: + *
+ *
FakeMPIHelper
+ *
A class adhering to the interface of MPIHelper + * that does not need MPI at all. This can be used + * to create a sequential program even if MPI is + * used to compile it. + *
+ *
MPIHelper
+ *
A real MPI helper. When the singleton + * gets instantiated MPI_Init will be + * called and before the program exits + * MPI_Finalize will be called. + *
+ *
+ * + * Example of who to use these classes: + * + * A program that is parallel if compiled with MPI + * and sequential otherwise: + * \code + * int main(int argc, char** argv){ + * typedef Dune::MPIHelper MPIHelper; + * MPIHelper::instance(argc, argv); + * typename MPIHelper::MPICommunicator world = + * MPIHelper::getCommunicator(); + * ... + * \endcode + * + * If one wants to have sequential program even if the code is + * compiled with mpi then one simply has to exchange the typedef + * with \code typedef Dune::MPIHelper FakeMPIHelper; \endcode. + * + * For checking whether we really use MPI or just fake please use + * MPIHelper::isFake (this is also possible at compile time!) + */ + /** + * @brief A fake mpi helper. + * + * This helper can be used if no MPI is available + * or one wants to run sequentially even if MPI is + * available and used. + */ + class FakeMPIHelper + { + public: + enum { + /** + * @brief Are we fake (i.e. pretend to have MPI support but are compiled + * without.) + */ + isFake = true + }; + + /** + * @brief The type of the mpi communicator. + */ + typedef No_Comm MPICommunicator; + + /** \brief get the default communicator + * + * Return a communicator to exchange data with all processes + * + * \returns a fake communicator + */ + DUNE_EXPORT static MPICommunicator getCommunicator () + { + static MPICommunicator comm; + return comm; + } + + /** \brief get a local communicator + * + * Returns a communicator to communicate with the local process only + * + * \returns a fake communicator + */ + static MPICommunicator getLocalCommunicator () + { + return getCommunicator(); + } + + + + // Will be deprecated after the 2.7 release + //[[deprecated("getCollectionCommunication is deprecated. Use getCommunication instead.")]] + static Communication getCollectiveCommunication() + { + return Communication(getCommunicator()); + } + + static Communication + getCommunication() + { + return Communication(getCommunicator()); + } + + /** + * @brief Get the singleton instance of the helper. + * + * This method has to be called with the same arguments + * that the main method of the program was called: + * \code + * int main(int argc, char** argv){ + * MPIHelper::instance(argc, argv); + * // program code comes here + * ... + * } + * \endcode + * @param argc The number of arguments provided to main. + * @param argv The arguments provided to main. + */ + DUNE_EXPORT static FakeMPIHelper& instance([[maybe_unused]] int argc, + [[maybe_unused]] char** argv) + { + return instance(); + } + + DUNE_EXPORT static FakeMPIHelper& instance() + { + static FakeMPIHelper singleton; + return singleton; + } + + /** + * @brief return rank of process, i.e. zero + */ + int rank () const { return 0; } + /** + * @brief return rank of process, i.e. one + */ + int size () const { return 1; } + + private: + FakeMPIHelper() {} + FakeMPIHelper(const FakeMPIHelper&); + FakeMPIHelper& operator=(const FakeMPIHelper); + }; + +#if HAVE_MPI + /** + * @brief A real mpi helper. + * @ingroup ParallelCommunication + * + * This helper should be used for parallel programs. + */ + class MPIHelper + { + public: + enum { + /** + * @brief Are we fake (i. e. pretend to have MPI support but are compiled + * without. + */ + isFake = false + }; + + /** + * @brief The type of the mpi communicator. + */ + typedef MPI_Comm MPICommunicator; + + /** \brief get the default communicator + * + * Return a communicator to exchange data with all processes + * + * \returns MPI_COMM_WORLD + */ + static MPICommunicator getCommunicator () + { + return MPI_COMM_WORLD; + } + + /** \brief get a local communicator + * + * Returns a communicator to exchange data with the local process only + * + * \returns MPI_COMM_SELF + */ + static MPICommunicator getLocalCommunicator () + { + return MPI_COMM_SELF; + } + + // Will be deprecated after the 2.7 release + //[[deprecated("getCollectionCommunication is deprecated. Use getCommunication instead.")]] + static Communication + getCollectiveCommunication() + { + return Communication(getCommunicator()); + } + + static Communication + getCommunication() + { + return Communication(getCommunicator()); + } + /** + * @brief Get the singleton instance of the helper. + * + * This method has to be called with the same arguments + * that the main method of the program was called: + * \code + * int main(int argc, char** argv){ + * MPIHelper::instance(argc, argv); + * // program code comes here + * ... + * } + * \endcode + * @param argc The number of arguments provided to main. + * @param argv The arguments provided to main. + */ + DUNE_EXPORT static MPIHelper& instance(int& argc, char**& argv) + { + // create singleton instance + if (!instance_){ + static std::mutex mutex; + std::lock_guard guard(mutex); + if(!instance_) + instance_.reset(new MPIHelper(argc,argv)); + } + return *instance_; + } + + DUNE_EXPORT static MPIHelper& instance() + { + if(!instance_) + DUNE_THROW(InvalidStateException, "MPIHelper not initialized! Call MPIHelper::instance(argc, argv) with arguments first."); + return *instance_; + } + + /** + * @brief return rank of process + */ + int rank () const { return rank_; } + /** + * @brief return number of processes + */ + int size () const { return size_; } + + //! \brief calls MPI_Finalize + ~MPIHelper() + { + int wasFinalized = -1; + MPI_Finalized( &wasFinalized ); + if(!wasFinalized && initializedHere_) + { + MPI_Finalize(); + dverb << "Called MPI_Finalize on p=" << rank_ << "!" < instance_ = {}; + + //! \brief calls MPI_Init with argc and argv as parameters + MPIHelper(int& argc, char**& argv) + : initializedHere_(false) + { + int wasInitialized = -1; + MPI_Initialized( &wasInitialized ); + if(!wasInitialized) + { + rank_ = -1; + size_ = -1; + static int is_initialized = MPI_Init(&argc, &argv); + prevent_warning(is_initialized); + initializedHere_ = true; + } + + MPI_Comm_rank(MPI_COMM_WORLD,&rank_); + MPI_Comm_size(MPI_COMM_WORLD,&size_); + + assert( rank_ >= 0 ); + assert( size_ >= 1 ); + + dverb << "Called MPI_Init on p=" << rank_ << "!" << std::endl; + } + + MPIHelper(const MPIHelper&); + MPIHelper& operator=(const MPIHelper); + }; +#else // !HAVE_MPI + // We do not have MPI therefore FakeMPIHelper + // is the MPIHelper + /** + * @brief If no MPI is available FakeMPIHelper becomes the MPIHelper + * @ingroup ParallelCommunication + */ + typedef FakeMPIHelper MPIHelper; + +#endif // !HAVE_MPI + +} // end namespace Dune +#endif diff --git a/dune/common/parallel/mpipack.hh b/dune/common/parallel/mpipack.hh new file mode 100644 index 0000000..09b7fb8 --- /dev/null +++ b/dune/common/parallel/mpipack.hh @@ -0,0 +1,225 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +/** + * @file + * + * @brief See MPI_Pack. + * + * This Wrapper class takes care of the + * memory management and provides methods to pack and unpack + * objects. All objects that can be used for MPI communication can + * also be packed and unpacked to/from MPIPack. + * + * @author Nils-Arne Dreier + * @ingroup ParallelCommunication + */ + + +#ifndef DUNE_COMMON_PARALLEL_MPIPACK_HH +#define DUNE_COMMON_PARALLEL_MPIPACK_HH + +#include +#if HAVE_MPI +#include +#include +#include + + +namespace Dune { + + class MPIPack { + std::vector _buffer; + int _position; + MPI_Comm _comm; + + friend struct MPIData; + friend struct MPIData; + public: + MPIPack(Communication comm, std::size_t size = 0) + : _buffer(size) + , _position(0) + , _comm(comm) + {} + + // Its not valid to copy a MPIPack but you can move it + MPIPack(const MPIPack&) = delete; + MPIPack& operator = (const MPIPack& other) = delete; + MPIPack(MPIPack&&) = default; + MPIPack& operator = (MPIPack&& other) = default; + + /** @brief Packs the data into the object. Enlarges the internal buffer if + * necessary. + * + * @throw MPIError + */ + template + void pack(const T& data){ + auto mpidata = getMPIData(data); + int size = getPackSize(mpidata.size(), _comm, mpidata.type()); + constexpr bool has_static_size = decltype(getMPIData(std::declval()))::static_size; + if(!has_static_size) + size += getPackSize(1, _comm, MPI_INT); + if (_position + size > 0 && size_t(_position + size) > _buffer.size()) // resize buffer if necessary + _buffer.resize(_position + size); + if(!has_static_size){ + int size = mpidata.size(); + MPI_Pack(&size, 1, MPI_INT, _buffer.data(), _buffer.size(), + &_position, _comm); + } + MPI_Pack(mpidata.ptr(), mpidata.size(), + mpidata.type(), _buffer.data(), _buffer.size(), + &_position, _comm); + } + + /** @brief Unpacks data from the object + * + * @throw MPIError + */ + template + auto /*void*/ unpack(T& data) + -> std::enable_if_t + { + auto mpidata = getMPIData(data); + MPI_Unpack(_buffer.data(), _buffer.size(), &_position, + mpidata.ptr(), mpidata.size(), + mpidata.type(), _comm); + } + + /** @brief Unpacks data from the object + * + * @throw MPIError + */ + template + auto /*void*/ unpack(T& data) + -> std::enable_if_t + { + auto mpidata = getMPIData(data); + int size = 0; + MPI_Unpack(_buffer.data(), _buffer.size(), &_position, + &size, 1, + MPI_INT, _comm); + mpidata.resize(size); + MPI_Unpack(_buffer.data(), _buffer.size(), &_position, + mpidata.ptr(), mpidata.size(), + mpidata.type(), _comm); + } + + + //! @copydoc pack + template + friend MPIPack& operator << (MPIPack& p, const T& t){ + p.pack(t); + return p; + } + + //! @copydoc unpack + template + friend MPIPack& operator >> (MPIPack& p, T& t){ + p.unpack(t); + return p; + } + + //! @copydoc unpack + template + MPIPack& read(T& t){ + unpack(t); + return *this; + } + + //! @copydoc pack + template + MPIPack& write(const T& t){ + pack(t); + return *this; + } + + /** @brief Resizes the internal buffer. + \param size new size of internal buffer + */ + void resize(size_t size){ + _buffer.resize(size); + } + + /** @brief Enlarges the internal buffer. + */ + void enlarge(int s) { + _buffer.resize(_buffer.size() + s); + } + + /** @brief Returns the size of the internal buffer. + */ + size_t size() const { + return _buffer.size(); + } + + /** @brief Sets the position in the buffer where the next + * pack/unpack operation should take place. + */ + void seek(int p){ + _position = p; + } + + /** @brief Gets the position in the buffer where the next + * pack/unpack operation should take place. + */ + int tell() const{ + return _position; + } + + /** @brief Checks whether the end of the buffer is reached. + */ + bool eof() const{ + return std::size_t(_position)==_buffer.size(); + } + + /** @brief Returns the size of the data needed to store the data + * in an MPIPack. See `MPI_Pack_size`. + */ + static int getPackSize(int len, const MPI_Comm& comm, const MPI_Datatype& dt){ + int size; + MPI_Pack_size(len, dt, comm, &size); + return size; + } + + friend bool operator==(const MPIPack& a, const MPIPack& b) { + return a._buffer == b._buffer && a._comm == b._comm; + } + friend bool operator!=(const MPIPack& a, const MPIPack& b) { + return !(a==b); + } + + }; + + template + struct MPIData, MPIPack>::value>> { + protected: + friend auto getMPIData

(P& t); + MPIData(P& t) : + data_(t) + {} + public: + static constexpr bool static_size = std::is_const

::value; + + void* ptr() { + return (void*) data_._buffer.data(); + } + + int size() { + return data_.size(); + } + + MPI_Datatype type() const{ + return MPI_PACKED; + } + + void resize(int size){ + data_.resize(size); + } + protected: + P& data_; + }; + +} // end namespace Dune + +#endif +#endif diff --git a/dune/common/parallel/mpitraits.hh b/dune/common/parallel/mpitraits.hh new file mode 100644 index 0000000..39f5221 --- /dev/null +++ b/dune/common/parallel/mpitraits.hh @@ -0,0 +1,205 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_MPITRAITS_HH +#define DUNE_MPITRAITS_HH + +/** @addtogroup ParallelCommunication + * + * @{ + */ +/** + * @file + * @brief Traits classes for mapping types onto MPI_Datatype. + * @author Markus Blatt + */ + +#if HAVE_MPI + +#include +#include +#include +#include +#include + +#include + +namespace Dune +{ + /** + * @brief A traits class describing the mapping of types onto MPI_Datatypes. + * + * Specializations exist for the default types. + * Specializations should provide a static method + * \code + * static MPI_Datatype getType(); + * \endcode + */ + template + struct MPITraits + { + private: + MPITraits(){} + MPITraits(const MPITraits&){} + static MPI_Datatype datatype; + static MPI_Datatype vectortype; + public: + static inline MPI_Datatype getType() + { + if(datatype==MPI_DATATYPE_NULL) { + MPI_Type_contiguous(sizeof(T),MPI_BYTE,&datatype); + MPI_Type_commit(&datatype); + } + return datatype; + } + static constexpr bool is_intrinsic = false; + }; + template + MPI_Datatype MPITraits::datatype = MPI_DATATYPE_NULL; + +#ifndef DOXYGEN + + // A Macro for defining traits for the primitive data types +#define ComposeMPITraits(p,m) \ + template<> \ + struct MPITraits

{ \ + static inline MPI_Datatype getType(){ \ + return m; \ + } \ + static constexpr bool is_intrinsic = true; \ + } + + ComposeMPITraits(char, MPI_CHAR); + ComposeMPITraits(unsigned char,MPI_UNSIGNED_CHAR); + ComposeMPITraits(short,MPI_SHORT); + ComposeMPITraits(unsigned short,MPI_UNSIGNED_SHORT); + ComposeMPITraits(int,MPI_INT); + ComposeMPITraits(unsigned int,MPI_UNSIGNED); + ComposeMPITraits(long,MPI_LONG); + ComposeMPITraits(unsigned long,MPI_UNSIGNED_LONG); + ComposeMPITraits(float,MPI_FLOAT); + ComposeMPITraits(double,MPI_DOUBLE); + ComposeMPITraits(long double,MPI_LONG_DOUBLE); + ComposeMPITraits(std::complex, MPI_CXX_DOUBLE_COMPLEX); + ComposeMPITraits(std::complex, MPI_CXX_LONG_DOUBLE_COMPLEX); + ComposeMPITraits(std::complex, MPI_CXX_FLOAT_COMPLEX); + + +#undef ComposeMPITraits + + template class FieldVector; + + template + struct MPITraits > + { + static MPI_Datatype datatype; + static MPI_Datatype vectortype; + + static inline MPI_Datatype getType() + { + if(datatype==MPI_DATATYPE_NULL) { + MPI_Type_contiguous(n, MPITraits::getType(), &vectortype); + MPI_Type_commit(&vectortype); + FieldVector fvector; + MPI_Aint base; + MPI_Aint displ; + MPI_Get_address(&fvector, &base); + MPI_Get_address(&(fvector[0]), &displ); + displ -= base; + int length[1]={1}; + + MPI_Type_create_struct(1, length, &displ, &vectortype, &datatype); + MPI_Type_commit(&datatype); + } + return datatype; + } + + }; + + template + MPI_Datatype MPITraits >::datatype = MPI_DATATYPE_NULL; + template + MPI_Datatype MPITraits >::vectortype = {MPI_DATATYPE_NULL}; + + + template + class bigunsignedint; + + template + struct MPITraits > + { + static MPI_Datatype datatype; + static MPI_Datatype vectortype; + + static inline MPI_Datatype getType() + { + if(datatype==MPI_DATATYPE_NULL) { + MPI_Type_contiguous(bigunsignedint::n, MPITraits::getType(), + &vectortype); + //MPI_Type_commit(&vectortype); + bigunsignedint data; + MPI_Aint base; + MPI_Aint displ; + MPI_Get_address(&data, &base); + MPI_Get_address(&(data.digit), &displ); + displ -= base; + int length[1]={1}; + MPI_Type_create_struct(1, length, &displ, &vectortype, &datatype); + MPI_Type_commit(&datatype); + } + return datatype; + } + }; +} + +namespace Dune +{ + template + MPI_Datatype MPITraits >::datatype = MPI_DATATYPE_NULL; + template + MPI_Datatype MPITraits >::vectortype = MPI_DATATYPE_NULL; + + template + struct MPITraits > + { + public: + inline static MPI_Datatype getType(); + private: + static MPI_Datatype type; + }; + template + MPI_Datatype MPITraits >::getType() + { + if(type==MPI_DATATYPE_NULL) { + int length[2] = {1, 1}; + MPI_Aint disp[2]; + MPI_Datatype types[2] = {MPITraits::getType(), + MPITraits::getType()}; + + using Pair = std::pair; + static_assert(std::is_standard_layout::value, "offsetof() is only defined for standard layout types"); + disp[0] = offsetof(Pair, first); + disp[1] = offsetof(Pair, second); + + MPI_Datatype tmp; + MPI_Type_create_struct(2, length, disp, types, &tmp); + + MPI_Type_create_resized(tmp, 0, sizeof(Pair), &type); + MPI_Type_commit(&type); + + MPI_Type_free(&tmp); + } + return type; + } + + template + MPI_Datatype MPITraits >::type=MPI_DATATYPE_NULL; + +#endif // !DOXYGEN + +} // namespace Dune + +#endif // HAVE_MPI + +/** @} group ParallelCommunication */ + +#endif diff --git a/dune/common/parallel/plocalindex.hh b/dune/common/parallel/plocalindex.hh new file mode 100644 index 0000000..599f6b4 --- /dev/null +++ b/dune/common/parallel/plocalindex.hh @@ -0,0 +1,320 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_PLOCALINDEX_HH +#define DUNE_PLOCALINDEX_HH + +#include "localindex.hh" +#include "indexset.hh" +#include "mpitraits.hh" + +#include + +namespace Dune +{ + + + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Provides classes for use as the local index in ParallelIndexSet for distributed computing. + * @author Markus Blatt + */ + + template class ParallelLocalIndex; + + /** + * @brief Print the local index to a stream. + * @param os The output stream to print to. + * @param index The index to print. + */ + template + std::ostream& operator<<(std::ostream& os, const ParallelLocalIndex& index) + { + os<<"{local="< + class ParallelLocalIndex + { +#if HAVE_MPI + // friend declaration needed for MPITraits + friend struct MPITraits >; +#endif + friend std::ostream& operator<<<>(std::ostream& os, const ParallelLocalIndex& index); + + public: + /** + * @brief The type of the attributes. + * Normally this will be an enumeration like + *

+     * enum Attributes{owner, border, overlap};
+     * 
+ */ + typedef T Attribute; + /** + * @brief Constructor. + * + * The local index will be initialized to 0. + * @param attribute The attribute of the index. + * @param isPublic True if the index might also be + * known to other processes. + */ + ParallelLocalIndex(const Attribute& attribute, bool isPublic); + + /** + * @brief Constructor. + * + * @param localIndex The local index. + * @param attribute The attribute of the index. + * @param isPublic True if the index might also be + * known to other processes. + */ + ParallelLocalIndex(size_t localIndex, const Attribute& attribute, bool isPublic=true); + /** + * @brief Parameterless constructor. + * + * Needed for use in container classes. + */ + ParallelLocalIndex(); + +#if 0 + /** + * @brief Constructor. + * @param globalIndex The global index. + * @param attribute The attribute of the index. + * @param local The local index. + * @param isPublic True if the index might also be + * known to other processes. + * + */ + ParallelLocalIndex(const Attribute& attribute, size_t local, bool isPublic); +#endif + + /** + * @brief Get the attribute of the index. + * @return The associated attribute. + */ + inline const Attribute attribute() const; + + /** + * @brief Set the attribute of the index. + * @param attribute The associated attribute. + */ + inline void setAttribute(const Attribute& attribute); + + /** + * @brief get the local index. + * @return The local index. + */ + inline size_t local() const; + + /** + * @brief Convert to the local index represented by an int. + */ + inline operator size_t() const; + + /** + * @brief Assign a new local index. + * + * @param index The new local index. + */ + inline ParallelLocalIndex& operator=(size_t index); + + /** + * @brief Check whether the index might also be known other processes. + * @return True if the index might be known to other processors. + */ + inline bool isPublic() const; + + /** + * @brief Get the state. + * @return The state. + */ + inline LocalIndexState state() const; + + /** + * @brief Set the state. + * @param state The state to set. + */ + inline void setState(const LocalIndexState& state); + + private: + /** @brief The local index. */ + size_t localIndex_; + + /** @brief An attribute for the index. */ + char attribute_; + + /** @brief True if the index is also known to other processors. */ + char public_; + + /** + * @brief The state of the index. + * + * Has to be one of LocalIndexState! + * @see LocalIndexState. + */ + char state_; + + }; + + template + bool operator==(const ParallelLocalIndex& p1, + const ParallelLocalIndex& p2) + { + if(p1.local()!=p2.local()) + return false; + if(p1.attribute()!=p2.attribute()) + return false; + if(p1.isPublic()!=p2.isPublic()) + return false; + return true; + } + template + bool operator!=(const ParallelLocalIndex& p1, + const ParallelLocalIndex& p2) + { + return !(p1==p2); + } + + + template + struct LocalIndexComparator > + { + static bool compare(const ParallelLocalIndex& t1, + const ParallelLocalIndex& t2){ + return t1.attribute() + class MPITraits > + { + public: + static MPI_Datatype getType(); + private: + static MPI_Datatype type; + + }; + +#endif + + template + ParallelLocalIndex::ParallelLocalIndex(const T& attribute, bool isPublic) + : localIndex_(0), attribute_(static_cast(attribute)), + public_(static_cast(isPublic)), state_(static_cast(VALID)) + {} + + + template + ParallelLocalIndex::ParallelLocalIndex(size_t local, const T& attribute, bool isPublic) + : localIndex_(local), attribute_(static_cast(attribute)), + public_(static_cast(isPublic)), state_(static_cast(VALID)) + {} + + template + ParallelLocalIndex::ParallelLocalIndex() + : localIndex_(0), attribute_(), public_(static_cast(false)), + state_(static_cast(VALID)) + {} + + template + inline const T ParallelLocalIndex::attribute() const + { + return T(attribute_); + } + + template + inline void + ParallelLocalIndex::setAttribute(const Attribute& attribute) + { + attribute_ = attribute; + } + + template + inline size_t ParallelLocalIndex::local() const + { + return localIndex_; + } + + template + inline ParallelLocalIndex::operator size_t() const + { + return localIndex_; + } + + template + inline ParallelLocalIndex& + ParallelLocalIndex::operator=(size_t index) + { + localIndex_=index; + return *this; + } + + template + inline bool ParallelLocalIndex::isPublic() const + { + return static_cast(public_); + } + + template + inline LocalIndexState ParallelLocalIndex::state() const + { + return LocalIndexState(state_); + } + + template + inline void ParallelLocalIndex::setState(const LocalIndexState& state) + { + state_=static_cast(state); + } + +#if HAVE_MPI + + template + MPI_Datatype MPITraits >::getType() + { + + if(type==MPI_DATATYPE_NULL) { + int length = 1; + MPI_Aint base, disp; + MPI_Datatype types[1] = {MPITraits::getType()}; + ParallelLocalIndex rep; + MPI_Get_address(&rep, &base); + MPI_Get_address(&(rep.attribute_), &disp); + disp -= base; + + MPI_Datatype tmp; + MPI_Type_create_struct(1, &length, &disp, types, &tmp); + + MPI_Type_create_resized(tmp, 0, sizeof(ParallelLocalIndex), &type); + MPI_Type_commit(&type); + + MPI_Type_free(&tmp); + } + return type; + } + + template + MPI_Datatype MPITraits >::type = MPI_DATATYPE_NULL; + +#endif + + + /** @} */ +} // namespace Dune + +#endif diff --git a/dune/common/parallel/remoteindices.hh b/dune/common/parallel/remoteindices.hh new file mode 100644 index 0000000..be9fa36 --- /dev/null +++ b/dune/common/parallel/remoteindices.hh @@ -0,0 +1,1869 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_REMOTEINDICES_HH +#define DUNE_REMOTEINDICES_HH + +#if HAVE_MPI + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace Dune { + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Classes describing a distributed indexset + * @author Markus Blatt + */ + + //! \todo Please doc me! + template + class MPITraits > > + { + public: + inline static MPI_Datatype getType(); + private: + static MPI_Datatype type; + }; + + + template + class RemoteIndices; + + template + class RemoteIndex; + + // forward declaration needed for friend declaration. + template + class IndicesSyncer; + + template + std::ostream& operator<<(std::ostream& os, const RemoteIndex& index); + + + template + class RemoteIndexListModifier; + + + /** + * @brief Information about an index residing on another processor. + */ + template + class RemoteIndex + { + template + friend class IndicesSyncer; + + template + friend void repairLocalIndexPointers(std::map,A> >&, + RemoteIndices&, + const T&); + + template + friend class RemoteIndexListModifier; + + public: + /** + * @brief the type of the global index. + * This type has to provide at least a operator< for sorting. + */ + typedef T1 GlobalIndex; + /** + * @brief The type of the attributes. + * Normally this will be an enumeration like + * \code + * enum Attributes{owner, border, overlap} + * \endcode + * e.g. OwnerOverlapCopyAttributes. + */ + typedef T2 Attribute; + + /** + * @brief The type of the index pair. + */ + typedef IndexPair > + PairType; + + /** + * @brief Get the attribute of the index on the remote process. + * @return The remote attribute. + */ + const Attribute attribute() const; + + /** + * @brief Get the corresponding local index pair. + * @return The corresponding local index pair. + */ + + const PairType& localIndexPair() const; + + /** + * @brief Parameterless Constructor. + */ + RemoteIndex(); + + + /** + * @brief Constructor. + * @param attribute The attribute of the index on the remote processor. + * @param local The corresponding local index. + */ + RemoteIndex(const T2& attribute, + const PairType* local); + + + /** + * @brief Constructor. + * Private as it should only be called from within Indexset. + * @param attribute The attribute of the index on the remote processor. + */ + RemoteIndex(const T2& attribute); + + bool operator==(const RemoteIndex& ri) const; + + bool operator!=(const RemoteIndex& ri) const; + private: + /** @brief The corresponding local index for this process. */ + const PairType* localIndex_; + + /** @brief The attribute of the index on the other process. */ + char attribute_; + }; + + template + std::ostream& operator<<(std::ostream& os, const RemoteIndices& indices); + + class InterfaceBuilder; + + template + class CollectiveIterator; + + // forward declaration needed for friend declaration. + template + class IndicesSyncer; + + // forward declaration needed for friend declaration. + template + class OwnerOverlapCopyCommunication; + + + /** + * @brief The indices present on remote processes. + * + * To set up communication between the set of processes active in + * the communication every process needs to know which + * indices are also known to other processes and which attributes + * are attached to them on the remote side. + * + * This information is managed by this class. The information can either + * be computed automatically calling rebuild (which requires information + * to be sent in a ring) or set up by hand using the + * RemoteIndexListModifiers returned by function getModifier(int). + * + * @tparam T The type of the underlying index set. + * @tparam A The type of the allocator to use. + */ + template > > + class RemoteIndices + { + friend class InterfaceBuilder; + friend class IndicesSyncer; + template + friend void repairLocalIndexPointers(std::map,A2> >&, + RemoteIndices&, + const T1&); + + template + friend void fillIndexSetHoles(const G& graph, Dune::OwnerOverlapCopyCommunication& oocomm); + friend std::ostream& operator<<<>(std::ostream&, const RemoteIndices&); + + public: + + /** + * @brief Type of the index set we use, e.g. ParallelLocalIndexSet. + */ + typedef T ParallelIndexSet; + + /** + * @brief The type of the collective iterator over all remote indices. */ + typedef CollectiveIterator CollectiveIteratorT; + + /** + * @brief The type of the global index. + */ + typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; + + + /** + * @brief The type of the local index. + */ + typedef typename ParallelIndexSet::LocalIndex LocalIndex; + + /** + * @brief The type of the attribute. + */ + typedef typename LocalIndex::Attribute Attribute; + + /** + * @brief Type of the remote indices we manage. + */ + typedef Dune::RemoteIndex RemoteIndex; + + + /** + * @brief The type of the allocator for the remote index list. + */ + using Allocator = typename std::allocator_traits::template rebind_alloc; + + /** @brief The type of the remote index list. */ + typedef Dune::SLList + RemoteIndexList; + + /** @brief The type of the map from rank to remote index list. */ + typedef std::map > + RemoteIndexMap; + + typedef typename RemoteIndexMap::const_iterator const_iterator; + + /** + * @brief Constructor. + * @param comm The communicator to use. + * @param source The indexset which represents the global to + * local mapping at the source of the communication + * @param destination The indexset to which the communication + * which represents the global to + * local mapping at the destination of the communication. + * May be the same as the source indexset. + * @param neighbours Optional: The neighbours the process shares indices with. + * If this parameter is omitted a ring communication with all indices will take + * place to calculate this information which is O(P). + * @param includeSelf If true, sending from indices of the processor to other + * indices on the same processor is enabled even if the same indexset is used + * on both the + * sending and receiving side. + */ + inline RemoteIndices(const ParallelIndexSet& source, const ParallelIndexSet& destination, + const MPI_Comm& comm, const std::vector& neighbours=std::vector(), bool includeSelf=false); + + RemoteIndices(); + + /** + * @brief Tell whether sending from indices of the processor to other + * indices on the same processor is enabled even if the same indexset is + * used on both the sending and receiving side. + * + * @param includeSelf If true it is enabled. + */ + void setIncludeSelf(bool includeSelf); + + /** + * @brief Set the index sets and communicator we work with. + * + * @warning All remote indices already setup will be deleted! + * + * @param comm The communicator to use. + * @param source The indexset which represents the global to + * local mapping at the source of the communication + * @param destination The indexset to which the communication + * which represents the global to + * local mapping at the destination of the communication. + * May be the same as the source indexset. + * @param neighbours Optional: The neighbours the process shares indices with. + * If this parameter is omitted a ring communication with all indices will take + * place to calculate this information which is O(P). + */ + void setIndexSets(const ParallelIndexSet& source, const ParallelIndexSet& destination, + const MPI_Comm& comm, const std::vector& neighbours=std::vector()); + + template + void setNeighbours(const C& neighbours) + { + neighbourIds.clear(); + neighbourIds.insert(neighbours.begin(), neighbours.end()); + + } + + const std::set& getNeighbours() const + { + return neighbourIds; + } + + /** + * @brief Destructor. + */ + ~RemoteIndices(); + + /** + * @brief Rebuilds the set of remote indices. + * + * This has to be called whenever the underlying index sets + * change. + * + * If the template parameter ignorePublic is true all indices will be treated + * as public. + */ + template + void rebuild(); + + bool operator==(const RemoteIndices& ri) const; + + /** + * @brief Checks whether the remote indices are synced with + * the indexsets. + * + * If they are not synced the remote indices need to be rebuild. + * @return True if they are synced. + */ + inline bool isSynced() const; + + /** + * @brief Get the mpi communicator used. + */ + inline MPI_Comm communicator() const; + + /** + * @brief Get a modifier for a remote index list. + * + * Sometimes the user knows in advance which indices will be present + * on other processors, too. Then he can set them up using this modifier. + * + * @warning Use with care. If the remote index list is inconsistent + * after the modification the communication might result in a dead lock! + * + * @tparam mode If true the index set corresponding to the remote indices might get modified. + * Therefore the internal pointers to the indices need to be repaired. + * @tparam send If true the remote index information at the sending side will + * be modified, if false the receiving side. + */ + template + inline RemoteIndexListModifier getModifier(int process); + + /** + * @brief Find an iterator over the remote index lists of a specific process. + * @param proc The identifier of the process. + * @return The iterator the remote index lists postioned at the process. + * If theres is no list for this process, the end iterator is returned. + */ + inline const_iterator find(int proc) const; + + /** + * @brief Get an iterator over all remote index lists. + * @return The iterator over all remote index lists postioned at the first process. + */ + inline const_iterator begin() const; + + /** + * @brief Get an iterator over all remote index lists. + * @return The iterator over all remote index lists postioned at the end. + */ + inline const_iterator end() const; + + /** + * @brief Get an iterator for colletively iterating over the remote indices of all remote processes. + */ + template + inline CollectiveIteratorT iterator() const; + + /** + * @brief Free the index lists. + */ + inline void free(); + + /** + * @brief Get the number of processors we share indices with. + * @return The number of neighbours. + */ + inline int neighbours() const; + + /** @brief Get the index set at the source. */ + inline const ParallelIndexSet& sourceIndexSet() const; + + /** @brief Get the index set at destination. */ + inline const ParallelIndexSet& destinationIndexSet() const; + + private: + /** copying is forbidden. */ + RemoteIndices(const RemoteIndices&) = delete; + + /** @brief Index set used at the source of the communication. */ + const ParallelIndexSet* source_; + + /** @brief Index set used at the destination of the communication. */ + const ParallelIndexSet* target_; + + /** @brief The communicator to use.*/ + MPI_Comm comm_; + + /** @brief The neighbours we share indices with. + * If not empty this will speedup rebuild. */ + std::set neighbourIds; + + /** @brief The communicator tag to use. */ + const static int commTag_=333; + + /** + * @brief The sequence number of the source index set when the remote indices + * where build. + */ + int sourceSeqNo_; + + /** + * @brief The sequence number of the destination index set when the remote indices + * where build. + */ + int destSeqNo_; + + /** + * @brief Whether the public flag was ignored during the build. + */ + bool publicIgnored; + + /** + * @brief Whether the next build will be the first build ever. + */ + bool firstBuild; + + /* + * @brief If true, sending from indices of the processor to other + * indices on the same processor is enabled even if the same indexset is used + * on both the + * sending and receiving side. + */ + bool includeSelf; + + /** @brief The index pair type. */ + typedef IndexPair + PairType; + + /** + * @brief The remote indices. + * + * The key is the process id and the values are the pair of remote + * index lists, the first for receiving, the second for sending. + */ + RemoteIndexMap remoteIndices_; + + /** + * @brief Build the remote mapping. + * + * If the template parameter ignorePublic is true all indices will be treated + * as public. + * @param includeSelf If true, sending from indices of the processor to other + * indices on the same processor is enabled even if the same indexset is used + * on both the + * sending and receiving side. + */ + template + inline void buildRemote(bool includeSelf); + + /** + * @brief Count the number of public indices in an index set. + * @param indexSet The index set whose indices we count. + * @return the number of indices marked as public. + */ + inline int noPublic(const ParallelIndexSet& indexSet); + + /** + * @brief Pack the indices to send if source_ and target_ are the same. + * + * If the template parameter ignorePublic is true all indices will be treated + * as public. + * @param myPairs Array to store references to the public indices in. + * @param p_out The output buffer to pack the entries to. + * @param type The mpi datatype for the pairs. + * @param bufferSize The size of the output buffer p_out. + * @param position The position to start packing. + */ + template + inline void packEntries(PairType** myPairs, const ParallelIndexSet& indexSet, + char* p_out, MPI_Datatype type, int bufferSize, + int* position, int n); + + /** + * @brief unpacks the received indices and builds the remote index list. + * + * @param remote The list to add the indices to. + * @param remoteEntries The number of remote entries to unpack. + * @param local The local indices to check whether we know the remote + * indices. + * @param localEntries The number of local indices. + * @param type The mpi data type for unpacking. + * @param p_in The input buffer to unpack from. + * @param position The position in the buffer to start unpacking from. + * @param bufferSize The size of the input buffer. + */ + inline void unpackIndices(RemoteIndexList& remote, int remoteEntries, + PairType** local, int localEntries, char* p_in, + MPI_Datatype type, int* position, int bufferSize, + bool fromOurself); + + inline void unpackIndices(RemoteIndexList& send, RemoteIndexList& receive, + int remoteEntries, PairType** localSource, + int localSourceEntries, PairType** localDest, + int localDestEntries, char* p_in, + MPI_Datatype type, int* position, int bufferSize); + + void unpackCreateRemote(char* p_in, PairType** sourcePairs, PairType** DestPairs, + int remoteProc, int sourcePublish, int destPublish, + int bufferSize, bool sendTwo, bool fromOurSelf=false); + }; + + /** @} */ + + /** + * @brief Modifier for adding and/or deleting remote indices from + * the remote index list. + * + * In some cases all the information about the indices also present + * on remote process might already be known. In this case this + * information can be provided to the RemoteIndices via this modifier. + * This prevents the global communication needed by a call to + * RemoteIndices::rebuild. + * + * In some cases it might advisable to run IndicesSyncer::sync afterwards. + * + * @warning Use with care. If the indices are not consistent afterwards + * communication attempts might deadlock! + */ + template + class RemoteIndexListModifier + { + + template + friend class RemoteIndices; + + public: + class InvalidPosition : public RangeError + {}; + + enum { + /** + * @brief If true the index set corresponding to the + * remote indices might get modified. + * + * If for example new indices are added to an index set + * all pointers of the remote indices to the local indices + * become invalid after ParallelIndexSet::endResize() was called. + */ + MODIFYINDEXSET=mode + }; + + /** + * @brief Type of the index set we use. + */ + typedef T ParallelIndexSet; + + /** + * @brief The type of the global index. + */ + typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; + + /** + * @brief The type of the local index. + */ + typedef typename ParallelIndexSet::LocalIndex LocalIndex; + + /** + * @brief The type of the attribute. + */ + typedef typename LocalIndex::Attribute Attribute; + + /** + * @brief Type of the remote indices we manage. + */ + typedef Dune::RemoteIndex RemoteIndex; + + /** + * @brief The type of the allocator for the remote index list. + */ + typedef A Allocator; + + /** @brief The type of the remote index list. */ + typedef Dune::SLList + RemoteIndexList; + + /** + * @brief The type of the modifying iterator of the remote index list. + */ + typedef SLListModifyIterator ModifyIterator; + + /** + * @brief The type of the remote index list iterator. + */ + typedef typename RemoteIndexList::const_iterator ConstIterator; + + /** + * @brief Insert an index to the list. + * + * Moves to the position where the index fits and inserts it. + * After the insertion only indices with an bigger global index + * than the inserted can be inserted. + * + * This method is only available if MODIFYINDEXSET is false. + * + * @param index The index to insert. + * @exception InvalidPosition Thrown if the index at the current position or + * the one before has bigger global index than the one to be inserted. + */ + void insert(const RemoteIndex& index); + + + /** + * @brief Insert an index to the list. + * + * Moves to the position where the index fits and inserts it. + * After the insertion only indices with an bigger global index + * than the inserted can be inserted. + * + * This method is only available if MODIFYINDEXSET is true. + * + * @param index The index to insert. + * @param global The global index of the remote index. + * @exception InvalidPosition Thrown if the index at the current position or + * the one before has bigger global index than the one to be inserted. + */ + void insert(const RemoteIndex& index, const GlobalIndex& global); + + /** + * @brief Remove a remote index. + * @param global The global index corresponding to the remote index. + * @return True If there was a corresponding remote index. + * @exception InvalidPostion If there was an insertion or deletion of + * a remote index corresponding to a bigger global index before. + */ + bool remove(const GlobalIndex& global); + + /** + * @brief Repair the pointers to the local index pairs. + * + * Due to adding new indices or/and deleting indices in the + * index set all pointers to the local index pair might become + * invalid during ParallelIndexSet::endResize(). + * This method repairs them. + * + * @exception InvalidIndexSetState Thrown if the underlying + * index set is not in ParallelIndexSetState::GROUND mode (only when + * compiled with DUNE_ISTL_WITH_CHECKING!). + */ + void repairLocalIndexPointers(); + + + RemoteIndexListModifier(const RemoteIndexListModifier&); + + /** + * @brief Default constructor. + * @warning Object is not usable! + */ + RemoteIndexListModifier() + : glist_() + {} + + private: + + /** + * @brief Create a modifier for a remote index list. + * @param indexSet The set of indices the process knows. + * @param rList The list of remote indices to modify. + */ + RemoteIndexListModifier(const ParallelIndexSet& indexSet, + RemoteIndexList& rList); + + typedef SLList GlobalList; + typedef typename GlobalList::ModifyIterator GlobalModifyIterator; + RemoteIndexList* rList_; + const ParallelIndexSet* indexSet_; + GlobalList glist_; + ModifyIterator iter_; + GlobalModifyIterator giter_; + ConstIterator end_; + bool first_; + GlobalIndex last_; + }; + + /** + * @brief A collective iterator for moving over the remote indices for + * all processes collectively. + */ + template + class CollectiveIterator + { + + /** + * @brief Type of the index set we use. + */ + typedef T ParallelIndexSet; + + /** + * @brief The type of the global index. + */ + typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; + + /** + * @brief The type of the local index. + */ + typedef typename ParallelIndexSet::LocalIndex LocalIndex; + + /** + * @brief The type of the attribute. + */ + typedef typename LocalIndex::Attribute Attribute; + + /** @brief The remote index type */ + typedef Dune::RemoteIndex RemoteIndex; + + /** @brief The allocator of the remote indices. */ + using Allocator = typename std::allocator_traits::template rebind_alloc; + + /** @brief The type of the remote index list. */ + typedef Dune::SLList RemoteIndexList; + + /** @brief The of map for storing the iterators. */ + typedef std::map > + Map; + + public: + + /** @brief The type of the map from rank to remote index list. */ + typedef std::map > + RemoteIndexMap; + + /** + * @brief Constructor. + * @param map_ The map of the remote indices. + * @param send True if we want iterate over the remote indices used for sending. + */ + inline CollectiveIterator(const RemoteIndexMap& map_, bool send); + + /** + * @brief Advances all underlying iterators. + * + * All iterators are advanced until they point to a remote index whose + * global id is bigger or equal to global. + * Iterators pointing to their end are removed. + * @param global The index we search for. + */ + inline void advance(const GlobalIndex& global); + + /** + * @brief Advances all underlying iterators. + * + * All iterators are advanced until they point to a remote index whose + * global id is bigger or equal to global. + * Iterators pointing to their end are removed. + * @param global The index we search for. + * @param attribute The attribute we search for. + */ + inline void advance(const GlobalIndex& global, const Attribute& attribute); + + CollectiveIterator& operator++(); + + /** + * @brief Checks whether there are still iterators in the map. + */ + inline bool empty() const; + + /** + * @brief Iterator over the valid underlying iterators. + * + * An iterator is valid if it points to a remote index whose + * global id is equal to the one currently examined. + */ + class iterator + { + public: + typedef typename Map::iterator RealIterator; + typedef typename Map::iterator ConstRealIterator; + + + //! \todo Please doc me! + iterator(const RealIterator& iter, const ConstRealIterator& end, GlobalIndex& index) + : iter_(iter), end_(end), index_(index), hasAttribute(false) + { + // Move to the first valid entry + while(iter_!=end_ && iter_->second.first->localIndexPair().global()!=index_) + ++iter_; + } + + iterator(const RealIterator& iter, const ConstRealIterator& end, GlobalIndex index, + Attribute attribute) + : iter_(iter), end_(end), index_(index), attribute_(attribute), hasAttribute(true) + { + // Move to the first valid entry or the end + while(iter_!=end_ && (iter_->second.first->localIndexPair().global()!=index_ + || iter_->second.first->localIndexPair().local().attribute()!=attribute)) + ++iter_; + } + //! \todo Please doc me! + iterator(const iterator& other) + : iter_(other.iter_), end_(other.end_), index_(other.index_) + { } + + //! \todo Please doc me! + iterator& operator++() + { + ++iter_; + // If entry is not valid move on + while(iter_!=end_ && (iter_->second.first->localIndexPair().global()!=index_ || + (hasAttribute && + iter_->second.first->localIndexPair().local().attribute()!=attribute_))) + ++iter_; + assert(iter_==end_ || + (iter_->second.first->localIndexPair().global()==index_)); + assert(iter_==end_ || !hasAttribute || + (iter_->second.first->localIndexPair().local().attribute()==attribute_)); + return *this; + } + + //! \todo Please doc me! + const RemoteIndex& operator*() const + { + return *(iter_->second.first); + } + + //! \todo Please doc me! + int process() const + { + return iter_->first; + } + + //! \todo Please doc me! + const RemoteIndex* operator->() const + { + return iter_->second.first.operator->(); + } + + //! \todo Please doc me! + bool operator==(const iterator& other) const + { + return other.iter_==iter_; + } + + //! \todo Please doc me! + bool operator!=(const iterator& other) const + { + return other.iter_!=iter_; + } + + private: + iterator(); + + RealIterator iter_; + RealIterator end_; + GlobalIndex index_; + Attribute attribute_; + bool hasAttribute; + }; + + iterator begin(); + + iterator end(); + + private: + + Map map_; + GlobalIndex index_; + Attribute attribute_; + bool noattribute; + }; + + template + MPI_Datatype MPITraits > >::getType() + { + if(type==MPI_DATATYPE_NULL) { + int length[2] = {1, 1}; + MPI_Aint base; + MPI_Aint disp[2]; + MPI_Datatype types[2] = {MPITraits::getType(), + MPITraits >::getType()}; + IndexPair > rep; + MPI_Get_address(&rep, &base); // lower bound of the datatype + MPI_Get_address(&(rep.global_), &disp[0]); + MPI_Get_address(&(rep.local_), &disp[1]); + for (MPI_Aint& d : disp) + d -= base; + + MPI_Datatype tmp; + MPI_Type_create_struct(2, length, disp, types, &tmp); + + MPI_Type_create_resized(tmp, 0, sizeof(IndexPair >), &type); + MPI_Type_commit(&type); + + MPI_Type_free(&tmp); + } + return type; + } + + template + MPI_Datatype MPITraits > >::type=MPI_DATATYPE_NULL; + + template + RemoteIndex::RemoteIndex(const T2& attribute, const PairType* local) + : localIndex_(local), attribute_(static_cast>(attribute)) + {} + + template + RemoteIndex::RemoteIndex(const T2& attribute) + : localIndex_(0), attribute_(static_cast>(attribute)) + {} + + template + RemoteIndex::RemoteIndex() + : localIndex_(0), attribute_() + {} + template + inline bool RemoteIndex::operator==(const RemoteIndex& ri) const + { + return localIndex_==ri.localIndex_ && attribute_==ri.attribute; + } + + template + inline bool RemoteIndex::operator!=(const RemoteIndex& ri) const + { + return localIndex_!=ri.localIndex_ || attribute_!=ri.attribute_; + } + + template + inline const T2 RemoteIndex::attribute() const + { + return T2(attribute_); + } + + template + inline const IndexPair >& RemoteIndex::localIndexPair() const + { + return *localIndex_; + } + + template + inline RemoteIndices::RemoteIndices(const ParallelIndexSet& source, + const ParallelIndexSet& destination, + const MPI_Comm& comm, + const std::vector& neighbours, + bool includeSelf_) + : source_(&source), target_(&destination), comm_(comm), + sourceSeqNo_(-1), destSeqNo_(-1), publicIgnored(false), firstBuild(true), + includeSelf(includeSelf_) + { + setNeighbours(neighbours); + } + + template + void RemoteIndices::setIncludeSelf(bool b) + { + includeSelf=b; + } + + template + RemoteIndices::RemoteIndices() + : source_(0), target_(0), sourceSeqNo_(-1), + destSeqNo_(-1), publicIgnored(false), firstBuild(true), + includeSelf(false) + {} + + template + void RemoteIndices::setIndexSets(const ParallelIndexSet& source, + const ParallelIndexSet& destination, + const MPI_Comm& comm, + const std::vector& neighbours) + { + free(); + source_ = &source; + target_ = &destination; + comm_ = comm; + firstBuild = true; + setNeighbours(neighbours); + } + + template + const typename RemoteIndices::ParallelIndexSet& + RemoteIndices::sourceIndexSet() const + { + return *source_; + } + + + template + const typename RemoteIndices::ParallelIndexSet& + RemoteIndices::destinationIndexSet() const + { + return *target_; + } + + + template + RemoteIndices::~RemoteIndices() + { + free(); + } + + template + template + inline void RemoteIndices::packEntries(IndexPair** pairs, + const ParallelIndexSet& indexSet, + char* p_out, MPI_Datatype type, + int bufferSize, + int *position, + [[maybe_unused]] int n) + { + // fill with own indices + const auto end = indexSet.end(); + + //Now pack the source indices + int i=0; + for(auto index = indexSet.begin(); index != end; ++index) + if(ignorePublic || index->local().isPublic()) { + + MPI_Pack(const_cast(&(*index)), 1, + type, + p_out, bufferSize, position, comm_); + pairs[i++] = const_cast(&(*index)); + + } + assert(i==n); + } + + template + inline int RemoteIndices::noPublic(const ParallelIndexSet& indexSet) + { + + int noPublic=0; + + const auto end=indexSet.end(); + for(auto index=indexSet.begin(); index!=end; ++index) + if(index->local().isPublic()) + noPublic++; + + return noPublic; + + } + + + template + inline void RemoteIndices::unpackCreateRemote(char* p_in, PairType** sourcePairs, + PairType** destPairs, int remoteProc, + int sourcePublish, int destPublish, + int bufferSize, bool sendTwo, + bool fromOurSelf) + { + + // unpack the number of indices we received + int noRemoteSource=-1, noRemoteDest=-1; + char twoIndexSets=0; + int position=0; + // Did we receive two index sets? + MPI_Unpack(p_in, bufferSize, &position, &twoIndexSets, 1, MPI_CHAR, comm_); + // The number of source indices received + MPI_Unpack(p_in, bufferSize, &position, &noRemoteSource, 1, MPI_INT, comm_); + // The number of destination indices received + MPI_Unpack(p_in, bufferSize, &position, &noRemoteDest, 1, MPI_INT, comm_); + + + // Indices for which we receive + RemoteIndexList* receive= new RemoteIndexList(); + // Indices for which we send + RemoteIndexList* send=0; + + MPI_Datatype type= MPITraits::getType(); + + if(!twoIndexSets) { + if(sendTwo) { + send = new RemoteIndexList(); + // Create both remote index sets simultaneously + unpackIndices(*send, *receive, noRemoteSource, sourcePairs, sourcePublish, + destPairs, destPublish, p_in, type, &position, bufferSize); + }else{ + // we only need one list + unpackIndices(*receive, noRemoteSource, sourcePairs, sourcePublish, + p_in, type, &position, bufferSize, fromOurSelf); + send=receive; + } + }else{ + + int oldPos=position; + // Two index sets received + unpackIndices(*receive, noRemoteSource, destPairs, destPublish, + p_in, type, &position, bufferSize, fromOurSelf); + if(!sendTwo) + //unpack source entries again as destination entries + position=oldPos; + + send = new RemoteIndexList(); + unpackIndices(*send, noRemoteDest, sourcePairs, sourcePublish, + p_in, type, &position, bufferSize, fromOurSelf); + } + + if(receive->empty() && send->empty()) { + if(send==receive) { + delete send; + }else{ + delete send; + delete receive; + } + }else{ + remoteIndices_.insert(std::make_pair(remoteProc, + std::make_pair(send,receive))); + } + } + + + template + template + inline void RemoteIndices::buildRemote(bool includeSelf_) + { + // Processor configuration + int rank, procs; + MPI_Comm_rank(comm_, &rank); + MPI_Comm_size(comm_, &procs); + + // number of local indices to publish + // The indices of the destination will be send. + int sourcePublish, destPublish; + + // Do we need to send two index sets? + char sendTwo = (source_ != target_); + + if(procs==1 && !(sendTwo || includeSelf_)) + // Nothing to communicate + return; + + sourcePublish = (ignorePublic) ? source_->size() : noPublic(*source_); + + if(sendTwo) + destPublish = (ignorePublic) ? target_->size() : noPublic(*target_); + else + // we only need to send one set of indices + destPublish = 0; + + int maxPublish, publish=sourcePublish+destPublish; + + // Calucate maximum number of indices send + MPI_Allreduce(&publish, &maxPublish, 1, MPI_INT, MPI_MAX, comm_); + + // allocate buffers + PairType** destPairs; + PairType** sourcePairs = new PairType*[sourcePublish>0 ? sourcePublish : 1]; + + if(sendTwo) + destPairs = new PairType*[destPublish>0 ? destPublish : 1]; + else + destPairs=sourcePairs; + + char** buffer = new char*[2]; + int bufferSize; + int position=0; + int intSize; + int charSize; + + // calculate buffer size + MPI_Datatype type = MPITraits::getType(); + + MPI_Pack_size(maxPublish, type, comm_, + &bufferSize); + MPI_Pack_size(1, MPI_INT, comm_, + &intSize); + MPI_Pack_size(1, MPI_CHAR, comm_, + &charSize); + // Our message will contain the following: + // a bool whether two index sets where sent + // the size of the source and the dest indexset, + // then the source and destination indices + bufferSize += 2 * intSize + charSize; + + if(bufferSize<=0) bufferSize=1; + + buffer[0] = new char[bufferSize]; + buffer[1] = new char[bufferSize]; + + + // pack entries into buffer[0], p_out below! + MPI_Pack(&sendTwo, 1, MPI_CHAR, buffer[0], bufferSize, &position, + comm_); + + // The number of indices we send for each index set + MPI_Pack(&sourcePublish, 1, MPI_INT, buffer[0], bufferSize, &position, + comm_); + MPI_Pack(&destPublish, 1, MPI_INT, buffer[0], bufferSize, &position, + comm_); + + // Now pack the source indices and setup the destination pairs + packEntries(sourcePairs, *source_, buffer[0], type, + bufferSize, &position, sourcePublish); + // If necessary send the dest indices and setup the source pairs + if(sendTwo) + packEntries(destPairs, *target_, buffer[0], type, + bufferSize, &position, destPublish); + + + // Update remote indices for ourself + if(sendTwo|| includeSelf_) + unpackCreateRemote(buffer[0], sourcePairs, destPairs, rank, sourcePublish, + destPublish, bufferSize, sendTwo, includeSelf_); + + neighbourIds.erase(rank); + + if(neighbourIds.size()==0) + { + Dune::dvverb<::size_type size_type; + size_type noNeighbours=neighbourIds.size(); + + // setup sends + for(std::set::iterator neighbour=neighbourIds.begin(); + neighbour!= neighbourIds.end(); ++neighbour) { + // Only send the information to the neighbouring processors + MPI_Issend(buffer[0], position , MPI_PACKED, *neighbour, commTag_, comm_, req++); + } + + //Test for received messages + + for(size_type received=0; received + inline void RemoteIndices::unpackIndices(RemoteIndexList& remote, + int remoteEntries, + PairType** local, + int localEntries, + char* p_in, + MPI_Datatype type, + int* position, + int bufferSize, + bool fromOurSelf) + { + if(remoteEntries==0) + return; + + PairType index; + MPI_Unpack(p_in, bufferSize, position, &index, 1, + type, comm_); + GlobalIndex oldGlobal=index.global(); + int n_in=0, localIndex=0; + + //Check if we know the global index + while(localIndexglobal()==index.global()) { + int oldLocalIndex=localIndex; + + while(localIndexglobal()==index.global()) { + if(!fromOurSelf || index.local().attribute() != + local[localIndex]->local().attribute()) + // if index is from us it has to have a different attribute + remote.push_back(RemoteIndex(index.local().attribute(), + local[localIndex])); + localIndex++; + } + + // unpack next remote index + if((++n_in) < remoteEntries) { + MPI_Unpack(p_in, bufferSize, position, &index, 1, + type, comm_); + if(index.global()==oldGlobal) + // Restart comparison for the same global indices + localIndex=oldLocalIndex; + else + oldGlobal=index.global(); + }else{ + // No more received indices + break; + } + continue; + } + + if (local[localIndex]->global() + inline void RemoteIndices::unpackIndices(RemoteIndexList& send, + RemoteIndexList& receive, + int remoteEntries, + PairType** localSource, + int localSourceEntries, + PairType** localDest, + int localDestEntries, + char* p_in, + MPI_Datatype type, + int* position, + int bufferSize) + { + int n_in=0, sourceIndex=0, destIndex=0; + + //Check if we know the global index + while(n_in= than the one in the unpacked index + while(sourceIndexglobal()global()global()==index.global()) + send.push_back(RemoteIndex(index.local().attribute(), + localSource[sourceIndex])); + + if(destIndex < localDestEntries && localDest[destIndex]->global() == index.global()) + receive.push_back(RemoteIndex(index.local().attribute(), + localDest[sourceIndex])); + } + + } + + template + inline void RemoteIndices::free() + { + auto lend = remoteIndices_.end(); + for(auto lists=remoteIndices_.begin(); lists != lend; ++lists) { + if(lists->second.first==lists->second.second) { + // there is only one remote index list. + delete lists->second.first; + }else{ + delete lists->second.first; + delete lists->second.second; + } + } + remoteIndices_.clear(); + firstBuild=true; + } + + template + inline int RemoteIndices::neighbours() const + { + return remoteIndices_.size(); + } + + template + template + inline void RemoteIndices::rebuild() + { + // Test whether a rebuild is Needed. + if(firstBuild || + ignorePublic!=publicIgnored || ! + isSynced()) { + free(); + + buildRemote(includeSelf); + + sourceSeqNo_ = source_->seqNo(); + destSeqNo_ = target_->seqNo(); + firstBuild=false; + publicIgnored=ignorePublic; + } + + + } + + template + inline bool RemoteIndices::isSynced() const + { + return sourceSeqNo_==source_->seqNo() && destSeqNo_ ==target_->seqNo(); + } + + template + template + RemoteIndexListModifier RemoteIndices::getModifier(int process) + { + + // The user are on their own now! + // We assume they know what they are doing and just set the + // remote indices to synced status. + sourceSeqNo_ = source_->seqNo(); + destSeqNo_ = target_->seqNo(); + + typename RemoteIndexMap::iterator found = remoteIndices_.find(process); + + if(found == remoteIndices_.end()) + { + if(source_ != target_) + found = remoteIndices_.insert(found, std::make_pair(process, + std::make_pair(new RemoteIndexList(), + new RemoteIndexList()))); + else{ + RemoteIndexList* rlist = new RemoteIndexList(); + found = remoteIndices_.insert(found, + std::make_pair(process, + std::make_pair(rlist, rlist))); + } + } + + firstBuild = false; + + if(send) + return RemoteIndexListModifier(*source_, *(found->second.first)); + else + return RemoteIndexListModifier(*target_, *(found->second.second)); + } + + template + inline typename RemoteIndices::const_iterator + RemoteIndices::find(int proc) const + { + return remoteIndices_.find(proc); + } + + template + inline typename RemoteIndices::const_iterator + RemoteIndices::begin() const + { + return remoteIndices_.begin(); + } + + template + inline typename RemoteIndices::const_iterator + RemoteIndices::end() const + { + return remoteIndices_.end(); + } + + + template + bool RemoteIndices::operator==(const RemoteIndices& ri) const + { + if(neighbours()!=ri.neighbours()) + return false; + + const auto rend = remoteIndices_.end(); + + for(auto rindex = remoteIndices_.begin(), rindex1=ri.remoteIndices_.begin(); rindex!=rend; ++rindex, ++rindex1) { + if(rindex->first != rindex1->first) + return false; + if(*(rindex->second.first) != *(rindex1->second.first)) + return false; + if(*(rindex->second.second) != *(rindex1->second.second)) + return false; + } + return true; + } + + template + RemoteIndexListModifier::RemoteIndexListModifier(const ParallelIndexSet& indexSet, + RemoteIndexList& rList) + : rList_(&rList), indexSet_(&indexSet), iter_(rList.beginModify()), end_(rList.end()), first_(true) + { + if(MODIFYINDEXSET) { + assert(indexSet_); + for(ConstIterator iter=iter_; iter != end_; ++iter) + glist_.push_back(iter->localIndexPair().global()); + giter_ = glist_.beginModify(); + } + } + + template + RemoteIndexListModifier::RemoteIndexListModifier(const RemoteIndexListModifier& other) + : rList_(other.rList_), indexSet_(other.indexSet_), + glist_(other.glist_), iter_(other.iter_), giter_(other.giter_), end_(other.end_), + first_(other.first_), last_(other.last_) + {} + + template + inline void RemoteIndexListModifier::repairLocalIndexPointers() + { + if(MODIFYINDEXSET) { + // repair pointers to local index set. +#ifdef DUNE_ISTL_WITH_CHECKING + if(indexSet_->state()!=GROUND) + DUNE_THROW(InvalidIndexSetState, "Index has to be in ground mode for repairing pointers to indices"); +#endif + auto giter = glist_.begin(); + auto index = indexSet_->begin(); + + for(auto iter=rList_->begin(); iter != end_; ++iter) { + while(index->global()<*giter) { + ++index; +#ifdef DUNE_ISTL_WITH_CHECKING + if(index == indexSet_->end()) + DUNE_THROW(InvalidPosition, "No such global index in set!"); +#endif + } + +#ifdef DUNE_ISTL_WITH_CHECKING + if(index->global() != *giter) + DUNE_THROW(InvalidPosition, "No such global index in set!"); +#endif + iter->localIndex_ = &(*index); + } + } + } + + template + inline void RemoteIndexListModifier::insert(const RemoteIndex& index) + { + static_assert(!mode,"Not allowed if the mode indicates that new indices" + "might be added to the underlying index set. Use " + "insert(const RemoteIndex&, const GlobalIndex&) instead"); + +#ifdef DUNE_ISTL_WITH_CHECKING + if(!first_ && index.localIndexPair().global()localIndexPair().global() < index.localIndexPair().global()) { + ++iter_; + } + + // No duplicate entries allowed + assert(iter_==end_ || iter_->localIndexPair().global() != index.localIndexPair().global()); + iter_.insert(index); + last_ = index.localIndexPair().global(); + first_ = false; + } + + template + inline void RemoteIndexListModifier::insert(const RemoteIndex& index, const GlobalIndex& global) + { + static_assert(mode,"Not allowed if the mode indicates that no new indices" + "might be added to the underlying index set. Use " + "insert(const RemoteIndex&) instead"); +#ifdef DUNE_ISTL_WITH_CHECKING + if(!first_ && globallocalIndexPair().global() != global); + iter_.insert(index); + giter_.insert(global); + + last_ = global; + first_ = false; + } + + template + bool RemoteIndexListModifier::remove(const GlobalIndex& global) + { +#ifdef DUNE_ISTL_WITH_CHECKING + if(!first_ && globallocalIndexPair().global() < global) + ++iter_; + + if(iter_->localIndexPair().global()==global) { + iter_.remove(); + found = true; + } + } + + last_ = global; + first_ = false; + return found; + } + + template + template + inline typename RemoteIndices::CollectiveIteratorT RemoteIndices::iterator() const + { + return CollectiveIterator(remoteIndices_, send); + } + + template + inline MPI_Comm RemoteIndices::communicator() const + { + return comm_; + + } + + template + CollectiveIterator::CollectiveIterator(const RemoteIndexMap& pmap, bool send) + { + + const auto end = pmap.end(); + for(auto process = pmap.begin(); process != end; ++process) { + const RemoteIndexList* list = send ? process->second.first : process->second.second; + using ri_iterator = typename RemoteIndexList::const_iterator; + map_.insert(std::make_pair(process->first, + std::pair(list->begin(), list->end()))); + } + } + + template + inline void CollectiveIterator::advance(const GlobalIndex& index) + { + const auto end = map_.end(); + + for(auto iter = map_.begin(); iter != end;) { + // Step the iterator until we are >= index + typename RemoteIndexList::const_iterator current = iter->second.first; + typename RemoteIndexList::const_iterator rend = iter->second.second; + RemoteIndex remoteIndex; + if(current != rend) + remoteIndex = *current; + + while(iter->second.first!=iter->second.second && iter->second.first->localIndexPair().global()second.first); + + // erase from the map if there are no more entries. + if(iter->second.first == iter->second.second) + map_.erase(iter++); + else{ + ++iter; + } + } + index_=index; + noattribute=true; + } + + template + inline void CollectiveIterator::advance(const GlobalIndex& index, + const Attribute& attribute) + { + const auto end = map_.end(); + + for(auto iter = map_.begin(); iter != end;) { + // Step the iterator until we are >= index + typename RemoteIndexList::const_iterator current = iter->second.first; + typename RemoteIndexList::const_iterator rend = iter->second.second; + RemoteIndex remoteIndex; + if(current != rend) + remoteIndex = *current; + + // Move to global index or bigger + while(iter->second.first!=iter->second.second && iter->second.first->localIndexPair().global()second.first); + + // move to attribute or bigger + while(iter->second.first!=iter->second.second + && iter->second.first->localIndexPair().global()==index + && iter->second.first->localIndexPair().local().attribute()second.first); + + // erase from the map if there are no more entries. + if(iter->second.first == iter->second.second) + map_.erase(iter++); + else{ + ++iter; + } + } + index_=index; + attribute_=attribute; + noattribute=false; + } + + template + inline CollectiveIterator& CollectiveIterator::operator++() + { + const auto end = map_.end(); + + for(auto iter = map_.begin(); iter != end;) { + // Step the iterator until we are >= index + auto current = iter->second.first; + auto rend = iter->second.second; + + // move all iterators pointing to the current global index to next value + if(iter->second.first->localIndexPair().global()==index_ && + (noattribute || iter->second.first->localIndexPair().local().attribute() == attribute_)) + ++(iter->second.first); + + // erase from the map if there are no more entries. + if(iter->second.first == iter->second.second) + map_.erase(iter++); + else{ + ++iter; + } + } + return *this; + } + + template + inline bool CollectiveIterator::empty() const + { + return map_.empty(); + } + + template + inline typename CollectiveIterator::iterator + CollectiveIterator::begin() + { + if(noattribute) + return iterator(map_.begin(), map_.end(), index_); + else + return iterator(map_.begin(), map_.end(), index_, + attribute_); + } + + template + inline typename CollectiveIterator::iterator + CollectiveIterator::end() + { + return iterator(map_.end(), map_.end(), index_); + } + + template + inline std::ostream& operator<<(std::ostream& os, const RemoteIndex& index) + { + os<<"[global="< + inline std::ostream& operator<<(std::ostream& os, const RemoteIndices& indices) + { + int rank; + MPI_Comm_rank(indices.comm_, &rank); + const auto rend = indices.remoteIndices_.end(); + + for(auto rindex = indices.remoteIndices_.begin(); rindex!=rend; ++rindex) { + os<first<<":"; + + if(!rindex->second.first->empty()) { + os<<" send:"; + + const auto send= rindex->second.first->end(); + + for(auto index = rindex->second.first->begin(); + index != send; ++index) + os<<*index<<" "; + os<second.second->empty()) { + os<first<<": "<<"receive: "; + + for(const auto& index : *(rindex->second.second)) + os << index << " "; + } + os< + +namespace Dune +{ + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Provides classes for selecting + * indices based on attribute flags. + * @author Markus Blatt + */ + + /** + * @brief A const iterator over an uncached selection. + */ + template + class SelectionIterator + { + public: + /** + * @brief The type of the Set of attributes. + * + * It has to provide a static method + * \code bool contains(AttributeType a); \endcode + * that returns true if a is in the set. + * Such types are EnumItem, EnumRange, Combine. + */ + typedef TS AttributeSet; + + /** + * @brief The type of the underlying index set. + */ + typedef Dune::ParallelIndexSet ParallelIndexSet; + + //typedef typename ParallelIndexSet::const_iterator ParallelIndexSetIterator; + + typedef ConstArrayListIterator, N, std::allocator > > ParallelIndexSetIterator; + /** + * @brief Constructor. + * @param iter The iterator over the index set. + * @param end The iterator over the index set positioned at the end. + */ + SelectionIterator(const ParallelIndexSetIterator& iter, const ParallelIndexSetIterator& end) + : iter_(iter), end_(end) + { + // Step to the first valid entry + while(iter_!=end_ && !AttributeSet::contains(iter_->local().attribute())) + ++iter_; + } + + void operator++() + { + assert(iter_!=end_); + for(++iter_; iter_!=end_; ++iter_) + if(AttributeSet::contains(iter_->local().attribute())) + break; + } + + + uint32_t operator*() const + { + return iter_->local().local(); + } + + bool operator==(const SelectionIterator& other) const + { + return iter_ == other.iter_; + } + + bool operator!=(const SelectionIterator& other) const + { + return iter_ != other.iter_; + } + + private: + ParallelIndexSetIterator iter_; + const ParallelIndexSetIterator end_; + }; + + + /** + * @brief An uncached selection of indices. + */ + template + class UncachedSelection + { + public: + /** + * @brief The type of the Set of attributes. + * + * It has to provide a static method + * \code bool contains(AttributeType a); \endcode + * that returns true if a is in the set. + * Such types are EnumItem, EnumRange, Combine. + */ + typedef TS AttributeSet; + + /** + * @brief The type of the global index of the underlying index set. + */ + typedef TG GlobalIndex; + + /** + * @brief The type of the local index of the underlying index set. + * + * It has to provide a function + * \code AttributeType attribute(); \endcode + */ + typedef TL LocalIndex; + + /** + * @brief The type of the underlying index set. + */ + typedef Dune::ParallelIndexSet ParallelIndexSet; + + /** + * @brief The type of the iterator of the selected indices. + */ + typedef SelectionIterator iterator; + + /** + * @brief The type of the iterator of the selected indices. + */ + typedef iterator const_iterator; + + UncachedSelection() + : indexSet_() + {} + + UncachedSelection(const ParallelIndexSet& indexset) + : indexSet_(&indexset) + {} + /** + * @brief Set the index set of the selection. + * @param indexset The index set to use. + */ + void setIndexSet(const ParallelIndexSet& indexset); + + /** + * @brief Get the index set we are a selection for. + */ + //const ParallelIndexSet& indexSet() const; + + /** + * @brief Get an iterator over the selected indices. + * @return An iterator positioned at the first selected index. + */ + const_iterator begin() const; + + /** + * @brief Get an iterator over the selected indices. + * @return An iterator positioned at the first selected index. + */ + const_iterator end() const; + + + private: + const ParallelIndexSet* indexSet_; + + }; + + /** + * @brief A cached selection of indices. + */ + template + class Selection + { + public: + /** + * @brief The type of the set of attributes. + * + * It has to provide a static method + * \code bool contains(AttributeType a); \endcode + * that returns true if a is in the set. + * Such types are EnumItem, EnumRange, Combine. + */ + typedef TS AttributeSet; + + /** + * @brief The type of the global index of the underlying index set. + */ + typedef TG GlobalIndex; + + /** + * @brief The type of the local index of the underlying index set. + * + * It has to provide a function + * \code AttributeType attribute(); \endcode + */ + typedef TL LocalIndex; + + /** + * @brief The type of the underlying index set. + */ + typedef Dune::ParallelIndexSet ParallelIndexSet; + + /** + * @brief The type of the iterator of the selected indices. + */ + typedef uint32_t* iterator; + + /** + * @brief The type of the iterator of the selected indices. + */ + typedef uint32_t* const_iterator; + + Selection() + : selected_() + {} + + Selection(const ParallelIndexSet& indexset) + : selected_(), size_(0), built_(false) + { + setIndexSet(indexset); + } + + ~Selection(); + + /** + * @brief Set the index set of the selection. + * @param indexset The index set to use. + */ + void setIndexSet(const ParallelIndexSet& indexset); + + /** + * @brief Free allocated memory. + */ + void free(); + + /** + * @brief Get the index set we are a selection for. + */ + //IndexSet indexSet() const; + + /** + * @brief Get an iterator over the selected indices. + * @return An iterator positioned at the first selected index. + */ + const_iterator begin() const; + + /** + * @brief Get an iterator over the selected indices. + * @return An iterator positioned at the first selected index. + */ + const_iterator end() const; + + + private: + uint32_t* selected_; + size_t size_; + bool built_; + + }; + + template + inline void Selection::setIndexSet(const ParallelIndexSet& indexset) + { + if(built_) + free(); + + // Count the number of entries the selection has to hold + typedef typename ParallelIndexSet::const_iterator const_iterator; + const const_iterator end = indexset.end(); + int entries = 0; + + for(const_iterator index = indexset.begin(); index != end; ++index) + if(AttributeSet::contains(index->local().attribute())) + ++entries; + + selected_ = new uint32_t[entries]; + built_ = true; + + entries = 0; + for(const_iterator index = indexset.begin(); index != end; ++index) + if(AttributeSet::contains(index->local().attribute())) + selected_[entries++]= index->local().local(); + + size_=entries; + built_=true; + } + + template + uint32_t* Selection::begin() const + { + return selected_; + } + + template + uint32_t* Selection::end() const + { + return selected_+size_; + } + + template + inline void Selection::free() + { + delete[] selected_; + size_=0; + built_=false; + } + + template + inline Selection::~Selection() + { + if(built_) + free(); + } + + template + SelectionIterator UncachedSelection::begin() const + { + return SelectionIterator(indexSet_->begin(), + indexSet_->end()); + } + + template + SelectionIterator UncachedSelection::end() const + { + return SelectionIterator(indexSet_->end(), + indexSet_->end()); + } + template + void UncachedSelection::setIndexSet(const ParallelIndexSet& indexset) + { + indexSet_ = &indexset; + } + + /** @} */ + + +} +#endif diff --git a/dune/common/parallel/test/CMakeLists.txt b/dune/common/parallel/test/CMakeLists.txt new file mode 100644 index 0000000..a789387 --- /dev/null +++ b/dune/common/parallel/test/CMakeLists.txt @@ -0,0 +1,60 @@ +dune_add_test(SOURCES communicationtest.cc + LINK_LIBRARIES dunecommon + MPI_RANKS 1 2 4 + TIMEOUT 300 + CMAKE_GUARD MPI_FOUND + LABELS quick) + +dune_add_test(SOURCES indexsettest.cc + LINK_LIBRARIES dunecommon + LABELS quick) + +dune_add_test(SOURCES remoteindicestest.cc + LINK_LIBRARIES dunecommon + MPI_RANKS 1 2 4 + TIMEOUT 300 + CMAKE_GUARD MPI_FOUND + LABELS quick) + +dune_add_test(SOURCES selectiontest.cc + LINK_LIBRARIES dunecommon + LABELS quick) + +dune_add_test(SOURCES syncertest.cc + LINK_LIBRARIES dunecommon + MPI_RANKS 1 2 4 + TIMEOUT 300 + CMAKE_GUARD MPI_FOUND + LABELS quick) + +dune_add_test(SOURCES variablesizecommunicatortest.cc + MPI_RANKS 1 2 4 + TIMEOUT 300 + CMAKE_GUARD MPI_FOUND + LABELS quick) + +dune_add_test(SOURCES mpidatatest.cc + LINK_LIBRARIES dunecommon + MPI_RANKS 2 + TIMEOUT 300 + LABELS quick) + +dune_add_test(SOURCES mpifuturetest.cc + LINK_LIBRARIES dunecommon + MPI_RANKS 1 2 4 + TIMEOUT 300 + LABELS quick) + +dune_add_test(SOURCES mpipacktest.cc + LINK_LIBRARIES dunecommon + MPI_RANKS 2 + TIMEOUT 300 + CMAKE_GUARD MPI_FOUND + LABELS quick) + +dune_add_test(SOURCES mpigatherscattertest.cc + LINK_LIBRARIES dunecommon + MPI_RANKS 2 + TIMEOUT 300 + CMAKE_GUARD MPI_FOUND + LABELS quick) diff --git a/dune/common/parallel/test/communicationtest.cc b/dune/common/parallel/test/communicationtest.cc new file mode 100644 index 0000000..14a6888 --- /dev/null +++ b/dune/common/parallel/test/communicationtest.cc @@ -0,0 +1,47 @@ +#if HAVE_CONFIG_H +#include +#endif + +#include +int main(int argc, char** argv) +{ + Dune::MPIHelper::instance(argc, argv); + + int ret = 0; + Dune::No_Comm nc1, nc2; + + if ( !(nc1 == nc2)) + { + std::cerr << "operator==: No_Comms need to compare equal"< +#include +#include + +#include +#include + +int testDeleteIndices() +{ + Dune::ParallelIndexSet indexSet; + Dune::ParallelIndexSet indexSet1; + + indexSet.beginResize(); + indexSet1.beginResize(); + + for(int i=0; i< 10; i++) { + indexSet.add(i, Dune::LocalIndex (i)); + indexSet1.add(i, Dune::LocalIndex (i)); + } + + indexSet.endResize(); + indexSet1.endResize(); + + typedef Dune::ParallelIndexSet::iterator + Iterator; + + Iterator entry = indexSet.begin(); + indexSet.beginResize(); + + for(int i=0; i < 5; i++) + ++entry; + + indexSet.markAsDeleted(entry); + + indexSet.endResize(); + + std::cout<<"Unchanged: "<global()==5) { + std::cerr<<"Entry was not deleted!"<9) { + std::cerr<<"Number of entries not correct!"<::iterator iter=indexSet1.begin(); + + // Test whether the local indices changed + for(entry = indexSet.begin(); entry != end; ++entry) { + while(iter->global() < entry->global()) + iter++; + if(iter->global() != entry->global()) { + std::cerr <<" Global indices do not match!"<local() != entry->local()) { + std::cerr <<" Local indices do not match!"< + +#include + +#include +#include + +#include +using namespace Dune; + +int main(int argc, char** argv){ + Dune::MPIHelper & mpihelper = Dune::MPIHelper::instance(argc, argv); + auto cc = mpihelper.getCommunication(); + + if(mpihelper.rank() == 0) + std::cout << "Test 1: static data (int)" << std::endl; + if(mpihelper.rank() == 0){ + cc.send(42, 1, 0); + int i = 42; + const int& j = i; + cc.send(j, 1, 0); + } + else if(mpihelper.rank() == 1){ + std::cout << "receive: " << cc.recv(0, 0, 0) << std::endl; + int i = 0; + cc.recv(i, 0, 0); + std::cout << i << std::endl; + } + + if(mpihelper.rank() == 0) + std::cout << "Test 2: dynamic data (std::vector)" << std::endl; + if(mpihelper.rank() == 0){ + cc.send(std::vector{ 42.0, 43.0, 4711}, 1, 0); + std::vector vec{ 42.0, 43.0, 4711}; + const std::vector& vec_ref = vec; + cc.send(vec_ref, 1, 0); + cc.send(std::move(vec), 1, 0); + } + else if(mpihelper.rank() == 1){ + auto vec = cc.recv(std::vector{0,0,0}, 0, 0); + std::cout << "receive: "; + for(double d : vec) + std::cout << d << ","; + std::cout << "\b" << std::endl; + std::vector vec2(3); + cc.recv(vec2, 0, 0); + for(double d : vec2) + std::cout << d << ","; + std::cout << "\b" << std::endl; + + std::vector vec3(3); + auto d = vec3.data(); + std::vector vec4 = cc.recv(std::move(vec3), 0, 0); + for(double d : vec4) + std::cout << d << ","; + std::cout << "\b" << std::endl; + if(d != vec4.data()) + DUNE_THROW(Exception, "The vector has not the same memory"); + } + + if(mpihelper.rank() == 0) + std::cout << "Test 3: DynamicVector" << std::endl; + if(mpihelper.rank() == 0){ + cc.send(DynamicVector{ 42.0, 43.0, 4711}, 1, 0); + DynamicVector vec{ 42.0, 43.0, 4711}; + const DynamicVector& vec_ref = vec; + cc.send(vec_ref, 1, 0); + cc.send(std::move(vec), 1, 0); + } + else if(mpihelper.rank() == 1){ + auto vec = cc.recv(DynamicVector{0,0,0}, 0, 0); + std::cout << "receive: "; + for(double d : vec) + std::cout << d << ","; + std::cout << "\b" << std::endl; + DynamicVector vec2(3); + cc.recv(vec2, 0, 0); + for(double d : vec2) + std::cout << d << ","; + std::cout << "\b" << std::endl; + + DynamicVector vec3(3); + auto d = vec3.container().data(); + DynamicVector vec4 = cc.recv(std::move(vec3), 0, 0); + for(double d : vec4) + std::cout << d << ","; + std::cout << "\b" << std::endl; + if(d != vec4.container().data()) + DUNE_THROW(Exception, "The vector has not the same memory"); + } + + + if(mpihelper.rank() == 0) + std::cout << "Test 3: DynamicVector (resize receive)" << std::endl; + if(mpihelper.rank() == 0){ + cc.send(DynamicVector{ 42.0, 43.0, 4711}, 1, 0); + DynamicVector vec{ 42.0, 43.0, 4711}; + const DynamicVector& vec_ref = vec; + cc.send(vec_ref, 1, 0); + cc.send(std::move(vec), 1, 0); + } + else if(mpihelper.rank() == 1){ + auto vec = cc.rrecv(DynamicVector{}, 0, 0); + std::cout << "receive: "; + for(double d : vec) + std::cout << d << ","; + std::cout << "\b" << std::endl; + DynamicVector vec2(3); + cc.recv(vec2, 0, 0); + for(double d : vec2) + std::cout << d << ","; + std::cout << "\b" << std::endl; + + DynamicVector vec3(3); + auto d = vec3.container().data(); + DynamicVector vec4 = cc.recv(std::move(vec3), 0, 0); + for(double d : vec4) + std::cout << d << ","; + std::cout << "\b" << std::endl; + if(d != vec4.container().data()) + DUNE_THROW(Exception, "The vector has not the same memory"); + } + + return 0; +} diff --git a/dune/common/parallel/test/mpifuturetest.cc b/dune/common/parallel/test/mpifuturetest.cc new file mode 100644 index 0000000..e5d7091 --- /dev/null +++ b/dune/common/parallel/test/mpifuturetest.cc @@ -0,0 +1,118 @@ +#include + +#include +#include +#include +#include + +namespace Dune { + template + struct MPIData + { + static_assert(Dune::AlwaysFalse::value, "MPIData of reference type should not be used!"); + // MPIData of reference type should not be used! + // This struct should never be used it just + // exists to generate a compiler error + }; +} + + +int main(int argc, char** argv){ + auto& mpihelper = Dune::MPIHelper::instance(argc, argv); + + auto cc = mpihelper.getCommunication(); + + // p2p + if(mpihelper.size() > 1){ + if(mpihelper.rank() == 0){ + Dune::Future f = cc.isend(42, 1, 0); + f.wait(); + int i = 42; + Dune::Future f2 = cc.isend(i, 1, 0); + f2.wait(); + }else if(mpihelper.rank() == 1){ + Dune::Future f = cc.irecv(41, 0, 0); + std::cout << "Rank 1 received " << f.get() << std::endl; + int j = 41; + Dune::Future f2 = cc.irecv(j, 0, 0); + std::cout << "Rank 1 received " << f2.get() << std::endl; + } + } + + int answer; + if(mpihelper.rank() == 0){ + std::cout << "Broadcast lvalue-reference" << std::endl; + answer = 42; + } + Dune::Future f = cc.template ibroadcast(answer, 0); + f.wait(); + std::cout << "Rank " << mpihelper.rank() << " knows: The answer is " << answer << std::endl; + if(mpihelper.rank() == 0) + std::cout << "Broadcast value" << std::endl; + Dune::Future f2 = cc.template ibroadcast(int(answer), 0); + std::cout << "Rank " << mpihelper.rank() << " knows: The answer is " << f2.get() << std::endl; + + Dune::DynamicVector vec(3); + if(mpihelper.rank() == 0){ + std::cout << "Broadcast vector" << std::endl; + std::iota(vec.begin(), vec.end(), 41); + } + Dune::Future> f3 = cc.ibroadcast(vec, 0); + f3.wait(); + std::cout << "Rank " << mpihelper.rank() << " received vector: " << vec << std::endl; + + if(mpihelper.rank() == 0) + std::cout << "nonb Barrier ==========================" << std::endl; + Dune::Future f4 = cc.ibarrier(); + f4.wait(); + + if(mpihelper.rank() == 0){ + std::cout << "nonb gather ===========================" << std::endl; + Dune::Future> f = cc.igather(mpihelper.rank() + 42, Dune::DynamicVector(mpihelper.size()), 0); + std::cout << "Gather result: " << f.get() << std::endl; + }else{ + cc.igather(mpihelper.rank(), {}, 0).wait(); + } + + if(mpihelper.rank() == 0){ + std::cout << "nonb scatter ===========================" << std::endl; + std::vector my_buddies(mpihelper.size()); + std::iota(my_buddies.begin(), my_buddies.end(), 42); + Dune::Future f = cc.iscatter(my_buddies, 0, 0); + std::cout << "Scatter result (Rank " << mpihelper.rank() << "): " << f.get() << std::endl; + }else{ + Dune::Future f = cc.iscatter(std::vector(0), 0, 0); + std::cout << "Scatter result (Rank " << mpihelper.rank() << "): " << f.get() << std::endl; + } + + { + if(mpihelper.rank() == 0) + std::cout << "nonb allreduce ===========================" << std::endl; + Dune::Future f = cc.iallreduce>(mpihelper.rank()+4, 0); + std::cout << "Allreduce result on rank " << mpihelper.rank() <<": " << f.get() << std::endl; + } + + { + if(mpihelper.rank() == 0) + std::cout << "nonb allreduce inplace ===========================" << std::endl; + Dune::Future> f = cc.iallreduce>(Dune::DynamicVector{42, 3+mpihelper.rank()}); + std::cout << "Allreduce result on rank " << mpihelper.rank() <<": " << f.get() << std::endl; + } + + { + if(mpihelper.rank() == 0) + std::cout << "check for MPI_SUM with double& ===========================" << std::endl; + double answer = 42; + auto f = cc.iallreduce>(answer); + std::cout << "Allreduce result on rank " << mpihelper.rank() <<": " << f.get() << std::endl; + } + + // that's wrong, MPIFuture will hold a dangeling reference: + // Dune::MPIFuture g; + // { + // int i = 42; + // g = cc.iallreduce>(i); + // } + // g.wait(); + return 0; +} diff --git a/dune/common/parallel/test/mpigatherscattertest.cc b/dune/common/parallel/test/mpigatherscattertest.cc new file mode 100644 index 0000000..c5dd00d --- /dev/null +++ b/dune/common/parallel/test/mpigatherscattertest.cc @@ -0,0 +1,37 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#include + +#include +#include +#include + + +#include +#include +#include + +using namespace Dune; + +int main(int argc, char** argv){ + MPIHelper & mpihelper = MPIHelper::instance(argc, argv); + auto cc = mpihelper.getCommunication(); + int rank = cc.rank(); + int size = cc.size(); + + std::array data = {1.0 + rank, 2.0 + rank}; + + auto gathered = cc.igather(data, std::vector(rank==0?2*size:0), 0).get(); + + for(auto& d : gathered) + d += 1; + + cc.iscatter(gathered, data, 0).get(); + if(data[0] != 2+rank || + data[1] != 3+rank){ + DUNE_THROW(Exception, "Wrong result after gather - scatter"); + } + + return 0; +} diff --git a/dune/common/parallel/test/mpipacktest.cc b/dune/common/parallel/test/mpipacktest.cc new file mode 100644 index 0000000..76f0d7b --- /dev/null +++ b/dune/common/parallel/test/mpipacktest.cc @@ -0,0 +1,40 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include + +constexpr int TAG = 42; +int main(int argc, char** argv){ + Dune::MPIHelper& helper = Dune::MPIHelper::instance(argc, argv); + Dune::TestSuite suite; + suite.require(helper.size() == 2) << "This test must be executed on two processes"; + auto comm = helper.getCommunication(); + Dune::MPIPack pack(comm); + + if(helper.rank() == 0){ + pack << 3 << helper.rank(); + pack << std::vector{4711, 42}; + comm.send(pack, 1, TAG); + } + if(helper.rank() == 1){ + Dune::MPIPack pack = comm.rrecv(Dune::MPIPack(comm), 0, TAG); + int drei; pack >> drei; + int rank_0; pack >> rank_0; + std::vector vec; + pack >> vec; + suite.check(drei==3) << "received wrong value"; + suite.check(rank_0==0) << "received wrong value"; + suite.check(vec.size() == 2) << "vector has wrong size!"; + suite.check(vec[0] == 4711 && vec[1] == 42) << "vector contains wrong values!"; + } + + return 0; +} diff --git a/dune/common/parallel/test/remoteindicestest.cc b/dune/common/parallel/test/remoteindicestest.cc new file mode 100644 index 0000000..f8f616d --- /dev/null +++ b/dune/common/parallel/test/remoteindicestest.cc @@ -0,0 +1,721 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +enum GridFlags { + owner, overlap, border +}; + +class Array; + +std::ostream& operator<<(std::ostream& os, const Array& a); + +class Array +{ + friend std::ostream& operator<<(std::ostream& os, const Array& a); +public: + typedef double value_type; + Array() : vals_(0), size_(0) + {} + + Array(int size) : size_(size) + { + vals_ = new double[size]; + } + + void build(int size) + { + vals_ = new double[size]; + size_ = size; + } + + Array& operator+=(double d) + { + for(int i=0; i < size_; i++) + vals_[i]+=d; + return *this; + } + + ~Array() + { + if(vals_!=0) + delete[] vals_; + } + + const double& operator[](int i) const + { + return vals_[i]; + } + + double& operator[](int i) + { + return vals_[i]; + } +private: + Array(const Array&) + {} + double *vals_; + int size_; +}; + +struct ArrayGatherScatter +{ + static double gather(const Array& a, int i); + + static void scatter(Array& a, double v, int i); + +}; + + +inline double ArrayGatherScatter::gather(const Array& a, int i) +{ + return a[i]; +} + +inline void ArrayGatherScatter::scatter(Array& a, double v, int i) +{ + a[i]=v; + +} + +std::ostream& operator<<(std::ostream& os, const Array& a) +{ + if(a.size_>0) + os<< "{ "< LocalIndexType; + + typedef Dune::ParallelIndexSet,45> ParallelIndexSet; + + ParallelIndexSet distIndexSet; + // global indexset + ParallelIndexSet globalIndexSet; + + // Set up the indexsets. + int start = std::max(rank*nx-1,0); + int end = std::min((rank + 1) * nx+1, Nx); + + distIndexSet.beginResize(); + + int localIndex=0; + int size = Ny*(end-start); + Array distArray(size); + Array* globalArray; + int index=0; + + std::cout< +void setupDistributed(Array& distArray, Dune::ParallelIndexSet >& distIndexSet, + int rank, int procs) +{ + // The local grid + int nx = NX/procs; + int mod = NX%procs; + + // Set up the indexsets. + int start, end; + int ostart, oend; + + if(rank0) + ostart = start - 1; + else + ostart = start; + + if(rank=end-1); + GridFlags flag = owner; + if((i=end)) { + distArray[localIndex]=-(i+j*NX+rank*NX*NY); + flag = overlap; + }else + distArray[localIndex]=i+j*NX+rank*NX*NY; + + distIndexSet.add(i+j*NX, Dune::ParallelLocalIndex (localIndex++,flag,isPublic)); + } + + distIndexSet.endResize(); + + +} + +template +void setupGlobal(Array& globalArray, Dune::ParallelIndexSet >& globalIndexSet) +{ + // build global indexset on first process + globalIndexSet.beginResize(); + globalArray.build(NX*NY); + int k=0; + for(int j=0; j (i+j*NX,owner,false)); + globalArray[i+j*NX]=-(i+j*NX); + k++; + + } + + globalIndexSet.endResize(); +} + +void testIndicesBuffered(MPI_Comm comm) +{ + //using namespace Dune; + + // The global grid size + const int Nx = 8; + const int Ny = 1; + + // Process configuration + int procs, rank, master=0; + MPI_Comm_size(comm, &procs); + MPI_Comm_rank(comm, &rank); + + typedef Dune::ParallelIndexSet > ParallelIndexSet; + + ParallelIndexSet distIndexSet; + // global indexset + ParallelIndexSet globalIndexSet; + + Array distArray; + Array globalArray; + + setupDistributed(distArray, distIndexSet, rank, procs); + + + if(rank==master) { + setupGlobal(globalArray, globalIndexSet); + } + + typedef Dune::RemoteIndices RemoteIndices; + + RemoteIndices accuIndices(distIndexSet, globalIndexSet, comm); + + accuIndices.rebuild(); + std::cout<<"dist "<(); + + Dune::Interface accuInterface; + Dune::Interface overlapInterface; + Dune::EnumItem sourceFlags; + Dune::Combine,Dune::EnumItem,GridFlags> destFlags; + // Dune::Bool2Type flag; + + accuInterface.build(accuIndices, sourceFlags, destFlags); + overlapInterface.build(overlapIndices, Dune::EnumItem(), + Dune::EnumItem()); + overlapInterface.print(); + accuInterface.print(); + + //accuInterface.print(); + + Dune::BufferedCommunicator accumulator, overlapExchanger; + + accumulator.build(accuInterface); + + overlapExchanger.build(overlapInterface); + + std::cout<< rank<<": before forward distArray="<< distArray<(distArray, distArray); + + std::cout<(distArray); + + std::cout< > ParallelIndexSet; + ParallelIndexSet sendIndexSet; + // global indexset + ParallelIndexSet receiveIndexSet; + + Array array, redistributedArray; + + // Set up the indexsets. + { + + int start = std::max(rank*nx-1,0); + int end = std::min((rank + 1) * nx+1, Nx); + + sendIndexSet.beginResize(); + + + array.build(Ny*(end-start)); + + for(int j=0, localIndex=0; j=end-2); + GridFlags flag = owner; + + if((i==start && i!=0)||(i==end-1 && i!=Nx-1)) + flag = overlap; + + sendIndexSet.add(i+j*Nx, ParallelLocalIndex (localIndex,flag,isPublic)); + array[localIndex]=i+j*Nx+rank*Nx*Ny; + } + + sendIndexSet.endResize(); + } + { + int newrank = (rank + 1) % procs; + + int start = std::max(newrank*nx-1,0); + int end = std::min((newrank + 1) * nx+1, Nx); + + std::cout<=end-2); + GridFlags flag = owner; + + if((i==start && i!=0)||(i==end-1 && i!=Nx-1)) + flag = overlap; + + receiveIndexSet.add(i+j*Nx, ParallelLocalIndex (localIndex,flag,isPublic)); + redistributedArray[localIndex]=-1; + } + + receiveIndexSet.endResize(); + } + + + std::cout<< rank<<": distributed and global index set!"< RemoteIndices; + + RemoteIndices redistributeIndices(sendIndexSet, + receiveIndexSet, comm); + RemoteIndices overlapIndices(receiveIndexSet, receiveIndexSet, comm); + + redistributeIndices.rebuild(); + overlapIndices.rebuild(); + + DatatypeCommunicator redistribute, overlapComm; + EnumItem fowner; + EnumItem foverlap; + + redistribute.build(redistributeIndices, fowner, array, fowner, redistributedArray); + + overlapComm.build(overlapIndices, fowner, redistributedArray, foverlap, redistributedArray); + std::cout< > ParallelIndexSet; + ParallelIndexSet sendIndexSet; + // global indexset + ParallelIndexSet receiveIndexSet; + + Array array, redistributedArray; + + std::vector neighbours; + + // Set up the indexsets. + { + + int start = std::max(rank*nx-1,0); + int end = std::min((rank + 1) * nx+1, Nx); + + neighbours.reserve(2); + + if(rank>0) neighbours.push_back(rank-1); + if(rank=end-2); + GridFlags flag = owner; + + if((i==start && i!=0)||(i==end-1 && i!=Nx-1)) + flag = overlap; + + sendIndexSet.add(i+j*Nx, ParallelLocalIndex (localIndex,flag,isPublic)); + array[localIndex]=i+j*Nx; //+rank*Nx*Ny; + if(flag==overlap) + array[localIndex]=-array[localIndex]; + } + + sendIndexSet.endResize(); + } + { + int newrank = (rank + 1) % procs; + + int start = std::max(newrank*nx-1,0); + int end = std::min((newrank + 1) * nx+1, Nx); + + std::cout<=end-2); + GridFlags flag = owner; + + if((i==start && i!=0)||(i==end-1 && i!=Nx-1)) + flag = overlap; + + receiveIndexSet.add(i+j*Nx, ParallelLocalIndex (localIndex,flag,isPublic)); + redistributedArray[localIndex]=-1; + } + + receiveIndexSet.endResize(); + } + + + std::cout<< rank<<": distributed and global index set!"< RemoteIndices; + RemoteIndices redistributeIndices(sendIndexSet, + receiveIndexSet, comm); + RemoteIndices overlapIndices(receiveIndexSet, receiveIndexSet, comm); + RemoteIndices sendIndices(sendIndexSet, + sendIndexSet, comm, neighbours); + RemoteIndices sendIndices1(sendIndexSet, + sendIndexSet, comm); + overlapIndices.rebuild(); + redistributeIndices.rebuild(); + sendIndices.rebuild(); + sendIndices1.rebuild(); + + if(rank==0) + std::cout< fowner; + EnumItem foverlap; + + redistributeInterface.build(redistributeIndices, fowner, fowner); + overlapInterface.build(overlapIndices, fowner, foverlap); + + BufferedCommunicator redistribute; + BufferedCommunicator overlapComm; + + redistribute.build(array, redistributedArray, redistributeInterface); + overlapComm.build(overlapInterface); + + std::cout<(array, redistributedArray); + + std::cout<(redistributedArray); + + std::cout<(array, redistributedArray); + + std::cout<(array, redistributedArray); + + std::cout<firstRank) { + if(rank==0) + key = firstRank; + if(rank==firstRank) + key=0; + } + + MPI_Comm_split(MPI_COMM_WORLD, 0, key, &comm); + +#ifdef DEBUG + bool wait=1; + while(size>1 && wait) ; +#endif + + // testIndices(comm); + testIndicesBuffered(comm); + + if(rank==0) + std::cout< + +#include +#include +#include +#include +#include + +enum GridFlags { + owner, overlap, border +}; + +template +int meassure(const T& selection) +{ + /* + return meassure<1>(selection); + } + + template + int meassure(const T& selection) + {*/ + typedef typename T::const_iterator iterator; + + const iterator end = selection.end(); + + int count=0; + Dune::Timer timer; + timer.reset(); + for(int i=0; i<10; i++) + for(iterator iter = selection.begin(); iter != end; ++iter) + count+=*iter; + + std::cout<<" took "<< timer.elapsed()<<" seconds"< +void test() +{ + const int Nx = SIZE; + const int Ny = SIZE; + + // Process configuration + const int ALSIZE=55; + + Dune::ParallelIndexSet,ALSIZE> distIndexSet; + + distIndexSet.beginResize(); + + for(int y=0, i=0; y < Ny; y++) + for(int x=0; x < Nx; x++, i++) { + GridFlags flag = owner; + if(x==0 || x == Nx-1 || y ==0 || y==Ny-1) + flag = overlap; + + distIndexSet.add(i, Dune::ParallelLocalIndex (i, flag, true)); + } + + distIndexSet.endResize(); + + Dune::UncachedSelection,int,Dune::ParallelLocalIndex,ALSIZE> + ownerUncached(distIndexSet); + + Dune::Selection,int,Dune::ParallelLocalIndex,ALSIZE> + ownerCached(distIndexSet); + + Dune::UncachedSelection,int,Dune::ParallelLocalIndex,ALSIZE> + overlapUncached(distIndexSet); + + Dune::Selection,int,Dune::ParallelLocalIndex,ALSIZE> + overlapCached(distIndexSet); + + int count=0; + + std::cout<<" Owner selection uncached:"; + count+=meassure(ownerUncached); + std::cout<<" Owner selection cached:"; + count+=meassure(ownerCached); + std::cout<<" Overlap selection uncached:"; + count+=meassure(overlapUncached); + std::cout<<" Overlap selection cached:"; + count+=meassure(overlapCached); + std::cout<(); +} diff --git a/dune/common/parallel/test/syncertest.cc b/dune/common/parallel/test/syncertest.cc new file mode 100644 index 0000000..0d84eb5 --- /dev/null +++ b/dune/common/parallel/test/syncertest.cc @@ -0,0 +1,362 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#include "config.h" + +#include +#include +#include +#include +#include + +enum GridFlags { + owner, overlap, border +}; + +template +void deleteOverlapEntries(T& indices, + Dune::RemoteIndices& remoteIndices) +{ + typedef typename T::iterator IndexIterator; + typedef typename T::GlobalIndex GlobalIndex; + typedef typename T::LocalIndex::Attribute Attribute; + typedef Dune::RemoteIndices RemoteIndices; + typedef typename RemoteIndices::RemoteIndexList::ModifyIterator RemoteModifier; + typedef typename RemoteIndices::RemoteIndexList::const_iterator RemoteIterator; + typedef Dune::SLList, typename RemoteIndices::RemoteIndexList::Allocator> GlobalList; + typedef typename GlobalList::ModifyIterator GlobalModifier; + typedef std::tuple IteratorTuple; + typedef std::map IteratorMap; + typedef typename RemoteIndices::const_iterator RemoteMapIterator; + + int rank; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + std::map globalLists; + + IteratorMap iterators; + RemoteMapIterator rmEnd = remoteIndices.end(); + + for(RemoteMapIterator remote = remoteIndices.begin(); + remote != rmEnd; ++remote) { + // Initialize global indices + GlobalList& gList=globalLists[remote->first]; + const RemoteIterator rend = remote->second.first->end(); + + for(RemoteIterator index= remote->second.first->begin(); + index != rend; ++index) + gList.push_back(std::make_pair(index->localIndexPair().global(), + index->localIndexPair().local().attribute())); + + assert(gList.size()==remote->second.first->size()); + std::cout << "Size of remote indices is "<first, + IteratorTuple(remote->second.first->beginModify(), + gList.beginModify(), + rend, + gList.end(), + &gList, + remote->second.first))); + } + + indices.beginResize(); + + const IndexIterator endIndex = indices.end(); + for(IndexIterator index = indices.begin(); index != endIndex; ++index) { + if(index->local().attribute()==overlap) { + std::cout << rank<<": Deleting "<<*index<(remote->second) != std::get<2>(remote->second) + && *(std::get<1>(remote->second)) < *index) { + // increment all iterators + ++(std::get<0>(remote->second)); + ++(std::get<1>(remote->second)); + if(std::get<0>(remote->second)!=std::get<2>(remote->second)) + assert(std::get<1>(remote->second)!=std::get<3>(remote->second)); + } + + // Delete the entry if present + if(std::get<0>(remote->second) != std::get<2>(remote->second)) { + assert(std::get<1>(remote->second) != std::get<3>(remote->second)); + + if(*(std::get<1>(remote->second)) == *index) { + + std::cout<(remote->second)->first<<", "<< + std::get<1>(remote->second)->second<<" of process " + << remote->first<(remote->second).remove(); + std::get<1>(remote->second).remove(); + assert(std::get<4>(remote->second)->size()==std::get<5>(remote->second)->size()); + } + } + } + } + } + + indices.endResize(); + + // Update the pointers to the local index pairs + Dune::repairLocalIndexPointers(globalLists, remoteIndices, indices); + globalLists.clear(); +} + + +template +bool areEqual(T& indices, + Dune::RemoteIndices& remoteIndices, + T& oIndices, + Dune::RemoteIndices& oRemoteIndices){ + + typedef typename T::iterator IndexIterator; + typedef Dune::RemoteIndices RemoteIndices; + typedef typename RemoteIndices::RemoteIndexList::iterator RemoteIterator; + + IndexIterator iEnd = indices.end(); + bool ret=true; + int rank; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + // Test the index sets + if(indices.size() != oIndices.size()) { + std::cerr<< rank<<": Size of index set is unequal!"<global() != oIndex->global()) { + std::cerr<global() <<" is missing!"<local().attribute() !=oIndex->local().attribute()) { + std::cerr<global() <<" has wrong attribute: "<< + index->local().attribute()<< "!= "<local().attribute()<second.first->size() != remote->second.first->size()) { + std::cerr <first + <<" does not match!"<second.first->end(); + for(RemoteIterator rIndex= remote->second.first->begin(), + oRIndex = oRemote->second.first->begin(); oRIndex != rEnd; + ++rIndex, ++oRIndex) { + + if(rIndex->localIndexPair().global() != oRIndex->localIndexPair().global()) { + + std::cerr<localIndexPair().global() + <<" is missing for process "<first<attribute() != oRIndex->attribute()) { + std::cerr<localIndexPair().global() + <<" for process "<< remote->first<<" is wrong: " + <attribute()<<" != "<attribute()< +void addFakeRemoteIndices(T& indices, + T& oIndices, + Dune::RemoteIndices& remoteIndices, + Dune::RemoteIndices& oRemoteIndices){ + typedef typename T::iterator IndexIterator; + typedef typename T::GlobalIndex GlobalIndex; + typedef typename T::LocalIndex::Attribute Attribute; + typedef typename Dune::RemoteIndices::RemoteIndexList RemoteIndexList; + assert(remoteIndices.neighbours()==0 && oRemoteIndices.neighbours()==0); + + RemoteIndexList* rlist = new RemoteIndexList(); + RemoteIndexList* orlist = new RemoteIndexList(); + int added=0; + IndexIterator iEnd = indices.end(); + + for(IndexIterator index = indices.begin(), oIndex = oIndices.begin(); + index != iEnd; ++index, ++oIndex) { + assert(*index == *oIndex); + if(index->local().attribute()==overlap) { + added++; + rlist->push_back(Dune::RemoteIndex(owner,&(*index))); + orlist->push_back(Dune::RemoteIndex(owner,&(*oIndex))); + } + } + + + remoteIndices.remoteIndices_.insert(std::make_pair(1,std::make_pair(rlist,rlist))); + oRemoteIndices.remoteIndices_.insert(std::make_pair(1,std::make_pair(orlist,orlist))); + + std::cout<<"Added "< LocalIndexType; + + typedef Dune::ParallelIndexSet > ParallelIndexSet; + ParallelIndexSet indexSet, changedIndexSet; + + // Set up the indexsets. + int start,end, ostart, oend; + if(rank0 &&start syncer(changedIndexSet, changedRemoteIndices); + // return 0; + + std::cout<<"Syncing!"< + void gather(B& buffer, int i) + { + if(!dataSendAt.insert(i).second) { + std::cerr << rank << ": Gather() was called twice for index " << i << "!" << std::endl; + std::abort(); + } + + std::cout< + void scatter(B& buffer, int i, int size) + { + if(!dataRecievedAt.insert(i).second) { + std::cerr << rank << ": Scatter() was called twice for index " << i << "!" << std::endl; + std::abort(); + } + + std::cout<0;--size) + { + double index; + buffer.read(index); + std::cout< + void scatter(B& buffer, int i, int size) + { + if(!dataRecievedAt.insert(i).second) { + std::cerr << rank << ": Scatter() was called twice for index " << i << "!" << std::endl; + std::abort(); + } + + std::cout<0;--size) + { + double index; + buffer.read(index); + std::cout< dataSendAt; + std::set dataRecievedAt; + + VarDataHandle(int r) + : rank(r) + {} + int rank; + typedef double DataType; + bool fixedSize() + { + return false; + } + void verify(int procs, int start, int end) { + std::vector indices; + if(procs==1) { + for(int k=0;k<=10;k+=2) { + indices.push_back(k); + } + } + else { + if(rank && rank < procs) { + indices.push_back(start-1); + indices.push_back(start); + } + if(rank < procs-1) { + indices.push_back(end-1); + indices.push_back(end); + } + } + + std::set::iterator it; + for(int idx : indices) { + it = dataSendAt.find(idx); + if(it == dataSendAt.end()) { + std::cerr << rank << ": No data send at index " << idx << "!" << std::endl; + std::abort(); + } + dataSendAt.erase(it); + + it = dataRecievedAt.find(idx); + if(it == dataRecievedAt.end() && idx%5) { + std::cerr << rank << ": No data recieved at index " << idx << "!" << std::endl; + std::abort(); + } + else if(it != dataRecievedAt.end()) { + dataRecievedAt.erase(it); + } + } + for(const int &i : dataSendAt) { + std::cerr << rank << ": Unexpected data send at index " << i << "!" << std::endl; + std::abort(); + } + for(const int &i : dataRecievedAt) { + std::cerr << rank << ": Unexpected data recieved at index " << i << "!" << std::endl; + std::abort(); + } + } + template + void gather(B& buffer, int i) + { + if(!dataSendAt.insert(i).second) { + std::cerr << rank << ": Gather() was called twice for index " << i << "!" << std::endl; + std::abort(); + } + + std::size_t s=i%5; + std::cout<(i+j)); + } + template + void scatter(B& buffer, int i, int size) + { + if(!dataRecievedAt.insert(i).second) { + std::cerr << rank << ": Scatter() was called twice for index " << i << "!" << std::endl; + std::abort(); + } + + std::cout< + void scatter(B& buffer, int i, int size) + { + if(!dataRecievedAt.insert(i).second) { + std::cerr << rank << ": Scatter() was called twice for index " << i << "!" << std::endl; + std::abort(); + } + + std::cout<::InterfaceMap Interface; + Dune::InterfaceInformation send, recv; + send.reserve(6); + for(std::size_t i=0; i<=10; i+=2) + send.add(i); + recv.reserve(6); + for(std::size_t i=10; i<=10; i-=2) + recv.add(i); + Interface inf; + inf[0]=std::make_pair(send, recv); + Dune::VariableSizeCommunicator<> comm(MPI_COMM_SELF, inf, 6); + MyDataHandle1D handle(0); + comm.forward(handle); + handle.verify(procs, 0, 0); + std::cout<<"===================== backward ========================="<2) + --procs; + + // Partition a consecutive set of indices among all active ranks + // (where the final rank possibly excluded above is considered + // inactive). Set up interfaces so each rank communicates with its + // predecessors at the two indices next to the common partition + // boundary, and likewise for the successor. Then use the data + // handles defined at the top of this file to do some test + // communications. + int N=100000; // number of indices + int num_per_proc=N/procs; + // start is our first index, end is one-past our last index. + int start, end; + if(rank::InterfaceMap Interface; + Interface inf; + if(rank && rank comm(MPI_COMM_WORLD, inf, 6); + MyDataHandle handle(rank); + comm.forward(handle); + MPI_Barrier(MPI_COMM_WORLD); + handle.verify(procs, start, end); + MPI_Barrier(MPI_COMM_WORLD); + if(rank==0) + std::cout<<"===================== backward ========================="< +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +/** + * @addtogroup Common_Parallel + * + * @{ + */ +/** + * @file + * @brief A communicator that only needs to know the number of elements per + * index at the sender side. + * @author Markus Blatt + * @} + */ +namespace Dune +{ + +namespace Concept { + +struct HasFixedSize { + template auto require(H &&h) -> decltype(h.fixedSize()); +}; + +} // namespace Concept + +namespace Impl { + +template (), int> = 0> +constexpr bool callFixedSize(H &&handle) { + return handle.fixedSize(); +} + +template (), int> = 0> +[[deprecated("Using handles with fixedsize() (lower case s) is deprecated and " + "will be removed after release 2.8. Implement fixedSize() " + "(camelCase) instead!")]] +constexpr bool callFixedSize(H &&handle) { + return handle.fixedsize(); +} + +} // namespace Impl + +namespace +{ +/** + * @brief A message buffer. + * @tparam T The type of data that the buffer will hold. + */ +template > +class MessageBuffer +{ +public: + /** + * @brief Constructs a message. + * @param size The number of elements that buffer should hold, + */ + explicit MessageBuffer(int size) + : buffer_(new T[size]), size_(size), position_(0) + {} + /** + * @brief Copy constructor. + * @param o The instance to copy. + */ + explicit MessageBuffer(const MessageBuffer& o) + : buffer_(new T[o.size_]), size_(o.size_), position_(o.position_) + { + } + /** @brief Destructor. */ + ~MessageBuffer() + { + delete[] buffer_; + } + /** + * @brief Write an item to the buffer. + * @param data The data item to write. + */ + void write(const T& data) + { + buffer_[position_++]=data; + } + + /** + * @brief Reads a data item from the buffer + * @param[out] data Reference to where to store the read data. + */ + void read(T& data) + { + data=buffer_[position_++]; + } + + /** + * @brief Reset the buffer. + * + * On return the buffer will be positioned at the start again. + */ + void reset() + { + position_=0; + } + + /** + * @brief Test whether the whole buffer was read. + * @return True if we read or wrot until the end of the buffer. + */ + bool finished() + { + return position_==size_; + } + + /** + * @brief Tests whether the buffer has enough space left to read/write data. + * @param notItems The number of items to read or write. + * @return True if there is enough space for noItems items. + */ + bool hasSpaceForItems(int noItems) + { + return position_+noItems<=size_; + } + /** + * @brief Get the size of the buffer. + * @return The number of elements the buffer can hold. + */ + std::size_t size() const + { + return size_; + } + /** + * @brief Converts the buffer to a C array. + * @return The underlying C array. + */ + operator T*() + { + return buffer_; + } + +private: + /** + * @brief Pointer to the current insertion point of the buffer. + */ + T* buffer_; + /** + * @brief The size of the buffer + */ + std::size_t size_; + /** + * @brief The current position in the buffer. + */ + std::size_t position_; +}; + +/** + * @brief A tracker for the current position in a communication interface. + */ +class InterfaceTracker +{ +public: + /** + * @brief Constructor. + * @param rank The other rank that the interface communicates with. + * @param info A list of local indices belonging to this interface. + */ + InterfaceTracker(int rank, InterfaceInformation info, std::size_t fixedsize=0, + bool allocateSizes=false) + : fixedSize(fixedsize),rank_(rank), index_(), interface_(info), sizes_() + { + if(allocateSizes) + { + sizes_.resize(info.size()); + } + } + + /** + * @brief Moves to the next index in the interface. + */ + void moveToNextIndex() + { + index_++; + assert(index_<=interface_.size()); + skipZeroIndices(); + } + /** + * @brief Increment index various times. + * @param i The number of times to increment. + */ + void increment(std::size_t i) + { + index_+=i; + assert(index_<=interface_.size()); + } + /** + * @brief Checks whether all indices have been visited. + * @return True if all indices have been visited. + */ + bool finished() const + { + return index_==interface_.size(); + } + + void skipZeroIndices() + { + // skip indices with size zero! + while(sizes_.size() && index_!=interface_.size() &&!size()) + ++index_; + } + + /** + * @brief Get the current local index of the interface. + * @return The current local index of the interface. + */ + std::size_t index() const + { + return interface_[index_]; + } + /** + * @brief Get the size at the current index. + */ + std::size_t size() const + { + assert(sizes_.size()); + return sizes_[index_]; + } + /** + * @brief Get a pointer to the array with the sizes. + */ + std::size_t* getSizesPointer() + { + return &sizes_[0]; + } + /** + * @brief Returns whether the interface is empty. + * @return True if there are no entries in the interface. + */ + bool empty() const + { + return !interface_.size(); + } + + /** + * @brief Checks whether there are still indices waiting to be processed. + * @return True if there are still indices waiting to be processed. + */ + std::size_t indicesLeft() const + { + return interface_.size()-index_; + } + /** + * @brief The number of data items per index if it is fixed, 0 otherwise. + */ + std::size_t fixedSize; + /** + * @brief Get the process rank that this communication interface is with. + */ + int rank() const + { + return rank_; + } + /** + * @brief Get the offset to the first index. + */ + std::size_t offset() const + { + return index_; + } +private: + /** @brief The process rank that this communication interface is with. */ + int rank_; + /** @brief The other rank that this interface communcates with. */ + std::size_t index_; + /** @brief The list of local indices of this interface. */ + InterfaceInformation interface_; + std::vector sizes_; +}; + + +} // end unnamed namespace + +/** + * @addtogroup Common_Parallel + * + * @{ + */ +/** + * @brief A buffered communicator where the amount of data sent does not have to be known a priori. + * + * In contrast to BufferedCommunicator the amount of data is determined by the container + * whose entries are sent and not known at the receiving side a priori. + * + * Note that there is no global index-space, only local index-spaces on each + * rank. Note also that each rank has two index-spaces, one used for + * gathering/sending, and one used for scattering/receiving. These may be the + * identical, but they do not have to be. + * + * For data send from rank A to rank B, the order that rank A inserts its + * indices into its send-interface for rank B has to be the same order that + * rank B inserts its matching indices into its receive interface for rank A. + * (This is because the `VariableSizeCommunicator` has no concept of a global + * index-space, so the order used to insert the indices into the interfaces is + * the only clue it has to know which source index should be communicated to + * which target index.) + * + * It is permissible for a rank to communicate with itself, i.e. it can define + * send- and receive-interfaces to itself. These interfaces do not need to + * contain the same indices, as the local send index-space can be different + * from the local receive index-space. This is useful for repartitioning or + * for aggregating in AMG. + * + * Do not assume that gathering to an index happens before scattering to the + * same index in the same communication, as `VariableSizeCommunicator` assumes + * they are from different index-spaces. This is a pitfall if you want do + * communicate a vector in-place, e.g. to sum up partial results from + * different ranks. Instead, have separate source and target vectors and copy + * the source vector to the target vector before communicating. + */ +template > > +class VariableSizeCommunicator +{ +public: + /** + * @brief The type of the map from process number to InterfaceInformation for + * sending and receiving to and from it. + */ + typedef std::map, + std::less, + typename std::allocator_traits::template rebind_alloc< std::pair > > > InterfaceMap; + +#ifndef DUNE_PARALLEL_MAX_COMMUNICATION_BUFFER_SIZE + /** + * @brief Creates a communicator with the default maximum buffer size. + * + * The default size ist either what the macro DUNE_MAX_COMMUNICATION_BUFFER_SIZE + * is set to or 32768 if is not set. + */ + VariableSizeCommunicator(MPI_Comm comm, const InterfaceMap& inf) + : maxBufferSize_(32768), interface_(&inf) + { + MPI_Comm_dup(comm, &communicator_); + } + /** + * @brief Creates a communicator with the default maximum buffer size. + * @param inf The communication interface. + */ + VariableSizeCommunicator(const Interface& inf) + : maxBufferSize_(32768), interface_(&inf.interfaces()) + { + MPI_Comm_dup(inf.communicator(), &communicator_); + } +#else + /** + * @brief Creates a communicator with the default maximum buffer size. + * + * The default size ist either what the macro DUNE_MAX_COMMUNICATION_BUFFER_SIZE + * is set to or 32768 if is not set. + */ + VariableSizeCommunicator(MPI_Comm comm, InterfaceMap& inf) + : maxBufferSize_(DUNE_PARALLEL_MAX_COMMUNICATION_BUFFER_SIZE), + interface_(&inf) + { + MPI_Comm_dup(comm, &communicator_); + } + /** + * @brief Creates a communicator with the default maximum buffer size. + * @param inf The communication interface. + */ + VariableSizeCommunicator(const Interface& inf) + : maxBufferSize_(DUNE_PARALLEL_MAX_COMMUNICATION_BUFFER_SIZE), + interface_(&inf.interfaces()) + { + MPI_Comm_dup(inf.communicator(), &communicator_); + } +#endif + /** + * @brief Creates a communicator with a specific maximum buffer size. + * @param comm The MPI communicator to use. + * @param inf The communication interface. + * @param max_buffer_size The maximum buffer size allowed. + */ + VariableSizeCommunicator(MPI_Comm comm, const InterfaceMap& inf, std::size_t max_buffer_size) + : maxBufferSize_(max_buffer_size), interface_(&inf) + { + MPI_Comm_dup(comm, &communicator_); + } + + /** + * @brief Creates a communicator with a specific maximum buffer size. + * @param inf The communication interface. + * @param max_buffer_size The maximum buffer size allowed. + */ + VariableSizeCommunicator(const Interface& inf, std::size_t max_buffer_size) + : maxBufferSize_(max_buffer_size), interface_(&inf.interfaces()) + { + MPI_Comm_dup(inf.communicator(), &communicator_); + } + + ~VariableSizeCommunicator() + { + MPI_Comm_free(&communicator_); + } + + /** + * @brief Copy-constructs a communicator + * @param other VariableSizeCommunicator that is copied. + */ + VariableSizeCommunicator(const VariableSizeCommunicator& other) { + maxBufferSize_ = other.maxBufferSize_; + interface_ = other.interface_; + MPI_Comm_dup(other.communicator_, &communicator_); + } + + /** + * @brief Copy-assignes a communicator + * @param other VariableSizeCommunicator that is copied. + */ + VariableSizeCommunicator& operator=(const VariableSizeCommunicator& other) { + if(this == &other) // don't do anything if objects are the same + return *this; + + maxBufferSize_ = other.maxBufferSize_; + interface_ = other.interface_; + MPI_Comm_free(&communicator_); + MPI_Comm_dup(other.communicator_, &communicator_); + + return *this; + } + + /** + * @brief Communicate forward. + * + * @tparam DataHandle The type of the handle describing the data. This type has to adhere + * to the following interface: + * \code{.cpp} + * // returns whether the number of data items per entry is fixed + * bool fixedsize(); + * // get the number of data items for an entry with index i + * std::size_t size(std::size_t i); + * // gather the data at index i + * template + * void gather(MessageBuffer& buf, std::size_t i); + * // scatter the n data items to index i + * template + * void scatter(MessageBuffer& buf, std::size_t i, std::size_t n); + * \endcode + * @param handle A handle responsible for describing the data, gathering, and scattering it. + */ + template + void forward(DataHandle& handle) + { + communicate(handle); + } + + /** + * @brief Communicate backwards. + * + * @tparam DataHandle The type of the handle describing the data. This type has to adhere + * to the following interface: + * \code{.cpp} + * // returns whether the number of data items per entry is fixed + * bool fixedsize(); + * // get the number of data items for an entry with index i + * std::size_t size(std::size_t i); + * // gather the data at index i + * template + * void gather(MessageBuffer& buf, std::size_t i); + * // scatter the n data items to index i + * template + * void scatter(MessageBuffer& buf, std::size_t i, std::size_t n); + * \endcode + * @param handle A handle responsible for describing the data, gathering, and scattering it. + */ + template + void backward(DataHandle& handle) + { + communicate(handle); + } + +private: + template + void communicateSizes(DataHandle& handle, + std::vector& recv_trackers); + + /** + * @brief Communicates data according to the interface. + * @tparam forward If true sends data forwards, otherwise backwards along the interface. + * @tparame DataHandle The type of the data handle @see forward for a description of the interface. + * @param handle The handle describing the data and responsible for gather and scatter operations. + */ + template + void communicate(DataHandle& handle); + /** + * @brief Initialize the trackers along the interface for the communication. + * @tparam FORWARD If true we send in the forward direction. + * @tparam DataHandle DataHandle The type of the data handle. + * @param handle The handle describing the data and responsible for gather + * and scatter operations. + * @param[out] send_trackers The trackers for the sending side. + * @param[out] recv_trackers The trackers for the receiving side. + */ + template + void setupInterfaceTrackers(DataHandle& handle, + std::vector& send_trackers, + std::vector& recv_trackers); + /** + * @brief Communicate data with a fixed amount of data per entry. + * @tparam FORWARD If true we send in the forward direction. + * @tparam DataHandle DataHandle The type of the data handle. + * @param handle The handle describing the data and responsible for gather + * and scatter operations. + */ + template + void communicateFixedSize(DataHandle& handle); + /** + * @brief Communicate data with a variable amount of data per entry. + * @tparam FORWARD If true we send in the forward direction. + * @tparam DataHandle DataHandle The type of the data handle. + * @param handle The handle describing the data and responsible for gather + * and scatter operations. + */ + template + void communicateVariableSize(DataHandle& handle); + /** + * @brief The maximum size if the buffers used for gather and scatter. + * + * @note If this process has n neighbours, then a maximum of 2n buffers of this size + * is allocate. Memory needed will be n*sizeof(std::size_t)+n*sizeof(Datahandle::DataType) + */ + std::size_t maxBufferSize_; + /** + * @brief description of the interface. + * + * This is a map of the neighboring process number to a pair of local index lists. + * The first is a list of indices to gather data for sending from and the second is a list of + * indices to scatter received data to during forward. + */ + const InterfaceMap* interface_; + /** + * @brief The communicator. + * + * This is a cloned communicator to ensure there are no interferences. + */ + MPI_Comm communicator_; +}; + +/** @} */ +namespace +{ +/** + * @brief A data handle for comunicating the sizes of variable sized data. + */ +template +class SizeDataHandle +{ +public: + typedef std::size_t DataType; + + SizeDataHandle(DataHandle& data, + std::vector& trackers) + : data_(data), trackers_(trackers), index_() + {} + bool fixedSize() + { + return true; + } + std::size_t size([[maybe_unused]] std::size_t i) + { + return 1; + } + template + void gather(B& buf, int i) + { + buf.write(data_.size(i)); + } + void setReceivingIndex(std::size_t i) + { + index_=i; + } + std::size_t* getSizesPointer() + { + return trackers_[index_].getSizesPointer(); + } + +private: + DataHandle& data_; + std::vector& trackers_; + int index_; +}; + +template +void setReceivingIndex(T&, int) +{} + +template +void setReceivingIndex(SizeDataHandle& t, int i) +{ + t.setReceivingIndex(i); +} + + +/** + * @brief Template meta program for choosing then send or receive interface + * information based on the direction. + * @tparam FORWARD If true the communication happens in the forward direction. + */ +template +struct InterfaceInformationChooser +{ + /** + * @brief Get the interface information for the sending side. + */ + static const InterfaceInformation& + getSend(const std::pair& info) + { + return info.first; + } + + /** + * @brief Get the interface information for the receiving side. + */ + static const InterfaceInformation& + getReceive(const std::pair& info) + { + return info.second; + } +}; + +template<> +struct InterfaceInformationChooser +{ + static const InterfaceInformation& + getSend(const std::pair& info) + { + return info.second; + } + + static const InterfaceInformation& + getReceive(const std::pair& info) + { + return info.first; + } +}; + +/** + * @brief A functor that packs entries into the message buffer. + * @tparam DataHandle The type of the data handle that describes + * the communicated data. + */ +template +struct PackEntries +{ + + int operator()(DataHandle& handle, InterfaceTracker& tracker, + MessageBuffer& buffer, + [[maybe_unused]] int i) const + { + return operator()(handle,tracker,buffer); + } + + /** + * @brief packs data. + * @param handle The handle describing the data and the gather and scatter operations. + * @param tracker The tracker of the interface to tell us where we are. + * @param buffer The buffer to use for packing. + * @return The number data entries that we packed. + */ + int operator()(DataHandle& handle, InterfaceTracker& tracker, + MessageBuffer& buffer) const + { + if(tracker.fixedSize) // fixed size if variable is >0! + { + + std::size_t noIndices=std::min(buffer.size()/tracker.fixedSize, tracker.indicesLeft()); + for(std::size_t i=0; i< noIndices; ++i) + { + handle.gather(buffer, tracker.index()); + tracker.moveToNextIndex(); + } + return noIndices*tracker.fixedSize; + } + else + { + int packed=0; + tracker.skipZeroIndices(); + while(!tracker.finished()) + if(buffer.hasSpaceForItems(handle.size(tracker.index()))) + { + handle.gather(buffer, tracker.index()); + packed+=handle.size(tracker.index()); + tracker.moveToNextIndex(); + } + else + break; + return packed; + } + } +}; + +/** + * @brief A functor that unpacks entries from the message buffer. + * @tparam DataHandle The type of the data handle that describes + * the communicated data. + */ +template +struct UnpackEntries{ + + /** + * @brief packs data. + * @param handle The handle describing the data and the gather and scatter operations. + * @param tracker The tracker of the interface to tell us where we are. + * @param buffer The buffer to use for packing. + * @return The number data entries that we packed. + */ + bool operator()(DataHandle& handle, InterfaceTracker& tracker, + MessageBuffer& buffer, + int count=0) + { + if(tracker.fixedSize) // fixed size if variable is >0! + { + std::size_t noIndices=std::min(buffer.size()/tracker.fixedSize, tracker.indicesLeft()); + + for(std::size_t i=0; i< noIndices; ++i) + { + handle.scatter(buffer, tracker.index(), tracker.fixedSize); + tracker.moveToNextIndex(); + } + return tracker.finished(); + } + else + { + assert(count); + for(int unpacked=0;unpacked +struct UnpackSizeEntries{ + + /** + * @brief packs data. + * @param handle The handle describing the data and the gather and scatter operations. + * @param tracker The tracker of the interface to tell us where we are. + * @param buffer The buffer to use for packing. + * @return The number data entries that we packed. + */ + bool operator()(SizeDataHandle& handle, InterfaceTracker& tracker, + MessageBuffer::DataType>& buffer) const + { + std::size_t noIndices=std::min(buffer.size(), tracker.indicesLeft()); + std::copy(static_cast(buffer), static_cast(buffer)+noIndices, + handle.getSizesPointer()+tracker.offset()); + tracker.increment(noIndices); + return noIndices; + } + bool operator()(SizeDataHandle& handle, InterfaceTracker& tracker, + MessageBuffer::DataType>& buffer, int) const + { + return operator()(handle,tracker,buffer); + } +}; + +/** + * @brief Sends the size in case of communicating a fixed amount of data per entry. + * @param[in] send_trackers The trackers for the sending side. + * @param[out] send_requests The request for the asynchronous send operations. + * @param[in] recv_trackers The trackers for the receiving side. + * @param[out] recv_requests The request for the asynchronous receive operations. + */ +void sendFixedSize(std::vector& send_trackers, + std::vector& send_requests, + std::vector& recv_trackers, + std::vector& recv_requests, + MPI_Comm communicator) +{ + typedef std::vector::iterator TIter; + std::vector::iterator mIter=recv_requests.begin(); + + for(TIter iter=recv_trackers.begin(), end=recv_trackers.end(); iter!=end; + ++iter, ++mIter) + { + MPI_Irecv(&(iter->fixedSize), 1, MPITraits::getType(), + iter->rank(), 933881, communicator, &(*mIter)); + } + + // Send our size to all neighbours using non-blocking synchronous communication. + std::vector::iterator mIter1=send_requests.begin(); + for(TIter iter=send_trackers.begin(), end=send_trackers.end(); + iter!=end; + ++iter, ++mIter1) + { + MPI_Issend(&(iter->fixedSize), 1, MPITraits::getType(), + iter->rank(), 933881, communicator, &(*mIter1)); + } +} + + +/** + * @brief Functor for setting up send requests. + * @tparam DataHandle The type of the data handle for describing the data. + */ +template +struct SetupSendRequest{ + void operator()(DataHandle& handle, + InterfaceTracker& tracker, + MessageBuffer& buffer, + MPI_Request& request, + MPI_Comm comm) const + { + buffer.reset(); + int size=PackEntries()(handle, tracker, buffer); + // Skip indices of zero size. + while(!tracker.finished() && !handle.size(tracker.index())) + tracker.moveToNextIndex(); + if(size) + MPI_Issend(buffer, size, MPITraits::getType(), + tracker.rank(), 933399, comm, &request); + } +}; + + +/** + * @brief Functor for setting up receive requests. + * @tparam DataHandle The type of the data handle for describing the data. + */ +template +struct SetupRecvRequest{ + void operator()(DataHandle& /*handle*/, + InterfaceTracker& tracker, + MessageBuffer& buffer, + MPI_Request& request, + MPI_Comm comm) const + { + buffer.reset(); + if(tracker.indicesLeft()) + MPI_Irecv(buffer, buffer.size(), MPITraits::getType(), + tracker.rank(), 933399, comm, &request); + } +}; + +/** + * @brief A functor that does nothing. + */ +template +struct NullPackUnpackFunctor +{ + int operator()(DataHandle&, InterfaceTracker&, + MessageBuffer&, int) + { + return 0; + } + int operator()(DataHandle&, InterfaceTracker&, + MessageBuffer&) + { + return 0; + } +}; + +/** + * @brief Check whether some of the requests finished and continue send/receive operation. + * @tparam DataHandle The type of the data handle describing the data. + * @tparam BufferFunctor A functor that packs or unpacks data from the buffer. + * E.g. NullPackUnpackFunctor. + * @tparam CommunicationFuntor A functor responsible for continuing the communication. + * @param handle The data handle describing the data. + * @param trackers The trackers indicating the current position in the communication. + * @param requests The requests to test whether they finished. + * @param requests2 The requests to use for setting up the continuing communication. Might + * be the same as requests. + * @param comm The MPI communicator to use. + * @param buffer_func The functor that does the packing or unpacking of the data. + */ +template +std::size_t checkAndContinue(DataHandle& handle, + std::vector& trackers, + std::vector& requests, + std::vector& requests2, + std::vector >& buffers, + MPI_Comm comm, + BufferFunctor buffer_func, + CommunicationFunctor comm_func, + bool valid=true, + bool getCount=false) +{ + std::size_t size=requests.size(); + std::vector statuses(size); + int no_completed; + std::vector indices(size, -1); // the indices for which the communication finished. + + MPI_Testsome(size, &(requests[0]), &no_completed, &(indices[0]), &(statuses[0])); + indices.resize(no_completed); + for(std::vector::iterator index=indices.begin(), end=indices.end(); + index!=end; ++index) + { + InterfaceTracker& tracker=trackers[*index]; + setReceivingIndex(handle, *index); + if(getCount) + { + // Get the number of entries received + int count; + MPI_Get_count(&(statuses[index-indices.begin()]), + MPITraits::getType(), + &count); + // Communication completed, we can reuse the buffers, e.g. unpack or repack + buffer_func(handle, tracker, buffers[*index], count); + }else + buffer_func(handle, tracker, buffers[*index]); + tracker.skipZeroIndices(); + if(!tracker.finished()){ + // Maybe start another communication. + comm_func(handle, tracker, buffers[*index], requests2[*index], comm); + tracker.skipZeroIndices(); + if(valid) + --no_completed; // communication not finished, decrement counter for finished ones. + } + } + return no_completed; + +} + +/** + * @brief Receive the size per data entry and set up requests for receiving the data. + * @tparam DataHandle The type of the data handle. + * @param trackers The trackers indicating the indices where we send and from which rank. + * @param size_requests The requests for receiving the size. + * @param data_requests The requests for sending the data. + * @param buffers The buffers to use for sending. + * @param comm The mpi communicator to use. + */ +template +std::size_t receiveSizeAndSetupReceive(DataHandle& handle, + std::vector& trackers, + std::vector& size_requests, + std::vector& data_requests, + std::vector >& buffers, + MPI_Comm comm) +{ + return checkAndContinue(handle, trackers, size_requests, data_requests, buffers, comm, + NullPackUnpackFunctor(), SetupRecvRequest(), false); +} + +/** + * @brief Check whether send request completed and continue sending if necessary. + * @tparam DataHandle The type of the data handle. + * @param trackers The trackers indicating the indices where we send and from which rank. + * @param requests The requests for the asynchronous communication. + * @param buffers The buffers to use for sending. + * @param comm The mpi communicator to use. + */ +template +std::size_t checkSendAndContinueSending(DataHandle& handle, + std::vector& trackers, + std::vector& requests, + std::vector >& buffers, + MPI_Comm comm) +{ + return checkAndContinue(handle, trackers, requests, requests, buffers, comm, + NullPackUnpackFunctor(), SetupSendRequest()); +} + +/** + * @brief Check whether receive request completed and continue receiving if necessary. + * @tparam DataHandle The type of the data handle. + * @param trackers The trackers indicating the indices where we receive and from which rank. + * @param requests The requests for the asynchronous communication. + * @param buffers The buffers to use for receiving. + * @param comm The mpi communicator to use. + */ +template +std::size_t checkReceiveAndContinueReceiving(DataHandle& handle, + std::vector& trackers, + std::vector& requests, + std::vector >& buffers, + MPI_Comm comm) +{ + return checkAndContinue(handle, trackers, requests, requests, buffers, comm, + UnpackEntries(), SetupRecvRequest(), + true, !Impl::callFixedSize(handle)); +} + + +bool validRecvRequests(const std::vector reqs) +{ + for(std::vector::const_iterator i=reqs.begin(), end=reqs.end(); + i!=end; ++i) + if(*i!=MPI_REQUEST_NULL) + return true; + return false; +} + +/** + * @brief Sets up all the send requests for the data. + * @tparam DataHandle The type of the data handle. + * @tparam Functor The type of the functor to set up the request. + * @param handle The data handle describing the data. + * @param trackers The trackers for the communication interfaces. + * @param buffers The buffers for the comunication. One for each neighbour. + * @param requests The send requests for each neighbour. + * @param setupFunctor The functor responsible for setting up the request. + */ +template +std::size_t setupRequests(DataHandle& handle, + std::vector& trackers, + std::vector >& buffers, + std::vector& requests, + const Functor& setupFunctor, + MPI_Comm communicator) +{ + typedef typename std::vector::iterator TIter; + typename std::vector >::iterator + biter=buffers.begin(); + typename std::vector::iterator riter=requests.begin(); + std::size_t complete=0; + for(TIter titer=trackers.begin(), end=trackers.end(); titer!=end; ++titer, ++biter, ++riter) + { + setupFunctor(handle, *titer, *biter, *riter, communicator); + complete+=titer->finished(); + } + return complete; +} +} // end unnamed namespace + +template +template +void VariableSizeCommunicator::setupInterfaceTrackers(DataHandle& handle, + std::vector& send_trackers, + std::vector& recv_trackers) +{ + if(interface_->size()==0) + return; + send_trackers.reserve(interface_->size()); + recv_trackers.reserve(interface_->size()); + + int fixedsize=0; + if(Impl::callFixedSize(handle)) + ++fixedsize; + + + typedef typename InterfaceMap::const_iterator IIter; + for(IIter inf=interface_->begin(), end=interface_->end(); inf!=end; ++inf) + { + + if(Impl::callFixedSize(handle) && InterfaceInformationChooser::getSend(inf->second).size()) + fixedsize=handle.size(InterfaceInformationChooser::getSend(inf->second)[0]); + assert(!Impl::callFixedSize(handle)||fixedsize>0); + send_trackers.push_back(InterfaceTracker(inf->first, + InterfaceInformationChooser::getSend(inf->second), fixedsize)); + recv_trackers.push_back(InterfaceTracker(inf->first, + InterfaceInformationChooser::getReceive(inf->second), fixedsize, fixedsize==0)); + } +} + +template +template +void VariableSizeCommunicator::communicateFixedSize(DataHandle& handle) +{ + std::vector size_send_req(interface_->size()); + std::vector size_recv_req(interface_->size()); + + std::vector send_trackers; + std::vector recv_trackers; + setupInterfaceTrackers(handle,send_trackers, recv_trackers); + sendFixedSize(send_trackers, size_send_req, recv_trackers, size_recv_req, communicator_); + + std::vector data_send_req(interface_->size(), MPI_REQUEST_NULL); + std::vector data_recv_req(interface_->size(), MPI_REQUEST_NULL); + typedef typename DataHandle::DataType DataType; + std::vector > send_buffers(interface_->size(), MessageBuffer(maxBufferSize_)), + recv_buffers(interface_->size(), MessageBuffer(maxBufferSize_)); + + + setupRequests(handle, send_trackers, send_buffers, data_send_req, + SetupSendRequest(), communicator_); + + std::size_t no_size_to_recv, no_to_send, no_to_recv, old_size; + no_size_to_recv = no_to_send = no_to_recv = old_size = interface_->size(); + + // Skip empty interfaces. + typedef typename std::vector::const_iterator Iter; + for(Iter i=recv_trackers.begin(), end=recv_trackers.end(); i!=end; ++i) + if(i->empty()) + --no_to_recv; + for(Iter i=send_trackers.begin(), end=send_trackers.end(); i!=end; ++i) + if(i->empty()) + --no_to_send; + + while(no_size_to_recv+no_to_send+no_to_recv) + { + // Receive the fixedsize and setup receives accordingly + if(no_size_to_recv) + no_size_to_recv -= receiveSizeAndSetupReceive(handle,recv_trackers, size_recv_req, + data_recv_req, recv_buffers, + communicator_); + + // Check send completion and initiate other necessary sends + if(no_to_send) + no_to_send -= checkSendAndContinueSending(handle, send_trackers, data_send_req, + send_buffers, communicator_); + if(validRecvRequests(data_recv_req)) + // Receive data and setup new unblocking receives if necessary + no_to_recv -= checkReceiveAndContinueReceiving(handle, recv_trackers, data_recv_req, + recv_buffers, communicator_); + } + + // Wait for completion of sending the size. + //std::vector statuses(interface_->size(), MPI_STATUSES_IGNORE); + MPI_Waitall(size_send_req.size(), &(size_send_req[0]), MPI_STATUSES_IGNORE); + +} + +template +template +void VariableSizeCommunicator::communicateSizes(DataHandle& handle, + std::vector& data_recv_trackers) +{ + std::vector send_trackers; + std::vector recv_trackers; + std::size_t size = interface_->size(); + std::vector send_requests(size, MPI_REQUEST_NULL); + std::vector recv_requests(size, MPI_REQUEST_NULL); + std::vector > + send_buffers(size, MessageBuffer(maxBufferSize_)), + recv_buffers(size, MessageBuffer(maxBufferSize_)); + SizeDataHandle size_handle(handle,data_recv_trackers); + setupInterfaceTrackers(size_handle,send_trackers, recv_trackers); + setupRequests(size_handle, send_trackers, send_buffers, send_requests, + SetupSendRequest >(), communicator_); + setupRequests(size_handle, recv_trackers, recv_buffers, recv_requests, + SetupRecvRequest >(), communicator_); + + // Count valid requests that we have to wait for. + auto valid_req_func = + [](const MPI_Request& req) { return req != MPI_REQUEST_NULL; }; + + auto size_to_send = std::count_if(send_requests.begin(), send_requests.end(), + valid_req_func); + auto size_to_recv = std::count_if(recv_requests.begin(), recv_requests.end(), + valid_req_func); + + while(size_to_send+size_to_recv) + { + if(size_to_send) + size_to_send -= + checkSendAndContinueSending(size_handle, send_trackers, send_requests, + send_buffers, communicator_); + if(size_to_recv) + // Could have done this using checkSendAndContinueSending + // But the call below is more efficient as UnpackSizeEntries + // uses std::copy. + size_to_recv -= + checkAndContinue(size_handle, recv_trackers, recv_requests, recv_requests, + recv_buffers, communicator_, UnpackSizeEntries(), + SetupRecvRequest >()); + } +} + +template +template +void VariableSizeCommunicator::communicateVariableSize(DataHandle& handle) +{ + + std::vector send_trackers; + std::vector recv_trackers; + setupInterfaceTrackers(handle, send_trackers, recv_trackers); + + std::vector send_requests(interface_->size(), MPI_REQUEST_NULL); + std::vector recv_requests(interface_->size(), MPI_REQUEST_NULL); + typedef typename DataHandle::DataType DataType; + std::vector > + send_buffers(interface_->size(), MessageBuffer(maxBufferSize_)), + recv_buffers(interface_->size(), MessageBuffer(maxBufferSize_)); + + communicateSizes(handle, recv_trackers); + // Setup requests for sending and receiving. + setupRequests(handle, send_trackers, send_buffers, send_requests, + SetupSendRequest(), communicator_); + setupRequests(handle, recv_trackers, recv_buffers, recv_requests, + SetupRecvRequest(), communicator_); + + // Determine number of valid requests. + auto valid_req_func = + [](const MPI_Request& req) { return req != MPI_REQUEST_NULL;}; + + auto no_to_send = std::count_if(send_requests.begin(), send_requests.end(), + valid_req_func); + auto no_to_recv = std::count_if(recv_requests.begin(), recv_requests.end(), + valid_req_func); + while(no_to_send+no_to_recv) + { + // Check send completion and initiate other necessary sends + if(no_to_send) + no_to_send -= checkSendAndContinueSending(handle, send_trackers, send_requests, + send_buffers, communicator_); + if(no_to_recv) + // Receive data and setup new unblocking receives if necessary + no_to_recv -= checkReceiveAndContinueReceiving(handle, recv_trackers, recv_requests, + recv_buffers, communicator_); + } +} + +template +template +void VariableSizeCommunicator::communicate(DataHandle& handle) +{ + if( interface_->size() == 0) + // Simply return as otherwise we will index an empty container + // either for MPI_Wait_all or MPI_Test_some. + return; + + if(Impl::callFixedSize(handle)) + communicateFixedSize(handle); + else + communicateVariableSize(handle); +} +} // end namespace Dune + +#endif // HAVE_MPI + +#endif diff --git a/dune/common/parameterizedobject.hh b/dune/common/parameterizedobject.hh new file mode 100644 index 0000000..bc55895 --- /dev/null +++ b/dune/common/parameterizedobject.hh @@ -0,0 +1,192 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +#ifndef DUNE_COMMON_PARAMETERIZEDOBJECT_HH +#define DUNE_COMMON_PARAMETERIZEDOBJECT_HH + +#include +#include +#include + +#include +#include + +namespace Dune { + +/** + * @brief A factory class for parameterized objects. + * + * It allows the construction of objects adhering to a certain interface that + * might be constructed quite differently for one another. + * + * The Signature parameter defined the "virtual" constructor signature + * in the form of Interface(Args...), where Interface is the type of + * the (abstract) interface class and Args... is the set of + * constrcutor parameters. + * + * Each type constructed by this factory is identified by a different key. This class + * allows for easy registration of type with new keys. + * + * @tparam Signature Signature of the "virtual" constructor call in the form for Interface(Args...). For default constructors one can omit the ()-brackets. + * @tparam KeyT The type of the objects that are used as keys in the lookup [DEFAULT: std::string]. + */ +template +class ParameterizedObjectFactory; + +template +class ParameterizedObjectFactory +{ + public: + + /** @brief The typ of the keys. */ + typedef KeyT Key; + + /** @brief The type of objects created by the factory. */ + using Type = TypeT; + + protected: + + using Creator = std::function; + + template + static constexpr auto has_proper_signature(Dune::PriorityTag<1>) + -> decltype( std::declval()(std::declval()...), std::true_type()) + { + return {}; + } + + template + static constexpr std::false_type has_proper_signature(Dune::PriorityTag<0>) + { + return {}; + } + + public: + + /** + * @brief Creates an object identified by a key from given parameters + * + * @param key The key the object is registered with @see define. + * @param args The parameters used for the construction. + * @return The object wrapped as Type + */ + Type create(Key const& key, Args ... args) const { + typename Registry::const_iterator i = registry_.find(key); + if (i == registry_.end()) { + DUNE_THROW(Dune::InvalidStateException, + "ParametrizedObjectFactory: key ``" << + key << "'' not registered"); + } + else return i->second(args...); + } + + /** + * @brief Registers a new type with a key. + * + * After registration objects of this type can be constructed with the + * specified key using a matching default creation function. If Type + * is a unique_ptr or shared_ptr, the object is created via make_unique + * or make_shared, respectively. Otherwise a constructor of Impl + * is called. + * + * @tparam Impl The type of objects to create. + * + * @param key The key associated with this type. + */ + template + void define(Key const& key) + { + registry_[key] = DefaultCreator(); + } + + /** + * @brief Registers a new creator with a key. + * + * After registration objects can be constructed using + * the given creator function. + * + * @tparam F Type of creator function. This must be callable with Args... . + * + * @param key The key associated with this type. + * @param f Function for creation of objects of type Impl + * + * \todo Replace has_proper_signature by concept check + */ + template(PriorityTag<42>()), int>::type = 0> + void define(Key const& key, F&& f) + { + registry_[key] = f; + } + + /** + * @brief Registers a new type with a key. + * + * After registration objects of this type can be created. + * This method will store a copy of the given object and + * create will hand out a copy to this. + * + * @tparam Impl The type of objects to create. + * + * @param key The key associated with this type. + * @param t reference object, "create" will call the copy-constructor + * + * note, this does not work fundamental types + */ + template::value + and not std::is_convertible::value, + int>::type = 0> + void define(Key const& key, Impl&& t) + { + registry_[key] = [=](Args...) { return t;}; + } + + bool contains(Key const& key) const + { + return registry_.count(key); + } + + private: + + template + struct Tag{}; + + template + struct DefaultCreator + { + template + Type operator()(T&&... args) const + { + return DefaultCreator::create(Tag(), PriorityTag<42>(), std::forward(args)...); + } + + template + static Type create(Tag, PriorityTag<1>, T&& ... args) { + return Impl(std::forward(args)...); + } + + template + static Type create(Tag>, PriorityTag<2>, T&& ... args) { + return std::make_unique(std::forward(args)...); + } + + template + static Type create(Tag>, PriorityTag<3>, T&& ... args) { + return std::make_shared(std::forward(args)...); + } + + }; + + typedef std::map Registry; + Registry registry_; +}; + + + +} // end namespace Dune + +#endif // DUNE_COMMON_PARAMETERIZEDOBJECT_HH diff --git a/dune/common/parametertree.cc b/dune/common/parametertree.cc new file mode 100644 index 0000000..25ee005 --- /dev/null +++ b/dune/common/parametertree.cc @@ -0,0 +1,241 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace Dune; + +ParameterTree::ParameterTree() +{} + +const Dune::ParameterTree Dune::ParameterTree::empty_; + +void ParameterTree::report(std::ostream& stream, const std::string& prefix) const +{ + typedef std::map::const_iterator ValueIt; + ValueIt vit = values_.begin(); + ValueIt vend = values_.end(); + + for(; vit!=vend; ++vit) + stream << vit->first << " = \"" << vit->second << "\"" << std::endl; + + typedef std::map::const_iterator SubIt; + SubIt sit = subs_.begin(); + SubIt send = subs_.end(); + for(; sit!=send; ++sit) + { + stream << "[ " << prefix + prefix_ + sit->first << " ]" << std::endl; + (sit->second).report(stream, prefix); + } +} + +bool ParameterTree::hasKey(const std::string& key) const +{ + std::string::size_type dot = key.find("."); + + if (dot != std::string::npos) + { + std::string prefix = key.substr(0,dot); + if (subs_.count(prefix) == 0) + return false; + + if (values_.count(prefix) > 0) + DUNE_THROW(RangeError,"key " << prefix << " occurs as value and as subtree"); + + const ParameterTree& s = sub(prefix); + return s.hasKey(key.substr(dot+1)); + } + else + if (values_.count(key) != 0) + { + if (subs_.count(key) > 0) + DUNE_THROW(RangeError,"key " << key << " occurs as value and as subtree"); + return true; + } + else + return false; + +} + +bool ParameterTree::hasSub(const std::string& key) const +{ + std::string::size_type dot = key.find("."); + + if (dot != std::string::npos) + { + std::string prefix = key.substr(0,dot); + if (subs_.count(prefix) == 0) + return false; + + if (values_.count(prefix) > 0) + DUNE_THROW(RangeError,"key " << prefix << " occurs as value and as subtree"); + + const ParameterTree& s = sub(prefix); + return s.hasSub(key.substr(dot+1)); + } + else + if (subs_.count(key) != 0) + { + if (values_.count(key) > 0) + DUNE_THROW(RangeError,"key " << key << " occurs as value and as subtree"); + return true; + } + else + return false; +} + +ParameterTree& ParameterTree::sub(const std::string& key) +{ + std::string::size_type dot = key.find("."); + + if (dot != std::string::npos) + { + ParameterTree& s = sub(key.substr(0,dot)); + return s.sub(key.substr(dot+1)); + } + else + { + if (values_.count(key) > 0) + DUNE_THROW(RangeError,"key " << key << " occurs as value and as subtree"); + if (subs_.count(key) == 0) + subKeys_.push_back(key.substr(0,dot)); + subs_[key].prefix_ = prefix_ + key + "."; + return subs_[key]; + } +} + +const ParameterTree& ParameterTree::sub(const std::string& key, bool fail_if_missing) const +{ + std::string::size_type dot = key.find("."); + + if (dot != std::string::npos) + { + const ParameterTree& s = sub(key.substr(0,dot)); + return s.sub(key.substr(dot+1),fail_if_missing); + } + else + { + if (values_.count(key) > 0) + DUNE_THROW(RangeError,"key " << key << " occurs as value and as subtree"); + if (subs_.count(key) == 0) + { + if (fail_if_missing) + { + DUNE_THROW(Dune::RangeError, "SubTree '" << key + << "' not found in ParameterTree (prefix " + prefix_ + ")"); + } + else + return empty_; + } + return subs_.find(key)->second; + } +} + +std::string& ParameterTree::operator[] (const std::string& key) +{ + std::string::size_type dot = key.find("."); + + if (dot != std::string::npos) + { + ParameterTree& s = sub(key.substr(0,dot)); + return s[key.substr(dot+1)]; + } + else + { + if (! hasKey(key)) + valueKeys_.push_back(key); + return values_[key]; + } +} + +const std::string& ParameterTree::operator[] (const std::string& key) const +{ + std::string::size_type dot = key.find("."); + + if (dot != std::string::npos) + { + const ParameterTree& s = sub(key.substr(0,dot)); + return s[key.substr(dot+1)]; + } + else + { + if (! hasKey(key)) + DUNE_THROW(Dune::RangeError, "Key '" << key + << "' not found in ParameterTree (prefix " + prefix_ + ")"); + return values_.find(key)->second; + } +} + +std::string ParameterTree::get(const std::string& key, const std::string& defaultValue) const +{ + if (hasKey(key)) + return (*this)[key]; + else + return defaultValue; +} + +std::string ParameterTree::get(const std::string& key, const char* defaultValue) const +{ + if (hasKey(key)) + return (*this)[key]; + else + return defaultValue; +} + +std::string ParameterTree::ltrim(const std::string& s) +{ + std::size_t firstNonWS = s.find_first_not_of(" \t\n\r"); + + if (firstNonWS!=std::string::npos) + return s.substr(firstNonWS); + return std::string(); +} + +std::string ParameterTree::rtrim(const std::string& s) +{ + std::size_t lastNonWS = s.find_last_not_of(" \t\n\r"); + + if (lastNonWS!=std::string::npos) + return s.substr(0, lastNonWS+1); + return std::string(); +} + +std::vector ParameterTree::split(const std::string & s) { + std::vector substrings; + std::size_t front = 0, back = 0, size = 0; + + while (front != std::string::npos) + { + // find beginning of substring + front = s.find_first_not_of(" \t\n\r", back); + back = s.find_first_of(" \t\n\r", front); + size = back - front; + if (size > 0) + substrings.push_back(s.substr(front, size)); + } + return substrings; +} + +const ParameterTree::KeyVector& ParameterTree::getValueKeys() const +{ + return valueKeys_; +} + +const ParameterTree::KeyVector& ParameterTree::getSubKeys() const +{ + return subKeys_; +} diff --git a/dune/common/parametertree.hh b/dune/common/parametertree.hh new file mode 100644 index 0000000..2b9db5a --- /dev/null +++ b/dune/common/parametertree.hh @@ -0,0 +1,358 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_PARAMETERTREE_HH +#define DUNE_PARAMETERTREE_HH + +/** \file + * \brief A hierarchical structure of string parameters + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Dune { + + /** \brief Hierarchical structure of string parameters + * \ingroup Common + */ + class ParameterTree + { + // class providing a single static parse() function, used by the + // generic get() method + template + struct Parser; + + public: + + /** \brief storage for key lists + */ + typedef std::vector KeyVector; + + /** \brief Create new empty ParameterTree + */ + ParameterTree(); + + + /** \brief test for key + * + * Tests whether given key exists. + * + * \param key key name + * \return true if key exists in structure, otherwise false + */ + bool hasKey(const std::string& key) const; + + + /** \brief test for substructure + * + * Tests whether given substructure exists. + * + * \param sub substructure name + * \return true if substructure exists in structure, otherwise false + */ + bool hasSub(const std::string& sub) const; + + + /** \brief get value reference for key + * + * Returns reference to value for given key name. + * This creates the key, if not existent. + * + * \param key key name + * \return reference to corresponding value + */ + std::string& operator[] (const std::string& key); + + + /** \brief get value reference for key + * + * Returns reference to value for given key name. + * This creates the key, if not existent. + * + * \param key key name + * \return reference to corresponding value + * \throw Dune::RangeError if key is not found + */ + const std::string& operator[] (const std::string& key) const; + + + /** \brief print distinct substructure to stream + * + * Prints all entries with given prefix. + * + * \param stream Stream to print to + * \param prefix for key and substructure names + */ + void report(std::ostream& stream = std::cout, + const std::string& prefix = "") const; + + + /** \brief get substructure by name + * + * \param sub substructure name + * \return reference to substructure + */ + ParameterTree& sub(const std::string& sub); + + + /** \brief get const substructure by name + * + * \param sub substructure name + * \param fail_if_missing if true, throw an error if substructure is missing + * \return reference to substructure + */ + const ParameterTree& sub(const std::string& sub, bool fail_if_missing = false) const; + + + /** \brief get value as string + * + * Returns pure string value for given key. + * + * \param key key name + * \param defaultValue default if key does not exist + * \return value as string + */ + std::string get(const std::string& key, const std::string& defaultValue) const; + + /** \brief get value as string + * + * Returns pure string value for given key. + * + * \todo This is a hack so get("my_key", "xyz") compiles + * (without this method "xyz" resolves to bool instead of std::string) + * \param key key name + * \param defaultValue default if key does not exist + * \return value as string + */ + std::string get(const std::string& key, const char* defaultValue) const; + + + /** \brief get value converted to a certain type + * + * Returns value as type T for given key. + * + * \tparam T type of returned value. + * \param key key name + * \param defaultValue default if key does not exist + * \return value converted to T + */ + template + T get(const std::string& key, const T& defaultValue) const { + if(hasKey(key)) + return get(key); + else + return defaultValue; + } + + /** \brief Get value + * + * \tparam T Type of the value + * \param key Key name + * \throws RangeError if key does not exist + * \throws NotImplemented Type is not supported + * \return value as T + */ + template + T get(const std::string& key) const { + if(not hasKey(key)) + DUNE_THROW(Dune::RangeError, "Key '" << key + << "' not found in ParameterTree (prefix " + prefix_ + ")"); + try { + return Parser::parse((*this)[key]); + } + catch(const RangeError& e) { + // rethrow the error and add more information + DUNE_THROW(RangeError, "Cannot parse value \"" << (*this)[key] + << "\" for key \"" << prefix_ << "." << key << "\"" + << e.what()); + } + } + + /** \brief get value keys + * + * Returns a vector of all keys associated to (key,values) entries in + * order of appearance + * + * \return reference to entry vector + */ + const KeyVector& getValueKeys() const; + + + /** \brief get substructure keys + * + * Returns a vector of all keys associated to (key,substructure) entries + * in order of appearance + * + * \return reference to entry vector + */ + const KeyVector& getSubKeys() const; + + protected: + + static const ParameterTree empty_; + + std::string prefix_; + + KeyVector valueKeys_; + KeyVector subKeys_; + + std::map values_; + std::map subs_; + + static std::string ltrim(const std::string& s); + static std::string rtrim(const std::string& s); + static std::vector split(const std::string & s); + + // parse into a fixed-size range of iterators + template + static void parseRange(const std::string &str, + Iterator it, const Iterator &end) + { + typedef typename std::iterator_traits::value_type Value; + std::istringstream s(str); + // make sure we are in locale "C" + s.imbue(std::locale::classic()); + std::size_t n = 0; + for(; it != end; ++it, ++n) { + s >> *it; + if(!s) + DUNE_THROW(RangeError, "as a range of items of type " + << className() + << " (" << n << " items were extracted successfully)"); + } + Value dummy; + s >> dummy; + // now extraction should have failed, and eof should be set + if(not s.fail() or not s.eof()) + DUNE_THROW(RangeError, "as a range of " + << n << " items of type " + << className() << " (more items than the range can hold)"); + } + }; + + template + struct ParameterTree::Parser { + static T parse(const std::string& str) { + T val; + std::istringstream s(str); + // make sure we are in locale "C" + s.imbue(std::locale::classic()); + s >> val; + if(!s) + DUNE_THROW(RangeError, " as a " << className()); + char dummy; + s >> dummy; + // now extraction should have failed, and eof should be set + if ((! s.fail()) || (! s.eof())) + DUNE_THROW(RangeError, " as a " << className()); + return val; + } + }; + + // "How do I convert a string into a wstring in C++?" "Why, that very simple + // son. You just need a these hundred lines of code." + // Instead im gonna restrict myself to string with charT=char here + template + struct ParameterTree::Parser > { + static std::basic_string + parse(const std::string& str) { + std::string trimmed = ltrim(rtrim(str)); + return std::basic_string(trimmed.begin(), + trimmed.end()); + } + }; + + template<> + struct ParameterTree::Parser< bool > { + struct ToLower { + char operator()(char c) + { + return std::tolower(c, std::locale::classic()); + } + }; + + static bool + parse(const std::string& str) { + std::string ret = str; + + std::transform(ret.begin(), ret.end(), ret.begin(), ToLower()); + + if (ret == "yes" || ret == "true") + return true; + + if (ret == "no" || ret == "false") + return false; + + return (Parser::parse(ret) != 0); + } + }; + + template + struct ParameterTree::Parser > { + static FieldVector + parse(const std::string& str) { + FieldVector val; + parseRange(str, val.begin(), val.end()); + return val; + } + }; + + template + struct ParameterTree::Parser > { + static std::array + parse(const std::string& str) { + std::array val; + parseRange(str, val.begin(), val.end()); + return val; + } + }; + + template + struct ParameterTree::Parser > { + static std::bitset + parse(const std::string& str) { + std::bitset val; + std::vector sub = split(str); + if (sub.size() != n) + DUNE_THROW(RangeError, "as a bitset<" << n << "> " + << "because of unmatching size " << sub.size()); + for (std::size_t i=0; i::parse(sub[i]); + } + return val; + } + }; + + template + struct ParameterTree::Parser > { + static std::vector + parse(const std::string& str) { + std::vector sub = split(str); + std::vector vec; + for (unsigned int i=0; i::parse(sub[i]); + vec.push_back(val); + } + return vec; + } + }; + +} // end namespace Dune + +#endif // DUNE_PARAMETERTREE_HH diff --git a/dune/common/parametertreeparser.cc b/dune/common/parametertreeparser.cc new file mode 100644 index 0000000..184b900 --- /dev/null +++ b/dune/common/parametertreeparser.cc @@ -0,0 +1,261 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "parametertreeparser.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +std::string Dune::ParameterTreeParser::ltrim(const std::string& s) +{ + std::size_t firstNonWS = s.find_first_not_of(" \t\n\r"); + + if (firstNonWS!=std::string::npos) + return s.substr(firstNonWS); + return std::string(); +} + +std::string Dune::ParameterTreeParser::rtrim(const std::string& s) +{ + std::size_t lastNonWS = s.find_last_not_of(" \t\n\r"); + + if (lastNonWS!=std::string::npos) + return s.substr(0, lastNonWS+1); + return std::string(); +} + +Dune::ParameterTree Dune::ParameterTreeParser::readINITree(const std::string& file) +{ + std::ifstream in(file); + + if (!in) + DUNE_THROW(Dune::IOError, "Could not open configuration file " << file); + + Dune::ParameterTree pt; + readINITree(in, pt, "file '" + file + "'", true); + return pt; +} + +Dune::ParameterTree Dune::ParameterTreeParser::readINITree(std::istream& in) +{ + Dune::ParameterTree pt; + readINITree(in, pt, "stream", true); + return pt; +} + +void Dune::ParameterTreeParser::readINITree(std::string file, + ParameterTree& pt, + bool overwrite) +{ + std::ifstream in(file.c_str()); + + if (!in) + DUNE_THROW(Dune::IOError, "Could not open configuration file " << file); + + readINITree(in, pt, "file '" + file + "'", overwrite); +} + + +void Dune::ParameterTreeParser::readINITree(std::istream& in, + ParameterTree& pt, + bool overwrite) +{ + readINITree(in, pt, "stream", overwrite); +} + + +void Dune::ParameterTreeParser::readINITree(std::istream& in, + ParameterTree& pt, + const std::string srcname, + bool overwrite) +{ + std::string prefix; + std::set keysInFile; + while(!in.eof()) + { + std::string line; + getline(in, line); + line = ltrim(line); + if (line.size() == 0) + continue; + switch (line[0]) { + case '#' : + break; + case '[' : + { + size_t pos = line.find(']'); + if (pos != std::string::npos) { + prefix = rtrim(ltrim(line.substr(1, pos-1))); + if (prefix != "") + prefix += "."; + } + } + break; + default : + std::string::size_type comment = line.find("#"); + line = line.substr(0,comment); + std::string::size_type mid = line.find("="); + if (mid != std::string::npos) + { + std::string key = prefix+rtrim(ltrim(line.substr(0, mid))); + std::string value = ltrim(line.substr(mid+1)); + + if (value.length()>0) + { + // handle quoted strings + if ((value[0]=='\'') || (value[0]=='"')) + { + char quote = value[0]; + value=value.substr(1); + while (*(rtrim(value).rbegin())!=quote) + { + if (! in.eof()) + { + std::string l; + getline(in, l); + value = value+"\n"+l; + } + else + value = value+quote; + } + value = rtrim(value); + value = value.substr(0,value.length()-1); + } + else + value = rtrim(value); + } + + if (keysInFile.count(key) != 0) + DUNE_THROW(ParameterTreeParserError, "Key '" << key << + "' appears twice in " << srcname << " !"); + else + { + if(overwrite || ! pt.hasKey(key)) + pt[key] = value; + keysInFile.insert(key); + } + } + break; + } + } + +} + +void Dune::ParameterTreeParser::readOptions(int argc, char* argv [], + ParameterTree& pt) +{ + for(int i=1; i keywords, + unsigned int required, + bool allow_more, + bool overwrite, + std::vector help) +{ + std::string helpstr = generateHelpString(argv[0], keywords, required, help); + std::vector done(keywords.size(),false); + std::size_t current = 0; + + for (std::size_t i=1; i= done.size()) + DUNE_THROW(ParameterTreeParserError, + "superfluous unnamed parameter" << "\n" << helpstr); + // do we overwrite an existing entry? + if (!overwrite && pt[keywords[current]] != "") + DUNE_THROW(ParameterTreeParserError, + "parameter " << keywords[current] << " already specified" << "\n" << helpstr); + pt[keywords[current]] = opt; + done[current] = true; // mark key as stored + } + } + // check that we receive all required keywords + std::string missing = ""; + for (unsigned int i=0; i keywords, unsigned int required, std::vector help) +{ + static const char braces[] = "<>[]"; + std::string helpstr = ""; + helpstr = helpstr + "Usage: " + progname; + for (std::size_t i=0; i +#include +#include + +#include +#include + +namespace Dune { + + /** \brief report parser error while reading ParameterTree */ + class ParameterTreeParserError : public RangeError {}; + /** \brief exception thrown if the user wants to see help string + + this exception is only thrown if the command line parameters + contain an option --help or -h + */ + class HelpRequest : public Exception {}; + + /** \brief Parsers to set up a ParameterTree from various input sources + * \ingroup Common + * + */ + class ParameterTreeParser + { + + static std::string ltrim(const std::string& s); + static std::string rtrim(const std::string& s); + + public: + + /** @name Parsing methods for the INITree file format + * + * INITree files should look like this + * \verbatim + * # this file configures fruit colors in fruitsalad + * + * + * #these are no fruit but could also appear in fruit salad + * honeydewmelon = yellow + * watermelon = green + * + * fruit.tropicalfruit.orange = orange + * + * [fruit] + * strawberry = red + * pomegranate = red + * + * [fruit.pipfruit] + * apple = green/red/yellow + * pear = green + * + * [fruit.stonefruit] + * cherry = red + * plum = purple + * + * \endverbatim + * + * + * If a '[prefix]' statement appears all following entries use this prefix + * until the next '[prefix]' statement. Fruitsalads for example contain: + * \verbatim + * honeydewmelon = yellow + * fruit.tropicalfruit.orange = orange + * fruit.pipfruit.apple = green/red/yellow + * fruit.stonefruit.cherry = red + * \endverbatim + * + * All keys with a common 'prefix.' belong to the same substructure called + * 'prefix'. Leading and trailing spaces and tabs are removed from the + * values unless you use single or double quotes around them. Using single + * or double quotes you can also have multiline values. + */ + //@{ + + /** \brief parse C++ stream + * + * Parses C++ stream and build hierarchical config structure. + * + * \param in The stream to parse + * \param[out] pt The parameter tree to store the config structure. + * \param overwrite Whether to overwrite already existing values. + * If false, values in the stream will be ignored + * if the key is already present. + */ + static void readINITree(std::istream& in, ParameterTree& pt, + bool overwrite); + + /** \brief parse C++ stream + * + * Parses C++ stream and returns hierarchical config structure. + * + * \param in The stream to parse + */ + static Dune::ParameterTree readINITree(std::istream& in); + + + /** \brief parse C++ stream + * + * Parses C++ stream and build hierarchical config structure. + * + * \param in The stream to parse + * \param[out] pt The parameter tree to store the config structure. + * \param srcname Name of the configuration source for error + * messages, "stdin" or a filename. + * \param overwrite Whether to overwrite already existing values. + * If false, values in the stream will be ignored + * if the key is already present. + */ + static void readINITree(std::istream& in, ParameterTree& pt, + const std::string srcname = "stream", + bool overwrite = true); + + + /** \brief parse file + * + * Parses file with given name and build hierarchical config structure. + * + * \param file filename + * \param[out] pt The parameter tree to store the config structure. + * \param overwrite Whether to overwrite already existing values. + * If false, values in the stream will be ignored + * if the key is already present. + */ + static void readINITree(std::string file, ParameterTree& pt, bool overwrite = true); + + /** \brief parse file and return tree + * + * Parses file with given name and returns hierarchical config structure. + * + * \param file filename + */ + static Dune::ParameterTree readINITree(const std::string& file); + + //@} + + /** \brief parse command line options and build hierarchical ParameterTree structure + * + * The list of command line options is searched for pairs of the type -key value + * (note the hyphen in front of the key). + * For each such pair of options a key-value pair with the corresponding names + * is then created in the ParameterTree. + * + * \param argc arg count + * \param argv arg values + * \param[out] pt The parameter tree to store the config structure. + */ + static void readOptions(int argc, char* argv [], ParameterTree& pt); + + /** + * \brief read [named] command line options and build hierarchical ParameterTree structure + * + * Similar to pythons named options we expect the parameters in the + * ordering induced by keywords, but allow the user to pass named options + * in the form of --key=value. Optionally the user can pass an additional + * vector with help strings. + * + * \param argc arg count + * \param argv arg values + * \param[out] pt The parameter tree to store the config structure. + * \param keywords vector with keywords names + * \param required number of required options (the first n keywords are required, default is all are required) + * \param allow_more allow more options than these listed in keywords (default = true) + * \param overwrite allow to overwrite existing options (default = true) + * \param help vector containing help strings + */ + static void readNamedOptions(int argc, char* argv[], + ParameterTree& pt, + std::vector keywords, + unsigned int required = std::numeric_limits::max(), + bool allow_more = true, + bool overwrite = true, + std::vector help = std::vector()); + + private: + static std::string generateHelpString(std::string progname, std::vector keywords, unsigned int required, std::vector help); + }; + +} // end namespace Dune + +#endif // DUNE_PARAMETER_PARSER_HH diff --git a/dune/common/path.cc b/dune/common/path.cc new file mode 100644 index 0000000..c993a9a --- /dev/null +++ b/dune/common/path.cc @@ -0,0 +1,197 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include + +namespace Dune { + /** + * @addtogroup Path Filesystem Paths + * @ingroup Common + * @{ + */ + + /** + * @file + * @author Jö Fahlke + * @brief Utilites for handling filesystem paths + */ + + //! concatenate two paths + std::string concatPaths(const std::string& base, const std::string& p) { + if(p == "") return base; + if(p[0] == '/') return p; + if(base == "") return p; + if(hasSuffix(base, "/")) return base+p; + else return base+'/'+p; + } + + //! sanitize a path for further processing + std::string processPath(const std::string& p) { + std::string result = p; + std::string::size_type src, dst; + + // append a '/' to non-empty paths + if(result != "") result += '/'; + + // each path component now has a trailing '/' + + // collapse any occurrence of multiple '/' to a single '/' + dst = src = 0; + while(src < result.size()) { + result[dst] = result[src]; + ++src; + if(result[dst] == '/') + while(src < result.size() && result[src] == '/') + ++src; + ++dst; + } + result.resize(dst); + + // the path is now free of multiple '/' in a row + + // collapse any occurrence of "/./" to "/" + dst = src = 0; + while(src < result.size()) { + result[dst] = result[src]; + ++src; + if(result[dst] == '/') + while(src+1 < result.size() && result[src] == '.' && + result[src+1] == '/') + src+=2; + ++dst; + } + result.resize(dst); + + // there may be at most one leading "./". If so, remove it + if(hasPrefix(result, "./")) result.erase(0, 2); + + // the path is now free of "."-components + + // remove any "/../" pairs + src = 0; + while(true) { + src = result.find("/../", src); + if(src == std::string::npos) + break; + for(dst = src; dst > 0 && result[dst-1] != '/'; --dst) ; + if(result.substr(dst, src-dst) == "..") { + // don't remove "../../" + src += 3; + continue; + } + if(dst == src) + // special case: "" is the empty component. This means we + // found a leading "/../" in an absolute path, remove "/.." + result.erase(0, 3); + else { + // remove "/../". + result.erase(dst, src-dst+4); + src = dst; + // try to back up one character so we are at a '/' instead of at the + // beginning of a component + if(src > 0) --src; + } + } + + // absolute paths are now free of ".." components, and relative paths + // contain only leading ".." components + + return result; + } + + //! check whether the given path indicates that it is a directory + bool pathIndicatesDirectory(const std::string& p) { + if(p == "") return true; + if(p == ".") return true; + if(p == "..") return true; + if(hasSuffix(p, "/")) return true; + if(hasSuffix(p, "/.")) return true; + if(hasSuffix(p, "/..")) return true; + else return false; + } + + //! pretty print path + std::string prettyPath(const std::string& p, bool isDirectory) { + std::string result = processPath(p); + // current directory + if(result == "") return "."; + // root directory + if(result == "/") return result; + + // remove the trailing '/' for now + result.resize(result.size()-1); + + // if the result ends in "..", we don't need to append '/' to make clear + // it's a directory + if(result == ".." || hasSuffix(result, "/..")) + return result; + + // if it's a directory, tuck the '/' back on + if(isDirectory) result += '/'; + + return result; + } + + //! pretty print path + std::string prettyPath(const std::string& p) { + return prettyPath(p, pathIndicatesDirectory(p)); + } + + //! compute a relative path between two paths + std::string relativePath(const std::string& newbase, const std::string& p) + { + bool absbase = hasPrefix(newbase, "/"); + bool absp = hasPrefix(p, "/"); + if(absbase != absp) + DUNE_THROW(NotImplemented, "relativePath: paths must be either both " + "relative or both absolute: newbase=\"" << newbase << "\" " + "p=\"" << p << "\""); + + std::string mybase = processPath(newbase); + std::string myp = processPath(p); + + // remove as many matching leading components as possible + // determine prefix length + std::string::size_type preflen = 0; + while(preflen < mybase.size() && preflen < myp.size() && + mybase[preflen] == myp[preflen]) + ++preflen; + // backup to the beginning of the component + while(preflen > 0 && myp[preflen-1] != '/') + --preflen; + mybase.erase(0, preflen); + myp.erase(0,preflen); + + // if mybase contains leading ".." components, we're screwed + if(hasPrefix(mybase, "../")) + DUNE_THROW(NotImplemented, "relativePath: newbase has too many leading " + "\"..\" components: newbase=\"" << newbase << "\" " + "p=\"" << p << "\""); + + // count the number of components in mybase + typedef std::iterator_traits::difference_type + count_t; + count_t count = std::count(mybase.begin(), mybase.end(), '/'); + + std::string result; + // prefix with that many leading components + for(count_t i = 0; i < count; ++i) + result += "../"; + // append what is left of p + result += myp; + + return result; + } + + /** @} group Path */ +} diff --git a/dune/common/path.hh b/dune/common/path.hh new file mode 100644 index 0000000..39d89cc --- /dev/null +++ b/dune/common/path.hh @@ -0,0 +1,182 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_PATH_HH +#define DUNE_COMMON_PATH_HH + +#include + +namespace Dune { + /** + * @addtogroup Path + * @{ + */ + + /** + * @file + * @author Jö Fahlke + * @brief Utilities for handling filesystem paths + */ + + //! concatenate two paths + /** + * \param base The base path. + * \param p The path to concatenate onto base. + * + * If p is an absolute path, return p. Otherwise return the + * string-concatenation of base and path, possibly with a '/' in between, if + * necessary. + * + * Some examples: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
base p result
anything "/abs/path" "/abs/path"
"a" "b" "a/b"
"/a" "b" "/a/b"
"a/" "b" "a/b"
"a" "b/" "a/b/"
".." "b" "../b"
"a" ".." "a/.."
"." "b" "./b"
"a" "." "a/."
"" "b" "b"
"a" "" "a"
"" "" ""
+ * + * If both base and p are sanitized as per processPath(), and if p does not + * contain any leading "../", then the result will also be sanitized. + */ + std::string concatPaths(const std::string& base, const std::string& p); + + //! sanitize a path for further processing + /** + * Sanitize the path as far as possible to make further processing easier. + * The resulting path has the following properties: + *
    + *
  • The path is a series of components, each followed by a single '/'. + *
  • An absolute path starts with an empty component followed by a '/', + * so its first character will be '/'. This is the only case where an + * empty component can occur. + *
  • The path does not contain any component ".". Any such component in + * the input is removed. + *
  • A ".." component may only occur in the following case: A relative + * path may contain a series of ".." in the beginning. Any other + * occurrences of ".." in the input is collapsed with a preceding + * component or simply removed if it is at the beginning of an absolute + * path. + *
+ * + * \note The result is really meant for processing only since it has two + * unusual properties: First, any path denoting the current directory + * in the input, such as "." will result in an empty path "". Second, + * any non-empty result path will have a trailing '/'. For other + * uses, prettyPath() may be more appropriate. + * + * Some examples: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
p result
"" ""
"." ""
"./" ""
"a/.." ""
".." "../"
"../a" "../a/"
"a" "a/"
"a//" "a/"
"a///b" "a/b/"
"/" "/"
"/." "/"
"/.." "/"
"/a/.." "/"
"/a" "/a/"
"/a/" "/a/"
"/../a/" "/a/"
+ */ + std::string processPath(const std::string& p); + + //! check whether the given path indicates that it is a directory + /** + * In particular the following kinds of paths indicate a directory: + *
    + *
  • The empty path (denotes the current directory), + *
  • any path with a trailing '/', + *
  • any path whose last component is "." or "..". + *
+ */ + bool pathIndicatesDirectory(const std::string& p); + + //! pretty print path + /** + * \param p Path to pretty-print. + * \param isDirectory Whether to append a '/' to make clear this is a + * directory. + * + * Pretty print the path. This removes any duplicate '/' and any + * superfluous occurrences of ".." and ".". The resulting path will have a + * trailing '/' if it is the root path or if isDirectory is true. It will + * however not have a trailing '/' if it is otherwise clear that it is a + * directory -- i.e. if its last component is "." or "..". + * + * Some examples: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
p isDirectory result
"" anything "."
"." anything "."
"./" anything "."
"a/.." anything "."
".." anything ".."
"../a" true "../a/"
"../a" false "../a"
"a" true "a/"
"a" false "a"
"a//" true "a/"
"a//" false "a"
"a///b" true "a/b/"
"a///b" false "a/b"
"/" anything "/"
"/." anything "/"
"/.." anything "/"
"/a/.." anything "/"
"/a" true "/a/"
"/a" false "/a"
"/a/" true "/a/"
"/a/" false "/a"
"/../a/" true "/a/"
"/../a/" false "/a"
+ */ + std::string prettyPath(const std::string& p, bool isDirectory); + + //! pretty print path + /** + * \param p Path to pretty-print. + * + * This is like prettyPath(const std::string& p, bool isDirectory) with + * isDirectory automatically determined using pathIndicatesDirectory(p). + */ + std::string prettyPath(const std::string& p); + + //! compute a relative path between two paths + /** + * \param newbase Base path for the resulting relative path. + * \param p Path re sulting path should resolve to, when taken + * reltively to newbase. + * + * Compute a relative path from newbase to p. newbase is assumed to be a + * directory. p and newbase should either both be absolute, or both be + * relative. In the latter case they are assumed to both be relative to + * the same unspecified directory. The has the form of something sanitized + * by processPath(). + * + * \throw NotImplemented The condition that newbase and p must both be + * relative or both be absolute does not hold. + * \throw NotImplemented After sanitization newbase has more leading ".." + * components than p. + */ + std::string relativePath(const std::string& newbase, const std::string& p); + + /** @} group Path */ +} + +#endif // DUNE_COMMON_PATH_HH diff --git a/dune/common/poolallocator.hh b/dune/common/poolallocator.hh new file mode 100644 index 0000000..f73f0c0 --- /dev/null +++ b/dune/common/poolallocator.hh @@ -0,0 +1,571 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_POOLALLOCATOR_HH +#define DUNE_COMMON_POOLALLOCATOR_HH + +/** \file + * \brief An stl-compliant pool allocator + */ + +#include +#include +#include +#include +#include + +#ifndef DOXYGEN +// forward declarations. +// we need to know the test function to declare it friend +template +struct testPoolMain; +#endif + +namespace Dune +{ + + template + class Pool; + + template + class PoolAllocator; + +} + +namespace std +{ + /* + template + inline ostream& operator<<(ostream& os, Dune::Pool& pool) + { + os<<"pool="<<&pool<<" allocated_="< + inline ostream& operator<<(ostream& os, Dune::PoolAllocator& pool) + { + os< + class Pool + { + // make the test function friend + friend struct ::testPoolMain; + + //friend std::ostream& std::operator<<<>(std::ostream&,Pool&); + template< class, std::size_t > friend class PoolAllocator; + + private: + + /** @brief Reference to next free element. */ + struct Reference + { + Reference *next_; + }; + + public: + + /** @brief The type of object we allocate memory for. */ + typedef T MemberType; + enum + { + /** + * @brief The size of a union of Reference and MemberType. + */ + unionSize = ((sizeof(MemberType) < sizeof(Reference)) ? + sizeof(Reference) : sizeof(MemberType)), + + /** + * @brief Size requirement. At least one object has to + * stored. + */ + size = ((sizeof(MemberType) <= s && sizeof(Reference) <= s) ? + s : unionSize), + + /** + * @brief The alignment that suits both the MemberType and + * the Reference (i.e. their least common multiple). + */ + alignment = std::lcm(alignof(MemberType), alignof(Reference)), + + /** + * @brief The aligned size of the type. + * + * This size is bigger than sizeof of the type and a multiple of + * the alignment requirement. + */ + alignedSize = ((unionSize % alignment == 0) ? + unionSize : + ((unionSize / alignment + 1) * alignment)), + + /** + * @brief The size of each chunk memory chunk. + * + * Will be adapted to be a multiple of the alignment + */ + chunkSize = ((size % alignment == 0) ? + size : ((size / alignment + 1)* alignment)), + + /** + * @brief The number of element each chunk can hold. + */ + elements = (chunkSize / alignedSize) + }; + + private: + /** @brief Chunk of memory managed by the pool. */ + struct Chunk + { + + //friend int testPool(); + + /** @brief The memory we hold. */ + alignas(alignment) char chunk_[chunkSize]; + + /** @brief The next element */ + Chunk *next_; + }; + + public: + /** @brief Constructor. */ + inline Pool(); + /** @brief Destructor. */ + inline ~Pool(); + /** + * @brief Get a new or recycled object + * @return A pointer to the object memory. + */ + inline void* allocate(); + /** + * @brief Free an object. + * @param o The pointer to memory block of the object. + */ + inline void free(void* o); + + /** + * @brief Print elements in pool for debugging. + */ + inline void print(std::ostream& os); + + private: + + // Prevent Copying! + Pool(const Pool&); + + void operator=(const Pool& pool) const; + /** @brief Grow our pool.*/ + inline void grow(); + /** @brief The first free element. */ + Reference *head_; + /** @brief Our memory chunks. */ + Chunk *chunks_; + /* @brief The number of currently allocated elements. */ + //size_t allocated_; + + }; + + /** + * @brief An allocator managing a pool of objects for reuse. + * + * This allocator is specifically useful for small data types + * where new and delete are too expensive. + * + * It uses a pool of memory chunks where the objects will be allocated. + * This means that assuming that N objects fit into memory only every N-th + * request for an object will result in memory allocation. + * + * @warning It is not suitable + * for the use in standard containers as it cannot allocate + * arrays of arbitrary size + * + * \tparam T The type that will be allocated. + * \tparam s The number of elements to fit into one memory chunk. + */ + template + class PoolAllocator + { + //friend std::ostream& std::operator<<<>(std::ostream&,PoolAllocator&); + + public: + /** + * @brief Type of the values we construct and allocate. + */ + typedef T value_type; + + enum + { + /** + * @brief The number of objects to fit into one memory chunk + * allocated. + */ + size=s*sizeof(value_type) + }; + + /** + * @brief The pointer type. + */ + typedef T* pointer; + + /** + * @brief The constant pointer type. + */ + typedef const T* const_pointer; + + /** + * @brief The reference type. + */ + typedef T& reference; + + /** + * @brief The constant reference type. + */ + typedef const T& const_reference; + + /** + * @brief The size type. + */ + typedef std::size_t size_type; + + /** + * @brief The difference_type. + */ + typedef std::ptrdiff_t difference_type; + + /** + * @brief Constructor. + */ + inline PoolAllocator(); + + /** + * @brief Copy Constructor that does not copy the memory pool. + */ + template + inline PoolAllocator(const PoolAllocator&) + { + // we allow copying but never copy the pool + // to have a clear ownership of allocated pointers. + } + + /// \brief Copy constructor that does not copy the memory pool. + PoolAllocator(const PoolAllocator&) + { + // we allow copying but never copy the pool + // to have a clear ownership of allocated pointers. + // For this behaviour we have to implement + // the copy constructor, because the default + // one would copy the pool and deallocation + // of it would break. + } + /** + * @brief Allocates objects. + * @param n The number of objects to allocate. Has to be one! + * @param hint Ignored hint. + * @return A pointer tp the allocated elements. + */ + inline pointer allocate(std::size_t n, const_pointer hint=0); + + /** + * @brief Free objects. + * + * Does not call the destructor! + * @param n The number of objects to free. Has to be one! + * @param p Pointer to the first object. + */ + inline void deallocate(pointer p, std::size_t n); + + /** + * @brief Construct an object. + * @param p Pointer to the object. + * @param value The value to initialize it to. + */ + inline void construct(pointer p, const_reference value); + + /** + * @brief Destroy an object without freeing memory. + * @param p Pointer to the object. + */ + inline void destroy(pointer p); + + /** + * @brief Convert a reference to a pointer. + */ + inline pointer address(reference x) const { return &x; } + + + /** + * @brief Convert a reference to a pointer. + */ + inline const_pointer address(const_reference x) const { return &x; } + + /** + * @brief Not correctly implemented, yet! + */ + inline int max_size() const noexcept { return 1; } + + /** + * @brief Rebind the allocator to another type. + */ + template + struct rebind + { + typedef PoolAllocator other; + }; + + /** @brief The type of the memory pool we use. */ + typedef Pool PoolType; + + private: + /** + * @brief The underlying memory pool. + */ + PoolType memoryPool_; + }; + + // specialization for void + template + class PoolAllocator + { + public: + typedef void* pointer; + typedef const void* const_pointer; + // reference to void members are impossible. + typedef void value_type; + template struct rebind + { + typedef PoolAllocator other; + }; + }; + + + template + bool operator==(const PoolAllocator&, const PoolAllocator&) + { + return false; + } + + + template + bool operator!=(const PoolAllocator&, const PoolAllocator&) + { + return true; + } + + template + bool operator==(const PoolAllocator& p1, const PoolAllocator& p2) + { + return &p1==&p2; + } + + + template + bool operator!=(const PoolAllocator& p1, const PoolAllocator& p2) + { + return &p1 != &p2; + } + + template + bool operator==(const PoolAllocator&, const PoolAllocator&) + { + return false; + } + + + template + bool operator!=(const PoolAllocator&, const PoolAllocator&) + { + return true; + } + + template + bool operator==(const PoolAllocator& p1, const PoolAllocator& p2) + { + return &p1==&p2; + } + + template + bool operator!=(const PoolAllocator& p1, const PoolAllocator& p2) + { + return &p1!=&p2; + } + + template + inline Pool::Pool() + : head_(0), chunks_(0) //, allocated_(0) + { + static_assert(sizeof(T)<=unionSize, "Library Error: type T is too big"); + static_assert(sizeof(Reference)<=unionSize, "Library Error: type of referene is too big"); + static_assert(unionSize<=alignedSize, "Library Error: alignedSize too small"); + static_assert(sizeof(T)<=chunkSize, "Library Error: chunkSize must be able to hold at least one value"); + static_assert(sizeof(Reference)<=chunkSize, "Library Error: chunkSize must be able to hold at least one reference"); + static_assert(chunkSize % alignment == 0, "Library Error: compiler cannot calculate!"); + static_assert(elements>=1, "Library Error: we need to hold at least one element!"); + static_assert(elements*alignedSize<=chunkSize, "Library Error: aligned elements must fit into chuck!"); + } + + template + inline Pool::~Pool() + { + /* + if(allocated_!=0) + std::cerr<<"There are still "< " + <(this)<<"! This is a memory leak and might result in segfaults" + <next_; + delete tmp; + } + } + + template + inline void Pool::print(std::ostream& os) + { + Chunk* current=chunks_; + while(current) { + os<next_; + } + os< + inline void Pool::grow() + { + Chunk *newChunk = new Chunk; + newChunk->next_ = chunks_; + chunks_ = newChunk; + + char* start = chunks_->chunk_; + char* last = &start[elements*alignedSize]; + Reference* ref = new (start) (Reference); + + // grow is only called if head==0, + assert(!head_); + + head_ = ref; + + for(char* element=start+alignedSize; elementnext_ = next; + ref = next; + } + ref->next_=0; + } + + template + inline void Pool::free(void* b) + { + if(b) { +#ifndef NDEBUG + Chunk* current=chunks_; + while(current) { + if(static_cast(current->chunk_)<=b && + static_cast(current->chunk_+chunkSize)>b) + break; + current=current->next_; + } + if(!current) + throw std::bad_alloc(); +#endif + Reference* freed = static_cast(b); + freed->next_ = head_; + head_ = freed; + //--allocated_; + } + else + { + std::cerr<< "Tried to free null pointer! "< + inline void* Pool::allocate() + { + if(!head_) + grow(); + + Reference* p = head_; + head_ = p->next_; + //++allocated_; + return p; + } + + template + inline PoolAllocator::PoolAllocator() + { } + + template + inline typename PoolAllocator::pointer + PoolAllocator::allocate(std::size_t n, const_pointer) + { + if(n==1) + return static_cast(memoryPool_.allocate()); + else + throw std::bad_alloc(); + } + + template + inline void PoolAllocator::deallocate(pointer p, std::size_t n) + { + for(size_t i=0; i + inline void PoolAllocator::construct(pointer p, const_reference value) + { + ::new (static_cast(p))T(value); + } + + template + inline void PoolAllocator::destroy(pointer p) + { + p->~T(); + } + + /** @} */ +} +#endif diff --git a/dune/common/power.hh b/dune/common/power.hh new file mode 100644 index 0000000..39952d8 --- /dev/null +++ b/dune/common/power.hh @@ -0,0 +1,48 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_POWER_HH +#define DUNE_COMMON_POWER_HH + +/** \file + \brief Various implementations of the power function for run-time and static arguments + */ + +#include + +namespace Dune { + + /** @addtogroup Common + + @{ + */ + + /** \brief Calculates m^p at compile time + * \deprecated Please use the method `power` from `math.hh` instead! + */ + template + struct StaticPower + { + /** \brief power stores m^p */ + static constexpr int power = Dune::power(m,p); + }; + + + /** \brief Compute power for a run-time mantissa and a compile-time integer exponent + * + * \deprecated Please use the method `power` from `math.hh` instead! + * + * \tparam p The exponent + */ + template + struct Power + { + template + static constexpr auto eval(const T & a) + { + return power(a,p); + } + }; + +} + +#endif diff --git a/dune/common/precision.hh b/dune/common/precision.hh new file mode 100644 index 0000000..72573ab --- /dev/null +++ b/dune/common/precision.hh @@ -0,0 +1,49 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_PRECISION_HH +#define DUNE_PRECISION_HH + +/** \file + * \brief Various precision settings for calculations with FieldMatrix and FieldVector + */ + +#include + +namespace Dune { + + /** + @addtogroup DenseMatVec + @{ + */ + + /** + * @brief Precisions for calculations with FieldMatrix and FieldVector. + */ + template + class FMatrixPrecision { + public: + //! return threshold to declare matrix singular + static ctype absolute_limit () + { + return _absolute; + } + + //! set singular threshold + static void set_absolute_limit (ctype absthres) + { + _absolute = absthres; + } + + private: + // just to demonstrate some state information + static ctype _absolute; + }; + + template + ctype FMatrixPrecision::_absolute = 1E-80; + + /** @} end documentation */ + +} // end namespace + +#endif diff --git a/dune/common/promotiontraits.hh b/dune/common/promotiontraits.hh new file mode 100644 index 0000000..cb7acc2 --- /dev/null +++ b/dune/common/promotiontraits.hh @@ -0,0 +1,39 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_PROMOTIONTRAITS_HH +#define DUNE_PROMOTIONTRAITS_HH + +#include + +namespace Dune { + /** + * @file + * @brief Compute type of the result of an arithmetic operation involving two different number types. + * + * @author Matthias Wohlmuth + */ + + /** @addtogroup Common + * + * @{ + */ + + /** \brief Compute type of the result of an arithmetic operation involving two different number types. + */ + template + struct PromotionTraits + { + typedef decltype(std::declval()+std::declval()) PromotedType; + }; + + // Specialization for the case of two equal types + // One should think that the generic template should handle this case as well. + // However, the fvectortest.cc unit test fails without it if ENABLE_GMP is set. + template + struct PromotionTraits { typedef T1 PromotedType; }; + + /** @} */ +} // end namespace + + +#endif // DUNE_PROMOTIONTRAITS_HH diff --git a/dune/common/propertymap.hh b/dune/common/propertymap.hh new file mode 100644 index 0000000..1a27d66 --- /dev/null +++ b/dune/common/propertymap.hh @@ -0,0 +1,332 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_PROPERTYMAP_HH +#define DUNE_PROPERTYMAP_HH + +#include +#include +#include + +namespace Dune +{ + + template + struct PropertyMapTraits + { + /** + * @brief The type of the key of the property map. + */ + typedef typename PM::KeyType KeyType; + /** + * @brief The type of the values of the property map. + */ + typedef typename PM::ValueType ValueType; + /** + * @brief The type of the reference to the values. + */ + typedef typename PM::Reference Reference; + /** + * @brief The category the property map belongs to. + */ + typedef typename PM::Category Category; + }; + + /** @brief Tag for the category of readable property maps. */ + struct ReadablePropertyMapTag + {}; + + /** @brief Tag for the category of writable property maps. */ + struct WritablePropertyMapTag + {}; + + /** + * @brief Tag for the category of readable and writable property + * maps. + */ + struct ReadWritePropertyMapTag + : public ReadablePropertyMapTag, public WritablePropertyMapTag + {}; + + /** + * @brief Tag for the category of lvalue property maps. + */ + struct LvaluePropertyMapTag + : public ReadWritePropertyMapTag + {}; + + template + struct PropertyMapTraits + { + typedef T ValueType; + typedef ValueType& Reference; + typedef std::ptrdiff_t KeyType; + typedef LvaluePropertyMapTag Category; + }; + + + template + struct PropertyMapTraits + { + typedef T ValueType; + typedef const ValueType& Reference; + typedef std::ptrdiff_t KeyType; + typedef LvaluePropertyMapTag Category; + }; + + template + struct RAPropertyMapHelper + {}; + + template + inline Reference + get(const RAPropertyMapHelper& pmap, + const Key& key) + { + return static_cast(pmap)[key]; + } + + template + inline void + put(const RAPropertyMapHelper& pmap, + const Key& key, const Value& value) + { + static_assert(std::is_convertible::value, + "WritablePropertyMapTag required!"); + static_cast(pmap)[key] = value; + } + + /** + * @brief Adapter to turn a random access iterator into a property map. + */ + template::value_type, + class R = typename std::iterator_traits::reference> + class IteratorPropertyMap + : public RAPropertyMapHelper > + { + public: + /** + * @brief The type of the random access iterator. + */ + typedef RAI RandomAccessIterator; + + /** + * @brief The type of the index map. + * + * This will convert the KeyType to std::ptrdiff_t via operator[](). + */ + typedef IM IndexMap; + + /** + * @brief The key type of the property map. + */ + typedef typename IndexMap::KeyType KeyType; + + /** + * @brief The value type of the property map. + */ + typedef T ValueType; + + /** + * @brief The reference type of the property map. + */ + typedef R Reference; + + /** + * @brief The category of this property map. + */ + typedef LvaluePropertyMapTag Category; + + /** + * @brief Constructor. + * @param iter The random access iterator that + * provides the mapping. + * @param im The index map that maps the KeyType + * to the difference_type of the iterator. + */ + inline IteratorPropertyMap(RandomAccessIterator iter, + const IndexMap& im=IndexMap()) + : iter_(iter), indexMap_(im) + {} + + /** @brief Constructor. */ + inline IteratorPropertyMap() + : iter_(), indexMap_() + {} + + /** @brief Access the a value by reference. */ + inline Reference operator[](KeyType key) const + { + return *(iter_ + get(indexMap_, key)); + } + + private: + /** @brief The underlying iterator. */ + RandomAccessIterator iter_; + /** @brief The index map to use for the lookup. */ + IndexMap indexMap_; + }; + + /** + * @brief An adapter to turn an unique associative container + * into a property map. + */ + template + class AssociativePropertyMap + : RAPropertyMapHelper > + { + /** + * @brief The type of the unique associative container. + */ + typedef T UniqueAssociativeContainer; + + /** + * @brief The key type of the property map. + */ + typedef typename UniqueAssociativeContainer::value_type::first_type + KeyType; + + /** + * @brief The value type of the property map. + */ + typedef typename UniqueAssociativeContainer::value_type::second_type + ValueType; + + /** + * @brief The reference type of the property map. + */ + typedef ValueType& Reference; + + /** + * @brief The category of the property map. + */ + typedef LvaluePropertyMapTag Category; + + /** @brief Constructor */ + inline AssociativePropertyMap() + : map_(0) + {} + + /** @brief Constructor. */ + inline AssociativePropertyMap(UniqueAssociativeContainer& map) + : map_(&map) + {} + + /** + * @brief Access a property. + * @param key The key of the property. + */ + inline Reference operator[](KeyType key) const + { + return map_->find(key)->second; + } + private: + UniqueAssociativeContainer* map_; + }; + + /** + * @brief An adaptor to turn an unique associative container + * into a property map. + */ + template + class ConstAssociativePropertyMap + : RAPropertyMapHelper > + { + /** + * @brief The type of the unique associative container. + */ + typedef T UniqueAssociativeContainer; + + /** + * @brief The key type of the property map. + */ + typedef typename UniqueAssociativeContainer::value_type::first_type + KeyType; + + /** + * @brief The value type of the property map. + */ + typedef typename UniqueAssociativeContainer::value_type::second_type + ValueType; + + /** + * @brief The reference type of the property map. + */ + typedef const ValueType& Reference; + + /** + * @brief The category of the property map. + */ + typedef LvaluePropertyMapTag Category; + + /** @brief Constructor */ + inline ConstAssociativePropertyMap() + : map_(0) + {} + + /** @brief Constructor. */ + inline ConstAssociativePropertyMap(const UniqueAssociativeContainer& map) + : map_(&map) + {} + + /** + * @brief Access a property. + * @param key The key of the property. + */ + inline Reference operator[](KeyType key) const + { + return map_->find(key)->second; + } + private: + const UniqueAssociativeContainer* map_; + }; + + /** + * @brief A property map that applies the identity function to integers. + */ + struct IdentityMap + : public RAPropertyMapHelper + { + /** @brief The key type of the map. */ + typedef std::size_t KeyType; + + /** @brief The value type of the map. */ + typedef std::size_t ValueType; + + /** @brief The reference type of the map. */ + typedef std::size_t Reference; + + /** @brief The category of the map. */ + typedef ReadablePropertyMapTag Category; + + inline ValueType operator[](const KeyType& key) const + { + return key; + } + }; + + + /** + * @brief Selector for the property map type. + * + * If present the type of the property map is accessible via the typedef Type. + */ + template + struct PropertyMapTypeSelector + { + /** + * @brief the tag identifying the property. + */ + typedef T Tag; + /** + * @brief The container type to whose entries the properties + * are attached. + */ + typedef C Container; + }; + +} + +#endif diff --git a/dune/common/proxymemberaccess.hh b/dune/common/proxymemberaccess.hh new file mode 100644 index 0000000..0dd1e43 --- /dev/null +++ b/dune/common/proxymemberaccess.hh @@ -0,0 +1,121 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_PROXYMEMBERACCESS_HH +#define DUNE_COMMON_PROXYMEMBERACCESS_HH + +/** + * \file + * \brief infrastructure for supporting operator->() on both references and proxies + * \ingroup CxxUtilities + */ + +#include +#include + +namespace Dune { + + namespace Impl { + + // helper struct to store a temporary / proxy + // for the duration of the member access + template + struct member_access_proxy_holder + { + + // only support moving the temporary into the holder object + member_access_proxy_holder(T&& t) + : _t(std::move(t)) + {} + + // The object is fundamentally a temporary, i.e. an rvalue, + // + const T* operator->() const + { + return &_t; + } + + T _t; + + }; + + } // end Impl namespace + + +#ifdef DOXYGEN + + //! Transparent support for providing member access to both lvalues and rvalues (temporary proxies). + /** + * If an iterator facade (like entity iterators) wants to allow the embedded implementation to + * return either an (internally stored) reference or a temporary object and expose these two + * behaviors to enable performance optimizations, operator->() needs special handling: If the + * implementation returns a reference, operator->() in the facade can simply return the address + * of the referenced object, but if the returned object is a temporary, we need to capture and + * store it in a helper object to make sure it outlives the member access. This function transparently + * supports both variants. It should be used like this: + * + * \code + * class iterator + * { + * ... + * + * decltype(handle_proxy_member_access(implementation.dereference())) + * operator->() const + * { + * return handle_proxy_member_access(implementation.dereference()); + * } + * + * ... + * }; + * \endcode + * + * \note This function exploits the special type deduction rules for unqualified rvalue references + * to distinguish between lvalues and rvalues and thus needs to be passed the object returned + * by the implementation. + * + * \ingroup CxxUtilities + */ + template + pointer_or_proxy_holder + handle_proxy_member_access(T&& t); + +#else // DOXYGEN + + + // This version matches lvalues (the C++ type deduction rules state that + // the T&& signature deduces to a reference iff the argument is an lvalue). + // As the argument is an lvalue, we do not have to worry about its lifetime + // and can just return its address. + template + inline typename std::enable_if< + std::is_lvalue_reference::value, + typename std::add_pointer< + typename std::remove_reference< + T + >::type + >::type + >::type + handle_proxy_member_access(T&& target) + { + return ⌖ + } + + // This version matches rvalues (the C++ type deduction rules state that + // the T&& signature deduces to a non-reference iff the argument is an rvalue). + // In this case, we have to capture the rvalue in a new object to make sure it + // is kept alive for the duration of the member access. For this purpose, we move + // it into a member_access_proxy_holder instance. + template + inline typename std::enable_if< + !std::is_lvalue_reference::value, + Impl::member_access_proxy_holder + >::type + handle_proxy_member_access(T&& target) + { + return {std::forward(target)}; + } + +#endif // DOXYGEN + +} // namespace Dune + +#endif // DUNE_COMMON_PROXYMEMBERACCESS_HH diff --git a/dune/common/quadmath.hh b/dune/common/quadmath.hh new file mode 100644 index 0000000..cb29ba8 --- /dev/null +++ b/dune/common/quadmath.hh @@ -0,0 +1,456 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_QUADMATH_HH +#define DUNE_QUADMATH_HH + +#if HAVE_QUADMATH +#include + +#include +#include +#include +#include // abs +#include +#include +#include +#include + +#include +#include + +namespace Dune +{ + namespace Impl + { + // forward declaration + class Float128; + + } // end namespace Impl + + using Impl::Float128; + + // The purpose of this namespace is to move the `` function overloads + // out of namespace `Dune`, see AlignedNumber in debugalign.hh. + namespace Impl + { + using float128_t = __float128; + + /// Wrapper for quad-precision type __float128 + class Float128 + { + float128_t value_ = 0.0q; + + public: + constexpr Float128() = default; + constexpr Float128(const float128_t& value) noexcept + : value_(value) + {} + + // constructor from any floating-point or integer type + template ::value, int> = 0> + constexpr Float128(const T& value) noexcept + : value_(value) + {} + + // constructor from pointer to null-terminated byte string + explicit Float128(const char* str) noexcept + : value_(strtoflt128(str, NULL)) + {} + + // accessors + constexpr operator float128_t() const noexcept { return value_; } + + constexpr float128_t const& value() const noexcept { return value_; } + constexpr float128_t& value() noexcept { return value_; } + + // I/O + template + friend std::basic_istream& + operator>>(std::basic_istream& in, Float128& x) + { + std::string buf; + buf.reserve(128); + in >> buf; + x.value() = strtoflt128(buf.c_str(), NULL); + return in; + } + + template + friend std::basic_ostream& + operator<<(std::basic_ostream& out, const Float128& x) + { + const std::size_t bufSize = 128; + CharT buf[128]; + + std::string format = "%." + std::to_string(out.precision()) + "Q" + + ((out.flags() | std::ios_base::scientific) ? "e" : "f"); + const int numChars = quadmath_snprintf(buf, bufSize, format.c_str(), x.value()); + if (std::size_t(numChars) >= bufSize) { + DUNE_THROW(Dune::RangeError, "Failed to print Float128 value: buffer overflow"); + } + out << buf; + return out; + } + + // Increment, decrement + constexpr Float128& operator++() noexcept { ++value_; return *this; } + constexpr Float128& operator--() noexcept { --value_; return *this; } + + constexpr Float128 operator++(int) noexcept { Float128 tmp{*this}; ++value_; return tmp; } + constexpr Float128 operator--(int) noexcept { Float128 tmp{*this}; --value_; return tmp; } + + // unary operators + constexpr Float128 operator+() const noexcept { return Float128{+value_}; } + constexpr Float128 operator-() const noexcept { return Float128{-value_}; } + + // assignment operators +#define DUNE_ASSIGN_OP(OP) \ + constexpr Float128& operator OP(const Float128& u) noexcept \ + { \ + value_ OP float128_t(u); \ + return *this; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_ASSIGN_OP(+=); + DUNE_ASSIGN_OP(-=); + + DUNE_ASSIGN_OP(*=); + DUNE_ASSIGN_OP(/=); + +#undef DUNE_ASSIGN_OP + + }; // end class Float128 + + // binary operators: + // For symmetry provide overloads with arithmetic types + // in the first or second argument. +#define DUNE_BINARY_OP(OP) \ + constexpr Float128 operator OP(const Float128& t, \ + const Float128& u) noexcept \ + { \ + return Float128{float128_t(t) OP float128_t(u)}; \ + } \ + constexpr Float128 operator OP(const float128_t& t, \ + const Float128& u) noexcept \ + { \ + return Float128{t OP float128_t(u)}; \ + } \ + constexpr Float128 operator OP(const Float128& t, \ + const float128_t& u) noexcept \ + { \ + return Float128{float128_t(t) OP u}; \ + } \ + template ::value, int> = 0> \ + constexpr Float128 operator OP(const T& t, \ + const Float128& u) noexcept \ + { \ + return Float128{float128_t(t) OP float128_t(u)}; \ + } \ + template ::value, int> = 0> \ + constexpr Float128 operator OP(const Float128& t, \ + const U& u) noexcept \ + { \ + return Float128{float128_t(t) OP float128_t(u)}; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_BINARY_OP(+); + DUNE_BINARY_OP(-); + DUNE_BINARY_OP(*); + DUNE_BINARY_OP(/); + +#undef DUNE_BINARY_OP + + // logical operators: + // For symmetry provide overloads with arithmetic types + // in the first or second argument. +#define DUNE_BINARY_BOOL_OP(OP) \ + constexpr bool operator OP(const Float128& t, \ + const Float128& u) noexcept \ + { \ + return float128_t(t) OP float128_t(u); \ + } \ + template ::value, int> = 0> \ + constexpr bool operator OP(const T& t, \ + const Float128& u) noexcept \ + { \ + return float128_t(t) OP float128_t(u); \ + } \ + template ::value, int> = 0> \ + constexpr bool operator OP(const Float128& t, \ + const U& u) noexcept \ + { \ + return float128_t(t) OP float128_t(u); \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_BINARY_BOOL_OP(==); + DUNE_BINARY_BOOL_OP(!=); + DUNE_BINARY_BOOL_OP(<); + DUNE_BINARY_BOOL_OP(>); + DUNE_BINARY_BOOL_OP(<=); + DUNE_BINARY_BOOL_OP(>=); + +#undef DUNE_BINARY_BOOL_OP + + // Overloads for the cmath functions + + // function with name `name` redirects to quadmath function `func` +#define DUNE_UNARY_FUNC(name,func) \ + inline Float128 name(const Float128& u) noexcept \ + { \ + return Float128{func (float128_t(u))}; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + // like DUNE_UNARY_FUNC but with cutom return type +#define DUNE_CUSTOM_UNARY_FUNC(type,name,func) \ + inline type name(const Float128& u) noexcept \ + { \ + return (type)(func (float128_t(u))); \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + // redirects to quadmath function with two arguments +#define DUNE_BINARY_FUNC(name,func) \ + inline Float128 name(const Float128& t, \ + const Float128& u) noexcept \ + { \ + return Float128{func (float128_t(t), float128_t(u))}; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_UNARY_FUNC(abs, fabsq); + DUNE_UNARY_FUNC(acos, acosq); + DUNE_UNARY_FUNC(acosh, acoshq); + DUNE_UNARY_FUNC(asin, asinq); + DUNE_UNARY_FUNC(asinh, asinhq); + DUNE_UNARY_FUNC(atan, atanq); + DUNE_UNARY_FUNC(atanh, atanhq); + DUNE_UNARY_FUNC(cbrt, cbrtq); + DUNE_UNARY_FUNC(ceil, ceilq); + DUNE_UNARY_FUNC(cos, cosq); + DUNE_UNARY_FUNC(cosh, coshq); + DUNE_UNARY_FUNC(erf, erfq); + DUNE_UNARY_FUNC(erfc, erfcq); + DUNE_UNARY_FUNC(exp, expq); + DUNE_UNARY_FUNC(expm1, expm1q); + DUNE_UNARY_FUNC(fabs, fabsq); + DUNE_UNARY_FUNC(floor, floorq); + DUNE_CUSTOM_UNARY_FUNC(int, ilogb, ilogbq); + DUNE_UNARY_FUNC(lgamma, lgammaq); + DUNE_CUSTOM_UNARY_FUNC(long long int, llrint, llrintq); + DUNE_CUSTOM_UNARY_FUNC(long long int, llround, llroundq); + DUNE_UNARY_FUNC(log, logq); + DUNE_UNARY_FUNC(log10, log10q); + DUNE_UNARY_FUNC(log1p, log1pq); + DUNE_UNARY_FUNC(log2, log2q); + // DUNE_UNARY_FUNC(logb, logbq); // not available in gcc5 + DUNE_CUSTOM_UNARY_FUNC(long int, lrint, lrintq); + DUNE_CUSTOM_UNARY_FUNC(long int, lround, lroundq); + DUNE_UNARY_FUNC(nearbyint, nearbyintq); + DUNE_BINARY_FUNC(nextafter, nextafterq); + DUNE_BINARY_FUNC(pow, powq); // overload for integer argument see below + DUNE_UNARY_FUNC(rint, rintq); + DUNE_UNARY_FUNC(round, roundq); + DUNE_UNARY_FUNC(sin, sinq); + DUNE_UNARY_FUNC(sinh, sinhq); + DUNE_UNARY_FUNC(sqrt, sqrtq); + DUNE_UNARY_FUNC(tan, tanq); + DUNE_UNARY_FUNC(tanh, tanhq); + DUNE_UNARY_FUNC(tgamma, tgammaq); + DUNE_UNARY_FUNC(trunc, truncq); + + DUNE_CUSTOM_UNARY_FUNC(bool, isfinite, finiteq); + DUNE_CUSTOM_UNARY_FUNC(bool, isinf, isinfq); + DUNE_CUSTOM_UNARY_FUNC(bool, isnan, isnanq); + DUNE_CUSTOM_UNARY_FUNC(bool, signbit, signbitq); + +#undef DUNE_UNARY_FUNC +#undef DUNE_CUSTOM_UNARY_FUNC +#undef DUNE_BINARY_FUNC + + // like DUNE_BINARY_FUNC but provide overloads with arithmetic + // types in the first or second argument. +#define DUNE_BINARY_ARITHMETIC_FUNC(name,func) \ + inline Float128 name(const Float128& t, \ + const Float128& u) noexcept \ + { \ + return Float128{func (float128_t(t), float128_t(u))}; \ + } \ + template ::value, int> = 0> \ + inline Float128 name(const T& t, \ + const Float128& u) noexcept \ + { \ + return Float128{func (float128_t(t), float128_t(u))}; \ + } \ + template ::value, int> = 0> \ + inline Float128 name(const Float128& t, \ + const U& u) noexcept \ + { \ + return Float128{func (float128_t(t), float128_t(u))}; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_BINARY_ARITHMETIC_FUNC(atan2,atan2q); + DUNE_BINARY_ARITHMETIC_FUNC(copysign,copysignq); + DUNE_BINARY_ARITHMETIC_FUNC(fdim,fdimq); + DUNE_BINARY_ARITHMETIC_FUNC(fmax,fmaxq); + DUNE_BINARY_ARITHMETIC_FUNC(fmin,fminq); + DUNE_BINARY_ARITHMETIC_FUNC(fmod,fmodq); + DUNE_BINARY_ARITHMETIC_FUNC(hypot,hypotq); + DUNE_BINARY_ARITHMETIC_FUNC(remainder,remainderq); + +#undef DUNE_BINARY_ARITHMETIC_FUNC + + // some more cmath functions with special signature + + inline Float128 fma(const Float128& t, const Float128& u, const Float128& v) + { + return Float128{fmaq(float128_t(t),float128_t(u),float128_t(v))}; + } + + inline Float128 frexp(const Float128& u, int* p) + { + return Float128{frexpq(float128_t(u), p)}; + } + + inline Float128 ldexp(const Float128& u, int p) + { + return Float128{ldexpq(float128_t(u), p)}; + } + + inline Float128 remquo(const Float128& t, const Float128& u, int* quo) + { + return Float128{remquoq(float128_t(t), float128_t(u), quo)}; + } + + inline Float128 scalbln(const Float128& u, long int e) + { + return Float128{scalblnq(float128_t(u), e)}; + } + + inline Float128 scalbn(const Float128& u, int e) + { + return Float128{scalbnq(float128_t(u), e)}; + } + + /// \brief Overload of `pow` function for integer exponents. + // NOTE: This is much faster than a pow(x, Float128(p)) call + // NOTE: This is a modified version of boost::math::cstdfloat::detail::pown + // (adapted to the type Float128) that is part of the Boost 1.65 Math toolkit 2.8.0 + // and is implemented by Christopher Kormanyos, John Maddock, and Paul A. Bristow, + // distributed under the Boost Software License, Version 1.0 + // (See http://www.boost.org/LICENSE_1_0.txt) + template ::value, int> = 0> + inline Float128 pow(const Float128& x, const Int p) + { + static const Float128 max_value = FLT128_MAX; + static const Float128 min_value = FLT128_MIN; + static const Float128 inf_value = float128_t{1} / float128_t{0}; + + const bool isneg = (x < 0); + const bool isnan = (x != x); + const bool isinf = (isneg ? bool(-x > max_value) : bool(+x > max_value)); + + if (isnan) { return x; } + if (isinf) { return Float128{nanq("")}; } + + const Float128 abs_x = (isneg ? -x : x); + if (p < Int(0)) { + if (abs_x < min_value) + return (isneg ? -inf_value : +inf_value); + else + return Float128(1) / pow(x, Int(-p)); + } + + if (p == Int(0)) { return Float128(1); } + if (p == Int(1)) { return x; } + if (abs_x > max_value) + return (isneg ? -inf_value : +inf_value); + + if (p == Int(2)) { return (x * x); } + if (p == Int(3)) { return ((x * x) * x); } + if (p == Int(4)) { const Float128 x2 = (x * x); return (x2 * x2); } + + Float128 result = ((p % Int(2)) != Int(0)) ? x : Float128(1); + Float128 xn = x; // binary powers of x + + Int p2 = p; + while (Int(p2 /= 2) != Int(0)) { + xn *= xn; // Square xn for each binary power + + const bool has_binary_power = (Int(p2 % Int(2)) != Int(0)); + if (has_binary_power) + result *= xn; + } + + return result; + } + + + } // end namespace Impl + + template <> + struct IsNumber + : public std::true_type {}; + +} // end namespace Dune + +namespace std +{ +#ifndef NO_STD_NUMERIC_LIMITS_SPECIALIZATION + template <> + class numeric_limits + { + using Float128 = Dune::Impl::Float128; + using float128_t = Dune::Impl::float128_t; + + public: + static constexpr bool is_specialized = true; + static constexpr Float128 min() noexcept { return FLT128_MIN; } + static constexpr Float128 max() noexcept { return FLT128_MAX; } + static constexpr Float128 lowest() noexcept { return -FLT128_MAX; } + static constexpr int digits = FLT128_MANT_DIG; + static constexpr int digits10 = 34; + static constexpr int max_digits10 = 36; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr int radix = 2; + static constexpr Float128 epsilon() noexcept { return FLT128_EPSILON; } + static constexpr Float128 round_error() noexcept { return float128_t{0.5}; } + static constexpr int min_exponent = FLT128_MIN_EXP; + static constexpr int min_exponent10 = FLT128_MIN_10_EXP; + static constexpr int max_exponent = FLT128_MAX_EXP; + static constexpr int max_exponent10 = FLT128_MAX_10_EXP; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_present; + static constexpr bool has_denorm_loss = false; + static constexpr Float128 infinity() noexcept { return float128_t{1}/float128_t{0}; } + static Float128 quiet_NaN() noexcept { return nanq(""); } + static constexpr Float128 signaling_NaN() noexcept { return float128_t{}; } + static constexpr Float128 denorm_min() noexcept { return FLT128_DENORM_MIN; } + static constexpr bool is_iec559 = true; + static constexpr bool is_bounded = false; + static constexpr bool is_modulo = false; + static constexpr bool traps = false; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_to_nearest; + }; +#endif +} // end namespace std + +#endif // HAVE_QUADMATH +#endif // DUNE_QUADMATH_HH diff --git a/dune/common/rangeutilities.hh b/dune/common/rangeutilities.hh new file mode 100644 index 0000000..136c7e1 --- /dev/null +++ b/dune/common/rangeutilities.hh @@ -0,0 +1,831 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_RANGE_UTILITIES_HH +#define DUNE_COMMON_RANGE_UTILITIES_HH + +#include +#include +#include +#include +#include + +/** + * \file + * + * \brief Utilities for reduction like operations on ranges + * \author Christian Engwer + */ + +namespace Dune +{ + + /** + * @addtogroup RangeUtilities + * @{ + */ + + /** + \brief compute the maximum value over a range + + overloads for scalar values, and ranges exist + */ + template ::value, int>::type = 0> + typename T::value_type + max_value(const T & v) { + using std::max_element; + return *max_element(v.begin(), v.end()); + } + + template ::value, int>::type = 0> + const T & max_value(const T & v) { return v; } + + /** + \brief compute the minimum value over a range + + overloads for scalar values, and ranges exist + */ + template ::value, int>::type = 0> + typename T::value_type + min_value(const T & v) { + using std::min_element; + return *min_element(v.begin(), v.end()); + } + + template ::value, int>::type = 0> + const T & min_value(const T & v) { return v; } + + /** + \brief similar to std::bitset::any() return true, if any entries is true + + overloads for scalar values, ranges, and std::bitset exist + */ + template ::value, int>::type = 0> + bool any_true(const T & v) { + bool b = false; + for (const auto & e : v) + b = b or bool(e); + return b; + } + + template ::value, int>::type = 0> + bool any_true(const T & v) { return v; } + + template + bool any_true(const std::bitset & b) + { + return b.any(); + } + + /** + \brief similar to std::bitset::all() return true, if any entries is true + + overloads for scalar values, ranges, and std::bitset exist + */ + template ::value, int>::type = 0> + bool all_true(const T & v) { + bool b = true; + for (const auto & e : v) + b = b and bool(e); + return b; + } + + template ::value, int>::type = 0> + bool all_true(const T & v) { return v; } + + template + bool all_true(const std::bitset & b) + { + return b.all(); + } + + + + namespace Impl + { + + template + class IntegralRangeIterator + { + public: + typedef std::random_access_iterator_tag iterator_category; + typedef T value_type; + typedef std::make_signed_t difference_type; + typedef const T *pointer; + typedef T reference; + + constexpr IntegralRangeIterator() noexcept : value_(0) {} + constexpr explicit IntegralRangeIterator(value_type value) noexcept : value_(value) {} + + pointer operator->() const noexcept { return &value_; } + constexpr reference operator*() const noexcept { return value_; } + + constexpr reference operator[]( difference_type n ) const noexcept { return (value_ + n); } + + constexpr bool operator==(const IntegralRangeIterator & other) const noexcept { return (value_ == other.value_); } + constexpr bool operator!=(const IntegralRangeIterator & other) const noexcept { return (value_ != other.value_); } + + constexpr bool operator<(const IntegralRangeIterator & other) const noexcept { return (value_ <= other.value_); } + constexpr bool operator<=(const IntegralRangeIterator & other) const noexcept { return (value_ <= other.value_); } + constexpr bool operator>(const IntegralRangeIterator & other) const noexcept { return (value_ >= other.value_); } + constexpr bool operator>=(const IntegralRangeIterator & other) const noexcept { return (value_ >= other.value_); } + + IntegralRangeIterator& operator++() noexcept { ++value_; return *this; } + IntegralRangeIterator operator++(int) noexcept { IntegralRangeIterator copy( *this ); ++(*this); return copy; } + + IntegralRangeIterator& operator--() noexcept { --value_; return *this; } + IntegralRangeIterator operator--(int) noexcept { IntegralRangeIterator copy( *this ); --(*this); return copy; } + + IntegralRangeIterator& operator+=(difference_type n) noexcept { value_ += n; return *this; } + IntegralRangeIterator& operator-=(difference_type n) noexcept { value_ -= n; return *this; } + + friend constexpr IntegralRangeIterator operator+(const IntegralRangeIterator &a, difference_type n) noexcept { return IntegralRangeIterator(a.value_ + n); } + friend constexpr IntegralRangeIterator operator+(difference_type n, const IntegralRangeIterator &a) noexcept { return IntegralRangeIterator(a.value_ + n); } + friend constexpr IntegralRangeIterator operator-(const IntegralRangeIterator &a, difference_type n) noexcept { return IntegralRangeIterator(a.value_ - n); } + + constexpr difference_type operator-(const IntegralRangeIterator &other) const noexcept { return (static_cast(value_) - static_cast(other.value_)); } + + private: + value_type value_; + }; + + } // namespace Impl + + + + /** + * \brief dynamic integer range for use in range-based for loops + * + * \note This range can also be used in Hybrid::forEach, resulting in a dynamic + * for loop over the contained integers. + * + * \tparam T type of integers contained in the range + **/ + template + class IntegralRange + { + public: + /** \brief type of integers contained in the range **/ + typedef T value_type; + /** \brief type of iterator **/ + typedef Impl::IntegralRangeIterator iterator; + /** \brief unsigned integer type corresponding to value_type **/ + typedef std::make_unsigned_t size_type; + + /** \brief construct integer range [from, to) **/ + constexpr IntegralRange(value_type from, value_type to) noexcept : from_(from), to_(to) {} + /** \brief construct integer range [0, to) **/ + constexpr explicit IntegralRange(value_type to) noexcept : from_(0), to_(to) {} + /** \brief construct integer range std::pair **/ + constexpr IntegralRange(std::pair range) noexcept : from_(range.first), to_(range.second) {} + + /** \brief obtain a random-access iterator to the first element **/ + constexpr iterator begin() const noexcept { return iterator(from_); } + /** \brief obtain a random-access iterator past the last element **/ + constexpr iterator end() const noexcept { return iterator(to_); } + + /** \brief access specified element **/ + constexpr value_type operator[](const value_type &i) const noexcept { return (from_ + i); } + + /** \brief check whether the range is empty **/ + constexpr bool empty() const noexcept { return (from_ == to_); } + /** \brief obtain number of elements in the range **/ + constexpr size_type size() const noexcept { return (static_cast(to_) - static_cast(from_)); } + + private: + value_type from_, to_; + }; + + + /** + * \brief static integer range for use in range-based for loops + * + * This is a compile-time static variant of the IntegralRange. Apart from + * returning all range information statically, it casts into the corresponding + * std::integer_sequence. + * + * \note This range can also be used in Hybrid::forEach, resulting in a static + * for loop over the contained integers like a std::integer_sequence. + * + * \tparam T type of integers contained in the range + * \tparam to first element not contained in the range + * \tparam from first element contained in the range, defaults to 0 + **/ + template + class StaticIntegralRange + { + template + static std::integer_sequence shift_integer_sequence(std::integer_sequence); + + public: + /** \brief type of integers contained in the range **/ + typedef T value_type; + /** \brief type of iterator **/ + typedef Impl::IntegralRangeIterator iterator; + /** \brief unsigned integer type corresponding to value_type **/ + typedef std::make_unsigned_t size_type; + + /** \brief type of corresponding std::integer_sequence **/ + typedef decltype(shift_integer_sequence(std::make_integer_sequence())) integer_sequence; + + /** \brief default constructor **/ + constexpr StaticIntegralRange() noexcept = default; + + /** \brief cast into dynamic IntegralRange **/ + constexpr operator IntegralRange() const noexcept { return {from, to}; } + /** \brief cast into corresponding std::integer_sequence **/ + constexpr operator integer_sequence() const noexcept { return {}; } + + /** \brief obtain a random-access iterator to the first element **/ + static constexpr iterator begin() noexcept { return iterator(from); } + /** \brief obtain a random-access iterator past the last element **/ + static constexpr iterator end() noexcept { return iterator(to); } + + /** \brief access specified element (static version) **/ + template + constexpr auto operator[](const std::integral_constant &) const noexcept + -> std::integral_constant(i)> + { + return {}; + } + + /** \brief access specified element (dynamic version) **/ + constexpr value_type operator[](const size_type &i) const noexcept { return (from + static_cast(i)); } + + /** \brief check whether the range is empty **/ + static constexpr std::integral_constant empty() noexcept { return {}; } + /** \brief obtain number of elements in the range **/ + static constexpr std::integral_constant(to) - static_cast(from) > size() noexcept { return {}; } + }; + + /** + \brief free standing function for setting up a range based for loop + over an integer range + for (auto i: range(0,10)) // 0,1,2,3,4,5,6,7,8,9 + or + for (auto i: range(-10,10)) // -10,-9,..,8,9 + or + for (auto i: range(10)) // 0,1,2,3,4,5,6,7,8,9 + */ + template, std::decay_t>::value, int> = 0, + std::enable_if_t>::value, int> = 0> + inline static IntegralRange> range(T &&from, U &&to) noexcept + { + return IntegralRange>(std::forward(from), std::forward(to)); + } + + template>::value, int> = 0> + inline static IntegralRange> range(T &&to) noexcept + { + return IntegralRange>(std::forward(to)); + } + + template>::value, int> = 0> + inline static IntegralRange>> range(T &&to) noexcept + { + return IntegralRange>>(std::forward(to)); + } + + template + inline static StaticIntegralRange range(std::integral_constant, std::integral_constant) noexcept + { + return {}; + } + + template + inline static StaticIntegralRange range(std::integral_constant) noexcept + { + return {}; + } + + + + /** + * \brief Tag to enable value based transformations in TransformedRangeView + */ + struct ValueTransformationTag {}; + + /** + * \brief Tag to enable iterator based transformations in TransformedRangeView + */ + struct IteratorTransformationTag {}; + + namespace Impl + { + + // Helper class to mimic a pointer for proxy objects. + // This is needed to implement operator-> on an iterator + // using proxy-values. It stores the proxy value but + // provides operator-> like a pointer. + template + class PointerProxy + { + public: + PointerProxy(ProxyType&& p) : p_(p) + {} + + ProxyType* operator->() + { + return &p_; + } + + ProxyType p_; + }; + + // An iterator transforming a wrapped iterator using + // an unary function. It inherits the iterator-category + // of the underlying iterator. + template ::iterator_category> + class TransformedRangeIterator; + + template + class TransformedRangeIterator + { + protected: + + static decltype(auto) transform(const F& f, const I& it) { + if constexpr (std::is_same_v) + return f(it); + else + return f(*it); + } + + public: + using iterator_category = std::forward_iterator_tag; + using reference = decltype(transform(std::declval(), std::declval())); + using value_type = std::decay_t; + using pointer = PointerProxy; + + // If we later want to allow standalone TransformedRangeIterators, + // we could customize the FunctionPointer to be a default-constructible, + // copy-assignable type storing a function but acting like a pointer + // to function. + using FunctionPointer = const F*; + + constexpr TransformedRangeIterator(const I& it, FunctionPointer f) noexcept : + it_(it), + f_(f) + {} + + // Explicitly initialize members. Using a plain + // + // constexpr TransformedRangeIterator() noexcept {} + // + // would default-initialize the members while + // + // constexpr TransformedRangeIterator() noexcept : it_(), f_() {} + // + // leads to value-initialization. This is a case where + // both are really different. If it_ is a raw pointer (i.e. POD) + // then default-initialization leaves it uninitialized while + // value-initialization zero-initializes it. + constexpr TransformedRangeIterator() noexcept : + it_(), + f_() + {} + + // Dereferencing returns a value created by the function + constexpr reference operator*() const noexcept { + return transform(*f_, it_); + } + + // Dereferencing returns a value created by the function + pointer operator->() const noexcept { + return transform(*f_, it_); + } + + constexpr TransformedRangeIterator& operator=(const TransformedRangeIterator& other) = default; + + constexpr bool operator==(const TransformedRangeIterator& other) const noexcept { + return (it_ == other.it_); + } + + constexpr bool operator!=(const TransformedRangeIterator& other) const noexcept { + return (it_ != other.it_); + } + + TransformedRangeIterator& operator++() noexcept { + ++it_; + return *this; + } + + TransformedRangeIterator operator++(int) noexcept { + TransformedRangeIterator copy(*this); + ++(*this); + return copy; + } + + protected: + I it_; + FunctionPointer f_; + }; + + + + template + class TransformedRangeIterator : + public TransformedRangeIterator + { + protected: + using Base = TransformedRangeIterator; + using Base::it_; + using Base::f_; + public: + using iterator_category = std::bidirectional_iterator_tag; + using reference = typename Base::reference; + using value_type = typename Base::value_type; + using pointer = typename Base::pointer; + + using FunctionPointer = typename Base::FunctionPointer; + + using Base::Base; + + // Member functions of the forward_iterator that need + // to be redefined because the base class methods return a + // forward_iterator. + constexpr TransformedRangeIterator& operator=(const TransformedRangeIterator& other) = default; + + TransformedRangeIterator& operator++() noexcept { + ++it_; + return *this; + } + + TransformedRangeIterator operator++(int) noexcept { + TransformedRangeIterator copy(*this); + ++(*this); + return copy; + } + + // Additional member functions of bidirectional_iterator + TransformedRangeIterator& operator--() noexcept { + --(this->it_); + return *this; + } + + TransformedRangeIterator operator--(int) noexcept { + TransformedRangeIterator copy(*this); + --(*this); + return copy; + } + }; + + + + template + class TransformedRangeIterator : + public TransformedRangeIterator + { + protected: + using Base = TransformedRangeIterator; + using Base::it_; + using Base::f_; + public: + using iterator_category = std::random_access_iterator_tag; + using reference = typename Base::reference; + using value_type = typename Base::value_type; + using pointer = typename Base::pointer; + using difference_type = typename std::iterator_traits::difference_type; + + using FunctionPointer = typename Base::FunctionPointer; + + using Base::Base; + + // Member functions of the forward_iterator that need + // to be redefined because the base class methods return a + // forward_iterator. + constexpr TransformedRangeIterator& operator=(const TransformedRangeIterator& other) = default; + + TransformedRangeIterator& operator++() noexcept { + ++it_; + return *this; + } + + TransformedRangeIterator operator++(int) noexcept { + TransformedRangeIterator copy(*this); + ++(*this); + return copy; + } + + // Member functions of the bidirectional_iterator that need + // to be redefined because the base class methods return a + // bidirectional_iterator. + TransformedRangeIterator& operator--() noexcept { + --(this->it_); + return *this; + } + + TransformedRangeIterator operator--(int) noexcept { + TransformedRangeIterator copy(*this); + --(*this); + return copy; + } + + // Additional member functions of random_access_iterator + TransformedRangeIterator& operator+=(difference_type n) noexcept { + it_ += n; + return *this; + } + + TransformedRangeIterator& operator-=(difference_type n) noexcept { + it_ -= n; + return *this; + } + + bool operator<(const TransformedRangeIterator& other) noexcept { + return it_(const TransformedRangeIterator& other) noexcept { + return it_>other.it_; + } + + bool operator>=(const TransformedRangeIterator& other) noexcept { + return it_>=other.it_; + } + + reference operator[](difference_type n) noexcept { + return Base::transform(*f_, it_+n); + } + + friend + TransformedRangeIterator operator+(const TransformedRangeIterator& it, difference_type n) noexcept { + return TransformedRangeIterator(it.it_+n, it.f_); + } + + friend + TransformedRangeIterator operator+(difference_type n, const TransformedRangeIterator& it) noexcept { + return TransformedRangeIterator(n+it.it_, it.f_); + } + + friend + TransformedRangeIterator operator-(const TransformedRangeIterator& it, difference_type n) noexcept { + return TransformedRangeIterator(it.it_-n, it.f_); + } + + friend + difference_type operator-(const TransformedRangeIterator& first, const TransformedRangeIterator& second) noexcept { + return first.it_-second.it_; + } + }; + + + } // namespace Impl + + + + /** + * \brief A range transforming the values of another range on-the-fly + * + * This behaves like a range providing `begin()` and `end()`. + * The iterators over this range internally iterate over + * the wrapped range. When dereferencing the iterator, + * the value is transformed on-the-fly using a given + * transformation function leaving the underlying range + * unchanged. + * + * The transformation may either return temporary values + * or l-value references. In the former case the range behaves + * like a proxy-container. In the latter case it forwards these + * references allowing, e.g., to sort a subset of some container + * by applying a transformation to an index-range for those values. + * + * The iterators of the TransformedRangeView have the same + * iterator_category as the ones of the wrapped container. + * + * If range is given as r-value, then the returned TransformedRangeView + * stores it by value, if range is given as (const) l-value, then the + * TransformedRangeView stores it by (const) reference. + * + * If R is a value type, then the TransformedRangeView stores the wrapped range by value, + * if R is a reference type, then the TransformedRangeView stores the wrapped range by reference. + * + * \tparam R Underlying range. + * \tparam F Unary function used to transform the values in the underlying range. + * \tparam T Class for describing how to apply the transformation + * + * T has to be either ValueTransformationTag (default) or IteratorTransformationTag. + * In the former case, the transformation is applied to the values + * obtained by dereferencing the wrapped iterator. In the latter case + * it is applied to the iterator directly, allowing to access non-standard + * functions of the iterator. + **/ + template + class TransformedRangeView + { + using RawConstIterator = std::decay_t().begin())>; + using RawIterator = std::decay_t().begin())>; + + public: + + /** + * \brief Const iterator type + * + * This inherits the iterator_category of the iterators + * of the underlying range. + */ + using const_iterator = Impl::TransformedRangeIterator; + + /** + * \brief Iterator type + * + * This inherits the iterator_category of the iterators + * of the underlying range. + */ + using iterator = Impl::TransformedRangeIterator; + + /** + * \brief Export type of the wrapped untransformed range. + * + * Notice that this will always be the raw type with references + * removed, even if a reference is stored. + */ + using RawRange = std::remove_reference_t; + + /** + * \brief Construct from range and function + */ + template + constexpr TransformedRangeView(RR&& rawRange, const F& f) noexcept : + rawRange_(std::forward(rawRange)), + f_(f) + { + static_assert(std::is_same_v or std::is_same_v, + "The TransformationType passed to TransformedRangeView has to be either ValueTransformationTag or IteratorTransformationTag."); + } + + /** + * \brief Obtain a iterator to the first element + * + * The life time of the returned iterator is bound to + * the life time of the range since it only contains a + * pointer to the transformation function stored + * in the range. + */ + constexpr const_iterator begin() const noexcept { + return const_iterator(rawRange_.begin(), &f_); + } + + constexpr iterator begin() noexcept { + return iterator(rawRange_.begin(), &f_); + } + + /** + * \brief Obtain a iterator past the last element + * + * The life time of the returned iterator is bound to + * the life time of the range since it only contains a + * pointer to the transformation function stored + * in the range. + */ + constexpr const_iterator end() const noexcept { + return const_iterator(rawRange_.end(), &f_); + } + + constexpr iterator end() noexcept { + return iterator(rawRange_.end(), &f_); + } + + /** + * \brief Obtain the size of the range + * + * This is only available if the underlying range + * provides a size() method. In this case size() + * just forwards to the underlying range's size() method. + * + * Attention: Don't select the template parameters explicitly. + * They are only used to implement SFINAE. + */ + template().size())>> + auto size() const + { + return rawRange_.size(); + } + + /** + * \brief Export the wrapped untransformed range. + */ + const RawRange& rawRange() const + { + return rawRange_; + } + + /** + * \brief Export the wrapped untransformed range. + */ + RawRange& rawRange() + { + return rawRange_; + } + + private: + R rawRange_; + F f_; + }; + + /** + * \brief Create a TransformedRangeView + * + * \param range The range to transform + * \param f Unary function that should the applied to the entries of the range. + * + * This behaves like a range providing `begin()` and `end()`. + * The iterators over this range internally iterate over + * the wrapped range. When dereferencing the iterator, + * the wrapped iterator is dereferenced, + * the given transformation function is applied on-the-fly, + * and the result is returned. + * I.e, if \code it \endcode is the wrapped iterator + * and \code f \endcode is the transformation function, + * then the result of \code f(*it) \endcode is returned + * + * The transformation may either return temporary values + * or l-value references. In the former case the range behaves + * like a proxy-container. In the latter case it forwards these + * references allowing, e.g., to sort a subset of some container + * by applying a transformation to an index-range for those values. + * + * The iterators of the TransformedRangeView have the same + * iterator_category as the ones of the wrapped container. + * + * If range is an r-value, then the TransformedRangeView stores it by value, + * if range is an l-value, then the TransformedRangeView stores it by reference. + **/ + template + auto transformedRangeView(R&& range, const F& f) + { + return TransformedRangeView(std::forward(range), f); + } + + /** + * \brief Create a TransformedRangeView using an iterator transformation + * + * \param range The range to transform + * \param f Unary function that should the applied to the entries of the range. + * + * This behaves like a range providing `begin()` and `end()`. + * The iterators over this range internally iterate over + * the wrapped range. When dereferencing the iterator, + * the given transformation function is applied to the wrapped + * iterator on-the-fly and the result is returned. + * I.e, if \code it \endcode is the wrapped iterator + * and \code f \endcode is the transformation function, + * then the result of \code f(it) \endcode is returned. + * + * The transformation may either return temorary values + * or l-value references. In the former case the range behaves + * like a proxy-container. In the latter case it forwards these + * references allowing, e.g., to sort a subset of some container + * by applying a transformation to an index-range for those values. + * + * The iterators of the TransformedRangeView have the same + * iterator_category as the ones of the wrapped container. + * + * If range is an r-value, then the TransformedRangeView stores it by value, + * if range is an l-value, then the TransformedRangeView stores it by reference. + **/ + template + auto iteratorTransformedRangeView(R&& range, const F& f) + { + return TransformedRangeView(std::forward(range), f); + } + + + /** + * \brief Allow structured-binding for-loops for sparse iterators + * + * Given a sparse range `R` whose iterators `it` + * provide (additionally to dereferencing) a method + * `it->index()` for accessing the index of the current entry in the + * sparse range, this allows to write code like + * \code + * for(auto&& [A_i, i] : sparseRange(R)) + * doSomethingWithValueAndIndex(A_i, i); + * \endcode + */ + template + auto sparseRange(Range&& range) { + return Dune::iteratorTransformedRangeView(std::forward(range), [](auto&& it) { + return std::tuple(*it, it.index()); + }); + } + + /** + * @} + */ + +} + +#endif // DUNE_COMMON_RANGE_UTILITIES_HH diff --git a/dune/common/reservedvector.hh b/dune/common/reservedvector.hh new file mode 100644 index 0000000..69df9c2 --- /dev/null +++ b/dune/common/reservedvector.hh @@ -0,0 +1,233 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_RESERVEDVECTOR_HH +#define DUNE_COMMON_RESERVEDVECTOR_HH + +/** \file + * \brief An stl-compliant random-access container which stores everything on the stack + */ + +#include +#include +#include +#include +#include + +#include + +#ifdef CHECK_RESERVEDVECTOR +#define CHECKSIZE(X) assert(X) +#else +#define CHECKSIZE(X) {} +#endif + +namespace Dune +{ + /** + \brief A Vector class with statically reserved memory. + + ReservedVector is something between std::array and std::vector. + It is a dynamically sized vector which can be extended and shrunk + using methods like push_back and pop_back, but reserved memory is + statically predefined. + + This implies that the vector cannot grow bigger than the predefined + maximum size. + + \tparam T The data type ReservedVector stores. + \tparam n The maximum number of objects the ReservedVector can store. + + */ + template + class ReservedVector + { + public: + + /** @{ Typedefs */ + + //! The type of object, T, stored in the vector. + typedef T value_type; + //! Pointer to T. + typedef T* pointer; + //! Reference to T + typedef T& reference; + //! Const reference to T + typedef const T& const_reference; + //! An unsigned integral type. + typedef size_t size_type; + //! A signed integral type. + typedef std::ptrdiff_t difference_type; + //! Iterator used to iterate through a vector. + typedef Dune::GenericIterator iterator; + //! Const iterator used to iterate through a vector. + typedef Dune::GenericIterator const_iterator; + + /** @} */ + + /** @{ Constructors */ + + //! Constructor + ReservedVector() = default; + + ReservedVector(std::initializer_list const &l) + { + assert(l.size() <= n);// Actually, this is not needed any more! + sz = l.size(); + std::copy_n(l.begin(), sz, data); + } + + /** @} */ + + bool operator == (const ReservedVector & other) const + { + bool eq = (sz == other.sz); + for (size_type i=0; ii); + return data[i]; + } + + //! Returns a const reference to the i'th element. + const_reference operator[] (size_type i) const + { + CHECKSIZE(sz>i); + return data[i]; + } + + //! Returns reference to first element of vector. + reference front() + { + CHECKSIZE(sz>0); + return data[0]; + } + + //! Returns const reference to first element of vector. + const_reference front() const + { + CHECKSIZE(sz>0); + return data[0]; + } + + //! Returns reference to last element of vector. + reference back() + { + CHECKSIZE(sz>0); + return data[sz-1]; + } + + //! Returns const reference to last element of vector. + const_reference back() const + { + CHECKSIZE(sz>0); + return data[sz-1]; + } + + /** @} */ + + /** @{ Informative Methods */ + + //! Returns number of elements in the vector. + size_type size () const + { + return sz; + } + + //! Returns true if vector has no elements. + bool empty() const + { + return sz==0; + } + + //! Returns current capacity (allocated memory) of the vector. + static constexpr size_type capacity() + { + return n; + } + + //! Returns the maximum length of the vector. + static constexpr size_type max_size() + { + return n; + } + + /** @} */ + + //! Send ReservedVector to an output stream + friend std::ostream& operator<< (std::ostream& s, const ReservedVector& v) + { + for (size_t i=0; i)) + +#undef CHECKSIZE + +#endif // DUNE_COMMON_RESERVEDVECTOR_HH diff --git a/dune/common/scalarmatrixview.hh b/dune/common/scalarmatrixview.hh new file mode 100644 index 0000000..275ff47 --- /dev/null +++ b/dune/common/scalarmatrixview.hh @@ -0,0 +1,204 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_SCALARMATRIXVIEW_HH +#define DUNE_COMMON_SCALARMATRIXVIEW_HH + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +namespace Dune { + +namespace Impl { + + /** + @addtogroup DenseMatVec + @{ + */ + + /*! \file + * \brief Implements a scalar matrix view wrapper around an existing scalar. + */ + + /** \brief A wrapper making a scalar look like a matrix + * + * This stores a pointer to a scalar of type K and + * provides the interface of a matrix with a single row + * and column represented by the data behind the pointer. + */ + template + class ScalarMatrixView : + public DenseMatrix> + { + ScalarVectorView data_; + using Base = DenseMatrix>; + + template + friend class ScalarMatrixView; + public: + + //===== type definitions and constants + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain. + //! This is always one for this type. + blocklevel = 1 + }; + + using size_type = typename Base::size_type; + using row_type = typename Base::row_type; + using row_reference = typename Base::row_reference; + using const_row_reference = typename Base::const_row_reference; + + //! export size + enum { + //! \brief The number of rows. + //! This is always one for this type. + rows = 1, + //! \brief The number of columns. + //! This is always one for this type. + cols = 1 + }; + + //===== constructors + /** \brief Default constructor + */ + constexpr ScalarMatrixView () + : data_() + {} + + /** \brief Construct from a pointer to a scalar */ + ScalarMatrixView (K* p) : + data_(p) + {} + + //! Copy constructor + ScalarMatrixView (const ScalarMatrixView &other) : + Base(), + data_(other.data_) + {} + + //! Move constructor + ScalarMatrixView (ScalarMatrixView &&other) : + Base(), + data_( other.data_ ) + {} + + //! Copy assignment operator + ScalarMatrixView& operator= (const ScalarMatrixView& other) + { + data_ = other.data_; + return *this; + } + + template + ScalarMatrixView& operator= (const ScalarMatrixView& other) + { + data_ = other.data_; + return *this; + } + + //! Assignment operator from a scalar + template::value, int> = 0> + inline ScalarMatrixView& operator= (const T& k) + { + data_ = k; + return *this; + } + + // make this thing a matrix + static constexpr size_type mat_rows() { return 1; } + static constexpr size_type mat_cols() { return 1; } + + row_reference mat_access ([[maybe_unused]] size_type i) + { + DUNE_ASSERT_BOUNDS(i == 0); + return data_; + } + + const_row_reference mat_access ([[maybe_unused]] size_type i) const + { + DUNE_ASSERT_BOUNDS(i == 0); + return data_; + } + }; // class ScalarMatrixView + + /** \brief Sends the matrix to an output stream */ + template + std::ostream& operator<< (std::ostream& s, const ScalarMatrixView& a) + { + s << a[0][0]; + return s; + } + + /** \brief Wrap a scalar as a 1-1-matrix */ + template::value, int> = 0> + auto asMatrix(T& t) + { + return ScalarMatrixView{&t}; + } + + /** \brief Wrap a const scalar as a const 1-1-matrix */ + template::value, int> = 0> + auto asMatrix(const T& t) + { + return ScalarMatrixView{&t}; + } + + /** \brief Non-scalar types are assumed to be matrices, and simply forwarded */ + template::value, int> = 0> + T& asMatrix(T& t) + { + return t; + } + + /** \brief Non-scalar types are assumed to be matrices, and simply forwarded */ + template::value, int> = 0> + const T& asMatrix(const T& t) + { + return t; + } + + /** @} end documentation */ + +} // end namespace Impl + + template + struct FieldTraits> : public FieldTraits> {}; + + template + struct DenseMatVecTraits> + { + using derived_type = Impl::ScalarMatrixView; + using row_type = Impl::ScalarVectorView; + using row_reference = row_type&; + using const_row_reference = const row_type&; + using value_type = std::remove_const_t; + using size_type = std::size_t; + }; + + + template + struct AutonomousValueType> + { + using type = FieldMatrix,1,1>; + }; + + +} // end namespace Dune + +#endif // DUNE_COMMON_SCALARMATRIXVIEW_HH diff --git a/dune/common/scalarvectorview.hh b/dune/common/scalarvectorview.hh new file mode 100644 index 0000000..1cadacc --- /dev/null +++ b/dune/common/scalarvectorview.hh @@ -0,0 +1,210 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_SCALARVECTORVIEW_HH +#define DUNE_COMMON_SCALARVECTORVIEW_HH + +#include +#include +#include + +#include +#include +#include +#include + +namespace Dune { + +namespace Impl { + + /** @addtogroup DenseMatVec + @{ + */ + + /*! \file + * \brief Implements a scalar vector view wrapper around an existing scalar. + */ + + /** \brief A wrapper making a scalar look like a vector + * + * This stores a pointer to a scalar of type K and + * provides the interface of a vector with a single + * entry represented by the data behind the pointer. + */ + template + class ScalarVectorView : + public DenseVector> + { + K* dataP_; + using Base = DenseVector>; + + template + friend class ScalarVectorView; + public: + + //! export size + enum { + //! The size of this vector. + dimension = 1 + }; + + /** \brief The type used for array indices and sizes */ + using size_type = typename Base::size_type; + + /** \brief The type used for references to the vector entry */ + using reference = std::decay_t&; + + /** \brief The type used for const references to the vector entry */ + using const_reference = const K&; + + //===== construction + + /** \brief Default constructor */ + constexpr ScalarVectorView () + : dataP_(nullptr) + {} + + /** \brief Construct from a pointer to a scalar */ + ScalarVectorView (K* p) : + dataP_(p) + {} + + //! Copy constructor + ScalarVectorView (const ScalarVectorView &other) : + Base(), + dataP_(other.dataP_) + {} + + //! Move constructor + ScalarVectorView (ScalarVectorView &&other) : + Base(), + dataP_( other.dataP_ ) + {} + + //! Copy assignment operator + ScalarVectorView& operator= (const ScalarVectorView& other) + { + assert(dataP_); + assert(other.dataP_); + *dataP_ = *(other.dataP_); + return *this; + } + + template + ScalarVectorView& operator= (const ScalarVectorView& other) + { + assert(dataP_); + assert(other.dataP_); + *dataP_ = *(other.dataP_); + return *this; + } + + //! Assignment operator from a scalar + template::value, int> = 0> + inline ScalarVectorView& operator= (const T& k) + { + *dataP_ = k; + return *this; + } + + /** \brief Container size -- this is always 1 */ + static constexpr size_type size () + { + return 1; + } + + /** \brief Random access operator, actually disregards its argument */ + K& operator[] ([[maybe_unused]] size_type i) + { + DUNE_ASSERT_BOUNDS(i == 0); + return *dataP_; + } + + /** \brief Const random access operator, actually disregards its argument */ + const K& operator[] ([[maybe_unused]] size_type i) const + { + DUNE_ASSERT_BOUNDS(i == 0); + return *dataP_; + } + }; // class ScalarVectorView + +} // namespace Impl + + + template< class K> + struct DenseMatVecTraits< Impl::ScalarVectorView > + { + using derived_type = Impl::ScalarVectorView; + using value_type = std::remove_const_t; + using size_type = std::size_t; + }; + + template< class K > + struct FieldTraits< Impl::ScalarVectorView > : public FieldTraits> {}; + + template + struct AutonomousValueType> + { + using type = FieldVector,1>; + }; + +namespace Impl { + + /** \brief Read a ScalarVectorView from an input stream + * \relates ScalarVectorView + * + * \note This operator is STL compliant, i.e., the content of v is only + * changed if the read operation is successful. + * + * \param[in] in std :: istream to read from + * \param[out] v ScalarVectorView to be read + * + * \returns the input stream (in) + */ + template + inline std::istream &operator>> ( std::istream &in, ScalarVectorView &v ) + { + K w; + if(in >> w) + v = w; + return in; + } + + + /** \brief Wrap a scalar as a 1-vector */ + template::value, int> = 0> + auto asVector(T& t) + { + return ScalarVectorView{&t}; + } + + /** \brief Wrap a const scalar as a const 1-vector */ + template::value, int> = 0> + auto asVector(const T& t) + { + return ScalarVectorView{&t}; + } + + /** \brief Non-scalar types are assumed to be arrays, and simply forwarded */ + template::value, int> = 0> + T& asVector(T& t) + { + return t; + } + + /** \brief Non-scalar types are assumed to be arrays, and simply forwarded */ + template::value, int> = 0> + const T& asVector(const T& t) + { + return t; + } + +} // end namespace Impl + +} // end namespace Dune + +#endif // DUNE_COMMON_SCALARVECTORVIEW_HH diff --git a/dune/common/shared_ptr.hh b/dune/common/shared_ptr.hh new file mode 100644 index 0000000..ed1398b --- /dev/null +++ b/dune/common/shared_ptr.hh @@ -0,0 +1,123 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_SHARED_PTR_HH +#define DUNE_SHARED_PTR_HH + +#include + +#include +/** + * @file + * @brief This file implements several utilities related to std::shared_ptr + * @author Markus Blatt + */ +namespace Dune +{ + /** + @brief implements the Deleter concept of shared_ptr without deleting anything + @relates shared_ptr + + If you allocate an object on the stack, but want to pass it to a class or function as a shared_ptr, + you can use this deleter to avoid accidental deletion of the stack-allocated object. + + For convenience we provide two free functions to create a shared_ptr from a stack-allocated object + (\see stackobject_to_shared_ptr): + + 1) Convert a stack-allocated object to a shared_ptr: + @code + int i = 10; + std::shared_ptr pi = stackobject_to_shared_ptr(i); + @endcode + 2) Convert a stack-allocated object to a std::shared_ptr of a base class + @code + class A {}; + class B : public A {}; + + ... + + B b; + std::shared_ptr
pa = stackobject_to_shared_ptr(b); + @endcode + + @tparam T type of the stack-allocated object + */ + template + struct null_deleter + { + void operator() (T*) const {} + }; + + /** + @brief Create a shared_ptr for a stack-allocated object + @relatesalso null_deleter + @code + #include + @endcode + + Usage: + @code + int i = 10; + std::shared_ptr pi = stackobject_to_shared_ptr(i); + @endcode + The @c std::shared_ptr points to the object on the stack, but its deleter is + set to an instance of @c null_deleter so that nothing happens when the @c + shared_ptr is destroyed. + + @sa null_deleter + */ + template + inline std::shared_ptr stackobject_to_shared_ptr(T & t) + { + return std::shared_ptr(&t, null_deleter()); + } + + + /** + * \brief Capture R-value reference to shared_ptr + * + * This will store a copy of the passed object in + * a shared_ptr. + * + * The two overloads of wrap_or_move are intended + * to capture references and temporaries in a unique + * way without creating copies and only moving if + * necessary. + * + * Be careful: Only use this function if you are + * aware of it's implications. You can e.g. easily + * end up storing a reference to a temporary if + * you use this inside of another function without + * perfect forwarding. + */ + template + auto wrap_or_move(T&& t) + { + return std::make_shared>(std::forward(t)); + } + + /** + * \brief Capture L-value reference to std::shared_ptr + * + * This will store a pointer for the passed reference + * in a non-owning std::shared_ptr. + * + * The two overloads of wrap_or_move are intended + * to capture references and temporaries in a unique + * way without creating copies and only moving if + * necessary. + * + * Be careful: Only use this function if you are + * aware of it's implications. You can e.g. easily + * end up storing a reference to a temporary if + * you use this inside of another function without + * perfect forwarding. + */ + template + auto wrap_or_move(T& t) + { + return stackobject_to_shared_ptr(t); + } + +} +#endif diff --git a/dune/common/simd.hh b/dune/common/simd.hh new file mode 100644 index 0000000..62c4618 --- /dev/null +++ b/dune/common/simd.hh @@ -0,0 +1,501 @@ +#ifndef DUNE_COMMON_SIMD_HH +#define DUNE_COMMON_SIMD_HH + +#warning dune/common/simd.hh is deprecated. +#warning Use the new infrastructure from dune/common/simd/simd.h instead. + +/** + \file + + \brief Abstractions for support of dedicated SIMD data types + + Libraries like Vc (https://github.com/VcDevel/Vc) add high-level + data types for SIMD (or vectorization) support in C++. Most of + these operations mimic the behavior of a numerical data type. Some + boolean operations can not be implemented in a compatible way to + trivial data types. + + This header contains additional abstractions to help writing code + that works with trivial numerical data types (like double) and Vc + vectorization data types. + + See also the conditional.hh and range_utils.hh headers. + + \deprecated Use the newer simd architecture from dune/common/simd/simd.hh + instead. + */ + +#include +#include +#include +#include + +#include +#include +#include +#if HAVE_VC +// include Vc part of new simd interface to provide compatibility for +// functionality that has been switched over. +#include +#endif +#include +#include + +namespace Dune +{ + +#if HAVE_VC + namespace VcImpl { + //! A reference-like proxy for elements of random-access vectors. + /** + * This is necessary because Vc's lane-access operation return a proxy + * that cannot constructed by non-Vc code (i.e. code that isn't + * explicitly declared `friend`). This means in particular that there + * is no copy/move constructor, meaning we cannot return such proxies + * from our own functions, such as `lane()`. To work around this, we + * define our own proxy class which internally holds a reference to the + * vector and a lane index. + */ + template + class Proxy + { + static_assert(std::is_same >::value, "Class Proxy " + "may only be instantiated with unqualified types"); + public: + using value_type = typename V::value_type; + + private: + static_assert(std::is_arithmetic::value, + "Only artihmetic types are supported"); + V &vec_; + std::size_t idx_; + + public: + Proxy(std::size_t idx, V &vec) + : vec_(vec), idx_(idx) + { } + + operator value_type() const { return vec_[idx_]; } + + // postfix operators + + template::value> > + value_type operator++(int) { return vec_[idx_]++; } + template::value> > + value_type operator--(int) { return vec_[idx_]--; } + + // unary (prefix) operators + template::value> > + Proxy &operator++() { ++(vec_[idx_]); return *this; } + template::value> > + Proxy &operator--() { --(vec_[idx_]); return *this; } + decltype(auto) operator!() const { return !(vec_[idx_]); } + decltype(auto) operator+() const { return +(vec_[idx_]); } + decltype(auto) operator-() const { return -(vec_[idx_]); } + template::value> > + decltype(auto) operator~() const { return ~(vec_[idx_]); } + + // binary operators +#define DUNE_SIMD_VC_BINARY_OP(OP) \ + template \ + auto operator OP(T &&o) const \ + -> decltype(vec_[idx_] OP valueCast(std::forward(o))) \ + { \ + return vec_[idx_] OP valueCast(std::forward(o)); \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_SIMD_VC_BINARY_OP(*); + DUNE_SIMD_VC_BINARY_OP(/); + DUNE_SIMD_VC_BINARY_OP(%); + + DUNE_SIMD_VC_BINARY_OP(+); + DUNE_SIMD_VC_BINARY_OP(-); + + DUNE_SIMD_VC_BINARY_OP(<<); + DUNE_SIMD_VC_BINARY_OP(>>); + + DUNE_SIMD_VC_BINARY_OP(<); + DUNE_SIMD_VC_BINARY_OP(>); + DUNE_SIMD_VC_BINARY_OP(<=); + DUNE_SIMD_VC_BINARY_OP(>=); + + DUNE_SIMD_VC_BINARY_OP(==); + DUNE_SIMD_VC_BINARY_OP(!=); + + DUNE_SIMD_VC_BINARY_OP(&); + DUNE_SIMD_VC_BINARY_OP(^); + DUNE_SIMD_VC_BINARY_OP(|); + + DUNE_SIMD_VC_BINARY_OP(&&); + DUNE_SIMD_VC_BINARY_OP(||); +#undef DUNE_SIMD_VC_BINARY_OP + +#define DUNE_SIMD_VC_ASSIGNMENT(OP) \ + template \ + auto operator OP(T &&o) \ + -> std::enable_if_t(o)) \ + )>::value, Proxy&> \ + { \ + vec_[idx_] OP valueCast(std::forward(o)); \ + return *this; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_SIMD_VC_ASSIGNMENT(=); + DUNE_SIMD_VC_ASSIGNMENT(*=); + DUNE_SIMD_VC_ASSIGNMENT(/=); + DUNE_SIMD_VC_ASSIGNMENT(%=); + DUNE_SIMD_VC_ASSIGNMENT(+=); + DUNE_SIMD_VC_ASSIGNMENT(-=); + DUNE_SIMD_VC_ASSIGNMENT(<<=); + DUNE_SIMD_VC_ASSIGNMENT(>>=); + DUNE_SIMD_VC_ASSIGNMENT(&=); + DUNE_SIMD_VC_ASSIGNMENT(^=); + DUNE_SIMD_VC_ASSIGNMENT(|=); +#undef DUNE_SIMD_VC_ASSIGNMENT + + // swap on proxies swaps the proxied vector entries. As such, it + // applies to rvalues of proxies too, not just lvalues + template + friend void swap(Proxy, Proxy); + + template + friend void swap(Proxy p1, T& s2) + { + // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is + // supported by Vc 1.3.2) + T tmp = p1.vec_[p1.idx_]; + p1.vec_[p1.idx_] = s2; + s2 = tmp; + } + + template + friend void swap(T& s1, Proxy p2) + { + T tmp = s1; + s1 = p2.vec_[p2.idx_]; + p2.vec_[p2.idx_] = tmp; + } + }; + + template + void swap(Proxy p1, Proxy p2) + { + typename V1::value_type tmp = p1.vec_[p1.idx_]; + p1.vec_[p1.idx_] = p2.vec_[p2.idx_]; + p2.vec_[p2.idx_] = tmp; + } + } // namespace VcImpl +#endif // HAVE_VC + + template + struct SimdScalarTypeTraits + { + using type = T; + }; + + template + using SimdScalar = typename SimdScalarTypeTraits::type; + +#if HAVE_VC + /* + Add Vc specializations for the SimdScalarTypeTraits trais class + */ + template + struct SimdScalarTypeTraits< Vc::Vector > + { + using type = T; + }; + + template + struct SimdScalarTypeTraits< Vc::SimdArray > + { + using type = T; + }; +#endif // HAVE_VC + + //! deduce the underlying scalar data type of an AlignedNumber + template + struct SimdScalarTypeTraits< AlignedNumber > + { + using type = T; + }; + + template + struct SimdIndexTypeTraits { + using type = std::size_t; + }; + + //! An simd vector of indices corresponding to a simd vector V + /** + * lanes(T()) == lanes(SimdIndex()) holds. + * + * \note The size of the elements of a SimdIndex isn't very well-defined. + * Be careful. + */ + template + using SimdIndex = typename SimdIndexTypeTraits::type; + +#if HAVE_VC + template + struct SimdIndexTypeTraits > { + using type = typename Vc::Vector::index_type; + }; + + template + struct SimdIndexTypeTraits > { + using type = typename Vc::SimdArray::index_type; + }; +#endif // HAVE_VC + + template + struct SimdMaskTypeTraits { + using type = bool; + }; + + //! A simd vector of truth values corresponding to a simd vector V + /** + * lanes(T()) == lanes(SimdMask()) holds. + */ + template + using SimdMask = typename SimdMaskTypeTraits::type; + +#if HAVE_VC + template + struct SimdMaskTypeTraits > { + using type = typename Vc::Vector::mask_type; + }; + + template + struct SimdMaskTypeTraits > { + using type = typename Vc::SimdArray::mask_type; + }; +#endif // HAVE_VC + +#if HAVE_VC + /* + Add Vc specializations for cond(), see conditional.hh + */ + template + Vc::Vector cond(const Vc::Mask & b, + const Vc::Vector & v1, + const Vc::Vector & v2) + { + return std::move(Vc::iif(b, v1, v2)); + } + + template + Vc::SimdArray cond(const typename Vc::SimdArray::mask_type & b, + const Vc::SimdArray & v1, + const Vc::SimdArray & v2) + { + return std::move(Vc::iif(b, v1, v2)); + } +#endif // HAVE_VC + +#if HAVE_VC + /* + Add Vc specializations for several boolean operations, see rangeutitlities.hh: + + max_value, min_value, any_true, all_true + */ + template + T max_value(const Vc::Vector & v) + { + return v.max(); + } + + template + double max_value(const Vc::SimdArray & v) + { + return v.max(); + } + + template + T min_value(const Vc::Vector & v) + { + return v.min(); + } + + template + double min_value(const Vc::SimdArray & v) + { + return v.min(); + } + + template + bool any_true(const Vc::Mask & v) + { + return Vc::any_of(v); + } + + template + bool any_true(const Vc::SimdMaskArray & v) + { + return Vc::any_of(v); + } + + template + bool all_true(const Vc::Mask & v) + { + return Vc::all_of(v); + } + + template + bool all_true(const Vc::SimdMaskArray & v) + { + return Vc::all_of(v); + } +#endif // HAVE_VC + + //! get the number of lanes of a simd vector (scalar version) + template + std::size_t lanes(const T &) { return 1; } + + //! access a lane of a simd vector (scalar version) + template + T lane(std::size_t l, const T &v) + { + assert(l == 0); + return v; + } + + //! access a lane of a simd vector (scalar version) + template + T &lane(std::size_t l, T &v) + { + assert(l == 0); + return v; + } + +#if HAVE_VC + template + std::size_t lanes(const Vc::Vector &) + { + return Vc::Vector::size(); + } + + template + T lane(std::size_t l, const Vc::Vector &v) + { + assert(l < lanes(v)); + return v[l]; + } + + template + auto lane(std::size_t l, Vc::Vector &v) + { + assert(l < lanes(v)); + return VcImpl::Proxy >{l, v}; + } + + template + std::size_t lanes(const Vc::SimdArray &) + { + return n; + } + + template + T lane(std::size_t l, const Vc::SimdArray &v) + { + assert(l < n); + return v[l]; + } + + template + auto lane(std::size_t l, Vc::SimdArray &v) + { + assert(l < n); + return VcImpl::Proxy >{l, v}; + } + + template + std::size_t lanes(const Vc::SimdMaskArray &) + { + return n; + } + + template + bool lane(std::size_t l, const Vc::SimdMaskArray &v) + { + assert(l < n); + return v[l]; + } + + template + auto lane(std::size_t l, Vc::SimdMaskArray &v) + { + assert(l < n); + return VcImpl::Proxy >{l, v}; + } +#endif // HAVE_VC + + //! masked Simd assignment (scalar version) + /** + * Assign \c src to \c dest for those lanes where \c mask is true. + */ + template + void assign(T &dst, const T &src, bool mask) + { + if(mask) dst = src; + } + +#if HAVE_VC + /* + Add Vc specializations for masked assignment + */ + template + void assign(Vc::Vector &dst, const Vc::Vector &src, + typename Vc::Vector::mask_type mask) + { + dst(mask) = src; + } + + template + void assign(Vc::SimdArray &dst, const Vc::SimdArray &src, + typename Vc::SimdArray::mask_type mask) + { + dst(mask) = src; + } +#endif // HAVE_VC + + template + void swap(T &v1, T &v2, bool mask) + { + using std::swap; + if(mask) swap(v1, v2); + } + +#if HAVE_VC + /* + Add Vc specializations for masked swap + */ + template + void swap(Vc::Vector &v1, Vc::Vector &v2, + typename Vc::Vector::mask_type mask) + { + auto tmp = v1; + v1(mask) = v2; + v2(mask) = tmp; + } + + template + void swap(Vc::SimdArray &v1, Vc::SimdArray &v2, + typename Vc::SimdArray::mask_type mask) + { + auto tmp = v1; + v1(mask) = v2; + v2(mask) = tmp; + } +#endif // HAVE_VC + +} // end namespace Dune + +#endif // DUNE_COMMON_SIMD_HH diff --git a/dune/common/simd/CMakeLists.txt b/dune/common/simd/CMakeLists.txt new file mode 100644 index 0000000..0a1faf3 --- /dev/null +++ b/dune/common/simd/CMakeLists.txt @@ -0,0 +1,18 @@ +add_subdirectory(test) + +if(NOT VC_FOUND) + exclude_dir_from_headercheck() +endif() + +#install headers +install(FILES + base.hh + defaults.hh + interface.hh + io.hh + loop.hh + simd.hh + standard.hh + test.hh # may be used from dependent modules + vc.hh +DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/common/simd) diff --git a/dune/common/simd/DESIGN.md b/dune/common/simd/DESIGN.md new file mode 100644 index 0000000..e53d188 --- /dev/null +++ b/dune/common/simd/DESIGN.md @@ -0,0 +1,306 @@ +This document is a collection of thoughts and rationals on a proper SIMD +interface for Dune. + +What do we want? +================ + +We want an abstraction layer for SIMD-libraries, that allows the core library +to handle simd-vector-like types where (until now) it could only handle scalar +types. It is expected that those parts of the core library that want to +support vector-like types will need some adaptation to account for corner +cases that appear only when vectorizing. However, it should usually be +uneccessary to maintain a scalar version of the code -- the vectorized version +should be able to handle both vectorized and scalar data types. + +What this abstraction layer does not provide (at least initially) is a way to +actually create vector types from scratch. It must however provide a way to +create corresponding types to a type that already exist. I.e. if your code +got simd-vector-type argument, the abstraction layer will provide you with the +the number of lanes, and the type of the entries. It will also provide you +with a way to create simd types with the same number of lanes but a different +entry type. + +Built-in Types +============== + +We generally do not want to have to modify existing interfaces. This implies +that the built-in types must be a valid "vectorization library" that the +abstraction layer can deal with. Since the built-in types are not classes, +this precludes certain idioms that are widespread among vectorization +libraries. + +For instance if `x` is of a vector type, in many libraries one would access +the `i`'th lane of `x` with the expression `x[i]`. We cannot support this +expression if `x` is of a scalar built-in type, because we can overload +`operator[]` only for class types. An alternative syntax is to use a +free-standing access function `lane(i, x)`. + +Restriction on Vectorization Libraries +====================================== + +We generally expect vectorization libraries to provide all the usual operators +(arithmetic operations, assignment, comparisons) for their vector types. +Comparisons should yield mask types specific to that vectorization library; +these must be summarized to `bool` with functions of the abstraction layer +(like `anyTrue()`) before they can be used in `if`-conditions. + +We may require them to provide conversions from scalar types to vector types +to some extend, however, an exact specification needs more experience. + +Specifically for vectors (or masks) `v1` and `v2` of type `V`, with associated +scalar type `T=Scalar`, we require + +- for any unary arithmetic expression `@v1` (where `@` is one of `+`, `-`, or + `~`): + `lane(l,@v1) == T(@lane(l,v1))` for all `l` + there are no side-effects + +- for any binary arithmetic expression `v1@v2` (where `@` is one of `+`, `-`, + `*`, `/`, `%`, `<<`, `>>`, `&`, `|`, `^`): + `lane(l,v1@v2) == T(lane(l,v1)@lane(l,v2))` for all `l` + there are no side-effects + +- for any compound assignment expression `v1@=v2` (where `@` is one of`+`, + `-`, `*`, `/`, `%`, `<<`, `>>`, `&`, `|`, `^`): + `v1@=v2` has the same side-effects as `lane(l,v1)@=lane(l,v2)` for all `l` + the result of `v1@=v2` is an lvalue denoting `v1` + +- for any comparison expression `v1@v2` (where `@` is one of `==`, `!=`, `<`, + `<=`, `>` or `>=`): + `lane(l,v1@v2) == lane(l,v1)@lane(l,v2)` for all `l` + The result of `v1@v2` is a prvalue of type `Mask` + there are no side-effects + +- for the unary logic expression `!v1`: + `lane(l,!v1) == !lane(l,v1)` for all `l` + The result of `!v1` is a prvalue of type `Mask` + there are no side-effects + +- for any binary logic expression `v1@v2` (where `@` is one of `&&` or `||`): + `lane(l,v1@v2) == lane(l,v1)@lane(l,v2)` for all `l` + The result of `v1@v2` is a prvalue of type `Mask` + there are no side-effects + +Note 1: Short-circuiting may or may not happen for `&&` and `||` -- it will +happen for the built-in types, but it cannot happen for proper multi-lane simd +types. + +Note 2: For all expressions there is a lane-wise equality requirement with the +scalar operation. This requirement is formulated such that promotions of +arguments are permitted, but not required. This is neccessary to allow both +the built-in types (which are promoted) and proper simd types (which typically +are not promoted to stay within the same simd register). + +Note 3: The `==` in the lane-wise equality requirement may be overloaded to +account for proxies returned by `lane()`. + +Note 4: Any expression that is invalid for the scalar case is not required for +the simd case either. + +`#include` Structure +==================== + +There will be one header that ensures that the interface names are available. +This include also pulls in the part of the abstraction layer that enables use +of the built-in scalar types. Any code that only makes use of the abstraction +layer needs to include this header, and only this header. + +Any compilation unit (generally a `.cc`-file) that creates vectorized types +(other then the scalar built-in types) using some vectorization library, and +hands those types to vectorization-enabled dune code, is responsible for + +1. including the neccessary headers providing the abstraction for that + vectorization library, as specified in the documentation of the + abstraction, and + +2. making sure the compilation with all the compiler/linker settings (flags, + defines, libraries) needed by the vectorization library. + +The ADL-Problem +=============== + +Consider the following example of a vectorization of +`Dune::FooVector::two_norm()`, which is implemented in +`dune/common/foovector.hh`: + +```c++ +// SIMD interface and implementation for scalar built-ins +#include + +namespace Dune { + template + class FooVector + { + T data_[FOO]; + public: + T two_norm2() const { + using Dune::Simd::lane; + using Dune::Simd::lanes; + T sum(0); + for(const auto &entry : data_) + { + // break vectorization for demonstration purposes + for(std::size_t l = 0; l < lanes(entry); ++l) + lane(l, sum) += lane(l, entry) * lane(l, entry); + } + return sum; + } + }; +} +``` + +This can then be used like this: + +```c++ +#include +// provide dune-abstraction for mysimdlib +// also pulls in the neccessary includes for mysimdlib +#include + +int main() +{ + using T = mysimdlib::Vector; + Dune::FooVector x(T(0)); + x.two_norm2(); +} +``` + +This will not work. At least not with a straightforward implementation of +`lane()` and `lanes()`, where `dune/common/simd/simdlib.hh` simply puts +overloads into the `Dune::Simd` namespace. Here's why. + +The compiler has several ways to find functions that are not qualified by +namespaces (or something similar). One way is unqualified lookup: the +compiler looks for functions that are in some enclosing scope _at the time the +template containing the function call is read_ (early binding). Another is +argument-dependend lookup (ADL): the compiler looks for functions in the +namespaces associated with the types of its arguments _at the time the +function call is instantiated_ (late binding). + +In the example above, `T` a.k.a. `mysimdlib::Vector` is defined in the +namespace `mysimdlib`, which is unlikely to contain the functions `lane()` and +`lanes()`. The abstraction layer could put overloads of those functions into +that namespace, but, well, you're not supposed to meddle in foreign namespace +unless given special permission. As a consequence, `lane()` and `lanes()` +cannot be found by ADL. + +And they cannot be found by any other lookup either. After preprocessing the +compiler will see something like this: + +```c++ +// from dune/common/simd/interface.hh +namespace Dune::Simd { + std::size_t lanes(double) { return 1; } + double lane(std::size_t, double v) { return v; } + double& lane(std::size_t, double& v) { return v; } +} + +// from dune/common/foovector.hh +namespace Dune { + template + class FooVector + { + T data_[FOO]; + public: + T two_norm2() const { + using Dune::Simd::lane; + using Dune::Simd::lanes; + T sum(0); + for(const auto &entry : data_) + { + // break vectorization for demonstration purposes + for(std::size_t l = 0; l < lanes(entry); ++l) + lane(l, sum) += lane(l, entry) * lane(l, entry); + } + return sum; + } + }; +} + +// from some mysimdlib-specific header +namespace mysimdlib { + class Vector { /*...*/ }; +} + +// from dune/common/simd/mysimdlib.hh +namespace Dune::Simd { + std::size_t lanes(mysimdlib::Vector v); + double lane(std::size_t, mysimdlib::Vector); + double& lane(std::size_t, mysimdlib::Vector&); +} + +// from myprog.cc +int main() +{ + using T = mysimdlib::Vector; + Dune::FooVector x(T(0)); + x.two_norm2(); +} +``` + +At the time when the definition of `Dune::FooVector::two_norm2()` is read, +only the declarations for `lane()` and `lanes()` for scalar built-in types are +visible. By the time `Dune::FooVector::two_norm2()` is +instanciated, the proper declarations for `lane()` and `lanes()` are visible. +But that is too late, because unqualified lookup does early binding. It would +be OK for late binding, but only ADL does that, and ADL does not work as noted +above. + +Note that ADL is the _only_ type of lookup that does late binding. So we +cannot simply require the user to use another type of lookup. + +Working around the ADL Problem +============================== + +To get around the ADL problem, we can attempt the following: + +```c++ +// dune/common/simd/interface.hh + +namespace Dune::Simd { + namespace Overloads { + struct ADLTag {}; + } + + template + std::size_t lanes(T v) + { + return lanes(Overloads::ADLTag(), v); + } + + template + auto lane(std::size_t i, T v) + { + return lane(Overloads::ADLTag(), i, v); + } + + //... + + // implementation for scalar built-ins + namespace Overloads { + std::size_t lanes(ADLTag, double) { return 1; } + double lane(ADLTag, std::size_t, double v) { return v; } + // etc... + } +} +``` + +And for each vectorization library: +```c++ +// dune/common/simd/mysimdlib.hh + +#include + +#include + +namespace Dune::Simd::Overloads { + std::size_t lanes(ADLTag, mysimdlib::Vector v); + double lane(ADLTag, std::size_t, mysimdlib::Vector v); + // etc... +} +``` + +Core Dune code can then use the functions in `Dune::Simd` without +restrictions. These functions themselves make sure to find the implementation +functions via ADL, so that the lookup uses late binding and thus can find +functions that are declared later. diff --git a/dune/common/simd/base.hh b/dune/common/simd/base.hh new file mode 100644 index 0000000..6768d7c --- /dev/null +++ b/dune/common/simd/base.hh @@ -0,0 +1,218 @@ +#ifndef DUNE_COMMON_SIMD_BASE_HH +#define DUNE_COMMON_SIMD_BASE_HH + +/** @file + * @brief Basic definitions for SIMD Implementations + * @ingroup SIMDAbstract + * + * This file provides basic definitions and template declarations that are + * used to write SIMD abtraction layers. + * + * This file should never be included by users of the SIMD + * abstraction. Include instead. + */ + +/** @defgroup SIMD Vectorization + * @ingroup Common + * @brief Abstractions for using vectorization libraries + * + * This vectorization abstraction targets three kinds of developers: + * + * - Application developers create SIMD types (usually with the help of some + * vectorization library) and pass them to the Dune library. They are + * responsible for a compilation unit, typically a .cc file that is compiled + * into a program or part of a library. Since they create the type, they + * have the knowledge which library abstraction is needed and are + * responsible for including that, as well as making sure the correct + * compiler flags are provided. + * + * - Library developers implement support in Dune for handling SIMD types, + * e.g. by extending some existing class. By using the interfaces provided + * here, they should not have to worry about the exact vectorization library + * beeing used, or whether a vectorization library is used at all. + * + * - Abstraction developers provide the necessary hooks to make a + * vectorization library known to this interface. They are also responsible + * for documenting for application developers how to meet the prerequisites + * for using the abstraction, e.g. which headers to include and how to add + * the necessary compiler flags. + */ + +/** @defgroup SIMDApp Application Developer's Interface + * @ingroup SIMD + * @brief How to request vectorization from Dune + * + * This module describes how to pass vectorized types to Dune classes. It + * lists the supported vectorization libraries and how to include each + * (although it cannot list those libraries where support is not part of the + * dune core). + */ + +/** @defgroup SIMDLib Library Developer's Interface + * @ingroup SIMD + * @brief How to support vectorization in Dune classes + * + * This module describes how a Dune library developer can add support for + * vectorization to library facilities. + */ + +/** @defgroup SIMDAbstract Abstraction Developer's Interface + * @ingroup SIMD + * @brief How to add support for a new vectorization library + * + * This module describes the interface that you must implement if you want to + * provide an abstraction layer for some vectorization library. To understand + * some of the design choices, have a look at dune/common/simd/DESIGN.md in + * dune-common's source. + * + * Everything an abstraction implementation needs to provide is in namespace + * `Dune::Simd::Overloads`. + * + * An implementation must specialize all the template classes in namespace + * `Overloads` (with the exception of `Overloads::ADLTag`, see below). To + * make it possible for certain specializations not to participate in overload + * resolution, each template class provides a dummy template parameter \c + * SFINAETag that defaults to \c void. + * + * An implementation must overload all functions within namespace `Overloads` + * that are defined deleted. It may overload other functions if the default + * behaviour is not suitable. All functions take a value of type + * `Overloads::ADLTag` as their first argument to enable + * argument-dependent lookup, to be able to prioritize different overloads + * with respect to each other, and to be able to inhibit certain overloads + * from taking part in overload resolution. See the documentation for + * `Overloads::ADLTag` for a detailed explanation. + * + * An abstraction implementation may not specialize `Overloads::ADLTag`, and + * may not introduce new names into namespace `Overloads`. + */ + +namespace Dune { + namespace Simd { + + //! @brief Namespace for the overloads and specializations that make up a + //! SIMD implementation + /** + * @ingroup SIMDAbstract + * + * This namespace contains three sets of things: the struct ADLTag, which + * is used to look up functions in this namespace using argument-dependet + * lookup, traits classes that must be specialized by abstraction + * implementations, and functions that must/can be overloaded by + * abstraction implementations. + * + * \note Only introduce new names into this namespace to extend the + * interface. This applies in particular to people in the + * "abstraction developer" role; they may meddle in this namespace + * only by providing overloads and/or specializations for existing + * names (and for `ADLTag` even that is prohibited). + */ + namespace Overloads { + + //! @addtogroup SIMDAbstract + //! @{ + + //! Tag used to force late-binding lookup in Dune::Simd::Overloads + /** + * This tag is used by functions in \c Dune::Simd to make + * argument-dependent lookups (ADL) for functions in \c + * Dune::Simd::Overloads. The property of ADL that is used here is that + * it binds the names of functions late, i.e. at the time of + * instantiation, while all other lookups bind early, i.e. at the time + * when the function call is parsed. Using late binding enables a + * function \c foo() to find a functions \c Overloads::foo() that has + * been declared only after \c foo() itself has been defined: + * + * \code + * template + * void foo(V v) + * { + * foo(Overloads::ADLTag<6>{}, v); + * } + * + * struct MyType {}; + * namespace Overloads { + * void foo(ADLTag<4>, MyType v); + * } + * \endcode + * + * \note It is generally an error to declare a function with an ADLTag + * argument outside of namespace Simd::Overloads. An exception + * would be an abstraction implementation that declares all its + * implementation functions in its own implementation namespace, + * and then pulls them into the namespace Overloads by way of \c + * using. + * + * `ADLTag` derives from `ADLTag`. Thus it is possible to + * prioritize overloads by choosing an appropriate \c i. The following + * values for \c i are predefined: + * - `i==0,1`: these are reserved for the defaults. + * - `i==2,3`: these are reserved for the implementation for standard + * types. + * - `i==5,6`: these should normally be used by other implementations + * + * The lower priority should be used by default. The higher priority + * can be used by an implementation to resolve ambiguities, e.g. between + * an overload with a by-value argument and an overload with an + * lvalue-reference argument. + * + * The folloing priorities should not normally be used. However, they + * may sometimes be necessary: + * - \c i==4: override standard implementation, but prefer other + * implementations + * - \c i==7: try to override other implementations + * + * \c i==7 is the highest supported priority. + * + * The second (bool) template argument is to make writing abstraction + * implementations that use SFINAE to remove (some of) their functions + * from the overload set more concise. \c ADLTag is not + * defined, so instead of + * \code + * std::enable_if_t > + * \endcode + * you may write the equivalent + * \code + * ADLTag<4, cond> + * \endcode + */ + template + struct ADLTag; + + template + struct ADLTag : ADLTag {}; + + template<> + struct ADLTag<0> {}; + + //! should have a member type \c type + /** + * Implements `Simd::Scalar`. `V` will never have cv or reference + * qualifiers, no need to strip those. + */ + template + struct ScalarType; + + //! should have a member type \c type + /** + * Implements `Simd::Rebind`. `V` and `S` will never have cv or + * reference qualifiers, no need to strip those. + */ + template + struct RebindType; + + //! should be derived from a `Dune::index_constant` + /** + * Implements `Simd::lanes()`. `V` will never have cv or reference + * qualifiers, no need to strip those. + */ + template + struct LaneCount; + + //! @} Group SIMDAbstract + + } // namespace Overloads + } // namespace Simd +} // namespace Dune + +#endif // DUNE_COMMON_SIMD_BASE_HH diff --git a/dune/common/simd/defaults.hh b/dune/common/simd/defaults.hh new file mode 100644 index 0000000..f89c78c --- /dev/null +++ b/dune/common/simd/defaults.hh @@ -0,0 +1,186 @@ +#ifndef DUNE_COMMON_SIMD_DEFAULTS_HH +#define DUNE_COMMON_SIMD_DEFAULTS_HH + +/** @file + * @brief Default implementations for SIMD Implementations + * @ingroup SIMDAbstract + * + * This file provides default overloads for SIMD implementation functions, and + * deleted placeholders where there are no default implementations. + * + * This file should never be included by users of the SIMD + * abstraction. Include instead. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace Dune { + namespace Simd { + namespace Overloads { + + /** + * @addtogroup SIMDAbstract + * @{ + */ + + /** @name Overloadable and default functions + * + * This group contains functions that you, as an abstraction developer, + * must implement. All functions that are deleted must be provided, + * functions that have a default implementation may be left + * unimplemented if the default behaviour is satisfactory. + * + * @{ + */ + + //! implements Simd::lane() + template + decltype(auto) lane(ADLTag<0>, std::size_t l, V v) = delete; + + //! implements Simd::implCast(V) + template + constexpr V implCast(ADLTag<0>, MetaType, const V &u) + { + return u; + } + + //! implements Simd::implCast(U) + template + constexpr V implCast(ADLTag<0>, MetaType, const U &u) + { + V result(Simd::Scalar(0)); + for(auto l : range(Simd::lanes(u))) + Simd::lane(l, result) = Simd::lane(l, u); + return result; + } + + //! implements Simd::broadcast() + template + auto broadcast(ADLTag<0>, MetaType, S s) + { + return V(Simd::Scalar(s)); + } + + //! implements Simd::cond() + template + V cond(ADLTag<0>, const Mask &mask, + const V &ifTrue, const V &ifFalse) = delete; + + //! implements binary Simd::max() + template + auto max(ADLTag<0>, const V &v1, const V &v2) + { + using std::max; + return max(v1, v2); + } + + //! implements binary Simd::min() + template + auto min(ADLTag<0>, const V &v1, const V &v2) + { + using std::min; + return min(v1, v2); + } + + //! implements Simd::anyTrue() + template + bool anyTrue(ADLTag<0>, const Mask &mask) = delete; + + //! implements Simd::allTrue() + /** + * Default uses Simd::anyTrue() + */ + template + bool allTrue(ADLTag<0>, const Mask &mask) + { + return !Dune::Simd::anyTrue(!mask); + } + + //! implements Simd::anyFalse() + /** + * Default uses Simd::anyTrue() + */ + template + bool anyFalse(ADLTag<0>, const Mask &mask) + { + return Dune::Simd::anyTrue(!mask); + } + + //! implements Simd::allFalse() + /** + * Default uses Simd::anyTrue() + */ + template + bool allFalse(ADLTag<0>, const Mask &mask) + { + return !Dune::Simd::anyTrue(mask); + } + + //! implements Simd::maxValue() + template + auto max(ADLTag<0>, const V &v) + { + Scalar m = Simd::lane(0, v); + for(std::size_t l = 1; l < Simd::lanes(v); ++l) + if(m < Simd::lane(l, v)) + m = Simd::lane(l, v); + return m; + } + + //! implements Simd::minValue() + template + auto min(ADLTag<0>, const V &v) + { + Scalar m = Simd::lane(0, v); + for(std::size_t l = 1; l < Simd::lanes(v); ++l) + if(Simd::lane(l, v) < m) + m = Simd::lane(l, v); + return m; + } + + //! implements Simd::mask() + template + Mask mask(ADLTag<0, std::is_same >::value>, + const V &v) + { + return v; + } + + //! implements Simd::mask() + template + auto mask(ADLTag<0, !std::is_same >::value>, + const V &v) + { + using Copy = AutonomousValue; // just in case we are handed a proxy + return v != Copy(Scalar(0)); + } + + //! implements Simd::maskOr() + template + auto maskOr(ADLTag<0>, const V1 &v1, const V2 &v2) + { + return Simd::mask(v1) || Simd::mask(v2); + } + + //! implements Simd::maskAnd() + template + auto maskAnd(ADLTag<0>, const V1 &v1, const V2 &v2) + { + return Simd::mask(v1) && Simd::mask(v2); + } + + //! @} Overloadable and default functions + //! @} Group SIMDAbstract + } // namespace Overloads + } // namespace Simd +} // namespace Dune + +#endif // DUNE_COMMON_SIMD_DEFAULTS_HH diff --git a/dune/common/simd/interface.hh b/dune/common/simd/interface.hh new file mode 100644 index 0000000..e5ab04f --- /dev/null +++ b/dune/common/simd/interface.hh @@ -0,0 +1,543 @@ +#ifndef DUNE_COMMON_SIMD_INTERFACE_HH +#define DUNE_COMMON_SIMD_INTERFACE_HH + +/** @file + * @brief User interface of the SIMD abstraction + * @ingroup SIMDLib + * + * This file provides the user interface functions of the SIMD abstraction + * layer. + * + * This file should never be included by users of the SIMD + * abstraction. Include instead. + */ + +#include +#include +#include +#include + +#include +#include + +namespace Dune { + + //! @brief Namespace for vectorization interface functions used by library + //! developers + /** + * @ingroup SIMDLib + */ + namespace Simd { + + /** @addtogroup SIMDLib + * + * @{ + * + * @section understand_simd Understanding SIMD types + * + * The (idealized) model of a SIMD type `V` used in this abstraction layer + * is that they are fixed-length vectors of some scalar type `S`. + * Operations and operators that take values of type `S` as arguments, + * except for `operator,()`, should be overloaded to support values of + * type `V` too. These operations should apply element-wise. If the + * operation takes more than one argument, it should accept arbitrary + * combinations of `V` and `S`. The exception is the combination of `S` + * on the left hand side and `V` on the right hand side of one of the + * assignment operators, which does not make sense. + * + * The result of a boolean operation is a mask type `M`, which is a SIMD + * type with scalar type `bool` with the same number of elements as `V`. + * The result of all other operations is again of type `V`, or of some + * type convertible to `V`. + * + * This is very similar to `std::valarray`, with the main difference + * being that `std::valarray` is dynamic in size, while for this + * abstraction the size is static. + * + * @section SIMDLibPromoWarn Type promotion issues + * + * True SIMD types have an issue with type promotion, which means they + * cannot behave completely analogous to built-in integral types (this is + * a non-issue with floating point types). Essentially, operations on + * true SIMD types cannot promote their arguments, because the promoted + * types typically require more storage than the original types, meaning + * an argument that was passed in a single vector register would need + * multiple vector registers after promotion, which would mean greater + * register pressure. Also, there would be conversion operations + * required, which (at least on x86) is not typically the case for + * promotions of the built-in types. Lastly, with larger types the vector + * units can typically operate on fewer lanes at a time. + * + * Omitting integral promotions has in many cases no negative impact, + * because many programmers do not really expect them anyway. There are + * however cases where they matter, and for illustration I want to explain + * one that crept up during unit testing. + * + * Here is a simplified (and somewhat pseudo-code) version of the test. + * The test checks the result of unary `-` on `Vc::Vector` + * by comparing the result of unary `-` when applied to the complete + * vector to the result of unary `-` when applied to each lane + * individually. + * \code + * Vc::Vector varg; + * for(std::size_t l = 0; l < lanes(varg); ++l) + * lane(l, varg) = l + 1; + * auto vresult = -varg; + * for(std::size_t l = 0; l < lanes(varg); ++l) + * assert(lane(l, vresult) == -lane(l, varg)); + * \endcode + * The test fails in lane 0. On the left side of the `==`, `lane(0, + * vresult)` is `(unsigned short)65535`, which is the same as `(unsigned + * short)-1`, as it should be. On the right side, `lane(0, varg)` is + * `(unsigned short)1`. `-` promotes its argument, so that becomes + * `(int)1`, and the result of the negation is `(int)-1`. + * + * Now the comparison is `(unsigned short)65535 == (int)-1`. The + * comparison operator applies the *usual arithmetic conversions* to bring + * both operands to the same type. In this case this boils down to + * converting the left side to `int` via integral promotions and the + * comparison becomes `(int)65535 == (int)-1`. The result is of course + * `false` and the assertion triggers. + * + * The only way to thoroughly prevent this kind of problem is to convert + * the result of any operation back to the expected type. In the above + * example, the assertion would need to be written as `assert(lane(l, + * vresult) == static_cast(-lane(l, varg)));`. In + * practice, this should only be a problem with operations on unsigned + * types where the result may be "negative". Most code in Dune will want + * to operate on floating point types, where this is a non-issue. + * + * (Of couse, this is also a problem for code that operates on untrusted + * input, but you should not be doing that with Dune anyway). + * + * Still, when writing code using the SIMD abstractions, you should be + * aware that in the following snippet + * \code + * auto var1 = lane(0, -vec); + * auto var2 = -lane(0, vec); + * \endcode + * the exact types of `var1` and `var2` may be somewhat surprising. + * + * @section simd_abstraction_limit Limitations of the Abstraction Layer + * + * Since the abstraction layer cannot overload operators of SIMD types + * (that would be meddling with the domain of the library that provides + * the SIMD types), nor provide it's own constructors, there are severe + * limitations in what the abstraction layer guarantees. Besides the + * standard types, the first SIMD library supported is Vc, so that is + * where most of the limitations stem from; see \ref SIMDVcRestrictions in + * \ref SIMDVc. + * + * The biggest limitations are with masks. In Vc masks support a very + * restricted set of operations compared to other SIMD types, so in what + * follows we will distinguish between masks with a very small set of + * operations and between vectors with a larger set of operations. + * + * Here is a compact table of the limitations as a quick reference, + * together with suggested workarounds for the constructs that don't work. + * `s` denotes a scalar object/expression (i.e. of type `double` or in the + * case of masks `bool`). `v` denotes a vector/mask object/expression. + * `sv` means that both scalar and vector arguments are accepted. `V` + * denotes a vector/mask type. `@` means any applicable operator that is + * not otherwise listed. + * + * + * \code + | | Vectors | workaround | Masks | workaround | + |-------------------------+---------+----------------------------+-------------+------------------| + | V v(s); | y | | y | | + | V v = s; | y | V v(s); | *N* | V v(s); | + | V v{s}; | *N* | V v(s); | y | V v(s); | + | V v = {s}; | *N* | V v(s); | y | V v(s); | + |-------------------------+---------+----------------------------+-------------+------------------| + | v = s; | y | v = V(s); | *N* | v = V(s); | + | v = {s}; | *N* | v = V(s); | *N* | v = V(s); | + |-------------------------+---------+----------------------------+-------------+------------------| + | v++; ++v; | *N* | v += Scalar(1); | *N*(n/a)[2] | v = V(true); | + | v--; --v; | *N* | v -= Scalar(1); | n/a | | + |-------------------------+---------+----------------------------+-------------+------------------| + | +v; -v; | y | | *N* | none | + | !v; | y | | y | | + | ~v; | y | | *N* | none | + |-------------------------+---------+----------------------------+-------------+------------------| + | sv @ sv; but see below | y | | *N* | none | + |-------------------------+---------+----------------------------+-------------+------------------| + | s << v; s >> v; | *N* | v << V(s); | *N* | none | + |-------------------------+---------+----------------------------+-------------+------------------| + | v == v; v != v; | y | | *N* [1] | !(v ^ v); v ^ v; | + |-------------------------+---------+----------------------------+-------------+------------------| + | v & v; v ^ v; v ¦ v; | y | | y | | + | v && v; v ¦¦ v; | *N* | maskAnd(v,v); maskOr(v,v); | y | | + |-------------------------+---------+----------------------------+-------------+------------------| + | v @= sv; but see below | y | | *N* | none | + | v &= v; v ^= v; v ¦= v; | y | | y | | + |-------------------------+---------+----------------------------+-------------+------------------| + | v, v;[3,4] | *N* | void(v), v; | y | | + * \endcode + * + * Notes: + * + * - [1] In Vc, mask-mask `==` and `!=` operations exist, but the result + * is of type `bool`, i.e. a scalar. + * + * - [2] `++` (either kind) on bools is deprecated by the standard. Our + * test suite does not check for it on masks, but it was supported by Vc + * masks at some point. + * + * - [3] Contrary to the other operators, the expected result for `(sv1, + * sv2)` is exactly `sv2`, no broadcasting applied. + * + * - [4] Try to avoid the use of `operator,` unless both operands are + * built-in types if possible. Libraries had a tendency to overload + * `operator,` to provide for things like container initialization + * before C++11, and these overloads may still be present in the library + * you are using and replace the default meaning of `operator,`. + * + * Support levels: + * + * - `y`: operation generally works; some instances of the operation may + * not apply + * + * - `*N*`: operation generally does not work; some instances of the + * operation may not apply + * + * - `n/a`: operation does not apply (i.e. bitwise operations to + * floating-point operands, `--` (and in the future possibly `++`) to + * boolean operands, assignment operators to scalar left hand sides) + */ + + /** @name Basic interface + * + * Templates and functions in this group are directly implemented by + * templates and functions in namespace Overloads. + * + * @{ + */ + + //! Element type of some SIMD type + /** + * \tparam V The SIMD (mask or vector) type. `const`, `volatile` or + * reference qualifiers are automatically ignored. + * + * Not all operations that access the element of a vector return (a + * reference to) the scalar type -- some may return proxy objects instead. + * Use `autoCopy()` to make sure you are getting a prvalue of the scalar + * type. + * + * Implemented by `Overloads::ScalarType`. + */ + template + using Scalar = typename Overloads::ScalarType >::type; + + //! Construct SIMD type with different scalar type + /** + * \tparam S The new scalar type + * \tparam V The SIMD (mask or vector) type. + * + * The resulting type a SIMD vector of `S` with the same number of lanes + * as `V`. `const`, `volatile` or reference qualifiers in `S` and `V` are + * automatically ignored, and the result will have no such qualifiers. + * + * Implementations shall rebind to `LoopSIMD()>` if they can't + * support a particular rebind natively. + * + * Implemented by `Overloads::RebindType`. + */ + template + using Rebind = + typename Overloads::RebindType, std::decay_t>::type; + + //! @} group Basic interface + + /** @name Syntactic Sugar + * + * Templates and functions in this group provide syntactic sugar, they are + * implemented using the functionality from @ref SimdInterfaceBase, and + * are not customizable by implementations. + * + * @{ + */ + + //! Mask type type of some SIMD type + /** + * \tparam V The SIMD (mask or vector) type. `const`, `volatile` or + * reference qualifiers are automatically ignored. + * + * The mask type is kind of a SIMD vector of `bool` with the same number + * of lanes as `V`. It results from comparison operations between values + * of type `V`. It is only "kind of" a SIMD vector, because the + * guaranteed supported operations are extremely limited. At the moment + * only the logical operators `&&`, `||` and `!` and the "bitwise" + * operators `&`, `^` and `|` between masks are supported, and even with + * those operators you cannot rely on automatic broadcasting of `bool` + * values. + * + * \note In particular, masks do not support comparison. As a workaround + * you can use `^` instead of `!=` and `!(m1 ^ m2)` instead of `m1 + * == m2`. (The reason why comparison is not supported is because + * in Vc `==` and `!=` between masks yield a single `bool` result + * and not a mask.) + * + * This is an alias for `Rebind`. + */ + template + using Mask = Rebind; + + //! @} group Syntactic Sugar + + /** @name Basic interface + * @{ + */ + + //! Number of lanes in a SIMD type + /** + * \tparam V The SIMD (mask or vector) type. `const`, `volatile` + * or reference qualifiers are automatically ignored. + * + * Implemented by `Overloads::LaneCount`. + */ + template + constexpr std::size_t lanes() + { + return Overloads::LaneCount>::value; + } + + //! Extract an element of a SIMD type + /** + * \param l Number of lane to extract + * \param v SIMD object to extract from + * + * \return If `v` is a non-`const` lvalue, a reference + * `Scalar>&`, or a proxy object through which the + * element of `v` may be modified. Overwise, `v` is a `const` + * lvalue or an rvalue, and the result is a prvalue (a temporary) + * of type `Scalar>`. + * + * Implemented by `Overloads::lane()`. + */ + template + decltype(auto) lane(std::size_t l, V &&v) + { + assert(l < lanes()); + return lane(Overloads::ADLTag<7>{}, l, std::forward(v)); + } + + //! Cast an expression from one implementation to another + /** + * Implemented by `Overloads::implCast()` + * + * Requires the scalar type and the number of lanes to match exactly. + * + * This is particularly useful for masks, which often know the type they + * were derived from. This can become a problem when doing a conditional + * operation e.g. on some floating point vector type, but with a mask + * derived from some index vector type. + * + * \note One of the few functions that explicitly take a template + * argument (`V` in this case). + */ + template + constexpr V implCast(U &&u) + { + static_assert(std::is_same, Scalar >::value, + "Scalar types must match exactly in implCast"); + static_assert(lanes() == lanes(), + "Number of lanes must match in implCast"); + return implCast(Overloads::ADLTag<7>{}, MetaType >{}, + std::forward(u)); + } + + //! Broadcast a scalar to a vector explicitly + /** + * Implemented by `Overloads::broadcast()` + * + * This is useful because the syntax for broadcasting can vary wildly + * between implementations. + * + * \note One of the few functions that explicitly take a template + * argument (`V` in this case). + */ + template + constexpr V broadcast(S s) + { + return broadcast(Overloads::ADLTag<7>{}, MetaType >{}, + std::move(s)); + } + + //! Like the ?: operator + /** + * Equivalent to + * \code + * V result; + * for(std::size_t l = 0; l < lanes(mask); ++l) + * lane(l, result) = + * ( lane(l, mask) ? lane(l, ifTrue) : lane(l ifFalse) ); + * return result; + * \endcode + * + * Implemented by `Overloads::cond()`. + */ + template + V cond(M &&mask, const V &ifTrue, const V &ifFalse) + { + return cond(Overloads::ADLTag<7>{}, + implCast >(std::forward(mask)), ifTrue, ifFalse); + } + + //! Like the ?: operator + /** + * Overload for plain bool masks, accepting any simd type + * + * Implemented by `Overloads::cond()`. + */ + template + V cond(bool mask, const V &ifTrue, const V &ifFalse) + { + return mask ? ifTrue : ifFalse; + } + + //! The binary maximum value over two simd objects + /** + * Implemented by `Overloads::max()`. + */ + template + auto max(const V &v1, const V &v2) + { + return max(Overloads::ADLTag<7>{}, v1, v2); + } + + //! The binary minimum value over two simd objects + /** + * Implemented by `Overloads::min()`. + */ + template + auto min(const V &v1, const V &v2) + { + return min(Overloads::ADLTag<7>{}, v1, v2); + } + + //! Whether any entry is `true` + /** + * Implemented by `Overloads::anyTrue()`. + */ + template + bool anyTrue(const Mask &mask) + { + return anyTrue(Overloads::ADLTag<7>{}, mask); + } + + //! Whether all entries are `true` + /** + * Implemented by `Overloads::allTrue()`. + */ + template + bool allTrue(const Mask &mask) + { + return allTrue(Overloads::ADLTag<7>{}, mask); + } + + //! Whether any entry is `false` + /** + * Implemented by `Overloads::anyFalse()`. + */ + template + bool anyFalse(const Mask &mask) + { + return anyFalse(Overloads::ADLTag<7>{}, mask); + } + + //! Whether all entries are `false` + /** + * Implemented by `Overloads::allFalse()`. + */ + template + bool allFalse(const Mask &mask) + { + return allFalse(Overloads::ADLTag<7>{}, mask); + } + + //! The horizontal maximum value over all lanes + /** + * Implemented by `Overloads::max()`. + */ + template + Scalar max(const V &v) + { + return max(Overloads::ADLTag<7>{}, v); + } + + //! The horizontal minimum value over all lanes + /** + * Implemented by `Overloads::min()`. + */ + template + Scalar min(const V &v) + { + return min(Overloads::ADLTag<7>{}, v); + } + + //! Convert to mask, analogue of bool(s) for scalars + /** + * Implemented by `Overloads::mask()`. + */ + template + auto mask(const V &v) + { + return mask(Overloads::ADLTag<7>{}, v); + } + + //! Logic or of masks + /** + * Implemented by `Overloads::maskOr()`. + */ + template + auto maskOr(const V1 &v1, const V2 &v2) + { + return maskOr(Overloads::ADLTag<7>{}, v1, v2); + } + + //! Logic and of masks + /** + * Implemented by `Overloads::maskAnd()`. + */ + template + auto maskAnd(const V1 &v1, const V2 &v2) + { + return maskAnd(Overloads::ADLTag<7>{}, v1, v2); + } + + //! @} group Basic interface + + /** @name Syntactic Sugar + * + * Templates and functions in this group provide syntactic sugar, they are + * implemented using the functionality from @ref SimdInterfaceBase, and + * are not customizable by implementations. + * + * @{ + */ + + //! Number of lanes in a SIMD type + /** + * \tparam V The SIMD (mask or vector) type. + * + * The value of the parameter is ignored; the call is simply forwarded to + * `lanes()`. + */ + template + std::size_t lanes(const V &) + { + return lanes(); + } + + //! @} group Syntactic Sugar + + //! @} Group SIMDLib + + } // namespace Simd +} // namespace Dune + +#endif // DUNE_COMMON_SIMD_INTERFACE_HH diff --git a/dune/common/simd/io.hh b/dune/common/simd/io.hh new file mode 100644 index 0000000..7779614 --- /dev/null +++ b/dune/common/simd/io.hh @@ -0,0 +1,116 @@ +#ifndef DUNE_COMMON_SIMD_IO_HH +#define DUNE_COMMON_SIMD_IO_HH + +/** @file + * @brief IO interface of the SIMD abstraction + * @ingroup SIMDLib + * + * This file provides IO interface functions of the SIMD abstraction layer. + * + * This file is intended for direct inclusion by header making use of the IO + * interface. + */ + +#include +#include + +#include +#include +#include + +namespace Dune { + + namespace SimdImpl { + + template + class Inserter { + T value_; + + public: + Inserter(const T &value) : value_(value) {} + + template::value> > + friend Stream& operator<<(Stream &out, const Inserter &ins) + { + const char *sep = "<"; + for(auto l : range(Simd::lanes(ins.value_))) + { + out << sep << autoCopy(Simd::lane(l, ins.value_)); + sep = ", "; + } + out << '>'; + return out; + } + }; + + template() != 1> > + Inserter io(const V &v) + { + return { v }; + } + + template() == 1> > + Simd::Scalar io(const V &v) + { + return Simd::lane(0, v); + } + + } + + namespace Simd { + + /** @addtogroup SIMDLib + * + * @{ + * + */ + + /** @name IO interface + * + * Templates and functions in this group provide syntactic sugar for IO. + * They are implemented using the functionality from @ref + * SimdInterfaceBase, and are not customizable by implementations. + * + * @{ + */ + + //! construct a stream inserter + /** + * \tparam V The SIMD (mask or vector) type. + * + * Construct an object that can be inserted into an output stream. + * Insertion prints the vector values separated by a comma and a space, + * and surrounded by angular brackets. + */ + template + auto vio(const V &v) + { + return SimdImpl::Inserter{ v }; + } + + //! construct a stream inserter + /** + * \tparam V The SIMD (mask or vector) type. + * + * Construct an object that can be inserted into an output stream. For + * one-lane vectors, behaves the same as scalar insertion. For multi-lane + * vectors, behaves as the inserter returned by `vio()`: insertion prints + * the vector values separated by a comma and a space, and surrounded by + * angular brackets. + */ + template + auto io(const V &v) + { + return SimdImpl::io(v); + } + + //! @} group IO interface + + //! @} Group SIMDLib + + } // namespace Simd +} // namespace Dune + +#endif // DUNE_COMMON_SIMD_IO_HH diff --git a/dune/common/simd/loop.hh b/dune/common/simd/loop.hh new file mode 100644 index 0000000..b2538cd --- /dev/null +++ b/dune/common/simd/loop.hh @@ -0,0 +1,578 @@ +#ifndef DUNE_COMMON_SIMD_LOOP_HH +#define DUNE_COMMON_SIMD_LOOP_HH + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Dune { + +/* + * silence warnings from GCC about using integer operands on a bool + * (when instantiated for T=bool) + */ +#if __GNUC__ >= 7 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wbool-operation" +# pragma GCC diagnostic ignored "-Wint-in-bool-context" +#endif + + /** + * This class specifies a vector-like type deriving from std::array + * for memory management and basic accessibility. + * This type is capable of dealing with all (well-defined) operators + * and is usable with the SIMD-interface. + * + * @tparam T Base type. Could also be vectorized type. + * @tparam S Size + * @tparam minimum alignment. It is inherited to rebound types. + */ + + template + class alignas(A==0?alignof(T):A) LoopSIMD : public std::array { + + public: + + //default constructor + LoopSIMD() { + assert(reinterpret_cast(this) % std::min(alignof(LoopSIMD),alignof(std::max_align_t)) == 0); + } + + // broadcast constructor initializing the content with a given value + LoopSIMD(Simd::Scalar i) : LoopSIMD() { + this->fill(i); + } + + template + explicit LoopSIMD(const LoopSIMD& other) + : std::array(other) + { + assert(reinterpret_cast(this) % std::min(alignof(LoopSIMD),alignof(std::max_align_t)) == 0); + } + + /* + * Definition of basic operators + */ + + //Prefix operators +#define DUNE_SIMD_LOOP_PREFIX_OP(SYMBOL) \ + auto operator SYMBOL() { \ + for(std::size_t i=0; i out; \ + for(std::size_t i=0; i> out; + for(std::size_t i=0; i out = *this; \ + SYMBOL(*this); \ + return out; \ + } \ + static_assert(true, "expecting ;") + + DUNE_SIMD_LOOP_POSTFIX_OP(++); + DUNE_SIMD_LOOP_POSTFIX_OP(--); +#undef DUNE_SIMD_LOOP_POSTFIX_OP + + //Assignment operators +#define DUNE_SIMD_LOOP_ASSIGNMENT_OP(SYMBOL) \ + auto operator SYMBOL(const Simd::Scalar s) { \ + for(std::size_t i=0; i &v) { \ + for(std::size_t i=0; i>=); + DUNE_SIMD_LOOP_ASSIGNMENT_OP(&=); + DUNE_SIMD_LOOP_ASSIGNMENT_OP(|=); + DUNE_SIMD_LOOP_ASSIGNMENT_OP(^=); +#undef DUNE_SIMD_LOOP_ASSIGNMENT_OP + }; + + //Arithmetic operators +#define DUNE_SIMD_LOOP_BINARY_OP(SYMBOL) \ + template \ + auto operator SYMBOL(const LoopSIMD &v, const Simd::Scalar s) { \ + LoopSIMD out; \ + for(std::size_t i=0; i \ + auto operator SYMBOL(const Simd::Scalar s, const LoopSIMD &v) { \ + LoopSIMD out; \ + for(std::size_t i=0; i \ + auto operator SYMBOL(const LoopSIMD &v, \ + const LoopSIMD &w) { \ + LoopSIMD out; \ + for(std::size_t i=0; i \ + auto operator SYMBOL(const LoopSIMD &v, const U s) { \ + LoopSIMD out; \ + for(std::size_t i=0; i \ + auto operator SYMBOL(const LoopSIMD &v, \ + const LoopSIMD &w) { \ + LoopSIMD out; \ + for(std::size_t i=0; i>); + +#undef DUNE_SIMD_LOOP_BITSHIFT_OP + + //Comparison operators +#define DUNE_SIMD_LOOP_COMPARISON_OP(SYMBOL) \ + template \ + auto operator SYMBOL(const LoopSIMD &v, const U s) { \ + Simd::Mask> out; \ + for(std::size_t i=0; i \ + auto operator SYMBOL(const Simd::Scalar s, const LoopSIMD &v) { \ + Simd::Mask> out; \ + for(std::size_t i=0; i \ + auto operator SYMBOL(const LoopSIMD &v, \ + const LoopSIMD &w) { \ + Simd::Mask> out; \ + for(std::size_t i=0; i); + DUNE_SIMD_LOOP_COMPARISON_OP(<=); + DUNE_SIMD_LOOP_COMPARISON_OP(>=); + DUNE_SIMD_LOOP_COMPARISON_OP(==); + DUNE_SIMD_LOOP_COMPARISON_OP(!=); +#undef DUNE_SIMD_LOOP_COMPARISON_OP + + //Boolean operators +#define DUNE_SIMD_LOOP_BOOLEAN_OP(SYMBOL) \ + template \ + auto operator SYMBOL(const LoopSIMD &v, const Simd::Scalar s) { \ + Simd::Mask> out; \ + for(std::size_t i=0; i \ + auto operator SYMBOL(const Simd::Mask s, const LoopSIMD &v) { \ + Simd::Mask> out; \ + for(std::size_t i=0; i \ + auto operator SYMBOL(const LoopSIMD &v, \ + const LoopSIMD &w) { \ + Simd::Mask> out; \ + for(std::size_t i=0; i + std::ostream& operator<< (std::ostream &os, const LoopSIMD &v) { + os << "["; + for(std::size_t i=0; i + struct ScalarType> { + using type = Simd::Scalar; + }; + + template + struct RebindType> { + using type = LoopSIMD,S,A>; + }; + + //Implementation of SIMD-interface-functionality + template + struct LaneCount> : index_constant()> {}; + + template + auto lane(ADLTag<5>, std::size_t l, LoopSIMD &&v) + -> decltype(std::move(Simd::lane(l%lanes(), v[l/lanes()]))) + { + return std::move(Simd::lane(l%lanes(), v[l/lanes()])); + } + + template + auto lane(ADLTag<5>, std::size_t l, const LoopSIMD &v) + -> decltype(Simd::lane(l%lanes(), v[l/lanes()])) + { + return Simd::lane(l%lanes(), v[l/lanes()]); + } + + template + auto lane(ADLTag<5>, std::size_t l, LoopSIMD &v) + -> decltype(Simd::lane(l%lanes(), v[l/lanes()])) + { + return Simd::lane(l%lanes(), v[l/lanes()]); + } + + template + auto cond(ADLTag<5>, Simd::Mask> mask, + LoopSIMD ifTrue, LoopSIMD ifFalse) { + LoopSIMD out; + for(std::size_t i=0; i + auto cond(ADLTag<5, std::is_same >::value + && Simd::lanes() == Simd::lanes >()>, + M mask, LoopSIMD ifTrue, LoopSIMD ifFalse) + { + LoopSIMD out; + for(auto l : range(Simd::lanes(mask))) + Simd::lane(l, out) = Simd::lane(l, mask) ? Simd::lane(l, ifTrue) : Simd::lane(l, ifFalse); + return out; + } + + template + bool anyTrue(ADLTag<5>, LoopSIMD mask) { + bool out = false; + for(std::size_t i=0; i + bool allTrue(ADLTag<5>, LoopSIMD mask) { + bool out = true; + for(std::size_t i=0; i + bool anyFalse(ADLTag<5>, LoopSIMD mask) { + bool out = false; + for(std::size_t i=0; i + bool allFalse(ADLTag<5>, LoopSIMD mask) { + bool out = true; + for(std::size_t i=0; i>::value> > \ + auto expr(const LoopSIMD &v) { \ + using std::expr; \ + LoopSIMD out; \ + for(std::size_t i=0; i>::value> > \ + auto expr(const LoopSIMD &v) { \ + using std::expr; \ + LoopSIMD out; \ + for(std::size_t i=0; i \ + auto expr(const LoopSIMD &v) { \ + using std::expr; \ + LoopSIMD out; \ + for(std::size_t i=0; i \ + auto expr(const LoopSIMD,S,A> &v) { \ + using std::expr; \ + LoopSIMD out; \ + for(std::size_t i=0; i \ + auto expr(const LoopSIMD &v, const LoopSIMD &w) { \ + using std::expr; \ + LoopSIMD out; \ + for(std::size_t i=0; i + auto isNaN(const LoopSIMD &v, PriorityTag<3>, ADLTag) { + Simd::Mask> out; + for(auto l : range(S)) + out[l] = Dune::isNaN(v[l]); + return out; + } + + template + auto isInf(const LoopSIMD &v, PriorityTag<3>, ADLTag) { + Simd::Mask> out; + for(auto l : range(S)) + out[l] = Dune::isInf(v[l]); + return out; + } + + template + auto isFinite(const LoopSIMD &v, PriorityTag<3>, ADLTag) { + Simd::Mask> out; + for(auto l : range(S)) + out[l] = Dune::isFinite(v[l]); + return out; + } + } //namepace MathOverloads + + template + struct IsNumber> : + public std::integral_constant::value>{ + }; + +#if __GNUC__ >= 7 +# pragma GCC diagnostic pop +#endif + +} //namespace Dune + +#endif diff --git a/dune/common/simd/simd.hh b/dune/common/simd/simd.hh new file mode 100644 index 0000000..409511d --- /dev/null +++ b/dune/common/simd/simd.hh @@ -0,0 +1,14 @@ +#ifndef DUNE_COMMON_SIMD_SIMD_HH +#define DUNE_COMMON_SIMD_SIMD_HH + +/** @file + * @brief Include file for users of the SIMD abstraction layer + * + * Include this file if you want to be able to handle SIMD types -- do not + * include the internal headers directly. + */ + +#include +#include + +#endif // DUNE_COMMON_SIMD_SIMD_HH diff --git a/dune/common/simd/standard.hh b/dune/common/simd/standard.hh new file mode 100644 index 0000000..73a643d --- /dev/null +++ b/dune/common/simd/standard.hh @@ -0,0 +1,117 @@ +#ifndef DUNE_COMMON_SIMD_STANDARD_HH +#define DUNE_COMMON_SIMD_STANDARD_HH + +/** @file + * @ingroup SIMDStandard + * @brief SIMD abstractions for the standard built-in types + * + * This file should not normally be included by users of the SIMD abstraction + * (i.e. other Dune headers). Neither should it be included by the + * translation units passing built-in types to Dune headers that in turn + * support SIMD types through the SIMD abstraction. Dune-functionality always + * supports built-in types. Either because that functionality does not + * support SIMD types and so only supports built-in types, or if it does + * support SIMD types it must include ``, which in + * turn includes this header. + */ + +#include +#include +#include + +#include +#include +#include + +/** @defgroup SIMDStandard SIMD Abstraction Implementation for standard types + * @ingroup SIMDApp + * + * This implements the vectorization interface for scalar types. It applies + * to any type that does not have a specialized interface implementation. + * + * As an application developer, there is nothing special you need to do to get + * support for standard types in the vectorization abstraction. If the dune + * classes you are using provide support for vectorization, they will include + * ``, which will pull in the abstraction for + * standard types automatically. You simply need to make sure that the types + * themselves are supported: + * - for built-in types there is nothing you need to do, + * - for `std::complex`, you need to `#include ` + * - etc. + */ + +namespace Dune { + namespace Simd { + + namespace Overloads { + + /** @name Specialized classes and overloaded functions + * @ingroup SIMDStandard + * @{ + */ + + //! should have a member type \c type + /** + * Implements Simd::Scalar + */ + template + struct ScalarType { using type = V; }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + */ + template + struct RebindType { using type = S; }; + + //! should be derived from an Dune::index_constant + /** + * Implements Simd::lanes() + */ + template + struct LaneCount : public index_constant<1> { }; + + //! implements Simd::lane() + /** + * This binds to rvalues and const lvalues. It would bind to non-const + * lvalues too, but those are caught by the overload with ADLTag<3>. + * Using a universal reference here would bind to any argument with a + * perfect match. This would mean ambiguous overloads with other + * abstractions, if those only declare overloads for `const TheirType &` + * and `TheirType &`, because because universal references match + * perfectly. + */ + template + V lane(ADLTag<2>, std::size_t, V v) + { + return v; + } + + template + V &lane(ADLTag<3>, std::size_t, V &v) + { + return v; + } + + // No Simd::cond() implementation, the overload for bool masks in the + // interface is sufficient + + //! implements Simd::anyTrue() + inline bool anyTrue(ADLTag<2>, bool mask) { return mask; } + + //! implements Simd::allTrue() + inline bool allTrue(ADLTag<2>, bool mask) { return mask; } + + //! implements Simd::anyFalse() + inline bool anyFalse(ADLTag<2>, bool mask) { return !mask; } + + //! implements Simd::allFalse() + inline bool allFalse(ADLTag<2>, bool mask) { return !mask; } + + //! @} group SIMDStandard + + } // namespace Overloads + } // namespace Simd +} // namespace Dune + +#endif // DUNE_COMMON_SIMD_STANDARD_HH diff --git a/dune/common/simd/test.cc b/dune/common/simd/test.cc new file mode 100644 index 0000000..09177e6 --- /dev/null +++ b/dune/common/simd/test.cc @@ -0,0 +1,23 @@ +#include + +#include +#include + +#include + +void Dune::Simd::UnitTest::complain(const char *file, int line, + const char *func, const char *expr) +{ + log_ << file << ":" << line << ": In " << func << ": Error: check (" << expr + << ") failed" << std::endl; + good_ = false; +} + +void Dune::Simd::UnitTest:: +complain(const char *file, int line, const char *func, + const std::string &opname, const char *expr) +{ + log_ << file << ":" << line << ": In " << func << ", while testing " + << opname << ": Error: check (" << expr << ") failed" << std::endl; + good_ = false; +} diff --git a/dune/common/simd/test.hh b/dune/common/simd/test.hh new file mode 100644 index 0000000..eb22cc1 --- /dev/null +++ b/dune/common/simd/test.hh @@ -0,0 +1,2034 @@ +#ifndef DUNE_COMMON_SIMD_TEST_HH +#define DUNE_COMMON_SIMD_TEST_HH + +/** @file + * @brief Common tests for simd abstraction implementations + * + * This file is an interface header and may be included without restrictions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Dune { + namespace Simd { + + namespace Impl { + + template + struct CanCall; // not defined unless Expr has the form Op(Args...) + template + struct CanCall : std::false_type {}; + template + struct CanCall > > + : std::true_type + {}; + + template + struct LessThenComparable : std::false_type {}; + template + struct LessThenComparable() + < std::declval())> > : + std::true_type + {}; + + template + struct CopyConstHelper + { + using type = Dst; + }; + template + struct CopyConstHelper + { + using type = std::add_const_t; + }; + + template + struct CopyVolatileHelper + { + using type = Dst; + }; + template + struct CopyVolatileHelper + { + using type = std::add_volatile_t; + }; + + template + struct CopyReferenceHelper + { + using type = Dst; + }; + template + struct CopyReferenceHelper + { + using type = std::add_lvalue_reference_t; + }; + + template + struct CopyReferenceHelper + { + using type = std::add_rvalue_reference_t; + }; + + template + using CopyRefQual = typename CopyReferenceHelper< + typename CopyVolatileHelper< + typename CopyConstHelper< + std::decay_t, + std::remove_reference_t + >::type, + std::remove_reference_t + >::type, + Src + >::type; + + template::value - 1> > + struct RemoveEnd; + template + struct RemoveEnd> + { + using Back = TypeListEntry_t::value - 1, Types>; + static_assert(std::is_same::value, + "TypeList not terminated by proper EndMark"); + using type = TypeList...>; + }; + + template + struct TypeInList; + + template + struct TypeInList > : std::false_type {}; + + template + struct TypeInList > : std::true_type {}; + + template + struct TypeInList, + std::enable_if_t::value> > : + TypeInList >::type + {}; + + template + struct IsLoop : std::false_type {}; + template + struct IsLoop > : std::true_type {}; + + // used inside static_assert to trick the compiler into printing a list + // of types: + // + // static_assert(debugTypes(Std::bool_constant{}), "msg"); + // + // Should include what the type `V` expands to in the error message. + template + constexpr bool debugTypes(std::true_type) { return true; } + template + [[deprecated]] + constexpr bool debugTypes(std::false_type) { return false; } + + } // namespace Impl + + //! final element marker for `RebindList` + struct EndMark {}; + //! A list of types with the final element removed + /** + * This is `TypeList`, where `NoEndTypes...` is `Types...` + * with the final element removed. The final element in `Types...` is + * required to be `EndMark`. + * + * This is useful to construct type lists in generated source files, since + * you don't need to avoid generating a trailing `,` in the list -- just + * terminate it with `EndMark`. + */ + template + using RebindList = + typename Impl::RemoveEnd >::type; + + //! check whether a type is an instance of LoopSIMD + template + using IsLoop = typename Impl::IsLoop::type; + + class UnitTest { + bool good_ = true; + std::ostream &log_ = std::cerr; + // records the types for which checks have started running to avoid + // infinite recursion + std::unordered_set seen_; + + //////////////////////////////////////////////////////////////////////// + // + // Helper functions + // + + void complain(const char *file, int line, const char *func, + const char *expr); + + void complain(const char *file, int line, const char *func, + const std::string &opname, const char *expr); + + // This macro is defined only within this file, do not use anywhere + // else. Doing the actual printing in an external function dramatically + // reduces memory use during compilation. Defined in such a way that + // the call will only happen for failed checks. +#define DUNE_SIMD_CHECK(expr) \ + ((expr) ? void() : complain(__FILE__, __LINE__, __func__, #expr)) + + // the function using this macro must define a way to compute the + // operator name in DUNE_SIMD_OPNAME +#define DUNE_SIMD_CHECK_OP(expr) \ + ((expr) ? void() : complain(__FILE__, __LINE__, __func__, \ + DUNE_SIMD_OPNAME, #expr)) + + // "cast" into a prvalue + template + static std::decay_t prvalue(T &&t) + { + return std::forward(t); + } + + // whether the vector is 42 in all lanes + template + static bool is42(const V &v) + { + bool good = true; + + for(std::size_t l = 0; l < lanes(v); ++l) + // need to cast in case we have a mask type + good &= (lane(l, v) == Scalar(42)); + + return good; + } + + // make a vector that contains the sequence { 1, 2, ... } + template + static V make123() + { + // initialize to avoid undefined behaviour if assigning to lane() + // involves lvalue-to-rvalue conversions, e.g. due to bitmask + // operations. Avoid using broadcast() for initialization to avoid + // test interdependencies. + V vec(Scalar(0)); + for(std::size_t l = 0; l < lanes(vec); ++l) + lane(l, vec) = l + 1; + return vec; + } + + // whether the vector contains the sequence { 1, 2, ... } + template + static bool is123(const V &v) + { + bool good = true; + + for(std::size_t l = 0; l < lanes(v); ++l) + // need to cast in case we have a mask type + good &= (lane(l, v) == Scalar(l+1)); + + return good; + } + + template + static V leftVector() + { + // Avoid using broadcast() for initialization to avoid test + // interdependencies. + V res(Scalar(0)); + for(std::size_t l = 0; l < lanes(res); ++l) + lane(l, res) = Scalar(l+1); + return res; + } + + template + static V rightVector() + { + // Avoid using broadcast() for initialization to avoid test + // interdependencies. + V res(Scalar(0)); + for(std::size_t l = 0; l < lanes(res); ++l) + // do not exceed number of bits in char (for shifts) + // avoid 0 (for / and %) + lane(l, res) = Scalar((l)%7+1); + return res; + } + + template + static T leftScalar() + { + return T(42); + } + + template + static T rightScalar() + { + // do not exceed number of bits in char (for shifts) + // avoid 0 (for / and %) + return T(5); + } + + template + using CanCall = Impl::CanCall; + + template + using CopyRefQual = Impl::CopyRefQual; + + // test whether the Op supports the operation on scalars. We do not use + // `lane()` to obtain the scalars, because that might return a proxy + // object, and we are interested in what exactly the scalar type can do, + // no a proxy that might have more overloads than needed. In addition, + // `lane()` may not preserve `const` and reference qualifiers. + template + using ScalarResult = + decltype(std::declval(). + scalar(std::declval, + Vectors> >()...)); + + ////////////////////////////////////////////////////////////////////// + // + // Check associated types + // + + template + void checkScalar() + { + // check that the type Scalar exists + using T = Scalar; + + static_assert(std::is_same >::value, "Scalar types " + "must not be references, and must not include " + "cv-qualifiers"); + [[maybe_unused]] T a{}; + } + + template + [[deprecated("Warning: please include bool in the Rebinds for " + "simd type V, as Masks are not checked otherwise.")]] + void warnMissingMaskRebind(std::true_type) {} + template + void warnMissingMaskRebind(std::false_type) {} + + template class RebindPrune, + template class RebindAccept, class Recurse> + void checkRebindOf(Recurse recurse) + { + Hybrid::forEach(Rebinds{}, [this,recurse](auto target) { + using T = typename decltype(target)::type; + + // check that the rebound type exists + using W = Rebind; + log_ << "Type " << className() << " rebound to " + << className() << " is " << className() << std::endl; + + static_assert(std::is_same >::value, "Rebound " + "types must not be references, and must not include " + "cv-qualifiers"); + static_assert(lanes() == lanes(), "Rebound types must have " + "the same number of lanes as the original vector " + "types"); + static_assert(std::is_same >::value, "Rebound types " + "must have the bound-to scalar type"); + + if constexpr (RebindPrune{}) { + log_ << "Pruning check of Simd type " << className() + << std::endl; + } + else { + using Impl::debugTypes; + static_assert(debugTypes(RebindAccept{}), + "Rebind is W, but that is not accepted " + "by RebindAccept"); + recurse(MetaType{}); + } + }); + + static_assert(std::is_same, V>, V>::value, "A type " + "rebound to its own scalar type must be the same type " + "as the original type"); + static_assert(std::is_same, Mask >::value, "A type " + "rebound to bool must be the mask type for that type"); + + constexpr bool hasBool = Impl::TypeInList::value; + warnMissingMaskRebind(Std::bool_constant{}); + } + + ////////////////////////////////////////////////////////////////////// + // + // Fundamental checks + // + + template + void checkLanes() + { + // check lanes + static_assert(std::is_same())>::value, + "return type of lanes() should be std::size_t"); + static_assert(std::is_same::value, + "return type of lanes(V{}) should be std::size_t"); + + // the result of lanes() must be constexpr + [[maybe_unused]] constexpr auto size = lanes(); + // but the result of lanes(vec) does not need to be constexpr + DUNE_SIMD_CHECK(lanes() == lanes(V{})); + } + + template + void checkDefaultConstruct() + { + { [[maybe_unused]] V vec; } + { [[maybe_unused]] V vec{}; } + { [[maybe_unused]] V vec = {}; } + } + + template + void checkLane() + { + // Avoid using broadcast() for initialization to avoid test + // interdependencies. + V vec(Scalar(0)); + // check lane() on mutable lvalues + for(std::size_t l = 0; l < lanes(vec); ++l) + lane(l, vec) = l + 1; + for(std::size_t l = 0; l < lanes(vec); ++l) + DUNE_SIMD_CHECK(lane(l, vec) == Scalar(l + 1)); + using MLRes = decltype(lane(0, vec)); + static_assert(std::is_same&>::value || + std::is_same >::value, + "Result of lane() on a mutable lvalue vector must " + "either be a mutable reference to a scalar of that " + "vector or a proxy object (which itself may not be a " + "reference nor const)."); + + // check lane() on const lvalues + const V &vec2 = vec; + for(std::size_t l = 0; l < lanes(vec); ++l) + DUNE_SIMD_CHECK(lane(l, vec2) == Scalar(l + 1)); + using CLRes = decltype(lane(0, vec2)); + static_assert(std::is_same&>::value || + std::is_same >::value, + "Result of lane() on a const lvalue vector must " + "either be a const lvalue reference to a scalar of that " + "vector or a proxy object (which itself may not be a " + "reference nor const)."); + static_assert(!std::is_assignable >::value, + "Result of lane() on a const lvalue vector must not be " + "assignable from a scalar."); + + // check lane() on rvalues + for(std::size_t l = 0; l < lanes(vec); ++l) + DUNE_SIMD_CHECK(lane(l, prvalue(vec)) == Scalar(l + 1)); + using RRes = decltype(lane(0, prvalue(vec))); + // TODO: do we really want to allow Scalar&& here? If we allow it, + // then `auto &&res = lane(0, vec*vec);` creates a dangling reference, + // and the scalar (and even the vector types) are small enough to be + // passed in registers anyway. On the other hand, the only comparable + // accessor function in the standard library that I can think of is + // std::get(), and that does return an rvalue reference in this + // situation. However, that cannot assume anything about the size of + // the returned types. + static_assert(std::is_same >::value || + std::is_same&&>::value, + "Result of lane() on a rvalue vector V must be " + "Scalar or Scalar&&."); + // Can't assert non-assignable, fails for any typical class, + // e.g. std::complex<>. Would need to return const Scalar or const + // Scalar&&, which would inhibit moving from the return value. + // static_assert(!std::is_assignable >::value, + // "Result of lane() on a rvalue vector must not be " + // "assignable from a scalar."); + } + + // check non-default constructors + template + void checkCopyMoveConstruct() + { + // elided copy/move constructors + { V vec (make123()); DUNE_SIMD_CHECK(is123(vec)); } + { V vec = make123() ; DUNE_SIMD_CHECK(is123(vec)); } + { V vec {make123()}; DUNE_SIMD_CHECK(is123(vec)); } + { V vec = {make123()}; DUNE_SIMD_CHECK(is123(vec)); } + + // copy constructors + { V ref(make123()); V vec (ref); + DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } + { V ref(make123()); V vec = ref ; + DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } + { V ref(make123()); V vec {ref}; + DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } + { V ref(make123()); V vec = {ref}; + DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } + { const V ref(make123()); V vec (ref); + DUNE_SIMD_CHECK(is123(vec)); } + { const V ref(make123()); V vec = ref ; + DUNE_SIMD_CHECK(is123(vec)); } + { const V ref(make123()); V vec {ref}; + DUNE_SIMD_CHECK(is123(vec)); } + { const V ref(make123()); V vec = {ref}; + DUNE_SIMD_CHECK(is123(vec)); } + + // move constructors + { V ref(make123()); V vec (std::move(ref)); + DUNE_SIMD_CHECK(is123(vec)); } + { V ref(make123()); V vec = std::move(ref) ; + DUNE_SIMD_CHECK(is123(vec)); } + { V ref(make123()); V vec {std::move(ref)}; + DUNE_SIMD_CHECK(is123(vec)); } + { V ref(make123()); V vec = {std::move(ref)}; + DUNE_SIMD_CHECK(is123(vec)); } + } + + template + void checkBroadcastVectorConstruct() + { + // broadcast copy constructors + { Scalar ref = 42; V vec (ref); + DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar(42)); } + { Scalar ref = 42; V vec = ref ; + DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar(42)); } + // { Scalar ref = 42; V vec {ref}; + // DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar(42)); } + // { Scalar ref = 42; V vec = {ref}; + // DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar(42)); } + { const Scalar ref = 42; V vec (ref); + DUNE_SIMD_CHECK(is42(vec)); } + { const Scalar ref = 42; V vec = ref ; + DUNE_SIMD_CHECK(is42(vec)); } + // { const Scalar ref = 42; V vec {ref}; + // DUNE_SIMD_CHECK(is42(vec)); } + // { const Scalar ref = 42; V vec = {ref}; + // DUNE_SIMD_CHECK(is42(vec)); } + + // broadcast move constructors + { Scalar ref = 42; V vec (std::move(ref)); + DUNE_SIMD_CHECK(is42(vec)); } + { Scalar ref = 42; V vec = std::move(ref) ; + DUNE_SIMD_CHECK(is42(vec)); } + // { Scalar ref = 42; V vec {std::move(ref)}; + // DUNE_SIMD_CHECK(is42(vec)); } + // { Scalar ref = 42; V vec = {std::move(ref)}; + // DUNE_SIMD_CHECK(is42(vec)); } + } + + template + void checkBroadcastMaskConstruct() + { + // broadcast copy constructors + { Scalar ref = 42; V vec (ref); + DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar(42)); } + // { Scalar ref = 42; V vec = ref ; + // DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar(42)); } + { Scalar ref = 42; V vec {ref}; + DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar(42)); } + // { Scalar ref = 42; V vec = {ref}; + // DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar(42)); } + { const Scalar ref = 42; V vec (ref); + DUNE_SIMD_CHECK(is42(vec)); } + // { const Scalar ref = 42; V vec = ref ; + // DUNE_SIMD_CHECK(is42(vec)); } + { const Scalar ref = 42; V vec {ref}; + DUNE_SIMD_CHECK(is42(vec)); } + // { const Scalar ref = 42; V vec = {ref}; + // DUNE_SIMD_CHECK(is42(vec)); } + + // broadcast move constructors + { Scalar ref = 42; V vec (std::move(ref)); + DUNE_SIMD_CHECK(is42(vec)); } + // { Scalar ref = 42; V vec = std::move(ref) ; + // DUNE_SIMD_CHECK(is42(vec)); } + { Scalar ref = 42; V vec {std::move(ref)}; + DUNE_SIMD_CHECK(is42(vec)); } + // { Scalar ref = 42; V vec = {std::move(ref)}; + // DUNE_SIMD_CHECK(is42(vec)); } + } + + // check the implCast function + template + void checkImplCast() + { + { // lvalue arg + FromV fromVec = make123(); + auto toVec = implCast(fromVec); + static_assert(std::is_same::value, + "Unexpected result type for implCast(FromV&)"); + DUNE_SIMD_CHECK(is123(fromVec)); + DUNE_SIMD_CHECK(is123(toVec)); + } + + { // const lvalue arg + const FromV fromVec = make123(); + auto toVec = implCast(fromVec); + static_assert(std::is_same::value, + "Unexpected result type for implCast(const " + "FromV&)"); + DUNE_SIMD_CHECK(is123(toVec)); + } + + { // rvalue arg + auto toVec = implCast(make123()); + static_assert(std::is_same::value, + "Unexpected result type for implCast(FromV&&)"); + DUNE_SIMD_CHECK(is123(toVec)); + } + } + + // check the implCast function + template + void checkImplCast() + { + // check against LoopSIMD + using LoopV = Dune::LoopSIMD, lanes()>; + + checkImplCast(); + checkImplCast(); + checkImplCast(); + } + + // check the broadcast function + template + void checkBroadcast() + { + // broadcast function + { // lvalue arg + Scalar ref = 42; + auto vec = broadcast(ref); + static_assert(std::is_same::value, + "Unexpected result type for broadcast()"); + DUNE_SIMD_CHECK(is42(vec)); + DUNE_SIMD_CHECK(ref == Scalar(42)); + } + + { // const lvalue arg + const Scalar ref = 42; + auto vec = broadcast(ref); + static_assert(std::is_same::value, + "Unexpected result type for broadcast()"); + DUNE_SIMD_CHECK(is42(vec)); + } + + { // rvalue arg + auto vec = broadcast(Scalar(42)); + static_assert(std::is_same::value, + "Unexpected result type for broadcast()"); + DUNE_SIMD_CHECK(is42(vec)); + } + + { // int arg + auto vec = broadcast(42); + static_assert(std::is_same::value, + "Unexpected result type for broadcast()"); + DUNE_SIMD_CHECK(is42(vec)); + } + + { // double arg + auto vec = broadcast(42.0); + static_assert(std::is_same::value, + "Unexpected result type for broadcast()"); + DUNE_SIMD_CHECK(is42(vec)); + } + } + + template + void checkBracedAssign() + { + // copy assignment + { V ref = make123(); V vec; vec = {ref}; + DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } + { const V ref = make123(); V vec; vec = {ref}; + DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } + + // move assignment + { V vec; vec = {make123()}; DUNE_SIMD_CHECK(is123(vec)); } + } + + template + void checkBracedBroadcastAssign() + { + // nothing works here + // // broadcast copy assignment + // { Scalar ref = 42; V vec; vec = {ref}; + // DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar(42)); } + // { const Scalar ref = 42; V vec; vec = {ref}; + // DUNE_SIMD_CHECK(is42(vec)); } + + // // broadcast move assignment + // { Scalar ref = 42; V vec; vec = {std::move(ref)}; + // DUNE_SIMD_CHECK(is42(vec)); } + } + + ////////////////////////////////////////////////////////////////////// + // + // checks for unary operators + // + +#define DUNE_SIMD_POSTFIX_OP(NAME, SYMBOL) \ + struct OpPostfix##NAME \ + { \ + template \ + auto operator()(V&& v) const \ + -> decltype(std::forward(v) SYMBOL) \ + { \ + return std::forward(v) SYMBOL; \ + } \ + } + +#define DUNE_SIMD_PREFIX_OP(NAME, SYMBOL) \ + struct OpPrefix##NAME \ + { \ + template \ + auto operator()(V&& v) const \ + -> decltype(SYMBOL std::forward(v)) \ + { \ + return SYMBOL std::forward(v); \ + } \ + } + + DUNE_SIMD_POSTFIX_OP(Decrement, -- ); + DUNE_SIMD_POSTFIX_OP(Increment, ++ ); + + DUNE_SIMD_PREFIX_OP (Decrement, -- ); + DUNE_SIMD_PREFIX_OP (Increment, ++ ); + + DUNE_SIMD_PREFIX_OP (Plus, + ); + DUNE_SIMD_PREFIX_OP (Minus, - ); + DUNE_SIMD_PREFIX_OP (LogicNot, ! ); + // Do not warn about ~ being applied to bool. (1) Yes, doing that is + // weird, but we do want to test the weird stuff too. (2) It avoids + // running into on + // g++-7.0 through 7.2. Also, ignore -Wpragmas to not warn about an + // unknown -Wbool-operation on compilers that do not know that option. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-warning-option" // clang 6.0.1 +#pragma GCC diagnostic ignored "-Wbool-operation" + DUNE_SIMD_PREFIX_OP (BitNot, ~ ); +#pragma GCC diagnostic pop + +#undef DUNE_SIMD_POSTFIX_OP +#undef DUNE_SIMD_PREFIX_OP + + template + std::enable_if_t< + CanCall())))>::value> + checkUnaryOpV(Op op) + { +#define DUNE_SIMD_OPNAME (className()) + // arguments + auto val = leftVector>(); + + // copy the arguments in case V is a references + auto arg = val; + auto &&result = op(static_cast(arg)); + using T = Scalar >; + for(std::size_t l = 0; l < lanes(val); ++l) + { + // `op` might promote the argument. This is a problem if the + // argument of the operation on the right of the `==` is + // e.g. `(unsigned short)1` and the operation is e.g. unary `-`. + // Then the argument is promoted to `int` before applying the + // negation, and the result is `(int)-1`. However, the left side of + // the `==` is still `(unsigned short)-1`, which typically is the + // same as `(unsigned short)65535`. The `==` promotes the left side + // before comparing, so that becomes `(int)65535`. It will then + // compare `(int)65535` and `(int)-1` and rightly declare them to be + // not equal. + + // To work around this, we explicitly convert the right side of the + // `==` to the scalar type before comparing. + DUNE_SIMD_CHECK_OP + (lane(l, result) + == static_cast(op(lane(l, static_cast(val))))); + } + // op might modify val, verify that any such modification also happens + // in the vector case + for(std::size_t l = 0; l < lanes >(); ++l) + DUNE_SIMD_CHECK_OP(lane(l, val) == lane(l, arg)); +#undef DUNE_SIMD_OPNAME + } + + template + std::enable_if_t< + !CanCall())))>::value> + checkUnaryOpV(Op op) + { + // log_ << "No " << className())))>() + // << std::endl + // << " ==> Not checking " << className() << std::endl; + } + + template + void checkUnaryOpsV(Op op) + { + checkUnaryOpV(op); + checkUnaryOpV(op); + checkUnaryOpV(op); + } + + ////////////////////////////////////////////////////////////////////// + // + // checks for binary operators + // + + // The operators contain an `operator()`, which will be invoked for both + // scalar and vector arguments. The function `scalar()` is used the + // test whether the scalar types support the operation (via + // `ScalarResult`). The difference is that `scalar()` should only ever + // receive `const`-ref-qualified version of `Scalar`, while the + // `operator()` may also be called with proxies representing scalars. +#define DUNE_SIMD_INFIX_OP(NAME, SYMBOL) \ + struct OpInfix##NAME \ + { \ + template \ + decltype(auto) operator()(V1&& v1, V2&& v2) const \ + { \ + return std::forward(v1) SYMBOL std::forward(v2); \ + } \ + template \ + auto scalar(S1&& s1, S2&& s2) const \ + -> decltype(std::forward(s1) SYMBOL std::forward(s2)); \ + } + + // for assign ops, accept only non-const lvalue arguments for scalars. + // This is needed for class scalars (e.g. std::complex) because + // non-const class rvalues are actually usually assignable. Though that + // assignment happens to a temporary, and thus is lost. Except that the + // tests would bind the result of the assignment to a reference. And + // because that result is returned from a function by reference, even + // though it is a temporary passed as an argument to that function, + // accessing the result later is undefined behaviour. +#define DUNE_SIMD_ASSIGN_OP(NAME, SYMBOL) \ + struct OpInfix##NAME \ + { \ + template \ + decltype(auto) operator()(V1&& v1, V2&& v2) const \ + { \ + return std::forward(v1) SYMBOL std::forward(v2); \ + } \ + template \ + auto scalar(S1& s1, S2&& s2) const \ + -> decltype(s1 SYMBOL std::forward(s2)); \ + } + +#define DUNE_SIMD_REPL_OP(NAME, REPLFN, SYMBOL) \ + struct OpInfix##NAME \ + { \ + template \ + decltype(auto) operator()(V1&& v1, V2&& v2) const \ + { \ + return Simd::REPLFN(std::forward(v1), std::forward(v2)); \ + } \ + template \ + auto scalar(S1&& s1, S2&& s2) const \ + -> decltype(std::forward(s1) SYMBOL std::forward(s2)); \ + } + + DUNE_SIMD_INFIX_OP(Mul, * ); + DUNE_SIMD_INFIX_OP(Div, / ); + DUNE_SIMD_INFIX_OP(Remainder, % ); + + DUNE_SIMD_INFIX_OP(Plus, + ); + DUNE_SIMD_INFIX_OP(Minus, - ); + + DUNE_SIMD_INFIX_OP(LeftShift, << ); + DUNE_SIMD_INFIX_OP(RightShift, >> ); + + DUNE_SIMD_INFIX_OP(Less, < ); + DUNE_SIMD_INFIX_OP(Greater, > ); + DUNE_SIMD_INFIX_OP(LessEqual, <= ); + DUNE_SIMD_INFIX_OP(GreaterEqual, >= ); + + DUNE_SIMD_INFIX_OP(Equal, == ); + DUNE_SIMD_INFIX_OP(NotEqual, != ); + + DUNE_SIMD_INFIX_OP(BitAnd, & ); + DUNE_SIMD_INFIX_OP(BitXor, ^ ); + DUNE_SIMD_INFIX_OP(BitOr, | ); + + // Those are not supported in any meaningful way by vectorclass + // We need to test replacement functions maskAnd() and maskOr() instead. + DUNE_SIMD_REPL_OP(LogicAnd, maskAnd, && ); + DUNE_SIMD_REPL_OP(LogicOr, maskOr, || ); + + DUNE_SIMD_ASSIGN_OP(Assign, = ); + DUNE_SIMD_ASSIGN_OP(AssignMul, *= ); + DUNE_SIMD_ASSIGN_OP(AssignDiv, /= ); + DUNE_SIMD_ASSIGN_OP(AssignRemainder, %= ); + DUNE_SIMD_ASSIGN_OP(AssignPlus, += ); + DUNE_SIMD_ASSIGN_OP(AssignMinus, -= ); + DUNE_SIMD_ASSIGN_OP(AssignLeftShift, <<=); + DUNE_SIMD_ASSIGN_OP(AssignRightShift, >>=); + DUNE_SIMD_ASSIGN_OP(AssignAnd, &= ); + DUNE_SIMD_ASSIGN_OP(AssignXor, ^= ); + DUNE_SIMD_ASSIGN_OP(AssignOr, |= ); + +#undef DUNE_SIMD_INFIX_OP +#undef DUNE_SIMD_REPL_OP +#undef DUNE_SIMD_ASSIGN_OP + + // just used as a tag + struct OpInfixComma {}; + + template + void checkCommaOp(const std::decay_t &val1, + const std::decay_t &val2) + { +#define DUNE_SIMD_OPNAME (className()) + static_assert(std::is_same(), + std::declval())), T2>::value, + "Type and value category of the comma operator must " + "match that of the second operand"); + + // copy the arguments in case T1 or T2 are references + auto arg1 = val1; + auto arg2 = val2; + // Do not warn that the left side of the comma operator is unused. + // Seems to work for g++-4.9 and clang++-3.8. Appears to be harmless + // for icpc (14 and 17), and icpc does not seem to issue a warning + // anyway. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-value" + auto &&result = (static_cast(arg1), + static_cast(arg2)); +#pragma GCC diagnostic pop + if(std::is_reference::value) + { + // comma should return the same object as the second argument for + // lvalues and xvalues + DUNE_SIMD_CHECK_OP(&result == &arg2); + // it should not modify any arguments + DUNE_SIMD_CHECK_OP(allTrue(val1 == arg1)); + DUNE_SIMD_CHECK_OP(allTrue(val2 == arg2)); + } + else + { + // comma should return the same value as the second argument for + // prvalues + DUNE_SIMD_CHECK_OP(allTrue(result == arg2)); + // it should not modify any arguments + DUNE_SIMD_CHECK_OP(allTrue(val1 == arg1)); + // second argument is a prvalue, any modifications happen to a + // temporary and we can't detect them + } +#undef DUNE_SIMD_OPNAME + } + + ////////////////////////////////////////////////////////////////////// + // + // checks for vector-vector binary operations + // + + // We check the following candidate operation + // + // vopres = vop1 @ vop2 + // + // against the reference operation + // + // arefres[l] = aref1[l] @ aref2[l] foreach l + // + // v... variables are simd-vectors and a... variables are arrays. The + // operation may modify the operands, but if is does the modification + // needs to happen in both the candidate and the reference. + // + // We do the following checks: + // 1. lanes(vopres) == lanes(vop1) + // 2. lane(l, vopres) == arefres[l] foreach l + // 3. lane(l, vop1) == aref1[l] foreach l + // 4. lane(l, vop2) == aref2[l] foreach l + template + std::enable_if_t > + checkBinaryOpVV(MetaType, MetaType, Op op) + { +#define DUNE_SIMD_OPNAME (className()) + static_assert(std::is_same, std::decay_t >::value, + "Internal testsystem error: called with two types that " + "don't decay to the same thing"); + + // reference arguments + auto vref1 = leftVector>(); + auto vref2 = rightVector>(); + + // candidate arguments + auto vop1 = vref1; + auto vop2 = vref2; + + // candidate operation + auto &&vopres = op(static_cast(vop1), static_cast(vop2)); + using VR = decltype(vopres); + + // check 1. lanes(vopres) == lanes(vop1) + static_assert(lanes >() == lanes >(), + "The result must have the same number of lanes as the " + "operands."); + + // do the reference operation, and simultaneously + // check 2. lane(l, vopres) == arefres[l] foreach l + using T = Scalar >; + for(auto l : range(lanes(vopres))) + { + // see the lengthy comment in `checkUnaryOpV()` as to why the + // `static_cast` around the `op()` is necessary + DUNE_SIMD_CHECK_OP + (lane(l, vopres) + == static_cast(op(lane(l, static_cast(vref1)), + lane(l, static_cast(vref2))))); + } + + // check 3. lane(l, vop1) == aref1[l] foreach l + for(auto l : range(lanes(vop1))) + DUNE_SIMD_CHECK_OP(lane(l, vop1) == lane(l, vref1)); + + // check 4. lane(l, vop2) == aref2[l] foreach l + for(auto l : range(lanes(vop2))) + DUNE_SIMD_CHECK_OP(lane(l, vop2) == lane(l, vref2)); + +#undef DUNE_SIMD_OPNAME + } + + template + std::enable_if_t > + checkBinaryOpVV(MetaType, MetaType, Op op) + { + // log_ << "No " << className())), + // decltype(lane(0, std::declval())))>() + // << std::endl + // << " ==> Not checking " << className() << std::endl; + } + + template + void checkBinaryOpVV(MetaType, MetaType, OpInfixComma) + { + static_assert(std::is_same, std::decay_t >::value, + "Internal testsystem error: called with two types that " + "don't decay to the same thing"); + + checkCommaOp(leftVector>(), + rightVector>()); + } + + ////////////////////////////////////////////////////////////////////// + // + // checks for vector-scalar binary operations + // + + // We check the following candidate operation + // + // vopres = vop1 @ sop2 + // + // against the reference operation + // + // arefres[l] = aref1[l] @ sref2 foreach l + // + // v... variables are simd-vectors, a... variables are arrays, and + // s... variables are scalars. The operation may modify the left + // operand, but if is does the modifications needs to happen in both the + // candidate and the reference. + // + // We do the following checks: + // 1. lanes(vopres) == lanes(vop1) + // 2. lane(l, vopres) == arefres[l] foreach l + // 3. lane(l, vop1) == aref1[l] foreach l + // 4. sop2 is never modified + // 5. sref2 is never modified + // + // In fact, if the property "sref2 is never modified" is violated that + // means the operation is unsuitable for an automatic broadcast of the + // second operand and should not be checked. There are no operations in + // the standard where the second operand is modified like this, but + // there are operations where the first operand is modified -- and this + // check is used for those ops as well by exchanging the first and second + // argument below. + + template + std::enable_if_t > + checkBinaryOpVS(MetaType, MetaType, Op op) + { +#define DUNE_SIMD_OPNAME (className()) + static_assert(std::is_same >, + std::decay_t >::value, + "Internal testsystem error: called with a scalar that " + "does not match the vector type."); + + // initial values + auto sinit2 = rightScalar>(); + + // reference arguments + auto vref1 = leftVector>(); + auto sref2 = sinit2; + + // candidate arguments + auto vop1 = vref1; + auto sop2 = sref2; + + // candidate operation + auto &&vopres = op(static_cast(vop1), static_cast(sop2)); + using VR = decltype(vopres); + + // check 1. lanes(vopres) == lanes(vop1) + static_assert(lanes >() == lanes >(), + "The result must have the same number of lanes as the " + "operands."); + + // check 4. sop2 is never modified + DUNE_SIMD_CHECK_OP(sop2 == sinit2); + + // do the reference operation, and simultaneously check 2. and 5. + using T = Scalar >; + for(auto l : range(lanes(vopres))) + { + // check 2. lane(l, vopres) == arefres[l] foreach l + // see the lengthy comment in `checkUnaryOpV()` as to why the + // `static_cast` around the `op()` is necessary + DUNE_SIMD_CHECK_OP + (lane(l, vopres) + == static_cast(op(lane(l, static_cast(vref1)), + static_cast(sref2) ))); + // check 5. sref2 is never modified + DUNE_SIMD_CHECK_OP(sref2 == sinit2); + } + + // check 3. lane(l, vop1) == aref1[l] foreach l + for(auto l : range(lanes(vop1))) + DUNE_SIMD_CHECK_OP(lane(l, vop1) == lane(l, vref1)); + +#undef DUNE_SIMD_OPNAME + } + + template + std::enable_if_t > + checkBinaryOpVS(MetaType, MetaType, Op op) + { + // log_ << "No " + // << className())), T2)>() + // << std::endl + // << " ==> Not checking " << className() << std::endl; + } + + template + void checkBinaryOpVS(MetaType, MetaType, OpInfixComma) + { + static_assert(std::is_same >, + std::decay_t >::value, + "Internal testsystem error: called with a scalar that " + "does not match the vector type."); + + checkCommaOp(leftVector>(), + rightScalar>()); + } + + ////////////////////////////////////////////////////////////////////// + // + // cross-check scalar-vector binary operations against vector-vector + // + + // We check the following candidate operation + // + // vopres = vop1 @ vop2, where vop2 = broadcast(sref2) + // + // against the reference operation + // + // vrefres = vref1 @ sref2 + // + // v... variables are simd-vectors, a... variables are arrays, and + // s... variables are scalars. + // + // We could check the following properties + // 1. lanes(vopres) == lanes(vop1) + // 2. lane(l, vopres) == lane(l, vrefres) foreach l + // 3. lane(l, vop1) == lane(l, vref1) foreach l + // but these are given by checking the operation against the scalar + // operation in the vector@vector and vector@scalar cases above. + // + // The only thing left to check is: + // 4. lane(l, vop2) foreach l is never modified + + template + std::enable_if_t > + checkBinaryOpVVAgainstVS(MetaType, MetaType, Op op) + { +#define DUNE_SIMD_OPNAME (className()) + static_assert(std::is_same >, + std::decay_t >::value, + "Internal testsystem error: called with a scalar that " + "does not match the vector type."); + + // initial values + auto sinit2 = rightScalar>(); + + // reference arguments + auto vop1 = leftVector>(); + using V2 = CopyRefQual; + std::decay_t vop2(sinit2); + + // candidate operation + op(static_cast(vop1), static_cast(vop2)); + + // 4. lane(l, vop2) foreach l is never modified + for(auto l : range(lanes(vop2))) + DUNE_SIMD_CHECK_OP(lane(l, vop2) == sinit2); + +#undef DUNE_SIMD_OPNAME + } + + template + std::enable_if_t > + checkBinaryOpVVAgainstVS(MetaType, MetaType, Op op) + { + // log_ << "No " + // << className())), T2)>() + // << std::endl + // << " ==> Not checking " << className() << std::endl; + } + + template + void checkBinaryOpVVAgainstVS(MetaType, MetaType, OpInfixComma) + { } + + ////////////////////////////////////////////////////////////////////// + // + // checks for vector-proxy binary operations + // + + // We check the following candidate operation + // + // vopres = vop1 @ pop2 + // + // against the reference operation + // + // arefres[l] = aref1[l] @ sref2 foreach l + // + // v... variables are simd-vectors, a... variables are arrays, + // p... variables are proxies of simd-vector entries and s... variables + // are scalars. The operation may modify the left operand, but if is + // does the modifications needs to happen in both the candidate and the + // reference. + // + // We do the following checks: + // 1. lanes(vopres) == lanes(vop1) + // 2. lane(l, vopres) == arefres[l] foreach l + // 3. lane(l, vop1) == aref1[l] foreach l + // 4. pop2 is never modified + // 5. sref2 is never modified + // + // In fact, if the property "sref2 is never modified" is violated that + // means the operation is unsuitable for an automatic broadcast of the + // second operand and should not be checked. There are no operations in + // the standard where the second operand is modified like this, but + // there are operations where the first operand is modified -- and this + // check is used for those ops as well by exchanging the first and second + // argument below. + + template + std::enable_if_t > + checkBinaryOpVP(MetaType, MetaType, Op op) + { + using P2 = decltype(lane(0, std::declval())); + using T2 = CopyRefQual, V2>; +#define DUNE_SIMD_OPNAME (className()) + static_assert(std::is_same, Scalar >::value, + "Internal testsystem error: called with two vector " + "types whose scalar types don't match."); + + // initial values + auto sinit2 = rightScalar>(); + + // reference arguments + auto vref1 = leftVector>(); + auto sref2 = sinit2; + + // candidate arguments + auto vop1 = vref1; + auto vop2 = std::decay_t(Scalar(0)); + lane(0, vop2) = sref2; // pop2 is just a name for `lane(0, vop2)` + + // candidate operation + auto &&vopres = + op(static_cast(vop1), lane(0, static_cast(vop2))); + using VR = decltype(vopres); + + // check 1. lanes(vopres) == lanes(vop1) + static_assert(lanes >() == lanes >(), + "The result must have the same number of lanes as the " + "operands."); + + // check 4. pop2 is never modified + DUNE_SIMD_CHECK_OP(lane(0, vop2) == sinit2); + + // do the reference operation, and simultaneously check 2. and 5. + using T = Scalar; + for(auto l : range(lanes(vopres))) + { + // check 2. lane(l, vopres) == arefres[l] foreach l + // see the lengthy comment in `checkUnaryOpV()` as to why the + // `static_cast` around the `op()` is necessary + DUNE_SIMD_CHECK_OP + (lane(l, vopres) + == static_cast(op(lane(l, static_cast(vref1)), + static_cast(sref2) ))); + // check 5. sref2 is never modified + DUNE_SIMD_CHECK_OP(sref2 == sinit2); + } + + // check 3. lane(l, vop1) == aref1[l] foreach l + for(auto l : range(lanes(vop1))) + DUNE_SIMD_CHECK_OP(lane(l, vop1) == lane(l, vref1)); + +#undef DUNE_SIMD_OPNAME + } + + template + std::enable_if_t > + checkBinaryOpVP(MetaType, MetaType, Op op) + { + // log_ << "No " + // << className())), T2)>() + // << std::endl + // << " ==> Not checking " << className() << std::endl; + } + + template + void checkBinaryOpVP(MetaType, MetaType, OpInfixComma) + { + // Don't really know how to check comma operator for proxies + } + + ////////////////////////////////////////////////////////////////////// + // + // checks for (scalar/proxy)-vector binary operations + // + + template + struct OpInfixSwappedArgs + { + Op orig; + + template + decltype(auto) operator()(V1&& v1, V2&& v2) const + { + return orig(std::forward(v2), std::forward(v1)); + } + template + auto scalar(S1&& s1, S2&& s2) const + -> decltype(orig.scalar(std::forward(s2), std::forward(s1))); + }; + + template + void checkBinaryOpSV(MetaType t1, MetaType v2, Op op) + { + checkBinaryOpVS(v2, t1, OpInfixSwappedArgs{op}); + } + + template + void checkBinaryOpSV(MetaType, MetaType, OpInfixComma) + { + static_assert(std::is_same, + Scalar > >::value, + "Internal testsystem error: called with a scalar that " + "does not match the vector type."); + + checkCommaOp(leftScalar>(), + rightVector>()); + } + + template + void checkBinaryOpPV(MetaType v1, MetaType v2, Op op) + { + checkBinaryOpVP(v2, v1, OpInfixSwappedArgs{op}); + } + + template + void checkBinaryOpPV(MetaType, MetaType, OpInfixComma) + { + // Don't really know how to check comma operator for proxies + } + + ////////////////////////////////////////////////////////////////////// + // + // cross-check scalar-vector binary operations against vector-vector + // + + // We check the following candidate operation + // + // vopres = vop1 @ vop2, where vop2 = broadcast(sref2) + // + // against the reference operation + // + // vrefres = vref1 @ sref2 + // + // v... variables are simd-vectors, a... variables are arrays, and + // s... variables are scalars. + // + // We could check the following properties + // 1. lanes(vopres) == lanes(vop1) + // 2. lane(l, vopres) == lane(l, vrefres) foreach l + // 3. lane(l, vop1) == lane(l, vref1) foreach l + // but these are given by checking the operation against the scalar + // operation in the vector@vector and vector@scalar cases above. + // + // The only thing left to check is: + // 4. lane(l, vop2) foreach l is never modified + + template + void checkBinaryOpVVAgainstSV(MetaType t1, MetaType v2, Op op) + { + checkBinaryOpVVAgainstVS(v2, t1, OpInfixSwappedArgs{op}); + } + + template + void checkBinaryOpVVAgainstSV(MetaType, MetaType, OpInfixComma) + { } + + ////////////////////////////////////////////////////////////////////// + // + // Invoke the checks for all combinations + // + + template + void checkBinaryRefQual(Checker checker) + { + if constexpr (condition) { + Hybrid::forEach(TypeList{}, [=] (auto t1) { + Hybrid::forEach(TypeList{}, [=] (auto t2) { + checker(t1, t2); + }); + }); + } + } + + template + void checkBinaryOps(Checker checker) + { + using Std::bool_constant; + + constexpr bool isMask = std::is_same, bool>::value; + + constexpr bool do_ = false; + constexpr bool do_SV = true; + constexpr bool do_VV = true; + constexpr bool do_VS = true; + +#define DUNE_SIMD_DO(M1, M2, M3, V1, V2, V3, NAME) \ + checker(bool_constant{}, \ + bool_constant{}, \ + bool_constant{}, \ + Op##NAME{}) + + // (Mask , Vector , Name ); + + DUNE_SIMD_DO( , , , SV, VV, VS, InfixMul ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixDiv ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixRemainder ); + + DUNE_SIMD_DO( , , , SV, VV, VS, InfixPlus ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixMinus ); + + DUNE_SIMD_DO( , , , , VV, VS, InfixLeftShift ); + DUNE_SIMD_DO( , , , , VV, VS, InfixRightShift ); + + DUNE_SIMD_DO( , , , SV, VV, VS, InfixLess ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixGreater ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixLessEqual ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixGreaterEqual ); + + DUNE_SIMD_DO( , , , SV, VV, VS, InfixEqual ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixNotEqual ); + + DUNE_SIMD_DO( , VV, , SV, VV, VS, InfixBitAnd ); + DUNE_SIMD_DO( , VV, , SV, VV, VS, InfixBitXor ); + DUNE_SIMD_DO( , VV, , SV, VV, VS, InfixBitOr ); + + DUNE_SIMD_DO(SV, VV, VS, SV, VV, VS, InfixLogicAnd ); + DUNE_SIMD_DO(SV, VV, VS, SV, VV, VS, InfixLogicOr ); + + DUNE_SIMD_DO( , VV, , , VV, VS, InfixAssign ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignMul ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignDiv ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignRemainder ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignPlus ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignMinus ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignLeftShift ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignRightShift); + DUNE_SIMD_DO( , VV, , , VV, VS, InfixAssignAnd ); + DUNE_SIMD_DO( , VV, , , VV, VS, InfixAssignXor ); + DUNE_SIMD_DO( , VV, , , VV, VS, InfixAssignOr ); + + DUNE_SIMD_DO(SV, VV, VS, SV, , VS, InfixComma ); + +#undef DUNE_SIMD_DO + } + + ////////////////////////////////////////////////////////////////////// + // + // SIMD interface functions + // + + template + void checkAutoCopy() + { + using RValueResult = decltype(autoCopy(lane(0, std::declval()))); + static_assert(std::is_same >::value, + "Result of autoCopy() must always be Scalar"); + + using MutableLValueResult = + decltype(autoCopy(lane(0, std::declval()))); + static_assert(std::is_same >::value, + "Result of autoCopy() must always be Scalar"); + + using ConstLValueResult = + decltype(autoCopy(lane(0, std::declval()))); + static_assert(std::is_same >::value, + "Result of autoCopy() must always be Scalar"); + + V vec = make123(); + for(std::size_t l = 0; l < lanes(vec); ++l) + DUNE_SIMD_CHECK(autoCopy(lane(l, vec)) == Scalar(l+1)); + } + + // may only be called for mask types + template + void checkBoolReductions() + { + M trueVec(true); + + // mutable lvalue + DUNE_SIMD_CHECK(allTrue (static_cast(trueVec)) == true); + DUNE_SIMD_CHECK(anyTrue (static_cast(trueVec)) == true); + DUNE_SIMD_CHECK(allFalse(static_cast(trueVec)) == false); + DUNE_SIMD_CHECK(anyFalse(static_cast(trueVec)) == false); + + // const lvalue + DUNE_SIMD_CHECK(allTrue (static_cast(trueVec)) == true); + DUNE_SIMD_CHECK(anyTrue (static_cast(trueVec)) == true); + DUNE_SIMD_CHECK(allFalse(static_cast(trueVec)) == false); + DUNE_SIMD_CHECK(anyFalse(static_cast(trueVec)) == false); + + // rvalue + DUNE_SIMD_CHECK(allTrue (M(true)) == true); + DUNE_SIMD_CHECK(anyTrue (M(true)) == true); + DUNE_SIMD_CHECK(allFalse(M(true)) == false); + DUNE_SIMD_CHECK(anyFalse(M(true)) == false); + + M falseVec(false); + + // mutable lvalue + DUNE_SIMD_CHECK(allTrue (static_cast(falseVec)) == false); + DUNE_SIMD_CHECK(anyTrue (static_cast(falseVec)) == false); + DUNE_SIMD_CHECK(allFalse(static_cast(falseVec)) == true); + DUNE_SIMD_CHECK(anyFalse(static_cast(falseVec)) == true); + + // const lvalue + DUNE_SIMD_CHECK(allTrue (static_cast(falseVec)) == false); + DUNE_SIMD_CHECK(anyTrue (static_cast(falseVec)) == false); + DUNE_SIMD_CHECK(allFalse(static_cast(falseVec)) == true); + DUNE_SIMD_CHECK(anyFalse(static_cast(falseVec)) == true); + + // rvalue + DUNE_SIMD_CHECK(allTrue (M(false)) == false); + DUNE_SIMD_CHECK(anyTrue (M(false)) == false); + DUNE_SIMD_CHECK(allFalse(M(false)) == true); + DUNE_SIMD_CHECK(anyFalse(M(false)) == true); + + auto mixedVec = broadcast(0); + for(std::size_t l = 0; l < lanes(mixedVec); ++l) + lane(l, mixedVec) = (l % 2); + + // mutable lvalue + DUNE_SIMD_CHECK + (allTrue (static_cast(mixedVec)) == false); + DUNE_SIMD_CHECK + (anyTrue (static_cast(mixedVec)) == (lanes() > 1)); + DUNE_SIMD_CHECK + (allFalse(static_cast(mixedVec)) == (lanes() == 1)); + DUNE_SIMD_CHECK + (anyFalse(static_cast(mixedVec)) == true); + + // const lvalue + DUNE_SIMD_CHECK + (allTrue (static_cast(mixedVec)) == false); + DUNE_SIMD_CHECK + (anyTrue (static_cast(mixedVec)) == (lanes() > 1)); + DUNE_SIMD_CHECK + (allFalse(static_cast(mixedVec)) == (lanes() == 1)); + DUNE_SIMD_CHECK + (anyFalse(static_cast(mixedVec)) == true); + + // rvalue + DUNE_SIMD_CHECK(allTrue (M(mixedVec)) == false); + DUNE_SIMD_CHECK(anyTrue (M(mixedVec)) == (lanes() > 1)); + DUNE_SIMD_CHECK(allFalse(M(mixedVec)) == (lanes() == 1)); + DUNE_SIMD_CHECK(anyFalse(M(mixedVec)) == true); + } + + template + void checkCond() + { + using M = Mask; + + static_assert + (std::is_same(), std::declval(), + std::declval())), V>::value, + "The result of cond(M, V, V) should have exactly the type V"); + + static_assert + (std::is_same(), + std::declval(), + std::declval())), V>::value, + "The result of cond(const M&, const V&, const V&) should have " + "exactly the type V"); + + static_assert + (std::is_same(), std::declval(), + std::declval())), V>::value, + "The result of cond(M&, V&, V&) should have exactly the type V"); + + V vec1 = leftVector(); + V vec2 = rightVector(); + + DUNE_SIMD_CHECK(allTrue(cond(M(true), vec1, vec2) == vec1)); + DUNE_SIMD_CHECK(allTrue(cond(M(false), vec1, vec2) == vec2)); + + auto mixedResult = broadcast(0); + auto mixedMask = broadcast(false); + for(std::size_t l = 0; l < lanes(mixedMask); ++l) + { + lane(l, mixedMask ) = (l % 2); + lane(l, mixedResult) = lane(l, (l % 2) ? vec1 : vec2); + } + + DUNE_SIMD_CHECK(allTrue(cond(mixedMask, vec1, vec2) == mixedResult)); + } + + template + void checkBoolCond() + { + static_assert + (std::is_same(), std::declval(), + std::declval())), V>::value, + "The result of cond(bool, V, V) should have exactly the type V"); + + static_assert + (std::is_same(), + std::declval(), + std::declval())), V>::value, + "The result of cond(const bool&, const V&, const V&) should have " + "exactly the type V"); + + static_assert + (std::is_same(), + std::declval(), + std::declval())), V>::value, + "The result of cond(bool&, V&, V&) should have exactly the type V"); + + V vec1 = leftVector(); + V vec2 = rightVector(); + + DUNE_SIMD_CHECK(allTrue(cond(true, vec1, vec2) == vec1)); + DUNE_SIMD_CHECK(allTrue(cond(false, vec1, vec2) == vec2)); + } + + template + std::enable_if_t >::value> + checkHorizontalMinMax() {} + + template + std::enable_if_t >::value> + checkHorizontalMinMax() + { + static_assert + (std::is_same())), Scalar >::value, + "The result of max(V) should be exactly Scalar"); + + static_assert + (std::is_same())), Scalar >::value, + "The result of min(V) should be exactly Scalar"); + + static_assert + (std::is_same())), Scalar >::value, + "The result of max(V) should be exactly Scalar"); + + static_assert + (std::is_same())), Scalar >::value, + "The result of min(V) should be exactly Scalar"); + + const V vec1 = leftVector(); + + DUNE_SIMD_CHECK(max(vec1) == Scalar(lanes(vec1))); + DUNE_SIMD_CHECK(min(vec1) == Scalar(1)); + } + + template + std::enable_if_t >::value> + checkBinaryMinMax() {} + + template + std::enable_if_t >::value> + checkBinaryMinMax() + { + using std::max; + using std::min; + + static_assert + (std::is_same(), + std::declval())), V>::value, + "The result of Simd::max(V, V) should be exactly V"); + static_assert + (std::is_same(), + std::declval())), V>::value, + "The result of Simd::min(V, V) should be exactly V"); + + static_assert + (std::is_same(), + std::declval())), V>::value, + "The result of Simd::max(V&, V&) should be exactly V"); + static_assert + (std::is_same(), + std::declval())), V>::value, + "The result of Simd::min(V&, V&) should be exactly V"); + + const V arg1 = leftVector(); + const V arg2 = rightVector(); + + V maxExp(Scalar(0)), minExp(Scalar(0)); + for(auto l : range(lanes())) + { + lane(l, maxExp) = max(lane(l, arg1), lane(l, arg2)); + lane(l, minExp) = min(lane(l, arg1), lane(l, arg2)); + } + + DUNE_SIMD_CHECK(allTrue(maxExp == Simd::max(arg1, arg2))); + DUNE_SIMD_CHECK(allTrue(minExp == Simd::min(arg1, arg2))); + } + + template + void checkIO() + { + const V vec1 = leftVector(); + + std::string reference; + { + const char *sep = ""; + for(auto l : range(lanes(vec1))) + { + std::ostringstream stream; + stream << lane(l, vec1); + + reference += sep; + reference += stream.str(); + sep = ", "; + } + } + + { + std::ostringstream stream; + stream << io(vec1); + if(lanes(vec1) == 1) + DUNE_SIMD_CHECK(stream.str() == reference); + else + DUNE_SIMD_CHECK(stream.str() == "<" + reference + ">"); + } + + { + std::ostringstream stream; + stream << vio(vec1); + DUNE_SIMD_CHECK(stream.str() == "<" + reference + ">"); + } + } + +#undef DUNE_SIMD_CHECK + + public: + /** + * @name Test instantiation points + * + * These functions should not be called directly, but serve as explicit + * instantiation points to keep memory usage bounded during compilation. + * There should be an explicit instantiation declaration (`extern + * template ...`) in the the overall header of your unit test for each + * type that is tested (possibly implicitly tested due to recursive + * checks). Similarly, there should be an explicit instantiation + * definition (`template ...`) in a separate translation unit. Ideally, + * there should be one translation unit per explicit instantiation + * definition, otherwise each of them will contribute to the overall + * memory used during compilation. + * + * If explicitly instantiating the top-level instantiation point + * `checkType()` is not sufficient, there are further instantiation + * points for improved granularity. The hierarchy of instantiation + * points is: + * - `checkType()` + * - `checkNonOps()` + * - `checkUnaryOps()` + * - `checkBinaryOps()` + * - `checkBinaryOpsVectorVector()` + * - `checkBinaryOpsScalarVector()` + * - `checkBinaryOpsVectorScalar()` + * - `checkBinaryOpsProxyVector()` + * - `checkBinaryOpsVectorProxy()` + * + * Each instantiation point in the hierarchy implicitly instantiates its + * descendants, unless there are explicit instantiation declarations for + * them. However, for future-proofing it can make sense to explicitly + * instantiate nodes in the hierarchy even if all their children are + * already explicitly instantiated. This will limit the impact of + * instantiation points added in the future. + * + * For an example of how to do the instantiations, look at + * `standardtest`, there is cmake machinery to support you. + * + * Background: The compiler can use a lot of memory when compiling a + * unit test for many Simd vector types. E.g. for standardtest.cc, + * which tests all the fundamental arithmetic types plus \c + * std::complex, g++ 4.9.2 (-g -O0 -Wall on x86_64 GNU/Linux) used + * ~6GByte. + * + * One mitigation was to explicitly instantiate \c checkVector() (a + * previous, now obsolete incarnation of this instantiation machinery) + * for the types that are tested. Still after doing that, + * standardtest.cc needed ~1.5GByte during compilation, which is more + * than the compilation units that actually instantiated \c + * checkVector() (which clocked in at maximum at around 800MB, depending + * on how many instantiations they contained). + * + * The second mitigation was to define \c checkVector() outside of the + * class. I have no idea why this helped, but it made compilation use + * less than ~100MByte. (Yes, functions defined inside the class are + * implicitly \c inline, but the function is a template so it has inline + * semantics even when defined outside of the class. And I tried \c + * __attribute__((__noinline__)), which had no effect on memory + * consumption.) + * + * @{ + */ + template void checkType(); + template void checkNonOps(); + template void checkUnaryOps(); + template void checkBinaryOps(); + template void checkBinaryOpsVectorVector(); + template void checkBinaryOpsScalarVector(); + template void checkBinaryOpsVectorScalar(); + template void checkBinaryOpsProxyVector(); + template void checkBinaryOpsVectorProxy(); + /** @} Group Test instantiation points */ + + //! run unit tests for simd vector type V + /** + * This function will also ensure that `check()` is run, for any type + * `W = Rebind` where `R` is in `Rebinds`, and + * `RebindPrune::value == false`. No test will be run twice for a + * given type. + * + * If the result of `Rebind` is not pruned by `RebindPrune`, it will be + * passed to `RebindAccept`. If that rejects the type, a static + * assertion will trigger. + * + * \tparam Rebinds A list of types, usually in the form of a + * `TypeList`. + * \tparam RebindPrune A type predicate determining whether to run + * `check()` for types obtained from `Rebinds`. + * \tparam RebindAccept A type predicate determining whether a type is + * acceptable as the result of a `Rebind`. + */ + template class RebindPrune = IsLoop, + template class RebindAccept = Dune::AlwaysTrue> + void check() { + // check whether the test for this type already started + if(seen_.emplace(typeid (V)).second == false) + { + // type already seen, nothing to do + return; + } + + // do these first so everything that appears after "Checking SIMD type + // ..." really pertains to that type + auto recurse = [this](auto w) { + using W = typename decltype(w)::type; + this->template check(); + }; + checkRebindOf(recurse); + + checkType(); + } + + //! whether all tests succeeded + bool good() const + { + return good_; + } + + }; // class UnitTest + + template void UnitTest::checkType() + { + static_assert(std::is_same >::value, "Simd types " + "must not be references, and must not include " + "cv-qualifiers"); + + log_ << "Checking SIMD type " << className() << std::endl; + + checkNonOps(); + checkUnaryOps(); + checkBinaryOps(); + } + template void UnitTest::checkNonOps() + { + constexpr auto isMask = typename std::is_same, bool>::type{}; + + checkLanes(); + checkScalar(); + + checkDefaultConstruct(); + checkLane(); + checkCopyMoveConstruct(); + checkImplCast(); + checkBroadcast(); + if constexpr (isMask) + this->template checkBroadcastMaskConstruct(); + else + this->template checkBroadcastVectorConstruct(); + checkBracedAssign(); + checkBracedBroadcastAssign(); + + checkAutoCopy(); + checkCond(); + checkBoolCond(); + + if constexpr (isMask) + this->template checkBoolReductions(); + // checkBoolReductions() is not applicable for non-masks + + checkHorizontalMinMax(); + checkBinaryMinMax(); + checkIO(); + } + template void UnitTest::checkUnaryOps() + { + if constexpr (std::is_same_v, bool>) { + // check mask + auto check = [this](auto op) { + this->template checkUnaryOpsV(op); + }; + + // postfix + // check(OpPostfixDecrement{}); + // clang deprecation warning if bool++ is tested + // check(OpPostfixIncrement{}); + + // prefix + // check(OpPrefixDecrement{}); + // clang deprecation warning if ++bool is tested + // check(OpPrefixIncrement{}); + + // check(OpPrefixPlus{}); + // check(OpPrefixMinus{}); + check(OpPrefixLogicNot{}); + // check(OpPrefixBitNot{}); + } + else { + // check vector + auto check = [this](auto op) { + this->template checkUnaryOpsV(op); + }; + + // postfix + // check(OpPostfixDecrement{}); + // check(OpPostfixIncrement{}); + + // prefix + // check(OpPrefixDecrement{}); + // check(OpPrefixIncrement{}); + + // check(OpPrefixPlus{}); + check(OpPrefixMinus{}); + check(OpPrefixLogicNot{}); + check(OpPrefixBitNot{}); + } + } + template void UnitTest::checkBinaryOps() + { + checkBinaryOpsVectorVector(); + checkBinaryOpsScalarVector(); + checkBinaryOpsVectorScalar(); + checkBinaryOpsProxyVector(); + checkBinaryOpsVectorProxy(); + } + template void UnitTest::checkBinaryOpsVectorVector() + { + auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [this,op](auto t1, auto t2) { + this->checkBinaryOpVV(t1, t2, op); + }; + this->checkBinaryRefQual(check); + }; + checkBinaryOps(checker); + } + template void UnitTest::checkBinaryOpsScalarVector() + { + auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [this,op](auto t1, auto t2) { + this->checkBinaryOpSV(t1, t2, op); + }; + this->checkBinaryRefQual, V, doSV>(check); + + auto crossCheck = [this,op](auto t1, auto t2) { + this->checkBinaryOpVVAgainstSV(t1, t2, op); + }; + this->checkBinaryRefQual, V, doSV && doVV>(crossCheck); + }; + checkBinaryOps(checker); + } + template void UnitTest::checkBinaryOpsVectorScalar() + { + auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [this,op](auto t1, auto t2) { + this->checkBinaryOpVS(t1, t2, op); + }; + this->checkBinaryRefQual, doVS>(check); + + auto crossCheck = [this,op](auto t1, auto t2) { + this->checkBinaryOpVVAgainstVS(t1, t2, op); + }; + this->checkBinaryRefQual, doVV && doVS>(crossCheck); + }; + checkBinaryOps(checker); + } + template void UnitTest::checkBinaryOpsProxyVector() + { + auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [this,op](auto t1, auto t2) { + this->checkBinaryOpPV(t1, t2, op); + }; + this->checkBinaryRefQual(check); + }; + checkBinaryOps(checker); + } + template void UnitTest::checkBinaryOpsVectorProxy() + { + auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [this,op](auto t1, auto t2) { + this->checkBinaryOpVP(t1, t2, op); + }; + this->checkBinaryRefQual(check); + }; + checkBinaryOps(checker); + } + + } // namespace Simd +} // namespace Dune + +#endif // DUNE_COMMON_SIMD_TEST_HH diff --git a/dune/common/simd/test/CMakeLists.txt b/dune/common/simd/test/CMakeLists.txt new file mode 100644 index 0000000..8b000af --- /dev/null +++ b/dune/common/simd/test/CMakeLists.txt @@ -0,0 +1,113 @@ +include(DuneCMakeCompat) + +# We need to explicitly instantiate the tests for small groups of types -- +# else the compiler will eat excessive amounts of memory. This way it seems +# to stay below 1GByte (with g++ 4.9.2 -O0 -g on x86_64 GNU/Linux, looking at +# standardtest). +include(DuneInstance) + +set(TYPES + char "unsigned char" "signed char" + short int long "long long" + "unsigned short" unsigned "unsigned long" "unsigned long long" + bool + float double "long double" + std::complex std::complex "std::complex" + ) + +# Generate files with instanciations, external declarations, and also the +# invocations in the test for each instance. +dune_instance_begin(FILES looptest.hh looptest.cc) +foreach(SCALAR IN LISTS TYPES) + dune_instance_add(ID "${SCALAR}") + foreach(POINT IN ITEMS + Type + BinaryOpsScalarVector BinaryOpsVectorScalar) + dune_instance_add(TEMPLATE POINT ID "${POINT}_${SCALAR}" + FILES looptest_vector.cc) + endforeach() +endforeach() +dune_instance_end() + +list(FILTER DUNE_INSTANCE_GENERATED INCLUDE REGEX [[\.cc$]]) +dune_add_test(NAME looptest + SOURCES ${DUNE_INSTANCE_GENERATED} + LINK_LIBRARIES dunecommon +) +# no need to install looptest.hh, used by looptest*.cc only + + + +set(TYPES + char "unsigned char" "signed char" + short int long "long long" + "unsigned short" unsigned "unsigned long" "unsigned long long" + bool + float double "long double" + std::complex std::complex "std::complex") + +# Generate files with instanciations, external declarations, and also the +# invocations in the test for each instance. +dune_instance_begin(FILES standardtest.hh standardtest.cc) +foreach(SCALAR IN LISTS TYPES) + dune_instance_add(ID "${SCALAR}" FILES standardtest_vector.cc) +endforeach() +dune_instance_end() + +list(FILTER DUNE_INSTANCE_GENERATED INCLUDE REGEX [[\.cc$]]) +dune_add_test(NAME standardtest + SOURCES ${DUNE_INSTANCE_GENERATED} + LINK_LIBRARIES dunecommon +) +# no need to install standardtest.hh, used by standardtest*.cc only + + +# as of Vc-1.3.2: Vc/common/simdarray.h:561: SimdArray may only be used +# with T = { double, float, int32_t, uint32_t, int16_t, uint16_t } +set(VCTEST_TYPES std::int16_t std::uint16_t std::int32_t std::uint32_t float double) + +# Generate files with instanciations, external declarations, and also the +# invocations in the test for each instance. +dune_instance_begin(FILES vcarraytest.hh vcarraytest.cc) +foreach(SCALAR IN LISTS VCTEST_TYPES) + dune_instance_add(ID "${SCALAR}") + foreach(POINT IN ITEMS + Type + BinaryOpsScalarVector BinaryOpsVectorScalar + BinaryOpsProxyVector BinaryOpsVectorProxy) + dune_instance_add(TEMPLATE POINT ID "${POINT}_${SCALAR}" + FILES vctest_simdarray.cc vctest_simdmaskarray.cc) + endforeach() +endforeach() +dune_instance_end() +list(FILTER DUNE_INSTANCE_GENERATED INCLUDE REGEX [[\.cc$]]) +dune_add_test(NAME vcarraytest + SOURCES ${DUNE_INSTANCE_GENERATED} + LINK_LIBRARIES dunecommon + CMAKE_GUARD Vc_FOUND +) +add_dune_vc_flags(vcarraytest) +# no need to install vcarraytest.hh, used by vctest*.cc only + +# Generate files with instanciations, external declarations, and also the +# invocations in the test for each instance. +dune_instance_begin(FILES vcvectortest.hh vcvectortest.cc) +foreach(SCALAR IN LISTS VCTEST_TYPES) + dune_instance_add(ID "${SCALAR}") + foreach(POINT IN ITEMS + Type + BinaryOpsScalarVector BinaryOpsVectorScalar + BinaryOpsProxyVector BinaryOpsVectorProxy) + dune_instance_add(TEMPLATE POINT ID "${POINT}_${SCALAR}" + FILES vctest_vector.cc vctest_mask.cc) + endforeach() +endforeach() +dune_instance_end() +list(FILTER DUNE_INSTANCE_GENERATED INCLUDE REGEX [[\.cc$]]) +dune_add_test(NAME vcvectortest + SOURCES ${DUNE_INSTANCE_GENERATED} + LINK_LIBRARIES dunecommon + CMAKE_GUARD Vc_FOUND +) +add_dune_vc_flags(vcvectortest) +# no need to install vcvectortest.hh, used by vctest*.cc only diff --git a/dune/common/simd/test/looptest.cc.in b/dune/common/simd/test/looptest.cc.in new file mode 100644 index 0000000..e84de34 --- /dev/null +++ b/dune/common/simd/test/looptest.cc.in @@ -0,0 +1,43 @@ +// @GENERATED_SOURCE@ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include + +template struct RebindAccept : std::false_type {}; +#cmake @template@ +template +struct RebindAccept > : std::true_type {}; +template +struct RebindAccept, 5, A2> > : std::true_type {}; +#cmake @endtemplate@ + +using Rebinds = Dune::Simd::RebindList< +#cmake @template@ + @SCALAR@, +#cmake @endtemplate@ + Dune::Simd::EndMark>; + +int main() +{ + Dune::Simd::UnitTest test; + +#cmake @template@ + test.check, + Rebinds, Dune::AlwaysFalse, RebindAccept>(); + test.check, + Rebinds, Dune::AlwaysFalse, RebindAccept>(); + test.check, 5>, + Rebinds, Dune::AlwaysFalse, RebindAccept>(); +#cmake @endtemplate@ + + return test.good() ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/dune/common/simd/test/looptest.hh.in b/dune/common/simd/test/looptest.hh.in new file mode 100644 index 0000000..3ab9fc1 --- /dev/null +++ b/dune/common/simd/test/looptest.hh.in @@ -0,0 +1,26 @@ +// @GENERATED_SOURCE@ + +#ifndef DUNE_COMMON_SIMD_TEST_LOOPTEST_HH +#define DUNE_COMMON_SIMD_TEST_LOOPTEST_HH + +#include // as inserted by substitutions + +#include +#include + +namespace Dune { + namespace Simd { + +#cmake @template POINT@ + extern template void + UnitTest::check@POINT@ >(); + extern template void + UnitTest::check@POINT@ >(); + extern template void + UnitTest::check@POINT@, 5> >(); +#cmake @endtemplate@ + + } //namespace Simd +} // namespace Dune + +#endif diff --git a/dune/common/simd/test/looptest_vector.cc.in b/dune/common/simd/test/looptest_vector.cc.in new file mode 100644 index 0000000..a587518 --- /dev/null +++ b/dune/common/simd/test/looptest_vector.cc.in @@ -0,0 +1,15 @@ +// @GENERATED_SOURCE@ + +#include + +#include + +namespace Dune { + namespace Simd { + + template void UnitTest::check@POINT@ >(); + template void UnitTest::check@POINT@ >(); + template void UnitTest::check@POINT@, 5> >(); + + } //namespace Simd +} // namespace Dune diff --git a/dune/common/simd/test/standardtest.cc.in b/dune/common/simd/test/standardtest.cc.in new file mode 100644 index 0000000..5e540d4 --- /dev/null +++ b/dune/common/simd/test/standardtest.cc.in @@ -0,0 +1,34 @@ +// @GENERATED_SOURCE@ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include + +template struct RebindAccept : std::false_type {}; +#cmake @template@ +template<> struct RebindAccept<@SCALAR@> : std::true_type {}; +#cmake @endtemplate@ + +using Rebinds = Dune::Simd::RebindList< +#cmake @template@ + @SCALAR@, +#cmake @endtemplate@ + Dune::Simd::EndMark>; + +int main() +{ + Dune::Simd::UnitTest test; + +#cmake @template@ + test.check<@SCALAR@, Rebinds, Dune::AlwaysFalse, RebindAccept>(); +#cmake @endtemplate@ + + return test.good() ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/dune/common/simd/test/standardtest.hh.in b/dune/common/simd/test/standardtest.hh.in new file mode 100644 index 0000000..adf8a96 --- /dev/null +++ b/dune/common/simd/test/standardtest.hh.in @@ -0,0 +1,20 @@ +// @GENERATED_SOURCE@ + +#ifndef DUNE_COMMON_SIMD_TEST_STANDARDTEST_HH +#define DUNE_COMMON_SIMD_TEST_STANDARDTEST_HH + +#include // for substituted types + +#include + +namespace Dune { + namespace Simd { + +#cmake @template@ + extern template void UnitTest::checkType<@SCALAR@>(); +#cmake @endtemplate@ + + } // namespace Simd +} // namespace Dune + +#endif // DUNE_COMMON_SIMD_TEST_STANDARDTEST_HH diff --git a/dune/common/simd/test/standardtest_vector.cc.in b/dune/common/simd/test/standardtest_vector.cc.in new file mode 100644 index 0000000..fde4428 --- /dev/null +++ b/dune/common/simd/test/standardtest_vector.cc.in @@ -0,0 +1,14 @@ +// @GENERATED_SOURCE@ + +#include + +#include +#include + +namespace Dune { + namespace Simd { + + template void UnitTest::checkType<@SCALAR@>(); + + } // namespace Simd +} // namespace Dune diff --git a/dune/common/simd/test/vcarraytest.cc.in b/dune/common/simd/test/vcarraytest.cc.in new file mode 100644 index 0000000..bf4be06 --- /dev/null +++ b/dune/common/simd/test/vcarraytest.cc.in @@ -0,0 +1,49 @@ +// @GENERATED_SOURCE@ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if !HAVE_VC +#error Inconsistent buildsystem. This program should not be built in the \ + absence of Vc. +#endif + +#include +#include +#include + +#include +#include +#include +#include + +template struct RebindAccept : std::false_type {}; +#cmake @template@ +template<> +struct RebindAccept > : std::true_type {}; +template<> +struct RebindAccept > : std::true_type {}; +#cmake @endtemplate@ + +using Rebinds = Dune::TypeList< +#cmake @template@ + @SCALAR@, +#cmake @endtemplate@ + bool, + std::size_t>; + +int main() +{ + using Vc::Vector; + using Vc::SimdArray; + + Dune::Simd::UnitTest test; + +#@template@ + test.check, + Rebinds, Dune::Simd::IsLoop, RebindAccept>(); +#@endtemplate@ + + return test.good() ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/dune/common/simd/test/vcarraytest.hh.in b/dune/common/simd/test/vcarraytest.hh.in new file mode 100644 index 0000000..5c3ca60 --- /dev/null +++ b/dune/common/simd/test/vcarraytest.hh.in @@ -0,0 +1,30 @@ +// @GENERATED_SOURCE@ + +#ifndef DUNE_COMMON_SIMD_TEST_VCTEST_HH +#define DUNE_COMMON_SIMD_TEST_VCTEST_HH + +#if HAVE_VC + +#include +#include // for std::[u]int#_t as substituted + +#include +#include + +constexpr std::size_t lanes = 4; + +namespace Dune { + namespace Simd { + +#cmake @template POINT@ + extern template void + UnitTest::check@POINT@ >(); + extern template void + UnitTest::check@POINT@ >(); +#cmake @endtemplate@ + + } // namespace Simd +} // namespace Dune + +#endif // HAVE_VC +#endif // DUNE_COMMON_SIMD_TEST_VCTEST_HH diff --git a/dune/common/simd/test/vctest_mask.cc.in b/dune/common/simd/test/vctest_mask.cc.in new file mode 100644 index 0000000..c5742e5 --- /dev/null +++ b/dune/common/simd/test/vctest_mask.cc.in @@ -0,0 +1,13 @@ +// @GENERATED_SOURCE@ + +#include + +#include + +namespace Dune { + namespace Simd { + + template void UnitTest::check@POINT@ >(); + + } // namespace Simd +} // namespace Dune diff --git a/dune/common/simd/test/vctest_simdarray.cc.in b/dune/common/simd/test/vctest_simdarray.cc.in new file mode 100644 index 0000000..61059d7 --- /dev/null +++ b/dune/common/simd/test/vctest_simdarray.cc.in @@ -0,0 +1,14 @@ +// @GENERATED_SOURCE@ + +#include + +#include + +namespace Dune { + namespace Simd { + + template void + UnitTest::check@POINT@ >(); + + } // namespace Simd +} // namespace Dune diff --git a/dune/common/simd/test/vctest_simdmaskarray.cc.in b/dune/common/simd/test/vctest_simdmaskarray.cc.in new file mode 100644 index 0000000..7d3f306 --- /dev/null +++ b/dune/common/simd/test/vctest_simdmaskarray.cc.in @@ -0,0 +1,14 @@ +// @GENERATED_SOURCE@ + +#include + +#include + +namespace Dune { + namespace Simd { + + template void + UnitTest::check@POINT@ >(); + + } // namespace Simd +} // namespace Dune diff --git a/dune/common/simd/test/vctest_vector.cc.in b/dune/common/simd/test/vctest_vector.cc.in new file mode 100644 index 0000000..468d77f --- /dev/null +++ b/dune/common/simd/test/vctest_vector.cc.in @@ -0,0 +1,13 @@ +// @GENERATED_SOURCE@ + +#include + +#include + +namespace Dune { + namespace Simd { + + template void UnitTest::check@POINT@ >(); + + } // namespace Simd +} // namespace Dune diff --git a/dune/common/simd/test/vcvectortest.cc.in b/dune/common/simd/test/vcvectortest.cc.in new file mode 100644 index 0000000..6ee1ae3 --- /dev/null +++ b/dune/common/simd/test/vcvectortest.cc.in @@ -0,0 +1,51 @@ +// @GENERATED_SOURCE@ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if !HAVE_VC +#error Inconsistent buildsystem. This program should not be built in the \ + absence of Vc. +#endif + +#include +#include +#include + +#include +#include +#include +#include + +template struct RebindAccept : std::false_type {}; +#cmake @template@ +template<> struct RebindAccept > : std::true_type {}; +template<> struct RebindAccept > : std::true_type {}; +#cmake @endtemplate@ + +// ignore rebinds to LoopSIMD as well as Vc::SimdArray +template struct Prune : Dune::Simd::IsLoop {}; +template +struct Prune > : std::true_type {}; + +using Rebinds = Dune::TypeList< +#cmake @template@ + @SCALAR@, +#cmake @endtemplate@ + bool, + std::size_t>; + +int main() +{ + using Vc::Vector; + using Vc::SimdArray; + + Dune::Simd::UnitTest test; + +#@template@ + test.check, Rebinds, Prune, RebindAccept>(); +#@endtemplate@ + + return test.good() ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/dune/common/simd/test/vcvectortest.hh.in b/dune/common/simd/test/vcvectortest.hh.in new file mode 100644 index 0000000..cf979b8 --- /dev/null +++ b/dune/common/simd/test/vcvectortest.hh.in @@ -0,0 +1,26 @@ +// @GENERATED_SOURCE@ + +#ifndef DUNE_COMMON_SIMD_TEST_VCTEST_HH +#define DUNE_COMMON_SIMD_TEST_VCTEST_HH + +#if HAVE_VC + +#include + +#include +#include +#include + +namespace Dune { + namespace Simd { + +#cmake @template POINT@ + extern template void UnitTest::check@POINT@ >(); + extern template void UnitTest::check@POINT@ >(); +#cmake @endtemplate@ + + } // namespace Simd +} // namespace Dune + +#endif // HAVE_VC +#endif // DUNE_COMMON_SIMD_TEST_VCTEST_HH diff --git a/dune/common/simd/vc.hh b/dune/common/simd/vc.hh new file mode 100644 index 0000000..bebb056 --- /dev/null +++ b/dune/common/simd/vc.hh @@ -0,0 +1,760 @@ +#ifndef DUNE_COMMON_SIMD_VC_HH +#define DUNE_COMMON_SIMD_VC_HH + +/** @file + * @ingroup SIMDVc + * @brief SIMD abstractions for Vc + */ + +#include +#include +#include + +#include +#include +#include // for anyFalse() +#include +#include +#include + +/** @defgroup SIMDVc SIMD Abstraction Implementation for Vc + * @ingroup SIMDApp + * + * This implements the vectorization interface for Vc types, namely + * `Vc::Vector`, `Vc::Mask`, `Vc::SimdArray` and `Vc::SimdMask`. + * + * As an application developer, you need to `#include + * `. You need to make sure that `HAVE_VC` is true + * before doing so: + * + * - If your program works both in the presence and the absence of Vc, wrap + * the include in `#if HAVE_VC` and `#endif` + * + * - If you write a unit test, in your `CMakeLists.txt` use + * `dune_add_test(... CMAKE_GUARD Vc_FOUND)` + * + * You also need to make sure that the compiler uses the correct flags and + * that the linker can find the library. (The compilation flags include one + * that ensures a name mangling scheme that can distiguish the + * compiler-intrinsic vector types from non-vector types is used.) + * + * - Either use `add_dune_vc_flags(your_application)` in `CMakeLists.txt`, + * + * - or use `dune_enable_all_packages()` in your module's toplevel + * `CMakeLists.txt`. + * + * There should be no need to explicitly call `find_package(Vc)` in your + * `CMakeLists.txt`, dune-common already does that. If your module can't live + * without Vc, you may however want to do something like this in your + * `cmake/modules/YourModuleMacros.cmake`: + * \code + * if(NOT Vc_FOUND) + * message(SEND_ERROR "This module requires Vc") + * endif(NOT Vc_FOUND) + * \endcode + * + * If you just want to compile dune, and have Vc installed to a location where + * cmake is not looking by default, you need to add that location to + * `CMAKE_PREFIX_PATH`. E.g. pass `-DCMAKE_PREFIX_PATH=$VCDIR` to cmake, for + * instance by including that in the variable `CMAKE_FLAGS` in the options + * file that you pass to dunecontrol. `$VCDIR` should be the same directory + * that you passed in `-DCMAKE_INSTALL_PREFIX=...` to cmake when you compiled + * Vc, i.e. Vc's main include file should be found under + * `$VCDIR/include/Vc/Vc`, and the library under `$VCDIR/lib/libVc.a` or + * similar. + * + * @section SIMDVcRestrictions Restrictions + * + * During thorough testing of the Vc abstraction implementation it turned out + * that certain operations were not supported, or were buggy. This meant that + * the tests had to be relaxed, and/or some restrictions had to made as to how + * things must be done through the SIMD abstraction, see @ref + * simd_abstraction_limit. + * + * For future reference, here is a detailed list of things that certain Vc + * types do or don't support. `s` denotes a scalar object/expression (i.e. of + * type `double` or in the case of masks `bool`). `v` denotes a vector/mask + * object/expression. `sv` means that both scalar and vector arguments are + * accepted. `V` denotes a vector/mask type. `@` means any applicable + * operator that is not otherwise listed. + * + * + * \code + | | Vector | Vector | SimdArray | SimdArray | Masks[4] | + | | AVX | SSE | | | | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | V v(s); | y | y | y | y | y | + | V v = s; | y | y | y | y | *N* | + | V v{s}; | *N* | y | *N* | *N* | y | + | V v = {s}; | *N* | y | *N* | *N* | *N* | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | v = s; | y | y | y | y | *N* | + | v = {s}; | *N* | *N* | *N* | *N* | *N* | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | v++; ++v; | y | y | *N* | *N* | y(n/a)[2] | + | v--; --v; | y | y | *N* | *N* | n/a | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | +v; -v; | y | y | y | y | *N* | + | !v; | y | y | y | y | y | + | ~v; | n/a | y | n/a | y | *N* | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | sv @ sv; but see below | y | y | y | y | *N* | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | s << v; s >> v; | n/a | *N* | n/a | *N* | *N* | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | v == v; v != v; | y | y | y | y | *N* [1] | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | v & v; v ^ v; v ¦ v; | n/a | y | n/a | y | y | + | sv && sv; sv ¦¦ sv; | y | y | *N* | *N* | *N* | + | v && v; v ¦¦ v; | y | y | *N* | *N* | y | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | v @= sv; but see below | y | y | y | y | *N* | + | v &= v; v ^= v; v ¦= v; | n/a | y | n/a | y | y | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | v, v;[3] | *N* | *N* | y | y | y | + * \endcode + * + * Notes: + * + * - [1] The result of the mask-mask `==` and `!=` operation is a scalar. + * + * - [2] `++` (either kind) on bools is deprecated by the standard + * + * - [3] contrary to the other operators, the expected result for `(sv1, sv2)` + * is exactly `sv2`, no broadcasting applied. + * + * - [4] Checked with `Vector::Mask` [SSE] and `SimdArray::Mask`, + * which behaved identical + * + * Support levels: + * + * - `y`: operation generally works; some instances of the operation may not + * apply + * + * - `*N*`: operation generally does not work; some instances of the operation + * may not apply + * + * - `n/a`: operation does not apply (i.e. bitwise operations to + * floating-point operands, `--` (and in the future possibly `++`) to + * boolean operands, assignment operators to scalar left hand sides) + * + * Each operation was tested with the full set of combinations of possible + * `const`/non-`const` lvalue/xvalue arguments. Each combination of constness + * and value category was applied to the scalar type and the operation tried + * in an SFINAE context; combinations that failed here were skipped for vector + * arguments too. + */ + +namespace Dune { + namespace Simd { + + namespace VcImpl { + + //! specialized to true for Vc mask types + template + struct IsMask : std::false_type {}; + + template + struct IsMask > : std::true_type {}; + + template + struct IsMask > : std::true_type {}; + + //! specialized to true for Vc vector and mask types + template + struct IsVector : IsMask {}; + + template + struct IsVector > : std::true_type {}; + + template + struct IsVector > : std::true_type {}; + + template struct IsVectorizable : std::false_type {}; + template<> struct IsVectorizable : std::true_type {}; + template<> struct IsVectorizable : std::true_type {}; + template<> struct IsVectorizable : std::true_type {}; + template<> struct IsVectorizable : std::true_type {}; + template<> struct IsVectorizable : std::true_type {}; + template<> struct IsVectorizable : std::true_type {}; + + //! A reference-like proxy for elements of random-access vectors. + /** + * This is necessary because Vc's lane-access operation return a proxy + * that cannot constructed by non-Vc code (i.e. code that isn't + * explicitly declared `friend`). This means in particular that there + * is no copy/move constructor, meaning we cannot return such proxies + * from our own functions, such as `lane()`. To work around this, we + * define our own proxy class which internally holds a reference to the + * vector and a lane index. + * + * Note: this should be unnecessary with C++17, as just returning a + * temporary object should not involve copying it. + */ + template + class Proxy + { + static_assert(std::is_same >::value, "Class Proxy " + "may only be instantiated with unqualified types"); + public: + using value_type = typename V::value_type; + + private: + static_assert(std::is_arithmetic::value, + "Only artihmetic types are supported"); + V &vec_; + std::size_t idx_; + + public: + Proxy(std::size_t idx, V &vec) + : vec_(vec), idx_(idx) + { } + + Proxy(const Proxy&) = delete; + // allow move construction so we can return proxies from functions + Proxy(Proxy&&) = default; + + operator value_type() const { return vec_[idx_]; } + + // assignment operators +#define DUNE_SIMD_VC_ASSIGNMENT(OP) \ + template() OP \ + autoCopy(std::declval()) )> \ + Proxy operator OP(T &&o) && \ + { \ + vec_[idx_] OP autoCopy(std::forward(o)); \ + return { idx_, vec_ }; \ + } + DUNE_SIMD_VC_ASSIGNMENT(=); + DUNE_SIMD_VC_ASSIGNMENT(*=); + DUNE_SIMD_VC_ASSIGNMENT(/=); + DUNE_SIMD_VC_ASSIGNMENT(%=); + DUNE_SIMD_VC_ASSIGNMENT(+=); + DUNE_SIMD_VC_ASSIGNMENT(-=); + DUNE_SIMD_VC_ASSIGNMENT(<<=); + DUNE_SIMD_VC_ASSIGNMENT(>>=); + DUNE_SIMD_VC_ASSIGNMENT(&=); + DUNE_SIMD_VC_ASSIGNMENT(^=); + DUNE_SIMD_VC_ASSIGNMENT(|=); +#undef DUNE_SIMD_VC_ASSIGNMENT + + // unary (prefix) operators + template::value> > + Proxy operator++() { ++(vec_[idx_]); return *this; } + template::value> > + Proxy operator--() { --(vec_[idx_]); return *this; } + + // postfix operators + template::value> > + value_type operator++(int) { return vec_[idx_]++; } + template::value> > + value_type operator--(int) { return vec_[idx_]--; } + + + // swap on proxies swaps the proxied vector entries. As such, it + // applies to rvalues of proxies too, not just lvalues + friend void swap(const Proxy &a, const Proxy &b) { + // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is + // supported by Vc 1.3.2) + value_type tmp = std::move(a.vec_[a.idx_]); + a.vec_[a.idx_] = std::move(b.vec_[b.idx_]); + b.vec_[b.idx_] = std::move(tmp); + } + friend void swap(value_type &a, const Proxy &b) { + // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is + // supported by Vc 1.3.2) + value_type tmp = std::move(a); + a = std::move(b.vec_[b.idx_]); + b.vec_[b.idx_] = std::move(tmp); + } + friend void swap(const Proxy &a, value_type &b) { + // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is + // supported by Vc 1.3.2) + value_type tmp = std::move(a.vec_[a.idx_]); + a.vec_[a.idx_] = std::move(b); + b = std::move(tmp); + } + + // binary operators + // + // Normally, these are provided by the conversion operator in + // combination with C++'s builtin binary operators. Other classes + // that need to provide the binary operators themselves should either + // 1. deduce the "foreign" operand type independently, i.e. use + // template + // auto operator@(MyClass, Foreign); + // or + // 2. not deduce anything from the foreign argument, i.e. + // template + // auto operator@(MyClass, + // typename MyClass::value_type); + // or + // template + // struct MyClass { + // auto operator@(T); + // } + // or + // template + // struct MyClass { + // friend auto operator@(MyClass, T); + // } + // + // This allows either for an exact match (in the case of option 1.) or + // for conversions to be applied to the foreign argument (options 2.). + // In contrast, allowing some of the template parameters being deduced + // from the self argument also being deduced from the foreign argument + // will likely lead to ambigous deduction when the foreign argument is + // a proxy: + // template + // auto operator@(MyClass, T); + // One class that suffers from this problem ist std::complex. + // + // Note that option 1. is a bit dangerous, as the foreign argument is + // catch-all. This seems tempting in the case of a proxy class, as + // the operator could just be forwarded to the proxied object with the + // foreign argument unchanged, immediately creating interoperability + // with arbitrary foreign classes. However, if the foreign class also + // choses option 1., this will result in ambigous overloads, and there + // is no clear guide to decide which class should provide the overload + // and which should not. + // + // Fortunately, deferring to the conversion and the built-in operators + // mostly works in the case of this proxy class, because only built-in + // types can be proxied anyway. Unfortunately, the Vc vectors and + // arrays suffer from a slightly different problem. They chose option + // 1., but they can't just accept the argument type they are given, + // since they need to somehow implement the operation in terms of + // intrinsics. So they check the argument whether it is one of the + // expected types, and remove the operator from the overload set if it + // isn't via SFINAE. Of course, this proxy class is not one of the + // expected types, even though it would convert to them... + // + // So what we have to do here, unfortunately, is to provide operators + // for the Vc types explicitly, and hope that there won't be some Vc + // version that gets the operators right, thus creating ambigous + // overloads. Well, if guess it will be #ifdef time if it comes to + // that. +#define DUNE_SIMD_VC_BINARY(OP) \ + template \ + friend auto operator OP(const Vc::Vector &l, Proxy&& r) \ + -> decltype(l OP std::declval()) \ + { \ + return l OP value_type(r); \ + } \ + template \ + auto operator OP(const Vc::Vector &r) && \ + -> decltype(std::declval() OP r) \ + { \ + return value_type(*this) OP r; \ + } \ + template \ + friend auto \ + operator OP(const Vc::SimdArray &l, Proxy&& r) \ + -> decltype(l OP std::declval()) \ + { \ + return l OP value_type(r); \ + } \ + template \ + auto operator OP(const Vc::SimdArray &r) && \ + -> decltype(std::declval() OP r) \ + { \ + return value_type(*this) OP r; \ + } + + DUNE_SIMD_VC_BINARY(*); + DUNE_SIMD_VC_BINARY(/); + DUNE_SIMD_VC_BINARY(%); + DUNE_SIMD_VC_BINARY(+); + DUNE_SIMD_VC_BINARY(-); + DUNE_SIMD_VC_BINARY(<<); + DUNE_SIMD_VC_BINARY(>>); + DUNE_SIMD_VC_BINARY(&); + DUNE_SIMD_VC_BINARY(^); + DUNE_SIMD_VC_BINARY(|); + DUNE_SIMD_VC_BINARY(<); + DUNE_SIMD_VC_BINARY(>); + DUNE_SIMD_VC_BINARY(<=); + DUNE_SIMD_VC_BINARY(>=); + DUNE_SIMD_VC_BINARY(==); + DUNE_SIMD_VC_BINARY(!=); +#undef DUNE_SIMD_VC_BINARY + + // this is needed to implement broadcast construction from proxy as + // the unadorned assignment operator cannot be a non-member + template::value> > + operator Vc::Vector() && + { + return value_type(*this); + } + template::value> > + operator Vc::SimdArray() && + { + return value_type(*this); + } + +#define DUNE_SIMD_VC_ASSIGN(OP) \ + template \ + friend auto operator OP(Vc::Vector &l, Proxy&& r) \ + -> decltype(l OP std::declval()) \ + { \ + return l OP value_type(r); \ + } + + DUNE_SIMD_VC_ASSIGN(*=); + DUNE_SIMD_VC_ASSIGN(/=); + DUNE_SIMD_VC_ASSIGN(%=); + DUNE_SIMD_VC_ASSIGN(+=); + DUNE_SIMD_VC_ASSIGN(-=); + DUNE_SIMD_VC_ASSIGN(&=); + DUNE_SIMD_VC_ASSIGN(^=); + DUNE_SIMD_VC_ASSIGN(|=); + // The shift assignment would not be needed for Vc::Vector since it + // has overloads for `int` rhs and the proxy can convert to that -- + // except that there is also overloads for Vector, and because of the + // conversion operator needed to support unadorned assignments, the + // proxy can convert to that, too. + DUNE_SIMD_VC_ASSIGN(<<=); + DUNE_SIMD_VC_ASSIGN(>>=); +#undef DUNE_SIMD_VC_ASSIGN + }; + + } // namespace VcImpl + + namespace Overloads { + + /** @name Specialized classes and overloaded functions + * @ingroup SIMDVc + * @{ + */ + + //! should have a member type \c type + /** + * Implements Simd::Scalar + */ + template + struct ScalarType::value> > + { + using type = typename V::value_type; + }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + * + * This specialization covers + * - Mask -> bool + * - Vector -> Scalar + */ + template + struct RebindType, V, + std::enable_if_t::value> > + { + using type = V; + }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + * + * This specialization covers + * - Vector -> bool + */ + template + struct RebindType::value && + !VcImpl::IsMask::value>> + { + using type = typename V::mask_type; + }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + * + * This specialization covers + * - Mask -> Scalar + */ + template + struct RebindType, M, + std::enable_if_t::value>> + { + using type = typename M::Vector; + }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + * + * This specialization covers + * - Mask -> Vc-vectorizable type except bool, Scalar + */ + template + struct RebindType::value && + VcImpl::IsVectorizable::value && + !std::is_same >::value + > > + { + using type = Vc::SimdArray()>; + }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + * + * This specialization covers + * - Vector -> Vc-vectorizable type except bool, Scalar + */ + template + struct RebindType::value && + !VcImpl::IsMask::value && + VcImpl::IsVectorizable::value && + !std::is_same >::value> > + { + using type = Vc::SimdArray()>; + }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + * + * This specialization covers + * - Mask -> non-Vc-vectorizable type except bool + * - Vector -> non-Vc-vectorizable type except bool + */ + template + struct RebindType::value && + !VcImpl::IsVectorizable::value && + !std::is_same::value && + !std::is_same >::value> > + { + using type = LoopSIMD()>; + }; + + //! should be derived from an Dune::index_constant + /** + * Implements Simd::lanes() + */ + template + struct LaneCount::value> > + : public index_constant + { }; + + //! implements Simd::lane() + template + VcImpl::Proxy lane(ADLTag<5, VcImpl::IsVector::value>, + std::size_t l, V &v) + { + return { l, v }; + } + + //! implements Simd::lane() + template + Scalar lane(ADLTag<5, VcImpl::IsVector::value>, + std::size_t l, const V &v) + { + return v[l]; + } + + //! implements Simd::lane() + /* + * The hack with the SFINAE is necessary, because if I use just + * Scalar as the return type, the compiler still errors out if V is + * an lvalue-reference T&. You'd think he'd notice that he can't + * instantiate this declaration for this template parameter, and would + * simply remove it from the overload set, but no... + */ + template::value> > + Scalar lane(ADLTag<5, VcImpl::IsVector::value>, + std::size_t l, V &&v) + { + return std::forward(v)[l]; + } + + //! implements Simd::cond() + template + V cond(ADLTag<5, VcImpl::IsVector::value && + !VcImpl::IsMask::value>, + const Mask &mask, const V &ifTrue, const V &ifFalse) + { + return Vc::iif(mask, ifTrue, ifFalse); + } + + //! implements Simd::cond() + /* + * Kludge because iif seems to be unimplemented for masks + */ + template + V cond(ADLTag<5, VcImpl::IsMask::value>, + const V &mask, const V &ifTrue, const V &ifFalse) + { + return (mask && ifTrue) || (!mask && ifFalse); + } + + //! implements binary Simd::max() + template + auto max(ADLTag<5, VcImpl::IsVector::value && + !VcImpl::IsMask::value>, + const V &v1, const V &v2) + { + return Simd::cond(v1 < v2, v2, v1); + } + + //! implements binary Simd::max() + template + auto max(ADLTag<5, VcImpl::IsMask::value>, + const M &m1, const M &m2) + { + return m1 || m2; + } + + //! implements binary Simd::min() + template + auto min(ADLTag<5, VcImpl::IsVector::value && + !VcImpl::IsMask::value>, + const V &v1, const V &v2) + { + return Simd::cond(v1 < v2, v1, v2); + } + + //! implements binary Simd::min() + template + auto min(ADLTag<5, VcImpl::IsMask::value>, + const M &m1, const M &m2) + { + return m1 && m2; + } + + //! implements Simd::anyTrue() + template + bool anyTrue (ADLTag<5, VcImpl::IsMask::value>, const M &mask) + { + return Vc::any_of(mask); + } + + //! implements Simd::allTrue() + template + bool allTrue (ADLTag<5, VcImpl::IsMask::value>, const M &mask) + { + return Vc::all_of(mask); + } + + // nothing like anyFalse() in Vc, so let defaults.hh handle it + + //! implements Simd::allFalse() + template + bool allFalse(ADLTag<5, VcImpl::IsMask::value>, const M &mask) + { + return Vc::none_of(mask); + } + + //! implements Simd::maxValue() + template + auto max(ADLTag<5, VcImpl::IsVector::value && + !VcImpl::IsMask::value>, + const V &v) + { + return v.max(); + } + + //! implements Simd::maxValue() + template + bool max(ADLTag<5, VcImpl::IsMask::value>, const M &mask) + { + return Vc::any_of(mask); + } + + //! implements Simd::minValue() + template + auto min(ADLTag<5, VcImpl::IsVector::value && + !VcImpl::IsMask::value>, + const V &v) + { + return v.min(); + } + + //! implements Simd::minValue() + template + bool min(ADLTag<5, VcImpl::IsMask::value>, const M &mask) + { + return !Vc::any_of(!mask); + } + + //! implements Simd::maskAnd() + template + auto maskAnd(ADLTag<5, std::is_same, bool>::value && + VcImpl::IsVector::value>, + const S1 &s1, const V2 &v2) + { + return Simd::Mask(Simd::mask(s1)) && Simd::mask(v2); + } + + //! implements Simd::maskAnd() + template + auto maskAnd(ADLTag<5, VcImpl::IsVector::value && + std::is_same, bool>::value>, + const V1 &v1, const S2 &s2) + { + return Simd::mask(v1) && Simd::Mask(Simd::mask(s2)); + } + + //! implements Simd::maskOr() + template + auto maskOr(ADLTag<5, std::is_same, bool>::value && + VcImpl::IsVector::value>, + const S1 &s1, const V2 &v2) + { + return Simd::Mask(Simd::mask(s1)) || Simd::mask(v2); + } + + //! implements Simd::maskOr() + template + auto maskOr(ADLTag<5, VcImpl::IsVector::value && + std::is_same, bool>::value>, + const V1 &v1, const S2 &s2) + { + return Simd::mask(v1) || Simd::Mask(Simd::mask(s2)); + } + + //! @} group SIMDVc + + } // namespace Overloads + + } // namespace Simd + + /* + * Specialize IsNumber for Vc::SimdArray and Vc::Vector to be able to use + * it as a scalar in DenseMatrix etc. + */ + template + struct IsNumber> + : public std::integral_constant::value> { + }; + + template + struct IsNumber> + : public std::integral_constant::value> { + }; + + //! Specialization of AutonomousValue for Vc proxies + template + struct AutonomousValueType > : + AutonomousValueType::value_type> {}; + +} // namespace Dune + +#endif // DUNE_COMMON_SIMD_VC_HH diff --git a/dune/common/singleton.hh b/dune/common/singleton.hh new file mode 100644 index 0000000..0cbc7bc --- /dev/null +++ b/dune/common/singleton.hh @@ -0,0 +1,77 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_SINGLETON_HH +#define DUNE_SINGLETON_HH + +#include + +/** + * @file + * @brief Useful wrapper for creating singletons. + * + * Inspired by the article + * CodeGuru: A Leak-Free Singleton class + */ +namespace Dune +{ + /** + * @brief An adapter to turn a class into a singleton. + * + * The class represented by the template parameter T must + * have a parameterless constructor. + * + * Class T can be publicly derived from Singleton: + * + * \code + * #include + * class Foo : public Dune::Singleton + * { + * public: + * Foo() + * { + * bytes = new char[1000]; + * } + * + * ~Foo() + * { + * delete[] bytes; + * } + * private: + * char* bytes; + * }; + * \endcode + * + * Or one can construct a Singleton of an existing class. Say Foo1 is a class + * with parameterless constructor then + * \code + * typedef Dune::Singleton FooSingleton; + * Foo1 instance& = FooSingleton::instance(); + * \endcode + * Creates a singleton of that class and accesses its instance. + */ + template + class Singleton + { + protected: + /* @brief Protected constructor. */ + Singleton() = default; + + public: + + Singleton(const Singleton&) = delete; + void operator=(const Singleton&) = delete; + + /** + * @brief Get the instance of the singleton. + * @return The instance of the singleton. + */ + DUNE_EXPORT static T& instance() + { + static T instance_; + return instance_; + } + }; + +} // namespace Dune + +#endif diff --git a/dune/common/sllist.hh b/dune/common/sllist.hh new file mode 100644 index 0000000..4cd980c --- /dev/null +++ b/dune/common/sllist.hh @@ -0,0 +1,807 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_SLLIST_HH +#define DUNE_SLLIST_HH + +#include +#include +#include "iteratorfacades.hh" +#include + +namespace Dune +{ + /** + * @addtogroup Common + * + * @{ + */ + /** + * @file + * \brief Implements a singly linked list together with + * the necessary iterators. + * @author Markus Blatt + */ + template + class SLListIterator; + + template + class SLListConstIterator; + + template + class SLListModifyIterator; + + /** + * @brief A single linked list. + * + * The list is capable of insertions at the front and at + * the end and of removing elements at the front. Those + * operations require constant time. + */ + template > + class SLList + { + struct Element; + friend class SLListIterator; + friend class SLListConstIterator; + + public: + + /** + * @brief The size type. + */ + typedef typename A::size_type size_type; + + /** + * @brief The type we store. + */ + typedef T MemberType; + + /** + * @brief The allocator to use. + */ + using Allocator = typename std::allocator_traits::template rebind_alloc; + + /** + * @brief The mutable iterator of the list. + */ + typedef SLListIterator iterator; + + /** + * @brief The constant iterator of the list. + */ + typedef SLListConstIterator const_iterator; + + /** + * @brief Constructor. + */ + SLList(); + + /** + * @brief Copy constructor with type conversion. + */ + template + SLList(const SLList& other); + + /** + * @brief Copy constructor. + */ + SLList(const SLList& other); + + /** + * @brief Destructor. + * + * Deallocates all elements in the list. + */ + ~SLList(); + + /** + * @brief The type of the iterator capable of deletion + * and insertion. + */ + typedef SLListModifyIterator ModifyIterator; + + /** + * @brief Assignment operator. + */ + SLList& operator=(const SLList& other); + + + /** + * @brief Add a new entry to the end of the list. + * @param item The item to add. + */ + inline void push_back(const MemberType& item); + + /** + * @brief Add a new entry to the beginning of the list. + * @param item The item to add. + */ + inline void push_front(const MemberType& item); + + /** + * @brief Remove the first item in the list. + */ + inline void pop_front(); + + /** @brief Remove all elements from the list. */ + inline void clear(); + + /** + * @brief Get an iterator pointing to the first + * element in the list. + * + * @return An iterator pointing to the first + * element or the end if the list is empty. + */ + inline iterator begin(); + + /** + * @brief Get an iterator pointing to the first + * element in the list. + * + * @return An iterator pointing to the first + * element or the end if the list is empty. + */ + inline const_iterator begin() const; + + /** + * @brief Get an iterator capable of deleting and + * inserting elements. + * + * @return Modifying iterator positioned at the beginning + * of the list. + */ + inline ModifyIterator beginModify(); + + /** + * @brief Get an iterator capable of deleting and + * inserting elements. + * + * @return Modifying iterator positioned after the end + * of the list. + */ + inline ModifyIterator endModify(); + + /** + * @brief Get an iterator pointing to the + * end of the list. + * + * @return An iterator pointing to the end. + */ + inline iterator end(); + + /** + * @brief Get an iterator pointing to the + * end of the list. + * + * @return An iterator pointing to the end. + */ + inline const_iterator end() const; + + /** + * @brief Check whether the list is empty. + * + * @return True if the list is empty; + */ + inline bool empty() const; + + /** + * @brief Get the number of elements the list + * contains. + */ + inline int size() const; + + bool operator==(const SLList& sl) const; + + + bool operator!=(const SLList& sl) const; + + private: + /** \todo Please doc me! */ + struct Element + { + /** + * @brief The next element in the list. + */ + Element* next_; + /** + * @brief The element we hold. + */ + MemberType item_; + + Element(const MemberType& item, Element* next_=0); + + Element(); + + ~Element(); + }; + + /** + * @brief Delete the next element in the list. + * @param current Element whose next element should be deleted. + */ + void deleteNext(Element* current); + + /** + * @brief Copy the elements from another list. + * @param other The other list. + */ + void copyElements(const SLList& other); + + /** + * @brief Delete the next element in the list. + * + * If the template parameter watchForTail is true, it is checked whether + * the deleted element is the tail and therefore the tail must be updated. + * @param current Element whose next element should be deleted. + */ + template + void deleteNext(Element* current); + /** + * @brief Insert an element after another one in the list. + * @param current The element after which we insert. + * @param item The item to insert. + */ + void insertAfter(Element* current, const T& item); + + /** @brief Pseudo element before the first entry. */ + Element beforeHead_; + + /** + * @brief Pointer to he last element in the list. + * + * If list is empty this will point to beforeHead_ + */ + Element* tail_; + + /** @brief The allocator we use. */ + Allocator allocator_; + + /** brief The number of elements the list holds. */ + int size_; + }; + + /** + * @brief A mutable iterator for the SLList. + */ + template + class SLListIterator : public Dune::ForwardIteratorFacade, T, T&, std::size_t> + { + friend class SLListConstIterator; + friend class SLListModifyIterator; + friend class SLList; + + public: + inline SLListIterator(typename SLList::Element* item, + SLList* sllist) + : current_(item), list_(sllist) + {} + + inline SLListIterator() + : current_(0), list_(0) + {} + + inline SLListIterator(const SLListModifyIterator& other) + : current_(other.iterator_.current_), list_(other.iterator_.list_) + {} + + /** + * @brief Dereferencing function for the iterator facade. + * @return A reference to the element at the current position. + */ + inline T& dereference() const + { + return current_->item_; + } + + /** + * @brief Equality test for the iterator facade. + * @param other The other iterator to check. + * @return true If the other iterator is at the same position. + */ + inline bool equals(const SLListConstIterator& other) const + { + return current_==other.current_; + } + + /** + * @brief Equality test for the iterator facade. + * @param other The other iterator to check. + * @return true If the other iterator is at the same position. + */ + inline bool equals(const SLListIterator& other) const + { + return current_==other.current_; + } + + /** + * @brief Equality test for the iterator facade. + * @param other The other iterator to check. + * @return true If the other iterator is at the same position. + */ + inline bool equals(const SLListModifyIterator& other) const + { + return current_==other.iterator_.current_; + } + + /** + * @brief Increment function for the iterator facade. + */ + inline void increment() + { + current_ = current_->next_; + } + + /** + * @brief Insert an element in the underlying list after + * the current position. + * @param v The value to insert. + */ + inline void insertAfter(const T& v) const + { + assert(list_ ); + list_->insertAfter(current_, v); + } + + /** + * @brief Delete the entry after the current position. + * + * @warning This will invalidate all iterators positioned at the delete position! Use with care! + */ + inline void deleteNext() const + { + assert(list_); + list_->deleteNext(current_); + } + + private: + /** @brief The current element. */ + typename SLList::Element* current_; + /** @brief The list we iterate over. */ + SLList* list_; + }; + + /** + * @brief A constant iterator for the SLList. + */ + template + class SLListConstIterator : public Dune::ForwardIteratorFacade, const T, const T&, std::size_t> + { + friend class SLListIterator; + friend class SLList; + + public: + inline SLListConstIterator() + : current_(0) + {} + + inline SLListConstIterator(typename SLList::Element* item) + : current_(item) + {} + + inline SLListConstIterator(const SLListIterator& other) + : current_(other.current_) + {} + + inline SLListConstIterator(const SLListConstIterator& other) + : current_(other.current_) + {} + + inline SLListConstIterator(const SLListModifyIterator& other) + : current_(other.iterator_.current_) + {} + + /** + * @brief Dereferencing function for the facade. + * @return A reference to the element at the current position. + */ + inline const T& dereference() const + { + return current_->item_; + } + + /** + * @brief Equality test for the iterator facade. + * @param other The other iterator to check. + * @return true If the other iterator is at the same position. + */ + inline bool equals(const SLListConstIterator& other) const + { + return current_==other.current_; + } + + /** + * @brief Increment function for the iterator facade. + */ + inline void increment() + { + current_ = current_->next_; + } + + private: + /** @brief The current element. */ + typename SLList::Element* current_; + }; + + /** + * @brief A mutable iterator for the SLList. + */ + template + class SLListModifyIterator : public Dune::ForwardIteratorFacade, T, T&, std::size_t> + { + friend class SLListConstIterator; + friend class SLListIterator; + public: + inline SLListModifyIterator(SLListIterator beforeIterator, + SLListIterator _iterator) + : beforeIterator_(beforeIterator), iterator_(_iterator) + {} + + inline SLListModifyIterator(const SLListModifyIterator& other) + : beforeIterator_(other.beforeIterator_), iterator_(other.iterator_) + {} + + inline SLListModifyIterator() + : beforeIterator_(), iterator_() + {} + + /** + * @brief Dereferencing function for the iterator facade. + * @return A reference to the element at the current position. + */ + inline T& dereference() const + { + return *iterator_; + } + + /** + * @brief Test whether another iterator is equal. + * @return true if the other iterator is at the same position as + * this one. + */ + inline bool equals(const SLListConstIterator& other) const + { + return iterator_== other; + } + + + /** + * @brief Test whether another iterator is equal. + * @return true if the other iterator is at the same position as + * this one. + */ + inline bool equals(const SLListIterator& other) const + { + return iterator_== other; + } + + + /** + * @brief Test whether another iterator is equal. + * @return true if the other iterator is at the same position as + * this one. + */ + inline bool equals(const SLListModifyIterator& other) const + { + return iterator_== other.iterator_; + } + + /** + * @brief Increment function for the iterator facade. + */ + inline void increment() + { + ++iterator_; + ++beforeIterator_; + } + + /** + * @brief Insert an element at the current position. + * + * Starting from the element at the current position all + * elements will be shifted by one position to the back. + * The iterator will point to the same element as before + * after the insertion, i.e the number of increments to + * reach the same position from a begin iterator increases + * by one. + * This means the inserted element is the one before the one + * the iterator points to. + * @param v The value to insert. + */ + inline void insert(const T& v) + { + beforeIterator_.insertAfter(v); + ++beforeIterator_; + } + + /** + * @brief Delete the entry at the current position. + * + * The iterator will be positioned at the next position after the + * deletion + * @warning This will invalidate all iterators positioned at the delete position! Use with care! + */ + inline void remove() + { + ++iterator_; + beforeIterator_.deleteNext(); + } + + private: + /** @brief Iterator positioned at the position before the current. */ + SLListIterator beforeIterator_; + /** @brief Iterator positioned at the current position. */ + SLListIterator iterator_; + }; + + template + std::ostream& operator<<(std::ostream& os, const SLList& sllist) + { + typedef typename SLList::const_iterator Iterator; + Iterator end = sllist.end(); + Iterator current= sllist.begin(); + + os << "{ "; + + if(current!=end) { + os<<*current<<" ("<(&(*current))<<")"; + ++current; + + for(; current != end; ++current) + os<<", "<<*current<<" ("<(&(*current))<<")"; + } + os<<"} "; + return os; + } + + template + SLList::Element::Element(const MemberType& item, Element* next) + : next_(next), item_(item) + {} + + template + SLList::Element::Element() + : next_(0), item_() + {} + + template + SLList::Element::~Element() + { + next_=0; + } + + template + SLList::SLList() + : beforeHead_(), tail_(&beforeHead_), allocator_(), size_(0) + { + beforeHead_.next_=0; + assert(&beforeHead_==tail_); + assert(tail_->next_==0); + } + + template + SLList::SLList(const SLList& other) + : beforeHead_(), tail_(&beforeHead_), allocator_(), size_(0) + { + copyElements(other); + } + + template + template + SLList::SLList(const SLList& other) + : beforeHead_(), tail_(&beforeHead_), allocator_(), size_(0) + { + copyElements(other); + } + + template + void SLList::copyElements(const SLList& other) + { + assert(tail_==&beforeHead_); + assert(size_==0); + typedef typename SLList::const_iterator Iterator; + Iterator iend = other.end(); + for(Iterator element=other.begin(); element != iend; ++element) + push_back(*element); + + assert(other.size()==size()); + } + + template + SLList::~SLList() + { + clear(); + } + + template + bool SLList::operator==(const SLList& other) const + { + if(size()!=other.size()) + return false; + for(const_iterator iter=begin(), oiter=other.begin(); + iter != end(); ++iter, ++oiter) + if(*iter!=*oiter) + return false; + return true; + } + + template + bool SLList::operator!=(const SLList& other) const + { + if(size()==other.size()) { + for(const_iterator iter=begin(), oiter=other.begin(); + iter != end(); ++iter, ++oiter) + if(*iter!=*oiter) + return true; + return false; + }else + return true; + } + template + SLList& SLList::operator=(const SLList& other) + { + clear(); + copyElements(other); + return *this; + } + + template + inline void SLList::push_back(const MemberType& item) + { + assert(size_>0 || tail_==&beforeHead_); + tail_->next_ = allocator_.allocate(1); + assert(size_>0 || tail_==&beforeHead_); + tail_ = tail_->next_; + ::new (static_cast(&(tail_->item_)))T(item); + tail_->next_=0; + assert(tail_->next_==0); + ++size_; + } + + template + inline void SLList::insertAfter(Element* current, const T& item) + { + assert(current); + +#ifndef NDEBUG + bool changeTail = (current == tail_); +#endif + + // Save old next element + Element* tmp = current->next_; + + assert(!changeTail || !tmp); + + // Allocate space + current->next_ = allocator_.allocate(1); + + // Use copy constructor to initialize memory + std::allocator_traits::construct(allocator_, current->next_, Element(item,tmp)); + + //::new(static_cast(&(current->next_->item_))) T(item); + + if(!current->next_->next_) { + // Update tail + assert(changeTail); + tail_ = current->next_; + } + ++size_; + assert(!tail_->next_); + } + + template + inline void SLList::push_front(const MemberType& item) + { + if(tail_ == &beforeHead_) { + // list was empty + beforeHead_.next_ = tail_ = allocator_.allocate(1, 0); + ::new(static_cast(&beforeHead_.next_->item_))T(item); + beforeHead_.next_->next_=0; + }else{ + Element* added = allocator_.allocate(1, 0); + ::new(static_cast(&added->item_))T(item); + added->next_=beforeHead_.next_; + beforeHead_.next_=added; + } + assert(tail_->next_==0); + ++size_; + } + + + template + inline void SLList::deleteNext(Element* current) + { + this->template deleteNext(current); + } + + template + template + inline void SLList::deleteNext(Element* current) + { + assert(current->next_); + Element* next = current->next_; + + if(watchForTail) + if(next == tail_) { + // deleting last element changes tail! + tail_ = current; + } + + current->next_ = next->next_; + std::allocator_traits::destroy(allocator_, next); + allocator_.deallocate(next, 1); + --size_; + assert(!watchForTail || &beforeHead_ != tail_ || size_==0); + } + + template + inline void SLList::pop_front() + { + deleteNext(&beforeHead_); + } + + template + inline void SLList::clear() + { + while(beforeHead_.next_ ) { + this->template deleteNext(&beforeHead_); + } + + assert(size_==0); + // update the tail! + tail_ = &beforeHead_; + } + + template + inline bool SLList::empty() const + { + return (&beforeHead_ == tail_); + } + + template + inline int SLList::size() const + { + return size_; + } + + template + inline SLListIterator SLList::begin() + { + return iterator(beforeHead_.next_, this); + } + + template + inline SLListConstIterator SLList::begin() const + { + return const_iterator(beforeHead_.next_); + } + + template + inline SLListIterator SLList::end() + { + return iterator(); + } + + template + inline SLListModifyIterator SLList::endModify() + { + return SLListModifyIterator(iterator(tail_, this),iterator()); + } + + + template + inline SLListModifyIterator SLList::beginModify() + { + return SLListModifyIterator(iterator(&beforeHead_, this), + iterator(beforeHead_.next_, this)); + } + + template + inline SLListConstIterator SLList::end() const + { + return const_iterator(); + } + + /** }@ */ +} +#endif diff --git a/dune/common/std/CMakeLists.txt b/dune/common/std/CMakeLists.txt new file mode 100644 index 0000000..564d433 --- /dev/null +++ b/dune/common/std/CMakeLists.txt @@ -0,0 +1,10 @@ +install( + FILES + apply.hh + functional.hh + make_array.hh + optional.hh + type_traits.hh + utility.hh + variant.hh + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/common/std) diff --git a/dune/common/std/apply.hh b/dune/common/std/apply.hh new file mode 100644 index 0000000..483ff81 --- /dev/null +++ b/dune/common/std/apply.hh @@ -0,0 +1,20 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_STD_APPLY_HH +#define DUNE_COMMON_STD_APPLY_HH + +#include + +namespace Dune +{ + namespace Std + { + + /// Invoke the Callable object f with a tuple of arguments. + /// \deprecated Use `std::apply` directly. + using std::apply; + + } // namespace Std +} // namespace Dune + +#endif // #ifndef DUNE_COMMON_STD_APPLY_HH diff --git a/dune/common/std/functional.hh b/dune/common/std/functional.hh new file mode 100644 index 0000000..c81a7ab --- /dev/null +++ b/dune/common/std/functional.hh @@ -0,0 +1,32 @@ +// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set ts=8 sw=2 et sts=2: +#ifndef DUNE_COMMON_STD_FUNCTIONAL_HH +#define DUNE_COMMON_STD_FUNCTIONAL_HH + +#include + +namespace Dune +{ + + namespace Std + { + + /** + * @brief A function object type whose operator() returns its argument unchanged + * @note Equivalent to: `return std::forward(t);` + * @warning When passing `r-values`, the result must be, at most, used for direct + * consumption in an outer function call + */ +#if DUNE_HAVE_CXX_STD_IDENTITY + using std::identity; +#else //DUNE_HAVE_CXX_STD_IDENTITY + struct identity { + template + constexpr T&& operator()(T&& t ) const noexcept {return std::forward(t);} + }; +#endif + } // namespace Std + +} // namespace Dune + +#endif // #ifndef DUNE_COMMON_STD_FUNCTIONAL_HH diff --git a/dune/common/std/make_array.hh b/dune/common/std/make_array.hh new file mode 100644 index 0000000..cdea323 --- /dev/null +++ b/dune/common/std/make_array.hh @@ -0,0 +1,49 @@ +#ifndef DUNE_COMMON_STD_MAKE_ARRAY_HH +#define DUNE_COMMON_STD_MAKE_ARRAY_HH + +#include +#include + +#if DUNE_HAVE_CXX_EXPERIMENTAL_MAKE_ARRAY +#include +#endif + +namespace Dune { +namespace Std { + +#if DUNE_HAVE_CXX_EXPERIMENTAL_MAKE_ARRAY + + /// \deprecated Use deduction guide of `std::array` or `std::to_array`. + using std::experimental::make_array; + +#else // DUNE_HAVE_CXX_EXPERIMENTAL_MAKE_ARRAY + + //! Create and initialize an array + /** + * \note This method is a somewhat limited dune-specific version of + * make_array() as proposed for C++17 (see N4391, + * accepted May + * 2015). The differences are that this version should + * never be used with expliclitly given template arguments, or + * with std::reference_wrapper<...> arguments, and we do not + * give a diagnostic when anyone happens to do that. + * + * \ingroup CxxUtilities + * \deprecated Use deduction guide of `std::array` or `std::to_array`. + */ + template + std::array::type, sizeof...(Args)> + make_array(const Args&... args) { + std::array::type, sizeof...(Args)> + result = {{args...}}; + return result; + } + +#endif // DUNE_HAVE_CXX_EXPERIMENTAL_MAKE_ARRAY + +} +} + +#endif diff --git a/dune/common/std/optional.hh b/dune/common/std/optional.hh new file mode 100644 index 0000000..ed32fc3 --- /dev/null +++ b/dune/common/std/optional.hh @@ -0,0 +1,39 @@ +#ifndef DUNE_COMMON_STD_OPTIONAL_HH +#define DUNE_COMMON_STD_OPTIONAL_HH + +#include +#include +#include +#include +#include + +#include + +#warning dune/common/std/optional.hh is deprecated and will be removed after Dune 2.8.\ + Include instead + +namespace Dune +{ + + namespace Std + { + // In case of C++ standard >= 17 we forward optionals into our namespace + template< class T > + using optional = std::optional< T >; + + using nullopt_t = std::nullopt_t; + using in_place_t = std::in_place_t; + + namespace + { + const std::nullopt_t nullopt = std::nullopt; + const std::in_place_t in_place = std::in_place; + } // anonymous namespace + + using bad_optional_access = std::bad_optional_access; + + } // namespace Std + +} // namespace Dune + +#endif // #ifndef DUNE_COMMON_STD_OPTIONAL_HH diff --git a/dune/common/std/type_traits.hh b/dune/common/std/type_traits.hh new file mode 100644 index 0000000..497e80e --- /dev/null +++ b/dune/common/std/type_traits.hh @@ -0,0 +1,471 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_STD_TYPE_TRAITS_HH +#define DUNE_COMMON_STD_TYPE_TRAITS_HH + +#include +#include +#include + +#if __has_include() +#include +#endif + +namespace Dune +{ + +//! Namespace for features backported from new C++ standards +/** + * The namespace Dune::Std contains library features of new C++ standards and + * technical specifications backported to older compilers. Most features are + * detected and pulled into this namespace from the standard library if your + * compiler has native support. If it doesn't, we provide a fallback implementation + * on a best-effort basis. + * + * \ingroup CxxUtilities + */ +namespace Std +{ + + // to_false_type + // ------------- + + /** \class to_false_type + * + * \brief template mapping a type to std::false_type + * \deprecated Use Dune::AlwaysFalse (from dune/common/typetraits.hh) instead + * \tparam T Some type + * + * Suppose you have a template class. You want to document the required + * members of this class in the non-specialized template, but you know that + * actually instantiating the non-specialized template is an error. You can + * try something like this: + * \code + * template + * struct Traits + * { + * static_assert(false, + * "Instanciating this non-specialized template is an " + * "error. You should use one of the specializations " + * "instead."); + * //! The type used to frobnicate T + * typedef void FrobnicateType; + * }; + * \endcode + * This will trigger static_assert() as soon as the compiler reads the + * definition for the Traits template, since it knows that "false" can never + * become true, no matter what the template parameters of Traits are. As a + * workaround you can use to_false_type: replace false by + * to_false_type::value, like this: + * \code + * template + * struct Traits + * { + * static_assert(Std::to_false_type::value, + * "Instanciating this non-specialized template is an " + * "error. You should use one of the specializations " + * "instead."); + * //! The type used to frobnicate T + * typedef void FrobnicateType; + * }; + * \endcode + * Since there might be an specialization of to_false_type for template + * parameter T, the compiler cannot trigger static_assert() until the type + * of T is known, that is, until Traits is instantiated. + * + * \ingroup CxxUtilities + */ + template< typename T > + struct [[deprecated("Will be removed after release 2.8. Use Dune::AlwaysFalse (from dune/common/typetraits.hh)")]] to_false_type : public std::false_type {}; + + + + // to_true_type + // ------------ + + /** \class to_true_type + * + * \brief template mapping a type to std::true_type + * \deprecated Use Dune::AlwaysFalse (from dune/common/typetraits.hh) instead + * \tparam T Some type + * + * \note This class exists mostly for consistency with to_false_type. + * + * \ingroup CxxUtilities + */ + template< typename T > + struct [[deprecated("Will be removed after release 2.8. Use Dune::AlwaysTrue (from dune/common/typetraits.hh)")]] to_true_type : public std::true_type {}; + + + /// A helper alias template std::bool_constant imported into the namespace Dune::Std + /// \deprecated Use the `std::bool_constant` directly. + using std::bool_constant; + + + namespace Impl { + + // If R is void we only need to check if F can be called + // with given Args... list. If this is not possible + // result_of_t is not defined and this overload is disabled. + template>, R>::value + , int> = 0> + std::true_type is_callable_helper(PriorityTag<2>) + { return {}; } + + // Check if result of F(Args...) can be converted to R. + // If F cannot even be called with given Args... then + // result_of_t is not defined and this overload is disabled. + template, R>::value + , int> = 0> + std::true_type is_callable_helper(PriorityTag<1>) + { return {}; } + + // If none of the above matches, F can either not be called + // with given Args..., or the result cannot be converted to + // void, or R is not void. + template + std::false_type is_callable_helper(PriorityTag<0>) + { return {}; } + } + + /** + * \brief Traits class to check if function is callable + * \deprecated Use std::is_invocable from + * + * \tparam D Function descriptor + * \tparam R Return value + * + * If D = F(Args...) this checks if F can be called with an + * argument list of type Args..., and if the return value can + * be converted to R. If R is void, any return type is accepted. + * The result is encoded by deriving from std::integral_constant. + * + * If D is not of the form D = F(Args...) this class is not defined. + * + * This implements std::is_callable as proposed in N4446 for C++17. + * + * \ingroup CxxUtilities + */ + template + struct is_callable; + + /** + * \brief Traits class to check if function is callable + * \deprecated Use std::is_invocable from + * + * \tparam D Function descriptor + * \tparam R Return value + * + * If D = F(Args...) this checks if F can be called with an + * argument list of type Args..., and if the return value can + * be converted to R. If R is void, any return type is accepted. + * The result is encoded by deriving from std::integral_constant. + * + * If D is not of the form D = F(Args...) this class is not defined. + * + * This implements std::is_callable as proposed in N4446 for C++17. + * + * \ingroup CxxUtilities + */ + template + struct [[deprecated("Use std::is_invocable from . Will be removed after release 2.8")]] is_callable< F(Args...), R> : + decltype(Impl::is_callable_helper(PriorityTag<42>())) + {}; + + + /** + * \brief Traits class to check if function is invocable + * \deprecated Use std::is_invocable from + * + * \tparam F Function to check + * \tparam Args Function arguments to check + * + * This checks if F can be called with an arguments list of type Args.... + * The result is encoded by deriving from std::integral_constant. + * + * This implements std::is_invocable from C++17. + * + * \ingroup CxxUtilities + */ + template + struct [[deprecated("Use std::is_invocable from . Will be removed after release 2.8")]] is_invocable : + decltype(Impl::is_callable_helper(PriorityTag<42>())) + {}; + + /** + * \brief Traits class to check if function is invocable and the return type is compatible + * \deprecated Use std::is_invocable_r from + * + * \tparam R Desired result type + * \tparam F Function to check + * \tparam Args Function arguments to check + * + * This checks if F can be called with an arguments list of type Args..., and + * if the return value can be converted to R. + * The result is encoded by deriving from std::integral_constant. + * + * This implements std::is_invocable_r from C++17. + * + * \ingroup CxxUtilities + */ + template + struct [[deprecated("Use std::is_invocable_r from . Will be removed after release 2.8")]] is_invocable_r : + decltype(Impl::is_callable_helper(PriorityTag<42>())) + {}; + + +#if DUNE_HAVE_CXX_EXPERIMENTAL_IS_DETECTED + + using std::experimental::nonesuch; + using std::experimental::detected_or; + using std::experimental::is_detected; + using std::experimental::detected_t; + using std::experimental::is_detected_v; + using std::experimental::detected_or_t; + using std::experimental::is_detected_exact; + using std::experimental::is_detected_exact_v; + using std::experimental::is_detected_convertible; + using std::experimental::is_detected_convertible_v; + +#else // DUNE_HAVE_CXX_EXPERIMENTAL_IS_DETECTED + + // fallback version of std::experimental::is_detected et al., heavily scribbled + // from cppreference.com (but there is actually not much implementation to the thing) + +#ifndef DOXYGEN + + namespace Impl { + + // default version of detector, this gets matched on failure + template class Op, typename... Args> + struct detector + { + using value_t = std::false_type; + using type = Default; + }; + + // specialization of detector that matches if Op can be instantiated + template class Op, typename... Args> + struct detector>, Op, Args...> + { + using value_t = std::true_type; + using type = Op; + }; + + } + +#endif // DOXYGEN + + //! Type representing a lookup failure by std::detected_or and friends. + /** + * This type cannot be constructed, destroyed or copied. + * + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + struct nonesuch + { + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(const nonesuch&) = delete; + void operator=(const nonesuch&) = delete; + }; + + //! Detects whether `Op` is valid and makes the result available. + /** + * This alias template is an alias for an unspecified class type with two + * nested `typedefs` `value_t` and `type`. It can be used to detect whether + * the meta function call `Op` is valid and access the result of + * the call by inspecting the returned type, which is defined as follows: + * + * * If `Op` can be instantiated, `value_t` is an alias for `std::true_type` + * and `type` is an alias for `Op`. + * * If `Op` is invalid, `value_t` is an alias for `std::false_type` + * and `type` is an alias for `Default`. + * + * This can be used to safely extract a nested `typedef` from a type `T` that + * might not define the `typedef`: + \code + struct A { using size_type = int ; }; + struct B; + + template + using SizeType = typename T::size_type; + + // this extracts the nested typedef for int + using st_a = typename detected_or::type; + // as there is no nested typedef in B, this yields std::size_t + using st_b = typename detected_or::type; + \endcode + * + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template class Op, typename... Args> + using detected_or = Impl::detector; + + //! Detects whether `Op` is valid. + /** + * This alias template checks whether `Op` can be instantiated. It is + * equivalent to `typename detected_or::value_t`. + * + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template class Op, typename... Args> + using is_detected = typename detected_or::value_t; + +#ifdef __cpp_variable_templates + //! Detects whether `Op` is valid and makes the result available as a value. + /** + * This constexpr variable checks whether `Op` can be instantiated. It is + * equivalent to `is_detected::value`. + * + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template class Op, typename... Args> + constexpr bool is_detected_v = is_detected::value; +#endif // __cpp_variable_templates + + //! Returns `Op` if that is valid; otherwise returns nonesuch. + /** + * This alias template can be used to instantiate `Op` in a context that is + * not SFINAE-safe by appropriately wrapping the instantiation. If instantiation fails, + * the marker type nonesuch is returned instead. + * + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template class Op, typename... Args> + using detected_t = typename detected_or::type; + + + //! Returns `Op` if that is valid; otherwise returns the fallback type `Default`. + /** + * This alias template can be used to instantiate `Op` in a context that is + * not SFINAE-safe by appropriately wrapping the instantiation and automatically falling back + * to `Default` if instantiation fails. + * + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template class Op, typename... Args> + using detected_or_t = typename detected_or::type; + + //! Checks whether `Op` is `Expected` without causing an error if `Op` is invalid. + /** + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template class Op, typename... Args> + using is_detected_exact = std::is_same>; + +#ifdef __cpp_variable_templates + //! Convenient access to the result value of is_detected_exact. + /** + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template class Op, typename... Args> + constexpr bool is_detected_exact_v = is_detected_exact::value; +#endif // __cpp_variable_templates + + //! Checks whether `Op` is convertible to `Target` without causing an error if `Op` is invalid. + /** + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template class Op, typename... Args> + using is_detected_convertible = std::is_convertible>; + +#ifdef __cpp_variable_templates + //! Convenient access to the result value of is_detected_convertible. + /** + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template class Op, typename... Args> + constexpr bool is_detected_convertible_v = is_detected_convertible::value; +#endif // __cpp_variable_templates + +#endif // DUNE_HAVE_CXX_EXPERIMENTAL_IS_DETECTED + + + + // conjunction + // ----------- + + /** + * \brief forms the logical conjunction of the type traits B... + * + * \note This functionality is part of the C++17 standard. + * + * \ingroup CxxUtilities + **/ + template< class... B > + struct [[deprecated("Will be removed after release 2.8. Use std::conjuction instead.")]] conjunction + : std::conjunction + {}; + + + // disjunction + // ----------- + + /** + * \brief forms the logical disjunction of the type traits B... + * + * \note This functionality is part of the C++17 standard. + * + * \ingroup CxxUtilities + **/ + template< class... B > + struct [[deprecated("Will be removed after release 2.8. Use std::disjunction instead.")]] disjunction + : std::disjunction + {}; + + + // negation + // -------- + + /** + * \brief forms the logical negation of the type traits B... + * + * \note This functionality is part of the C++17 standard. + * + * \ingroup CxxUtilities + **/ + template + struct [[deprecated("Will be removed after release 2.8. Use std::negation instead.")]] negation + : std::negation + {}; + +} // namespace Std + +} // namespace Dune + +#endif // #ifndef DUNE_COMMON_STD_TYPE_TRAITS_HH diff --git a/dune/common/std/utility.hh b/dune/common/std/utility.hh new file mode 100644 index 0000000..1f521bc --- /dev/null +++ b/dune/common/std/utility.hh @@ -0,0 +1,25 @@ +#ifndef DUNE_COMMON_STD_UTILITY_HH +#define DUNE_COMMON_STD_UTILITY_HH + +#include + +#warning dune/common/std/utility.hh is deprecated and will be removed after Dune 2.8.\ + Include instead + +namespace Dune +{ + + namespace Std + { + + using std::integer_sequence; + using std::index_sequence; + using std::make_integer_sequence; + using std::make_index_sequence; + using std::index_sequence_for; + + } // namespace Std + +} // namespace Dune + +#endif // #ifndef DUNE_COMMON_STD_UTILITY_HH diff --git a/dune/common/std/variant.hh b/dune/common/std/variant.hh new file mode 100644 index 0000000..103e606 --- /dev/null +++ b/dune/common/std/variant.hh @@ -0,0 +1,24 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_STD_VARIANT_HH +#define DUNE_COMMON_STD_VARIANT_HH + +#warning dune/common/std/variant.hh is deprecated and will be removed after Dune 2.8.\ + Include instead + +#include + +namespace Dune { +namespace Std { + using std::variant; + using std::visit; + using std::variant_size; + using std::variant_size_v; + using std::get; + using std::get_if; + using std::holds_alternative; + using std::monostate; +} +} + +#endif diff --git a/dune/common/stdstreams.cc b/dune/common/stdstreams.cc new file mode 100644 index 0000000..1dc56b1 --- /dev/null +++ b/dune/common/stdstreams.cc @@ -0,0 +1,44 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "stdstreams.hh" + +namespace Dune { + + /* + + The standard debug streams declared in stdstreams.hh exist in this + file so that they can be compiled into libdune + + */ + + /* stream for very verbose output: information on the lowest + level. This is expected to report insane amounts of + information. Use of the activation-flag to only generate output + near the problem is recommended */ + DVVerbType dvverb(std::cout); + + /* stream for verbose output: information that helps to trace in + more detail what the modules do */ + DVerbType dverb(std::cout); + + /* stream for informative output: summary infos on what a module + does, runtimes, etc. */ + DInfoType dinfo(std::cout); + + /* stream for warnings: messages which may indicate problems */ + DWarnType dwarn(std::cerr); + + /* stream for strong warnings: when a failure */ + DGraveType dgrave(std::cerr); + + /* stream for error messages: only packages integrating Dune + completely will redirect it. The output of derr is independent of + the debug-level, only the activation-flag is checked */ + DErrType derr(std::cerr); + +} diff --git a/dune/common/stdstreams.hh b/dune/common/stdstreams.hh new file mode 100644 index 0000000..c23cea8 --- /dev/null +++ b/dune/common/stdstreams.hh @@ -0,0 +1,198 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +/** + \file + \brief Standard Dune debug streams + + The standard debug streams are compiled into libdune to exist + globally. This file declares the stream types and the global debug + level. + */ + +#ifndef DUNE_COMMON_STDSTREAMS_HH +#define DUNE_COMMON_STDSTREAMS_HH + +#include "debugstream.hh" + +namespace Dune { + + /** + \addtogroup DebugOut + @{ + + standard debug streams with level below MINIMAL_DEBUG_LEVEL will + collapse to doing nothing if output is requested. + + MINIMAL_DEBUG_LEVEL is set to DUNE_MINIMAL_DEBUG_LEVEL, which is + defined in config.h and can be changed by the configure option + @code --with-minimal-debug-level=[grave|warn|info|verb|vverb] @endcode + + For a Dune-Release this should be set to at least 4 so that only + important messages are active. Dune-developers may adapt this + setting to their debugging needs locally + + Keep in mind that libdune has to be recompiled if this value is changed! + + + + The singleton instances of the available debug streams can be found in + the \ref DebugOut "Standard Debug Streams" module + + @} + */ + + /** + \defgroup StdStreams Standard Debug Streams + \ingroup DebugOut + @{ + + Dune defines several standard output streams for the library + routines. + + Applications may control the standard streams via the attach/detach, + push/pop interface but should define an independent set of streams (see \ref DebugAppl ) + + */ + + /** + @brief The default minimum debug level. + + If the level of a stream is bigger than this value + it will be activated. + */ +#ifndef DUNE_MINIMAL_DEBUG_LEVEL +#define DUNE_MINIMAL_DEBUG_LEVEL 4 +#endif + static const DebugLevel MINIMAL_DEBUG_LEVEL = DUNE_MINIMAL_DEBUG_LEVEL; + + /** + @brief The level of the very verbose debug stream. + @see dvverb + */ + static const DebugLevel VERY_VERBOSE_DEBUG_LEVEL = 1; + + /** + @brief Type of very verbose debug stream. + @see dvverb + */ + typedef DebugStream DVVerbType; + + /** + \brief stream for very verbose output. + + \code + #include + \endcode + + Information on the lowest + level. This is expected to report insane amounts of + information. Use of the activation-flag to only generate output + near the problem is recommended. + */ + extern DVVerbType dvverb; + + /** + @brief The level of the verbose debug stream. + @see dvverb + */ + static const DebugLevel VERBOSE_DEBUG_LEVEL = 2; + + /** + @brief Type of more verbose debug stream. + @see dverb + */ + typedef DebugStream DVerbType; + + /** + @brief Singleton of verbose debug stream. + + \code + #include + \endcode + */ + extern DVerbType dverb; + + /** + @brief The level of the informative debug stream. + @see dinfo + */ + static const DebugLevel INFO_DEBUG_LEVEL = 3; + + /** + @brief Type of debug stream with info level. + @see dinfo + */ + typedef DebugStream DInfoType; + + /** + @brief Stream for informative output. + + \code + #include + \endcode + + Summary infos on what a module + does, runtimes, etc. + */ + extern DInfoType dinfo; + + /** + @brief The level of the debug stream for warnings. + @see dwarn + */ + static const DebugLevel WARN_DEBUG_LEVEL = 4; + + /** + @brief Type of debug stream with warn level. + @see dwarn + */ + typedef DebugStream DWarnType; + + /** + @brief Stream for warnings indicating problems. + + \code + #include + \endcode + */ + extern DWarnType dwarn; + + /** + @brief The level of the debug stream for fatal errors. + @see dgrave + */ + static const DebugLevel GRAVE_DEBUG_LEVEL = 5; + + /** @brief Type of debug stream for fatal errors.*/ + typedef DebugStream DGraveType; + + /** + @brief Stream for warnings indicating fatal errors. + + \code + #include + \endcode + */ + extern DGraveType dgrave; + + /** @brief The type of the stream used for error messages. */ + typedef DebugStream<1> DErrType; + + /** + @brief Stream for error messages. + + \code + #include + \endcode + + Only packages integrating Dune + completely will redirect it. The output of derr is independent of + the debug-level, only the activation-flag is checked. + */ + extern DErrType derr; + + /** }@ */ +} + +#endif diff --git a/dune/common/stdthread.cc b/dune/common/stdthread.cc new file mode 100644 index 0000000..aae4a37 --- /dev/null +++ b/dune/common/stdthread.cc @@ -0,0 +1,80 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#if HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include + +namespace Dune +{ + + namespace { + + void printCallOnceError(const char *file, int line, const char *function, + const char *msg) + { + if(file) + std::cerr << file << ":" << line << ": "; + std::cerr << "error: "; + if(function) + std::cerr << "(in " << function << "()) "; + std::cerr << "std::call_once() is broken.\n" + << "\n" + << msg << std::endl; + } + + void setBool(bool *v) + { + *v = true; + } + + } // anonymous namespace + + void doAssertCallOnce(const char *file, int line, const char *function) + { + std::once_flag once; + bool works = false; + try { + // pass address to works since call_once passes by value + std::call_once(once, setBool, &works); + } + catch(...) { + printCallOnceError(file, line, function, +"std::call_once() throws an exception. This suggests that the program was\n" +"linked without a threading library. Common ways to link to a threading\n" +"library is to specify one of the following during linking: -pthread, \n" +"-lpthread, or -pthreads. The build system should have tried various of\n" +"these options, but unfortunately that is only a guess and we cannot verify\n" +"that we found a working configuration until runtime.\n" +"\n" +"Going to rethrow the exception now to give the system library a chance to\n" +"print more information about it, just in case that helps with debugging.\n" + ); + throw; + } + if(!works) + { + printCallOnceError(file, line, function, +"std::call_once() never calls the function. This suggests that your\n" +"libctdc++ or your gcc built without threading support (--disable-threads,\n" +"see https://gcc.gnu.org/install/configure.html). This is probably a bug in\n" +"__gthread_once() in /usr/include/c++/4.7/x86_64-linux-gnu/bits/gthr-single.h\n" +"(which should not silently return success without doing anything, but\n" +"apparently does so in some versions).\n" +"\n" +"To fix the issue, either recompile gcc with a working threading\n" +"implementation, or file a bug for gthr-single.h, or file a bug at\n" +"https://dune-project.org/flyspray/ and request a workaround at the dune-side." + ); + std::abort(); + } + } + +} // namespace Dune diff --git a/dune/common/stdthread.hh b/dune/common/stdthread.hh new file mode 100644 index 0000000..da22897 --- /dev/null +++ b/dune/common/stdthread.hh @@ -0,0 +1,52 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_COMMON_STDTHREAD_HH +#define DUNE_COMMON_STDTHREAD_HH + +namespace Dune +{ + + // used internally by assertCallOnce for the actual check + void doAssertCallOnce(const char *file, int line, const char *function); + + //! \brief Make sure call_once() works and provide a helpful error message + //! otherwise. + /** + * For call_once() to work, certain versions of libstdc++ need to be + * _linked_ with -pthread or similar flags. If that is not the case, + * call_once() will throw an exception. This function checks that + * call_once() can indeed be used, i.e. that it does not throw an exception + * when it should not, and that the code does indeed get executed. If + * call_once() cannot be used, assertCallOnce() aborts the program with a + * helpful error message. + * + * The check is only actually executed the first time assertCallOnce() is + * called. + * + * The arguments \c file and \c line specify the filename and line number + * that should appear in the error message. They are ignored if \c file is + * 0. The argument \c function specifies the name of the function to appear + * in the error message. It is ignored if \c function is 0. + */ + + inline void assertCallOnce(const char *file = nullptr, int line = -1, + const char *function = nullptr) + { + // make sure to call this only the first time this function is invoked + [[maybe_unused]] static const bool works + = (doAssertCallOnce(file, line, function), true); + } + + //! \brief Make sure call_once() works and provide a helpful error message + //! otherwise. + /** + * This calls assertCallOnce() and automatically provides information about + * the caller in the error message. + */ +#define DUNE_ASSERT_CALL_ONCE() \ + ::Dune::assertCallOnce(__FILE__, __LINE__, __func__) + +} // namespace Dune + +#endif // DUNE_COMMON_STDTHREAD_HH diff --git a/dune/common/streamoperators.hh b/dune/common/streamoperators.hh new file mode 100644 index 0000000..6465584 --- /dev/null +++ b/dune/common/streamoperators.hh @@ -0,0 +1,67 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_STREAMOPERATORS_HH +#define DUNE_STREAMOPERATORS_HH + +/** \file + \brief Implementation of stream operators for std::array and std::tuple + */ + +#include +#include +#include + +#include + +namespace Dune +{ + /** @addtogroup Common + + @{ + */ + + //! Print a std::tuple + template + inline Stream& operator<<(Stream& stream, const std::tuple& t) + { + stream<<"["; + if(sizeof...(Ts)>0) + { + Hybrid::forEach(std::make_index_sequence{}, + [&](auto i){stream<(t)<<",";}); + stream<(t); + } + stream<<"]"; + return stream; + } + + //! Read a std::tuple + template + inline Stream& operator>>(Stream& stream, std::tuple& t) + { + Hybrid::forEach(std::make_index_sequence{}, + [&](auto i){stream>>std::get(t);}); + return stream; + } + + //! Print a std::array + template + inline Stream& operator<<(Stream& stream, const std::array& a) + { + stream<<"["; + if(N>0) + { + for(std::size_t i=0; i +#include +#include +#include +#include +#include +#include +#include + +#include + + +namespace Dune { + + /** + * @addtogroup StringUtilities + * + * @{ + */ + + /** \brief Check whether a character container has a given prefix + * + * The container must support the begin() and size() methods. + */ + template + bool hasPrefix(const C& c, const char* prefix) { + std::size_t len = std::strlen(prefix); + return c.size() >= len && + std::equal(prefix, prefix+len, c.begin()); + } + + /** \brief Check whether a character container has a given suffix + * + * The container must support the begin() and size() methods and the + * const_iterator member type. + * + * \note This is slow for containers which don't have random access iterators. + * In the case of containers with bidirectional iterators, this + * slowness is unnecessary. + */ + template + bool hasSuffix(const C& c, const char* suffix) { + std::size_t len = std::strlen(suffix); + if(c.size() < len) return false; + typename C::const_iterator it = c.begin(); + std::advance(it, c.size() - len); + return std::equal(suffix, suffix+len, it); + } + + /** + * \brief Format values according to printf format string + * + * \param s The format string to be used + * \param args The valued to be formatted + * + * This is a wrapper to std::snprintf that provides + * overflow save printf functionality. For up to 1000 + * characters a static buffer is used. If this is not sufficient + * a dynamic buffer of appropriate size is allocated. + */ + template + static std::string formatString(const std::string& s, const T&... args) + { + static const int bufferSize=1000; + char buffer[bufferSize]; + + // try to format with static buffer + int r = std::snprintf(buffer, bufferSize, s.c_str(), args...); + + // negative return values correspond to errors + if (r<0) + DUNE_THROW(Dune::Exception,"Could not convert format string using given arguments."); + + // if buffer was large enough return result as string + if (r dynamicBuffer; + try { + dynamicBuffer = std::make_unique(dynamicBufferSize); + } + catch (const std::bad_alloc&) { + DUNE_THROW(Dune::Exception,"Could allocate large enough dynamic buffer in formatString."); + } + + // convert and check for errors again + r = std::snprintf(dynamicBuffer.get(), dynamicBufferSize, s.c_str(), args...); + if (r<0) + DUNE_THROW(Dune::Exception,"Could not convert format string using given arguments."); + + // the new buffer should always be large enough + assert(r + +#include +#include +#include + +#include +#include +#include +#include + + +template +void checkAlignment(Dune::TestSuite &test) +{ + std::vector> defaultalignment(4); + + test.check(Dune::isAligned(defaultalignment.data(), std::alignment_of::value), "defaultalignment isAligned") + << "alignment(" << std::alignment_of::value << ") not detected for " << Dune::className(); + + std::vector> alignment16(4); + + test.check(Dune::isAligned(alignment16.data(), 16), "alignment16 isAligned") + << "alignment(16) not detected for " << Dune::className(); +} + +int main(int argc, char **argv) +{ + Dune::TestSuite test; + + using ArithmeticTypes = std::tuple< + char, signed char, unsigned char, + short, unsigned short, + int, unsigned, + long, long unsigned, + long long, long long unsigned, + wchar_t, char16_t, char32_t, + float, double, long double>; + + Dune::Hybrid::forEach(ArithmeticTypes(), [&](auto val) { + using T = decltype(val); + checkAlignment(test); + }); + + return test.exit(); +} diff --git a/dune/common/test/arithmetictestsuite.hh b/dune/common/test/arithmetictestsuite.hh new file mode 100644 index 0000000..9769003 --- /dev/null +++ b/dune/common/test/arithmetictestsuite.hh @@ -0,0 +1,800 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_TEST_ARITHMETICTESTSUITE_HH +#define DUNE_COMMON_TEST_ARITHMETICTESTSUITE_HH + +#include +#include +#include + +#include +#include + +namespace Dune { + +/* + * silence warnings from GCC about using integer operands on a bool + * (when instantiated for T=bool) + */ +#if __GNUC__ >= 7 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wbool-operation" +# pragma GCC diagnostic ignored "-Wint-in-bool-context" +#endif + + //! Test suite for arithmetic types + /** + * You usually want to call the member function `checkArithmetic()`. The + * individual component tests are however available for special needs. + */ + class ArithmeticTestSuite : + public TestSuite + { + public: + // inherit all constructors from TestSuite + using TestSuite::TestSuite; + + //! tag denoting any arithmetic type + struct Arithmetic {}; + //! tag denoting integral types + struct Integral : Arithmetic {}; + //! tag denoting boolean types + struct Boolean : Integral {}; + //! tag denoting signed integral types + struct Signed : Integral {}; + //! tag denoting unsigned integral types + struct Unsigned : Integral {}; + //! tag denoting floating point types + struct Floating : Arithmetic {}; + + private: + static const char *name(Arithmetic) { return "Arithmetic"; } + static const char *name(Integral ) { return "Integral"; } + static const char *name(Boolean ) { return "Boolean"; } + static const char *name(Signed ) { return "Signed"; } + static const char *name(Unsigned ) { return "Unsigned"; } + static const char *name(Floating ) { return "Floating"; } + + template + using Cond = typename std::conditional::type; + + public: + //! determine arithmetic tag for the given type + /** + * `T` can be either one of the fundamental arithmetic types, in which + * case a default-constructed tag object for that type is returned. Or it + * can be a class derived from `Arithmetic`, in which case a + * default-constructed object of that class is is returned. + */ + template + constexpr static auto tag(T = T{}) + { + return + Cond, T, + Cond, Floating, + Cond, + Cond, Boolean, + Cond, Signed, + Cond, Unsigned + > > > + > > >{}; + } + +#define DUNE_TEST_FUNCTION(T, tag) \ + static const auto function = \ + std::string(__func__) + "<" + className() + ">(" + name(tag) + ")" + +#define DUNE_TEST_CHECK(expr) \ + (check((expr), function) \ + << __FILE__ << ":" << __LINE__ << ": Check \"" << #expr << "\"") + + // + // check basic operations: construct, copy, compare + // + + //! check the default constructors + template + void checkDefaultConstruct([[maybe_unused]] Arithmetic arithmetic_tag) + { + [[maybe_unused]] T t0; + (void)T(); + [[maybe_unused]] T t1{}; + [[maybe_unused]] T t2 = {}; + } + + //! check explicit conversion from and to int + template + void checkExplicitIntConvert(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + // this test may be applied to boolean-type types. 0 and 1 are the only + // values that survive that. + T t0(0); DUNE_TEST_CHECK(int(t0) == 0); + T t1(1); DUNE_TEST_CHECK(int(t1) == 1); + } + + //! check the move constructor + template + void checkMoveConstruct(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + for(int i : { 0, 1 }) + { + T t0(i); + + T t1(std::move(t0)); + T t2 = std::move(t1); + T t3{ std::move(t2) }; + T t4 = { std::move(t3) }; + + DUNE_TEST_CHECK(bool(t4 == T(i))); + } + } + + //! check the copy constructor + template + void checkCopyConstruct(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + for(int i : { 0, 1 }) + { + T t0(i); + + T t1(t0); + T t2 = t1; + T t3{ t2 }; + T t4 = { t3 }; + + DUNE_TEST_CHECK(bool(t0 == T(i))); + DUNE_TEST_CHECK(bool(t1 == T(i))); + DUNE_TEST_CHECK(bool(t2 == T(i))); + DUNE_TEST_CHECK(bool(t3 == T(i))); + DUNE_TEST_CHECK(bool(t4 == T(i))); + } + } + + //! check the move assignment operator + template + void checkMoveAssign(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + for(int i : { 0, 1 }) + { + T t0(i); + T t2, t4; + + t2 = std::move(t0); + t4 = { std::move(t2) }; + + DUNE_TEST_CHECK(bool(t4 == T(i))); + } + } + + //! check the copy assignment operator + template + void checkCopyAssign(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + for(int i : { 0, 1 }) + { + T t0(i); + T t2, t4; + + t2 = t0; + t4 = { t2 }; + + DUNE_TEST_CHECK(bool(t0 == T(i))); + DUNE_TEST_CHECK(bool(t2 == T(i))); + DUNE_TEST_CHECK(bool(t4 == T(i))); + } + } + + //! check `==` and `!=` + /** + * \note We do not require the result to be _implicitly_ convertible to + * bool, but it must be contextually convertible to bool. + */ + template + void checkEqual(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + T t0(0); + T t1(1); + + DUNE_TEST_CHECK(bool(t0 == T(0))); + DUNE_TEST_CHECK(bool(t1 == T(1))); + + DUNE_TEST_CHECK(!bool(t0 == T(1))); + DUNE_TEST_CHECK(!bool(t1 == T(0))); + DUNE_TEST_CHECK(!bool(t0 == t1)); + + DUNE_TEST_CHECK(!bool(t0 != T(0))); + DUNE_TEST_CHECK(!bool(t1 != T(1))); + + DUNE_TEST_CHECK(bool(t0 != T(1))); + DUNE_TEST_CHECK(bool(t1 != T(0))); + DUNE_TEST_CHECK(bool(t0 != t1)); + } + + // + // checks for unary operators + // + + //! check postfix `++` + /** + * Applies to boolean (deprecated), integral, and floating point. + */ + template + void checkPostfixInc(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + T t0(0); + DUNE_TEST_CHECK(bool(T(t0++) == T(0))); + DUNE_TEST_CHECK(bool(t0 == T(1))); + } + template + void checkPostfixInc(Boolean) {} + + //! check postfix `--` + /** + * Applies to integral (no boolean), and floating point. + */ + template + void checkPostfixDec(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + T t1(1); + DUNE_TEST_CHECK(bool(T(t1--) == T(1))); + DUNE_TEST_CHECK(bool(t1 == T(0))); + } + template + void checkPostfixDec(Boolean) {} + + //! check prefix `+` + /** + * Applies to boolean, integral, and floating point. + */ + template + void checkPrefixPlus(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(+T(0)) == T(0))); + DUNE_TEST_CHECK(bool(T(+T(1)) == T(1))); + } + + //! check prefix `-` + /** + * Applies to boolean, integral, and floating point. + */ + template + void checkPrefixMinus(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(-T(0)) == T( 0))); + DUNE_TEST_CHECK(bool(T(-T(1)) == T(-1))); + } + + //! check prefix `!` + /** + * Applies to boolean, integral, and floating point. + */ + template + void checkPrefixNot(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(!T(0))); + DUNE_TEST_CHECK(!bool(!T(1))); + } + + //! check prefix `~` + /** + * Applies to boolean and integral. + */ + template + void checkPrefixBitNot(Boolean arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(~T(0)))); + } + template + void checkPrefixBitNot(Integral arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(~T(0)))); + DUNE_TEST_CHECK(bool(T(~T(1)))); + + DUNE_TEST_CHECK(bool(T(~T(~T(0))) == T(0))); + DUNE_TEST_CHECK(bool(T(~T(~T(1))) == T(1))); + } + template + void checkPrefixBitNot(Unsigned arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + checkPrefixBitNot(Integral{}); + + DUNE_TEST_CHECK(bool(T(~T(0)) == T(-1))); + DUNE_TEST_CHECK(bool(T(~T(1)) == T(-2))); + } + template + void checkPrefixBitNot(Floating) {} + + //! check postfix `++` + /** + * Applies to boolean (deprecated), integral, and floating point. + */ + template + void checkPrefixInc(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + T t0(0); + DUNE_TEST_CHECK(bool(T(++t0) == T(1))); + DUNE_TEST_CHECK(bool(t0 == T(1))); + ++t0 = T(0); + DUNE_TEST_CHECK(bool(t0 == T(0))); + } + template + void checkPrefixInc(Boolean) {} + + //! check postfix `--` + /** + * Applies to integral (no boolean), and floating point. + */ + template + void checkPrefixDec(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + T t1(1); + DUNE_TEST_CHECK(bool(T(--t1) == T(0))); + DUNE_TEST_CHECK(bool(t1 == T(0))); + t1 = T(1); + --t1 = T(1); + DUNE_TEST_CHECK(bool(t1 == T(1))); + } + template + void checkPrefixDec(Boolean) {} + + // + // checks for infox operators + // + + //! check infix `*` + /** + * Applies to boolean, integral, and floating point. + */ + template + void checkInfixMul(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)*T(0)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)*T(0)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(0)*T(1)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)*T(1)) == T(1))); + } + + //! check infix `/` + /** + * Applies to boolean, integral, and floating point. + */ + template + void checkInfixDiv(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)/T(1)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)/T(1)) == T(1))); + } + + //! check infix `%` + /** + * Applies to boolean and integral. + */ + template + void checkInfixRem(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)%T(1)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)%T(1)) == T(0))); + } + template + void checkInfixRem(Floating) {} + + //! check infix `+` + /** + * Applies to boolean, integral, and floating point. + */ + template + void checkInfixPlus(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)+T(0)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)+T(0)) == T(1))); + DUNE_TEST_CHECK(bool(T(T(0)+T(1)) == T(1))); + DUNE_TEST_CHECK(bool(T(T(1)+T(1)) == T(2))); + } + + //! check infix `-` + /** + * Applies to boolean, integral, and floating point. + */ + template + void checkInfixMinus(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)-T(0)) == T( 0))); + DUNE_TEST_CHECK(bool(T(T(1)-T(0)) == T( 1))); + DUNE_TEST_CHECK(bool(T(T(0)-T(1)) == T(-1))); + DUNE_TEST_CHECK(bool(T(T(1)-T(1)) == T( 0))); + } + + //! check infix `<<` + /** + * Applies to boolean and integral. + */ + template + void checkInfixLShift(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)< + void checkInfixLShift(Floating) {} + + //! check infix `>>` + /** + * Applies to boolean and integral. + */ + template + void checkInfixRShift(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)>>T(0)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)>>T(0)) == T(1))); + DUNE_TEST_CHECK(bool(T(T(0)>>T(1)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)>>T(1)) == T(0))); + } + template + void checkInfixRShift(Floating) {} + + //! check infix `<` + /** + * Applies to boolean, integral, and floating point. + */ + template + void checkInfixLess(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(0) + void checkInfixLess(Signed arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + checkInfixLess(Integral{}); + + DUNE_TEST_CHECK(bool(T(-1) + void checkInfixLess(Unsigned arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + checkInfixLess(Integral{}); + + DUNE_TEST_CHECK(bool(T(-1)` + /** + * Applies to boolean, integral, and floating point. + */ + template + void checkInfixGreater(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + int values[] = { -1, 0, 1 }; + for(int i : values) + for(int j : values) + DUNE_TEST_CHECK(bool(T(i) > T(j)) == bool(T(j) < T(i))); + } + + //! check infix `<=` + /** + * Applies to boolean, integral, and floating point. + */ + template + void checkInfixLessEqual(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + int values[] = { -1, 0, 1 }; + for(int i : values) + for(int j : values) + DUNE_TEST_CHECK(bool(T(i) <= T(j)) != bool(T(j) < T(i))); + } + + //! check infix `>=` + /** + * Applies to boolean, integral, and floating point. + */ + template + void checkInfixGreaterEqual(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + int values[] = { -1, 0, 1 }; + for(int i : values) + for(int j : values) + DUNE_TEST_CHECK(bool(T(i) >= T(j)) != bool(T(i) < T(j))); + } + + //! check infix `&` + /** + * Applies to boolean and integral. + */ + template + void checkInfixBitAnd(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 4; ++i) + for(int j = 0; j < 4; ++j) + DUNE_TEST_CHECK(bool(T(T(i) & T(j)) == T(i&j))); + } + template + void checkInfixBitAnd(Boolean arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 2; ++i) + for(int j = 0; j < 2; ++j) + DUNE_TEST_CHECK(bool(T(T(i) & T(j)) == T(i&j))); + } + template + void checkInfixBitAnd(Floating) {} + + //! check infix `^` + /** + * Applies to boolean and integral. + */ + template + void checkInfixBitXor(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 4; ++i) + for(int j = 0; j < 4; ++j) + DUNE_TEST_CHECK(bool(T(T(i) ^ T(j)) == T(i^j))); + } + template + void checkInfixBitXor(Boolean arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 2; ++i) + for(int j = 0; j < 2; ++j) + // compare the bit-flipped versions so we don't depend on the number + // of bits in T. + DUNE_TEST_CHECK(bool(T(~T(T(i) ^ T(j))) == T(~(i^j)))); + } + template + void checkInfixBitXor(Floating) {} + + //! check infix `|` + /** + * Applies to boolean and integral. + */ + template + void checkInfixBitOr(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 4; ++i) + for(int j = 0; j < 4; ++j) + DUNE_TEST_CHECK(bool(T(T(i) | T(j)) == T(i|j))); + } + template + void checkInfixBitOr(Boolean arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 2; ++i) + for(int j = 0; j < 2; ++j) + DUNE_TEST_CHECK(bool(T(T(i) | T(j)) == T(i|j))); + } + template + void checkInfixBitOr(Floating) {} + + //! check infix `&&` + /** + * Applies to boolean, integral and floating point. + */ + template + void checkInfixAnd(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 4; ++i) + for(int j = 0; j < 4; ++j) + DUNE_TEST_CHECK(bool(T(i) && T(j)) == (i && j)); + } + + //! check infix `||` + /** + * Applies to boolean, integral and floating point. + */ + template + void checkInfixOr(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 4; ++i) + for(int j = 0; j < 4; ++j) + DUNE_TEST_CHECK(bool(T(i) || T(j)) == (i || j)); + } + + // + // checks for compound assignment operators + // + +#define DUNE_TEST_PEEL(...) __VA_ARGS__ +#define DUNE_TEST_ASSIGN(OP, name, Tag, lrange, rrange) \ + template \ + void checkAssign##name(Tag arithmetic_tag) \ + { \ + DUNE_TEST_FUNCTION(T, arithmetic_tag); \ + \ + for(int i : { DUNE_TEST_PEEL lrange }) \ + for(int j : { DUNE_TEST_PEEL rrange }) \ + { \ + T t(i); \ + DUNE_TEST_CHECK(bool((t OP##= T(j)) == T(T(i) OP T(j)))); \ + DUNE_TEST_CHECK(bool(t == T(T(i) OP T(j)))); \ + } \ + } + +#define DUNE_TEST_ASSIGN_DISABLE(name, Tag) \ + template \ + void checkAssign##name(Tag) {} + + DUNE_TEST_ASSIGN(*, Mul, Arithmetic, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN(/, Div, Arithmetic, (0, 1, 2, 3), ( 1, 2, 4)) + DUNE_TEST_ASSIGN(%, Rem, Arithmetic, (0, 1, 2, 3), ( 1, 2, 3)) + DUNE_TEST_ASSIGN_DISABLE(Rem, Floating) + + DUNE_TEST_ASSIGN(+, Plus, Arithmetic, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN(-, Minus, Arithmetic, (0, 1, 2, 3), (0, 1, 2, 3)) + + DUNE_TEST_ASSIGN(<<, LShift, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN(>>, RShift, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN(<<, LShift, Boolean, (0, 1 ), (0, 1 )) + DUNE_TEST_ASSIGN(>>, RShift, Boolean, (0, 1 ), (0, 1 )) + DUNE_TEST_ASSIGN_DISABLE(LShift, Floating) + DUNE_TEST_ASSIGN_DISABLE(RShift, Floating) + + DUNE_TEST_ASSIGN(&, BitAnd, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN(^, BitXor, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN(|, BitOr, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN_DISABLE(BitAnd, Floating) + DUNE_TEST_ASSIGN_DISABLE(BitXor, Floating) + DUNE_TEST_ASSIGN_DISABLE(BitOr, Floating) + +#undef DUNE_TEST_ASSIGN_DISABLE +#undef DUNE_TEST_ASSIGN +#undef DUNE_TEST_PEEL +#undef DUNE_TEST_FUNCTION +#undef DUNE_TEST_CHECK + + // + // checks collections + // + + //! run the full arithmetic type testsuite + /** + * `T` is the type to check. `Tag` determines the arithmetic category; it + * is one of the classes derived from `Arithmetic`. Alternatively, `Tag` + * may be one of the fundamental arithmetic types, in which case it will + * be converted to the appropriate category tag automatically. + * + * To check an extended unsigned integer type, you might use one of the + * following calls: + * \code + * test.checkArithmetic(); + * test.checkArithmetic(0u); + * test.checkArithmetic(); + * test.checkArithmetic(ArithemticTestSuite::Unsigned{}); + * \endcode + */ + template + void checkArithmetic(Tag = Tag{}) + { + auto arithmetic_tag = this->tag(); + + checkDefaultConstruct(arithmetic_tag); + checkExplicitIntConvert(arithmetic_tag); + checkMoveConstruct(arithmetic_tag); + checkCopyConstruct(arithmetic_tag); + checkMoveAssign(arithmetic_tag); + checkCopyAssign(arithmetic_tag); + checkEqual(arithmetic_tag); + + checkPostfixInc(arithmetic_tag); + checkPostfixDec(arithmetic_tag); + + checkPrefixPlus(arithmetic_tag); + checkPrefixMinus(arithmetic_tag); + checkPrefixNot(arithmetic_tag); + checkPrefixBitNot(arithmetic_tag); + + checkPrefixInc(arithmetic_tag); + checkPrefixDec(arithmetic_tag); + + checkInfixMul(arithmetic_tag); + checkInfixDiv(arithmetic_tag); + checkInfixRem(arithmetic_tag); + + checkInfixPlus(arithmetic_tag); + checkInfixMinus(arithmetic_tag); + + checkInfixLShift(arithmetic_tag); + checkInfixRShift(arithmetic_tag); + + checkInfixLess(arithmetic_tag); + checkInfixGreater(arithmetic_tag); + checkInfixLessEqual(arithmetic_tag); + checkInfixGreaterEqual(arithmetic_tag); + + checkInfixBitAnd(arithmetic_tag); + checkInfixBitXor(arithmetic_tag); + checkInfixBitOr(arithmetic_tag); + + checkInfixAnd(arithmetic_tag); + checkInfixOr(arithmetic_tag); + + checkAssignMul(arithmetic_tag); + checkAssignDiv(arithmetic_tag); + checkAssignRem(arithmetic_tag); + + checkAssignPlus(arithmetic_tag); + checkAssignMinus(arithmetic_tag); + + checkAssignLShift(arithmetic_tag); + checkAssignRShift(arithmetic_tag); + + checkAssignBitAnd(arithmetic_tag); + checkAssignBitXor(arithmetic_tag); + checkAssignBitOr(arithmetic_tag); + } + }; + +#if __GNUC__ >= 7 +# pragma GCC diagnostic pop +#endif + +} // namespace Dune + +#endif // DUNE_COMMON_TEST_ARITHMETICTESTSUITE_HH diff --git a/dune/common/test/arithmetictestsuitetest.cc b/dune/common/test/arithmetictestsuitetest.cc new file mode 100644 index 0000000..48af531 --- /dev/null +++ b/dune/common/test/arithmetictestsuitetest.cc @@ -0,0 +1,31 @@ +#include + +#include + +#include +#include +#include + +int main(int argc, char **argv) +{ + Dune::MPIHelper::instance(argc, argv); + + Dune::ArithmeticTestSuite test; + + using ArithmeticTypes = std::tuple< + bool, + char, signed char, unsigned char, + short, unsigned short, + int, unsigned, + long, long unsigned, + long long, long long unsigned, + wchar_t, char16_t, char32_t, + float, double, long double>; + + Dune::Hybrid::forEach(ArithmeticTypes(), [&](auto val) { + using T = decltype(val); + test.checkArithmetic(); + }); + + return test.exit(); +} diff --git a/dune/common/test/arraylisttest.cc b/dune/common/test/arraylisttest.cc new file mode 100644 index 0000000..0b0e0eb --- /dev/null +++ b/dune/common/test/arraylisttest.cc @@ -0,0 +1,204 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +class Double { +public: + double val; + Double() : val(0){} + Double(double d) : val(d){} + Double& operator=(double d){ + val=d; + return *this; + } +}; + +bool operator<(Double a, Double b){ + return a.val +void randomizeList(Dune::ArrayList& alist){ + using namespace Dune; + + srand(300); + + int lowest=0, highest=1000, range=(highest-lowest)+1; + + for(int i=0; i < 250; i++) + alist.push_back(T(static_cast(range*(rand()/(RAND_MAX+1.0))))); +} + +int testSorting(){ + using namespace Dune; + ArrayList alist; + + randomizeList(alist); + std::sort(alist.begin(), alist.end()); + double last=-1; + + for(ArrayList::iterator iter=alist.begin(), end=alist.end(); + iter != end; ++iter) { + if((*iter)>=last) { + last=*iter; + }else{ + std::cerr << last<<">"<<(*iter)<<" List is not sorted! "<<__FILE__ <<":"<<__LINE__< +void initConsecutive(Dune::ArrayList& alist){ + using namespace Dune; + + for(int i=0; i < 100; i++) + alist.push_back(i); +} + +int testIteratorRemove(){ + using namespace Dune; + ArrayList alist; + initConsecutive(alist); + ArrayList::iterator iter=alist.begin(); + + iter+=8; + + iter.eraseToHere(); + ++iter; + + if((*iter)!=10) { + std::cerr<<"Removing from iterator failed! "<<__FILE__<<":"<<__LINE__< alist; + initConsecutive(alist); + + ArrayList::iterator iter=alist.begin(); + + + for(int i=0; i < 100; i++) { + if(iter[i]!=i) { + std::cerr << "Random Access failed: "< alist; + initConsecutive(alist); + + ArrayList::iterator iter=alist.begin(), iter1=alist.begin(); + iter1=iter+5; + iter1=iter-5; + iter1=iter+5; + + + if(!(iter="<<*iter1<<" Operator< seems to be wrong! "<< __FILE__ <<__LINE__<iter)) { + std::cerr<<"operator> seems to be wrong! "<< __FILE__ <<__LINE__<=iter)) { + std::cerr<<"operator>= seems to be wrong! "<< __FILE__ <<__LINE__< alist; + + randomizeList(alist); + + int ret=testIterator(alist); + + if(0!=testComparison()) { + ret++; + cerr<< "Comparison failed!"< +#include + +#include +#include +#include +#include + +template +void f(const std::array &a) +{ + using Dune::operator<<; + std::cout << "Got a " << Dune::className(a) << " with elements " << a << std::endl; +} + +int main() { + // check that make_array works + f(Dune::Std::make_array(1, 2)); + f(Dune::Std::make_array(1, 2, 3)); + f(Dune::Std::make_array(1, 2, 3, 4)); + f(Dune::Std::make_array(1, 2, 3, 4, 5)); + f(Dune::Std::make_array(1, 2, 3, 4, 5, 6)); + f(Dune::Std::make_array(1, 2, 3, 4, 5, 6, 7)); + f(Dune::Std::make_array(1, 2, 3, 4, 5, 6, 7, 8)); + f(Dune::Std::make_array(1, 2, 3, 4, 5, 6, 7, 8, 9)); + f(Dune::Std::make_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); + + Dune::FieldVector x(0); + f(Dune::Std::make_array(x, x)); +} diff --git a/dune/common/test/assertandreturntest.cc b/dune/common/test/assertandreturntest.cc new file mode 100644 index 0000000..b9e6b27 --- /dev/null +++ b/dune/common/test/assertandreturntest.cc @@ -0,0 +1,86 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#include + +#ifdef NDEBUG + #undef NDEBUG +#endif +#ifdef TEST_NDEBUG + #define NDEBUG TEST_NDEBUG +#endif + +#include +#include +#include +#include + +struct Foo +{ + static constexpr auto lessAndReturn([[maybe_unused]] int a, [[maybe_unused]] int b, int x) + { + return DUNE_ASSERT_AND_RETURN(a::value != 3) + DUNE_THROW(Dune::Exception, "DUNE_ASSERT_AND_RETURN returned incorrect value in constexpr context"); + +// If EXPECT_FAIL would work with failing assertions, +// we could test if the assertion is triggered with +// a target +// +// dune_add_test(NAME assertandreturntest_runtime_fail +// SOURCES assertandreturntest.cc +// LINK_LIBRARIES dunecommon +// COMPILE_DEFINITIONS "TEST_RUNTIME_FAIL" +// EXPECT_FAIL +// LABELS quick) +// +// and the following code: +#ifdef TEST_RUNTIME_FAIL + // This should fail at runtime because 0>-3 + if (Foo::lessAndReturn(0,-1,3) != 3) + DUNE_THROW(Dune::Exception, "DUNE_ASSERT_AND_RETURN returned incorrect value in dynamic context"); +#endif + +#ifdef TEST_COMPILETIME_FAIL + // This should fail at compile time because 0>-3 + if (std::integral_constant::value != 3) + DUNE_THROW(Dune::Exception, "DUNE_ASSERT_AND_RETURN returned incorrect value in constexpr context"); +#endif + +#ifdef TEST_NDEBUG + // This should not fail because NDEBUG is set + if (Foo::lessAndReturn(0,-1,3) != 3) + DUNE_THROW(Dune::Exception, "DUNE_ASSERT_AND_RETURN returned incorrect value in dynamic context"); + + // This should not fail because NDEBUG is set + if (std::integral_constant::value != 3) + DUNE_THROW(Dune::Exception, "DUNE_ASSERT_AND_RETURN returned incorrect value in constexpr context"); +#endif + + return 0; +} +catch( Dune::Exception &e ) +{ + std::cerr << "Dune reported error: " << e << std::endl; + return 1; +} +catch(...) +{ + std::cerr << "Unknown exception thrown!" << std::endl; + return 1; +} diff --git a/dune/common/test/autocopytest.cc b/dune/common/test/autocopytest.cc new file mode 100644 index 0000000..ed1a66a --- /dev/null +++ b/dune/common/test/autocopytest.cc @@ -0,0 +1,52 @@ +#include "config.h" + +#include +#include +#include + +#include +#include + +template +constexpr auto doAutoCopy(T &&v) +{ + return Dune::autoCopy(std::forward(v)); +} + +// an example expression object that evaluates to int(0) +struct ZeroExpr { + constexpr operator int() const volatile { return 0; } +}; + +namespace Dune { + template<> + struct AutonomousValueType { using type = int; }; + + // doAutoCopy should not pick up this overload + constexpr auto autoCopy(ZeroExpr) = delete; +} // namespace Dune + +int main() +{ + + { + std::vector v{true}; + auto ref = v[0]; + static_assert(!std::is_same::value, + "sanity check failed"); + auto val = Dune::autoCopy(v[0]); + static_assert(std::is_same::value, + "vector::reference not resolved"); + } + + { + constexpr ZeroExpr zexpr{}; + auto val = doAutoCopy(zexpr); + static_assert(std::is_same::value, + "Custom type was not resolved"); + + static_assert(doAutoCopy(zexpr) == 0, + "Resolution is not constexpr"); + } + +} diff --git a/dune/common/test/bigunsignedinttest.cc b/dune/common/test/bigunsignedinttest.cc new file mode 100644 index 0000000..c4f6611 --- /dev/null +++ b/dune/common/test/bigunsignedinttest.cc @@ -0,0 +1,122 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include + +#define CHECK(x) \ + do { \ + if (!(x)) { \ + pass = false; \ + std::cerr << "FAILED: " << #x << std::endl; \ + } \ + } while(false) + +int main() +{ + bool pass = true; + + typedef Dune::bigunsignedint<16> ShortInteger; + typedef Dune::bigunsignedint<128> BigInteger; + + /* Test std::numeric_limits for ShortInteger (should be same as for uint16_t) */ + CHECK(std::numeric_limits::min() == std::numeric_limits::min()); + CHECK(std::numeric_limits::max() == std::numeric_limits::max()); + CHECK(std::numeric_limits::digits == std::numeric_limits::digits); + CHECK(std::numeric_limits::epsilon() == std::numeric_limits::epsilon()); + CHECK(std::numeric_limits::round_error() == std::numeric_limits::round_error()); + + CHECK(std::numeric_limits::is_exact); + CHECK(std::numeric_limits::is_integer); + CHECK(!std::numeric_limits::is_signed); + + /* Test std::numeric_limits for BigInteger */ + CHECK(std::numeric_limits::min() == 0u); + CHECK(std::numeric_limits::digits == 128); + CHECK(std::numeric_limits::epsilon() == 0u); + CHECK(std::numeric_limits::round_error() == 0u); + + CHECK(std::numeric_limits::is_exact); + CHECK(std::numeric_limits::is_integer); + CHECK(!std::numeric_limits::is_signed); + + /* Test constructor */ + CHECK(BigInteger(10u) == 10u); + CHECK(BigInteger(10) == BigInteger(10u)); + + try { + BigInteger tmp(-10); + pass = false; + std::cerr << "FAILED: BigInteger(-10) should throw an exception." << std::endl; + } + catch(const Dune::Exception&) { + /* Ignore */ + } + catch(...) { + pass = false; + std::cerr << "FAILED: BigInteger(-10) threw an unexpected exception." << std::endl; + } + + /* Test conversion */ + CHECK(BigInteger(10u).touint() == 10u); + CHECK(BigInteger(10u).todouble() == 10.0); + + /* Check BigInteger arithmetic */ + CHECK(BigInteger(10u) + BigInteger(3u) == BigInteger(10u + 3u)); + BigInteger tmp(10u); tmp += BigInteger(3u); + CHECK(tmp == BigInteger(10u + 3u)); + CHECK(BigInteger(10u) - BigInteger(3u) == BigInteger(10u - 3u)); + tmp = BigInteger(10u); tmp -= BigInteger(3u); + CHECK(tmp == BigInteger(10u - 3u)); + CHECK(BigInteger(10u) * BigInteger(3u) == BigInteger(10u * 3u)); + tmp = BigInteger(10u); tmp *= BigInteger(3u); + CHECK(tmp == BigInteger(10u * 3u)); + CHECK(BigInteger(10u) / BigInteger(3u) == BigInteger(10u / 3u)); + tmp = BigInteger(10u); tmp /= BigInteger(3u); + CHECK(tmp == BigInteger(10u / 3u)); + CHECK(BigInteger(10u) % BigInteger(3u) == BigInteger(10u % 3u)); + tmp = BigInteger(10u); tmp %= BigInteger(3u); + CHECK(tmp == BigInteger(10u % 3u)); + + CHECK(BigInteger(100000u) + BigInteger(30000u) == BigInteger(100000u + 30000u)); + tmp = BigInteger(100000u); tmp += BigInteger(30000u); + CHECK(tmp == BigInteger(100000u + 30000u)); + CHECK(BigInteger(100000u) - BigInteger(30000u) == BigInteger(100000u - 30000u)); + tmp = BigInteger(100000u); tmp -= BigInteger(30000u); + CHECK(tmp == BigInteger(100000u - 30000u)); + CHECK(BigInteger(70000u) - BigInteger(30000u) == BigInteger(70000u - 30000u)); + tmp = BigInteger(70000u); tmp -= BigInteger(30000u); + CHECK(tmp == BigInteger(70000u - 30000u)); + CHECK(BigInteger(100000u) * BigInteger(30000u) == BigInteger(100000u * 30000u)); + tmp = BigInteger(100000u); tmp *= BigInteger(30000u); + CHECK(tmp == BigInteger(100000u * 30000u)); + CHECK(BigInteger(100000u) / BigInteger(30000u) == BigInteger(100000u / 30000u)); + tmp = BigInteger(100000u); tmp /= BigInteger(30000u); + CHECK(tmp == BigInteger(100000u / 30000u)); + CHECK(BigInteger(100000u) % BigInteger(30000u) == BigInteger(100000u % 30000u)); + tmp = BigInteger(100000u); tmp %= BigInteger(30000u); + CHECK(tmp == BigInteger(100000u % 30000u)); + + /* Test hashing */ + { + Dune::hash hasher; + CHECK(hasher(BigInteger(100)) == hasher(BigInteger(100))); + } + const BigInteger one{1}; + const BigInteger zero{0}; + CHECK((one & one) == one); + CHECK((one & zero) == zero); + CHECK((one | one) == one); + CHECK((one | zero) == one); + CHECK((one ^ one) == zero); + CHECK((one ^ zero) == one); + + return pass ? 0 : 1; +} diff --git a/dune/common/test/bitsetvectortest.cc b/dune/common/test/bitsetvectortest.cc new file mode 100644 index 0000000..0faf6ed --- /dev/null +++ b/dune/common/test/bitsetvectortest.cc @@ -0,0 +1,192 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#if defined(__GNUC__) && ! defined(__clang__) +#include +#endif + +#include + +template +struct ConstReferenceOp +{ + typedef typename BBF::value_type bitset; + typedef typename BBF::const_reference const_reference; + + void operator()(const_reference t){ + [[maybe_unused]] bitset x = t; + } +}; + +template +void testConstBitSetMethods(const T t) +{ + t.size(); + t[0]; + t[t.size()-1]; + t << 2; + t >> 2; + ~t; + t.count(); + t.any(); + t.none(); + t.test(0); +} + +template +void testContainer(BBF & bbf) +{ + typedef typename BBF::value_type bitset; + typedef typename BBF::reference reference; + typedef typename BBF::const_reference const_reference; + + const BBF & cbbf = bbf; + + bitset x = bbf[3]; + reference y = bbf[4]; + const_reference z = bbf[4]; + const reference v = bbf[4]; + + // assignement + y = false; + y[2] = true; + y = x; + y = z; + y = v; + x = y; + x = z; + x = v; + y = cbbf[1]; + x = cbbf[1]; + bbf[4] = x; + bbf[4] = v; + bbf[4] = y; + bbf[4] = true; + + // invoke methods + testConstBitSetMethods(x); + testConstBitSetMethods(y); + testConstBitSetMethods(z); + testConstBitSetMethods(v); + testConstBitSetMethods(bbf[1]); + testConstBitSetMethods(cbbf[2]); + + // equality + [[maybe_unused]] bool res; + res = (y == cbbf[2]); + res = (y == bbf[3]); + res = (y == x); + res = (x == y); + res = (x == z); + res = (z == x); + res = (z == y); + res = (y == z); + + // inequality + res = (y != cbbf[2]); + res = (y != bbf[3]); + res = (y != x); + res = (x != y); + res = (x != z); + res = (z != x); + res = (z != y); + res = (y != z); + + // &= + y &= cbbf[2]; + y &= bbf[3]; + y &= x; + x &= y; + x &= z; + y &= z; + + // |= + y |= cbbf[2]; + y |= bbf[3]; + y |= x; + x |= y; + x |= z; + y |= z; + + // ^= + y ^= cbbf[2]; + y ^= bbf[3]; + y ^= x; + x ^= y; + x ^= z; + y ^= z; + + // shift operator + y <<= 1; + y >>= 1; + + // flip + y.flip(); + y.flip(2); + y[3].flip(); +} + +template +void testConstContainer(const BBF& bbf){ + typedef typename BBF::value_type bitset; + typedef typename BBF::iterator iterator; + typedef typename std::iterator_traits::value_type value_type; + typedef typename BBF::const_reference reference; + + const BBF & cbbf = bbf; + + bitset x = bbf[3]; + [[maybe_unused]] value_type z; + reference y = bbf[4]; + + // assignement + x = y; + x = cbbf[1]; + + // equality + [[maybe_unused]] bool res; + res = (y == cbbf[2]); + res = (y == bbf[3]); + res = (y == x); + res = (x == y); + + // inequality + res = (y != cbbf[2]); + res = (y != bbf[3]); + res = (y != x); + res = (x != y); +} + +template +void doTest() { + typedef Dune::BitSetVector BBF; + + BBF bbf(10,true); + const BBF & cbbf = bbf; + + // test containers and some basic bitset operations + testContainer(bbf); + testConstContainer(bbf); + testConstContainer(cbbf); + + // iterator interface +#ifndef NDEBUG + ConstReferenceOp cop; + assert(testIterator(bbf, cop) == 0); + assert(testIterator(cbbf, cop) == 0); +#endif +} + +int main() +{ + doTest<4, std::allocator >(); +#if defined(__GNUC__) && ! defined(__clang__) + doTest<4, __gnu_cxx::malloc_allocator >(); +#endif + return 0; +} diff --git a/dune/common/test/boundscheckingmvtest.cc b/dune/common/test/boundscheckingmvtest.cc new file mode 100644 index 0000000..95c1738 --- /dev/null +++ b/dune/common/test/boundscheckingmvtest.cc @@ -0,0 +1,391 @@ +#include +#include +#include +#include + +int main() try { + bool passed = true; + + // Free matrix-vector multiplication (mv): Input size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2}; + Dune::FieldVector b(0); + Dune::DenseMatrixHelp::multAssign(A,x,b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Free matrix-vector multiplication (mv): Output size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2, 3}; + Dune::FieldVector b(0); + Dune::DenseMatrixHelp::multAssign(A,x,b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Matrix-vector multiplication (mv): Input size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2}; + Dune::FieldVector b(0); + A.mv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Matrix-vector multiplication (mv): Output size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2, 3}; + Dune::FieldVector b(0); + A.mv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Matrix-vector multiplication (mtv): Input size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2, 3}; + Dune::FieldVector b(0); + A.mtv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Matrix-vector multiplication (mtv): Output size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2}; + Dune::FieldVector b(0); + A.mtv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Matrix-vector multiplication (umv): Input size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2}; + Dune::FieldVector b(0); + A.umv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Matrix-vector multiplication (umv): Output size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2, 3}; + Dune::FieldVector b(0); + A.umv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Matrix-vector multiplication (umtv): Input size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2, 3}; + Dune::FieldVector b(0); + A.umtv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Matrix-vector multiplication (umtv): Output size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2}; + Dune::FieldVector b(0); + A.umtv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Matrix-vector multiplication (umhv): Input size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2, 3}; + Dune::FieldVector b(0); + A.umhv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Matrix-vector multiplication (umhv): Output size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2}; + Dune::FieldVector b(0); + A.umhv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Matrix-vector multiplication (mmv): Input size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2}; + Dune::FieldVector b(0); + A.mmv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Matrix-vector multiplication (mmv): Output size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2, 3}; + Dune::FieldVector b(0); + A.mmv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Matrix-vector multiplication (mmtv): Input size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2, 3}; + Dune::FieldVector b(0); + A.mmtv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Matrix-vector multiplication (mmtv): Output size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2}; + Dune::FieldVector b(0); + A.mmtv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Matrix-vector multiplication (mmhv): Input size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2, 3}; + Dune::FieldVector b(0); + A.mmhv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Matrix-vector multiplication (mmhv): Output size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2}; + Dune::FieldVector b(0); + A.mmhv(x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Matrix-vector multiplication (usmv): Input size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2}; + Dune::FieldVector b(0); + A.usmv(2, x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Matrix-vector multiplication (usmv): Output size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2, 3}; + Dune::FieldVector b(0); + A.usmv(2, x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Matrix-vector multiplication (usmtv): Input size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2, 3}; + Dune::FieldVector b(0); + A.usmtv(2, x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Matrix-vector multiplication (usmtv): Output size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2}; + Dune::FieldVector b(0); + A.usmtv(2, x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Matrix-vector multiplication (usmhv): Input size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2, 3}; + Dune::FieldVector b(0); + A.usmhv(2, x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Matrix-vector multiplication (usmhv): Output size incorrect + try { + Dune::FieldMatrix A = {{1, 2, 3}, {10, 20, 30}}; + Dune::FieldVector x = {1, 2}; + Dune::FieldVector b(0); + A.usmhv(2, x, b); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // right multiplication: None-square Matrix argument + try { + Dune::DynamicMatrix A(2, 3, 5); + Dune::DynamicMatrix const B(3, 2, 5); + A.rightmultiply(B); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // right multiplication: Incorrect number of rows + try { + Dune::DynamicMatrix A(2, 2, 5); + Dune::DynamicMatrix const B(3, 3, 5); + A.rightmultiply(B); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // left multiplication: None-square Matrix argument + try { + Dune::FieldMatrix A = {{1, 2}, {10, 20}, {100, 200}}; + Dune::FieldMatrix const B = {{1, 2, 3}, {10, 20, 30}}; + A.leftmultiply(B); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // left multiplication: Incorrect number of rows + try { + Dune::FieldMatrix A = { + {1, 2, 3}, {10, 20, 30}, {100, 200, 300}}; + Dune::FieldMatrix const B = {{1, 2}, {10, 20}, {100, 200}}; + A.leftmultiply(B); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + return passed ? 0 : 1; +} catch (Dune::Exception &e) { + std::cerr << e << std::endl; + return 1; +} catch (std::exception &e) { + std::cerr << e.what() << std::endl; + return 1; +} diff --git a/dune/common/test/boundscheckingoptest.cc b/dune/common/test/boundscheckingoptest.cc new file mode 100644 index 0000000..e0d5217 --- /dev/null +++ b/dune/common/test/boundscheckingoptest.cc @@ -0,0 +1,140 @@ +#include +#include +#include + +int main() try { + bool passed = true; + + // Add vectors of different sizes + try { + Dune::FieldVector v1 = {1, 2, 3}; + Dune::FieldVector const v2 = {1, 2}; + v1 += v2; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Subtract vectors of different sizes + try { + Dune::FieldVector v1 = {1, 2, 3}; + Dune::FieldVector const v2 = {1, 2}; + v1 -= v2; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Check vectors of different sizes for equality + try { + Dune::FieldVector const v1 = {1, 2, 3}; + Dune::FieldVector const v2 = {1, 2}; + [[maybe_unused]] bool res = (v1 == v2); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Check vectors of different sizes for inequality + try { + Dune::FieldVector const v1 = {1, 2, 3}; + Dune::FieldVector const v2 = {1, 2}; + [[maybe_unused]] bool res = (v1 != v2); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Apply axpy to vectors of different sizes + try { + Dune::FieldVector v1 = {1, 2, 3}; + Dune::FieldVector const v2 = {1, 2}; + v1.axpy(2, v2); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + try { + Dune::FieldMatrix m1 = {{1, 2, 3}}; + Dune::FieldMatrix const m2 = {{1, 2, 3}, {10, 20, 30}}; + m1 += m2; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + try { + Dune::FieldMatrix m1 = {{1, 2, 3}}; + Dune::FieldMatrix const m2 = {{1, 2, 3}, {10, 20, 30}}; + m1 -= m2; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + try { + Dune::FieldMatrix m1 = {{1, 2, 3}}; + Dune::FieldMatrix const m2 = {{1, 2, 3}, {10, 20, 30}}; + [[maybe_unused]] bool res = (m1 == m2); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + try { + Dune::FieldMatrix m1 = {{1, 2, 3}}; + Dune::FieldMatrix const m2 = {{1, 2, 3}, {10, 20, 30}}; + [[maybe_unused]] bool res = (m1 != m2); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + try { + Dune::FieldMatrix m1 = {{1, 2, 3}}; + Dune::FieldMatrix const m2 = {{1, 2, 3}, {10, 20, 30}}; + m1.axpy(2, m2); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + return passed ? 0 : 1; +} catch (Dune::Exception &e) { + std::cerr << e << std::endl; + return 1; +} catch (std::exception &e) { + std::cerr << e.what() << std::endl; + return 1; +} diff --git a/dune/common/test/boundscheckingtest.cc b/dune/common/test/boundscheckingtest.cc new file mode 100644 index 0000000..05d3742 --- /dev/null +++ b/dune/common/test/boundscheckingtest.cc @@ -0,0 +1,299 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +int main() try { + bool passed = true; + + // Write beyond end of singleton vector + try { + Dune::FieldVector v = {1}; + v[1] = 10; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Read beyond end of singleton vector + try { + Dune::FieldVector const v = {1}; + [[maybe_unused]] double const x = v[1]; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Write beyond end of vector + try { + Dune::FieldVector v = {1, 2, 3}; + v[3] = 10; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + try { + Dune::DynamicVector v = {1, 2, 3}; + v[3] = 10; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Read beyond end of vector + try { + Dune::FieldVector const v = {1, 2, 3}; + [[maybe_unused]] double const x = v[3]; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + try { + Dune::DynamicVector const v = {1, 2, 3}; + [[maybe_unused]] double const x = v[3]; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + + // Write beyond end of singleton matrix + try { + Dune::FieldMatrix m = {{1}}; + m[1][0] = 100; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Read beyond end of singleton matrix + try { + Dune::FieldMatrix const m = {{1}}; + [[maybe_unused]] double const x = m[1][0]; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Write beyond end of matrix + try { + Dune::FieldMatrix m = {{1, 2, 3}, {10, 20, 30}}; + m[2][0] = 100; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + try { + Dune::DynamicMatrix m = {{1, 2, 3}, {10, 20, 30}}; + m[2][0] = 100; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Read beyond end of matrix + try { + Dune::FieldMatrix const m = {{1, 2, 3}, {10, 20, 30}}; + [[maybe_unused]] double const x = m[2][0]; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + try { + Dune::DynamicMatrix const m = {{1, 2, 3}, {10, 20, 30}}; + [[maybe_unused]] double const x = m[2][0]; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Write beyond end of diagonal matrix (way #1) + try { + Dune::DiagonalMatrix d(5); + d[3][3] = 9; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Write beyond end of diagonal matrix (way #2) + try { + Dune::DiagonalMatrix d(5); + d.diagonal(3) = 9; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Read beyond end of diagonal matrix (way #1) + try { + Dune::DiagonalMatrix const d(5); + [[maybe_unused]] double const x = d[3][3]; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + // Read beyond end of diagonal matrix (way #2) + try { + Dune::DiagonalMatrix const d(5); + [[maybe_unused]] double const x = d.diagonal(3); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Write outside of diagonal matrix pattern + try { + Dune::DiagonalMatrix d(5); + d[1][2] = 9; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Read outside of diagonal matrix pattern + try { + Dune::DiagonalMatrix const d(5); + [[maybe_unused]] double const x = d[1][2]; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Check for entry beyond diagonal matrix size + try { + Dune::DiagonalMatrix const d(5); + d.exists(3, 3); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Check for entry beyond matrix size + try { + Dune::FieldMatrix const m = {{1, 2, 3}, {10, 20, 30}}; + m.exists(2, 2); + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Read beyond end of bitsetvector + try { + Dune::BitSetVector<3> const b(10); + [[maybe_unused]] auto const x = b[10]; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Write beyond end of bitsetvector + try { + Dune::BitSetVector<3> b(10); + b[10] = true; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Read beyond end of bitsetvectorreference + try { + Dune::BitSetVector<3> const b(10); + [[maybe_unused]] auto const x = b[10][3]; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + // Write beyond end of bitsetvectorreference + try { + Dune::BitSetVector<3> b(10); + b[10][3] = true; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + return passed ? 0 : 1; +} catch (Dune::Exception &e) { + std::cerr << e << std::endl; + return 1; +} catch (std::exception &e) { + std::cerr << e.what() << std::endl; + return 1; +} diff --git a/dune/common/test/calloncetest.cc b/dune/common/test/calloncetest.cc new file mode 100644 index 0000000..ebc82d7 --- /dev/null +++ b/dune/common/test/calloncetest.cc @@ -0,0 +1,12 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +int main() { + DUNE_ASSERT_CALL_ONCE(); +} diff --git a/dune/common/test/check_fvector_size.cc b/dune/common/test/check_fvector_size.cc new file mode 100644 index 0000000..d327f2c --- /dev/null +++ b/dune/common/test/check_fvector_size.cc @@ -0,0 +1,15 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#include +#include + +int main(int, char **) +{ + + Dune::DynamicVector one(1); + Dune::DynamicVector two(2); + + two = one; + + return 0; +} diff --git a/dune/common/test/check_fvector_size_fail.cc b/dune/common/test/check_fvector_size_fail.cc new file mode 100644 index 0000000..530d4f1 --- /dev/null +++ b/dune/common/test/check_fvector_size_fail.cc @@ -0,0 +1,15 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#include +#include + +int main(int argc, char * argv[]) +{ + + Dune::FieldVector one(1); + Dune::FieldVector two(2); + + one=two; + + return 0; +} diff --git a/dune/common/test/checkmatrixinterface.hh b/dune/common/test/checkmatrixinterface.hh new file mode 100644 index 0000000..f80cfea --- /dev/null +++ b/dune/common/test/checkmatrixinterface.hh @@ -0,0 +1,435 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_CHECK_MATRIX_INTERFACE_HH +#define DUNE_COMMON_CHECK_MATRIX_INTERFACE_HH + +#include +#include + +#include +#include +#include +#include + + +/* + * @file + * @brief This file provides an interface check for dense matrices. + * @author Christoph Gersbacher + */ + + +namespace Dune +{ + + // External forward declarations for namespace Dune + // ------------------------------------------------ + + template< class, int, int > class FieldMatrix; + template< class, int > class DiagonalMatrix; + +} // namespace Dune + + + +namespace CheckMatrixInterface +{ + + namespace Capabilities + { + + // hasStaticSizes + // -------------- + + template< class Matrix > + struct hasStaticSizes + { + static const bool v = false; + static const int rows = ~0; + static const int cols = ~0; + }; + + template< class Matrix > + struct hasStaticSizes< const Matrix > + { + static const bool v = hasStaticSizes< Matrix >::v; + static const int rows = hasStaticSizes< Matrix >::rows; + static const int cols = hasStaticSizes< Matrix >::cols; + }; + + + + // isSquare + // -------- + + template< class Matrix > + struct isSquare + { + static const bool v = false; + }; + + template< class Matrix > + struct isSquare< const Matrix > + { + static const bool v = isSquare< Matrix >::v; + }; + + + + // Template specializations for Dune::FieldMatrix + // ---------------------------------------------- + + template< class K, int r, int c > + struct hasStaticSizes< Dune::FieldMatrix< K, r, c > > + { + static const bool v = true; + static const int rows = r; + static const int cols = c; + }; + + template< class K, int rows, int cols > + struct isSquare< Dune::FieldMatrix< K, rows, cols > > + { + static const bool v = ( rows == cols ); + }; + + + + // Template specializations for Dune::DiagonalMatrix + // ------------------------------------------------- + + template< class K, int n > + struct hasStaticSizes< Dune::DiagonalMatrix > + { + static const bool v = true; + static const int rows = n; + static const int cols = n; + }; + + template< class K, int n > + struct isSquare< Dune::DiagonalMatrix > + { + static const bool v = true; + }; + + } // namespace Capabilities + + + + // UseDynamicVector + // ---------------- + + template< class Matrix > + struct UseDynamicVector + { + typedef typename Matrix::value_type value_type; + + typedef Dune::DynamicVector< value_type > domain_type; + typedef domain_type range_type; + + static domain_type domain ( const Matrix &matrix, value_type v = value_type() ) + { + return domain_type( matrix.M(), v ); + } + + static range_type range ( const Matrix &matrix, value_type v = value_type() ) + { + return range_type( matrix.N(), v ); + } + }; + + + + // UseFieldVector + // -------------- + + template< class K, int rows, int cols > + struct UseFieldVector + { + typedef K value_type; + + typedef Dune::FieldVector< K, cols > domain_type; + typedef Dune::FieldVector< K, rows > range_type; + + template< class Matrix > + static domain_type domain ( const Matrix &, value_type v = value_type() ) + { + return domain_type( v ); + } + + template< class Matrix > + static range_type range ( const Matrix &, value_type v = value_type() ) + { + return range_type( v ); + } + }; + + + + // MatrixSizeHelper + // ---------------- + + template< class Matrix, bool hasStaticSizes = Capabilities::hasStaticSizes< Matrix >::v > + struct MatrixSizeHelper; + + template< class Matrix > + struct MatrixSizeHelper< Matrix, false > + { + typedef typename Matrix::size_type size_type; + static const size_type rows ( const Matrix &matrix ) { return matrix.rows(); } + static const size_type cols ( const Matrix &matrix ) { return matrix.cols(); } + }; + + template< class Matrix > + struct MatrixSizeHelper< Matrix, true > + { + typedef typename Matrix::size_type size_type; + static const size_type rows ( const Matrix & ) { return Matrix::rows; } + static const size_type cols ( const Matrix & ) { return Matrix::cols; } + }; + + + + // CheckIfSquareMatrix + // ------------------- + + template< class Matrix, class Traits, bool isSquare = Capabilities::isSquare< Matrix >::v > + struct CheckIfSquareMatrix; + + template< class Matrix, class Traits > + struct CheckIfSquareMatrix< Matrix, Traits, false > + { + static void apply ( const Matrix &) {} + + static void apply ( Matrix &) {} + }; + + template< class Matrix, class Traits > + struct CheckIfSquareMatrix< Matrix, Traits, true > + { + typedef typename Matrix::value_type value_type; + + static value_type tolerance () + { + return value_type( 16 ) * std::numeric_limits< value_type >::epsilon(); + } + + static void apply ( const Matrix &matrix ) + { + const value_type determinant = matrix.determinant(); + if( determinant > tolerance() ) + { + typename Traits::domain_type x = Traits::domain( matrix ); + const typename Traits::range_type b = Traits::range( matrix ); + matrix.solve( x, b ); + } + } + + static void apply ( Matrix &matrix ) + { + apply( const_cast< const Matrix & >( matrix ) ); + if( matrix.determinant() > tolerance() ) + matrix.invert(); + } + }; + + + + // CheckConstMatrix + // ---------------- + + template< class Matrix, class Traits > + struct CheckConstMatrix + { + // required type definitions + typedef typename Matrix::size_type size_type; + + typedef typename Matrix::value_type value_type; + typedef typename Matrix::field_type field_type; + typedef typename Matrix::block_type block_type; + + typedef typename Matrix::const_row_reference const_row_reference; + + typedef typename Matrix::ConstIterator ConstIterator; + + static void apply ( const Matrix &matrix ) + { + checkDataAccess ( matrix ); + checkIterators ( matrix ); + checkLinearAlgebra ( matrix ); + checkNorms ( matrix ); + checkSizes ( matrix ); + CheckIfSquareMatrix< Matrix, Traits >::apply( matrix ); + + // TODO: check comparison + // bool operator == ( const Matrix &other ); + // bool operator != ( const Matrix &other ); + } + + // check size methods + static void checkSizes ( const Matrix &matrix ) + { + [[maybe_unused]] const size_type size = matrix.size(); + const size_type rows = MatrixSizeHelper< Matrix >::rows( matrix ); + const size_type cols = MatrixSizeHelper< Matrix >::cols( matrix ); + const size_type N = matrix.N(); + const size_type M = matrix.M(); + + if( N != rows || M != cols ) + DUNE_THROW( Dune::RangeError, "Returned inconsistent sizes." ); + } + + // check read-only access to data + static void checkDataAccess ( const Matrix &matrix ) + { + const size_type size = matrix.size(); + for( size_type i = size_type( 0 ); i < size; ++i ) + [[maybe_unused]] const_row_reference row = matrix[ i ]; + + const size_type rows = MatrixSizeHelper< Matrix >::rows( matrix ); + const size_type cols = MatrixSizeHelper< Matrix >::cols( matrix ); + for( size_type i = size_type( 0 ); i < rows; ++i ) + { + for( size_type j = size_type( 0 ); j < cols; ++j ) + { + [[maybe_unused]] bool exists = matrix.exists( i, j ); + [[maybe_unused]] const value_type &value = matrix[ i ][ j ]; + } + } + } + + // check norms + static void checkNorms ( const Matrix &matrix ) + { + typedef typename Dune::FieldTraits< value_type >::real_type real_type; + real_type frobenius_norm = matrix.frobenius_norm(); + real_type frobenius_norm2 = matrix.frobenius_norm2(); + real_type infinity_norm = matrix.infinity_norm() ; + real_type infinity_norm_real = matrix.infinity_norm_real(); + + if( std::min( std::min( frobenius_norm, frobenius_norm2 ), + std::min( infinity_norm, infinity_norm_real ) ) < real_type( 0 ) ) + DUNE_THROW( Dune::InvalidStateException, "Norms must return non-negative value." ); + } + + // check basic linear algebra methods + static void checkLinearAlgebra ( const Matrix &matrix ) + { + typename Traits::domain_type domain = Traits::domain( matrix ); + typename Traits::range_type range = Traits::range( matrix ); + typename Traits::value_type alpha( 1 ); + + matrix.mv( domain, range ); + matrix.mtv( range, domain ); + matrix.umv( domain, range ); + matrix.umtv( range, domain ); + matrix.umhv( range, domain ); + matrix.mmv( domain, range ); + matrix.mmtv( range, domain ); + matrix.mmhv( range, domain ); + matrix.usmv( alpha, domain, range ); + matrix.usmtv( alpha, range, domain ); + matrix.usmhv( alpha, range, domain ); + } + + // check iterator methods + static void checkIterators ( const Matrix &matrix ) + { + const ConstIterator end = matrix.end(); + for( ConstIterator it = matrix.begin(); it != end; ++it ) + [[maybe_unused]] const_row_reference row = *it; + } + }; + + + + // CheckNonConstMatrix + // ------------------- + + template< class Matrix, class Traits > + struct CheckNonConstMatrix + { + // required type definitions + typedef typename Matrix::size_type size_type; + typedef typename Matrix::value_type value_type; + typedef typename Matrix::row_reference row_reference; + typedef typename Matrix::row_type row_type; + typedef typename Matrix::Iterator Iterator; + + static void apply ( Matrix &matrix ) + { + checkIterators( matrix ); + checkAssignment( matrix ); + + CheckIfSquareMatrix< Matrix, Traits >::apply( matrix ); + + // TODO: check scalar/matrix and matrix/matrix operations + // Matrix &operator+= ( const Matrix &other ); + // Matrix &operator-= ( const Matrix &other ); + // Matrix &operator*= ( const value_type &v ); + // Matrix &operator/= ( const value_type &v ); + // Matrix &axpy += ( const value_type &v, const Matrix &other ); + // Matrix &axpy += ( const value_type &v, const Matrix &other ); + // Matrix &leftmultiply ( const Matrix &other ); + // Matrix &rightmultiply ( const Matrix &other ); + } + + // check assignment + static void checkAssignment ( Matrix &matrix ) + { + matrix = value_type( 1 ); + + const size_type size = matrix.size(); + for( size_type i = size_type( 0 ); i < size; ++i ) + { + row_reference row = matrix[ i ]; + row = row_type( value_type( 0 ) ); + } + + const size_type rows = MatrixSizeHelper< Matrix >::rows( matrix ); + const size_type cols = MatrixSizeHelper< Matrix >::cols( matrix ); + for( size_type i = size_type( 0 ); i < rows; ++i ) + { + for( size_type j = size_type( 0 ); j < cols; ++j ) + matrix[ i ][ j ] = ( i == j ? value_type( 1 ) : value_type( 0 ) ); + } + } + + // check iterator methods + static void checkIterators ( Matrix &matrix ) + { + const Iterator end = matrix.end(); + for( Iterator it = matrix.begin(); it != end; ++it ) + { + row_reference row = *it; + row = row_type( value_type( 0 ) ); + } + } + }; + +} // namespace CheckMatrixInterface + + + +namespace Dune +{ + + // checkMatrixInterface + // -------------------- + + template< class Matrix, class Traits = CheckMatrixInterface::UseDynamicVector< Matrix > > + void checkMatrixInterface ( const Matrix &matrix ) + { + CheckMatrixInterface::CheckConstMatrix< Matrix, Traits >::apply( matrix ); + } + + template< class Matrix, class Traits = CheckMatrixInterface::UseDynamicVector< Matrix > > + void checkMatrixInterface ( Matrix &matrix ) + { + checkMatrixInterface( const_cast< const Matrix & >( matrix ) ); + CheckMatrixInterface::CheckNonConstMatrix< Matrix, Traits >::apply( matrix ); + } + +} // namespace Dune + +#endif // #ifndef DUNE_COMMON_CHECK_MATRIX_INTERFACE_HH diff --git a/dune/common/test/classnametest.cc b/dune/common/test/classnametest.cc new file mode 100644 index 0000000..682d8d4 --- /dev/null +++ b/dune/common/test/classnametest.cc @@ -0,0 +1,140 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + + +using CVRef = std::bitset<4>; +constexpr CVRef is_const = 1; +constexpr CVRef is_volatile = 2; +constexpr CVRef is_lvalue_reference = 4; +constexpr CVRef is_rvalue_reference = 8; +constexpr CVRef is_reference = 12; + +void checkname(Dune::TestSuite &t, const std::string &name, CVRef cvref, + const std::string &pattern) +{ + const auto npos = std::string::npos; + + std::cout << name << std::endl; + + t.check(std::regex_search(name, std::regex{pattern})) + << '`' << name << "` does not look like `" << pattern << '`'; + + static const std::regex const_pattern{ R"(\bconst\b)" }; + bool found_const = std::regex_search(name, const_pattern); + if((cvref & is_const) == is_const) + t.check(found_const) << '`' << name << "` contains `const`"; + else + t.check(!found_const) << '`' << name << "` does not contain `const`"; + + static const std::regex volatile_pattern{ R"(\bvolatile\b)" }; + bool found_volatile = std::regex_search(name, volatile_pattern); + if((cvref & is_volatile) == is_volatile) + t.check(found_volatile) << '`' << name << "` contains `volatile`"; + else + t.check(!found_volatile) << '`' << name << "` does not contain `volatile`"; + + bool found_reference = name.find('&') != npos; + bool found_rvalue_reference = name.find("&&") != npos; + if((cvref & is_reference) == is_reference) + t.check(found_reference) + << '`' << name << "` does not contain `&` or `&&`"; + else if((cvref & is_lvalue_reference) == is_lvalue_reference) + t.check(found_reference && !found_rvalue_reference) + << '`' << name << "` contains `&&` or does not contain `&`"; + else if((cvref & is_rvalue_reference) == is_rvalue_reference) + t.check(found_rvalue_reference) + << '`' << name << "` does not contain `&&`"; + else + t.check(!found_reference) + << '`' << name << "` contains `&` or `&&`"; +} + +struct Base { + virtual ~Base() = default; +}; + +struct Derived : Base {}; + +int main() +{ + Dune::TestSuite t("className()"); + + std::cout << "First three simple class names extracted from variables:" + << std::endl; + Dune::FieldVector xi; + checkname(t, Dune::className(xi), {}, + R"(\bFieldVector\s*<\s*int\s*,\s*3\s*>)"); + Dune::FieldVector xd; + checkname(t, Dune::className(xd), {}, + R"(\bFieldVector\s*<\s*double\s*,\s*1\s*>)"); + Dune::FieldVector, 10> xcd; + checkname(t, Dune::className(xcd), {}, + R"(\bFieldVector\s*<.*\bcomplex\s*<\s*double\s*>\s*,\s*10\s*>)"); + std::cout << std::endl; + + std::cout << "Adding const:" << std::endl; + const Dune::FieldVector cxi; + checkname(t, Dune::className(cxi), is_const, + R"(\bFieldVector\s*<\s*int\s*,\s*3\s*>)"); + std::cout << std::endl; + + std::cout << "If a variable is a reference that can not be extracted (needs " + << "decltype as used below): " << std::endl; + Dune::FieldVector &rxd = xd; + checkname(t, Dune::className(rxd), {}, + R"(\bFieldVector\s*<\s*double\s*,\s*1\s*>)"); + std::cout << std::endl; + + std::cout << "Extracting the class name using a type directly - " + << "also extractes references correctly: " << std::endl; + checkname(t, Dune::className(), is_lvalue_reference, + R"(\bFieldVector\s*<\s*double\s*,\s*1\s*>)"); + const Dune::FieldVector &rcxd = xd; + checkname(t, Dune::className(), is_const|is_lvalue_reference, + R"(\bFieldVector\s*<\s*double\s*,\s*1\s*>)"); + const Dune::FieldVector &rcxi = cxi; + checkname(t, Dune::className(), is_const|is_lvalue_reference, + R"(\bFieldVector\s*<\s*int\s*,\s*3\s*>)"); + std::cout << std::endl; + + std::cout << "Test some further types:" << std::endl; + using RVXCD = volatile Dune::FieldVector, 10>&; + checkname(t, Dune::className(), is_volatile|is_lvalue_reference, + R"(\bFieldVector\s*<.*\bcomplex\s*<\s*double\s*>\s*,\s*10\s*>)"); + using RRXCD = Dune::FieldVector, 10>&&; + checkname(t, Dune::className(), is_rvalue_reference, + R"(\bFieldVector\s*<.*\bcomplex\s*<\s*double\s*>\s*,\s*10\s*>)"); + std::cout << std::endl; + + std::cout << "Test printing dynamic vs. static types:" << std::endl; + Derived d{}; + Base &b = d; + checkname(t, Dune::className(b), {}, R"(\bDerived\b)"); + checkname(t, Dune::className(), is_lvalue_reference, + R"(\bBase\b)"); + t.check(Dune::className() == Dune::className(b)) + << "dynamic type of base reference should match derived type"; + std::cout << std::endl; + + std::cout << "Test rvalue argument to className(expr):" << std::endl; + checkname(t, Dune::className(Base{}), {}, R"(\bBase\b)"); + std::cout << std::endl; + + #if !HAVE_CXA_DEMANGLE + // in this case we only make sure that no segfault or similar happens + return 0; + #else + return t.exit(); + #endif +} diff --git a/dune/common/test/collectorstream.hh b/dune/common/test/collectorstream.hh new file mode 100644 index 0000000..4c31a57 --- /dev/null +++ b/dune/common/test/collectorstream.hh @@ -0,0 +1,81 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_TEST_COLLECTORSTREAM_HH +#define DUNE_COMMON_TEST_COLLECTORSTREAM_HH + +#include +#include +#include + + +#include + + +namespace Dune { + + + +/** + * \brief Data collector stream + * + * A class derived from std::ostringstream that allows to + * collect data via a temporary returned object. To facilitate + * this it stores a callback that is used to pass the collected + * data to its creator on destruction. + * + * In order to avoid passing the same data twice, copy construction + * is forbidden and only move construction is allowed. + */ +class CollectorStream : public std::ostringstream +{ +public: + + /** + * \brief Create from callback + * + * \tparam CallBack Type of callback. Must be convertible to std::function + * \param callBack A copy of this function will be stored and called on destruction. + */ + template = 0> + CollectorStream(CallBack&& callBack) : + callBack_(callBack) + {} + + CollectorStream(const CollectorStream& other) = delete; + + /** + * \brief Move constructor + * + * This will take over the data and callback from the + * moved from CollectorStream and disable the callback + * in the latter. + */ + CollectorStream(CollectorStream&& other) : + callBack_(other.callBack_) + { + (*this) << other.str(); + other.callBack_ = [](std::string){}; + } + + /** + * \brief Destructor + * + * This calls the callback function given on creation + * passing all collected data as a single string argument. + */ + ~CollectorStream() + { + callBack_(this->str()); + } + +private: + std::function callBack_; +}; + + + +} // namespace Dune + + +#endif // DUNE_COMMON_TEST_COLLECTORSTREAM_HH diff --git a/dune/common/test/concept.cc b/dune/common/test/concept.cc new file mode 100644 index 0000000..5fc0070 --- /dev/null +++ b/dune/common/test/concept.cc @@ -0,0 +1,200 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#include + +#include + +#include +#include + +#include +#include + + + +struct HasFoo +{ + template + auto require(const T& t) -> decltype( + t.foo() + ); +}; + +struct HasBar +{ + template + auto require(const T& t) -> decltype( + t.bar() + ); +}; + +struct HasFooAndBar1 : Dune::Concept::Refines +{ + template + auto require(const T& t) -> decltype( + t.bar() + ); +}; + +struct HasFooAndBar2 : Dune::Concept::Refines +{ + template + auto require(const T& t) -> decltype( + t.foo() + ); +}; + +struct HasFooAndBar3 +{ + template + auto require(const T& t) -> decltype( + t.foo(), + t.bar() + ); +}; + +struct HasFooAndBar4 : Dune::Concept::Refines +{ + template + auto require(const T& t) -> decltype( + 0 + ); +}; + +struct HasFooAndBar5 +{ + template + auto require(const T& t) -> decltype( + 0 + ); + using BaseConceptList = Dune::TypeList; +}; + + + + +template +struct Foo +{ + T foo() const + { return T(); } +}; + +template +struct Bar +{ + T bar() const + { return T(); } +}; + +template +struct FooBar +{ + T foo() const + { return T(); } + + T bar() const + { return T(); } +}; + + + + +int main ( int argc, char **argv ) +try +{ + using namespace Dune; + + MPIHelper::instance(argc, argv); + + TestSuite test; + + test.check(models>()) + << "models>() gives wrong result"; + + test.check(not models>()) + << "models>() gives wrong result"; + + test.check(models>()) + << "models>() gives wrong result"; + + + + test.check(not models>()) + << "models>() gives wrong result"; + + test.check(models>()) + << "models>() gives wrong result"; + + test.check(models>()) + << "models>() gives wrong result"; + + + + test.check(not models>()) + << "models>() gives wrong result"; + + test.check(not models>()) + << "models>() gives wrong result"; + + test.check(models>()) + << "models>() gives wrong result"; + + + + test.check(not models>()) + << "models>() gives wrong result"; + + test.check(not models>()) + << "models>() gives wrong result"; + + test.check(models>()) + << "models>() gives wrong result"; + + + + test.check(not models>()) + << "models>() gives wrong result"; + + test.check(not models>()) + << "models>() gives wrong result"; + + test.check(models>()) + << "models>() gives wrong result"; + + + + test.check(not models>()) + << "models>() gives wrong result"; + + test.check(not models>()) + << "models>() gives wrong result"; + + test.check(models>()) + << "models>() gives wrong result"; + + + + test.check(not models>()) + << "models>() gives wrong result"; + + test.check(not models>()) + << "models>() gives wrong result"; + + test.check(models>()) + << "models>() gives wrong result"; + + + + return test.exit(); +} +catch( Dune::Exception &e ) +{ + std::cerr << "Dune reported error: " << e << std::endl; + return 1; +} +catch(...) +{ + std::cerr << "Unknown exception thrown!" << std::endl; + return 1; +} diff --git a/dune/common/test/constexprifelsetest.cc b/dune/common/test/constexprifelsetest.cc new file mode 100644 index 0000000..6548dff --- /dev/null +++ b/dune/common/test/constexprifelsetest.cc @@ -0,0 +1,17 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#include "config.h" + +#include + +#include + +int main() +{ + // check that the id argument is a constexpr functor + Dune::Hybrid::ifElse(std::true_type{}, [](auto id) { + static_assert(id(true), + "id() argument of ifElse() branches should be a constexpr functor"); + }); +} diff --git a/dune/common/test/debugalignsimd.cc.in b/dune/common/test/debugalignsimd.cc.in new file mode 100644 index 0000000..3c2178c --- /dev/null +++ b/dune/common/test/debugalignsimd.cc.in @@ -0,0 +1,15 @@ +// @GENERATED_SOURCE@ + +#include + +#include +#include +#include + +namespace Dune { + namespace Simd { + + template void UnitTest::check@POINT@ >(); + + } //namespace Simd +} // namespace Dune diff --git a/dune/common/test/debugalignsimdtest.cc.in b/dune/common/test/debugalignsimdtest.cc.in new file mode 100644 index 0000000..2937a25 --- /dev/null +++ b/dune/common/test/debugalignsimdtest.cc.in @@ -0,0 +1,38 @@ +// @GENERATED_SOURCE@ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +template struct RebindAccept : std::false_type {}; +#cmake @template@ +template<> +struct RebindAccept > : std::true_type {}; +#cmake @endtemplate@ + +int main(int argc, char **argv) +{ + Dune::MPIHelper::instance(argc, argv); + + Dune::Simd::UnitTest test; + + using Rebinds = Dune::Simd::RebindList< +#cmake @template@ + @SCALAR@, +#cmake @endtemplate@ + Dune::Simd::EndMark>; + +#cmake @template@ + test.check, + Rebinds, Dune::AlwaysFalse, RebindAccept>(); +#cmake @endtemplate@ + + return test.good() ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/dune/common/test/debugalignsimdtest.hh.in b/dune/common/test/debugalignsimdtest.hh.in new file mode 100644 index 0000000..c98cb3b --- /dev/null +++ b/dune/common/test/debugalignsimdtest.hh.in @@ -0,0 +1,19 @@ +// @GENERATED_SOURCE@ + +#ifndef DUNE_COMMON_TEST_DEBUGALIGNSIMDTEST_HH +#define DUNE_COMMON_TEST_DEBUGALIGNSIMDTEST_HH + +#include +#include + +namespace Dune { + namespace Simd { + +#cmake @template POINT@ + extern template void UnitTest::check@POINT@ >(); +#cmake @endtemplate@ + + } //namespace Simd +} // namespace Dune + +#endif // DUNE_COMMON_TEST_DEBUGALIGNSIMDTEST_HH diff --git a/dune/common/test/debugaligntest.cc b/dune/common/test/debugaligntest.cc new file mode 100644 index 0000000..2f0ffa7 --- /dev/null +++ b/dune/common/test/debugaligntest.cc @@ -0,0 +1,110 @@ +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +class WithViolatedAlignmentHandler { + Dune::ViolatedAlignmentHandler oldhandler; +public: + template + WithViolatedAlignmentHandler(H &&newhandler) : + oldhandler(Dune::violatedAlignmentHandler()) + { + Dune::violatedAlignmentHandler() = std::forward(newhandler); + } + + WithViolatedAlignmentHandler(const WithViolatedAlignmentHandler &) = delete; + WithViolatedAlignmentHandler(WithViolatedAlignmentHandler &&) = delete; + + WithViolatedAlignmentHandler& + operator=(const WithViolatedAlignmentHandler &) = delete; + WithViolatedAlignmentHandler& + operator=(WithViolatedAlignmentHandler &&) = delete; + + ~WithViolatedAlignmentHandler() + { + Dune::violatedAlignmentHandler() = oldhandler; + } +}; + +// intentionally violate alignment and check that that is detected +template +void checkAlignmentViolation(Dune::TestSuite &test) +{ + bool misalignmentDetected = false; + WithViolatedAlignmentHandler + guard([&](auto&&...){ misalignmentDetected = true; }); + + char buffer[alignof(T)+sizeof(T)]; + + void* misalignedAddr; + { + // a more portable way to ddo this would be to use std::align(), but that + // isn't supported by g++-4.9 yet + auto addr = std::uintptr_t( (void*)buffer ); + addr += alignof(T) - 1; + addr &= -std::uintptr_t(alignof(T)); + addr += 1; + misalignedAddr = (void*)addr; + } + + auto ptr = new(misalignedAddr) T; + test.check(misalignmentDetected, "default construct") + << "misalignment not detected for " << Dune::className(); + + misalignmentDetected = false; + + ptr->~T(); + test.check(misalignmentDetected, "destruct") + << "misalignment not detected for " << Dune::className(); + + misalignmentDetected = false; + + ptr = new(misalignedAddr) T(T(0)); + test.check(misalignmentDetected, "move construct") + << "misalignment not detected for " << Dune::className(); + ptr->~T(); // ignore any misalignment here + + misalignmentDetected = false; + + T t(0); + ptr = new(misalignedAddr) T(t); + test.check(misalignmentDetected, "copy construct") + << "misalignment not detected for " << Dune::className(); + ptr->~T(); // ignore any misalignment here +} + +int main(int argc, char **argv) +{ + Dune::MPIHelper::instance(argc, argv); + + Dune::ArithmeticTestSuite test; + + using ArithmeticTypes = std::tuple< + bool, + char, signed char, unsigned char, + short, unsigned short, + int, unsigned, + long, long unsigned, + long long, long long unsigned, + wchar_t, char16_t, char32_t, + float, double, long double>; + + Dune::Hybrid::forEach(ArithmeticTypes(), [&](auto val) { + using T = decltype(val); + using Aligned = Dune::AlignedNumber; + test.checkArithmetic(); + + checkAlignmentViolation(test); + }); + + return test.exit(); +} diff --git a/dune/common/test/densematrixassignmenttest.cc b/dune/common/test/densematrixassignmenttest.cc new file mode 100644 index 0000000..66170d2 --- /dev/null +++ b/dune/common/test/densematrixassignmenttest.cc @@ -0,0 +1,390 @@ +#include "config.h" + +#define DUNE_CHECK_BOUNDS + +#include + +#include +#include +#include +#include +#include + +template +void populateMatrix(M &A, int rows, int cols) { + for (int i = 0; i < rows; ++i) + for (int j = 0; j < cols; ++j) + A[i][j] = i + 10 * j; +} + + +template< class K, int rows, int cols > +struct Foo +{ + constexpr static int M () noexcept { return cols; } + constexpr static int N () noexcept { return rows; } + + operator Dune::FieldMatrix< K, rows, cols > () const + { + Dune::FieldMatrix< K, rows, cols > A; + populateMatrix( A, rows, cols ); + return A; + } +}; + +struct Bar {}; + +template +bool identicalContents(A const &a, B const &b) { + typedef typename A::size_type Size; + + if (a.N() != b.N() or a.M() != b.M()) + return false; + + for (Size i = 0; i < a.N(); ++i) + for (Size j = 0; j < b.N(); ++j) + if (a[i][j] != b[i][j]) + return false; + return true; +} + +template +bool run() { + ft const constant = 47.11; + std::cout << "Testing with type: " << Dune::className(constant) << std::endl; + + Dune::FieldMatrix fieldM; + Dune::FieldMatrix fieldMWrong11; + Dune::FieldMatrix fieldMWrong22; + Dune::FieldMatrix fieldMWrong33; + populateMatrix(fieldM, 2, 3); + populateMatrix(fieldMWrong11, 1, 1); + populateMatrix(fieldMWrong22, 2, 2); + populateMatrix(fieldMWrong33, 3, 3); + + Foo< ft, 2, 3 > fooM; + fieldM = static_cast< Dune::FieldMatrix< ft, 2, 3 > >( fooM ); + + Dune::DynamicMatrix dynM(2, 3); + Dune::DynamicMatrix dynMWrong11(1, 1); + Dune::DynamicMatrix dynMWrong22(2, 2); + Dune::DynamicMatrix dynMWrong33(3, 3); + populateMatrix(dynM, 2, 3); + populateMatrix(dynMWrong11, 1, 1); + populateMatrix(dynMWrong22, 2, 2); + populateMatrix(dynMWrong33, 3, 3); + + Dune::DiagonalMatrix const diagMWrong1 = {1}; + Dune::DiagonalMatrix const diagMWrong2 = {1, 2}; + Dune::DiagonalMatrix const diagMWrong3 = {1, 2, 3}; + + bool passed = true; + + static_assert(!Dune::HasDenseMatrixAssigner< Dune::FieldMatrix, std::vector< Dune::FieldMatrix > >::value, + "FieldMatrix is not assignable by a std::vector< FieldMatrix >!"); + static_assert(!Dune::HasDenseMatrixAssigner< Dune::FieldMatrix, Bar >::value, + "FieldMatrix is not assignable by a Bar!"); + static_assert(Dune::HasDenseMatrixAssigner< Dune::FieldMatrix, Dune::FieldMatrix >::value, + "FieldMatrix is assignable by FieldMatrix!"); + static_assert(Dune::HasDenseMatrixAssigner< Dune::FieldMatrix, Dune::DynamicMatrix >::value, + "FieldMatrix is assignable by a DynamicMatrix!"); + + // class: FieldMatrix + { + using M = Dune::FieldMatrix; + + // Assignment + { + M fieldT; + fieldT = fieldM; + if (!identicalContents(fieldT, fieldM)) { + std::cout << "FAIL: Content mismatch on line: " << __LINE__ + << std::endl; + passed = false; + } + } + { + M fieldT; + fieldT = dynM; + if (!identicalContents(fieldT, dynM)) { + std::cout << "FAIL: Content mismatch on line: " << __LINE__ + << std::endl; + passed = false; + } + } + { + M fieldT; + fieldT = constant; + } + + // Copy construction + { + M const fieldT = fieldM; + if (!identicalContents(fieldT, fieldM)) { + std::cout << "FAIL: Content mismatch on line: " << __LINE__ + << std::endl; + passed = false; + } + } + { + M const fieldT = dynM; + if (!identicalContents(fieldT, dynM)) { + std::cout << "FAIL: Content mismatch on line: " << __LINE__ + << std::endl; + passed = false; + } + } + { + [[maybe_unused]] M const fieldT = constant; + } + } + + // class: DynamicMatrix + { + using M = Dune::DynamicMatrix; + + // Assignment + { + M dynT; + dynT = fieldM; + if (!identicalContents(dynT, fieldM)) { + std::cout << "FAIL: Content mismatch on line: " << __LINE__ + << std::endl; + passed = false; + } + dynT = fieldMWrong11; + if (!identicalContents(dynT, fieldMWrong11)) { + std::cout << "FAIL: Content mismatch on line: " << __LINE__ + << std::endl; + passed = false; + } + } + { + M dynT; + dynT = dynM; + if (!identicalContents(dynT, dynM)) { + std::cout << "FAIL: Content mismatch on line: " << __LINE__ + << std::endl; + passed = false; + } + } + { + M dynT; + dynT = constant; + } + + // Copy construction + { + M const dynT = fieldM; + if (!identicalContents(dynT, fieldM)) { + std::cout << "FAIL: Content mismatch on line: " << __LINE__ + << std::endl; + passed = false; + } + } + { + M const dynT = dynM; + if (!identicalContents(dynT, dynM)) { + std::cout << "FAIL: Content mismatch on line: " << __LINE__ + << std::endl; + passed = false; + } + } + } + + // Assignment from other classes + { + using M = Dune::FieldMatrix; + Dune::DiagonalMatrix diagM({1, 2, 3}); + { [[maybe_unused]] M const fieldT = diagM; } + { + M fieldT; + fieldT = diagM; + } + } + { + using M = Dune::DynamicMatrix; + Dune::DiagonalMatrix diagM({1, 2, 3}); + { [[maybe_unused]] M const dynT = diagM; } + { + M dynT; + dynT = diagM; + } + } + + // Invalid assignments + { + using M = Dune::FieldMatrix; +#ifdef FAILURE0 + { + // Should fail at compile-time + M fieldT; + fieldT = fieldMWrong11; + } +#endif +#ifdef FAILURE1 + { + // Should fail at compile-time + M fieldT; + fieldT = fieldMWrong22; + } +#endif +#ifdef FAILURE2 + { + // Should fail at compile-time + M fieldT; + fieldT = fieldMWrong33; + } +#endif + try { + // Should fail at run-time with RangeError + M fieldT; + fieldT = dynMWrong11; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + try { + // Should fail at run-time with RangeError + M fieldT; + fieldT = dynMWrong22; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + try { + // Should fail at run-time with RangeError + M fieldT; + fieldT = dynMWrong33; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + + try { + // Should fail at run-time with RangeError + // Note: this could be made to fail at compile-time already if + // we further specialised DenseMatrixAssigner to (FieldMatrix, + // DiagonalMatrix) + M fieldT; + fieldT = diagMWrong1; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + try { + // Should fail at run-time with RangeError + // Note: this could be made to fail at compile-time already if + // we further specialised DenseMatrixAssigner to (FieldMatrix, + // DiagonalMatrix) + M fieldT; + fieldT = diagMWrong2; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + try { + // Should fail at run-time with RangeError + // Note: this could be made to fail at compile-time already if + // we further specialised DenseMatrixAssigner to (FieldMatrix, + // DiagonalMatrix) + M fieldT; + fieldT = diagMWrong3; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + } + + // Invalid copy construction + { + using M = Dune::FieldMatrix; +#ifdef FAILURE3 + { + // Should fail at compile-time + [[maybe_unused]] M const fieldT = fieldMWrong11; + } +#endif +#ifdef FAILURE4 + { + // Should fail at compile-time + [[maybe_unused]] M const fieldT = fieldMWrong22; + } +#endif +#ifdef FAILURE5 + { + // Should fail at compile-time + [[maybe_unused]] M const fieldT = fieldMWrong33; + } +#endif + try { + // Should fail at run-time with RangeError + [[maybe_unused]] M const fieldT = dynMWrong11; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + try { + // Should fail at run-time with RangeError + [[maybe_unused]] M const fieldT = dynMWrong22; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + try { + // Should fail at run-time with RangeError + [[maybe_unused]] M const fieldT = dynMWrong33; + std::cout << "(line " << __LINE__ << ") Error: No exception thrown." + << std::endl; + passed = false; + } catch (const Dune::RangeError&) { + std::cout << "(line " << __LINE__ + << ") All good: Exception thrown as expected." << std::endl; + } + } + { +#ifdef FAILURE6 + using M = Dune::DynamicMatrix; + { + // Should fail at compile-time + [[maybe_unused]] M const dynT = constant; + } +#endif + } + std::cout << std::endl; + return passed; +} + +int main() { + bool passed = true; + passed = passed && run(); + passed = passed && run>(); +#ifdef HAVE_GMP + passed = passed && run>(); +#endif + return passed ? 0 : 1; +} diff --git a/dune/common/test/densevectorassignmenttest.cc b/dune/common/test/densevectorassignmenttest.cc new file mode 100644 index 0000000..a29aeb1 --- /dev/null +++ b/dune/common/test/densevectorassignmenttest.cc @@ -0,0 +1,49 @@ +#include + +#include +#include +#include +#include + +using namespace Dune; + +template +void assign(DenseVector& first, const DenseVector& second) +{ + first = second; +} + +bool run() +{ + bool passed = true; + FieldVector fvec1{1, 2, 3}; + DynamicVector dynvec1{1, 2, 3}; + FieldVector fvec2; + DynamicVector dynvec2(3); + // check mixed assignments + assign(fvec2, dynvec1); + assign(dynvec2, fvec1); + for (size_t i = 0; i < 3; ++i) { + if (fvec2[i] != dynvec1[i]) { + std::cerr << "Assigning a DynamicVector to a FieldVector as DenseVectors does not work!" + << std::endl << i << "-th entry after assignment is " << fvec2[i] << ", should be " + << i+1 << "!" << std::endl; + passed = false; + } + if (dynvec1[i] != dynvec2[i]) { + std::cerr << "Assigning a FieldVector to a DynamicVector as DenseVectors does not work" + << std::endl << i << "-th entry after assignment is " << dynvec1[i] << ", should be " + << i+1 << "!" << std::endl; + passed = false; + } + } + return passed; +} + +int main() +{ + bool passed = run(); + if (!passed) + DUNE_THROW(Dune::Exception, "Test failed"); + return !passed; +} diff --git a/dune/common/test/densevectortest.cc b/dune/common/test/densevectortest.cc new file mode 100644 index 0000000..c19c2b3 --- /dev/null +++ b/dune/common/test/densevectortest.cc @@ -0,0 +1,58 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include + +class MyVector; + +namespace Dune +{ + template< > + struct DenseMatVecTraits< MyVector > + { + typedef MyVector derived_type; + typedef double value_type; + typedef unsigned int size_type; + }; +} + +class MyVector : public Dune::DenseVector< MyVector > +{ +public: + MyVector ( unsigned int size, double v = 0 ) + : data_( size, v ) {} + + unsigned int size () const { return data_.size(); } + + double& operator[] ( unsigned int i ) { return data_[ i ]; } + const double& operator[] ( unsigned int i ) const { return data_[ i ]; } +protected: + std::vector< double > data_; +}; + + +int main() +{ + try + { + unsigned int n = 15; + MyVector v( n, 1 ); + if( ( v.end() - v.begin() ) < 0 ) + DUNE_THROW(Dune::Exception, "Negative value reported for end() - begin()" ); + + return 0; + } catch (Dune::Exception& e) { + std::cerr << e << std::endl; + return 1; + } catch (...) { + std::cerr << "Generic exception!" << std::endl; + return 2; + } +} diff --git a/dune/common/test/diagonalmatrixtest.cc b/dune/common/test/diagonalmatrixtest.cc new file mode 100644 index 0000000..cf88a59 --- /dev/null +++ b/dune/common/test/diagonalmatrixtest.cc @@ -0,0 +1,99 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef DUNE_FMatrix_WITH_CHECKING +#define DUNE_FMatrix_WITH_CHECKING +#endif + +#include +#include + +#include +#include +#include + +#include "checkmatrixinterface.hh" + +using namespace Dune; + +template +void test_matrix() +{ + [[maybe_unused]] typedef typename DiagonalMatrix::size_type size_type; + + DiagonalMatrix A(1); + FieldVector f; + FieldVector v; + + // test constexpr size + static_assert(A.N() == n, ""); + static_assert(A.M() == n, ""); + + // assign matrix + A=2; + + // assign vector + f = 1; + v = 2; + + // matrix vector product + A.umv(v,f); + + + // test norms + A.frobenius_norm(); + A.frobenius_norm2(); + A.infinity_norm(); + A.infinity_norm_real(); + + std::sort(v.begin(), v.end()); + + // print matrix + std::cout << A << std::endl; + // print vector + std::cout << f << std::endl; + + // assign to FieldMatrix + [[maybe_unused]] FieldMatrix AFM = FieldMatrix(A); + [[maybe_unused]] FieldMatrix AFM2 = A; + [[maybe_unused]] FieldMatrix AFM3; + AFM3 = A; +} + +template +void test_interface() +{ + typedef CheckMatrixInterface::UseFieldVector Traits; + typedef Dune::DiagonalMatrix DiagonalMatrix; + + const DiagonalMatrix A(1); + checkMatrixInterface< DiagonalMatrix >( A ); + checkMatrixInterface< DiagonalMatrix, Traits >( A ); +} + +void test_initialisation() +{ + [[maybe_unused]] Dune::DiagonalMatrix const b = { 1, 2 }; + + assert(b.diagonal(0) == 1); + assert(b.diagonal(1) == 2); +} + +int main() +{ + try { + test_matrix(); + test_interface(); + test_matrix(); + test_interface(); + test_matrix(); + test_interface(); + } + catch (Dune::Exception & e) + { + std::cerr << "Exception: " << e << std::endl; + } +} diff --git a/dune/common/test/dummyiterator.hh b/dune/common/test/dummyiterator.hh new file mode 100644 index 0000000..bb08dfe --- /dev/null +++ b/dune/common/test/dummyiterator.hh @@ -0,0 +1,44 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_DUMMYITERATOR_HH +#define DUNE_COMMON_DUMMYITERATOR_HH + +#include +#include + +#include + +template +class dummyiterator + : public Dune::BidirectionalIteratorFacade, T, T&, + std::ptrdiff_t> +{ + friend class dummyiterator::type>; + + T *value; + +public: + dummyiterator(T& value_) + : value(&value_) + {} + + template + dummyiterator + ( const dummyiterator& o, + typename std::enable_if::value>::type* = 0) + : value(o.value) + {} + + T& derefence() const { + return *value; + } + + bool equals(const dummyiterator& o) const { + return value == o.value; + } + + void increment() {} + void decrement() {} +}; + +#endif // DUNE_COMMON_DUMMYITERATOR_HH diff --git a/dune/common/test/dynmatrixtest.cc b/dune/common/test/dynmatrixtest.cc new file mode 100644 index 0000000..62fb280 --- /dev/null +++ b/dune/common/test/dynmatrixtest.cc @@ -0,0 +1,410 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +// Activate checking +#ifndef DUNE_FMatrix_WITH_CHECKING +#define DUNE_FMatrix_WITH_CHECKING +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include "checkmatrixinterface.hh" + +using namespace Dune; + +template +int test_invert_solve(Dune::DynamicMatrix &A, + Dune::DynamicMatrix &inv, + Dune::FieldVector &x, + Dune::FieldVector &b) +{ + int ret=0; + + std::cout <<"Checking inversion of:"< calced_inv(n,n); + FieldVector calced_x; + + std::cout< prod = A; + prod.rightmultiply(inv); + for (size_t i=0; i 1e-6) { + std::cerr<<"Given inverse wrong"< copy(A); + A.invert(); + + calced_inv = A; + A-=inv; + + + auto epsilon = std::numeric_limits::real_type>::epsilon(); + auto tolerance = 10*epsilon; + for(size_t i =0; i < n; ++i) + for(size_t j=0; j tolerance) { + std::cerr<<"calculated inverse wrong at ("< xcopy(calced_x); + xcopy-=x; + + equal=true; + + for(size_t i =0; i < n; ++i) + if(std::abs(xcopy[i])>tolerance) { + std::cerr<<"calculated isolution wrong at ("<; + using FV = Dune::FieldVector; + + DM A = {{1, 5, 7}, {2, 14, 15}, {4, 40, 39}}; + DM inv = {{-9.0 / 4, 85.0 / 24, -23.0 / 24}, + {-3.0 / 4, 11.0 / 24, -1.0 / 24}, + {1, -5.0 / 6, 1.0 / 6}}; + FV b = {32, 75, 201}; + FV x = {1, 2, 3}; + + ret += test_invert_solve(A, inv, x, b); + + DM A0 = {{-0.5, 0, -0.25}, {0.5, 0, -0.25}, {0, 0.5, 0}}; + DM inv0 = {{-1, 1, 0}, {0, 0, 2}, {-2, -2, 0}}; + FV b0 = {32, 75, 201}; + FV x0 = {43, 402, -214}; + + ret += test_invert_solve(A0, inv0, x0, b0); + + DM A1 = {{0, 1, 0}, {1, 0, 0}, {0, 0, 1}}; + FV b1 = {0, 1, 2}; + FV x1 = {1, 0, 2}; + + ret += test_invert_solve(A1, A1, x1, b1); + + DM A2 = {{3, 1, 6}, {2, 1, 3}, {1, 1, 1}}; + DM inv2 = {{-2, 5, -3}, {1, -3, 3}, {1, -2, 1}}; + FV b2 = {2, 7, 4}; + FV x2 = {19, -7, -8}; + + return ret + test_invert_solve(A2, inv2, x2, b2); +} + +template +void test_mult(DynamicMatrix& A, + X& v, Y& f) +{ + // test the various matrix-vector products + A.mv(v,f); + A.mtv(f,v); + A.umv(v,f); + A.umtv(f,v); + A.umhv(f,v); + A.mmv(v,f); + A.mmtv(f,v); + A.mmhv(f,v); + A.usmv((K)0.5,v,f); + A.usmtv((K)0.5,f,v); + A.usmhv((K)0.5,f,v); +} + + +template +void test_matrix() +{ + typedef typename DynamicMatrix::size_type size_type; + + DynamicMatrix A(n,m); + DynamicVector f(n); + DynamicVector v(m); + + // assign matrix + A=K(); + // random access matrix + for (size_type i=0; i::RowIterator rit = A.begin(); + for (; rit!=A.end(); ++rit) + { + rit.index(); + typename DynamicMatrix::ColIterator cit = rit->begin(); + for (; cit!=rit->end(); ++cit) + { + cit.index(); + (*cit) *= 2; + } + } + + // assign vector + f = 1; + + // random access vector + for (size_type i=0; i::iterator it = v.begin(); + typename DynamicVector::ConstIterator end = v.end(); + for (; it!=end; ++it) + { + it.index(); + (*it) *= 2; + } + // reverse iterator vector + it = v.beforeEnd(); + end = v.beforeBegin(); + for (; it!=end; --it) + (*it) /= 2; + // find vector + for (size_type i=0; i res2(n,0); + DynamicVector res1(n); + + DynamicVector b(m,1); + + A.mv(b, res1); + A.umv(b, res2); + + if( (res1 - res2).two_norm() > 1e-12 ) + { + DUNE_THROW(FMatrixError,"mv and umv are not doing the same!"); + } + } + + { + FieldVector v0; + for (size_t i=0; i v0 ( v ); + test_mult(A, v0, f ); + } + + // { + // std::vector v1( m ) ; + // std::vector f1( n, 1 ) ; + // // random access vector + // for (size_type i=0; i= 0 ); + assert( A.frobenius_norm2() >= 0 ); + assert( A.infinity_norm() >= 0 ); + assert( A.infinity_norm_real() >= 0); + + std::sort(v.begin(), v.end()); + + // print matrix + std::cout << A << std::endl; + // print vector + std::cout << f << std::endl; + + + { + DynamicMatrix A2 = A; + A2 *= 2; + + DynamicMatrix B = A; + B += A; + B -= A2; + if (std::abs(B.infinity_norm()) > 1e-12) + DUNE_THROW(FMatrixError,"Operator +=/-= test failed!"); + } + { + DynamicMatrix A3 = A; + A3 *= 3; + + DynamicMatrix B = A; + B.axpy( K( 2 ), B ); + B -= A3; + if (std::abs(B.infinity_norm()) > 1e-12) + DUNE_THROW(FMatrixError,"Axpy test failed!"); + } + { + DynamicMatrix A2(n,n+1); + for(size_type i=0; i& Aref = A2; + + + DynamicMatrix B(n+1,n+1); + for(size_type i=0; i& Bref = B; + + DynamicMatrix C(n,n); + for(size_type i=0; i& Cref = C; + +#if 0 + DynamicMatrix AB = Aref.rightmultiplyany(B); + for(size_type i=0; i(AB[i][j] - i*n*(n+1)/2) > 1e-10) + DUNE_THROW(FMatrixError,"Rightmultiplyany test failed!"); + + DynamicMatrix AB2 = A; + AB2.rightmultiply(B); + AB2 -= AB; + if (std::abs(AB2.infinity_norm() > 1e-10)) + DUNE_THROW(FMatrixError,"Rightmultiply test failed!"); + + DynamicMatrix AB3 = Bref.leftmultiplyany(A); + AB3 -= AB; + if (std::abs(AB3.infinity_norm() > 1e-10)) + DUNE_THROW(FMatrixError,"Leftmultiplyany test failed!"); + + DynamicMatrix CA = Aref.leftmultiplyany(C); + for(size_type i=0; i(CA[i][j] - i*n*(n-1)/2) > 1e-10) + DUNE_THROW(FMatrixError,"Leftmultiplyany test failed!"); + + DynamicMatrix CA2 = A; + CA2.leftmultiply(C); + CA2 -= CA; + if (std::abs(CA2.infinity_norm() > 1e-10)) + DUNE_THROW(FMatrixError,"Leftmultiply test failed!"); + + DynamicMatrix CA3 = Cref.rightmultiplyany(A); + CA3 -= CA; + if (std::abs(CA3.infinity_norm() > 1e-10)) + DUNE_THROW(FMatrixError,"Rightmultiplyany test failed!"); +#endif + } +} + +int test_determinant() +{ + int ret = 0; + + DynamicMatrix B(4,4); + B[0][0] = 3.0; B[0][1] = 0.0; B[0][2] = 1.0; B[0][3] = 0.0; + B[1][0] = -1.0; B[1][1] = 3.0; B[1][2] = 0.0; B[1][3] = 0.0; + B[2][0] = -3.0; B[2][1] = 0.0; B[2][2] = -1.0; B[2][3] = 2.0; + B[3][0] = 0.0; B[3][1] = -1.0; B[3][2] = 0.0; B[3][3] = 1.0; + if (std::abs(B.determinant() + 2.0) > 1e-12) + { + std::cerr << "Determinant 1 test failed" << std::endl; + ++ret; + } + + B[0][0] = 3.0; B[0][1] = 0.0; B[0][2] = 1.0; B[0][3] = 0.0; + B[1][0] = -1.0; B[1][1] = 3.0; B[1][2] = 0.0; B[1][3] = 0.0; + B[2][0] = -3.0; B[2][1] = 0.0; B[2][2] = -1.0; B[2][3] = 2.0; + B[3][0] = -1.0; B[3][1] = 3.0; B[3][2] = 0.0; B[3][3] = 2.0; + if (B.determinant() != 0.0) + { + std::cerr << "Determinant 2 test failed" << std::endl; + ++ret; + } + + return 0; +} + +int main() +{ + try { + Dune::DynamicMatrix A( 5, 5 ); + checkMatrixInterface( A ); + + test_matrix(); + test_matrix(); + test_matrix(); + test_matrix(); + test_determinant(); + Dune::DynamicMatrix B(34, 34, 1e-15); + for (int i=0; i<34; i++) B[i][i] = 1; + B.invert(); + return test_invert_solve(); + } + catch (Dune::Exception & e) + { + std::cerr << "Exception: " << e << std::endl; + } +} diff --git a/dune/common/test/dynvectortest.cc b/dune/common/test/dynvectortest.cc new file mode 100644 index 0000000..89a15af --- /dev/null +++ b/dune/common/test/dynvectortest.cc @@ -0,0 +1,80 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +using Dune::DynamicVector; + +template +void dynamicVectorTest(int d) { + ct a = 1; + DynamicVector v(d,1); + DynamicVector w(d,2); + DynamicVector z(d,2); + [[maybe_unused]] bool b; + + // Test whether the norm methods compile + (w+v).two_norm(); + (w+v).two_norm2(); + (w+v).one_norm(); + (w+v).one_norm_real(); + (w+v).infinity_norm(); + (w+v).infinity_norm_real(); + + // test op(vec,vec) + z = v + w; + z = v - w; + DynamicVector z2 = v + w; + w -= v; + w += v; + + // test op(vec,scalar) + w +=a; + w -= a; + w *= a; + w /= a; + + // test scalar product, axpy + a = v * w; + z = v.axpy(a,w); + + // test comparison + b = (w != v); + b = (w == v); + + + // test istream operator + std::stringstream s; + for (int i=0; i> w; + assert(v == w); + +} + +int main() +{ + try { + for (int d=1; d<6; d++) + { + dynamicVectorTest(d); + dynamicVectorTest(d); + dynamicVectorTest(d); + } + } catch (Dune::Exception& e) { + std::cerr << e << std::endl; + return 1; + } catch (...) { + std::cerr << "Generic exception!" << std::endl; + return 2; + } +} diff --git a/dune/common/test/eigenvaluestest.cc b/dune/common/test/eigenvaluestest.cc new file mode 100644 index 0000000..45b5b3f --- /dev/null +++ b/dune/common/test/eigenvaluestest.cc @@ -0,0 +1,357 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace Dune; + +#if HAVE_LAPACK +/** \brief Test the eigenvalue code with the Rosser test matrix + + This matrix was a challenge for many matrix eigenvalue + algorithms. But the Francis QR algorithm, as perfected by + Wilkinson and implemented in EISPACK, has no trouble with it. The + matrix is 8-by-8 with integer elements. It has: + + * A double eigenvalue + * Three nearly equal eigenvalues + * Dominant eigenvalues of opposite sign + * A zero eigenvalue + * A small, nonzero eigenvalue + +*/ +template +void testRosserMatrix() +{ + DynamicMatrix A = { + { 611, 196, -192, 407, -8, -52, -49, 29 }, + { 196, 899, 113, -192, -71, -43, -8, -44 }, + { -192, 113, 899, 196, 61, 49, 8, 52 }, + { 407, -192, 196, 611, 8, 44, 59, -23 }, + { -8, -71, 61, 8, 411, -599, 208, 208 }, + { -52, -43, 49, 44, -599, 411, 208, 208 }, + { -49, -8, 8, 59, 208, 208, 99, -911 }, + { 29, -44, 52, -23, 208, 208, -911, 99} + }; + + // compute eigenvalues + DynamicVector > eigenComplex; + DynamicMatrixHelp::eigenValuesNonSym(A, eigenComplex); + + // test results + /* + reference solution computed with octave 3.2 + + > format long e + > eig(rosser()) + + */ + std::vector reference = { + -1.02004901843000e+03, + -4.14362871168386e-14, + 9.80486407214362e-02, + 1.00000000000000e+03, + 1.00000000000000e+03, + 1.01990195135928e+03, + 1.02000000000000e+03, + 1.02004901843000e+03 + }; + + std::vector eigenRealParts(8); + for (int i=0; i<8; i++) + eigenRealParts[i] = std::real(eigenComplex[i]); + + std::sort(eigenRealParts.begin(), eigenRealParts.end()); + + for (int i=0; i<8; i++) + { + if (std::fabs(std::imag(eigenComplex[i])) > 1e-10) + DUNE_THROW(MathError, "Symmetric matrix has complex eigenvalue"); + + if( std::fabs(reference[i] - eigenRealParts[i]) > 1e-10 ) + DUNE_THROW(MathError,"error computing eigenvalues of Rosser-matrix"); + } + + std::cout << "Eigenvalues of Rosser matrix: " << eigenComplex << std::endl; +} +#endif // HAVE_LAPACK + +template +void testSymmetricFieldMatrix() +{ + int numberOfTestMatrices = 10; + + for (int i=0; i testMatrix; + for (int j=0; j eigenValues; + FieldMatrix eigenVectors; + FMatrixHelp::eigenValuesVectors(testMatrix, eigenValues, eigenVectors); + + // Make sure the compute numbers really are the eigenvalues + /*for (int j=0; j copy = testMatrix; + for (int k=0; k 1e-8) + DUNE_THROW(MathError, "Value computed by FMatrixHelp::eigenValues is not an eigenvalue, Determinant: "+std::to_string(std::fabs(copy.determinant()))); + }*/ + + // Make sure eigenvalues and eigenvectors are not NaN (the subsequent tests do not find this!) + for (int j=0; j eigenValues[j+1] + 1e-10) + DUNE_THROW(MathError, "Values computed by FMatrixHelp::eigenValues are not in ascending order"); + + // Make sure the vectors really are eigenvectors for the computed eigenvalues + for (int j=0; j Av; + testMatrix.mv(eigenVectors[j], Av); + if((Av - eigenValues[j]*eigenVectors[j]).two_norm() > dim*std::sqrt(std::numeric_limits::epsilon())) + DUNE_THROW(MathError, "Vector computed by FMatrixHelp::eigenValuesVectors is not an eigenvector"); + } + + // Make sure the eigenvectors have unit length + for(auto& ev : eigenVectors) { + constexpr double tol = std::max(std::numeric_limits::epsilon(), + std::numeric_limits::epsilon()); + if(std::abs(ev.two_norm())-1 > dim*tol) + DUNE_THROW(MathError, "Vector computed by FMatrixHelp::eigenValuesVectors does not have unit length"); + } + + } +} + +template +void compareEigenvectorSets(FieldMatrix evec, + FieldVector refEval, + FieldMatrix refEvec) +{ + field_type th = dim*std::sqrt(std::numeric_limits::epsilon()); + + std::size_t i=0; + std::size_t shift; + std::list> refEvecList; + field_type currentEval; + + while(i +void checkMatrixWithReference(FieldMatrix matrix, + FieldMatrix refEvec, + FieldVector refEval) +{ + //normalize reference + for(auto& ev : refEvec) + ev /= ev.two_norm(); + + field_type th = dim*std::sqrt(std::numeric_limits::epsilon()); + + FieldMatrix eigenvectors; + FieldVector eigenvalues; + + FMatrixHelp::eigenValuesVectors(matrix, eigenvalues, eigenvectors); + + if((eigenvalues-refEval).two_norm() > th) + DUNE_THROW(MathError, "Eigenvalues [" << eigenvalues << "] computed by FMatrixHelp::eigenValuesVectors do not match the reference solution [" << refEval << "]"); + try { + compareEigenvectorSets(eigenvectors, refEval, refEvec); + } + catch(Dune::MathError& e) { + std::cerr << "Computations by `FMatrixHelp::eigenValuesVectors`: " << e.what() << std::endl; + } +} + +template +void checkMatrixWithLAPACK(FieldMatrix matrix) +{ + field_type th = dim*std::sqrt(std::numeric_limits::epsilon()); + + FieldMatrix eigenvectors, refEvec; + FieldVector eigenvalues, refEval; + + FMatrixHelp::eigenValuesVectors(matrix, eigenvalues, eigenvectors); + FMatrixHelp::eigenValuesVectorsLapack(matrix, refEval, refEvec); + + if((eigenvalues-refEval).two_norm() > th) + DUNE_THROW(MathError, "Eigenvalues [" << eigenvalues << "] computed by FMatrixHelp::eigenValuesVectorsLapack do not match the reference solution [" << refEval << "]"); + try { + compareEigenvectorSets(eigenvectors, refEval, refEvec); + } + catch(Dune::MathError& e) { + std::cerr << "Computations by `FMatrixHelp::eigenValuesVectorsLapack`: " << e.what() << std::endl; + } +} + +template +void checkMultiplicity() +{ + //--2d-- + //repeated eigenvalue (x2) + checkMatrixWithReference({{1, 0},{0, 1}}, {{1,0}, {0,1}}, {1, 1}); + + //eigenvalues with same magnitude (x2) + checkMatrixWithReference({{0, 1}, {1, 0}}, {{1,-1}, {1,1}}, {-1, 1}); + + // singular matrix + checkMatrixWithReference({{1, 0},{0, 0}}, {{0,1}, {1,0}}, {0, 1}); + + // another singular matrix (triggers a different code path) + checkMatrixWithReference({{0, 0},{0, 1}}, {{1,0}, {0,1}}, {0, 1}); + + // Seemingly simple diagonal matrix -- triggers unstable detection of zero columns + checkMatrixWithReference({{1.01, 0},{0, 1}}, {{0,1}, {1,0}}, {1, 1.01}); + + // check 2x2 zero matrix + checkMatrixWithReference({{ 0, 0}, + { 0, 0}}, + {{1,0}, {0,1}}, + {0, 0}); + + //--3d-- + //repeated eigenvalue (x3) + checkMatrixWithReference({{ 1, 0, 0}, + { 0, 1, 0}, + { 0, 0, 1}}, + {{1,0,0}, {0,1,0}, {0,0,1}}, + {1, 1, 1}); + + //eigenvalues with same magnitude (x2) + checkMatrixWithReference({{ 0, 1, 0}, + { 1, 0, 0}, + { 0, 0, 5}}, + {{-1,1,0}, {1,1,0}, {0,0,1}}, + {-1, 1, 5}); + + //repeated eigenvalue (x2) + checkMatrixWithReference({{ 3, -2, 0}, + { -2, 3, 0}, + { 0, 0, 5}}, + {{1,1,0}, {0,0,1}, {1,-1,0}}, + {1, 5, 5}); + + // singular non-diagonal matrix + checkMatrixWithReference({{ 0, 0, 0}, + { 0, 1, 1}, + { 0, 1, 1}}, + {{1,0,0}, {0,FT(-1.0/std::sqrt(2.0)),FT(1.0/std::sqrt(2.0))}, {0,FT(1.0/std::sqrt(2.0)),FT(1.0/std::sqrt(2.0))}}, + {0, 0, 2}); + + // singular diagonal matrix (that's a different code path again) + checkMatrixWithReference({{ 0, 0, 0}, + { 0, 1, 0}, + { 0, 0, 0}}, + {{1,0,0}, {0,0,1}, {0,1,0}}, + {0, 0, 1}); + + // diagonal matrix whose largest eigenvalue is not 1 + // this tests the matrix scaling employed by the eigenvector code. + checkMatrixWithReference({{ 3, 0, 0}, + { 0, 2, 0}, + { 0, 0, 4}}, + {{0,1,0}, {1,0,0}, {0,0,1}}, + {2, 3, 4}); + + // check 3x3 zero matrix + checkMatrixWithReference({{ 0, 0, 0}, + { 0, 0, 0}, + { 0, 0, 0}}, + {{1,0,0}, {0,1,0}, {0,0,1}}, + {0, 0, 0}); + + //repeat tests with LAPACK (if found) +#if HAVE_LAPACK + checkMatrixWithLAPACK({{1, 0}, {0, 1}}); + checkMatrixWithLAPACK({{0, 1}, {1, 0}}); + checkMatrixWithLAPACK({{1,0,0}, {0,1,0}, {0,0,1}}); + checkMatrixWithLAPACK({{0,1,0}, {1,0,0}, {0,0,5}}); + checkMatrixWithLAPACK({{3,-2,0}, {-2,3,0}, {0,0,5}}); +#endif + +} + +int main() +{ +#if HAVE_LAPACK + testRosserMatrix(); + testRosserMatrix(); + testRosserMatrix(); +#else + std::cout << "WARNING: eigenvaluetest needs LAPACK, test disabled" << std::endl; +#endif // HAVE_LAPACK + + //we basically just test LAPACK here, so maybe discard those tests +#if HAVE_LAPACK + testSymmetricFieldMatrix(); + testSymmetricFieldMatrix(); + testSymmetricFieldMatrix(); + testSymmetricFieldMatrix(); + testSymmetricFieldMatrix(); + testSymmetricFieldMatrix(); +#endif // HAVE_LAPACK + + testSymmetricFieldMatrix(); + testSymmetricFieldMatrix(); + testSymmetricFieldMatrix(); + testSymmetricFieldMatrix(); + testSymmetricFieldMatrix(); + testSymmetricFieldMatrix(); + + checkMultiplicity(); + checkMultiplicity(); + checkMultiplicity(); + + return 0; +} diff --git a/dune/common/test/enumsettest.cc b/dune/common/test/enumsettest.cc new file mode 100644 index 0000000..6c57a41 --- /dev/null +++ b/dune/common/test/enumsettest.cc @@ -0,0 +1,16 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +int main() +{ + using namespace Dune; + std::cout<,EnumItem,int>::contains(1)<< + " "<,EnumItem,int>::contains(2)<< + " "<,EnumItem,int>,EnumItem,int>::contains(3)<< + " "<::contains(3)< +#include +#include + +#include + +int main() { + + int status = 0; + + auto test1 = Dune::filledArray<2>(2.0); + static_assert(std::is_same >::value, + "Wrong result type for Dune::filledArray()"); + + if(test1[0] != 2.0 || test1[1] != 2.0) + { + std::cerr << "Dune::filledArray() produces wrong value" << std::endl; + status = 1; + } + +#ifdef __cpp_lib_array_constexpr + std::cout << "The result of Dune::filledArray() is constexpr" << std::endl; + constexpr auto test2 = Dune::filledArray<2>(2); + (void)test2; +#else // !__cpp_lib_array_constexpr + std::cout << "Not checking whether Dune::filledArray() is constexpr\n" + << "since the library does not declare std::array as constexpr\n" + << "(__cpp_lib_array_constexpr is not defined)." << std::endl; +#endif // !__cpp_lib_array_constexpr + + return status; +} diff --git a/dune/common/test/fmatrixtest.cc b/dune/common/test/fmatrixtest.cc new file mode 100644 index 0000000..b2b343c --- /dev/null +++ b/dune/common/test/fmatrixtest.cc @@ -0,0 +1,950 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// Activate checking. +#ifndef DUNE_FMatrix_WITH_CHECKING +#define DUNE_FMatrix_WITH_CHECKING +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_VC +#include +#endif + +#include "checkmatrixinterface.hh" + +using namespace Dune; + +template +int test_invert_solve(Dune::FieldMatrix &A, + Dune::FieldMatrix &inv, + Dune::FieldVector &x, + Dune::FieldVector &b, + bool doPivoting = true) +{ + using std::abs; + + int ret=0; + + std::cout <<"Checking inversion of:"< calced_inv; + FieldVector calced_x; + + std::cout< prod = A; + prod.rightmultiply(inv); + for (size_t i=0; i 1e-6)) { + std::cerr<<"Given inverse wrong"< copy(A); + A.invert(doPivoting); + + calced_inv = A; + A-=inv; + + + auto epsilon = std::numeric_limits::real_type>::epsilon(); + auto tolerance = 10*epsilon; + for(size_t i =0; i < n; ++i) + for(size_t j=0; j tolerance)) { + std::cerr<<"calculated inverse wrong at ("< xcopy(calced_x); + xcopy-=x; + + equal=true; + + for(size_t i =0; i < n; ++i) + if(Simd::anyTrue(abs(xcopy[i])>tolerance)) { + std::cerr<<"calculated isolution wrong at ("<; + using FV = Dune::FieldVector; + + FM A_data = {{1, 5, 7}, {2, 14, 15}, {4, 40, 39}}; + FM inv_data = {{-9.0 / 4, 85.0 / 24, -23.0 / 24}, + {-3.0 / 4, 11.0 / 24, -1.0 / 24}, + {1, -5.0 / 6, 1.0 / 6}}; + FV b = {32, 75, 201}; + FV x = {1, 2, 3}; + ret += test_invert_solve(A_data, inv_data, x, b); + + FM A_data0 = {{-0.5, 0, -0.25}, {0.5, 0, -0.25}, {0, 0.5, 0}}; + FM inv_data0 = {{-1, 1, 0}, {0, 0, 2}, {-2, -2, 0}}; + FV b0 = {32, 75, 201}; + FV x0 = {43, 402, -214}; + ret += test_invert_solve(A_data0, inv_data0, x0, b0); + + FM A_data1 = {{0, 1, 0}, {1, 0, 0}, {0, 0, 1}}; + FV b1 = {0, 1, 2}; + FV x1 = {1, 0, 2}; + ret += test_invert_solve(A_data1, A_data1, x1, b1); + + FM A_data2 = {{3, 1, 6}, {2, 1, 3}, {1, 1, 1}}; + FM inv_data2 = {{-2, 5, -3}, {1, -3, 3}, {1, -2, 1}}; + FV b2 = {2, 7, 4}; + FV x2 = {19, -7, -8}; + ret += test_invert_solve(A_data2, inv_data2, x2, b2); + + using FM6 = Dune::FieldMatrix; + using FV6 = Dune::FieldVector; + using FM6f = Dune::FieldMatrix; + using FV6f = Dune::FieldVector; + using FM6c = Dune::FieldMatrix, 6, 6>; + using FV6c = Dune::FieldVector, 6>; + using FM6cf = Dune::FieldMatrix, 6, 6>; + using FV6cf = Dune::FieldVector, 6>; + FM6 A_data3 = {{0.1756212892262638, 0.18004482126181995, -0.49348712464381461, 0.49938830949606494, -0.7073160963417815, 1.0595994834402057e-06}, + {0.17562806606385517, 0.18005184462676252, -0.49354113600539418, 0.50059575375120657, 0.70689735319270453, -3.769499436967368e-07}, + {0.17562307226079987, 0.1800466692525447, -0.49350050991711036, -0.5000065175076156, 0.00018887507812282846, -0.70710715811504954}, + {0.17562308446070105, 0.18004668189625178, -0.49350060714612815, -0.50000869003275417, 0.00019031361405394119, 0.70710640425695015}, + {-0.0072214111281474463, 0.93288324029450198, -0.11009998093332186, -1.7482015044681947e-06, -2.35420746900079e-06, -4.2380607559371285e-09}, + {0.93625470097440933, -0.0077746247590777659, -0.11696151733678119, -1.8717676241478393e-06, -2.5225363177584535e-06, -4.5410877139483271e-09}}; + FM6 inv_data3 = {{-0.069956619842954, -0.069956322880040, -0.069956501823745, -0.069956501289142, 0.063349638850509, 1.121064161778902}, + {-0.066113473123754, -0.066113223084417, -0.066113362249636, -0.066113361799508, 1.123470950632021, 0.058271943290769}, + {-0.555587502096003, -0.555615651279932, -0.555585807267011, -0.555585857939820, 0.432422844944552, 0.420211281044740}, + { 0.499710573383257, 0.500274796075355, -0.500006831431901, -0.500007846623773, 0.000003909674199, 0.000003817686226}, + {-0.707554041861306, 0.706659150542343, 0.000405628342406, 0.000407065756770, 0.000010628642550, 0.000010383891450}, + { 0.000001450379141, 0.000000012708409, -0.707107586716496, 0.707105975654669, 0.000000019133995, 0.000000018693387}}; + FV6 b3 = {1, 1, 1, 1, 1, 1}; + FV6 x3 = {0.904587854793530, 0.917289473665475, -1.369740692593475, -0.000021581236636, -0.000061184685788, -0.000000110146895}; + FM6f A_data3f, inv_data3f; + FM6c A_data3c, inv_data3c; + FM6cf A_data3cf, inv_data3cf; + std::copy(A_data3.begin(), A_data3.end(), A_data3f.begin()); + std::copy(inv_data3.begin(), inv_data3.end(), inv_data3f.begin()); + std::copy(A_data3.begin(), A_data3.end(), A_data3c.begin()); + std::copy(inv_data3.begin(), inv_data3.end(), inv_data3c.begin()); + std::copy(A_data3.begin(), A_data3.end(), A_data3cf.begin()); + std::copy(inv_data3.begin(), inv_data3.end(), inv_data3cf.begin()); + FV6f b3f = b3; + FV6f x3f = x3; + FV6c b3c = b3; + FV6c x3c = x3; + FV6cf b3cf = b3; + FV6cf x3cf = x3; +#if HAVE_VC + using FM6vc = Dune::FieldMatrix< Vc::SimdArray, 6, 6>; + using FV6vc = Dune::FieldVector< Vc::SimdArray, 6>; + FM6vc A_data3vc, inv_data3vc; + std::copy(A_data3.begin(), A_data3.end(), A_data3vc.begin()); + std::copy(inv_data3.begin(), inv_data3.end(), inv_data3vc.begin()); + FV6vc b3vc = b3; + FV6vc x3vc = x3; + ret += test_invert_solve< Vc::SimdArray, 6>(A_data3vc, inv_data3vc, x3vc, b3vc); +#endif + ret += test_invert_solve(A_data3, inv_data3, x3, b3); + ret += test_invert_solve, 6>(A_data3c, inv_data3c, x3c, b3c); + ret += test_invert_solve, 6>(A_data3cf, inv_data3cf, x3cf, b3cf); + ret += test_invert_solve(A_data3f, inv_data3f, x3f, b3f); + + FM A_data4 = {{2, -1, 0}, {-1, 2, -1}, {0, -1, 2}}; + FM inv_data4 = {{0.75, 0.5, 0.25}, {0.5, 1, 0.5}, {0.25, 0.5, 0.75}}; + FV b4 = {1, 2, 3}; + FV x4 = {2.5, 4, 3.5}; + ret += test_invert_solve(A_data4, inv_data4, x4, b4, false); + return ret; +} + +template +void test_mult(FieldMatrix& A, + X& v, Y& f, XT& vT, YT& fT) +{ + // test the various matrix-vector products + A.mv(v,f); + A.mtv(fT,vT); + A.umv(v,f); + A.umtv(fT,vT); + A.umhv(fT,vT); + A.mmv(v,f); + A.mmtv(fT,vT); + A.mmhv(fT,vT); + using S = typename FieldTraits::field_type; + using S2 = typename FieldTraits::field_type; + S scalar = (S)(0.5); + S2 scalar2 = (S2)(0.5); + A.usmv(scalar,v,f); + A.usmtv(scalar2,fT,vT); + A.usmhv(scalar2,fT,vT); +} + +template +void test_matrix() +{ + typedef typename FieldMatrix::size_type size_type; + + FieldMatrix A; + FieldVector v; + FieldVector f; + + // test constexpr size + static_assert(A.N() == n, ""); + static_assert(A.M() == m, ""); + + // assign matrix + A=K(); + // random access matrix + for (size_type i=0; ibegin(); + for (; cit!=rit->end(); ++cit) + { + cit.index(); + (*cit) *= 2; + } + } + + // assign vector + f = 1; + + // random access vector + for (size_type i=0; i res2(0); + FieldVector res1; + + FieldVector b(1); + + A.mv(b, res1); + A.umv(b, res2); + + if( (res1 - res2).two_norm() > 1e-12 ) + { + DUNE_THROW(FMatrixError,"mv and umv are not doing the same!"); + } + } + + { + FieldVector v0 (v); + FieldVector f0 (f); + FieldVector vT (0); + FieldVector fT (0); + test_mult(A, v0, f0, vT, fT); + } + + // { + // std::vector v1( m ) ; + // std::vector f1( n, 1 ) ; + // // random access vector + // for (size_type i=0; i= 0 ); + assert( A.frobenius_norm2() >= 0 ); + assert( A.infinity_norm() >= 0 ); + assert( A.infinity_norm_real() >= 0); + + // print matrix + std::cout << A << std::endl; + // print vector + std::cout << f << std::endl; + + A[0][0] += 5; // Make matrix non-zero + { + // Test that operator= and operator-= work before we can test anything else + using FM = FieldMatrix; + FM A0 = A; + { + if (A0.infinity_norm() < 1e-12) + DUNE_THROW(FMatrixError, "Assignment had no effect!"); + } + A0 -= A; + { + if (A0.infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Operator-= had no effect!"); + } + FM A1 = A; // A1 == A + FM A2 = (A1 *= 2); // A1 == A2 == 2*A + { + FM tmp = A1; tmp -= A; + if (tmp.infinity_norm() < 1e-12) + DUNE_THROW(FMatrixError,"Operator*= had no effect!"); + } + { + FM tmp = A2; tmp -= A1; + if (tmp.infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Return value of Operator*= incorrect!"); + } + [[maybe_unused]] FM A3 = (A2 *= 3); // A2 == A3 == 6*A + FM A4 = (A2 /= 2); // A2 == A4 == 3*A; + FM A5 = A; + A5 *= 3; // A5 == 3*A + { + FM tmp = A2; tmp -= A5; + if (tmp.infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Operator/= had no effect!"); + } + { + FM tmp = A4; tmp -= A5; + if (tmp.infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Return value of Operator/= incorrect!"); + } + + FM A6 = A; + FM A7 = (A6 += A); // A6 == A7 == 2*A + { + FM tmp = A1; tmp -= A6; + if (tmp.infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Operator+= had no effect!"); + } + { + FM tmp = A1; tmp -= A7; + if (tmp.infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Return value of Operator+= incorrect!"); + } + + FM A8 = A2; // A8 == A2 == 3*A + FM A9 = (A8 -= A); // A9 == A8 == 2*A; + { + FM tmp = A8; tmp -= A1; + if (tmp.infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Operator-= had no effect!"); + } + { + FM tmp = A9; tmp -= A1; + if (tmp.infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Return value of Operator-= incorrect!"); + } + FM A10 = A; + FM A11 = A10.axpy(2, A); // A11 = 3*A; + { + FM tmp = A10; tmp -= A2; + if (tmp.infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "axpy() had no effect!"); + } + { + FM tmp = A10; tmp -= A11; + if (tmp.infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Return value of axpy() incorrect!"); + } + + // Scalar * Matrix and Matrix * Scalar + { + typename FM::field_type scalar = 3; + FM sA = scalar * A; + FM aS = A * scalar; + FM ref = A; + ref *= scalar; + + if ((sA-ref).infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Return value of operator*(scalar,matrix) incorrect!"); + + if ((aS-ref).infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Return value of operator*(matrix,scalar) incorrect!"); + } + + // Matrix / Scalar + { + typename FM::field_type scalar = 3; + FM aS = A / scalar; + FM ref = A; + ref /= scalar; + + if ((aS-ref).infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Return value of operator/(matrix,scalar) incorrect!"); + } + + // Matrix + Matrix + { + FM twiceA = A + A; + FM ref = typename FM::field_type(2)*A; + + if ((twiceA-ref).infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Return value of operator+(matrix,matrix) incorrect!"); + } + + // Matrix - Matrix + { + FM zero = A - A; + + if (zero.infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Return value of operator-(matrix,matrix) incorrect!"); + } + + // -Matrix + { + FM neg = -A; + FM ref = typename FM::field_type(-1)*A; + + if ((neg-ref).infinity_norm() > 1e-12) + DUNE_THROW(FMatrixError, "Return value of operator-(matrix) incorrect!"); + } + + // Matrix * Matrix + { + auto transposed = [](const FM& A) + { + FieldMatrix AT; + for (int i=0; i A3 = A; + A3 *= 3; + + FieldMatrix B = A; + B.axpy( K( 2 ), B ); + B -= A3; + if (abs(B.infinity_norm()) > 1e-12) + DUNE_THROW(FMatrixError,"Axpy test failed!"); + } + { + using std::abs; + + FieldMatrix A2; + for(size_type i=0; i& Aref = A2; + + + FieldMatrix B; + for(size_type i=0; i& Bref = B; + + FieldMatrix C; + for(size_type i=0; i& Cref = C; + + FieldMatrix AB = Aref.rightmultiplyany(B); + for(size_type i=0; i 1e-10) + DUNE_THROW(FMatrixError,"Rightmultiplyany test failed!"); + + FieldMatrix AB2 = A2; + AB2.rightmultiply(B); + AB2 -= AB; + if (abs(AB2.infinity_norm()) > 1e-10) + DUNE_THROW(FMatrixError,"Rightmultiply test failed!"); + + FieldMatrix AB3 = Bref.leftmultiplyany(A2); + AB3 -= AB; + if (abs(AB3.infinity_norm()) > 1e-10) + DUNE_THROW(FMatrixError,"Leftmultiplyany test failed!"); + + FieldMatrix CA = Aref.leftmultiplyany(C); + for(size_type i=0; i 1e-10) + DUNE_THROW(FMatrixError,"Leftmultiplyany test failed!"); + + FieldMatrix CA2 = A2; + CA2.leftmultiply(C); + CA2 -= CA; + if (abs(CA2.infinity_norm()) > 1e-10) + DUNE_THROW(FMatrixError,"Leftmultiply test failed!"); + + FieldMatrix CA3 = Cref.rightmultiplyany(A2); + CA3 -= CA; + if (abs(CA3.infinity_norm()) > 1e-10) + DUNE_THROW(FMatrixError,"Rightmultiplyany test failed!"); + } +} + +template +int test_determinant() +{ + using std::abs; + + int ret = 0; + + FieldMatrix B; + B[0][0] = 3.0; B[0][1] = 0.0; B[0][2] = 1.0; B[0][3] = 0.0; + B[1][0] = -1.0; B[1][1] = 3.0; B[1][2] = 0.0; B[1][3] = 0.0; + B[2][0] = -3.0; B[2][1] = 0.0; B[2][2] = -1.0; B[2][3] = 2.0; + B[3][0] = 0.0; B[3][1] = -1.0; B[3][2] = 0.0; B[3][3] = 1.0; + if (Simd::anyTrue(abs(B.determinant() + 2.0) > 1e-12)) + { + std::cerr << "Determinant 1 test failed (" << Dune::className() << ")" + << std::endl; + std::cerr << "Determinant 1 is " << B.determinant(true) << ", expected 2.0" + << std::endl; + ++ret; + } + + B[0][0] = 3.0; B[0][1] = 0.0; B[0][2] = 1.0; B[0][3] = 0.0; + B[1][0] = -1.0; B[1][1] = 3.0; B[1][2] = 0.0; B[1][3] = 0.0; + B[2][0] = -3.0; B[2][1] = 0.0; B[2][2] = -1.0; B[2][3] = 2.0; + B[3][0] = -1.0; B[3][1] = 3.0; B[3][2] = 0.0; B[3][3] = 2.0; + if (Simd::anyTrue(B.determinant(false) != 0.0)) + { + std::cerr << "Determinant 2 test failed (" << Dune::className() << ")" + << std::endl; + std::cerr << "Determinant 2 is " << B.determinant(false) << ", expected 0.0" + << std::endl; + ++ret; + } + + return ret; +} + +template +struct ScalarOperatorTest +{ + ScalarOperatorTest() + { + ft a = 1; + ft c = 2; + FieldMatrix v(2); + FieldMatrix w(2); + [[maybe_unused]] bool b; + + std::cout << __func__ << "\t ( " << className(v) << " )" << std::endl; + + a = a * c; + a = a + c; + a = a / c; + a = a - c; + + v = a; + v = w = v; + a = v; + + a = v + a; + a = v - a; + a = v * a; + a = v / a; + + v = v + a; + v = v - a; + v = v * a; + v = v / a; + + a = a + v; + a = a - v; + a = a * v; + a = a / v; + + v = a + v; + v = a - v; + v = a * v; + v = a / v; + + v -= w; + v -= a; + v += w; + v += a; + v *= a; + v /= a; + + b = (v == a); + b = (v != a); + b = (a == v); + b = (a != v); + + } +}; + +template +void test_ev() +{ + // rosser test matrix + + /* + This matrix was a challenge for many matrix eigenvalue + algorithms. But the Francis QR algorithm, as perfected by + Wilkinson and implemented in EISPACK, has no trouble with it. The + matrix is 8-by-8 with integer elements. It has: + + * A double eigenvalue + * Three nearly equal eigenvalues + * Dominant eigenvalues of opposite sign + * A zero eigenvalue + * A small, nonzero eigenvalue + + */ + Dune::FieldMatrix A = { + { 611, 196, -192, 407, -8, -52, -49, 29 }, + { 196, 899, 113, -192, -71, -43, -8, -44 }, + { -192, 113, 899, 196, 61, 49, 8, 52 }, + { 407, -192, 196, 611, 8, 44, 59, -23 }, + { -8, -71, 61, 8, 411, -599, 208, 208 }, + { -52, -43, 49, 44, -599, 411, 208, 208 }, + { -49, -8, 8, 59, 208, 208, 99, -911 }, + { 29, -44, 52, -23, 208, 208, -911, 99} + }; + + // compute eigenvalues + Dune::FieldVector eig; + Dune::FMatrixHelp::eigenValues(A, eig); + + // test results + Dune::FieldVector ref; + /* + reference solution computed with octave 3.2 + + > format long e + > eig(rosser()) + + */ + ref = { -1.02004901843000e+03, + -4.14362871168386e-14, + 9.80486407214362e-02, + 1.00000000000000e+03, + 1.00000000000000e+03, + 1.01990195135928e+03, + 1.02000000000000e+03, + 1.02004901843000e+03 }; + + if( (ref - eig).two_norm() > 1e-10 ) + { + DUNE_THROW(FMatrixError,"error computing eigenvalues"); + } + + std::cout << "Eigenvalues of Rosser matrix: " << eig << std::endl; +} + +template< class K, int n > +void test_invert () +{ + Dune::FieldMatrix< K, n, n > A( 1e-15 ); + for( int i = 0; i < n; ++i ) + A[ i ][ i ] = K( 1 ); + A.invert(); +} + +template +void checkNormNAN(M const &v, int line) { + if (!std::isnan(v.frobenius_norm())) { + std::cerr << "error: norm not NaN: frobenius_norm() on line " + << line << " (type: " << Dune::className(v[0]) << ")" + << std::endl; + std::exit(-1); + } + if (!std::isnan(v.infinity_norm())) { + std::cerr << "error: norm not NaN: infinity_norm() on line " + << line << " (type: " << Dune::className(v[0]) << ")" + << std::endl; + std::exit(-1); + } +} + +// Make sure that matrices with NaN entries have norm NaN. +// See also bug flyspray/FS#1147 +template +void +test_nan(T const &mynan) +{ + T const n(0); + { + Dune::FieldMatrix m = { + { mynan, mynan }, + { mynan, mynan } + }; + checkNormNAN(m, __LINE__); + } + { + Dune::FieldMatrix m = { + { mynan, n }, + { n, n } + }; + checkNormNAN(m, __LINE__); + } + { + Dune::FieldMatrix m = { + { n, mynan }, + { n, n } + }; + checkNormNAN(m, __LINE__); + } + { + Dune::FieldMatrix m = { + { n, n }, + { mynan, n } + }; + checkNormNAN(m, __LINE__); + } + { + Dune::FieldMatrix m = { + { n, n }, + { n, mynan } + }; + checkNormNAN(m, __LINE__); + } +} + +// The computation of infinity_norm_real() was flawed from r6819 on +// until r6915. +void +test_infinity_norms() +{ + using std::abs; + + std::complex threefour(3.0, -4.0); + std::complex eightsix(8.0, -6.0); + + Dune::FieldMatrix, 2, 2> m; + m[0] = threefour; + m[1] = eightsix; + assert(abs(m.infinity_norm() -20.0) < 1e-10); // max(5+5, 10+10) + assert(abs(m.infinity_norm_real()-28.0) < 1e-10); // max(7+7, 14+14) +} + + +template< class K, class K2, int rows, int cols > +void test_interface() +{ + typedef CheckMatrixInterface::UseFieldVector< K2, rows, cols > Traits; + typedef Dune::FieldMatrix< K, rows, cols > FMatrix; + +#if __GNUC__ != 5 || defined(__clang__) + static_assert( + !std::is_trivially_copyable::value || std::is_trivially_copyable::value, + "FieldMatrix must be trivally copyable type when T is trivial type" + ); +#endif + static_assert( + std::is_standard_layout::value, + "FieldMatrix<...> must be a standard layout type" + ); + + FMatrix m( 1 ); + checkMatrixInterface< FMatrix >( m ); + checkMatrixInterface< FMatrix, Traits >( m ); +} + +void test_initialisation() +{ + [[maybe_unused]] Dune::FieldMatrix const A = { + { 1, 2 }, + { 3, 4 } + }; + + assert(A[0][0] == 1); + assert(A[0][1] == 2); + assert(A[1][0] == 3); + assert(A[1][1] == 4); +} + +int main() +{ + try { + int errors = 0; // counts errors + + static_assert( + std::is_same< Dune::FieldMatrix, Dune::FieldMatrix >::value, + "default parameter for square matrices" + ); + + { + double nan = std::nan(""); + test_nan(nan); + } + { + std::complex nan( std::nan(""), 17 ); + test_nan(nan); + } + test_infinity_norms(); + test_initialisation(); + + // test 1 x 1 matrices + test_interface(); + test_matrix(); + ScalarOperatorTest(); + test_matrix(); + ScalarOperatorTest(); +#if HAVE_QUADMATH + test_matrix(); + ScalarOperatorTest(); +#endif + // test n x m matrices + test_interface(); + test_matrix(); + test_matrix(); + test_interface(); +#if HAVE_QUADMATH + test_matrix(); + test_interface(); +#endif + // mixed precision + test_interface(); + test_matrix(); +#if HAVE_QUADMATH + test_matrix(); +#endif + // test complex matrices + test_matrix, std::complex, std::complex, 1, 1>(); + test_matrix, std::complex, std::complex, 5, 10>(); + // test complex/real matrices mixed case + test_matrix, std::complex, 1, 1>(); + test_matrix, float, std::complex, 1, 1>(); +#if HAVE_LAPACK + // test eigemvalue computation + test_ev(); +#endif + // test high level methods + errors += test_determinant< double >(); +#if HAVE_VC + errors += test_determinant< Vc::SimdArray >(); +#endif + + //test LoopSIMD stuff + errors += test_determinant< Dune::LoopSIMD >(); + + test_invert< float, 34 >(); + test_invert< double, 34 >(); + test_invert< std::complex< long double >, 2 >(); + test_invert< std::complex< float >, 2 >(); + errors += test_invert_solve(); + + { // Test whether multiplying one-column matrices by scalars work + FieldMatrix A = {1,2,3}; + double v = 0; + FieldVector f = {2,3,4}; + double vT = 0; + FieldVector fT = {3,4,5}; + test_mult(A, v, f, vT, fT); + } + + { // Test whether result of multiplying a one-row matrix can be a scalar + FieldMatrix A = {{1,2,3}}; + FieldVector v = {2,3,4}; + double f = 0; + FieldVector vT = {3,4,5}; + double fT = 0; + test_mult(A, v, f, vT, fT); + } + + { // Test multiplication of 1x1 matrix with scalars + FieldMatrix A = {42}; + double v = 0; + double f = 2; + double vT = 0; + double fT = 5; + test_mult(A, v, f, vT, fT); + } + + return (errors > 0 ? 1 : 0); // convert error count to unix exit status + } + catch (Dune::Exception & e) + { + std::cerr << "Exception: " << e << std::endl; + return 1; + } +} diff --git a/dune/common/test/functiontest.cc b/dune/common/test/functiontest.cc new file mode 100644 index 0000000..e9dfebf --- /dev/null +++ b/dune/common/test/functiontest.cc @@ -0,0 +1,44 @@ +#include "config.h" + +#include + + +#include +#define DUNE_FUNCTION_HH_SILENCE_DEPRECATION +#include +#include + +int main() +{ + Dune::TestSuite t; + + DUNE_NO_DEPRECATED_BEGIN + { + auto f = Dune::makeVirtualFunction( + [](int x) -> long { return x*x; }); + + static_assert( + std::is_base_of< Dune::VirtualFunction, decltype(f) >::value, + "makeVirtualFunction() must return type derived from VirtualFunction"); + + long y; + f.evaluate(2, y); + t.check(y == 4); + } + + { + auto f1 = [](int x) -> long { return x*x; }; + auto f = Dune::makeVirtualFunction(f1); + + static_assert( + std::is_base_of< Dune::VirtualFunction, decltype(f) >::value, + "makeVirtualFunction() must return type derived from VirtualFunction"); + + long y; + f.evaluate(2, y); + t.check(y == 4); + } + DUNE_NO_DEPRECATED_END + + return t.exit(); +} diff --git a/dune/common/test/fvectorconversion1d.cc b/dune/common/test/fvectorconversion1d.cc new file mode 100644 index 0000000..84a01f5 --- /dev/null +++ b/dune/common/test/fvectorconversion1d.cc @@ -0,0 +1,136 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include + +//! @file +/** + * This test tests for a regression, where `std::is_assignable` would return + * `true` for certain assignments, but it was not actually possible to + * instantiate those assignments. In the fix `std::is_assignable` was fixed + * to report false, and that is what is checked now. + */ + +template +class MyVector; + +namespace Dune +{ + template + struct DenseMatVecTraits< MyVector > + { + using derived_type = MyVector; + using value_type = Component; + using size_type = std::size_t; + }; + + template + struct IsFieldVectorSizeCorrect, Size> + : std::integral_constant + {}; +} + +template +class MyVector : public Dune::DenseVector< MyVector > +{ + public: + static constexpr std::size_t size () { return Dim; } + + Component& operator[] ( std::size_t i ) { return data_; } + const Component& operator[] ( std::size_t i ) const { return data_; } + protected: + Component data_; +}; + +int main() +{ + try + { + // Pure 1d case. Here OuterMV is assignable to MiddleFV as the the + // 1d FieldVector implements a type-case to the underlying + // field. This is expected behaviour. + { + using InnerFV = Dune::FieldVector; + using MiddleFV = Dune::FieldVector; + using OuterFV = Dune::FieldVector; + + using MiddleMV = MyVector; + using OuterMV = MyVector; + + MiddleFV mfv; + OuterMV mv; + OuterFV fv; + + static_assert(std::is_convertible::value, + "DenseVectors should be convertible."); + fv = mv; + + static_assert(std::is_assignable::value, + "Reduced assignability detected."); + mfv = mv; + } + + // The following would trigger a problem in the DenseVector + // operator=() which was cured by first checking whether the + // value_types are assignable. + { + using InnerFV = Dune::FieldVector; + using MiddleFV = Dune::FieldVector; + using OuterFV = Dune::FieldVector; + + using MiddleMV = MyVector; + using OuterMV = MyVector; + + // MiddleFV mfv; + OuterMV mv; + OuterFV fv; + + static_assert(std::is_convertible::value, + "DenseVectors should be convertible."); + fv = mv; + + // before the fix, `is_assignable` returned `true`, + static_assert(!std::is_assignable::value, + "Inconsistent assignability detected."); + // mfv = mv; // <- but this assignment failed instantiation + } + + { + using InnerFV = Dune::FieldMatrix; + using MiddleFV = Dune::FieldVector; + using OuterFV = Dune::FieldVector; + + using MiddleMV = MyVector; + using OuterMV = MyVector; + + // MiddleFV mfv; + OuterMV mv; + OuterFV fv; + + static_assert(std::is_assignable::value, + "DenseVectors should be assignable."); + fv = mv; + + // before the fix, `is_assignable` returned `true`, + static_assert(!std::is_assignable::value, + "Inconsistent assignability detected."); + // mfv = mv; // <- but this assignment failed instantiation + } + return 0; + } catch (Dune::Exception& e) { + std::cerr << e << std::endl; + return 1; + } catch (...) { + std::cerr << "Generic exception!" << std::endl; + return 2; + } +} diff --git a/dune/common/test/fvectortest.cc b/dune/common/test/fvectortest.cc new file mode 100644 index 0000000..d755557 --- /dev/null +++ b/dune/common/test/fvectortest.cc @@ -0,0 +1,629 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct FVectorTestException : Dune::Exception {}; + +#define FVECTORTEST_ASSERT(EXPR) \ + if(!(EXPR)) { \ + DUNE_THROW(FVectorTestException, \ + "Test assertion " << #EXPR << " failed"); \ + } \ + static_assert(true, "enforce terminating ;") + + +using Dune::FieldVector; +using std::complex; + +// Tests that can be run without the construction of complex +template +struct FieldVectorMainTestCommons +{ + FieldVectorMainTestCommons() { +#if __GNUC__ != 5 || defined(__clang__) + static_assert( + !std::is_trivially_copyable::value || std::is_trivially_copyable< FieldVector >::value, + "FieldVector must be a trivially copyable type when T is a trivial type" + ); +#endif + static_assert( + std::is_standard_layout< FieldVector >::value, + "FieldVector<...> must be a standard layout type" + ); + + ft a = 1; + FieldVector v(1); + FieldVector w(2); + FieldVector z(2); + const FieldVector x(z); + if (x.size()>0) + a = x[0]; + [[maybe_unused]] bool b; + [[maybe_unused]] rt n; + + std::cout << __func__ << "\t ( " << className(v) << " )" << std::endl; + + // test exported types + static_assert( + std::is_same::value_type>::value, + "FieldVector::value_type is not the correct type" + ); + + // test traits + static_assert( + ( std::is_same< typename Dune::FieldTraits< + FieldVector >::field_type, ft >::value ), + "FieldTraits yields wrong field_type" + ); + static_assert( + ( std::is_same< typename Dune::FieldTraits::real_type, rt >::value ), + "FieldTraits yields wrong real_type" + ); + static_assert( + ( std::is_same< typename Dune::FieldTraits< + FieldVector >::real_type, rt >::value ), + "FieldTraits yields wrong real_type" + ); + + // Test whether the norm methods compile + n = (w+v).two_norm(); + n = (w+v).two_norm2(); + n = (w+v).one_norm(); + n = (w+v).one_norm_real(); + n = (w+v).infinity_norm(); + n = (w+v).infinity_norm_real(); + + // test op(vec,vec) + z = v + w; + z = v - w; + [[maybe_unused]] FieldVector z2 = v + w; + w -= v; + w += v; + + // test op(vec,scalar) + w +=a; + w -= a; + w *= a; + w /= a; + w = a * v; + w = v * a; + w = v / a; + + // Negation + -v; + + // test scalar product, axpy + a = v * w; + a = v.dot(w); + z = v.axpy(a,w); + + // test comparison + b = (w != v); + b = (w == v); + + // test istream operator + std::stringstream s; + for (int i=0; i> w; + FVECTORTEST_ASSERT(v == w); + + // test container methods + typename FieldVector::size_type size = FieldVector::dimension; + FVECTORTEST_ASSERT(size == w.size()); + + if (w.size() > 0) { + FVECTORTEST_ASSERT(!w.empty()); + FVECTORTEST_ASSERT(std::addressof(w[0]) == std::addressof(w.front())); + FVECTORTEST_ASSERT(std::addressof(w[0]) == w.data()); + FVECTORTEST_ASSERT(std::addressof(w[d-1]) == std::addressof(w.back())); + } + } +}; + +// Additional tests for floating point types, for which complex will work +template::value> +struct FieldVectorMainTest + : FieldVectorMainTestCommons +{ + FieldVectorMainTest() + : FieldVectorMainTestCommons() + { + ft a = 1; + FieldVector v(1); + FieldVector z(2); + const FieldVector x(z); + + // assignment to vector of complex + FieldVector< std::complex ,d> cv = v; + cv = a; + [[maybe_unused]] const FieldVector< std::complex ,d> ccv = x; + } +}; + +template +struct FieldVectorMainTest + : FieldVectorMainTestCommons +{ + FieldVectorMainTest() + : FieldVectorMainTestCommons() + {} +}; + +template +struct ScalarOperatorTest +{ + ScalarOperatorTest() + { + // testft has to initializable with an int + testft a = 1; + testft c = 2; + FieldVector v(2); + FieldVector w(2); + [[maybe_unused]] bool b; + + std::cout << __func__ << "\t ( " << className(v) << " )" << std::endl; + + a = a * c; + a = a + c; + a = a / c; + a = a - c; + + v = a; + v = w = v; + a = v; + + a = v + a; + a = v - a; + a = v * a; + a += 1; // make sure a!=0 + a = v / a; + + v = v + a; + v = v - a; + v = v * a; + a += 1; // make sure a!=0 + v = v / a; + + a = a + v; + a = a - v; + a = a * v; + v += 1; // make sure v!=0 + a = a / v; + + v = a + v; + v = a - v; + v = a * v; + v += 1; // make sure v!=0 + v = a / v; + + v -= w; + v -= a; + v += w; + v += a; + v *= a; + a += 1; // make sure a!=0 + v /= a; + + b = (v == a); + b = (v != a); + b = (a == v); + b = (a != v); + + } +}; + +// scalar ordering doesn't work for complex numbers +template +struct ScalarOrderingTest +{ + ScalarOrderingTest() + { + ft a = 1; + ft c = 2; + FieldVector v(2); + FieldVector w(2); + [[maybe_unused]] bool b; + + std::cout << __func__ << "\t ( " << className(v) << " )" << std::endl; + + b = (a < c); + b = (a <= c); + b = (a >= c); + b = (a > c); + + b = (v == a); + b = (v != a); + b = (a == v); + b = (a != v); + + b = (v < a); + b = (v <= a); + b = (v >= a); + b = (v > a); + + b = (v < w); + b = (v <= w); + b = (v >= w); + b = (v > w); + + b = (a < w); + b = (a <= w); + b = (a >= w); + b = (a > w); + } +}; + +template +struct Epsilon +{ + static T value() { return T(1e-6); } +}; + +template<> +struct Epsilon +{ + static int value() { return 0; } +}; + +// scalar ordering doesn't work for complex numbers +template ::value> +struct DotProductTest +{ + DotProductTest() { + typedef std::complex ct; + [[maybe_unused]] const rt myEps = Epsilon::value(); + + static_assert( + ( std::is_same< typename Dune::FieldTraits::real_type, rt>::value ), + "DotProductTest requires real data type as template parameter!" + ); + + const ct I(0.,1.); // imaginary unit + const FieldVector one(1.); // vector filled with 1 + const FieldVector iVec(ct(0.,1.)); // vector filled with I + + std::cout << __func__ << "\t \t ( " << Dune::className(one) << " and " << Dune::className(iVec) << ")" << std::endl; + + const bool isRealOne = std::is_same::field_type,typename Dune::FieldTraits::real_type>::value; + const bool isRealIVec = std::is_same::field_type,typename Dune::FieldTraits::real_type> ::value; + static_assert(isRealOne,"1-vector expected to be real"); + static_assert(!isRealIVec,"i-vector expected to be complex"); + + ct result = ct(); + ct length = ct(d); + + + // one^H*one should equal d + result = dot(one,one); + FVECTORTEST_ASSERT(std::abs(result-length)<= myEps); + result = one.dot(one); + FVECTORTEST_ASSERT(std::abs(result-length)<= myEps); + + + // iVec^H*iVec should equal d + result = dot(iVec,iVec); + FVECTORTEST_ASSERT(std::abs(result-length)<= myEps); + result = iVec.dot(iVec); + FVECTORTEST_ASSERT(std::abs(result-length)<= myEps); + + + // test that we do conjugate first argument + result = dot(one,iVec); + FVECTORTEST_ASSERT(std::abs(result-length*I)<= myEps); + result = dot(one,iVec); + FVECTORTEST_ASSERT(std::abs(result-length*I)<= myEps); + + + // test that we do not conjugate second argument + result = dot(iVec,one); + FVECTORTEST_ASSERT(std::abs(result+length*I)<= myEps); + result = iVec.dot(one); + FVECTORTEST_ASSERT(std::abs(result+length*I)<= myEps); + + + // test that dotT does not conjugate at all + result = dotT(one,one) + one*one; + FVECTORTEST_ASSERT(std::abs(result-ct(2)*length)<= myEps); + result = dotT(iVec,iVec) + iVec*iVec; + FVECTORTEST_ASSERT(std::abs(result+ct(2)*length)<= myEps); + result = dotT(one,iVec) + one*iVec; + FVECTORTEST_ASSERT(std::abs(result-ct(2)*length*I)<= myEps); + result = dotT(iVec,one) + iVec*one; + FVECTORTEST_ASSERT(std::abs(result-ct(2)*length*I)<= myEps); + + } +}; + +// scalar ordering doesn't work for complex numbers +template +struct DotProductTest +{ + DotProductTest() { + [[maybe_unused]] const rt myEps = Epsilon::value(); + + static_assert( + ( std::is_same< typename Dune::FieldTraits::real_type, rt>::value ), + "DotProductTest requires real data type as template parameter!" + ); + + const FieldVector one(1.); // vector filled with 1 + + std::cout << __func__ << "\t \t ( " << Dune::className(one) << " only)" << std::endl; + + const bool isRealOne = std::is_same::field_type,typename Dune::FieldTraits::real_type>::value; + static_assert(isRealOne,"1-vector expected to be real"); + + rt result = rt(); + rt length = rt(d); + + // one^H*one should equal d + result = dot(one,one); + FVECTORTEST_ASSERT(abs(result-length)<= myEps); + result = one.dot(one); + FVECTORTEST_ASSERT(abs(result-length)<= myEps); + + // test that dotT does not conjugate at all + result = dotT(one,one) + one*one; + FVECTORTEST_ASSERT(abs(result-rt(2)*length)<= myEps); + } +}; + +template::value> +struct FieldVectorTest +{ + FieldVectorTest() + { + // --- test complex and real valued vectors + FieldVectorMainTest(); + FieldVectorMainTest,ft,d>(); + DotProductTest(); + // --- test next lower dimension + FieldVectorTest(); + } +}; + +// specialisation for non-floating-point vectors +template +struct FieldVectorTest +{ + FieldVectorTest() + { + // --- test real valued vectors + FieldVectorMainTest(); + DotProductTest(); + // --- test next lower dimension + FieldVectorTest(); + } +}; + +// specialization for 1d floating point vector +template +class FieldVectorTest +{ +public: + FieldVectorTest() + { + // --- real valued + FieldVectorMainTest(); + ScalarOperatorTest(); + ScalarOrderingTest(); + DotProductTest(); + // --- complex valued + FieldVectorMainTest,ft,1>(); + ScalarOperatorTest< complex >(); + // ordering doesn't work for complex numbers + + // --- test with an integer + ScalarOperatorTest< ft, int >(); + // --- test next lower dimension + FieldVectorMainTest(); + } +}; + +// specialization for other 1d vectors +template +class FieldVectorTest +{ +public: + FieldVectorTest() + { + // --- real valued + FieldVectorMainTest(); + ScalarOperatorTest(); + ScalarOrderingTest(); + DotProductTest(); + + // --- test with an integer + ScalarOperatorTest< ft, int >(); + // --- test next lower dimension + FieldVectorMainTest(); + } +}; + +template +void checkNormNAN(V const &v, int line) { + if (!std::isnan(v.one_norm())) { + std::cerr << "error: norm not NaN: one_norm() on line " + << line << " (type: " << Dune::className(v[0]) << ")" + << std::endl; + std::exit(-1); + } + if (!std::isnan(v.two_norm())) { + std::cerr << "error: norm not NaN: two_norm() on line " + << line << " (type: " << Dune::className(v[0]) << ")" + << std::endl; + std::exit(-1); + } + if (!std::isnan(v.infinity_norm())) { + std::cerr << "error: norm not NaN: infinity_norm() on line " + << line << " (type: " << Dune::className(v[0]) << ")" + << std::endl; + std::exit(-1); + } +} + +// Make sure that vectors with NaN entries have norm NaN. +// See also bug flyspray/FS#1147 +template +void +test_nan(T const &mynan) +{ + { + Dune::FieldVector v = { mynan, mynan }; + checkNormNAN(v, __LINE__); + } + { + Dune::FieldVector v = { mynan, 0 }; + checkNormNAN(v, __LINE__); + } + { + Dune::FieldVector v = { 0, mynan }; + checkNormNAN(v, __LINE__); + } +} + +void +test_infinity_norms() +{ + std::complex threefour(3.0, -4.0); + std::complex eightsix(8.0, -6.0); + + Dune::FieldVector, 2> v; + v[0] = threefour; + v[1] = eightsix; + FVECTORTEST_ASSERT(std::abs(v.infinity_norm() -10.0) < 1e-10); // max(5,10) + FVECTORTEST_ASSERT(std::abs(v.infinity_norm_real()-14.0) < 1e-10); // max(7,14) +} + +void +test_initialisation() +{ + [[maybe_unused]] Dune::FieldVector const b = { 1, 2 }; + + FVECTORTEST_ASSERT(b[0] == 1); + FVECTORTEST_ASSERT(b[1] == 2); +} + +void fieldvectorMathclassifiersTest() { + double nan = std::nan(""); + double inf = std::numeric_limits::infinity(); + + FieldVector fv_normal(1.); + FieldVector fv_nan(1.); + FieldVector fv_inf(1.); + + fv_nan[2] = nan; + fv_inf[2] = inf; + + //test vector containing only doubles + if(Dune::isNaN(fv_normal) == true) { + std::abort(); + } + if(Dune::isInf(fv_normal) == true) { + std::abort(); + } + if(Dune::isFinite(fv_normal) == false) { + std::abort(); + } + + //test vector containing a NaN-entry + if(Dune::isNaN(fv_nan) == false) { + std::abort(); + } + if(Dune::isInf(fv_nan) == true) { + std::abort(); + } + if(Dune::isFinite(fv_nan) == true) { + std::abort(); + } + + //test vector containing an infinity-entry + if(Dune::isNaN(fv_inf) == true) { + std::abort(); + } + if(Dune::isInf(fv_inf) == false) { + std::abort(); + } + if(Dune::isFinite(fv_inf) == true) { + std::abort(); + } +} + + +int main() +{ + { + FieldVectorTest(); + FieldVectorTest(); + FieldVectorTest(); + FieldVectorTest(); + FieldVectorTest(); + FieldVectorTest(); + FieldVectorTest(); + FieldVectorTest(); +#if HAVE_GMP + { + // we skip the complex test and the int test, as these will be very hard to implement with GMPField + typedef Dune::GMPField<128u> ft; + FieldVectorMainTest(); + FieldVectorMainTest(); + FieldVectorMainTest(); + FieldVectorMainTest(); + ScalarOperatorTest(); + ScalarOrderingTest(); + DotProductTest(); + } +#endif // HAVE_GMP + +#if HAVE_QUADMATH + { + // we skip the int test, as these will be very hard to implement with Float128 + typedef Dune::Float128 ft; + FieldVectorMainTest(); + FieldVectorMainTest(); + FieldVectorMainTest(); + FieldVectorMainTest(); + ScalarOperatorTest(); + ScalarOrderingTest(); + DotProductTest(); + } +#endif + + //test the mathclassifiers Dune::isNaN, Dune::isInf, Dune::isFinite + fieldvectorMathclassifiersTest(); + + { + double nan = std::nan(""); + test_nan(nan); + } + { + std::complex nan( std::nan(""), 17 ); + test_nan(nan); + } + test_infinity_norms(); + test_initialisation(); + } +} diff --git a/dune/common/test/genericiterator_compile_fail.cc b/dune/common/test/genericiterator_compile_fail.cc new file mode 100644 index 0000000..9650fd4 --- /dev/null +++ b/dune/common/test/genericiterator_compile_fail.cc @@ -0,0 +1,20 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +int main(){ + // Test the TestIterator; + typedef TestContainer Container; + Container bidicontainer; + + Container::const_iterator cit = bidicontainer.begin(); + //This should fail since making a mutable iterator from a const iterator + //discard qualifiers + [[maybe_unused]] Container::iterator it; + it = cit; +} diff --git a/dune/common/test/hybridutilitiestest.cc b/dune/common/test/hybridutilitiestest.cc new file mode 100644 index 0000000..5a7705c --- /dev/null +++ b/dune/common/test/hybridutilitiestest.cc @@ -0,0 +1,115 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include + + + +template +auto incrementAll(C&& c) +{ + using namespace Dune::Hybrid; + forEach(c, [](auto&& ci) { + ++ci; + }); +} + +template +auto addIndex(C&& c) +{ + using namespace Dune::Hybrid; + forEach(integralRange(Dune::Hybrid::size(c)), [&](auto&& i) { + c[i] += i; + }); +} + +template +auto incAndAppendToFirst(C&& c) +{ + using namespace Dune::Hybrid; + forEach(integralRange(Dune::Hybrid::size(c)), [&](auto&& i) { + using namespace Dune::Hybrid; + using namespace Dune::Indices; + ifElse(equals(i, _0), [&](auto id) { + id(c[i]).append("+1"); + }, [&](auto id) { + ++id(c[i]); + }); + }); +} + +template +constexpr auto sum(C&& c) +{ + using namespace Dune::Hybrid; + using namespace Dune::Indices; + return accumulate(c, 0.0, [](auto&& a, auto&& b) { + return a+b; + }); +} + +template +auto sumSubsequence(C&& c, I&& indices) +{ + using namespace Dune::Hybrid; + double result = 0; + forEach(indices, [&](auto i) { + result += Dune::Hybrid::elementAt(c, i); + }); + return result; +} + + + +int main() +{ + auto vector = std::vector{1, 2, 3}; + auto numberTuple = Dune::makeTupleVector(0.1, 2, 3); + + Dune::TestSuite test; + + incrementAll(vector); + test.check(vector == std::vector{2, 3, 4}) + << "Incrementing vector entries with Hybrid::forEach failed."; + + incrementAll(numberTuple); + test.check(numberTuple == Dune::makeTupleVector(1.1, 3, 4)) + << "Incrementing tuple entries with Hybrid::forEach failed."; + + addIndex(vector); + test.check(vector == std::vector{2, 4, 6}) + << "Adding indices to vector entries with Hybrid::forEach failed."; + + addIndex(numberTuple); + test.check(numberTuple == Dune::makeTupleVector(1.1, 4, 6)) + << "Adding indices to vector entries with Hybrid::forEach failed."; + + + auto mixedTuple = Dune::makeTupleVector(std::string("1"), 2, 3); + incAndAppendToFirst(mixedTuple); + test.check(mixedTuple == Dune::makeTupleVector(std::string("1+1"), 3, 4)) + << "Adding indices to vector entries with Hybrid::forEach failed."; + + constexpr auto values = std::make_integer_sequence(); + test.check((30*29)/2 == sum(values)) + << "accumulate() yields incorrect result."; + + test.check((29*28)/2 == sumSubsequence(values, std::make_integer_sequence())) + << "Summing up subsequence failed."; + + // Compile time checks + static_assert(sum(values) == (30*29)/2, "Wrong compile time sum!"); + constexpr auto numberTupleConstexpr = Dune::makeTupleVector(0.1, 2, 3); + static_assert(sum(numberTupleConstexpr) == 5.1, "Wrong compile time sum!"); + + return test.exit(); +} diff --git a/dune/common/test/indicestest.cc b/dune/common/test/indicestest.cc new file mode 100644 index 0000000..4906ba2 --- /dev/null +++ b/dune/common/test/indicestest.cc @@ -0,0 +1,32 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +using namespace Dune; + + + +int main() +{ + using namespace Dune::Indices; + + // Test whether indices can be used to index a data structure + Dune::TupleVector v; + v[_0] = 42; + v[_1] = 3.14; + v[_2] = 2.7; + + // Test whether the indices can be used as numbers + std::get<_0>(v) = 43; + std::get<_1>(v) = 4.14; + std::get<_2>(v) = 3.7; + + return 0; +} diff --git a/dune/common/test/iscallabletest.cc b/dune/common/test/iscallabletest.cc new file mode 100644 index 0000000..ee69b50 --- /dev/null +++ b/dune/common/test/iscallabletest.cc @@ -0,0 +1,72 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +int main() +{ + Dune::TestSuite test; + + { + auto f = [](int /*i*/) { return 0; }; + using F = decltype(f); + + test.check(Dune::IsCallable() == true) + << "Dune::IsCallable does not accept copy from r-value"; + test.check(Dune::IsCallable() == true) + << "Dune::IsCallable does not accept copy from l-value reference"; + test.check(Dune::IsCallable() == true) + << "Dune::IsCallable does not accept copy from r-value reference"; + + test.check(Dune::IsCallable() == false) + << "Dune::IsCallable accepts invalid argument type"; + test.check(Dune::IsCallable() == false) + << "Dune::IsCallable accepts invalid argument count"; + test.check(Dune::IsCallable() == true) + << "Dune::IsCallable does not accept valid return type"; + test.check(Dune::IsCallable() == false) + << "Dune::IsCallable accepts invalid return type"; + } + + { + auto f = [](const int& /*i*/) {}; + using F = decltype(f); + + test.check(Dune::IsCallable() == true) + << "Dune::IsCallable does not accept const& temporary from r-value"; + test.check(Dune::IsCallable() == true) + << "Dune::IsCallable does not accept const& temporary from l-value reference"; + test.check(Dune::IsCallable() == true) + << "Dune::IsCallable does not accept const& temporary from r-value reference"; + } + + { + auto f = [](int& /*i*/) {}; + using F = decltype(f); + + test.check(Dune::IsCallable() == false) + << "Dune::IsCallable accepts l-value reference from r-value"; + test.check(Dune::IsCallable() == true) + << "Dune::IsCallable does not accept l-value reference from l-value reference"; + test.check(Dune::IsCallable() == false) + << "Dune::IsCallable accepts l-value reference from r-value reference"; + } + + { + auto f = [](int&& /*i*/) {}; + using F = decltype(f); + + test.check(Dune::IsCallable() == true) + << "Dune::IsCallable does not accept r-value reference from r-value"; + test.check(Dune::IsCallable() == false) + << "Dune::IsCallable accepts r-value reference from l-value reference"; + test.check(Dune::IsCallable() == true) + << "Dune::IsCallable does not accept r-value reference from r-value reference"; + } + + return test.exit(); +} diff --git a/dune/common/test/iteratorfacadetest.cc b/dune/common/test/iteratorfacadetest.cc new file mode 100644 index 0000000..17c35e2 --- /dev/null +++ b/dune/common/test/iteratorfacadetest.cc @@ -0,0 +1,60 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +template +void randomize(Container& cont){ + srand(300); + double size=1000; + + for(int i=0; i < 100; i++) { + cont[i] = (size*(rand()/(RAND_MAX+1.0))); + + } +} + +template +void print(Container& cont){ + for(int i=0; i < 100; i++) + std::cout< +int containerTest(Container & container) +{ + randomize(container); + // print(container); + //std::sort(container.begin(), container.end()); + //print(container); + + const Container ccontainer(container); + int ret=0; + Printer print; + ret += testIterator(container, print); + ret += testIterator(ccontainer, print); + + return ret; +} + +int main(){ + // Test the TestIterator; + TestContainer forwardcontainer; + TestContainer bidicontainer; + TestContainer randomcontainer; + + int ret=0; + + ret += containerTest(forwardcontainer); + ret += containerTest(bidicontainer); + ret += containerTest(randomcontainer); + + return (ret); +} diff --git a/dune/common/test/iteratorfacadetest.hh b/dune/common/test/iteratorfacadetest.hh new file mode 100644 index 0000000..bf1bbfe --- /dev/null +++ b/dune/common/test/iteratorfacadetest.hh @@ -0,0 +1,50 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_ITERATORFACADETEST_HH +#define DUNE_ITERATORFACADETEST_HH +#include +#include +#include + +template class IteratorFacade=Dune::RandomAccessIteratorFacade> +class TestContainer { +public: + typedef Dune::GenericIterator,T,T&,std::ptrdiff_t,IteratorFacade> iterator; + + typedef Dune::GenericIterator,const T,const T&,std::ptrdiff_t,IteratorFacade> const_iterator; + + TestContainer(){ + for(int i=0; i < 100; i++) + values_[i]=i; + } + + iterator begin(){ + return iterator(*this, 0); + } + + const_iterator begin() const { + return const_iterator(*this, 0); + } + + iterator end(){ + return iterator(*this, 100); + } + + const_iterator end() const { + return const_iterator(*this, 100); + } + + T& operator[](int i){ + return values_[i]; + } + + + const T& operator[](int i) const { + return values_[i]; + } +private: + T values_[100]; +}; + +#endif diff --git a/dune/common/test/iteratorfacadetest2.cc b/dune/common/test/iteratorfacadetest2.cc new file mode 100644 index 0000000..cb96fc3 --- /dev/null +++ b/dune/common/test/iteratorfacadetest2.cc @@ -0,0 +1,22 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dummyiterator.hh" + +int main(){ + // Check that iterator can be compared with iterator as soon as + // a conversion from iterator to iterator exists + + int value = 0; + dummyiterator mit(value); + dummyiterator cit(value); + + bool result = mit == cit; + + if(result) return 0; + else return 1; +} diff --git a/dune/common/test/iteratortest.hh b/dune/common/test/iteratortest.hh new file mode 100644 index 0000000..b16a803 --- /dev/null +++ b/dune/common/test/iteratortest.hh @@ -0,0 +1,410 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_COMMON_TEST_ITERATORTEST_HH +#define DUNE_COMMON_TEST_ITERATORTEST_HH +#include +#include +#include +#include + +/** + * @brief Test whether the class Iter implements the interface of an STL output iterator + * + * @param iterator Iterator to test + * @param iterations Number of times that 'iterator' can be safely incremented + * @param value A value that is sent to the output iterator + */ +template +void testOutputIterator(Iter iterator, std::size_t iterations, Value value) +{ + // Test whether iterator is copy-constructible + // The new iterator object will go out of scope at the end of this method, and hence + // destructibility will also be tested. + Iter tmp1(iterator); + + // Test whether iterator is copy-assignable + Iter tmp2 = iterator; + + // Test whether pre-increment and assignment works + for (size_t i=0; i construction allows one to test whether the type A exists at all, + // without assuming anything further about A. + static_assert(Dune::AlwaysTrue::difference_type>::value, + "std::iterator_traits::difference_type is not defined!"); + static_assert(Dune::AlwaysTrue::value_type>::value, + "std::iterator_traits::value_type is not defined!"); + static_assert(Dune::AlwaysTrue::pointer>::value, + "std::iterator_traits::pointer is not defined!"); + static_assert(Dune::AlwaysTrue::reference>::value, + "std::iterator_traits::reference is not defined!"); + + // Make sure the iterator_category is properly set + static_assert(std::is_same::iterator_category, std::output_iterator_tag>::value, + "std::iterator_traits::iterator_category is not properly defined!"); +} + +/** + * @brief Test whether the class Iter implements the interface of an STL forward iterator + * + * @param begin Iterator positioned at the start + * @param end Iterator positioned at the end + * @param opt Functor for doing whatever one wants + */ +template +int testForwardIterator(Iter begin, Iter end, Opt& opt) +{ + // Return status + int ret=0; + + // Test whether iterator is can be value-initialized. + // These object will go out of scope at the end of this method, and hence + // it will also test whether these objects are destructible. + Iter defaultConstructedIterator1{}, defaultConstructedIterator2{}; + + // Since C++14, value-initialized forward iterators are specified as the + // end iterator of the same, empty sequence. Hence, they should compare equal. + // Notice that value-initialization and default-initialization are not the + // same for raw pointers. Since these are POD, value-initialization leads + // to zero-initialization while default-initialization would leave them + // uninitialized such that the comparison is undefined behaviour. + if (defaultConstructedIterator1 != defaultConstructedIterator2) { + std::cerr<<"Default constructed iterators do not compare equal for "+Dune::className()+"."< construction allows one to test whether the type A exists at all, + // without assuming anything further about A. + static_assert(std::is_same::difference_type, typename std::iterator_traits::difference_type>::value, + "std::iterator_traits::difference_type is not defined!"); + static_assert(std::is_same::value_type, typename std::iterator_traits::value_type>::value, + "std::iterator_traits::value_type is not defined!"); + static_assert(std::is_same::pointer, typename std::iterator_traits::pointer>::value, + "std::iterator_traits::pointer is not defined!"); + static_assert(std::is_same::reference, typename std::iterator_traits::reference>::value, + "std::iterator_traits::reference is not defined!"); + + // Make sure the iterator_category is properly set + static_assert(std::is_same::iterator_category, std::forward_iterator_tag>::value + or std::is_same::iterator_category, std::bidirectional_iterator_tag>::value + or std::is_same::iterator_category, std::random_access_iterator_tag>::value, + "std::iterator_traits::iterator_category is not properly defined!"); + + return ret; +} + +/** + * @brief Tests the capabilities of a bidirectional iterator. + * + * Namely it test whether random positions can be reached from + * each directions. + * + * @param begin Iterator positioned at the stsrt. + * @param end Iterator positioned at the end. + * @param opt Functor for doing whatever one wants. + */ +template +int testBidirectionalIterator(Iter begin, Iter end, Opt opt) +{ + int ret=testForwardIterator(begin, end, opt); + for(Iter pre = end, post = end; pre != begin; ) + { + if(pre != post--) + { + std::cerr << "Postdecrement did not return the old iterator" + << std::endl; + ++ret; + } + if(--pre != post) + { + std::cerr << "Predecrement did not return the new iterator" + << std::endl; + ++ret; + } + opt(*pre); + } + + typename Iter::difference_type size = std::distance(begin, end); + srand(300); + + int no= (size>10) ? 10 : size; + + for(int i=0; i < no; i++) + { + int index = static_cast(size*(rand()/(RAND_MAX+1.0))); + int backwards=size-index; + Iter tbegin = begin; + Iter tend = end; + for(int j=0; j < index; j++) ++tbegin; + for(int j=0; j < backwards; j++) --tend; + + if(tbegin != tend) + { + std::cerr<<"Did not reach same index by starting forward from " + <<"begin and backwards from end."< +int testRandomAccessIterator(Iter begin, Iter end, Opt opt){ + int ret=testBidirectionalIterator(begin, end, opt); + + typename Iter::difference_type size = end-begin; + + srand(300); + + int no= (size>10) ? 10 : size; + + for(int i=0; i < no; i++) + { + int index = static_cast(size*(rand()/(RAND_MAX+1.0))); + opt(begin[index]); + } + + // Test the less than operator + if(begin != end &&!( begin= 0) { + std::cerr<<"begin!=end, but begin-end >= 0!"<(size*(rand()/(RAND_MAX+1.0))); + Iter rand(begin), test(begin), res{}; + rand+=index; + + if((res=begin+index) != rand) + { + std::cerr << " i+n should have the result i+=n, where i is the " + <<"iterator and n is the difference type!" <(size*(rand()/(RAND_MAX+1.0))); + Iter iter2 = begin+static_cast(size*(rand()/(RAND_MAX+1.0))); + typename Iter::difference_type diff = iter2 -iter1; + if((iter1+diff)!=iter2) { + std::cerr<< "i+(j-i) = j should hold, where i,j are iterators!"< +int testIterator(Iter& begin, Iter& end, Opt& opt, iterator_category cat); + +template +int testIterator(Iter& begin, Iter& end, Opt& opt, std::forward_iterator_tag) +{ + return testForwardIterator(begin, end, opt); +} + +template +int testIterator(Iter& begin, Iter& end, Opt& opt, std::bidirectional_iterator_tag) +{ + return testBidirectionalIterator(begin, end, opt); +} + +template +int testIterator(Iter& begin, Iter& end, Opt& opt, std::random_access_iterator_tag) +{ + // std::cout << "Testing iterator "; + int ret = testRandomAccessIterator(begin, end, opt); + //std::cout< +int testConstIterator(Iter& begin, Iter& end, Opt& opt) +{ + //std::cout << "Testing constant iterator: "; + int ret=testIterator(begin, end, opt, typename std::iterator_traits::iterator_category()); + //std::cout< +struct TestSorting +{ + template + static void testSorting(Container&, IteratorTag) + {} + template + static void testSorting(Container& c, std::random_access_iterator_tag) + { + std::sort(c.begin(), c.end()); + } +} +; + +template<> +struct TestSorting +{ + template + static void testSorting(Container&, std::random_access_iterator_tag) + {} + template + static void testSorting(Container&, IteratorTag) + {} +}; + + +template +int testIterator(Container& c, Opt& opt) +{ + typename Container::iterator begin=c.begin(), end=c.end(); + typename Container::const_iterator cbegin(begin); + [[maybe_unused]] typename Container::const_iterator cbegin1 = begin; + typename Container::const_iterator cend=c.end(); + int ret = 0; + + TestSorting::testSorting(c, typename std::iterator_traits::iterator_category()); + + if(end!=cend || cend!=end) + { + std::cerr<<"constant and mutable iterators should be equal!"< +int testIterator(Container& c, Opt& opt) +{ + return testIterator(c,opt); +} + +template +void testAssignment(Iter begin, Iter end, Opt&) +{ + //std::cout << "Assignment: "; + for(; begin!=end; begin++) + *begin=typename std::iterator_traits::value_type(); + //std::cout<<" Done."<< std::endl; +} + +template +int testIterator(Iter& begin, Iter& end, Opt& opt) +{ + testAssignment(begin, end, opt); + return testConstIterator(begin, end, opt); +} + + +template +class Printer { + typename std::remove_const::type res; +public: + Printer() : res(0){} + void operator()(const T& t){ + res+=t; + // std::cout << t <<" "; + } +}; + +template +int testIterator(const Container& c, Opt& opt) +{ + typename Container::const_iterator begin=c.begin(), end=c.end(); + return testConstIterator(begin,end, opt); +} + + +template +int testIterator(Container& c) +{ + Printer::value_type> print; + return testIterator(c,print); +} + +#endif diff --git a/dune/common/test/lrutest.cc b/dune/common/test/lrutest.cc new file mode 100644 index 0000000..d34dcce --- /dev/null +++ b/dune/common/test/lrutest.cc @@ -0,0 +1,45 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#include +#include +#include +#include + +void lru_test() +{ + std::cout << "testing Dune::lru\n"; + + Dune::lru lru; + lru.insert(10, 1.0); + assert(lru.front() == lru.back()); + lru.insert(11, 2.0); + assert(lru.front() == 2.0 && lru.back() == 1.0); + lru.insert(12, 99); + lru.insert(13, 1.3); + lru.insert(14, 12345); + lru.insert(15, -17); + assert(lru.front() == -17 && lru.back() == 1.0); + // update + lru.insert(10); + assert(lru.front() == 1.0 && lru.back() == 2.0); + // update + lru.touch(13); + assert(lru.front() == 1.3 && lru.back() == 2.0); + // remove item + lru.pop_front(); + assert(lru.front() == 1.0 && lru.back() == 2.0); + // remove item + lru.pop_back(); + assert(lru.front() == 1.0 && lru.back() == 99); + + std::cout << "... passed\n"; +} + +int main (int argc, char** argv) +{ + Dune::MPIHelper::instance(argc,argv); + + lru_test(); + + return 0; +} diff --git a/dune/common/test/mathclassifierstest.cc b/dune/common/test/mathclassifierstest.cc new file mode 100644 index 0000000..27938c9 --- /dev/null +++ b/dune/common/test/mathclassifierstest.cc @@ -0,0 +1,67 @@ +#include +#include +#include + +#include + +int main() { + //Initialize some variables + int a = 42; + const int b = 42; + + double nan = std::nan(""); + double inf = std::numeric_limits::infinity(); + + std::complex complex_nonan(42., 42.); + std::complex complex_nan1(42.,nan); + std::complex complex_nan2(nan, 42.); + std::complex complex_nan3(nan, nan); + + std::complex complex_noinf(42., 42.); + std::complex complex_inf1(42.,inf); + std::complex complex_inf2(inf, 42.); + std::complex complex_inf3(inf, inf); + + std::cout << std::boolalpha + //check isNaN() + << "isNaN(int): " << Dune::isNaN(a) << "\n" + << "isNaN(const int): " << Dune::isNaN(b) << "\n" + << "isNaN(42): " << Dune::isNaN(42) << "\n" + << "isNaN(nan): " << Dune::isNaN(nan) << "\n" + << "isNaN(inf): " << Dune::isNaN(inf) << "\n" + + << "isNaN(std::complex without NaN): " + << Dune::isNaN(complex_nonan) << "\n" + << "isNaN(std::complex with NaN): " + << Dune::isNaN(complex_nan1) << " " + << Dune::isNaN(complex_nan2) << " " + << Dune::isNaN(complex_nan3) << "\n" + + //check isInf() + << "isInf(int): " << Dune::isInf(a) << "\n" + << "isInf(const int): " << Dune::isInf(b) << "\n" + << "isInf(42): " << Dune::isInf(42) << "\n" + << "isInf(inf): " << Dune::isInf(inf) << "\n" + + << "isInf(std::complex without inf): " + << Dune::isInf(complex_noinf) << "\n" + << "isInf(std::complex with inf): " + << Dune::isInf(complex_inf1) << " " + << Dune::isInf(complex_inf2) << " " + << Dune::isInf(complex_inf3) << "\n" + + //check isFinite() + << "isFinite(int): " << Dune::isFinite(a) << "\n" + << "isFinite(const int): " << Dune::isFinite(b) << "\n" + << "isFinite(42): " << Dune::isFinite(42) << "\n" + << "isFinite(inf): " << Dune::isFinite(inf) << "\n" + + << "isFinite(std::complex without inf): " + << Dune::isFinite(complex_noinf) << "\n" + << "isFinite(std::complex with inf): " + << Dune::isFinite(complex_inf1) << " " + << Dune::isFinite(complex_inf2) << " " + << Dune::isFinite(complex_inf3) << "\n" + + << std::endl; +} diff --git a/dune/common/test/mathtest.cc b/dune/common/test/mathtest.cc new file mode 100644 index 0000000..9da7fa7 --- /dev/null +++ b/dune/common/test/mathtest.cc @@ -0,0 +1,80 @@ +#include "config.h" + +#include + +#include +#include +#include +#include + +#include + + +using namespace Dune::Hybrid; +using namespace Dune::Indices; +using Dune::TestSuite; + +template +constexpr inline static auto next(std::integral_constant) + -> std::integral_constant +{ + return {}; +} + +template +auto testStaticFactorial (std::integral_constant _k = {}) -> TestSuite +{ + TestSuite t; + + std::cout << "test static factorial\n{"; + + forEach(integralRange(_k), [&](auto _i) { + auto value = Dune::factorial(_i); + + t.check(decltype(value)::value == Dune::Factorial::factorial); + + std::cout<< ' ' << value() << ','; + }); + + std::cout << "};\n\n"; + + return t; +} + +template +auto testStaticBinomial (std::integral_constant _k = {}) -> TestSuite +{ + TestSuite t; + + std::cout << "test static binomial\n"; + + forEach(integralRange(_k), [&](auto _i) { + std::cout << "{"; + forEach(integralRange(next(_i)), [&](auto _j) { + const auto value = Dune::binomial(_i,_j); + + auto control = Dune::Factorial::factorial + / Dune::Factorial::factorial + / Dune::Factorial::factorial; + t.check(decltype(value)::value == control); + + std::cout<< ' ' << value() << ','; + }); + + std::cout << "};\n"; + }); + + std::cout << "\n"; + + return t; +} + +int main(int argc, char** argv) +{ + TestSuite t; + + t.subTest(testStaticFactorial(_5)); + t.subTest(testStaticBinomial(_5)); + + return t.exit(); +} diff --git a/dune/common/test/metistest.cc b/dune/common/test/metistest.cc new file mode 100644 index 0000000..d63770c --- /dev/null +++ b/dune/common/test/metistest.cc @@ -0,0 +1,105 @@ +#include +#include +#include + +#if ! HAVE_METIS +#error "METIS is required for this test" +#endif + +#if HAVE_SCOTCH_METIS +extern "C" { + #include +} +#endif + +extern "C" { + #include +} + +#if HAVE_SCOTCH_METIS && !defined(SCOTCH_METIS_RETURN) + // NOTE: scotchmetis does not define a return type for METIS functions + #define METIS_OK 1 +#endif + +int main() +{ +#if defined(REALTYPEWIDTH) || defined(SCOTCH_METIS_DATATYPES) + using real_t = ::real_t; +#else + using real_t = double; +#endif + +#if defined(IDXTYPEWIDTH) || defined(SCOTCH_METIS_DATATYPES) + using idx_t = ::idx_t; +#elif HAVE_SCOTCH_METIS + using idx_t = SCOTCH_Num; +#else + using idx_t = int; +#endif + + idx_t nVertices = 6; // number of vertices + idx_t nCon = 1; // number of constraints + idx_t nParts = 2; // number of partitions + + // Partition index for each vertex. Will be filled by METIS_PartGraphKway. + std::vector part(nVertices, 0); + + // Indices of starting points in adjacent array + std::vector xadj{0,2,5,7,9,12,14}; + + // Adjacent vertices in consecutive order + std::vector adjncy{1,3,0,4,2,1,5,0,4,3,1,5,4,2}; + + // Weights of vertices. If all weights are equal, they can be set to 1. + std::vector vwgt(nVertices * nCon, 1); + + // Load-imbalance tolerance for each constraint. +#if HAVE_SCOTCH_METIS + // NOTE: scotchmetis interprets this parameter differently + std::vector ubvec(nCon, 0.01); +#else + std::vector ubvec(nCon, 1.001); +#endif + +#if METIS_API_VERSION >= 5 + + std::cout << "using METIS API version 5\n"; + + idx_t objval; + int err = METIS_PartGraphKway(&nVertices, &nCon, xadj.data(), adjncy.data(), + vwgt.data(), nullptr, nullptr, &nParts, nullptr, + ubvec.data(), nullptr, &objval, part.data()); + +#elif METIS_API_VERSION >= 3 + + std::cout << "using METIS API version 3\n"; + + int wgtflag = 2; + int numflag = 0; + int options = 0; // use default options + + int edgecut; +#if HAVE_SCOTCH_METIS && ! defined(SCOTCH_METIS_RETURN) + METIS_PartGraphKway(&nVertices, xadj.data(), adjncy.data(), vwgt.data(), + nullptr, &wgtflag, &numflag, &nParts, &options, &edgecut, part.data()); + int err = METIS_OK; +#else + int err = METIS_PartGraphKway(&nVertices, xadj.data(), adjncy.data(), vwgt.data(), + nullptr, &wgtflag, &numflag, &nParts, &options, &edgecut, part.data()); +#endif + +#endif // METIS_API_VERSION + + if (err != METIS_OK) + return 1; + + for (std::size_t part_i = 0; part_i < part.size(); ++part_i) { + // partition index must be in range [0,nParts) + if (part[part_i] >= nParts) + return 2; + + std::cout << part_i << " " << part[part_i] << std::endl; + } + + return 0; +} \ No newline at end of file diff --git a/dune/common/test/mpicollectivecommunication.cc b/dune/common/test/mpicollectivecommunication.cc new file mode 100644 index 0000000..156d842 --- /dev/null +++ b/dune/common/test/mpicollectivecommunication.cc @@ -0,0 +1,65 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +int main(int argc, char** argv) +{ + Dune::TestSuite t; + + typedef Dune::MPIHelper Helper; + Helper& mpi = Helper::instance(argc, argv); + + { + typedef Helper::MPICommunicator MPIComm; + Dune::Communication comm(mpi.getCommunicator()); + + enum { length = 5 }; + double values[5]; + for(int i=0; i +#include +#include + +#include +int main(int argc, char** argv) +{ + Dune::TestSuite t; + + typedef Dune::MPIHelper Helper; + Helper& mpi = Helper::instance(argc, argv); + + { + typedef Helper::MPICommunicator MPIComm; + Dune::Communication comm(mpi.getCommunicator()); + + enum { length = 5 }; + double values[5]; + for(int i=0; i + +#include +#include + +int main(int argc, char** argv) +{ + Dune::MPIHelper & mpihelper = Dune::MPIHelper::instance(argc, argv); + + if (mpihelper.rank() == 0) + std::cout << "---- default constructor" << std::endl; + try + { + // at the end of this block the guard is destroyed and possible exceptions are communicated + { + Dune::MPIGuard guard; + if (mpihelper.rank() > 0) + DUNE_THROW(Dune::Exception, "Fakeproblem on process " << mpihelper.rank()); + guard.finalize(); + } + } + catch (Dune::Exception & e) + { + std::cout << "Error (rank " << mpihelper.rank() << "): " + << e.what() << std::endl; + } + + mpihelper.getCommunication().barrier(); + if (mpihelper.rank() == 0) + std::cout << "---- guard(MPI_COMM_WORLD)" << std::endl; + try + { +#if HAVE_MPI + // at the end of this block the guard is destroyed and possible exceptions are communicated + { + Dune::MPIGuard guard(MPI_COMM_WORLD); + if (mpihelper.rank() > 0) + DUNE_THROW(Dune::Exception, "Fakeproblem on process " << mpihelper.rank()); + guard.finalize(); + } +#else + std::cout << "Info: no mpi used\n"; +#endif + } + catch (Dune::Exception & e) + { + std::cout << "Error (rank " << mpihelper.rank() << "): " + << e.what() << std::endl; + } + + mpihelper.getCommunication().barrier(); + if (mpihelper.rank() == 0) + std::cout << "---- guard(MPIHelper)" << std::endl; + try + { + // at the end of this block the guard is destroyed and possible exceptions are communicated + { + Dune::MPIGuard guard(mpihelper); + if (mpihelper.rank() > 0) + DUNE_THROW(Dune::Exception, "Fakeproblem on process " << mpihelper.rank()); + guard.finalize(); + } + } + catch (Dune::Exception & e) + { + std::cout << "Error (rank " << mpihelper.rank() << "): " + << e.what() << std::endl; + } + + + mpihelper.getCommunication().barrier(); + if (mpihelper.rank() == 0) + std::cout << "---- manual error" << std::endl; + try + { + // at the end of this block the guard is destroyed and possible exceptions are communicated + { + Dune::MPIGuard guard; + guard.finalize(mpihelper.rank() > 0); + } + } + catch (Dune::Exception & e) + { + std::cout << "Error (rank " << mpihelper.rank() << "): " + << e.what() << std::endl; + } + + mpihelper.getCommunication().barrier(); + if (mpihelper.rank() == 0) + std::cout << "---- done" << std::endl; +} diff --git a/dune/common/test/mpihelpertest.cc b/dune/common/test/mpihelpertest.cc new file mode 100644 index 0000000..c0257b3 --- /dev/null +++ b/dune/common/test/mpihelpertest.cc @@ -0,0 +1,43 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +int main(int argc, char** argv) +{ + +#ifdef MPIHELPER_PREINITIALIZE +#if HAVE_MPI + MPI_Init(&argc, &argv); +#endif +#endif + + typedef Dune::MPIHelper Helper; + + { + Helper& mpi = Helper::instance(argc, argv); + + [[maybe_unused]] Helper::MPICommunicator comm = mpi.getCommunicator(); + comm= mpi.getCommunicator(); + } + + { + Helper& mpi = Helper::instance(argc, argv); + + [[maybe_unused]] Helper::MPICommunicator comm = mpi.getCommunicator(); + comm= mpi.getCommunicator(); + +#ifdef MPIHELPER_PREINITIALIZE +#if HAVE_MPI + MPI_Finalize(); +#endif +#endif + } + std::cout << "We are at the end!"< + +#include +#include + +#include + +struct Bar { + int bar() const { return 0; } +}; + + + +int main() +{ + Dune::TestSuite test; + + { + auto foo = Dune::overload( + [](double /*i*/) { return 0; }, + [](int /*i*/) { return 1; }, + [](long /*i*/) { return 2; }); + + test.check(foo(3.14) == 0) + << "incorrect overload selected from OverloadSet"; + test.check(foo(int(42)) == 1) + << "incorrect overload selected from OverloadSet"; + test.check(foo(long(42)) == 2) + << "incorrect overload selected from OverloadSet"; + } + + { + auto foo = Dune::orderedOverload( + [](double /*i*/) { return 0; }, + [](int /*i*/) { return 1; }, + [](long /*i*/) { return 2; }); + + test.check(foo(3.14) == 0) + << "incorrect overload selected from OverloadSet"; + test.check(foo(int(42)) == 0) + << "incorrect overload selected from OverloadSet"; + test.check(foo(long(42)) == 0) + << "incorrect overload selected from OverloadSet"; + } + + { + auto foo = Dune::overload( + [](const int& /*i*/) { return 0; }, + [](int&& /*i*/) { return 1; }); + + int i = 0; + test.check(foo(long(42)) == 1) + << "incorrect overload selected from OverloadSet"; + test.check(foo(int(42)) == 1) + << "incorrect overload selected from OverloadSet"; + test.check(foo(i) == 0) + << "incorrect overload selected from OverloadSet"; + } + + { + auto foo = Dune::orderedOverload( + [](const int& /*i*/) { return 0; }, + [](int&& /*i*/) { return 1; }); + + int i = 0; + test.check(foo(long(42)) == 0) + << "incorrect overload selected from OverloadSet"; + test.check(foo(int(42)) == 0) + << "incorrect overload selected from OverloadSet"; + test.check(foo(i) == 0) + << "incorrect overload selected from OverloadSet"; + } + + { + auto t = std::make_tuple(42, "foo", 3.14); + + auto typeToName = Dune::overload( + [](int) { return "int"; }, + [](long) { return "long"; }, + [](std::string) { return "string"; }, + [](float) { return "float"; }, + [](double) { return "double"; }); + + std::string tupleTypes; + Dune::Hybrid::forEach(t, [&](auto&& ti) { + tupleTypes += typeToName(ti); + }); + + test.check(tupleTypes == "intstringdouble") + << "traversal of tuple called incorrect overloads"; + } + + { + // Check if templated and non-templed overloads work + // nicely together. + auto f = Dune::overload( + [](const int& t) { (void) t;}, + [](const auto& t) { t.bar();}); + f(0); + } + + + return test.exit(); +} diff --git a/dune/common/test/parameterizedobjectfactorysingleton.cc b/dune/common/test/parameterizedobjectfactorysingleton.cc new file mode 100644 index 0000000..3f8c89a --- /dev/null +++ b/dune/common/test/parameterizedobjectfactorysingleton.cc @@ -0,0 +1,22 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +#include "parameterizedobjectfactorysingleton.hh" + +DefineImplementation(InterfaceA, Aix, int); +DefineImplementation(InterfaceA, Bix, int); + +int init_Factory() +{ + globalPtrFactory().define("Aix"); + globalPtrFactory().define("Bix", [](int i) { return std::make_unique(i); }); + return 0; +} + +[[maybe_unused]] static const int init = init_Factory(); diff --git a/dune/common/test/parameterizedobjectfactorysingleton.hh b/dune/common/test/parameterizedobjectfactorysingleton.hh new file mode 100644 index 0000000..d63d47a --- /dev/null +++ b/dune/common/test/parameterizedobjectfactorysingleton.hh @@ -0,0 +1,43 @@ +#ifndef DUNE_COMMON_TEST_PARAMETERIZEDOBJECTFACTORYSINGLETON_HH +#define DUNE_COMMON_TEST_PARAMETERIZEDOBJECTFACTORYSINGLETON_HH + +#include +#include +#include + +#define DefineImplementation2(IF,T) \ + struct T : public IF { \ + T() {} \ + std::string info() override { \ + return #T; \ + } \ + } + +#define DefineImplementation(IF,T,...) \ + struct T : public IF { \ + T(__VA_ARGS__) {} \ + std::string info() override { \ + return #T; \ + } \ + } + +struct InterfaceA +{ + virtual std::string info() = 0; + virtual ~InterfaceA() = default; +}; + +struct InterfaceB +{ + virtual std::string info() = 0; + virtual ~InterfaceB() = default; +}; + +template +Dune::ParameterizedObjectFactory(int)> & +globalPtrFactory() +{ + return Dune::Singleton(int)>>::instance(); +} + +#endif //#ifndef DUNE_COMMON_TEST_PARAMETERIZEDOBJECTFACTORYSINGLETON_HH diff --git a/dune/common/test/parameterizedobjecttest.cc b/dune/common/test/parameterizedobjecttest.cc new file mode 100644 index 0000000..ec53cb8 --- /dev/null +++ b/dune/common/test/parameterizedobjecttest.cc @@ -0,0 +1,104 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include + +#include "parameterizedobjectfactorysingleton.hh" + +DefineImplementation(InterfaceA, Ai, int); +DefineImplementation(InterfaceA, Bi, int); +DefineImplementation2(InterfaceA, Ax); +DefineImplementation2(InterfaceA, Bx); +DefineImplementation(InterfaceA, Ad, const Dune::ParameterTree&); +DefineImplementation(InterfaceA, Bd, Dune::ParameterTree); +DefineImplementation(InterfaceB, Ais, int, std::string); +DefineImplementation(InterfaceB, Bis, int, std::string); + +#define CheckInstance2(F,T) \ + assert(#T == F.create(#T)->info()) + +#define CheckInstance(F,T,...) \ + assert(#T == F.create(#T,##__VA_ARGS__)->info()) + +struct AImp : public InterfaceA +{ + AImp(std::string s) : + s_(s) + {} + + AImp(const AImp& /*other*/) : + s_("copied") + {} + + std::string info() override + { + return s_; + } + std::string s_; +}; + +int main() +{ + // int as parameter + // Dune::ParameterizedObjectFactory(int)> FactoryA; + globalPtrFactory().define("Ai"); + globalPtrFactory().define("Bi"); + globalPtrFactory().define("Ax", [](int /*i*/) { return std::make_unique(); }); + CheckInstance(globalPtrFactory(), Ai, 0); + CheckInstance(globalPtrFactory(), Bi, 1); + CheckInstance(globalPtrFactory(), Ax, 1); + // int as parameter for external factory + CheckInstance(globalPtrFactory(), Aix, 0); + CheckInstance(globalPtrFactory(), Bix, 1); + + // default constructor + Dune::ParameterizedObjectFactory()> FactoryAd; + FactoryAd.define("Ax"); + FactoryAd.define("Bx"); + FactoryAd.define("Ai", []() { return std::make_shared(0); }); + AImp aimp("onStack"); + FactoryAd.define("AImp", [&]() { return Dune::stackobject_to_shared_ptr(aimp); }); + FactoryAd.define("AImp2", Dune::stackobject_to_shared_ptr(aimp)); + FactoryAd.define("AImp3", std::make_shared("shared")); + Dune::ParameterTree param; + CheckInstance2(FactoryAd, Ax); + CheckInstance2(FactoryAd, Bx); + CheckInstance2(FactoryAd, Ai); + std::cout << FactoryAd.create("AImp")->info() << std::endl; + std::cout << FactoryAd.create("AImp2")->info() << std::endl; + std::cout << FactoryAd.create("AImp3")->info() << std::endl; + + // explicitly request the default constructor + Dune::ParameterizedObjectFactory()> FactoryAx; + FactoryAx.define("Ax"); + FactoryAx.define("Bx"); + CheckInstance2(FactoryAx, Ax); + CheckInstance2(FactoryAx, Bx); + + // multiple parameters + Dune::ParameterizedObjectFactory(int, std::string)> FactoryB; + FactoryB.define("Ais"); + FactoryB.define("Bis"); + CheckInstance(FactoryB, Ais, 0, std::to_string(2)); + CheckInstance(FactoryB, Bis, 1, "Hallo"); + + // check for ambiguous overloads + Dune::ParameterizedObjectFactory FactoryBool; + FactoryBool.define("true",true); + FactoryBool.define("false",[](){return false;}); + + // value semantics + Dune::ParameterizedObjectFactory(int)> FactoryC; + FactoryC.define("fi", [](int i) { + return [=](double x) { return x+i;}; + }); + FactoryC.define("fi1", [](int i) { + return [=](double x) { return x+i+1;}; + }); + assert(FactoryC.create("fi", 42)(0) == 42); + assert(FactoryC.create("fi1", 42)(0) == 43); + +} diff --git a/dune/common/test/parametertreelocaletest.cc b/dune/common/test/parametertreelocaletest.cc new file mode 100644 index 0000000..868a4e0 --- /dev/null +++ b/dune/common/test/parametertreelocaletest.cc @@ -0,0 +1,112 @@ +#if HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// This assert macro does not depend on the value of NDEBUG +#define check_assert(expr) \ + do \ + { \ + if(!(expr)) \ + { \ + std::cerr << __FILE__ << ":" << __LINE__ << ": check_assert(" \ + << #expr << ") failed" << std::endl; \ + std::abort(); \ + } \ + } while(false) + +// Check that the given expression throws the given exception +#define check_throw(expr, except) \ + do { \ + try { \ + expr; \ + std::cerr << __FILE__ << ":" << __LINE__ << ": " << #expr \ + << " should throw " << #except << std::endl; \ + std::abort(); \ + } \ + catch(const except&) {} \ + catch(...) { \ + std::cerr << __FILE__ << ":" << __LINE__ << ": " << #expr \ + << " should throw " << #except << std::endl; \ + std::abort(); \ + } \ + } while(false) + +// globally set a locale that uses "," as the decimal seperator. +// return false if no such locale is installed on the system +bool setCommaLocale() +{ + static char const* const commaLocales[] = { + "de", "de@euro", "de.UTF-8", + "de_AT", "de_AT@euro", "de_AT.UTF-8", + "de_BE", "de_BE@euro", "de_BE.UTF-8", + "de_CH", "de_CH@euro", "de_CH.UTF-8", + "de_DE", "de_DE@euro", "de_DE.UTF-8", + "de_LI", "de_LI@euro", "de_LI.UTF-8", + "de_LU", "de_LU@euro", "de_LU.UTF-8", + NULL + }; + for(char const* const* loc = commaLocales; *loc; ++loc) + { + try { + std::locale::global(std::locale(*loc)); + std::cout << "Using comma-locale " << std::locale().name() << std::endl; + return true; + } + catch(const std::runtime_error&) { } + } + + std::cout << "No comma-using locale found on system, tried the following:"; + std::string sep = " "; + for(char const* const* loc = commaLocales; *loc; ++loc) + { + std::cout << sep << *loc; + sep = ", "; + } + std::cout << std::endl; + return false; +} + +int main() +{ + if(!setCommaLocale()) + { + std::cerr << "No locale using comma as decimal seperator found on system" + << std::endl; + return 77; + } + { // Try with comma + Dune::ParameterTree ptree; + check_throw((ptree["setting"] = "42,42", + ptree.get("setting")), + Dune::RangeError); + check_throw((ptree["setting"] = "42 2,5", + ptree.get >("setting")), + Dune::RangeError); + check_throw((ptree["setting"] = "42 2,5", + ptree.get >("setting")), + Dune::RangeError); + } + { // Try with point + Dune::ParameterTree ptree; + check_assert((ptree["setting"] = "42.42", + ptree.get("setting") == 42.42)); + check_assert((ptree["setting"] = "42 2.5", + ptree.get >("setting") + == Dune::FieldVector{42.0, 2.5})); + check_assert((ptree["setting"] = "42 2.5", + ptree.get >("setting") + == std::vector{42.0, 2.5})); + } +} diff --git a/dune/common/test/parametertreetest.cc b/dune/common/test/parametertreetest.cc new file mode 100644 index 0000000..00b7295 --- /dev/null +++ b/dune/common/test/parametertreetest.cc @@ -0,0 +1,335 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +// This assert macro does not depend on the value of NDEBUG +#define check_assert(expr) \ + do \ + { \ + if(!(expr)) \ + { \ + std::cerr << __FILE__ << ":" << __LINE__ << ": check_assert(" \ + << #expr << ") failed" << std::endl; \ + std::abort(); \ + } \ + } while(false) + +// Check that the given expression throws the given exception +#define check_throw(expr, except) \ + do { \ + try { \ + expr; \ + std::cerr << __FILE__ << ":" << __LINE__ << ": " << #expr \ + << " should throw " << #except << std::endl; \ + std::abort(); \ + } \ + catch(const except&) {} \ + catch(...) { \ + std::cerr << __FILE__ << ":" << __LINE__ << ": " << #expr \ + << " should throw " << #except << std::endl; \ + std::abort(); \ + } \ + } while(false) + +template +void testparam(const P & p) +{ + // try accessing key + check_assert(p.template get("x1") == 1); + check_assert(p.template get("x1") == 1.0); + check_assert(p.template get("x2") == "hallo"); + check_assert(p.template get("x3") == false); + // try reading array like structures + std::vector + array1 = p.template get< std::vector >("array"); + std::array + array2 = p.template get< std::array >("array"); + Dune::FieldVector + array3 = p.template get< Dune::FieldVector >("array"); + check_assert(array1.size() == 8); + for (unsigned int i=0; i<8; i++) + { + check_assert(array1[i] == i+1); + check_assert(array2[i] == i+1); + check_assert(array3[i] == i+1); + } + // try accessing subtree + p.sub("Foo"); + p.sub("Foo").template get("peng"); + // check hasSub and hasKey + check_assert(p.hasSub("Foo")); + check_assert(!p.hasSub("x1")); + check_assert(p.hasKey("x1")); + check_assert(!p.hasKey("Foo")); + // try accessing inexistent key + try { + p.template get("bar"); + DUNE_THROW(Dune::Exception, "failed to detect missing key"); + } + catch (Dune::RangeError & r) {} + // try accessing inexistent subtree in throwing mode + try { + p.sub("bar",true); + DUNE_THROW(Dune::Exception, "failed to detect missing subtree"); + } + catch (Dune::RangeError & r) {} + // try accessing inexistent nested subtree in throwing mode + try { + p.sub("Foo.Zoo",true); + DUNE_THROW(Dune::Exception, "failed to detect missing nested subtree"); + } + catch (Dune::RangeError & r) {} + // try accessing inexistent subtree in non-throwing mode + p.sub("bar"); + // try accessing inexistent subtree that shadows a value key + try { + p.sub("x1.bar"); + DUNE_THROW(Dune::Exception, "succeeded to access non-existent subtree that shadows a value key"); + } + catch (Dune::RangeError & r) {} + // try accessing key as subtree + try { + p.sub("x1"); + DUNE_THROW(Dune::Exception, "succeeded to access key as subtree"); + } + catch (Dune::RangeError & r) {} + // try accessing subtree as key + try { + p.template get("Foo"); + DUNE_THROW(Dune::Exception, "succeeded to access subtree as key"); + } + catch (Dune::RangeError & r) {} +} + +template +void testmodify(P parameterSet) +{ + parameterSet["testDouble"] = "3.14"; + parameterSet["testInt"] = "42"; + parameterSet["testString"] = "Hallo Welt!"; + parameterSet["testVector"] = "2 3 5 7 11"; + parameterSet.sub("Foo")["bar"] = "2"; + + double testDouble = parameterSet.template get("testDouble"); + int testInt = parameterSet.template get("testInt"); + ++testDouble; + ++testInt; + std::string testString = parameterSet.template get("testString"); + typedef Dune::FieldVector FVector; + FVector testFVector = parameterSet.template get("testVector"); + typedef std::vector SVector; + SVector testSVector = parameterSet.template get("testVector"); + if(testSVector.size() != 5) + DUNE_THROW(Dune::Exception, "Testing std::vector: expected " + "size()==5, got size()==" << testSVector.size()); + for(unsigned i = 0; i < 5; ++i) + if(testFVector[i] != testSVector[i]) + DUNE_THROW(Dune::Exception, + "testFVector[" << i << "]==" << testFVector[i] << " but " + "testSVector[" << i << "]==" << testSVector[i]); + if (parameterSet.template get("Foo.bar") != "2") + DUNE_THROW(Dune::Exception, "Failed to write subtree entry"); + if (parameterSet.sub("Foo").template get("bar") != "2") + DUNE_THROW(Dune::Exception, "Failed to write subtree entry"); +} + +void testOptionsParserResults(std::vector args, + const std::vector & keywords, + unsigned int required, + bool allow_more, + bool overwrite, + std::string foo, std::string bar, + const std::string referr = "") +{ + Dune::ParameterTree pt; + try { + char * argv[10]; + for (std::size_t i = 0; i < args.size(); ++i) + argv[i] = &args[i][0]; + Dune::ParameterTreeParser::readNamedOptions(args.size(), argv, pt, keywords, required, allow_more, overwrite); + check_assert(referr == ""); + } + catch (const Dune::ParameterTreeParserError & e) + { + std::string err = e.what(); + std::size_t offset = err.find("]: "); + err = err.substr(offset + 3, err.find('\n') - offset - 3); + check_assert(referr == err); + } + if (foo != "" && foo != pt.get("foo")) + DUNE_THROW(Dune::Exception, "Options parser failed... foo = " + << pt.get("foo") << " != " << foo); + if (bar != "" && bar != pt.get("bar")) + DUNE_THROW(Dune::Exception, "Options parser failed... bar = " + << pt.get("bar") << " != " << bar); +} + +void testOptionsParser() +{ + std::vector keywords = { "foo", "bar" }; + // check normal behaviour + { + std::vector args = { "progname", "--bar=ligapokal", "peng", "--bar=ligapokal", "--argh=other"}; + testOptionsParserResults(args,keywords,keywords.size(),true,true,"peng","ligapokal", + "" /* no error */ ); + } + // bail out on overwrite + { + std::vector args = { "progname", "--bar=ligapokal", "peng", "--bar=ligapokal", "--argh=other"}; + testOptionsParserResults(args,keywords,keywords.size(),true,false,"peng","ligapokal", + "parameter bar already specified"); + } + // bail out on unknown options + { + std::vector args = { "progname", "--bar=ligapokal", "peng", "--bar=ligapokal", "--argh=other"}; + testOptionsParserResults(args,keywords,keywords.size(),false,true,"peng","ligapokal", + "unknown parameter argh"); + } + // bail out on missing parameter + { + std::vector args = { "progname", "--bar=ligapokal"}; + testOptionsParserResults(args,keywords,keywords.size(),true,true,"","ligapokal", + "missing parameter(s) ... foo"); + } + // check optional parameter + { + std::vector args = { "progname", "--foo=peng"}; + testOptionsParserResults(args,keywords,1,true,true,"peng","", + "" /* no error */); + } + // check optional parameter, but bail out on missing parameter + { + std::vector args = { "progname", "--bar=ligapokal"}; + testOptionsParserResults(args,keywords,1,true,true,"","ligapokal", + "missing parameter(s) ... foo"); + } + // bail out on too many parameters + { + std::vector args = { "progname", "peng", "ligapokal", "hurz"}; + testOptionsParserResults(args,keywords,keywords.size(),true,true,"peng","ligapokal", + "superfluous unnamed parameter"); + } + // bail out on missing value + { + std::vector args = { "progname", "--foo=peng", "--bar=ligapokal", "--hurz"}; + testOptionsParserResults(args,keywords,keywords.size(),true,true,"peng","ligapokal", + "value missing for parameter --hurz"); + } +} + +void testFS1527() +{ + { // Check that junk at the end is not accepted (int) + Dune::ParameterTree ptree; + check_throw(ptree["setting"] = "0.5"; ptree.get("setting", 0), + Dune::RangeError); + } + { // Check that junk at the end is not accepted (double) + Dune::ParameterTree ptree; + check_throw(ptree["setting"] = "0.5 junk"; ptree.get("setting", 0.0), + Dune::RangeError); + } +} + +// check that negative values can be given on the command line +void testFS1523() +{ + static char arg0[] = "progname"; + static char arg1[] = "-setting"; + static char arg2[] = "-1"; + static char *argv[] = { arg0, arg1, arg2, NULL }; + int argc = sizeof argv / sizeof (char *) - 1; + + Dune::ParameterTree ptree; + Dune::ParameterTreeParser::readOptions(argc, argv, ptree); + + check_assert(ptree.get("setting") == -1); +} + +void check_recursiveTreeCompare(const Dune::ParameterTree & p1, + const Dune::ParameterTree & p2) +{ + check_assert(p1.getValueKeys() == p2.getValueKeys()); + check_assert(p1.getSubKeys() == p2.getSubKeys()); + typedef Dune::ParameterTree::KeyVector::const_iterator Iterator; + for (Iterator it = p1.getValueKeys().begin(); + it != p1.getValueKeys().end(); ++it) + check_assert(p1[*it] == p2[*it]); + for (Iterator it = p1.getSubKeys().begin(); + it != p1.getSubKeys().end(); ++it) + check_recursiveTreeCompare(p1.sub(*it), p2.sub(*it)); +} + +// test report method and read back in +void testReport() +{ + std::stringstream s; + s << "foo.i = 1 \n foo.bar.peng = hurz"; + Dune::ParameterTree ptree; + Dune::ParameterTreeParser::readINITree(s, ptree); + + std::stringstream s2; + ptree.report(s2); + Dune::ParameterTree ptree2; + Dune::ParameterTreeParser::readINITree(s2, ptree2); + check_recursiveTreeCompare(ptree, ptree2); +} + +int main() +{ + try { + // read config + std::stringstream s; + s << "x1 = 1 # comment\n" + << "x2 = hallo\n" + << "x3 = no\n" + << "array = 1 2 3 4 5\t6 7 8\n" + << "\n" + << "[Foo] # another comment\n" + << "peng = ligapokal\n"; + + auto c = Dune::ParameterTreeParser::readINITree(s); + + // test modifying and reading + testmodify(c); + try { + c.get("testInt"); + DUNE_THROW(Dune::Exception, "unexpected shallow copy of ParameterTree"); + } + catch (Dune::RangeError & r) {} + + // test for complex + c.get>("x1"); + + // more const tests + testparam(c); + + // check the command line parser + testOptionsParser(); + + // check report + testReport(); + + // check for specific bugs + testFS1527(); + testFS1523(); + } + catch (Dune::Exception & e) + { + std::cout << e << std::endl; + return 1; + } + return 0; +} diff --git a/dune/common/test/parmetistest.cc b/dune/common/test/parmetistest.cc new file mode 100644 index 0000000..c5622c4 --- /dev/null +++ b/dune/common/test/parmetistest.cc @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +#if ! HAVE_PARMETIS +#error "ParMETIS is required for this test." +#endif + +#include + +#if HAVE_PTSCOTCH_PARMETIS +extern "C" { + #include +} +#endif + +extern "C" { + #include +} + +int main(int argc, char **argv) +{ +#if defined(REALTYPEWIDTH) + using real_t = ::real_t; +#else + using real_t = float; +#endif + +#if defined(IDXTYPEWIDTH) + using idx_t = ::idx_t; +#elif HAVE_PTSCOTCH_PARMETIS + using idx_t = SCOTCH_Num; +#else + using idx_t = int; +#endif + + MPI_Init(&argc, &argv); + + MPI_Comm comm; + MPI_Comm_dup(MPI_COMM_WORLD, &comm); + + int rank, size; + MPI_Comm_rank(comm, &rank); + MPI_Comm_size(comm, &size); + + // This test is design for 3 cores + assert(size == 3); + + // local adjacency structure of the graph + std::vector xadj; // size n+1 + std::vector adjncy; // size 2*m + + if (rank == 0) { + xadj = std::vector{0,2,5,8,11,13}; + adjncy = std::vector{1,5,0,2,6,1,3,7,2,4,8,3,9}; + } + else if (rank == 1) { + xadj = std::vector{0,3,7,11,15,18}; + adjncy = std::vector{0,6,10,1,5,7,11,2,6,8,12,3,7,9,13,4,8,14}; + } + else if (rank == 2) { + xadj = std::vector{0,2,5,8,11,13}; + adjncy = std::vector{5,11,6,10,12,7,11,13,8,12,14,9,13}; + } + + // Array describing how the vertices of the graph are distributed among the processors. + std::vector vtxdist{0,5,10,15}; + + // No weights + idx_t wgtflag = 0; + // C-style numbering that starts from 0. + idx_t numflag = 0; + // Number of weights that each vertex has + idx_t ncon = 1; + // Number of sub-domains + idx_t nparts = size; + // Fraction of vertex weight that should be distributed to each sub-domain for each + // balance constraint + std::vector tpwgts(ncon * nparts, 1.0/nparts); + std::vector ubvec(ncon, 1.05); + std::vector options{0, 0, 0}; + + idx_t edgecut; + std::vector part(xadj.size()-1, 0); + + ParMETIS_V3_PartKway(vtxdist.data(), xadj.data(), adjncy.data(), + nullptr, nullptr, &wgtflag, &numflag, &ncon, &nparts, tpwgts.data(), + ubvec.data(), options.data(), &edgecut, part.data(), &comm); + + for (std::size_t part_i = 0; part_i < part.size(); ++part_i) { + std::cout << "[" << rank << "] " << part_i << " => " << part[part_i] << std::endl; + } + + MPI_Finalize(); + return 0; +} \ No newline at end of file diff --git a/dune/common/test/pathtest.cc b/dune/common/test/pathtest.cc new file mode 100644 index 0000000..4cf9df0 --- /dev/null +++ b/dune/common/test/pathtest.cc @@ -0,0 +1,205 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include + +void setCode(int& code, bool status) { + if(!status) + code = 1; + else + if(code == 77) + code = 0; +} + +void concatPathsTests(int& code) { + typedef const char* const triple[3]; + static const triple data[] = { + {"a" , "b" , "a/b" }, + {"/a", "b" , "/a/b"}, + {"a/", "b" , "a/b" }, + {"a" , "b/", "a/b/"}, + {"..", "b" , "../b"}, + {"a" , "..", "a/.."}, + {"." , "b" , "./b" }, + {"a" , "." , "a/." }, + {"" , "b" , "b" }, + {"a" , "" , "a" }, + {"" , "" , "" }, + {NULL, NULL, NULL } + }; + for(const triple* p = data; (*p)[0] != NULL; ++p) { + const std::string& result = Dune::concatPaths((*p)[0], (*p)[1]); + bool success = result == (*p)[2]; + setCode(code, success); + if(!success) + std::cerr << "concatPaths(\"" << (*p)[0] << "\", " + << "\"" << (*p)[1] << "\"): got \"" << result << "\", " + << "expected \"" << (*p)[2] << "\"" << std::endl; + } +} + +void processPathTests(int& code) { + typedef const char* const pair[3]; + static const pair data[] = { + {"" , "" }, + {"." , "" }, + {"./" , "" }, + {"a/.." , "" }, + {".." , "../" }, + {"../a" , "../a/"}, + {"a" , "a/" }, + {"a//" , "a/" }, + {"a///b" , "a/b/" }, + {"/" , "/" }, + {"/." , "/" }, + {"/.." , "/" }, + {"/a/.." , "/" }, + {"/a" , "/a/" }, + {"/a/" , "/a/" }, + {"/../a/", "/a/" }, + {NULL , NULL } + }; + for(const pair* p = data; (*p)[0] != NULL; ++p) { + const std::string& result = Dune::processPath((*p)[0]); + bool success = result == (*p)[1]; + setCode(code, success); + if(!success) + std::cerr << "processPath(\"" << (*p)[0] << "\"): got " + << "\"" << result << "\", expected " + << "\"" << (*p)[1] << "\"" << std::endl; + } +} + +void prettyPathTests(int& code) { + struct triple { + const char* p; + bool isDir; + const char* result; + }; + static const triple data[] = { + {"" , true , "." }, + {"" , false, "." }, + {"." , true , "." }, + {"." , false, "." }, + {"./" , true , "." }, + {"./" , false, "." }, + {"a/.." , true , "." }, + {"a/.." , false, "." }, + {".." , true , ".." }, + {".." , false, ".." }, + {"../a" , true , "../a/"}, + {"../a" , false, "../a" }, + {"a" , true , "a/" }, + {"a" , false, "a" }, + {"a//" , true , "a/" }, + {"a//" , false, "a" }, + {"a///b" , true , "a/b/" }, + {"a///b" , false, "a/b" }, + {"/" , true , "/" }, + {"/" , false, "/" }, + {"/." , true , "/" }, + {"/." , false, "/" }, + {"/.." , true , "/" }, + {"/.." , false, "/" }, + {"/a/.." , true , "/" }, + {"/a/.." , false, "/" }, + {"/a" , true , "/a/" }, + {"/a" , false, "/a" }, + {"/a/" , true , "/a/" }, + {"/a/" , false, "/a" }, + {"/../a/", true , "/a/" }, + {"/../a/", false, "/a" }, + {NULL, false, NULL } + }; + + Dune::ios_base_all_saver state(std::cerr); + std::cerr << std::boolalpha; + + for(const triple* p = data; p->p != NULL; ++p) { + const std::string& result = Dune::prettyPath(p->p, p->isDir); + bool success = result == p->result; + setCode(code, success); + if(!success) + std::cerr << "prettyPath(\"" << p->p << "\", " << p->isDir << "): got " + << "\"" << result << "\", expected \"" << p->result << "\"" + << std::endl; + } +} + +void relativePathTests(int& code) { + typedef const char* const triple[3]; + static const triple data[] = { + {"" , "" , "" }, + {"" , "b" , "b/" }, + {"" , "..", "../" }, + {"a" , "" , "../" }, + {"a" , "b" , "../b/"}, + {"/" , "/" , "" }, + {"/a", "/" , "../" }, + {"/" , "/b", "b/" }, + {"/a", "/b", "../b/"}, + {NULL, NULL, NULL } + }; + + for(const triple* p = data; (*p)[0] != NULL; ++p) { + const std::string& result = Dune::relativePath((*p)[0], (*p)[1]); + bool success = result == (*p)[2]; + setCode(code, success); + if(!success) + std::cerr << "relativePath(\"" << (*p)[0] << "\", " + << "\"" << (*p)[1] << "\"): got \"" << result << "\", " + << "expected \"" << (*p)[2] << "\"" << std::endl; + } + + typedef const char* const pair[2]; + static const pair except_data[] = { + {"" , "/" }, + {"a" , "/" }, + {"/" , "" }, + {"/" , "b" }, + {"..", "" }, + {NULL, NULL} + }; + + for(const pair* p = except_data; (*p)[0] != NULL; ++p) { + std::string result; + try { + result = Dune::relativePath((*p)[0], (*p)[1]); + } + catch(const Dune::NotImplemented&) { + setCode(code, true); + continue; + } + setCode(code, false); + std::cerr << "relativePath(\"" << (*p)[0] << "\", " + << "\"" << (*p)[1] << "\"): got \"" << result << "\", " + << "expected exception thrown" << std::endl; + } +} + +int main () +{ + try { + int code = 77; + + concatPathsTests(code); + processPathTests(code); + prettyPathTests(code); + relativePathTests(code); + + return code; + } + catch(const Dune::Exception& e) { + std::cerr << "Exception thrown: " << e << std::endl; + throw; + } +} diff --git a/dune/common/test/poolallocatortest.cc b/dune/common/test/poolallocatortest.cc new file mode 100644 index 0000000..2737b83 --- /dev/null +++ b/dune/common/test/poolallocatortest.cc @@ -0,0 +1,166 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include + +using namespace Dune; + +struct UnAligned +{ + char t; + char s; + char k; +}; + + + +template +struct testPoolMain +{ + static int test() + { + int ret=0; + + Pool pool; + + int elements = Pool::elements; + //int poolSize = Pool::size; + //int chunkSize = Pool::chunkSize; + //int alignedSize = Pool::alignedSize; + + std::vector oelements(10*elements); + + typedef typename Pool::Chunk Chunk; + + //Fill 10 chunks + for(int chunk=0; chunk < 10; ++chunk) { + //std::cout<< std::endl<<"Chunk "<(pool.allocate()); + //void* celement = reinterpret_cast(element); + //std::cout << element<<" "<< celement<<", "<(currentChunk->chunk_)); + std::uintptr_t end = reinterpret_cast(currentChunk->chunk_)+Pool::chunkSize; + + if(element< reinterpret_cast(currentChunk->chunk_)) + { + std::cerr <<" buffer overflow during first alloc: "<(currentChunk->chunk_) + <<">"<(pool.allocate()); + //celement = reinterpret_cast(element); + //std::cout << element<<" "<(currentChunk->chunk_)) { + std::cerr <<" buffer underflow during first alloc: "<(currentChunk->chunk_) + <<">"<element) { + std::cerr<<"allocated elements overlap!"<(oelements[i])); + + return ret; + } +}; + +template +int testPool() +{ + const std::size_t size = sizeof(T)>=2 ? sizeof(T)-2 : 0; + + int ret=0; + + std::cout<<"Checking "<(); + + ret+= testPool(); + + ret+= testPool(); + + ret += testPool >(); + + ret+=testPoolAllocator(); + + std::cout<< alignof(UnAligned) <<" "<(); + + return ret; +} diff --git a/dune/common/test/powertest.cc b/dune/common/test/powertest.cc new file mode 100644 index 0000000..ba9b600 --- /dev/null +++ b/dune/common/test/powertest.cc @@ -0,0 +1,60 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#include + +#include + +#include +#include +#include + +using namespace Dune; + +int main (int argc, char** argv) try +{ + // Zero and positive powers + if (power(4,0) != 1) + DUNE_THROW(MathError, "power(4,0) does not compute the correct result"); + + if (power(4,1) != 4) + DUNE_THROW(MathError, "power(4,1) implementation does not compute the correct result"); + + if (power(4,2) != 16) + DUNE_THROW(MathError, "power(4,2) implementation does not compute the correct result"); + + if (power(4,3) != 64) + DUNE_THROW(MathError, "power(4,3) implementation does not compute the correct result"); + + // Negative powers + if (power(4.0,-1) != 0.25) + DUNE_THROW(MathError, "power(4,-1) implementation does not compute the correct result"); + + if (power(4.0,-2) != 0.0625) + DUNE_THROW(MathError, "power(4,-2) implementation does not compute the correct result"); + + if (power(4.0,-3) != 0.015625) + DUNE_THROW(MathError, "power(4,-3) implementation does not compute the correct result"); + + // Test whether the result can be used in a compile-time expression + enum { dummy = power(2,2) }; + + // Test legacy power implementation + if (Power<0>::eval(4) != 1) + DUNE_THROW(MathError, "Power implementation does not compute the correct result"); + + if (Power<1>::eval(4) != 4) + DUNE_THROW(MathError, "Power implementation does not compute the correct result"); + + if (Power<2>::eval(4) != 16) + DUNE_THROW(MathError, "Power implementation does not compute the correct result"); + + if (Power<3>::eval(4) != 64) + DUNE_THROW(MathError, "Power implementation does not compute the correct result"); + + return 0; +} +catch (Exception& e) +{ + std::cout << e.what() << std::endl; +} diff --git a/dune/common/test/quadmathtest.cc b/dune/common/test/quadmathtest.cc new file mode 100644 index 0000000..1ba5a2f --- /dev/null +++ b/dune/common/test/quadmathtest.cc @@ -0,0 +1,140 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#include "config.h" + +#include +#include + +#include +#include +#include +#include +#include + +using namespace Dune; + +template +struct Comparator +{ + Comparator(T tol) + : tol_(tol) + {} + + bool operator()(T const& x, T const& y) + { + return Dune::FloatCmp::eq(x, y, tol_); + } + +private: + T tol_; +}; + +int main() +{ + // check vector and matrix type with Float128 field type + TestSuite test{}; + Comparator cmp{std::numeric_limits::epsilon() * 8}; + Comparator weakcmp{cbrt(std::numeric_limits::epsilon())}; + + // implicit conversion + Float128 x1 = 1; + Float128 x2 = 1.0f; + Float128 x3 = 1.0; + Float128 x4 = 1.0l; + + [[maybe_unused]] int z1 = x1; + [[maybe_unused]] float z2 = x2; + [[maybe_unused]] double z3 = x3; + [[maybe_unused]] long double z4 = x4; + + // field-vector + FieldVector v{1,2,3}, x; + FieldMatrix M{ {1,2,3}, {2,3,4}, {3,4,6} }, A; + FieldMatrix M2{ {1,2,3}, {2,3,4}, {3,4,7} }; + + auto y1 = v.one_norm(); + test.check(cmp(y1, 6.q), "vec.one_norm()"); + + auto y2 = v.two_norm(); + test.check(cmp(y2, sqrtq(14.q)), "vec.two_norm()"); + + auto y3 = v.infinity_norm(); + test.check(cmp(y3, 3.q), "vec.infinity_norm()"); + + M.mv(v, x); // x = M*v + M.mtv(v, x); // x = M^T*v + M.umv(v, x); // x+= M*v + M.umtv(v, x); // x+= M^T*v + M.mmv(v, x); // x-= M*v + M.mmtv(v, x); // x-= M^T*v + + auto w1 = M.infinity_norm(); + test.check(cmp(w1, 13.q), "mat.infinity_norm()"); + + auto w2 = M.determinant(); + test.check(cmp(w2, -1.q), "mat.determinant()"); + + M.solve(v, x); // x = M^(-1)*v + + [[maybe_unused]] auto M3 = M.leftmultiplyany(M2); + [[maybe_unused]] auto M4 = M.rightmultiplyany(M2); + + using namespace FMatrixHelp; + + invertMatrix(M,A); + + // test cmath functions for Float128 type + using T = Float128; + test.check(cmp(T(0.5), T("0.5")), "string constructor"); + + test.check(cmp(abs(T{-1}),T{1}), "abs"); + test.check(cmp(fabs(T{-1}),T{1}), "fabs"); + + test.check(cmp(cos(acos(T{0.5})),T{0.5}), "cos(acos)"); + test.check(cmp(cosh(acosh(T{1.5})),T{1.5}), "cosh(acosh)"); + test.check(cmp(sin(asin(T{0.5})),T{0.5}), "sin(asin)"); + test.check(cmp(sinh(asinh(T{0.5})),T{0.5}), "sinh(asinh)"); + test.check(cmp(tan(atan(T{0.5})),T{0.5}), "tan(atan)"); + test.check(cmp(atan2(T{1},T{2}), atan(T{0.5})), "atan2"); + test.check(cmp(tanh(atanh(T{0.5})),T{0.5}), "tanh(atanh)"); + + test.check(cmp(fdim(T{4},T{1}),T{3}), "fdim"); // a > b ? a - b : +0 + test.check(cmp(fma(T{0.5},T{0.4},T{1.8}),(T{0.5} * T{0.4}) + T{1.8}), "fma"); + test.check(cmp(fmax(T{0.6},T{0.4}),T{0.6}), "fmax"); + test.check(cmp(fmin(T{0.6},T{0.4}),T{0.4}), "fmin"); + test.check(cmp(hypot(T{1.6}, T{2.3}), sqrt(T{1.6}*T{1.6} + T{2.3}*T{2.3})), "hypot"); + // ilogb + test.check(cmp(llrint(T{2.3}),(long long int)(2)), "llrint"); + test.check(cmp(lrint(T{2.3}),(long int)(2)), "lrint"); + test.check(cmp(rint(T{2.3}),T{2}), "lrint"); + test.check(cmp(llround(T{2.3}),(long long int)(2)), "llround"); + test.check(cmp(lround(T{2.3}),(long int)(2)), "lround"); + test.check(cmp(round(T{2.3}),T{2}), "round"); + test.check(cmp(nearbyint(T{2.3}),T{2}), "nearbyint"); + test.check(cmp(trunc(T{2.7}),T{2}), "trunc"); + test.check(cmp(ceil(T{1.6}),T{2}), "ceil"); + test.check(cmp(floor(T{1.6}),T{1}), "floor"); + + test.check(cmp(log(exp(T{1.5})),T{1.5}), "log(exp)"); + test.check(cmp(exp(T{0.2}+T{0.4}), exp(T{0.2})*exp(T{0.4})), "exp"); // exp(a+b) = exp(a)*exp(b) + test.check(cmp(expm1(T{0.6}),exp(T{0.6})-T{1}), "expm1"); + test.check(cmp(log10(T{1000}),T{3}), "log10"); + test.check(cmp(log2(T{8}),T{3}), "log2"); + test.check(cmp(log1p(T{1.6}),log(T{1} + T{1.6})), "log1p"); + // nextafter + + // these two functions produce larger errors + test.check(weakcmp(fmod(T{5.1},T{3}),T{2.1}), "fmod"); + test.check(weakcmp(remainder(T{5.1},T{3}),T{-0.9}), "remainder"); + + test.check(cmp(pow(T{2},T{3}),T{8}), "pow"); + test.check(cmp(pow(T{M_PIq},T{3}),pow(T{M_PIq},3)), "pow"); // compare pow with float exponent and integer exponent + test.check(cmp(cbrt(T{0.5*0.5*0.5}),T{0.5}), "cbrt"); + test.check(cmp(sqrt(T{4}),T{2}), "sqrt"); + + test.check(cmp(erf(T{0}),T{0}), "erf"); + test.check(cmp(erfc(T{0.6}), T{1}-erf(T{0.6})), "erfc"); + test.check(cmp(lgamma(T{3}),log(T{2})), "lgamma"); + test.check(cmp(tgamma(T{3}),T{2}), "tgamma"); +} diff --git a/dune/common/test/rangeutilitiestest.cc b/dune/common/test/rangeutilitiestest.cc new file mode 100644 index 0000000..16b9200 --- /dev/null +++ b/dune/common/test/rangeutilitiestest.cc @@ -0,0 +1,320 @@ +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +template +auto checkRangeIterators(R&& r) +{ + auto it = r.begin(); + auto end = r.end(); + auto op = [](const auto& x){}; + return (testConstIterator(it, end, op)==0); +} + +template +auto checkRangeSize(R&& r) +{ + std::size_t counter = 0; + for([[maybe_unused]] auto&& dummy : r) + ++counter; + return (r.size()==counter); +} + +template +auto checkRandomAccessNumberRangeSums(R&& r, V sum, V first, V last) +{ + bool passed = true; + passed = passed and (std::accumulate(r.begin(), r.end(), 0) == sum); + passed = passed and (std::accumulate(r.begin()+1, r.end(), 0) == (sum-first)); + passed = passed and (std::accumulate(r.begin(), r.end()-1, 0) == (sum-last)); + passed = passed and (std::accumulate(r.begin()+1, r.end()-1, 0) == (sum-first-last)); + return passed; +} + +template +struct is_const_reference : public std::conjunction, std::is_const>> +{}; + +template +struct is_mutable_reference : public std::conjunction, std::negation>> +{}; + + +auto testTransformedRangeView() +{ + Dune::TestSuite suite("Check transformedRangeView()"); + + // Check transformedRangeView with container range + Dune::Hybrid::forEach(std::make_tuple(std::array({1,2,3}), std::vector({1,2,3})), [&](auto&& a) + { + auto a_backup = a; + // Pass original range by l-value, modify it, and then traverse. + // This should traverse the modified original range. + { + auto r = Dune::transformedRangeView(a, [](auto&& x) { return 2*x;}); + a[0] = 2; + suite.check(checkRandomAccessNumberRangeSums(r, 14, 4, 6)) + << "incorrect values in transformedRangeView of l-value"; + suite.check(checkRangeIterators(r)) + << "iterator test fails for transformedRangeView of l-value"; + suite.check(checkRangeSize(r)) + << "checking size fails for transformedRangeView of l-value"; + a = a_backup; + } + // Pass original range by const l-value, modify it, and then traverse. + // This should traverse the modified original range. + { + const auto& a_const = a; + auto r = Dune::transformedRangeView(a_const, [](auto&& x) { return 2*x;}); + a[0] = 2; + suite.check(checkRandomAccessNumberRangeSums(r, 14, 4, 6)) + << "incorrect values in transformedRangeView of const l-value"; + suite.check(checkRangeIterators(r)) + << "iterator test fails for transformedRangeView of const l-value"; + suite.check(checkRangeSize(r)) + << "checking size fails for transformedRangeView of const l-value"; + a = a_backup; + } + // Modify original range, pass it by r-value, restore it, and then traverse. + // This should traverse a modified copy of the range and not the restored + // original one. + { + a[0] = 2; + auto r = Dune::transformedRangeView(std::move(a), [](auto&& x) { return 2*x;}); + a = a_backup; + suite.check(checkRandomAccessNumberRangeSums(r, 14, 4, 6)) + << "incorrect values in transformedRangeView of r-value"; + suite.check(checkRangeIterators(r)) + << "iterator test fails for transformedRangeView of r-value"; + suite.check(checkRangeSize(r)) + << "checking size fails for transformedRangeView of r-value"; + } + // Check if returning real references in the transformation works + { + auto r = Dune::transformedRangeView(a, [](auto&& x) -> decltype(auto) { return x;}); + suite.check(is_mutable_reference::value) + << "iterator with mutable-reference returning transformation does not return mutable references"; + suite.check(&(*(r.begin())) == &(a[0])) + << "reference points to wrong location"; + (*r.begin()) = 0; + suite.check(a[0] == 0) + << "modifying range by reference returning transformation failed"; + a = a_backup; + } + // Check if returning real references in the transformation works + { + const auto& a_const = a; + auto r = Dune::transformedRangeView(a_const, [](auto&& x) -> decltype(auto) { return x;}); + suite.check(is_const_reference::value) + << "iterator with const-reference returning transformation does not return const references"; + suite.check(&(*(r.begin())) == &(a[0])) + << "reference points to wrong location"; + } + // Check iterator based transformation + { + auto r = Dune::iteratorTransformedRangeView(a, [&](auto&& it) { return (*it)+(it-a.begin());}); + suite.check(checkRandomAccessNumberRangeSums(r, 9, 1, 5)) + << "incorrect values in transformedRangeView of l-value"; + suite.check(checkRangeIterators(r)) + << "iterator test fails for transformedRangeView of l-value"; + suite.check(checkRangeSize(r)) + << "checking size fails for transformedRangeView of l-value"; + a = a_backup; + } + }); + // Check transformedRangeView with on the fly range + { + auto r = Dune::transformedRangeView(Dune::range(10), [](auto&& x) { return 2*x;}); + suite.check(checkRandomAccessNumberRangeSums(r, 90, 0, 18)) + << "transformation of on-the-fly range gives incorrect results"; + suite.check(checkRangeIterators(r)) + << "iterator test fails for transformedRangeView"; + suite.check(checkRangeSize(r)) + << "checking size fails for transformedRangeView of on-the-fly range"; + } + // Check if we can indirectly sort subrange via reference returning transformations + { + auto a = std::vector{4,3,2,1,0}; + auto r = Dune::transformedRangeView(Dune::range(1,4), [&](auto&& i) -> decltype(auto){ return a[i];}); + std::sort(r.begin(), r.end()); + suite.check(a == std::vector{4,1,2,3,0}) + << "sorting reference returning transformedRangeView failed"; + + auto r2 = Dune::transformedRangeView(std::array{0, 2, 4}, [&](auto&& i) -> decltype(auto){ return a[i];}); + std::sort(r2.begin(), r2.end()); + suite.check(a == std::vector{0,1,2,3,4}) + << "sorting reference returning transformedRangeView failed"; + + // Remap values of certain keys in a std::map such that + // they are sorted according to the keys. + auto m = std::map{{-1,5},{0,4}, {1,3}, {2,2}}; + auto r3 = Dune::transformedRangeView(std::array{1, -1, 2}, [&](auto&& i) -> decltype(auto){ return m[i];}); + std::sort(r3.begin(), r3.end()); + suite.check(m == std::map{{1,2},{-1,3}, {2,5},{0,4}}) + << "sorting reference returning transformedRangeView failed"; + } + return suite; +} + + +auto testSparseRange() +{ + Dune::TestSuite suite("Check sparseRange()"); + + auto checkWithMatrix = [&suite](auto&& M) { + for(std::size_t i=0; i({42}); + checkWithMatrix(M1); + checkWithMatrix(std::as_const(M1)); + + auto M2 = Dune::DiagonalMatrix({42, 41}); + checkWithMatrix(M2); + checkWithMatrix(std::as_const(M2)); + + auto M3 = Dune::DiagonalMatrix({42, 41, 40}); + checkWithMatrix(M3); + checkWithMatrix(std::as_const(M3)); + + return suite; +} + + + +int main() +{ + // Check IsIterable<> for https://gitlab.dune-project.org/core/dune-common/issues/58 + static_assert(Dune::IsIterable< std::array >::value, "std::array must be a range"); + static_assert(Dune::IsIterable< Dune::IteratorRange >::value, "IteratorRange must be a range"); + static_assert(!Dune::IsIterable< int >::value, "int must not be a range"); + + Dune::TestSuite suite; + + // max_value, min_value + { + const int value = 12; + suite.check(Dune::max_value(value) == value); + suite.check(Dune::min_value(value) == value); + + std::array values{-42, 0, 42}; + suite.check(Dune::max_value(values) == 42) + << "maximum of values is 42, but got " << Dune::max_value(values); + suite.check(Dune::min_value(values) == -42) + << "minimum of values is -42, but got " << Dune::min_value(values); + + std::array positiveValues{1, 2, 3}; + suite.check(Dune::max_value(positiveValues) == 3) + << "maximum of positiveValues is 3, but got " << Dune::max_value(positiveValues); + suite.check(Dune::min_value(positiveValues) == 1) + << "minimum of positiveValues is 1, but got " << Dune::min_value(positiveValues); + + std::array negativeValues{-1, -3, -1}; + suite.check(Dune::max_value(negativeValues) == -1) + << "maximum of negativeValues is -1, but got " << Dune::max_value(negativeValues); + suite.check(Dune::min_value(negativeValues) == -3) + << "minimum of negativeValues is -3, but got " << Dune::min_value(negativeValues); + } + + // any_true, all_true + { + const std::array allTrue{true, true, true}; + const std::array allFalse{false, false, false}; + const std::array someTrue{false, true, false}; + + suite.check(Dune::any_true(allTrue)) + << "any_true(allTrue) must be true"; + suite.check(!Dune::any_true(allFalse)) + << "any_true(allFalse) must be false"; + suite.check(Dune::any_true(someTrue)) + << "any_true(someTrue) must be true"; + + suite.check(Dune::all_true(allTrue)) + << "all_true(allTrue) must be true"; + suite.check(!Dune::all_true(allFalse)) + << "all_true(allFalse) must be false"; + suite.check(!Dune::all_true(someTrue)) + << "all_true(someTrue) must be false"; + + const bool t = true; + const bool f = false; + + suite.check(Dune::any_true(t)) + << "any_true(true) must be true"; + suite.check(!Dune::any_true(f)) + << "any_true(false) must be false"; + + suite.check(Dune::all_true(t)) + << "all_true(true) must be true"; + suite.check(!Dune::all_true(f)) + << "all_true(false) must be false"; + } + + // integer ranges + using Dune::range; + std::vector numbers(range(6).begin(), range(6).end()); + int sum = 0; + for( auto i : range(numbers.size()) ) + sum += numbers[i]; + suite.check(sum == 15) << "sum over range( 0, 6 ) must be 15."; + suite.check(range(sum, 100)[5] == 20) << "range(sum, 100)[5] must be 20."; + sum = 0; + for( auto i : range(-10, 11) ) + sum += i; + suite.check(sum == 0) << "sum over range( -10, 11 ) must be 0."; + + static_assert(std::is_same()))::integer_sequence, std::make_integer_sequence>::value, + "decltype(range(std::integral_constant))::integer_sequence must be the same as std::make_integer_sequence"); + + // Hybrid::forEach for integer ranges + Dune::Hybrid::forEach(range(std::integral_constant()), [] (auto &&i) { + static_assert(std::is_same, std::integral_constant>::value, + "Hybrid::forEach(range(std::integral_constant()), ...) should only visit std::integral_constant."); + }); + + + { + auto r = range(-10,11); + auto it = r.begin(); + auto end = r.end(); + auto op = [](const auto& x){}; + suite.check(testConstIterator(it, end, op)==0) + << "iterator test fails for range(-10,11)"; + } + + suite.subTest(testTransformedRangeView()); + + suite.subTest(testSparseRange()); + + return suite.exit(); + +} diff --git a/dune/common/test/reservedvectortest.cc b/dune/common/test/reservedvectortest.cc new file mode 100644 index 0000000..0be8fdc --- /dev/null +++ b/dune/common/test/reservedvectortest.cc @@ -0,0 +1,62 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include + +int main() { + Dune::TestSuite test; + // check that make_array works + Dune::ReservedVector rv = {3,2,1}; + test.check(rv.size() == 3); + test.check(rv.back() == 1); + test.check(rv.front() == 3); + + // check assignment from an initializer list + rv = {1,2,3,4}; + test.check(rv.size() == 4); + test.check(rv.back() == 4); + test.check(rv.front() == 1); + + // check push_back + rv.push_back(5); + test.check(rv.size() == 5); + test.check(rv.back() == 5); + + // check copy constructor + Dune::ReservedVector rv2 = rv; + test.check(rv2[0] == 1 && + rv2[1] == 2 && + rv2[2] == 3 && + rv2[3] == 4 && + rv2[4] == 5); + + // check pop_back + rv2.pop_back(); + test.check(rv2.size() == 4); + test.check(rv2.back() == 4); + + // make sure we can hash a reserved vector + std::hash< Dune::ReservedVector > rv_hash; + auto hash_value = rv_hash(rv); + auto hash_value2 = rv_hash(rv2); + test.check( hash_value != hash_value2 ); + + // try using an unordered map + std::unordered_map< Dune::ReservedVector, double > rv_map; + rv_map[rv] = 1.0; + rv_map[rv2] = 2.0; + + // try and try again with a const ReservedVector + std::unordered_map< const Dune::ReservedVector, double> const_rv_map; + + return 0; +} diff --git a/dune/common/test/scotchtest.cc b/dune/common/test/scotchtest.cc new file mode 100644 index 0000000..25f30f3 --- /dev/null +++ b/dune/common/test/scotchtest.cc @@ -0,0 +1,57 @@ +#include + +#include +#include + +#include + +#include + +// write graph to file +void prepare (std::string filename) +{ + std::ofstream out(filename, std::ios_base::out); + + // out << "0\n4 4\n0 000\n1 1\n1 0\n1 3\n1 2"; + + out << "0\n16 48\n0 000\n2 1 4\n3 0 2 5\n3 1 3 6\n2 2 7\n3 0 5 8\n4 1 4 6 9\n4 2 5 7 10\n3 3 6 11\n3 4 9 12\n4 5 8 10 13\n4 6 9 11 14\n3 7 10 15\n2 8 13\n3 9 12 14\n3 10 13 15\n2 11 14"; +} + +int main (int argc, char** argv) +{ + SCOTCH_errorProg (argv[0]); + + // Initialize source graph + SCOTCH_Graph grafdat; + if (SCOTCH_graphInit (&grafdat) != 0) { + DUNE_THROW(Dune::Exception, "cannot initialize graph"); + } + + prepare("graph_file.grf"); + + FILE* fileptr = nullptr; + if ((fileptr = fopen ("graph_file.grf", "r")) == nullptr) { + DUNE_THROW(Dune::Exception, "cannot open file"); + } + + // Read source graph + if (SCOTCH_graphLoad (&grafdat, fileptr, -1, 0) != 0) { + DUNE_THROW(Dune::Exception, "cannot load graph"); + } + + fclose (fileptr); + + if (SCOTCH_graphCheck (&grafdat) != 0) { + DUNE_THROW(Dune::Exception, "graph check failed"); + } + + SCOTCH_Num vertnbr = 0, edgenbr = 0; + SCOTCH_graphSize (&grafdat, &vertnbr, &edgenbr); + + std::cout << "Number of vertices: " << vertnbr << std::endl; + std::cout << "Number of edges: " << edgenbr << std::endl; + + SCOTCH_graphExit (&grafdat); + + return EXIT_SUCCESS; +} diff --git a/dune/common/test/shared_ptrtest.cc b/dune/common/test/shared_ptrtest.cc new file mode 100644 index 0000000..6b85b82 --- /dev/null +++ b/dune/common/test/shared_ptrtest.cc @@ -0,0 +1,47 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +// make sure assert works even when not compiling for debugging +#ifdef NDEBUG +#undef NDEBUG +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + + +class A {}; +class B : public A {}; +class C : A {}; + + +int main(){ + using namespace Dune; + int ret=0; + { + // test shared_ptr for stack allocation + { + int i = 10; + std::shared_ptr pi = stackobject_to_shared_ptr(i); + } + + // test shared_ptr for stack allocation with down cast + { +DUNE_NO_DEPRECATED_BEGIN + B b2; + std::shared_ptr pa = stackobject_to_shared_ptr(b2); +DUNE_NO_DEPRECATED_END +#ifdef SHARED_PTR_COMPILE_FAIL + C c; + pa = stackobject_to_shared_ptr(c); // A is an inaccessible base of C +#endif + } + } + return (ret); +} diff --git a/dune/common/test/singletontest.cc b/dune/common/test/singletontest.cc new file mode 100644 index 0000000..8d25fc3 --- /dev/null +++ b/dune/common/test/singletontest.cc @@ -0,0 +1,100 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +class Foo : public Dune::Singleton +{ +public: + Foo() + { + bytes = new char[1000]; + } + + ~Foo() + { + delete[] bytes; + } +private: + char* bytes; +}; + +class Foo1 +{ +public: + Foo1() + { + bytes = new char[1000]; + } + + ~Foo1() + { + delete[] bytes; + } +private: + char* bytes; +}; + +typedef Dune::Singleton FooSingleton; + + +Foo* globalFoo = 0; +Foo1* globalFoo1 = 0; + +void setFoo() +{ + globalFoo = &Foo::instance(); +} + + +void setFoo1() +{ + globalFoo1 = &FooSingleton::instance(); +} + +int testFoo() +{ + if(globalFoo != &Foo::instance()) { + std::cerr<<" Foo is not a real singleton!"< +#include +#include +#include + +class DoubleWrapper +{ +public: + DoubleWrapper(double b) + : d(b) + { + std::cout<<"Constructed "< IntAllocator; +//typedef std::allocator IntAllocator; +typedef Dune::PoolAllocator DoubleAllocator; +//typedef std::allocator DoubleAllocator; +typedef Dune::PoolAllocator DoubleWAllocator; +//typedef std::allocator DoubleWAllocator; + +template +const T& tail(const Dune::SLList& alist) +{ + typedef typename Dune::SLList::const_iterator Iterator; + Iterator tail=alist.begin(); + + for(int i = alist.size() - 1; i > 0; --i) + ++tail; + return *tail; +} + +template +int check(const Dune::SLList& alist, const T* vals) +{ + typedef typename Dune::SLList::const_iterator iterator; + int i=0; + for(iterator iter = alist.begin(); iter != alist.end(); ++iter, i++) { + if( vals[i] != *iter ) { + std::cerr<<" List missmatch! "<<__FILE__<<":"<<__LINE__< +void randomizeListBack(Dune::SLList& alist){ + using namespace Dune; + + srand(300); + + int lowest=0, highest=1000, range=(highest-lowest)+1; + + T vals[10]; + + for(int i=0; i < 10; i++) { + T d = T(range*(rand()/(RAND_MAX+1.0))); + alist.push_back(d); + vals[i]=d; + } + + check(alist, vals); +} + +template +void randomizeListFront(Dune::SLList& alist){ + using namespace Dune; + + srand(300); + T vals[10]; + + int lowest=0, highest=1000, range=(highest-lowest)+1; + + for(int i=0; i < 10; i++) { + T d = T(range*(rand()/(RAND_MAX+1.0))); + alist.push_front(d); + vals[9-i]=d; + } + + check(alist, vals); +} +int testAssign() +{ + typedef Dune::SLList List; + List alist, blist; + + alist.push_back(3); + alist.push_back(4); + alist.push_back(5); + + blist.push_back(-1); + + blist=alist; + List::iterator biter=blist.begin(), aiter=alist.begin(); + for(; aiter!=alist.end(); ++aiter, ++biter) + if(*aiter!=*biter) { + std::cerr<<"Asignment failed "<<__FILE__<<":"<<__LINE__< List; + List alist; + + alist.push_back(3); + alist.push_back(4); + alist.push_back(5); + + List::ModifyIterator iter = alist.beginModify(); + iter.remove(); + if(*(alist.begin())!=4) { + std::cerr<<"delete next on position before head failed! "<<__FILE__<<":"<<__LINE__< List; + int ret = 0; + + List alist; + if(!alist.empty()) { + std::cerr<<"Newly created list not empty! "<<__FILE__<<":"<<__LINE__<0; --elements) + alist.pop_front(); + + if(!alist.empty()) { + std::cerr<<"Emptied list not empty! "<<__FILE__<<":"<<__LINE__< List; + //typedef Dune::SLList List; + + List alist; + + alist.push_back(3); + List::ModifyIterator iter=alist.beginModify(); + iter.insert(7); + int ret=0; + + if(*iter!=3) { + std::cerr<<"Value at current position changed due to insert! "<<__FILE__<<":"<<__LINE__< alist; + //std::cout<<"PushPop 1:"< list; + Dune::SLList list, list1; + Dune::SLList list2; + + randomizeListBack(list1); + randomizeListFront(list); + + Dune::SLList copied(list); + if(copied.size()!=list.size()) { + std::cerr << "Size of copied list does not match!"<::const_iterator Iterator; + Iterator iend = list.end(); + for(Iterator iter1=list.begin(), iter2=copied.begin(); iter1 != iend; ++iter1, ++iter2) + if(*iter1!=*iter2) { + std::cerr << "Entries of copied are not the same!"<::ModifyIterator>::value_type> print; + + Dune::SLList::ModifyIterator lbegin = list.beginModify(), lend = list.endModify(); + + double& d = lbegin.dereference(); + + d=2.0; + + double& d1 = lbegin.dereference(); + + d1=3.0; + + lbegin.dereference()=5.0; + + lbegin.operator*()=5.0; + + *lbegin=5.0; + + std::cout << "Testing ConstIterator "< +#include +#include + +#include + +#include + + +template +void nop(std::initializer_list&&) +{} + + +int main() +{ + auto test_args = std::make_tuple(true, 2, 3, "abc"); + + Dune::TestSuite test; + + auto concat = [](auto&&... args) { + bool first = true; + std::stringstream stream; + nop({(stream << (first ? "":",") << args, first = false)...}); + return stream.str(); + }; + + test.check(Dune::Std::apply(concat, test_args) == "1,2,3,abc") << "Dune::Std::apply failed with concat lambda"; + + auto makeTuple = [](auto&&... args) { + return std::make_tuple(args...); + }; + + test.check(Dune::Std::apply(makeTuple, test_args) == test_args) << "Dune::Std::apply failed with makeTuple lambda"; + + auto intTuple = std::make_tuple(1,2,3); + auto&& intTuple0 = Dune::Std::apply([](auto&& arg0, auto&&... /*args*/) -> decltype(auto) { return arg0; }, intTuple); + intTuple0 = 42; + + test.check(std::get<0>(intTuple) == intTuple0) << "Dune::Std::apply does not properly return references"; + + // transformTuple implemented using Std::apply + auto transformTuple = [](auto&& t, auto&& f) { + return Dune::Std::apply([&](auto&&... args) { + return std::make_tuple((f(std::forward(args)))...); + }, t); + }; + + auto t1 = std::make_tuple(1, 0.2); + auto t2 = transformTuple(t1, [](auto&& x) { return 1.0/x; }); + + test.check(t2 == std::make_tuple(1.0, 5.0)) << "transformTuple implementation based on Dune::Std::apply fails"; + + return test.exit(); +} diff --git a/dune/common/test/stdidentity.cc b/dune/common/test/stdidentity.cc new file mode 100644 index 0000000..dea8a99 --- /dev/null +++ b/dune/common/test/stdidentity.cc @@ -0,0 +1,49 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include + +struct Foo { + static int count; + Foo() { ++count; std::cout << "construct" << std::endl; } + Foo(const Foo&) { ++count; std::cout << "copy construct" << std::endl; } + Foo(Foo&&) { ++count; std::cout << "move construct" << std::endl; } + ~Foo() { --count; std::cout << "deconstruct" << std::endl; } +}; +int Foo::count = 0; + +template +T&& assert_count(T&& arg, int count) +{ + std::cout << std::decay_t::count << std::endl; + if (std::decay_t::count != count) + std::cerr << "Passed count does not match state of the argument" << std::endl; + return std::forward(arg); +} + +int main() +{ + auto id = Dune::Std::identity(); + + assert_count(id(Foo()),1); // pass an r-value to identity, still constructed on the assert + + const auto& foo0 = id(Foo()); // pass an r-value to identity + assert_count(foo0,0); // id(Foo()) is alredy doconstructed at this point + + auto foo1 = id(Foo()); // pass an r-value to identity and move it to foo1 + assert_count(foo1,1); // foo0 is alredy doconstructed at this point + + Foo foo2; + assert_count(id(foo2),2); // pass an l-value to identity + + const auto& foo3 = id(foo2); // pass an l-value to identity + assert_count(foo3,2); // foo still exist at this point + + auto foo4 = id(foo2); // pass an l-value to identity and copy its result + assert_count(foo4,3); // copy of foo still exist at this point +} diff --git a/dune/common/test/streamoperatorstest.cc b/dune/common/test/streamoperatorstest.cc new file mode 100644 index 0000000..8ee7f7b --- /dev/null +++ b/dune/common/test/streamoperatorstest.cc @@ -0,0 +1,46 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include + +using Dune::operator>>; +using Dune::operator<<; + +int main() +{ + typedef std::tuple Tuple; + + { + const Tuple t{1, 2, 3}; + const std::string expected = "[1,2,3]"; + + std::ostringstream out; + out << t; + + if( out.str() != expected ) + return 1; + } + + { + const std::string data = "1 2 3"; + const Tuple expected{1, 2, 3}; + + std::istringstream in(data); + Tuple t; + in >> t; + + if( t != expected ) + return 1; + } + + return 0; +} diff --git a/dune/common/test/streamtest.cc b/dune/common/test/streamtest.cc new file mode 100644 index 0000000..8923941 --- /dev/null +++ b/dune/common/test/streamtest.cc @@ -0,0 +1,55 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +/* + + Test to check if the standard streams in libdune can be properly + linked with this program and if they work + + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include + +#include + +// enums are a nice special case (was a bug) +enum check { VALUE = 5 }; + +int main () { + try { + // let output happen but vanish + std::ofstream dummy("/dev/null"); + Dune::derr.attach(dummy); + + Dune::derr.push(true); + Dune::derr << "Teststring" << std::endl; + + Dune::derr << VALUE << std::endl; + Dune::dverb << VALUE << std::endl; + Dune::dvverb << VALUE << std::endl; + Dune::dinfo << VALUE << std::endl; + Dune::dwarn << VALUE << std::endl; + Dune::dgrave << VALUE << std::endl; + + // instantiate private stream and connect global stream + { + Dune::DebugStream<> mystream(dummy); + Dune::derr.tie(mystream); + Dune::derr << "Blah" << std::endl; + // untie before mystream gets destructed + Dune::derr.untie(); + } + + Dune::derr << "Still working" << std::endl; + } catch (Dune::Exception &e) { + std::cerr << e << std::endl; + return 2; + } catch (...) { + return 1; + }; + + return 0; +} diff --git a/dune/common/test/stringutilitytest.cc b/dune/common/test/stringutilitytest.cc new file mode 100644 index 0000000..4539382 --- /dev/null +++ b/dune/common/test/stringutilitytest.cc @@ -0,0 +1,58 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include + +namespace { +const std::string hello_world("hello world"); +} /* namespace */ + +bool test_hasPrefix() +{ + bool pass = true; + + using Dune::hasPrefix; + pass &= hasPrefix(hello_world, "hello"); + pass &= !hasPrefix(hello_world, "world"); + + return pass; +} + +bool test_hasSuffix() +{ + bool pass = true; + + using Dune::hasSuffix; + pass &= hasSuffix(hello_world, "world"); + pass &= !hasSuffix(hello_world, "hello"); + + return pass; +} + +bool test_formatString() +{ + bool pass = true; + const int one = 1; + const static std::string format("hello %i"); + const static std::string expected("hello 1"); + + using Dune::formatString; + const std::string s = formatString(format, one); + pass &= (s == expected); + + return pass; +} + +int main() +{ + bool pass = true; + + pass &= test_hasPrefix(); + pass &= test_hasSuffix(); + pass &= test_formatString(); + + return pass ? 0 : 1; +} diff --git a/dune/common/test/testdebugallocator.cc b/dune/common/test/testdebugallocator.cc new file mode 100644 index 0000000..6cfc944 --- /dev/null +++ b/dune/common/test/testdebugallocator.cc @@ -0,0 +1,114 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// #define DEBUG_ALLOCATOR_KEEP 1 +#define DEBUG_NEW_DELETE 3 + +#include +#if HAVE_MPROTECT + +#include +#include +#include +#include + +class A +{ +public: + A() { std::cout << "INIT A\n"; } + int x; + void foo() {}; +}; + +void basic_tests () +{ + using Dune::DebugMemory::alloc_man; + + size_t s = 256; + double * x = alloc_man.allocate(s); + x[s-1] = 10; + + // access out of bounds +#ifdef FAILURE1 + x[s+1] = 1; +#endif + + // lost allocation, free and double-free +#ifndef FAILURE2 + alloc_man.deallocate(x); +#endif +#ifdef FAILURE3 + alloc_man.deallocate(x); +#endif + + // access after free +#ifdef FAILURE4 + x[s-1] = 10; +#endif +} + +void allocator_tests() +{ + std::vector > v; + v.push_back(10); + v.push_back(12); + v.size(); + std::cout << v[0] << "\n"; + std::cout << v[1] << "\n"; +#ifdef FAILURE5 + std::cout << v[v.capacity()] << "\n"; +#endif +} + +void new_delete_tests() +{ + std::cout << "alloc double[3]\n"; + double * y = new double[3]; + delete[] y; + + std::cout << "alloc A[2]\n"; + A * z = new A[2]; + z->foo(); + delete[] z; + z = 0; + + std::cout << "alloc (buf) A[3]\n"; + char * buf = (char*)malloc(128); + A * z2 = new (buf) A[3]; + z2->foo(); + free(buf); + z2 = 0; + + std::cout << "alloc A[4]\n"; + A * z4 = ::new A[4]; + z4->foo(); + ::delete[] z4; + z4 = 0; +} + +#endif // HAVE_MPROTECT + +int main(int, char**) +{ +#if EXPECTED_SIGNAL + std::signal(EXPECTED_SIGNAL, std::_Exit); +#endif +#if EXPECTED_ALT_SIGNAL + std::signal(EXPECTED_ALT_SIGNAL, std::_Exit); +#endif + +#if HAVE_MPROTECT + basic_tests(); + allocator_tests(); + new_delete_tests(); +#endif + +#ifdef EXPECTED_SIGNAL + return 1; +#else + return 0; +#endif +} diff --git a/dune/common/test/testfloatcmp.cc b/dune/common/test/testfloatcmp.cc new file mode 100644 index 0000000..3ed8554 --- /dev/null +++ b/dune/common/test/testfloatcmp.cc @@ -0,0 +1,217 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +// Test the new (Dune) interface of float_cmp + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +using std::cout; +using std::endl; +using std::flush; + +///////////////////////// +// +// compile time checks +// + +// check that we can access the functions as FloatCmp::function from within the Dune namespace +namespace Dune { + void checkNamespaceAccess() { + FloatCmp::eq(0.0, 0.0); + } +} // namespace Dune + // check that we can access the functions as FloatCmp::function with using namespace Dune +void checkUsingAccess() { + using namespace Dune; + FloatCmp::eq(0.0, 0.0); +} + +// run time checks +const char* repr(bool b) { + if(b) return "true "; + else return "false"; +} + +int passed = 0; +int failed = 0; + +void count(bool pass) { + if(pass) { cout << "passed"; ++passed; } + else { cout << "failed"; ++failed; } +} + +template +void tests(F f1, F f2, typename Dune::FloatCmp::EpsilonType::Type eps, bool inside) +{ + bool result; + + cout << "eq(" << f1 << ", " << f2 << ", " << eps << ") = " << flush; + result = Dune::FloatCmp::eq(f1, f2, eps); + cout << repr(result) << "\t"; + count(result == inside); + cout << endl; + + cout << "ge(" << f1 << ", " << f2 << ", " << eps << ") = " << flush; + result = Dune::FloatCmp::ge(f1, f2, eps); + cout << repr(result) << "\t"; + count(result == (inside || f1 > f2)); + cout << endl; + + cout << "le(" << f1 << ", " << f2 << ", " << eps << ") = " << flush; + result = Dune::FloatCmp::le(f1, f2, eps); + cout << repr(result) << "\t"; + count(result == (inside || f1 < f2)); + cout << endl; + + cout << "ne(" << f1 << ", " << f2 << ", " << eps << ") = " << flush; + result = Dune::FloatCmp::ne(f1, f2, eps); + cout << repr(result) << "\t"; + count(result == !inside); + cout << endl; + + cout << "gt(" << f1 << ", " << f2 << ", " << eps << ") = " << flush; + result = Dune::FloatCmp::gt(f1, f2, eps); + cout << repr(result) << "\t"; + count(result == (!inside && f1 > f2)); + cout << endl; + + cout << "lt(" << f1 << ", " << f2 << ", " << eps << ") = " << flush; + result = Dune::FloatCmp::lt(f1, f2, eps); + cout << repr(result) << "\t"; + count(result == (!inside && f1 < f2)); + cout << endl; +} + +template +void vectortests(F f1, F f2, typename Dune::FloatCmp::EpsilonType::Type eps, bool inside) +{ + bool result; + + cout << "eq({" << f1[0] << ", " << f1[1] << "}, {" + << f2[0] << ", " << f2[1] << "}, " << eps << ") = " << flush; + result = Dune::FloatCmp::eq(f1, f2, eps); + cout << repr(result) << "\t"; + count(result == inside); + cout << endl; + + cout << "ne({" << f1[0] << ", " << f1[1] << "}, {" + << f2[0] << ", " << f2[1] << "}, " << eps << ") = " << flush; + result = Dune::FloatCmp::ne(f1, f2, eps); + cout << repr(result) << "\t"; + count(result == !inside); + cout << endl; +} + +template +void tests(F f1, F f2, const typename Dune::FloatCmpOps &ops, bool inside) +{ + bool result; + cout << "ops = operations(" << ops.epsilon() << ")" << endl; + + cout << "ops.eq(" << f1 << ", " << f2 << ") = " << flush; + result = ops.eq(f1, f2); + cout << repr(result) << "\t"; + count(result == inside); + cout << endl; + + cout << "ops.ge(" << f1 << ", " << f2 << ") = " << flush; + result = ops.ge(f1, f2); + cout << repr(result) << "\t"; + count(result == (inside || f1 > f2)); + cout << endl; + + cout << "ops.le(" << f1 << ", " << f2 << ") = " << flush; + result = ops.le(f1, f2); + cout << repr(result) << "\t"; + count(result == (inside || f1 < f2)); + cout << endl; + + cout << "ops.ne(" << f1 << ", " << f2 << ") = " << flush; + result = ops.ne(f1, f2); + cout << repr(result) << "\t"; + count(result == !inside); + cout << endl; + + cout << "ops.gt(" << f1 << ", " << f2 << ") = " << flush; + result = ops.gt(f1, f2); + cout << repr(result) << "\t"; + count(result == (!inside && f1 > f2)); + cout << endl; + + cout << "ops.lt(" << f1 << ", " << f2 << ") = " << flush; + result = ops.lt(f1, f2); + cout << repr(result) << "\t"; + count(result == (!inside && f1 < f2)); + cout << endl; +} + +template +void vectortests(F f1, F f2, typename Dune::FloatCmpOps &ops, bool inside) +{ + bool result; + cout << "ops = operations(" << ops.epsilon() << ")" << endl; + + cout << "ops.eq({" << f1[0] << ", " << f1[1] << "}, {" + << f2[0] << ", " << f2[1] << "}) = " << flush; + result = ops.eq(f1, f2); + cout << repr(result) << "\t"; + count(result == inside); + cout << endl; + + cout << "ops.ne({" << f1[0] << ", " << f1[1] << "}, {" + << f2[0] << ", " << f2[1] << "}) = " << flush; + result = ops.ne(f1, f2); + cout << repr(result) << "\t"; + count(result == !inside); + cout << endl; +} + +int main() { + cout.setf(std::ios_base::scientific, std::ios_base::floatfield); + cout.precision(16); + Dune::FloatCmpOps ops(1e-7); + Dune::FloatCmpOps> std_vec_ops(1e-7); + Dune::FloatCmpOps> fvec_ops(1e-7); + + cout << "Tests inside the epsilon environment" << endl; + tests(1.0, 1.00000001, 1e-7, true); + tests(1.0, 1.00000001, ops, true); + vectortests(std::vector({1.0, 1.0}), std::vector({1.00000001, 1.0}), 1e-7, true); + vectortests(std::vector({1.0, 1.0}), std::vector({1.00000001, 1.0}), std_vec_ops, true); + vectortests(Dune::FieldVector({1.0, 1.0}), Dune::FieldVector({1.00000001, 1.0}), 1e-7, true); + vectortests(Dune::FieldVector({1.0, 1.0}), Dune::FieldVector({1.00000001, 1.0}), fvec_ops, true); + + cout << "Tests outside the epsilon environment, f1 < f2" << endl; + tests(1.0, 1.000001, 1e-7, false); + tests(1.0, 1.000001, ops, false); + vectortests(std::vector({1.0, 1.0}), std::vector({1.000001, 1.0}), 1e-7, false); + vectortests(std::vector({1.0, 1.0}), std::vector({1.000001, 1.0}), std_vec_ops, false); + vectortests(Dune::FieldVector({1.0, 1.0}), Dune::FieldVector({1.000001, 1.0}), 1e-7, false); + vectortests(Dune::FieldVector({1.0, 1.0}), Dune::FieldVector({1.000001, 1.0}), fvec_ops, false); + + cout << "Tests outside the epsilon environment, f1 > f2" << endl; + tests(1.000001, 1.0, 1e-7, false); + tests(1.000001, 1.0, ops, false); + vectortests(std::vector({1.000001, 1.0}), std::vector({1.0, 1.0}), 1e-7, false); + vectortests(std::vector({1.000001, 1.0}), std::vector({1.0, 1.0}), std_vec_ops, false); + vectortests(Dune::FieldVector({1.000001, 1.0}), Dune::FieldVector({1.0, 1.0}), 1e-7, false); + vectortests(Dune::FieldVector({1.000001, 1.0}), Dune::FieldVector({1.0, 1.0}), fvec_ops, false); + + cout << "Tests with f1 = f2 = 0" << endl; + tests(0, 0, 1e-7, true); + tests(0, 0, ops, true); + vectortests(std::vector({0, 0}), std::vector({0, 0}), 1e-7, true); + vectortests(std::vector({0, 0}), std::vector({0, 0}), std_vec_ops, true); + vectortests(Dune::FieldVector({0, 0}), Dune::FieldVector({0, 0}), 1e-7, true); + vectortests(Dune::FieldVector({0, 0}), Dune::FieldVector({0, 0}), fvec_ops, true); + + int total = passed + failed; + cout << passed << "/" << total << " tests passed; " << failed << "/" << total << " tests failed" << endl; + if(failed > 0) return 1; + else return 0; +} diff --git a/dune/common/test/testsuite.hh b/dune/common/test/testsuite.hh new file mode 100644 index 0000000..70657dc --- /dev/null +++ b/dune/common/test/testsuite.hh @@ -0,0 +1,206 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_TEST_TESTSUITE_HH +#define DUNE_COMMON_TEST_TESTSUITE_HH + +#include +#include +#include + +#include +#include + + + +namespace Dune { + + + + /** + * \brief A Simple helper class to organize your test suite + * + * Usage: Construct a TestSuite and call check() or require() + * with the condition to check and probably a name for this check. + * These methods return a stream such that you can pipe in an + * explanantion accompanied by respective data to give a reason + * for a test failure. + */ + class TestSuite + { + public: + enum ThrowPolicy + { + AlwaysThrow, + ThrowOnRequired + }; + + /** + * \brief Create TestSuite + * + * \param name A name to identify this TestSuite. Defaults to "". + * \param policy If AlwaysThrow any failing check will throw, otherwise only required checks will do. + */ + TestSuite(ThrowPolicy policy, std::string name="") : + name_(name), + checks_(0), + failedChecks_(0), + throwPolicy_(policy==AlwaysThrow) + {} + + /** + * \brief Create TestSuite + * + * \param name A name to identify this TestSuite. Defaults to "". + * \param policy If AlwaysThrow any failing check will throw, otherwise only required checks will do. Defaults to ThrowOnRequired + */ + TestSuite(std::string name="", ThrowPolicy policy=ThrowOnRequired) : + name_(name), + checks_(0), + failedChecks_(0), + throwPolicy_(policy==AlwaysThrow) + {} + + /** + * \brief Check condition + * + * This will throw an exception if the check fails and if the AlwaysThrow policy was used on creation. + * + * \param conditon Checks if this is true and increases the failure counter if not. + * \param name A name to identify this check. Defaults to "" + * \returns A CollectorStream that can be used to create a diagnostic message to be printed on failure. + */ + CollectorStream check(bool condition, std::string name="") + { + ++checks_; + if (not condition) + ++failedChecks_; + + return CollectorStream([condition, name, this](std::string reason) { + if (not condition) + this->announceCheckResult(throwPolicy_, "CHECK ", name, reason); + }); + } + + /** + * \brief Check a required condition + * + * This will always throw an exception if the check fails. + * + * \param conditon Checks if this is true and increases the failure counter if not. + * \param name A name to identify this check. Defaults to "" + * \returns A CollectorStream that can be used to create a diagnostic message to be printed on failure. + */ + CollectorStream require(bool condition, std::string name="") + { + ++checks_; + if (not condition) + ++failedChecks_; + + return CollectorStream([condition, name, this](std::string reason) { + if (not condition) + this->announceCheckResult(true, "REQUIRED CHECK", name, reason); + }); + } + + /** + * \brief Collect data from a sub-TestSuite + * + * This will incorporate the accumulated results of the sub-TestSuite + * into this one. If the sub-TestSuite failed, i.e., contained failed + * checks, a summary will be printed. + */ + void subTest(const TestSuite& subTest) + { + checks_ += subTest.checks_; + failedChecks_ += subTest.failedChecks_; + + if (not subTest) + announceCheckResult(throwPolicy_, "SUBTEST", subTest.name(), std::to_string(subTest.failedChecks_)+"/"+std::to_string(subTest.checks_) + " checks failed in this subtest."); + } + + /** + * \brief Check if this TestSuite failed + * + * \returns False if any of the executed tests failed, otherwise true. + */ + explicit operator bool () const + { + return (failedChecks_==0); + } + + /** + * \brief Query name + * + * \returns Name of this TestSuite + */ + std::string name() const + { + return name_; + } + + /** + * \brief Print a summary of this TestSuite + * + * \returns False if any of the executed tests failed, otherwise true. + */ + bool report() const + { + if (failedChecks_>0) + std::cout << composeMessage("TEST ", name(), std::to_string(failedChecks_)+"/"+std::to_string(checks_) + " checks failed in this test.") << std::endl; + return (failedChecks_==0); + } + + /** + * \brief Exit the test. + * + * This wil print a summary of the test and return an integer + * to be used on program exit. + * + * \returns 1 if any of the executed tests failed, otherwise 0. + */ + int exit() const + { + return (report() ? 0: 1); + } + + protected: + + // Compose a diagnostic message + static std::string composeMessage(std::string type, std::string name, std::string reason) + { + std::ostringstream s; + s << type << " FAILED"; + if (name!="") + s << "(" << name << ")"; + s << ": "; + if (reason!="") + s << reason; + return s.str(); + } + + // Announce check results. To be called on failed checks + static void announceCheckResult(bool throwException, std::string type, std::string name, std::string reason) + { + std::string message = composeMessage(type, name, reason); + std::cout << message << std::endl; + if (throwException) + { + Dune::Exception ex; + ex.message(message); + throw ex; + } + } + + std::string name_; + std::size_t checks_; + std::size_t failedChecks_; + bool throwPolicy_; + }; + + + +} // namespace Dune + + + +#endif // DUNE_COMMON_TEST_TESTSUITE_HH diff --git a/dune/common/test/timing.cc b/dune/common/test/timing.cc new file mode 100644 index 0000000..3abd6d3 --- /dev/null +++ b/dune/common/test/timing.cc @@ -0,0 +1,140 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +template +void timing_vector() +{ + std::cout << "timing_vector<" << bs << ", " << sz << ">\n"; + typedef Dune::FieldVector VB; + typedef Dune::BlockVector BV; + typedef Dune::BlockVector BBV; + BV bv1(sz), bv2(sz); + BV bv3(sz), bv4(sz); + bv1 = 1; + bv2 = 0; + bv2[1][0]=1; + bv2[1][1]=2; + + bv3 = 0; + bv4 = 0; + + BBV bbv(2); + bbv[0].resize(bv1.N()); + bbv[1].resize(bv2.N()); + + BBV bbv2(2); +#warning deep copy is broken! + /* bbv2 = bbv2; */ + bbv2[0] = bv3; + bbv2[1] = bv4; + // bbv2 = 0; + + Dune::Timer stopwatch; + stopwatch.reset(); + for (int i=0; i<10; i++) + { +#ifdef DUNE_EXPRESSIONTEMPLATES +#ifdef DUNE_FLATIT + for (int a=0; a<2; a++) + for (int b=0; b +template +void timing_matrix() +{ + std::cout << "timing_matrix<" << BN << ", " << BM << ", " + << N << ", " << M << ">\n"; + + typedef double matvec_t; + typedef Dune::FieldVector LVB; + typedef Dune::FieldVector VB; + typedef Dune::FieldMatrix MB; + typedef Dune::BlockVector LeftVector; + typedef Dune::BlockVector Vector; + typedef Dune::BCRSMatrix Matrix; + + Matrix A(N,M,Matrix::row_wise); + typename Matrix::CreateIterator i=A.createbegin(); + typename Matrix::CreateIterator end=A.createend(); + std::cout << "Building matrix structure\n"; + // build up the matrix structure + int c=0; + for (; i!=end; ++i) + { + // insert a non zero entry for myself + i.insert(c); + // insert index M-1 + i.insert(M-1); + c++; + } + std::cout << "...done\n"; + + LeftVector v(N); + v = 0; + Vector x(M); + x = 1; + + Dune::Timer stopwatch; + stopwatch.reset(); +#ifdef DUNE_EXPRESSIONTEMPLATES + v += A * x; +#else + A.umv(x,v); +#endif + std::cout << "Time [v+=A*x] " << stopwatch.elapsed() << std::endl; + + std::cout << std::endl; +} +#endif + +int main () +{ +#ifdef DUNE_EXPRESSIONTEMPLATES +#ifdef DUNE_FLATIT + std::cout << "Handwritten loops\n"; +#else + std::cout << "Expression Templates\n"; +#endif +#else + std::cout << "Template Meta Program\n"; +#endif + + timing_vector<1,1000000>(); + timing_vector<2,500000>(); + timing_vector<10,100000>(); + timing_vector<40,25000>(); + timing_vector<100,10000>(); + timing_vector<400,2500>(); + + // timing_matrix<150,150,500,4000>(); + // timing_matrix<150,150,1000,2000>(); + // timing_matrix<1,18,400000,500000>(); + // timing_matrix<6,3,400000,500000>(); + // timing_matrix<3,6,400000,500000>(); + // timing_matrix<18,1,400000,500000>(); + // timing_matrix<50,50,9000,10000>(); + + std::cout << std::endl; +} diff --git a/dune/common/test/transposetest.cc b/dune/common/test/transposetest.cc new file mode 100644 index 0000000..a81ccf4 --- /dev/null +++ b/dune/common/test/transposetest.cc @@ -0,0 +1,98 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + + + +// check A*transpose(B) +template +auto checkAxBT(const A& a, const B&b) +{ + Dune::TestSuite suite(std::string{"Check transpose with A="} + Dune::className() + " and B=" + Dune::className()); + + // compute abt + auto abt = a * transpose(b); + + // check result type + using FieldA = typename Dune::FieldTraits::field_type; + using FieldB = typename Dune::FieldTraits::field_type; + using Field = typename Dune::PromotionTraits::PromotedType; + using ABT = Dune::FieldMatrix; + suite.check(std::is_same()) + << "Result type of A*transpose(B) should be " << Dune::className() << " but is " << Dune::className(); + + // manually compute result value + auto abt_check = ABT{}; + for(std::size_t i=0; i{}; + auto b = Dune::FieldMatrix{}; + testFillDense(a); + testFillDense(b); + suite.subTest(checkAxBT(a,b)); + } + + { + auto a = Dune::FieldMatrix{}; + auto b = Dune::FieldMatrix{}; + testFillDense(a); + testFillDense(b); + suite.subTest(checkAxBT(a,b)); + } + + { + auto a = Dune::FieldMatrix{}; + auto b = Dune::FieldMatrix{}; + testFillDense(a); + testFillDense(b); + suite.subTest(checkAxBT(a,b)); + } + + { + auto a = Dune::FieldMatrix{}; + auto b = Dune::DiagonalMatrix{}; + testFillDense(a); + b = {0, 1, 2, 3}; + suite.subTest(checkAxBT(a,b)); + } + + return suite.exit(); +} diff --git a/dune/common/test/tupleutilitytest.cc b/dune/common/test/tupleutilitytest.cc new file mode 100644 index 0000000..0c16355 --- /dev/null +++ b/dune/common/test/tupleutilitytest.cc @@ -0,0 +1,183 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include + +////////////////////////////////////////////////////////////////////// +// +// check FirstTypeIndex +// +typedef std::tuple MyTuple; +static_assert((Dune::FirstTypeIndex::value == 0), + "FirstTypeIndex finds the wrong index for double in MyTuple!"); +static_assert((Dune::FirstTypeIndex::value == 1), + "FirstTypeIndex finds the wrong index for double in MyTuple!"); +static_assert((Dune::FirstTypeIndex::value == 2), + "FirstTypeIndex finds the wrong index for double in MyTuple!"); + + + +////////////////////////////////////////////////////////////////////// +// +// check PushBackTuple +typedef Dune::PushBackTuple::type MyTupleAppended1; +typedef std::tuple MyTupleAppended2; +static_assert((std::is_same::value), + "PushBackTuple failed!"); + + + +////////////////////////////////////////////////////////////////////// +// +// check PushFrontTuple +typedef Dune::PushFrontTuple::type MyTuplePrepended1; +typedef std::tuple MyTuplePrepended2; +static_assert((std::is_same::value), + "PushFrontTuple failed!"); + + + +////////////////////////////////////////////////////////////////////// +// +// check JoinTuples +typedef Dune::JoinTuples::type MyTupleMyTuple1; +typedef std::tuple MyTupleMyTuple2; +static_assert((std::is_same::value), + "JoinTuples failed!"); + + + +////////////////////////////////////////////////////////////////////// +// +// check FlattenTuple +typedef std::tuple MyTuple2; +typedef std::tuple MyTupleTuple; +typedef Dune::FlattenTuple::type MyTupleTupleFlat1; +typedef std::tuple MyTupleTupleFlat2; +static_assert((std::is_same::value), + "FlattenTuples failed!"); + + + +////////////////////////////////////////////////////////////////////// +// +// check nested ReduceTuple with a litte TMP + +// A tuple of a range of integers wrapped in integral_constant types +template +struct Range +{ + typedef typename Dune::PushBackTuple< + typename Range::type, + typename std::integral_constant + >::type type; +}; + +template +struct Range +{ + typedef std::tuple<> type; +}; + +// An accumulator to build up a list of divisors of an integer using reduce +template +struct DivisorAccumulator +{ + enum {value = Data::first_type::value}; + enum {isDivisor = (PotentialDivisor::value*(value / PotentialDivisor::value)==value)}; + + typedef typename Data::second_type OldTuple; + typedef typename Dune::PushBackTuple::type ExtendedTuple; + typedef typename std::conditional::type NewTuple; + + typedef typename std::pair type; +}; + +// Construct list of divisors using reduce +template +struct Divisors +{ + typedef typename Dune::ReduceTuple< + DivisorAccumulator, + typename Range<1,X+1>::type, + typename std::pair, typename std::tuple<> > + >::type::second_type type; + + enum {value = std::tuple_size::value}; +}; + +// An accumulator to build up a list of primes up to a fixed integer +template +struct PrimeAccumulator +{ + enum {isPrime = (Divisors::value==2)}; + + typedef typename std::conditional::type, Data>::type type; +}; + +// Construct list primes +template +struct Primes +{ + typedef typename Dune::ReduceTuple< + PrimeAccumulator, + typename Range<1,X+1>::type, + typename std::tuple<> + >::type type; +}; + +typedef Primes<9>::type Primes1; +typedef std::tuple< + std::integral_constant, + std::integral_constant, + std::integral_constant, + std::integral_constant > Primes2; +static_assert((std::is_same::value), + "ReduceTuple failed in primes-tmp!"); + +struct Reciprocal +{ + template + struct TypeEvaluator + { + typedef double Type; + }; + template + typename TypeEvaluator::Type operator()(const T& val) const { + return 1./val; + } +}; + +int main() +{ + const std::tuple t1(1, 2.); + auto t2 = Dune::genericTransformTuple(t1, Reciprocal()); + static_assert(std::is_same>::value, + "Type after genericTransformTuple does not match!"); + if(fabs(std::get<0>(t2)-1.) > 1e-8 || + fabs(std::get<1>(t2)-.5) > 1e-8) + { + std::cout << "genericTransformTuple gives wrong result!\n"; + std::abort(); + } + + auto t3 = Dune::applyPartial([&] (auto&&... x) { + return std::make_tuple((1./x)...); + }, t1, std::make_index_sequence<2>()); + + if(t2 != t3) + { + std::cout << "genericTransformTuple gives wrong result!\n"; + std::abort(); + } + +} diff --git a/dune/common/test/typelisttest.cc b/dune/common/test/typelisttest.cc new file mode 100644 index 0000000..36e69c4 --- /dev/null +++ b/dune/common/test/typelisttest.cc @@ -0,0 +1,243 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +template +int isTypeListByOverload(const Dune::TypeList *); + +template +struct IsTypeListByOverload : std::false_type {}; + +template +struct IsTypeListByOverload + ()))> > +: std::true_type {}; + +template +struct IsTypeListBySpecialization : std::false_type {}; + +template +struct IsTypeListBySpecialization > : std::true_type {}; + +template +void staticLiteralTests() { + // check default-constructible + TL tl1; + TL tl2{}; + + // check copy construction + TL tl3 = tl1; + TL tl4(tl1); + + // check move construction + TL tl5 = std::move(tl1); + TL tl6(std::move(tl2)); + + // check copying + tl1 = tl5; + tl2 = { tl1 }; + + // check moving + tl3 = std::move(tl1); + tl4 = { std::move(tl2) }; + + // check literal type requirement + constexpr TL tl7{}; + + // specialization/overload resolution tests + static_assert(IsTypeListBySpecialization::value, + "TypeList cannot be recongized by class specialization"); + static_assert(IsTypeListByOverload::value, + "TypeList cannot be recongized by function overload resolution"); + + // avoid compiler warnings + (void)tl1; + (void)tl2; + (void)tl3; + (void)tl4; + (void)tl5; + (void)tl6; + (void)tl7; + + // destructor checked on scope exit +} + +static constexpr struct {} skipOverloadTest{}; + +template +void checkNonTypeList(decltype(skipOverloadTest)) +{ + // make sure IsTypeList and IsEmptyTypeList reject non-typelists + static_assert(!Dune::IsTypeList::value, + "IsTypeList accepts non-TypeList"); + static_assert(!Dune::IsEmptyTypeList::value, + "IsEmptyTypeList accepts non-TypeList"); + + // specialization tests + static_assert(!IsTypeListBySpecialization::value, + "Non-TypeList recongized as TypeList by class specialization"); +} + +template +void checkNonTypeList() +{ + checkNonTypeList(skipOverloadTest); + + // overload resolution tests + static_assert(!IsTypeListByOverload::value, + "Non-TypeList recongized as TypeList by function overload resolution"); +} + +void staticTests() +{ + { + using TL = Dune::TypeList<>; + static_assert(Dune::IsTypeList::value, + "TypeList not recognized by IsTypeList"); + static_assert(Dune::IsEmptyTypeList::value, + "Empty TypeList not recognized by IsEmptyTypeList"); + static_assert(Dune::TypeListSize::value == 0, + "Incorrect result of TypeListeSize"); + + staticLiteralTests(); + } + + { + using TL = Dune::TypeList; + static_assert(Dune::IsTypeList::value, + "TypeList not recognized by IsTypeList"); + static_assert(!Dune::IsEmptyTypeList::value, + "Nonempty TypeList declared empty by IsEmptyTypeList"); + static_assert(Dune::TypeListSize::value == 1, + "Incorrect result of TypeListeSize"); + static_assert(std::is_same::type, + void>::value, + "TypeListElement returns wrong type"); + static_assert(std::is_same, void>::value, + "TypeListEntry_t returns wrong type"); + + staticLiteralTests(); + } + + { + using TL = Dune::TypeList; + static_assert(Dune::IsTypeList::value, + "TypeList not recognized by IsTypeList"); + static_assert(!Dune::IsEmptyTypeList::value, + "Nonempty TypeList declared empty by IsEmptyTypeList"); + static_assert(Dune::TypeListSize::value == 3, + "Incorrect result of TypeListeSize"); + + static_assert(std::is_same::type, + const int>::value, + "TypeListElement returns wrong type"); + static_assert(std::is_same, const int>::value, + "TypeListEntry_t returns wrong type"); + + static_assert(std::is_same::type, + int[10]>::value, + "TypeListElement returns wrong type"); + static_assert(std::is_same, int[10]>::value, + "TypeListEntry_t returns wrong type"); + + static_assert(std::is_same::type, + int(int, int)>::value, + "TypeListElement returns wrong type"); + static_assert(std::is_same, + int(int, int)>::value, + "TypeListEntry_t returns wrong type"); + + staticLiteralTests(); + } + + // make sure IsTypeList and IsEmptyTypeList reject non-typelists + checkNonTypeList(); + checkNonTypeList(); + // don't check tuple<>, that may actually be an implementation of TypeList<> + checkNonTypeList >(); + // `tuple` is a complete, but noninstantiable type. Attempting to use + // an object of type `tuple` as an argument to a function call + // necessiates instantiation -- which is illegal even in an SFINAE context. + // The instantiation is necessary to check for conversions (via conversion + // operators and base classes) during overload resolution. Even if the + // signature of the function is of the form `f(const Expr*)` for some + // template parameter `T` and we call it as `f(declval*>())` the + // base classes `tuple` must be determined to figure out whether + // `tuple*` can be converted to `Expr*`. + checkNonTypeList >(skipOverloadTest); +} + +struct NonConstructible { + NonConstructible() = delete; +}; + +template +auto getTypeInfos(TypeList typeList) +{ + using namespace Dune::Hybrid; + + std::vector result; + forEach(typeList, [&](auto metaType) { + using type = typename decltype(metaType)::type; + result.emplace_back(typeid (type)); + }); + return result; +} + +template +auto getMetaTypeInfos(TypeList typeList) +{ + using namespace Dune::Hybrid; + + std::vector result; + // The parens around `forEach` are needed to suppress ADL here to avoid + // instantiation attempts for the member types of typeList + (forEach)(typeList, [&](auto metaType) { + result.emplace_back(typeid (metaType)); + }); + return result; +} + +int main() +{ + staticTests(); + + Dune::TestSuite test; + + auto typeList = Dune::TypeList{}; + auto expectedTypeInfoList = std::vector{ + typeid (void), typeid (NonConstructible), typeid (int) + }; + test.check(getTypeInfos(typeList) == expectedTypeInfoList) + << "Iterating over TypeList yields unexpected type information"; + + // This test also ensures that the type passed to the lamda in the + // `forEach()` is indeed an instance of `MetaType` + auto metaTypeList = + Dune::TypeList >{}; + auto expectedMetaTypeInfoList = std::vector{ + typeid (Dune::MetaType), + typeid (Dune::MetaType), + typeid (Dune::MetaType >), + }; + // parens around `getMetaTypeInfos` needed to suppress ADL + test.check((getMetaTypeInfos)(metaTypeList) == expectedMetaTypeInfoList) + << "Iterating over TypeList yields unexpected MetaTypes"; + + return test.exit(); +} diff --git a/dune/common/test/typeutilitytest.cc b/dune/common/test/typeutilitytest.cc new file mode 100644 index 0000000..421bf55 --- /dev/null +++ b/dune/common/test/typeutilitytest.cc @@ -0,0 +1,32 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +////////////////////////////////////////////////////////////////////// +// +// check disableCopyMove +// + +struct Foo { + + template< class ...Args, Dune::disableCopyMove< Foo, Args ... > = 0 > + Foo( Args&& ... ) + {} + + Foo( const Foo& ) = delete; + Foo( Foo&& ) = delete; +}; + +static_assert( std::is_default_constructible< Foo >::value, "Foo is not default constructible." ); +static_assert( not std::is_copy_constructible< Foo >::value, "Foo is copy constructible." ); +static_assert( not std::is_move_constructible< Foo >::value, "Foo is move constructible." ); + +int main() +{} diff --git a/dune/common/test/utilitytest.cc b/dune/common/test/utilitytest.cc new file mode 100644 index 0000000..5317f77 --- /dev/null +++ b/dune/common/test/utilitytest.cc @@ -0,0 +1,86 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include + +template +struct Eval +{ + typedef void* Type; +}; + +int main(int, char**) +{ + typedef std::tuple PointerTuple; + PointerTuple pointers = Dune::NullPointerInitialiser::apply(); + + int ret=0; + + if(std::get<0>(pointers)!=nullptr) { + std::cerr<<"First pointer not null"<(pointers)!=nullptr) { + std::cerr<<"Second pointer not null"<(pointers)!=nullptr) { + std::cerr<<"Third pointer not null"<(pointers)!=nullptr) { + std::cerr<<"Fourth pointer not null"< Tuple1; + typedef std::tuple RefTuple1; + typedef std::tuple PointerTuple1; + static_assert((std::is_same::Type>::value), + "RefTuple1 with added pointers should be the same as " + "PointerTuple1, but it isn't!"); + + Tuple1 t1(i,c,l,c); + RefTuple1 refs(i, c, l, c); + + [[maybe_unused]] RefTuple1 refs2(Dune::transformTuple(t1)); + PointerTuple1 pointers1(Dune::transformTuple(refs)); + if(&i != std::get<0>(pointers1) || &c != std::get<1>(pointers1) || + &l != std::get<2>(pointers1) || &c != std::get<3>(pointers1)) { + std::cerr << "utilitytest: error: incorrect pointers in pointers1" + << std::endl; + ret = 1; + } + + if(Dune::At<2>::get(pointers)!=std::get<1>(pointers)) { + ret+=10; + std::cerr<<"at inconsistent!"<::Type ConvertedType; + Dune::PointerPairDeletor::apply(p); + if(p != PointerTuple1(nullptr,nullptr,nullptr,nullptr)){ + ret+=20; + std::cerr<<"PointerPairDeletor not working!"< +#include +#include +#include + +#include +#include + +const std::map impl_names = { + {Vc::ScalarImpl, "Scalar" }, + {Vc::SSE2Impl, "SSE2" }, + {Vc::SSE3Impl, "SSE3" }, + {Vc::SSSE3Impl, "SSSE3" }, + {Vc::SSE41Impl, "SSE41" }, + {Vc::SSE42Impl, "SSE42" }, + {Vc::AVXImpl, "AVX" }, + {Vc::AVX2Impl, "AVX2" }, + {Vc::MICImpl, "MIC" }, +}; + +const std::string expected_var = "DUNE_TEST_EXPECTED_VC_IMPLEMENTATION"; + +int main() +{ + + auto p = impl_names.find(Vc::CurrentImplementation::current()); + if(p == impl_names.end()) + DUNE_THROW(Dune::NotImplemented, "Unexpected current implementation value " + << Vc::CurrentImplementation::current()); + auto current_impl = p->second; + + std::cout << "The current Vc implementation is " << current_impl + << std::endl; + + std::string expected_impl; + if(auto env_impl = std::getenv(expected_var.c_str())) + expected_impl = env_impl; + + if(expected_impl.empty()) + { + std::cerr << "No expected vc imlementation provided, skipping test\n" + << "Please set " << expected_var + << " environment variable to one of the following values:"; + for(const auto &item : impl_names) + std::cerr << ' ' << item.second; + std::cerr << std::endl; + return 77; + } + + std::cout << "The expected Vc implementation is " << expected_impl + << std::endl; + + if(current_impl == expected_impl) { + std::cout << "OK: Current and expected Vc implementation match" + << std::endl; + return 0; + } + else { + std::cout << "Error: Current Vc implementation does not match expected" + << std::endl; + return 1; + } + +} diff --git a/dune/common/test/versiontest.cc b/dune/common/test/versiontest.cc new file mode 100644 index 0000000..0c29f0b --- /dev/null +++ b/dune/common/test/versiontest.cc @@ -0,0 +1,61 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +// DUNE_MODTEST_VERSION 3.2.1 +#define DUNE_MODTEST_VERSION_MAJOR 3 +#define DUNE_MODTEST_VERSION_MINOR 2 +#define DUNE_MODTEST_VERSION_REVISION 1 + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +// test 3.3 >= 3.2 +#if DUNE_VERSION_GTE(DUNE_MODTEST, 3, 3) +#error "3.2 >= 3.3" +#endif + +// test 3.2 >= 3.1 +#if DUNE_VERSION_LTE(DUNE_MODTEST, 3, 1) +#error "3.2 <= 3.1" +#endif + +// test 3.2 == 3.2 +#if DUNE_VERSION_GT(DUNE_MODTEST, 3, 2) + #error "3.2 > 3.2" +#elif DUNE_VERSION_LT(DUNE_MODTEST, 3, 2) + #error "3.2 < 3.2" +#else + #if ! DUNE_VERSION_EQUAL(DUNE_MODTEST, 3, 2) + #error "3.2 != 3.2" + #endif +#endif + +// test 3.2.2 >= 3.2.1 +#if DUNE_VERSION_GTE_REV(DUNE_MODTEST, 3, 2, 2) +#error "3.2.1 >= 3.2.2" +#endif + +// test 3.2.1 >= 3.2.0 +#if DUNE_VERSION_LTE_REV(DUNE_MODTEST, 3, 2, 0) +#error "3.2.1 <= 3.2.0" +#endif + +// test 3.2.1 == 3.2.1 +#if DUNE_VERSION_GT_REV(DUNE_MODTEST, 3, 2, 1) + #error "3.2.1 > 3.2.1" +#elif DUNE_VERSION_LT_REV(DUNE_MODTEST, 3, 2, 1) + #error "3.2.1 < 3.2.1" +#else + #if ! DUNE_VERSION_EQUAL_REV(DUNE_MODTEST, 3, 2, 1) + #error "3.2.1 != 3.2.1" + #endif +#endif + +int main() +{ + return 0; +} diff --git a/dune/common/timer.hh b/dune/common/timer.hh new file mode 100644 index 0000000..4c502f8 --- /dev/null +++ b/dune/common/timer.hh @@ -0,0 +1,151 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_TIMER_HH +#define DUNE_TIMER_HH + +#ifndef TIMER_USE_STD_CLOCK +// headers for std::chrono +#include +#else +// headers for std::clock +#include +#endif + +namespace Dune { + + /** @addtogroup Common + @{ + */ + + /*! \file + \brief A simple timing class. + */ + + + /** \brief A simple stop watch + + This class reports the elapsed user-time, i.e. time spent computing, + after the last call to Timer::reset(). The results are seconds and + fractional seconds. Note that the resolution of the timing depends + on your OS kernel which should be somewhere in the milisecond range. + + The class is basically a wrapper for the libc-function getrusage() + + \warning In a multi-threading situation, this class does NOT return wall-time! + Instead, the run time for all threads will be added up. + For example, if you have four threads running in parallel taking one second each, + then the Timer class will return an elapsed time of four seconds. + + */ + class Timer + { + public: + + /** \brief A new timer, create and reset + * + * \param startImmediately If true (default) the timer starts counting immediately + */ + Timer (bool startImmediately=true) noexcept + { + isRunning_ = startImmediately; + reset(); + } + + //! Reset timer while keeping the running/stopped state + void reset() noexcept + { + sumElapsed_ = 0.0; + storedLastElapsed_ = 0.0; + rawReset(); + } + + + //! Start the timer and continue measurement if it is not running. Otherwise do nothing. + void start() noexcept + { + if (not (isRunning_)) + { + rawReset(); + isRunning_ = true; + } + } + + + //! Get elapsed user-time from last reset until now/last stop in seconds. + double elapsed () const noexcept + { + // if timer is running add the time elapsed since last start to sum + if (isRunning_) + return sumElapsed_ + lastElapsed(); + + return sumElapsed_; + } + + + //! Get elapsed user-time from last start until now/last stop in seconds. + double lastElapsed () const noexcept + { + // if timer is running return the current value + if (isRunning_) + return rawElapsed(); + + // if timer is not running return stored value from last run + return storedLastElapsed_; + } + + + //! Stop the timer and return elapsed(). + double stop() noexcept + { + if (isRunning_) + { + // update storedLastElapsed_ and sumElapsed_ and stop timer + storedLastElapsed_ = lastElapsed(); + sumElapsed_ += storedLastElapsed_; + isRunning_ = false; + } + return elapsed(); + } + + + private: + + bool isRunning_; + double sumElapsed_; + double storedLastElapsed_; + + +#ifdef TIMER_USE_STD_CLOCK + void rawReset() noexcept + { + cstart = std::clock(); + } + + double rawElapsed () const noexcept + { + return (std::clock()-cstart) / static_cast(CLOCKS_PER_SEC); + } + + std::clock_t cstart; +#else + void rawReset() noexcept + { + cstart = std::chrono::high_resolution_clock::now(); + } + + double rawElapsed () const noexcept + { + std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now(); + std::chrono::duration time_span = std::chrono::duration_cast >(now - cstart); + return time_span.count(); + } + + std::chrono::high_resolution_clock::time_point cstart; +#endif + }; // end class Timer + + /** @} end documentation */ + +} // end namespace + +#endif diff --git a/dune/common/to_unique_ptr.hh b/dune/common/to_unique_ptr.hh new file mode 100644 index 0000000..9c83661 --- /dev/null +++ b/dune/common/to_unique_ptr.hh @@ -0,0 +1,28 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_TO_UNIQUE_PTR_HH +#define DUNE_TO_UNIQUE_PTR_HH + +#warning to_unique_ptr.hh and ToUniquePtr are deprecated. Use std::unique_ptr or std::shared_ptr instead. + +#include + +namespace Dune +{ + /// \brief Alias for `std::unique_ptr` introduced as transition wrapper. + /// \deprecated + template + using ToUniquePtr [[deprecated]] = std::unique_ptr; + + /// \brief Alias for `std::make_unique` introduced as transition wrapper. + /// \deprecated + template + [[deprecated]] std::unique_ptr makeToUnique (Args&&... args) + { + return std::make_unique(std::forward(args)...); + } + +} // end namespace Dune + +#endif // DUNE_TO_UNIQUE_PTR_HH diff --git a/dune/common/transpose.hh b/dune/common/transpose.hh new file mode 100644 index 0000000..22433f4 --- /dev/null +++ b/dune/common/transpose.hh @@ -0,0 +1,77 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_TRANSPOSE_HH +#define DUNE_COMMON_TRANSPOSE_HH + +#include + +#include +#include + +namespace Dune { + +namespace Impl { + + // Wrapper representing the transposed of a matrix. + // Creating the wrapper does not compute anything + // but only serves for tagging the wrapped matrix + // for transposition. + template + class TransposedMatrixWrapper + { + public: + + enum { + //! The number of rows. + rows = M::cols, + //! The number of columns. + cols = M::rows + }; + + TransposedMatrixWrapper(const M& matrix) : matrix_(matrix) {} + TransposedMatrixWrapper(const TransposedMatrixWrapper&) = delete; + TransposedMatrixWrapper(TransposedMatrixWrapper&&) = delete; + + template + friend auto operator* (const FieldMatrix& matrixA, + const TransposedMatrixWrapper& matrixB) + { + using ThisField = typename FieldTraits::field_type; + using Field = typename PromotionTraits::PromotedType; + FieldMatrix result; + for (std::size_t j=0; j +auto transpose(const Matrix& matrix) { + return Impl::TransposedMatrixWrapper(matrix); +} + + +} // namespace Dune + +#endif // DUNE_COMMON_TRANSPOSE_HH diff --git a/dune/common/tupleutility.hh b/dune/common/tupleutility.hh new file mode 100644 index 0000000..f49240d --- /dev/null +++ b/dune/common/tupleutility.hh @@ -0,0 +1,577 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifndef DUNE_TUPLE_UTILITY_HH +#define DUNE_TUPLE_UTILITY_HH + +#include +#include +#include +#include + +#include +#include + +namespace Dune { + + /** @addtogroup TupleUtilities + * + * @{ + */ + + /** + * @file + * @brief Contains utility classes which can be used with std::tuple. + */ + + /** + * \brief Apply function with arguments from a given tuple + * + * \param f A callable object + * \param args Tuple containing the arguments + * \param indices Indices to arguments in tuple as std::integer_sequence + * + * This will call the function with arguments generated by unpacking those + * entries of the tuple that show up given integer_sequence. + * + * \ingroup Utility + */ + template + decltype(auto) applyPartial(F&& f, ArgTuple&& args, std::integer_sequence /*indices*/) + { + return f(std::get(args)...); + } + + template + struct TupleAccessTraits + { + typedef typename std::add_const::type& ConstType; + typedef T& NonConstType; + typedef const typename std::remove_const::type& ParameterType; + }; + + template + struct TupleAccessTraits + { + typedef typename std::add_const::type* ConstType; + typedef T* NonConstType; + typedef T* ParameterType; + }; + + template + struct TupleAccessTraits + { + typedef T& ConstType; + typedef T& NonConstType; + typedef T& ParameterType; + }; + + /** + * @brief A helper template that initializes a std::tuple consisting of pointers + * to nullptr. + * + * A std::tuple of nullptr may be useful when you use a std::tuple of pointers + * in a class which you can only initialise in a later stage. + */ + template + struct NullPointerInitialiser; + + template + struct NullPointerInitialiser > + { + typedef std::tuple ResultType; + static ResultType apply() + { + return ResultType(static_cast(nullptr)...); + } + }; + + /** + * @brief Helper template to clone the type definition of a std::tuple with the + * storage types replaced by a user-defined rule. + * + * Suppose all storage types A_i in a std::tuple define a type A_i::B. You can + * build up a pair consisting of the types defined by A_i::B in the following + * way: + * + * \code + * template + * struct MyEvaluator + * { + * typedef typename A::B Type; + * }; + * + * typedef ForEachType::Type BTuple; + * \endcode + * + * Here, MyEvaluator is a helper struct that extracts the correct type from + * the storage types of the tuple defined by the tuple ATuple. + * + * \sa AddRefTypeEvaluator, AddPtrTypeEvaluator, genericTransformTuple(), + * and transformTuple(). + */ + template