From 3bd6b6838ca9c68ec2afd74e41a56a20f9742772 Mon Sep 17 00:00:00 2001 From: Ansgar Date: Wed, 20 May 2020 20:06:31 +0100 Subject: [PATCH 1/1] Import dune-common_2.7.0.orig.tar.gz [dgit import orig dune-common_2.7.0.orig.tar.gz] --- CHANGELOG.md | 169 + CMakeLists.txt | 29 + CONTRIBUTING.md | 71 + COPYING | 1 + INSTALL | 75 + LICENSE.md | 433 ++ README.md | 78 + TODO | 2 + bin/CMakeLists.txt | 6 + bin/dune-ctest | 227 + bin/dune-git-whitespace-hook | 169 + bin/dunecontrol | 1095 ++++ bin/duneproject | 698 +++ cmake/modules/AddGMPFlags.cmake | 28 + cmake/modules/AddMETISFlags.cmake | 22 + cmake/modules/AddPTScotchFlags.cmake | 29 + cmake/modules/AddParMETISFlags.cmake | 29 + cmake/modules/AddQuadMathFlags.cmake | 28 + cmake/modules/AddSuiteSparseFlags.cmake | 23 + cmake/modules/AddUMFPackFlags.cmake | 24 + cmake/modules/AddVcFlags.cmake | 32 + .../CMakeBuiltinFunctionsDocumentation.cmake | 83 + cmake/modules/CMakeLists.txt | 52 + cmake/modules/CheckCXXFeatures.cmake | 596 +++ cmake/modules/DuneCMakeCompat.cmake | 98 + cmake/modules/DuneCommonMacros.cmake | 55 + cmake/modules/DuneCxaDemangle.cmake | 17 + cmake/modules/DuneDoc.cmake | 159 + cmake/modules/DuneDoxygen.cmake | 125 + cmake/modules/DuneEnableAllPackages.cmake | 368 ++ cmake/modules/DuneExecuteProcess.cmake | 58 + cmake/modules/DuneInstance.cmake | 922 ++++ cmake/modules/DuneMPI.cmake | 72 + cmake/modules/DuneMacros.cmake | 1273 +++++ cmake/modules/DunePathHelper.cmake | 101 + cmake/modules/DunePkgConfig.cmake | 47 + cmake/modules/DunePythonCommonMacros.cmake | 150 + cmake/modules/DunePythonFindPackage.cmake | 115 + cmake/modules/DunePythonInstallPackage.cmake | 146 + cmake/modules/DunePythonRequireVersion.cmake | 14 + cmake/modules/DunePythonTestCommand.cmake | 78 + cmake/modules/DunePythonVirtualenv.cmake | 233 + cmake/modules/DuneSphinxCMakeDoc.cmake | 183 + cmake/modules/DuneStreams.cmake | 29 + cmake/modules/DuneSymlinkOrCopy.cmake | 209 + cmake/modules/DuneTestMacros.cmake | 435 ++ cmake/modules/FindGMP.cmake | 110 + cmake/modules/FindInkscape.cmake | 24 + cmake/modules/FindLatexMk.cmake | 75 + cmake/modules/FindMETIS.cmake | 156 + cmake/modules/FindMProtect.cmake | 18 + cmake/modules/FindPTScotch.cmake | 110 + cmake/modules/FindParMETIS.cmake | 163 + cmake/modules/FindQuadMath.cmake | 59 + cmake/modules/FindSphinx.cmake | 41 + cmake/modules/FindSuiteSparse.cmake | 281 + cmake/modules/FindTBB.cmake | 467 ++ cmake/modules/FindUMFPack.cmake | 83 + cmake/modules/Headercheck.cmake | 82 + cmake/modules/LanguageSupport.cmake | 66 + cmake/modules/OverloadCompilerFlags.cmake | 142 + cmake/modules/UseInkscape.cmake | 78 + cmake/modules/UseLATEX.cmake | 1967 +++++++ cmake/modules/UseLatexMk.cmake | 254 + cmake/modules/latexmkrc.cmake | 13 + cmake/pkg/dune-common-config.cmake.in | 23 + cmake/scripts/CMakeLists.txt | 15 + cmake/scripts/CreateDoxyFile.cmake | 18 + cmake/scripts/FinalizeHeadercheck.cmake | 13 + cmake/scripts/InstallFile.cmake | 6 + cmake/scripts/RunDoxygen.cmake | 3 + cmake/scripts/conf.py.in | 27 + cmake/scripts/envdetect.py | 22 + cmake/scripts/extract_cmake_data.py | 109 + cmake/scripts/index.rst.in | 41 + cmake/scripts/main77.cc.in | 8 + cmake/scripts/module_library.cc.in | 11 + cmake/scripts/pyversion.py | 31 + cmake/scripts/run-in-dune-env.sh.in | 4 + cmake/scripts/sphinx_cmake_dune.py | 191 + config.h.cmake | 213 + doc/CMakeLists.txt | 5 + doc/buildsystem/CMakeLists.txt | 5 + doc/buildsystem/dune-common.rst | 391 ++ .../examples/Toolchain-Ubuntu-mingw32.cmake | 34 + doc/buildsystem/examples/opts.mingw32 | 2 + doc/comm/CMakeLists.txt | 15 + doc/comm/buildindexset.hh | 54 + doc/comm/communication.bib | 79 + doc/comm/communication.tex | 562 ++ doc/comm/figures/darray.eps | 230 + doc/comm/figures/distindex.eps | 4736 +++++++++++++++++ doc/comm/indexset.cc | 51 + doc/comm/poosc08.cc | 92 + doc/comm/poosc08_test.cc | 155 + doc/comm/reverse.hh | 38 + doc/doxygen/CMakeLists.txt | 10 + doc/doxygen/Doxylocal | 8 + doc/doxygen/Doxystyle | 206 + doc/doxygen/doxygen-macros | 19 + doc/doxygen/mainpage.txt | 17 + doc/doxygen/modules.txt | 78 + doc/dunecontrol.1 | 173 + dune-common.pc.in | 15 + dune.module | 4 + dune/CMakeLists.txt | 1 + dune/common/CMakeLists.txt | 125 + dune/common/alignedallocator.hh | 100 + dune/common/arraylist.hh | 733 +++ dune/common/assertandreturn.hh | 25 + dune/common/bartonnackmanifcheck.hh | 64 + dune/common/bigunsignedint.hh | 691 +++ dune/common/binaryfunctions.hh | 47 + dune/common/bitsetvector.hh | 653 +++ dune/common/boundschecking.hh | 41 + dune/common/classname.hh | 76 + dune/common/concept.hh | 331 ++ dune/common/conditional.hh | 33 + dune/common/debugalign.cc | 45 + dune/common/debugalign.hh | 541 ++ dune/common/debugallocator.cc | 31 + dune/common/debugallocator.hh | 357 ++ dune/common/debugstream.hh | 435 ++ dune/common/densematrix.hh | 1263 +++++ dune/common/densevector.hh | 761 +++ dune/common/deprecated.hh | 224 + dune/common/diagonalmatrix.hh | 1103 ++++ dune/common/documentation.hh | 58 + dune/common/dotproduct.hh | 96 + dune/common/dynmatrix.hh | 149 + dune/common/dynmatrixev.hh | 100 + dune/common/dynvector.hh | 202 + dune/common/enumset.hh | 176 + dune/common/exceptions.cc | 40 + dune/common/exceptions.hh | 289 + dune/common/filledarray.hh | 44 + dune/common/float_cmp.cc | 506 ++ dune/common/float_cmp.hh | 385 ++ dune/common/fmatrix.hh | 709 +++ dune/common/fmatrixev.cc | 246 + dune/common/fmatrixev.hh | 303 ++ dune/common/ftraits.hh | 61 + dune/common/function.hh | 142 + dune/common/fvector.hh | 633 +++ dune/common/gcd.hh | 77 + dune/common/genericiterator.hh | 278 + dune/common/gmpfield.hh | 80 + dune/common/hash.hh | 349 ++ dune/common/hybridutilities.hh | 496 ++ dune/common/indent.hh | 115 + dune/common/indices.hh | 130 + dune/common/interfaces.hh | 30 + dune/common/ios_state.cc | 34 + dune/common/ios_state.hh | 75 + dune/common/iteratorfacades.hh | 736 +++ dune/common/iteratorrange.hh | 64 + dune/common/keywords.hh | 38 + dune/common/lcm.hh | 44 + dune/common/lru.hh | 237 + dune/common/mallocallocator.hh | 117 + dune/common/math.hh | 364 ++ dune/common/matvectraits.hh | 33 + dune/common/modules.txt | 9 + dune/common/overloadset.hh | 212 + dune/common/parallel/CMakeLists.txt | 27 + dune/common/parallel/benchmark/CMakeLists.txt | 5 + .../benchmark/mpi_collective_benchmark.cc | 316 ++ dune/common/parallel/benchmark/options.ini | 5 + .../parallel/collectivecommunication.hh | 3 + dune/common/parallel/communication.hh | 528 ++ dune/common/parallel/communicator.hh | 1548 ++++++ dune/common/parallel/future.hh | 187 + dune/common/parallel/indexset.hh | 1163 ++++ dune/common/parallel/indicessyncer.hh | 1226 +++++ dune/common/parallel/interface.hh | 528 ++ dune/common/parallel/localindex.hh | 120 + .../parallel/mpicollectivecommunication.hh | 3 + dune/common/parallel/mpicommunication.hh | 468 ++ dune/common/parallel/mpidata.hh | 138 + dune/common/parallel/mpifuture.hh | 173 + dune/common/parallel/mpiguard.hh | 232 + dune/common/parallel/mpihelper.hh | 306 ++ dune/common/parallel/mpipack.hh | 225 + dune/common/parallel/mpitraits.hh | 201 + dune/common/parallel/plocalindex.hh | 320 ++ dune/common/parallel/remoteindices.hh | 1892 +++++++ dune/common/parallel/selection.hh | 341 ++ dune/common/parallel/test/CMakeLists.txt | 46 + dune/common/parallel/test/indexsettest.cc | 83 + dune/common/parallel/test/mpidatatest.cc | 128 + dune/common/parallel/test/mpifuturetest.cc | 106 + dune/common/parallel/test/mpipacktest.cc | 40 + .../common/parallel/test/remoteindicestest.cc | 723 +++ dune/common/parallel/test/selectiontest.cc | 95 + dune/common/parallel/test/syncertest.cc | 362 ++ .../test/variablesizecommunicatortest.cc | 437 ++ .../parallel/variablesizecommunicator.hh | 1225 +++++ dune/common/parameterizedobject.hh | 192 + dune/common/parametertree.cc | 241 + dune/common/parametertree.hh | 358 ++ dune/common/parametertreeparser.cc | 244 + dune/common/parametertreeparser.hh | 177 + dune/common/path.cc | 197 + dune/common/path.hh | 182 + dune/common/poolallocator.hh | 571 ++ dune/common/power.hh | 48 + dune/common/precision.hh | 49 + dune/common/promotiontraits.hh | 39 + dune/common/propertymap.hh | 332 ++ dune/common/proxymemberaccess.hh | 121 + dune/common/quadmath.hh | 446 ++ dune/common/rangeutilities.hh | 747 +++ dune/common/reservedvector.hh | 233 + dune/common/scalarmatrixview.hh | 206 + dune/common/scalarvectorview.hh | 212 + dune/common/shared_ptr.hh | 128 + dune/common/simd.hh | 501 ++ dune/common/simd/CMakeLists.txt | 18 + dune/common/simd/DESIGN.md | 306 ++ dune/common/simd/base.hh | 218 + dune/common/simd/defaults.hh | 186 + dune/common/simd/interface.hh | 543 ++ dune/common/simd/io.hh | 116 + dune/common/simd/loop.hh | 558 ++ dune/common/simd/simd.hh | 14 + dune/common/simd/standard.hh | 117 + dune/common/simd/test.cc | 23 + dune/common/simd/test.hh | 2082 ++++++++ dune/common/simd/test/CMakeLists.txt | 115 + dune/common/simd/test/looptest.cc.in | 37 + dune/common/simd/test/looptest.hh.in | 22 + dune/common/simd/test/looptest_vector.cc.in | 13 + dune/common/simd/test/standardtest.cc.in | 33 + dune/common/simd/test/standardtest.hh.in | 20 + .../simd/test/standardtest_vector.cc.in | 14 + dune/common/simd/test/vcarraytest.cc.in | 49 + dune/common/simd/test/vcarraytest.hh.in | 30 + dune/common/simd/test/vctest_mask.cc.in | 13 + dune/common/simd/test/vctest_simdarray.cc.in | 14 + .../simd/test/vctest_simdmaskarray.cc.in | 14 + dune/common/simd/test/vctest_vector.cc.in | 13 + dune/common/simd/test/vcvectortest.cc.in | 51 + dune/common/simd/test/vcvectortest.hh.in | 26 + dune/common/simd/vc.hh | 760 +++ dune/common/singleton.hh | 77 + dune/common/sllist.hh | 807 +++ dune/common/std/CMakeLists.txt | 10 + dune/common/std/apply.hh | 57 + dune/common/std/functional.hh | 32 + dune/common/std/make_array.hh | 47 + dune/common/std/memory.hh | 22 + dune/common/std/optional.hh | 493 ++ dune/common/std/type_traits.hh | 510 ++ dune/common/std/utility.hh | 46 + dune/common/std/variant.hh | 560 ++ dune/common/stdstreams.cc | 44 + dune/common/stdstreams.hh | 198 + dune/common/stdthread.cc | 80 + dune/common/stdthread.hh | 54 + dune/common/streamoperators.hh | 67 + dune/common/stringutility.hh | 113 + dune/common/test/CMakeLists.txt | 436 ++ dune/common/test/alignedallocatortest.cc | 46 + dune/common/test/arithmetictestsuite.hh | 802 +++ dune/common/test/arithmetictestsuitetest.cc | 31 + dune/common/test/arraylisttest.cc | 204 + dune/common/test/arraytest.cc | 37 + dune/common/test/assertandreturntest.cc | 91 + dune/common/test/autocopytest.cc | 52 + dune/common/test/bigunsignedinttest.cc | 122 + dune/common/test/bitsetvectortest.cc | 191 + dune/common/test/boundscheckingmvtest.cc | 391 ++ dune/common/test/boundscheckingoptest.cc | 141 + dune/common/test/boundscheckingtest.cc | 300 ++ dune/common/test/calloncetest.cc | 12 + dune/common/test/check_fvector_size.cc | 15 + dune/common/test/check_fvector_size_fail.cc | 15 + dune/common/test/checkmatrixinterface.hh | 435 ++ dune/common/test/classnametest.cc | 135 + dune/common/test/collectorstream.hh | 81 + dune/common/test/concept.cc | 200 + dune/common/test/constexprifelsetest.cc | 17 + dune/common/test/debugalignsimd.cc.in | 15 + dune/common/test/debugalignsimdtest.cc.in | 37 + dune/common/test/debugalignsimdtest.hh.in | 19 + dune/common/test/debugaligntest.cc | 110 + dune/common/test/densematrixassignmenttest.cc | 391 ++ dune/common/test/densevectorassignmenttest.cc | 49 + dune/common/test/densevectortest.cc | 59 + dune/common/test/diagonalmatrixtest.cc | 96 + dune/common/test/dummyiterator.hh | 44 + dune/common/test/dynmatrixtest.cc | 410 ++ dune/common/test/dynvectortest.cc | 81 + dune/common/test/eigenvaluestest.cc | 134 + dune/common/test/enumsettest.cc | 16 + dune/common/test/filledarraytest.cc | 39 + dune/common/test/fmatrixtest.cc | 946 ++++ dune/common/test/functiontest.cc | 39 + dune/common/test/fvectorconversion1d.cc | 127 + dune/common/test/fvectortest.cc | 629 +++ dune/common/test/gcdlcmtest.cc | 33 + .../test/genericiterator_compile_fail.cc | 20 + dune/common/test/hybridutilitiestest.cc | 110 + dune/common/test/indicestest.cc | 32 + dune/common/test/integersequence.cc | 34 + dune/common/test/iteratorfacadetest.cc | 60 + dune/common/test/iteratorfacadetest.hh | 50 + dune/common/test/iteratorfacadetest2.cc | 22 + dune/common/test/iteratortest.hh | 411 ++ dune/common/test/lrutest.cc | 45 + dune/common/test/mathclassifierstest.cc | 67 + dune/common/test/mathtest.cc | 80 + .../common/test/mpicollectivecommunication.cc | 65 + dune/common/test/mpicommunicationtest.cc | 65 + dune/common/test/mpiguardtest.cc | 93 + dune/common/test/mpihelpertest.cc | 44 + dune/common/test/optionaltest.cc | 99 + dune/common/test/overloadsettest.cc | 115 + .../parameterizedobjectfactorysingleton.cc | 22 + .../parameterizedobjectfactorysingleton.hh | 43 + dune/common/test/parameterizedobjecttest.cc | 104 + dune/common/test/parametertreelocaletest.cc | 112 + dune/common/test/parametertreetest.cc | 330 ++ dune/common/test/pathtest.cc | 205 + dune/common/test/poolallocatortest.cc | 166 + dune/common/test/powertest.cc | 61 + dune/common/test/quadmathtest.cc | 141 + dune/common/test/rangeutilitiestest.cc | 267 + dune/common/test/reservedvectortest.cc | 62 + dune/common/test/shared_ptrtest.cc | 241 + dune/common/test/singletontest.cc | 100 + dune/common/test/sllisttest.cc | 444 ++ dune/common/test/stdapplytest.cc | 62 + dune/common/test/stdidentity.cc | 49 + dune/common/test/stdtypetraitstest.cc | 88 + dune/common/test/streamoperatorstest.cc | 46 + dune/common/test/streamtest.cc | 55 + dune/common/test/stringutilitytest.cc | 58 + dune/common/test/testdebugallocator.cc | 108 + dune/common/test/testfloatcmp.cc | 217 + dune/common/test/testsuite.hh | 206 + dune/common/test/timing.cc | 140 + dune/common/test/to_unique_ptrtest.cc | 92 + dune/common/test/tupleutilitytest.cc | 183 + dune/common/test/typelisttest.cc | 243 + dune/common/test/typeutilitytest.cc | 32 + dune/common/test/utilitytest.cc | 86 + dune/common/test/varianttest.cc | 147 + dune/common/test/vcexpectedimpltest.cc | 71 + dune/common/test/versiontest.cc | 61 + dune/common/timer.hh | 151 + dune/common/to_unique_ptr.hh | 99 + dune/common/tupleutility.hh | 577 ++ dune/common/tuplevector.hh | 99 + dune/common/typelist.hh | 245 + dune/common/typetraits.hh | 730 +++ dune/common/typeutilities.hh | 95 + dune/common/unused.hh | 26 + dune/common/vc.hh | 28 + dune/common/version.hh | 278 + dune/common/visibility.hh | 43 + lib/CMakeLists.txt | 2 + lib/dunemodules.lib | 638 +++ share/CMakeLists.txt | 1 + share/bash-completion/CMakeLists.txt | 1 + .../completions/CMakeLists.txt | 4 + share/bash-completion/completions/dunecontrol | 83 + 367 files changed, 78954 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 CMakeLists.txt create mode 100644 CONTRIBUTING.md create mode 120000 COPYING create mode 100644 INSTALL create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 TODO create mode 100644 bin/CMakeLists.txt create mode 100755 bin/dune-ctest create mode 100755 bin/dune-git-whitespace-hook create mode 100755 bin/dunecontrol create mode 100755 bin/duneproject create mode 100644 cmake/modules/AddGMPFlags.cmake create mode 100644 cmake/modules/AddMETISFlags.cmake create mode 100644 cmake/modules/AddPTScotchFlags.cmake create mode 100644 cmake/modules/AddParMETISFlags.cmake create mode 100644 cmake/modules/AddQuadMathFlags.cmake create mode 100644 cmake/modules/AddSuiteSparseFlags.cmake create mode 100644 cmake/modules/AddUMFPackFlags.cmake create mode 100644 cmake/modules/AddVcFlags.cmake create mode 100644 cmake/modules/CMakeBuiltinFunctionsDocumentation.cmake create mode 100644 cmake/modules/CMakeLists.txt create mode 100644 cmake/modules/CheckCXXFeatures.cmake create mode 100644 cmake/modules/DuneCMakeCompat.cmake create mode 100644 cmake/modules/DuneCommonMacros.cmake create mode 100644 cmake/modules/DuneCxaDemangle.cmake create mode 100644 cmake/modules/DuneDoc.cmake create mode 100644 cmake/modules/DuneDoxygen.cmake create mode 100644 cmake/modules/DuneEnableAllPackages.cmake create mode 100644 cmake/modules/DuneExecuteProcess.cmake create mode 100644 cmake/modules/DuneInstance.cmake create mode 100644 cmake/modules/DuneMPI.cmake create mode 100644 cmake/modules/DuneMacros.cmake create mode 100644 cmake/modules/DunePathHelper.cmake create mode 100644 cmake/modules/DunePkgConfig.cmake create mode 100644 cmake/modules/DunePythonCommonMacros.cmake create mode 100644 cmake/modules/DunePythonFindPackage.cmake create mode 100644 cmake/modules/DunePythonInstallPackage.cmake create mode 100644 cmake/modules/DunePythonRequireVersion.cmake create mode 100644 cmake/modules/DunePythonTestCommand.cmake create mode 100644 cmake/modules/DunePythonVirtualenv.cmake create mode 100644 cmake/modules/DuneSphinxCMakeDoc.cmake create mode 100644 cmake/modules/DuneStreams.cmake create mode 100644 cmake/modules/DuneSymlinkOrCopy.cmake create mode 100644 cmake/modules/DuneTestMacros.cmake create mode 100644 cmake/modules/FindGMP.cmake create mode 100644 cmake/modules/FindInkscape.cmake create mode 100644 cmake/modules/FindLatexMk.cmake create mode 100644 cmake/modules/FindMETIS.cmake create mode 100644 cmake/modules/FindMProtect.cmake create mode 100644 cmake/modules/FindPTScotch.cmake create mode 100644 cmake/modules/FindParMETIS.cmake create mode 100644 cmake/modules/FindQuadMath.cmake create mode 100644 cmake/modules/FindSphinx.cmake create mode 100644 cmake/modules/FindSuiteSparse.cmake create mode 100644 cmake/modules/FindTBB.cmake create mode 100644 cmake/modules/FindUMFPack.cmake create mode 100644 cmake/modules/Headercheck.cmake create mode 100644 cmake/modules/LanguageSupport.cmake create mode 100644 cmake/modules/OverloadCompilerFlags.cmake create mode 100644 cmake/modules/UseInkscape.cmake create mode 100644 cmake/modules/UseLATEX.cmake create mode 100644 cmake/modules/UseLatexMk.cmake create mode 100644 cmake/modules/latexmkrc.cmake create mode 100644 cmake/pkg/dune-common-config.cmake.in create mode 100644 cmake/scripts/CMakeLists.txt create mode 100644 cmake/scripts/CreateDoxyFile.cmake create mode 100644 cmake/scripts/FinalizeHeadercheck.cmake create mode 100644 cmake/scripts/InstallFile.cmake create mode 100644 cmake/scripts/RunDoxygen.cmake create mode 100644 cmake/scripts/conf.py.in create mode 100644 cmake/scripts/envdetect.py create mode 100755 cmake/scripts/extract_cmake_data.py create mode 100644 cmake/scripts/index.rst.in create mode 100644 cmake/scripts/main77.cc.in create mode 100644 cmake/scripts/module_library.cc.in create mode 100644 cmake/scripts/pyversion.py create mode 100755 cmake/scripts/run-in-dune-env.sh.in create mode 100644 cmake/scripts/sphinx_cmake_dune.py create mode 100644 config.h.cmake create mode 100644 doc/CMakeLists.txt create mode 100644 doc/buildsystem/CMakeLists.txt create mode 100644 doc/buildsystem/dune-common.rst create mode 100644 doc/buildsystem/examples/Toolchain-Ubuntu-mingw32.cmake create mode 100644 doc/buildsystem/examples/opts.mingw32 create mode 100644 doc/comm/CMakeLists.txt create mode 100644 doc/comm/buildindexset.hh create mode 100644 doc/comm/communication.bib create mode 100644 doc/comm/communication.tex create mode 100644 doc/comm/figures/darray.eps create mode 100644 doc/comm/figures/distindex.eps create mode 100644 doc/comm/indexset.cc create mode 100644 doc/comm/poosc08.cc create mode 100644 doc/comm/poosc08_test.cc create mode 100644 doc/comm/reverse.hh create mode 100644 doc/doxygen/CMakeLists.txt create mode 100644 doc/doxygen/Doxylocal create mode 100644 doc/doxygen/Doxystyle create mode 100644 doc/doxygen/doxygen-macros create mode 100644 doc/doxygen/mainpage.txt create mode 100644 doc/doxygen/modules.txt create mode 100644 doc/dunecontrol.1 create mode 100644 dune-common.pc.in create mode 100644 dune.module create mode 100644 dune/CMakeLists.txt create mode 100644 dune/common/CMakeLists.txt create mode 100644 dune/common/alignedallocator.hh create mode 100644 dune/common/arraylist.hh create mode 100644 dune/common/assertandreturn.hh create mode 100644 dune/common/bartonnackmanifcheck.hh create mode 100644 dune/common/bigunsignedint.hh create mode 100644 dune/common/binaryfunctions.hh create mode 100644 dune/common/bitsetvector.hh create mode 100644 dune/common/boundschecking.hh create mode 100644 dune/common/classname.hh create mode 100644 dune/common/concept.hh create mode 100644 dune/common/conditional.hh create mode 100644 dune/common/debugalign.cc create mode 100644 dune/common/debugalign.hh create mode 100644 dune/common/debugallocator.cc create mode 100644 dune/common/debugallocator.hh create mode 100644 dune/common/debugstream.hh create mode 100644 dune/common/densematrix.hh create mode 100644 dune/common/densevector.hh create mode 100644 dune/common/deprecated.hh create mode 100644 dune/common/diagonalmatrix.hh create mode 100644 dune/common/documentation.hh create mode 100644 dune/common/dotproduct.hh create mode 100644 dune/common/dynmatrix.hh create mode 100644 dune/common/dynmatrixev.hh create mode 100644 dune/common/dynvector.hh create mode 100644 dune/common/enumset.hh create mode 100644 dune/common/exceptions.cc create mode 100644 dune/common/exceptions.hh create mode 100644 dune/common/filledarray.hh create mode 100644 dune/common/float_cmp.cc create mode 100644 dune/common/float_cmp.hh create mode 100644 dune/common/fmatrix.hh create mode 100644 dune/common/fmatrixev.cc create mode 100644 dune/common/fmatrixev.hh create mode 100644 dune/common/ftraits.hh create mode 100644 dune/common/function.hh create mode 100644 dune/common/fvector.hh create mode 100644 dune/common/gcd.hh create mode 100644 dune/common/genericiterator.hh create mode 100644 dune/common/gmpfield.hh create mode 100644 dune/common/hash.hh create mode 100644 dune/common/hybridutilities.hh create mode 100644 dune/common/indent.hh create mode 100644 dune/common/indices.hh create mode 100644 dune/common/interfaces.hh create mode 100644 dune/common/ios_state.cc create mode 100644 dune/common/ios_state.hh create mode 100644 dune/common/iteratorfacades.hh create mode 100644 dune/common/iteratorrange.hh create mode 100644 dune/common/keywords.hh create mode 100644 dune/common/lcm.hh create mode 100644 dune/common/lru.hh create mode 100644 dune/common/mallocallocator.hh create mode 100644 dune/common/math.hh create mode 100644 dune/common/matvectraits.hh create mode 100644 dune/common/modules.txt create mode 100644 dune/common/overloadset.hh create mode 100644 dune/common/parallel/CMakeLists.txt create mode 100644 dune/common/parallel/benchmark/CMakeLists.txt create mode 100644 dune/common/parallel/benchmark/mpi_collective_benchmark.cc create mode 100644 dune/common/parallel/benchmark/options.ini create mode 100644 dune/common/parallel/collectivecommunication.hh create mode 100644 dune/common/parallel/communication.hh create mode 100644 dune/common/parallel/communicator.hh create mode 100644 dune/common/parallel/future.hh create mode 100644 dune/common/parallel/indexset.hh create mode 100644 dune/common/parallel/indicessyncer.hh create mode 100644 dune/common/parallel/interface.hh create mode 100644 dune/common/parallel/localindex.hh create mode 100644 dune/common/parallel/mpicollectivecommunication.hh create mode 100644 dune/common/parallel/mpicommunication.hh create mode 100644 dune/common/parallel/mpidata.hh create mode 100644 dune/common/parallel/mpifuture.hh create mode 100644 dune/common/parallel/mpiguard.hh create mode 100644 dune/common/parallel/mpihelper.hh create mode 100644 dune/common/parallel/mpipack.hh create mode 100644 dune/common/parallel/mpitraits.hh create mode 100644 dune/common/parallel/plocalindex.hh create mode 100644 dune/common/parallel/remoteindices.hh create mode 100644 dune/common/parallel/selection.hh create mode 100644 dune/common/parallel/test/CMakeLists.txt create mode 100644 dune/common/parallel/test/indexsettest.cc create mode 100644 dune/common/parallel/test/mpidatatest.cc create mode 100644 dune/common/parallel/test/mpifuturetest.cc create mode 100644 dune/common/parallel/test/mpipacktest.cc create mode 100644 dune/common/parallel/test/remoteindicestest.cc create mode 100644 dune/common/parallel/test/selectiontest.cc create mode 100644 dune/common/parallel/test/syncertest.cc create mode 100644 dune/common/parallel/test/variablesizecommunicatortest.cc create mode 100644 dune/common/parallel/variablesizecommunicator.hh create mode 100644 dune/common/parameterizedobject.hh create mode 100644 dune/common/parametertree.cc create mode 100644 dune/common/parametertree.hh create mode 100644 dune/common/parametertreeparser.cc create mode 100644 dune/common/parametertreeparser.hh create mode 100644 dune/common/path.cc create mode 100644 dune/common/path.hh create mode 100644 dune/common/poolallocator.hh create mode 100644 dune/common/power.hh create mode 100644 dune/common/precision.hh create mode 100644 dune/common/promotiontraits.hh create mode 100644 dune/common/propertymap.hh create mode 100644 dune/common/proxymemberaccess.hh create mode 100644 dune/common/quadmath.hh create mode 100644 dune/common/rangeutilities.hh create mode 100644 dune/common/reservedvector.hh create mode 100644 dune/common/scalarmatrixview.hh create mode 100644 dune/common/scalarvectorview.hh create mode 100644 dune/common/shared_ptr.hh create mode 100644 dune/common/simd.hh create mode 100644 dune/common/simd/CMakeLists.txt create mode 100644 dune/common/simd/DESIGN.md create mode 100644 dune/common/simd/base.hh create mode 100644 dune/common/simd/defaults.hh create mode 100644 dune/common/simd/interface.hh create mode 100644 dune/common/simd/io.hh create mode 100644 dune/common/simd/loop.hh create mode 100644 dune/common/simd/simd.hh create mode 100644 dune/common/simd/standard.hh create mode 100644 dune/common/simd/test.cc create mode 100644 dune/common/simd/test.hh create mode 100644 dune/common/simd/test/CMakeLists.txt create mode 100644 dune/common/simd/test/looptest.cc.in create mode 100644 dune/common/simd/test/looptest.hh.in create mode 100644 dune/common/simd/test/looptest_vector.cc.in create mode 100644 dune/common/simd/test/standardtest.cc.in create mode 100644 dune/common/simd/test/standardtest.hh.in create mode 100644 dune/common/simd/test/standardtest_vector.cc.in create mode 100644 dune/common/simd/test/vcarraytest.cc.in create mode 100644 dune/common/simd/test/vcarraytest.hh.in create mode 100644 dune/common/simd/test/vctest_mask.cc.in create mode 100644 dune/common/simd/test/vctest_simdarray.cc.in create mode 100644 dune/common/simd/test/vctest_simdmaskarray.cc.in create mode 100644 dune/common/simd/test/vctest_vector.cc.in create mode 100644 dune/common/simd/test/vcvectortest.cc.in create mode 100644 dune/common/simd/test/vcvectortest.hh.in create mode 100644 dune/common/simd/vc.hh create mode 100644 dune/common/singleton.hh create mode 100644 dune/common/sllist.hh create mode 100644 dune/common/std/CMakeLists.txt create mode 100644 dune/common/std/apply.hh create mode 100644 dune/common/std/functional.hh create mode 100644 dune/common/std/make_array.hh create mode 100644 dune/common/std/memory.hh create mode 100644 dune/common/std/optional.hh create mode 100644 dune/common/std/type_traits.hh create mode 100644 dune/common/std/utility.hh create mode 100644 dune/common/std/variant.hh create mode 100644 dune/common/stdstreams.cc create mode 100644 dune/common/stdstreams.hh create mode 100644 dune/common/stdthread.cc create mode 100644 dune/common/stdthread.hh create mode 100644 dune/common/streamoperators.hh create mode 100644 dune/common/stringutility.hh create mode 100644 dune/common/test/CMakeLists.txt create mode 100644 dune/common/test/alignedallocatortest.cc create mode 100644 dune/common/test/arithmetictestsuite.hh create mode 100644 dune/common/test/arithmetictestsuitetest.cc create mode 100644 dune/common/test/arraylisttest.cc create mode 100644 dune/common/test/arraytest.cc create mode 100644 dune/common/test/assertandreturntest.cc create mode 100644 dune/common/test/autocopytest.cc create mode 100644 dune/common/test/bigunsignedinttest.cc create mode 100644 dune/common/test/bitsetvectortest.cc create mode 100644 dune/common/test/boundscheckingmvtest.cc create mode 100644 dune/common/test/boundscheckingoptest.cc create mode 100644 dune/common/test/boundscheckingtest.cc create mode 100644 dune/common/test/calloncetest.cc create mode 100644 dune/common/test/check_fvector_size.cc create mode 100644 dune/common/test/check_fvector_size_fail.cc create mode 100644 dune/common/test/checkmatrixinterface.hh create mode 100644 dune/common/test/classnametest.cc create mode 100644 dune/common/test/collectorstream.hh create mode 100644 dune/common/test/concept.cc create mode 100644 dune/common/test/constexprifelsetest.cc create mode 100644 dune/common/test/debugalignsimd.cc.in create mode 100644 dune/common/test/debugalignsimdtest.cc.in create mode 100644 dune/common/test/debugalignsimdtest.hh.in create mode 100644 dune/common/test/debugaligntest.cc create mode 100644 dune/common/test/densematrixassignmenttest.cc create mode 100644 dune/common/test/densevectorassignmenttest.cc create mode 100644 dune/common/test/densevectortest.cc create mode 100644 dune/common/test/diagonalmatrixtest.cc create mode 100644 dune/common/test/dummyiterator.hh create mode 100644 dune/common/test/dynmatrixtest.cc create mode 100644 dune/common/test/dynvectortest.cc create mode 100644 dune/common/test/eigenvaluestest.cc create mode 100644 dune/common/test/enumsettest.cc create mode 100644 dune/common/test/filledarraytest.cc create mode 100644 dune/common/test/fmatrixtest.cc create mode 100644 dune/common/test/functiontest.cc create mode 100644 dune/common/test/fvectorconversion1d.cc create mode 100644 dune/common/test/fvectortest.cc create mode 100644 dune/common/test/gcdlcmtest.cc create mode 100644 dune/common/test/genericiterator_compile_fail.cc create mode 100644 dune/common/test/hybridutilitiestest.cc create mode 100644 dune/common/test/indicestest.cc create mode 100644 dune/common/test/integersequence.cc create mode 100644 dune/common/test/iteratorfacadetest.cc create mode 100644 dune/common/test/iteratorfacadetest.hh create mode 100644 dune/common/test/iteratorfacadetest2.cc create mode 100644 dune/common/test/iteratortest.hh create mode 100644 dune/common/test/lrutest.cc create mode 100644 dune/common/test/mathclassifierstest.cc create mode 100644 dune/common/test/mathtest.cc create mode 100644 dune/common/test/mpicollectivecommunication.cc create mode 100644 dune/common/test/mpicommunicationtest.cc create mode 100644 dune/common/test/mpiguardtest.cc create mode 100644 dune/common/test/mpihelpertest.cc create mode 100644 dune/common/test/optionaltest.cc create mode 100644 dune/common/test/overloadsettest.cc create mode 100644 dune/common/test/parameterizedobjectfactorysingleton.cc create mode 100644 dune/common/test/parameterizedobjectfactorysingleton.hh create mode 100644 dune/common/test/parameterizedobjecttest.cc create mode 100644 dune/common/test/parametertreelocaletest.cc create mode 100644 dune/common/test/parametertreetest.cc create mode 100644 dune/common/test/pathtest.cc create mode 100644 dune/common/test/poolallocatortest.cc create mode 100644 dune/common/test/powertest.cc create mode 100644 dune/common/test/quadmathtest.cc create mode 100644 dune/common/test/rangeutilitiestest.cc create mode 100644 dune/common/test/reservedvectortest.cc create mode 100644 dune/common/test/shared_ptrtest.cc create mode 100644 dune/common/test/singletontest.cc create mode 100644 dune/common/test/sllisttest.cc create mode 100644 dune/common/test/stdapplytest.cc create mode 100644 dune/common/test/stdidentity.cc create mode 100644 dune/common/test/stdtypetraitstest.cc create mode 100644 dune/common/test/streamoperatorstest.cc create mode 100644 dune/common/test/streamtest.cc create mode 100644 dune/common/test/stringutilitytest.cc create mode 100644 dune/common/test/testdebugallocator.cc create mode 100644 dune/common/test/testfloatcmp.cc create mode 100644 dune/common/test/testsuite.hh create mode 100644 dune/common/test/timing.cc create mode 100644 dune/common/test/to_unique_ptrtest.cc create mode 100644 dune/common/test/tupleutilitytest.cc create mode 100644 dune/common/test/typelisttest.cc create mode 100644 dune/common/test/typeutilitytest.cc create mode 100644 dune/common/test/utilitytest.cc create mode 100644 dune/common/test/varianttest.cc create mode 100644 dune/common/test/vcexpectedimpltest.cc create mode 100644 dune/common/test/versiontest.cc create mode 100644 dune/common/timer.hh create mode 100644 dune/common/to_unique_ptr.hh create mode 100644 dune/common/tupleutility.hh create mode 100644 dune/common/tuplevector.hh create mode 100644 dune/common/typelist.hh create mode 100644 dune/common/typetraits.hh create mode 100644 dune/common/typeutilities.hh create mode 100644 dune/common/unused.hh create mode 100644 dune/common/vc.hh create mode 100644 dune/common/version.hh create mode 100644 dune/common/visibility.hh create mode 100644 lib/CMakeLists.txt create mode 100644 lib/dunemodules.lib create mode 100644 share/CMakeLists.txt create mode 100644 share/bash-completion/CMakeLists.txt create mode 100644 share/bash-completion/completions/CMakeLists.txt create mode 100644 share/bash-completion/completions/dunecontrol diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..93e7ef9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,169 @@ +# 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. + +- 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..6831f35 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +# set up project +project("dune-common" C CXX) + +# general stuff +cmake_minimum_required(VERSION 3.1) + +# make sure our own modules are found +set(CMAKE_MODULE_PATH ${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("lib") +add_subdirectory("share") +add_subdirectory("dune") +add_subdirectory("bin") +add_subdirectory("doc") +add_subdirectory("cmake/modules") +add_subdirectory("cmake/scripts") + +# 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..4bc0674 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,433 @@ +Copyright holders: +================== + +2015--2017 Marco Agnese +2015 Martin Alkämper +2003--2019 Peter Bastian +2004--2020 Markus Blatt +2013 Andreas Buhr +2011--2019 Ansgar Burchardt +2004--2005 Adrian Burri +2014 Benjamin Bykowski (may appear in the logs as "Convex Function") +2014 Marco Cecchetti +2018 Matthew Collins +2006--2019 Andreas Dedner +2019--2019 Nils-Arne Dreier +2003 Marc Droske +2003--2019 Christian Engwer +2004--2019 Jorrit Fahlke +2016 Thomas Fetzer +2008--2017 Bernd Flemisch +2013--2014 Christoph Gersbacher +2017--2020 Janick Gerstenberger +2015 Stefan Girke +2005--2019 Carsten Gräser +2015--2017 Felix Gruber +2010--2019 Christoph Grüninger +2006 Bernhard Haasdonk +2015--2018 Claus-Justus Heine +2015--2019 René Heß +2017--2019 Stephan Hilb +2017--2018 Lasse Hinrichsen +2012--2013 Olaf Ippisch +2013--2019 Dominic Kempf +2009 Leonard Kern +2017--2018 Daniel Kienle +2013 Torbjörn Klatt +2003--2019 Robert Klöfkorn +2017--2020 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 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 Santiago Ospina De Los Rios +2014 Steffen Persvold +2008--2017 Elias Pipping +2017--2019 Simon Praetorius +2011 Dan Popovic +2009 Atgeirr Rasmussen +2017-2018 Lukas Renelt +2006--2014 Uli Sack +2003--2019 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 + + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..69e2ccc --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +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 +- GNU C, C++ >=5 + these might also work: + icc (C/C++) >= 16.0 + Clang >= 3.8 + The compiler has to support C++14. + +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..61e35fe --- /dev/null +++ b/bin/CMakeLists.txt @@ -0,0 +1,6 @@ +install(PROGRAMS + dune-ctest + duneproject + dunecontrol + dune-git-whitespace-hook + 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/duneproject b/bin/duneproject new file mode 100755 index 0000000..f55e66a --- /dev/null +++ b/bin/duneproject @@ -0,0 +1,698 @@ +#!/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" <= 2.8.12 + +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 +# We require version CMake version 3.1 to prevent issues +# with dune_enable_all_packages and older CMake versions. +cmake_minimum_required(VERSION 3.1) +project($PROJECT CXX) + +if(NOT (dune-common_DIR OR dune-common_ROOT OR + "\${CMAKE_PREFIX_PATH}" MATCHES ".*dune-common.*")) + string(REPLACE \${CMAKE_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 +# 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" <_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..b91fd70 --- /dev/null +++ b/cmake/modules/CMakeLists.txt @@ -0,0 +1,52 @@ +install(FILES + AddGMPFlags.cmake + AddMETISFlags.cmake + AddParMETISFlags.cmake + AddPTScotchFlags.cmake + AddQuadMathFlags.cmake + AddSuiteSparseFlags.cmake + AddUMFPackFlags.cmake + AddVcFlags.cmake + CheckCXXFeatures.cmake + CMakeBuiltinFunctionsDocumentation.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 + DunePythonRequireVersion.cmake + DunePythonTestCommand.cmake + DunePythonVirtualenv.cmake + DuneSphinxCMakeDoc.cmake + DuneStreams.cmake + DuneSymlinkOrCopy.cmake + DuneTestMacros.cmake + FindGMP.cmake + FindInkscape.cmake + FindLatexMk.cmake + FindMETIS.cmake + FindMProtect.cmake + FindParMETIS.cmake + FindPTScotch.cmake + FindSphinx.cmake + FindSuiteSparse.cmake + FindTBB.cmake + FindUMFPack.cmake + Headercheck.cmake + LanguageSupport.cmake + latexmkrc.cmake + OverloadCompilerFlags.cmake + UseInkscape.cmake + UseLATEX.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..205d34d --- /dev/null +++ b/cmake/modules/CheckCXXFeatures.cmake @@ -0,0 +1,596 @@ +# .. cmake_module:: +# +# Module that checks for supported C++20, C++17, C++14 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++14 (c++17, ...) +# +# 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 +# +# :code:`DUNE_HAVE_CXX_CLASS_TEMPLATE_ARGUMENT_DEDUCTION` +# True if C++17's class template argument deduction is supported +# +# :code:`DUNE_HAVE_CXX_OPTIONAL` +# True if C++17's optional implementation is supported +# +# :code:`DUNE_HAVE_CXX_VARIANT` +# True if C++17's variant implementation 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 14) + + +# Compile tests for the different standard revisions; these test both the compiler +# and the associated library to avoid problems like using a C++14 user-installed +# compiler together with a non C++14-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 >{}; + } + ") + +string(REPLACE ";" "\;" cxx_14_test + " + #include + + constexpr auto f(int i) + { + if (i > 0) + return i; + else + return -i; + } + + int main() { + // lambdas with auto parameters are C++14 - so this checks the compiler + auto l = [](auto x) { return x; }; + static_assert(f(4) == f(-4),\"\"); + // std::make_unique() is a C++14 library feature - this checks whether the + // compiler uses a C++14 compliant library. + auto v = std::make_unique(l(0)); + return *v; + } + ") + +# build a list out of the pre-escaped tests +set(CXX_VERSIONS_TEST "${cxx_20_test}" "${cxx_17_test}" "${cxx_14_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" "14\;1y") + +# 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 3) + set(CXX_STD_NAME 03) + 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++03. \ +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. 14). 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 3) + 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++14 +dune_require_cxx_standard(MODULE "DUNE" VERSION 14) + +# 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 +) + +# full support for is_indexable (checking whether a type supports operator[]) +check_cxx_source_compiles(" + #include + #include + #include + + template + typename std::add_rvalue_reference::type declval(); + + namespace detail { + + template + struct _is_indexable + : public std::false_type + {}; + + template + struct _is_indexable()[declval()]) > 0),int>::type> + : public std::true_type + {}; + + } + + template + struct is_indexable + : public detail::_is_indexable + {}; + + struct foo_type {}; + + int main() + { + double x; + std::array y; + double z[5]; + foo_type f; + + static_assert(not is_indexable::value,\"scalar type\"); + static_assert(is_indexable::value,\"indexable class\"); + static_assert(is_indexable::value,\"array\"); + static_assert(not is_indexable::value,\"not indexable class\"); + static_assert(not is_indexable::value,\"custom index type\"); + + return 0; + } +" HAVE_IS_INDEXABLE_SUPPORT + ) + +# support for C++17's class template deduction guides +check_cxx_source_compiles(" + #include + + template + struct A { + A(T1) {} + + template + A(T2, T2) {} + }; + + struct B { + using type = bool; + }; + + template + A(T2, T2) + -> A; + + int main() + { + A a1(1); + static_assert(std::is_same_v< decltype(a1), A >); + + B b; + A a2(b, b); + static_assert(std::is_same_v< decltype(a2), A >); + } +" DUNE_HAVE_CXX_CLASS_TEMPLATE_ARGUMENT_DEDUCTION + ) + + +# support for C++17's optional implementation +check_cxx_source_compiles(" + #include + #include + + int main() + { + std::optional< std::string > a; + std::string b = a.value_or( \"empty\" ); + } +" DUNE_HAVE_CXX_OPTIONAL + ) + + +# support for C++17's variant implementation +check_cxx_source_compiles(" + #include + #include + + int main() + { + std::variant< int, std::string > a; + a = \"stringvalue\"; + std::string b = std::get< std::string >(a); + } +" DUNE_HAVE_CXX_VARIANT + ) + + +# find the threading library +if(NOT DEFINED THREADS_PREFER_PTHREAD_FLAG) + set(THREADS_PREFER_PTHREAD_FLAG 1) +endif() +find_package(Threads) +# text for feature summary +set_package_properties("Threads" PROPERTIES + DESCRIPTION "Multi-threading library") + +# see whether threading needs -no-as-needed +if(EXISTS /etc/dpkg/origins/ubuntu) + set(NO_AS_NEEDED "-Wl,-no-as-needed ") +else(EXISTS /etc/dpkg/origins/ubuntu) + set(NO_AS_NEEDED "") +endif(EXISTS /etc/dpkg/origins/ubuntu) + +set(STDTHREAD_LINK_FLAGS "${NO_AS_NEEDED}${CMAKE_THREAD_LIBS_INIT}" + CACHE STRING "Linker flags needed to get working C++11 threads support. On Ubuntu it may be necessary to include -Wl,-no-as-needed (see FS#1650).") + +# set linker flags +# +# in all implementations I know it is sufficient to set the linker flags when +# linking the final executable, so this should work. In cmake, this appears +# to only work when building the project however, not for later config tests +# (contrary to CMAKE_CXX_FLAGS). Luckily, later tests don't seem to use any +# threading... (except for our own sanity check) +if(NOT STDTHREAD_LINK_FLAGS STREQUAL "") + #set(vars CMAKE_EXE_LINKER_FLAGS ${CMAKE_CONFIGURATION_TYPES}) + # CMAKE_CONFIGURATION_TYPES seems to be empty. Use the configurations from + # adding -std=c++11 above instead. + set(vars CMAKE_EXE_LINKER_FLAGS DEBUG MINSIZEREL RELEASE RELWITHDEBINFO) + string(REPLACE ";" ";CMAKE_EXE_LINKER_FLAGS_" vars "${vars}") + string(TOUPPER "${vars}" vars) + foreach(var ${vars}) + if(NOT var STREQUAL "") + set(${var} "${${var}} ${STDTHREAD_LINK_FLAGS}") + endif() + endforeach(var ${vars}) +endif(NOT STDTHREAD_LINK_FLAGS STREQUAL "") + +include(CheckCXXSourceRuns) +# check that the found configuration works +if(CMAKE_CROSSCOMPILING) + message(WARNING "Crosscompiling, cannot run test program to see whether " + "std::thread works. Assuming that the found configuration does indeed " + "work.") +endif(CMAKE_CROSSCOMPILING) + +if(NOT DEFINED STDTHREAD_WORKS) + if(NOT CMAKE_CROSSCOMPILING) + # The value is not in the cache, so run check + cmake_push_check_state() + # tests seem to ignore CMAKE_EXE_LINKER_FLAGS + set(CMAKE_REQUIRED_LIBRARIES "${STDTHREAD_LINK_FLAGS} ${CMAKE_REQUIRED_LIBRARIES}") + check_cxx_source_runs(" + #include + + void dummy() {} + + int main() { + std::thread t(dummy); + t.join(); + } + " STDTHREAD_WORKS) + cmake_pop_check_state() + endif(NOT CMAKE_CROSSCOMPILING) + # put the found value into the cache. Put it there even if we're + # cross-compiling, so the user can find it. Use FORCE: + # check_cxx_source_runs() already puts the value in the cache but without + # documentation; also the "if(NOT DEFINED STDTHREAD_WORKS)" will prevent us + # from overwriting a value set by the user. + set(STDTHREAD_WORKS "${STDTHREAD_WORKS}" + CACHE BOOL "Whether std::thread works." FORCE) +endif(NOT DEFINED STDTHREAD_WORKS) + +if(NOT STDTHREAD_WORKS) + # Working C++11 threading support is required for dune. In particular to + # make things like lazyly initialized caches thread safe + # (e.g. QuadratureRules::rule(), which needs std::call_once()). If we don't + # include the correct options during linking, there will be very funny + # errors at runtime, ranging from segfaults to + # + # terminate called after throwing an instance of 'std::system_error' + # what(): Unknown error 18446744073709551615 + message(FATAL_ERROR "Your system does not seem to have a working " + "implementation of std::thread. If it does, please set the linker flags " + "required to get std::thread working in the cache variable " + "STDTHREAD_LINK_FLAGS. If you think this test is wrong, set the cache " + "variable STDTHREAD_WORKS.") +endif(NOT STDTHREAD_WORKS) + + +# Check whether we can conditionally throw exceptions in constexpr context to +# signal errors both at compile time and at run time - this does not work in GCC 5 +check_cxx_source_compiles(" + constexpr int foo(int bar) + { + if (bar < 0) + throw bar; + int r = 1; + for (int i = 0 ; i < bar ; ++i) + r += r; + return r; + } + + int main() + { + static_assert(foo(4) == 16, \"test failed\"); + return 0; + } +" DUNE_SUPPORTS_CXX_THROW_IN_CONSTEXPR + ) + +# Check whether the stadard library supports aligned_alloc() +check_cxx_source_compiles(" + #include + int main() + { + int* p = static_cast(aligned_alloc(64, 64*sizeof *p)); + } +" DUNE_HAVE_C_ALIGNED_ALLOC + ) + + + +# ****************************************************************************** +# +# 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 whether we have (for is_detected et. al.) +check_include_file_cxx( + experimental/type_traits + DUNE_HAVE_HEADER_EXPERIMENTAL_TYPE_TRAITS + ) + +check_cxx_symbol_exists( + "std::move>" + "utility;type_traits" + DUNE_HAVE_CXX_BOOL_CONSTANT + ) + +if (NOT DUNE_HAVE_CXX_BOOL_CONSTANT) + check_cxx_symbol_exists( + "std::move>" + "utility;experimental/type_traits" + DUNE_HAVE_CXX_EXPERIMENTAL_BOOL_CONSTANT + ) +endif() + +check_cxx_symbol_exists( + "std::apply,std::tuple>" + "functional;tuple" + DUNE_HAVE_CXX_APPLY + ) + +if (NOT DUNE_HAVE_CXX_APPLY) + check_cxx_symbol_exists( + "std::experimental::apply,std::tuple>" + "functional;experimental/tuple" + DUNE_HAVE_CXX_EXPERIMENTAL_APPLY + ) +endif() + +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/DuneCMakeCompat.cmake b/cmake/modules/DuneCMakeCompat.cmake new file mode 100644 index 0000000..8c5a90f --- /dev/null +++ b/cmake/modules/DuneCMakeCompat.cmake @@ -0,0 +1,98 @@ +# 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. + +# do not implicitly deference variable names in `if()` if they appear inside +# bracketed or quoted arguments. Unquouted arguments continue to be +# considered as variables names. +cmake_policy(SET CMP0054 NEW) + +# 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) + # 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..ce1925d --- /dev/null +++ b/cmake/modules/DuneCommonMacros.cmake @@ -0,0 +1,55 @@ +# .. cmake_module:: +# +# This modules content is executed whenever a module required or suggests dune-common! +# + +# enforce C++-14 +dune_require_cxx_standard(MODULE "dune-common" VERSION 14) + +include(DuneStreams) +dune_set_minimal_debug_level() + +if(Fortran_Works) + # search for lapack + find_package(LAPACK) + set(HAVE_LAPACK ${LAPACK_FOUND}) + if(${HAVE_LAPACK}) + dune_register_package_flags(LIBRARIES "${LAPACK_LIBRARIES}") + endif(${HAVE_LAPACK}) + set(HAVE_BLAS ${BLAS_FOUND}) +else(Fortran_Works) + set(HAVE_LAPACK Off) + set(HAVE_BLAS Off) +endif(Fortran_Works) +set_package_properties("BLAS" PROPERTIES + DESCRIPTION "fast linear algebra routines") +set_package_properties("LAPACK" PROPERTIES + DESCRIPTION "fast linear algebra routines") + +find_package(GMP) +include(AddGMPFlags) +find_package(QuadMath) +include(AddQuadMathFlags) +find_package(Inkscape) +include(UseInkscape) +include(FindMProtect) + +find_package(TBB OPTIONAL_COMPONENTS cpf allocator) + +# 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) +# 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") + +# 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..e37138f --- /dev/null +++ b/cmake/modules/DuneCxaDemangle.cmake @@ -0,0 +1,17 @@ +# This module checks whether the compiler supports the +# abi::__cxa_demangle function required to +# make the type names returned by typeid() human-readable +# +# It sets the variable :code:`HAVE_CXA_DEMANGLE` with the result. +# + +include(CheckCXXSourceCompiles) + +CHECK_CXX_SOURCE_COMPILES("#include +#include +int main(void){ + int foobar = 0; + const char *foo = typeid(foobar).name(); + int status; + char *demangled = abi::__cxa_demangle( foo, 0, 0, &status ); +}" HAVE_CXA_DEMANGLE) diff --git a/cmake/modules/DuneDoc.cmake b/cmake/modules/DuneDoc.cmake new file mode 100644 index 0000000..ddb4b90 --- /dev/null +++ b/cmake/modules/DuneDoc.cmake @@ -0,0 +1,159 @@ +# +# 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:: +# +# build a pdf document through the Dune build system. +# +# .. cmake_param:: texfile +# :single: +# :required: +# :positional: +# +# The texfile to compile into a pdf. +# +# .. note:: +# +# This function is a wrapper around add_latex_document. +# With Dune 2.7 the function from UseLatexmk.cmake is +# used and the one from UseLATEX.cmake is deprecated +# and will be removed after Dune 2.7. +# +# .. 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(UseLatexMk) + +# deprecated part for UseLATEX.cmake +find_package(LATEX) +set_package_properties("LATEX" PROPERTIES + DESCRIPTION "Type setting system" + PURPOSE "To generate the documentation") +find_program(IMAGEMAGICK_CONVERT convert) +set(HAVE_IMAGEMAGICK_CONVERT IMAGEMAGICK_CONVERT) +set_package_properties("IMAGEMAGICK_CONVERT" PROPERTIES + DESCRIPTION "convert program that comes with ImageMagick" + URL "www.imagemagick.org" + PURPOSE "To generate the documentation with LaTeX") +set(LATEX_USABLE TRUE) + +# check needed LaTeX executables +if(NOT LATEX_COMPILER) + message(WARNING " Need latex to create documentation!") + set(LATEX_USABLE FALSE) +endif() +if(NOT BIBTEX_COMPILER) + message(WARNING " Need bibtex to create documentation!") + set(LATEX_USABLE FALSE) +endif() +if(NOT MAKEINDEX_COMPILER) + message(WARNING " Need makeindex to create documentation!") + set(LATEX_USABLE FALSE) +endif() +if(NOT IMAGEMAGICK_CONVERT) + message(WARNING " Need imagemagick to create latex documentation!") + set(LATEX_USABLE FALSE) +endif() +if(LATEX_USABLE) + include(UseLATEX) + set_package_properties("UnixCommands" PROPERTIES + DESCRIPTION "Some common Unix commands" + PURPOSE "To generate the documentation with LaTeX") +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 tex_file) + set(latex_arguments "${ARGN}") + + # UseLatexmk iff one of the arguments is "SOURCE" + if (";${tex_file};${latex_arguments};" MATCHES ";SOURCE;") + add_latex_document(${tex_file} ${latex_arguments}) + + # UseLATEX, the old way + else() + message(AUTHOR_WARNING + "This function is deprecated and will be removed after Dune 2.7. \ + Use dune_add_latex_target with SOURCE as first argument as from \ + UseLatexMk.cmake instead!") + + if(LATEX_USABLE) + # add rule to create latex document + add_latex_document_deprecated(${tex_file} ${latex_arguments} + EXCLUDE_FROM_ALL + EXCLUDE_FROM_DEFAULTS) + # add dependency for target doc, but first construct document's target name + string(REGEX REPLACE "(.+).tex" "\\1" tex_file_base_name ${tex_file}) + list(FIND latex_arguments FORCE_DVI has_forcedvi) + if(has_forcedvi EQUAL -1) + add_dependencies(doc "${tex_file_base_name}") + else() + add_dependencies(doc "${tex_file_base_name}_safepdf") + endif() + else() + message(WARNING "Not adding rule to create ${file} as LaTeX is not usable!") + endif() + endif() +endmacro(dune_add_latex_document tex_file) diff --git a/cmake/modules/DuneDoxygen.cmake b/cmake/modules/DuneDoxygen.cmake new file mode 100644 index 0000000..76508d0 --- /dev/null +++ b/cmake/modules/DuneDoxygen.cmake @@ -0,0 +1,125 @@ +# 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`. +# + +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("${CMAKE_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) + 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..b258d99 --- /dev/null +++ b/cmake/modules/DuneEnableAllPackages.cmake @@ -0,0 +1,368 @@ +# 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 newer CMake +# versions 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`. +# +# .. warning:: +# The library feature requires CMake 3.1+. If you use the feature with older versions, CMake +# will emit a fatal error. Moreover, it will issue a warning if the :code:`cmake_minimum_required` +# version is older than 3.1. +# +# .. 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. +# + +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) + + # This only works for CMAKE 3.1+ because target_sources() - which we use to add sources to the + # libraries after creating them - was added in that version + if (CMAKE_VERSION VERSION_LESS 3.1.0) + message(FATAL_ERROR "dune_enable_all_packages() only supports MODULE_LIBRARIES for CMake 3.1+") + elseif(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.1.0) + message(WARNING +"You are using dune_enable_all_packages() with the MODULE_LIBRARIES feature. +This requires at least CMake 3.1, but your Dune module only requires ${CMAKE_MINIMUM_REQUIRED_VERSION}. +Update the cmake_minimum_required() call in your main CMakeLists.txt file to get rid of this warning.") + endif() + + # 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) + + # This only works for CMAKE 3.1+ because target_sources() - which we use to add sources to the + # libraries after creating them - was added in that version + if (CMAKE_VERSION VERSION_LESS 3.1.0) + message(FATAL_ERROR "dune_library_add_sources() requires CMake 3.1+") + endif() + + 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..f7f837d --- /dev/null +++ b/cmake/modules/DuneExecuteProcess.cmake @@ -0,0 +1,58 @@ +# 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. +# + +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..cbce943 --- /dev/null +++ b/cmake/modules/DuneInstance.cmake @@ -0,0 +1,922 @@ +# 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() +# +# dune_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. + + +###################################################################### +# +# 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(STATUS "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(STATUS "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(STATUS "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..99bd592 --- /dev/null +++ b/cmake/modules/DuneMPI.cmake @@ -0,0 +1,72 @@ +# Searches for MPI and thread support and sets the following +# DUNE specific flags: +# +# MPI_DUNE_COMPILE_FLAGS Compiler flags for MPI applications. +# MPI_DUNE_INCLUDE_PATH Include path for MPI applications. +# MPI_DUNE_LINK_FLAGS Linker flags for MPI applications. +# MPI_DUNE_LIBRARIES Libraries for MPI applications. +# +# 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. +# + + +find_package(MPI) +find_package(Threads) + +# 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}) + # We do not support the CXX bindings of MPI + set(MPI_DUNE_COMPILE_FLAGS ${MPI_C_COMPILE_FLAGS} CACHE STRING + "Compile flags used by DUNE when compiling MPI programs") + set(MPI_DUNE_INCLUDE_PATH ${MPI_C_INCLUDE_PATH} CACHE STRING + "Include path used by DUNE when compiling MPI programs") + # There seems to be no target specific include path, use the global one. + include_directories(${MPI_DUNE_INCLUDE_PATH}) + set(MPI_DUNE_LINK_FLAGS ${MPI_C_LINK_FLAGS} CACHE STRING + "Linker flags used by DUNE when compiling MPI programs") + set(MPI_DUNE_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} ${MPI_C_LIBRARIES} CACHE STRING + "Libraries used by DUNE when linking MPI programs") + + # TODO check on where to position this exactly, doesn't look completely thought through + dune_register_package_flags(COMPILE_DEFINITIONS "ENABLE_MPI=1;MPICH_SKIP_MPICXX;MPIPP_H;MPI_NO_CPPBIND" + INCLUDE_DIRS "${MPI_DUNE_INCLUDE_PATH}" + LIBRARIES "${MPI_DUNE_LIBRARIES}") +endif(MPI_C_FOUND) + +# adds MPI flags to the targets +function(add_dune_mpi_flags) + cmake_parse_arguments(ADD_MPI "SOURCE_ONLY;OBJECT" "" "" ${ARGN}) + if(ADD_MPI_SOURCE_ONLY) + set(_prefix SOURCE) + else() + set(_prefix TARGET) + endif() + if(MPI_C_FOUND) + separate_arguments(MPI_DUNE_COMPILE_FLAGS_LIST UNIX_COMMAND ${MPI_DUNE_COMPILE_FLAGS}) + set_property(${_prefix} ${ADD_MPI_UNPARSED_ARGUMENTS} APPEND PROPERTY COMPILE_OPTIONS ${MPI_DUNE_COMPILE_FLAGS_LIST}) + set_property(${_prefix} ${ADD_MPI_UNPARSED_ARGUMENTS} APPEND PROPERTY COMPILE_DEFINITIONS ENABLE_MPI=1 + MPICH_SKIP_MPICXX MPIPP_H) + if(NOT (ADD_MPI_SOURCE_ONLY OR ADD_MPI_OBJECT)) + set_property(${_prefix} ${ADD_MPI_UNPARSED_ARGUMENTS} APPEND_STRING PROPERTY LINK_FLAGS " ${MPI_DUNE_LINK_FLAGS} ") + foreach(target ${ADD_MPI_UNPARSED_ARGUMENTS}) + target_link_libraries(${target} ${MPI_DUNE_LIBRARIES}) + endforeach(target ${ADD_MPI_UNPARSED_ARGUMENTS}) + endif(NOT (ADD_MPI_SOURCE_ONLY OR ADD_MPI_OBJECT)) + endif(MPI_C_FOUND) +endfunction(add_dune_mpi_flags) diff --git a/cmake/modules/DuneMacros.cmake b/cmake/modules/DuneMacros.cmake new file mode 100644 index 0000000..1c9b041 --- /dev/null +++ b/cmake/modules/DuneMacros.cmake @@ -0,0 +1,1273 @@ +# 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:: 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. +# + +# Make CMake use rpath on OS X +if(POLICY CMP0042) + # this policy only needed for CMake older then 3.0 + cmake_policy(SET CMP0042 NEW) +endif() + +enable_language(C) # Enable C to skip CXX bindings for some tests. + +include(FeatureSummary) +include(DuneEnableAllPackages) +include(DuneTestMacros) +include(OverloadCompilerFlags) +include(DuneSymlinkOrCopy) +include(DunePathHelper) +include(DuneExecuteProcess) + +# 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}) + + # 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}") + + # configure CPack + set(CPACK_PACKAGE_NAME "${DUNE_MOD_NAME}") + set(CPACK_PACKAGE_VERSION "${DUNE_VERSION_MAJOR}.${DUNE_VERSION_MINOR}.${DUNE_VERSION_REVISION}") + set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") + set(CPACK_SOURCE_IGNORE_FILES "${CMAKE_BINARY_DIR}" "\\\\.svn" "\\\\.git" ".*/*\\\\.gitignore") +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 whether a compiler name instead of compiler path is given, this causes serious problems with older cmake versions. + # Unfortunately those errors only surface on a second run, when the build directory already exists. The compiler + # variable is then (for obscure reasons) expanded to ${CMAKE_BINARY_DIR}/... + if((${CMAKE_CXX_COMPILER} MATCHES "${CMAKE_BINARY_DIR}.*") AND (${CMAKE_VERSION} VERSION_LESS "3.0")) + message(FATAL_ERROR "You need to specify an absolute path to your compiler instead of just the compiler name. cmake >= 3.0 fixes this issue.") + endif() + + # check if CXX flag overloading has been enabled (see OverloadCompilerFlags.cmake) + initialize_compiler_script() + + # extract information from dune.module + dune_module_information(${CMAKE_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}") + + # 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 CMAKE_PROJECT_NAME)) + message(FATAL_ERROR "Module name from dune.module does not match the name given in CMakeLists.txt.") + endif() + + # optional Fortran support + include(LanguageSupport) + workaround_9220(Fortran Fortran_Works) + if(Fortran_Works) + enable_language(Fortran OPTIONAL) + if(NOT CMAKE_Fortran_COMPILER) + set(Fortran_Works OFF) + endif() + endif() + + option(DUNE_USE_ONLY_STATIC_LIBS "If set to ON, we will force static linkage everywhere" OFF) + if(DUNE_USE_ONLY_STATIC_LIBS) + if(BUILD_SHARED_LIBS) + message(FATAL_ERROR "Your requesting to use only static libraries " + "(DUNE_USE_ONLY_STATIC_LIBS==True) while at same time requesting to " + "build shared libraries (BUILD_SHARED_LIBS==True). This is a " + "contradiction!") + endif() + endif() + option(DUNE_BUILD_BOTH_LIBS "If set to ON, shared and static libs will be built" + ${_default_enable_static}) + + # 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) + + if(DUNE_USE_ONLY_STATIC_LIBS) + # Use only static libraries. + # We do this by overriding the library suffixes. + set( BLA_STATIC 1) + set( _dune_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + if(WIN32) + set(CMAKE_FIND_LIBRARY_SUFFIXES .lib ${CMAKE_FIND_LIBRARY_SUFFIXES}) + endif() + if(APPLE) + set(CMAKE_FIND_LIBRARY_SUFFIXES .lib ${CMAKE_FIND_LIBRARY_SUFFIXES}) + else() + set(CMAKE_FIND_LIBRARY_SUFFIXES .a) + endif() + endif() + + # check for C++ features, set compiler flags for C++14 or C++11 mode + include(CheckCXXFeatures) + include(DuneCxaDemangle) + + # set include path and link path for the current project. + include_directories("${CMAKE_BINARY_DIR}") + include_directories("${CMAKE_SOURCE_DIR}") + include_directories("${CMAKE_CURRENT_BINARY_DIR}") + include_directories("${CMAKE_CURRENT_SOURCE_DIR}") + add_definitions(-DHAVE_CONFIG_H) + + # Search for MPI and set the relevant variables. + include(DuneMPI) + + # Make calling fortran routines from C/C++ possible + if(Fortran_Works) + include(FortranCInterface) + FortranCInterface_VERIFY(CXX) + # Write FC.h header containing information about + # how to call fortran routined. + # It will be included in config.h + FortranCInterface_HEADER(FC.h MACRO_NAMESPACE "FC_") + else() + # Write empty FC.h header + # Make sure to only write this file once, otherwise every cmake run + # will trigger a full rebuild of the whole project. + unset(_FC_H CACHE) + find_file(_FC_H NAME FC.h PATHS "${CMAKE_BINARY_DIR}" NO_DEFAULT_PATH) + if(NOT _FC_H) + file(WRITE "${CMAKE_BINARY_DIR}/FC.h" "") + endif() + endif() + + # 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 "${CMAKE_BINARY_DIR}/config_collected.h.cmake") + if(EXISTS ${CMAKE_SOURCE_DIR}/config.h.cmake) + file(READ ${CMAKE_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(${CMAKE_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 ${CMAKE_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 + add_custom_target(OUTPUT config_collected.h.cmake + COMMAND dune_regenerate_config_cmake()) + # 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() + + include(CPack) + + feature_summary(WHAT ALL) + + # 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} ${_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) + dune_target_link_libraries(${basename} "${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") + + set(_created_libs ${basename}) + + if(DUNE_BUILD_BOTH_LIBS) + if(BUILD_SHARED_LIBS) + #create static lib + add_library(${basename}-static STATIC ${DUNE_LIB_SOURCES}) + # make sure both libs have the same name. + set_target_properties(${basename}-static PROPERTIES + OUTPUT_NAME ${basename} + ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") + list(APPEND _created_libs ${basename}-static) + # link with specified libraries. + if(DUNE_LIB_ADD_LIBS) + dune_target_link_libraries(${basename}-static "${DUNE_LIB_ADD_LIBS}") + endif() + if(DUNE_LIB_COMPILE_FLAGS) + set_property(${basename}-static APPEND_STRING COMPILE_FLAGS + "${DUNE_LIB_COMPILE_FLAGS}") + endif() + else() + #create shared libs + add_library(${basename}-shared SHARED ${DUNE_LIB_SOURCES}) + set_target_properties(${basename}-shared PROPERTIES + OUTPUT_NAME ${basename} + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") + # link with specified libraries. + if(DUNE_LIB_ADD_LIBS) + dune_target_link_libraries(${basename}-shared "${DUNE_LIB_ADD_LIBS}") + endif() + if(DUNE_LIB_COMPILE_FLAGS) + set_property(${basename}-shared APPEND_STRING COMPILE_FLAGS + "${DUNE_LIB_COMPILE_FLAGS}") + endif() + list(APPEND _created_libs ${basename}-shared) + endif() + endif() + + 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 ${_created_libs} + 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 ${_created_libs} ${_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} ${libraries}) + if(DUNE_BUILD_BOTH_LIBS) + if(BUILD_SHARED_LIBS) + target_link_libraries(${basename}-static ${libraries}) + else() + target_link_libraries(${basename}-shared ${libraries}) + endif() + endif() +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} ${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..48e0610 --- /dev/null +++ b/cmake/modules/DunePathHelper.cmake @@ -0,0 +1,101 @@ +# 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. +# + +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 ${CMAKE_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 ${CMAKE_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 ${CMAKE_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 ${CMAKE_SOURCE_DIR}) + set(IF_NOT_CURRENT_MOD ${${PATH_MODULE}_PREFIX}) + endif() + + # Now set the path in the outer scope! + if(CMAKE_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..b3f4eec --- /dev/null +++ b/cmake/modules/DunePkgConfig.cmake @@ -0,0 +1,47 @@ +# searches for pkg-config, creates the +# file .pc from .pc.in, +# and adds installation directives. +# + +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) + set( prefix ${CMAKE_INSTALL_PREFIX}) + 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..270272d --- /dev/null +++ b/cmake/modules/DunePythonCommonMacros.cmake @@ -0,0 +1,150 @@ +# 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 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(DunePythonRequireVersion) +include(DunePythonTestCommand) + +# Update the list of valid python versions, the shipped CMake modules tend to outdate... +# Mention all those not present in CMake 2.8.12 +set(Python_ADDITIONAL_VERSIONS 3.8 3.7 3.6 3.5 3.4) + +# Find the Python Interpreter +find_package(PythonInterp 3) + +# Find the Python libraries +find_package(PythonLibs) + +# Determine whether the given interpreter is running inside a virtualenv +if(PYTHONINTERP_FOUND) + include(DuneExecuteProcess) + include(DunePathHelper) + dune_module_path(MODULE dune-common + RESULT scriptdir + SCRIPT_DIR) + + dune_execute_process(COMMAND "${PYTHON_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 + ${CMAKE_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() diff --git a/cmake/modules/DunePythonFindPackage.cmake b/cmake/modules/DunePythonFindPackage.cmake new file mode 100644 index 0000000..8901d66 --- /dev/null +++ b/cmake/modules/DunePythonFindPackage.cmake @@ -0,0 +1,115 @@ +# 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:`${PYTHON_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. +# + +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 "${PYTHON_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..f001cd5 --- /dev/null +++ b/cmake/modules/DunePythonInstallPackage.cmake @@ -0,0 +1,146 @@ +# 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`. +# + +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} + "${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 ${PYTHON_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 ${PYTHON_EXECUTABLE} -m pip wheel -w ${DUNE_PYTHON_WHEELHOUSE} ${PYINST_FULLPATH}) + + # Add the installation rule + install(CODE "message(\"Installing wheel for python package at ${PYINST_FULLPATH}...\") + dune_execute_process(COMMAND ${WHEEL_COMMAND} + ERROR_MESSAGE \"Error installing wheel for python package at ${PYINST_FULLPATH}\" + )" + ) +endfunction() diff --git a/cmake/modules/DunePythonRequireVersion.cmake b/cmake/modules/DunePythonRequireVersion.cmake new file mode 100644 index 0000000..757fdc4 --- /dev/null +++ b/cmake/modules/DunePythonRequireVersion.cmake @@ -0,0 +1,14 @@ +# This module provided functions to implement constraints on the major version of the python interpreter. +# With Python2 support being dropped from Dune these are not needed anymore. + +if(DUNE_PYTHON_FORCE_PYTHON2 OR DUNE_PYTHON_FORCE_PYTHON3 OR DUNE_PYTHON_FORCE_PYTHON_VERSION) + message(WARNING "Python 2 support has been dropped from Dune. The variables DUNE_PYTHON_FORCE_PYTHON2, DUNE_PYTHON_FORCE_PYTHON3 and DUNE_PYTHON_FORCE_PYTHON_VERSION variables are not doing anything anymore.") +endif() + +function(dune_python_require_version) + message(WARNING "Dune has dropped Python2 support. The function dune_python_require_version is now no-op, is deprecated, and will be removed after Dune 2.7.") +endfunction() + +function(dune_python_force_version) + message(WARNING "Dune has dropped Python2 support. The function dune_python_force_version is now no-op, is deprecated, and will be removed after Dune 2.7.") +endfunction() diff --git a/cmake/modules/DunePythonTestCommand.cmake b/cmake/modules/DunePythonTestCommand.cmake new file mode 100644 index 0000000..a01ba75 --- /dev/null +++ b/cmake/modules/DunePythonTestCommand.cmake @@ -0,0 +1,78 @@ +# Wrap python testing commands into the CMake build system +# +# .. cmake_function:: dune_python_add_test +# +# .. cmake_param:: COMMAND +# :multi: +# :required: +# +# The command to run. It will be executed during :code:`make test_python` +# and during `ctest`. +# +# .. note:: +# +# If your testing command involves an invocation of the python +# interpreter you should use :code:`${PYTHON_EXECUTABLE}` for that. +# Also calling python executables through :code:`-m` is generally to +# be favored, e.g. :code:`${PYTHON_EXECUTABLE} -m pytest` instead of +# :code:`py.test`. +# +# .. 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`. +# + +function(dune_python_add_test) + # Parse Arguments + set(OPTION) + set(SINGLE WORKING_DIRECTORY NAME) + set(MULTI COMMAND) + include(CMakeParseArguments) + cmake_parse_arguments(PYTEST "${OPTION}" "${SINGLE}" "${MULTI}" ${ARGN}) + if(PYTEST_UNPARSED_ARGUMENTS) + message(WARNING "Unparsed arguments in dune_python_add_test: This often indicates typos!") + endif() + + # Apply defaults + if(NOT PYTEST_WORKING_DIRECTORY) + set(PYTEST_WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + endif() + if(NOT PYTEST_COMMAND) + message(FATAL_ERROR "dune_python_add_test: no COMMAND to execute specified!") + endif() + if(NOT PYTEST_NAME) + set(commandstr "") + foreach(comm ${PYTEST_COMMAND}) + 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_COMMAND} + WORKING_DIRECTORY ${PYTEST_WORKING_DIRECTORY}) + + # Build this during make test_python + add_dependencies(test_python target_${PYTEST_NAME}) + + # Also build this during ctest + _add_test(NAME ${PYTEST_NAME} + COMMAND ${PYTEST_COMMAND} + WORKING_DIRECTORY ${PYTEST_WORKING_DIRECTORY} + ) +endfunction() diff --git a/cmake/modules/DunePythonVirtualenv.cmake b/cmake/modules/DunePythonVirtualenv.cmake new file mode 100644 index 0000000..3d5dc24 --- /dev/null +++ b/cmake/modules/DunePythonVirtualenv.cmake @@ -0,0 +1,233 @@ +# 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. +# + +# 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 ${PYTHON_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 ${PYTHON_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 ${PYTHON_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..c85b13d --- /dev/null +++ b/cmake/modules/DuneSphinxCMakeDoc.cmake @@ -0,0 +1,183 @@ +# 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 +# + +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 PYTHONINTERP_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(${CMAKE_PROJECT_NAME}_PREFIX ${CMAKE_SOURCE_DIR}) + foreach(dep ${DOC_CMAKE_MODULES} ${CMAKE_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(${CMAKE_PROJECT_NAME}_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) + foreach(dep ${DOC_CMAKE_MODULES} ${CMAKE_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 ${PYTHON_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 ${CMAKE_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/DuneStreams.cmake b/cmake/modules/DuneStreams.cmake new file mode 100644 index 0000000..e3ad927 --- /dev/null +++ b/cmake/modules/DuneStreams.cmake @@ -0,0 +1,29 @@ +# 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`. +# + +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..0f96b10 --- /dev/null +++ b/cmake/modules/DuneSymlinkOrCopy.cmake @@ -0,0 +1,209 @@ +# 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. +# + +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( ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_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 ${CMAKE_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 "${CMAKE_SOURCE_DIR}/${dir}") + if(DUNE_SYMLINK_RELATIVE_LINKS) + file(RELATIVE_PATH _target "${CMAKE_BINARY_DIR}/${dir}" "${_target}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} "-E" "create_symlink" "${_target}" "${CMAKE_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( ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_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..2d3d9ee --- /dev/null +++ b/cmake/modules/DuneTestMacros.cmake @@ -0,0 +1,435 @@ +# 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. 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`. +# + +# 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) + set(ADDTEST_COMMAND ${ADDTEST_NAME}) + 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} ${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..23b2084 --- /dev/null +++ b/cmake/modules/FindGMP.cmake @@ -0,0 +1,110 @@ +# .. cmake_module:: +# +# Find the GNU MULTI-Precision Bignum (GMP) library +# and the corresponding C++ bindings GMPxx +# +# You may set the following variables to modify the +# behaviour of this module: +# +# :ref:`GMP_ROOT` +# Path list to search for GMP and GMPxx +# +# Sets the following variables: +# +# :code:`GMP_FOUND` +# True if the GMP library, the GMPxx headers and +# the GMPxx library were found. +# +# .. cmake_variable:: GMP_ROOT +# +# You may set this variable to have :ref:`FindGMP` look +# for the gmp and gmpxx packages in the given path before +# inspecting system paths. +# + + +# search for location of header gmpxx.h", only at positions given by the user +find_path(GMPXX_INCLUDE_DIR + NAMES "gmpxx.h" + PATHS ${GMP_PREFIX} ${GMP_ROOT} + PATH_SUFFIXES include + NO_DEFAULT_PATH) +# try default paths now +find_path(GMPXX_INCLUDE_DIR + NAMES "gmpxx.h") + +# check if header is accepted +include(CMakePushCheckState) +cmake_push_check_state() +set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${GMPXX_INCLUDE_DIR}) +include(CheckIncludeFileCXX) +check_include_file_cxx("gmpxx.h" GMP_HEADER_WORKS) + +# look for library gmp, only at positions given by the user +find_library(GMP_LIB gmp + PATHS ${GMP_PREFIX} ${GMP_ROOT} + PATH_SUFFIXES lib lib64 + NO_DEFAULT_PATH + DOC "GNU GMP library") +# try default paths now +find_library(GMP_LIB gmp) + +# look for library gmpxx, only at positions given by the user +find_library(GMPXX_LIB gmpxx + PATHS ${GMP_PREFIX} ${GMP_ROOT} + PATH_SUFFIXES lib lib64 + NO_DEFAULT_PATH + DOC "GNU GMPXX library") +# try default paths now +find_library(GMPXX_LIB gmpxx) + +# check if library works +if(GMP_LIB AND GMPXX_LIB) + include(CheckSymbolExists) + check_library_exists(${GMP_LIB} __gmpz_abs "" GMPXX_LIB_WORKS) +endif(GMP_LIB AND GMPXX_LIB) +cmake_pop_check_state() + +# behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + "GMP" + DEFAULT_MSG + GMPXX_INCLUDE_DIR GMP_LIB GMPXX_LIB GMP_HEADER_WORKS GMPXX_LIB_WORKS +) + +mark_as_advanced(GMP_LIB GMPXX_LIB GMPXX_INCLUDE_DIR) + +# text for feature summary +set_package_properties("GMP" PROPERTIES + DESCRIPTION "GNU multi-precision library including the C++ bindings GMPxx" + PURPOSE "Multi-precision quadrature rules, basis function evaluation etc.") + +# if GMPxx headers, GMP library, and GMPxx library are found, store results +if(GMP_FOUND) + set(GMP_INCLUDE_DIRS ${GMPXX_INCLUDE_DIR}) + set(GMP_LIBRARIES ${GMP_LIB} ${GMPXX_LIB}) + set(GMP_COMPILE_FLAGS "-DENABLE_GMP=1") + # log result + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determining location of GMP, GMPxx succeeded:\n" + "Include directory: ${GMP_INCLUDE_DIRS}\n" + "Library directory: ${GMP_LIBRARIES}\n\n") +else() + # log errornous result + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Determining location of GMP, GMPxx failed:\n" + "Include directory: ${GMPXX_INCLUDE_DIR}\n" + "gmp library directory: ${GMP_LIB}\n" + "gmpxx library directory: ${GMPXX_LIB}\n\n") +endif() + +# set HAVE_GMP for config.h +set(HAVE_GMP ${GMP_FOUND}) + +# register all GMP related flags +if(HAVE_GMP) + dune_register_package_flags(COMPILE_DEFINITIONS "ENABLE_GMP=1" + LIBRARIES "${GMP_LIB};${GMPXX_LIB}" + INCLUDE_DIRS "${GMPXX_INCLUDE_DIR}") +endif() diff --git a/cmake/modules/FindInkscape.cmake b/cmake/modules/FindInkscape.cmake new file mode 100644 index 0000000..0c1af73 --- /dev/null +++ b/cmake/modules/FindInkscape.cmake @@ -0,0 +1,24 @@ +# .. 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) +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..a6aa8d3 --- /dev/null +++ b/cmake/modules/FindMETIS.cmake @@ -0,0 +1,156 @@ +# .. cmake_module:: +# +# Find the METIS library +# +# You may set the following variables to modify the +# behaviour of this module: +# +# :ref:`METIS_ROOT` +# Prefix, where METIS is installed +# +# :ref:`METIS_LIB_NAME` +# Name of the METIS library (default: metis) +# +# :ref:`METIS_LIBRARY` +# Full path to the METIS library +# +# Sets the following variables: +# +# :code:`METIS_FOUND` +# True if the METIS library was found. +# +# :code:`METIS_LIBRARY` +# Full path to the METIS library +# +# :code:`METIS_LIBRARIES` +# List of libraries needed for linking with METIS +# +# .. cmake_variable:: METIS_ROOT +# +# You may set this variable to have :ref:`FindMETIS` look +# for the METIS library and includes in the given path +# before inspecting default system paths. +# +# .. cmake_variable:: METIS_LIB_NAME +# +# You may set this variable to specify the name of the METIS +# library that :ref:`FindMETIS` looks for. +# +# .. cmake_variable:: METIS_LIBRARY +# +# You may set this variable to specify the full path to the METIS +# library, that should be used by :ref:`FindMETIS`. +# + + +# search metis header +find_path(METIS_INCLUDE_DIR metis.h + PATHS ${METIS_DIR} ${METIS_ROOT} + PATH_SUFFIXES metis include include/metis Lib METISLib + NO_DEFAULT_PATH + DOC "Include directory of metis") +find_path(METIS_INCLUDE_DIR metis.h + PATH_SUFFIXES metis include include/metis Lib METISLib) + +set(METIS_LIBRARY METIS_LIBRARY-NOTFOUND CACHE FILEPATH "Full path of the METIS library") + +# check metis header +include(CMakePushCheckState) +cmake_push_check_state() # Save variables +set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${METIS_INCLUDE_DIR}) +check_include_file(metis.h METIS_FOUND) + +# search metis library +if(NOT METIS_LIB_NAME) + set(METIS_LIB_NAME metis) +endif(NOT METIS_LIB_NAME) + +find_library(METIS_LIBRARY ${METIS_LIB_NAME} + PATHS ${METIS_DIR} ${METIS_ROOT} + PATH_SUFFIXES lib + NO_DEFAULT_PATH) +find_library(METIS_LIBRARY ${METIS_LIB_NAME} + PATH_SUFFIXES lib +) + +# 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_LM_LIBRARY "-lm") +endif() + +# check metis library +if(METIS_LIBRARY) + set(_CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}") # do a backup + list(APPEND CMAKE_REQUIRED_LIBRARIES ${METIS_LIBRARY} ${_METIS_LM_LIBRARY}) + include(CheckFunctionExists) + check_function_exists(METIS_PartGraphKway HAVE_METIS_PARTGRAPHKWAY) + + if(NOT HAVE_METIS_PARTGRAPHKWAY) + # Maybe we are using static scotch libraries. In this case we need to link + # the other scotch libraries too. Let's make a best effort. + # Get the path where METIS_LIBRARY resides + get_filename_component(_lib_root ${METIS_LIBRARY} DIRECTORY) + # Search for additional libs only in this directory. + # Otherwise we might find incompatible ones, e.g. for int instead of long + find_library(SCOTCH_LIBRARY scotch PATHS ${_lib_root} "The Scotch library." NO_DEFAULT_PATH) + find_library(SCOTCHERR_LIBRARY scotcherr PATHS ${_lib_root} "The Scotch error library." + NO_DEFAULT_PATH) + if(SCOTCH_LIBRARY AND SCOTCHERR_LIBRARY) + set(_METIS_SCOTCH_LIBRARIES ${SCOTCH_LIBRARY} ${SCOTCHERR_LIBRARY} ${STDTHREAD_LINK_FLAGS} ) + set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES} ${METIS_LIBRARY} ${_METIS_SCOTCH_LIBRARIES} ${_METIS_LM_LIBRARY}) + # unset HAVE_METIS_PARTGRAPHKWAY to force another + # run of check_function_exists(METIS_PartGraphKway + unset(HAVE_METIS_PARTGRAPHKWAY CACHE) + check_function_exists(METIS_PartGraphKway HAVE_METIS_PARTGRAPHKWAY) + endif() + endif() + set(CMAKE_REQUIRED_LIBRARIES "${_CMAKE_REQUIRED_LIBRARIES}") +endif(METIS_LIBRARY) + +# behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + "METIS" + DEFAULT_MSG + METIS_INCLUDE_DIR + METIS_LIBRARY + HAVE_METIS_PARTGRAPHKWAY +) + +cmake_pop_check_state() + +mark_as_advanced(METIS_INCLUDE_DIR METIS_LIBRARIES METIS_LIB_NAME) + +# if both headers and library are found, store results +if(METIS_FOUND) + set(METIS_INCLUDE_DIRS ${METIS_INCLUDE_DIR}) + # We need to cache METIS_LIBRARIES as for subsequent runs + # The scotch stuff will not be set again!!! + set(METIS_LIBRARIES ${METIS_LIBRARY} ${_METIS_SCOTCH_LIBRARIES} ${_METIS_LM_LIBRARY} + CACHE STRING "List of all libraries needed to link to METIS") + set(HAVE_METIS ${METIS_FOUND}) + # log result + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determing location of METIS succeeded:\n" + "Include directory: ${METIS_INCLUDE_DIRS}\n" + "Library directory: ${METIS_LIBRARIES}\n\n") + # deprecate versions < 5 + file(READ "${METIS_INCLUDE_DIR}/metis.h" metisheader) + string(REGEX MATCH "#define METIS_VER_MAJOR[ ]+[0-9]+" versionMacroDef "${metisheader}") + string(REGEX MATCH "[0-9]+" MetisMajorVersion "${versionMacroDef}") + if("${versionMacroDef}" STREQUAL "" OR "${MetisMajorVersion}" LESS 5) + message(AUTHOR_WARNING "Support for METIS older than version 5.x is deprecated in Dune 2.7") + endif() +else(METIS_FOUND) + # log erroneous result + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Determing location of METIS failed:\n" + "Include directory: ${METIS_INCLUDE_DIRS}\n" + "Library directory: ${METIS_LIBRARIES}\n\n") +endif(METIS_FOUND) + +# register all METIS related flags +if(METIS_FOUND) + dune_register_package_flags(LIBRARIES "${METIS_LIBRARIES}" + INCLUDE_DIRS "${METIS_INCLUDE_DIRS}") +endif() diff --git a/cmake/modules/FindMProtect.cmake b/cmake/modules/FindMProtect.cmake new file mode 100644 index 0000000..75b3fcb --- /dev/null +++ b/cmake/modules/FindMProtect.cmake @@ -0,0 +1,18 @@ +# .. cmake_module:: +# +# Module that detects mprotect support +# +# Sets the following variables: +# +# * :code:`HAVE_SYS_MMAN_H` +# * :code:`HAVE_MPROTECT` +# + +include(CheckIncludeFile) +check_include_file("sys/mman.h" HAVE_SYS_MMAN_H) +include(CheckCSourceCompiles) +check_c_source_compiles(" +#include +int main(void){ + mprotect(0,0,PROT_NONE); +}" HAVE_MPROTECT) diff --git a/cmake/modules/FindPTScotch.cmake b/cmake/modules/FindPTScotch.cmake new file mode 100644 index 0000000..49ecbfa --- /dev/null +++ b/cmake/modules/FindPTScotch.cmake @@ -0,0 +1,110 @@ +# .. cmake_module:: +# +# Module that checks whether PT-Scotch is available. +# +# You may set the following variables to customize this modules behaviour: +# +# :ref:`PTSCOTCH_ROOT` +# Prefix where PT-Scotch is installed. +# +# :ref:`PTSCOTCH_SUFFIX` +# Scotch might be compiled using different +# integer sizes (int32, int64, long). When +# this is is set the headers and libaries +# are search under the suffix +# :code:`include/scotch-${PTSCOTCH_SUFFIX}`, and +# :code:`lib/scotch-${PTSCOTCH_SUFFIX}`, respectively. +# +# This module sets the following variables: +# +# :code:`PTSCOTCH_FOUND` +# True if PT-Scotch was found. +# +# :code:`PTSCOTCH_INCLUDE_DIRS` +# All include directories needed to compile PT-Scotch programs. +# +# :code:`PTSCOTCH_LIBRARIES` +# All libraries needed to link PT-Scotch programs. +# +# :code:`PTSCOTCH_FOUND` +# True if PT-Scotch was found. +# +# .. cmake_variable:: PTSCOTCH_ROOT +# +# You may set this variable to have :ref:`FindPTScotch` look +# for the PTScotch package in the given path before inspecting +# system paths. +# +# .. cmake_variable:: PTSCOTCH_SUFFIX +# +# PTScotch might be compiled using different +# integer sizes (int32, int64, long). When +# this is is set the headers and libaries +# are search under the suffix +# :code:`include/scotch-${PTSCOTCH_SUFFIX}`, and +# :code:`lib/scotch-${PTSCOTCH_SUFFIX}`, respectively. +# + +include(DuneMPI) +macro(_search_pt_lib libvar libname doc) + find_library(${libvar} ${libname} + PATHS ${PTSCOTCH_ROOT} ${PTSCOTCH_ROOT}/lib PATH_SUFFIXES ${PATH_SUFFIXES} + NO_DEFAULT_PATH + DOC "${doc}") + find_library(${libvar} ${libname}) +endmacro(_search_pt_lib) + +if(PTSCOTCH_SUFFIX) + set(PATH_SUFFIXES "scotch-${PTSCOTCH_SUFFIX}") +else(PTSCOTCH_SUFFIX) + set(PATH_SUFFIXES "scotch") +endif(PTSCOTCH_SUFFIX) + +include(CMakePushCheckState) +cmake_push_check_state() # Save variables +set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${MPI_DUNE_INCLUDE_PATH}) +set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${MPI_DUNE_COMPILE_FLAGS}") + +find_path(PTSCOTCH_INCLUDE_DIR ptscotch.h + PATHS ${PTSCOTCH_ROOT} ${PTSCOTCH_ROOT}/include + PATH_SUFFIXES ${PATH_SUFFIXES} + NO_DEFAULT_PATH + DOC "Include directory of PT-Scotch") +find_path(PTSCOTCH_INCLUDE_DIR ptscotch.h + PATH_SUFFIXES ${PATH_SUFFIXES}) + +_search_pt_lib(PTSCOTCH_LIBRARY ptscotch "The main PT-Scotch library.") +_search_pt_lib(SCOTCH_LIBRARY scotch "The Scotch library.") +_search_pt_lib(PTSCOTCHERR_LIBRARY ptscotcherr "The PT-Scotch error library.") + +# behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + "PTScotch" + DEFAULT_MSG + PTSCOTCH_INCLUDE_DIR + PTSCOTCH_LIBRARY + SCOTCH_LIBRARY + PTSCOTCHERR_LIBRARY +) +#restore old values +cmake_pop_check_state() + +if(PTSCOTCH_FOUND) + set(PTSCOTCH_INCLUDE_DIRS ${PTSCOTCH_INCLUDE_DIR}) + set(PTSCOTCH_LIBRARIES ${PTSCOTCH_LIBRARY} ${SCOTCH_LIBRARY} ${PTSCOTCHERR_LIBRARY} ${MPI_DUNE_LIBRARIES} + CACHE FILEPATH "All libraries needed to link programs using PT-Scotch") + set(PTSCOCH_LINK_FLAGS "${DUNE_MPI_LINK_FLAGS}" + CACHE STRING "PT-Scotch link flags") + set(HAVE_PTSCOTCH 1) + # log result + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determing location of PT-Scotch succeeded:\n" + "Include directory: ${PTSCOTCH_INCLUDE_DIRS}\n" + "Library directory: ${PTSCOTCH_LIBRARIES}\n\n") + + dune_register_package_flags(LIBRARIES "${PTSCOTCH_LIBRARIES}" + INCLUDE_DIRS "${PTSCOTCH_INCLUDE_DIRS}") +endif(PTSCOTCH_FOUND) + +mark_as_advanced(PTSCOTCH_INCLUDE_DIR PTSCOTCH_LIBRARIES HAVE_PTSCOTCH) diff --git a/cmake/modules/FindParMETIS.cmake b/cmake/modules/FindParMETIS.cmake new file mode 100644 index 0000000..f1b12dd --- /dev/null +++ b/cmake/modules/FindParMETIS.cmake @@ -0,0 +1,163 @@ +# .. cmake_module:: +# +# Module that checks whether ParMETIS is available. +# +# You may set the following variables to configure this modules behavior: +# +# :ref:`PARMETIS_ROOT` +# Prefix where ParMETIS is installed. +# +# :ref:`PARMETIS_LIB_NAME` +# Name of the ParMETIS library (default: parmetis). +# +# :ref:`PARMETIS_LIBRARY` +# Full path of the ParMETIS library +# +# Sets the following variables: +# +# :code:`PARMETIS_FOUND` +# True if ParMETIS was found. +# +# :code:`PARMETIS_LIBRARY` +# Full path of the ParMETIS library. +# +# :code:`PARMETIS_LIBRARIES` +# List of all libraries needed for linking with ParMETIS, +# +# .. cmake_variable:: PARMETIS_ROOT +# +# You may set this variable to have :ref:`FindParMETIS` look +# for the ParMETIS library and includes in the given path +# before inspecting default system paths. +# +# .. cmake_variable:: PARMETIS_LIB_NAME +# +# You may set this variable to specify the name of the ParMETIS +# library that :ref:`FindParMETIS` looks for. +# +# .. cmake_variable:: PARMETIS_LIBRARY +# +# You may set this variable to specify the full path to the ParMETIS +# library, that should be used by :ref:`FindParMETIS`. +# + +# find METIS first +find_package(METIS QUIET) +if(NOT METIS_FOUND) + find_package_handle_standard_args( + "ParMETIS" + DEFAULT_MSG "METIS not found which is required for ParMETIS." + ) +endif() + + +find_path(PARMETIS_INCLUDE_DIR parmetis.h + PATHS ${PARMETIS_DIR} ${PARMETIS_ROOT} + PATH_SUFFIXES include parmetis + NO_DEFAULT_PATH + DOC "Include directory of ParMETIS") +find_path(PARMETIS_INCLUDE_DIR parmetis.h + PATH_SUFFIXES include parmetis) + +set(PARMETIS_LIB_NAME parmetis + CACHE STRING "Name of the ParMETIS library (default: parmetis).") +set(PARMETIS_LIBRARY ParMETIS_LIBRARY-NOTFOUND + CACHE FILEPATH "Full path of the ParMETIS library") + +# check ParMETIS headers +include(CMakePushCheckState) +cmake_push_check_state() # Save variables +set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${MPI_DUNE_INCLUDE_PATH} ${METIS_INCLUDE_DIRS} ${PARMETIS_INCLUDE_DIR}) +set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${MPI_DUNE_COMPILE_FLAGS}") +# set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES} ${METIS_LIBRARIES}") +check_include_file(parmetis.h PARMETIS_FOUND) + +if(PARMETIS_FOUND) + set(ParMETIS_INCLUDE_PATH ${CMAKE_REQUIRED_INCLUDES}) + set(ParMETIS_COMPILE_FLAGS "${CMAKE_REQUIRED_FLAGS} -DENABLE_PARMETIS=1") + + # search ParMETIS library + find_library(PARMETIS_LIBRARY ${PARMETIS_LIB_NAME} + PATHS ${PARMETIS_DIR} ${PARMETIS_ROOT} + PATH_SUFFIXES lib + NO_DEFAULT_PATH) + find_library(PARMETIS_LIBRARY ${PARMETIS_LIB_NAME}) + + # check ParMETIS library + if(PARMETIS_LIBRARY) + set(_CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}") # do a backup + set(_PARMETIS_LIBRARIES "${PARMETIS_LIBRARY};${MPI_DUNE_LIBRARIES}") + list(APPEND CMAKE_REQUIRED_LIBRARIES "${_PARMETIS_LIBRARIES};${METIS_LIBRARIES}") + include(CheckFunctionExists) + check_function_exists(ParMETIS_V3_PartKway HAVE_PARMETIS) + if(NOT HAVE_PARMETIS) + # Maybe we are using static scotch libraries. In this case we need to link + # the other scotch libraries too. Let's make a best effort. + # Get the path where ParMETIS_LIBRARY resides + get_filename_component(_lib_root ${METIS_LIBRARY} DIRECTORY) + # Search for additional libs only in this directory. + # Otherwise we might find incompatible ones, e.g. for int instead of long + find_library(PTSCOTCH_LIBRARY ptscotch PATHS ${_lib_root} "The PT-Scotch library." + NO_DEFAULT_PATH) + find_library(PTSCOTCHERR_LIBRARY ptscotcherr PATHS ${_lib_root} "The Scotch error library." + NO_DEFAULT_PATH) + if(PTSCOTCH_LIBRARY AND PTSCOTCHERR_LIBRARY) + set(_PARMETIS_LIBRARIES ${PARMETIS_LIBRARY} ${PTSCOTCH_LIBRARY} + ${PTSCOTCHERR_LIBRARY} ${METIS_LIBRARIES} ${MPI_DUNE_LIBRARIES}) + set(CMAKE_REQUIRED_LIBRARIES ${_PARMETIS_LIBRARIES} + ${_CMAKE_REQUIRED_LIBRARIES}) + unset(HAVE_PARMETIS CACHE) + check_function_exists(ParMETIS_V3_PartKway HAVE_PARMETIS) + endif() + endif() + set(CMAKE_REQUIRED_LIBRARIES "${_CMAKE_REQUIRED_LIBRARIES}") # get backup + endif() +endif() + +# behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + "ParMETIS" + DEFAULT_MSG + PARMETIS_INCLUDE_DIR + PARMETIS_LIBRARY + HAVE_PARMETIS +) + +mark_as_advanced(PARMETIS_INCLUDE_DIR PARMETIS_LIBRARY PARMETIS_LIB_NAME) + +#restore old values +cmake_pop_check_state() + +if(PARMETIS_FOUND) + set(PARMETIS_INCLUDE_DIRS ${PARMETIS_INCLUDE_DIR}) + set(PARMETIS_LIBRARIES "${_PARMETIS_LIBRARIES}" + CACHE FILEPATH "ParMETIS libraries needed for linking") + set(PARMETIS_LINK_FLAGS "${DUNE_MPI_LINK_FLAGS}" + CACHE STRING "ParMETIS link flags") + # log result + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determing location of ParMETIS succeeded:\n" + "Include directory: ${PARMETIS_INCLUDE_DIRS}\n" + "Library directory: ${PARMETIS_LIBRARIES}\n\n") + # deprecate versions < 4 + file(READ "${PARMETIS_INCLUDE_DIR}/parmetis.h" parmetisheader) + string(REGEX MATCH "#define PARMETIS_MAJOR_VERSION[ ]+[0-9]+" versionMacroDef "${parmetisheader}") + string(REGEX MATCH "[0-9]+" ParMetisMajorVersion "${versionMacroDef}") + if("${versionMacroDef}" STREQUAL "" OR "${ParMetisMajorVersion}" LESS 4) + message(AUTHOR_WARNING "Support for ParMETIS older than version 4.x is deprecated in Dune 2.7") + endif() +else() + # log erroneous result + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Determing location of ParMETIS failed:\n" + "Include directory: ${PARMETIS_INCLUDE_DIR}\n" + "ParMETIS library directory: ${PARMETIS_LIBRARY}\n\n") +endif() + +# register all ParMETIS related flags +if(PARMETIS_FOUND) + dune_register_package_flags(COMPILE_DEFINITIONS "ENABLE_PARMETIS=1" + LIBRARIES "${PARMETIS_LIBRARIES}" + INCLUDE_DIRS "${PARMETIS_INCLUDE_DIRS}") +endif() diff --git a/cmake/modules/FindQuadMath.cmake b/cmake/modules/FindQuadMath.cmake new file mode 100644 index 0000000..ff70b33 --- /dev/null +++ b/cmake/modules/FindQuadMath.cmake @@ -0,0 +1,59 @@ +# .. cmake_module:: +# +# Find the GCC Quad-Precision library +# +# Sets the following variables: +# +# :code:`QUADMATH_FOUND` +# True if the Quad-Precision library was found. +# +# + + +# search for the header quadmath.h +include(CheckIncludeFile) +check_include_file(quadmath.h QUADMATH_HEADER) + +include(CheckCSourceCompiles) +include(CMakePushCheckState) + +cmake_push_check_state() # Save variables +set(CMAKE_REQUIRED_LIBRARIES quadmath) +check_c_source_compiles(" +#include + +int main () +{ + __float128 r = 1.0q; + r = strtoflt128(\"1.2345678\", NULL); + return 0; +}" QUADMATH_COMPILES) +cmake_pop_check_state() + + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + "QuadMath" + DEFAULT_MSG + QUADMATH_HEADER + QUADMATH_COMPILES +) + +# text for feature summary +set_package_properties("QuadMath" PROPERTIES + DESCRIPTION "GCC Quad-Precision library") + +# set HAVE_QUADMATH for config.h +set(HAVE_QUADMATH ${QUADMATH_FOUND}) + +# -fext-numeric-literals is a GCC extension not available in other compilers like clang +if(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) + set(_QUADMATH_EXT_NUMERIC_LITERALS "-fext-numeric-literals") +endif() + +# register all QuadMath related flags +if(HAVE_QUADMATH) + dune_register_package_flags(COMPILE_DEFINITIONS "ENABLE_QUADMATH=1" "_GLIBCXX_USE_FLOAT128=1" + COMPILE_OPTIONS ${_QUADMATH_EXT_NUMERIC_LITERALS} + LIBRARIES "quadmath") +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..e0bfc91 --- /dev/null +++ b/cmake/modules/FindSuiteSparse.cmake @@ -0,0 +1,281 @@ +# .. cmake_module:: +# +# Find the SuiteSparse libraries like UMFPACK or SPQR. +# +# Example which tries to find Suite Sparse's UMFPack component: +# +# :code:`find_package(SuiteSparse OPTIONAL_COMPONENTS UMFPACK)` +# +# `OPTIONAL_COMPONENTS` +# A list of components. Components are: +# AMD, BTF, CAMD, CCOLAMD, CHOLMOD, COLAMD, CXSPARSE, +# KLU, LDL, RBIO, SPQR, UMFPACK +# +# :ref:`SuiteSparse_ROOT` +# Path list to search for SuiteSparse +# +# Sets the following variables: +# +# :code:`SuiteSparse_FOUND` +# True if SuiteSparse was found. +# +# :code:`SuiteSparse_INCLUDE_DIRS` +# Path to the SuiteSparse include dirs. +# +# :code:`SuiteSparse_LIBRARIES` +# Name of the SuiteSparse libraries. +# +# :code:`SuiteSparse__FOUND` +# Whether was found as part of SuiteSparse. +# +# .. cmake_variable:: SuiteSparse_ROOT +# +# You may set this variable to have :ref:`FindSuiteSparse` look +# for SuiteSparse in the given path before inspecting +# system paths. +# + +find_package(BLAS QUIET) + +# look for desired componenents +set(SUITESPARSE_COMPONENTS ${SuiteSparse_FIND_COMPONENTS}) + +# resolve inter-component dependencies +list(FIND SUITESPARSE_COMPONENTS "UMFPACK" WILL_USE_UMFPACK) +if(NOT WILL_USE_UMFPACK EQUAL -1) + list(APPEND SUITESPARSE_COMPONENTS AMD CHOLMOD) +endif() +list(FIND SUITESPARSE_COMPONENTS "CHOLMOD" WILL_USE_CHOLMOD) +if(NOT WILL_USE_CHOLMOD EQUAL -1) + list(APPEND SUITESPARSE_COMPONENTS AMD CAMD COLAMD CCOLAMD) +endif() + +if(SUITESPARSE_COMPONENTS) + list(REMOVE_DUPLICATES SUITESPARSE_COMPONENTS) +endif() + +# find SuiteSparse config: +# look for library at positions given by the user +find_library(SUITESPARSE_CONFIG_LIB + NAMES "suitesparseconfig" + PATHS ${SuiteSparse_ROOT} + PATH_SUFFIXES "lib" "lib32" "lib64" "Lib" + NO_DEFAULT_PATH +) +# now also include the default paths +find_library(SUITESPARSE_CONFIG_LIB + NAMES "suitesparseconfig" + PATH_SUFFIXES "lib" "lib32" "lib64" "Lib" +) + +#look for header files at positions given by the user +find_path(SUITESPARSE_INCLUDE_DIR + NAMES "SuiteSparse_config.h" + PATHS ${SuiteSparse_ROOT} + PATH_SUFFIXES "SuiteSparse_config" "SuiteSparse_config/include" "suitesparse" "include" "src" "SuiteSparse_config/Include" + NO_DEFAULT_PATH +) +#now also look for default paths +find_path(SUITESPARSE_INCLUDE_DIR + NAMES "SuiteSparse_config.h" + PATH_SUFFIXES "SuiteSparse_config" "SuiteSparse_config/include" "suitesparse" "include" "src" "SuiteSparse_config/Include" +) + +foreach(_component ${SUITESPARSE_COMPONENTS}) + string(TOLOWER ${_component} _componentLower) + + #look for library at positions given by the user + find_library(${_component}_LIBRARY + NAMES "${_componentLower}" + PATHS ${SuiteSparse_ROOT} + PATH_SUFFIXES "lib" "lib32" "lib64" "${_component}" "${_component}/Lib" + NO_DEFAULT_PATH + ) + #now also include the default paths + find_library(${_component}_LIBRARY + NAMES "${_componentLower}" + PATH_SUFFIXES "lib" "lib32" "lib64" "${_component}" "${_component}/Lib" + ) + + #look for header files at positions given by the user + find_path(${_component}_INCLUDE_DIR + NAMES "${_componentLower}.h" + PATHS ${SuiteSparse_ROOT} + PATH_SUFFIXES "${_componentLower}" "include/${_componentLower}" "suitesparse" "include" "src" "${_component}" "${_component}/Include" + NO_DEFAULT_PATH + ) + #now also look for default paths + find_path(${_component}_INCLUDE_DIR + NAMES "${_componentLower}.h" + PATH_SUFFIXES "${_componentLower}" "include/${_componentLower}" "suitesparse" "include" "${_component}" "${_component}/Include" + ) +endforeach() + +# SPQR has different header file name SuiteSparseQR.hpp +#look for header files at positions given by the user +find_path(SPQR_INCLUDE_DIR + NAMES "SuiteSparseQR.hpp" + PATHS ${SuiteSparse_ROOT} + PATH_SUFFIXES "spqr" "include/spqr" "suitesparse" "include" "src" "SPQR" "SPQR/Include" + NO_DEFAULT_PATH +) +#now also look for default paths +find_path(SPQR_INCLUDE_DIR + NAMES "SuiteSparseQR.hpp" + PATH_SUFFIXES "spqr" "include/spqr" "suitesparse" "include" "SPQR" "SPQR/Include" +) + +# resolve inter-modular dependencies + +# CHOLMOD requires AMD, COLAMD; CAMD and CCOLAMD are optional +if(CHOLMOD_LIBRARY) + if(NOT (AMD_LIBRARY AND COLAMD_LIBRARY)) + message(WARNING "CHOLMOD requires AMD and COLAMD which were not found, skipping the test.") + set(SuiteSparse_CHOLMOD_FOUND "CHOLMOD requires AMD and COLAMD-NOTFOUND") + endif() + + list(APPEND CHOLMOD_LIBRARY ${AMD_LIBRARY} ${COLAMD_LIBRARY}) + if(CAMD_LIBRARY) + list(APPEND CHOLMOD_LIBRARY ${CAMD_LIBRARY}) + endif() + if(CCOLAMD_LIBRARY) + list(APPEND CHOLMOD_LIBRARY ${CCOLAMD_LIBRARY}) + endif() + list(REVERSE CHOLMOD_LIBRARY) + # remove duplicates + list(REMOVE_DUPLICATES CHOLMOD_LIBRARY) + list(REVERSE CHOLMOD_LIBRARY) +endif() + +# UMFPack requires AMD, can depend on CHOLMOD +if(UMFPACK_LIBRARY) + # check wether cholmod was found + if(CHOLMOD_LIBRARY) + list(APPEND UMFPACK_LIBRARY ${CHOLMOD_LIBRARY}) + else() + list(APPEND UMFPACK_LIBRARY ${AMD_LIBRARY}) + endif() + list(REVERSE UMFPACK_LIBRARY) + # remove duplicates + list(REMOVE_DUPLICATES UMFPACK_LIBRARY) + list(REVERSE UMFPACK_LIBRARY) +endif() + +# check wether everything was found +foreach(_component ${SUITESPARSE_COMPONENTS}) + # variable used for component handling + if(${_component}_LIBRARY AND ${_component}_INCLUDE_DIR) + set(SuiteSparse_${_component}_FOUND TRUE) + else() + set(SuiteSparse_${_component}_FOUND FALSE) + endif() + set(HAVE_SUITESPARSE_${_component} ${SuiteSparse_${_component}_FOUND}) + if(SuiteSparse_${_component}_FOUND) + list(APPEND SUITESPARSE_INCLUDE_DIR "${${_component}_INCLUDE_DIR}") + list(APPEND SUITESPARSE_LIBRARY "${${_component}_LIBRARY}") + endif() + + mark_as_advanced( + HAVE_SUITESPARSE_${_component} + SuiteSparse_${_component}_FOUND + ${_component}_INCLUDE_DIR + ${_component}_LIBRARY) +endforeach() + +# check version, for SPQR we need at least SuiteSparse 4.3 +if(SuiteSparse_SPQR_FOUND) + include(CheckCSourceCompiles) + include(CMakePushCheckState) + cmake_push_check_state() + set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${SUITESPARSE_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${SUITESPARSE_LIBRARY}) + # check whether version is at least 4.3 + check_c_source_compiles(" + #include + int main(void) + { + #ifndef SUITESPARSE_HAS_VERSION_FUNCTION + #error SuiteSparse <= 4.2.0 too old, required version 4.3 or newer for SPQR. + #endif + #if SUITESPARSE_VERSION <= 4003 + #error SuiteSparse too old, required version 4.3 or newer for SPQR. + #endif + return 0; + }" + SUITESPARSE_MIN_VERSION_4_3) + + if(NOT SUITESPARSE_MIN_VERSION_4_3) + set(SuiteSparse_SPQR_FOUND FALSE) + set(HAVE_SUITESPARSE_SPQR FALSE) + endif() + cmake_pop_check_state() +endif() + +list(APPEND SUITESPARSE_LIBRARY ${SUITESPARSE_CONFIG_LIB}) + +# make them unique +if(SUITESPARSE_INCLUDE_DIR) + list(REMOVE_DUPLICATES SUITESPARSE_INCLUDE_DIR) +endif() +if(SUITESPARSE_LIBRARY) + list(REVERSE SUITESPARSE_LIBRARY) + list(REMOVE_DUPLICATES SUITESPARSE_LIBRARY) + list(REVERSE SUITESPARSE_LIBRARY) +endif() + +# behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + "SuiteSparse" + FOUND_VAR SuiteSparse_FOUND + REQUIRED_VARS + BLAS_FOUND + SUITESPARSE_INCLUDE_DIR + SUITESPARSE_LIBRARY + HANDLE_COMPONENTS +) + +mark_as_advanced( + SUITESPARSE_INCLUDE_DIR + SUITESPARSE_LIBRARY + SUITESPARSE_CONFIG_LIB + SUITESPARSE_MIN_VERSION_4_3 + WILL_USE_CHOLMOD + WILL_USE_UMFPACK) + +# if both headers and library are found, store results +if(SuiteSparse_FOUND) + set(SuiteSparse_LIBRARIES ${SUITESPARSE_LIBRARY}) + set(SuiteSparse_INCLUDE_DIRS ${SUITESPARSE_INCLUDE_DIR}) + # log result + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determining location of SuiteSparse succeeded:\n" + "Include directory: ${SuiteSparse_INCLUDE_DIRS}\n" + "Library directory: ${SuiteSparse_LIBRARIES}\n\n") + set(SuiteSparse_COMPILER_FLAGS) + foreach(dir ${SuiteSparse_INCLUDE_DIRS}) + set(SuiteSparse_COMPILER_FLAGS "${SuiteSparse_COMPILER_FLAGS} -I${dir}/") + endforeach() + set(SuiteSparse_DUNE_COMPILE_FLAGS ${SuiteSparse_COMPILER_FLAGS} + CACHE STRING "Compile Flags used by DUNE when compiling with SuiteSparse programs") + set(SuiteSparse_DUNE_LIBRARIES ${BLAS_LIBRARIES} ${SuiteSparse_LIBRARIES} + CACHE STRING "Libraries used by DUNE when linking SuiteSparse programs") +else() + # log errornous result + file(APPEND ${CMAKE_BINARY_DIR}${CMAKES_FILES_DIRECTORY}/CMakeError.log + "Determing location of SuiteSparse failed:\n" + "Include directory: ${SuiteSparse_INCLUDE_DIRS}\n" + "Library directory: ${SuiteSparse_LIBRARIES}\n\n") +endif() + +#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_LIBRARIES}" + INCLUDE_DIRS "${SuiteSparse_INCLUDE_DIRS}") +endif() diff --git a/cmake/modules/FindTBB.cmake b/cmake/modules/FindTBB.cmake new file mode 100644 index 0000000..84e0a3d --- /dev/null +++ b/cmake/modules/FindTBB.cmake @@ -0,0 +1,467 @@ +# Locate Intel Threading Building Blocks include paths and libraries +# +# .. cmake_module:: +# +# TBB is a little special because there are three different ways to provide it: +# +# * It can be installed using a package manager and just be available on the system include +# and library paths. +# * It can be compiled from source. The package doesn't really provide an installation script +# for this, but expects you to source an environment file called :code:`tbbvars.sh` that updates the +# required variables like :code:`CPATH` etc. +# * TBB is shipped as part of the Intel compilers. This bundled version can be enabled by adding +# :code:`-tbb` to the compiler flags. +# +# This module can find all three types of installations. They are looked for in the following +# order of preference: :code:`tbbvars.sh` file (for a custom installation), system paths and finally +# the built-in version if an Intel compiler is present. +# +# .. note:: +# If you provide a tbbvars.sh script (via the CMake variable :ref:`TBB_VARS_SH`), this module will +# not find any libraries installed in the system path! This is on purpose to avoid +# accidental fallbacks. +# +# If the option :ref:`TBB_DEBUG` is set to ON, the module will look for the debug version of TBB. Note that +# this does not work for the built-in library of the Intel Compiler due to linking problems. You can +# however provide the module with the tbbvars.sh from that built-in installation (usually in the +# subdirectory :code:`tbb/` of the Intel compiler root path), which will fix that problem. +# +# Variables used by this module which you may want to set: +# +# :ref:`TBB_VARS_SH` +# Path to the :code:`tbbvars.sh` script +# +# :ref:`TBB_INCLUDE_DIR` +# Path to the include directory with the TBB headers +# +# :ref:`TBB_LIBRARY_DIR` +# Path to the library directory with the TBB libraries +# +# :ref:`TBB_DEBUG` +# Option that turns on TBB debugging +# +# This module supports additional components of TBB that can be listed in the :ref:`find_package` call: +# +# :code:`cpf` +# Use comunity preview edition (links to :code:`libtbb_preview` instead of :code:`libtbb`). cpf +# is not available for the built-in version of the Intel Compiler, but see the note +# on debug mode above for a fix. +# +# :code:`allocator` +# Use TBB's scalable allocator (links to libtbbmalloc). +# +# +# This module sets the following variables: +# +# :code:`TBB_FOUND` +# True if TBB was found and is usable +# +# :code:`TBB_cpf_FOUND` +# True if community preview edition was found and is usable +# +# :code:`TBB_allocator_FOUND` +# True if scalable allocator library was found and is usable +# +# :code:`TBB_INCLUDE_DIRS` +# Path to the TBB include dirs. This variable is empty if the +# internal TBB version of an Intel compiler is in use +# +# :code:`TBB_LIBRARIES` +# List of the TBB libraries that a target must be linked to +# +# :code:`TBB_COMPILE_DEFINITIONS` +# Required compile definitions to use TBB +# +# :code:`TBB_COMPILE_OPTIONS` +# Required compile options to use TBB +# +# :code:`TBB_INTEL_COMPILER_INTERNAL_TBB` +# True if internal TBB version of Intel compiler is in use +# +# In addition, TBB is automatically registered with the :ref:`dune_enable_all_packages` facility. If you +# don't want to use that feature, the module also provides the function :ref:`add_dune_tbb_flags`. +# +# .. cmake_function:: add_dune_tbb_flags +# +# .. cmake_param:: targets +# :positional: +# :single: +# :required: +# +# Adds all flags required to use TBB to the listed targets +# +# .. cmake_variable:: TBB_VARS_SH +# +# May be set to have the :ref:`FindTBB` module look for a :code:`tbbvars.sh` script +# +# .. cmake_variable:: TBB_INCLUDE_DIR +# +# May be set to have the :ref:`FindTBB` module look for TBB includes in custom locations. +# +# .. cmake_variable:: TBB_LIBRARY_DIR +# +# May be set to have the :ref:`FindTBB` module look for TBB libraries in custom locations. +# +# .. cmake_variable:: TBB_DEBUG +# +# May be set to have the :ref:`FindTBB` module look for debug TBB libraries. +# + +option( + TBB_DEBUG + "Turn on TBB debugging (modifies compiler flags and links against debug version of libraries)" + ) + +# source for our little test program. We have to compile this multiple times, so +# store it in a variable for DRY and better readability +set(tbb_compile_source " +#include +#include + +int main() +{ + int x[10] = {0}; + tbb::parallel_for(0,10,[&](int i){ x[i] = i; }); + return !(std::accumulate(x,x+10,0) == (9*10)/2); +} +") + + +# Function to parse a tbbvars.sh file and extract include and library paths. +# This function relies on the bash shell to source the tbbvars.sh file +function(parse_tbb_vars_sh) + message(STATUS "Taking TBB location from ${TBB_VARS_SH}") + find_package(UnixCommands) + set(tbb_vars_works FALSE) + execute_process( + COMMAND ${BASH} -c ". ${TBB_VARS_SH} > /dev/null" + RESULT_VARIABLE shell_result + OUTPUT_VARIABLE shell_out + ) + if (${shell_result} EQUAL 0) + set(tbb_vars_opt "") + set(tbb_vars_works TRUE) + else() + # try script from binary Linux installs that requires an 'intel64' argument + execute_process( + COMMAND ${BASH} -c ". ${TBB_VARS_SH} intel64 >/dev/null" + RESULT_VARIABLE shell_result + OUTPUT_VARIABLE shell_out + ) + if (${shell_result} EQUAL 0) + set(tbb_vars_opt "intel64") + set(tbb_vars_works TRUE) + endif() + endif() + if(tbb_vars_works) + execute_process( + COMMAND ${BASH} -c "unset CPATH ; . ${TBB_VARS_SH} ${tbb_vars_opt} >/dev/null && echo -n $CPATH" + RESULT_VARIABLE shell_result + OUTPUT_VARIABLE shell_out + ) + find_path( + TBB_INCLUDE_DIR + NAMES tbb/task_scheduler_init.h + PATHS ${shell_out} + DOC "Path to TBB include directory" + NO_DEFAULT_PATH + ) + execute_process( + COMMAND ${BASH} -c "unset LIBRARY_PATH ; . ${TBB_VARS_SH} ${tbb_vars_opt} >/dev/null && echo -n $LIBRARY_PATH" + RESULT_VARIABLE shell_result + OUTPUT_VARIABLE shell_out + ) + set( + TBB_LIBRARY_DIR ${shell_out} + CACHE PATH "Path to TBB library directory" + ) + else() + message(WARNING "Could not parse tbbvars.sh file at {TBB_VARS_SH}") + endif() +endfunction() + + +# Check whether the user gave us an existing tbbvars.sh file +find_file( + TBB_VARS_SH + tbbvars.sh + DOC "Path to tbbvars.sh script" + NO_DEFAULT_PATH + ) + + +if (TBB_VARS_SH) + parse_tbb_vars_sh() +else() + # Try to find TBB in standard include paths + find_path( + TBB_INCLUDE_DIR + tbb/task_scheduler_init.h + PATHS ENV CPATH + DOC "Path to TBB include directory" + ) + # Try to find some version of the TBB library in standard library paths + find_path( + TBB_LIBRARY_DIR + "${CMAKE_SHARED_LIBRARY_PREFIX}tbb_preview${CMAKE_SHARED_LIBRARY_SUFFIX}" + "${CMAKE_SHARED_LIBRARY_PREFIX}tbb${CMAKE_SHARED_LIBRARY_SUFFIX}" + "${CMAKE_SHARED_LIBRARY_PREFIX}tbb_preview_debug${CMAKE_SHARED_LIBRARY_SUFFIX}" + "${CMAKE_SHARED_LIBRARY_PREFIX}tbb_debug${CMAKE_SHARED_LIBRARY_SUFFIX}" + PATHS ENV LIBRARY_PATH + DOC "Path to TBB library directory" + ) + message(STATUS "Library dir: ${TBB_LIBRARY_DIR}") +endif() + + +# helper function to invoke find_library() correctly +# If we are using tbbvars.sh, we exclude system-default library search paths, +# otherwise we leave them in +function(find_tbb_library) + include(CMakeParseArguments) + set(OPTIONS) + set(SINGLEARGS VAR NAME DOC) + set(MULTIARGS) + cmake_parse_arguments(LIB "${OPTIONS}" "${SINGLEARGS}" "${MULTIARGS}" ${ARGN}) + + if(TBB_VARS_SH) + find_library( + ${LIB_VAR} + ${LIB_NAME} + PATHS ${TBB_LIBRARY_DIR} + DOC "${LIB_DOC}" + NO_DEFAULT_PATH + ) + else() + find_library( + ${LIB_VAR} + ${LIB_NAME} + PATHS ${TBB_LIBRARY_DIR} + DOC "${LIB_DOC}" + ) + endif() +endfunction() + + +# we always want to use TBB in C++11 mode +set(TBB_COMPILE_DEFINITIONS _TBB_CPP0X) + + +if(TBB_DEBUG) + message(STATUS "Linking against debug version of TBB") + set(tbb_debug_suffix "_debug") + # TBB requires this additional compile definition when used in debug mode + list(APPEND TBB_COMPILE_DEFINITIONS TBB_USE_DEBUG) +else() + set(tbb_debug_suffix "") +endif() + +# start looking for components +# We first look for component libraries, because the "special component" cpf +# actually replaces the standard libtbb + +set(TBB_cpf_FOUND FALSE) +set(TBB_allocator_FOUND FALSE) + +foreach(component ${TBB_FIND_COMPONENTS}) + + if(component STREQUAL "cpf") + find_tbb_library( + VAR TBB_LIBTBB_PREVIEW + NAME "tbb_preview${tbb_debug_suffix}" + DOC "Path to TBB community preview library" + ) + if(TBB_LIBTBB_PREVIEW) + list(APPEND TBB_LIBRARIES ${TBB_LIBTBB_PREVIEW}) + list(APPEND TBB_COMPILE_DEFINITIONS TBB_PREVIEW_LOCAL_OBSERVER=1) + set(TBB_cpf_FOUND TRUE) + endif() + + elseif(component STREQUAL "allocator") + find_tbb_library( + VAR TBB_LIBTBBMALLOC + NAME "tbbmalloc${tbb_debug_suffix}" + DOC "Path to TBB malloc library" + ) + if(TBB_LIBTBBMALLOC) + list(APPEND TBB_LIBRARIES ${TBB_LIBTBBMALLOC}) + set(TBB_allocator_FOUND TRUE) + endif() + + else() + message(FATAL_ERROR "Unknown TBB component: ${component}") + endif() +endforeach() + + +# If we could not find libtbb_preview, look for plain libtbb instead +if(NOT TBB_cpf_FOUND) + find_tbb_library( + VAR TBB_LIBTBB + NAME "tbb${tbb_debug_suffix}" + DOC "Path to TBB library" + ) + if(TBB_LIBTBB) + list(APPEND TBB_LIBRARIES ${TBB_LIBTBB}) + endif() +else() + # This avoids special-casing later on + set( + TBB_LIBTBB ${TBB_LIBTBB_PREVIEW} + CACHE FILEPATH "Path to TBB library" + ) +endif() + +# Don't show these to the user, they are just confusing +mark_as_advanced( + TBB_LIBTBB_PREVIEW + TBB_LIBTBB + TBB_LIBTBBMALLOC + ) + +include(CheckCXXSourceCompiles) +include(CMakePushCheckState) + +# make sure this variable always exists; it is only used if we pick the internal +# TBB implementation from an Intel Compiler +set(TBB_COMPILE_OPTIONS "") +set(TBB_INTEL_COMPILER_INTERNAL_TBB OFF) + +# We didn't manage to find TBB yet, so try if we can fall back to the one shipped +# as part of Intel's compiler +if ((NOT TBB_LIBTBB) AND("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")) + + # This while doesn't work in debug mode because -tbb always injects -ltbb into the linker flags, and that clashes + # with -ltbb_debug + if(NOT TBB_DEBUG) + message(STATUS "Could not find TBB in normal places, trying to fall back to internal version of Intel Compiler") + + # We'll just compile a program with -tbb and see whether that works + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_FLAGS -tbb) + foreach(_definition ${TBB_COMPILE_DEFINITIONS}) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D${_definition}") + endforeach() + set(CMAKE_REQUIRED_QUIET ON) + check_cxx_source_compiles( + "${tbb_compile_source}" + TBB_INTEL_COMPILER_INTERNAL_TBB + ) + + if(TBB_INTEL_COMPILER_INTERNAL_TBB) + # yeah, success + set(TBB_COMPILE_OPTIONS -tbb) + + # now check components + foreach(component ${TBB_FIND_COMPONENTS}) + + # again, this doesn't work because of the default -ltbb injected by -tbb + if(component STREQUAL "cpf") + message(STATUS "Cannot link to community preview version when using compiler-internal version of TBB. Please specify the tbbvars.sh script in TBB_VARS_SH") + + elseif(component STREQUAL "allocator") + # we'll check for this by trying to link against the library + set(CMAKE_REQUIRED_LIBRARIES tbbmalloc) + check_cxx_source_compiles( + "${tbb_compile_source}" + TBB_INTEL_COMPILER_INTERNAL_TBBMALLOC + ) + if(TBB_INTEL_COMPILER_INTERNAL_TBBMALLOC) + list(APPEND TBB_LIBRARIES tbbmalloc) + set(TBB_allocator_FOUND TRUE) + endif() + else() + message(FATAL_ERROR "Unknown TBB component: ${component}") + endif() + endforeach() + + # clean up check state + cmake_pop_check_state() + endif() + + else() + message(STATUS "Could not find TBB in normal places, and don't know how to fall back to internal version of Intel Compiler for debug version. +You can either turn of the TBB_DEBUG option or point the TBB_VARS_SH variable at the tbbvars.sh script shipped with your compiler.") + endif() +endif() + +# make sure everything works +cmake_push_check_state(RESET) +set(CMAKE_REQUIRED_INCLUDES ${TBB_INCLUDE_DIR}) +foreach(_definition ${TBB_COMPILE_DEFINITIONS}) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D${_definition}") +endforeach() +set(CMAKE_REQUIRED_FLAGS ${TBB_COMPILE_OPTIONS}) +set(CMAKE_REQUIRED_LIBRARIES ${TBB_LIBRARIES}) +check_cxx_source_compiles( + "${tbb_compile_source}" + TBB_COMPILE_TEST + ) +cmake_pop_check_state() + +# we don't want to leak any helper variables +unset(tbb_compile_source) + +# provide standard find_package() interface +include(FindPackageHandleStandardArgs) + +if(TBB_INTEL_COMPILER_INTERNAL_TBB) + set(TBB_INCLUDE_DIRS "") + set(TBB_LIBRARIES "") + + find_package_handle_standard_args( + TBB + REQUIRED_VARS TBB_COMPILE_OPTIONS TBB_COMPILE_TEST + HANDLE_COMPONENTS + ) +else() + + set(TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR}) + + find_package_handle_standard_args( + TBB + REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES TBB_COMPILE_TEST + HANDLE_COMPONENTS + ) + +endif() + +# set variable for config.h +set(HAVE_TBB ${TBB_FOUND}) + +# perform DUNE-specific setup tasks +if (TBB_FOUND) + set(TBB_CACHE_ALIGNED_ALLOCATOR_ALIGNMENT 128) + message(STATUS "defaulting TBB_CACHE_ALIGNED_ALLOCATOR_ALIGNMENT to 128") + dune_register_package_flags( + COMPILE_DEFINITIONS ENABLE_TBB=1 ${TBB_COMPILE_DEFINITIONS} + COMPILE_OPTIONS ${TBB_COMPILE_OPTIONS} + INCLUDE_DIRS ${TBB_INCLUDE_DIRS} + LIBRARIES ${TBB_LIBRARIES} + ) +endif() + + +# function for adding TBB flags to a list of targets +function(add_dune_tbb_flags _targets) + foreach(_target ${_targets}) + target_compile_definitions(${_target} PUBLIC ENABLE_TBB=1) + if(TBB_COMPILE_DEFINITIONS) + target_compile_definitions(${_target} PUBLIC ${TBB_COMPILE_DEFINITIONS}) + endif() + if(TBB_COMPILE_OPTIONS) + target_compile_options(${_target} PUBLIC ${TBB_COMPILE_OPTIONS}) + endif() + if(TBB_INCLUDE_DIRS) + target_include_directories(${_target} PUBLIC ${TBB_INCLUDE_DIRS}) + endif() + if(TBB_LIBRARIES) + target_link_libraries(${_target} ${TBB_LIBRARIES}) + endif() + endforeach(_target) +endfunction(add_dune_tbb_flags) + +# text for feature summary +set_package_properties("TBB" PROPERTIES + DESCRIPTION "Threading Building Blocks library" + PURPOSE "Parallel programming on multi-core processors") diff --git a/cmake/modules/FindUMFPack.cmake b/cmake/modules/FindUMFPack.cmake new file mode 100644 index 0000000..811253a --- /dev/null +++ b/cmake/modules/FindUMFPack.cmake @@ -0,0 +1,83 @@ +# .. cmake_module:: +# +# Find the UMFPack library +# +# .. deprecated:: 3.0 +# Use :code:`find_package(SuiteSparse OPTIONAL_COMPONENTS UMFPACK)` instead +# +# You may set the following variables to modify the +# behaviour of this module: +# +# :ref:`UMFPACK_ROOT` +# Path list to search for UMFPack. +# +# Sets the following variables: +# +# :code:`UMFPACK_FOUND` +# True if the GMP library was found. +# +# :code:`UMFPACK_INCLUDE_DIRS` +# List of include directories with the UMFPack headers +# +# :code:`UMFPACK_LIBRARIES` +# List of libraries to link with UMFPack. +# +# .. cmake_variable:: UMFPACK_ROOT +# +# You may set this variable to have :ref:`FindUMFPack` look +# for the UMFPack package in the given path before inspecting +# system paths. +# + +find_package(SuiteSparse OPTIONAL_COMPONENTS UMFPACK) + +# use find_package(SuiteSparse OPTIONAL_COMPONENTS UMFPACK) instead +message(WARNING "find_package(UMFPack) is deprecated, please use FindSuiteSparse instead") + +set(UMFPACK_INCLUDE_DIRS ${SuiteSparse_INCLUDE_DIRS}) +set(UMFPACK_LIBRARIES ${SuiteSparse_LIBRARIES}) + +# behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + "UMFPack" + DEFAULT_MSG + UMFPACK_INCLUDE_DIRS + UMFPACK_LIBRARIES +) + +mark_as_advanced(UMFPACK_INCLUDE_DIRS UMFPACK_LIBRARIES) + +# if both headers and library are found, store results +if(UMFPACK_FOUND) + foreach( dir ${UMFPACK_INCLUDE_DIR} ) + list( APPEND UMFPACK_INCLUDE_FLAGS "-I${dir}/ " ) + endforeach() + set(UMFPACK_LIBRARIES ${UMFPACK_LIBRARIES}) + # log result + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determining location of UMFPack succeeded:\n" + "Include directory: ${UMFPACK_INCLUDE_DIRS}\n" + "Library directory: ${UMFPACK_LIBRARIES}\n\n") + set(UMFPACK_DUNE_COMPILE_FLAGS "${UMFPACK_INCLUDE_FLAGS}" + CACHE STRING "Compile Flags used by DUNE when compiling with UMFPack programs") + set(UMFPACK_DUNE_LIBRARIES ${UMFPACK_LIBRARIES} ${BLAS_LIBRARIES} ${AMD_LIBRARY} + CACHE STRING "Libraries used by DUNE when linking UMFPack programs") +else(UMFPACK_FOUND) + # log errornous result + file(APPEND ${CMAKE_BINARY_DIR}${CMAKES_FILES_DIRECTORY}/CMakeError.log + "Determing location of UMFPack failed:\n" + "Include directory: ${UMFPACK_INCLUDE_DIRS}\n" + "Library directory: ${UMFPACK_LIBRARIES}\n\n") +endif(UMFPACK_FOUND) + +#set HAVE_UMFPACK for config.h +set(HAVE_UMFPACK ${UMFPACK_FOUND}) +set(HAVE_SUITESPARSE_UMFPACK ${UMFPACK_FOUND}) + +# register all umfpack related flags +if(UMFPACK_FOUND) + dune_register_package_flags(COMPILE_DEFINITIONS "ENABLE_UMFPACK=1" + LIBRARIES "${UMFPACK_LIBRARIES}" + INCLUDE_DIRS "${UMFPACK_INCLUDE_DIRS}") +endif() diff --git a/cmake/modules/Headercheck.cmake b/cmake/modules/Headercheck.cmake new file mode 100644 index 0000000..872ff55 --- /dev/null +++ b/cmake/modules/Headercheck.cmake @@ -0,0 +1,82 @@ +# .. 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. +# + +# 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) + add_custom_target(headercheck ${CMAKE_COMMAND} -DENABLE_HEADERCHECK=${ENABLE_HEADERCHECK} -P ${scriptdir}/FinalizeHeadercheck.cmake + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +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_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/LanguageSupport.cmake b/cmake/modules/LanguageSupport.cmake new file mode 100644 index 0000000..68159e8 --- /dev/null +++ b/cmake/modules/LanguageSupport.cmake @@ -0,0 +1,66 @@ +# cmake/modules/language_support.cmake +# +# Temporary additional general language support is contained within this +# file. + +# This additional function definition is needed to provide a workaround for +# CMake bug 9220. + +# On debian testing (cmake 2.6.2), I get return code zero when calling +# cmake the first time, but cmake crashes when running a second time +# as follows: +# +# -- The Fortran compiler identification is unknown +# CMake Error at /usr/share/cmake-2.6/Modules/CMakeFortranInformation.cmake:7 (GET_FILENAME_COMPONENT): +# get_filename_component called with incorrect number of arguments +# Call Stack (most recent call first): +# CMakeLists.txt:3 (enable_language) +# +# My workaround is to invoke cmake twice. If both return codes are zero, +# it is safe to invoke ENABLE_LANGUAGE(Fortran OPTIONAL) + +function(workaround_9220 language language_works) + #message("DEBUG: language = ${language}") + set(text + "project(test NONE) + cmake_minimum_required(VERSION 2.8.0) + set (CMAKE_Fortran_FLAGS \"${CMAKE_Fortran_FLAGS}\") + set (CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS}\") + enable_language(${language} OPTIONAL) + ") + file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/language_tests/${language}) + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/language_tests/${language}) + file(WRITE ${CMAKE_BINARY_DIR}/language_tests/${language}/CMakeLists.txt + ${text}) + execute_process( + COMMAND ${CMAKE_COMMAND} . -G "${CMAKE_GENERATOR}" + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/language_tests/${language} + RESULT_VARIABLE return_code + OUTPUT_QUIET + ERROR_QUIET + ) + + if(return_code EQUAL 0) + # Second run + execute_process ( + COMMAND ${CMAKE_COMMAND} . + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/language_tests/${language} + RESULT_VARIABLE return_code + OUTPUT_QUIET + ERROR_QUIET + ) + if(return_code EQUAL 0) + set(${language_works} ON PARENT_SCOPE) + else(return_code EQUAL 0) + set(${language_works} OFF PARENT_SCOPE) + endif(return_code EQUAL 0) + else(return_code EQUAL 0) + set(${language_works} OFF PARENT_SCOPE) + endif(return_code EQUAL 0) +endfunction(workaround_9220) + +# Temporary tests of the above function. +#workaround_9220(CXX CXX_language_works) +#message("CXX_language_works = ${CXX_language_works}") +#workaround_9220(CXXp CXXp_language_works) +#message("CXXp_language_works = ${CXXp_language_works}") diff --git a/cmake/modules/OverloadCompilerFlags.cmake b/cmake/modules/OverloadCompilerFlags.cmake new file mode 100644 index 0000000..c7fda9e --- /dev/null +++ b/cmake/modules/OverloadCompilerFlags.cmake @@ -0,0 +1,142 @@ +# 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. +# + +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..ce3f936 --- /dev/null +++ b/cmake/modules/UseInkscape.cmake @@ -0,0 +1,78 @@ +# 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! +# +# +# .. cmake_function:: inkscape_generate_eps_from_svg +# +# .. deprecated:: 2.7 +# Switch to PNG instead of EPS and use +# inkscape_generate_png_from_svg. +# + +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}) + execute_process( + COMMAND ${INKSCAPE} -z --export-dpi=${INKSCAPE_DPI} -e ${pic} ${CMAKE_CURRENT_SOURCE_DIR}/${input} + WORKING_DIRECTORY ${INKSCAPE_OUTPUT_DIR}) + endforeach() +endfunction() + +function(inkscape_generate_eps_from_svg) + message(AUTHOR_WARNING "inkscape_generate_eps_from_svg is deprecated and will be removed after Dune 2.7.") + cmake_parse_arguments(INKSCAPE "" "INPUT_DIR;OUTPUT_DIR;DPI" "" ${ARGN}) + if(NOT INKSCAPE_INPUT_DIR) + set(INKSCAPE_INPUT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + if(NOT INKSCAPE_INPUT_DIR) + set(INKSCAPE_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) + endif() + + foreach(_pic ${INKSCAPE_UNPARSED_ARGUMENTS}) + string(REGEX REPLACE "\\.[a-zA-Z]+" ".png" input "${_pic}") + string(REGEX REPLACE "\\.[a-zA-Z]+" ".svg" svginput "${_pic}") + + add_custom_target(${input} + COMMAND ${INKSCAPE} -z --export-dpi=${INKSCAPE_DPI} -e ${input} ${CMAKE_CURRENT_SOURCE_DIR}/${svginput} + COMMENT "Generating ${INKSCAPE_OUTPUT_DIR}/${svginput} from ${CMAKE_CURRENT_SOURCE_DIR}/${input}") + add_custom_command(OUTPUT ${_pic} + COMMAND ${CONVERT} ${INKSCAPE_OUTPUT_DIR}/${input} EPS:${_pic} + DEPENDS ${input} + COMMENT "Converting ${INKSCAPE_OUTPUT_DIR}/${input} to ${INKSCAPE_OUTPUT_DIR}/${_pic}" + WORKING_DIRECTORY ${INKSCAPE_OUTPUT_DIR}) + endforeach() +endfunction() diff --git a/cmake/modules/UseLATEX.cmake b/cmake/modules/UseLATEX.cmake new file mode 100644 index 0000000..f689a5a --- /dev/null +++ b/cmake/modules/UseLATEX.cmake @@ -0,0 +1,1967 @@ +# File: UseLATEX.cmake +# CMAKE commands to actually use the LaTeX compiler +# Version: 2.4.9 +# Author: Kenneth Moreland +# +# Copyright 2004, 2015 Sandia Corporation. +# Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive +# license for use of this work by or on behalf of the U.S. Government. +# +# This software is released under the BSD 3-Clause License. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. 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. +# +# 3. Neither the name of the copyright holder 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. +# +# +# The following function is defined: +# +# add_latex_document_deprecated( +# [BIBFILES ] +# [INPUTS ] +# [IMAGE_DIRS] +# [IMAGES] +# [CONFIGURE] +# [DEPENDS] +# [MULTIBIB_NEWCITES] +# [USE_BIBLATEX] +# [USE_INDEX] +# [INDEX_NAMES ] +# [USE_GLOSSARY] [USE_NOMENCL] +# [FORCE_PDF] [FORCE_DVI] [FORCE_HTML] +# [TARGET_NAME] +# [EXCLUDE_FROM_ALL] +# [EXCLUDE_FROM_DEFAULTS]) +# Adds targets that compile . The latex output is placed +# in LATEX_OUTPUT_PATH or CMAKE_CURRENT_BINARY_DIR if the former is +# not set. The latex program is picky about where files are located, +# so all input files are copied from the source directory to the +# output directory. This includes the target tex file, any tex file +# listed with the INPUTS option, the bibliography files listed with +# the BIBFILES option, and any .cls, .bst, .clo, .sty, .ist, and .fd +# files found in the current source directory. Images found in the +# IMAGE_DIRS directories or listed by IMAGES are also copied to the +# output directory and converted to an appropriate format if necessary. +# Any tex files also listed with the CONFIGURE option are also processed +# with the CMake CONFIGURE_FILE command (with the @ONLY flag). Any file +# listed in CONFIGURE but not the target tex file or listed with INPUTS +# has no effect. DEPENDS can be used to specify generated files that are +# needed to compile the latex target. +# +# The following targets are made. The name prefix is based off of the +# base name of the tex file unless TARGET_NAME is specified. If +# TARGET_NAME is specified, then that name is used for the targets. +# +# name_dvi: Makes .dvi +# name_pdf: Makes .pdf using pdflatex. +# name_safepdf: Makes .pdf using ps2pdf. If using the +# default program arguments, this will ensure all fonts +# are embedded and no lossy compression has been +# performed on images. +# name_ps: Makes .ps +# name_html: Makes .html +# name_auxclean: Deletes .aux and other auxiliary files. +# This is sometimes necessary if a LaTeX error occurs +# and writes a bad aux file. Unlike the regular clean +# target, it does not delete other input files, such as +# converted images, to save time on the rebuild. +# +# Unless the EXCLUDE_FROM_ALL option is given, one of these targets +# is added to the ALL target and built by default. Which target is +# determined by the LATEX_DEFAULT_BUILD CMake variable. See the +# documentation of that variable for more details. +# +# Unless the EXCLUDE_FROM_DEFAULTS option is given, all these targets +# are added as dependencies to targets named dvi, pdf, safepdf, ps, +# html, and auxclean, respectively. +# +# USE_BIBLATEX enables the use of biblatex/biber as an alternative to +# bibtex. Bibtex remains the default if USE_BIBLATEX is not +# specified. +# +# If the argument USE_INDEX is given, then commands to build an index +# are made. If the argument INDEX_NAMES is given, an index file is +# generated for each name in this list. See the LaTeX package multind +# for more information about how to generate multiple indices. +# +# If the argument USE_GLOSSARY is given, then commands to +# build a glossary are made. If the argument MULTIBIB_NEWCITES is +# given, then additional bibtex calls are added to the build to +# support the extra auxiliary files created with the \newcite command +# in the multibib package. +# +# History: +# +# 2.4.9 Use biblatex.cfg file if it exists and the USE_BIBLATEX option is ON. +# +# 2.4.8 Fix synctex issue with absolute paths not being converted. +# +# 2.4.7 Fix some issues with spaces in the path of the working directory where +# LaTeX is executed. +# +# 2.4.6 Fix parse issue with older versions of CMake. +# +# 2.4.5 Fix issues with files and paths containing spaces. +# +# 2.4.4 Improve error reporting message when LaTeX fails. +# +# When LaTeX fails, delete the output file, which is invalid. +# +# Add warnings for "missing characters." These usually mean that a +# non-ASCII character is in the document and will not be printed +# correctly. +# +# 2.4.3 Check for warnings from the natbib package. When using natbib, +# warnings for missing bibliography references look different. So +# far, natbib seems to be quiet unless something is important, so +# look for all natbib warnings. (We can change this later if +# necessary.) +# +# 2.4.2 Fix an issue where new versions of ImageMagick expect the order of +# options in command line execution of magick/convert. (See, for +# example, http://www.imagemagick.org/Usage/basics/#why.) +# +# 2.4.1 Add ability to dump LaTeX log file when using batch mode. Batch +# mode suppresses most output, often including error messages. To +# make sure critical error messages get displayed, show the full log +# on failures. +# +# 2.4.0 Remove "-r 600" from the default PDFTOPS_CONVERTER_FLAGS. The -r flag +# is available from the Poppler version of pdftops, but not the Xpdf +# version. +# +# Fix an issue with the flags for the different programs not being +# properly separated. +# +# Fix an issue on windows where the = character is not allowed for +# ps2pdf arguments. +# +# Change default arguments for latex and pdflatex commands. Makes the +# output more quiet and prints out the file/line where errors occur. +# (Thanks to Nikos Koukis.) +# +# After a LaTeX build, check the log file for warnings that are +# indicative of problems with the build. +# +# Remove support for latex2html. Instead, use the htlatex program. +# This is now part of TeX Live and most other distributions. It also +# behaves much more like the other LaTeX programs. Also fixed some +# nasty issues with the htlatex arguments. +# +# 2.3.2 Declare LaTeX input files as sources for targets so that they show +# up in IDEs like QtCreator. +# +# Fix issue where main tex files in subdirectories were creating +# invalid targets for building HTML. Just disable the HTML targets in +# this case. +# +# 2.3.1 Support use of magick command instead of convert command for +# ImageMagick 7. +# +# 2.3.0 Add USE_BIBLATEX option to support the biblatex package, which +# requires using the program biber as a replacement for bibtex +# (thanks to David Tracey). +# +# 2.2.1 Add STRINGS property to LATEX_DEFAULT_BUILD to make it easier to +# select the default build in the CMake GUI. +# +# 2.2.0 Add TARGET_NAME option. +# +# 2.1.1 Support for finding bmp, ppm, and other image files. +# +# 2.1.0 Fix an error where the pdf target and others were defined multiple +# times if UseLATEX.cmake was included multiple times. +# +# Added INDEX_NAMES option to support multiple indexes in a single +# document from the multind package (thanks to Dan Lipsa). +# +# 2.0.0 First major revision of UseLATEX.cmake updates to more recent features +# of CMake and some non-backward compatible changes. +# +# Changed all function and macro names to lower case. CMake's identifiers +# are case insensitive, but the convention moved from all upper case to +# all lower case somewhere around the release of CMake 2. (The original +# version of UseLATEX.cmake predates that.) +# +# Remove condition matching in if statements. They are no longer necessary +# and are even discouraged (because else clauses get confusing). +# +# Use "new" features available in CMake such as list and argument parsing. +# +# Remove some code that has been deprecated for a while. +# +# Mark variables for compiler and converter executables as advanced to +# match the more conventional CMake behavior. +# +# Changed how default builds are specified and add the ability to force +# a particular build. +# +# Made the base targets (pdf, dvi, etc.) global. add_latex_document +# always mangles its target names and these base targets depend on +# the targets with mangled names. +# +# 1.10.5 Fix for Window's convert check (thanks to Martin Baute). +# +# 1.10.4 Copy font files to binary directory for packages that come with +# their own fonts. +# +# 1.10.3 Check for Windows version of convert being used instead of +# ImageMagick's version (thanks to Martin Baute). +# +# 1.10.2 Use htlatex as a fallback when latex2html is not available (thanks +# to Tomasz Grzegurzko). +# +# 1.10.1 Make convert program mandatory only if actually used (thanks to +# Julien Schueller). +# +# 1.10.0 Added NO_DEFAULT and DEFAULT_PS options. +# Fixed issue with cleaning files for LaTeX documents originating in +# a subdirectory. +# +# 1.9.6 Fixed problem with LATEX_SMALL_IMAGES. +# Strengthened check to make sure the output directory does not contain +# the source files. +# +# 1.9.5 Add support for image types not directly supported by either latex +# or pdflatex. (Thanks to Jorge Gerardo Pena Pastor for SVG support.) +# +# 1.9.4 Fix issues with filenames containing multiple periods. +# +# 1.9.3 Hide some variables that are now cached but should not show up in +# the ccmake list of variables. +# +# 1.9.2 Changed MACRO declarations to FUNCTION declarations. The better +# FUNCTION scoping will hopefully avoid some common but subtle bugs. +# This implicitly increases the minimum CMake version to 4.6 (although +# I honestly only test it with the latest 4.8 version). +# +# Since we are updating the minimum CMake version, I'm going to start +# using the builtin LIST commands that are now available. +# +# Favor using pdftops from the Poppler package to convert from pdf to +# eps. It does a much better job than ImageMagick or ghostscript. +# +# 1.9.1 Fixed typo that caused the LATEX_SMALL_IMAGES option to fail to +# activate. +# +# 1.9.0 Add support for the multibib package (thanks to Antonio LaTorre). +# +# 1.8.2 Fix corner case when an argument name was also a variable containing +# the text of an argument. In this case, the CMake IF was matching +# the argument text with the contents of the variable with the same +# argument name. +# +# 1.8.1 Fix problem where ps2pdf was not getting the appropriate arguments. +# +# 1.8.0 Add support for synctex. +# +# 1.7.7 Support calling xindy when making glossaries. +# +# Improved make clean support. +# +# 1.7.6 Add support for the nomencl package (thanks to Myles English). +# +# 1.7.5 Fix issue with bibfiles being copied two different ways, which causes +# Problems with dependencies (thanks to Edwin van Leeuwen). +# +# 1.7.4 Added the DEFAULT_SAFEPDF option (thanks to Raymond Wan). +# +# Added warnings when image directories are not found (and were +# probably not given relative to the source directory). +# +# 1.7.3 Fix some issues with interactions between makeglossaries and bibtex +# (thanks to Mark de Wever). +# +# 1.7.2 Use ps2pdf to convert eps to pdf to get around the problem with +# ImageMagick dropping the bounding box (thanks to Lukasz Lis). +# +# 1.7.1 Fixed some dependency issues. +# +# 1.7.0 Added DEPENDS options (thanks to Theodore Papadopoulo). +# +# 1.6.1 Ported the makeglossaries command to CMake and embedded the port +# into UseLATEX.cmake. +# +# 1.6.0 Allow the use of the makeglossaries command. Thanks to Oystein +# S. Haaland for the patch. +# +# 1.5.0 Allow any type of file in the INPUTS lists, not just tex file +# (suggested by Eric Noulard). As a consequence, the ability to +# specify tex files without the .tex extension is removed. The removed +# function is of dubious value anyway. +# +# When copying input files, skip over any file that exists in the +# binary directory but does not exist in the source directory with the +# assumption that these files were added by some other mechanism. I +# find this useful when creating large documents with multiple +# chapters that I want to build separately (for speed) as I work on +# them. I use the same boilerplate as the starting point for all +# and just copy it with different configurations. This was what the +# separate ADD_LATEX_DOCUMENT method was supposed to originally be for. +# Since its external use is pretty much deprecated, I removed that +# documentation. +# +# 1.4.1 Copy .sty files along with the other class and package files. +# +# 1.4.0 Added a MANGLE_TARGET_NAMES option that will mangle the target names. +# +# Fixed problem with copying bib files that became apparent with +# CMake 2.4. +# +# 1.3.0 Added a LATEX_OUTPUT_PATH variable that allows you or the user to +# specify where the built latex documents to go. This is especially +# handy if you want to do in-source builds. +# +# Removed the ADD_LATEX_IMAGES macro and absorbed the functionality +# into ADD_LATEX_DOCUMENT. The old interface was always kind of +# clunky anyway since you had to specify the image directory in both +# places. It also made supporting LATEX_OUTPUT_PATH problematic. +# +# Added support for jpeg files. +# +# 1.2.0 Changed the configuration options yet again. Removed the NO_CONFIGURE +# Replaced it with a CONFIGURE option that lists input files for which +# configure should be run. +# +# The pdf target no longer depends on the dvi target. This allows you +# to build latex documents that require pdflatex. Also added an option +# to make the pdf target the default one. +# +# 1.1.1 Added the NO_CONFIGURE option. The @ character can be used when +# specifying table column separators. If two or more are used, then +# will incorrectly substitute them. +# +# 1.1.0 Added ability include multiple bib files. Added ability to do copy +# sub-tex files for multipart tex files. +# +# 1.0.0 If both ps and pdf type images exist, just copy the one that +# matches the current render mode. Replaced a bunch of STRING +# commands with GET_FILENAME_COMPONENT commands that were made to do +# the desired function. +# +# 0.4.0 First version posted to CMake Wiki. +# + +if(__USE_LATEX_INCLUDED) + return() +endif() +set(__USE_LATEX_INCLUDED TRUE) + +############################################################################# +# Find the location of myself while originally executing. If you do this +# inside of a macro, it will recode where the macro was invoked. +############################################################################# +set(LATEX_USE_LATEX_LOCATION ${CMAKE_CURRENT_LIST_FILE} + CACHE INTERNAL "Location of UseLATEX.cmake file." FORCE + ) + +############################################################################# +# Generic helper functions +############################################################################# + +include(CMakeParseArguments) + +function(latex_list_contains var value) + set(input_list ${ARGN}) + list(FIND input_list "${value}" index) + if(index GREATER -1) + set(${var} TRUE PARENT_SCOPE) + else() + set(${var} PARENT_SCOPE) + endif() +endfunction(latex_list_contains) + +# Match the contents of a file to a regular expression. +function(latex_file_match variable filename regexp default) + # The FILE STRINGS command would be a bit better, but I'm not totally sure + # the match will always be to a whole line, and I don't want to break things. + file(READ ${filename} file_contents) + string(REGEX MATCHALL "${regexp}" + match_result ${file_contents} + ) + if(match_result) + set(${variable} "${match_result}" PARENT_SCOPE) + else() + set(${variable} "${default}" PARENT_SCOPE) + endif() +endfunction(latex_file_match) + +# A version of GET_FILENAME_COMPONENT that treats extensions after the last +# period rather than the first. To the best of my knowledge, all filenames +# typically used by LaTeX, including image files, have small extensions +# after the last dot. +function(latex_get_filename_component varname filename type) + set(result) + if("${type}" STREQUAL "NAME_WE") + get_filename_component(name ${filename} NAME) + string(REGEX REPLACE "\\.[^.]*\$" "" result "${name}") + elseif("${type}" STREQUAL "EXT") + get_filename_component(name ${filename} NAME) + string(REGEX MATCH "\\.[^.]*\$" result "${name}") + else() + get_filename_component(result ${filename} ${type}) + endif() + set(${varname} "${result}" PARENT_SCOPE) +endfunction(latex_get_filename_component) + +############################################################################# +# Functions that perform processing during a LaTeX build. +############################################################################# +function(latex_execute_latex) + if(NOT LATEX_TARGET) + message(SEND_ERROR "Need to define LATEX_TARGET") + endif() + + if(NOT LATEX_WORKING_DIRECTORY) + message(SEND_ERROR "Need to define LATEX_WORKING_DIRECTORY") + endif() + + if(NOT LATEX_FULL_COMMAND) + message(SEND_ERROR "Need to define LATEX_FULL_COMMAND") + endif() + + if(NOT LATEX_OUTPUT_FILE) + message(SEND_ERROR "Need to define LATEX_OUTPUT_FILE") + endif() + + set(full_command_original "${LATEX_FULL_COMMAND}") + + # Chose the native method for parsing command arguments. Newer versions of + # CMake allow you to just use NATIVE_COMMAND. + if (CMAKE_VERSION VERSION_GREATER 3.8) + set(separate_arguments_mode NATIVE_COMMAND) + else() + if (WIN32) + set(separate_arguments_mode WINDOWS_COMMAND) + else() + set(separate_arguments_mode UNIX_COMMAND) + endif() + endif() + + # Preps variables for use in execute_process. + # Even though we expect LATEX_WORKING_DIRECTORY to have a single "argument," + # we also want to make sure that we strip out any escape characters that can + # foul up the WORKING_DIRECTORY argument. + separate_arguments(LATEX_FULL_COMMAND UNIX_COMMAND "${LATEX_FULL_COMMAND}") + separate_arguments(LATEX_WORKING_DIRECTORY_SEP UNIX_COMMAND "${LATEX_WORKING_DIRECTORY}") + + execute_process( + COMMAND ${LATEX_FULL_COMMAND} + WORKING_DIRECTORY "${LATEX_WORKING_DIRECTORY_SEP}" + RESULT_VARIABLE execute_result + ) + + if(NOT ${execute_result} EQUAL 0) + # LaTeX tends to write a file when a failure happens. Delete that file so + # that LaTeX will run again. + file(REMOVE "${LATEX_WORKING_DIRECTORY}/${LATEX_OUTPUT_FILE}") + + message("\n\nLaTeX command failed") + message("${full_command_original}") + message("Log output:") + file(READ "${LATEX_WORKING_DIRECTORY}/${LATEX_TARGET}.log" log_output) + message("${log_output}") + message(FATAL_ERROR + "Successfully executed LaTeX, but LaTeX returned an error.") + endif() +endfunction(latex_execute_latex) + +function(latex_makeglossaries) + # This is really a bare bones port of the makeglossaries perl script into + # CMake scripting. + message("**************************** In makeglossaries") + if(NOT LATEX_TARGET) + message(SEND_ERROR "Need to define LATEX_TARGET") + endif() + + set(aux_file ${LATEX_TARGET}.aux) + + if(NOT EXISTS ${aux_file}) + message(SEND_ERROR "${aux_file} does not exist. Run latex on your target file.") + endif() + + latex_file_match(newglossary_lines ${aux_file} + "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" + "@newglossary{main}{glg}{gls}{glo}" + ) + + latex_file_match(istfile_line ${aux_file} + "@istfilename[ \t]*{([^}]*)}" + "@istfilename{${LATEX_TARGET}.ist}" + ) + string(REGEX REPLACE "@istfilename[ \t]*{([^}]*)}" "\\1" + istfile ${istfile_line} + ) + + string(REGEX MATCH ".*\\.xdy" use_xindy "${istfile}") + if(use_xindy) + message("*************** Using xindy") + if(NOT XINDY_COMPILER) + message(SEND_ERROR "Need to define XINDY_COMPILER") + endif() + else() + message("*************** Using makeindex") + if(NOT MAKEINDEX_COMPILER) + message(SEND_ERROR "Need to define MAKEINDEX_COMPILER") + endif() + endif() + + foreach(newglossary ${newglossary_lines}) + string(REGEX REPLACE + "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" + "\\1" glossary_name ${newglossary} + ) + string(REGEX REPLACE + "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" + "${LATEX_TARGET}.\\2" glossary_log ${newglossary} + ) + string(REGEX REPLACE + "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" + "${LATEX_TARGET}.\\3" glossary_out ${newglossary} + ) + string(REGEX REPLACE + "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" + "${LATEX_TARGET}.\\4" glossary_in ${newglossary} + ) + + if(use_xindy) + latex_file_match(xdylanguage_line ${aux_file} + "@xdylanguage[ \t]*{${glossary_name}}{([^}]*)}" + "@xdylanguage{${glossary_name}}{english}" + ) + string(REGEX REPLACE + "@xdylanguage[ \t]*{${glossary_name}}{([^}]*)}" + "\\1" + language + ${xdylanguage_line} + ) + # What crazy person makes a LaTeX index generator that uses different + # identifiers for language than babel (or at least does not support + # the old ones)? + if(${language} STREQUAL "frenchb") + set(language "french") + elseif(${language} MATCHES "^n?germanb?$") + set(language "german") + elseif(${language} STREQUAL "magyar") + set(language "hungarian") + elseif(${language} STREQUAL "lsorbian") + set(language "lower-sorbian") + elseif(${language} STREQUAL "norsk") + set(language "norwegian") + elseif(${language} STREQUAL "portuges") + set(language "portuguese") + elseif(${language} STREQUAL "russianb") + set(language "russian") + elseif(${language} STREQUAL "slovene") + set(language "slovenian") + elseif(${language} STREQUAL "ukraineb") + set(language "ukrainian") + elseif(${language} STREQUAL "usorbian") + set(language "upper-sorbian") + endif() + if(language) + set(language_flags "-L ${language}") + else() + set(language_flags "") + endif() + + latex_file_match(codepage_line ${aux_file} + "@gls@codepage[ \t]*{${glossary_name}}{([^}]*)}" + "@gls@codepage{${glossary_name}}{utf}" + ) + string(REGEX REPLACE + "@gls@codepage[ \t]*{${glossary_name}}{([^}]*)}" + "\\1" + codepage + ${codepage_line} + ) + if(codepage) + set(codepage_flags "-C ${codepage}") + else() + # Ideally, we would check that the language is compatible with the + # default codepage, but I'm hoping that distributions will be smart + # enough to specify their own codepage. I know, it's asking a lot. + set(codepage_flags "") + endif() + + message("${XINDY_COMPILER} ${MAKEGLOSSARIES_COMPILER_ARGS} ${language_flags} ${codepage_flags} -I xindy -M ${glossary_name} -t ${glossary_log} -o ${glossary_out} ${glossary_in}" + ) + exec_program(${XINDY_COMPILER} + ARGS ${MAKEGLOSSARIES_COMPILER_ARGS} + ${language_flags} + ${codepage_flags} + -I xindy + -M ${glossary_name} + -t ${glossary_log} + -o ${glossary_out} + ${glossary_in} + OUTPUT_VARIABLE xindy_output + ) + message("${xindy_output}") + + # So, it is possible (perhaps common?) for aux files to specify a + # language and codepage that are incompatible with each other. Check + # for that condition, and if it happens run again with the default + # codepage. + if("${xindy_output}" MATCHES "^Cannot locate xindy module for language (.+) in codepage (.+)\\.$") + message("*************** Retrying xindy with default codepage.") + exec_program(${XINDY_COMPILER} + ARGS ${MAKEGLOSSARIES_COMPILER_ARGS} + ${language_flags} + -I xindy + -M ${glossary_name} + -t ${glossary_log} + -o ${glossary_out} + ${glossary_in} + ) + endif() + + else() + message("${MAKEINDEX_COMPILER} ${MAKEGLOSSARIES_COMPILER_ARGS} -s ${istfile} -t ${glossary_log} -o ${glossary_out} ${glossary_in}") + exec_program(${MAKEINDEX_COMPILER} ARGS ${MAKEGLOSSARIES_COMPILER_ARGS} + -s ${istfile} -t ${glossary_log} -o ${glossary_out} ${glossary_in} + ) + endif() + + endforeach(newglossary) +endfunction(latex_makeglossaries) + +function(latex_makenomenclature) + message("**************************** In makenomenclature") + if(NOT LATEX_TARGET) + message(SEND_ERROR "Need to define LATEX_TARGET") + endif() + + if(NOT MAKEINDEX_COMPILER) + message(SEND_ERROR "Need to define MAKEINDEX_COMPILER") + endif() + + set(nomencl_out ${LATEX_TARGET}.nls) + set(nomencl_in ${LATEX_TARGET}.nlo) + + exec_program(${MAKEINDEX_COMPILER} ARGS ${MAKENOMENCLATURE_COMPILER_ARGS} + ${nomencl_in} -s "nomencl.ist" -o ${nomencl_out} + ) +endfunction(latex_makenomenclature) + +function(latex_correct_synctex) + message("**************************** In correct SyncTeX") + if(NOT LATEX_TARGET) + message(SEND_ERROR "Need to define LATEX_TARGET") + endif() + + if(NOT GZIP) + message(SEND_ERROR "Need to define GZIP") + endif() + + if(NOT LATEX_SOURCE_DIRECTORY) + message(SEND_ERROR "Need to define LATEX_SOURCE_DIRECTORY") + endif() + + if(NOT LATEX_BINARY_DIRECTORY) + message(SEND_ERROR "Need to define LATEX_BINARY_DIRECTORY") + endif() + message("${LATEX_BINARY_DIRECTORY}") + message("${LATEX_SOURCE_DIRECTORY}") + + set(synctex_file ${LATEX_BINARY_DIRECTORY}/${LATEX_TARGET}.synctex) + set(synctex_file_gz ${synctex_file}.gz) + + if(EXISTS ${synctex_file_gz}) + + message("Making backup of synctex file.") + configure_file(${synctex_file_gz} ${synctex_file}.bak.gz COPYONLY) + + message("Uncompressing synctex file.") + exec_program(${GZIP} + ARGS --decompress ${synctex_file_gz} + ) + + message("Reading synctex file.") + file(READ ${synctex_file} synctex_data) + + message("Replacing output paths with input paths.") + foreach(extension tex cls bst clo sty ist fd) + # Relative paths + string(REGEX REPLACE + "(Input:[0-9]+:)([^/\n][^\n]\\.${extension}*)" + "\\1${LATEX_SOURCE_DIRECTORY}/\\2" + synctex_data + "${synctex_data}" + ) + + # Absolute paths + string(REGEX REPLACE + "(Input:[0-9]+:)${LATEX_BINARY_DIRECTORY}([^\n]*\\.${extension})" + "\\1${LATEX_SOURCE_DIRECTORY}\\2" + synctex_data + "${synctex_data}" + ) + endforeach(extension) + + message("Writing synctex file.") + file(WRITE ${synctex_file} "${synctex_data}") + + message("Compressing synctex file.") + exec_program(${GZIP} + ARGS ${synctex_file} + ) + + else() + + message(SEND_ERROR "File ${synctex_file_gz} not found. Perhaps synctex is not supported by your LaTeX compiler.") + + endif() + +endfunction(latex_correct_synctex) + +function(latex_check_important_warnings) + set(log_file ${LATEX_TARGET}.log) + + message("\nChecking ${log_file} for important warnings.") + if(NOT LATEX_TARGET) + message(SEND_ERROR "Need to define LATEX_TARGET") + endif() + + if(NOT EXISTS ${log_file}) + message("Could not find log file: ${log_file}") + return() + endif() + + set(found_error) + + file(READ ${log_file} log) + + # Check for undefined references + string(REGEX MATCHALL + "\n[^\n]*Reference[^\n]*undefined[^\n]*" + reference_warnings + "${log}") + if(reference_warnings) + set(found_error TRUE) + message("\nFound missing reference warnings.") + foreach(warning ${reference_warnings}) + string(STRIP "${warning}" warning_no_newline) + message("${warning_no_newline}") + endforeach(warning) + endif() + + # Check for natbib warnings + string(REGEX MATCHALL + "\nPackage natbib Warning:[^\n]*" + natbib_warnings + "${log}") + if(natbib_warnings) + set(found_error TRUE) + message("\nFound natbib package warnings.") + foreach(warning ${natbib_warnings}) + string(STRIP "${warning}" warning_no_newline) + message("${warning_no_newline}") + endforeach(warning) + endif() + + # Check for overfull + string(REGEX MATCHALL + "\nOverfull[^\n]*" + overfull_warnings + "${log}") + if(overfull_warnings) + set(found_error TRUE) + message("\nFound overfull warnings. These are indicative of layout errors.") + foreach(warning ${overfull_warnings}) + string(STRIP "${warning}" warning_no_newline) + message("${warning_no_newline}") + endforeach(warning) + endif() + + # Check for invalid characters + string(REGEX MATCHALL + "\nMissing character:[^\n]*" + invalid_character_warnings + "${log}") + if(invalid_character_warnings) + set(found_error TRUE) + message("\nFound invalid character warnings. These characters are likely not printed correctly.") + foreach(warning ${invalid_character_warnings}) + string(STRIP "${warning}" warning_no_newline) + message("${warning_no_newline}") + endforeach(warning) + endif() + + if(found_error) + latex_get_filename_component(log_file_path ${log_file} ABSOLUTE) + message("\nConsult ${log_file_path} for more information on LaTeX build.") + else() + message("No known important warnings found.") + endif(found_error) +endfunction(latex_check_important_warnings) + +############################################################################# +# Helper functions for establishing LaTeX build. +############################################################################# + +function(latex_needit VAR NAME) + if(NOT ${VAR}) + message(SEND_ERROR "I need the ${NAME} command.") + endif() +endfunction(latex_needit) + +function(latex_wantit VAR NAME) + if(NOT ${VAR}) + message(STATUS "I could not find the ${NAME} command.") + endif() +endfunction(latex_wantit) + +function(latex_setup_variables) + set(LATEX_OUTPUT_PATH "${LATEX_OUTPUT_PATH}" + CACHE PATH "If non empty, specifies the location to place LaTeX output." + ) + + find_package(LATEX) + + find_program(XINDY_COMPILER + NAME xindy + PATHS ${MIKTEX_BINARY_PATH} /usr/bin + ) + + find_package(UnixCommands) + + find_program(PDFTOPS_CONVERTER + NAMES pdftops + DOC "The pdf to ps converter program from the Poppler package." + ) + + find_program(HTLATEX_COMPILER + NAMES htlatex + PATHS ${MIKTEX_BINARY_PATH} + /usr/bin + ) + + mark_as_advanced( + LATEX_COMPILER + PDFLATEX_COMPILER + BIBTEX_COMPILER + BIBER_COMPILER + MAKEINDEX_COMPILER + XINDY_COMPILER + DVIPS_CONVERTER + PS2PDF_CONVERTER + PDFTOPS_CONVERTER + LATEX2HTML_CONVERTER + HTLATEX_COMPILER + ) + + latex_needit(LATEX_COMPILER latex) + latex_wantit(PDFLATEX_COMPILER pdflatex) + latex_wantit(HTLATEX_COMPILER htlatex) + latex_needit(BIBTEX_COMPILER bibtex) + latex_wantit(BIBER_COMPILER biber) + latex_needit(MAKEINDEX_COMPILER makeindex) + latex_wantit(DVIPS_CONVERTER dvips) + latex_wantit(PS2PDF_CONVERTER ps2pdf) + latex_wantit(PDFTOPS_CONVERTER pdftops) + + set(LATEX_COMPILER_FLAGS "-interaction=batchmode -file-line-error" + CACHE STRING "Flags passed to latex.") + set(PDFLATEX_COMPILER_FLAGS ${LATEX_COMPILER_FLAGS} + CACHE STRING "Flags passed to pdflatex.") + set(HTLATEX_COMPILER_TEX4HT_FLAGS "html" + CACHE STRING "Options for the tex4ht.sty and *.4ht style files.") + set(HTLATEX_COMPILER_TEX4HT_POSTPROCESSOR_FLAGS "" + CACHE STRING "Options for the text4ht postprocessor.") + set(HTLATEX_COMPILER_T4HT_POSTPROCESSOR_FLAGS "" + CACHE STRING "Options for the t4ht postprocessor.") + set(HTLATEX_COMPILER_LATEX_FLAGS ${LATEX_COMPILER_FLAGS} + CACHE STRING "Flags passed from htlatex to the LaTeX compiler.") + set(LATEX_SYNCTEX_FLAGS "-synctex=1" + CACHE STRING "latex/pdflatex flags used to create synctex file.") + set(BIBTEX_COMPILER_FLAGS "" + CACHE STRING "Flags passed to bibtex.") + set(BIBER_COMPILER_FLAGS "" + CACHE STRING "Flags passed to biber.") + set(MAKEINDEX_COMPILER_FLAGS "" + CACHE STRING "Flags passed to makeindex.") + set(MAKEGLOSSARIES_COMPILER_FLAGS "" + CACHE STRING "Flags passed to makeglossaries.") + set(MAKENOMENCLATURE_COMPILER_FLAGS "" + CACHE STRING "Flags passed to makenomenclature.") + set(DVIPS_CONVERTER_FLAGS "-Ppdf -G0 -t letter" + CACHE STRING "Flags passed to dvips.") + if(NOT WIN32) + set(PS2PDF_CONVERTER_FLAGS "-dMaxSubsetPct=100 -dCompatibilityLevel=1.3 -dSubsetFonts=true -dEmbedAllFonts=true -dAutoFilterColorImages=false -dAutoFilterGrayImages=false -dColorImageFilter=/FlateEncode -dGrayImageFilter=/FlateEncode -dMonoImageFilter=/FlateEncode" + CACHE STRING "Flags passed to ps2pdf.") + else() + # Most windows ports of ghostscript utilities use .bat files for ps2pdf + # commands. bat scripts interpret "=" as a special character and separate + # those arguments. To get around this, the ghostscript utilities also + # support using "#" in place of "=". + set(PS2PDF_CONVERTER_FLAGS "-dMaxSubsetPct#100 -dCompatibilityLevel#1.3 -dSubsetFonts#true -dEmbedAllFonts#true -dAutoFilterColorImages#false -dAutoFilterGrayImages#false -dColorImageFilter#/FlateEncode -dGrayImageFilter#/FlateEncode -dMonoImageFilter#/FlateEncode" + CACHE STRING "Flags passed to ps2pdf.") + endif() + set(PDFTOPS_CONVERTER_FLAGS "" + CACHE STRING "Flags passed to pdftops.") + mark_as_advanced( + LATEX_COMPILER_FLAGS + PDFLATEX_COMPILER_FLAGS + HTLATEX_COMPILER_TEX4HT_FLAGS + HTLATEX_COMPILER_TEX4HT_POSTPROCESSOR_FLAGS + HTLATEX_COMPILER_T4HT_POSTPROCESSOR_FLAGS + HTLATEX_COMPILER_LATEX_FLAGS + LATEX_SYNCTEX_FLAGS + BIBTEX_COMPILER_FLAGS + BIBER_COMPILER_FLAGS + MAKEINDEX_COMPILER_FLAGS + MAKEGLOSSARIES_COMPILER_FLAGS + MAKENOMENCLATURE_COMPILER_FLAGS + DVIPS_CONVERTER_FLAGS + PS2PDF_CONVERTER_FLAGS + PDFTOPS_CONVERTER_FLAGS + ) + + # Because it is easier to type, the flags variables are entered as + # space-separated strings much like you would in a shell. However, when + # using a CMake command to execute a program, it works better to hold the + # arguments in semicolon-separated lists (otherwise the whole string will + # be interpreted as a single argument). Use the separate_arguments to + # convert the space-separated strings to semicolon-separated lists. + separate_arguments(LATEX_COMPILER_FLAGS) + separate_arguments(PDFLATEX_COMPILER_FLAGS) + separate_arguments(HTLATEX_COMPILER_LATEX_FLAGS) + separate_arguments(LATEX_SYNCTEX_FLAGS) + separate_arguments(BIBTEX_COMPILER_FLAGS) + separate_arguments(BIBER_COMPILER_FLAGS) + separate_arguments(MAKEINDEX_COMPILER_FLAGS) + separate_arguments(MAKEGLOSSARIES_COMPILER_FLAGS) + separate_arguments(MAKENOMENCLATURE_COMPILER_FLAGS) + separate_arguments(DVIPS_CONVERTER_FLAGS) + separate_arguments(PS2PDF_CONVERTER_FLAGS) + separate_arguments(PDFTOPS_CONVERTER_FLAGS) + + # Not quite done. When you call separate_arguments on a cache variable, + # the result is written to a local variable. That local variable goes + # away when this function returns (which is before any of them are used). + # So, copy these variables with local scope to cache variables with + # global scope. + set(LATEX_COMPILER_ARGS "${LATEX_COMPILER_FLAGS}" CACHE INTERNAL "") + set(PDFLATEX_COMPILER_ARGS "${PDFLATEX_COMPILER_FLAGS}" CACHE INTERNAL "") + set(HTLATEX_COMPILER_ARGS "${HTLATEX_COMPILER_LATEX_FLAGS}" CACHE INTERNAL "") + set(LATEX_SYNCTEX_ARGS "${LATEX_SYNCTEX_FLAGS}" CACHE INTERNAL "") + set(BIBTEX_COMPILER_ARGS "${BIBTEX_COMPILER_FLAGS}" CACHE INTERNAL "") + set(BIBER_COMPILER_ARGS "${BIBER_COMPILER_FLAGS}" CACHE INTERNAL "") + set(MAKEINDEX_COMPILER_ARGS "${MAKEINDEX_COMPILER_FLAGS}" CACHE INTERNAL "") + set(MAKEGLOSSARIES_COMPILER_ARGS "${MAKEGLOSSARIES_COMPILER_FLAGS}" CACHE INTERNAL "") + set(MAKENOMENCLATURE_COMPILER_ARGS "${MAKENOMENCLATURE_COMPILER_FLAGS}" CACHE INTERNAL "") + set(DVIPS_CONVERTER_ARGS "${DVIPS_CONVERTER_FLAGS}" CACHE INTERNAL "") + set(PS2PDF_CONVERTER_ARGS "${PS2PDF_CONVERTER_FLAGS}" CACHE INTERNAL "") + set(PDFTOPS_CONVERTER_ARGS "${PDFTOPS_CONVERTER_FLAGS}" CACHE INTERNAL "") + + find_program(IMAGEMAGICK_CONVERT + NAMES magick convert + DOC "The convert program that comes with ImageMagick (available at http://www.imagemagick.org)." + ) + mark_as_advanced(IMAGEMAGICK_CONVERT) + + if(DEFINED ENV{LATEX_DEFAULT_BUILD}) + set(default_build $ENV{LATEX_DEFAULT_BUILD}) + else() + set(default_build pdf) + endif() + + set(LATEX_DEFAULT_BUILD "${default_build}" CACHE STRING + "Choose the default type of LaTeX build. Valid options are pdf, dvi, ps, safepdf, html" + ) + set_property(CACHE LATEX_DEFAULT_BUILD + PROPERTY STRINGS pdf dvi ps safepdf html + ) + + option(LATEX_USE_SYNCTEX + "If on, have LaTeX generate a synctex file, which WYSIWYG editors can use to correlate output files like dvi and pdf with the lines of LaTeX source that generates them. In addition to adding the LATEX_SYNCTEX_FLAGS to the command line, this option also adds build commands that \"corrects\" the resulting synctex file to point to the original LaTeX files rather than those generated by UseLATEX.cmake." + OFF + ) + + option(LATEX_SMALL_IMAGES + "If on, the raster images will be converted to 1/6 the original size. This is because papers usually require 600 dpi images whereas most monitors only require at most 96 dpi. Thus, smaller images make smaller files for web distribution and can make it faster to read dvi files." + OFF) + if(LATEX_SMALL_IMAGES) + set(LATEX_RASTER_SCALE 16 PARENT_SCOPE) + set(LATEX_OPPOSITE_RASTER_SCALE 100 PARENT_SCOPE) + else() + set(LATEX_RASTER_SCALE 100 PARENT_SCOPE) + set(LATEX_OPPOSITE_RASTER_SCALE 16 PARENT_SCOPE) + endif() + + # Just holds extensions for known image types. They should all be lower case. + # For historical reasons, these are all declared in the global scope. + set(LATEX_DVI_VECTOR_IMAGE_EXTENSIONS .eps CACHE INTERNAL "") + set(LATEX_DVI_RASTER_IMAGE_EXTENSIONS CACHE INTERNAL "") + set(LATEX_DVI_IMAGE_EXTENSIONS + ${LATEX_DVI_VECTOR_IMAGE_EXTENSIONS} + ${LATEX_DVI_RASTER_IMAGE_EXTENSIONS} + CACHE INTERNAL "" + ) + + set(LATEX_PDF_VECTOR_IMAGE_EXTENSIONS .pdf CACHE INTERNAL "") + set(LATEX_PDF_RASTER_IMAGE_EXTENSIONS .jpeg .jpg .png CACHE INTERNAL "") + set(LATEX_PDF_IMAGE_EXTENSIONS + ${LATEX_PDF_VECTOR_IMAGE_EXTENSIONS} + ${LATEX_PDF_RASTER_IMAGE_EXTENSIONS} + CACHE INTERNAL "" + ) + + set(LATEX_OTHER_VECTOR_IMAGE_EXTENSIONS .ai .dot .svg CACHE INTERNAL "") + set(LATEX_OTHER_RASTER_IMAGE_EXTENSIONS + .bmp .bmp2 .bmp3 .dcm .dcx .ico .gif .pict .ppm .tif .tiff + CACHE INTERNAL "") + set(LATEX_OTHER_IMAGE_EXTENSIONS + ${LATEX_OTHER_VECTOR_IMAGE_EXTENSIONS} + ${LATEX_OTHER_RASTER_IMAGE_EXTENSIONS} + CACHE INTERNAL "" + ) + + set(LATEX_VECTOR_IMAGE_EXTENSIONS + ${LATEX_DVI_VECTOR_IMAGE_EXTENSIONS} + ${LATEX_PDF_VECTOR_IMAGE_EXTENSIONS} + ${LATEX_OTHER_VECTOR_IMAGE_EXTENSIONS} + CACHE INTERNAL "" + ) + set(LATEX_RASTER_IMAGE_EXTENSIONS + ${LATEX_DVI_RASTER_IMAGE_EXTENSIONS} + ${LATEX_PDF_RASTER_IMAGE_EXTENSIONS} + ${LATEX_OTHER_RASTER_IMAGE_EXTENSIONS} + CACHE INTERNAL "" + ) + set(LATEX_IMAGE_EXTENSIONS + ${LATEX_DVI_IMAGE_EXTENSIONS} + ${LATEX_PDF_IMAGE_EXTENSIONS} + ${LATEX_OTHER_IMAGE_EXTENSIONS} + CACHE INTERNAL "" + ) +endfunction(latex_setup_variables) + +function(latex_setup_targets) + if(NOT TARGET pdf) + add_custom_target(pdf) + endif() + if(NOT TARGET dvi) + add_custom_target(dvi) + endif() + if(NOT TARGET ps) + add_custom_target(ps) + endif() + if(NOT TARGET safepdf) + add_custom_target(safepdf) + endif() + if(NOT TARGET html) + add_custom_target(html) + endif() + if(NOT TARGET auxclean) + add_custom_target(auxclean) + endif() +endfunction(latex_setup_targets) + +function(latex_get_output_path var) + set(latex_output_path) + if(LATEX_OUTPUT_PATH) + get_filename_component( + LATEX_OUTPUT_PATH_FULL "${LATEX_OUTPUT_PATH}" ABSOLUTE + ) + if("${LATEX_OUTPUT_PATH_FULL}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") + message(SEND_ERROR "You cannot set LATEX_OUTPUT_PATH to the same directory that contains LaTeX input files.") + else() + set(latex_output_path "${LATEX_OUTPUT_PATH_FULL}") + endif() + else() + if("${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") + message(SEND_ERROR "LaTeX files must be built out of source or you must set LATEX_OUTPUT_PATH.") + else() + set(latex_output_path "${CMAKE_CURRENT_BINARY_DIR}") + endif() + endif() + set(${var} ${latex_output_path} PARENT_SCOPE) +endfunction(latex_get_output_path) + +function(latex_add_convert_command + output_path + input_path + output_extension + input_extension + flags + ) + set(require_imagemagick_convert TRUE) + set(convert_flags "") + if(${input_extension} STREQUAL ".eps" AND ${output_extension} STREQUAL ".pdf") + # ImageMagick has broken eps to pdf conversion + # use ps2pdf instead + if(PS2PDF_CONVERTER) + set(require_imagemagick_convert FALSE) + set(converter ${PS2PDF_CONVERTER}) + set(convert_flags -dEPSCrop ${PS2PDF_CONVERTER_ARGS}) + else() + message(SEND_ERROR "Using postscript files with pdflatex requires ps2pdf for conversion.") + endif() + elseif(${input_extension} STREQUAL ".pdf" AND ${output_extension} STREQUAL ".eps") + # ImageMagick can also be sketchy on pdf to eps conversion. Not good with + # color spaces and tends to unnecessarily rasterize. + # use pdftops instead + if(PDFTOPS_CONVERTER) + set(require_imagemagick_convert FALSE) + set(converter ${PDFTOPS_CONVERTER}) + set(convert_flags -eps ${PDFTOPS_CONVERTER_ARGS}) + else() + message(STATUS "Consider getting pdftops from Poppler to convert PDF images to EPS images.") + set(convert_flags ${flags}) + endif() + else() + set(convert_flags ${flags}) + endif() + + if(require_imagemagick_convert) + if(IMAGEMAGICK_CONVERT) + string(TOLOWER ${IMAGEMAGICK_CONVERT} IMAGEMAGICK_CONVERT_LOWERCASE) + if(${IMAGEMAGICK_CONVERT_LOWERCASE} MATCHES "system32[/\\\\]convert\\.exe") + message(SEND_ERROR "IMAGEMAGICK_CONVERT set to Window's convert.exe for changing file systems rather than ImageMagick's convert for changing image formats. Please make sure ImageMagick is installed (available at http://www.imagemagick.org). If you have a recent version of ImageMagick (7.0 or higher), use the magick program instead of convert for IMAGEMAGICK_CONVERT.") + else() + set(converter ${IMAGEMAGICK_CONVERT}) + # ImageMagick requires a special order of arguments where resize and + # arguments of that nature must be placed after the input image path. + add_custom_command(OUTPUT ${output_path} + COMMAND ${converter} + ARGS ${input_path} ${convert_flags} ${output_path} + DEPENDS ${input_path} + ) + endif() + else() + message(SEND_ERROR "Could not find convert program. Please download ImageMagick from http://www.imagemagick.org and install.") + endif() + else() # Not ImageMagick convert + add_custom_command(OUTPUT ${output_path} + COMMAND ${converter} + ARGS ${convert_flags} ${input_path} ${output_path} + DEPENDS ${input_path} + ) + endif() +endfunction(latex_add_convert_command) + +# Makes custom commands to convert a file to a particular type. +function(latex_convert_image + output_files_var + input_file + output_extension + convert_flags + output_extensions + other_files + ) + set(output_file_list) + set(input_dir ${CMAKE_CURRENT_SOURCE_DIR}) + latex_get_output_path(output_dir) + + latex_get_filename_component(extension "${input_file}" EXT) + + # Check input filename for potential problems with LaTeX. + latex_get_filename_component(name "${input_file}" NAME_WE) + set(suggested_name "${name}") + if(suggested_name MATCHES ".*\\..*") + string(REPLACE "." "-" suggested_name "${suggested_name}") + endif() + if(suggested_name MATCHES ".* .*") + string(REPLACE " " "-" suggested_name "${suggested_name}") + endif() + if(NOT suggested_name STREQUAL name) + message(WARNING "Some LaTeX distributions have problems with image file names with multiple extensions or spaces. Consider changing ${name}${extension} to something like ${suggested_name}${extension}.") + endif() + + string(REGEX REPLACE "\\.[^.]*\$" ${output_extension} output_file + "${input_file}") + + latex_list_contains(is_type ${extension} ${output_extensions}) + if(is_type) + if(convert_flags) + latex_add_convert_command(${output_dir}/${output_file} + ${input_dir}/${input_file} ${output_extension} ${extension} + "${convert_flags}") + set(output_file_list ${output_dir}/${output_file}) + else() + # As a shortcut, we can just copy the file. + add_custom_command(OUTPUT ${output_dir}/${input_file} + COMMAND ${CMAKE_COMMAND} + ARGS -E copy ${input_dir}/${input_file} ${output_dir}/${input_file} + DEPENDS ${input_dir}/${input_file} + ) + set(output_file_list ${output_dir}/${input_file}) + endif() + else() + set(do_convert TRUE) + # Check to see if there is another input file of the appropriate type. + foreach(valid_extension ${output_extensions}) + string(REGEX REPLACE "\\.[^.]*\$" ${output_extension} try_file + "${input_file}") + latex_list_contains(has_native_file "${try_file}" ${other_files}) + if(has_native_file) + set(do_convert FALSE) + endif() + endforeach(valid_extension) + + # If we still need to convert, do it. + if(do_convert) + latex_add_convert_command(${output_dir}/${output_file} + ${input_dir}/${input_file} ${output_extension} ${extension} + "${convert_flags}") + set(output_file_list ${output_dir}/${output_file}) + endif() + endif() + + set(${output_files_var} ${output_file_list} PARENT_SCOPE) +endfunction(latex_convert_image) + +# Adds custom commands to process the given files for dvi and pdf builds. +# Adds the output files to the given variables (does not replace). +function(latex_process_images dvi_outputs_var pdf_outputs_var) + latex_get_output_path(output_dir) + set(dvi_outputs) + set(pdf_outputs) + foreach(file ${ARGN}) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${file}") + latex_get_filename_component(extension "${file}" EXT) + set(convert_flags) + + # Check to see if we need to downsample the image. + latex_list_contains(is_raster "${extension}" + ${LATEX_RASTER_IMAGE_EXTENSIONS}) + if(LATEX_SMALL_IMAGES) + if(is_raster) + set(convert_flags -resize ${LATEX_RASTER_SCALE}%) + endif() + endif() + + # Make sure the output directory exists. + latex_get_filename_component(path "${output_dir}/${file}" PATH) + make_directory("${path}") + + # Do conversions for dvi. + latex_convert_image(output_files "${file}" .eps "${convert_flags}" + "${LATEX_DVI_IMAGE_EXTENSIONS}" "${ARGN}") + list(APPEND dvi_outputs ${output_files}) + + # Do conversions for pdf. + if(is_raster) + latex_convert_image(output_files "${file}" .png "${convert_flags}" + "${LATEX_PDF_IMAGE_EXTENSIONS}" "${ARGN}") + list(APPEND pdf_outputs ${output_files}) + else() + latex_convert_image(output_files "${file}" .pdf "${convert_flags}" + "${LATEX_PDF_IMAGE_EXTENSIONS}" "${ARGN}") + list(APPEND pdf_outputs ${output_files}) + endif() + else() + message(WARNING "Could not find file ${CMAKE_CURRENT_SOURCE_DIR}/${file}. Are you sure you gave relative paths to IMAGES?") + endif() + endforeach(file) + + set(${dvi_outputs_var} ${dvi_outputs} PARENT_SCOPE) + set(${pdf_outputs_var} ${pdf_outputs} PARENT_SCOPE) +endfunction(latex_process_images) + +function(latex_copy_globbed_files pattern dest) + file(GLOB file_list ${pattern}) + foreach(in_file ${file_list}) + latex_get_filename_component(out_file ${in_file} NAME) + configure_file(${in_file} ${dest}/${out_file} COPYONLY) + endforeach(in_file) +endfunction(latex_copy_globbed_files) + +function(latex_copy_input_file file) + latex_get_output_path(output_dir) + + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}) + latex_get_filename_component(path ${file} PATH) + file(MAKE_DIRECTORY ${output_dir}/${path}) + + latex_list_contains(use_config ${file} ${LATEX_CONFIGURE}) + if(use_config) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${file} + ${output_dir}/${file} + @ONLY + ) + add_custom_command(OUTPUT ${output_dir}/${file} + COMMAND ${CMAKE_COMMAND} + ARGS ${CMAKE_BINARY_DIR} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${file} + ) + else() + add_custom_command(OUTPUT ${output_dir}/${file} + COMMAND ${CMAKE_COMMAND} + ARGS -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${file} ${output_dir}/${file} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${file} + ) + endif() + else() + if(EXISTS ${output_dir}/${file}) + # Special case: output exists but input does not. Assume that it was + # created elsewhere and skip the input file copy. + else() + message("Could not find input file ${CMAKE_CURRENT_SOURCE_DIR}/${file}") + endif() + endif() +endfunction(latex_copy_input_file) + +############################################################################# +# Commands provided by the UseLATEX.cmake "package" +############################################################################# + +function(latex_usage command message) + message(SEND_ERROR + "${message}\n Usage: ${command}(\n [BIBFILES ...]\n [INPUTS ...]\n [IMAGE_DIRS ...]\n [IMAGES \n [CONFIGURE ...]\n [DEPENDS ...]\n [MULTIBIB_NEWCITES] \n [USE_BIBLATEX] [USE_INDEX] [USE_GLOSSARY] [USE_NOMENCL]\n [FORCE_PDF] [FORCE_DVI] [FORCE_HTML]\n [TARGET_NAME] \n [EXCLUDE_FROM_ALL]\n [EXCLUDE_FROM_DEFAULTS])" + ) +endfunction(latex_usage command message) + +# Parses arguments to add_latex_document and ADD_LATEX_TARGETS and sets the +# variables LATEX_TARGET, LATEX_IMAGE_DIR, LATEX_BIBFILES, LATEX_DEPENDS, and +# LATEX_INPUTS. +function(parse_add_latex_arguments command latex_main_input) + set(options + USE_BIBLATEX + USE_INDEX + USE_GLOSSARY + USE_NOMENCL + FORCE_PDF + FORCE_DVI + FORCE_HTML + EXCLUDE_FROM_ALL + EXCLUDE_FROM_DEFAULTS + # Deprecated options + USE_GLOSSARIES + DEFAULT_PDF + DEFAULT_SAFEPDF + DEFAULT_PS + NO_DEFAULT + MANGLE_TARGET_NAMES + ) + set(oneValueArgs + TARGET_NAME + ) + set(multiValueArgs + BIBFILES + MULTIBIB_NEWCITES + INPUTS + IMAGE_DIRS + IMAGES + CONFIGURE + DEPENDS + INDEX_NAMES + ) + cmake_parse_arguments( + LATEX "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # Handle invalid and deprecated arguments + if(LATEX_UNPARSED_ARGUMENTS) + latex_usage(${command} "Invalid or deprecated arguments: ${LATEX_UNPARSED_ARGUMENTS}") + endif() + if(LATEX_USE_GLOSSARIES) + latex_usage(${command} "USE_GLOSSARIES option removed in version 1.6.1. Use USE_GLOSSARY instead.") + endif() + if(LATEX_DEFAULT_PDF) + latex_usage(${command} "DEFAULT_PDF option removed in version 2.0. Use FORCE_PDF option or LATEX_DEFAULT_BUILD CMake variable instead.") + endif() + if(LATEX_DEFAULT_SAFEPDF) + latex_usage(${command} "DEFAULT_SAFEPDF option removed in version 2.0. Use LATEX_DEFAULT_BUILD CMake variable instead.") + endif() + if(LATEX_DEFAULT_DVI) + latex_usage(${command} "DEFAULT_DVI option removed in version 2.0. Use FORCE_DVI option or LATEX_DEFAULT_BUILD CMake variable instead.") + endif() + if(LATEX_NO_DEFAULT) + latex_usage(${command} "NO_DEFAULT option removed in version 2.0. Use EXCLUDE_FROM_ALL instead.") + endif() + if(LATEX_MANGLE_TARGET_NAMES) + latex_usage(${command} "MANGLE_TARGET_NAMES option removed in version 2.0. All LaTeX targets use mangled names now.") + endif() + + # Capture the first argument, which is the main LaTeX input. + latex_get_filename_component(latex_target ${latex_main_input} NAME_WE) + set(LATEX_MAIN_INPUT ${latex_main_input} PARENT_SCOPE) + set(LATEX_TARGET ${latex_target} PARENT_SCOPE) + + # Propagate the result variables to the caller + foreach(arg_name ${options} ${oneValueArgs} ${multiValueArgs}) + set(var_name LATEX_${arg_name}) + set(${var_name} ${${var_name}} PARENT_SCOPE) + endforeach(arg_name) +endfunction(parse_add_latex_arguments) + +function(add_latex_targets_internal) + latex_get_output_path(output_dir) + + if(LATEX_USE_SYNCTEX) + set(synctex_flags ${LATEX_SYNCTEX_ARGS}) + else() + set(synctex_flags) + endif() + + # The commands to run LaTeX. They are repeated multiple times. + set(latex_build_command + ${LATEX_COMPILER} ${LATEX_COMPILER_ARGS} ${synctex_flags} ${LATEX_MAIN_INPUT} + ) + if(LATEX_COMPILER_ARGS MATCHES ".*batchmode.*") + # Wrap command in script that dumps the log file on error. This makes sure + # errors can be seen. + set(latex_build_command + ${CMAKE_COMMAND} + -D LATEX_BUILD_COMMAND=execute_latex + -D LATEX_TARGET=${LATEX_TARGET} + -D LATEX_WORKING_DIRECTORY="${output_dir}" + -D LATEX_FULL_COMMAND="${latex_build_command}" + -D LATEX_OUTPUT_FILE="${LATEX_TARGET}.dvi" + -P "${LATEX_USE_LATEX_LOCATION}" + ) + endif() + set(pdflatex_build_command + ${PDFLATEX_COMPILER} ${PDFLATEX_COMPILER_ARGS} ${synctex_flags} ${LATEX_MAIN_INPUT} + ) + if(PDFLATEX_COMPILER_ARGS MATCHES ".*batchmode.*") + # Wrap command in script that dumps the log file on error. This makes sure + # errors can be seen. + set(pdflatex_build_command + ${CMAKE_COMMAND} + -D LATEX_BUILD_COMMAND=execute_latex + -D LATEX_TARGET=${LATEX_TARGET} + -D LATEX_WORKING_DIRECTORY="${output_dir}" + -D LATEX_FULL_COMMAND="${pdflatex_build_command}" + -D LATEX_OUTPUT_FILE="${LATEX_TARGET}.pdf" + -P "${LATEX_USE_LATEX_LOCATION}" + ) + endif() + + if(NOT LATEX_TARGET_NAME) + # Use the main filename (minus the .tex) as the target name. Remove any + # spaces since CMake cannot have spaces in its target names. + string(REPLACE " " "_" LATEX_TARGET_NAME ${LATEX_TARGET}) + endif() + + # Some LaTeX commands may need to be modified (or may not work) if the main + # tex file is in a subdirectory. Make a flag for that. + get_filename_component(LATEX_MAIN_INPUT_SUBDIR ${LATEX_MAIN_INPUT} DIRECTORY) + + # Set up target names. + set(dvi_target ${LATEX_TARGET_NAME}_dvi) + set(pdf_target ${LATEX_TARGET_NAME}_pdf) + set(ps_target ${LATEX_TARGET_NAME}_ps) + set(safepdf_target ${LATEX_TARGET_NAME}_safepdf) + set(html_target ${LATEX_TARGET_NAME}_html) + set(auxclean_target ${LATEX_TARGET_NAME}_auxclean) + + # Probably not all of these will be generated, but they could be. + # Note that the aux file is added later. + set(auxiliary_clean_files + ${output_dir}/${LATEX_TARGET}.aux + ${output_dir}/${LATEX_TARGET}.bbl + ${output_dir}/${LATEX_TARGET}.blg + ${output_dir}/${LATEX_TARGET}-blx.bib + ${output_dir}/${LATEX_TARGET}.glg + ${output_dir}/${LATEX_TARGET}.glo + ${output_dir}/${LATEX_TARGET}.gls + ${output_dir}/${LATEX_TARGET}.idx + ${output_dir}/${LATEX_TARGET}.ilg + ${output_dir}/${LATEX_TARGET}.ind + ${output_dir}/${LATEX_TARGET}.ist + ${output_dir}/${LATEX_TARGET}.log + ${output_dir}/${LATEX_TARGET}.out + ${output_dir}/${LATEX_TARGET}.toc + ${output_dir}/${LATEX_TARGET}.lof + ${output_dir}/${LATEX_TARGET}.xdy + ${output_dir}/${LATEX_TARGET}.synctex.gz + ${output_dir}/${LATEX_TARGET}.synctex.bak.gz + ${output_dir}/${LATEX_TARGET}.dvi + ${output_dir}/${LATEX_TARGET}.ps + ${output_dir}/${LATEX_TARGET}.pdf + ) + + set(image_list ${LATEX_IMAGES}) + + # For each directory in LATEX_IMAGE_DIRS, glob all the image files and + # place them in LATEX_IMAGES. + foreach(dir ${LATEX_IMAGE_DIRS}) + if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${dir}) + message(WARNING "Image directory ${CMAKE_CURRENT_SOURCE_DIR}/${dir} does not exist. Are you sure you gave relative directories to IMAGE_DIRS?") + endif() + foreach(extension ${LATEX_IMAGE_EXTENSIONS}) + file(GLOB files ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*${extension}) + foreach(file ${files}) + latex_get_filename_component(filename ${file} NAME) + list(APPEND image_list ${dir}/${filename}) + endforeach(file) + endforeach(extension) + endforeach(dir) + + latex_process_images(dvi_images pdf_images ${image_list}) + + set(make_dvi_command + ${CMAKE_COMMAND} -E chdir ${output_dir} + ${latex_build_command}) + set(make_pdf_command + ${CMAKE_COMMAND} -E chdir ${output_dir} + ${pdflatex_build_command} + ) + + set(make_dvi_depends ${LATEX_DEPENDS} ${dvi_images}) + set(make_pdf_depends ${LATEX_DEPENDS} ${pdf_images}) + foreach(input ${LATEX_MAIN_INPUT} ${LATEX_INPUTS}) + list(APPEND make_dvi_depends ${output_dir}/${input}) + list(APPEND make_pdf_depends ${output_dir}/${input}) + if(${input} MATCHES "\\.tex$") + # Dependent .tex files might have their own .aux files created. Make + # sure these get cleaned as well. This might replicate the cleaning + # of the main .aux file, which is OK. + string(REGEX REPLACE "\\.tex$" "" input_we ${input}) + list(APPEND auxiliary_clean_files + ${output_dir}/${input_we}.aux + ${output_dir}/${input}.aux + ) + endif() + endforeach(input) + + set(all_latex_sources ${LATEX_MAIN_INPUT} ${LATEX_INPUTS} ${image_list}) + + if(LATEX_USE_GLOSSARY) + foreach(dummy 0 1) # Repeat these commands twice. + set(make_dvi_command ${make_dvi_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${CMAKE_COMMAND} + -D LATEX_BUILD_COMMAND=makeglossaries + -D LATEX_TARGET=${LATEX_TARGET} + -D MAKEINDEX_COMPILER=${MAKEINDEX_COMPILER} + -D XINDY_COMPILER=${XINDY_COMPILER} + -D MAKEGLOSSARIES_COMPILER_ARGS=${MAKEGLOSSARIES_COMPILER_ARGS} + -P ${LATEX_USE_LATEX_LOCATION} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${latex_build_command} + ) + set(make_pdf_command ${make_pdf_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${CMAKE_COMMAND} + -D LATEX_BUILD_COMMAND=makeglossaries + -D LATEX_TARGET=${LATEX_TARGET} + -D MAKEINDEX_COMPILER=${MAKEINDEX_COMPILER} + -D XINDY_COMPILER=${XINDY_COMPILER} + -D MAKEGLOSSARIES_COMPILER_ARGS=${MAKEGLOSSARIES_COMPILER_ARGS} + -P ${LATEX_USE_LATEX_LOCATION} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${pdflatex_build_command} + ) + endforeach(dummy) + endif() + + if(LATEX_USE_NOMENCL) + foreach(dummy 0 1) # Repeat these commands twice. + set(make_dvi_command ${make_dvi_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${CMAKE_COMMAND} + -D LATEX_BUILD_COMMAND=makenomenclature + -D LATEX_TARGET=${LATEX_TARGET} + -D MAKEINDEX_COMPILER=${MAKEINDEX_COMPILER} + -D MAKENOMENCLATURE_COMPILER_ARGS=${MAKENOMENCLATURE_COMPILER_ARGS} + -P ${LATEX_USE_LATEX_LOCATION} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${latex_build_command} + ) + set(make_pdf_command ${make_pdf_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${CMAKE_COMMAND} + -D LATEX_BUILD_COMMAND=makenomenclature + -D LATEX_TARGET=${LATEX_TARGET} + -D MAKEINDEX_COMPILER=${MAKEINDEX_COMPILER} + -D MAKENOMENCLATURE_COMPILER_ARGS=${MAKENOMENCLATURE_COMPILER_ARGS} + -P ${LATEX_USE_LATEX_LOCATION} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${pdflatex_build_command} + ) + endforeach(dummy) + endif() + + if(LATEX_BIBFILES) + if(LATEX_USE_BIBLATEX) + if(NOT BIBER_COMPILER) + message(SEND_ERROR "I need the biber command.") + endif() + set(bib_compiler ${BIBER_COMPILER}) + set(bib_compiler_flags ${BIBER_COMPILER_ARGS}) + + if (LATEX_USE_BIBLATEX_CONFIG) + list(APPEND auxiliary_clean_files ${output_dir}/biblatex.cfg) + list(APPEND make_dvi_depends ${output_dir}/biblatex.cfg) + list(APPEND make_pdf_depends ${output_dir}/biblatex.cfg) + endif() + else() + set(bib_compiler ${BIBTEX_COMPILER}) + set(bib_compiler_flags ${BIBTEX_COMPILER_ARGS}) + endif() + if(LATEX_MULTIBIB_NEWCITES) + foreach (multibib_auxfile ${LATEX_MULTIBIB_NEWCITES}) + latex_get_filename_component(multibib_target ${multibib_auxfile} NAME_WE) + set(make_dvi_command ${make_dvi_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${bib_compiler} ${bib_compiler_flags} ${multibib_target}) + set(make_pdf_command ${make_pdf_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${bib_compiler} ${bib_compiler_flags} ${multibib_target}) + set(auxiliary_clean_files ${auxiliary_clean_files} + ${output_dir}/${multibib_target}.aux) + endforeach (multibib_auxfile ${LATEX_MULTIBIB_NEWCITES}) + else() + set(make_dvi_command ${make_dvi_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${bib_compiler} ${bib_compiler_flags} ${LATEX_TARGET}) + set(make_pdf_command ${make_pdf_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${bib_compiler} ${bib_compiler_flags} ${LATEX_TARGET}) + endif() + + foreach (bibfile ${LATEX_BIBFILES}) + list(APPEND make_dvi_depends ${output_dir}/${bibfile}) + list(APPEND make_pdf_depends ${output_dir}/${bibfile}) + endforeach (bibfile ${LATEX_BIBFILES}) + else() + if(LATEX_MULTIBIB_NEWCITES) + message(WARNING "MULTIBIB_NEWCITES has no effect without BIBFILES option.") + endif() + endif() + + if(LATEX_USE_INDEX) + if(LATEX_INDEX_NAMES) + set(INDEX_NAMES ${LATEX_INDEX_NAMES}) + else() + set(INDEX_NAMES ${LATEX_TARGET}) + endif() + foreach(idx_name ${INDEX_NAMES}) + set(make_dvi_command ${make_dvi_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${latex_build_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${MAKEINDEX_COMPILER} ${MAKEINDEX_COMPILER_ARGS} ${idx_name}.idx) + set(make_pdf_command ${make_pdf_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${pdflatex_build_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${MAKEINDEX_COMPILER} ${MAKEINDEX_COMPILER_ARGS} ${idx_name}.idx) + set(auxiliary_clean_files ${auxiliary_clean_files} + ${output_dir}/${idx_name}.idx + ${output_dir}/${idx_name}.ilg + ${output_dir}/${idx_name}.ind) + endforeach() + else() + if(LATEX_INDEX_NAMES) + message(WARNING "INDEX_NAMES has no effect without USE_INDEX option.") + endif() + endif() + + set(make_dvi_command ${make_dvi_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${latex_build_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${latex_build_command}) + set(make_pdf_command ${make_pdf_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${pdflatex_build_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${pdflatex_build_command}) + + # Need to run one more time to remove biblatex' warning + # about page breaks that have changed. + if(LATEX_USE_BIBLATEX) + set(make_dvi_command ${make_dvi_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${latex_build_command}) + set(make_pdf_command ${make_pdf_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${pdflatex_build_command}) + endif() + + if(LATEX_USE_SYNCTEX) + if(NOT GZIP) + message(SEND_ERROR "UseLATEX.cmake: USE_SYNTEX option requires gzip program. Set GZIP variable.") + endif() + set(make_dvi_command ${make_dvi_command} + COMMAND ${CMAKE_COMMAND} + -D LATEX_BUILD_COMMAND=correct_synctex + -D LATEX_TARGET=${LATEX_TARGET} + -D GZIP=${GZIP} + -D "LATEX_SOURCE_DIRECTORY=${CMAKE_CURRENT_SOURCE_DIR}" + -D "LATEX_BINARY_DIRECTORY=${output_dir}" + -P ${LATEX_USE_LATEX_LOCATION} + ) + set(make_pdf_command ${make_pdf_command} + COMMAND ${CMAKE_COMMAND} + -D LATEX_BUILD_COMMAND=correct_synctex + -D LATEX_TARGET=${LATEX_TARGET} + -D GZIP=${GZIP} + -D "LATEX_SOURCE_DIRECTORY=${CMAKE_CURRENT_SOURCE_DIR}" + -D "LATEX_BINARY_DIRECTORY=${output_dir}" + -P ${LATEX_USE_LATEX_LOCATION} + ) + endif() + + # Check LaTeX output for important warnings at end of build + set(make_dvi_command ${make_dvi_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${CMAKE_COMMAND} + -D LATEX_BUILD_COMMAND=check_important_warnings + -D LATEX_TARGET=${LATEX_TARGET} + -P ${LATEX_USE_LATEX_LOCATION} + ) + set(make_pdf_command ${make_pdf_command} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${CMAKE_COMMAND} + -D LATEX_BUILD_COMMAND=check_important_warnings + -D LATEX_TARGET=${LATEX_TARGET} + -P ${LATEX_USE_LATEX_LOCATION} + ) + + # Capture the default build. + string(TOLOWER "${LATEX_DEFAULT_BUILD}" default_build) + + if((NOT LATEX_FORCE_PDF) AND (NOT LATEX_FORCE_DVI) AND (NOT LATEX_FORCE_HTML)) + set(no_force TRUE) + endif() + + # Add commands and targets for building pdf outputs (with pdflatex). + if(LATEX_FORCE_PDF OR no_force) + if(LATEX_FORCE_PDF) + set(default_build pdf) + endif() + + if(PDFLATEX_COMPILER) + add_custom_command(OUTPUT ${output_dir}/${LATEX_TARGET}.pdf + COMMAND ${make_pdf_command} + DEPENDS ${make_pdf_depends} + ) + add_custom_target(${pdf_target} + DEPENDS ${output_dir}/${LATEX_TARGET}.pdf + SOURCES ${all_latex_sources} + ) + if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) + add_dependencies(pdf ${pdf_target}) + endif() + endif() + endif() + + # Add commands and targets for building dvi outputs. + if(LATEX_FORCE_DVI OR LATEX_FORCE_HTML OR no_force) + if(LATEX_FORCE_DVI) + if((NOT default_build STREQUAL dvi) AND + (NOT default_build STREQUAL ps) AND + (NOT default_build STREQUAL safepdf)) + set(default_build dvi) + endif() + endif() + + add_custom_command(OUTPUT ${output_dir}/${LATEX_TARGET}.dvi + COMMAND ${make_dvi_command} + DEPENDS ${make_dvi_depends} + ) + add_custom_target(${dvi_target} + DEPENDS ${output_dir}/${LATEX_TARGET}.dvi + SOURCES ${all_latex_sources} + ) + if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) + add_dependencies(dvi ${dvi_target}) + endif() + + if(DVIPS_CONVERTER) + add_custom_command(OUTPUT ${output_dir}/${LATEX_TARGET}.ps + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${DVIPS_CONVERTER} ${DVIPS_CONVERTER_ARGS} -o ${LATEX_TARGET}.ps ${LATEX_TARGET}.dvi + DEPENDS ${output_dir}/${LATEX_TARGET}.dvi) + add_custom_target(${ps_target} + DEPENDS ${output_dir}/${LATEX_TARGET}.ps + SOURCES ${all_latex_sources} + ) + if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) + add_dependencies(ps ${ps_target}) + endif() + if(PS2PDF_CONVERTER) + # Since both the pdf and safepdf targets have the same output, we + # cannot properly do the dependencies for both. When selecting safepdf, + # simply force a recompile every time. + add_custom_target(${safepdf_target} + ${CMAKE_COMMAND} -E chdir ${output_dir} + ${PS2PDF_CONVERTER} ${PS2PDF_CONVERTER_ARGS} ${LATEX_TARGET}.ps ${LATEX_TARGET}.pdf + DEPENDS ${ps_target} + ) + if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) + add_dependencies(safepdf ${safepdf_target}) + endif() + endif() + endif() + endif() + + if(LATEX_FORCE_HTML OR no_force) + if (LATEX_FORCE_HTML) + set(default_build html) + endif() + + if(HTLATEX_COMPILER AND LATEX_MAIN_INPUT_SUBDIR) + message(STATUS + "Disabling HTML build for ${LATEX_TARGET_NAME}.tex because the main file is in subdirectory ${LATEX_MAIN_INPUT_SUBDIR}" + ) + # The code below to run HTML assumes that LATEX_TARGET.tex is in the + # current directory. I have tried to specify that LATEX_TARGET.tex is + # in a subdirectory. That makes the build targets correct, but the + # HTML build still fails (at least for htlatex) because files are not + # generated where expected. I am getting around the problem by simply + # disabling HTML in this case. If someone really cares, they can fix + # this, but make sure it runs on many platforms and build programs. + elseif(HTLATEX_COMPILER) + # htlatex places the output in a different location + set(HTML_OUTPUT "${output_dir}/${LATEX_TARGET}.html") + add_custom_command(OUTPUT ${HTML_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} + ${HTLATEX_COMPILER} ${LATEX_MAIN_INPUT} + "${HTLATEX_COMPILER_TEX4HT_FLAGS}" + "${HTLATEX_COMPILER_TEX4HT_POSTPROCESSOR_FLAGS}" + "${HTLATEX_COMPILER_T4HT_POSTPROCESSOR_FLAGS}" + ${HTLATEX_COMPILER_ARGS} + DEPENDS + ${output_dir}/${LATEX_TARGET}.tex + ${output_dir}/${LATEX_TARGET}.dvi + VERBATIM + ) + add_custom_target(${html_target} + DEPENDS ${HTML_OUTPUT} ${dvi_target} + SOURCES ${all_latex_sources} + ) + if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) + add_dependencies(html ${html_target}) + endif() + endif() + endif() + + # Set default targets. + if("${default_build}" STREQUAL "pdf") + add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${pdf_target}) + elseif("${default_build}" STREQUAL "dvi") + add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${dvi_target}) + elseif("${default_build}" STREQUAL "ps") + add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${ps_target}) + elseif("${default_build}" STREQUAL "safepdf") + add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${safepdf_target}) + elseif("${default_build}" STREQUAL "html") + add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${html_target}) + else() + message(SEND_ERROR "LATEX_DEFAULT_BUILD set to an invalid value. See the documentation for that variable.") + endif() + + if(NOT LATEX_EXCLUDE_FROM_ALL) + add_custom_target(_${LATEX_TARGET_NAME} ALL DEPENDS ${LATEX_TARGET_NAME}) + endif() + + set_directory_properties(. + ADDITIONAL_MAKE_CLEAN_FILES "${auxiliary_clean_files}" + ) + + add_custom_target(${auxclean_target} + COMMENT "Cleaning auxiliary LaTeX files." + COMMAND ${CMAKE_COMMAND} -E remove ${auxiliary_clean_files} + ) + add_dependencies(auxclean ${auxclean_target}) +endfunction(add_latex_targets_internal) + +function(add_latex_targets latex_main_input) + latex_get_output_path(output_dir) + parse_add_latex_arguments(ADD_LATEX_TARGETS ${latex_main_input} ${ARGN}) + + add_latex_targets_internal() +endfunction(add_latex_targets) + +function(add_latex_document_deprecated latex_main_input) + latex_get_output_path(output_dir) + if(output_dir) + parse_add_latex_arguments(add_latex_document ${latex_main_input} ${ARGN}) + + latex_copy_input_file(${LATEX_MAIN_INPUT}) + + foreach (bib_file ${LATEX_BIBFILES}) + latex_copy_input_file(${bib_file}) + endforeach (bib_file) + + if (LATEX_USE_BIBLATEX AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/biblatex.cfg) + latex_copy_input_file(biblatex.cfg) + set(LATEX_USE_BIBLATEX_CONFIG TRUE) + endif() + + foreach (input ${LATEX_INPUTS}) + latex_copy_input_file(${input}) + endforeach(input) + + latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.cls ${output_dir}) + latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.bst ${output_dir}) + latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.clo ${output_dir}) + latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.sty ${output_dir}) + latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.ist ${output_dir}) + latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.fd ${output_dir}) + + add_latex_targets_internal() + endif() +endfunction(add_latex_document_deprecated) + +############################################################################# +# Actually do stuff +############################################################################# + +if(LATEX_BUILD_COMMAND) + set(command_handled) + + if("${LATEX_BUILD_COMMAND}" STREQUAL execute_latex) + latex_execute_latex() + set(command_handled TRUE) + endif() + + if("${LATEX_BUILD_COMMAND}" STREQUAL makeglossaries) + latex_makeglossaries() + set(command_handled TRUE) + endif() + + if("${LATEX_BUILD_COMMAND}" STREQUAL makenomenclature) + latex_makenomenclature() + set(command_handled TRUE) + endif() + + if("${LATEX_BUILD_COMMAND}" STREQUAL correct_synctex) + latex_correct_synctex() + set(command_handled TRUE) + endif() + + if("${LATEX_BUILD_COMMAND}" STREQUAL check_important_warnings) + latex_check_important_warnings() + set(command_handled TRUE) + endif() + + if(NOT command_handled) + message(SEND_ERROR "Unknown command: ${LATEX_BUILD_COMMAND}") + endif() + +else() + # Must be part of the actual configure (included from CMakeLists.txt). + latex_setup_variables() + latex_setup_targets() +endif() diff --git a/cmake/modules/UseLatexMk.cmake b/cmake/modules/UseLatexMk.cmake new file mode 100644 index 0000000..aa2b926 --- /dev/null +++ b/cmake/modules/UseLatexMk.cmake @@ -0,0 +1,254 @@ +# 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. +# + +# 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} + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/cmake + ${CMAKE_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 ${CMAKE_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..a59060b --- /dev/null +++ b/cmake/scripts/CMakeLists.txt @@ -0,0 +1,15 @@ +install(FILES + conf.py.in + CreateDoxyFile.cmake + envdetect.py + extract_cmake_data.py + FinalizeHeadercheck.cmake + index.rst.in + InstallFile.cmake + main77.cc.in + module_library.cc.in + pyversion.py + run-in-dune-env.sh.in + RunDoxygen.cmake + sphinx_cmake_dune.py + 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..6084a2b --- /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 300) 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..f129bed --- /dev/null +++ b/cmake/scripts/index.rst.in @@ -0,0 +1,41 @@ +.. title:: @CMAKE_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..9054fc4 --- /dev/null +++ b/config.h.cmake @@ -0,0 +1,213 @@ +/* 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 compiler support C++17's class template argument deduction? */ +#cmakedefine DUNE_HAVE_CXX_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1 + +/* does the compiler support C++17's optional? */ +#cmakedefine DUNE_HAVE_CXX_OPTIONAL 1 + +/* does the compiler support C++17's variant? */ +#cmakedefine DUNE_HAVE_CXX_VARIANT 1 + +/* does the compiler support conditionally throwing exceptions in constexpr context? */ +#cmakedefine DUNE_SUPPORTS_CXX_THROW_IN_CONSTEXPR 1 + +/* does the standard library provides aligned_alloc()? */ +#cmakedefine DUNE_HAVE_C_ALIGNED_ALLOC 1 + +/* does the standard library provide ? */ +#cmakedefine DUNE_HAVE_HEADER_EXPERIMENTAL_TYPE_TRAITS 1 + +/* does the standard library provide bool_constant ? */ +#cmakedefine DUNE_HAVE_CXX_BOOL_CONSTANT 1 + +/* does the standard library provide experimental::bool_constant ? */ +#cmakedefine DUNE_HAVE_CXX_EXPERIMENTAL_BOOL_CONSTANT 1 + +/* does the standard library provide apply() ? */ +#cmakedefine DUNE_HAVE_CXX_APPLY 1 + +/* does the standard library provide experimental::apply() ? */ +#cmakedefine DUNE_HAVE_CXX_EXPERIMENTAL_APPLY 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 + +/* does the compiler support abi::__cxa_demangle */ +#cmakedefine HAVE_CXA_DEMANGLE 1 + +/* Define if you have LAPACK library. */ +#cmakedefine HAVE_LAPACK 1 + +/* Define to 1 if you have the header file. */ +// Not used! #cmakedefine01 HAVE_MALLOC_H + +/* Define if you have the MPI library. */ +#cmakedefine HAVE_MPI ENABLE_MPI + +/* 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 symbol mprotect. */ +#cmakedefine HAVE_MPROTECT 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDINT_H 1 + +/* Define to 1 if you have . */ +#cmakedefine HAVE_SYS_MMAN_H 1 + +/* 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.4, 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 HAVE_NULLPTR 1 +#define HAVE_CONSTEXPR 1 +#define HAVE_RANGE_BASED_FOR 1 +#define HAVE_NOEXCEPT_SPECIFIER 1 +#define HAVE_STD_DECLVAL 1 +#define HAVE_KEYWORD_FINAL 1 +#define MPI_2 1 + +/* Define to 1 if the compiler properly supports testing for operator[] */ +#cmakedefine 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 ENABLE_PARMETIS if you have the Parmetis library. + This is only true if MPI was found + by configure _and_ if the application uses the PARMETIS_CPPFLAGS */ +#cmakedefine HAVE_PARMETIS ENABLE_PARMETIS + +/* Define to 1 if PT-Scotch is available */ +#cmakedefine HAVE_PTSCOTCH 1 + +/* Include always useful headers */ +#include "FC.h" +#define FC_FUNC FC_GLOBAL_ + +/* 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..c212fb0 --- /dev/null +++ b/doc/buildsystem/dune-common.rst @@ -0,0 +1,391 @@ +=========== +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 + +Dune got support for CMake in version 2.3 alongside the old Autotools build system. It got the default in the +2.4 release. + +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.1. + +.. _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` or :code:`Fortran`) 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` or :code:`Fortran`). + +.. _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. + +.. _tarball: + +How do I create tarballs? +========================= + +CMake has a packaging tool CPack. This creates tarballs or binary packages. To build tarballs add +the variable :code:`CPACK_SET_DESTDIR` which must be set to :code:`true` for the configuration +of all modules. Inside the build directory run :code:`make package_source` and you'll find the +packages below :code:`/_CPack_Packages`. + +Note that an un-packed copy is located there which contains a :code:`dune.module` file. Delete the +subdirectory. Otherwise it is going to break your next :code:`dunecontrol` run, because the module +is defined multiple times. + +.. _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..454194e --- /dev/null +++ b/doc/comm/CMakeLists.txt @@ -0,0 +1,15 @@ +add_executable(poosc08 "poosc08.cc") +target_link_libraries(poosc08 "dunecommon") + +add_executable(poosc08_test "poosc08_test.cc") +target_link_libraries(poosc08_test "dunecommon") + +add_executable(indexset "indexset.cc") +target_link_libraries(indexset "dunecommon") + +add_dune_mpi_flags("poosc08;poosc08_test;indexset") + +dune_add_latex_document( + SOURCE communication.tex + FATHER_TARGET doc + 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..f83914a --- /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 = +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..5965364 --- /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: Dune (Distributed and Unified Numerics Environment) common module +URL: http://dune-project.org/ +Requires: ${DEPENDENCIES} +Libs: -L${libdir} -ldunecommon +Cflags: -I${includedir} diff --git a/dune.module b/dune.module new file mode 100644 index 0000000..23389ef --- /dev/null +++ b/dune.module @@ -0,0 +1,4 @@ +Module: dune-common +Version: 2.7.0 +Maintainer: dune-devel@lists.dune-project.org +Whitespace-Hook: Yes diff --git a/dune/CMakeLists.txt b/dune/CMakeLists.txt new file mode 100644 index 0000000..b85be7f --- /dev/null +++ b/dune/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory("common") diff --git a/dune/common/CMakeLists.txt b/dune/common/CMakeLists.txt new file mode 100644 index 0000000..bc98dfc --- /dev/null +++ b/dune/common/CMakeLists.txt @@ -0,0 +1,125 @@ +add_subdirectory("parallel") +add_subdirectory("simd") +add_subdirectory("std") +add_subdirectory("test") + +#build the library dunecommon +if(LAPACK_FOUND) + set(_additional_libs ${LAPACK_LIBRARIES}) +elseif(BLAS_FOUND) + set(_additional_libs ${BLAS_LIBRARIES}) +endif(LAPACK_FOUND) + +if(HAVE_MPROTECT) + set(debugallocator_src "debugallocator.cc") +endif(HAVE_MPROTECT) + +dune_add_library("dunecommon" + debugalign.cc + ${debugallocator_src} + exceptions.cc + fmatrixev.cc + ios_state.cc + parametertree.cc + parametertreeparser.cc + path.cc + simd/test.cc + stdstreams.cc + stdthread.cc + ADD_LIBS "${_additional_libs}") + +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 + 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..ac9e203 --- /dev/null +++ b/dune/common/alignedallocator.hh @@ -0,0 +1,100 @@ +// -*- 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 + +#if !(DUNE_HAVE_C_ALIGNED_ALLOC || (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)) + #error Need either aligned_alloc() or posix_memalign() to compile AlignedAllocator +#endif + +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 { + +#if !DUNE_HAVE_C_ALIGNED_ALLOC + + /* + * posix_memalign() on macOS has pretty draconian restrictions on the + * alignments that you may ask for: It has to be + * + * 1) a power of 2 + * 2) at least as large as sizeof(void*) + * + * So here is a little constexpr function that calculates just that + * (together with the correct starting value for align fed in further down). + */ + static constexpr int fixAlignment(int align) + { + return ((Alignment==-1) ? std::alignment_of::value : Alignment) > align + ? fixAlignment(align << 1) : align; + } + +#else + + /* + * Non-Apple platforms just have to 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; + } + +#endif + + 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, const void* hint = 0) + { + + DUNE_UNUSED_PARAMETER(hint); + if (n > this->max_size()) + throw std::bad_alloc(); + +#if !DUNE_HAVE_C_ALIGNED_ALLOC + /* + * Apple's standard library doesn't have aligned_alloc() - C11 is still something + * from the future in Cupertino. Luckily, they got around to finally implementing + * posix_memalign(), so let's use that instead. + */ + void* ret = nullptr; + if (posix_memalign(&ret, alignment, n * sizeof(T)) != 0) + throw std::bad_alloc(); + + return static_cast(ret); +#else + /* + * Everybody else gets the standard treatment. + */ + pointer ret = static_cast(aligned_alloc(alignment, n * sizeof(T))); + if (!ret) + throw std::bad_alloc(); + + return ret; +#endif + } + }; + +} + +#endif // DUNE_ALIGNED_ALLOCATOR_HH diff --git a/dune/common/arraylist.hh b/dune/common/arraylist.hh new file mode 100644 index 0000000..2eb6c78 --- /dev/null +++ b/dune/common/arraylist.hh @@ -0,0 +1,733 @@ +// -*- 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. + */ + typedef typename A::template rebind > >::other + SmartPointerAllocator; + + /** + * @brief The allocator for the fixed array. + */ + typedef typename A::template rebind >::other + ArrayAllocator; + + /** + * @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::reference, + 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; + + typedef typename A::reference reference; + + typedef typename A::const_reference const_reference; + + 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::const_reference, + 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; + + typedef typename A::reference reference; + + typedef typename A::const_reference const_reference; + 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..d1ec24b --- /dev/null +++ b/dune/common/classname.hh @@ -0,0 +1,76 @@ +// -*- 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 HAVE_CXA_DEMANGLE +#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..4ebce59 --- /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..a833050 --- /dev/null +++ b/dune/common/debugallocator.cc @@ -0,0 +1,31 @@ +// -*- 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" + +#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 diff --git a/dune/common/debugallocator.hh b/dune/common/debugallocator.hh new file mode 100644 index 0000000..c6d3a8c --- /dev/null +++ b/dune/common/debugallocator.hh @@ -0,0 +1,357 @@ +// -*- 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_SYS_MMAN_H && HAVE_MPROTECT +#include +#else +enum DummyProtFlags { PROT_NONE, PROT_WRITE, PROT_READ }; +#endif + +#include "mallocallocator.hh" + +#if not HAVE_MPROTECT +#error mprotect is required to use the DebugAllocator +#else + +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(void* from, difference_type len, 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 + DUNE_UNUSED_PARAMETER(from); + DUNE_UNUSED_PARAMETER(len); + DUNE_UNUSED_PARAMETER(prot); + 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, + DebugAllocator::const_pointer hint = 0) + { + DUNE_UNUSED_PARAMETER(hint); + 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 // HAVE_PROTECT + +#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..096f3f4 --- /dev/null +++ b/dune/common/densematrix.hh @@ -0,0 +1,1263 @@ +// -*- 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 +#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 + */ + template class FieldMatrix; + template class FieldVector; + namespace { + template + struct DUNE_DEPRECATED_MSG("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 DUNE_DEPRECATED_MSG("VectorSize is deprecated; please call the 'size()' method directly instead") VectorSize< const FieldVector > + { + typedef FieldVector V; + static typename V::size_type size(const V & v) + { + DUNE_UNUSED_PARAMETER(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 + MAT & asImp() { return static_cast(*this); } + 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 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 field_type = typename FieldTraits::field_type; + for(size_type i = 0; i < cols(); ++i) + { + yy[i] = 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; + typedef typename decltype(result)::size_type size_type; + + for (size_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..60ff00b --- /dev/null +++ b/dune/common/deprecated.hh @@ -0,0 +1,224 @@ +// -*- 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 +/** + * \code + *#include + * \endcode + * + * This is a preprocessor define which can be used to mark functions, + * typedefs, enums and other stuff deprecated. If something is marked + * deprecated, users are advised to migrate to the new interface, since it + * will probably be removed in the next release of Dune. + * + * DUNE_DEPRECATED currently works with g++ and clang++. For other compilers it will + * be defined empty. This way the user will not get any deprecation warning, + * but at least his code still compiles (well, until the next Dune release, + * that is). + * + * Here are some examples how to mark different stuff deprecated: + * - Classes + * \code + class DUNE_DEPRECATED Class {}; // 1) + class Class {} DUNE_DEPRECATED; // 2) + * \endcode + * Both forms do not work properly with g++-4.1: no deprecation warning + * will be given, although the code still compiles. 1) should be preferred + * over 2) since 2) does not work with clang++-1.1 (again, no warning given + * but code still compiles, works with clang++-3.1) + * - Template classes + * \code + template + class DUNE_DEPRECATED Class {}; // 1) + template + class Class {} DUNE_DEPRECATED; // 2) + * \endcode + * This works works with g++ >=4.3 only; g++-4.1 and + * clang++ compile the code without warning in both cases. Furthermore, + * the warning is only triggered when copying an object of that template + * class, neither making a typedef nor simply creating such an object emit + * the warning. It is thus recommended that some essential class member be + * marked deprecated as well, if possible. + * - Member constants + * \code + template struct Class { + static const int c0 DUNE_DEPRECATED = 0; + static const int DUNE_DEPRECATED c1 = 1; + }; + * \endcode + * Works with g++-4.1, g++ >=4.3 and clang++3.1. + * No warning but clean compile with clang++-1.1. + * - Member enumerators + * \code + template struct Class { + enum enumeration { enumerator = 0 }; + }; + * \endcode + * No form of deprecation is known that does not trigger an error on most + * compilers. + * - Member functions + * \code + template struct Class { + void frob() DUNE_DEPRECATED {} + }; // 1) + template struct Class { + void DUNE_DEPRECATED frob() {} + }; // 2) + template struct Class { + DUNE_DEPRECATED void frob() {} + }; // 3) + * \endcode + * With g++ only 2) emits a warning for templated member functions. + */ +#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 +/** + * \code + *#include + * \endcode + * + * This is a preprocessor define which can be used to mark functions, + * typedefs, enums and other stuff deprecated and to also specify a + * hint what replaces the given functionality. If something is marked + * deprecated, users are advised to migrate to the new interface, + * since it will probably be removed in the next release of Dune. + * + * DUNE_DEPRECATED_MSG currently works only for compilers which + * support the attribute __attribute__((deprecated("message")). For + * other compilers it will be defined empty. This way the user will + * not get any deprecation warning, but at least his code still + * compiles (well, until the next Dune release, that is). + * + * Here are some examples how to mark different stuff deprecated: + * - Classes + * \code + class DUNE_DEPRECATED_MSG("In the future, please use 'Glass'") Class {}; // 1) + class Class {} DUNE_DEPRECATED_MSG("In the future, please use 'Glass'"); // 2) + * \endcode + * For both forms, deprecation warnings and the additional hint "In + * the future, please use 'Glass'" will be printed on compilers + * which support it (e.g. G++ >= 4.5.0, clang++ >= 1.1). For compilers + * which support deprecating but do not take an additional hint + * (e.g. G++ < 4.5.0), only the deprecation warning is printed, and + * finally compilers which do not support deprecation of code won't + * print anything, but the code will still compile. 1) should be + * preferred over 2) since 2) does not work with clang++-1.1 + * (again, no warning will be given but code still compiles) + * - Template classes + * \code + template + class DUNE_DEPRECATED_MSG("In the future, please use 'Glass'") Class {}; // 1) + template + class Class {} DUNE_DEPRECATED_MSG("In the future, please use 'Glass'"); // 2) + * \endcode + * This works works with g++ >= 4.5, clang++ until at least version + * 1.1 will compile the code without warning in both cases. + * Furthermore, the warning is only triggered when copying an + * object of that template class, neither making a typedef nor + * simply creating such an object emit the warning. It is thus + * recommended that some essential class member be marked + * deprecated as well, if possible. + * - Member constants + * \code + template struct Class { + static const int c0 DUNE_DEPRECATED_MSG("c2 is the new hype") = 0; + static const int DUNE_DEPRECATED_MSG("c2 is the new hype") c1 = 1; + }; + * \endcode + * Works without printing the hint on g++-4.1, g++-4.3, g++-4.4 and + * fully on g++ >= 4.5. Works for clang++-3.1. + * No warning but clean compile with clang++-1.1. + * - Member enumerators + * \code + template struct Class { + enum enumeration { enumerator = 0 }; + }; + * \endcode + * No form of deprecation is known that does not trigger an error on most + * compilers. + * - Member functions + * \code + template struct Class { + void frob() DUNE_DEPRECATED_MSG("use frog") {} + }; // 1) + template struct Class { + void DUNE_DEPRECATED_MSG("use frog") frob() {} + }; // 2) + template struct Class { + DUNE_DEPRECATED_MSG("use frog") void frob() {} + }; // 3) + * \endcode + * With g++ only 2) emits a warning for templated member functions. + */ +#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..6f3d0f4 --- /dev/null +++ b/dune/common/diagonalmatrix.hh @@ -0,0 +1,1103 @@ +// -*- 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 +#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 + + size_type size () const + { + 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[] (size_type i) const + { + DUNE_UNUSED_PARAMETER(i); + 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(int i) const + { + DUNE_UNUSED_PARAMETER(i); + return rowIndex(); + } + + K* pointer(size_type i) const + { + DUNE_UNUSED_PARAMETER(i); + 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[] (size_type i) + { + DUNE_UNUSED_PARAMETER(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..06e6dec --- /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(Std::to_false_type::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..974954b --- /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..0bc18a6 --- /dev/null +++ b/dune/common/dynmatrixev.hh @@ -0,0 +1,100 @@ +// -*- 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 { + + using Dune::FMatrixHelp::eigenValuesNonsymLapackCall; + + /** \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 + ) + { + { + 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]); + } + } + } + } + + } + +} +/** @} */ +#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..18bb0c7 --- /dev/null +++ b/dune/common/enumset.hh @@ -0,0 +1,176 @@ +// -*- 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 +#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(const Type& attribute) + { + DUNE_UNUSED_PARAMETER(attribute); + return false; + } + + template + inline bool AllSet::contains(const Type& attribute) + { + DUNE_UNUSED_PARAMETER(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(const TI1& set1, const TI2& set2) + { + DUNE_UNUSED_PARAMETER(set1); + DUNE_UNUSED_PARAMETER(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..384416b --- /dev/null +++ b/dune/common/fmatrix.hh @@ -0,0 +1,709 @@ +// -*- 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 + */ + 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..7593ea3 --- /dev/null +++ b/dune/common/fmatrixev.cc @@ -0,0 +1,246 @@ +// -*- 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 +#include + +#if HAVE_LAPACK + +// symmetric matrices +#define DSYEV_FORTRAN FC_FUNC (dsyev, DSYEV) + +// nonsymmetric matrices +#define DGEEV_FORTRAN FC_FUNC (dgeev, DGEEV) + +// 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); + + /* + * + ** 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); + +} // end extern C +#endif + +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) + { +#if HAVE_LAPACK + // call LAPACK dsyev + DSYEV_FORTRAN(jobz, uplo, n, a, lda, w, work, lwork, info); +#else + // silence unused variable warnings + DUNE_UNUSED_PARAMETER(jobz), DUNE_UNUSED_PARAMETER(uplo), DUNE_UNUSED_PARAMETER(n); + DUNE_UNUSED_PARAMETER(a), DUNE_UNUSED_PARAMETER(lda), DUNE_UNUSED_PARAMETER(w); + DUNE_UNUSED_PARAMETER(work), DUNE_UNUSED_PARAMETER(lwork), DUNE_UNUSED_PARAMETER(info); + DUNE_THROW(NotImplemented,"eigenValuesLapackCall: LAPACK not found!"); +#endif + } + + 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) + { +#if HAVE_LAPACK + // call LAPACK dgeev + DGEEV_FORTRAN(jobvl, jobvr, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, + work, lwork, info); +#else + // silence unused variable warnings + DUNE_UNUSED_PARAMETER(jobvl), DUNE_UNUSED_PARAMETER(jobvr), DUNE_UNUSED_PARAMETER(n); + DUNE_UNUSED_PARAMETER(a), DUNE_UNUSED_PARAMETER(lda), DUNE_UNUSED_PARAMETER(wr), DUNE_UNUSED_PARAMETER(wi); + DUNE_UNUSED_PARAMETER(vl), DUNE_UNUSED_PARAMETER(ldvl), DUNE_UNUSED_PARAMETER(vr); + DUNE_UNUSED_PARAMETER(ldvr), DUNE_UNUSED_PARAMETER(work), DUNE_UNUSED_PARAMETER(lwork), DUNE_UNUSED_PARAMETER(info); + DUNE_THROW(NotImplemented,"eigenValuesNonsymLapackCall: LAPACK not found!"); +#endif + } + + } // end namespace FMatrixHelp + +} // end namespace Dune +#endif diff --git a/dune/common/fmatrixev.hh b/dune/common/fmatrixev.hh new file mode 100644 index 0000000..1c534d5 --- /dev/null +++ b/dune/common/fmatrixev.hh @@ -0,0 +1,303 @@ +// -*- 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 + +namespace Dune { + + /** + @addtogroup DenseMatVec + @{ + */ + + namespace FMatrixHelp { + + // 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); + + /** \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 + */ + template + static void eigenValues(const FieldMatrix& matrix, + FieldVector& eigenvalues) + { + eigenvalues[0] = matrix[0][0]; + } + + /** \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 + */ + template + static void eigenValues(const FieldMatrix& matrix, + FieldVector& eigenvalues) + { + using std::sqrt; + const K detM = matrix[0][0] * matrix[1][1] - matrix[1][0] * matrix[0][1]; + const K p = 0.5 * (matrix[0][0] + matrix [1][1]); + K q = p * p - detM; + 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; + } + + /** \brief Calculates the eigenvalues of a symmetric 3x3 field matrix + \param[in] matrix matrix eigenvalues are calculated for + \param[out] eigenvalues Eigenvalues in ascending order + + \note If the input matrix is not symmetric the behavior of this method is undefined. + + 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 void eigenValues(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 <= 1e-8) + { + // A is diagonal. + eigenvalues[0] = matrix[0][0]; + eigenvalues[1] = matrix[1][1]; + eigenvalues[2] = matrix[2][2]; + } + 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 * 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. + K phi; + if (r <= -1) + phi = pi / 3.0; + else if (r >= 1) + phi = 0; + else + phi = acos(r) / 3; + + // 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 + } + } + + /** \brief calculates the eigenvalues and/or 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 + \param[in] jobz jobz=n (only eigenvalues), jobz=v (eigenvalues and eigenvetors) + + \note LAPACK::dsyev is used to calculate the eigenvalues and/or eigenvectors + */ + template + static void eigenValuesVectorsLapack(const FieldMatrix& matrix, + FieldVector& eigenValues, + FieldMatrix& eigenVectors, + const char& jobz) + { + { + 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 ; + + // matrix to put into dsyev + double matrixVector[dim * dim]; + + // copy matrix + int row = 0; + for(int i=0; i + static void eigenValues(const FieldMatrix& matrix, + FieldVector& eigenValues) + { + FieldMatrix dummy; + // calculate only eigenValues + eigenValuesVectorsLapack(matrix,eigenValues,dummy,'n'); + } + + /** \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 LAPACK::dsyev is used to calculate the eigenvalues and eigenvectors + */ + template + static void eigenValuesVectors(const FieldMatrix& matrix, + FieldVector& eigenValues, + FieldMatrix& eigenVectors) + { + // calculate eigenValues and eigenVectors + eigenValuesVectorsLapack(matrix,eigenValues,eigenVectors,'v'); + } + + /** \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 eigen values + */ + template + static void eigenValuesNonSym(const FieldMatrix& matrix, + FieldVector& eigenValues) + { + { + const long int N = dim ; + const char jobvl = 'n'; + const char jobvr = 'n'; + + // matrix to put into dgeev + double 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..e3d45c7 --- /dev/null +++ b/dune/common/function.hh @@ -0,0 +1,142 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_FUNCTION_HH +#define DUNE_FUNCTION_HH + +#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 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 + + + + /** + * \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 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 + + namespace Impl { + + 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_; + }; + + } /* 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 + 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..2c183b3 --- /dev/null +++ b/dune/common/fvector.hh @@ -0,0 +1,633 @@ +// -*- 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 "unused.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, typename std::enable_if::value>::type* dummy=0 ) + { + DUNE_UNUSED_PARAMETER(dummy); + // 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[](size_type i) + { + DUNE_UNUSED_PARAMETER(i); + DUNE_ASSERT_BOUNDS(i == 0); + return _data; + } + const K & operator[](size_type i) const + { + DUNE_UNUSED_PARAMETER(i); + 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..30a69a0 --- /dev/null +++ b/dune/common/gcd.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_GCD_HH +#define DUNE_GCD_HH + +namespace Dune +{ + /** + * @addtogroup Common + * @{ + */ + /** + * @file + * \brief Statically compute the greatest common divisor of two integers + */ + +#ifndef DOXYGEN + /** + * @brief Helper for calculating the gcd. + */ + template + struct GcdHelper + {}; + + + template + struct GcdHelper + { + /** + * @brief Check that a>b. + */ + static void conceptCheck() + { + static_assert(b::gcd; + }; + + template + struct GcdHelper + { + /** + * @brief The greatest common divisor of the numbers a and b. + */ + const static long gcd = GcdHelper::gcd; + }; + template + struct GcdHelper + { + const static long gcd=a; + }; + +#endif + + /** + * @brief Calculator of the greatest common divisor. + */ + template + struct Gcd + { + /** + * @brief The greatest common divisior of a and b. */ + const static long value = GcdHelperb)>::gcd; + }; + + /** + * @} + */ +} + +#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..788b47a --- /dev/null +++ b/dune/common/gmpfield.hh @@ -0,0 +1,80 @@ +// -*- 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 + +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 { + }; + +} + +#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..e8a2815 --- /dev/null +++ b/dune/common/hybridutilities.hh @@ -0,0 +1,496 @@ +// -*- 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 +#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 + 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 +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&& i) { DUNE_UNUSED_PARAMETER(i); }); +} + + + +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) +{ + return 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..cf8c5d3 --- /dev/null +++ b/dune/common/lcm.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_LCM_HH +#define DUNE_LCM_HH + +/** \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 Lcm + { + static void conceptCheck() + { + static_assert(0::value)*n; + }; +} + +#endif diff --git a/dune/common/lru.hh b/dune/common/lru.hh new file mode 100644 index 0000000..5da80a4 --- /dev/null +++ b/dune/common/lru.hh @@ -0,0 +1,237 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_LRU_HH +#define DUNE_COMMON_LRU_HH + +#include +#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 allocator::template rebind >::other > 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; + typedef typename allocator::pointer pointer; + typedef typename allocator::const_pointer const_pointer; + typedef typename allocator::const_reference const_reference; + typedef typename allocator::reference reference; + 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 (int i) const + { + DUNE_UNUSED_PARAMETER(i); + 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..c73b176 --- /dev/null +++ b/dune/common/mallocallocator.hh @@ -0,0 +1,117 @@ +// -*- 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 +#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, + const void* hint = 0) + { + DUNE_UNUSED_PARAMETER(hint); + 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, size_type n) + { + DUNE_UNUSED_PARAMETER(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..519e20b --- /dev/null +++ b/dune/common/overloadset.hh @@ -0,0 +1,212 @@ +// -*- 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 { + +#if __cpp_variadic_using >= 201611 + + template + class OverloadSet + : public F... + { + + public: + + template + OverloadSet(FF&&... ff) + : F(std::forward(ff))... + {} + + using F::operator()...; + + }; + +#else // __cpp_variadic_using >= 201611 + + // This overload set derives from + // all passed functions. Since we + // cannot do argument pack expansion + // on using statements this is done recursively. + template + class OverloadSet: public OverloadSet, F0 + { + using Base = OverloadSet; + public: + + template + OverloadSet(FF0&& f0, FF&&... ff) : + Base(std::forward(ff)...), + F0(std::forward(f0)) + {} + + // pull in operator() of F0 and of all F... via the base class + using F0::operator(); + using Base::operator(); + }; + + template + class OverloadSet: public F0 + { + public: + + template + OverloadSet(FF0&& f0) : + F0(std::forward(f0)) + {} + + // pull in operator() of F0 + using F0::operator(); + }; + +#endif // __cpp_variadic_using >= 201611 + +} // 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. + * + * On gcc 5 and gcc 6 mixing templated overloads + * (i.e. using auto-parameter) and non-templated + * ones may not compile if both they are captureless + * lambdas. The problem can be avoided by capturing + * a dummy value. + * + * \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(Std::is_callable::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. + * + * On gcc 5 and gcc 6 mixing templated overloads + * (i.e. using auto-parameter) and non-templated + * ones may not compile if both they are captureless + * lambdas. The problem can be avoided by capturing + * a dummy value. + * + * \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..6ca379e --- /dev/null +++ b/dune/common/parallel/CMakeLists.txt @@ -0,0 +1,27 @@ +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..3c02368 --- /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 "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..0715ed5 --- /dev/null +++ b/dune/common/parallel/communication.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_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 +#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 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; + } + + //! 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(const T& data, int dest_rank, int tag){ + DUNE_UNUSED_PARAMETER(data); + DUNE_UNUSED_PARAMETER(dest_rank); + DUNE_UNUSED_PARAMETER(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(const T&& data, int dest_rank, int tag){ + DUNE_UNUSED_PARAMETER(data); + DUNE_UNUSED_PARAMETER(dest_rank); + DUNE_UNUSED_PARAMETER(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(T&& data, int source_rank, int tag, void* status = 0){ + DUNE_UNUSED_PARAMETER(data); + DUNE_UNUSED_PARAMETER(source_rank); + DUNE_UNUSED_PARAMETER(tag); + DUNE_UNUSED_PARAMETER(status); + 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(T&& data, int source_rank, int tag){ + DUNE_UNUSED_PARAMETER(data); + DUNE_UNUSED_PARAMETER(source_rank); + DUNE_UNUSED_PARAMETER(tag); + DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); + } + + template + T rrecv(T&& data, int source_rank, int tag, void* status = 0) const + { + DUNE_UNUSED_PARAMETER(data); + DUNE_UNUSED_PARAMETER(source_rank); + DUNE_UNUSED_PARAMETER(tag); + DUNE_UNUSED_PARAMETER(status); + 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 (T* inout, int len) const + { + DUNE_UNUSED_PARAMETER(inout); + DUNE_UNUSED_PARAMETER(len); + 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 (T* inout, int len) const + { + DUNE_UNUSED_PARAMETER(inout); + DUNE_UNUSED_PARAMETER(len); + 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 (T* inout, int len) const + { + DUNE_UNUSED_PARAMETER(inout); + DUNE_UNUSED_PARAMETER(len); + 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 (T* inout, int len) const + { + DUNE_UNUSED_PARAMETER(inout); + DUNE_UNUSED_PARAMETER(len); + 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 (T* inout, int len, int root) const + { + DUNE_UNUSED_PARAMETER(inout); + DUNE_UNUSED_PARAMETER(len); + DUNE_UNUSED_PARAMETER(root); + 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, int root) const // note out must have same size as in + { + DUNE_UNUSED_PARAMETER(root); + 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 sendlen 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] sendlen 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] recvlen 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 sendlen 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 sendlen, T* out, int* recvlen, int* displ, int root) const + { + DUNE_UNUSED_PARAMETER(recvlen); + DUNE_UNUSED_PARAMETER(root); + for (int i=*displ; i + int scatter (const T* send, T* recv, int len, int root) const // note out must have same size as in + { + DUNE_UNUSED_PARAMETER(root); + 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 recvlen-1. + * @param[in] send The array to scatter. May have length zero on non-root + * tasks. + * @param[in] sendlen 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 recvlen 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] recv 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] recvlen The number of elements in the recv 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* send, int* sendlen, int* displ, T* recv, int recvlen, int root) const + { + DUNE_UNUSED_PARAMETER(recvlen); + DUNE_UNUSED_PARAMETER(root); + for (int i=*displ; i<*sendlen; i++) + recv[i] = send[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] sendlen The number of elements to send on each task. + * @param[out] out The buffer to store the received data in. + * @param[in] recvlen 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 sendlen 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 sendlen, T* out, int* recvlen, int* displ) const + { + DUNE_UNUSED_PARAMETER(recvlen); + for (int i=*displ; i + int allreduce(Type* inout, int len) const + { + DUNE_UNUSED_PARAMETER(inout); + DUNE_UNUSED_PARAMETER(len); + 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..51e3549 --- /dev/null +++ b/dune/common/parallel/communicator.hh @@ -0,0 +1,1548 @@ +// -*- 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 +#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(const V& v, int index) + { + DUNE_UNUSED_PARAMETER(v); + DUNE_UNUSED_PARAMETER(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, size_t bufferSize) const + { + DUNE_UNUSED_PARAMETER(bufferSize); + 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, size_t bufferSize) const + { + DUNE_UNUSED_PARAMETER(bufferSize); + 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..2e0a1ef --- /dev/null +++ b/dune/common/parallel/indexset.hh @@ -0,0 +1,1163 @@ +// -*- 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 + +#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(const T& t1, const T& t2){ + DUNE_UNUSED_PARAMETER(t1); + DUNE_UNUSED_PARAMETER(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; + typedef typename ArrayList::iterator iterator; + typedef typename ArrayList::const_iterator const_iterator; + + iterator old=localIndices_.begin(); + iterator added=newIndices_.begin(); + const const_iterator endold=localIndices_.end(); + const const_iterator 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 + + typedef typename ArrayList::iterator iterator; + const const_iterator end_ = end(); + uint32_t index=0; + + for(iterator 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..5367869 --- /dev/null +++ b/dune/common/parallel/indicessyncer.hh @@ -0,0 +1,1226 @@ +// -*- 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 +#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()(const GlobalIndex& global) + { + DUNE_UNUSED_PARAMETER(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) + { + typedef typename RemoteIndices::const_iterator RemoteIterator; + + for(RemoteIterator remote = remoteIndices.begin(), end =remoteIndices.end(); remote != end; ++remote) { + typedef typename RemoteIndices::RemoteIndexList RemoteIndexList; + typedef typename RemoteIndexList::const_iterator RemoteIndexIterator; + typedef SLList,A> GlobalIndexList; + GlobalIndexList& global = globalMap[remote->first]; + RemoteIndexList& rList = *(remote->second.first); + + for(RemoteIndexIterator 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) + { + typedef typename RemoteIndices::RemoteIndexMap::iterator RemoteIterator; + typedef typename RemoteIndices::RemoteIndexList::iterator RemoteIndexIterator; + typedef typename T::GlobalIndex GlobalIndex; + typedef typename T::LocalIndex::Attribute Attribute; + typedef std::pair GlobalIndexPair; + typedef SLList GlobalIndexPairList; + typedef typename GlobalIndexPairList::iterator GlobalIndexIterator; + + assert(globalMap.size()==static_cast(remoteIndices.neighbours())); + // Repair pointers to index set in remote indices. + typename std::map::iterator global = globalMap.begin(); + RemoteIterator end = remoteIndices.remoteIndices_.end(); + + for(RemoteIterator remote = remoteIndices.remoteIndices_.begin(); remote != end; ++remote, ++global) { + typedef typename T::const_iterator IndexIterator; + + assert(remote->first==global->first); + assert(remote->second.first->size() == global->second.size()); + + RemoteIndexIterator riEnd = remote->second.first->end(); + RemoteIndexIterator rIndex = remote->second.first->begin(); + GlobalIndexIterator gIndex = global->second.begin(); + IndexIterator 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() + { + typedef typename ParallelIndexSet::const_iterator IndexIterator; + typedef CollectiveIterator CollectiveIterator; + + IndexIterator iEnd = indexSet_.end(); + CollectiveIterator collIter = remoteIndices_.template iterator(); + + for(IndexIterator index = indexSet_.begin(); index != iEnd; ++index) { + collIter.advance(index->global(), index->local().attribute()); + if(collIter.empty()) + break; + int knownRemote=0; + + typedef typename CollectiveIterator::iterator ValidIterator; + ValidIterator end = collIter.end(); + + // Count the remote indices we know. + for(ValidIterator valid = collIter.begin(); valid != end; ++valid) { + ++knownRemote; + } + + if(knownRemote>0) { + Dune::dverb<global()<< " for processes "; + + // Update MessageInformation + for(ValidIterator valid = collIter.begin(); valid != end; ++valid) { + ++(infoSend_[valid.process()].publish); + (infoSend_[valid.process()].pairs) += knownRemote; + Dune::dverb<::const_iterator + MessageIterator; + + const MessageIterator end = infoSend_.end(); + + // registerMessageDatatype(); + + // Now determine the buffersizes needed for each neighbour using MPI_Pack_size + MessageInformation dummy; + + MessageIterator messageIter= infoSend_.begin(); + + typedef typename RemoteIndices::RemoteIndexMap::const_iterator RemoteIterator; + const RemoteIterator rend = remoteIndices_.end(); + int neighbour=0; + + for(RemoteIterator remote = remoteIndices_.begin(); remote != rend; ++remote, ++neighbour) { + MessageInformation* message; + MessageInformation recv; + + if(messageIter != end && messageIter->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) + { + typedef typename ParallelIndexSet::const_iterator IndexIterator; + + IndexIterator 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(IndexIterator index = indexSet_.begin(); index != iEnd; ++index) { + // Search for corresponding remote indices in all iterator tuples + typedef typename IteratorsMap::iterator Iterator; + Iterator iteratorsEnd = iteratorsMap_.end(); + + // advance all iterators to a position with global index >= index->global() + for(Iterator 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(Iterator 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(Iterator 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) + { + typedef typename ParallelIndexSet::const_iterator IndexIterator; + + IndexIterator iEnd = indexSet_.end(); + IndexIterator index = indexSet_.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? + IndexIterator 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. + typedef typename IteratorsMap::iterator Iterator; + typedef typename RemoteIndices::RemoteIndexMap::iterator + RemoteIterator; + typedef typename GlobalIndicesMap::iterator GlobalIterator; + typedef typename BoolMap::iterator BoolIterator; + + const RemoteIterator remoteEnd = remoteIndices_.remoteIndices_.end(); + Iterator iterators = iteratorsMap_.begin(); + GlobalIterator global = globalMap_.begin(); + BoolIterator added = oldMap_.begin(); + + for(RemoteIterator 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. + typedef typename IteratorsMap::iterator Iterator; + typedef typename RemoteIndices::RemoteIndexMap::iterator + RemoteIterator; + typedef typename GlobalIndicesMap::iterator GlobalIterator; + typedef typename BoolMap::iterator BoolIterator; + + const RemoteIterator remoteEnd = remoteIndices_.remoteIndices_.end(); + Iterator iterators = iteratorsMap_.begin(); + GlobalIterator global = globalMap_.begin(); + BoolIterator added = oldMap_.begin(); + bool ret = true; + + for(RemoteIterator 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..f0a226b --- /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){ + 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 sendlen, T* out, int* recvlen, int* displ, int root) const + { + return MPI_Gatherv(const_cast(in),sendlen,MPITraits::getType(), + out,recvlen,displ,MPITraits::getType(), + root,communicator); + } + + //! @copydoc Communication::scatter() + //! @note out must have space for P*len elements + template + int scatter (const T* send, T* recv, int len, int root) const + { + return MPI_Scatter(const_cast(send),len,MPITraits::getType(), + recv,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(); + 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* send, int* sendlen, int* displ, T* recv, int recvlen, int root) const + { + return MPI_Scatterv(const_cast(send),sendlen,displ,MPITraits::getType(), + recv,recvlen,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 sendlen, T* out, int* recvlen, int* displ) const + { + return MPI_Allgatherv(const_cast(in),sendlen,MPITraits::getType(), + out,recvlen,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..b473d2a --- /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..bac085a --- /dev/null +++ b/dune/common/parallel/mpifuture.hh @@ -0,0 +1,173 @@ +// -*- 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..adb94f5 --- /dev/null +++ b/dune/common/parallel/mpiguard.hh @@ -0,0 +1,232 @@ +// -*- 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 + 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..386a337 --- /dev/null +++ b/dune/common/parallel/mpihelper.hh @@ -0,0 +1,306 @@ +// -*- 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 +#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(int argc, char** argv) + { + (void)argc; (void)argv; + // create singleton 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 + static MPIHelper singleton (argc, argv); + return singleton; + } + + /** + * @brief return rank of process + */ + int rank () const { return rank_; } + /** + * @brief return number of processes + */ + int size () const { return size_; } + + private: + int rank_; + int size_; + bool initializedHere_; + void prevent_warning(int){} + + //! \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; + } + //! \brief calls MPI_Finalize + ~MPIHelper() + { + int wasFinalized = -1; + MPI_Finalized( &wasFinalized ); + if(!wasFinalized && initializedHere_) + { + MPI_Finalize(); + dverb << "Called MPI_Finalize on p=" << rank_ << "!" < +#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..2a6a6a5 --- /dev/null +++ b/dune/common/parallel/mpitraits.hh @@ -0,0 +1,201 @@ +// -*- 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 + +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); + + +#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..a20aa20 --- /dev/null +++ b/dune/common/parallel/remoteindices.hh @@ -0,0 +1,1892 @@ +// -*- 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. + */ + typedef typename A::template rebind::other Allocator; + + /** @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); + + /** + * @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. */ + typedef typename A::template rebind::other Allocator; + + /** @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(); + + /** + * @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) + { + return other.iter_==iter_; + } + + //! \todo Please doc me! + bool operator!=(const iterator& other) + { + 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_(attribute) + {} + + template + RemoteIndex::RemoteIndex(const T2& attribute) + : localIndex_(0), attribute_(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, int n) + { + DUNE_UNUSED_PARAMETER(n); + // fill with own indices + typedef typename ParallelIndexSet::const_iterator const_iterator; + typedef IndexPair PairType; + const const_iterator end = indexSet.end(); + + //Now pack the source indices + int i=0; + for(const_iterator 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) + { + typedef typename ParallelIndexSet::const_iterator const_iterator; + + int noPublic=0; + + const const_iterator end=indexSet.end(); + for(const_iterator 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 + typedef IndexPair PairType; + + 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() + { + typedef typename RemoteIndexMap::iterator Iterator; + Iterator lend = remoteIndices_.end(); + for(Iterator 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) + { + if(neighbours()!=ri.neighbours()) + return false; + + typedef RemoteIndexList RList; + typedef typename std::map >::const_iterator const_iterator; + + const const_iterator rend = remoteIndices_.end(); + + for(const_iterator 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 + typedef typename ParallelIndexSet::const_iterator IndexIterator; + typedef typename GlobalList::const_iterator GlobalIterator; + typedef typename RemoteIndexList::iterator Iterator; + GlobalIterator giter = glist_.begin(); + IndexIterator index = indexSet_->begin(); + + for(Iterator 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) + { + typedef typename RemoteIndexMap::const_iterator const_iterator; + + const const_iterator end=pmap.end(); + for(const_iterator process=pmap.begin(); process != end; ++process) { + const RemoteIndexList* list = send ? process->second.first : process->second.second; + typedef typename RemoteIndexList::const_iterator iterator; + map_.insert(std::make_pair(process->first, + std::pair(list->begin(), list->end()))); + } + } + + template + inline void CollectiveIterator::advance(const GlobalIndex& index) + { + typedef typename Map::iterator iterator; + typedef typename Map::const_iterator const_iterator; + const const_iterator end = map_.end(); + + for(iterator 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) + { + typedef typename Map::iterator iterator; + typedef typename Map::const_iterator const_iterator; + const const_iterator end = map_.end(); + + for(iterator 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++() + { + typedef typename Map::iterator iterator; + typedef typename Map::const_iterator const_iterator; + const const_iterator end = map_.end(); + + for(iterator 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; + + // 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() + { + 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); + + typedef typename RemoteIndices::RemoteIndexList RList; + typedef typename std::map >::const_iterator const_iterator; + + const const_iterator rend = indices.remoteIndices_.end(); + + for(const_iterator rindex = indices.remoteIndices_.begin(); rindex!=rend; ++rindex) { + os<first<<":"; + + if(!rindex->second.first->empty()) { + os<<" send:"; + + const typename RList::const_iterator send= rindex->second.first->end(); + + for(typename RList::const_iterator 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..e5cb498 --- /dev/null +++ b/dune/common/parallel/test/CMakeLists.txt @@ -0,0 +1,46 @@ +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) diff --git a/dune/common/parallel/test/indexsettest.cc b/dune/common/parallel/test/indexsettest.cc new file mode 100644 index 0000000..24b320a --- /dev/null +++ b/dune/common/parallel/test/indexsettest.cc @@ -0,0 +1,83 @@ +// -*- 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 + +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..c64e82b --- /dev/null +++ b/dune/common/parallel/test/mpifuturetest.cc @@ -0,0 +1,106 @@ +#include + +#include +#include +#include +#include + +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/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..e69021b --- /dev/null +++ b/dune/common/parallel/test/remoteindicestest.cc @@ -0,0 +1,723 @@ +// -*- 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 +#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..bfa4fce --- /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 +{ +/** + * @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 Allocator::template rebind > >::other> 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(std::size_t i) + { + DUNE_UNUSED_PARAMETER(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, + int i) const + { + DUNE_UNUSED_PARAMETER(i); + 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, !handle.fixedsize()); +} + + +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(handle.fixedsize()) + ++fixedsize; + + + typedef typename InterfaceMap::const_iterator IIter; + for(IIter inf=interface_->begin(), end=interface_->end(); inf!=end; ++inf) + { + + if(handle.fixedsize() && InterfaceInformationChooser::getSend(inf->second).size()) + fixedsize=handle.size(InterfaceInformationChooser::getSend(inf->second)[0]); + assert(!handle.fixedsize()||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(handle.fixedsize()) + 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..745d9e1 --- /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)); + } + 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..2030950 --- /dev/null +++ b/dune/common/parametertreeparser.cc @@ -0,0 +1,244 @@ +// -*- 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(); +} + + + +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. + * + * \note This method is identical to parseStream(std::istream&, + * const std::string&, bool) with the exception that that + * method allows one to give a custom name for the stream. + */ + static void readINITree(std::istream& in, ParameterTree& pt, + bool overwrite); + + + /** \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 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..3946bbf --- /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 "lcm.hh" +#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 = Lcm::value, + + /** + * @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..fa5838d --- /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 */ + enum { 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..5ddf19a --- /dev/null +++ b/dune/common/quadmath.hh @@ -0,0 +1,446 @@ +// -*- 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)}; \ + } \ + 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..049a315 --- /dev/null +++ b/dune/common/rangeutilities.hh @@ -0,0 +1,747 @@ +// -*- 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 + */ + +/** + * @addtogroup RangeUtilities + * @{ + */ + +namespace Dune +{ + /** + \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 {}; + } + + + + 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 + { + public: + using iterator_category = std::forward_iterator_tag; + using reference = decltype(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 (*f_)(*it_); + } + + // Dereferencing returns a value created by the function + pointer operator->() const noexcept { + return (*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 (*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 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 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. + **/ + 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) + {} + + /** + * \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 value is transformed on-the-fly using a given + * transformation function leaving the underlying range + * unchanged. + * + * 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 transformedRangeView(R&& range, const F& f) + { + return TransformedRangeView(std::forward(range), f); + } + +} + +/** + * @} + */ + +#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..48f8e55 --- /dev/null +++ b/dune/common/scalarmatrixview.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_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 ( size_type i ) + { + DUNE_UNUSED_PARAMETER(i); + DUNE_ASSERT_BOUNDS(i == 0); + return data_; + } + + const_row_reference mat_access ( size_type i ) const + { + DUNE_UNUSED_PARAMETER(i); + 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..12a3bf2 --- /dev/null +++ b/dune/common/scalarvectorview.hh @@ -0,0 +1,212 @@ +// -*- 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[] (size_type i) + { + DUNE_UNUSED_PARAMETER(i); + DUNE_ASSERT_BOUNDS(i == 0); + return *dataP_; + } + + /** \brief Const random access operator, actually disregards its argument */ + const K& operator[] (size_type i) const + { + DUNE_UNUSED_PARAMETER(i); + 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..1b35c55 --- /dev/null +++ b/dune/common/shared_ptr.hh @@ -0,0 +1,128 @@ +// -*- 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 the class shared_ptr (a reference counting + * pointer), for those systems that don't have it in the standard library. + * @author Markus Blatt + */ +namespace Dune +{ + // pull in default implementations + using std::shared_ptr; + using std::make_shared; + + /** + @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; + shared_ptr pi = stackobject_to_shared_ptr(i); + @endcode + 2) Convert a stack-allocated object to a shared_ptr of a base class + @code + class A {}; + class B : public A {}; + + ... + + B b; + 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; + shared_ptr pi = stackobject_to_shared_ptr(i); + @endcode + The @c 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 shared_ptr, null_deleter + */ + template + inline shared_ptr stackobject_to_shared_ptr(T & t) + { + return 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 shared_ptr + * + * This will store a pointer for the passed reference + * in a non-owning 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..4eb2f4b --- /dev/null +++ b/dune/common/simd/loop.hh @@ -0,0 +1,558 @@ +#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. + */ + + template + class LoopSIMD : public std::array { + + public: + + //default constructor + LoopSIMD() {} + + // broadcast constructor initializing the content with a given value + LoopSIMD(T i) : LoopSIMD() { + this->fill(i); + } + + /* + * 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 T 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 T s) { \ + LoopSIMD out; \ + for(std::size_t i=0; i \ + auto operator SYMBOL(const T 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) { \ + LoopSIMD out; \ + for(std::size_t i=0; i \ + auto operator SYMBOL(const T 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); + 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 T s) { \ + LoopSIMD out; \ + for(std::size_t i=0; i \ + auto operator SYMBOL(const bool 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 + std::ostream& operator<< (std::ostream &os, const LoopSIMD &v) { + os << "["; + for(std::size_t i=0; i + struct ScalarType> { + using type = T; + }; + + template + struct RebindType> { + using type = LoopSIMD; + }; + + //Implementation of SIMD-interface-functionality + template + struct LaneCount> : index_constant {}; + + template + T&& lane(ADLTag<5>, std::size_t l, LoopSIMD &&v) { + return std::move(v[l]); + } + + template + const T& lane(ADLTag<5>, std::size_t l, const LoopSIMD &v) { + return v[l]; + } + + template + T& lane(ADLTag<5>, std::size_t l, LoopSIMD &v) { + return v[l]; + } + + template + auto cond(ADLTag<5>, LoopSIMD mask, + LoopSIMD ifTrue, LoopSIMD ifFalse) { + LoopSIMD out; + for(std::size_t i=0; i + auto cond(ADLTag<5, std::is_same >::value>, M mask, + LoopSIMD()> ifTrue, + LoopSIMD()> ifFalse) + { + LoopSIMD()> out; + for(auto l : range(Simd::lanes(mask))) + out[l] = Simd::lane(l, mask) ? ifTrue[l] : ifFalse[l]; + 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> &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) { + LoopSIMD out; + for(auto l : range(S)) + out[l] = Dune::isNaN(v[l]); + return out; + } + + template + auto isInf(const LoopSIMD &v, PriorityTag<3>, ADLTag) { + LoopSIMD out; + for(auto l : range(S)) + out[l] = Dune::isInf(v[l]); + return out; + } + + template + auto isFinite(const LoopSIMD &v, PriorityTag<3>, ADLTag) { + LoopSIMD 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..7fd14f2 --- /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 l, V v) + { + return v; + } + + template + V &lane(ADLTag<3>, std::size_t l, 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..82a83e2 --- /dev/null +++ b/dune/common/simd/test.hh @@ -0,0 +1,2082 @@ +#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 +#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 + DUNE_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"); + T DUNE_UNUSED a{}; + } + + template + DUNE_DEPRECATED_MSG("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{}, [=](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"); + + Hybrid::ifElse(RebindPrune{}, + [this](auto id) { + log_ << "Pruning check of Simd type " << className() + << std::endl; + }, + [=](auto id) { + using Impl::debugTypes; + static_assert(debugTypes(id(RebindAccept{})), + "Rebind is W, but that is not accepted " + "by RebindAccept"); + recurse(id(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 + constexpr auto DUNE_UNUSED size = lanes(); + // but the result of lanes(vec) does not need to be constexpr + DUNE_SIMD_CHECK(lanes() == lanes(V{})); + } + + template + void checkDefaultConstruct() + { + { V DUNE_UNUSED vec; } + { V DUNE_UNUSED vec{}; } + { V DUNE_UNUSED 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) + { + Hybrid::ifElse(std::integral_constant{}, + [=] (auto id) { + Hybrid::forEach(id(TypeList{}), + [=] (auto t1) { + Hybrid::forEach(id(TypeList{}), + [=] (auto t2) { id(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. + * + * @{ + */ + 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 = Std::to_true_type> + 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(); + } + + //! run unit tests for simd vector type V + /** + * This function will also ensure that `checkVector>()` + * (for any `R` in `Rebinds`) is run. No test will be run twice for a + * given type. + * + * \tparam Rebinds A list of types, usually in the form of a `TypeList`. + * \tparam Prune A type predicate determining whether to run + * `checkVector()` for types obtained from `Rebinds`. + * + * \deprecated Rather than calling this function, call `check()` with + * the same template arguments. Rather than explicitly + * instantiating this function as described below, + * explicitly instantiate `checkType()` and friends. + * + * \note As an implementor of a unit test, you are encouraged to + * explicitly instantiate this function in separate compilation + * units for the types you are testing. Look at `standardtest.cc` + * for how to do this. + * + * 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 is to explicitly instantiate \c checkVector() 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 is to define \c checkVector() outside of the + * class. I have no idea why this helps, but it makes 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 class Prune = IsLoop> + DUNE_DEPRECATED_MSG("Call check() instead, and explicitly instantiate " + "checkType() and friends instead") + void checkVector(); + + //! 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(); + Hybrid::ifElse(isMask, + [this](auto id) { id(this)->template checkBroadcastMaskConstruct(); }, + [this](auto id) { id(this)->template checkBroadcastVectorConstruct(); }); + checkBracedAssign(); + checkBracedBroadcastAssign(); + + checkAutoCopy(); + checkCond(); + checkBoolCond(); + + Hybrid::ifElse(isMask, + [this](auto id) { id(this)->template checkBoolReductions(); }); + // checkBoolReductions() is not applicable for non-masks + + checkHorizontalMinMax(); + checkBinaryMinMax(); + checkIO(); + } + template void UnitTest::checkUnaryOps() + { + auto checkMask = [=](auto id) { + auto check = [=](auto op) { + id(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{}); + }; + + auto checkVector = [=](auto id) { + auto check = [=](auto op) { + id(this)->template checkUnaryOpsV(op); + }; + + // postfix + // check(OpPostfixDecrement{}); + // check(OpPostfixIncrement{}); + + // prefix + // check(OpPrefixDecrement{}); + // check(OpPrefixIncrement{}); + + // check(OpPrefixPlus{}); + check(OpPrefixMinus{}); + check(OpPrefixLogicNot{}); + check(OpPrefixBitNot{}); + }; + + Hybrid::ifElse(std::is_same, bool>{}, checkMask, checkVector); + } + template void UnitTest::checkBinaryOps() + { + checkBinaryOpsVectorVector(); + checkBinaryOpsScalarVector(); + checkBinaryOpsVectorScalar(); + checkBinaryOpsProxyVector(); + checkBinaryOpsVectorProxy(); + } + template void UnitTest::checkBinaryOpsVectorVector() + { + auto checker = [=](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [=](auto t1, auto t2) { + this->checkBinaryOpVV(t1, t2, op); + }; + this->checkBinaryRefQual(check); + }; + checkBinaryOps(checker); + } + template void UnitTest::checkBinaryOpsScalarVector() + { + auto checker = [=](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [=](auto t1, auto t2) { + this->checkBinaryOpSV(t1, t2, op); + }; + this->checkBinaryRefQual, V, doSV>(check); + + auto crossCheck = [=](auto t1, auto t2) { + this->checkBinaryOpVVAgainstSV(t1, t2, op); + }; + this->checkBinaryRefQual, V, doSV && doVV>(crossCheck); + }; + checkBinaryOps(checker); + } + template void UnitTest::checkBinaryOpsVectorScalar() + { + auto checker = [=](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [=](auto t1, auto t2) { + this->checkBinaryOpVS(t1, t2, op); + }; + this->checkBinaryRefQual, doVS>(check); + + auto crossCheck = [=](auto t1, auto t2) { + this->checkBinaryOpVVAgainstVS(t1, t2, op); + }; + this->checkBinaryRefQual, doVV && doVS>(crossCheck); + }; + checkBinaryOps(checker); + } + template void UnitTest::checkBinaryOpsProxyVector() + { + auto checker = [=](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [=](auto t1, auto t2) { + this->checkBinaryOpPV(t1, t2, op); + }; + this->checkBinaryRefQual(check); + }; + checkBinaryOps(checker); + } + template void UnitTest::checkBinaryOpsVectorProxy() + { + auto checker = [=](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [=](auto t1, auto t2) { + this->checkBinaryOpVP(t1, t2, op); + }; + this->checkBinaryRefQual(check); + }; + checkBinaryOps(checker); + } + + // Needs to be defined outside of the class to bring memory consumption + // during compilation down to an acceptable level. + template class Prune> + void UnitTest::checkVector() + { + // 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 checkVector(); + }; + checkRebindOf(recurse); + + checkType(); + } + + } // 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..ded25be --- /dev/null +++ b/dune/common/simd/test/CMakeLists.txt @@ -0,0 +1,115 @@ +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" + wchar_t char16_t char32_t + 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() + +dune_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" + wchar_t char16_t char32_t + 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() + +dune_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() +dune_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() +dune_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..ec142f2 --- /dev/null +++ b/dune/common/simd/test/looptest.cc.in @@ -0,0 +1,37 @@ +// @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 {}; +#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::Std::to_false_type, 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..4ca479a --- /dev/null +++ b/dune/common/simd/test/looptest.hh.in @@ -0,0 +1,22 @@ +// @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@ >(); +#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..933ae21 --- /dev/null +++ b/dune/common/simd/test/looptest_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/standardtest.cc.in b/dune/common/simd/test/standardtest.cc.in new file mode 100644 index 0000000..6b5821b --- /dev/null +++ b/dune/common/simd/test/standardtest.cc.in @@ -0,0 +1,33 @@ +// @GENERATED_SOURCE@ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#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::Std::to_false_type, 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..1dc4b19 --- /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. + */ + typedef typename A::template rebind::other Allocator; + + /** + * @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, 0); + 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, 0); + + // Use copy constructor to initialize memory + allocator_.construct(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_; + allocator_.destroy(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..40004d3 --- /dev/null +++ b/dune/common/std/CMakeLists.txt @@ -0,0 +1,10 @@ +install( + FILES + apply.hh + make_array.hh + memory.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..aa0b629 --- /dev/null +++ b/dune/common/std/apply.hh @@ -0,0 +1,57 @@ +// -*- 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 + +#if DUNE_HAVE_CXX_APPLY + #include +#elif DUNE_HAVE_CXX_EXPERIMENTAL_APPLY + #include +#else + #include + #include + #include +#endif + +#include + + +namespace Dune +{ + + namespace Std + { + +#if DUNE_HAVE_CXX_APPLY + + using std::apply; + +#elif DUNE_HAVE_CXX_EXPERIMENTAL_APPLY + + using std::experimental::apply; + +#else + + /** + * \brief Apply function with arguments given as tuple + * + * \param f A callable object + * \param args Tuple of arguments + * + * This will call the function with arguments generated by unpacking the tuple. + * + * \ingroup CxxUtilities + */ + template + decltype(auto) apply(F&& f, ArgTuple&& args) + { + auto indices = std::make_index_sequence>::value>(); + return applyPartial(std::forward(f), std::forward(args), indices); + } + +#endif + + } // 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..afe5418 --- /dev/null +++ b/dune/common/std/make_array.hh @@ -0,0 +1,47 @@ +#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 + + 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 + */ + 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/memory.hh b/dune/common/std/memory.hh new file mode 100644 index 0000000..deb9f11 --- /dev/null +++ b/dune/common/std/memory.hh @@ -0,0 +1,22 @@ +// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set ts=8 sw=2 et sts=2: +#ifndef DUNE_COMMON_STD_MEMORY_HH +#define DUNE_COMMON_STD_MEMORY_HH + +#warning dune/common/std/memory.hh is deprecated; use std::make_unique from instead + +#include + +namespace Dune +{ + + namespace Std + { + + using std::make_unique; + + } // namespace Std + +} // namespace Dune + +#endif // #ifndef DUNE_COMMON_STD_MEMORY_HH diff --git a/dune/common/std/optional.hh b/dune/common/std/optional.hh new file mode 100644 index 0000000..ed5925a --- /dev/null +++ b/dune/common/std/optional.hh @@ -0,0 +1,493 @@ +#ifndef DUNE_COMMON_STD_OPTIONAL_HH +#define DUNE_COMMON_STD_OPTIONAL_HH + +#include +#include +#include +#include +#include + +#ifdef DUNE_HAVE_CXX_OPTIONAL +#include +#endif // #ifdef DUNE_HAVE_CXX_OPTIONAL + + +namespace Dune +{ + + namespace Std + { + +#ifdef DUNE_HAVE_CXX_OPTIONAL + // 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; + +#else + // In case of C++ standard < 17 we take the fallback implementation + + // nullopt + // ------- + + struct nullopt_t {}; + + namespace + { + + const nullopt_t nullopt = {}; + + } // anonymous namespace + + + + // in_place + // -------- + + struct in_place_t {}; + + namespace + { + + const in_place_t in_place = {}; + + } // anonymous namespace + + + + + // bad_optional_access + // ------------------- + + class bad_optional_access + : public std::logic_error + { + public: + explicit bad_optional_access ( const std::string &what ) : std::logic_error( what ) {} + explicit bad_optional_access ( const char *what ) : std::logic_error( what ) {} + }; + + + + + // optional + // -------- + + /** + * \class optional + */ + template< class T > + class optional + { + public: + /** \brief type of value */ + typedef T value_type; + + /** + * \name Construction + * \{ + */ + + constexpr optional () noexcept : engaged_( false ) {} + + constexpr optional ( nullopt_t ) noexcept : engaged_( false ) {} + + template< class U = value_type, + std::enable_if_t< std::is_constructible< value_type, U&& >::value, int > = 0, + std::enable_if_t< !std::is_convertible< U&&, value_type >::value, int > = 0 > + explicit constexpr optional ( U && value ) + : engaged_( true ), value_( std::forward< U >( value ) ) + {} + + template< class U = value_type, + std::enable_if_t< std::is_constructible< value_type, U&& >::value, int > = 0, + std::enable_if_t< not(std::is_same, optional>::value), int > = 0, + std::enable_if_t< not(std::is_same, in_place_t>::value), int > = 0 + > + constexpr optional ( U && value ) + : engaged_( true ), value_( std::forward< U >( value ) ) + {} + + optional ( const value_type &value ) : engaged_( true ), value_( value ) {} + optional ( value_type &&value ) : engaged_( true ), value_( std::move( value ) ) {} + + template< class... Args > + explicit constexpr optional ( in_place_t, Args &&... args ) + : engaged_( true ), value_( std::forward< Args >( args )... ) + {} + + /** \} */ + + /** + * \name Copying and Assignment + * \{ + */ + + optional ( const optional &other ) noexcept( std::is_nothrow_copy_constructible< T >::value ) + : engaged_( other.engaged_ ) + { + if( engaged_ ) + rawEmplace(other.value_); + } + + optional ( optional &&other ) noexcept( std::is_nothrow_move_constructible< T >::value ) + : engaged_( other.engaged_ ) + { + if( engaged_ ) + rawEmplace(std::move( other.value_ )); + } + + template< class U, + std::enable_if_t< std::is_constructible< value_type, const U& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< value_type, optional< U >& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< value_type, const optional< U >& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< value_type, optional< U >&& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< optional< U >&, value_type >::value, int > = 0, + std::enable_if_t< !std::is_constructible< const optional< U >&, value_type >::value, int > = 0, + std::enable_if_t< !std::is_constructible< optional< U >&&, value_type >::value, int > = 0, + std::enable_if_t< !std::is_convertible< const U&, value_type >::value, int > = 0 > + explicit optional ( const optional< U > &other ) + : engaged_( other.engaged_ ) + { + if( engaged_ ) + rawEmplace(other.value_); + } + + template< class U, + std::enable_if_t< std::is_constructible< value_type, const U& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< value_type, optional< U >& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< value_type, const optional< U >& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< value_type, optional< U >&& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< optional< U >&, value_type >::value, int > = 0, + std::enable_if_t< !std::is_constructible< const optional< U >&, value_type >::value, int > = 0, + std::enable_if_t< !std::is_constructible< optional< U >&&, value_type >::value, int > = 0, + std::enable_if_t< std::is_convertible< const U&, value_type >::value, int > = 0 > + optional ( const optional< U > &other ) + : engaged_( other.engaged_ ) + { + if( engaged_ ) + rawEmplace(other.value_); + } + + template< class U, + std::enable_if_t< std::is_constructible< value_type, const U& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< value_type, optional< U >& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< value_type, const optional< U >& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< value_type, optional< U >&& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< optional< U >&, value_type >::value, int > = 0, + std::enable_if_t< !std::is_constructible< const optional< U >&, value_type >::value, int > = 0, + std::enable_if_t< !std::is_constructible< optional< U >&&, value_type >::value, int > = 0, + std::enable_if_t< !std::is_convertible< const U&, value_type >::value, int > = 0 > + explicit optional ( optional< U > &&other ) + : engaged_( other.engaged_ ) + { + if( engaged_ ) + rawEmplace(std::move(other.value_)); + } + + template< class U, + std::enable_if_t< std::is_constructible< value_type, const U& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< value_type, optional< U >& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< value_type, const optional< U >& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< value_type, optional< U >&& >::value, int > = 0, + std::enable_if_t< !std::is_constructible< optional< U >&, value_type >::value, int > = 0, + std::enable_if_t< !std::is_constructible< const optional< U >&, value_type >::value, int > = 0, + std::enable_if_t< !std::is_constructible< optional< U >&&, value_type >::value, int > = 0, + std::enable_if_t< std::is_convertible< const U&, value_type >::value, int > = 0 > + optional ( optional< U > &&other ) + : engaged_( other.engaged_ ) + { + if( engaged_ ) + rawEmplace(std::move(other.value_)); + } + + optional &operator= ( nullopt_t ) noexcept + { + if( engaged_ ) + value_.~value_type(); + engaged_ = false; + return *this; + } + + optional &operator= ( const optional &other ) noexcept( std::is_nothrow_copy_constructible< T >::value && std::is_nothrow_copy_assignable< T >::value ) + { + if( engaged_ ) + { + if( other.engaged_ ) + value_ = other.value_; + else + value_.~value_type(); + } + else if( other.engaged_ ) + rawEmplace(other.value_); + engaged_ = other.engaged_; + return *this; + } + + optional &operator= ( optional &&other ) noexcept( std::is_nothrow_move_constructible< T >::value && std::is_nothrow_move_assignable< T >::value ) + { + if( engaged_ ) + { + if( other.engaged_ ) + value_ = std::move( other.value_ ); + else + value_.~value_type(); + } + else if( other.engaged_ ) + rawEmplace(std::move(other.value_)); + engaged_ = other.engaged_; + return *this; + } + + template< class U = value_type > + typename std::enable_if< std::is_constructible< value_type, U >::value && std::is_assignable< value_type, U >::value, optional & >::type + operator= ( U &&value ) + { + if( engaged_ ) + value_ = std::forward( value ); + else + rawEmplace(std::forward(value)); + engaged_ = true; + return *this; + } + + /** \} */ + + ~optional () + { + if( engaged_ ) + value_.~value_type(); + } + + /** + * \name Observers + * \{ + */ + + /** \brief return \b true if optional is engaged, \b false otherwise */ + explicit constexpr operator bool () const noexcept { return engaged_; } + + /** \brief dereference pointer */ + const value_type &operator* () const noexcept { assert( engaged_ ); return value_; } + /** \brief dereference pointer */ + value_type &operator* () noexcept { assert( engaged_ ); return value_; } + + /** \brief pointer operator */ + const value_type *operator->() const noexcept { assert( engaged_ ); return &value_; } + /** \brief pointer operator */ + value_type *operator->() noexcept { assert( engaged_ ); return &value_; } + + const value_type &value () const + { + if( engaged_ ) + return value_; + else + throw bad_optional_access( "Cannot access value of disengaged optional." ); + } + + value_type &value () + { + if( engaged_ ) + return value_; + else + throw bad_optional_access( "Cannot access value of disengaged optional." ); + } + + template< class U > + value_type value_or ( U &&value ) const + { + return (engaged_ ? value_ : static_cast< value_type >( std::forward< U >( value ) )); + } + + /** \} */ + + /** + * \name Modifiers + * \{ + */ + + template< class... Args > + void emplace ( Args &&... args ) + { + *this = nullopt; + // note: At this point, the optional is disengaged. If the following + // constructor throws, the object is left in a disengaged state. + rawEmplace(std::forward(args)...); + engaged_ = true; + } + + void reset () noexcept + { + if( engaged_) + { + value_.~value_type(); + engaged_ = false; + } + } + + void swap ( optional &other ) noexcept( std::is_nothrow_move_constructible< T >::value && noexcept( std::swap( std::declval< T & >(), std::declval< T & >() ) ) ) + { + std::swap( engaged_, other.engaged_ ); + if( engaged_) + { + if( other.engaged_ ) + std::swap( value_, other.value_ ); + else + { + rawEmplace( std::move( other.value_ ) ); + other.value_.~value_type(); + } + } + else if( other.engaged_ ) + { + other.rawEmplace( std::move( value_ ) ); + value_.~value_type(); + } + } + + /** \} */ + + private: + + // Construct value using placement new. This is needed + // for all assignments and conditional constructions + // whenever the original object cannot be assigned. + template + void rawEmplace(Args&&... args) + { + // To access the raw storage we need to cast away constness first. + // Otherwise placement new is not allowed. + new( const_cast*>(&value_) ) value_type( std::forward< Args >( args )... ); + } + + bool engaged_; + union { value_type value_; }; + }; + + + + // Relatonal Operators for optional + // -------------------------------- + + template< class T > + inline static constexpr bool operator== ( const optional< T > &lhs, const optional< T > &rhs ) + { + return (lhs && rhs ? *lhs == *rhs : static_cast< bool >( lhs ) == static_cast< bool >( rhs )); + } + + + template< class T > + inline static constexpr bool operator< ( const optional< T > &lhs, const optional< T > &rhs ) + { + return (rhs && (lhs ? std::less< T >()( *lhs, *rhs ) : true)); + } + + + template< class T > + inline static constexpr bool operator== ( const optional< T > &lhs, nullopt_t ) noexcept + { + return !lhs; + } + + template< class T > + inline static constexpr bool operator== ( nullopt_t, const optional< T > &rhs ) noexcept + { + return !rhs; + } + + template< class T > + inline static constexpr bool operator< ( const optional< T > &lhs, nullopt_t ) noexcept + { + return false; + } + + template< class T > + inline static constexpr bool operator< ( nullopt_t, const optional< T > &rhs ) noexcept + { + return static_cast< bool >( rhs ); + } + + template< class T > + inline static constexpr bool operator== ( const optional< T > &lhs, const T &rhs ) + { + return (lhs && (*lhs == rhs)); + } + + template< class T > + inline static constexpr bool operator== ( const T &lhs, const optional< T > &rhs ) + { + return (rhs && (lhs == *rhs)); + } + + template< class T > + inline static constexpr bool operator< ( const optional< T > &lhs, const T &rhs ) + { + return (lhs ? std::less< T >()( *lhs, rhs ) : true); + } + + template< class T > + inline static constexpr bool operator< ( const T &lhs, const optional< T > &rhs ) + { + return (rhs ? std::less< T >()( lhs, *rhs ) : false); + } + + + + // make_optional + // ------------- + + template< class T > + inline static constexpr optional< typename std::decay< T >::type > make_optional ( T &&value ) + { + return optional< typename std::decay< T >::type >( std::forward< T >( value ) ); + } + +#endif //#ifdef DUNE_HAVE_CXX_OPTIONAL + + } // namespace Std + +} // namespace Dune + + +#ifndef DUNE_HAVE_CXX_OPTIONAL +namespace std +{ + + // swap for optional + // ----------------- + + template< class T > + inline static void swap ( Dune::Std::optional< T > &lhs, Dune::Std::optional< T > &rhs ) noexcept( noexcept( lhs.swap( rhs ) ) ) + { + lhs.swap( rhs ); + } + + + + // hash for optional + // ----------------- + + template< class T > + struct hash< Dune::Std::optional< T > > + { + std::size_t operator() ( const Dune::Std::optional< T > &arg ) const + { + return (arg ? std::hash< T >()( arg ) : 0); + } + }; + +} // namespace std + +#endif //#ifndef DUNE_HAVE_CXX_OPTIONAL + +#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..7c32c76 --- /dev/null +++ b/dune/common/std/type_traits.hh @@ -0,0 +1,510 @@ +// -*- 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 DUNE_HAVE_HEADER_EXPERIMENTAL_TYPE_TRAITS +#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 + * + * \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 to_false_type : public std::false_type {}; + + + + // to_true_type + // ------------ + + /** \class to_true_type + * + * \brief template mapping a type to std::true_type + * + * \tparam T Some type + * + * \note This class exists mostly for consistency with to_false_type. + * + * \ingroup CxxUtilities + */ + template< typename T > + struct to_true_type : public std::true_type {}; + + +#if DUNE_HAVE_CXX_BOOL_CONSTANT + + using std::bool_constant; + +#elif DUNE_HAVE_CXX_EXPERIMENTAL_BOOL_CONSTANT + + using std::experimental::bool_constant; + +#else + + /** + * \brief A template alias for std::integral_constant + * + * \tparam value Boolean value to encode as std::integral_constant + * + * \ingroup CxxUtilities + */ + template + using bool_constant = std::integral_constant; + +#endif + + + 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 + * + * \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 + * + * \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< F(Args...), R> : + decltype(Impl::is_callable_helper(PriorityTag<42>())) + {}; + + + /** + * \brief Traits class to check if function is invocable + * + * \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 is_invocable : + decltype(Impl::is_callable_helper(PriorityTag<42>())) + {}; + + /** + * \brief Traits class to check if function is invocable and the return type is compatible + * + * \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 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 conjunction; + + template<> + struct conjunction<> + : std::true_type + {}; + + template< class B > + struct conjunction< B > + : B + {}; + + template< class B1, class... Bn > + struct conjunction< B1, Bn... > + : std::conditional_t< static_cast< bool >( B1::value ), conjunction< Bn... >, B1 > + {}; + + + + // 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 disjunction; + + template<> + struct disjunction<> + : std::false_type + {}; + + template< class B > + struct disjunction< B > + : B + {}; + + template< class B1, class... Bn > + struct disjunction< B1, Bn... > + : std::conditional_t< static_cast< bool >( B1::value ), B1, disjunction< Bn... > > + {}; + + // 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 negation : public bool_constant(B::value)> + {}; + +} // 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..7a349ba --- /dev/null +++ b/dune/common/std/utility.hh @@ -0,0 +1,46 @@ +#ifndef DUNE_COMMON_STD_UTILITY_HH +#define DUNE_COMMON_STD_UTILITY_HH + +#include + +#include +#include + +#include + +namespace Dune +{ + + namespace Std + { + + + using std::integer_sequence; + using std::index_sequence; + using std::make_integer_sequence; + using std::make_index_sequence; + + /** + * \brief Create index_sequence from 0 to sizeof...(T)-1 + * + * This should do the same as std::index_sequence_for. + * But due to a bug in the sizeof... operator this + * may produce wrong results with clang<3.8. + * + * As a workaround we provide our own implementation + * that avoids this bug even if the std:: version + * exists. + * + * This implemenation can be dropped, once we require + * a minimum clang version that has this bug fixed (i.e. >=3.8). + */ + template + using index_sequence_for = make_index_sequence{}>; + + + + } // 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..c6d4fd8 --- /dev/null +++ b/dune/common/std/variant.hh @@ -0,0 +1,560 @@ +// -*- 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 +#ifdef DUNE_HAVE_CXX_VARIANT +#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; +} +} +#else +#include +#include +#include +#include +#include +#include + +namespace Dune { +namespace Std { +namespace Impl { + + // indicator value if something's not yet (or not any longer) valid + constexpr const auto invalidIndex = std::numeric_limits::max(); + + /* helper constructs to find position of a type T in a pack Ts... */ + template + struct index_in_pack; + + template + struct index_in_pack : std::integral_constant {}; + + template + struct index_in_pack : std::integral_constant::value> {}; + + /* end helper constructs to find position of a type T in a pack Ts... */ + + template + struct TypeStorage { + + using Buffer = std::aligned_storage_t; + + // We only allow construction of empty TypeStorage + // objects and no asignment. Actual construction + // and assignment of stored values is done using + // special methods. + TypeStorage() = default; + + TypeStorage(const TypeStorage&) = delete; + TypeStorage(TypeStorage&&) = delete; + TypeStorage& operator=(const TypeStorage&) = delete; + TypeStorage& operator=(TypeStorage&&) = delete; + + void construct(const T& t) { + ::new (&buffer_) T(t); + } + + void construct(T&& t) { + ::new (&buffer_) T(std::move(t)); + } + + void assign(const T& t) { + this->get() = t; + } + + void assign(T&& t) { + this->get() = std::move(t); + } + + void destruct() { + reinterpret_cast(&buffer_)->~T(); + } + + auto& get() { + return *(reinterpret_cast(&buffer_)); + } + + const auto& get() const { + return *(reinterpret_cast(&buffer_)); + } + + private: + Buffer buffer_; + }; + + + + // A variadic union type providing access by index + // of member. + template + union VariadicUnion; + + // This is the recursion closure dummy. + // It's methods should never be called. + template<> + union VariadicUnion<> { + template + void construct(Ti&&) { assert(false); } + + template + void assign(Ti&&) { assert(false); } + + void destruct(std::size_t) { assert(false); }; + + }; + + template + union VariadicUnion + { + // We only allow construction of empty VariadicUnion + // objects and no asignment. Actual construction + // and assignment of stored values is done using + // special methods. + constexpr VariadicUnion() = default; + + VariadicUnion(const VariadicUnion& other) = delete; + VariadicUnion(VariadicUnion&& other) = delete; + VariadicUnion& operator=(const VariadicUnion& other) = delete; + VariadicUnion& operator=(VariadicUnion&& other) = delete; + + // Construct stored object + void construct(const Head& obj) { + new (&head_) TypeStorage; + head_.construct(obj); + } + + void construct(Head&& obj) { + new (&head_) TypeStorage; + head_.construct(std::move(obj)); + } + + template, Head>::value, int> = 0> + void construct(Ti&& obj) { + new (&tail_) VariadicUnion; + tail_.construct(std::forward(obj)); + } + + // Assign to stored object. This should + // only be called if it's clear, that + // the VariadicUnion already stores + // on object of the passed type. + void assign(const Head& obj){ + head_.assign(obj); + } + + void assign(Head&& obj){ + head_.assign(std::move(obj)); + } + + template, Head>::value, int> = 0> + void assign(Ti&& obj){ + tail_.assign(std::forward(obj)); + } + + // Destruct stored object. This should only + // be called with the appropriate index of + // the stored object. + void destruct(size_t indexToReset) { + if (indexToReset == 0) { + head_.destruct(); + return; + } + else + tail_.destruct(indexToReset-1); + } + + // Access to stored object + auto& getByIndex(std::integral_constant) { + return head_.get(); + } + + const auto& getByIndex(std::integral_constant) const { + return head_.get(); + } + + template + auto& getByIndex(std::integral_constant) { + return tail_.getByIndex(std::integral_constant()); + } + + template + const auto& getByIndex(std::integral_constant) const { + return tail_.getByIndex(std::integral_constant()); + } + + constexpr size_t size() const { + return sizeof...(Tail)+1; + } + + private: + TypeStorage head_; + VariadicUnion tail_; + }; + + template + struct variant_{ + + // Compute index of Ti in T... + template + constexpr static auto typeIndex() + { + return index_in_pack, T...>::value; + } + + // Create static index range for iterating over T... + constexpr static auto indexRange() + { + return Dune::range(Dune::index_constant()); + } + + constexpr void destructIfValid() + { + if (index_ != invalidIndex) + unions_.destruct(index_); + index_ = invalidIndex; + } + + // All methods will only use the default constructor but + // no other constructors or assignment operators of VariadicUnion. + // The construction and assignment of stored values is done + // using special methods. + + // Default constructor. + // Default construct T_0 if possible, otherwise set to invalid state + constexpr variant_() : + index_(invalidIndex) + { + using T0 = TypeListEntry_t<0, TypeList>; + Dune::Hybrid::ifElse(std::is_default_constructible(), + [&](auto&& id) { + unions_.construct(id(T0{})); + index_ = 0; + }); + } + + // Construct from some Ti + template = 0> + constexpr variant_(Ti&& obj) : + index_(typeIndex()) + { + unions_.construct(std::forward(obj)); + } + + // Copy constructor + variant_(const variant_& other) { + index_ = other.index_; + if (index_==invalidIndex) + return; + Dune::Hybrid::forEach(indexRange(), [&](auto i) { + if(i==index_) + unions_.construct(other.template get()); + }); + } + + // Move constructor + variant_(variant_&& other) { + index_ = other.index_; + if (index_==invalidIndex) + return; + Dune::Hybrid::forEach(indexRange(), [&](auto i) { + if(i==index_) + unions_.construct(std::move(other.template get())); + }); + other.destructIfValid(); + } + + // Copy assignment operator + variant_& operator=(const variant_& other) { + if(index_ == other.index_) { + if (index_ != invalidIndex) + Dune::Hybrid::forEach(indexRange(), [&](auto i) { + if(i==index_) + unions_.assign(other.template get()); + }); + } + else { + destructIfValid(); + index_ = other.index_; + if (index_ != invalidIndex) + Dune::Hybrid::forEach(indexRange(), [&](auto i) { + if(i==index_) + unions_.construct(other.template get()); + }); + } + return *this; + } + + // Move assignment operator + variant_& operator=(variant_&& other) { + if(index_ == other.index_) { + if (index_ != invalidIndex) + Dune::Hybrid::forEach(indexRange(), [&](auto i) { + if(i==index_) + unions_.assign(std::move(other.template get())); + }); + } + else { + destructIfValid(); + index_ = other.index_; + if (index_ != invalidIndex) + Dune::Hybrid::forEach(indexRange(), [&](auto i) { + if(i==index_) + unions_.construct(std::move(other.template get())); + }); + } + other.destructIfValid(); + return *this; + } + + // Assignment from some Ti + template = 0> + constexpr variant_& operator=(Ti&& obj) { + constexpr auto newIndex = typeIndex(); + if (index_ == newIndex) + unions_.assign(std::forward(obj)); + else + { + destructIfValid(); + index_ = newIndex; + unions_.construct(std::forward(obj)); + } + return *this; + } + + template + auto& get() { + constexpr auto idx = typeIndex(); + if (index_ != idx) + DUNE_THROW(Dune::Exception, "Bad variant access."); + + return get(); + } + + template + const auto& get() const { + constexpr auto idx = typeIndex(); + if (index_ != idx) + DUNE_THROW(Dune::Exception, "Bad variant access."); + + return get(); + } + + template + Tp* get_if() { + if (not holds_alternative()) + return (Tp*) nullptr; + else + return &(get()); + } + + template + const Tp* get_if() const { + if (not holds_alternative()) + return (Tp*) nullptr; + else + return &(get()); + } + + template + auto* get_if() { + using Tp = std::decay_t())>; + if (not holds_alternative()) + return (Tp*) nullptr; + else + return &(get()); + } + + template + const auto* get_if() const { + using Tp = std::decay_t())>; + if (not holds_alternative()) + return (Tp*) nullptr; + else + return &(get()); + } + + template + auto& get() { + if (index_ != N || index_ == invalidIndex) + DUNE_THROW(Dune::Exception, "Bad variant access."); + return unions_.template getByIndex(std::integral_constant()); + } + template + const auto& get() const { + if (index_ != N || index_ == invalidIndex) + DUNE_THROW(Dune::Exception, "Bad variant access."); + return unions_.template getByIndex(std::integral_constant()); + } + + constexpr std::size_t index() const noexcept { + return index_; + } + + constexpr auto size() const { + return sizeof...(T); + } + + ~variant_() { + destructIfValid(); + } + + /** \brief Apply visitor to the active variant. + * + * visit assumes that the result of + * func(T) has the same type for all types T + * in this variant. + */ + template + decltype(auto) visit(F&& func) { + auto dummyElseBranch = [&]() -> decltype(auto) { return func(this->get<0>());}; + auto indices = std::make_index_sequence{}; + return Hybrid::switchCases(indices, index(), [&](auto staticIndex) -> decltype(auto) { + return func(this->template get()); + }, dummyElseBranch); + } + + template + decltype(auto) visit(F&& func) const { + auto dummyElseBranch = [&]() -> decltype(auto) { return func(this->get<0>());}; + auto indices = std::make_index_sequence{}; + return Hybrid::switchCases(indices, index(), [&](auto staticIndex) -> decltype(auto) { + return func(this->template get()); + }, dummyElseBranch); + } + + /** \brief Check if a given type is the one that is currently active in the variant. */ + template + constexpr bool holds_alternative() const { + // I have no idea how this could be really constexpr, but for STL-conformity, + // I'll leave the modifier there. + return (typeIndex() == index_); + } + + /** \brief Check if a given type is the one that is currently active in the variant. */ + template + constexpr bool holds_alternative() const { + // I have no idea how this could be really constexpr, but for STL-conformity, + // I'll leave the modifier there. + return (N == index_); + } + + private: + VariadicUnion unions_; + std::size_t index_; + constexpr static auto size_ = sizeof...(T); + }; + +} // end namespace Impl + + /** \brief Incomplete re-implementation of C++17's std::variant. */ + template + using variant = Impl::variant_; + + template + auto& get(variant& var) { + return var.template get(); + } + + template + const auto& get(const variant& var) { + return var.template get(); + } + + template + decltype(auto) visit(F&& visitor, variant& var) { + return var.visit(std::forward(visitor)); + } + + template + decltype(auto) visit(F&& visitor, const variant& var) { + return var.visit(std::forward(visitor)); + } + + template + auto& get(variant& var) { + return var.template get(); + } + + template + const auto& get(const variant& var) { + return var.template get(); + } + + template + const auto* get_if(const variant* var) { + if (var == nullptr) + return (const Tp*) nullptr; + return var->template get_if(); + } + + template + auto* get_if(variant* var) { + if (var == nullptr) + return (Tp*) nullptr; + return var->template get_if(); + } + + template + const auto* get_if(const variant* var) { + using Tp = std::decay_ttemplate get())>; + if (var == nullptr) + return (const Tp*) nullptr; + return var->template get_if(); + } + + template + auto* get_if(variant* var) { + using Tp = std::decay_ttemplate get())>; + if (var == nullptr) + return (Tp*) nullptr; + return var->template get_if(); + } + + template + constexpr bool holds_alternative(const variant& var) { + return var.template holds_alternative(); + } + + template + struct variant_size {}; + + template + struct variant_size> + : std::integral_constant { }; + + // this cannot be inline (as it is in the STL) as this would need C++17 + template + constexpr std::size_t variant_size_v = variant_size::value; + + /** + * \brief Trial default constructible class + * + * This can be used to make Std::variant default-constructible. + */ + struct monostate {}; + + constexpr bool operator<(monostate, monostate) noexcept { return false; } + constexpr bool operator>(monostate, monostate) noexcept { return false; } + constexpr bool operator<=(monostate, monostate) noexcept { return true; } + constexpr bool operator>=(monostate, monostate) noexcept { return true; } + constexpr bool operator==(monostate, monostate) noexcept { return true; } + constexpr bool operator!=(monostate, monostate) noexcept { return false; } + + +} // end namespace Std +} // end namespace Dune +#endif +#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..3a922b8 --- /dev/null +++ b/dune/common/stdthread.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 DUNE_COMMON_STDTHREAD_HH +#define DUNE_COMMON_STDTHREAD_HH + +#include + +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 + static const bool DUNE_UNUSED 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..08a7b46 --- /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..412bf2c --- /dev/null +++ b/dune/common/test/arithmetictestsuite.hh @@ -0,0 +1,802 @@ +// -*- 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 +#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(Arithmetic arithmetic_tag) + { + DUNE_UNUSED_PARAMETER(arithmetic_tag); + T DUNE_UNUSED t0; + (void)T(); + T DUNE_UNUSED t1{}; + T DUNE_UNUSED 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..aa10b17 --- /dev/null +++ b/dune/common/test/assertandreturntest.cc @@ -0,0 +1,91 @@ +// -*- 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 +#include + + +struct Foo +{ + static constexpr auto lessAndReturn(int a, 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..6f2ecb9 --- /dev/null +++ b/dune/common/test/bitsetvectortest.cc @@ -0,0 +1,191 @@ +// -*- 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 + +#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){ + DUNE_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 + DUNE_UNUSED_PARAMETER(y == cbbf[2]); + DUNE_UNUSED_PARAMETER(y == bbf[3]); + DUNE_UNUSED_PARAMETER(y == x); + DUNE_UNUSED_PARAMETER(x == y); + DUNE_UNUSED_PARAMETER(x == z); + DUNE_UNUSED_PARAMETER(z == x); + DUNE_UNUSED_PARAMETER(z == y); + DUNE_UNUSED_PARAMETER(y == z); + + // inequality + DUNE_UNUSED_PARAMETER(y != cbbf[2]); + DUNE_UNUSED_PARAMETER(y != bbf[3]); + DUNE_UNUSED_PARAMETER(y != x); + DUNE_UNUSED_PARAMETER(x != y); + DUNE_UNUSED_PARAMETER(x != z); + DUNE_UNUSED_PARAMETER(z != x); + DUNE_UNUSED_PARAMETER(z != y); + DUNE_UNUSED_PARAMETER(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]; + value_type DUNE_UNUSED z; + reference y = bbf[4]; + + // assignement + x = y; + x = cbbf[1]; + + // equality + DUNE_UNUSED_PARAMETER(y == cbbf[2]); + DUNE_UNUSED_PARAMETER(y == bbf[3]); + DUNE_UNUSED_PARAMETER(y == x); + DUNE_UNUSED_PARAMETER(x == y); + + // inequality + DUNE_UNUSED_PARAMETER(y != cbbf[2]); + DUNE_UNUSED_PARAMETER(y != bbf[3]); + DUNE_UNUSED_PARAMETER(y != x); + DUNE_UNUSED_PARAMETER(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..7aa98a6 --- /dev/null +++ b/dune/common/test/boundscheckingoptest.cc @@ -0,0 +1,141 @@ +#include +#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}; + DUNE_UNUSED_PARAMETER(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}; + DUNE_UNUSED_PARAMETER(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}}; + DUNE_UNUSED_PARAMETER(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}}; + DUNE_UNUSED_PARAMETER(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..73b0b8a --- /dev/null +++ b/dune/common/test/boundscheckingtest.cc @@ -0,0 +1,300 @@ +#include + +#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}; + DUNE_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}; + DUNE_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}; + DUNE_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}}; + DUNE_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}}; + DUNE_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}}; + DUNE_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); + DUNE_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); + DUNE_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); + DUNE_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); + DUNE_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); + DUNE_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..6144ea0 --- /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 ) + { + DUNE_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 ) + DUNE_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 ) + { + DUNE_UNUSED bool exists = matrix.exists( i, j ); + DUNE_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 ) + DUNE_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..9f696bb --- /dev/null +++ b/dune/common/test/classnametest.cc @@ -0,0 +1,135 @@ +// -*- 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; + + return t.exit(); +} 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..e96ae25 --- /dev/null +++ b/dune/common/test/debugalignsimdtest.cc.in @@ -0,0 +1,37 @@ +// @GENERATED_SOURCE@ + +#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::Std::to_false_type, 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..4ea3a6c --- /dev/null +++ b/dune/common/test/densematrixassignmenttest.cc @@ -0,0 +1,391 @@ +#include "config.h" + +#define DUNE_CHECK_BOUNDS + +#include + +#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; + } + } + { + DUNE_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}); + { DUNE_UNUSED M const fieldT = diagM; } + { + M fieldT; + fieldT = diagM; + } + } + { + using M = Dune::DynamicMatrix; + Dune::DiagonalMatrix diagM({1, 2, 3}); + { DUNE_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 + DUNE_UNUSED M const fieldT = fieldMWrong11; + } +#endif +#ifdef FAILURE4 + { + // Should fail at compile-time + DUNE_UNUSED M const fieldT = fieldMWrong22; + } +#endif +#ifdef FAILURE5 + { + // Should fail at compile-time + DUNE_UNUSED M const fieldT = fieldMWrong33; + } +#endif + try { + // Should fail at run-time with RangeError + DUNE_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 + DUNE_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 + DUNE_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 + DUNE_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..bc93f50 --- /dev/null +++ b/dune/common/test/densevectortest.cc @@ -0,0 +1,59 @@ +// -*- 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 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..8713a91 --- /dev/null +++ b/dune/common/test/diagonalmatrixtest.cc @@ -0,0 +1,96 @@ +// -*- 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 + +#include "checkmatrixinterface.hh" + +using namespace Dune; + +template +void test_matrix() +{ + typedef typename DiagonalMatrix::size_type size_type DUNE_UNUSED; + + DiagonalMatrix A(1); + FieldVector f; + FieldVector v; + + // 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 + DUNE_UNUSED FieldMatrix AFM = FieldMatrix(A); + DUNE_UNUSED FieldMatrix AFM2 = A; + DUNE_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() +{ + DUNE_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..6f7ea8a --- /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 DUNE_UNUSED = A2; + + + DynamicMatrix B(n+1,n+1); + for(size_type i=0; i& Bref DUNE_UNUSED = B; + + DynamicMatrix C(n,n); + for(size_type i=0; i& Cref DUNE_UNUSED = 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..8425839 --- /dev/null +++ b/dune/common/test/dynvectortest.cc @@ -0,0 +1,81 @@ +// -*- 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 Dune::DynamicVector; + +template +void dynamicVectorTest(int d) { + ct a = 1; + DynamicVector v(d,1); + DynamicVector w(d,2); + DynamicVector z(d,2); + bool b DUNE_UNUSED; + + // 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..632e41f --- /dev/null +++ b/dune/common/test/eigenvaluestest.cc @@ -0,0 +1,134 @@ +// -*- 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 + +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"); + } + + 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; + FMatrixHelp::eigenValues(testMatrix, eigenValues); + + // 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"); + } + + // Make sure the eigenvalues are in ascending order + for (int j=0; j eigenValues[j+1] + 1e-10) + DUNE_THROW(MathError, "Values computed by FMatrixHelp::eigenValues are not in ascending order"); + } +} + +int main() +{ +#if HAVE_LAPACK + testRosserMatrix(); +#else + std::cout << "WARNING: eigenvaluetest needs LAPACK, test disabled" << std::endl; +#endif // HAVE_LAPACK + + testSymmetricFieldMatrix(); + testSymmetricFieldMatrix(); + + 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..397b17c --- /dev/null +++ b/dune/common/test/fmatrixtest.cc @@ -0,0 +1,946 @@ +// -*- 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 +#if HAVE_VC +#include +#endif +#include + +#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; + + // 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!"); + } + FM DUNE_UNUSED 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); + bool b DUNE_UNUSED; + + 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() +{ + DUNE_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..51da9c9 --- /dev/null +++ b/dune/common/test/functiontest.cc @@ -0,0 +1,39 @@ +#include "config.h" + +#include + +#include +#include + +int main() +{ + Dune::TestSuite t; + + { + 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); + } + + return t.exit(); +} diff --git a/dune/common/test/fvectorconversion1d.cc b/dune/common/test/fvectorconversion1d.cc new file mode 100644 index 0000000..137e774 --- /dev/null +++ b/dune/common/test/fvectorconversion1d.cc @@ -0,0 +1,127 @@ +// -*- 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 + +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 will trigger a problem in the DenseVector + // operator=() which can be 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; + + static_assert(!std::is_assignable::value, + "Inconsistent assignability detected."); + // mfv = mv; // <- this would not compile dispite the assignability check. + } + + { + 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; + + static_assert(!std::is_assignable::value, + "Inconsistent assignability detected."); + // mfv = mv; // <- this would not compile dispite the assignability check. + } + 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..4b5316e --- /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]; + bool b DUNE_UNUSED; + rt n DUNE_UNUSED; + + 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; + FieldVector z2 DUNE_UNUSED = 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; + const FieldVector< std::complex ,d> ccv DUNE_UNUSED = 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); + bool b DUNE_UNUSED; + + 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); + bool b DUNE_UNUSED; + + 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; + DUNE_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() { + DUNE_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() +{ + DUNE_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/gcdlcmtest.cc b/dune/common/test/gcdlcmtest.cc new file mode 100644 index 0000000..31d478f --- /dev/null +++ b/dune/common/test/gcdlcmtest.cc @@ -0,0 +1,33 @@ +// -*- 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 + +void test() +{ + static_assert((Dune::Gcd<2*2*2*5*5*5*11, 2*2*5*13>::value == 2*2*5), + "gcd not working properly"); + static_assert((Dune::Lcm<11,3>::value == 33), + "lcm not working properly"); + static_assert((Dune::Lcm<18,15>::value == 18*15/3), + "lcm not working properly"); + static_assert((Dune::Lcm<10800,Dune::Lcm<36000,7680>::value>::value==1728000), + "lcm not working properly"); +} + +int main() +{ + std::cout<<" gcd(2,5)="<::value<<" gcd(3, 18)=" + <::value<<" gcd("<<2*2*2*5*5*5*11<<", " + << 2*2*5*13<<")="<::value + < +#include + +int main(){ + // Test the TestIterator; + typedef TestContainer Container; + Container bidicontainer; + + Container::const_iterator cit = bidicontainer.begin(); + //This should fail since makeing a mutable iterator from a const iterator + //discard qualifiers + Container::iterator it DUNE_UNUSED; + it = cit; +} diff --git a/dune/common/test/hybridutilitiestest.cc b/dune/common/test/hybridutilitiestest.cc new file mode 100644 index 0000000..77d1ee3 --- /dev/null +++ b/dune/common/test/hybridutilitiestest.cc @@ -0,0 +1,110 @@ +// -*- 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 +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."; + + 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."; + + 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/integersequence.cc b/dune/common/test/integersequence.cc new file mode 100644 index 0000000..10791cc --- /dev/null +++ b/dune/common/test/integersequence.cc @@ -0,0 +1,34 @@ +#include + +#include +#include +#include + +#include +#include +#include + +template< class T, std::size_t... Ints > +std::tuple< typename std::tuple_element< Ints, std::array< T, sizeof...( Ints ) > >::type... > +array_to_tuple_impl ( const std::array< T, sizeof...( Ints ) > &array, Dune::Std::index_sequence< Ints... > ) +{ + auto tuple = std::make_tuple( array[ Ints ]... ); + return tuple; +} + +template< class T, std::size_t N > +auto array_to_tuple ( const std::array< T, N > &array ) + -> decltype( array_to_tuple_impl( array, Dune::Std::make_index_sequence< N >{} ) ) +{ + return array_to_tuple_impl( array, Dune::Std::make_index_sequence< N >{} ); +} + +int main ( int, char** ) +{ + using Dune::operator<<; + std::array< int, 4 > array{{ 1, 2, 3, 4 }}; + + auto tuple = array_to_tuple_impl( array, Dune::Std::make_index_sequence< 4 >{} ); + std::cout << Dune::className( tuple ) << std::endl; + std::cout << tuple << std::endl; +} 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..6a9e062 --- /dev/null +++ b/dune/common/test/iteratortest.hh @@ -0,0 +1,411 @@ +// -*- 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 +#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); + typename Container::const_iterator cbegin1 DUNE_UNUSED = 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/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..4ec9a68 --- /dev/null +++ b/dune/common/test/mpihelpertest.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 + +#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); + + Helper::MPICommunicator comm DUNE_UNUSED = mpi.getCommunicator(); + comm= mpi.getCommunicator(); + } + + { + Helper& mpi = Helper::instance(argc, argv); + + Helper::MPICommunicator comm DUNE_UNUSED = 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 +#include +#include + +#include +#include +#include + +class T { +public: + + T() : valid_(true) {} + + T(const T& other) : valid_(true) {} + + T(T&& other) : valid_(true) { other.valid_ = false; } + + T& operator=(const T& other) { valid_ = true; return *this; } + + T& operator=(T&& other) { valid_ = true; other.valid_ = false; return *this; } + + bool valid() const { return valid_; } + +protected: + bool valid_; +}; + +Dune::Std::optional< std::string > create ( bool b ) +{ + if( b ) + return "void"; + else + return {}; +} + +int main() +{ + Dune::TestSuite test; + auto optFalse = create( false ); + auto optTrue = create( true ); + + // construction/value check + test.check( !optFalse ); + test.check( bool(optTrue) ); + test.check( optTrue.value() == "void" ); + test.check( *optTrue == "void" ); + + // value_or check (Not implemented atm) + test.check( optFalse.value_or( "empty" ) == "empty" ); + test.check( optTrue.value_or( "notEmpty" ) == "void" ); + + // emplace check + optFalse.emplace( "foo" ); + test.check( bool(optFalse) ); + test.check( optFalse.value() == "foo" ); + + // reset test (Not implemented atm) + optTrue.reset(); + test.check( !optTrue ); + + // swap check + optFalse.swap( optTrue ); + test.check( !optFalse ); + test.check( *optTrue == "foo" ); + + // Check construction of optional for const types + Dune::Std::optional o1(42); + Dune::Std::optional DUNE_UNUSED o2(o1); + Dune::Std::optional DUNE_UNUSED o3(std::move(o1)); + + optFalse = create( true ); + test.check( *optFalse == "void" ); + + { + T t1; + Dune::Std::optional o(t1); + test.check(t1.valid()); + + T t2; + o = t2; + test.check(t2.valid()); + + T t3; + o = std::move(t3); + test.check(not(t3.valid())); + } + + return test.exit(); +} diff --git a/dune/common/test/overloadsettest.cc b/dune/common/test/overloadsettest.cc new file mode 100644 index 0000000..b87c428 --- /dev/null +++ b/dune/common/test/overloadsettest.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 + +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. The following should work with an + // empty capture list []. Unfortunately it does not on + // gcc 5 and gcc 6. As a workaround we capture a dummy + // value. + int dummyCapture=0; + auto f = Dune::overload( + [dummyCapture](const int& t) { (void) t;}, + [dummyCapture](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..049f78d --- /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; +} + +static const int DUNE_UNUSED 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..91b68af --- /dev/null +++ b/dune/common/test/parametertreetest.cc @@ -0,0 +1,330 @@ +// -*- 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 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"; + + Dune::ParameterTree c; + Dune::ParameterTreeParser::readINITree(s, c); + + // 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/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..90fbee2 --- /dev/null +++ b/dune/common/test/powertest.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: + +#include + +#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..604fabf --- /dev/null +++ b/dune/common/test/quadmathtest.cc @@ -0,0 +1,141 @@ +// -*- 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 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; + + int DUNE_UNUSED z1 = x1; + float DUNE_UNUSED z2 = x2; + double DUNE_UNUSED z3 = x3; + long double DUNE_UNUSED 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 + + auto DUNE_UNUSED M3 = M.leftmultiplyany(M2); + auto DUNE_UNUSED 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..61b44dc --- /dev/null +++ b/dune/common/test/rangeutilitiestest.cc @@ -0,0 +1,267 @@ +#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(auto&& dummy DUNE_UNUSED : 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 Dune::Std::conjunction, std::is_const>> +{}; + +template +struct is_mutable_reference : public Dune::Std::conjunction, Dune::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 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; +} + + + +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()); + + 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/shared_ptrtest.cc b/dune/common/test/shared_ptrtest.cc new file mode 100644 index 0000000..d05ba6a --- /dev/null +++ b/dune/common/test/shared_ptrtest.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: + +// 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 +#include +#include +#include +#include + +template +class Deleter +{ +public: + Deleter(bool& deleted) : + deleted_(deleted) + {} + + void operator() (T* p) const + { + delete p; + deleted_ = true; + } + +private: + bool& deleted_; +}; + +class A {}; +class B : public A {}; +class C : A {}; + + +Dune::shared_ptr test_make_shared() +{ + return Dune::make_shared(); +} + + +int main(){ + using namespace Dune; + int ret=0; + { + // test default constructor + shared_ptr foo; + + // test conversion in make_shared + shared_ptr a=test_make_shared(); + + { + shared_ptr b(new B); + a=b; + + if(b.use_count()!=2) { + std::cout << "Reference count is wrong! "<<__LINE__<<":"<< + __FILE__< bar(foo); + shared_ptr baz; + baz = foo; + } + + // test cast-to-bool + if (foo) { + std::cout << "Default constructor doesn't create a NULL pointer!" << std::endl; + ret=1; + } + + // test custom deleter + bool deleted = false; + { + shared_ptr bar(new int(1), Deleter(deleted)); + + //test if deleter is called + deleted = false; + bar.reset(new int(2)); // this should call the deleter in the constructor + if (not (deleted)) + { + std::cout << "Custom deleter not called!" << std::endl; + ret=1; + } + + //test if old deleter is not called + deleted = false; + bar.reset(); // this should call no deleter + if (deleted) + { + std::cout << "Old deleter was called!" << std::endl; + ret=1; + } + + //test if old deleter is not called + deleted = false; + bar.reset(new int(3), Deleter(deleted)); // this should call no deleter + if (deleted) + { + std::cout << "Old deleter was called!" << std::endl; + ret=1; + } + // going out of scope should call the deleter + } + if (not (deleted)) + { + std::cout << "Custom deleter not called!" << std::endl; + ret=1; + } + { + shared_ptr bar(new int(1), Deleter(deleted)); + + bar.reset(new int(4)); // this should call the deleter... + + deleted = false; + // ... but going out of scope should call no deleter + } + if (deleted) + { + std::cout << "1Old deleter was called!" << std::endl; + ret=1; + } + + // test constructor from a given pointer + shared_ptr bar(new double(43.0)); + assert(bar); + + // test constructor from nullptr + shared_ptr bar_null(nullptr); + assert(!bar_null); + assert(!bar_null.get()); + + // test reset() + bar.reset(); + assert(!bar); + + // test get() for empty shared_ptr + assert(!bar.get()); + + // test reset(T*) + double* p = new double(44.0); + bar.reset(p); + assert(bar); + assert(bar.use_count()==1); + + // test get() + double* barPtr = bar.get(); + assert(barPtr==p); + + // test constructor from a given pointer + shared_ptr b(new double(42.0)); + { + shared_ptr d(b); + *b = 7; + } + + if(b.use_count()!=1) { + std::cout << "Reference count is wrong! "<<__LINE__<<":"<< + __FILE__< c(b); + + if(*b!=*c) { + std::cerr<<"References do not match! "<<__LINE__<<":"<< + __FILE__< foobar = shared_ptr(new int(42)); + shared_ptr foobaz; //null ptr + + foobar = foobaz; // should release memory held by foo + } + + // test shared_ptr for stack allocation + { + int i = 10; + shared_ptr pi = stackobject_to_shared_ptr(i); + } + + // test shared_ptr for stack allocation with down cast + { +DUNE_NO_DEPRECATED_BEGIN + B b2; + 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/stdtypetraitstest.cc b/dune/common/test/stdtypetraitstest.cc new file mode 100644 index 0000000..15e1e08 --- /dev/null +++ b/dune/common/test/stdtypetraitstest.cc @@ -0,0 +1,88 @@ +// -*- 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; + + // Check is_callable + { + auto f = [](int /*i*/) { return 0; }; + using F = decltype(f); + + test.check(Dune::Std::is_callable() == true) + << "Dune::Std::is_callable does not accept copy from r-value"; + test.check(Dune::Std::is_callable() == true) + << "Dune::Std::is_callable does not accept copy from l-value reference"; + test.check(Dune::Std::is_callable() == true) + << "Dune::Std::is_callable does not accept copy from r-value reference"; + + test.check(Dune::Std::is_callable() == false) + << "Dune::Std::is_callable accepts invalid argument type"; + test.check(Dune::Std::is_callable() == false) + << "Dune::Std::is_callable accepts invalid argument count"; + test.check(Dune::Std::is_callable() == true) + << "Dune::Std::is_callable does not accept valid return type"; + test.check(Dune::Std::is_callable() == false) + << "Dune::Std::is_callable accepts invalid return type"; + } + + { + auto f = [](const int& /*i*/) {}; + using F = decltype(f); + + test.check(Dune::Std::is_callable() == true) + << "Dune::Std::is_callable does not accept const& temporary from r-value"; + test.check(Dune::Std::is_callable() == true) + << "Dune::Std::is_callable does not accept const& temporary from l-value reference"; + test.check(Dune::Std::is_callable() == true) + << "Dune::Std::is_callable does not accept const& temporary from r-value reference"; + } + + { + auto f = [](int& /*i*/) {}; + using F = decltype(f); + + test.check(Dune::Std::is_callable() == false) + << "Dune::Std::is_callable accepts l-value reference from r-value"; + test.check(Dune::Std::is_callable() == true) + << "Dune::Std::is_callable does not accept l-value reference from l-value reference"; + test.check(Dune::Std::is_callable() == false) + << "Dune::Std::is_callable accepts l-value reference from r-value reference"; + } + + { + auto f = [](int&& /*i*/) {}; + using F = decltype(f); + + test.check(Dune::Std::is_callable() == true) + << "Dune::Std::is_callable does not accept r-value reference from r-value"; + test.check(Dune::Std::is_callable() == false) + << "Dune::Std::is_callable accepts r-value reference from l-value reference"; + test.check(Dune::Std::is_callable() == true) + << "Dune::Std::is_callable does not accept r-value reference from r-value reference"; + } + + // Check negation + { + test.check(Dune::Std::negation::value == false) + << "Dune::Std::negation of std::true_type is not false"; + test.check(Dune::Std::negation::value == true) + << "Dune::Std::negation of std::false_type is not true"; + test.check(Dune::Std::negation>::value == true) + << "Double Dune::Std::negation is not the identity"; + test.check(Dune::Std::negation>::value == false) + << "Double Dune::Std::negation is not the identity"; + } + + return test.exit(); +} 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..85ac225 --- /dev/null +++ b/dune/common/test/testdebugallocator.cc @@ -0,0 +1,108 @@ +// -*- 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 + +#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; +} + +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 + + basic_tests(); + allocator_tests(); + new_delete_tests(); +#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/to_unique_ptrtest.cc b/dune/common/test/to_unique_ptrtest.cc new file mode 100644 index 0000000..91554e7 --- /dev/null +++ b/dune/common/test/to_unique_ptrtest.cc @@ -0,0 +1,92 @@ +// -*- 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* f_old() { return new int(0); } + +Dune::ToUniquePtr f1() { return new int(1); } +auto f2() { return Dune::makeToUnique(1); } + +struct A { double x = 1.0; }; +Dune::ToUniquePtr g() { return new A{}; } + +int main() +{ + using namespace Dune; + TestSuite t; + + { + int* ptr = new int(1); + + // test constructor + ToUniquePtr w1( ptr ); + + // test assignment from makeToUnique + ToUniquePtr w2 = makeToUnique(2); + + // test conversion to pointer + DUNE_NO_DEPRECATED_BEGIN + int* p1 = f1(); + A* p2 = g(); + DUNE_NO_DEPRECATED_END + + delete p1; + delete p2; + + // test conversion to unique_ptr + { + std::unique_ptr u1 = f1(); + std::unique_ptr u2 = f2(); + std::unique_ptr u3 = g(); + } + + // test conversion to shared_ptr + { + std::shared_ptr s1 = f1(); + std::shared_ptr s2 = g(); + std::shared_ptr s3 = w2; + t.check(!bool(w2)) << "w2 should be invalidated"; + } + + // test move assignment + { + ToUniquePtr w3( new int(3) ); + w1 = std::move(w3); + + t.check(!bool(w3)) << "w3 should be invalidated after move assignment"; + } + t.check(bool(w1)) << "w1 should not be invalidated"; + + // test move construction + { + ToUniquePtr w4( std::move(w1) ); + t.check(!bool(w1)) << "w1 should be invalidated after move construction"; + } + + // test management of ownership in ToUniquePtr + { + auto w5 = makeToUnique(5); // should free at the end of scope + } + + // test unique_ptr-like interface of ToUniquePtr + auto w6 = makeToUnique(6); + t.check(*w6 == 6) << "Access to value of ToUniquePtr"; + + w6.reset(new int(7)); + t.check(*w6 == 7) << "Access to value of ToUniquePtr after reset"; + + w6.reset(); + t.check(!bool(w6)) << "w6 should be invalidated"; + } + + std::unique_ptr x0{ f_old() }; + std::unique_ptr x1{ f1() }; + std::unique_ptr x2 = f1(); + return t.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..4bb8022 --- /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..396602d --- /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); + + DUNE_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_UNUSED; + Dune::PointerPairDeletor::apply(p); + if(p != PointerTuple1(nullptr,nullptr,nullptr,nullptr)){ + ret+=20; + std::cerr<<"PointerPairDeletor not working!"< +#include +#include // An initializer of MPI +#include // We use exceptions +#include + +#include + +// some non-default constructible type +struct F { + int i; + F() = delete; + F(int j) : + i(j) {} +}; + +Dune::TestSuite testVariant() { + using namespace Dune; + TestSuite suite; + + int i = 42; + double d = 3.14; + F f(13); + using V = std::vector; + + auto variant = Std::variant(); + + suite.check(Std::variant_size_v == 4, "Test variant_size_v"); + + + variant = d; + suite.check(Std::holds_alternative(variant), "Test holds_alternative"); + + variant = f; + suite.check(Std::holds_alternative(variant), "Test holds_alternative"); + + variant = i; + suite.check(Std::holds_alternative(variant), "Test holds_alternative"); + + suite.check(Std::get(variant) == i, "Test get"); + suite.check(Std::get<0>(variant) == i, "Test get"); + + suite.check(Std::get_if(&variant) != nullptr, "Test get_if on right type"); + suite.check(Std::get_if(&variant) == nullptr, "Test get_if on wrong type"); + + suite.check(Std::get_if<0>(&variant) != nullptr, "Test get_if on right index"); + suite.check(Std::get_if<1>(&variant) == nullptr, "Test get_if on wrong index"); + + Std::variant* var_nullptr = nullptr; + suite.check(Std::get_if<0>(var_nullptr) == nullptr, "Test get_if on nullptr input"); + + // test if get throws if one tries to get the wrong type: + try { + // currently hold type is still int, so double should throw + Std::get(variant); + suite.check(false, "Test get on wrong type should have thrown"); + } + catch (...) { + suite.check(true, "Test get on wrong type has thrown"); + } + + + variant = V(1); + suite.check(Std::get(variant).size() == 1, "Test with non-trivial type"); + + variant = f; + + suite.check(variant.index() == 2, "Test index()"); // we're at type F, which has position 2 + + // Demonstrate visit concept and using vector as an example of a non-POD type + using V2 = std::vector; + Std::variant variant2; + variant2 = V(1); + auto size = [](auto&& v) {return v.size();}; + suite.check(Std::visit(size, variant2)== 1, "Test visit"); + variant2 = V2(2); + suite.check(Std::visit(size, variant2)== 2, "Test visit"); + + // try on a const reference: + const auto& constv2 = variant2; + suite.check(Std::visit(size, constv2)== 2, "Test const visit"); + suite.check(Std::get_if(&constv2) != nullptr, "Test const get_if"); + + + // Check visitor returning lvalue + using A = struct { int j; int i;}; + using B = struct { int i;}; + Std::variant variant3; + variant3 = A{1,2}; + Std::visit([](auto&& value) -> decltype(auto) { return (value.i); }, variant3) = 42; + suite.check(Std::visit([](auto&& value) { return value.i;}, variant3)== 42, "Std::visit returning lvalue"); + + /// test copy and move construction/assignment + { + auto variant_copy_constructed = variant2; + // test if deep copy happened + auto getPtrToData = [&](const auto& vec) {return static_cast(vec.data()); }; + suite.check(Std::visit(getPtrToData, variant_copy_constructed) != Std::visit(getPtrToData, variant2), "Check deep copy") << "Both vector copies point to same data"; + + auto variant_move_constructed = std::move(variant_copy_constructed); + // TODO: Add sensible test condition here. + // Testing if the pointer to the data is the same as before is probably not a good idea, + // as moving does not imply that the data really stays at the same place (though it probably + // does). + // At least if this compiles and runs we can be confident no double frees happened. + // + // First idea: Test if the state looks somewhat valid + suite.check(Std::holds_alternative(variant_move_constructed), "Check if move constructed variant holds the right type"); + + Std::variant variant_copy_assigned; + variant_copy_assigned = variant2; + // test if deep copy happened + suite.check(Std::visit(getPtrToData, variant_copy_assigned) != Std::visit(getPtrToData, variant2), "Check deep copy at operator=") << "Both vector copies point to same data"; + + Std::variant variant_move_assigned; + variant_move_assigned = std::move(variant_copy_assigned); + // TODO: Again, as above, find a better test for this. + suite.check(Std::holds_alternative(variant_move_assigned), "Check if move assigned variant holds the right type"); + } + + return suite; +} + +int main(int argc, char** argv) +{ + try{ + // Maybe initialize MPI + Dune::MPIHelper::instance(argc, argv); + + Dune::TestSuite suite; + suite.subTest(testVariant()); + + return suite.exit(); + } + catch (Dune::Exception &e){ + std::cerr << "Dune reported error: " << e << std::endl; + } + catch (...){ + std::cerr << "Unknown exception thrown!" << std::endl; + } +} diff --git a/dune/common/test/vcexpectedimpltest.cc b/dune/common/test/vcexpectedimpltest.cc new file mode 100644 index 0000000..4da519c --- /dev/null +++ b/dune/common/test/vcexpectedimpltest.cc @@ -0,0 +1,71 @@ +#include "config.h" + +#if !HAVE_VC +#error Inconsistent buildsystem. This program should not be built in the \ + absence of Vc. +#endif + +#include +#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..9e7b385 --- /dev/null +++ b/dune/common/to_unique_ptr.hh @@ -0,0 +1,99 @@ +// -*- 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 + +#include + +#include + +namespace Dune +{ + /// \brief An owning pointer wrapper that can be assigned to (smart) pointers. Cannot be copied. + /// Transfers ownership by cast to any (smart) pointer type. Releases the stored pointer on transfer. + /// NOTE: This is an intermediate solution to switch to std::unique_ptr in later releases smoothly. + /** + * Example of usage: + * ``` + * ToUniquePtr f() { return new int(1); } + * auto g() { return makeToUnique(2); } + * + * int* p1 = f(); // p1 gets ownership, must delete explicitly + * delete p1; + * + * std::unique_ptr p2 = f(); + * std::shared_ptr p3 = f(); + * + * auto p4 = f(); // ToUniquePtr has itself pointer semantic + * std::cout << *p4 << '\n'; + * + * std::unique_ptr p5( g() ); + * ``` + **/ + template + class ToUniquePtr + : public std::unique_ptr + { + using Super = std::unique_ptr; + + public: + // Member types: + //@{ + + using pointer = typename Super::pointer; + + //@} + + + public: + // Constructors: + //@{ + + /// Constructor, stores the pointer. + ToUniquePtr(pointer ptr = pointer()) noexcept + : Super(ptr) + {} + + /// Constructor, creates a `nullptr` + ToUniquePtr(std::nullptr_t) noexcept + : Super(nullptr) + {} + + //@} + + + public: + // Conversion operators: + //@{ + + /// Convert to underlying pointer, releases the stored pointer. NOTE: deprecated + DUNE_DEPRECATED_MSG("Cast to raw pointer is deprecated. Use std::unique_ptr or std::shared_ptr instead.") + operator pointer() noexcept { return Super::release(); } + + /// Convert to unique_ptr, invalidates the stored pointer + operator std::unique_ptr() noexcept { return std::move(static_cast(*this)); } + + /// Convert to shared_ptr, invalidates the stored pointer + operator std::shared_ptr() noexcept { return std::move(static_cast(*this)); } + + /// Checks whether *this owns an object + explicit operator bool() noexcept { return bool(static_cast(*this)); } + + /// Checks whether *this owns an object + explicit operator bool() const noexcept { return bool(static_cast(*this)); } + + //@} + }; + + + /// Constructs an object of type T and wraps it in a ToUniquePtr, \relates ToUniquePtr + template + ToUniquePtr makeToUnique(Args&&... args) + { + return {new T(std::forward(args)...)}; + } + +} // end namespace Dune + +#endif // DUNE_TO_UNIQUE_PTR_HH diff --git a/dune/common/tupleutility.hh b/dune/common/tupleutility.hh new file mode 100644 index 0000000..86a4b4b --- /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